The Universal MUX Building Block
Since all the vector types mentioned are actually arrays of STD_LOGIC we could create first a generic STD_LOGIC MUX module and then build all the other ones as parallel instantiations of STD_LOGIC MUXes with a for generate. So let's start with a generic STD_LOGIC MUX:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
entity MUX is port(I:in STD_LOGIC_VECTOR; – unconstrained vector of STD_LOGIC
SEL:in UNSIGNED; – unconstrained size selection port
O:out STD_LOGIC);
end MUX;
architecture TEST of MUX is
begin
assert (2**SEL'length<=I'length) and (I'length<2**(SEL'length+1)) report "Ports I and SEL have inconsistent sizes!" severity warning; – severity level can be note, warning or error
O<=I(TO_INTEGER(SEL)+I'low); – I is not necessarily 0 based so we add I'low to SEL
end TEST;
Since the I and SEL input ports are unconstrained, it is a good coding practice to add assert statement(s) to check for port size consistency. The assert is ignored during synthesis, at most you might get a message in the synthesis log, but during functional simulation you will definitely get a message and depending on the severity level and simulator you can even stop the simulation and inspect the design to see why things are not as expected.
The other notable thing is that the MUX itself is just one line of behavioral VHDL code, all we need to do is convert the UNSIGNED SEL to INTEGER, add I'low in case the actual signal connected to the I port is not zero based and then select the I element to assign to O. Now we can build any type of multiplexer by instantiating one MUX for every STD_LOGIC bit of the O output. As an example here is how an SFIXED multiplexer called SMUX would look like:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
use work.TYPES_PKG.all; – we need this packge we introduced earlier for SFIXED type support
entity SMUX is port(I:in SFIXED_VECTOR; – unconstrained vector of SFIXED, itself an unconstrained vector of STD_LOGIC
SEL:in UNSIGNED; – unconstrained size selection port
O:out SFIXED);
end SMUX;
architecture TEST of SMUX is
begin
lk:for K in 0 to O'length-1 generate
signal II:STD_LOGIC_VECTOR(I(K)'length-1 downto 0);
begin
lj:for J in II'range generate
II(J)<=I(K+I'low)(J+I(K)'low); – I might not be zero based and I(K) might not be zero based either
end generate;
mx:entity work.MUX port map(I=>II, – we need the intermediate II signal to convert from SFIXED to STD_LOGIC_VECTOR
SEL=>SEL, O=>O(K+O'low)); – O might not be zero based
end generate;
end TEST;
The intermediate II signal has two roles, to convert from SFIXED to STD_LOGIC and to avoid problems when I(K) has negative range since STD_LOGIC_VECTOR is restricted to positive ranges only. Muxes for other types like STD_LOGIC_VECTOR, SIGNED, UNSIGNED, CFIXED and so on are very similar and all are based on the same STD_LOGIC MUX module instantiated many times.
The question we need to ask now is if all this two-level hierarchical decomposition of a generic multiplexer is really required, beyond its educational aspects? After all, we can index an input port or internal signal I which is an array of virtually any user-defined base type in just one line of code, by indexing the I array using TO_INTEGER(SEL) directly. The answer to this question is yes and no.
If the synthesis tool gives you an optimal implementation of such a multiplexer inferred from one line of behavioral code then that is ideal - one line of code is simple to understand and maintain, faster to simulate and all the two-level hierarchy of generic mixes is unnecessarily complicated. But if the synthesis result is not optimal, for example uses too many LUT6es or has too many logic levels and cannot meet the desired clock speed then the coding style I have just introduced can become a life saver. But before even asking if the behavioral synthesis result is optimal or not, we need to be able to set up expectations and be able to answer questions like "how many LUT6es and logic levels are required to implement a 1024-input mux (or any other size)". Answering this question properly requires an understanding of the configurable logic block (CLB) slice, in particular a little know resource called FxMUXes.
This will make the object of my next weekly post.
Back to the top: The Art of FPGA Design
Top Comments