Pingpong

Pingpong操作就是交替使用存储器的过程,一般在跨时钟域中使用,本文给出了由慢时钟到快时钟数据处理的pingpong示意图,并贴出对应的代码以及tb。供新手学习参考~

数据读写pingpong示意图

Pingpong_第1张图片

数据pingpong写:

通过写使能控制两片DPRAM的工作过程,写使能交替有效,交替写入数据为0~1023,实现pingpong效果。黄颜色表示DPRAM1的使能和要写入的数据,蓝颜色表示DPRAM2的使能和要写入的数据;外界数据一直传入,两片DPRAM一直不间断交替写入数据。

 

数据pingpong读:

通过读使能控制两片DPRAM的工作过程,读使能交替有效,交替读出对应DPRAM数据为0~1023,实现pingpong效果。黄颜色图表示DPRAM1写满后,写使能拉低(此时正在写DPRAM2),读时钟下DPRAM1读使能有效,开始读出数据,由于读时钟为100MHz,写时钟为20MHz,所以写满DPRAM需要的时间是读空DPRAM需要时间的五倍。蓝颜色图表示DPRAM2写满后,写使能拉低(此时正在写DPRAM1),读时钟下DPRAM2读使能有效,开始读出数据,当任意一片DPRAM数据写满后,内部的数据随即会被读出。

 

实现环境:ISE14.7

仿真环境:modelsim 10.5

附Verilog代码和tb:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    22:59:47 03/26/2019 
// Design Name: 
// Module Name:    pingpong 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module pingpong(

    input clk_wr,      //写时钟速率20Mhz
    input clk_rd,      //读时钟速率100Mhz
    input rst_n,
    input [9:0] din,   
    output reg out_valid,
    output reg [9:0] dout
);

reg [9:0] addr_wr;
reg [9:0] addr_rd;
reg en_wr1, en_wr2;
reg we_wr1, we_wr2;
reg en_rd1, en_rd2;
wire [9:0] dout1;
wire [9:0] dout2;

dpram dpram1 (
  .clka(clk_wr), // input clka
  .ena(en_wr1), // input ena
  .wea(we_wr1), // input [0 : 0] wea
  .addra(addr_wr), // input [9 : 0] addra
  .dina(din), // input [9 : 0] dina
  .douta(), // output [9 : 0] douta
  
  .clkb(clk_rd), // input clkb
  .enb(en_rd1), // input enb
  .web(1'b0), // input [0 : 0] web
  .addrb(addr_rd), // input [9 : 0] addrb
  .dinb(10'd0), // input [9 : 0] dinb
  .doutb(dout1) // output [9 : 0] doutb
);



dpram dpram2 (
  .clka(clk_wr),      //写端口
  .ena(en_wr2),    
  .wea(we_wr2),      
  .addra(addr_wr),  
  .dina(din),   
  .douta(), 
  
  .clkb(clk_rd),      //读端口
  .enb(en_rd2),      
  .web(1'b0),      
  .addrb(addr_rd),  
  .dinb(10'd0),    
  .doutb(dout2) 
);

//写端口乒乓操作
always @ (posedge clk_wr)     //写地址信号控制0~1023
    if (!rst_n) //一旦复位信号拉低,即生效~
	    addr_wr <= 1023;
    else 
	    addr_wr <= addr_wr + 1'b1;

always @ (posedge clk_wr)     //轮流写RAM1与RAM2
    if (!rst_n) begin //第一块dpbram被选中
	    we_wr1 <= 1'b0; // input [0 : 0] wea
		 we_wr2 <= 1'b1; // input [0 : 0] wea
       en_wr1 <= 1'b0; 
		 en_wr2 <= 1'b1;  
	 end
    else if (addr_wr == 1023) begin//第二块dpbram被选中
       we_wr1 <= ~we_wr1; 
		 we_wr2 <= ~we_wr2;
       en_wr1 <= ~en_wr1; 
		 en_wr2 <= ~en_wr2;
    end 
 
//读端口乒乓操作:读时钟下读地址变化规则
always @ (posedge clk_rd)    //读地址信号控制0~1023
    if (!rst_n) 
	    addr_rd <= 1021;  //匹配延迟
    else 
	    addr_rd <= addr_rd + 1'b1; 
    
reg [15:0] cnt;
//0~5119变化
always @ (posedge clk_rd)    //读时钟为写时钟的5倍
    if (!rst_n) 
	    cnt <= 16'hFFFE;  //匹配延迟
    else if (cnt == 5119) 
	    cnt <= 0;
    else 
	    cnt <= cnt + 1'b1;
    
	 
reg flag1, flag2;
always @ (posedge clk_rd)    //读RAM标志,RAM1或RAM2
    if (!rst_n) begin //复位时:读RAM1
	    flag1 = 1'b0; 
		 flag2 = 1'b1; 
	 end
    else if (cnt == 5119) begin //读空RAM1后:读RAM2
	    flag1 = ~flag1; 
		 flag2 = ~flag2; 
	 end    
    else begin 
	    flag1 = flag1; 
		 flag2 = flag2; 
	 end
    
always @ (posedge clk_rd)    //读RAM使能,选择cnt的前1/5时间读取
    if (!rst_n) begin 
	    en_rd1 <= 1'b1; 
		 en_rd2 <= 1'b0; 
	 end 
    else if (cnt < 1024) begin 
	    en_rd1 <= flag1; 
		 en_rd2 <= flag2; 
	 end
    else begin 
	    en_rd1 <= 1'b0; 
		 en_rd2 <= 1'b0; 
	 end

reg en_rd1_reg, en_rd2_reg;
always @ (posedge clk_rd)    //延迟一级,匹配时序
    if (!rst_n) begin 
	    en_rd1_reg <= 0; 
		 en_rd1_reg <= en_rd1_reg; 
	 end
    else begin 
	    en_rd1_reg <= en_rd1; 
	    en_rd2_reg <= en_rd2; 
	 end

always @ (posedge clk_rd)    //输出选择,RAM1或RAM2;控制输出使能信号
    if (!rst_n) begin 
	    dout <= 0; 
		 out_valid <= 0; 
	 end
    else if (en_rd1_reg) begin 
	    dout <= dout1; 
		 out_valid <= 1; 
	 end
    else if (en_rd2_reg) begin 
	    dout <= dout2; 
		 out_valid <= 1; 
	 end
    else begin 
	    dout <= 0; 
		 out_valid <= 0; 
	 end
    
endmodule
`timescale 1ns / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:
//
// Create Date:   23:42:41 03/26/2019
// Design Name:   pingpong
// Module Name:   D:/E_learning/Labs/pingpong/test_pingpong.v
// Project Name:  pingpong
// Target Device:  
// Tool versions:  
// Description: 
//
// Verilog Test Fixture created by ISE for module: pingpong
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
////////////////////////////////////////////////////////////////////////////////

module test_pingpong;

	// Inputs
	reg clk_wr;
	reg clk_rd;
	reg rst_n;
	reg [9:0] din;

	// Outputs
	wire out_valid;
	wire [9:0] dout;

   reg [9:0] din_reg1;

	// Instantiate the Unit Under Test (UUT)
	pingpong uut (
		.clk_wr(clk_wr), 
		.clk_rd(clk_rd), 
		.rst_n(rst_n), 
		.din(din_reg1), 
		.out_valid(out_valid), 
		.dout(dout)
	);

	initial begin
		// Initialize Inputs
		clk_wr = 0;
		clk_rd = 0;
		din = 0;
		din_reg1 = 0;

      rst_n = 0;
		// Wait 100 ns for global reset to finish
		#100;
      rst_n = 1;  
		// Add stimulus here

	end
 parameter period0 = 50;
 always begin
   clk_wr=0;
	#(period0/2) clk_wr=1;
	#(period0/2) clk_wr=0;  
  end	

 parameter period1 = 10;
 always begin
   clk_rd=0;
	#(period1/2) clk_rd=1;
	#(period1/2) clk_rd=0;  
  end	 
  
 	
 always @(posedge clk_wr)begin
 if(!rst_n)begin
   din<= 'b0;
	din_reg1<= 'b0;
 end
 else begin
   din<= din+1; 
	din_reg1<= din;
 end
 end

	
endmodule

 

你可能感兴趣的:(Verilog)