Introduction

In this week’s lab, we will be learning about two more foundational components in Structural Verilog – the multiplexer and demultiplexer. The names sound complicated, but the actual theory of operation is extremely simple, as usual, I am going to start with a contrived example and go from there.

A Contrived Example

Let’s take for example a car. In nearly all cases, this car will have a single air conditioning condenser, the thing that actually gets cold to chill the air entering the vehicle cabin. Side note: heat pumps are very cool and you should learn about them since they’re everywhere.

Anyway, you have a single stream of cold air coming into the HVAC system… but how does it get to the footwell? Or the face vents? Or maybe even the defroster? Well, there’s that dial you can turn that changes where the stream of air goes. Depending on the car, this might move flaps with vacuum or electric motors, or any number of other methods – but at the end of the day, this is acting as a demultiplexer:

HVAC position selector
Figure 1. HVAC position selector

We can see a single input coming in, and some kind of switching that routes that output to one of many different output ports. Multiplexing is the opposite operation, many separate inputs being routed via some switch to a single output. In the context of our Verilog problems, these would be incoming signals (of any width) to an output signal (or vice versa for demultiplex).

In our systems, instead of a physical dial to select the output (or input), there’s typically a set of selector bits and an enable line. There are enough selector bits to describe which output (or input) to select, that is 1 bit for a 2-MUX, 2 bits for a 4-MUX, 3 bits for an 8-MUX, and so on. The enable line disables the outputs entirely when deasserted, and enables normal operation when asserted.

Let’s take for example a simple 1 bit wide two input mux. We want to be able to chose which of the two input signals we assign to the output. We can describe it like this:

module simple_2_mux(
    input A, B, Sel, Enable,
    output Y
);

    assign Y = (A & ~Sel | B & Sel) & Enable;

endmodule

Now, we can display its truth table like this:

Sel Enable Y

0

1

A

1

1

B

X

0

0

The demultiplexer would do the opposite, assigning Y into A or B depending on Sel. Similarly, 1 bit wide four input mux would look like this:

module simple_4_mux(
    input [1:0] Sel,
    input A, B, C, D, Enable,
    output Y
);

    assign Y = (A & ~Sel[0] & ~Sel[1] |
                B &  Sel[0] & ~Sel[1] |
                C & ~Sel[0] &  Sel[1] |
                D &  Sel[0] &  Sel[1] |) & Enable;

endmodule

You can see here that the binary representation of Sel is used to select a particular input. We simply count from 00 to 11 with Sel to pick A through D. This scales all the way up as far as you want to go. However… what do we do when we want more than a single bit multiplexed in? We could do this:

module simple_2_mux_2_bits(
    input [1:0] A, [1:0] B,
    input Sel,
    output [1:0] Y
);

    simple_2_mux bit0 (
        .Enable(1),
        .A(A[0]),
        .B(B[0]),
        .Sel(Sel),
        .Y(.Y[0])
    );

    simple_2_mux bit1 (
        .Enable(1),
        .A(A[1]),
        .B(B[1]),
        .Sel(Sel),
        .Y(.Y[1])
    );

endmodule

… and so on for more bits. But, that doesn’t scale well. It feels clumsy, and while it is very structural and compositional there are better ways to do these things. Introducing… the conditional operator. You may be somewhat familiar with these from other languages like C or Java where they are called Ternary Statements. Let’s look at one of them in Verilog below:

module ternary_2_mux(
    input Sel,
    input A, B, Enable,
    output Y
);

    assign Y = (Sel ? B : A) & Enable;

endmodule

They work like this: Condition ? True statement : False statement. We can use this to our advantage in the higher bit count muxes. Let’s see what the same 2 bit wide 2 input mux from above would look like with conditional operators:

module ternary_2_mux_2_bits(
    input [1:0] A, [1:0] B,
    input Sel,
    output [1:0] Y
);

    assign Y = (Sel ? B : A);

endmodule

Woah! We don’t have to decompose our input signals! We assign the entire vector of A or B into the output vector of Y. When the width of signals matches well, the conditional statement is one of the best methods to do assignments like this.

What kinds of things would we use multiplexers and demultiplexers for? Likely the most common application is bus arbitration within CPUs or other similar circuits. However, this is a fairly advanced topic, so we will stick with a more contrived example – a really terrible Internet Service Provider.

Hint: For the higher count multiplexers, like the 4-MUX, the conditional statement will have to evaluate the value of the select vector. There are multiple ways to do this in verilog:

module equivalence_check(
    input [1:0] select,
    output is_zero, is_one, is_two, is_three
);
    // Option one:
    assign is_zero  = ~select[0] && ~select[1]; // 0b00
    assign is_one   =  select[0] && ~select[1]; // 0b01
    assign is_two   = ~select[0] &&  select[1]; // 0b10
    assign is_three =  select[0] &&  select[1]; // 0b11

    // Option two:
    assign is_zero  = select == 0;
    assign is_one   = select == 1;
    assign is_two   = select == 2;
    assign is_three = select == 3;
endmodule

Notice the && instead of &. The result of & is the two numbers bitwise-and’ed together. The result of && is a boolean operation. If both sides of the && are true (that is, nonzero), then the output is also true.

Part 1

In this scenario you are an employee at Monopolistic Communications Company. You are the sole provider of internet in your town, and your boss has tasked you with updating the internet infrastructure. Right now, there’s only a single line, connecting the CEO of the company to the local Library:

CEO --> Library

Apparently, the townsfolk (all three of them, including you), aren’t happy with this situation. They all want to be able to connect to the library too! To do this, the CEO has asked you to install an Internet Valve that people can go out and switch the connection from their house onto the line. The internet connection here is slow, of course, and is only four bits of data. By the end, you will need to be able to move the four bits from the CEO, and the three other residents onto the internet line to the Library, like so:

CEO ----┐
You ----|
Fred----├--->Library
Jill----┘

Hey! That looks a lot like a multiplexer…

Part 2

In a shock to no one (as we’ve only used a multiplexer not a demultiplexer), the three other businesses in the town, the School, Fire Department, and Rib Shack also want to be able to receive information from the townsfolk. You’ve been asked by the CEO to now add another Internet Valve to switch the data from the townsfolk and into a given business, like so:

CEO ----┐     ┌---->Library
You ----|     |---->Fire Department
Fred----├-----┤---->School
Jill----┘     └---->Rib Shack

The demultiplexer is the reverse of the multiplexer. It takes input of a given width (four bits in our case) and assigns it to one of (2^N) outputs where N is the width of select (in our case, two bits and four outputs). It will use logic that is the reverse of the multiplexer, where instead of a single assign statement with chained ternaries, it will use four assign statements with single ternaries.

Here’s an example of a two bit wide, two output demux:

module demux(
    input [1:0] In,
    input Sel,
    output [1:0] Y1,
    output [1:0] Y2
);

    assign Y1 = (Sel == 1'b0 ? In : 0); // Drive Y1 if Sel == 0
    assign Y2 = (Sel == 1'b1 ? In : 0); // Drive Y2 if Sel == 1

endmodule

Implementation

Wire your multiplexer into your demultiplexer to make the full internet system, as shown below.

The IO table is as follows:

Signal Purpose Direction

sw[3:0]

CEO data

IN

sw[7:4]

Your data

IN

sw[11:8]

Fred’s data

IN

sw[15:12]

Jill’s data

IN

btnL

Sel[0] of the multiplexer

IN

btnU

Sel[1] of the multiplexer

IN

btnD

Sel[0] of the de-multiplexer

IN

btnR

Sel[1] of the de-multiplexer

IN

btnC

Enable of the multiplexer/de-multiplexer

IN

led[3:0]

Data at the library

OUT

led[7:4]

Data at the fire department

OUT

led[11:8]

Data at the school

OUT

led[15:12]

Data at the rib shack

OUT

NOTE: DO NOT USE THE TERM library in your verilog code. This is a reserved keyword. Use local_lib instead.

The idea behind this is that you should be able to set the four switches of data corresponding to any given sender, then press & hold a combination of BTNL/U to select the source of data to the library, and see that set of LEDs light up.

With this setup you should now be able to not only switch who is sending, but who is receiving the four bits.

Lab Deliverables

  • A completed design with the above multiplexing and demultiplexing

  • Demonstrate the design on the Basys3 board to the lab instructor or TA

Appendix A: Chaining Ternary Statements

For the above solutions, you will need to chain three ternary statements to make things work. Here’s a quick example of this:

module chaining_ternaries(
    input [1:0] sel,
    output [1:0] Y
);

    assign Y = sel == 'b00 ? 'b00 : // If 0, then... else
                sel == 'b01 ? 'b01 : // If 1, then... else
                sel == 'b10 ? 'b10 : 'b11; // If 2, then... else
                // We end here and do not use a fourth ternary
                // because 1:0 bits can only be 0, 1, 2, 3
                // and the else case of the third ternary can
                // only be 3

endmodule

Appendix B: Concatenation

For this lab, you will need to combine scalars (the buttons) into a vector for the select logic. There are many ways to do this:

module concat(
    input btnU, btnD
);

    // Method 1: brute force
    wire [1:0] brute;
    assign brute[0] = btnU;
    assign brute[1] = btnD;

    wire [1:0] concat;
    // Method 2: concat structured assignment:
    //               1  :  0
    //               v     v
    assign concat = {btnD, btnU}; // NOTE: Pay VERY VERY close
                                  // attention to your order

endmodule