After reading this post, you’ll be able to:
- Write the Verilog code for a 4:1 MUX in all layers of abstraction (modeling styles)
- Generate the RTL schematic for the 4:1 MUX and simulate the design code using testbench.
What is a multiplexer?
A multiplexer is a data selector device that selects one input from several input lines, depending upon the enabled, select lines, and yields one single output.
A multiplexer of 2n inputs has n select lines, are used to select which input line to send to the output. There is only one output in the multiplexer, no matter what’s its configuration.
These devices are used extensively in the areas where the multiple data can be transferred over a single line like in the communication systems and bus architecture hardware. Visit this post for a crystal clear explanation to multiplexers.
We’ll code the 4:1 multiplexer in the following abstraction layers:
A brief description for each modeling level has been presented before we start coding the HDL models in Verilog HDL.
Gate level modeling
The gate-level abstraction is the lowest level of modeling. The switch level model is also a low level of modeling but it isn’t that common. The gate-level modeling style uses the built-in basic logic gates predefined in Verilog. We only need to know the logic diagram of the system since the only requirement is to know the layout of the particular logic gates.
Now, this circuit shows we need two NOT gates, four AND gates, and one OR gate for implementing the 4×1 MUX in gate-level modeling.
Verilog code for 4×1 multiplexer using gate-level modeling
To start with the design code, as expected, we’ll declare the
module first. The port-list will contain the output variable first in gate-level modeling. This is because the built-in logic gates are designed such that the
output is written first, followed by the other
input variables or signals.
module m41(out, a, b, c, d, s0, s1);
Now since the nature or behavior of the circuit in the gate – level isn’t concerned, there is no need to define the data type of variable.
output out; input a, b, c, d, s0, s1;
The intermediate signals are declared as
wires. Note that the intermediate signals are those that are not involved in the port list. Example: signals that are emerging from the NOT gate.
wire sobar, s1bar, T1, T2, T3, T4;
Time for us to write for the logic gates. Separate the list for a particular gate by appropriate brackets, if there exists more than one same logic gate. Here’s how you would do it for the two NOT gates.
not (s0bar, s0), (s1bar, s1);
s1bar are the output to the first and second NOT gate respectively and
s1 are the input to the first and second NOT gate.
Similarly for the rest of the two gates;
and (T1, a, s0bar, s1bar), (T2, b, s0bar, s1), (T3, c, s0, s1bar), (T4, d, s0, s1); or(out, T1, T2, T3, T4);
So the final code is:
module m41(out, a, b, c, d, s0, s1); output out; input a, b, c, d, s0, s1; wire sobar, s1bar, T1, T2, T3, T4; not (s0bar, s0), (s1bar, s1); and (T1, a, s0bar, s1bar), (T2, b, s0bar, s1),(T3, c, s0, s1bar), (T4, d, s0, s1); or(out, T1, T2, T3, T4); endmodule
This hardware schematic is the RTL design of the circuit. Notice the resemblance between the logic circuit of 4:1 MUX and this picture. It is clear that the gate-level modeling will give the exact involved hardware in the circuit of the system.
Data flow modeling
The dataflow modeling represents the flow of the data. It is described through the data flow through the combinational circuits rather than the logic gates used.
In Verilog, the
assign statement is used in data-flow abstraction.
It is necessary to know the logical expression of the circuit to make a dataflow model. The equation for 4:1 MUX is:
Logical Expression: out = (a. s1′.s0′) + (b.s1′.s0) + (c.s1.s0′) + (d. s1.s0)
Verilog code for 4×1 multiplexer using data flow modeling
Start with the
module and input-output declaration.
m41 is the name of the
module m41 ( input a, input b, input c, input d, input s0, s1, output out);
assign statement to express the logical expression of the circuit. A ternary operator
? is used here to implement the logic. This operator works similar to that of C programming language.
assign out = s1 ? (s0 ? d : c) : (s0 ? b : a);
This shows that if
s1 is high, the (s0 ? d : c) block will be executed, else (s0 ? b : a) will be executed. Further, if
s0 is high, d OR b will get transferred to the out variable, depending on the
s1 select line, else c OR a will be the output.
Thus, the final code for the 4:1 multiplexer using data-flow modeling is given below.
module m41 ( input a, input b, input c, input d, input s0, s1, output out); assign out = s1 ? (s0 ? d : c) : (s0 ? b : a); endmodule
You can observe how the RTL of 4:1 MUX in dataflow is different from the gate-level modeling. The figure consists of two individual 2:1 multiplexers, connected by the two select lines
The behavioral style, as the name suggests, describes the behavior of a circuit. It is the highest abstraction layer in the Verilog modeling of digital systems. The other techniques are detailed with their internal hardware whereas the behavioral level doesn’t demand the knowledge of the actual circuitry involved in the system.
The truth table of the 4:1 MUX has six input variables, out of which two are select lines, and one is the output signal. The input data lines a, b, c, d are selected depending on the values of the select lines.
Verilog code for 4×1 multiplexer using behavioral modeling
To start with the behavioral style of coding, we first need to declare the name of the
module and its port associativity list, which will further contain the input and output variables. Point to be noted here; we are supposed to define the data- type of the declared variable also since it will account for the behavior of the input and output signals.
s0 s1 select lines will be vector quantities, and vector net entities are declared as
wire. The output variable
module m41 ( a, b, c, d, s0, s1, out); input wire a, b, c, d; input wire s0, s1; output reg out;
Next comes the
always. In behavioral modeling, there are two main statements responsible for the construct of Verilog.
One is the
initial statement, which is executed only once during the simulation; another one is the
always statement that can be executed every time its sensitivity list gets triggered.
If you carefully look at the equation, the output is explicitly dependent on the input variables. To implement this, we’ll use the
always statement, followed by
always @ (a or b or c or d or s0 or s1) begin ... end
In most of the cases, the input variables are present in the sensitivity list. Another way of expressing this list is by using the asterisk symbol *.
This implicitly expresses the event expression/sensitivity list. It is always convenient to eliminate the source errors with the
always @ (*). This method will let the program decide what to include in the sensitivity list.
Next, to describe the behavior of 4×1 MUX, look at the following line statements:
- When s0 s1 are both high, input d is the output
- s0 low s1 high, input c is the output
- When s0 high s1 low, input b is the output
- Otherwise, s0 s1 are both low, input a is the output.
To implement this, we can either use the if-else statement or the case statement. I am using the case statement over here.
The case statement starts with the
case keyword and ends with the
endcase. The syntax for the case statement is:
case (case_expression) case_item1: procedural_expression; case_item2: begin procedural_statements; end .... default: expression; endcase
The expression for case_expression is the OR (symbol |) operation between select lines. Analyze the truth table and write down the case statement for the first row.
2'b00 : out <= a;
The above line shows that when select line
s1 is 00,
a input is transferred to the output
out. Repeat this for the rest of the rows of cases.
The final code for 4×1 MUX in behavioral modeling is as follows:
module m41 ( a, b, c, d, s0, s1, out); input wire a, b, c, d; input wire s0, s1; output reg out; always @ (a or b or c or d or s0, s1) begin case (s0 | s1) 2'b00 : out <= a; 2'b01 : out <= b; 2'b10 : out <= c; 2'b11 : out <= d; endcase end endmodule
This hardware schematic is the actual schematic of a multiplexer.
In structural modeling, we describe the physical structure of a digital system. It is implemented using the logic gates in the circuit diagram. It gives us the internal hardware involved in the system.
There’s one thing that should be noted over here. Gate-level modeling is different from structural level modeling. In gate-level, we use the predefined built-in logical gates. In contrast, in structural-level, we create a separate module for each functional logic gate with its logical expression assigned to that module.
This circuit has four AND gates, two NOT gates and one OR gate. We’ll structurize each gate with its respective module.
Verilog code for 4×1 multiplexer using structural modeling
To start with the design code, we’ll first define the modules for AND, OR, and NOT gates.
The declaration of the AND gate is shown below. The name of the module is and_gate. The input and output can be defined either along the port-list or separately in the next line. There is no need to specify the data-type of the signals since we are coding in the structural style.
module and_gate(output a, input b, c, d);
assign statement, write down the logical expression for AND gate.
assign a = b & c & d;
The end of the module is marked by
Repeat the above for the rest of the gates=>
module not_gate(output f, input e); assign e = ~ f; endmodule
module or_gate(output l, input m, n, o, p); assign l = m | n | o | p; endmodule
You may notice the names of the input and output variables are different from each of the modules. This ensures no mixing up of signals during the simulation of the circuit.
Time for us to combine these three gates to form a 4:1 MUX. This is called the module instantiation. First, start with the name of the module (defined and declared above) and write the name of the instance of your choice. The port-list will contain the output signals, followed by the input ones.
and_gate u3(T1, a, s0bar, s1bar);
- Here, the module used: and_gate
- Name of the instance: u3
- Output variable: T1 (which is an intermediate signal defined as a wire)
- Input variable:
Repeat the same for the rest of the instances.
not_gate u1(s1bar, s1); not_gate u2(s0bar, s0); and_gate u3(T1, a, s0bar, s1bar); and_gate u4(T2, b, s0, s1bar); and_gate u5(T3, c, s0bar, s1); and_gate u6(T4, d, s0, s1); or_gate u7(out, T1, T2, T3, T4);
Final structural code:
module and_gate(output a, input b, c, d); assign a = b & c & d; endmodule module not_gate(output f, input e); assign e = ~ f; endmodule module or_gate(output l, input m, n, o, p); assign l = m | n | o | p; endmodule module m41(out, a, b, c, d, s0, s1); output out; input a, b, c, d, s0, s1; wire s0bar, s1bar, T1, T2, T3; not_gate u1(s1bar, s1); not_gate u2(s0bar, s0); and_gate u3(T1, a, s0bar, s1bar); and_gate u4(T2, b, s0, s1bar); and_gate u5(T3, c, s0bar, s1); and_gate u6(T4, d, s0, s1); or_gate u7(out, T1, T2, T3, T4); endmodule
You can see each instantiate represents a particular functionality, comprising different logic gates.
Testbench for 4×1 mux using Verilog
A testbench is an HDL code that allows you to provide a set of stimuli input to test the functionality and wide-range of plausible inputs for support to a system. Check out this post to learn how to write the testbench via our step-by-step instructions.
The name of the module: top
Output variable: out
Input variables: a, b, c, d
Select lines: s0, s1
Name of the module instance: name
module top; wire out; reg a; reg b; reg c; reg d; reg s0, s1; m41 name(.out(out), .a(a), .b(b), .c(c), .d(d), .s0(s0), .s1(s1)); initial begin a=1'b0; b=1'b0; c=1'b0; d=1'b0; s0=1'b0; s1=1'b0; #500 $finish; end always #40 a=~a; always #20 b=~b; always #10 c=~c; always #5 d=~d; always #80 s0=~s0; always #160 s1=~s1; always@(a or b or c or d or s0 or s1) $monitor("At time = %t, Output = %d", $time, out); endmodule;