A Simple DesignA design is described in Verilog using the concept of a module. A module can be conceptualised as consisting of two parts, the port declarations and the module body. The port declarations represent the external interface to the module. The module body represents the internal description of the module - its behaviour, its structure, or a mixture of both. Let's imagine we want to describe an and-or-invert (AOI) gate in Verilog.
Verilog: an AOI gate module
// Verilog code for AND-OR-INVERT gate
module AOI (input A, B, C, D, output F);
assign F = ~((A & B) | (C & D));
endmodule
// end of Verilog codeOK, that's the code. Let's dissect it line by line...
Comments// Verilog code for AND-OR-INVERT gateLike all programming languages, Verilog supports comments. There are two types of comment in Verilog, line comments and block comments; we will look at line comments for now. Comments are not part of the Verilog design, but allow the user to make notes referring to the Verilog code, usually as an aid to understanding it. Here the comment is a “header” that tells us that the Verilog describes an AOI gate. It is no more than an aide de memoire in this case. A Verilog compiler will ignore this line of Verilog. Two forward slashes mark the start of a line comment, which is ignored by the Verilog compiler. A line comment can be on a separate line or at the end of a line of Verilog code, but in any case stops at the end of the line.
Module and Port declarationsmodule AOI (input A, B, C, D, output F);The name of the module is just an arbitrary label invented by the user. It does not correspond to a name pre-defined in a Verilog component library. module is a Verilog keyword. This line defines the start of a new Verilog module definition. All of the input and output ports of the module must appear in parentheses after the module name. The ordering of ports is not important for the module definition per se, although it is conventional to specify input ports first.
A port may correspond to a pin on an IC, an edge connector on a board, or any logical channel of communication with a block of hardware. The port declarations include the names of the ports ( e.g.,
A,
B ), and the direction that information is allowed to flow through the ports (
input,
output or
inout).
EndmoduleendmoduleThe module definition is terminated by the Verilog keyword
endmodule.
FunctionalityWell, that's the interface to the module taken care of, but what about it's functionality?
assign F = ~((A & B) | (C & D));In this module body, there is but one statement, and all the names referenced in this statement are in fact the ports of the design. Because all of the names used in the module body are declared in the module header and port declarations, there are no further declarations for internal elements required in the module body.
assign is a Verilog keyword. It denotes a concurrent continuous assignment, which describes the functionality of the module. The concurrent assignment executes whenever one of the four ports A, B, C or D change value. The ~, & and | symbols represent the bit-wise not, and and or operators respectively, which are built in to the Verilog language. That's it! That's all there is to describing the functionality of an AOI gate in Verilog.
// end of Verilog codeAnother Verilog comment, and that's the end of a Verilog description for an AOI gate.
Verilog 1995The above example is written using Verilog-2001 syntax. Many people continue to use the 1995 syntax, which is still allowed in Verilog-2001. In Verilog-1995 the module header would look like this:
module AOI (A, B, C, D, F);
input A, B, C, D;
output F;Note that the port names are listed after the module name, and declared as inputs and outputs in separate statements. The port declarations must repeat the names of the ports in the module header.
WiresThe module shown on the “Modules” page, was simple enough to describe using a continuous assignment where the output was a function of the inputs. Usually, modules are more complex than this, and internal connections are required. To make a continuous assignment to an internal signal, the signal must first be declared as a wire.
A Verilog wire represents an electrical connection.
Verilog: Internal signals of an AOI gate module// Verilog code for AND-OR-INVERT gate
module AOI (input A, B, C, D, output F);
wire F; // the default
wire AB, CD, O; // necessary
assign AB = A & B;
assign CD = C & D;
assign O = AB | CD;
assign F = ~O;
endmodule
// end of Verilog codeOK, that's the code. Let's examine it a little more closely...
Wire Declarationswire AB, CD, O;This is the syntax for a wire declaration. A wire declaration looks like a Verilog-1995 style port declaration, with a type (wire), an optional vector width and a name or list of names. You can create separate wire declarations if you wish, for example:
wire AB, CD;
wire O;is an alternative way of creating wire declarations.
Note that ports default to being wires, so the definition of wire F in the Verilog code is optional.
Continuous Assignmentsassign AB = A & B;
assign CD = C & D;
assign O = AB | CD;
assign F = ~O;In this module body, there are four continuous assignment statements. These statements are independent and executed concurrently. They are not necessarily executed in the order in which they are written. This does not affect the functionality of the design. Suppose
assign AB = A & B; changes value. This causes
B to be evaluated. If
AB changes as a result then
assign O = AB | CD; is evaluated. If O changes value then
assign F = ~O; will be evaluated; possibly the output of the module will change due to a change on
B.
Wire AssignmentsA wire can be declared and continuously assigned in a single statement - a wire assignment. This is a shortcut which saves declaring and assigning a wire separately. There are no advantages or disadvantages between the two methods other than the obvious difference that wire assignments reduce the size the the text.
Later on we will discuss delays on assignments and wires. A delay in a wire assignment is equivalent to a delay in the corresponding continuous assignment, not a delay on the wire. Thus it could be necessary to separate the wire declaration from the continuous assignment to put the delay onto the wire rather than the assignment. Note that this is a subtle point that you are unlikely to encounter in practice!
Verilog: Using wire assignments to describe an AOI gate module// Verilog code for AND-OR-INVERT gate
module AOI (input A, B, C, D, output F);
/* start of a block comment
wire F;
wire AB, CD, O;
assign AB = A & B;
assign CD = C & D;
assign O = AB | CD;
assign F = ~O;
end of a block comment */
// Equivalent...
wire AB = A & B;
wire CD = C & D;
wire O = AB | CD;
wire F = ~O;
endmodule
// end of Verilog codeSo in this sample code, each of the wire declarations and its corresponding assign statement are effectively merged into one wire assignment.
Note the use of a block comment in the Verilog code, rather than the line comments we have seen so far. A block comment may span several lines of code. Block comments may not be nested.
A Design HierarchyModules can reference other modules to form a hierarchy. Here we see a 2:1 multiplexer with an inverting data path consisting of an AOI gate and a pair of inverters.
Module InstancesThe MUX_2 module contains references to each of the lower level modules, and describes the interconnections between them. In Verilog jargon, a reference to a lower level module is called a module instance.
Each instance is an independent, concurrently active copy of a module. Each module instance consists of the name of the module being instanced (e.g. AOI or INV), an instance name (unique to that instance within the current module) and a port connection list.
The module port connections can be given in order (positional mapping), or the ports can be explicitly named as they are connected (named mapping). Named mapping is usually preferred for long connection lists as it makes errors less likely.
Verilog: 2-input multiplexer module// Verilog code for 2-input multiplexer
module INV (input A, output F); // An inverter
assign F = ~A;
endmodule
module AOI (input A, B, C, D, output F);
assign F = ~((A & B) | (C & D));
endmodule
module MUX2 (input SEL, A, B, output F); // 2:1 multiplexer
// wires SELB and FB are implicit
// Module instances...
INV G1 (SEL, SELB);
AOI G2 (SELB, A, SEL, B, FB);
INV G3 (.A(FB), .F(F)); // Named mapping
endmodule
// end of Verilog codeYes, it's time to dissect the code line by line again, but we'll concentrate on the new lines as the module interface has been covered before (see
A Simple Design).
Implicit Wires// wires SELB and FB are implicitThe wires used in continuous assignments MUST be declared. However, one-bit wires connecting component instances together do not need to be declared. Such wires are regarded as implicit wires. Note that implicit wires are only one bit wide, if a connection between two components is a bus, you must declare the bus as a wire.
Module InstancesAOI G2 (SELB, A, SEL, B, FB);In a module instance, the ports defined in the module interface are connected to wires in the instantiating module through the use of port mapping. For the instance of AOI, the first wire in the port list is SELB. In the module header for the AOI gate, A is the first port in the port list, so SELB is connected to A. The second port in the module header is B, the second wire in the port list is A, thus the wire A in MUX2 is connecyted to the port B of the AOI gate instance.
INV G3 (.A(FB), .F(F));The second INV instance, G3, uses named mapping rather than positional mapping. In the port list for the G£ instance, the wire FB is connected to the input port, A, of the INV instance. The period character is followed by the name of the module header port; in brackets following the formal port, the name of the wire is entered.
Test BenchesTest benches help you to verify that a design is correct. How do you create a simple testbench in Verilog?
Let's take the exisiting MUX_2 example module and create a testbench for it. We can create a template for the testbench code simply by refering to the diagram above.
module MUX2TEST; // No ports!
...
initial
// Stimulus
...
MUX2 M (SEL, A, B, F);
initial
// Analysis
...
endmoduleInitial StatementIn this code fragment, the stimulus and response capture are going to be coded using a pair of initial blocks. An initial block can contain sequential statements that can be used to describe the behaviour of signals in a test bench.
In the Stimulus initial block, we need to generate waveform on the A, B and SEL inputs. Thus:
initial // Stimulus
begin
SEL = 0; A = 0; B = 0;
#10 A = 1;
#10 SEL = 1;
#10 B = 1;
endOnce again, let's look at each line in turn.
SEL = 0; A = 0; B = 0;This line contains three sequential statements. First of all, SEL is set to 0, then A, then B. All three are set to 0 at simulation time 0.
#10 A = 1;In terms of simulation, the simulator now advances by 10 time units and then assigns 1 to A. Note that we are at simulation time = 10 time units, not 10 ns or 10 ps! Unless we direct the Verilog simulator otherwise, a Verilog simulation works in dimensionless time units.
#10 SEL = 1;
#10 B = 1;These two lines are similar to the one above. 10 time units after A is set to 1, SEL is set to 1. Another 10 time units later (so we are now at simulation time = 30 time units), B is set to 1. The diagram below shows how the initial block has created a waveform sequence for the three signals.
We shall look at the use of the initial block to capture the MUX_2's response in the next section of the tutorial.
Response CaptureIn the previous section of the tutorial, we looked at describing stimuli in Verilog to test our 2-input multiplexer. So next, we’ll look at how to capture the response of our device under test.
Remember from the module template that we are using initial blocks to code up the Stimulus and Response blocks.
module MUX2TEST; // No ports!
...
initial
// Stimulus
... MUX2 M (SEL, A, B, F);
initial
// Analysis
...
endmoduleThe Response initial block can be described very easily in Verilog as we can benefit from a built-in Verilog system task. Thus:
initial // Response
$monitor($time, , SEL, A, B, F);Once again, let's look at each item in turn.
$monitor();$monitor is a system task that is part of the Verilog language. Its mission in life is to print values to the screen. The values it prints are those corresponding to the arguments that you pass to the task when it is executed. The
$monitor task is executed whenever any one of its arguments changes, with one or two notable exceptions.
$time$time is a system function (as opposed to a system task). It returns the current simulation time. In the above example,
$time is an argument to
$monitor. However,
$time changing does not cause
$monitor to execute -
$monitor is clever enough to know that you wouldn't really want to print to the screen the values of all of the arguments every time the simulation time changed.
, , ,The space at position 2 in the argument list ensures that a space is printed to the screen after the value of
$time each time
$monitor is executed. This is a simple method of formatting the screen output.
SEL, A, B, FFinally we come to the signal arguments themselves. Each time one of these signals changes value, $monitor will execute. When
$monitor executes it will print all of the argument values to the screen, including
$time. This is the output created by
$monitor in our
MUX2 testbench:
0 0000
10 0101
20 1100
30 1111This is simply a tabular listing of the waveforms that would be generated during simulation (if we had a waveform viewer, that is!).
It's amazing what you can learn from two lines of code, isn't it? We'll look at more elaborate output formatting soon.
RTL VerilogRemember this?
Now we are going to look at the principles of RTL coding for synthesis tools.
Most commercially available synthesis tools expect to be given a design description in RTL form. RTL is an acronym for register transfer level. This implies that your Verilog code describes how data is transformed as it is passed from register to register. The transforming of the data is performed by the combinational logic that exists between the registers. Don't worry! RTL code also applies to pure combinational logic - you don't have to use registers. To show you what we mean by RTL code, let's consider a simple example.
module AOI (input A, B, C, D, output F);
assign F = ~((A & B) | (C & D));
endmoduleYes! The AOI gate that we have used as an example so far has actually been written in RTL form. This means that continuous assignments are a valid way of describing designs for input to RTL synthesis tools. What other code techniques can we use? How about:
module MUX2 (input SEL, A, B, output F);
input SEL, A, B;
output F;
INV G1 (SEL, SELB);
AOI G2 (SELB, A, SEL, B, FB);
INV G3 (.A(FB), .F(F));
endmoduleModule instances are also examples of synthesizable RTL statements. However, one of the reasons to use synthesis technology is to be able to describe the design at a higher level of abstraction than using a collection of module instances or low-level binary operators in a continuous assignment. We would like to be able to describe what the design does and leave the consideration of how the design is implemented up to the synthesis tool. This is a first step (and a pretty big conceptual one) on the road to high-level design. We are going to use a feature of the Verilog language that allows us to specify the functionality of a design (the ‘what') that can be interpreted by a synthesis tool.
Always blocksAlways blocks are akin to the initial blocks that you have met already in Test Benches. Initial blocks are procedural blocks that contain sequential statements. Initial blocks execute just once. Always blocks on the other hand are always available for execution. This means that the statements inside an always block are executed up until the closing
end keyword:
always
begin
// statements
endBut then they can be executed again! This means that a way of controlling execution through an always block is required. In describing synthesizable designs, a sensitivity list is often used to control execution (we shall see other approaches later).
always @(sensitivity-list)
begin
// statements
endThe sensitivity list consists of one or more signals. When at least one of these signals changes, the always block executes through to the end keyword as before. Except that now, the sensitivity list prevents the always block from executing again until another change occurs on a signal in the sensitivity list.
The statements inside the always block describe the functionality of the design (or a part of it). Let's reconsider the AOI gate:
always @(sensitivity-list)
begin
F = ~((a & b) | (c & d));
endInstead of a continuous assignment, we now have a procedural assignment to describe the functionality of the AOI gate. Notice that the sensitivity list isn't valid Verilog code. We need to create a meaningful sensitivity list. How do we decide when to execute the always block? Perhaps a better question is what do we need to do in order to have
F change value. Answer:
F can only change when at least one of
a, b, c or d changes. After all, these are the four inputs to the AOI gate. That's our sensitivity list:
always @(a or b or c or d)
begin
F = ~((a & b) | (c & d));
endVerilog-2001 introduced additional syntax for describing sensitivity lists.
always @(a, b, c, d)
always @(*)
always @*In the first of these, we have simply replaced the word or with a comma. The other two are equivalent and create an implicit sensitivity list that contains all the signals whose values are read in the statements of the always block. In this example @* or @(*) are equivalent to @(a,b,c,d). When describing combinational logic, it is important to make sure that sensitivity lists are complete; this syntax helps to ensure that this is holds.
Now for the MUX_2 design. In the above code snippet, we simply replaced the continuous assignment with an equivalent always block. We can do the same with the module instances in the MUX_2 design - strip away each instance and replace it with the equivalent always block.
always @(sel)
begin
selb = ~sel;
end
always @(a or sel or b or selb)
begin
fb = ~((a & sel) | (b & selb));
end
always @(fb)
begin
f = ~fb;
endBut we can do better than this. Let's merge the three always blocks together remembering that in the process (a pun for the VHDL'ers amongst you!) the sensitivity list of the resulting one always block contains only those signals that cause F to change value.
always @(sel or a or b)
begin
selb = ~sel;
fb = ~((a & sel) | (b & selb));
f = ~fb;
endWhen writing RTL code, “think functionality, think inputs” is a useful aide memoire in terms of bridging the gap between concept and code. Well, we have already taken care of the inputs as the sensitivity list now consists of only the MUX_2 input ports.
For the functionality, let’s get conceptual. If sel is a logic 1, a is routed through to the f output. On the other hand if sel is a logic 0, b is routed through to the f output. Rather than think about routing one of the inputs through to the output let's think about the output getting one of the inputs, and let's write the text on separate lines depending upon whether we are making a decision or performing an action (sometimes referred to as pseudo-code):
if sel is logic 1
f gets a
otherwise
f gets bThis can be translated into Verilog code:
if (sel == 1)
f = a;
else
f = b;Now before we go any further, we'll just take this code snippet a line at a time.
if (sel == 1)The Verilog language allows for many different kinds of sequential statement. The procedural assignment is one you have already come across not only on this page but also in test benches (assignments to
SEL,
A and
B in the stimulus initial block, if you remember). Here's another: the if statement. Actually this line is part of the if-else statement that is the entire code snippet. if is a Verilog keyword. After the if keyword you have a conditional expression, in this case
(sel == 1) - does sel have the value logic 1? If so...
f = a;f gets the value on the
a input. Or in Verilog jargon, a procedural assignment. But what if sel is not logic 1?
elseOtherwise (assume sel is logic 0 - more on this assumption later)...
f = b;
f gets the value on the b input.
So, as it turns out, we have described the funcionality of the MUX_2 design using a single procedural statement, the if-else statement. In each branch of this if-else statement, there is an additional procedural statement, either assigning a to f, or b to f, depending upon the value of sel. But we have to remember that this procedural statement lives inside an always block, so...
always @(sel or a or b)
begin
if (sel == 1)
f = a;
else
f = b;
endThis now enables us to describe a design using a list of continuous assignments, a hierarchy of designs or an always block. Compare the 3 approaches for yourself:
// continuous assignments
assign selb = ~sel;
assign fb = ~((a & sel) | (b & selb));
assign f = ~fb
// a hierarchy of designs
INV G1 (SEL, SELB);
AOI G2 (SELB, A, SEL, B, FB);
INV G3 (.A(FB), .F(F));
// always block
always @(sel or a or b)
begin
if (sel == 1)
f = a;
else
f = b;
endAnd of course you can mix'n'match coding styles if you wish. On a simple design, such as a MUX_2 it is perhaps not apparent how succinct the use of always blocks is in general compared to module instances and continuous assignments. But you can readily appreciate that the use of just one always block in this design is enabling us to describe the design in terms of its functionality without regard to the implementation. You can describe what you want without having to worry about how you are going to implement the design (because you don't have to - that's the synthesis tool's job!).
Go on! Read the MUX_2 design into your synthesis tool and have a play.
If statementIn 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
endThe 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;
endVariable declarationIt 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 statementThe 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.
Combinational logicIt 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.
If statementThe 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
endIf 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;
endNotice 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
endNote that the order of assignments to
f,
g and
h has been played around with (just to keep you on your toes!).
Synthesis considerationsIf 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.
Synthesizing LatchesIn the last section, if statements were used to describe simple combinational logic circuits. Synthesizing the Verilog code produced multiplexing circuits, although the exact implementation depends upon the synthesis tool used and the target architecture of the device.
As well as enabling the creation of multiplexers, if statements can also be used to implement tristate buffers and transparent latches. In this article we will look at how transparent latches are synthesized from if statements and how to avoid the inadvertent creation of latches when you meant to create combinational logic circuits from Verilog code containing if statements.
If StatementsIn the processes that have been coded up so far, if-else statements rather than simple if statements have been used. Let's use a simple if statement rather than an if-else statement in an example you have already seen:
reg sel, a, b;
always @ (sel or a or b)
begin : if_else
if (sel == 1)
f = a;
else
f = b;
end
becomes...
reg sel, a, b;
always @ (sel or a or b)
begin : pure_if
f = b;
if (sel == 1)
f = a;
end
Note that the behaviour being described is the same. In the
pure_if always block,
f initially gets
b. Only if
sel is active HIGH does
f get
a. This is perhaps a slightly odd way to describe a multiplexing circuit but it is accepted by all synthesis tools. Synthesis tools expect to create circuits responding to binary values. As far as a synthesis tool is concerned if s
el is 1
a is routed through to
f. If sel is not 1 it must be 0 and thus sel being 0 leaves f being driven by the initial assignment from b.
Let's lose the b input to the always block so that we have:
reg sel, a;
always @ (sel, a)
begin : latching_if
if (sel == 1)
f = a;
endIncomplete AssignmentNow analyze the behaviour of the code. If
sel is 1,
f gets
a. But what happens when
sel is 0? Well, very simply, nothing!
f does not and can not change. When sel is fixed at 0, we can change
a as much as we like,
f will not be assigned the value of a. If we suppose that an if statement synthesises to a multiplexer, then we must be able to configure the multiplexer such that
f only gets the value of
a when
sel is 1. This can be achieved by feeding back the multiplexer
f output back to the 0 input; in hardware terms this is a transparent latch and this is exactly the hardware synthesized by a synthesis tool given this Verilog code.
If the target architecture does not contain transparent latches the synthesis tool will generate multiplexer circuits that employ combinational feedback in order to mimic the latching behaviour required.
Now, this is very well but what's really happening here? One minute if statements create multiplexers, the next they create latches. Well, it's not the if statements, but the process as a whole that counts. If it is possible to execute an always block without assigning a value to a signal in that always block, the reg variable will be implemented as a transparent latch. This is known as incomplete assignment.
Golden Rule 2:
To synthesize combinational logic using an always block, all variables must be assigned under all conditions.Simplifying code analysisSuppose you are creating an always block to describe combinational logic. This always block consists of nested if-else statements as follows:
reg f, g;
always @ (sel or sel_2 or sel_3 or a or b)
begin
if (sel == 1)
begin
f = a;
if (sel_2 == 1)
g = ~ a;
else
begin
g = ~ b;
if (sel_3 == 1)
g = a ^ b;
end
end
else
begin
if (sel_2 == 1)
g = a & b;
else
if (sel_3 == 1)
g = ~(a & b);
// oops! no else
// else
// g = ...
f = b;
end
endWill you get transparent latches on the f and g outputs? Not easy is it? If you look carefully you will see that in fact, g is latched when sel is 0, sel_2 is 0 and sel_3 is 0. The ‘oops!' comment should help you to see where the complete assignment is NOT made.
Default AssignmentFortunately, it is possible to save yourself the bother of scouring through the always block code to locate possible incomplete assignments by setting variables to default values at the start of the always block. Using this approach you may get undesired functionality if you have missed out an assignment (which should be easy to fix) as opposed to unwanted transparent latches. For our current example,
always @ (sel or sel_2 or sel_3 or a or b)
begin
// default values assigned to f, g
f = b;
g = a & b;
if (sel == 1)
begin
f = a;
if (sel_2 == 1)
g = ~ a;
else
begin
g = ~ b;
if (sel_3 == 1)
g = a ^ b;
end
end
else
if (sel_2 == 1)
g = a & b;
else
if (sel_3 == 1)
g = ~(a & b);
end
Nguồn doulos