Visualize
the Hardware:
|
Describe the
Hardware:
|
Comments:
|
“Do Nothing” circuit
|
module Gadget;
endmodule
|
A minimal Verilog description. The system has no I/O
connections or internal functionality.
Verilog is case-sensitive throughout. Keywords must appear in lower case.
|
“Do Nothing” with I/O
|
module
Gadget (a,b,c);
// Port modes
input a,b;
output c;
endmodule
|
I/O port names
Valid Verilog names include alphanumerics, underscore ‘_’, and dollar sign
‘$. Verilog names can be quite long.
Describe the “mode” (input or output) of each port.
Has no internal functionality.
|
Two-input NAND gate based on assign keyword
|
module
Gadget (a,b,c);
/* Port modes */
input a,b;
output c;
// Functionality
assign c = ~(a & b);
endmodule
|
Alternative comment style
Describe a NAND gate using two “bitwise” operators.
NOTE: The “assign” technique is called a “continuous assignment.”
|
|
Bitwise Operators:
~ NOT
& AND
| OR
^ EXOR
|
Bitwise operators work on a bit-by-bit basis; for example:
“1 & 1” is 1
“01 | 10” is 11 (binary)
|
Two-Input NAND gate based on always keyword
|
module
Gadget (a,b,c);
// Port modes
input a,b;
output c;
// Registered identifiers
reg c;
// Functionality
always @ (a or b)
c <= ~(a & b);
endmodule
|
“Registered identifiers” can generate
a logic value. The concept of a registered identifier is broader than simply
an array of flip-flops; combinational circuit outputs must also be declared
as registered identifiers when the “always” method (“procedural assignment”)
is used.
Describes a combinational circuit using the procedural assignment technique.
The “always” keyword means the circuit always (continuously) watches the
signals in the input list ( the “@ (a or b)” part) and responds to changes in
those signals by immediately updating the output identifier.
Use the “<=” symbol to assign an output inside an “always” block.
|
Two-input NAND and EXOR
|
module
Gadget (a,b,c,d);
// Port modes
input a,b;
output c;
output d;
// Registered identifiers
reg c,d;
// Functionality
always @ (a or b) begin
c <= ~(a & b);
d <= a ^ b;
end
endmodule
|
Can have multiple “input” and “output” declarations
Can enclose multiple output signal assignments inside a “begin” - “end”
block; everything between “begin” and “end” is treated as a single statement.
|
Two-input MUX
|
module Mux2 (
A, // A input
B, //
B input
Sel, // Selector
Y //
Output
);
// Port modes
input A,B,Sel;
output Y;
// Registered identifiers
reg Y;
// Functionality
always @ (A or B or Sel)
if (Sel==0)
Y <= A;
else
Y <= B;
endmodule
|
Demonstrates ability to embed comments inside the code
Use “if-else” technique to connect output “Y” to one of the two data inputs
based on the value of the data selector “Sel”.
The expression inside the parentheses after “if” must evaluate to either “1”
(true) or “0” (false).
|
|
Relational Operators:
== Equal to
!= Not equal
< Less than
> Greater than
<= Less than or equal
>= Greater than or equal
&& AND
|| OR
|
Relational operators evaluate the comparison and return
either true (1) or false (0):
In the previous example, “Sel==0” evaluates to “1” if “Sel” is zero,
otherwise the comparison evaluates to “0”.
|
|
More Operators:
>> Shift right
<< Shift left
+ Add
- Subtract
* Multiply
/ Divide
% Modulus
|
NOTE: Multiplication and division are not supported by standard hardware
synthesis tools
|
Two-input MUX: Alternative method
|
// Functionality
always @ (A or B or Sel)
if (Sel)
Y <= B;
else
Y <= A;
|
Since “Sel” is a single-bit control signal, the expression
inside the “(...)” test can be written more concisely.
|
Two-input MUX: Another alternative method
|
// Functionality
always @ (A or B or Sel)
Y <= (Sel) ? B : A;
|
Here the ternary (three-part) operator is used. The “if-else”
test is embedded into the assignment. Read the statement like this: “Is the
control signal ‘Sel’ equal to 1? If yes, then use ‘B’ when making the
assignment to ‘Y’, otherwise use ‘A’ ”.
|
Four-input MUX
|
module
Mux4 (
Data, // Data input
Sel, // Selector
Y //
Output
);
// Port modes
input
[3:0] Data;
input
[1:0] Sel;
output Y;
// Registered identifiers
reg Y;
// Functionality
always @ (Data or Sel)
if (Sel == 0)
Y <= Data[0];
else if (Sel == 1)
Y <= Data[1];
else if (Sel == 2)
Y <= Data[2];
else Y <= Data[3];
endmodule
|
Bus widths are not included in the
port list.
A four-bit bus. The square brackets define the bus width. The left-side
number is the MSB.
A two-bit bus is used for the data selector.
“if-else-if” technique
Method for referring to individual bus signals
|
Four-Input MUX: Alternative method
|
// Functionality
always @ (Data or Sel)
case (Sel)
0: Y <= Data[0];
1: Y <= Data[1];
2: Y <= Data[2];
3: Y <= Data[3];
default: Y <= Data[0];
endcase
|
“case” technique (similar to “switch” statement in C language). First match
between the case labels (left of colon) and the signal inside the parentheses
causes the associated assignment to be made.
“default” keyword is a catch-all – if a match is not found, the default
assignment is made.
Use “begin-end” blocks to do multiple assignments for a given case selector
statement.
|
16-Input MUX with custom behavior
|
module
Mux16 (
Data, // Data input
Sel, // Selector
Y //
Output
);
// Port modes
input [15:0] Data;
input [3:0] Sel;
output Y;
// Registered identifiers
reg Y;
// Functionality
always @ (Data or Sel)
casez (Sel)
4’b0000: Y <= Data[0];
4’b0001: Y <= Data[1];
4’b01??: Y <= Data[2];
default: Y <= Data[3];
endcase
endmodule
|
Fully-specified constant – the “4” numerical value says the constant is 4
bits wide, and the ‘b says the digits are expressed in binary (use ‘d for
decimal, ‘h for hexadecimal, and ‘o for octal). Underscores can be used to
improve readability of long constants: 7’b010_0010
The question mark is a “don’t care” when it appears inside a numerical
constant. In this example, the case selection ignores the two lowest bits and
looks for a match on the two highest bits. You need to use the ‘casez’
version of the ‘case’ statement when using “don’t cares”.
All other values of the “Sel” control signal cause the fourth data bit to be
selected.
Data bits 15 through 4 are effectively not used in this example.
|
Code Translator (specified as a truth table)
|
module
Code_Translator (
Code_In,
Code_Out,
);
// Port modes
input [2:0] Code_In;
output [2:0] Code_Out;
// Registered identifiers
reg [2:0] Code_Out;
// Functionality
always @ (Code_In)
case
(Code_In)
3’b000: Code_Out <= 3’b101;
3’b001: Code_Out <= 3’b111;
3’b010: Code_Out <= 3’b001;
3’b011: Code_Out <= 3’b000;
3’b100: Code_Out <= 3’b100;
3’b101: Code_Out <= 3’b010;
3’b110: Code_Out <= 3’b110;
3’b111: Code_Out <= 3’b011;
endcase
endmodule
|
The “case” statement can be used to directly implement a truth-table
specification.
The “default” keyword is optional – however, you can get unexpected results
if the preceding cases do not cover all possible input combinations.
|
Miscellaneous Techniques
|
{a,b}
{Data[13:12],a,b,Data[11:0]}
16{a}
assign Y = (en) ? X : 1’bz;
|
Group signals together with curly braces
Complex grouping – in this example, two signals are inserted between existing
bus signals to form a new bus.
Replicates the single-bit signal ‘a’ into a 16-bit bus (each bus bit value is
the same as ‘a’).
Tristate output control. When the enable (‘en’) is active, the output gets
the signal ‘X’, otherwise the output is in high-impedance state.
|
4-bit magnitude comparator
|
module
Compare4 (
A, //
Data input A
B, //
Data input B
AltB, // A is less than B
AeqB, // A is equal to B
AgtB // A is > than B
);
// Port modes
input [3:0] A,B;
output AltB,AeqB,AgtB;
// Registered identifiers
reg AltB,AeqB,AgtB;
// Functionality
always @ (A or B) begin
AltB <= (A < B);
AeqB <= (A == B);
AgtB <= (A > B);
end
endmodule
|
Since the relational operator evaluates to either a “1” or a “0”, the result
of the comparison can be directly assigned to the single-bit result.
|
Parameterized design for n-bit comparator
|
module
Compare4 (
A, //
Data input A
B, //
Data input B
AltB, // A is less than B
AeqB, // A is equal to B
AgtB // A is > than B
);
// Define bus width
parameter BusWidth = 8;
// Port modes
input [BusWidth-1:0] A,B;
output AltB,AeqB,AgtB;
// Registered identifiers
reg AltB,AeqB,AgtB;
// Functionality
always @ (A or B) begin
AltB <= (A < B);
AeqB <= (A == B);
AgtB <= (A > B);
end
endmodule
|
Define bus width (use “8” in this example).
Make all subsequent references to bus width using the parameter. This way
entire design can be adjusted simply by changing the original parameter
statement.
|
Parameterized design (alternative method)
|
`define BusWidth = 8;
|
This technique uses a the “define” compiler directive. All
compiler directives begin with the backwards apostrophe. The advantage of
this method over the “parameter” method is that CAD tools will allow you to
specify the value of your “define” directive outside your Verilog modules,
obviating the need to edit the Verilog files themselves. This is particularly
valuable if you want to deliver parameterizable designs to another designer
without that designer having to edit the files.
|