Академический Документы
Профессиональный Документы
Культура Документы
Note that, each node and each component in the schematics have a unique name. The inputs and
output of the components are important. For instance the inputs of “nd3” are a and d, and the output
is c.
Now let's see the code in detail.
1. The first line is the comment line.
2. This line describes the name of the circuit. The format is as below:
module <circuit_name> ( <pin1>,<pin2>,...,<pin n>);
module is the keyword. The name of the circuit here is flop, and there are 5 pins with
corresponding names. Note that the direction of the pins (either input or output) are not
described yet.
3. This line describes the input pins, and its format is as below:
input <pin 1>, <pin 2>,..., <pin k>;
According to the format above, data, clock, and clear pins are input pins.
4. Similar to line 3, this line describes the output pins. So q and qb are the output pins.
6. Now the description of the circuit starts. For the lines from 6 to 15, the following format is valid:
<primitive_gate> <delay_time> <name> (<out>, <in1>,<in2>...);
This line inserts a component with type of primitive_gate having a transition time of delay_time.
The name of the component is name, and the output and inputs are out and in1, in2,... Writing this
line is same as inserting the component in the schematics, because this line says that there is a gate of
type primitive_gate, and its preferences are like this. For instance see the line 6. It says that there is
a nand gate with 10ns transition time. Its name is nd1, its output is a, and its inputs are data, clock,
and clear. Note that this is exactly one of the nand gates in the schematics (check it!). Each line
between 6 and 15 describe one component. So as you can see, gate-level Verilog-XL code is not very
much different than drawing schematics. Actually I usually prefer drawing schematics instead of this
method :)
One point here is important. Lines 7 to 11 have a slightly different format. It has only the
name and input/output pins of the component. This is a shortcut for describing many components in a
shorter form. This means that nd2, nd4, nd5, nd6, and nd8 have the same transition time of 10ns. Be
careful about the punctuations at the ends of the lines. Line 6 has “,”, and line 11 has “;” as the last
character.
16.States the end of the circuits code with endmodule keyword.
Gate-level HDL representation of the circuit is not much different than drawing the schematics.
Furthermore, you need the schematics of the circuit before typing gate-level code. So, this
method is not preferred much. However, it is a good starting point to learn Verilog. The next
example will be behavioral and it is much more important.
A 1-bit synchronous counter with parallel load and freeze modes, and
with asynchronous reset option
In this part, Verilog-XL code of a 1-bit synchronous counter with parallel load and freeze modes, and
with asynchronous reset option will be described. The following code is the final code, and it will be
described in detail.
To start with, // indicates a single comment line (as in the C++). The CNT module is defined at the
very beginning by keyword module.
module CNT1(D, L, Ti, RB, CLK, Q, Qb, Tout);
Note that, at this point, we do not know which pins are input and which are output. To define these we
should write:
input D, L, Ti, RB, CLK;
output Q, Qb, Tout;
With the above 2 lines, we described the input/output pins of the circuit.
In addition to this, we should also define internal variables, to store results before assigning them to
the output pins. In other words, the output pins cannot be changed directly in the code. Instead, an
internal variable will be used and it will be assigned (or let's say attached) to the output pin. When
this internal variable changes, the output pin which is assigned to it will change also.
In this example, there are 3 outputs. However, since Qb is logical inverse of Q, we need only 2 internal
variables. q and tout are the internal variables (registers) in our example. At the end of the code, tout
is assigned to Tout, q is assigned to Q, and ~q (logical inverse of q) is assigned to Qb. The following
line defines the registers to be used in the code.
reg q, tout;
The next lines define a function. Such functions make codes much simpler and shorter. Frequently used
architectures can be defined as a function, and then they can be invoked from the main block as many
times as desired. As you all know, we need a 4-to-1 multiplexer to perform, both load, toggle, and
freeze operations. For this purpose we have to define a 4-to-1 multiplexer function, which can be done
as follows: (We have a 4-bit input [3:0] in, and a two bit selection bits [1:0] s).
function mux;
input [3:0] in;
input [1:0] s;
case (s)
2'b00: mux = in[0];
2'b01: mux = in[1];
2'b10: mux = in[2];
2'b11: mux = in[3];
endcase
endfunction
The definition of a function is made between function and endfunction keywords. The multiplexing
definition is best described using case. You should refer to the Verilog Tutorial for further information, if
required.
Here note that, 2'b00 represents a 2-bit number of 00. The definition “input [3:0] in;” states that the
input in is a 4-bit number. in[0] corresponds to the 0th bit, and so on.
Is it possible to write this code without using function?
Now, we will define how the registers (and so outputs) will change according to the inputs. For this,
always statement will be used. There are a number of ways to use it.
always @(posedge CLK) q = mux({D, D, ~q, q}, {L, Ti});
The line above describes the synchronous operation of the circuit. Note that, the definitions starting
with always indicates the function dependency of certain signals. Here, the statement in the always
block will be done when positive edge of CLK is sensed (Note that @ and posedge are also keywords).
The statement in the always block is changing the value of q register. Note the use of function mux.
{D,D,~q,q} is the 4-bit input with q is the least significant bit. So in[0] corresponds to q, and so on.
{L, Ti} is the 2-bit output of the function. So, when a positive edge (low-to-high) transition is sensed,
the register q will be changed by the results of mux function.
always @(RB) begin
if (!RB)
assign q = 1'b0;
else
deassign q;
end
This always statement models the asynchronous reset operation. Here, we define that q variable
should be changed whenever there is a change in RB. Inside the always block (begin-end is required
for multi commands), we check the value of RB. If its zero (0), then we assign 0 to q, if not we
deassign q.
Now, one important point should be clear. Two always statements described above changes the value
of q. However, we want to fix the value of q to 0 when RB is low (why?). This is done by using assign-
deassign statement pair. This statement pair is used to fix the value of a register until it is released by
deassign. So, when the value of q is fixed when Rb is low, the other always statements affecting the
value of q are ignored. When Rb becomes high again, q register is released by deassign statement.
You may understand this better by simulating the circuit without deassign statement.
always @(Ti or q) tout = Ti & q;
The last always statement is generating the Tout signal. This time, the module or the lines inside that
block should be executed whenever there is a change in Ti or q. Therefore @(Ti or q) is written after
the always block. Since it is again a single assignment no begin-end are used. Note that & is used for
logical and operation.
So, we used 3 always statements. This is obvious actually, because we should model the synchronous
operation of the circuit, asynchronous reset operation, and the toggle output.
This is it. I am sure that you will be a Verilog expert after a few trials.
The above code is supplied without any warranty. That is it may function or not. It is given just to give
an idea. But verfication is left to you.