首页 文章

VHDL循环 - 仅完成最后一次增量

提问于
浏览
1

我在下面的代码中遇到了for循环的问题 - 在模拟中它显示了循环的唯一最后一个增量,例如:

在我给出的输入上(显然在w0,w1,w2的8位SIGNED中):

x1 = 1; x2 = 1; w0 = -32; w1 = 63; w2 = 63

并在输出上我收到 u = 31 而不是 u = 94. 所以似乎方程是:

u = (x2 * w2) - w0

代替:

u = (x1 * w1) + (x2 * w2) - w0

我知道VHDL中的循环与C中的循环不同,但变量的使用应该可以解决问题 . 不幸的是,我错过了一些东西 . 它可能是什么?

LIBRARY IEEE;
    USE IEEE.STD_LOGIC_1164.ALL;
    USE IEEE.NUMERIC_STD.ALL;

    ENTITY NeuronBehavioral IS
         GENERIC ( n: INTEGER := 1;
                      m: INTEGER := 2;
                      b: INTEGER := 8);
        PORT ( x1  : in   STD_LOGIC;
               x2  : in   STD_LOGIC;
               w0  : in   SIGNED (b-1 downto 0); --11100000 (-32)
               w1  : in   SIGNED (b-1 downto 0); --00111111  (63)
               w2  : in   SIGNED (b-1 downto 0); --00111111  (63)
               u   : out  STD_LOGIC_VECTOR (b-1 downto 0));
    END NeuronBehavioral;

    ARCHITECTURE Behavioral OF NeuronBehavioral IS
        TYPE weights IS ARRAY (1 TO n*m) OF SIGNED(b-1 DOWNTO 0);
        TYPE inputs IS ARRAY (1 TO m) OF SIGNED(b-1 DOWNTO 0);
        TYPE outputs IS ARRAY (1 TO n) OF SIGNED(b-1 DOWNTO 0);
    BEGIN
        PROCESS (w0, w1, w2, x1, x2)
            VARIABLE weight: weights;
            VARIABLE input: inputs;
            VARIABLE output: outputs;
            VARIABLE prod, acc: SIGNED(b-1 DOWNTO 0);
        BEGIN
            input(1)  := "0000000" & x1;
            input(2)  := "0000000" & x2;
            weight(1) := w1;
            weight(2) := w2;
            L1: FOR i IN 1 TO n LOOP
                acc := (OTHERS => '0');
                L2: FOR j IN 1 TO m LOOP
                    prod := input(j)*weight(m*(i-1)+j);
                    acc := acc + prod;
                END LOOP L2;
                output(i) := acc + w0;
            END LOOP L1;
            u <= STD_LOGIC_VECTOR(output(1));
        END PROCESS;
    END Behavioral;

试验台:

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY NeuronTB IS
END NeuronTB;

ARCHITECTURE behavior OF NeuronTB IS 

    -- Component Declaration for the Unit Under Test (UUT)

    COMPONENT NeuronBehavioral
    PORT(
         x1 : IN  std_logic;
         x2 : IN  std_logic;
         w0 : IN  SIGNED(7 downto 0);
         w1 : IN  SIGNED(7 downto 0);
         w2 : IN  SIGNED(7 downto 0);
         u : OUT  std_logic_vector(7 downto 0)
        );
    END COMPONENT;


   --Inputs
   signal x1 : std_logic := '0';
   signal x2 : std_logic := '0';
   signal w0 : SIGNED(7 downto 0) := (others => '0');
   signal w1 : SIGNED(7 downto 0) := (others => '0');
   signal w2 : SIGNED(7 downto 0) := (others => '0');

    --Outputs
   signal u : std_logic_vector(7 downto 0);

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: NeuronBehavioral PORT MAP (
          x1 => x1,
          x2 => x2,
          w0 => w0,
          w1 => w1,
          w2 => w2,
          u => u
        );

   -- Stimulus process
   stim_proc: process
   begin        
      -- hold reset state for 100 ns.
      wait for 100 ns;

            x1 <= '1';
            x2 <= '1';
            w0 <= "11100000";
            w1 <= "00111111";
            w2 <= "00111111";

        wait for 100 ns;

            x1 <= '1';
            x2 <= '0';
            w0 <= "11100000";
            w1 <= "00111111";
            w2 <= "00111111";

        wait for 100 ns;

            x1 <= '0';
            x2 <= '1';
            w0 <= "11100000";
            w1 <= "00111111";
            w2 <= "00111111";


        wait for 100 ns;

            x1 <= '0';
            x2 <= '0';
            w0 <= "11100000";
            w1 <= "00111111";
            w2 <= "00111111";

      wait;
   end process;

END;

1 回答

  • 1

    该问题最初没有提供Minimal, Complete and Verifiable example,缺乏复制错误的方法,远远低于预期的结果 . 这不是实际问题的总障碍 .

    有一个超出界限的错误

    prod := input(j) * weight( m * (i - 1) + j);
    

    signed的类型的右 Watch 达式将具有被乘数( input(j) )和乘数( weight( m * (i - 1) + j) )的长度之和的长度 .

    检测通过从赋值语句中的右 Watch 达式评估投影输出波形而产生的最终有效值,标准需要针对目标的每个元素的匹配元素(参见IEEE Std 1076-2008 14.7.3.4信号更新, - 1993年至2002年12.6.2信号值的传播) .

    (当工具允许通过命令行标志或配置暂停执行此检查时,期望它在某个时刻完成,并且在消除它时性能会提高 . )

    关于不需要MCVe,一些模拟器允许运行具有顶级端口的模型 . 通过为所有输入提供默认值可以识别此问题 . 根据VHDL修订版,带有to_string的报告语句(output(1)可以显示原始引用的答案 .

    port ( 
        x1:   in   std_logic := '1'; -- default added
        x2:   in   std_logic := '1'; -- default added
        w0:   in   signed (b-1 downto 0) := to_signed(-32,b); --11100000 (-32) -- default added
        w1:   in   signed (b-1 downto 0) := to_signed(63, b); --00111111  (63)
        w2:   in   signed (b-1 downto 0) := to_signed(63, b); --00111111  (63)
        u:    out  std_logic_vector (b-1 downto 0)
    );
    

    当使用ghdl运行时,设计规范在循环L2中产生了边界失败 .

    在未标记的过程中更改prod的声明:

    variable prod: signed(b * 2 - 1 downto 0);
    

    并赋予acc:

    acc := acc + prod (b - 1 downto 0);
    

    允许计算完成,生成

    neuronbehavioral.vhdl:58:9:@ 0ms :(报告说明):u = 01011110

    在流程中添加了最后一条语句:

    report "u = " & to_string (output(1));
    

    对于非VHDL-2008兼容的模拟器,可以将to_string函数添加到process语句的声明性区域:

    function to_string (inp: signed) return string is
                variable image_str: string (1 to inp'length);
                alias input_str:  signed (1 to inp'length) is inp;
            begin
                for i in input_str'range loop
                    image_str(i) := character'value(std_ulogic'image(input_str(i)));
                end loop;
                return image_str;
            end function;
    

    请注意,报告值是94的8位有符号值 .

    还应检查prod,acc和u的声明,以确保设计能够在w0,w1和w2的输入值范围内产生结果 .

    VHDL不仅强类型,而且特别关注数学意义 . 如果结果不正确,则会出错,因此“*”运算符的乘积具有足以产生有效数学结果的长度 . 这可以在numeric_std包体中看到 .

    通过上述设计规范的补丁,测试平台可以生成:

    ghdl -r neurontb
    neuronbehavioral.vhdl:58:9:@ 0ms :(报告说明):u = 00000000
    neuronbehavioral.vhdl:58:9:@ 100ns :(报告说明):u = 01011110
    neuronbehavioral.vhdl:58:9:@ 200ns :(报告说明):u = 00011111
    neuronbehavioral.vhdl:58:9:@ 300ns :(报告说明):u = 00011111
    neuronbehavioral.vhdl:58:9:@ 400ns :(报告说明):u = 11100000

    因为 input(j) 只能是基于输入 x1x2 的"00000000"或"000000001",所以可以替代上述更改:

    prod :=  resize(input(j) * weight( m * (i - 1) + j), b);
    

    乘法器结果可以调整大小(取最低有效b长度位) . 最左边的乘数是0或1 .

    因为 input(j) 的值为0或1(作为8位有符号值),所以可以消除第一个乘法:

    architecture foo of neuronbehavioral is
        type weights is array (1 to n*m) of signed(b-1 downto 0);
        -- type inputs is array (1 to m) of signed(b-1 downto 0); -- CHANGED
        type inputs is array (1 to m) of std_logic;
        type outputs is array (1 to n) of signed(b-1 downto 0);
    begin
        process (w0, w1, w2, x1, x2)
            variable weight: weights;
            variable input: inputs;
            variable output: outputs;
            -- variable prod: signed(b * 2 - 1 downto 0);  -- RESTORED:
            variable prod: signed(b - 1 downto 0);
            variable acc: signed(b - 1 downto 0);
    
            function to_string (inp: signed) return string is
                variable image_str: string (1 to inp'length);
                alias input_str:  signed (1 to inp'length) is inp;
            begin
                for i in input_str'range loop
                    image_str(i) := character'value(std_ulogic'image(input_str(i)));
                end loop;
                return image_str;
            end function;
    
        begin
            -- input(1)  := "0000000" & x1;  -- CHANGED
            -- input(2)  := "0000000" & x2;  -- CHANGED
            input := x1 & x2;    -- ADDED
            weight(1) := w1;
            weight(2) := w2;
    l1: 
            for i in 1 to n loop
                acc := (others => '0');
    l2: 
                for j in 1 to m loop
                    if input(j) = '1' then   -- ADDED
                    -- prod := input(j) * weight( m * (i - 1) + j); -- CHANGED
                        prod :=  weight(m * (i - 1) + j);  -- ADDED
                    else                                   -- ADDED
                        prod := (others => '0');           -- ADDED
                    end if;                                -- ADDED
                    -- acc := acc + prod (b - 1 downto 0); -- RESTORED:
                    acc := acc + prod;
                end loop l2;
                output(i) := acc + w0;
            end loop l1;
            u <= std_logic_vector(output(1));
            report "u = " & to_string (output(1));
        end process;
    
    end architecture foo;
    

    对于计算 weight 的索引的第二个乘数,观察所有变量是通用常量或在循环语句中隐式声明 . 虽然后者在VHDL中的执行时动态详细说明,但在遍历每个循环语句中的顺序语句期间,它们的值被认为是静态的 .

    循环语句中的语句序列在综合中展开 . 并发语句中的等价物是通过使用for语句将各种语句复制为并发语句 . 请注意,这将需要信号(共享变量不可移植,也不保证不同的供应商工具链支持) .

    并发语句版本看起来像:

    architecture foo of neuronbehavioral is
        type weights is array (1 to n*m) of signed(b - 1 downto 0);
        type inputs is array (1 to m) of std_logic;
        type outputs is array (1 to n) of signed(b - 1 downto 0);
        signal weight: weights;
        signal input: inputs;
        signal output: outputs;
    
        function to_string (inp: signed) return string is
            variable image_str: string (1 to inp'length);
            alias input_str:  signed (1 to inp'length) is inp;
        begin
            for i in input_str'range loop
                image_str(i) := character'value(std_ulogic'image(input_str(i)));
            end loop;
            return image_str;
        end function;
    begin
        weight <= w1 & w2;
        input  <= x1 & x2;
    
    l1: 
        for i in 1 to n generate
            type accums is array (1 to m) of signed (b - 1 downto 0);
            signal accum: accums;
    
            function acc (inp: accums) return signed is
                variable retval: signed (b - 1 downto 0) := (others => '0');
            begin
                for i in accums'range loop
                    retval := retval + inp(i);
                end loop;
                return retval;
            end function;
        begin
    l2: 
            for j in 1 to m generate
                accum(j) <= weight(m * (i - 1) + j) when input(j) = '1' else
                            (others => '0');
            end generate;
            output(i) <= acc(accum) + w0;
        end generate;
    
        u <= std_logic_vector(output(1));
    
    MONITOR:
        process
        begin
            wait on x1, x2, w0, w1, w2;
            wait for 0 ns;
            wait for 0 ns;
            wait for 0 ns;
            wait for 0 ns;
            report "u = " & to_string (output(1));
        end process;
    
    end architecture foo;
    

    没有使用乘法且所有静态索引元素都是积累在两个地方 . MONITOR过程中的 wait for 0 ns; 语句是通过连续信号克服0延迟分配中的delta延迟 . (某处有某些事情正在进行离散事件,对于 x1x2 ,如果没有其他目的 . )

    这给出了与上面相同的答案:

    ghdl -r neurontb
    neuronbehavioral.vhdl:169:9:@ 100ns :(报告说明):u = 01011110
    neuronbehavioral.vhdl:169:9:@ 200ns :(报告说明):u = 00011111
    neuronbehavioral.vhdl:169:9:@ 300ns :(报告说明):u = 00011111
    neuronbehavioral.vhdl:169:9:@ 400ns :(报告说明):u = 11100000

    并代表相同的硬件 .

相关问题