FIFO实现位宽转换

之前在博客中实现了位宽相同的同步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

  • 8
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
FIFO(First-In-First-Out)是一种存储数据的先进先出的队列结构。在FIFO读写操作中,如果读写位宽不一致,意味着读取或写入的数据长度和格式不同。 读写位宽不一致可能导致一些问题,例如: 1. 数据丢失:当写入和读取的位宽不一致时,可能会导致数据在写入或读取时发生截断,造成数据丢失。 2. 数据错位:读写位宽不一致可能导致数据在读取时发生错位。例如,如果写入的数据位宽较小,而读取的数据位宽较大,读取时可能会读取到不正确的数据。 3. 数据转换问题:读写位宽不一致可能需要进行数据转换。如果写入和读取的数据位宽不同,那么在读取时需要进行数据格式的转换,这可能会增加系统的复杂性和开销。 为了解决读写位宽不一致的问题,可以采用以下方法: 1. 数据位宽匹配:在FIFO读写操作之前,将写入和读取的数据位宽进行匹配,保证它们一致。可以使用位宽转换器或者数据格式转换器来实现。 2. 数据标志:在FIFO读写操作时,可以使用数据标志来标识不同位宽的数据。在读取时,根据数据标志来确定读取的数据位宽,并进行相应的处理。 3. 数据缓存:如果无法直接解决读写位宽不一致的问题,可以考虑使用数据缓存。在写入时,将数据缓存起来,并进行相应的位宽转换。在读取时,根据需要从缓存中读取相应位宽的数据。 总之,读写位宽不一致可能会导致数据的丢失、错位或需要进行数据转换。为了解决这个问题,可以采用数据位宽匹配、数据标志或数据缓存等方法来保证数据的正确读写。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FPGA硅农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值