存储器的设计
寻址存储器(RAM和ROM)
ROM和RAM属于通用大规模器件,一般不需要自行设
计,特别是采用PLD器件进行设计时;
但是在数字系统中,有时也需要设计一些小型的存储器
件,用于特定的用途:临时存放数据,构成查表运算等。
此类器件的特点为地址与存储内容直接对应,设计时将
输入地址作为给出输出内容的条件;
RAM 随机存储器
RAM的用途是存储数据,其指标为存储容量和字长;
RAM的内部可以分为地址译码和存储单元两部分;
外部端口为: wr 写读控制 cs 片选
d 数据端口 adr 地址端口
例 16x8位RAM的设计
设计思想:
将每个8位数组作为一个字(word);总共存储16个字;
将ram作为由16个字构成的数组,以地址为下标;
通过读写控制模式实现对特定地址上字的读出或写入;
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity kram is
port ( clk,wr,cs: in std_logic;
d: inout std_logic_vector(7 downto 0);
adr: in std_logic_vector(3 downto 0));
end kram;
architecture beh of kram is
subtype word is std_logic_vector(7 downto 0);
type memory is array (0 to 15) of word;
signal adr_in:integer range 0 to 15;
signal sram:memory;
begin
adr_in<=conv_integer (adr);
process(clk)
begin
if(clk'event and clk='1') then
if (cs='1'and wr='1') then
sram (adr_in)<=d;
end if;
if (cs='1'and wr='0' ) then
d<=sram (adr_in);
end if;
end if;
end process;
end beh;
RAM的数据端口通常为inout模式,设置仿真输入时只
能在写入时将信号加到该输入端口上,在其他时候输入应设
置为高阻态;
RAM设计时需要注意器件的大小,一个16x8位的RAM
大约占用200个门,而256x16的RAM则需要6200门以上,
因此大规模RAM不适合于采用PLD设计,最好采用通用器
件;
ROM只读存储器
ROM的内容是初始设计电路时就写入到内部的,通常采
用电路的固定结构来实现存储;ROM只需设置数据输出端口
和地址输入端口;
例1 简单ROM的设计(16x8位ROM)
设计思想:采用二进制译码器的设计方式,但将每个输入组
态对应的输出与一组存储数据对应起来;
library ieee;
use ieee.std_logic_1164.all;
entity rom is
port(dataout: out std_logic_vector(7 downto 0);
addr: in std_logic_vector(3 downto 0);
ce: in std_logic);
end rom;
architecture d of rom is
begin
dataout <= "00001111" when addr ="0000" and ce='0' else
"11110000" when addr ="0001" and ce='0' else
"11001100" when addr ="0010" and ce='0' else
"00110011" when addr ="0011" and ce='0' else
"10101010" when addr ="0100" and ce='0' else
"01010101" when addr ="0101" and ce='0' else
"10011001" when addr ="0110" and ce='0' else
"01100110" when addr ="0111" and ce='0' else
"00000000" when addr ="1000" and ce='0' else
"11111111" when addr ="1001" and ce='0' else
"00010001" when addr ="1010" and ce='0' else
"10001000" when addr ="1011" and ce='0' else
"10011001" when addr ="1100" and ce='0' else
"01100110" when addr ="1101" and ce='0' else
"10100110" when addr ="1110" and ce='0' else
"01100111" when addr ="1111" and ce='0' else
"XXXXXXXX";
end d;
对于较大的ROM,可以采用结构设计的方法,直接调用
参数化模块进行设计;
例 ROM的LPM设计 (256x8位ROM)
library ieee;
use ieee.std_logic_1164.all;
library lpm;
use lpm.lpm_components.all;
entity romlpm is
port(address: in std_logic_vector(7 downto 0);
inclock: in std_logic;
q: out std_logic_vector(7 downto 0));
end romlpm;
architecture str of romlpm is
signal sub_wire0:std_logic_vector(7 downto 0);
begin
q<=sub_wire0(7 downto 0);
lpm_rom_component:lpm_rom
generic map(
lpm_width =>8,
lpm_widthad =>8,
lpm_address_control=>"registered",
lpm_outdata => "unregistered",
lpm_file=> "krom2.mif")
port map(
address=>address,
inclock=>inclock,
q =>sub_wire0);
end str;
ROM的初始化
在ROM的设计中,必须要预先设置好数据存储文件,这
是一种以.mif为后缀的文本文件,在任何文本编辑器中,按如
下文件格式写入:
DEPTH = 16; 字线数量
WIDTH = 4; 位线数量
ADDRESS_RADIX = HEX; 地址与数据的表达类型
DATA_RADIX = HEX; 可以选择:HEX OCT DEC BIN
CONTENT 存储内容 地址 :数据;
BEGIN
[0..F] : 3;
2 : 4 5 6 7;
8 : F E 5;
END ;
文件写完后,保存为.mif即可。
例:4位查表式乘法器设计
功能:将两个4位二进制数A和B相乘,输出乘积结果
C(8位二进制数);
设计方案:采用256x8位ROM实现,8位地址输入(高
4位为A,低4位为B),256个存储字;8位数据输出;
数据存储文件(krom2.mif): 填写相应的乘法表即可
depth = 256; width = 8;
address_radix = hex; data_radix = hex;
content
begin
[00..0f] : 00 ;
10 : 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f;
20 : 00 02 04 06 08 0a 0c 0e 10 12 14 16 18 1a 1c 1e;
30 : 00 03 06 09 0c 0f 12 15 18 1b 1e 21 24 27 2a 2d;
40 : 00 04 08 0c 10 14 18 1c 20 24 28 2c 30 34 38 3c;
50 : 00 05 0a 0f 14 19 1d 23 28 2d 32 37 3c 41 46 4b;
60 : 00 06 0c 12 18 1e 24 2a 30 36 3c 42 48 4e 54 5a;
70 : 00 07 0e 15 1c 23 2a 31 38 3f 46 4d 54 5b 62 69;
80 : 00 08 10 18 20 28 30 38 40 48 50 58 60 68 70 78;
90 : 00 09 12 1b 24 2d 36 3f 48 51 5a 63 6c 75 7e 87;
a0 : 00 0a 14 1e 28 32 3c 46 50 5a 64 6e 78 82 8c 96;
b0 : 00 0b 16 21 2c 37 42 4d 58 63 6e 79 84 8f 9a a5;
c0 : 00 0c 18 24 30 3c 48 54 60 6c 78 84 90 9c a8 b4;
d0 : 00 0d 1a 27 34 41 4e 5b 68 75 82 8f 9c a9 b6 c3;
e0 : 00 0e 1c 2a 38 46 54 62 70 7e 8c 9a a8 b6 c4 d2;
f0 : 00 0f 1e 2d 3c 4b 5a 66 78 87 96 a5 b4 c3 d2 e1;
end;
该乘法器ROM设计完毕后,将其设置为符号文件,将来
就可以在VHDL程序中用component语句调用了。
在maxplus2的仿真器窗口内,也可以直接生成ROM的
初始化文件,其步骤如下:
选择Initialize/Initialize Memory…;
按ROM地址输入数据;
存盘即可生成指定的mif文件(文件名已经在结构体内
指明);
顺序存储器(堆栈和FIFO)
顺序存储器的特点是不设置地址,所有数据的写入和读
出都按顺序进行;数据写入或读出时通常会进行移位操作;
在设计时必须考虑各存储单元的存储状态;
堆栈(后进先出存储器)
要求:存入数据按顺序排放,存储器全满时给出信号并拒绝
继续存入;读出时按后进先出原则;存储数据一旦读出就从
存储器中消失;
设计思想:
将每个存储单元设置为字(word);存储器整体作为由字
构成的数组;为每个字设置一个标记(flag),用以表达该存
储单元是否已经存放了数据;每写入或读出一个数据时,字
的数组内容进行相应的移动,标记也做相应的变化;
程序示例:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_signed.all;
entity stack is
port(datain: in std_logic_vector(7 downto 0);
push,pop,reset,clk:in std_logic;
stackfull:out std_logic;
dataout: buffer std_logic_vector(7 downto 0));
end stack;
architecture b of stack is
type arraylogic is array(15 downto 0) of std_logic_vector(7
downto 0);
signal data :arraylogic;
signal stackflag:std_logic_vector(15 downto 0);
begin
stackfull<=stackflag(0);
process(clk,reset,pop,push)
variable selfunction: std_logic_vector(1 downto 0);
begin
selfunction:=push & pop;
if reset='1' then
stackflag<=(others=>'0'); dataout<=(others=>'0');
for i in 0 to 15 loop
data(i)<="00000000";
end loop;
elsif clk'event and clk='1' then
case selfunction is
when "10" =>
if stackflag(0)=’0’ then
data(15)<=datain;
stackflag<='1'&stackflag(15 downto 1);
for i in 0 to 14 loop
data(i)<=data(i+1);
end loop;
end if;
when "01" =>
dataout<=data(15);
stackflag<=stackflag(14 downto 0)&'0';
for i in 15 downto 1 loop
data(i)<=data(i-1);
end loop;
when others=>null;
end case;
end if;
end process;
end b;
以上程序是基于移位寄存器的设计思想;若基于存储器
的设计思想,则可以设置一个指针(point),表示出当前写入
或读出单元的地址,使这种地址进行顺序变化,就可以实现
数据的顺序读出或写入;
程序示例
library说明和entity设计与上面程序完全相同;
architecture b of stack is
type arraylogic is array(15 downto 0) of std_logic_vector(7
downto 0);
signal data :arraylogic;
begin
process(clk,reset,pop,push)
variable p:natural range 0 to 15;
variable selfunction: std_logic_vector(1 downto 0);
variable s:std_logic;
begin
stackfull<=s;
selfunction:=push & pop;
if reset='1' then
p:=0;dataout<=(others=>'0');s:='0';
for i in 0 to 15 loop
data(i)<="00000000";
end loop;
elsif clk'event and clk='1' then
if p<15 and selfunction="10" then
data(p)<=datain; p:=p+1;
end if;
if p=15 and selfunction="10" and s='0' then
data(p)<=datain; s:='1';
end if;
if p>0 and selfunction="01" and s='0' then
p:=p-1; dataout<=data(p);
end if;
if p=15 and selfunction="01" and s='1' then
dataout<=data(p); s:='0';
end if;
end if;
end process;
end b;
FIFO(先进先出存储器)
要求:存入数据按顺序排放,存储器全满时给出信号并拒绝
继续存入,全空时也给出信号并拒绝读出;读出时按先进先
出原则;存储数据一旦读出就从存储器中消失;
设计思想:
结合堆栈指针的设计思想,采用环行寄存器方式进行设
计;分别设置写入指针wp和读出指针rp,标记下一个写入地
址和读出地址;地址随写入或读出过程顺序变动;设置全空
标记和全满标记以避免读出或写入的错误;
设计时需要注意处理好从地址最高位到地址最地位的变
化;
程序示例
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_signed.all;
entity kfifo is
port(datain: in std_logic_vector(7 downto 0);
push,pop,reset,clk:in std_logic;
full,empty:out std_logic;
dataout: out std_logic_vector(7 downto 0));
end kfifo;
architecture b of kfifo is
type arraylogic is array(15 downto 0) of std_logic_vector(7
downto 0);
signal data :arraylogic;
signal fi,ei:std_logic;--为全满全空设置内部信号,以便内部调
用;
signal wp,rp:natural range 0 to 15; --指针;
begin
process(clk,reset,pop,push)
variable selfunction: std_logic_vector(1 downto 0);
begin
full<=fi;empty<=ei;
selfunction:=push & pop;
if reset='1' then
wp<=0;rp<=0;fi<='0';ei<='1'; --初始指针处于0位;
dataout<=(others=>'0');
for i in 0 to 15 loop
data(i)<="00000000";
end loop;
elsif clk'event and clk='1' then
--write;
if fi='0' and selfunction="10" and wp<15 then
data(wp)<=datain;
wp<=wp+1;
if wp=rp then fi<='1';end if;
if ei='1' then ei<='0';end if;
end if;
if fi='0' and selfunction="10" and wp=15 then
data(wp)<=datain;
wp<=0;
if wp=rp then fi<='1';end if;
if ei='1' then ei<='0';end if;
end if;
--read;
if ei='0' and selfunction="01" and rp<15 then
dataout<=data(rp);
rp<=rp+1;
if wp=rp then ei<='1';end if;
if fi='1' then fi<='0';end if;
end if;
if ei='0' and selfunction="01" and rp=15 then
dataout<=data(rp);
rp<=0;
if wp=rp then ei<='1';end if;
if fi='1' then fi<='0';end if;
end if;
end if;
end process;
end b;
采用参数化模块直接形成FIFO的设计
由于各类存储器通常都会占用较多的硬件资源,直接采
用已得到优化的参数化模块通常可以取得较好的效果;
程序示例
library ieee;
use ieee.std_logic_1164.all;
library lpm;
use lpm.lpm_components.all;
entity fifo2 is
port(data: in std_logic_vector(7 downto 0);
wrreq,rdreq,clock,aclr:in std_logic;
q: out std_logic_vector(7 downto 0);
full,empty: out std_logic;
usedw: out std_logic_vector(3 downto 0));
end fifo2;
architecture str of fifo2 is
signal sub_wire0:std_logic_vector(3 downto 0);
signal sub_wire1:std_logic;
signal sub_wire2:std_logic_vector(7 downto 0);
signal sub_wire3:std_logic;
begin
usedw<=sub_wire0(3 downto 0);
empty<=sub_wire1;
q<=sub_wire2(7 downto 0);
full<=sub_wire3;
lpm_fifo_component:lpm_fifo
generic map(
lpm_width =>8,
lpm_numwords =>16,
lpm_widthu=>4,
lpm_showahead => "off",
lpm_hint=> "use_eab=on,maximize_speed=5")
port map(
rdreq=>rdreq,aclr=>aclr,clock=>clock,
wrreq=>wrreq,data=>data,
usedw=>sub_wire0,
empty=>sub_wire1,
q =>sub_wire2,
full=>sub_wire3);
end str;