Traditionally when we want to iterate, we use a counter. A behavior for-loop has the structure as follows
for (<initial>; <expression>; <step>) begin <something> end
A for loop can only be synthesized inside a process/always block because it is a sequential statement. It synthesizes when the loop range is constant at compile time, and does not contain WAIT statements.
The synthesizer will unroll every statements inside the for loop.
For example, some for loop code might be
for (...) begin P = P ^ SW[i] end
P = P ^ SW; P = P ^ SW; P = P ^ SW;
Note that this example could be replaced by
Let’s consider the ‘power of’ circuit again.
Writing out using for loops, we get
for (i=0; i < 3; i=i+1) P = P * X;
This will be unrolled to
always @(X) begin P = 1; P = P * X; P = P * X; P = P * X; end endmodule
We cannot create hardware for an arbitrary number of exponents. Since we need a real, constant number in the for loop during compile time.
Loop isn’t a iterative events happening over time. It is just a shortcut to make writing hardware easier.
Loops are very useful in simulations and test benches to make life easier.
Consider a problem where we have a bunch of multiplexers:
It becomes really tedious when we have much more components to add and connect.
genvar i; generate for (i=0; i<4; i=i+1) begin: big_mux mux_4to1 mux(W[4*i], W[4*i+1], W[4*i+1], W[4*i+2], W[4*i+3], S[1:0], M[i]); end endgenerate
Notice that the assignment for
muxports depends on
i. And that
Wis concatenated beforehand.
Similar to for loops, the generate statements will unroll it. But for loop is sequential, and generate statement is concurrent, meaning that all modules are placed at the same time in parallel.
An example of a useful scenario to use generate statements is an adder.
Note that we can also use conditional statements in the generate statement.
Normally when we have some kind of wire, it could be driven by some gate or input signal. Sometimes it is useful to have a bus where multiple module is connected to the same wire. Typically, there a module is driving the bus, and some other module that is connected to the bus is listening.
So the three states corresponding to:
- Bus is driven to 0
- Bus is driven to 1
- Bus is left floating
What happens when the same wire is being driven by two sources? We don’t know.
When Verilog sees
1 driven into the same signal, it sets the value of the signal to
X, which means the state is unknown. Note that in the real circuit, the node will always have some voltage.
Quartus synthesizer will thrown an error (
Cant resolve multiple constant drivers for net).
Some how we need a control to say which one is actually driving it. Hence the tristate driver
enable is high,
out is driven with the value on
enable is low, the output is not being driven. Not being driven can be denoted as
Z in simulation.
The truth table is as:
module tristate_driver9(I, ENABLE, F); input I, ENABLE; output reg F; always @(*) if (ENABLE == 1) F <= I; else F <= 1'bz; endmodule
What actually happen in FPGA’s?
People typically do not use tristate logic in their designs (because of speed issues). Modern FPGAs actually have no facility for implementing tristate drivers. (Synthesizer like Quartus will try to convert the tristate logic into multiplexer networks).
However, tristate is still used in off-chip I/O.