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