zl程序教程

您现在的位置是:首页 >  其他

当前栏目

FIFO实现位宽转换

转换 实现 FIFO
2023-09-14 09:10:01 时间

之前在博客中实现了位宽相同的同步FIFO,然而,实际的应用场景往往更加复杂,它不仅需要FIFO实现先入先出的功能,有时候还需要实现数据位宽的转换,本文就介绍了如何通过Verilog实现一个具有位宽转换功能的同步FIFO。

写数据低位宽,读数据高位宽

在这种情况下,需要写入RDATA_WIDTH/WDATA_WIDTH个数据,才能读出一个数据,相应的RTL代码如下(主要设置了一个wr_cnt计数器):

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/02/28 23:17:35
// Design Name: 
// Module Name: fifo_low2high
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module fifo_low2high
#(parameter WDATA_WIDTH = 8,
  parameter RDATA_WIDTH = 32,
  parameter WDEPTH =128)
(
input logic clk,
input logic rst,
input logic wr_en,
input logic rd_en,
input logic [WDATA_WIDTH-1:0] wdata,
output logic [RDATA_WIDTH-1:0] rdata,
output logic full,
output logic empty
    );
parameter RDEPTH=WDATA_WIDTH*WDEPTH/RDATA_WIDTH;
parameter ADDR_LEN=$clog2(RDEPTH);
//
logic [ADDR_LEN-1:0] wr_addr;
logic [ADDR_LEN-1:0] rd_addr;                                 //以读数据位宽为单位的地址
logic [ADDR_LEN:0] wr_ptr;
logic [ADDR_LEN:0] rd_ptr;
logic [31:0] wr_cnt;     
logic [RDATA_WIDTH-1:0] RAM [0:RDEPTH-1];                                      //32/8=4,需要二位
//
assign wr_addr=wr_ptr[ADDR_LEN-1:0];
assign rd_addr=rd_ptr[ADDR_LEN-1:0];
//
always_ff@(posedge clk,posedge rst)
if(rst)
    wr_ptr<=0;
else if(wr_en&&~full&&wr_cnt==RDATA_WIDTH/WDATA_WIDTH-1)   //!
    wr_ptr<=wr_ptr+1;
//rd_ptr
always_ff@(posedge clk,posedge rst)
if(rst)
    rd_ptr<=0;
else if(rd_en&&~empty)
    rd_ptr<=rd_ptr+1;
//read data
always_ff@(posedge clk)
if(rd_en&&~empty)
    rdata<=RAM[rd_addr];
//write data
always_ff@(posedge clk)
if(wr_en&&~full)
case(wr_cnt)
    2'b00:RAM[wr_addr][31:24]<=wdata;
    2'b01:RAM[wr_addr][23:16]<=wdata;
    2'b10:RAM[wr_addr][15:8]<=wdata;
    2'b11:RAM[wr_addr][7:0]<=wdata;
endcase
//wr_cnt
always_ff@(posedge clk,posedge rst)
if(rst)
    wr_cnt<=0;
else if(wr_en&&~full)
if(wr_cnt==RDATA_WIDTH/WDATA_WIDTH-1)
    wr_cnt<=0;
else
    wr_cnt<=wr_cnt+1;
//full
assign full=(wr_ptr=={~rd_ptr[ADDR_LEN],rd_ptr[ADDR_LEN-1:0]})?1'b1:1'b0;
assign empty=(wr_ptr==rd_ptr)?1'b1:1'b0;

endmodule

相应的测试平台:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/02/28 23:38:01
// Design Name: 
// Module Name: test_tb_low2high
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module test_tb_low2high;
parameter WDATA_WIDTH = 8;
parameter RDATA_WIDTH = 32;

logic clk;
logic rst;
logic wr_en;
logic rd_en;
logic [WDATA_WIDTH-1:0] wdata;
logic [RDATA_WIDTH-1:0] rdata;
logic full;
logic empty;
logic wr_en_r;
logic rd_en_r;
logic error;
logic rd_en_ff;
logic [RDATA_WIDTH-1:0] ref_data;
//clk
initial begin
    clk=0;
    forever begin
        #5 clk=~clk;
    end
end
//rst
initial
begin
    rst=1;
    #100
    rst=0;
end
//wr_en_r
always_ff@(posedge clk,posedge rst)
if(rst)
    wr_en_r<=0;
else if($urandom%100<90)
    wr_en_r<=1;
else
    wr_en_r<=0;
//rd_en_r
always_ff@(posedge clk,posedge rst)
if(rst)
    rd_en_r<=0;
else if($urandom%100<20)
    rd_en_r<=1;
else 
    rd_en_r<=0;
//rd_en,wr_en
assign rd_en=(~empty)?rd_en_r:1'b0;
assign wr_en=(~full)?wr_en_r:1'b0;
//wdata
always_ff@(posedge clk,posedge rst)
if(rst)
    wdata<=0;
else if(wr_en&&~full)
    wdata<=wdata+1;
//rd_en_ff
always_ff@(posedge clk)
   rd_en_ff<=rd_en;
//error
always_ff@(posedge clk,posedge rst)
if(rst)
    error<=0;
else if(rd_en_ff&&rdata!=ref_data)
    error<=1;
//ref_data
always_ff@(posedge clk,posedge rst)
if(rst)
    ref_data<=32'h00010203;
else if(rd_en_ff)
    ref_data<={ref_data[31:24]+8'd4,ref_data[23:16]+8'd4,ref_data[15:8]+8'd4,ref_data[7:0]+8'd4};
  
//inst
fifo_low2high
#(.WDATA_WIDTH(WDATA_WIDTH),
  .RDATA_WIDTH(RDATA_WIDTH),
  .WDEPTH (128))
U(.*
// input logic clk,
// input logic rst,
// input logic wr_en,
// input logic rd_en,
// input logic [WDATA_WIDTH-1:0] wdata,
// output logic [RDATA_WIDTH-1:0] rdata,
// output logic full,
// output logic empty
    );
endmodule

写数据高位宽,读数据低位宽

这种情况下,平均读取WDATA_WIDTH/RDATA_WIDTH个数据,才能将写入的一个数据清空。其相应的代码如下:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/02/28 20:59:43
// Design Name: 
// Module Name: fifo
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module fifo
#(parameter WDATA_WIDTH = 32,                       //assert WDATA_WIDTH>RDATA_WIDTH
  parameter RDATA_WIDTH = 8,
  parameter WDEPTH = 32)
(
input logic clk,
input logic rst,
input logic wr_en,
input logic [WDATA_WIDTH-1:0] wdata,
input logic rd_en,
output logic [RDATA_WIDTH-1:0] rdata,
output logic full,
output logic empty
    );
//
parameter RDEPTH=WDATA_WIDTH*WDATA_WIDTH/RDATA_WIDTH;
//
logic  [WDATA_WIDTH-1:0] RAM [0:WDEPTH-1];
logic [$clog2(WDEPTH)-1:0] wr_addr;                                                          //无论读写,地址按照高位宽(此处是WDATA_WIDTH)计算
logic [$clog2(WDEPTH)-1:0] rd_addr;                                //RAM地址
logic [$clog2(WDEPTH):0] wr_ptr;                                   //增加了额外bit的指针
logic [$clog2(WDEPTH):0] rd_ptr;
logic [31:0] rd_cnt;
//rd_cnt
always_ff@(posedge clk,posedge rst)
if(rst)
    rd_cnt<=0;
else if(rd_en&&~empty)
if(rd_cnt==WDATA_WIDTH/RDATA_WIDTH-1)
    rd_cnt<=0;                           //0->1->2->3->0
else
   rd_cnt<=rd_cnt+1;
//wr_addr,rd_addr
assign wr_addr=wr_ptr[$clog2(WDEPTH)-1:0];
assign rd_addr=rd_ptr[$clog2(WDEPTH)-1:0];
//wr_ptr
always_ff@(posedge clk,posedge rst)
if(rst)
    wr_ptr<=0;
else if(wr_en&&~full)              //非满且写使能
    wr_ptr<=wr_ptr+1;              //自增1,指向下一个要写的数据
//rd_ptr
always_ff@(posedge clk,posedge rst)
if(rst)
    rd_ptr<=0;
else if(rd_en&&~empty&&rd_cnt==WDATA_WIDTH/RDATA_WIDTH-1)                       //
    rd_ptr<=rd_ptr+1;                                   //自增1,指向下一个要读的数据,读出4个数据才能自增1
//full,empty
assign full=(rd_ptr=={~wr_ptr[$clog2(WDEPTH)],wr_ptr[$clog2(WDEPTH)-1:0]})?1'b1:1'b0;
assign empty=(rd_ptr==wr_ptr)?1'b1:1'b0;
//rdata
always_ff@(posedge clk)
if(rd_en&&~empty)
begin
case(rd_cnt)
    2'b00:rdata<=RAM[rd_addr][31:24];              //先读取高字节
    2'b01:rdata<=RAM[rd_addr][23:16];
    2'b10:rdata<=RAM[rd_addr][15:8];
    2'b11:rdata<=RAM[rd_addr][7:0];
endcase
end
//wdata
always_ff@(posedge clk)
if(wr_en&&~full)
    RAM[wr_addr]<=wdata;

endmodule

相应的测试平台:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/02/28 21:21:38
// Design Name: 
// Module Name: test_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module test_tb;
parameter WDATA_WIDTH = 32;
parameter RDATA_WDITH = 8;
logic clk;
logic rst;
logic [WDATA_WIDTH-1:0] wdata;
logic [RDATA_WDITH-1:0] rdata;
logic wr_en;
logic rd_en;
logic full;
logic empty;
logic wr_en_r;
logic rd_en_r;
logic error;
logic rd_en_ff;
logic [RDATA_WDITH-1:0] ref_rdata;
//clk
initial begin
    clk=0;
    forever begin
        #5 clk=~clk;
    end
end
//rst
initial
begin
    rst=1;
    #100
    rst=0;
end
//wr_en,rd_en
assign wr_en=(~full)?wr_en_r:1'b0;
assign rd_en=(~empty)?rd_en_r:1'b0;
//ref_rdata
always_ff@(posedge clk,posedge rst)
if(rst)
    ref_rdata<=0;
else if(rd_en_ff)
    ref_rdata<=ref_rdata+1;
//
always_ff@(posedge clk)
    rd_en_ff<=rd_en;
//compare
always_ff@(posedge clk,posedge rst)
if(rst)
    error<=0;
else if(rd_en_ff&&ref_rdata!=rdata)
    error<=1;
//wr_en_r
always_ff@(posedge clk,posedge rst)
if(rst)
    wr_en_r<=0;
else
begin
if($urandom%100<25)
    wr_en_r<=1;
else
    wr_en_r<=0;
end
//rd_en_r
always_ff@(posedge clk,posedge rst)
if(rst)
    rd_en_r<=0;
else
begin
if($urandom%100<80)
    rd_en_r<=1;
else
    rd_en_r<=0;
end
//wdata
always_ff@(posedge clk,posedge rst)
if(rst)
    wdata<={8'd0,8'd1,8'd2,8'd3};
else if(wr_en&&~full)
    wdata<={wdata[31:24]+8'd4,wdata[23:16]+8'd4,wdata[15:8]+8'd4,wdata[7:0]+8'd4};
//inst
fifo 
#(.WDATA_WIDTH(32),                       //assert WDATA_WIDTH>RDATA_WIDTH
  .RDATA_WIDTH(8),
  .WDEPTH(32)
)U
(.*
// input logic clk,
// input logic rst,
// input logic wr_en,
// input logic [WDATA_WIDTH-1:0] wdata,
// input logic rd_en,
// output logic [RDATA_WIDTH-1:0] rdata,
// output logic full,
// output logic empty
    );
endmodule