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.

** 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:

modulemodule_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 and`S0`

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 is`T4, 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`

, and`S0bar`

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 between`b`

,`c`

,`d`

, and`e`

.

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 of`S1`

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.