Declare a variable number of signals with variable bitwidth in VHDL'93 - vhdl

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?

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.

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).

Related

Vivado VHDL width mismatch - how can I fix it?

Please consider this very simple minimal reproducible code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity test is
generic ( LENGTH : integer range 1 to 16 := 5 );
Port ( x : in STD_LOGIC;
y : out STD_LOGIC_VECTOR(15 downto 0)
);
end test;
architecture Behavioral of test is
signal a : std_logic_vector (15 downto 0);
signal b : std_logic_vector (LENGTH - 1 downto 0);
signal i : integer range 0 to LENGTH-1 := 1;
begin
y <= a;
process
begin
if i = LENGTH then
i <= 1;
else
a <= a(15 downto i + 1) & b(i downto 0);
end if;
i <= i + 1;
end process;
end Behavioral;
My need is to join some elements of b into a, depending on i. By running the RTL on Vivado, it says:
[Synth 8-690] width mismatch in assignment; target has 16 bits, source has 20 bits
I don't really get why. Anyhow, the overall range will be 15 - (i + 1) + (i - 0) = 15 ... 0 and fits in the 16 bits of output -- what's the deal for 20 bits?
I should say the problem vanishes (obviously) if I use plain constants instead of i, but I still don't get what's going on.
For runtime variable I (as per the question)...
instead of a big CASE, you can use the value of I to generate masks, and evaluate (A and MASKA) or (B and MASKB). Which is equivalent to the multiplexer the synthesis tool would generate if it wasn't broken.
For generic I (it's not fair to move the goalposts in the comments!)
this approach generates unnecessary hardware, which will be optimised out by any competent synthesis tool.
(There are of course other problems with this code; I assume you deleted the clock, taking the MCVE notion a bit too far. You should leave it valid synthesisable code)

If statement in a for loop VHDL

I want to do a for loop for 8 inputs and an if statement.My purpose is to find minimum of these 8 portsI know what the error is but i want to make (Ι-1) when the (i) take the value of 7.Any ideas?
if (a_unss(i)
LIBRARY ieee;
USE ieee.std_logic_1164 .all;
USe ieee.numeric_std .all;
---------------------------------------
ENTITY bitmin IS
generic
(
size: integer :=8
);
PORT
(
A0,A1,A2,A3,A4,A5,A6,A7 : IN UNSIGNED (size-1 downto 0);
MinOut:out UNSIGNED (size-1 downto 0)
);
END Entity;
-------------------------------------------------------------------------
ARCHITECTURE compare OF bitmin IS
type a_uns is array (0 to 7) of unsigned(7 downto 0);
signal a_unss:a_uns;
begin
a_unss(0)<=(A0);
a_unss(1)<=(A1);
a_unss(2)<=(A2);
a_unss(3)<=(A3);
a_unss(4)<=(A4);
a_unss(5)<=(A5);
a_unss(6)<=(A6);
a_unss(7)<=(A7);
process(a_unss)
begin
MinOut<="00000000";
for i in 0 to 7 loop
if (a_unss(i)<a_unss(i+1))and (a_unss(i)<a_unss(i+1)) and (a_unss(i)<a_unss(i+1)) and (a_unss(i)<a_unss(i+1))and (a_unss(i)<a_unss(i+1)) and (a_unss(i)<a_unss(i+1)) and (a_unss(i)<a_unss(i+1)) then
MinOut<=a_unss(i);
end if;
end loop;
end process;
END compare;
Error:
Error (10385): VHDL error at bitmin.vhd(48): index value 8 is outside the range (0 to 7) of object "a_unss"
Error (10658): VHDL Operator error at bitmin.vhd(48): failed to evaluate call to operator ""<""
Error (10658): VHDL Operator error at bitmin.vhd(48): failed to evaluate call to operator ""and""
Error (12153): Can't elaborate top-level user hierarchy
Error: Quartus Prime Analysis & Synthesis was unsuccessful. 4 errors, 1 warning
Error: Peak virtual memory: 4826 megabytes
Error: Processing ended: Thu Apr 09 19:39:04 2020
Error: Elapsed time: 0enter code here0:00:17
Error: Total CPU time (on all processors): 00:00:43
As others have pointed out, the for-loop index goes out of range of the array length. You also need to produce a chain of minimums. And the bit width within the Compare architecture should be dependent upon the generic SIZE.
In Version 1 below, a single long chain is used.
In Version 2 below, two half-length chains are used which gives a shorter overall propagation delay.
In Version 3 below, a tree structure is used which gives the shortest overall propagation delay.
Version 1 - One long chain
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity BitMin is
generic
(
SIZE: integer := 8
);
port
(
a0, a1, a2, a3, a4, a5, a6, a7: in unsigned(SIZE - 1 downto 0);
minout: out unsigned(SIZE - 1 downto 0)
);
end entity;
architecture Compare of BitMin is
subtype TBits is unsigned(SIZE - 1 downto 0); -- Changed TByte to TBits because the bit width is dependent upon the generic SIZE.
type TBitsArray is array(0 to 7) of TBits;
signal inputs: TBitsArray;
signal min_chain: TBitsArray;
function Minimum(a, b: TBits) return TBits is
begin
if a < b then
return a;
end if;
return b;
end function;
begin
inputs <= ( a0, a1, a2, a3, a4, a5, a6, a7 );
-- Version 1 (one long chain)
process(inputs, min_chain)
begin
min_chain(0) <= inputs(0); -- Assume the first element in the array is the minimum.
for i in 1 to 7 loop -- Cycle through the remaining items to find the minimum.
min_chain(i) <= Minimum(min_chain(i - 1), inputs(i));
end loop;
minout <= min_chain(7);
end process;
end Compare;
Version 2 - Two half-length chains
-- Version 2 (two half-length chains: 0..3 and 7..4)
process(inputs, min_chain)
begin
min_chain(0) <= inputs(0); -- Assume the first element in the array is the minimum.
min_chain(7) <= inputs(7); -- Assume the last element in the array is the minimum.
for i in 1 to 3 loop -- Cycle through the remaining items to find the minimum.
min_chain(i) <= Minimum(min_chain(i - 1), inputs(i)); -- Work forwards from element 1.
min_chain(7 - i) <= Minimum(min_chain(7 - i + 1), inputs(7 - i)); -- Work backwards from element 6.
end loop;
minout <= Minimum(min_chain(3), min_chain(4)); -- Find the minimum of the two chains.
end process;
Version 3 - Tree
-- Version 3 (tree structure)
process(inputs)
constant NUM_INPUTS: natural := inputs'length;
constant NUM_STAGES: natural := natural(ceil(log2(real(NUM_INPUTS))));
type TTree is array(0 to NUM_STAGES) of TBitsArray; -- This declares a matrix, but we only use half of it (a triangle shape). The unused part will not be synthesized.
variable min_tree: TTree;
variable height: natural;
variable height_int: natural;
variable height_rem: natural;
variable a, b: TBits;
begin
-- Stage 0 is simply the inputs
min_tree(0) := inputs;
height := NUM_INPUTS;
for i in 1 to NUM_STAGES loop
-- Succeeding stages are half the height of the preceding stage.
height_int := height / 2;
height_rem := height rem 2; -- Remember the odd one out.
-- Process pairs in the preceding stage and assign the result to the succeeding stage.
for j in 0 to height_int - 1 loop
a := min_tree(i - 1)(j);
b := min_tree(i - 1)(j + height_int);
min_tree(i)(j) := Minimum(a, b);
end loop;
-- Copy the odd one out in the preceding stage to the succeeding stage
if height_rem = 1 then
a := min_tree(i - 1)(height - 1);
min_tree(i)(height_int) := a;
end if;
-- Adjust the ever-decreasing height for the succeeding stage.
height := height_int + height_rem;
end loop;
-- Get the value at the point of the triangle which is the minimum of all inputs.
minout <= min_tree(NUM_STAGES)(0);
end process;
Test Bench
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity BitMin_TB is
end entity;
architecture V1 of BitMin_TB is
constant SIZE_TB: natural := 8;
component BitMin is
generic
(
SIZE: integer := 8
);
port
(
a0, a1, a2, a3, a4, a5, a6, a7: in unsigned (SIZE - 1 downto 0);
minout: out unsigned (SIZE - 1 downto 0)
);
end component;
signal a0_tb, a1_tb, a2_tb, a3_tb, a4_tb, a5_tb, a6_tb, a7_tb: unsigned(SIZE_TB - 1 downto 0);
signal minout_tb: unsigned(SIZE_TB - 1 downto 0);
begin
DUT: BitMin
generic map
(
SIZE => SIZE_TB
)
port map
(
a0 => a0_tb,
a1 => a1_tb,
a2 => a2_tb,
a3 => a3_tb,
a4 => a4_tb,
a5 => a5_tb,
a6 => a6_tb,
a7 => a7_tb,
minout => minout_tb
);
process
begin
wait for 10 ns;
a0_tb <= "00000100";
a1_tb <= "00001000";
a2_tb <= "00010000";
a3_tb <= "00100000";
a4_tb <= "01000000";
a5_tb <= "10000000";
a6_tb <= "00000010";
a7_tb <= "00000001";
wait for 10 ns;
--std.env.stop;
wait;
end process;
end architecture;
Synthesis Comparison
All three versions synthesise to the same amount of logic elements, but Version 3 is the fastest.
Version 1 RTL - one long chain
Version 2 RTL - two half-length chains
Version 3 RTL - tree
if (a_unss(i)<a_unss(i+1))and (a_unss(i)<a_unss(i+1)) and (a_unss(i)<a_unss(i+1)) and (a_unss(i)<a_unss(i+1))and (a_unss(i)<a_unss(i+1)) and (a_unss(i)<a_unss(i+1)) and (a_unss(i)<a_unss(i+1)) then
The indexing of a_unss(i+1) is causing a problem as you are iterating form 0 to 7. When i reaches 7, i+1 is equal to 8 which is greater than the boundaries of a_unss. This is what the message : Error (10385): VHDL error at bitmin.vhd(48): index value 8 is outside the range (0 to 7) of object "a_unss" is saying.
EDIT
Suggestion to update the code:
LIBRARY ieee;
USE ieee.std_logic_1164 .all;
USe ieee.numeric_std .all;
---------------------------------------
ENTITY bitmin IS
generic
(
size: integer :=8
);
PORT
(
A0,A1,A2,A3,A4,A5,A6,A7 : IN UNSIGNED (size-1 downto 0);
MinOut:out UNSIGNED (size-1 downto 0)
);
END Entity;
-------------------------------------------------------------------------
ARCHITECTURE compare OF bitmin IS
type a_uns is array (0 to 7) of unsigned(7 downto 0);
signal a_unss:a_uns;
signal MinOut_tmp : UNSIGNED (size-1 downto 0) := 0;
signal done_flag: STD_LOGIC := '0';
begin
a_unss(0)<=(A0);
a_unss(1)<=(A1);
a_unss(2)<=(A2);
a_unss(3)<=(A3);
a_unss(4)<=(A4);
a_unss(5)<=(A5);
a_unss(6)<=(A6);
a_unss(7)<=(A7);
process(a_unss) begin
done_flag <= '0';
for i in 0 to 7 loop
if (a_unss(i) < MinOut_tmp) then
MinOut_tmp<=a_unss(i);
end if;
end loop;
done_flag <= '1';
end process;
END compare;
process(done_flag) begin
if (done_flag == '1') then
MinOut <= MinOut_tmp;
end if;
end process;

VHDL Data Flow description of Gray Code Incrementer

I am trying to write the VHDL code for a Gray Code incrementer using the Data Flow description style. I do not understand how to translate the for loop I used in the behavioral description into the Data Flow description. Any suggestion?
This is my working code in behavioral description
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity graycode is
Generic (N: integer := 4);
Port ( gcode : in STD_LOGIC_VECTOR (N-1 downto 0);
nextgcode : out STD_LOGIC_VECTOR (N-1 downto 0));
end graycode;
architecture Behavioral of graycode is
begin
process(gcode)
variable bcode : STD_LOGIC_VECTOR(N-1 downto 0);
variable int_bcode : integer;
begin
for i in gcode'range loop
if(i < gcode'length - 1) then
bcode(i) := gcode(i) XOR bcode(i+1);
else
bcode(i) := gcode(i);
end if;
end loop;
int_bcode := to_integer(unsigned(bcode));
int_bcode := int_bcode + 1;
bcode := std_logic_vector(to_unsigned(int_bcode, N));
for i in gcode'range loop
if(i < gcode'length - 1) then
nextgcode(i) <= bcode(i) XOR bcode(i+1);
else
nextgcode(i) <= bcode(i);
end if;
end loop;
end process;
end Behavioral;
'Dataflow' means 'like it would look in a circuit diagram'. In other words, the flow of data through a real circuit, rather than a high-level algorithmic description. So, unroll your loops and see what you've actually described. Start with N=2, and draw out your unrolled circuit. You should get a 2-bit input bus, with an xor gate in it, followed by a 2-bit (combinatorial) incrementor, followed by a 2-bit output bus, with another xor gate, in it. Done, for N=2.
Your problem now is to generalise N. One obvious way to do this is to put your basic N=2 circuit in a generate loop (yes, this is dataflow, since it just duplicates harwdare), and extend it. Ask in another question if you can't do this.
BTW, your integer incrementor is clunky - you should be incrementing an unsigned bcode directly.
Dataflow means constructed of concurrent statements using signals.
That means using generate statements instead of loops. The if statement can be an if generate statement with an else in -2008 or for earlier revisions of the VHDL standard two if generate statements with the conditions providing opposite boolean results for the same value being evaluated.
It's easier to just promote the exception assignments to their own concurrent signal assignments:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity graycode is
generic (N: natural := 4); -- CHANGED negative numbers wont be interesting
port (
gcode: in std_logic_vector (N - 1 downto 0);
nextgcode: out std_logic_vector (N - 1 downto 0)
);
end entity graycode;
architecture dataflow of graycode is
signal int_bcode: std_logic_vector (N - 1 downto 0); -- ADDED
signal bcode: std_logic_vector (N - 1 downto 0); -- ADDED
begin
int_bcode(N - 1) <= gcode (N - 1);
TO_BIN:
for i in N - 2 downto 0 generate
int_bcode(i) <= gcode(i) xor int_bcode(i + 1);
end generate;
bcode <= std_logic_vector(unsigned(int_bcode) + 1);
nextgcode(N - 1) <= bcode(N - 1);
TO_GRAY:
for i in N - 2 downto 0 generate
nextgcode(i) <= bcode(i) xor bcode(i + 1);
end generate;
end architecture dataflow;
Each iteration of a for generate scheme will elaborate a block statement with an implicit label of the string image of i concatenated on the generate statement label name string.
In each of these blocks there's a declaration for the iterated value of i and any concurrent statements are elaborated into those blocks.
The visibility rules tell us that any names not declared in the block state that are visible in the enclosing declarative region are visible within the block.
These mean concurrent statements in the block are equivalent to concurrent statement in the architecture body here with a value of i replaced by a literal equivalent.
The concurrent statements in the generate statements and architecture body give us a dataflow representation.
And with a testbench:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity graycode_tb is
end entity;
architecture foo of graycode_tb is
constant N: natural := 4;
signal gcode: std_logic_vector (N - 1 downto 0);
signal nextgcode: std_logic_vector (N - 1 downto 0);
signal bcode: std_logic_vector (N - 1 downto 0);
begin
DUT:
entity work.graycode
generic map ( N => N)
port map (
gcode => gcode,
nextgcode => nextgcode
);
STIMULi:
process
variable gv: std_logic_vector (N - 1 downto 0);
variable bv: std_logic_vector (N - 1 downto 0);
begin
wait for 10 ns;
for i in 0 to 2 ** N - 1 loop
bv := std_logic_vector(to_unsigned( i, bv'length));
gv(N - 1) := bv (N - 1);
for i in N - 2 downto 0 loop
gv(i) := bv(i) xor bv(i + 1);
end loop;
gcode <= gv;
bcode <= bv;
wait for 10 ns;
end loop;
wait;
end process;
end architecture;
We can see the effects of incrementing int_bcode:

Vivado synthesis: complex assignment not supported

I implemented a Booth modified multiplier in vhdl. I need to make a synthesis with Vivado but it's not possible because of this error:
"complex assignment not supported".
This is the shifter code that causes the error:
entity shift_register is
generic (
N : integer := 6;
M : integer := 6
);
port (
en_s : in std_logic;
cod_result : in std_logic_vector (N+M-1 downto 0);
position : in integer;
shift_result : out std_logic_vector(N+M-1 downto 0)
);
end shift_register;
architecture shift_arch of shift_register is
begin
process(en_s)
variable shift_aux : std_logic_vector(N+M-1 downto 0);
variable i : integer := 0; --solo per comoditÃ
begin
if(en_s'event and en_s ='1') then
i := position;
shift_aux := (others => '0');
shift_aux(N+M-1 downto i) := cod_result(N+M-1-i downto 0); --ERROR!!
shift_result <= shift_aux ;
end if;
end process;
end shift_arch;
the booth multiplier works with any operator dimension. So I can not change this generic code with a specific one.
Please help me! Thanks a lot
There's a way to make your index addressing static for synthesis.
First, based on the loop we can tell position must have a value within the range of shift_aux, otherwise you'd end up with null slices (IEEE Std 1076-2008 8.5 Slice names).
That can be shown in the entity declaration:
library ieee;
use ieee.std_logic_1164.all;
entity shift_register is
generic (
N: integer := 6;
M: integer := 6
);
port (
en_s: in std_logic;
cod_result: in std_logic_vector (N + M - 1 downto 0);
position: in integer range 0 to N + M - 1 ; -- range ADDED
shift_result: out std_logic_vector(N + M - 1 downto 0)
);
end entity shift_register;
What's changed is the addition of a range constraint to the port declaration of position. The idea is to support simulation where the default value of can be integer is integer'left. Simulating your shift_register would fail on the rising edge of en_s if position (the actual driver) did not provide an initial value in the index range of shift_aux.
From a synthesis perspective an unbounded integer requires you take both positive and negative integer values in to account. Your for loop is only using positive integer values.
The same can be done in the declaration of the variable i in the process:
variable i: integer range 0 to N + M - 1 := 0; -- range ADDED
To address the immediate synthesis problem we look at the for loop.
Xilinx support issue AR# 52302 tells us the issue is using dynamic values for indexes.
The solution is to modify what the for loop does:
architecture shift_loop of shift_register is
begin
process (en_s)
variable shift_aux: std_logic_vector(N + M - 1 downto 0);
-- variable i: integer range 0 to N + M - 1 := 0; -- range ADDED
begin
if en_s'event and en_s = '1' then
-- i := position;
shift_aux := (others => '0');
for i in 0 to N + M - 1 loop
-- shift_aux(N + M - 1 downto i) := cod_result(N + M - 1 - i downto 0);
if i = position then
shift_aux(N + M - 1 downto i)
:= cod_result(N + M - 1 - i downto 0);
end if;
end loop;
shift_result <= shift_aux;
end if;
end process;
end architecture shift_loop;
If i becomes a static value when the loop is unrolled in synthesis it can be used in calculation of indexes.
Note this gives us an N + M input multiplexer where each input is selected when i = position.
This construct can actually be collapsed into a barrel shifter by optimization, although you might expect the number of variables involved for large values of N and M might take a prohibitive synthesis effort or simply fail.
When synthesis is successful you'll collapse each output element in the assignment into a separate multiplexer that will match Patrick's
barrel shifter.
For sufficiently large values of N and M we can defined the depth in number of multiplexer layers in the barrel shifter based on the number of bits in a binary expression of the integer range of distance.
That either requires a declared integer type or subtype for position or finding the log2 value of N + M. We can use the log2 value because it would only be used statically. (XST supports log2(x) where x is a Real for determining static values, the function is found in IEEE package math_real). This gives us the binary length of position. (How many bits are required to to describe the shift distance, the number of levels of multiplexers).
architecture barrel_shifter of shift_register is
begin
process (en_s)
use ieee.math_real.all; -- log2 [real return real]
use ieee.numeric_std.all; -- to_unsigned, unsigned
constant DISTLEN: natural := integer(log2(real(N + M))); -- binary lengh
type muxv is array (0 to DISTLEN - 1) of
unsigned (N + M - 1 downto 0);
variable shft_aux: muxv;
variable distance: unsigned (DISTLEN - 1 downto 0);
begin
if en_s'event and en_s = '1' then
distance := to_unsigned(position, DISTLEN); -- position in binary
shft_aux := (others => (others =>'0'));
for i in 0 to DISTLEN - 1 loop
if i = 0 then
if distance(i) = '1' then
shft_aux(i) := SHIFT_LEFT(unsigned(cod_result), 2 ** i);
else
shft_aux(i) := unsigned(cod_result);
end if;
else
if distance(i) = '1' then
shft_aux(i) := SHIFT_LEFT(shft_aux(i - 1), 2 ** i);
else
shft_aux(i) := shft_aux(i - 1);
end if;
end if;
end loop;
shift_result <= std_logic_vector(shft_aux(DISTLEN - 1));
end if;
end process;
end architecture barrel_shifter;
XST also supports ** if the left operand is 2 and the value of i is treated as a constant in the sequence of statements found in a loop statement.
This could be implemented with signals instead of variables or structurally in a generate statement instead of a loop statement inside a process, or even as a subprogram.
The basic idea here with these two architectures derived from yours is to produce something synthesis eligible.
The advantage of the second architecture over the first is in reduction in the amount of synthesis effort during optimization for larger values of N + M.
Neither of these architectures have been verified lacking a testbench in the original. They both analyze and elaborate.
Writing a simple case testbench:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity shift_register_tb is
end entity;
architecture foo of shift_register_tb is
constant N: integer := 6;
constant M: integer := 6;
signal clk: std_logic := '0';
signal din: std_logic_vector (N + M - 1 downto 0)
:= (0 => '1', others => '0');
signal dout: std_logic_vector (N + M - 1 downto 0);
signal dist: integer := 0;
begin
DUT:
entity work.shift_register
generic map (
N => N,
M => M
)
port map (
en_s => clk,
cod_result => din,
position => dist,
shift_result => dout
);
CLOCK:
process
begin
wait for 10 ns;
clk <= not clk;
if now > (N + M + 2) * 20 ns then
wait;
end if;
end process;
STIMULI:
process
begin
for i in 1 to N + M loop
wait for 20 ns;
dist <= i;
din <= std_logic_vector(SHIFT_LEFT(unsigned(din),1));
end loop;
wait;
end process;
end architecture;
And simulating reveals that the range of position and the number of loop iterations only needs to cover the number of bits in the multiplier and not the multiplicand. We don't need a full barrel shifter.
That can be easily fixed in both shift_register architectures and has the side effect of making the shift_loop architecture much more attractive, it would be easier to synthesize based on the multiplier bit length (presumably M) and not the product bit length (N+ M).
And that would give you:
library ieee;
use ieee.std_logic_1164.all;
entity shift_register is
generic (
N: integer := 6;
M: integer := 6
);
port (
en_s: in std_logic;
cod_result: in std_logic_vector (N + M - 1 downto 0);
position: in integer range 0 to M - 1 ; -- range ADDED
shift_result: out std_logic_vector(N + M - 1 downto 0)
);
end entity shift_register;
architecture shift_loop of shift_register is
begin
process (en_s)
variable shift_aux: std_logic_vector(N + M - 1 downto 0);
-- variable i: integer range 0 to M - 1 := 0; -- range ADDED
begin
if en_s'event and en_s = '1' then
-- i := position;
shift_aux := (others => '0');
for i in 0 to M - 1 loop
-- shift_aux(N + M - 1 downto i) := cod_result(N + M - 1 - i downto 0);
if i = position then -- This creates an N + M - 1 input MUX
shift_aux(N + M - 1 downto i)
:= cod_result(N + M - 1 - i downto 0);
end if;
end loop; -- The loop is unrolled in synthesis, i is CONSTANT
shift_result <= shift_aux;
end if;
end process;
end architecture shift_loop;
Modifying the testbench:
STIMULI:
process
begin
for i in 1 to M loop -- WAS N + M loop
wait for 20 ns;
dist <= i;
din <= std_logic_vector(SHIFT_LEFT(unsigned(din),1));
end loop;
wait;
end process;
gives a result showing the shifts are over the range of the multiplier value (specified by M):
So the moral here is you don't need a full barrel shifter, only one that works over the multiplier range and not the product range.
The last bit of code should be synthesis eligible.
You are trying to create a range using a run-time varying value, and this is not supported by the synthesis tool. cod_result(N+M-1 downto 0); would be supported, because N, M, and 1 are all known at synthesis time.
If you're trying to implement a multiplier, you will get the best result using x <= a * b, and letting the synthesis tool choose the best way to implement it. If you have operands wider than the multiplier widths in your device, then you need to look at the documentation to determine the best route, which will normally involve pipelining of some sort.
If you need a run-time variable shift, look for a 'Barrel Shifter'. There are existing answers on these, for example this one.

Conversion from numeric_std unsigned to std_logic_vector in vhdl

I have a question related to conversion from numeric_std to std_logic_vector. I am using moving average filter code that I saw online and filtering my ADC values to stable the values.
The filter package code is:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package filterpack is
subtype number is unsigned(27 downto 0);
type numbers is array(natural range <>) of number;
function slv_to_num(signal slv: in std_logic_vector) return number;
procedure MAF_filter(
signal x: in number;
signal h: inout numbers;
signal y: out number
);
end filterpack;
package body filterpack is
function slv_to_num(signal slv: in std_logic_vector) return number is
variable x: number := (others => '0');
begin
for i in slv'range loop
if slv(i) = '1' then
x(i+4) := '1';
end if;
end loop;
return x;
end function slv_to_num;
procedure MAF_filter(
signal x: in number;
signal h: inout numbers;
signal y: out number
) is
begin
h(0) <= x + h(1); -- h[n] = x[n] + h[n-1]
y <= h(0) - h(h'high); -- y[n] = h[n] - h[n-M]
end MAF_filter;
end package body filterpack;
In my top level file, I call the MAF_filter procedure.
Asign_x: x <= slv_to_num(adc_dat);
Filter: MAF_filter(x,h,y);
The adc_dat is defined as:
adc_dat : out std_logic_vector (23 downto 0);
I want to convert the output of the MAF_Filter to std_logic_vector (23 downto 0). Can anyone tell how can I convert filter output 'y' to 'std_logic_vector'?
Many Thanks!
What do you want to do with the 4 extra bits? Your type number has 28 bits, but your signal adc_dat has only 24.
If it's ok to discard them, you could use:
adc_dat <= std_logic_vector(y(adc_dat'range));
Also, is there a reason not to write your function slv_to_num as shown below?
function slv_to_num(signal slv: in std_logic_vector) return number is
begin
return number(slv & "0000");
end function slv_to_num;
The conversion has to solve 2 problems : the type difference you noted, and the fact that the two words are different sizes.
The type difference is easy : std_logic_vector (y) will give you the correct type. Because the two types are related types, this is just a cast.
The size difference ... only you have the knowledge to do that.
adc_dat <= std_logic_vector(y(23 downto 0)) will give you the LSBs of Y - i.e. the value of Y itself, but can overflow. Or as Rick says, adc_dat <= std_logic_vector(y(adc_dat'range)); which is usually better, but I wanted to expose the details.
adc_dat <= std_logic_vector(y(27 downto 4)) cannot overflow, but actually gives you y/16.

Resources