我接到了一项描述我大学RAM的任务。在我看来,我已经编写了一个代码,可以模拟上述设备的行为。但它似乎不起作用。
我以下列方式描述的设备实体:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
LIBRARY RAM_lib;
USE RAM_lib.RAM_pkg.all;
ENTITY RAM IS
GENERIC(
WORD_LENGTH: INTEGER := 8;
ADDRESS_LENGTH: integer := 8
);
PORT(
Enable : IN std_logic;
DATA_IN : IN std_logic_vector(WORD_LENGTH - 1 downto 0);
DATA_OUT : OUT std_logic_vector(WORD_LENGTH - 1 downto 0);
ADDR : IN std_logic_vector(ADDRESS_LENGTH - 1 downto 0);
RESET : IN std_logic;
CLK : IN std_logic;
WR : IN std_logic;
RD : IN std_logic
);
-- Declarations
END RAM ;
行为部分如下:
use work.RAM_pkg.all;
architecture behavior of ram is
---- DATA TYPES DECLARATIONS
-- used type declarations
subtype DATA is std_logic_vector(WORD_LENGTH - 1 downto 0);
subtype ADDRESS is std_logic_vector(ADDRESS_LENGTH - 1 downto 0);
type MEMORY is array (0 to 2**ADDRESS_LENGTH - 1) of DATA;
---- SIGNAL DECLARATION
signal RAM : MEMORY;
begin use work.RAM_pkg.all;
architecture behavior of ram is
---- DATA TYPES DECLARATIONS
-- used type declarations
subtype DATA is std_logic_vector(WORD_LENGTH - 1 downto 0);
subtype ADDRESS is std_logic_vector(ADDRESS_LENGTH - 1 downto 0);
type MEMORY is array (0 to 2**ADDRESS_LENGTH - 1) of DATA;
---- SIGNAL DECLARATION
signal RAM : MEMORY;
begin
-- plug in or plug out ram
plug_in_out: process (enable) is
variable first_load : boolean := true;
begin
if ((enable = '0' and enable'event) or first_load = true) then
data_out <= (others => 'Z');
if (first_load = true) then
first_load := false;
end if;
end if;
end process;
reset_ram: process (reset) is
variable initialized: boolean := false;
begin
if ((reset = '1' and reset'event) or (initialized = false)) then
ram <= (OTHERS => (OTHERS => '0'));
if (initialized = false) then
initialized := true;
end if;
end if;
end process;
-- it serves both "read" and "write" operation for the RAM
read_write: process (clk) is
variable index : integer range 0 to 2**address_length - 1;
begin
if (enable = '1' and clk = '1' and clk'event) then
index := toInt(addr);
if (wr = '1') then
ram(index) <= data_in;
end if;
if (rd = '1') then
data_out <= ram(index);
end if;
end if;
end process;
end architecture behavior;
-- plug in or plug out ram
plug_in_out: process (enable) is
variable first_load : boolean := true;
begin
if ((enable = '0' and enable'event) or first_load = true) then
data_out <= (others => 'Z');
if (first_load = true) then
first_load := false;
end if;
end if;
end process;
reset_ram: process (reset) is
variable initialized: boolean := false;
begin
if ((reset = '1' and reset'event) or (initialized = false)) then
ram <= (OTHERS => (OTHERS => '0'));
if (initialized = false) then
initialized := true;
end if;
end if;
end process;
-- it serves both "read" and "write" operation for the RAM
read_write: process (clk) is
variable index : integer range 0 to 2**address_length - 1;
begin
if (enable = '1' and clk = '1' and clk'event) then
index := toInt(addr);
if (wr = '1') then
ram(index) <= data_in;
end if;
if (rd = '1') then
data_out <= ram(index);
end if;
end if;
end process;
end architecture behavior;
因此,主要问题是为什么初始化没有按照reset_ram
过程中的第5行所示进行。代码调试表明上述行已执行但RAM信号的值保持不变。
答案 0 :(得分:1)
在VHDL中,应在单个过程中分配信号。否则,您将获得多个驱动程序,这些驱动程序通常无法合成。如果驱动值不兼容(例如同时驱动&#39; 0&#39;和&#39; 1',那么在模拟中为同一信号分配多个值将导致冲突,这将产生&#39; X&#39;。)
通过代码扫描,我发现至少data_out
和ram
在多个进程中都有分配。合并您的流程可以解决您的问题。
此外,RAM无法复位。这与模拟无关,但会影响合成。您可以重置输出寄存器(data_out
),但RAM本身无法复位。如果这样做,合成器将使用寄存器而不是嵌入式RAM。但是,您可以通过像signal ram: MEMORY := (others => (others => '0'));
这样定义RAM来获得RAM的初始内容。这得到了综合的支持。
答案 1 :(得分:0)
这里有几个问题。首先,如前所述,您不应该使用多个进程写入相同的信号,或者您有多个驱动程序问题。
如果你真的想要一个异步启用(最接近你在plug_in_out
中描述的那个),那么你需要一个单独的信号用于实际输出。例如:
data_out_real <= (others => 'Z') when enable = '0' else data_out;
这是单行组合VHDL(不在进程内)。不过,我没有建议异步启用,你可以在主进程中运行同步启用。
用于获得初始状态的变量操作可能也不会合成,但您不需要。 data_out_real将始终具有显式("ZZZZ..."
)或data_out的副本的值。当您使用:
signal data_out : std_logic_vector(word_length-1 downto 0) := (others => '0');
假设您正在尝试创建一个针对FPGA的ram模块,那么您不能一次重置所有内容(您可以在寄存器文件中,但RAM不同),因为您尝试在{{1进程(它将模拟,可能合成,但不会进入FPGA的Block RAM)。执行此操作的常规方法是在复位变为高电平时触发复位状态,该复位状态具有计数器,该计数器遍历每个地址,每个时钟将一个位置指定为0。请注意,此状态机占用与正常读/写行为相同的进程(处于非重置状态)。另请注意,这不会像reset_ram
中描述的那样进行异步重置。
实际的reset_ram
进程对于ram的读/写状态看起来没问题,虽然您不需要索引变量/赋值,但在2 ram中进行转换同样容易使用更少的行进行索引操作。
要在此处实施启用,您可能需要将read_write
和enable='1'
分隔为单独的if图层。这允许您为数据输出默认值0或甚至Z(必须在clk上升沿检查内,在启用检查的else部分之外/之内)。如果你真的想要一个同步启用,那么最好做一些像这样的事情而不是我上面演示的组合比特。此外,如果 希望/有异步启用,则需要处理跨时钟路径以在此处的同步过程中使用它。
最后,考虑一下你真正希望你能做什么。在我所描述的大多数RAM中,当启用变低时,我不想丢失最后的读取值,我只是不想让它改变。这更容易实现,因为你可以不提及当启用低时要做什么,并且信号将保持其最后的值。