Table of Contents

Dataflow Verilog

This is a quick introduction to Dataflow Verilog. Dataflow Verilog is a more flexible and powerful style of Verilog coding. Dataflow Verilog will also appear more similar to other programming languages with operators that you've seen before. See the book for more information. 1)

Assign

To give wires or ports values in Dataflow Verilog, you must use assign with =.
Example: assign wire_name = 4'b0101; will make the wire wire_name hold the value 4'b0101.

These can include conditions and operations on the right side.
Example: assign wire_name = 4'b0101 + 4'b0011 will make the wire wire_name the value of 4'b0101 added to 4'b0011.

Operators

Dataflow Verilog has operators that you are most likely familiar with from other programming languages! Nearly all of them operate just as you would expect. The following table is a simplified version of the one found in the textbook. 2) Some of these will be explained in detail after the table.

Note that nearly all of these operators are simply shorthand for sometimes large, complex circuits. Behind the scenes, Vivado will convert these operators into the necessary circuits and place them in your design when it is implemented.

Type Symbol Operation
Arithmetic *, /, +, - Multiplication, Division, Addition, Subtraction
% Modulo
Logical ! Not
&& And
|| Or
Bitwise ~ Not
& And
| Or
^ Xor
~^ Xnor
Comparison <, >, <=, >= Greater than, Less than, etc.
Equality ==, != Equal, Not equal
Reduction & And
~& Nand
| Or
~| Nor
^ Xor
~^ Xnor
Shift <<, >> Shift left, Shift right
Concatenate { , } Concatenate items in list
Replicate { {}} Replicate item in inner braces
Ternary ?: Conditional

Shortcuts

The Dataflow Verilog operators allow you to use several clever tricks to instantiate some complex components very quickly and concisely. You should know these well.

Decoder

You can build an n:m decoder (meaning, any size of decoder) by using the shift left << operator. The following example is functionally equivalent to a 2:4 decoder. (Note that the bit widths of the input, output, and number constant will changed depending on the size of the decoder you are building.) Note that this is not the type of decoder that you need to build in lab 5.

  input [1:0] sel;
  output [3:0] result;
 
  assign result = 4'b0001 << sel;

Multiplexer

Any size of multiplexer is easy to build using nested ternary operators. The following example is a 4:1 multiplexer.

  input [1:0] sel;
  input a, b, c, d;
  output result;
 
  assign result = (sel == 0) ? a :
                  (sel == 1) ? b :
                  (sel == 2) ? c :
                  d;

Parameterization

Parameterization allows for much more flexible Verilog designs. It allows you to build modules with inputs and outputs that have bit widths that can be set upon instantiation of that module, rather than hard-coded into the module itself. This is an extremely useful concept of Dataflow Verilog.

Declaring

To use parameterization, you must first build your module with parameterization in mind. The syntax to do so is explained in the example below. This uses the multiplexer shortcut explained above to make a 2:1 multiplexer with bus sizes that are variable.

module pMux(result, sel, a, b);
  parameter X = 1;
  input sel;
  input [X-1:0] a, b;
  output [X-1:0] result;
 
  assign result = sel ? b : a;
endmodule

parameter X defines a parameter for this module and sets its name to X. This is the parameter that can be set on instantiation.
X = 1; sets a default value for this parameter. Meaning, if no value is given for the parameter when instantiating this module, it defaults to 1.
input [X-1:0] a, b; sets the size of the inputs a and b to be whatever value X is. Remember that the bus syntax [X-1:0] sets the MSB index and the LSB index. Thus, for a bus of size X, the MSB index must be X-1.
output [X-1:0] result; does the same for the output.

Instantiating

A module with a parameter can be instantiated without setting the parameter value. This will cause the parameter to take its default value, as explained above. The following example will create a 2:1 multiplexer with 1-bit inputs and output.

  pMux myMux(result, sel, a, b);

The real power of parameterization is utilized when setting parameter values. The following example also creates a 2:1 multiplexer, but with 16 bit inputs and output!

  pMux #(16) myBigMux(result, sel, a, b);

#(16) is the syntax for setting the parameter. The number in parenthesis is what the parameter will be set to which, in this case, is 16.

Using more than one parameter

The following examples show how to use more than one parameter in a parameterized module.

Creating a module.

module myModule(result, sel, a, b);
  parameter X = 1;
  parameter Y = 1;
 
  input [X-1:0] a, b;
  output [Y-1:0] result;
  input sel;

Instantiating that module.

myModule #(4, 5) myName(result, sel, a, b);
1)
Appendix B
2)
Page 268