Modeling Digital Buses in Verilog-AΒΆ

It is generally preferred to use Verilog-AMS when simulating mixed-signal systems. However, sometimes that is not possible. This tutorial demonstrates a reasonably efficient and robust way to convert the value of an electrical bus into an integer. Before starting this tutorial, you should read Functional Modeling.

There are a two challenges that must be overcome to implement electrical digital buses in Verilog-A:

  1. The threshold crossings must be resolved to avoid undesirable delays in recognizing the value of the bus.

  2. The individual bit values must be combined into a single integer.

Consider the following model of a 8-bit DAC:

module dac (out_p, out_n, in, enable, vdda, gnda);
output out_p, out_n; electrical out_p, out_n;
input [7:0] in; electrical [7:0] in;
input enable; electrical enable;
input vdda; electrical vdda;
input gnda; electrical gnda;
integer code;
real value;
genvar i;

analog begin
    // convert the input to an integer
    code = 0;
    for (i = 0; i < 8; i = i + 1) begin
        @(cross(V(in[i]) - V(vdda)/2));
        if (V(in[i]) > V(vdda)/2)
            code = code + (1 << i);
        end
    if (code >= 128)
        code = code - 256;

    // implement enable
    @(cross(V(enable) - V(vdda)/2));
    if (V(enable) < V(vdda)/2)
        value = 0;
    else
        value = code/256.0;

    // drive the differential output
    V(out_p) <+ V(vdda)/2 + transition(value/2, 0, 10n);
    V(out_n) <+ V(vdda)/2 - transition(value/2, 0, 10n);
end
endmodule

This model starts by applying a threshold to the individual lines in an electrical array and combining the resulting Booleans into an integer. The electrical array is in and the integer is code. The if statement computes the sign of the integer. It is only needed if in represents a signed number. Specifically, it should be deleted in in is an unsigned number.

The next block of code implements the enable feature and scales the output to the desired range.

The final block of code drives the differential outputs with the computed result. Notice that transition functions are used to smooth the otherwise discontinuous output signal.

The conversion from electrical signal to Booleans includes use of cross functions to assure that the threshold crossings are properly resolved.

The for loop in the first block is genvar loop. Such loops are fully expanded before the simulation begins. That loop expands as follows:

// convert the input to an integer
code = 0;
    @(cross(V(in[0]) - V(vdda)/2));
    if (V(in[0]) > V(vdda)/2)
        code = code + (1 << 0);
    end
    @(cross(V(in[1]) - V(vdda)/2));
    if (V(in[1]) > V(vdda)/2)
        code = code + (1 << 1);
    end

    ...

    @(cross(V(in[7]) - V(vdda)/2));
    if (V(in[7]) > V(vdda)/2)
        code = code + (1 << 7);
    end

This DAC updates immediately after the input changes. It is possible to modify this model so it updates on a clock edge:

module dac (out_p, out_n, in, clock, enable, vdda, gnda);
output out_p, out_n; electrical out_p, out_n;
input [7:0] in; electrical [7:0] in;
input clock; electrical clock;
input enable; electrical enable;
input vdda; electrical vdda;
input gnda; electrical gnda;
integer code;
real value;
genvar i;

analog begin
    // convert the input to an integer
    code = 0;
    for (i = 0; i < 8; i = i + 1) begin
        @(cross(V(in[i]) - V(vdda)/2));
        if (V(in[i]) > V(vdda)/2)
            code = code + (1 << i);
        end
    if (code >= 128)
        code = code - 256;

    // implement the clock and enable inputs
    @(cross(V(clock) - V(vdda)/2), +1);
        if (V(enable) < V(vdda)/2)
            value = 0;
        else
            value = code/256.0;

    // drive the differential output
    V(out_p) <+ V(vdda)/2 + transition(value/2, 0, 10n);
    V(out_n) <+ V(vdda)/2 - transition(value/2, 0, 10n);
end
endmodule

In this model the effect of changes in either in or enable are delayed until the next rising edge of the clock.