Reputation: 476
I am trying to instantiate a mux with a generic number of case statements. Currently my code looks like this:
In these examples data_array and selector are inputs, data is the output and the width of the mux is 4.
process(all)
begin
case? selector is
when "1---" => data <= data_array(3);
when "01--" => data <= data_array(2);
when "001-" => data <= data_array(1);
when "0001" => data <= data_array(0);
when others => data <= (others => '-');
end case?;
end process;
Is there a way to have a generic number of case statements? Or is there a similar feature that I could use?
I could solve this using code generation to generate the appropriate number of case statements but I was wondering if there is a VHDL(-2008) feature that I could use to solve this.
I have rewritten the mux to use a for loop but unfortunately my implementation tool is not handling this very well. The logic that is inferred is not optimal and quite bad in terms of timing.
In this example GENERIC_WIDTH is the width of the mux.
process(all)
begin
data_v := (others => '0');
for i in 0 to GENERIC_WIDTH-1 loop
if selector(i) then
data <= data_array(i);
end if;
end loop;
end process;
I am targeting a Xilinx device using Vivado 2017.3. Implementation results show that using the case statement yields more efficient logic (in terms of WNS and logic depth) than using the for loop.
Upvotes: 2
Views: 3139
Reputation: 11271
It doesn't matter anymore: modern synthesis tools will correctly optimize all logic. I made a comparison between Vivado 2017.3 outputs. The base entity is
library ieee;
use ieee.std_logic_1164.all;
entity MyMux is
generic(
data_width : positive := 32;
data_depth : positive := 4
);
port(
clk : in std_logic;
data_in : in std_logic_vector(data_width-1 downto 0);
selector : in std_logic_vector(data_depth-1 downto 0);
data_out : out std_logic_vector(data_width-1 downto 0)
);
end entity;
Architecture 1:
architecture rtl of MyMux is
subtype data_type is std_logic_vector(data_width-1 downto 0);
type data_array_type is array (0 to data_depth-1) of data_type;
signal data_array : data_array_type := (others => (others => '0'));
begin
read_data : process(clk) begin
if rising_edge(clk) then
for i in data_depth-1 downto 1 loop
data_array(i) <= data_array(i-1);
end loop;
data_array(0) <= data_in;
end if;
end process;
select_output: process(all) begin
case? selector is
when "1---" => data_out <= data_array(3);
when "01--" => data_out <= data_array(2);
when "001-" => data_out <= data_array(1);
when "0001" => data_out <= data_array(0);
when others => data_out <= (others => '-');
end case?;
end process;
end architecture;
Architecture 2:
architecture rtl of MyMux is
subtype data_type is std_logic_vector(data_width-1 downto 0);
type data_array_type is array (0 to data_depth-1) of data_type;
signal data_array : data_array_type := (others => (others => '0'));
begin
read_data : process(clk) begin
if rising_edge(clk) then
for i in data_depth-1 downto 1 loop
data_array(i) <= data_array(i-1);
end loop;
data_array(0) <= data_in;
end if;
end process;
select_output: process(all) begin
data_out <= (others => '-');
for i in 0 to data_depth-1 loop
if selector(i) then
data_out <= data_array(i);
end if;
end loop;
end process;
end architecture;
Architecture 3:
architecture rtl of MyMux is
subtype data_type is std_logic_vector(data_width-1 downto 0);
type data_array_type is array (0 to data_depth-1) of data_type;
signal data_array : data_array_type := (others => (others => '0'));
function my_mux(
selector : std_logic_vector(data_depth-1 downto 0);
data_array : data_array_type) return data_type is
variable data : data_type;
begin
data := (others => '-');
for i in 0 to data_depth-1 loop
if selector(i)='1' then
data := data_array(i);
end if;
end loop;
return data;
end function;
begin
read_data : process(clk) begin
if rising_edge(clk) then
for i in data_depth-1 downto 1 loop
data_array(i) <= data_array(i-1);
end loop;
data_array(0) <= data_in;
end if;
end process;
data_out <= my_mux(selector, data_array);
end architecture;
Output:
So they are all practically the same.
The problem is you case seems to be the non deterministic component: initial placement randomization. In my experience this initial placement is based on some randomizer seed extracted from the hash of the code. The same code will always give the same implementation. But make a very small change in the code, and timing and resource use might be completely different.
You should take note that the logic you describe in you code will implement as a chain of multiplexers. When the GENERIC_WIDTH
increases, so will the delay. This is inevitable.
Upvotes: 3