VHDL Multiplexer (MUX)
Quick Syntax
output <= input1 when mux_sel = "000" else
input2 when mux_sel = "001" else
input3 when mux_sel = "010" else
....more options here...
(others => '0');
Purpose
A multiplexer, or MUX for short, is simply a way to select between multiple inputs to a common output. It is one of the most common things that you will code in VHDL and is used frequently.
With that in mind, you often see:
1) asynchronous MUX, or one that is not clocked: implemented as concurrent
2) asynchronous MUX, or one that is not clocked: implemented as an unclocked process
3) synchronous MUX, or one that is clocked: implemented as a clocked process
You will typically see people calling asynchronous the same as concurrent, which is the way it's implemented. However, you can also have an asynchronous MUX in a process that is not clocked. You will also see them calling a synchronous a clocked MUX, since that is the way they are implemented.
Which one you use is usually determined by the needs of the design. When it comes to routing and timing, the tools will have an easier time of routing a clocked MUX, since it allows it to more easily fit things in the part instead of stuffing logic in between clocked signals.
Keep in mind that many times you will see asynchronous MUXes used even though the output and inputs are to and from clocked signals. The tools will put timing constraints on these based on the clock domain constraints, so you need to be thoughtful here. Don't make the mux be a clock domain crossing since that's not good design practice.
Also, it sticks levels of logic in between clocked signals when you do that, which makes it harder to meet timing on higher frequency designs.
Examples
Note that we include the else others at the end even though all select states are covered. It's just good design practice to do so. Read on to find out why. Some will debate whether it's necessary.
Here are some great examples of asynchronous/concurrent MUXes. First up is a 2 to 1 MUX:
output <= input1 when mux_sel = '0' else
input2 when mux_sel = '1' else
(others => '0');
Here's a 4 to 1 MUX:
output <= input1 when mux_sel = "00" else
input2 when mux_sel = "01" else
input3 when mux_sel = "10" else
input4 when mux_sel = "11" else
(others => '0');
Here are some great examples of synchronous/clocked MUXes. First up is a 2 to 1 MUX:
MUX : process (clk,mux_sel)
begin
if rising_edge(clk) then
case mux_sel is
when "0" =>
output <= input_1;
when "1" =>
output <= input_2;
when others =>
output <= (others => '0');
end case ;
end if;
end process;
Here's a 4 to 1 MUX:
MUX : process (clk,mux_sel)
begin
if rising_edge(clk) then
case mux_sel is
when "00" =>
output <= input_1;
when "01" =>
output <= input_2;
when "10" =>
output <= input_3;
when "11" =>
output <= input_4;
when others =>
output <= (others => '0');
end case ;
end if;
end process;
If you want an asynchronous/unclocked MUX but in a process, just remove the if rising_edge(clk) statement from the above examples.
Best Practices
1. MUXes are very common, so get comfortable with using them.
2. A clocked MUX in a process will give you better timing and routing, so try to use them for higher frequency designs. Your logic needs to account for the extra clock delay of course on the output of the MUX.
3. It's smart to use an else others to have a catch all in case the select signal doesn't cover all cases. Even if your design does, you always have the risk of someone coming in behind you and removing one of the options down the road.
4. Make sure that the output, inputs, and select are on the same clock domain, unless the MUX is purely asynchronous. Otherwise, use proper clock domain crossing practices.
5. Don't manually MUX clock signals. Use the device's intended primitives for that. Consult the documentation for your FPGA part family to see what the best way to do it is in your situation.