我编写了一些VHDL代码,用于存储输入信号的最后512个值,并计算存储值的最大值。此代码有效,但使用了FPGA的许多LUT资源。代码的目的是计算最后512个样本的最大值,是否有更有效的方法来实现这一目标? (重要的是它计算最后512个值中的最大值,而不是从该输入中观察到的最大值,后者可以很容易地实现存储单个数字)。
或者在某种程度上,我可以对VHDL进行编码,使得合成器将阵列实现为Block RAM(BRAM)而不是LUT?
我使用的合成器是LabVIEW FPGA(我相信在内部使用XilinX ISE来编译/合成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;
答案 0 :(得分:1)
你想要的东西有两种可能性。
首先,您可以选择通过创建专用进程和数组来实例化BRAM,以便合成器选择使用块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;
对于主合成器,此语法将实现为Block RAM。
然后,您需要使用RAM的数据端口,而不是PastVals
信号。请注意读取周期,因为您需要一个时钟周期来更改读取接口的地址(rd_add_s
),并且需要一个时钟来实际读取数据(rd_data_s
)。
第二个选项(根据我的说法,它是最简单和最快的)只是为了实现512字大小的fifo内存(你可以使用Xilinx IP内核生成器)。
然后你只需要写入fifo直到它已满,最后逐字逐字地读取数据,直到它为空并注册最高值,就像你在设计中所做的那样。
答案 1 :(得分:0)
对于这个特定的应用程序,只需每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;