A multiplexer is a data selector which selects a particular input data line and produce that in the output section. It is implemented using combinational circuits and is very commonly used in digital systems. Sending data over multiplexing reduces the cost of transmission lines, and saves bandwidth.
A 2^n:1 multiplexer has 2^n input lines, n select lines, and a single output line. You can find a detailed explanation and schematic representation for multiplexers over here.
There are four layers of abstraction in an HDL:
- Gate level modeling
- Dataflow modeling
- Behavioral modeling
- Structural modeling
This article will deal with the modeling styles for an 8:1 multiplexer. You may find the Verilog code for 2:1 MUX and 4:1 MUX in our Verilog course section. Now let’s start with gate-level modeling.
Contents
Gate level modeling
The gate-level modeling is virtually the lowest abstract level of modeling. This style of modeling will include primitive gates that are predefined in Verilog HDL. The designer should know the basic logic circuit and the logic gates that are employed in that circuit for a particular system. There is another abstraction layer below gate-level: switch level modeling, which deals with the transistor technologies.
Logic circuit
The following figure is the 8×1 multiplexer. Now this 8×1 MUX is a high-level multiplexer. For simplicity, the 8×1 mux can also be implemented using 2×1 or 4×1 multiplexers.
You can observe that the input signals are D0
, D1
, D2
, D3
, D4
, D5
, D6
, D7
, S0
, S1
, S2
and the output signal is out
.
Verilog code for 8:1 mux using gate-level modeling
First of all, we need to mention the timescale directive for the compiler. This will control the time unit, which measures the delays and simulation time, and time precision specifies how delays are rounded off for the simulation. It starts with `timescale
.
`timescale 1ns/1ps
The following code will be simulated in nanoseconds, as mentioned in the time unit (1 ns), and the precision is up to 1 picosecond.
- Next will be the module declaration and definition. The syntax is:
module module_name(port-list) endmodule
module
and endmodule
are the keywords defined in Verilog IEEE 1134. Let’s name the module by m81
the port list will contain the input and output variables.
- Input variables: D0, D1, D2, D3, D4, D5, D6, D7, S0, S1, S2
- Output variable: out
module m81(input D0, D1, D2, D3, D4, D5, D6, D7, S0, S1, S2, output out);
There’s another way to define the input-output ports. We can declare the data lines and select lines as vector nets also. Here’s the declaration.
input wire [7:0] D; input wire [2:0] S;
In some of the complex circuits, we need intermediate signals, and they are declared as wire
s.
wire T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11;
The next thing to proceed with is to instantiate the predefined logical gates.
- Write the name of the gate which you’re using. Now in the brackets, first mention the output variable, followed by the input signals.
- For NOT gate:-
not(T1, S0);
- Here, intermediate signal
T1
is the output andS0
is the input signal. - Similarly for AND and OR gate,
and(T4, D0, T1, T2, T3); or(out, T4, T5, T6, T7, T8, T9, T10, T11);
T4
is the output for AND gate,D0, T1, T2, and T3
are the input variables.- For OR gate, the output is
out
and input isT4, T5, T6, T7, T8, T9, T10 and T11
.
If there exist more than two same gates, we can concatenate the expression into one single statement.
not(T1, S0), (T2, S1), (T3, S2);
Summing up, we will get the final gate-level modeling Verilog code:
`timescale 1ns/1ps module m81(input D0, D1, D2, D3, D4, D5, D6, D7, S0, S1, S2, output out); wire T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11; not(T1, S0); not(T2, S1); not(T3, S2); and(T4, D0, T1, T2, T3), (T5, D1, S0, T2, T3); and(T6, D2, T1, S1, T3), (T7, D3, S0, S1, T3); and(T8, D4, T1, T2, S2), (T9, D5, S0, T2, S2); and(T10, D6, T1, S1, S2), (T11, D7, S0, S1, S2); or(out, T4, T5, T6, T7, T8, T9, T10, T11); endmodule
RTL Schematic
The RTL schematic shows the hardware layout of a circuit. The following window will open up when you click on the RTL analysis section.
Data flow modeling
This modeling represents the flow of the data through the combinational circuit. The Verilog code in this abstraction layer doesn’t include any logic gates. Instead, we should know the final output expression of the given circuit.
The logical equation for the 8:1 multiplexer is:-
out = (D0.S2′.S1′.S0′) + (D1.S2′.S1′.S0) + (D2.S2′.S1.S0′) + (D3.S2′.S1.S0) + (D4.S2.S1′.S0′) + (D5.S2.S1′.S0) + (D6.S2.S1.S0′) + (D7.S2.S1.S0)
where D0
, D1
, D2
, D3
, D4
, D5
, D6
, D7
, S0
, S1
, and S2
are the inout variables and the output variable is out
.
Verilog code for 8:1 mux using dataflow modeling
- Beginning with the coding part, first, we should keep in mind that the dataflow model of a system has an
assign
statement, which is used to express the logical expression for a given circuit. - The first line is always a module declaration statement.
module m81(output out, input D0, D1, D2, D3, D4, D5, D6, D7, S0, S1, S2);
- If you look at the logical equation of 8:1 MUX, you’ll realize it is the AND and OR operation between the signals. There are multiple ways to implement this equation. One of the simplest methods is just to mention the same equation using logical operations.
- The variable out will store the result of the right-hand side expression.
- Remember to use the logical operators for AND
&
, NOT~
and OR|
gates, not the predefined keywords.
assign out = (D0 & S2bar & S1bar & S0bar) | (D1 & S2bar & S1bar & S0) | (D2 & S2bar & S1 & S0bar) + (D3 & S2bar & S1 & S0) + (D4 & S2 & S1bar & S0bar) + (D5 & S2 & S1bar & S0) + (D6 & S2 & S1 & S0bar) + (D7 & S2 & S1 & S0);
- The variables
S2bar
,S1bar
, andS0bar
haven’t been mentioned in the logic circuit as well as in the module. One may declare them in the port-list itself or can be treated as intermediate signals. Therefore, we need to define these signals, which are technically the NOT operation of select lines.
assign S0bar=~S0; assign S1bar=~S1; assign S2bar=~S2;
So the whole code is:
module m81(output out, input D0, D1, D2, D3, D4, D5, D6, D7, S0, S1, S2); assign S1bar=~S1; assign S0bar=~S0; assign S2bar=~S2; assign out = (D0 & S2bar & S1bar & S0bar) | (D1 & S2bar & S1bar & S0) | (D2 & S2bar & S1 & S0bar) + (D3 & S2bar & S1 & S0) + (D4 & S2 & S1bar & S0bar) + (D5 & S2 & S1bar & S0) + (D6 & S2 & S1 & S0bar) + (D7 & S2 & S1 & S0); endmodule
One might find the assign
statement a bit lengthy; we can also implement the 8×1 multiplexer using the lower order multiplexers also, i.e., 2×1 or 4×1 MUX.
RTL Schematic
The hardware layout is:
Behavioral modeling
This is the highest abstraction layer of all. It emphasizes the behavior of the digital circuit. In most cases, implementing the truth table will describe the behavior with no failure.
Truth table
Verilog code for 8:1 mux using behavioral modeling
The module declaration will remain the same as that of the above styles with m81
as the module’s name.
module m81(out, D0, D1, D2, D3, D4, D5, D6, D7, S0, S1, S2);
In behavioral modeling, we have to define the data-type of signals/variables. Input signals as wire
and output as reg
.
input wire D0, D1, D2, D3, D4, D5, D6, D7, S0, S1, S2; output reg out;
Behavioral modeling mainly includes two statements:
- An
initial
statement which is executed the only once always
statement, which is executed once the sensitivity list is activated.
One can find numerous ways to implement the truth table, whether it is a nested if-else
statement or case
statement.
Here, I’ve used the case
statement under always
block.
always@(S0 & S1 & S2) begin case(S0 & S1 & S2)
Let’s write down the cases for each row of the truth table. For S0=0, S1=0, S2=0,
the input variable D0
will get transferred to the output variable out
.
3'b000: out=D0;
Similarly,
3'b001: out=D1; 3'b010: out=D2; 3'b011: out=D3; 3'b100: out=D4; 3'b101: out=D5; 3'b110: out=D6; 3'b111: out=D7;
3'b000
represents the 3- bit binary value for the expression inside the case statement.
Design code:
module m81(out, D0, D1, D2, D3, D4, D5, D6, D7, S0, S1, S2); input wire D0, D1, D2, D3, D4, D5, D6, D7, S0, S1, S2; output reg out; always@(*) begin case(S0 & S1 & S2) 3'b000: out=D0; 3'b001: out=D1; 3'b010: out=D2; 3'b011: out=D3; 3'b100: out=D4; 3'b101: out=D5; 3'b110: out=D6; 3'b111: out=D7; default: out=1'b0; endcase end endmodule
RTL Schematic
Structural level modeling
In the structural style of modeling, we only define the physical structure of the circuit. We don’t care about the nature of the system, nor we’re interested what’s the relationship between the input and output variables along with the clock generated.
This modeling is somewhat similar to gate-level modeling. The difference lies in the use of predefined gates. In structural style, we will declare and define the operation of each of the logic gate and then use that expression for implementing the rest of the gates, by the concept of module instantiation.
Logic circuit
Verilog code for 8:1 mux using structural modeling
Decide which logical gates you want to implement the circuit with. In the 8×1 MUX, we need eight AND gates, one OR gate, and three NOT gates.
- Start defining each gate within a
module
. - Here’s the module for AND gate with the module name
and_gate
. The port-list will contains the output and input variables. Note that there’s no need to follow any sequence for mentioning output variables first, then input signals.
module and_gate(output a, input b, c, d, e); assign a = b & c & d & e; endmodule
- Output variable
a
will store the AND operation betweenb
,c
,d
, ande
.
Similarly for NOT gate:
module not_gate(output f, input g); assign f = ~ g; endmodule
OR gate:
module or_gate(output l, input m, n, o, p, q, r, s, t); assign l = m | n | o | p | q | r | s | t; endmodule
The next thing to be done is the instantiation of modules. We’ll combine the above modules into one single module for 8:1 multiplexer.
- Start with the name of the module you need. This will work as an instance. Give this instance a name. Then mention the port-list.
not_gate u1(s1bar, S1);
u1
is the instance name,s1bar
is the output which contains the NOT operation ofS1
input.- You can declare names for input-output other than the names used in defining modules. The order, however, is very important here. It should be the same as that of the modules for the gates. This is similar to the function call and arguments in C.
Similarly for others instances:
not_gate u2(s0bar, S0); not_gate u3(s2bar, S2); and_gate u4(T1, D0, s0bar, s1bar, s2bar); and_gate u5(T2, D1, S0, s1bar, s2bar); and_gate u6(T3, D2, s0bar, S1, s2bar); and_gate u7(T4, D3, S0, S1, s2bar); and_gate u8(T5, D4, s0bar, s1bar, S2); and_gate u9(T6, D5, S0, s1bar, S2); and_gate u10(T7, D6, s0bar, S1, S2); and_gate u11(T8, D7, S0, S1, S2); or_gate u12(out, T1, T2, T3, T4, T5, T6, T7, T8);
The final code is:
module and_gate(output a, input b, c, d, e); assign a = b & c & d & e; endmodule module not_gate(output f, input g); assign f = ~ g; endmodule module or_gate(output l, input m, n, o, p, q, r, s, t); assign l = m | n | o | p | q | r | s | t; endmodule module m81(out, D0, D1, D2, D3, D4, D5, D6, D7, S0, S1, S2); output out; input D0, D1, D2, D3, D4, D5, D6, D7, S0, S1, S2; wire s0bar, s1bar, T1, T2, T3, T4, T5, T6, T7, T8; not_gate u1(s1bar, S1); not_gate u2(s0bar, S0); not_gate u3(s2bar, S2); and_gate u4(T1, D0, s0bar, s1bar, s2bar); and_gate u5(T2, D1, S0, s1bar, s2bar); and_gate u6(T3, D2, s0bar, S1, s2bar); and_gate u7(T4, D3, S0, S1, s2bar); and_gate u8(T5, D4, s0bar, s1bar, S2); and_gate u9(T6, D5, S0, s1bar, S2); and_gate u10(T7, D6, s0bar, S1, S2); and_gate u11(T8, D7, S0, S1, S2); or_gate u12(out, T1, T2, T3, T4, T5, T6, T7, T8); endmodule
RTL schematic
Testbench for 8×1 Mux using Verilog
The testbench is a set of lines that are used to test and simulate the design code for a given system. It tests the design for a variety of possible inputs. Follow up this post for step-by-step instruction to write a testbench.
`timescale 1ns/1ps module top; wire out; reg D0, D1, D2, D3, D4, D5, D6, D7, D8, S0, S1, S2; m81 name(.D0(D0), .D1(D1), .D2(D2), .D3(D3), .D4(D4), .D5(D5), .D6(D6), .D7(D7), .S0(S0), .S1(S1), .S2(S2), .out(out)); initial begin D0=1'b0; D1=1'b0; D2=1'b0; D3=1'b0; D4=1'b0; D5=1'b0; D6=1'b0; D7=1'b0;S0=1'b0; S1=1'b0; S2=1'b0; #500 $finish; end always #1 D0=~D0; always #2 D1=~D1; always #3 D2=~D2; always #4 D3=~D3; always #5 D4=~D4; always #6 D5=~D5; always #7 D6=~D6; always #8 D7=~D7; always #9 S0=~S0; always #10 S1=~S1; always #11 S2=~S2; always@(D0 or D1 or D2 or D3 or D4 or D5 or D6 or D7 or S0 or S1 or S2) $monitor("At time = %t, Output = %d", $time, out); endmodule;
TCL Console
Since we’ve added a $monitor
statement in the testbench, we’ll get the following output for user interaction.
Simulation Waveforms
The simulation waveform for 8X1 MUX is:
The above simulation result is the same for each of the abstraction layers, truly satisfying the truth table.