Dataflow Modeling: Continuous Assignments

Dataflow modeling provides a higher level of abstraction than structural (gate-level) modeling. Instead of describing a circuit by connecting individual gates, dataflow modeling describes it in terms of how data flows through the circuit and how it is processed. This style focuses on the function of the circuit rather than its specific gate implementation, making it a more efficient and intuitive way to design combinational logic.

The core of dataflow modeling is the continuous assignment, which uses the `assign` keyword. These assignments model how a value is continuously driven onto a net (like a `wire`), similar to how a physical wire in a circuit is always driven by the output of a gate.

Continuous Assignments (`assign`)

The `assign` statement is the fundamental construct in dataflow modeling. It is used to drive a value onto a net variable.

Key Characteristics:

  • Continuous Evaluation: An `assign` statement is always active. The expression on the right-hand side is continuously evaluated, and whenever any of its operands change, the result is immediately propagated to the net on the left-hand side.
  • Target Must Be a Net: The left-hand side of an `assign` statement must be a net data type, most commonly a `wire`. It cannot be a `reg` data type.
  • Concurrent Execution: All `assign` statements in a module execute concurrently (in parallel), reflecting the parallel nature of hardware.

Syntax

assign [delay] net_variable = expression;
  • net_variable is typically a `wire`.
  • expression is a combination of other signals and operators.
  • The optional delay is used for simulation purposes to model gate delays.

Practical Examples of Dataflow Modeling

Example 1: Simple Logic Gates

Here’s how you can model basic logic gates using a single `assign` statement for each output.

module basic_gates (
    input  wire a, b,
    output wire y_and, y_or, y_xor, y_nand, y_not_a
);

    assign y_and   = a & b;
    assign y_or    = a | b;
    assign y_xor   = a ^ b;
    assign y_nand  = ~(a & b);
    assign y_not_a = ~a;

endmodule

Example 2: 2-to-4 Decoder

A 2-to-4 decoder takes a 2-bit input and activates one of four output lines. We can model this with four separate `assign` statements.

module decoder_2_to_4 (
    input  wire [1:0] in,
    input  wire       en,
    output wire [3:0] out
);

    assign out[0] = en & ~in[1] & ~in[0];
    assign out[1] = en & ~in[1] &  in[0];
    assign out[2] = en &  in[1] & ~in[0];
    assign out[3] = en &  in[1] &  in[0];

endmodule

Example 3: 4-bit Full Adder

A 4-bit adder can be modeled concisely using dataflow assignments. The concatenation operator `{}` is used to combine the final carry-out with the 4-bit sum.

module full_adder_4bit (
    input  wire [3:0] a, b,
    input  wire       c_in,
    output wire [3:0] sum,
    output wire       c_out
);

    // An intermediate 5-bit wire to hold the full result
    wire [4:0] result;

    // The addition is performed in a single expression
    assign result = a + b + c_in;

    // The lower 4 bits are the sum, the 5th bit is the carry-out
    assign sum   = result[3:0];
    assign c_out = result[4];

    // Alternative one-line assignment using concatenation:
    // assign {c_out, sum} = a + b + c_in;

endmodule

 

Leave a Reply

Your email address will not be published. Required fields are marked *