View Course Path

Verilog code for 8:1 Multiplexer (MUX) – All modeling styles

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:

  1. Gate level modeling
  2. Dataflow modeling
  3. Behavioral modeling
  4. 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.

logic diagram for 8×1 MUX

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 wires.

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.

RTL Schematic For Gate-level Modeling

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:

RTL Schematic for Dataflow Modeling

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

Truth Table for 8:1 MUX

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

RTL Schematic for Behavioral Modeling

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

logic diagram for 8×1 MUX

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

RTL Schematic for Structural Modeling

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.

TCL Console

Simulation Waveforms

The simulation waveform for 8X1 MUX is:

Simulation Waveform 8×1 Multiplexer

The above simulation result is the same for each of the abstraction layers, truly satisfying the truth table.


 

5 thoughts on “Verilog code for 8:1 Multiplexer (MUX) – All modeling styles

  1. This is with respect to behavioral style of modeling.
    In the statement, case(S0 & S1 & S2), let us suppose values of S0, S1, S2 are 0,1,0 respectively.
    so, S0 & S1 & S2 evaluates to 0&1&0 = 0.
    And therefore the control switches to 1’b000: out=0; (i.e. 0),
    instead of switching to 1’b010: out =D2;

    can’t we simply leave case(S0 S1 S2) ??

      1. He’s pointing out a mistake. case(S0 & S1 & S2) should be replaced with

        case({S2,S1,S0})

        or

        wire [2:0] select;
        assign select = {S2,S1,S0};
        case(select)

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.