Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
8 years ago

Generic One-Hot Multiplexer

I am looking for a way to dynamic instantiate a multiplexer of N channels which has a one-hot encoded select. My current hard-coded version for a 3 channel system looks like this:


signal ChannelSelect    : unsigned(CHAN_SEL_BITS-1 downto 0);
signal EventOneHotFlag  : std_logic_vector(NUM_CHANNELS-1 downto 0);
.....
ChannelSelect <= "00" when EventOneHotFlag(0) = '1' else
                 "01" when EventOneHotFlag(1) = '1' else
                 "10" when EventOneHotFlag(2) = '1' else
                 "00";
MuxOutputData  <= MuxInputArray(to_integer(ChannelSelect))

However, I also have up to 32 channels in some systems that result in the following:


    ChannelSelect <= '0'&x"0" when EventOneHotFlag(0) = '1' else
                     '0'&x"1" when EventOneHotFlag(1) = '1' else
                     '0'&x"2" when EventOneHotFlag(2) = '1' else
                     '0'&x"3" when EventOneHotFlag(3) = '1' else
.... Sparing you the whole list......
                     '1'&x"D" when EventOneHotFlag(29) = '1' else
                     '1'&x"E" when EventOneHotFlag(30) = '1' else
                     '1'&x"F" when EventOneHotFlag(31) = '1' else
                     '0'&x"0";
MuxOutputData  <= MuxInputArray(to_integer(ChannelSelect))

Both work correctly, but I have not found a synthesize solution to parameterize the mux to make it generic for use across system builds. I have been unsuccessful trying Generate statements or For Loops to handle the One-Hot select case.

Thanks,

Aaron

4 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    I am looking for a way to dynamic instantiate a multiplexer of N channels which has a one-hot encoded select. My current hard-coded version for a 3 channel system looks like this:

    Both work correctly, but I have not found a synthesize solution to parameterize the mux to make it generic for use across system builds. I have been unsuccessful trying Generate statements or For Loops to handle the One-Hot select case.

    Thanks,

    Aaron

    --- Quote End ---

    Here is a solution with a for loop:

    
    entity combmuxs is
    	generic (
    		WIDTH_D		:	natural := 4 ;
    		SIZE		:	natural := 16
    		) ;
    	port (
    		D		:	in	std_logic_2D( SIZE - 1 downto 0 , WIDTH_D - 1 downto 0) ;
    		Sel		:	in	std_logic_vector(SIZE - 1 downto 0) ;
    		Q		:	out	std_logic_vector(WIDTH_D - 1 downto 0)  
    		) ;
    	end combmuxs ;
    architecture a of combmuxs is
    	function smux3( d : std_logic_2D ; sel : std_logic_vector)
    		return	std_logic_vector
    		is
    			variable r : std_logic_vector( d'high(2) downto 0) ;
    		begin
    			r:= (others => '0') ;	-- default return if none selected
    			for i in 0 to d'high(1) loop
    				if (sel(i) = '1') then
    					return  to_std_logic_vector( d , i ) ;
    				end if ;
    			end loop ;
    			return r ;
    		end function ;
    begin
    	process( D , Sel )
    		begin
    			Q <= smux3(D , Sel) ;
    		end process ;
    end a ;
    

    It is written for a std_logic_2d from the lpm_components package but it is easy to replace this by a 1Dx1D array. I have written it as a function so I could test other approaches, like a recursive version.

    I add the supporting function:

    
    	function to_std_logic_vector( source : std_logic_2D ; idx : natural )
    		return std_logic_vector
    		is
    			variable r : std_logic_vector( source'high(2) downto 0) ;
    		begin
    			for i in  0 to source'high(2) loop
    				r(i) := source(idx , i) ;
    			end loop ;
    			return r ;
    		end function ;
    
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    If you're happy to use VHDL 2008, you can avoid the annoying (IMO) std_logic_2d type:

    
    --in a package:
    type slv_array_t is array(integer range <>) of std_logic_vector;
    ....
    entity combmuxs is
        generic (
            WIDTH_D        :    natural := 4 ;
            SIZE        :    natural := 16
           ) ;
        port (
            D        :    in    slv_array_t( SIZE - 1 downto 0)(WIDTH_D - 1 downto 0) ;
            Sel        :    in    std_logic_vector(SIZE - 1 downto 0) ;
            Q        :    out    std_logic_vector(WIDTH_D - 1 downto 0)  
            ) ;
        end combmuxs ;
    architecture a of combmuxs is
        function smux3( d : slv_array_t ; sel : std_logic_vector)
            return    std_logic_vector
      is
        constant r : std_logic_vector( d(0)'range ) := (others=>'0');
      begin
        
        for i in d'range loop
          if (sel(i) = '1') then
            return d(i) ;
          end if ;
        end loop ;
        
        return r ;
        end function ;
    begin
      process( D , Sel )
      begin
        Q <= smux3(D , Sel) ;
      end process ;
    end architecture;
    
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    If you're happy to use VHDL 2008, you can avoid the annoying (IMO) std_logic_2d type:

    
    --in a package:
    type slv_array_t is array(integer range <>) of std_logic_vector;
    ....
    entity combmuxs is
        generic (
            WIDTH_D        :    natural := 4 ;
            SIZE        :    natural := 16
           ) ;
        port (
            D        :    in    slv_array_t( SIZE - 1 downto 0)(WIDTH_D - 1 downto 0) ;
            Sel        :    in    std_logic_vector(SIZE - 1 downto 0) ;
            Q        :    out    std_logic_vector(WIDTH_D - 1 downto 0)  
            ) ;
        end combmuxs ;
    architecture a of combmuxs is
        function smux3( d : slv_array_t ; sel : std_logic_vector)
            return    std_logic_vector
      is
        constant r : std_logic_vector( d(0)'range ) := (others=>'0');
      begin
        
        for i in d'range loop
          if (sel(i) = '1') then
            return d(i) ;
          end if ;
        end loop ;
        
        return r ;
        end function ;
    begin
      process( D , Sel )
      begin
        Q <= smux3(D , Sel) ;
      end process ;
    end architecture;
    

    --- Quote End ---

    I just wanted to show the use of a for loop.

    This code was originally written before Quartus supported VHDL-2008 and unconstrained arrays of unconstrained std_logic_vector. The std_logic_2D was/is highly underrated (IMO). Mind you, Altera used std_logic_2D to model the array ports of their AHDL-based LPM_COMPONENT blocks. I have written (had to) numerous supporting functions to support the std_logic_2D type and it beat (at the time) writing custom packages for every user array-type ... and allowing very modular code. E.g. in Qsys you can only transport std_logic_vector, so I wrote a general function to convert this into a std_logic_2D and vice-versa.

    I also have a recursive approach on the OP's requirement, this involves a few more std_logic_2D (and std_logic_vector) support functions

    
    	-- overloading the "and" operator
    	function "and" ( s : std_logic ; v : std_logic_vector)
    		return	std_logic_vector
    		is
    			variable r : std_logic_vector(v'high downto 0) ;
    		begin
    			for i in 0 to v'high loop
    				r(i) := s and v(i) ;
    			end loop ;
    			return r ;
    		end function ;
    	function "and" (v : std_logic_vector ;  s : std_logic )
    		return	std_logic_vector
    		is
    			variable r : std_logic_vector(v'high downto 0) ;
    		begin
    			for i in 0 to v'high loop
    				r(i) := s and v(i) ;
    			end loop ;
    			return r ;
    		end function ;
    	function smux( d : std_logic_2D ; sel : std_logic_vector)
    		return	std_logic_vector
    		is		
    		begin
    			if (d'high(1) = 0) then
    				return ( sel(0) and to_std_logic_vector(d , 0) ) ;
    			elsif (d'high(1) = 1) then
    				return( (sel(0) and to_std_logic_vector(d , 0)) or (sel(1) and to_std_logic_vector(d , 1)) ) ;
    			else
    				return ( smux( split_std_logic_2D(d , UPPER) , split_slv( sel , UPPER) )
    						or
    						 smux( split_std_logic_2D(d , LOWER) , split_slv( sel , LOWER) )
    						) ;	
    			end if ;
    		end function ;
    

    The split functions divide a std_logic_2D/std_logic_vector in two parts.

    The nice thing about the recursive version is that iso converting the one-hot bit into an index and then accessing an array, it builds a binary tree of and and or functions.

    Anyway, that's behind me now. I now develop with myhdl (http://www.myhdl.org), which is a Python library for HDL development. It beats VHDL for writing (self-checking) test-benches by a mile...
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Thanks everyone. An updated For Loop function for the One-Hot to Integer conversion worked. In my case I am driving multiple Muxes and have some other logic based on channel number. I ended up with a converting to an integer output to drive Mux selects and that I could also cast to unsigned:

    
    -- Convert One-Hot array to integer channel select
    function ONEHOT_TO_INT( OneHotCode : std_logic_vector ) return integer is
        constant DefaultReturn: integer := 0;
    begin
        for NumChan in OneHotCode'range loop
            if (OneHotCode(NumChan) = '1') then
                return NumChan;
            end if;
        end loop;
        return DefaultReturn;
    end function;
    ........
    ChannelSelect <= ONEHOT_TO_INT(EventOneHotFlag);
    SomeData_slv <= (others => '0') when (ChannelSelect = LAST_CHANNEL)
                                    else std_logic_vector(OtherData_u(ChannelSelect));
    PacketOut_slv <= Header_slv & std_logic_vector(to_unsigned(ChannelSelect, 8)) & PacketData_slv;