IC設計例項解析之"Memory"
//源自微信公眾號 “數字晶片實驗室”
為了
建模
memory,Verilog提供了對
二維陣列
的支援,透過宣告暫存器陣列來建模memory的
行為模型
。可以使用陣列中的索引訪問陣列中的任何資料。
例如:
reg [wordsize:0] array_name [0:arraysize]
這裡
wordsizes
是memory的寬度,
arraysize
是memory的深度:
我們可以透過:
my_memory [address] = data_in
和
data_out = my_memory [address]
來分別對memory進行資料儲存和讀取
有時候,可能只需要讀取或寫入
某個資料中的一位
,這是就需要進行以下操作:
data_out = my_memory [address];
data_out_it_0 = data_out [0];
我們可以使用系統任務$
readmemb
和$
readmemh
,透過從磁碟讀取
memory檔案
來初始化memory。
$ readmemb用於讀取二進位制表示的memory,$ readmemh用於讀取十六進位制表示的memory。
$ readmemh(“FILE_NAME”,mem_array,start_addr,stop_addr),其中start_addr和stop_addr是可選的。
示例 - 簡單的memory
module memory();
reg [7:0] my_memory [0:255];
initial begin
$readmemh(“memory。list”, my_memory);
end
endmodule
其中memory.list中內容為:
1 //Comments are allowed
2 1100_1100 // This is first address i。e 8‘h00
3 1010_1010 // This is second address i。e 8’h01
4 @ 55 // Jump to new address 8‘h55
5 0101_1010 // This is address 8’h55
6 0110_1001 // This is address 8‘h56
上圖是同步SRAM的Verilog模組。該同步SRAM可儲存8個8位資料。同步SRAM模組由8位資料輸入dataIn和8位資料輸出dataOut組成。該模組使用8位地址線Addr來定位
memory
陣列中資料位元組的位置。利用8位地址線,可以定址深度為256的SRAM。但在本例中,為了簡單起見,設計了一個深度為8的SRAM。該模組使用1位輸入時鐘線Clk進行時鐘控制。該模組還具有1位片選線CS。
1位RD訊號用於指示同步SRAM上的資料讀操作,1位WE訊號用於指示同步SRAM上的資料寫操作。 RD和WE線均為
高電平
有效。
module syncRAM( dataIn,
dataOut,
Addr,
CS,
WE,
RD,
Clk
);
// parameters for the width
parameter ADR = 8;
parameter DAT = 8;
parameter DPTH = 8;
//ports
input [DAT-1:0] dataIn;
output reg [DAT-1:0] dataOut;
input [ADR-1:0] Addr;
input CS,
WE,
RD,
Clk;
//internal variables
reg [DAT-1:0] SRAM [DPTH-1:0];
always @ (posedge Clk)
begin
if (CS == 1’b1) begin
if (WE == 1‘b1 && RD == 1’b0) begin
SRAM [Addr] = dataIn;
end
else if (RD == 1‘b1 && WE == 1’b0) begin
dataOut = SRAM [Addr];
end
else;
end
else;
end
endmodule
下面是例項SRAM的Testbench:
`timescale 1ns / 1ps
module syncRAM_tb;
// Inputs
reg [7:0] dataIn;
reg [7:0] Addr;
reg CS;
reg WE;
reg RD;
reg Clk;
// Outputs
wire [7:0] dataOut;
// Instantiate the Unit Under Test (UUT)
syncRAM uut (
。dataIn(dataIn),
。dataOut(dataOut),
。Addr(Addr),
。CS(CS),
。WE(WE),
。RD(RD),
。Clk(Clk)
);
initial begin
// Initialize Inputs
dataIn = 8‘h0;
Addr = 8’h0;
CS = 1‘b0;
WE = 1’b0;
RD = 1‘b0;
Clk = 1’b0;
// Wait 100 ns for global reset to finish
#100;
// Add
stimulus here
dataIn = 8‘h0;
Addr = 8’h0;
CS = 1‘b1;
WE = 1’b1;
RD = 1‘b0;
#20;
dataIn = 8’h0;
Addr = 8‘h0;
#20;
dataIn = 8’h1;
Addr = 8‘h1;
#20;
dataIn = 8’h10;
Addr = 8‘h2;
#20;
dataIn = 8’h6;
Addr = 8‘h3;
#20;
dataIn = 8’h12;
Addr = 8‘h4;
#40;
Addr = 8’h0;
WE = 1‘b0;
RD = 1’b1;
#20;
Addr = 8‘h1;
#20;
Addr = 8’h2;
#20;
Addr = 8‘h3;
#20;
Addr = 8’h4;
end
always #10 Clk = ~Clk;
endmodule
在FPGA和ASIC中,通常會設計中例項化RAM,但是這樣你的HDL程式碼就無法移植和可重用。
FPGA相關工具提供自動識別RAM的功能,可以根據需要綜合出可以推斷
distributed RAM或者Block RAM
。涵蓋了RAM的同步寫入、使能、非同步或同步讀取、資料輸出鎖存、復位、單埠,雙埠讀寫等型別
綜合出的RAM型別取決於其RTL描述。
以下是綜合出各種型別RAM的Verilog模板:
具有非同步讀取的單埠RAM:
module raminfr (clk, we, a, di, do);
input clk
;
input we;
input [4:0] a;
input [3:0] di;
output [3:0] do;
reg [3:0] ram [31:0];
always @(posedge clk) begin
if (we)
ram[a] <= di;
end
assign do = ram[a];
endmodule
具有“虛假”同步讀取的單埠RAM
module raminfr (clk, we, a, di, do);
input clk;
input we;
input [4:0] a;
input [3:0] di;
output [3:0] do;
reg [3:0] ram [31:0];
reg [3:0] do;
always @(posedge clk) begin
if (we)
ram[a] <= di;
do <= ram[a];
end
endmodule
具有同步讀取的單埠RAM
module raminfr (clk, we, a, di, do);
input clk;
input we;
input [4:0] a;
input [3:0] di;
output [3:0] do;
reg [3:0] ram [31:0];
reg [4:0] read_a;
always @(posedge clk) begin
if (we)
ram[a] <= di;
read_a <= a;
end
assign do = ram[read_a];
endmodule
具有使能功能的單埠RAM
module raminfr (clk, en, we, a, di, do);
input clk;
input en;
input we;
input [4:0] a;
input [3:0] di;
output [3:0] do;
reg [3:0] ram [31:0];
reg [4:0] read_a;
always @(posedge clk) begin
if (en)
begin
if (we)
ram[a] <= di;
read_a <= a;
end
end
assign do = ram[read_a];
endmodule
具有非同步讀取功能的雙埠RAM
module raminfr
(clk, we, a, dpra, di, spo, dpo);
input clk;
input we;
input [4:0] a;
input [4:0] dpra;
input [3:0] di;
output [3:0] spo;
output [3:0] dpo;
reg [3:0] ram [31:0];
always @(posedge clk) begin
if (we)
ram[a] <= di;
end
assign spo = ram[a];
assign dpo = ram[dpra];
endmodule
一個使能控制兩個讀埠的雙埠RAM
Verilog
module raminfr
(clk, en, we, addra, addrb, di, doa, dob);
input clk;
input en;
input we;
input [4:0] addra;
input [4:0] addrb;
input [3:0] di;
output [3:0] doa;
output [3:0] dob;
reg [3:0] ram [31:0];
reg [4:0] read_addra;
reg [4:0] read_addrb;
always @(posedge clk) begin
if (ena)
begin
if (wea)
ram[addra] <= di;
read_aaddra <= addra;
read_aaddrb <= addrb;
end
end
assign doa = ram[read_addra];
assign dob = ram[read_addrb];
endmodule
雙埠RAM,使能控制每個埠
module raminfr
(clk,ena,enb,wea,addra,addrb,dia,doa,dob);
input clk;
input ena;
input enb;
input wea;
input [4:0] addra;
input [4:0] addrb;
input [3:0] dia;
output [3:0] doa;
output [3:0] dob;
reg [3:0] ram [31:0];
reg [4:0] read_addra;
reg [4:0] read_addrb;
always @(posedge clk) begin
if (ena)
begin
if (wea)
ram[addra] <= dia;
read_addra <= addra;
end
if (enb)
read_addrb <= addrb;
end
assign doa = ram[read_addra];
assign dob = ram[read_addrb];
endmodule
多埠RAM
多埠RAM的不同讀埠訪問不同地址的RAM內容。 但是,只能有一個寫埠。
Verilog
module raminfr
(clk, we, wa, ra1, ra2, di, do1, do2);
input clk;
input we;
input [4:0] wa;
input [4:0] ra1;
input [4:0] ra2;
input [3:0] di;
output [3:0] do1;
output [3:0] do2;
reg [3:0] ram [31:0];
always @(posedge clk) begin
if (we)
ram[wa] <= di;
end
assign do1 = ram[ra1];
assign do2 = ram[ra2];
endmodule
我們可以對FPGA進行屬性約束,以便綜合出distributed RAM或者Block RAM。