Modeling Multiplexers

Modeling multiplexer represent a challenge in that they combine both discrete and continuous behaviors in an analog model. The discrete signals require the used of transition functions to smooth the discontinuities, whereas the continuous signals cannot pass through a transition function. There is a bit of a trick to combining these two that is demonstrated in this tutorial. The trick is not difficult, but it can be hard to come up with on your own. So if you like puzzles, you might want to stop reading now and see if you can come up with it on your own.

You should read Introduction to Verilog-A and Functional Modeling before this tutorial.

A Verilog-AMS model for a 4-channel mux can be implemented as follows:

module(out, in, sel);
output out; electrical out;
input [3:0] in; electrical [3:0] in;
input [3:0] sel; logic [3:0] sel;

analog begin
    @(sel)
        ;
    V(out) <+ V(in[0])*transition(sel === 0, 0, 100n);
    V(out) <+ V(in[1])*transition(sel === 1, 0, 100n);
    V(out) <+ V(in[2])*transition(sel === 2, 0, 100n);
    V(out) <+ V(in[3])*transition(sel === 3, 0, 100n);
end
endmodule

Notice that the input voltages are outside the transition function, as expected. Multiple contributions are made to V(out), which accumulate. Each input is multiplied by either 0 or 1 before accumulating with all inputs but one being multiplied by 0. So if sel=2 then the output will be Vin[0]*0 + Vin[1]*0 + Vin[2]*1 + Vin[3]*0, which equals just Vin[2]. The transitions between 0 and 1 are smoothed by the transition filters. The event statement is used to synchronize the kernels. Specifically, it causes the continuous kernel to place a time point whenever there is a change to sel.

Unfortunately, this model is not accepted by Cadence’s simulator. For reasons I have never understood, Cadence has severe restrictions on what can be used as an analog event. Specifically, digital signals must be preceded by either posedge or negedge in analog event expression. With Cadence’s simulator, you can use the following:

module(out, in, sel);
output out; electrical out;
input [3:0] in; electrical [3:0] in;
input [3:0] sel; logic [3:0] sel;
reg sync = 0;

always @(sel) sync <= !sync;

analog begin
    @(posedge sync or negedge sync)
        ;
    V(out) <+ V(in[0])*transition(sel === 0, 0, 100n);
    V(out) <+ V(in[1])*transition(sel === 1, 0, 100n);
    V(out) <+ V(in[2])*transition(sel === 2, 0, 100n);
    V(out) <+ V(in[3])*transition(sel === 3, 0, 100n);
end
endmodule

To implement a Verilog-A version of a multiplexer, it is helpful to read Modeling Digital Buses in Verilog-A:

module(out, in, sel);
output out; electrical out;
input [3:0] in; electrical [3:0] in;
input [3:0] sel; logic [3:0] sel;
parameter real vdd = 2.5;
integer SEL;
genvar i;

analog begin
    // convert the input to an integer
    SEL = 0;
    for (i = 0; i < 2; i = i + 1) begin
        @(cross(V(sel[i]) - vdd/2));
        if (V(sel[i]) > vdd/2)
            SEL = SEL + (1 << i);
        end

    V(out) <+ V(in[0])*transition(SEL == 0, 0, 100n);
    V(out) <+ V(in[1])*transition(SEL == 1, 0, 100n);
    V(out) <+ V(in[2])*transition(SEL == 2, 0, 100n);
    V(out) <+ V(in[3])*transition(SEL == 3, 0, 100n);
end
endmodule