1
votes

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.

1
You don't show full code. Can't see the type of control. Also std logic arith and std logic unsigned are not standard vhdl libraries. Numeric_std would be the standard library of choice. Plus numeric_std_unsigned from vhdl2008 allows arithmetic on std logic vectors. - Tricky
Your problems with no name unsigned being visible is due multiple declarations of types with that being potentially visible from multiple use clauses. IEEE Std 1076-2008 12.4 Use clauses para 8 c). Either use IEEE or Synopsys arithmetic packages not both. Provide a minimal reproducible example. - user1155120
64 sixteen bit values is hardly a giant case structure. Without knowing the type of position it could also be an array of constants in a look up table. type sixto16 is array (0 to 63) of unsigned(15 downto 0); constant cvt6_16: sixto16 := (x"CCC, ...x"1999); and value := cvt6_16(to_integer(position)); The aggregate value expression for the constant can be produced by a function call without writing case statement alternatives. - user1155120

1 Answers

0
votes

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

Simulation of Scale.V2

RTL of Scale.V2

RTL of Scale.V2

Post Mapping of Scale.V2

Post mapping of Scale

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.