您如何在 VHDL 中适当地乘以 std_logic:vector?

How do you appropriately multiply std_logic:vector in VHDL?

我正在尝试制作一个模块来操纵伺服电机 sg90。 但是我在架构的一部分上遇到了问题;该模块有一个 6 位的条目,它控制我想要伺服电机的位置,但用 16 位矢量控制电机。我这样做的方法是乘以一个 6 位变量(与条目具有相同的值)并将其放在 16 位输出向量上,如下所示:

case position is
    when "000000" =>
        value:= X"0ccc";
    when "111111" =>
        value := X"1999";
    when others =>
        value:=std_logic_vector((control*52)+3276);
end case; 

这应该做的是,例如,如果我输入“000000”,输出将是“0ccc”,将伺服电机置于其起始位置。 “111111”将是“1999”或结束位置结束之间的所有其他内容都应由该表达式考虑。但是,我收到以下错误:

Error (10327): VHDL error at ServomotorF.vhd(46): can't determine definition of operator ""*"" -- found 0 possible definitions

如果有帮助,我使用的库是

use ieee.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;

我也尝试过使用 numeric_std,但这只会给我带来更多错误。 我能想到的唯一其他解决方案是使用巨大的案例结构一个一个地做。

如果我使用“unsigned”,我会收到多个 unsigned 定义的错误。

它的数学原理很简单:

value_out <= value_in * STEP_SIZE + MIN_VALUE_OUT;

但 VHDL 需要更多的努力,本质上:

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

以下是 VHDL 中定标器的完整实现。 V1 计算 VHDL 中的缩放值,V2 从查找中选择缩放值 table 由编译器预先计算。

规模

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;

模拟Scale.V2

Scale.V2 的 RTL

Post 比例映射.V2

FPGA 综合比较

架构V1

  • 25 个具有定点运算的逻辑元素。
  • 没有查找 table。
  • STEP_SIZE 是自然类型。
    • V1a:整数。
    • V1b:定点数。
  • 可变舍入误差取决于定点运算的备用位数,例如19 个带有 OP 值的备用位。

架构 V2

  • 16个逻辑元素。 Quartus 在编译几次后对设计进行了更多优化。原来用了54个逻辑元素。
  • 使用查找 table。
  • STEP_SIZE 是实数类型。
  • 更小的舍入误差。