获取最后 512 个值中最大值的更节省资源的方法

More resource efficient way to get the maximum of the last 512 values

我编写了一些 VHDL 代码,用于存储输入信号的最后 512 个值并计算存储值中的最大值。此代码有效,但使用了我的 FPGA 的大量 LUT 资源。代码的目的是计算最后 512 个样本的最大值,是否有更节省资源的方法来实现这一点? (重要的是它计算最后 512 个值中的最大值,而不是从该输入中观察到的最大值,后者可以很容易地通过存储单个数字来实现)。

或者,我是否可以通过某种方式编写 VHDL,以便合成器将阵列实现为块 RAM (BRAM) 而不是 LUT?

我使用的合成器是 LabVIEW FPGA(我相信它在内部使用 XilinX ISE compile/synthesise VHDL)。

我当前的代码如下所示:

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

entity RecentMax is
  port (
    clk : in std_logic;
    reset : in std_logic;
    InputSignal : in std_logic_vector(15 downto 0);
    Max : out std_logic_vector(15 downto 0)
    );
end RecentMax;

architecture RTL of RecentMax is
-- declarations
  type Array512 is array(0 to 511) of signed(15 downto 0);
  signal PastVals : Array512;
  type Array256 is array(0 to 255) of signed(15 downto 0);
  signal result : Array256;

  signal CalculationState : unsigned(1 downto 0);
  signal NLeftToCompute : unsigned(8 downto 0);
begin
-- behaviour
  process(clk)
  begin
    if(rising_edge(clk)) then
      if(reset = '1') then
        -- reset values
        for i in PastVals'low to PastVals'high loop
          PastVals(i) <= (others => '0');
        end loop;
        for i in result'low to result'high loop
          result(i) <= (others => '0');
        end loop;
        CalculationState <= to_unsigned(0, 2);
        Max <= std_logic_vector(to_signed(0, 16));
        NLeftToCompute <= to_unsigned(256, 9);
      else
        -- do stuff
        case to_integer(CalculationState) is
          when 0 =>
            for i in PastVals'low to PastVals'high-1 loop
              PastVals(i+1) <= PastVals(i);
            end loop;
            PastVals(0) <= signed(InputSignal);
            Max <= std_logic_vector(result(0));
            NLeftToCompute <= to_unsigned(256, 9);
            CalculationState <= to_unsigned(1, 2);
          when 1 =>
            for i in 0 to 255 loop
              if (i <= to_integer(NLeftToCompute)-1) then
                if PastVals(i*2) > PastVals(i*2+1) then
                  result(i) <= PastVals(i*2);
                else
                  result(i) <= PastVals(i*2+1);
                end if;
              end if;
            end loop;
            NLeftToCompute <= shift_right(NLeftToCompute, 1);
            CalculationState <= to_unsigned(2, 2);
          when 2 =>;
            for i in 0 to 127 loop
              if (i <= to_integer(NLeftToCompute)-1) then
                if result(i*2) > result(i*2+1) then
                  result(i) <= result(i*2);
                else
                  result(i) <= result(i*2+1);
                end if;
              end if;
            end loop;
            if NLeftToCompute > 2 then
              NLeftToCompute <= shift_right(NLeftToCompute, 1);
            else
              CalculationState <= to_unsigned(0, 2);
            end if;
          when others =>
            --- do nothing - shouldn't get here
        end case;
    end if;
  end if;
end process;
end RTL;

你想要的有两种可能。

首先你可以选择通过创建专用进程和数组来实例化一个BRAM,这样合成器就可以选择使用block ram而不是512 luts。

CONSTANT DATA_WIDTH     : integer := 16;
CONSTANT ADD_WIDTH      : integer := 9; -- 512 addresses
CONSTANT DPRAM_DEPTH    : integer := 2**ADD_WIDTH; -- depth of the memory

TYPE dpram IS ARRAY (0 TO DPRAM_DEPTH - 1) OF std_logic_vector(DATA_WIDTH - 1 DOWNTO 0);
SIGNAL mem              : dpram;

然后是过程:

dpram_gen_p : PROCESS (clk_i)
BEGIN
    IF (rising_edge(clk_i)) THEN
        IF (wr_req_i = '1') THEN
            mem(wr_add_s) <= wr_data_i;
        END IF;
        rd_data_s <= mem(rd_add_s);
    END IF;
END PROCESS;

对于主合成器,此语法将作为块 RAM 实现。 然后,您需要使用 RAM 的数据端口,而不是您的 PastVals 信号。请注意读取周期,因为您需要一个时钟周期来更改读取接口的地址 (rd_add_s) 和一个实际读取数据 (rd_data_s)。

第二个选项(根据我的说法,这是最简单和最快的)只是实现一个大小为 512 字的 fifo 存储器(您可以使用 Xilinx IP 核生成器)。

https://www.xilinx.com/support/documentation/ip_documentation/fifo_generator/v13_1/pg057-fifo-generator.pdf

然后你只需要在 fifo 中写入直到它已满,最后逐字读取数据直到它为空并注册最高值,就像你在设计中所做的那样。

对于这个特定的应用程序,每 512 个时钟周期更新一次最大值就足够了。我更新的代码解决方案如下所示。我仍然对这个问题的答案很感兴趣,关于是否有一种资源效率更高的方法可以在较少的时钟周期内工作。

代码解决方案:

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

entity RecentMax is
  port (
    clk : in std_logic;
    reset : in std_logic;
    InputSignal : in std_logic_vector(15 downto 0);
    Max : out std_logic_vector(15 downto 0)
    );
end RecentMax;

architecture RTL of RecentMax is
-- declarations
signal counter : integer;
signal RecentMax : signed(15 downto 0);

begin
-- behaviour
  process(clk)
  begin
    if(rising_edge(clk)) then
      if(reset = '1') then
        -- reset values
        counter <= 0;
        RecentMax <= to_signed(0, 16);
      else
      -- do stuff
      if counter = 0 then
        Max <= std_logic_vector(RecentMax);
        counter <= counter + 1;
        RecentMax <= to_signed(0, 16);
      else
        if signed(InputSignal) > RecentMax then
          RecentMax <= signed(InputSignal);
        end if;
        if counter >= 511 then
          counter <= 0;
        else
          counter <= counter + 1;
        end if;
      end if;  
    end if;
  end if;
end process;
end RTL;