A 2:1 multiplexer (MUX) is a fundamental building block in digital electronics. It acts like a digital switch, selecting one of two input signals and forwarding it to a single output, based on the value of a control signal known as the select line. This capability is crucial for routing data, implementing logic functions, and building more complex digital systems. In this guide, we'll explore how to design a 2:1 MUX using Verilog at the Register-Transfer Level (RTL), verify its functionality with a testbench, and understand the process of generating a bit file for implementation on a Field-Programmable Gate Array (FPGA).
A 2:1 multiplexer, often abbreviated as MUX, is a combinational logic circuit. It features two data inputs (commonly labeled I0 and I1), a single select input (S), and one data output (Y). The select input determines which of the data inputs is connected to the output.
This behavior can be concisely represented by the Boolean expression: \( Y = (\neg S \cdot I0) + (S \cdot I1) \).
Fig. 1: Basic block diagram of a 2:1 Multiplexer.
The functionality of a 2:1 MUX is clearly defined by its truth table:
| Select (S) | Input I0 | Input I1 | Output (Y) |
|---|---|---|---|
| 0 | 0 | X | 0 |
| 0 | 1 | X | 1 |
| 1 | X | 0 | 0 |
| 1 | X | 1 | 1 |
(Where 'X' denotes a "don't care" condition, as that input is not selected)
Alternatively, showing the output directly in terms of inputs:
| Select (S) | Output (Y) |
|---|---|
| 0 | I0 |
| 1 | I1 |
Register-Transfer Level (RTL) design in Verilog describes the flow of data between registers and the logical operations performed on that data. For a combinational circuit like a MUX, RTL primarily focuses on describing its logical behavior. There are several ways to model a 2:1 MUX in Verilog.
This is a common and straightforward way to describe combinational logic in RTL. It uses continuous assignments with the assign keyword. The ternary operator (?:) is perfectly suited for MUX logic.
module mux_2_to_1_dataflow (
input wire i0, // Data input 0
input wire i1, // Data input 1
input wire sel, // Select input
output wire y // Output
);
// If sel is 1, y = i1; otherwise, y = i0
assign y = sel ? i1 : i0;
endmodule
Behavioral modeling describes the circuit's behavior using procedural blocks like always. For combinational logic, an always @(*) block (or always @(input_list)) is used, and outputs must be declared as reg type within this context (though they will synthesize to wires).
module mux_2_to_1_behavioral (
input wire i0, // Data input 0
input wire i1, // Data input 1
input wire sel, // Select input
output reg y // Output (declared as reg for procedural assignment)
);
always @(*) begin
if (sel == 1'b0) begin
y = i0;
end else begin
y = i1;
end
end
endmodule
module mux_2_to_1_case (
input wire i0, // Data input 0
input wire i1, // Data input 1
input wire sel, // Select input
output reg y // Output (declared as reg for procedural assignment)
);
always @(*) begin
case (sel)
1'b0: y = i0;
1'b1: y = i1;
default: y = 1'bx; // Optional: handle unknown select value, assigns 'x' (unknown)
endcase
end
endmodule
All these RTL descriptions (dataflow, behavioral with if-else, behavioral with case) will typically synthesize to the same underlying hardware logic gates: two AND gates, one OR gate, and one NOT gate (to invert the select signal for one of the AND gates).
Often, you'll need to multiplex buses (multiple bits) rather than single bits. Verilog parameters allow for flexible and reusable designs.
module mux_2_to_1_nbit #(parameter WIDTH = 4) (
input wire [WIDTH-1:0] i0, // N-bit data input 0
input wire [WIDTH-1:0] i1, // N-bit data input 1
input wire sel, // Select input
output wire [WIDTH-1:0] y // N-bit output
);
assign y = sel ? i1 : i0;
endmodule
In this example, WIDTH defaults to 4, creating a 4-bit 2:1 MUX. You can override this parameter when instantiating the module (e.g., mux_2_to_1_nbit #(.WIDTH(8)) my_8bit_mux (...) for an 8-bit MUX).
Before synthesizing your RTL code and generating a bit file, it's crucial to verify its correctness through simulation. A Verilog testbench is a separate Verilog module written to apply stimuli to your design (the "Device Under Test" or DUT) and observe its responses.
module tb_mux_2_to_1;
// Inputs to the DUT are declared as 'reg'
reg tb_i0;
reg tb_i1;
reg tb_sel;
// Outputs from the DUT are declared as 'wire'
wire tb_y;
// Instantiate the Device Under Test (DUT)
// Connect testbench signals to DUT ports
mux_2_to_1_dataflow uut (
.i0(tb_i0),
.i1(tb_i1),
.sel(tb_sel),
.y(tb_y)
);
// Stimulus generation block
initial begin
// Initialize inputs
tb_i0 = 0; tb_i1 = 0; tb_sel = 0;
$display("Time=%0t: sel=%b, i0=%b, i1=%b => y=%b", $time, tb_sel, tb_i0, tb_i1, tb_y);
#10; // Wait for 10 time units
tb_i0 = 0; tb_i1 = 1; tb_sel = 0; // i0 selected, y should be 0
$display("Time=%0t: sel=%b, i0=%b, i1=%b => y=%b", $time, tb_sel, tb_i0, tb_i1, tb_y);
#10;
tb_sel = 1; // i1 selected, y should be 1
$display("Time=%0t: sel=%b, i0=%b, i1=%b => y=%b", $time, tb_sel, tb_i0, tb_i1, tb_y);
#10;
tb_i0 = 1; tb_i1 = 0; tb_sel = 0; // i0 selected, y should be 1
$display("Time=%0t: sel=%b, i0=%b, i1=%b => y=%b", $time, tb_sel, tb_i0, tb_i1, tb_y);
#10;
tb_sel = 1; // i1 selected, y should be 0
$display("Time=%0t: sel=%b, i0=%b, i1=%b => y=%b", $time, tb_sel, tb_i0, tb_i1, tb_y);
#10;
$finish; // End simulation
end
// Optional: Monitor changes
// always @(*)
// $monitor("At time = %t, Sel = %b, I0 = %b, I1 = %b, Y = %b", $time, tb_sel, tb_i0, tb_i1, tb_y);
endmodule
You would use a Verilog simulator (like ModelSim, VCS, Xilinx Vivado Simulator, or Intel Quartus Prime Simulator) to compile and run this testbench along with your MUX module. The simulator will show the values of signals over time, often as waveforms, allowing you to verify that the output tb_y behaves as expected for all combinations of inputs.
The journey from a concept like a 2:1 MUX to a physical implementation on an FPGA involves several interconnected stages. The mindmap below illustrates this flow, highlighting the key components and processes from initial RTL coding to the final bit file generation.
This mindmap provides a visual overview of the entire process, starting from understanding the MUX's requirements, moving through Verilog coding and simulation, and culminating in the generation of a bit file ready to configure an FPGA.
A "bit file" (commonly with a .bit extension for Xilinx FPGAs or .sof/.pof for Intel FPGAs) is not directly generated from Verilog RTL code alone. It's the final output of a complex toolchain that translates your high-level hardware description into a low-level configuration file specific to a target FPGA device.
You'll need Electronic Design Automation (EDA) tools provided by FPGA vendors. The most common ones are:
While specific commands and GUI steps vary between tools, the general process is consistent:
mux_2_to_1_dataflow.v) to the project. If you have a testbench, you can add it for simulation purposes within the tool..bit file, often found in a subdirectory like project_name.runs/impl_1/. For Intel Quartus, it might be a .sof (SRAM Object File) or .pof (Programming Object File).As of 2025, these tools often incorporate advanced features, including AI-assisted synthesis for optimization and error reduction, as well as cloud-based platforms for managing larger designs and collaborative development. However, the fundamental flow from RTL to bit file remains.
Implementing a 2:1 MUX involves various considerations, from the ease of RTL design to the complexities of FPGA tool usage. The radar chart below provides a qualitative comparison of different facets of the MUX implementation process. These are subjective assessments intended to give a general idea.
This chart visually represents that while RTL design for a simple MUX is relatively straightforward and synthesizes efficiently, the process of generating a bit file involves a learning curve with FPGA tools and isn't a direct one-step conversion from code. The testbench for a 2:1 MUX is typically simple due to its limited inputs.
Verilog offers different ways to describe hardware. For a 2:1 MUX, the choice of modeling style can influence readability and abstraction, though synthesizers are often adept at producing similar hardware for common constructs like multiplexers.
| Modeling Style | Description | Key Verilog Constructs | Typical Use Case for MUX | Synthesizability |
|---|---|---|---|---|
| Dataflow | Describes how data flows between signals and registers using continuous assignments. Focuses on the structure of data paths. | assign, operators (e.g., ternary ?:, logical, arithmetic) |
Most common and direct way to represent simple combinational logic like a MUX at RTL. Clear and concise. | High |
| Behavioral | Describes the behavior of the circuit algorithmically using procedural blocks. | always blocks (e.g., always @(*), always @(posedge clk)), if-else, case, loops. |
Useful for more complex logic or when describing sequential behavior. For a MUX, it's an alternative to dataflow, often very readable. | High (when used correctly for combinational or synchronous sequential logic) |
| Structural | Describes the circuit as a hierarchy of interconnected modules or predefined primitives (gates). | Module instantiation, port mapping, gate primitives (and, or, not, etc.). |
Used when building a design from lower-level components or explicitly defining the gate structure. Less common for a simple MUX at the RTL design phase unless specific gate-level structure is required. | High |
For a 2:1 MUX, both Dataflow and Behavioral styles are excellent choices for RTL design, leading to efficient synthesis. Structural modeling is also possible but often more verbose for such a simple component unless you are explicitly instantiating custom gates or building a larger MUX from smaller ones.
The following video provides a concise explanation of a 2:1 multiplexer, covering its functionality and how it can be described using Verilog RTL. It serves as a good visual and auditory supplement to the concepts discussed.
Video: Verilog code for 2:1 MUX | 2:1 Multiplexer Functionality & RTL
This video explores the fundamental concept of a multiplexer, which is a crucial component in digital systems. It delves into the design of a 2:1 multiplexer, explaining its operational principles and demonstrating how to implement it using Verilog Hardware Description Language (HDL) at the Register-Transfer Level (RTL). Such tutorials are valuable for understanding both the theoretical basis and practical coding aspects of digital logic components like the 2:1 MUX.