0

I want to slice a std_logic_vector in VHDL obtaining parts of it of fixed dimensions.

The general problem is:

din  N*M bits 
dout M bits 
sel  clog2(N) bits

Expected behaviour in an example (pseudocode): input 16 bit, want to slice it in 4 subvectors of 4bit each.

signal in: std_logic_vector(N*M-1 downto 0);
 signal sel: integer;
 --  with sel = 0 
output <= in(N-1:0); 
--with sel = 1 output <= in(2N-1:N)
 -- with sel = 2
 output <= in(3N-1:2N)
 ..... 
--with sel = M-1
 output <= in(M*N-1:(M-1)N)

I know a couples of way to do this, but I don't know which one is the best practice and give the best results in synthesis. the entity

din: in std_logic_vector(15 downto 0);
dout: out std_logic_vector(3 downto 0);
sel: in std_logic_vecotor(1 downto 0)

CASE STATEMENT

case sel is
     when "00" => dout <= din(3:0);
     when "01" => dout <= din(7:4);
     when "10" => dout <= din(11:8);
     when "11" => dout <= din(15:12);
     when others => ....`

It clearly implement a mux, but it's not generic at all and If the input gets big it's really hard to write and to codecover.

INTEGER INDEXING

sel_int <= to_integer(unsigned(sel)); 
dout <= din(4*(sel_int+1) - 1 downto 4*sel_int);

Extremely easy to write and to mantain, BUT it can have problems when the input is not a power of 2. For example, if I want to slice a 24bit vector in chunks of 4, what happen when the integer conversion of sel brings to the index 7?

A STRANGE TRADEOFF

sel_int <= to_integer(unsigned(sel)); 
for i in 0 to 4 generate    
    din_slice(i) <= din(4*(i+1)-1 downto 4*i); 
end generate dout <= din_slice(sel_int); 

I'm searching a solution that is general enough to be used with various input/output relationships and safe enough to be synthesized consistently everytime. The Case statement is the only one with the Others case (that feels really safe), the other solutions rely on the slv to integer conversion and indexing that feels really comfortable but not so reliable.

Which solution would you use?

practical usecase I have a 250bit std_logic_vector and I need to select 10 contigous bits inside of it starting from a certain point from 0 to 239. How can I do that in a way that is good for synthesis?

dadduni
  • 3
  • 2
  • Hello and welcome to SO. Your question is opinion based and hence frowned upon on SO (for an example, [see here](https://meta.stackoverflow.com/questions/255468/opinion-based-questions)). For a "generic" solution, then the integer version is the one to maintain. It shouldnt matter its not a power of two. It is still building a mix on each bit. So it just creates a 2:1 mux for each bit, as per the `case` statement option. – Tricky Jan 30 '23 at 10:19
  • The issue with the generic solution is that some tools do not like non-constant indexes for slicing, but some will accept it. – Tricky Jan 30 '23 at 10:19
  • *I know a couples of way to do this, but I don't know which one is the best practice and give the best results in synthesis.* where best is a subjective term and your question as a result invites opinion as does *Which solution would you use?* and *How can I do that in a way that is good for synthesis?*. [How can I get my question to be not flagged as opinion-based?](https://meta.stackoverflow.com/questions/407140/how-can-i-get-my-question-to-be-not-flagged-as-opinion-based?cb=1) – user16145658 Jan 30 '23 at 20:14
  • [The Line Between "Legitimate SO Question" and "Opinion-Based"](https://meta.stackoverflow.com/questions/396562/the-line-between-legitimate-so-question-and-opinion-based) and from the accepted answer *Questions that contain words like "which is better" are inherently unanswerable without including some means for judging "better." Instead of asking "which is better," ask "which of these options most effectively meets [my specific requirements]?"*, and here you have not articulated specific requirements. – user16145658 Jan 30 '23 at 20:19
  • Stackoverflow search is also rich in potential answers to your as yet non-specific question. See [Search...*[vhdl]generic mux*](https://stackoverflow.com/search?q=%5Bvhdl%5Dgeneric+mux) for example. See [ask]. – user16145658 Jan 30 '23 at 20:25

2 Answers2

2

There is another option that is accepted by tools that allow VHDL 2008 (which includes Vivado and Prime Pro). You can use an unconstrained 2d type from a package:

type slv_array_t is array(natural range <>) of std_logic_vector; --vhdl 2008 unconstrained array type

then you can simply select which port you want. And it is as generic as you like.

library ieee;
use ieee.std_logic_1164.all;

use work.my_pkg.all;

entity mux is
generic (
  N : natural;
  M : natural
);
port (
  sel : in  natural;
  ip  : in  slv_array_t      (N-1 downto 0)(M-1 downto 0);
  op  : out std_logic_vector               (M-1 downto 0);
);
end entity;

architecture rtl of mux is
begin
  op <= ip(sel);
end architecture;
Tricky
  • 3,791
  • 3
  • 10
  • 23
  • Thanks for the answer! You're right. this is another solution. But it has a problem. If you start with a monodimensinal std_logic_vector then to obrtain a bi dimensional array like your slv_array_t, you have to slice it up with a loop so you are basically using my case "a strage tradeoff" where i firstly slice the vector into "din_slice" and then index into it – dadduni Jan 30 '23 at 10:30
  • I dont understand your problem, or how it relates to the loop. You simply connect N options to the ports of ip, you can use `slv_array_t` locally as signals to get the values. You shouldnt need a mono-dimensional SLV in the first place. For connecting to stuff like Xilinx IP that uses mono-dimensional SLVs, you can simply add a conversion function to the package alongside the `slv_array_t` to do the conversion. – Tricky Jan 30 '23 at 11:04
  • Thank you so much for the answer, I added "practial usecase" to my question just to clarify my intention. If you can give me your toughts it would be greatly appreciated! – dadduni Jan 30 '23 at 12:37
  • Your "real usecase" doesnt really match the other posted code. Your other code has a vector N*M long, where you select M bits starting from a multiple of M. Is the new usecase still this, or do you want to start at any arbitrary location? – Tricky Jan 30 '23 at 13:24
  • You are totally right, the practial problem is the one in the usecase I just tried to generalize it in the various cases, at the end it should be pretty similar in any case, it's a MUX that select from different portions of the input. In any case yes, I want to select from any possible location 10 contigous bits. – dadduni Jan 30 '23 at 13:27
  • 1
    the problem with your idea is that, assuming you can start anywhere from 0 to 239 (to give a continious 10 bits from a 250 bit vector) you have to build 240-1 mux for every bit. I would be surprised if this meets timing and any useable clock speed.. Do you really need this many slices? usually it would be better to go back to the design and create what you actually need, rather than create general code that you likely wont re-use. – Tricky Jan 30 '23 at 16:07
1

First you must extend the incoming data to be sure to have always as much bits as you need for connecting all multiplexer inputs (see the code below, process p_extend). This will not create any logic at synthesis. Second you must convert the resulting vector into an array, which you can access later by an index (see the code below, process p_create_array). Again this will not create any logic at synthesis. At last you must access this array by the select input signal (see the code below, process p_mux).

library ieee;
use ieee.std_logic_1164.all;
entity mux is
    generic (
        g_data_width  : natural := 250;
        g_slice_width : natural := 10;
        g_sel_width   : natural := 5;
        g_start_point : natural := 27
    );
    port (
        d_i   : in  std_logic_vector(g_data_width-1 downto 0);
        sel_i : in  std_logic_vector(g_sel_width-1 downto 0);
        d_o   : out std_logic_vector(g_slice_width-1 downto 0)
    );
end entity mux;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
architecture struct of mux is
    signal data : std_logic_vector(g_slice_width * 2**g_sel_width-1 downto 0);
    type t_std_logic_slice_array is array (natural range <>) of std_logic_vector(g_slice_width-1 downto 0);
    signal mux_in : t_std_logic_slice_array (2**g_sel_width-1 downto 0);
begin
    p_extend: process(d_i)
    begin
        for i in 0 to g_slice_width * 2**g_sel_width-1 loop
            if i+g_start_point<g_data_width then
                data(i) <= d_i(i+g_start_point);
            else
                data(i) <= '0';
            end if;
        end loop;
    end process;
    p_create_array: process (data)
    begin
        for i in 0 to 2**g_sel_width-1 loop
            mux_in(i) <= data((i+1)*g_slice_width-1 downto i*g_slice_width);
        end loop;
    end process;
    p_mux: d_o <= mux_in(to_integer(unsigned(sel_i)));
end architecture;
  • Thanks for the comment, the idea of extending the input to cover all the selector possibilities is really good. One question: would it be the same assigning d_o directly as a slice in function of sel, without passing through a intermediate array? In you solution the slicing is in the array, and then the indexing is based on sel: can they be merged safely? – dadduni Jan 30 '23 at 15:43
  • Yes, you could pick the bits from signal data directly by (I hope, I did not intrduce a bug): `d_o <= data(g_slice_width*(to_integer(unsigned(sel_i))+1) + g_start_point -1 downto g_slice_width*to_integer(unsigned(sel_i)) + g_start_point);` But this is very bad to read/understand and I expect it to be not synthesizable. – Matthias Schweikart Jan 30 '23 at 15:54
  • I introduced a bug in my comment above: adding g_start_point to the indices is wrong and not needed here. – Matthias Schweikart Jan 30 '23 at 16:11