//源自微信公眾號 「數字晶元實驗室」
為了建模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 8h00
3 1010_1010 // This is second address i.e 8h01
4 @ 55 // Jump to new address 8h55
5 0101_1010 // This is address 8h55
6 0110_1001 // This is address 8h56
上圖是同步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 == 1b1) begin
if (WE == 1b1 && RD == 1b0) begin
SRAM [Addr] = dataIn;
end
else if (RD == 1b1 && WE == 1b0) 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 = 8h0;
Addr = 8h0;
CS = 1b0;
WE = 1b0;
RD = 1b0;
Clk = 1b0;
// Wait 100 ns for global reset to finish
#100;
// Add stimulus here
dataIn = 8h0;
Addr = 8h0;
CS = 1b1;
WE = 1b1;
RD = 1b0;
#20;
dataIn = 8h0;
Addr = 8h0;
#20;
dataIn = 8h1;
Addr = 8h1;
#20;
dataIn = 8h10;
Addr = 8h2;
#20;
dataIn = 8h6;
Addr = 8h3;
#20;
dataIn = 8h12;
Addr = 8h4;
#40;
Addr = 8h0;
WE = 1b0;
RD = 1b1;
#20;
Addr = 8h1;
#20;
Addr = 8h2;
#20;
Addr = 8h3;
#20;
Addr = 8h4;
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。
推薦閱讀: