乒乓buffer的verilog设计

基本概念

ping-pong buffer 也叫双缓存 double buffer, (必须是两个)就是一个缓存在写入的时候, 另一个缓存同时在处理的结构. 用来提高计算机运行速度, 在显示数据处理中常常用到。
乒乓操作的处理流程为:输入数据流通过“ 输入数据选择单元”将数据流等时分配到两个“数据缓冲模块”, 数据缓冲模块可以为任何存储模块,比较常用的存储单元为双口RAM(DPRAM)、单口RAM(SPRAM)、FIFO等。
在低速处理高速数据流时,可以使用乒乓操作,10M的数据流用乒乓操作,分流成两个FIFO,一个FIFO的吞吐速度只有原来的一半5M,就可以满足低速的处理方法处理高速的数据,处理后在用合并成一个10M的数据流,top层看起就是10M的处理速度而且数据就被不丢失且高速的处理过
乒乓操作的最大特点是通过 “ 输入数据选择单元 ” 和 “ 输出数据选择单元 ” 按节拍、相互配合的切换,将经过缓冲的数据流没有停顿地送到 “ 数据流运算处理模块 ” 进行运算与处理。把乒乓操作模块当作一个整体,站在这个模块的两端看数据,输入数据流和输出数据流都是连续不断的,没有任何停顿,因此非常适合对数据流进行流水线式处理。所以乒乓操作常常应用于流水线式算法,完成数据的无缝缓冲与处理。
综上所述,乒乓缓存结构实际上相当于一个双口RAM,但它与普通的双口RAM又有所不同。普通双口RAM是单个存储体构成的IC,乒乓ram结构则由包含两个相互独立存储体的多片IC构成,从而使其在结构、速度、容量等方面具有更大的灵活性;若双口在访问同一地址时,普通双口SAM指向的必定是存储体内的同一存储单元,而乒乓ram结构则分别指向属于SRAM1和SRAM2的两个不同的存储单元,更易操作。乒乓缓存结构的上述特点决定了可以相对较便宜的高速大容量SRAM、外围逻辑器件构成比双口RAM以及高速FIFO更适合视频处理的系统所需要的缓冲存储器。
下图为乒乓RAM的模块示意图。先在时钟控制下输入两路信号,经过粗略处理,产生两路数据线和地址线,以及两个RAM模块的控制线,分别控制两个RAM的读和写,并且两个RAM的读(或者写)互锁,即一个若处于读状态,则另一个处于写状态。最后把另一个RAM保存的数据经过一个二选一模块输出,分时复用,产生在时间上连续的数据流输出

在这里插入图片描述

代码编写

我们采用乒乓ram结构编写一个模块,ping-pong buffer不是独立的模块,需要根据具体的使用场景进行设计输入输出缓存电路。系统主要根据sel,rden和wren进行操作,当sel拉高时,a、b模块在wren和rden有效时分别写和读;当sel拉低时,a、b模块在rdenwren有效时分别读和写

ram模块



// Engineer: Reborn Lee
// Module Name: single_port_syn_ram
module sram#(
	parameter ADDR_WIDTH = 4,   //地址位宽
	parameter DATA_WIDTH = 8,  // 数据位宽
	parameter DEPTH = 2**ADDR_WIDTH    //ram 列表长度
	)(
	input  i_clk,                        //时钟信号
	input [ADDR_WIDTH - 1 : 0] addr,    // ram 地址参数
	input [DATA_WIDTH - 1 : 0] din,    //这个是一个零时变量法,位宽与ram 一样,用于存放缓存的数据
	
	input wr_en,                            // 写使能
	input rd_en   ,                         // output enable,输出使能时RAM读取的结果才能输出
    output reg[DATA_WIDTH - 1 : 0]  dout
    );

    reg [DATA_WIDTH - 1 : 0] mem[0 : DEPTH - 1];   //ram 的位宽 是 16  DATA_WIDTH ,列表的长度是 DEPTH  16
   
	// write part                                 //写数据部分
	always@(posedge i_clk) begin                  //在时钟上升沿
		if(wr_en&&!rd_en) begin                          //如果  cs=1 并且 写 wr=1  使能  ,则操作数据的写
			mem[addr] <= din;                   //数据放在缓存寄存器
		end
		else ;
	end
	// read part
	always@(posedge i_clk) begin              //在时钟上升沿
		if(rd_en&&!wr_en) begin           //如果  cs=1 并且 写 wr=0  使能  ,则操作数据的读数据,这里一共只有两个状态,
		                             //读和写,这里并没有 读使能 read_enable 
			dout <= mem[addr];   //数据放在缓存寄存器
		end
		else ;
	end

endmodule

乒乓顶层模块:

module m_pp_fifo#(
	parameter addr_width = 4,   //地址位宽
	parameter DATA_WIDTH = 8,  // 数据位宽
	parameter DEPTH = 2**addr_width    //ram 列表长度
	)
	(input clk,input rstn,
	input  rden, input wren,
	input [addr_width-1:0]raddr,input [addr_width-1:0]waddr,
	input [DATA_WIDTH-1:0]din,input sel,
	output [DATA_WIDTH - 1:0]  dout,dout1,dout2
);
wire[DATA_WIDTH-1:0] im_dout1,im_dout2,rm_dout1,rm_dout2;
wire wr1en,wr2en,rd1en,rd2en;
wire [addr_width-1:0] addr1, addr2;
assign im_dout1=sel?din:'dx;
assign im_dout2=sel?'dx:din;//set input mux
assign wr1en=(sel&&wren);
assign rd1en=(!sel&&rden);
assign wr2en=(!sel&&wren);
assign rd2en=(sel&&rden);//set data operate signal. when sel wr r1 and rd r2,else  rd r1 and wr r2
assign addr1 = sel ? waddr : raddr;
assign addr2 = sel ? raddr : waddr;//set data operate addr
reg sel_dl1;
assign dout=(sel_dl1)?rm_dout2:rm_dout1;
assign dout1=rm_dout1,dout2=rm_dout2;
always @(posedge clk or negedge rstn) begin
	if(!rstn)
	begin
		sel_dl1<='d0;
	end
	else begin
		sel_dl1<=sel;
	end
end
sram sram_u1(
	.i_clk(clk),
    .wr_en(wr1en),
    .rd_en(rd1en),
    .addr(addr1),
    .din(im_dout1),
    .dout(rm_dout1)
);
sram sram_u2(
	.i_clk(clk),
    .wr_en(wr2en),
    .rd_en(rd2en),
    .addr(addr2),
    .din(im_dout2),
    .dout(rm_dout2)
);
endmodule

testbench文件:

`timescale 1ns/1ps
module m_pp_fifo_tb();
parameter addr_width = 4;   //地址位宽
parameter DATA_WIDTH = 8;  // 数据位宽
parameter DEPTH = 2**addr_width ;   //ram 列表长度
reg clk, rstn,rden, sel,wren;
reg[addr_width-1:0]raddr,waddr;
reg [DATA_WIDTH-1:0]din;
wire [DATA_WIDTH - 1:0]  dout,dout1,dout2;
m_pp_fifo pp_fifo_u(
    .clk(clk),
    .rstn(rstn),
    .rden(rden),      
    .wren(wren),      
    .sel(sel),       
    .din(din),
    .raddr(raddr),
    .waddr(waddr),
    .dout(dout),
    .dout1(dout1),
    .dout2(dout2)
);
initial begin
    #0;
    clk = 0;
    rstn = 1;
    #10;
    rstn = 0;
    #10;
    rstn = 1;
end
always
    #5 clk = ~clk;
integer i,j;



initial
begin
    #0;
    wren = 0;
    #20;
    wren = 1;
    repeat(1)
    begin
    for(i=0;i<16;i=i+1)
    begin
        @(posedge clk)
        begin
            waddr = i;
            din = i;
        end
    end
    for(i=0;i<16;i=i+1)
    begin
        @(posedge clk)
        begin
            waddr = i;
            din = 15 - i;
        end
    end
end
    @(posedge clk);
    #70 wren = 0;
end

initial
begin
    #0;
    rden = 0;
    #190;
    rden = 1;
    repeat(3)
    for(j=0;j<16;j=j+1)
        @(posedge clk)
        raddr = j;
    @(posedge clk)
    rden = 0;
    #50;
end
initial
begin
    #0;
        sel = 1;
    #190;
        sel = 0;
    repeat(2)
    #160 sel = ~sel;
end

endmodule

这里对各个端口进行说明 clk是系统时钟, rstn是复位,rden是读使能, sel是选择ram模块,wren是写使能,raddr是读地址,waddr是写地址,din是输入数据, dou是输出数据t,dout1是ram1的输出,dout2是ram2的输出,dout1和dout2可以去掉不影响功能这里保留是为了观察各个信号之间的变化关系
modelsim仿真结果:
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值