Verilog--帶雙向握手的乒乓BUFFER


  網上沒什么比較好的乒乓sram設計,有的還需要收費,於是自己寫了一個Verilog源碼,與大家討論與學習。

 

一:介紹

“ 乒乓操作” 是一個常常應用於數據流控制的處理技巧, 典型的乒乓操作方法如圖 1 所示。

 

 

乒乓操作的處理流程為:輸入數據流通過“ 輸入數據選擇單元” 將數據流等時分配到兩個數據緩沖區, 數據緩沖模塊可以為任何存儲模塊, 比較常用的存儲單元為雙口RAM(DPRAM)、單口RAM(SPRAM)、FIFO等。

 

在第 1個緩沖周期,將輸入的數據流緩存到“ 數據緩沖模塊1” ;

在第2 個緩沖周期, 通過“ 輸入數據選擇單元” 的切換, 將輸入的數據流緩存到“ 數據緩沖模塊2” , 同時將“ 數據緩沖模塊1” 緩存的第1 個周期數據通過“ 輸入數據選擇單元” 的選擇, 送到“ 數據流運算處理模塊” 進行運算處理;

在第3 個緩沖周期通過“ 輸入數據選擇單元” 的再次切換,將輸入的數據流緩存到“ 數據緩沖模塊1” ,同時將“ 數據緩沖模塊2”緩存的第2 個周期的數據通過“ 輸入數據選擇單元” 切換,送到“ 數據流運算處理模塊” 進行運算處理。 如此循環。

 

乒乓操作的最大特點是通過“ 輸入數據選擇單元” 和“ 輸出數據選擇單元” 按節拍、相互配合的切換, 將經過緩沖的數據流沒有停頓地送到“ 數據流運算處理模塊” 進行運算與處理。


把乒乓操作模塊當做一個整體, 站在這個模塊的兩端看數據, 輸入數據流和輸出數據流都是連續不斷的, 沒有任何停頓, 因此非常適合對數據流進行流水線式處理。 所以乒乓操作常常應用於流水線式算法, 完成數據的無縫緩沖與處理。

 

二:設計思想

本次設計的乒乓buffer因為需要存儲的數據量較大,使用了兩個單端口的sram作為存儲,而不是reg(當然要改為reg豈不是更簡單)。sram前后有兩個2選1MUX做選擇,附帶有前后級ready&valid握手信號,使用靈活。

在這里就不給sram model的源碼了,自己在vivado中插入IP就好了。

源碼如下:

module pp_buffer(
//Output 
rd_rdy , wr_rdy , rd_vld , rdata,
//Input
clk , rst_n , rd_en , wr_en , wr_be, raddr , wdata ,waddr);

parameter DEPTH = 64;
parameter DATAWIDTH = 128;

localparam ADDRWIDTH = clogb2(DEPTH-1);
localparam IDLE = 4'b0001;
localparam WRAM1 = 4'b0010;
localparam WRAM2_RRAM1 = 4'b0100;
localparam WRAM1_RRAM2 = 4'b1000;

//----------INPUT/OUTPUT--------------
input clk;
input rst_n;
input rd_en;
input wr_en;
input [DATAWIDTH/8-1:0] wr_be;

output reg rd_rdy;
output reg wr_rdy;
output reg rd_vld;

output reg [DATAWIDTH-1:0] rdata;
input [ADDRWIDTH-1:0] raddr;
input [DATAWIDTH-1:0] wdata;
input [ADDRWIDTH-1:0] waddr;
//---------------reg definitions----------------//
wire [DATAWIDTH-1:0] rdata1;
reg cen1;
reg [DATAWIDTH/8-1:0] wen1;
reg [ADDRWIDTH-1:0] addr1;
reg [DATAWIDTH-1:0] wdata1;

wire [DATAWIDTH-1:0] rdata2;
reg cen2;
reg [DATAWIDTH/8-1:0] wen2;
reg [ADDRWIDTH-1:0] addr2;
reg [DATAWIDTH-1:0] wdata2;

reg [3:0] state,next_state;
reg rd_vld_r;
reg [3:0] state_r;
reg wr_en_r;
//---------------FSM-----------------------//
always@(*)begin
    case(state)
        IDLE:begin
            if(wr_en == 1'b1)
                next_state = WRAM1;
            else
                next_state = IDLE;
        end
        WRAM1:begin
            if(addr1 == {(ADDRWIDTH){1'b1}})
                next_state = WRAM2_RRAM1;
            else
                next_state = WRAM1;
        end
        WRAM2_RRAM1:begin
            if((addr1 == {(ADDRWIDTH){1'b1}}) && (addr2 == {(ADDRWIDTH){1'b1}}) && wr_en_r)
                next_state = WRAM1_RRAM2;
            else
                next_state = WRAM2_RRAM1;                
        end
        WRAM1_RRAM2:begin
            if((addr1 == {(ADDRWIDTH){1'b1}}) && (addr2 == {(ADDRWIDTH){1'b1}}) && wr_en_r)
                next_state = WRAM2_RRAM1;
            else
                next_state = WRAM1_RRAM2;            
        end
        default:next_state = IDLE;
    endcase
end

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        state <= IDLE;
    else begin
        state <= next_state; 
end

//rdata
always@(*)begin
    if(state_r == WRAM2_RRAM1) begin
        rdata = rdata1;
    end
    else if(state_r == WRAM1_RRAM2) begin
        rdata = rdata2;
    end
end
//RAM1 wr_be 

//RAM2 wr_be 

//RAM1 cen waddr wdata

//RAM1 cen waddr wdata

//---------------sub module----------------//
sp_mem#(.DEPTH(DEPTH), .DATAWIDTH(DATAWIDTH))
u_sram0(
.Q(rdata1),
.CLK(clk),
.CEN(cen1),
.WEN(wen1),
.A(addr1),
.D(wdata1)
);

sp_mem#(.DEPTH(DEPTH), .DATAWIDTH(DATAWIDTH))
u_sram1(
.Q(rdata2),
.CLK(clk),
.CEN(cen2),
.WEN(wen2),
.A(addr2),
.D(wdata2)
);

function integer clogb2 (input integer depth);
    integer depth_t;
    begin
        depth_t = depth;
            for(clogb2 = 0; depth_t>0; clogb2 = clogb2+1)
                depth_t = depth_t >>1;
    end
endfunction

endmodule

 上述代碼的缺陷在於狀態機控制邏輯還是寫復雜了,並且入sram時進行了打拍,出sram時沒有打拍,對Timing不友好。這里我還在后級input 了rd_en信號,如果要簡化其實可以砍掉,直接valid輸出,out_rdy握手就好。

 

待有時間了補全源碼

 

還有種使用fifo控制乒乓buffer讀寫的邏輯,大大簡化了這版代碼~,大家可以思考一下~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM