Gate level modeling works best for circuits having a limited number of gates. It allows the designer to instantiate and connect each gate individually. But as the circuit becomes bigger, Gate level modeling starts to become tough. Thus, we shift to the next level of abstraction in Verilog, Dataflow modeling.
Dataflow modeling makes use of the functions that define the working of the circuit instead of its gate structure. The designer has to bear in mind how data flows within the design. Dataflow modeling has become a well-liked design approach, as logic synthesis tools became sophisticated. This approach allows the designer to focus on optimizing the circuit in terms of the flow of data.
Let’s see how that works.
Contents
What is Dataflow modeling?
Dataflow modeling describes hardware in terms of the flow of data from input to output. For example, to describe an AND gate using dataflow, the code will look something like this:
module and_gate(a,b,out); input a,b; output out; assign out = a&b; endmodule
In the above code, we executed the functionality of the AND gate with the help of the AND (&) operator. That is what dataflow modeling is about. It utilizes operators that act on operands and gives the desired relationship between output and input.
Definition of Net
Nets are a datatype in Verilog that represent connections between hardware elements. Nets don’t store values. They have the values of the drivers. For instance, in the figure, the net out connected to the output is driven by the driving output value A&B.
A net data type must be used when a signal is:
- driven by the output of some device.
- declared as an input or inout port.
- on the left-hand side of a continuous assignment.
This can be a bit tricky to understand in theory. We will look at its practical application below.
- Verilog has different net types, such as wire, trior, wand, trireg, etc.
- Nets need to be declared too.
- They are primarily declared using the keyword wire. Since other net types are not used commonly, we need not go much into detail about that.
// nets from above AND gate module and_in_out(A,B,out); input wire A,B;//declaring the nets for the input output wire out;//declaring the nets for the output endmodule
Nets can also be declared as vectors. This is really useful when dealing with a large number of wires. For example, if you want to code a 16:1 multiplexer, it would be annoying to declare each of the 16 inputs individually. You would rather declare them as a vector quantity.
Take a look at the example below to understand how vector nets are declared.
module adder_in_out(a,b,out); input wire[2:0] a,b; output wire[3:0] out; //3 out wires endmodule
Continuous Assignment
The continuous assignment statement is the main construct of dataflow modeling and is used to drive (assign) value to the net. It starts with the keyword assign. How convenient!
So we can write general syntax as:
assign <net_expression> = [drive_strength] [delay] <expression of different signals or constant value>
Does the syntax feel too complicated? Don’t fret!
Let’s go through some examples to understand continuous assignments better.
For instance, we have used an assignment statement in the above code for AND:
assign out = a&b;
This statement means that (a & b) is evaluated and then assigned to out.
Let’s see another example:
assign sum = in1 + in2; // in1 + in2 is evaluated and assigned to sum
There are some characteristics we should keep in mind while we use dataflow modeling. They are:
- Continuous assignments are always active. That is the LHS net value changes as soon as the value of any operand in the RHS changes.
- The LHS of an assignment should be either scalar or vector nets or a concatenation of both. Registers are not applicable on the LHS.
- The RHS of the assignment can be register, net, or function calls of scalar or vector type.
- Delays can be specified. We’ll see this below.
Now, let’s discuss different ways of how a continuous assignment is placed on a net.
Continuous Assignment on Vectors
As described in the characteristics, the continuous assignment can be performed on vector nets.
module adder(a,b,sum); input [2:0] a,b; output [3:0] sum; assign sum = a + b; $display("a = %b, b = %b, sum=%b", a,b,sum); endmodule
The above code describes a 3-bit adder. The MSB of the sum is dedicated to carry in the above module.
//Simulation log
a = 100, b = 111, sum = 1011 // (a = 4, b = 7, sum = 011, carry = 1)
The concatenation of vector and scalar nets are also possible. The same code for 3-bit adder is written using concatenation below:
module adder(a,b,sum); input [2:0] a,b; output [2:0] sum; //sum is a vector output carry; // carry is a scalar assign {carry,sum} = a + b; //assigning result to a concatenation of scalar and vector $display("a = %b, b = %b, sum=%b, carry = %b", a,b,sum,carry); endmodule
Simulation log
a = 100, b = 111, sum = 011, carry = 1 //
Regular continuous assignment
It follows the steps shown:
- Declare net
- Write continuous assignment on the net
The below code follows Regular continuous assignment. Basic stuff.
wire out; // net 'out' is declared assign out = a&b; //continuous assignment on declared net
Implicit continuous assignment
We can also place a continuous assignment on a net when it is declared. The format will look like the one below:
wire out = a & b; // net declaration and assignment together
Implicit Net Declaration
Even if we don’t declare LHS as a net, it will automatically create a net for the signal name. So, the assignment statement will look like
wire a,b; out = a&b; // out not declared as wire but an implicit wire declaration is done by the simulator
Delays
As promised earlier, we can take a detailed look into how delays can be specified in the continuous assignment.
What is delay?
In real-world hardware, there is a time gap between change in inputs and the corresponding output. For example, a delay of 2 ns in an AND gate implies that the output will change after 2 ns from the time input has changed.
Delay values control the time between the change in an RHS operand and when the new value is assigned to LHS. It is similar to specifying delays for gates. Adding delays helps in modeling the timing behavior in real circuits. It is basically getting us closer to simulating the practical reality of a functioning circuit.
There are different ways to specify a delay in continuous assignment statements. Let’s learn about it.
Regular Assignment Delay
We assign a delay value in the continuous assignment statement. The delay value is specified after the keyword assign. For example,
assign #10 out = a & b;
To get a clear understanding of how it works, let’s see how the simulated waveform looks like for the following code:
module and_gate(a,b,out) input a,b; output out; assign #10 out = a & b; endmodule
Note the following changes in waveform:
- a and b goes low at 5 ns, out goes low 10 time units later. That is 15 ns.
- a and b is high at 20 ns, out goes high 10 time units later. That is 30 ns
- a goes low at 40 ns, out goes low 10 time units later. That is 50 ns.
- a goes high at 65 ns but becomes low at 70 ns. Therefore, at the time of re-computation (i.e., 75 ns), a and b are low, out will be low. Thus, a pulse of width less than the specified delay is not propagated to the output. This property is called inertial delay.
Implicit Continuous Assignment Delay.
Here, we use an implicit continuous assignment to specify both a delay and an assignment on the net.
wire #10 out = a & b; //implicit Continuous Assignment Delay.
same as
wire out assign #10 out = a & b;
Net Declaration Delay
Here, delay is added when the net is declared without putting continuous assignment.
wire #10 out; assign out = a &b;
This code has the same effect as the following:
wire out assign #10 out = a & b;
That covers the second modeling style that we will be studying in this Verilog course. In the next post, we will take a look at the behavioral style of modeling in Verilog.