热度 25
2014-1-3 21:02
3675 次阅读|
11 个评论
DPRAM的读写操作 下面是本人写的DPRAM的读写操作,自己定制DPRAM,不用altera FPGA 内部自带的IP core,选择的存储器块类型是M9K(对于不同类型的存储器块,当同时对某一个地址进行读写操作时读出来 的结果不一样),当dpram的读写操作对同一个地址进行操作的时候,读出的数据是该地址中原来的数据,当下一个时钟上升沿到来时,读出的数据是刚才写入的数据。如果想避免两个端口同时对某一地址写操作的冲突就要用以下方法进行避免。 避免 就要用一下方法 双口RAM 是在一个SRAM 存储器 上具有两套完全独立的数据线、 地址线 和读写控制线,并允许两个独立的系统同时对该存储器进行随机性的访问。双口RAM最大的特点是存储数据共享。一个 存储器 配备两套独立的地址、数据和控制线,允许两个独立的CPU或控制器同时异步地访问 存储单元 。因为数据共享,就必须存在访问仲裁控制。内部仲裁逻辑控制提供以下功能:对同一地址单元访问的时序控制; 存储单元 数据块 的访问权限分配;信令交换逻辑(例如中断信号)等。 双口RAM可用于提高RAM的 吞吐率 ,适用于作于实时的 数据缓存 。 如果同时访问双口RAM的同一 存储单元 ,势必造成数据访问失真。为了防止冲突的发生,采用Busy逻辑控制,也称硬件地址仲裁逻辑。此处只给出了地址总线选通信信号先于 片选 脉冲信号的情况,而且,两端的 片选信号 至少相差tAPS——仲裁最小时间间隔,内部仲裁逻辑控制才可给后访问的一方输出Busy闭锁信号,将访问权交给另一方直至结束对该地址单元的访问,才撤消Busy闭锁信号,将访问权交给另一方直至结束对该地址单元的访问,才撤消Busy闭锁信号。即使在极限情况,两个CPU几乎同时访问同一单元——地址匹配时 片选信号 低跳变之差少于tAPS,Busy闭锁信号也仅输出给其中任一CPU,只允许一个CPU访问该地址单元。仲裁控制不会同时向两个CPU发Busy闭锁信号。 存储单元数据块的访问权限分配只允许在某一时间段内由1个CPU对自定义的某一数据块进行读写操作,这将有助于存储数据的保护,更有效地避免地址冲突。信号(Semaphore,简称SEM)仲裁闭锁就是一种硬件电路结合软件实现访问权限分配方法。SEM单元是与 存储单元 无关的独立标志单元。两个 触发器 在初始化时均使SEM允许输出为高电平,等待双方申请SEM。如果收到一方写入的SEM信号(通常低电平写入),仲裁电路将使其中一个 触发器 的SEM允许输出端为低电平,而闭锁另一个SEM允许输出端使其继续保持高电平。只有当先请求的一方撤消SEM信号,即写入高电平,才使另一SEM允许输出端的闭锁得到解除,恢复等待新的SEM申请。 下面是具体代码及结果分析: library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.std_logic_arith.all; entity test_dpram is port( clk1: in std_logic; clr: in std_logic; addr_a,addr_b: in std_logic_vector(9 downto 0); we_a,we_b: in std_logic; rd_a,rd_b: in std_logic; cs_a,cs_b: in std_logic; data_a,data_b: in std_logic_vector(15 downto 0); q_a,q_b: out std_logic_vector(15 downto 0) ); end entity; architecture behav of test_dpram is type ram_t is array(2**10-1 downto 0) of std_logic_vector(15 downto 0); signal ram: ram_t; signal addr_a_temp: std_logic_vector(9 downto 0); signal addr_b_temp: std_logic_vector(9 downto 0); attribute ram_init_file: string; attribute ram_init_file of ram: signal is "test_dpram.mif"; begin process(clk1,we_a,cs_a,rd_a) begin if(rising_edge(clk1)) then -- port a' read or write if(we_a='1' and rd_a='0') then ram(conv_integer(addr_a))=data_a; end if; q_a=ram(conv_integer(addr_a)); end if; end process; process(clk1,we_b,cs_b,rd_b) begin if(rising_edge(clk1)) then -- port b' read or write if(we_b='1' and rd_b='0') then ram(conv_integer(addr_b))=data_b; end if; q_b=ram(conv_integer(addr_b)); end if; end process; end architecture behav; 仿真结果及分析: 对于第一个时钟上升沿,A端口的地址为1,B端口的地址为2,而A是写,B端口读操作,由于初始化存储器文件中地址1,2的内容均为0000,且当dpram的读写操作对同一个地址进行操作的时候,读出的数据是该地址中原来的数据,所以A,B端口读出的数据均为0000, 此时地址1里面已经写入了1111,但是对于地址2,由于B端口没有进行写操作,地址2的内容仍为0000;当第二个时钟上升沿到来时,A,B端口均操作地址2,A端口仍为RDW(read during write)操作,写入数据为1234,A读出的仍是原来的数据0000,B端口只是读操作,所以读出的数据也是0000,此时地址2里已经写入了数据1234;当第三个上升沿到来时,两个端口都是读地址2中的数据,为1234(即刚才已经写入的数据);当第四个时钟上升沿到来时,A、B端口都是进行的RDW操作,读出来的数据均为原来的数据0000,但是这时由于两个端口同时同一个地址进行写操作,这样就会产生冲突,所以当下一个时钟上升沿到来时就会出现读出的数据为未知数据,如图中红色的部分所示。当第5个时钟上升沿到来时,A,B端口都是读操作,但是地址不同,所以不会产生冲突,对应的内容分别为地址768,769中的内容;第6个时钟上升沿到来时的情况与第5个类似,这里不再分析。 注:给双口RAM初始化的操作为 attribute ram_init_file: string; attribute ram_init_file of ram: signal is "test_dpram.mif"; 其中,test_fpram.mif为事先建立的存储器初始化文件。 另一种写法:读和写分开操作, library ieee; use ieee.std_logic_1164.all; --调用常用的程序包 use ieee.std_logic_unsigned.all; use ieee.std_logic_arith.all; entity ram_test is --定义实体 generic(width:integer :=8;length:integer:=8); --根据这个来改变RAM的大小,width为数据长度,length为数据个数 port(r_clk,w_clk:in std_logic;---定义写时钟和读时钟 r_add,w_add:in std_logic_vector(2 downto 0);--写地址和读地址 r_en,w_en:in std_logic;--读使能和写使能 d_in:in std_logic_vector(width-1 downto 0);--数据输入 d_out:out std_logic_vector(width-1 downto 0));--数据输出 end entity; architecture art of ram_test is type memory is array (0 to length-1) of std_logic_vector(width-1 downto 0);---定义一数组类型来存储数据 signal data:memory; begin process(w_clk,w_add,w_en,d_in)--写数据进程 begin if w_clk'event and w_clk='1' then--在时钟上升沿来时 if w_en='1' then --若使能为1,则写数据 data(conv_integer(w_add))=d_in; end if; end if; end process; process(r_clk,r_add,r_en,data)--读数据进程 begin if r_clk'event and r_clk='1' then if r_en='1' then d_out=data(conv_integer(r_add)); end if; end if; end process; end art;