问题
I'm trying to implement an generic adder tree similar to here. For storing the intermediate results, I need to declare a variable number of signals with variable bitwidth. For example:
4 input values with bitwidth = 8:
- after first stage: 2 values with bitwidth = 9
- after second stage: 1 value with bitwidth = 10
9 input values with bitwidth = 8:
- after first stage: 5 values with bitwidth = 9
- after second stage: 3 values with bitwidth = 10
- after third stage: 2 values with bitwidth = 11
- after forth stage: 1 value with bitwidth = 12
I just found one solution to instantiate an array with length = # input values
and bitwidth = bitwidth of the last signal
. But I want to have something like the following. A record including the values of each stage concatenated to an std_logic_vector, but it's obviously not working:
lb(INPUT_VALUES) == number of stages
nr_val(i) == number of values at stage -> calculated in a separate function
type adder_stages is record
for i in 1 to lb(INPUT_VALUES) generate
stage(i-1) : std_logic_vector(nr_val(i)*(BITWIDTH+i)-1 downto 0);
end generate;
end record adder_stages;
Is it possible to declare a variable amount of signals with increasing bitwidth and dependent on the number of input values in VHDL '93?
回答1:
Contrary to NiM's assertion that it's impossible to declare a variable amount of signals with increasing bitwidth and dependent on the number of input values
in any version (revision) of VHDL, it is possible in -2008.
The secret is to use component instantiation recursion with an input port whose type is an unbounded array with an element subtype indication provided in the object declaration. The number of inputs and their length can be changed (number of inputs down, element subtype length up) in successive recursion levels. The output port is of a constant width and is driven by the lowest level adder output.
Defining an unbounded array definition with a deferred element subtype indication is not supported in -1993.
This code hasn't been verified other than guaranteeing the lengths and numbers of levels work correctly. It uses unsigned arithmetic because the OP didn't specify otherwise. Resize is used to increase the adder result length.
The report statements were used for debugging and can be removed (amazing how many simple errors you can make in something only mildly convoluted).
library ieee;
use ieee.std_logic_1164.all;
package adder_tree_pkg is
function clog2 (n: positive) return natural;
type input_array is array (natural range <>) of
std_logic_vector; -- -2008 unbounded array definition
function isodd (n: positive) return natural;
end package;
package body adder_tree_pkg is
function clog2 (n: positive) return natural is
variable r: natural := 0;
variable m: natural := n - 1;
begin
while m /= 0 loop
r := r + 1;
m := m / 2;
end loop;
return r;
end function clog2;
function isodd (n: positive) return natural is
begin
if (n/2 * 2 < n) then
return 1;
else
return 0;
end if;
end function;
end package body;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std_unsigned.all;
use work.adder_tree_pkg.all;
entity adder_tree_level is
generic (
constant INPUTS: positive := 9;
constant BITS: positive := 8;
constant LEVEL: positive := clog2(INPUTS);
constant Y_OUT_LEN: positive := LEVEL + BITS
);
port (
clk: in std_logic;
rst_n: in std_logic;
x_in: in input_array (INPUTS - 1 downto 0) (BITS - 1 downto 0);
y_out: out std_logic_vector (Y_OUT_LEN - 1 downto 0)
);
end entity;
architecture foo of adder_tree_level is
constant ODD_NUM_IN: natural := isodd(INPUTS);
constant NXT_INPS: natural := INPUTS/2 + ODD_NUM_IN;
signal x: input_array (INPUTS - 1 downto 0) (BITS - 1 downto 0);
signal nxt_x: input_array (NXT_INPS - 1 downto 0)
(BITS downto 0);
constant NPAIRS: natural := (INPUTS)/2;
begin
INPUT_REGISTER:
process (clk, rst_n)
begin
if rst_n = '0' then
x <= (others =>(others => '0'));
elsif rising_edge (clk) then
x <= x_in;
end if;
end process;
ADDERS:
process (x)
begin
report "LEVEL = " & integer'image(LEVEL);
report "y_out'length = " & integer'image(y_out'length);
report "nxt_x(0)'length = " & integer'image(nxt_x(0)'length);
for i in 0 to NPAIRS - 1 loop -- odd out is x'high ('left)
nxt_x(i) <= resize(x(i * 2), BITS + 1) + x(i * 2 + 1);
report "i * 2 = " & integer'image (i * 2);
report "i * 2 + 1 = " & integer'image (i * 2 + 1);
end loop;
if ODD_NUM_IN = 1 then
report "x'left = " & integer'image(x'left);
nxt_x(nxt_x'HIGH) <= resize(x(x'LEFT), BITS + 1);
end if;
end process;
RECURSE:
if LEVEL > 1 generate
NEXT_LEVEL:
entity work.adder_tree_level
generic map (
INPUTS => NXT_INPS,
BITS => BITS + 1,
LEVEL => LEVEL - 1,
Y_OUT_LEN => Y_OUT_LEN
)
port map (
clk => clk,
rst_n => rst_n,
x_in => nxt_x,
y_out => y_out
);
end generate;
OUTPUT:
if LEVEL = 1 generate
FINAL_OUTPUT:
y_out <= nxt_x(0);
end generate;
end architecture;
This example doesn't meet the criteria for answering Yes to the OP's question (which is a yes/no question) and simply refutes NiM's assertion that you can't do it in any version (revision) of VHDL.
It's ports are inspired by the Pipelined Adder Tree VHDL code found by the image the OP linked.
回答2:
What you are asking for is not possible in any version of VHDL, v93 or otherwise. You can define a type inside a generate statement, but not use a generate within a type definition.
Your initial solution is the way that I would do it personally - if targeting an FPGA using modern tools the unused MSBs at each stage will be optimised away during synthesis, so the resulting circuit is as you've described with no additional overhead (i.e. the tools are clever enough to know that adding two 8-bit numbers can never occupy more than 9 bits).
来源:https://stackoverflow.com/questions/49981526/declare-a-variable-number-of-signals-with-variable-bitwidth-in-vhdl93