How do you appropriately multiply std_logic:vector in VHDL? - vhdl

I'm trying to make a module to manipulate a servomotor sg90.
But I'm having problems with a part of the architecture; the module has an entry of 6 bits which controls where I want the servomotor to be at, but controls the motor with a 16bit vector. My way of doing this was multiplying a variable of 6 bits (that has the same value as the entry) and putting that on the 16bit out vector, something like this:
case position is
when "000000" =>
value:= X"0ccc";
when "111111" =>
value := X"1999";
when others =>
value:=std_logic_vector((control*52)+3276);
end case;
What this should do is, for instance, if I put "000000" the out would be "0ccc", putting the servomotor on its start position. "111111" would be "1999" or the end position end everything else in between should be considered by that expression. But, I'm getting the following error:
Error (10327): VHDL error at ServomotorF.vhd(46): can't determine definition of operator ""*"" -- found 0 possible definitions
If it helps, the libraries I'm using are
use ieee.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;
I also tried using numeric_std but that just gives me more errors.
The only other solution I can think of is doing one by one using a giant case structure.
if I use "unsigned" I get the error of multiple definitions of unsigned.

The mathematics of it is simple:
value_out <= value_in * STEP_SIZE + MIN_VALUE_OUT;
But VHDL requires a bit more effort, in essence:
constant MIN_VALUE_IN: natural := 0;
constant MAX_VALUE_IN: natural := 16#3F#;
constant MIN_VALUE_OUT: natural := 16#0CCC#;
constant MAX_VALUE_OUT: natural := 16#1999#;
constant STEP_SIZE: natural := natural(floor(real(MAX_VALUE_OUT - MIN_VALUE_OUT) / real(MAX_VALUE_IN - MIN_VALUE_IN))); -- Beware of rounding errors.
signal std_in, std_out: std_logic_vector(5 downto 0);
signal value_in, value_out: natural;
value_in <= to_integer(unsigned(std_in));
value_out <= value_in * STEP_SIZE + MIN_VALUE_OUT;
std_out <= std_logic_vector(to_unsigned(value_out, std_out'length));
Below is the full implementation of a scaler in VHDL. V1 calculates the scaled value in the VHDL, and V2 selects scaled values from a look up table pre-calculated by the compiler.
Scale
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity Scale is
generic
(
MIN_VALUE_IN: natural := 0;
MAX_VALUE_IN: natural := 16#3F#;
MIN_VALUE_OUT: natural := 16#0CCC#;
MAX_VALUE_OUT: natural := 16#1999#
);
port
(
value_in: in natural range MIN_VALUE_IN to MAX_VALUE_IN;
value_out: out natural range MIN_VALUE_OUT to MAX_VALUE_OUT
);
end entity;
architecture V1 of Scale is
constant RANGE_IN: natural := MAX_VALUE_IN - MIN_VALUE_IN;
constant RANGE_OUT: natural := MAX_VALUE_OUT - MIN_VALUE_OUT;
-- V1a
--constant STEP_SIZE: natural := natural(floor(real(RANGE_OUT) / real(RANGE_IN))); -- Beware of rounding errors.
-- V1b
-- Use the spare bits in the natural range for fixed point arithmetic.
constant NATURAL_BITS: natural := natural(log2(real(natural'high))); -- 31
constant USED_BITS: natural := natural(ceil(log2((real(RANGE_OUT) / real(RANGE_IN) * real(MAX_VALUE_IN)))));
constant SPARE_BITS: natural := NATURAL_BITS - USED_BITS; -- 19
constant MULT: real := 2.0**SPARE_BITS;
constant DIV: natural := natural(MULT);
constant HALF: natural := DIV / 2; -- For rounding off the fixed point number.
constant STEP_SIZE: natural := natural(floor(real(RANGE_OUT) * MULT / real(RANGE_IN))); -- Convert to a fixed point number. Accuracy depends on the number of spare bits. Beware of rounding errors.
begin
-- V1a
--value_out <= (value_in - MIN_VALUE_IN) * STEP_SIZE + MIN_VALUE_OUT;
-- V1b
value_out <= ((value_in - MIN_VALUE_IN) * STEP_SIZE + HALF) / DIV + MIN_VALUE_OUT; -- Convert fixed point to natural.
end architecture;
architecture V2 of Scale is
subtype TScaledValue is natural range MIN_VALUE_OUT to MAX_VALUE_OUT;
type TScaledValues is array(MIN_VALUE_IN to MAX_VALUE_IN) of TScaledValue;
function GetScaledValues return TScaledValues is
variable result: TScaledValues;
constant STEP_SIZE: real := real(MAX_VALUE_OUT - MIN_VALUE_OUT) / real(MAX_VALUE_IN - MIN_VALUE_IN);
begin
for i in TScaledValues'range loop
result(i) := natural(real(i - MIN_VALUE_IN) * STEP_SIZE) + MIN_VALUE_OUT;
end loop;
return result;
end function;
constant SCALED_VALUES: TScaledValues := GetScaledValues;
begin
value_out <= SCALED_VALUES(value_in);
end architecture;
Test Bench
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Scale_TB is
end entity;
architecture V1 of Scale_TB is
constant SYS_CLOCK_FREQ: real := 100000000.0; -- Hz
constant SYS_CLOCK_PERIOD: time := 1.0 sec / SYS_CLOCK_FREQ;
signal halt_sys_clock: boolean := false;
signal sys_clock: std_logic := '0';
constant MIN_VALUE_IN: natural := 0;
constant MAX_VALUE_IN: natural := 16#3F#;
constant MIN_VALUE_OUT: natural := 16#0CCC#;
constant MAX_VALUE_OUT: natural := 16#1999#;
--constant MAX_VALUE_OUT: natural := 7700; -- To see effect of rounding errors for Scale architecture V1.
signal position: natural range MIN_VALUE_IN to MAX_VALUE_IN;
signal servo_pos: natural range MIN_VALUE_OUT to MAX_VALUE_OUT;
signal servo_pos_slv: std_logic_vector(15 downto 0);
component Scale is
generic
(
MIN_VALUE_IN: natural := 0;
MAX_VALUE_IN: natural := 16#3F#;
MIN_VALUE_OUT: natural := 16#0CCC#;
MAX_VALUE_OUT: natural := 16#1999#
);
port
(
value_in: in natural range 0 to 63;
value_out: out natural range MIN_VALUE_OUT to MAX_VALUE_OUT
);
end component;
begin
SysClockGenerator: process
begin
while not halt_sys_clock loop
sys_clock <= '1';
wait for SYS_CLOCK_PERIOD / 2.0;
sys_clock <= '0';
wait for SYS_CLOCK_PERIOD / 2.0;
end loop;
wait;
end process SysClockGenerator;
StimulusProcess: process
begin
for i in MIN_VALUE_IN to MAX_VALUE_IN loop
position <= i;
wait for SYS_CLOCK_PERIOD;
end loop;
wait for SYS_CLOCK_PERIOD;
halt_sys_clock <= true;
wait;
end process;
DUT: Scale
generic map
(
MIN_VALUE_IN => MIN_VALUE_IN,
MAX_VALUE_IN => MAX_VALUE_IN,
MIN_VALUE_OUT => MIN_VALUE_OUT,
MAX_VALUE_OUT => MAX_VALUE_OUT
)
port map
(
value_in => position,
value_out => servo_pos
);
servo_pos_slv <= std_logic_vector(to_unsigned(servo_pos, servo_pos_slv'length));
end architecture;
Simulation of Scale.V2
RTL of Scale.V2
Post Mapping of Scale.V2
Synthesis Comparison for FPGA
Architecture V1
25 logic elements with fixed point arithmetic.
No look-up table.
STEP_SIZE is of type natural.
V1a: Whole number.
V1b: Fixed point number.
Variable rounding errors depending on number of spare bits for fixed point arithmetic, e.g. 19 spare bits with OP's values.
Architecture V2
16 logic elements. Quartus optimised the design a bit more after compiling it a few times. Originally used 54 logic elements.
Uses a look-up table.
STEP_SIZE is of type real.
Smaller rounding errors.

Related

VHDL: need to calculate float variables in VHDL

I am new to VHDL, for my project I need to use float value. I have used variable data type in my design. To have float value I can use real data type, but real data type is not synthesizable. In the below I have given my code. In cA part I am dividing it by 2, where I need to have float number. But the vivado software is rounding off the number. Also, after cA part, I need to multiply cA with sqrt 2, which is a float number.
After the calculation I need to send these data through AXI peripheral.
Is there any solution where I can have float number in my VHDL code?
Thanks a lot.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.numeric_std.signed;
use IEEE.MATH_REAL.ALL;
entity sinewave_dwt is
Port ( clk : in STD_LOGIC;
dataout : out integer range 0 to 127);
end sinewave_dwt;
architecture Behavioral of sinewave_dwt is
signal cA_A : integer :=0;
signal cD_D : integer :=0;
signal i : integer :=0;
begin
process(clk)
variable cA : integer :=0;
variable cD : integer :=0;
type memory_type is array (0 to 7) of integer range 0 to 127;
variable sine : memory_type :=(4,6,10,12,8,6,5,6);
begin
if(rising_edge(clk)) then
cA := sine(i) + sine(i+1);
cA := cA/2;
cA_A <= cA;
report "cA: " & integer'image(cA);
cD := sine(i) - sine(i+1);
cD := cD/2;
cD_D <= cD;
report "cD: " & integer'image(cD);
i <= i + 2;
if(i > 7) then
cA := 0;
cD := 0;
i <= 0;
end if;
end if;
end process;
end Behavioral;
Vhdl cannot synthesize floating numbered codes because to be able to work with floating numbers, either you need to design a FPU (floating point unit) or just work with fixed point integer arithmetic. You need to determine your minimum and maximum numbers you are working. For example, if your range is 0.01 and 30, then convert minimum floating number you work to integer number by multiplying it 100. Then, your new range will be 100*(0.01-30) => 1-3000. If you want to 5/2 operation, this means 500/2 = 250.

Constant initialisation from a user-made function synthesis takes forever but easily created in simulation

I have created a function "my_func" in a package which when inputted with x produced a matrix of integers of shape [log2(x), x]. I wish to place this slice into ROM memory for synthesis.
For the sake of synthesis I attatch a counter to the ROM and read out one register per clk cycle. I have been able to simulate this and get the expected answers. The synthesis of the code never finishes, even when the generic controling the function is very small as shown in the code.
I am confused as to why Vivado can very quickly simulate the desired design but takes forever to synthesise. I get no errors from vivado saying that the design is unsynthesisable.
Has anyone else experienced this problem, and what are the steps I can take to avoid the problem in the future?
Please see my synthesis code below and a snippet of the function code below
For the record for this question Synthesis = Elaborate Design (RTL)
library IEEE;
library WORK;
use WORK.mylib.all;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use IEEE.math_real.all;
entity try is
generic(
x : integer := 8
);
port(
clk : in std_logic;
ouput: out integer;
);
end entity try;
architecture v1 of try is
constant sig : my_matrix_of_integers(0 to integer(log2(real(x)))-1, 0 to x-1) := my_func(x);
signal counter : unsigned(integer(log2(real(x)))-1 downto 0);
begin
process(clk)
begin
if rising_edge(clk) then
output <= sig(0, to_integer(counter));
counter <= counter + 1;
end if;
end process;
end architecture v1;
Here is a snippet of my function code
function my_func (x: integer) return mat_t is
variable y: integer := integer(log2(real(x)));
variable cluster : integer;
variable index : integer;
variable mat : my_matrix_of_integers(0 to y-1, 0 to x-1);
begin
for s in 0 to y-1 loop
index := x/(2**(s+1));
cluster := x/index;
for c in 0 to cluster - 1 loop
for i in 0 to index -1 loop
if c mod 2 = 0 then
mat(s, (c*index) + i) := 0;
else
mat(s, (c*index) + i) := i*(2**(s));
end if;
end loop;
end loop;
end loop;
return mat;
end function my_func;
and type ...
type my_matrix_of_integers is array(integer range <>, integer range <>) of integer;

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.

How to solve the division between variables trouble on vhdl?

I'm working with a function generator project based on the DDS method (Direct Digital Synthesis) to implement in a Basys 2, But when I run the Synthesize I get an
ERROR ("ERROR:Xst:769 - "C:/Users/dell6410/Desktop/DDS VHDL/DDS_VHDL/triangularwave.vhd"
line 55: Operator <DIVIDE> must have constant operands or first operand must be power of 2")`
The error is caused by a division between two variable integers and this does not allow me to generate the programming file. Does someone know a solution?
n :in integer;
variable count:integer:=0;
op <= count*255 / n;
Copying from the Xilinx support document (page 183), a divider can be implemented only when,
The divisor is constant and a power of 2.
Clearly, your n is not a constant. In such divisions, it's difficult for hardware to determine the resule efficiently in one cycle.
In a general purpose divider, it's better you implement the division as a process subtracting the number every clock instance.
signal quotient: integer := 0;
signal remainder: integer := 0;
signal division_complete: std_logic := '0';
-- Begin architecture
process(clk, dividend, divisor, quotient, remainder)
variable n_quotient: integer := 0;
variable n_remainder: integer := 0;
variable n_division_complete: std_logic := '0';
begin
n_quotient := quotient;
n_remainder := remainder;
n_division_complete := '0';
if (dividend < divisor) then
-- End of division
n_division_complete := '1';
elif (division_complete = '1') then
-- Start of next division
n_division_complete := '1';
n_quotient := 0;
else
-- Division in progress
n_quotient := n_quotient + 1;
n_remainder := dividend - divisor;
end if;
if (clk'event and clk = '1') then
quotient <= n_quotient;
remainder <= n_remainder;
division_complete <= n_division_complete;
end if;
end process;
In case a synchronous solution as above is not feasible, you will need to constraint your inputs to the Xilinx conditions as specified above. In case your division is a simple divide by a power of 2, you could use the >> (right shift) operator.

VHDL: Converting from floating point to fixed point explanation?

In the Designer's Guide to VHDL in Chapter 6.2 there is an entity and architecture body for a converter from floating point to fixed point representation. I'm confused by it
library ieee; use ieee.std_logic_1164 all;
entity to_fp is
port(vec: in std_u_logic_vector(15 downto 0);
r: out real);
end entity to_fp;
architecture behavioral of to_fp is
begin
behavior : process (vec) is
variable temp: bit_vector(vec'range);
variable negative: boolean;
variable int_result: integer;
begin
temp := to_bitvector(vec);
negative := temp(temp'left) = '1';
if negative then
temp := not temp;
end if;
int_result := 0;
for index in vec'range loop
int_result := int_result*2 + bit'pos(temp(index));
end loop;
if negative then
int_result := (-int_result) -1;
end if;
r <= real(int_result) / 2.0**15;
end process behavior;
end architecture behavioral;
I understand most of it. I just don't understand the for loop. How does this give us the integer representation of the bit vector? Please explain in as much detail as possible, Thanks :) .
for index in vec'range loop
This loops over the range of vec. In this case this (15 downto 0).
bit'pos(temp(index));
bit is an enumaration type (type BIT is ('0', '1'); in std.standard). The pos attribute returns the position number (as an integer type) of the given value. So bit'pos(...) converts a bit to an integer.
So what the loop does is convert a bit_vector to an integer.
I recommend using to_integer(unsigned(vec)) for this purpose, though. Remember to use ieee.numeric_std.all;.
The last line converts (casts) the integer to a real.

Resources