Thursday, April 23, 2020

Verilog Tutorial 6: Synthesizable Constructs

This is the sixth part of the Verilog Tutorial series. Click here for the first part.

As discussed before, every piece of Verilog code is written so as to model some hardware component. Our aim is to ensure that we stick to this rule. The code has to be completely synthesizable (to model hardware).
Here we will discuss about all the Synthesizable Verilog constructs.

Synthesizable Verilog constructs that we have used till now in the tutorials:
module, input, output, reg, wire, assign, always, blocking & non-blocking assignments, if-else, case, parameter

Synthesizable Verilog constructs that will be discussed in this post:
`define, generate, for, verilog primitives 

Non-synthesizable verilog constructs are used only in testbenches. They cannot be used to synthesize hardware. Examples are:  
delays, fork-join


Let us discuss about the above mentioned synthesizable constructs:

`define
1. `define macro is a compiler directive that performs global macro substitution (it is similar in working to a parameter).
2. A parameter always resides within a module whereas `define can exist even outside of a module.
3. The define can be evaluated with `ifdef-`elsif-`else-`endif statements.
4. A `define remains active for all files until the macro is redefined or it is undefined using `undef compiler directive. 

Consider the code snippet as shown.

`define B1

always @ *
begin
`ifdef A1
 begin
 a = 1'b1;
 end
`elsif B1
 begin
 b = 1'b1;
 end
`else
 begin
 c = 1'b1;
 end
`endif
end

Here since `define B1 is mentioned on top, b gets the value of 1 wheres a and c remain unchanged (`elsif B1 condition will be satisfied).
For complex designs, we can have a large number of defines like this which can be imported from a separate defines file. (extension is .vh)
To import a file with defines, we add the file name on top of the module: `include filename.vh

Let us use `define to create a counter that modifies the parametrized counter used in the previous part into an up or down counter.

Verilog Code:

module counter(cnt,clk,rst);

parameter BITS = 3;  

input clk,rst;
output [BITS-1:0]cnt;  

reg [BITS-1:0]cnt;
wire [BITS-1:0]next_cnt;

`ifdef UP assign next_cnt = cnt + 1'b1; //conditions
`else assign next_cnt = cnt - 1'b1;
`endif  

always @ (posedge clk or negedge rst)   
begin
 if(!rst)
 begin
 cnt <= {BITS{1'b0}};
 end
 else
 begin
 cnt <= next_cnt;
 end
end

endmodule

Note that the `define condition can be called from anywhere, but the condition exists throughout the design. For the above design, if I call `define UP, then an up counter will be realized. Otherwise by default, it will be a down counter.

Generate
Types:
1. Generate for-loop
2. Generate if-else
3. Generate case
Generate is used to allow multiple module instantiation (Generate for loop) or select the instance to be instantiated (Generate if-else and Generate case)

Let us look at our 3-bit up counter design.
Let us instantiate 3 modules of D Flip-Flop instead of performing flopping in the same module. This can be done in a compact form using the Generate for loop.

module counter(cnt,clk,rst);
input clk,rst;
output [2:0]cnt;

wire [2:0]next_cnt;
wire [2:0]cnt;

assign next_cnt = cnt + 1'b1;

genvar i; //variable for generate for-loop

generate

for(i=0; i<3; i=i+1)
begin :DFF_Instances  //generate for-loop name is DFF_Instances
DFlipFlop dff(.d(next_cnt[i]),.clk(clk),.rst(rst),.q(cnt[i]));
end

endgenerate

endmodule

Notes:
1. Variable used in the instantiation process is called genvar.
2. Provide a name to the generate block as shown. (: followed by generate name)
3. The above module will generate 3 instances of D-Flip Flop just like how we require.
Similarly, one can write a code for Generate if-else and Generate case constructs.


For-Loop
For-loop is also a synthesizable construct, though it is less frequently used in digital design. 
There are two cases where for loop can be distinctively useful:
1. Replicating a piece of code multiple times (as we saw in the above example where multiple instances where replicated using a for-loop under generate)
2. When using dynamic indexing to select a part (few bits) of a variable.

Verilog does not support dynamic indexing. It means that you cannot select a random number of bits from a variable dynamically.

For example:
Assume that I have a variable called temp which has to be stored in another variable declared as mem.
But I want to store only a few bits of temp in mem, where the bit position is indicated by a variable 'counter'. Example: If I want to store 8 bits of temp in mem, counter will indicate 8.
Then what one would normally do is:

module main;
reg [31:0] mem;
reg [31:0] counter;
reg [5:0] temp;

always @ *
begin
mem = temp[counter-1:0];
end
endmodule

However, Verilog compilers will throw an error stating part select expressions must be constant. Since counter is a dynamic variable, the above code will not work.
Hence, the above program can be rewritten using for-loop as shown:

module main;
reg [31:0] mem;
reg [31:0] counter;
reg [5:0] temp;

always @ *
begin
integer i;
for(i=0;i<counter;i++)
mem[i] = temp[i];
end
endmodule

Code Replication case
For-loop for creating multiple variables dynamically
Consider a case in which a master has to create variables addr, len and size for each slave based on the number of slaves (NUM_SLAVE).
This can be done using a dynamic for-loop with the following syntax:

`for(i=0; i<NUM_SLAVE; i=i+1)

reg s`i::_addr;
reg s`i::_len;
reg s`i::_size;

`endfor

Here `i:: will be replaced by the appropriate number from 0 to NUM_SLAVE.
Eg. If there is 1 slave, it will create variables s0_addr, s0_len and s0_size. 

There is another construct that we use called the let construct.
`let construct is very similar to `define construct but the difference is that `let has a local scope whereas `define has a global scope.
So `let can be used when the expression is valid only for that particular module.


Verilog Primitives

Verilog language provides along with it a set of primitive constructs that can be used in design.
Examples:
Gates: and, nand, or, nor, xor, xnor, not
Buffer: buf
Tri-state buffer with active low enable: bufif0
Tri-state buffer with active high enable: bufif1
Transistor switch: pmos, nmos, cmos

Example Usage:
and (out, in1, in2) -> Here out gets the ANDed value of in1 and in2
bufif1 (inout_net, input_net, en) -> Here the inout_net will be driven by input_net whenever en is high
This is useful for modelling bidirectional pads.


No comments:

Post a Comment