Vivado synthesis: complex assignment not supported - vhdl

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.

Related

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;

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

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

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:

Implementing a VHDL binary search on a std_logic_vector [vhdl]

I'm attempting to create synthesizable VHDL (function or procedure) for an ASIC (it must be part of the ASIC) that will look for the first '1' in a standard_logic_vector and output which vector position that '1' was in. For example, I have an 8-bit slv of "10001000" (a '1' in position 3 and 7). If I use this slv, the output should be 4 (the output is 1 based).
The actual VHDL will be searching a large slv, up to 512 bits in length. I tried implementing a binary search function but I get synthesis errors that states "Could not synthesize non-constant range values. [CDFG-231] [elaborate]
The non-constant range values are in file '...' on line 61" I indicated in the code below where it complains. I'm not sure how to implement a binary search algorithm without having non-constant range values. How would I modify this code so it's synthesizable?
I have attempted to search for binary search algorithms for HDL for potential code to look at and for my error, but I didn't find anything.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_misc.all;
entity bin_search is
generic (
constant NREGS : positive := 16 -- number of registers
);
port (
clk_i : in std_logic; -- clock
bin_i : in unsigned( NREGS-1 downto 0 ); -- input
en_i : in std_logic; -- input enable
addr_o : out natural range 0 to NREGS -- first binary location
);
end bin_search;
architecture rtl of bin_search is
function f_bin_search( input: unsigned; nob: positive ) return natural is
constant nbits : positive := 2**nob;
variable lower : natural range 0 to 1 := 0;
variable upper : natural range 0 to 1 := 0;
variable idx : natural range 0 to nob := 4;
variable cnt : natural range 0 to nbits := 0;
variable mid : positive range 1 to nbits := nbits/2; --
variable ll : natural range 0 to nbits := 0;
variable ul : positive range 1 to nbits := nbits; --
begin
if input = 0 then
cnt := 0;
return cnt;
else
loop1: while ( idx > 0 ) loop
if ( input( mid-1 downto ll ) > 0 ) then -- <===WHERE SYNTH COMPLAINS
lower := 1;
else
lower := 0;
end if;
if ( input( ul-1 downto mid ) > 0 ) then
upper := 1;
else
upper := 0;
end if;
if ( idx = 1 ) then
if ( lower = 1 ) then
cnt := mid;
else
cnt := ul;
end if;
elsif ( lower = 1 ) then
ul := mid;
mid := ( ( ll+ul )/2 );
elsif ( upper = 1 ) then
ll := mid;
mid := ( ll+ul )/2;
else
cnt := 0;
exit loop1;
end if;
idx := idx-1;
end loop loop1;
return cnt;
end if;
end f_bin_search;
begin
test_proc: process ( clk_i )
begin
if rising_edge( clk_i ) then
if en_i = '1' then
addr_o <= f_bin_search( bin_i, 4 );
end if;
end if;
end process test_proc;
end rtl;
Here's a simple test bench where the input is inc'd by '1'. The addr_o should be the location (1 based) of the input lsb with a '1'.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_misc.all;
use ieee.numeric_std.all;
entity bin_search_tb is
end bin_search_tb;
architecture behavior of bin_search_tb is
constant NREGS : positive := 16;
signal clk : std_logic;
signal input : unsigned( NREGS-1 downto 0 );
signal start : std_logic;
signal addr : natural range 0 to NREGS;
constant clk_per : time := 1 ns;
signal row : natural range 0 to 2**NREGS-1;
begin
bin_search_inst: entity work.bin_search( rtl )
generic map (
NREGS => NREGS
)
port map (
clk_i => clk, -- master clock
bin_i => input, -- captured events
en_i => start, -- start binary search
addr_o => addr -- addr where the first '1' appears
);
-- master clock process
clk_proc: process
begin
clk <= '0';
wait for clk_per / 2;
clk <= '1';
wait for clk_per / 2;
end process clk_proc;
--
stim1_proc: process
begin
input <= ( others => '0' );
start <= '0';
row <= 1;
wait until clk'event and clk = '1';
loop
wait until clk'event and clk = '1';
input <= to_unsigned( row, input'length );
start <= '1';
wait until clk'event and clk = '1';
start <= '0';
wait for 4*clk_per;
row <= row+1;
end loop;
end process stim1_proc;
end architecture behavior;
Thanks for your assistance!
-Jason
Edited code and added a testbench
Your design will most certainly depend on latency and other performance requirements, but, you could use some combination of or-reduction, sequencers (for mux selection of sliced vectors), shift register, and counters. I drew up a simple circuit that should find your lsb instance of "1" in ~30 clock cycles
The RTL translation that implements this design should be straight forward.
You say that you are thinking in hardware, but in fact you're not. Or you are misleading yourself.
input( mid-1 downto ll ) > 0
is not an OR-reduction, but a comparison operation. You must know > is the larger than comparison operator. The synthesis will therefor infer a comparator. But how many inputs must that comparator have, I ask? Well, there's your problem: it depends on the value of mid, which:
initially depends on the value of nbits, which depends on the value of nob which is a variable input for the function.
is changed within the loop. Thus it's value is not constant.
A hardware component cannot have a variable amount of wires.
But why do you want binary search? Why not keep-it-simple?
library ieee;
use ieee.std_logic_1164.all;
entity detect_one is
generic(
input_size : positive := 512);
port(
input : in std_logic_vector (input_size-1 downto 0);
output : out natural range 0 to input_size);
end entity;
architecture rtl of detect_one is
begin
main: process(input)
begin
output <= 0;
for i in input_size-1 downto 0 loop
if input(i)='1' then
output <= i+1;
end if;
end loop;
end process;
end architecture;
entity detect_one_tb is end entity;
library ieee;
architecture behavior of detect_one_tb is
constant input_size : positive := 512;
use ieee.std_logic_1164.all;
signal input : std_logic_vector (input_size-1 downto 0) := (others => '0');
signal output : integer;
begin
DUT : entity work.detect_one
generic map ( input_size => input_size )
port map(
input => input,
output => output);
test: process begin
wait for 1 ns;
assert (output = 0) report "initial test failure" severity warning;
for i in 0 to input_size-1 loop
input <= (others => '0');
input(i) <= '1';
wait for 1 ns;
assert (output = i+1) report "single ones test failure" severity warning;
end loop;
input <= (others => '1');
wait for 1 ns;
assert (output = 1) report "initial multiple ones test failure" severity warning;
for i in 0 to input_size-2 loop
input(i) <= '0';
wait for 1 ns;
assert (output = i+2) report "multiple ones test failure" severity warning;
end loop;
wait;
end process;
end architecture;

Booth Multiplication Algorithm

I'm new to VHDL and am trying to code up Booth's Multiplication Algorithm. I'm using XILINX and when I synthesize my code, I end up with a lot of warnings:
Upper is assigned but never used,
Product is used but never assigned,
LowerPrevLSB is assigned but never used,
Lower is assigned but never used,
A_2sComp is assigned but never used,
Z has a constant value of 0,
Product has a constant value of 0.
I thought I assigned and wrote the code correctly, but evidently I am not. Any advice and help would be appreciated.
library IEEE;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_arith.ALL;
use IEEE.STD_LOGIC_unsigned.ALL;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
-- X * Y = Z
entity BoothMultiplier is
generic
(
numBits_X : integer := 8;
numBits_Y : integer := 8
);
port
(
CLK : in std_logic;
X : in std_logic_vector((numBits_X - 1) downto 0);
Y : in std_logic_vector((numBits_Y - 1) downto 0);
Z : out std_logic_vector((numBits_X + numBits_Y - 1) downto 0)
);
end BoothMultiplier;
architecture Behavioral of BoothMultiplier is
-- Two's Complement Function
function TwosComplement(inputNum : std_logic_vector) return std_logic_vector;
function TwosComplement(inputNum : std_logic_vector) return std_logic_vector is
variable temp : std_logic_vector(inputNum'range);
begin
temp := (not inputNum) + 1;
return temp;
end TwosComplement;
-- End Two's Complement Function
-- MIN Function
function MIN(Left, Right : integer) return integer;
function MIN(Left, Right : integer) return integer is
begin
if Left < Right then return Left;
else return Right;
end if;
end Min;
-- End MIN Function
-- MAX Function
function MAX(Left, Right : integer) return integer;
function MAX(Left, Right : integer) return integer is
begin
if Left > Right then return Left;
else return Right;
end if;
end MAX;
-- End MAX Function
-- Signals
signal Upper : std_logic_vector(MAX(numBits_X, numBits_Y) - 1 downto 0)
:= (others => '0');
signal Lower : std_logic_vector(MIN(numBits_X, numBits_Y) - 1 downto 0)
:= (others => '0');
signal LowerPrevLSB : std_logic := '0';
signal Product : std_logic_vector(numBits_X + numBits_Y - 1 downto 0)
:= (others => '0');
signal A, A_2sComp : std_logic_vector(MAX(numBits_X, numBits_y) - 1 downto 0)
:= (others => '0');
signal counter : integer := 0;
-- End Signals
begin
assert Z'length = (X'length + Y'length) report "Bad Product Length" severity failure;
Lower <= X when (numBits_X <= numBits_Y) else Y;
A <= X when (numBits_X > numBits_Y) else Y;
A_2sComp <= TwosComplement(A);
process(CLK)
begin
if rising_edge(CLK) then
if (Lower(0) = '0' and LowerPrevLSB = '1') then
Upper <= Upper + A;
elsif (Lower(0) = '1' and LowerPrevLSB = '0') then
Upper <= Upper + A_2sComp;
end if;
LowerPrevLSB <= Lower(0);
Product <= Upper & Lower;
for i in 0 to Product'length - 2 loop
Product(i) <= Product(i+1);
end loop;
Product(Product'length-1) <= Product(Product'length-1);
Upper <= Product(Product'length - 1 downto MIN(numBits_X, numBits_Y));
Lower <= Product(MIN(numBits_X, numBits_Y) - 1 downto 0);
counter <= counter + 1;
if (counter = MIN(numBits_X, numBits_Y)) then
Z <= Product;
end if;
end if;
end process;
end Behavioral;
In VHDL, successive assignments to the same signal in a process overrides previous assignments, thus:
if (Lower(0) = '0' and LowerPrevLSB = '1') then
Upper <= Upper + A;
elsif (Lower(0) = '1' and LowerPrevLSB = '0') then
Upper <= Upper + A_2sComp;
end if;
...
Upper <= Product(Product'length - 1 downto MIN(numBits_X, numBits_Y));
The first assignments, in the if block, is completely ignored. If you look at your code, assignments to Product, Upper and Lower are overridden.
I suggest you simulate your design before synthesizing your design with Xilinx. It will be much easier to test and debug. For instance, your counter signal is never reset, and will count up to 2^31-1, then wrap to -2^31. What will happen to your design in those cases? Simulation would point out these error easily, leave synthesis for later!

Resources