Wires
wire input a,b;
wire output y;
wire input a2[1:0],b2[1:0];
wire output y2[1:0];
wire output y3[2:0];
Assignment
An assign statement is used for modeling only combinational logic and it is executed continuously. So the assign statement is called 'continuous assignment statement' as there is no sensitive list (see always blocks later).
assign y = a;
Logic bitwise primitives
Negation
assign y = ~a;
AND Gate
assign y = a & b;
OR Gate
assign y = a | b;
Exclusive OR Gate
assign y = a ^ b;
Reduction
assign y = | a2;
is equivalent to:
assign y = a2[1] | a2[0];
Concatenation and Replication
assign y2 = {a,b};
assign y2 = {a,1'b0};
assign y3 = {a,b,1'b1};
assign y3 = {a,2'b10};
assign y3 = {a,a2};
assign y3 = {a,a2[0],1'b1};
assign {y2,y} = {y3[1:0],a};
assign y3 = {a,2{1'b1}};
Shifting
// a a >> 2 a >>> 2 a << 2 a << 3
// 01001111 00010011 00010011 00111100 01111000
// 11001111 00110011 11110011 00111100 01111000
assign y2 = a2 >> 1; // Logical 0's shifted in
assign y2 = a2 >>> 1; // Arithemtic MSB sign bit shifted in
assign y2 = a2 << 1; // Logical shift left same result as
assign y2 = a2 <<< 1; // Arithmetic shift left
Rotation
Rotate right 1 bit
assign y3r = {y3[0],y3[2:1]};
Rotate right 2 bit
assign y3r = {y3[1:0],y3[2]};
Operator precedence
!,~,+,-(uni),**,*,/,%,+,-(bin),>>,<<,>>>,<<<,==,!=,===,!==,&,^,|,&&,||,?: */
Conditionals
Tertiary
assign max = (a > b) ? a : b;
If/Else
if(a < b)
assign min = a;
else
assign min = b;
if(boolean)
// if code
else if (boolean)
// if else 1 code
else
// else code
if(boolean)
begin
// begin code
end
else
begin
// else code
end
Synthesis of Z and X Values
Z values can only be synthesised by tristate bufferes and thus infer them
these have output enable inputs to control their output state for example here
is a single bit tristate buffer with an output enable
assign y = (oen) ? a : 1'bz;
This sort of construuct is useful for biderectional ports or buses.
The synthesis of X is don't care, the value may be either 0 or 1, this can
improve the efficiency or optimisation of combinational circuits
assign y = (a == 2'b00) ? 1'b0:
(a == 2'b01) ? 1'b1:
(a == 2'b10) ? 1'b1:
1'bx; // i == 2'b11
Behavioural Blocks
Procedural blocks using always block, these black box sections describe
behaviour using procedural statements, always behavioural blocks are defined
with an event control expression or sensitivity list
always @(sensitivity list)
begin [Optional label]
[optional local variable declarations];
[procedural statements];
end [optional label]
Procedual Assignment
[variable] = [expression]; // blocking, assigned before next statement
// like normal C
[variable] <= [expression]; // non blocking, assigned at end of always
// block
Blocking tends to be used for combinational circuits, non-blocking for
sequential
In a procedural assignment, an expression can only be assigned to an output
with one of the variable data types, which are reg, integer, real, time, and
realtime. The reg data type is like the wire data type but used with a
procedural output. The integer data type represents a fixed-size (usually 32
bits) signed number in 2's-complement format. Since its size is fixed, we
usually don't use it in synthesis. The other data types are for modeling and
simulation and cannot be synthesized.
Registers
A register is simple memory wire to hold state, normally implemented as
D-Types
output reg
Conditional Examples
binary encoder
en a1 a2 y
0 - - 0000
1 0 0 0001
1 0 1 0010
1 1 0 0100
1 1 1 1000
module pri_encoder
(
input wire [3:0] r,
output wire en
output wire [1:0] y
)
always @*
if(r[3])
{en,y} = 3'b111;
else if(r[2])
{en,y} = 3'b110;
else if(r[1])
{en,y} = 3'b101;
else if(r[0])
{en,y} = 3'b100;
else
{en,y} = 3'b000;
endmodule
module decoder_1
(
input wire [1:0] a,
input wire en,
output reg [3:0] y
)
always @*
if(~en)
y = 4'b0000;
else if(a == 2'b00)
y = 4'b0001;
else if(a == 2'b01)
y = 4'b0010;
else if(a == 2'b10)
y = 4'b0100;
else
y = 4'b1000;
endmodule
Case
module decoder_2
(
input wire [1:0] a,
input wire en,
output reg [3:0] y
)
always @*
case ({en,a})
3'b000, 3'b001,3'b010,3'b011: y = 4'b0000;
3'b100: y = 4'b0001;
3'b101: y = 4'b0010;
3'b110: y = 4'b0100;
3'b111: y = 4'b1000;
endcase // {en,a}
endmodule
module decoder_3
(
input wire [1:0] a,
input wire en,
output reg [3:0] y
)
always @*
case ({en,a})
3'b100: y = 4'b0001;
3'b101: y = 4'b0010;
3'b110: y = 4'b0100;
3'b111: y = 4'b1000;
default: y = 4'b0000;
endcase // {en,a}
endmodule
Casex
module decoder_4
(
input wire [1:0] a,
input wire en,
output reg [3:0] y
)
always @*
casex ({en,a})
3'b0xx: y = 4'b0000;
3'b100: y = 4'b0001;
3'b101: y = 4'b0010;
3'b110: y = 4'b0100;
3'b111: y = 4'b1000;
endcase // {en,a}
endmodule
When the values in the item expressions are mutually exclusive (i.e., a value
appears in only one item expression), the statement is known as a parallel
case statement. When synthesized, a parallel case statement usually infers a
multiplexing routing network and a non-parallel case statement usually infers
a priority routing network. Unlike C where conditional constructs are executed
serially using branches and jumps, with HDL these are realised by routing
netowrks.
Casez
module decoder_4
(
input wire [1:0] a,
input wire en,
output reg [3:0] y
)
always @*
casez ({en,a})
3'b0??: y = 4'b0000;
3'b100: y = 4'b0001;
3'b101: y = 4'b0010;
3'b110: y = 4'b0100;
3'b111: y = 4'b1000;
endcase // {en,a}
endmodule
In casez, the ?
is used to indicate either X or Z state.
Tasks & Functions
Tasks and Functions are used when a set of operations are commonly repeated and used, this save you writing the same thing out over and over a gain. Functions can only be used for modelling combinational logic cannot drive more than one output and cannot contain delays, unlike tasks which can. Tasks also can not return a value unlike functions which can.
Below shows a function for calculating parity, something that may be used frequently in digital logic design.
function parity;
input [31:0] data;
integer i;
begin
parity = 0;
for (i= 0; i < 32; i = i + 1) begin
parity = parity ^ data[i];
end
end
endfunction
Common Errors
- Variable assigned in multiple always blocks
- Incomplete sensitivity list
- Incomplete branch and incomplete output assignment
Multiple assignment
always @*
if(en) y = 1'b0;
always @*
y = a & b;
y
is the output of two circuits which could be contridictary, this is not
synthesiseable. Below is how this should have been writtten:
always @*
if(en)
y = 1'b0;
else
y = a & b;
Incomplete sensitivity list
Incomplete sensitivity list (missing b
). b
could change but the y output
would not, causing unexpected behavior againg this is not synthesiseable.
always @(a)
y = a & b;
/* Fixed versions */
always @(a,b)
y = a & b;
/* or simple cure all */
always @*
y = a & b;
incomplete branch or output assignment
Incomplete branch or output assignment, do not infer state in combinational
circuits.
always @*
if(a > b)
gt = 1'b1; // no eq assignment in branch
else if(a == b)
eq = 1'b1; // no gt assignment in branch
// final else branch omiitted
Here we break both incomplete output assinment rules and branch According to
Verilog definition gt
and eq
keep their previous values when not assigned
which implies internal state, unintended latches are inferred, these sort of
issues cause endless hair pulling avoid such things. Here is how we could
correct this:
always @*
if(a > b)
begin
gt = 1'b1;
eq = 1'b0;
end
else if (a == b)
begin
gt = 1'b0;
eq = 1'b1;
end
else
begin
gt = 1'b0;
eq = 1'b0;
end
Or easier still assign default values to variables at the beginning of the
always block
always @*
begin
gt = 1'b0;
eq = 1'b0;
if(a > b)
gt = 1'b1;
else if (a==b);
eq = 1'b1;
end
Similar errors can creep into case statements
case(a)
2'b00: y =1'b1;
2'b10: y =1'b0;
2'b11: y =1'b1;
endcase
Here the case 2'b01
is not handled, is a has this value y gets it's previous
value and a latch is assumed, the solution is to include missing case, assign
y
a value before the case or add a default clause.
case(a)
2'b00: y =1'b1;
2'b10: y =1'b0;
2'b11: y =1'b1;
default : y = 1'b1;
endcase
Adder with carry
module adder #(parameter N=4)
(
input wire [N-1:0] a,b,
output wire [N-1:0] sum,
output wire cout
);
/* Constant Declaration */
localparam N1 = N-1;
/* Signal Declaration */
wire [N:0] sum_ext;
/* module body */
assign sum_ext = {1'b0, a} + {1'b0, b};
assign sum = sum_ext[N1:0];
assign cout = sum_ext[N];
endmodule
module adder_example
(
input wire [3:0] a4,b4,
output wire [3:0] sum4,
output wire c4
)
// Instantiate a 4 bit adder
adder #(.N(4)) four_bit_adder (.a(a4), .b(b4), .sum(sum4), .cout(c4));
endmodule
LocalParams
localparam N = 4