After reading this post, you’ll be able to
- Get knowledge on different styles of modeling in Verilog HDL
- Design the 2:1 MUX in Verilog with all abstraction layers (modeling styles).
- Generate RTL Schematic and simulate the 2:1 MUX using testbench.
What is a multiplexer?
A multiplexer is a device that selects one output from multiple inputs. It is also known as a data selector. Multiplexers are used in communication systems to increase the amount of data that can be sent over a network within a certain amount of time and bandwidth. It allows us to ‘squeeze’ multiple data lines into one data line.
The multiplexer (MUX) functions as a multi-input and single-output switch. The selection of the input is done using select lines. A MUX with 2^n input lines have n select lines and is said to be a 2^n: 1 MUX with one output. You can find the detailed working and schematic representation of a multiplexer here.
Now let’s start the coding part. Well, in Verilog hardware descriptive language, we have four main abstraction layers (or modeling styles).
In this article, we’ll write the Verilog code for the simplest multiplexer, i.e. a 2:1 MUX.
Comparing 2:1 with 2^n: 1 (as mentioned before) we get n = 1, which is the number of select lines (input variables = 2, select lines = 1, output signal = 1). Now before jumping to the coding section, a brief description of each modeling style has been presented before you.
Gate Level Modeling
As the name suggests, this style of modeling will include primitive gates that are predefined in Verilog. This is virtually the lowest abstraction layer, which is used by designers for implementing the lowest level modules, as the switch level modeling isn’t that common. The prerequisite for this style is knowing the basic logic diagram of the digital circuit that you wish to code.
Since we’re concerned about designing the Verilog code for a 2:1 MUX, have a look at its circuit diagram.
The input signals are
S is the select line with
Y as its output. We can orally solve for the expression of the output that comes out to be:
Y = D0.S’ + D1.S
Verilog code for 2:1 MUX using gate-level modeling
For the gate level, we will first declare the module for 2: 1 MUX, followed by the input-output signals. The order of mentioning output and input variables is crucial here, the output variable is written first in the bracket, then the input ones.
module m21(Y, D0, D1, S);
module is a keyword here.
m21 is the name of the
Y is the
input are written after. Note that we don’t declare intermediate signals while defining the module.
Next comes the declaration of input, output, and intermediate signals. You might have noticed that other modeling styles include the declaration of variables along-with their respective data- types. But in the gate- level, we only declare the intermediate variables as wire; there’s no need for
wire declaration for input-output entities.
output Y; input D0, D1, S; wire T1, T2, Sbar;
Next comes the instantiation part for gates.
or are the predefined built-in gates, and we’re instantiating these gates with their respective input-output ports.
For example for
Sbar is the output and
S is the input. Similarly for
D2 are inputs to two
and gates and
Sbar are their respective output.
and (T1, D1, S), (T2, D0, Sbar); not (Sbar, S); or (Y, T1, T2);
endmodule marks the end of the
Here’s the final code of the 2:1 mux using gate-level modeling.
module m21(Y, D0, D1, S); output Y; input D0, D1, S; wire T1, T2, Sbar; and (T1, D1, S), (T2, D0, Sbar); not (Sbar, S); or (Y, T1, T2); endmodule
This is the design abstraction, which shows the internal circuitry involved. It is the hardware implementation of a system.
Data flow modeling
The dataflow level shows the nature of the flow of data in continuous assignment statements (
assign keyword). It describes the combinational circuit by their functions rather than their gate structures. For coding in the dataflow style, we only need to know about the logical expression of the circuit.
The equation for 2:1 mux is:
Y = D0.S’ + D1.S
where Y is the final output, D0, D1, and S are inputs.
Verilog code for 2:1 MUX using data flow modeling
To start with this, first, you need to declare the module. There’s no need for data- type declaration in this modeling.
module m21(Y, D0, D1, S); output Y; input D0, D1, S;
Now since this the dataflow style, one is supposed to use
assign statements. I have used a ternary operator for the output
Y. This operator
? means that the output
Y becomes equal to data
D1 if select line
S is true otherwise
D0 is the final output.
assign Y= (S)? D1: D0;
module m21(D0, D1, S, Y); output Y; input D0, D1, S; assign Y=(S)?D1:D0; endmodule
The hardware schematic for a 2:1 multiplexer in dataflow level modeling is shown below. You will notice that this schematic is different from that of the gate-level. It involves the symbol of a multiplexer rather than showing up the logic gates involved, unlike gate-level modeling.
This level describes the behavior of a digital system. In most of the cases, we code the behavioral model using the truth table of the circuit.
Truth-table for 2:1 MUX
Now to find the expression, we will use K- map for final output
Equation from the truth table: Y = D0.S’ + D1.S
Verilog code for 2:1 MUX using behavioral modeling
First, define the
module m21 and declare the input and output variables.
module m21( D0, D1, S, Y);
Don’t forget to mention the data- type of the ports. Since it is the behavioral modeling, we will declare the output
reg while the rest of the inputs as
input wire D0, D1, S; output reg Y;
Behavioral modeling mainly includes two statements:
initialstatement which is executed the only once
alwaysstatement, which is executed once the sensitivity list is activated.
You should notice that the output is equal to the second input if the select line is high. Otherwise, it is equal to the first input itself. This logic can be stated by using the
Since the output of 2:1 MUX changes once there is a change in
D0 OR D1 OR S we’ll use
always statement. Now, if the S event is true, the output Y will be D1, else the output will be D0.
always @(D0 or D1 or S) begin if(S) Y= D1; else Y=D0; end
Summing up the final code:
module m21( D0, D1, S, Y); input wire D0, D1, S; output reg Y; always @(D0 or D1 or S) begin if(S) Y= D1; else Y=D0; end endmodule
Hardware schematic for 2:1 MUX:
Structural modeling describes the hardware structure of a digital system. It is usually written in RTL and is somewhat similar to gate-level modeling. The only difference is it doesn’t include any built-in gates. The components and connections all need to separately defined here. There’s a proper definition for the expression of the digital system within the module itself. It includes module declaration and instantiation, port-list and it’s associates.
Now the logical diagram for a 2:1 MUX shows that we need two AND gates, one OR gate and one NOT gate. We’ll structurize for each of the gates separately.
First, we’ll start by declaring the modules for each logic gate. Below is the declaration of a module for AND gate, we can define the input-output variables in the next line also. We don’t need the data- type for signals since it’s the structure of the circuit that needs to be emphasized.
module and_gate(output a, input b, c);
Now using the
assign statement, write the function of the logic gate in a statement. You may use the delay.
assign a = b & c; endmodule
That marks the end of a module for AND gate. Similarly for other gates also:
NOT gate =>
module not_gate(output d, input e); assign d = ~ e; endmodule
OR gate =>
module or_gate(output l, input m, n); assign l = m | n; endmodule
NOTE: use a different variable name for each input and output signal.
Time for us to combine these individual modules for logic gates into one single module for 2:1 MUX. This is done with the help of a concept called module instantiation and falls under a separate module top. The above individual modules will be used in the following by instantiating them with the name_of_the_instance. Instantiation is used when we want to repeat a particular function/ module for different sets of input.
First, write the name of the module you need. Then give the instance a name. The association list will contain the output signal first, followed by the input ones.
and_gate u1(T1, D1, S);
Here and_gate is the name of the module,
u1 is the instance’s name.
wire(which is the intermediate signal) is the output,
S are input. Repeat this for the rest of the modules after considering the logic diagram.
and_gate u1(T1, D1, S); not_gate u2(T2, S); and_gate u3(T3, D0, T2); or_gate u4(Y, T1, T3);
Here’s the final code for 2:1 mux in structural style.
module and_gate(output a, input b, c); assign a = b & c; endmodule module not_gate(output d, input e); assign d = ~ e; endmodule module or_gate(output l, input m, n); assign l = m | n; endmodule module m21(Y, D0, D1, S); output Y; input D0, D1, S; wire T1, T2, T3; and_gate u1(T1, D1, S); not_gate u2(T2, S); and_gate u3(T3, D0, T2); or_gate u4(Y, T1, T3); endmodule
Testbench for the 2:1 Mux in Verilog
A testbench drives the input to the design code of the system. It is used to provide the initial stimulus to the input signals and check for the entire range of possible combinations. You may find a detailed explanation and steps to write the testbench over here!
This is the testbench code for the 2:1 multiplexer.
module top; wire out; reg d0, d1, s; m21 name(.Y(out), .D0(d0), .D1(d1), .S(s)); initial begin d0=1'b0; d1=1'b0; s=1'b0; #100 $finish; end always #40 d0=~d0; always #20 d1=~d1; always #10 s=~s; always@(d0 or d1 or s) $monitor("At time = %t, Output = %d", $time, out); endmodule;
Here is the final simulated waveform for the 2X1 MUX circuit.