使用 VHDL 在 FPGA 中实例化 RAM
Instantiation of RAM in FPGAs using VHDL
我试图按照 this excellent blog post 中的指导实现双端口 RAM。但是,ModelSim 在编译时给出以下警告:
** Warning: fifo_ram.vhdl(24): (vcom-1236) Shared variables must be of a protected type.
我似乎也无法将其创建为 wave,这表明使用下面的代码无法识别该变量。
如何正确地将此变量声明为 "protected" 类型?另外,作为一个关于共享变量的更普遍的问题——这个变量是否在设计中的所有实体之间共享?
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity fifo_ram is
generic (data : natural := 8;
addr : natural := 16);
port (w_clk : in std_logic;
w_en : in std_logic;
w_addr : in std_logic_vector (addr-1 downto 0);
w_data : in std_logic_vector (data-1 downto 0);
--
r_clk : in std_logic;
r_rdy : in std_logic;
r_addr : in std_logic_vector (addr-1 downto 0);
r_data : out std_logic_vector (data-1 downto 0));
end fifo_ram;
architecture rtl of fifo_ram is
-- shared memory
type mem_type is array ( (2**addr) - 1 downto 0 ) of std_logic_vector(data-1 downto 0);
shared variable mem : mem_type;
begin
write: process (w_clk)
begin
if (rising_edge(w_clk)) then
if (w_en = '1') then
mem(conv_integer(w_addr)) := w_data;
end if;
end if;
end process write;
end architecture;
----------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity tb_fifo is
generic (data : natural := 8;
addr : natural := 16);
end entity;
architecture testbed of tb_fifo is
signal tb_w_clk, tb_w_en : std_logic := '0';
signal tb_w_addr : std_logic_vector (addr-1 downto 0);
signal tb_w_data : std_logic_vector (data-1 downto 0);
signal tb_r_clk, tb_r_rdy : std_logic := '0';
signal tb_r_addr : std_logic_vector (addr-1 downto 0);
signal tb_r_data : std_logic_vector (data-1 downto 0);
begin
dut : entity work.fifo_ram(rtl)
port map(tb_w_clk, tb_w_en, tb_w_addr, tb_w_data,
tb_r_clk, tb_r_rdy, tb_r_addr, tb_r_data);
wclock : process is
begin
tb_w_clk <= '1';
wait for 10 ns;
tb_w_clk <= '0';
wait for 10 ns;
end process wclock;
wdata : process is
begin
tb_w_addr <= x"FFFF";
tb_w_data <= x"AA";
wait for 100 ns;
tb_w_en <= '1';
wait for 70 ns;
tb_w_en <= '0';
wait;
end process wdata;
end architecture;
好的,看完博客 post 我现在明白他们为什么使用共享变量而不是信号了。这是因为多个进程正在分配给这个变量,这在 Verilog 中的 reg 或 VHDL 中的 signal 的情况下是不可能的。在那种情况下,合成器将产生一个错误,抱怨 mem 有多个驱动程序。但是为了在这种情况下使用共享变量,您必须将其声明为受保护的。您需要做的是声明一个受保护的数据类型,然后将您的 mem 变量封装在其中,就像面向对象语言中的 类 一样。下面是受保护数据类型的示例:
type mem_envelope is protected -- protected type declaration
variable mem : mem_type;
function GetVal( addr : integer ) return std_logic_vector(data - 1 downto 0);
function SetVal( addr : integer; val : std_logic_vector(data - 1 downto 0) ) return boolean; --may be used to indicate whether write was successfull or not
end protected mem_envelope;
然后声明一个 mem_envelope 类型的共享变量,并使用 GetVal 和 SetVal 函数为 read/write 值到你进程中的内存。
另一种实现真双端口 (TDP) RAM 的方法是使用 一个 进程和 两个 个时钟。
signal ram : ram_t;
signal a1_reg : unsigned(A_BITS-1 downto 0);
signal a2_reg : unsigned(A_BITS-1 downto 0);
....
process (clk1, clk2)
begin -- process
if rising_edge(clk1) then
if ce1 = '1' then
if we1 = '1' then
ram(to_integer(a1)) <= d1;
end if;
a1_reg <= a1;
end if;
end if;
if rising_edge(clk2) then
if ce2 = '1' then
if we2 = '1' then
ram(to_integer(a2)) <= d2;
end if;
a2_reg <= a2;
end if;
end if;
end process;
q1 <= ram(to_integer(a1_reg)); -- returns new data
q2 <= ram(to_integer(a2_reg)); -- returns new data
这甚至可以使用 Xilinx 工具进行综合。 Altera 工具需要 altsyncram 宏才能正确识别 TDP-RAM。
我试图按照 this excellent blog post 中的指导实现双端口 RAM。但是,ModelSim 在编译时给出以下警告:
** Warning: fifo_ram.vhdl(24): (vcom-1236) Shared variables must be of a protected type.
我似乎也无法将其创建为 wave,这表明使用下面的代码无法识别该变量。
如何正确地将此变量声明为 "protected" 类型?另外,作为一个关于共享变量的更普遍的问题——这个变量是否在设计中的所有实体之间共享?
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity fifo_ram is
generic (data : natural := 8;
addr : natural := 16);
port (w_clk : in std_logic;
w_en : in std_logic;
w_addr : in std_logic_vector (addr-1 downto 0);
w_data : in std_logic_vector (data-1 downto 0);
--
r_clk : in std_logic;
r_rdy : in std_logic;
r_addr : in std_logic_vector (addr-1 downto 0);
r_data : out std_logic_vector (data-1 downto 0));
end fifo_ram;
architecture rtl of fifo_ram is
-- shared memory
type mem_type is array ( (2**addr) - 1 downto 0 ) of std_logic_vector(data-1 downto 0);
shared variable mem : mem_type;
begin
write: process (w_clk)
begin
if (rising_edge(w_clk)) then
if (w_en = '1') then
mem(conv_integer(w_addr)) := w_data;
end if;
end if;
end process write;
end architecture;
----------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity tb_fifo is
generic (data : natural := 8;
addr : natural := 16);
end entity;
architecture testbed of tb_fifo is
signal tb_w_clk, tb_w_en : std_logic := '0';
signal tb_w_addr : std_logic_vector (addr-1 downto 0);
signal tb_w_data : std_logic_vector (data-1 downto 0);
signal tb_r_clk, tb_r_rdy : std_logic := '0';
signal tb_r_addr : std_logic_vector (addr-1 downto 0);
signal tb_r_data : std_logic_vector (data-1 downto 0);
begin
dut : entity work.fifo_ram(rtl)
port map(tb_w_clk, tb_w_en, tb_w_addr, tb_w_data,
tb_r_clk, tb_r_rdy, tb_r_addr, tb_r_data);
wclock : process is
begin
tb_w_clk <= '1';
wait for 10 ns;
tb_w_clk <= '0';
wait for 10 ns;
end process wclock;
wdata : process is
begin
tb_w_addr <= x"FFFF";
tb_w_data <= x"AA";
wait for 100 ns;
tb_w_en <= '1';
wait for 70 ns;
tb_w_en <= '0';
wait;
end process wdata;
end architecture;
好的,看完博客 post 我现在明白他们为什么使用共享变量而不是信号了。这是因为多个进程正在分配给这个变量,这在 Verilog 中的 reg 或 VHDL 中的 signal 的情况下是不可能的。在那种情况下,合成器将产生一个错误,抱怨 mem 有多个驱动程序。但是为了在这种情况下使用共享变量,您必须将其声明为受保护的。您需要做的是声明一个受保护的数据类型,然后将您的 mem 变量封装在其中,就像面向对象语言中的 类 一样。下面是受保护数据类型的示例:
type mem_envelope is protected -- protected type declaration
variable mem : mem_type;
function GetVal( addr : integer ) return std_logic_vector(data - 1 downto 0);
function SetVal( addr : integer; val : std_logic_vector(data - 1 downto 0) ) return boolean; --may be used to indicate whether write was successfull or not
end protected mem_envelope;
然后声明一个 mem_envelope 类型的共享变量,并使用 GetVal 和 SetVal 函数为 read/write 值到你进程中的内存。
另一种实现真双端口 (TDP) RAM 的方法是使用 一个 进程和 两个 个时钟。
signal ram : ram_t;
signal a1_reg : unsigned(A_BITS-1 downto 0);
signal a2_reg : unsigned(A_BITS-1 downto 0);
....
process (clk1, clk2)
begin -- process
if rising_edge(clk1) then
if ce1 = '1' then
if we1 = '1' then
ram(to_integer(a1)) <= d1;
end if;
a1_reg <= a1;
end if;
end if;
if rising_edge(clk2) then
if ce2 = '1' then
if we2 = '1' then
ram(to_integer(a2)) <= d2;
end if;
a2_reg <= a2;
end if;
end if;
end process;
q1 <= ram(to_integer(a1_reg)); -- returns new data
q2 <= ram(to_integer(a2_reg)); -- returns new data
这甚至可以使用 Xilinx 工具进行综合。 Altera 工具需要 altsyncram 宏才能正确识别 TDP-RAM。