Full Training Programs
Free Technical Resources
In the last section, we looked at describing hardware conceptually using always blocks. What kind of hardware can we describe? What are the limitations? What kinds of Verilog statement can be used in always blocks to describe hardware? Well, we have already seen the use of an if statement to describe a multiplexer, so let's dwell on if statements in this section.
always @(sensitivity-list) // invalid Verilog code! begin // statements end
The code snippet above outlines a way to describe combinational logic using always blocks. To model a multiplexer, an if statement was used to describe the functionality. In addition, all of the inputs to the multiplexer were specified in the sensitivity list.
reg f; always @(sel or a or b) begin if (sel == 1) f = a; else f = b; end
It is a fundamental rule of the Verilog HDL that any object that is assigned a value in an always statement must be declared as a variable. Hence,
reg f; // must be declared before it is used in a statement
The term variable was introduced in the verilog-2001 standard. Previously, the term used was register. This was confusing, because a Verilog variable (register) does not necessarily imply that a hardware register would be synthesised, hence the change of terminology.
It transpires that in order to create Verilog code that can be input to a synthesis tool for the synthesis of combinational logic, the requirement for all inputs to the hardware to appear in the sensitivity list is a golden rule.
Golden Rule 1:
To synthesize combinational logic using an always block, all inputs to the design must appear in the sensitivity list.
Altogether there are 3 golden rules for synthesizing combinational logic, we will address each of these golden rules over the next couple of sections in this tutorial.
The if statement in Verilog is a sequential statement that conditionally executes other sequential statements, depending upon the value of some condition. An if statement may optionally contain an else part, executed if the condition is false. Although the else part is optional, for the time being, we will code up if statements with a corresponding else rather than simple if statements. In order to have more than one sequential statement executed in an if statement, multiple statements are bracketed together using the begin..end keywords,
reg f, g; // a new reg variable, g always @(sel or a or b) begin if (sel == 1) begin f = a; g = ~a; end else begin f = b; g = a & b; end end
If statements can be nested if you have more complex behaviour to describe:
reg f, g; always @(sel or sel_2 or a or b) if (sel == 1) begin f = a; if (sel_2 == 1) g = ~a; else g = ~b; end else begin f = b; if (sel_2 == 1) g = a & b; else g = a | b; end
Notice that the code is beginning to look a little bit confusing! In the code above, begin..end blocks have only been used where they must be used, that is, where we have multiple statements. It is probably a good idea to use begin..end blocks throughout your Verilog code - you end up typing in a bit more Verilog but it's easier to read. Also, if you have to add more functionality to an always block later on (more sequential statement), at least the begin..end block is already in place. So,
reg f, g, h; // yes, an extra reg variable, h always @(sel or sel_2 or a or b) begin if (sel == 1) begin f = a; if (sel_2 == 1) begin h = ~b; g = ~a; end else begin g = a | b; h = a & b; end end else begin if (sel_2 == 1) begin g = a & b; h = ~(a & b); end else begin h = ~(a | b); g = a | b; end f = b; // here's f! end end
Note that the order of assignments to f, g and h has been played around with (just to keep you on your toes!).
If statements are synthesized by generating a multiplexer for each variable assigned within the if statement. The select input on each mux is driven by logic determined by the if condition, and the data inputs are determined by the expressions on the right hand sides of the assignments. During subsequent optimization by a synthesis tool, the multiplexer architecture may be changed to a structure using and-or-invert gates as surrounding functionality such as the a & b and the ~a can be merged into complex and-or-invert gates to yield a more compact hardware implementation.