欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【FPGA】双端口RAM的设计(同步读写)

程序员文章站 2024-02-22 21:26:46
...

之前的博文都是讲单端口RAM的,它们仅有一套控制输入,例如cs,we,oe,还有数据总线以及地址。

【FPGA】单端口RAM的设计(同步读、同步写)

附上太多链接,我也累,自己找吧。

双端口RAM,顾名思义,有两套地址,数据总线,以及cs等。

从输入输出也可以看出来:

input clk       , // Clock Input
input [ADDR_WIDTH - 1 : 0] address_0 , // address_0 Input
inout [data_0_WIDTH-1 : 0] data_0    , // data_0 bi-directional
input cs_0      , // Chip Select
input we_0      , // Write Enable/Read Enable
input oe_0      , // Output Enable
input [ADDR_WIDTH - 1 : 0] address_1 , // address_1 Input
inout [data_0_WIDTH-1 : 0] data_1    , // data_1 bi-directional
input cs_1      , // Chip Select
input we_1      , // Write Enable/Read Enable
input oe_1        // Output Enable

写部分:

//--------------Code Starts Here------------------ 
// Memory Write Block 
// Write Operation : When we_0 = 1, cs_0 = 1
always @ (posedge clk)
begin : MEM_WRITE
  if ( cs_0 && we_0 ) begin
     mem[address_0] <= data_0;
  end 
  else if (cs_1 && we_1) begin 
     mem[address_1] <= data_1;
  end
end

读部分:

// Tri-State Buffer control 
// output : When we_0 = 0, oe_0 = 1, cs_0 = 1
assign data_0 = (cs_0 && oe_0 && !we_0) ? data_0_out : 8'bz; 

// Memory Read Block 
// Read Operation : When we_0 = 0, oe_0 = 1, cs_0 = 1
always @ (posedge clk)
begin : MEM_READ_0
  if (cs_0 && !we_0 && oe_0) begin
    data_0_out <= mem[address_0]; 
  end 
  else begin
    data_0_out <= 0; 
  end  
end 

//Second Port of RAM
// Tri-State Buffer control 
// output : When we_0 = 0, oe_0 = 1, cs_0 = 1
assign data_1 = (cs_1 && oe_1 && !we_1) ? data_1_out : 8'bz; 
// Memory Read Block 1 
// Read Operation : When we_1 = 0, oe_1 = 1, cs_1 = 1
always @ (posedge clk)
begin : MEM_READ_1
  if (cs_1 && !we_1 && oe_1) begin
    data_1_out <= mem[address_1]; 
  end else begin
    data_1_out <= 0;
  end
end

可见,可以操作地址0以及地址1读写,因此可以实现同时读写操作。

如果对两套端口同时进行写操作,可见地址0优先,而读就不存在这种情况。(可不必考虑这句话!)

下面给出完整Verilog HDL设计代码:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Create Date: 2019/05/28 20:50:48
// Design Name: 
// Module Name: ram_dp_sr_sw
//////////////////////////////////////////////////////////////////////////////////

module ram_dp_sr_sw #(
parameter data_0_WIDTH = 8,
parameter ADDR_WIDTH = 8,
parameter RAM_DEPTH = 1 << ADDR_WIDTH
)(
input clk       , // Clock Input
input [ADDR_WIDTH - 1 : 0] address_0 , // address_0 Input
inout [data_0_WIDTH-1 : 0] data_0    , // data_0 bi-directional
input cs_0      , // Chip Select
input we_0      , // Write Enable/Read Enable
input oe_0      , // Output Enable
input [ADDR_WIDTH - 1 : 0] address_1 , // address_1 Input
inout [data_0_WIDTH-1 : 0] data_1    , // data_1 bi-directional
input cs_1      , // Chip Select
input we_1      , // Write Enable/Read Enable
input oe_1        // Output Enable
); 



//--------------Internal variables---------------- 
reg [data_0_WIDTH-1:0] data_0_out ; 
reg [data_0_WIDTH-1:0] data_1_out ;
reg [data_0_WIDTH-1:0] mem [0:RAM_DEPTH-1];


//initialization

// synopsys_translate_off
integer i;
initial begin
    for(i=0; i < RAM_DEPTH; i = i + 1) begin
        mem[i] = 8'h00;
    end
end
// synopsys_translate_on


//--------------Code Starts Here------------------ 
// Memory Write Block 
// Write Operation : When we_0 = 1, cs_0 = 1
always @ (posedge clk)
begin : MEM_WRITE
  if ( cs_0 && we_0 ) begin
     mem[address_0] <= data_0;
  end 
  else if (cs_1 && we_1) begin 
     mem[address_1] <= data_1;
  end
end

  

// Tri-State Buffer control 
// output : When we_0 = 0, oe_0 = 1, cs_0 = 1
assign data_0 = (cs_0 && oe_0 && !we_0) ? data_0_out : 8'bz; 

// Memory Read Block 
// Read Operation : When we_0 = 0, oe_0 = 1, cs_0 = 1
always @ (posedge clk)
begin : MEM_READ_0
  if (cs_0 && !we_0 && oe_0) begin
    data_0_out <= mem[address_0]; 
  end 
  else begin
    data_0_out <= 0; 
  end  
end 

//Second Port of RAM
// Tri-State Buffer control 
// output : When we_0 = 0, oe_0 = 1, cs_0 = 1
assign data_1 = (cs_1 && oe_1 && !we_1) ? data_1_out : 8'bz; 
// Memory Read Block 1 
// Read Operation : When we_1 = 0, oe_1 = 1, cs_1 = 1
always @ (posedge clk)
begin : MEM_READ_1
  if (cs_1 && !we_1 && oe_1) begin
    data_1_out <= mem[address_1]; 
  end else begin
    data_1_out <= 0;
  end
end

endmodule // End of Module ram_dp_sr_sw

下面对此进行测试:

过程如下:

两个端口同时对RAM初值:

【FPGA】双端口RAM的设计(同步读写)

端口0写,同时端口1读:

【FPGA】双端口RAM的设计(同步读写)

两端口同时读:

【FPGA】双端口RAM的设计(同步读写)

【FPGA】双端口RAM的设计(同步读写)

可见,读出来的数据与时钟同步。

最后给出测试文件:

`timescale 1ns / 1ps

module ram_dp_sr_sw_tb;

reg clk       ; // Clock
reg [7 : 0] address_0 ; // address_0 input
wire [7 : 0] data_0    ; // data_0 bi-directional
reg cs_0      ; // Chip Select
reg we_0      ; // Write Enable/Read Enable
reg oe_0      ; // Output Enable
reg [7 : 0] address_1 ; // address_1 input
wire [7 : 0] data_1    ; // data_1 bi-directional
reg cs_1      ; // Chip Select
reg we_1      ; // Write Enable/Read Enable
reg oe_1 ;        // Output Enable


initial begin
	clk = 0;
	forever
		#2 clk = ~clk;
end

reg [7 : 0] data_in0; //写数据时候,双向总线与data_in0连接(这样做的目的是保证总线在某一时刻读和写,二者之一有效)
assign data_0 = (cs_0 && we_0 && !oe_0) ? data_in0 : 8'dz;

reg [7 : 0] data_in1; //写数据时候,双向总线与data_in1连接(这样做的目的是保证总线在某一时刻读和写,二者之一有效)
assign data_1 = (cs_1 && we_1 && !oe_1) ? data_in1 : 8'dz;

integer i = 0;

initial begin
oe_0 = 0;
oe_1 = 0;
we_0 = 0;
we_1 = 0;
cs_0 = 0;
cs_1 = 0;
address_0 = 0;
address_1 = 0;
data_in0 = 0;
data_in1 = 0;
//先读出初识值(两套地址一起读)
#4
cs_0 = 1;
cs_1 = 1;
oe_0 = 1;
oe_1 = 1;

for(i = 0; i < 256; i = i + 1) begin
	@(negedge clk) begin
		address_0 = i;
		address_1 = i;
	end

end

//地址0写,地址1读
 @(negedge clk) begin
	we_0 = 1;
	we_1 = 0;
	oe_0 = 0;
	oe_1 = 1;
	
 end
 
for(i = 0; i < 256; i = i + 1) begin
	@(negedge clk) begin
	address_0 = i;
	data_in0 = data_in0 + 1;
	address_1 = i;
	end
 
end

//地址0读·,地址1读
 @(negedge clk) begin
	we_0 = 0;
	we_1 = 0;
	oe_0 = 1;
	oe_1 = 1;
	
 end

for(i = 0; i < 256; i = i + 1) begin
	@(negedge clk) begin
	address_0 = i;
	address_1 = i;
	end
 
end


//结束吧,片选结束
@(negedge clk) begin
cs_0 = 0;
cs_1 = 0;
end

#100 $stop;


end


ram_dp_sr_sw #(
.ADDR_WIDTH(8), //给参数
.data_0_WIDTH(8)
 ) u_ram(
.clk(clk),
.address_0(address_0),
.data_0(data_0),
.cs_0(cs_0),
.we_0(we_0),
.oe_0(oe_0),
.address_1(address_1),
.data_1(data_1),
.cs_1(cs_1),
.we_1(we_1),
.oe_1(oe_1)
);



endmodule 

参考链接:http://www.asic-world.com/examples/verilog/ram_dp_sr_sw.html