//源自微信公眾號 「數字晶元實驗室」

為了建模memory,Verilog提供了對二維數組的支持,通過聲明寄存器數組來建模memory的行為模型。可以使用數組中的索引訪問數組中的任何數據。

例如:

reg [wordsize:0] array_name [0:arraysize]

這裡wordsizes是memory的寬度, arraysize是memory的深度:

我們可以通過:

my_memory [address] = data_indata_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。


推薦閱讀:
相关文章