View Course Path

Verilog Code for Demultiplexer Using Behavioral Modeling

In this guide, we’ll walk you through the essential Verilog code for implementing a 1:4 demultiplexer (demux) using behavioral modeling technique. Whether you’re designing digital circuits or preparing for exams, this guide offers practical code examples and real-world applications to help you implement and test your demux design with ease. Skip the fluff and dive straight into the essential concepts that matter for your project or learning goals.

Update 2025: This guide has been updated for 2025 to reflect the latest best practices and code examples for implementing a 1:4 demux. Whether you’re upgrading legacy designs or starting fresh, this updated version ensures you’re aligned with the latest industry techniques.

What is a demultiplexer?

A demultiplexer (demux) is a circuit that takes the value from a single data input and routes it to one of several outputs based on control lines. It’s like a data distributor. The demultiplexer circuit can also be implemented using a decoder circuit. For our purposes, we’ll focus on a 1-to-4 demultiplexer, which has:

  • One input data line,
  • Four output lines, and
  • Two control lines to determine which output is selected.

The below diagram shows the circuit of the 1-to-4 demultiplexer. Here a1 and a0 are control or select lines y0, y1, y2, y3 are outputs, and Din is the data line.

1:4 demultiplexer
Circuit diagram of a 1:4 demultiplexer

Now, let’s observe its truth table –

truth table of demultiplexer

The values of a1a0 determine which of the outputs are set to the value of Din. When Din=0, all the outputs are set to 0, including the one selected by the valuation of a1a0. When Din=1, the valuation of a1a0 sets the appropriate output (anyone from y0, y1, y2, y3) to 1.

Now that we have thoroughly understood the concepts of the demultiplexer, let’s dive directly into the Verilog code for the demultiplexer.

Different methods used in behavioral modeling of a demultiplexer

Behavioral modeling in Verilog allows you to describe the functionality of a circuit without specifying its structure. We’ll focus on two common methods used for a 1:4 demultiplexer:

  1. Case statements
  2. Assignment statements

Along the way, we’ll also point out some common design pitfalls.

Verilog code for demultiplexer – Using case statements

The Verilog code for a 1-to-4 demux begins with the module declaration, which specifies the ports and their directions. Here’s how to define a simple demux module using a case statement:

module Demultiplexer_1_to_4_case (output reg [3:0] Y, input [1:0] A, input din);

For starters, module is a keyword. It is followed by an identifier. Identifier=name of the module. After naming the module, in a pair of parentheses, we specify:

  • the direction of a port as input, output or inout.
  • Port size, and
  • port name.

Taking into consideration the first line of the code, Demultiplexer_1_to_4_case is the identifier, the input is called port direction. If a port has multiple bits, then it is known as a vector. Hence, [1:0] states that the port named as  A is a vector with MSB = 1 and LSB = 0.

The reg data object holds its value from one procedural assignment statement to the next and means it holds its value over simulation data cycles.

Another style of declaration in the port list is to declare the port size and port direction after the module declaration.

module Demultiplexer_1_to_4_case (Y, A, din);
output reg [3:0] Y;
input [1:0] A;
input din;

[3:0] here signifies that the output is of 4 bits. Next up, since its behavioral modeling style, here comes the always statement. This procedural statement will trigger whenever the values of A or Y change, and it will evaluate the case statement based on the value of A.

always @(Y, A) begin  

Using the always statement, a procedural statement in Verilog, we will run the program sequentially. (Y, A) is known as the sensitivity list or the trigger list. The sensitivity list includes all input signals used by the always block. It controls when the statements in the always block are to be evaluated. @ is a part of the syntax, used before the sensitivity list. In Verilog, begin embarks and  end concludes any block which contains more than one statement in it.

Note that the always statement always @(Y, A) could be written as always @ *. * would mean that the code itself has to decide on the input signals of the sensitivity list.

Then inside always block we write,

case (A)

The case statement in Verilog is analogous to switch-case in C language. First, let us see the general format to write a case statement in Verilog.

case (<expression>) 
    case_item1 :   <single statement>
    case_item2 :   <single statement>
    case_item3 :   begin
                    <multiple statements>
                   end
    default    :   <statement>
endcase

If the expression corresponds to any of the case_item, then those design statements are executed. Otherwise, the default case is executed. So, now we can write

case (A)
2'b00 : begin 
         Y[0] = din; Y[3:1] = 0;
        end
2'b01 : begin 
         Y[1] = din; Y[0]   = 0; 
        end
2'b10 : begin 
         Y[2] = din; Y[1:0] = 0; 
        end
2'b11 : begin 
         Y[3] = din; Y[2:0] = 0;
        end
endcase

As we see here in the first case, 2'b00 represents the case when the input A is 2'b00. These cases indicate that, according to the value of A, one of the four statements is selected. The colon then marks the end of a case item and starts the action that must happen in that particular case. The terms begin and end are part of the Verilog syntax if you are writing more than one statement in that block. After this, those statements are mentioned, such as the output port Y[0] should be attached to the din, Y[3:0] to 0, and so on, according to the truth table.

This modeling is based on the behavior of the circuit; hence it is called behavioral modeling. Observe that we are not specifying the structure of the circuit, we are only creating the logic of the circuit which can implement that hardware.

Here is the full code:

module Demultiplexer_1_to_4_case (output reg [3:0] Y, input [1:0] A, input din);
always @(Y, A) begin
    case (A)
        2'b00 : begin Y[0] = din; Y[3:1] = 0; end
        2'b01 : begin Y[1] = din; Y[0] = 0;   end
        2'b10 : begin Y[2] = din; Y[1:0] = 0; end
        2'b11 : begin Y[3] = din; Y[2:0] = 0; end
    endcase   
end
endmodule

Test Bench for the Demultiplexer

A test bench is used to verify the functionality of the Verilog code by simulating inputs and observing the outputs. Here’s how you can set up a test bench for the 1-to-4 demux module.

The following line includes the pre-written file Demultiplexer_1_to_4_case.v into the testbench. We start by writing 'include which is a keyword to include a file. It is followed by the file name in inverted commas.

`include "Demultiplexer_1_to_4_case.v"

The next line declares the name of the module for testbench according to the syntax as mentioned above. But we do not specify any ports in this module as there will be ports inside the testbench and not outside. Here we declare the data types of the arguments used in the instantiation of the demultiplexer design. A continuous assignment statement assigns values to the wire datatype and makes a connection to an actual wire in the circuit.

module Demultiplexer_1_to_4_case_tb; 
wire [3:0] Y;
reg [1:0] A;
reg din;

Notice that the inputs in the demux here become the reg datatypes and the outputs are specified as wire.

Now, we instantiate a module in Verilog.  Instantiation is a very exciting concept and you will be using it frequently in all the examples in this Verilog course.

Demultiplexer_1_to_4_case Instant0 (Y, A, din);                // syntax for instantiation.

As in the include file Demultiplexer_1_to_4_case.v  we have a module named Demultiplexer_1_to_4_case which contains our circuit design. Now to test it, we should use the circuit, apply a few inputs, and check the outputs, right? For this purpose, we create an instance.

What happens in Verilog when you create an instance of the circuit?

What actually happens is that the whole circuitry/design gets copied in that testbench or that module, in which it was instantiated. There is nothing like calling of a function (which happens in other programming languages, like C programming) because the code here we are writing is for hardware.

Hardware can not be something where instruction is passed. It has to be there physically, so a copy of that circuit is created in the module where it was instantiated. Now, we write:

initial begin
    din = 1;
    A = 2'b00;
#1  A = 2'b01;
#1  A = 2'b10;
#1  A = 2'b11;
end

The inputs to the Verilog model are given test values in the initial block. Like din is given 1 value, A is first given 2'b00, 2 is the number of bits,'(called as a tick), b for binary, and the two bits to carry information. #1 gives a delay of one unit of time in between the test cases. Next is,

initial begin
    $monitor("%t| Din = %d| A[1] = %d| A[0] = %d| Y[0] = %d| Y[1] = %d| Y[2] = %d| Y[3] = %d",
              $time, din, A[1], A[0], Y[0], Y[1], Y[2], Y[3]);
end

To view our results in the console, we have the ‘monitor’ keyword. This is housed in an initial block. Note that %t is the format specifier for time, %b is for binary, %d for decimal. The port names after inverted commas are given the same order as required while assigning values. That is all required to build a testbench.

Here is the complete testbench for Verilog code using case statements.

// Include the Demultiplexer module
`include "Demultiplexer_1_to_4_case.v"

// Define the testbench module
module Demultiplexer_1_to_4_case_tb;
wire [3:0] Y;
reg [1:0] A;
reg din;

// Instantiate the Demultiplexer module
Demultiplexer_1_to_4_case UUT (Y, A, din);

initial begin
// Set initial values
din = 1;
A = 2'b00;
#1 A = 2'b01;
#1 A = 2'b10;
#1 A = 2'b11;
end

initial begin
// Monitor output values
$monitor("%t| Din = %d | A[1] = %d | A[0] = %d | Y[0] = %d | Y[1] = %d | Y[2] = %d | Y[3] = %d",
$time, din, A[1], A[0], Y[0], Y[1], Y[2], Y[3]);
end
endmodule

Verilog code for Demultiplexer – Using Assignment Statement

First of all, we initiate by module and port declaration following the same syntax. We assign identifier as Demultiplexer_1_to_4_assign, input as A, din and output as Y.

module Demultiplexer_1_to_4_assign(output [3:0] Y, input [1:0] A, input din);

We also set up the size and type of the port, which can only be either input, outputs, or inout.

Then we assign the output as the logical and operation of the select lines and data line. assign is a keyword in which the expression or the signal on the right-hand side is evaluated and assigned to the expression on the left side.

assign Y[0] = din & (~A[0]) & (~A[1]);
assign Y[1] = din & (~A[1]) & A[0];
assign Y[2] = din & A[1] & (~A[0]);
assign Y[3] = din & A[1] & A[0];

A[0] means that we are addressing the zeroth bit of the multi-bit bus, similar goes for Y[1], we are accessing the first bit of Y vector. & stands for and operation, ~ is for not operation.

We give all the possible conditions as per our truth table of the demultiplexer.

This is also behavioral modeling as we are not identifying the circuitry, we are only assigning the outputs to bitwise and of data and select lines.

endmodule

This marks the end of the module. You may view the complete code here.

module Demultiplexer_1_to_4_assign(output [3:0] Y, input [1:0] A, input din);
assign Y[0] = din & (~A[0]) & (~A[1]);
assign Y[1] = din & (~A[1]) & A[0];
assign Y[2] = din & A[1] & (~A[0]);
assign Y[3] = din & A[1] & A[0];
endmodule

Test Bench for the Demultiplexer

The first line is:

 `include "Demultiplexer_1_to_4_assign.v"

It includes the Verilog file for the design. Notice that the file name has to be in inverted commas and no semicolon at the end.

Then write:

module Demultiplexer_1_to_4_assign_tb;

This assigns an identifier for the testbench and ends in a semicolon. Again, like previous testbench, no ports for the test bench.

wire [3:0] Y;
reg [1:0] A;
reg din;

Using keywords such as wire, reg, etc. we define the data type of the ports.

Demultiplexer_1_to_4_assign Instant1 (Y, A, din);

The preceding line instantiates the module Demultiplexer_1_to_4_assign as Instant1, which is an identifier and, the ports are given in the precise order. Then comes the initial block:

initial begin
    din = 1;
    A[1] = 0; A[0] = 0;
 #1 A[1] = 0; A[0] = 1;
 #1 A[1] = 1; A[0] = 0;
 #1 A[1] = 1; A[0] = 1;
end

The initial block contains the test inputs for the design. In the test cases here, we provide din = 1 with all the combinations of A with delays.

Here, this initial block is used to observe the ports and, more importantly, the output ports. $time is the value of the time unit for %t format specifier. Other than that, the syntax remains the same.

initial begin
    $monitor("%t| Din = %d| A[1] = %d| A[0] = %d| Y[0] = %d| Y[1] = %d| Y[2] = %d| Y[3] = %d",
              $time, din, A[1], A[0], Y[0], Y[1], Y[2], Y[3]);
end

monitor is a keyword in Verilog, and it displays the contents in the parenthesis on the console. Look at the whole code here.

`include "Demultiplexer_1_to_4_assign.v" 
module Demultiplexer_1_to_4_assign_tb; 
wire [3:0] Y; 
reg [1:0] A;
reg din; 
Demultiplexer_1_to_4_assign I0 (Y, A, din); 
initial begin 
din = 1;
A[1] = 0; A[0] = 0; 
 #1 A[1] = 0; A[0] = 1; 
 #1 A[1] = 1; A[0] = 0;
 #1 A[1] = 1; A[0] = 1; 
end 
initial begin
 $monitor("%t| Din = %d| A[1] = %d| A[0] = %d| Y[0] = %d| Y[1] = %d| Y[2] = %d| Y[3] = %d", 
 $time, din, A[1], A[0], Y[0], Y[1], Y[2], Y[3]); 
end endmodule

Hardware schematic for the demultiplexer

Here is the Hardware schematic which you may develop using Xilinx for demultiplexer.

hardware schematic of demultiplexer

 

Simulation log for the demultiplexer

Simulation log relating to our truth table.

output simulation waveforms of demultiplexerWe can observe that din is always 1; all combinations of A are made, the output can be verified easily. For example- A[0] = 0, A[1] = 0, see that the waveform of Y[0] is high.

 

Frequently Asked Questions (FAQs) about demultiplexer in Verilog.

1. What is a demultiplexer (demux)?

A demultiplexer is a digital circuit that takes a single input and directs it to one of several output lines based on the values of control (select) signals. It is the opposite of a multiplexer, which combines multiple inputs into a single output.

2. What is the difference between behavioral and gate-level modeling in Verilog?

  • Behavioral Modeling: This approach focuses on describing the behavior of the circuit without specifying its structure. It is usually written using constructs like case, if-else, and assign.
  • Gate-Level Modeling: This approach specifies the exact structure of the circuit, using primitive gates like AND, OR, and NOT to represent the circuit’s functionality at the gate level.

3. How does the case statement work in Verilog?

The case statement in Verilog is used to select one of several possible actions based on the value of an expression (like a control signal). It is similar to the switch-case statement in C programming. Each case item corresponds to a specific value of the expression, and the corresponding action is executed.

4. How do I test my Verilog code for the demultiplexer?

You can test your Verilog code by writing a testbench, which provides the necessary input signals and monitors the outputs. In the testbench, you instantiate the demultiplexer module, apply input values to the control and data lines, and check the resulting outputs.

5. What is a Verilog testbench?

A testbench is a simulation file that helps in verifying the functionality of your Verilog code. It contains instances of the modules being tested, provides test inputs, and monitors the outputs to ensure correctness. Testbenches do not interact with hardware; they are used for simulation and verification purposes.

6. How do I handle multiple bits in Verilog?

In Verilog, you can handle multiple bits using vectors. For example, to define a 2-bit wide input, you would use input [1:0] A;. Similarly, for a 4-bit output, you would use output reg [3:0] Y;.

7. What are the key Verilog constructs used in this guide?

  • Module: Defines the basic building block of Verilog code.
  • Input/Output/Reg: Specifies the direction and type of the signals.
  • Always Block: Contains procedural statements that are executed sequentially.
  • Case Statement: Used to select one of many actions based on the value of a control signal.

8. Why use a reg data type for outputs in Verilog?

The reg data type is used for variables that store values across simulation cycles. It is particularly useful for outputs in procedural blocks like always because the values persist between different iterations of the simulation.

9. What is the purpose of the $monitor statement in Verilog?

The $monitor statement in Verilog is used to display the current values of specified variables in the simulation. It allows you to observe how your signals change over time and helps in debugging and verifying the functionality of your design.

10. Can this Verilog code be used for other types of multiplexers or demultiplexers?

Yes, the same approach can be adapted for other types of multiplexers or demultiplexers by adjusting the number of control and data lines. For example, you can create a 1-to-8 demultiplexer by modifying the number of control lines and outputs accordingly.

Leave a Reply

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