vhdl中的算术运算。如何将std_logic向量乘以实数?

时间:2018-03-20 09:28:21

标签: vhdl

对于学校教程,我需要创建一个在0到1000区间内接收整数值的组件。输出返回S = V * C,其中C取决于:

    C在[0,10] 范围内时,
  • V = 1 当C在[11,100]
  • 范围内时,
  • V = 0.75 当C在范围[101,1000]
  • 时,
  • V = 0.3125

我尝试了下面的代码,但它没有编译。我猜,我的类型有问题。如何编制real号码以与std_logic_vector相乘?

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity comp1 is
    port(
        V: in std_logic_vector(9 downto 0);
        S:out std_logic_vector(13 dowto 0));
end comp1;

architecture syn of comp1 is
begin
    process is
        variable c: unsigned(4 downto 0);
        variable r: unsigned(13 downto 0);
    begin
        if unsigned(V) < 11 then
            c:=1;
        elsif unsigned(V) < 101 then
            c:=0.75;
        elsif others =>
            c:=0.3125;
        end if;
        r:=unsigned(V)*C;
        S<=std_logic_vector(r(13 downto 0));
    end process;
end architecture;

3 个答案:

答案 0 :(得分:0)

您的问题并不完全清楚:您需要什么代码?根据您的答案,实际上有多种解决方案。

先前问题:表示和舍入

正如您已经发现的,看到您使用numeric_stdstd_logic_vector本身不代表任何值。它只是一个std_logic元素的数组。因此,您不应该执行裸std_logic_vector的任何操作。 现在,通过将向量转换为unsigned类型,可以将其定义为表示(无符号)整数值。但现在你得到的问题是整数不能代表分数。那么整数是0.1?这很容易。但是整数是0.9?那么,这取决于你的舍入计划。如果您只是截断数字,则答案再次为0。但是使用标准舍入(+0.5),答案是1.你没有告诉我们你想要什么样的舍入方案(我不知道你是否想过它)

P.S。为什么你的S 14位宽?如果V是10位且最大因子是1​​.0,那么S也只需要10位......

实现

让我们先定义一个实体

library ieee;
use ieee.std_logic_1164.all;

entity comp1 is
    port(
        V : in  std_logic_vector(9 downto 0);
        S : out std_logic_vector(9 downto 0)
        );
end entity;

实际操作

您可以将所有内容转换为浮点(real)并执行操作。这将解决你的所有四舍五入,你有很大的自由。问题是合成中不支持real类型。不过,对于测试来说,它的工作原理应该是

architecture behavior of comp1 is
    signal V_real, S_real : real;
    use ieee.numeric_std.all;
begin
    V_real <= real(to_integer(unsigned(V)));

    S_real <=
        V_real when V_real <= 10.0 else
        V_real * 0.75 when V_real <= 100.0
        else V_real * 0.3125;

    S <= std_logic_vector(to_unsigned(integer(S_real), S'length));
end architecture;

定点

借助VHDL-2008,他们试图通过引入可合成的定点包来弥合不具有合成点表示的问题。使用这些包时,您甚至可以选择所需的舍入方案。这是因为舍入需要额外的资源,并不总是必要的。警告:使用这些包需要一些时间来适应。

architecture behavior of comp1 is
    use ieee.fixed_pkg.all;
    signal V_fix : ufixed(9 downto 0);
    signal C : ufixed(0 downto -15);
    signal S_fix : ufixed(10 downto -15); -- range of V*C+1
    use ieee.numeric_std.all;
begin
    V_fix <= to_ufixed(V, V_fix);

    C <= to_ufixed(1, C) when V_fix <= 10 else
         to_ufixed(0.75, C) when V_fix <= 100 else
         to_ufixed(0.3125, C);

    S_fix <= V_fix * C;

    S <= std_logic_vector(to_unsigned(S_fix, S'length));
end architecture;

P.S。如上所述,您需要在VHDL-2008模式下进行编译才能使其正常工作。

整数算术

如果你看一下乘法因子,你会发现它们可以用分数表示:

  • 0.75 = 3/4
  • 0.3125 = 5/16

这意味着你可以简单地使用整数运算来执行缩放。

architecture behavior of comp1 is
    signal V_int, S_int : integer range 0 to 1000;
    use ieee.numeric_std.all;
begin
    V_int <= to_integer(unsigned(V));

    S_int <=
        V_int when V_int <= 10 else
        V_int*3/4 when V_int <= 100 
        else V_int*5/16;

    S <= std_logic_vector(to_unsigned(S_int, S'length));
end architecture;

NB 整数算术没有舍入方案,因此数字被截断!

低级优化:Shift-and-add

Brian在评论中提到了使用shift和add操作。回到我的答案的整数算术部分,你会看到分母实际上是2的幂,可以使用位移操作来实现

  • x / 4 = x /(2 ^ 2)= x&gt;&gt; 2(右移2)
  • x / 16 = x /(2 ^ 4)= x&gt;&gt; 4(右移4)

同时,也可以使用bitshift和add操作来实现分子

  • x * 3 = x *“11”b =&gt; x + x <1(左移1)
  • x * 5 = x *“101”b =&gt; x + x <&lt; 2(左移2)

两者都可以合并为一个操作。注意,虽然你必须记住,左移会丢掉移出的位。这可能会导致问题,因为正确结果需要值的分数。因此,您需要添加位来计算中间结果。

architecture behavior of comp1 is
    use ieee.numeric_std.all;
    signal V_uns4, S_uns4 : unsigned(13 downto 0); -- add 4 bits for correct adding
begin
    V_uns4 <= resize(unsigned(V),V_uns4'length);

    S_uns4 <=
        shift_left(V_uns4,4) when V_uns4 <= 10 else
        shift_left(V_uns4,3) + shift_left(V_uns4,2) when V_uns4 <= 100 -- "11" >> 2
        else shift_left(V_uns4,2) + V_uns4; --"101" >> 4

    S <= std_logic_vector(resize(shift_right(S_uns4,4),S'length));
end architecture;

这种方法可能需要最少的合成资源。但确实需要进行低级优化,这需要额外的设计工作。

的测试平台

以下是我测试代码的方式

entity comp1_tb is end entity;

library ieee;

architecture tb of comp1_tb is
    use ieee.std_logic_1164.all;
    signal V,S : std_logic_vector(9 downto 0);
    use ieee.numeric_std.all;
    signal V_int, S_int : integer range 0 to 1000;
begin
    DUT: entity work.comp1
        port map(
            V => V,
            S => S);

    V <= std_logic_vector(to_unsigned(V_int, V'length));
    S_int <= to_integer(unsigned(S));

    V_stim : process begin
        V_int <= 1;
        wait for 1 ns;
        assert (S_int = 1) report "S should be 1*1 = 1;" severity warning;
        V_int <= 10;
        wait for 1 ns;
        assert (S_int = 10) report "S should be 10*1 = 10;" severity warning;
        V_int <= 100;
        wait for 1 ns;
        assert (S_int = 75) report "S should be 100*0.75 = 75;" severity warning;
        V_int <= 1000;
        wait for 1 ns;
        assert (S_int = 312 OR S_int = 313) report "S should be 1000*0.3125 = 312 or 313;" severity warning;
        wait;
    end process;
end architecture;

答案 1 :(得分:0)

我需要从表中选择C,所以我不能只写V_int * 3/4。这是我的问题我无法避免调用C变量或smth并在进程中分配值,而且,在/ if / case部分之后,我必须将V和C相乘。

答案 2 :(得分:-2)

  1. 将V信号添加到过程灵敏度列表中;
  2. 使用移位和添加而不是直接乘法,如Brian所说。