User Tools


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
  • Logical operators only work with true and false, or 1 and 0. The operand(s) are evaluated to be either 1 or 0, and then the operation is performed on them.
    Example: 4'b0101 && 4'b1010 will evaluate to 1 because both numbers equate to true. This happens because both numbers are not 0. 0 is the only number that is equivalent to false, while any other number equates to true.
  • Bitwise operators perform the operation on each individual bit of the two operands. Both operands are required to have the same number of bits, and the result will also have the same number of bits.
    Example: The bitwise and operation 4'b0101 & 4'b0011 will evaluate to 4'b0001 whereas for a bitwise or 4'b0101 | 4'b0011 will evaluate to 4'b0111.
  • Reduction operators only take one operand. They perform the given operation between all the bits in the operand one by one with each other.
    Example: &(4'b1100) is the same as performing 1 & 1 & 0 & 0 which evaluates to 0.
  • Shift operators shift all the bits in a number to the given direction and fill the new empty spots with zeroes.
    Example: 4'b1001 << 1 results in the number 4'b0010.
    Note that shifting to the left once is equivalent to multiplying by 2, and shifting to the right once is equivalent to dividing by 2.
  • Concatenate expects a comma separated list with 2 or more items inside its braces. It will result in all the items in that list being combined into one result.
    Example: { 4'b0101, 4'b0000, 4'b1111 } returns the number 12'b010100001111.
  • Replicate replicates whatever is in its inner brackets a certain number of times. Its full synatx is {#{a}} where a is anything and # is a number. The # determines how many times to replicate whatever a is.
    Example: {4{4'b1010}} returns 16'b1010101010101010.
  • The ternary operator is like an if statement. The full syntax is (condition) ? (if true) : (if false). The (condition) is a statement that is evaluated to be either true or false. (if true) is executed if (condition) is true, and (if false) is executed if (condition) is false. You can nest multiple ternary operations inside each other.
    Example: (4'b0001 == 4'b0010) ? 2'b11 : (4'b1110 + 1'b1) evaluates to (4'b1110 + 1'b1) which is immediately executed and its result 4'b1111 is returned.
    Nesting Example: (a == b) ? 2'b11 : (a == c) ? 2'b01 : (a == d) ? 2'b10 : 2'b00
    To hook this up to an actual wire or input or output, you have to use the assign statement.
    Example: assign myOutput = (4'b0001 == 4'b0010) ? 2'b11 : (4'b1110 + 1'b1)

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