Search code examples
hardwarevhdlslice

VHDL Dynamic slicing using mathematical expression


Is there an easy way to implement the following line:

DataTX(255-index*8 downto 248-index*8)  <= encoded;

index is an integer that could be in the range 0 to 31.

DataTx is a STD_LOCIC_VECTOR(263 downto 0).

Now it currently throws an error saying there are multiple drivers for DataTx(263) and DataTx(260), which isn't actually occurring in this line.

Is there any way to write this line or implement it without have to do a massive if statement or case statement.

Thanks in the advance.

Edit: The line above is placed in a process. Meanwhile the other parts of DataTx are set outside the process as follows

DataTX(263 downto 261) <= "000" when spi_mode=reading else "001" when spi_mode=writing 
                        else "011" when spi_mode=rxMode  else "101" when spi_mode=txMode;
DataTX(260 downto 256) <= SPI_register;

Edit2:

DataTX(263 downto 261) <= "000" when spi_mode=reading else "001" when spi_mode=writing 
                        else "011" when spi_mode=rxMode  else "101" when spi_mode=txMode;
DataTX(260 downto 256) <= SPI_register;
-- Set the RF Chan based on input
rf_chan <= RfChans(0) when base_selection="00" else  RfChans(1) when base_selection="01" else
         RfChans(2) when base_selection="10" else  RfChans(3) when base_selection="11";
-- Set the Base Address based on input
base_addr <= Bases(0) when base_selection="00" else  Bases(1) when base_selection="01" else
         Bases(2) when base_selection="10" else  Bases(3) when base_selection="11";
process(clk)
variable s_index : integer range 0 to 31 := 0;
variable e_index : integer range 0 to 31 := 0;
variable r_index : integer range 0 to 31 := 0;
variable counter : integer range 0 to 7 := 0;
begin
    if (rst = '1') then
        ...
    elsif (clk'event and clk='1') then
        case state is
            when setup =>
                case setup_state is
                        -- Some code like this is set here
                        spi_mode <= writing;
                        spi_register <= "10000";
                        DataTX(255 downto 224) <= base_addr;
                        DataTX(223 downto 0) <= (others => '1'); -- Fill with FF
            when idle =>
                ... -- nothing here of relevance
                -- Moves to E_setup
            when E_setup =>
                -- DataTx set here
                spi_mode <= writing;
                spi_register <= "00000";
                SPI_trigger <= '1';
                state <= E_setupd;
            when E_setupd => -- one cycle delay
                SPI_Trigger <= '0';
                        if ((counter = 7) and SS='1') then
                            -- Signals that SPI module has finished
                            state <= E_encode;
                            counter := 0;
                        elsif (counter < 7) then -- Give it some time to start
                            counter := counter +1;
                        end if; 
            when E_encode => 
                encode_input <= SEQUENCE(e_index);
                e_index := e_index +1;
                state <= E_done;
            when E_done =>
                spi_register <= "00000";
                -- Error here
                DataTX(255-index*8 downto 248-index*8)  <= encoded;
                if (e_index = 31) then
                        e_index := 0;
                        state <= E_send;
                else
                    e_index := e_index +1;
                    state <= E_encode;
                end if;
        end case;
    end if;
end process;

Solution

  • The updated question gives enough information to attempt an answer. It comes down to what is "locally static" i.e. computable without actually executing the process, so that hardware can be synthesised to meet the specification.

    Now it might be obvious to you that

    DataTX(<arbitrary range expression>)  <= encoded;
    

    only drives selected parts of DataTX leaving others undriven, without evaluating <arbitrary range expression>, but the synthesis tool isn't quite that smart. So the language places a restriction on the process, that the "locally static" expression is ALL of DataTX and so the process drives all of DataTX.

    (NOTE: had the range expression been purely in terms of constants or numeric literals,this restriction would not apply)

    The external assignments then form your additional drivers giving rise to "multiple drivers" errors.

    Two fixes :

    1) Drive the entirety of DataTX within the process, i.e. move the other slice assignments within the process ... this looks like unclean design to me

    2) Assign a new signal e.g. EncodedTX within the process, and externally assign it to the correct slice of DataTX

    DataTX(255 downto 0) <= EncodedTX;
    

    or better (clearer design intent)

    DataTX <= SPI_Mode_Bits & SPI_Register & Encoded_TX;
    

    with a suitable declaration and assignment for the SPI_Mode_Bits signal.

    Possibly there are other fixes too, but the second of these would probably be my first choice...

    Given Edit2 I now believe option (1) is cleaner : e.g. assigning the extra bits in the main state machine process, which is where you set up SPI_Mode and SPI_Register anyway. You can even make SPI_Register an alias for the relevant slice of Data_TX. Then you don't need to cross reference between the SM and the external cruft needed to support it.

    For example, consider the following declarations :

    alias SPI_Register : std_logic_vector(4 downto 0) is DataTX(260 downto 256);
    subtype SPI_Mode_Type is std_logic_vector(2 downto 0);
    alias SPI_Mode     : SPI_Mode_Type is DataTX(263 downto 261);
    constant writing   : SPI_Mode_Type := "001"; -- and a few more of these
    

    The state machine, without change, will now perform all the assignments to Datatx and the external assignments can simply be deleted.