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
相关文章
- 使用AutoMapper 处理DTO数据对象的转换
- mybatis --- 如何相互转换逗号分隔的字符串和List
- Java实现 LeetCode 756 金字塔转换矩阵(DFS)
- Java实现 LeetCode 678 有效的括号字符串(暴力+思路转换)
- Java实现 LeetCode 538 把二叉搜索树转换为累加树(遍历树)
- Java实现蓝桥杯单位转换
- java实现 洛谷 P1017 进制转换
- Java实现 蓝桥杯 算法训练 大小写转换
- 用C语言实现中文到unicode码的转换
- python轻松实现代码编码格式转换
- LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
- Linux查看文件编码格式及文件编码转换
- ImageMagick 转换图片格式
- Excel表格文本格式的数字和数字格式如何批量转换
- 装箱转换
- PHP进制转换[实现2、8、16、36、64进制至10进制相互转换]
- 【基础入门题068】数制转换(八)
- Atitit 加强学生就业的规划与艾提拉的治学理念 目录 1. 思路的转换1 1.1. 发展内需为主模型 vs 外贸模式1 1.2. 批发模式vs 零售模式vs1 1.3. 天堂模式vs地狱模
- atitit.XML类库选型及object 对象bean 跟json转换方案
- atitit.商业版 源码保护 与 java本地原生代码转换 的方案总结
- flutter 字节和模型的转换
- ZZNUOJ_用C语言编写程序实现1141:进制转换(附完整源码)
- ZZNUOJ_用C语言编写程序实现1718:大小写转换(附完整源码)
- Python语言学习:Python语言学习之python包/库package的简介(模块的封装/模块路径搜索/模块导入方法/自定义导入模块实现华氏-摄氏温度转换案例应用)、使用方法、管理工具之详细攻略
- 【华为OD机试】1029 - 整数与IP地址间的转换
- 【LeetCode Python实现】8. 字符串转换整数 (atoi)(中等)
- PHP:Laravel cast array json数据存数据库时unicode 编码问题和update更新不触发数据转换
- dateTime格式转换
- 将peaks转换为基因活跃矩阵
- PE格式:实现VA与FOA之间的转换