0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

握手型接口的同步FIFO实现

冬至子 来源:芯时代青年 作者:尼德兰的喵 2023-12-04 14:03 次阅读

按照正常的思路,在前文完成前向时序优化和后向时序优化后,后面紧跟的应该是双向时序优化策略了,不过不急,需要先实现一下握手型同步FIFO。本章所述的FIFO具备以下的特性:

1.写入与读出均采用握手型接口
2.支持2的整数次与非整数次深度;
3.FIFO写入数据至少一拍后才能读出,不能bypass;
4.输出逻辑为寄存器输出;

接口

握手型同步FIFO,接口如下:

module sync_fifo #(
	parameter DEPTH = 8,
	parameter WIDTH = 32
)(
    input 		clk,
    input 		rst_n,
	
    input  		in_valid,
    input  [WIDTH -1:0] in_data,
    output 		in_ready,
	
    output		out_valid,
    output [WIDTH -1:0] out_data,
    input  		out_ready
);

localparam DP_WD = $clog2(DEPTH);

endmodule

鉴于并非所有的工具和版本都能很好的支持$clog2函数,所以也可以将该函数展开来写。

根据通常的设计要求,对外输出的in_ready和out_valid最优为寄存器输出,以保证时序最优。out_data虽然理论上要作为寄存器输出,但是out_data可以接受有mux逻辑。不过这样在深度较浅时时序会比较好,深度太深mux逻辑带来的时序则不能忽略。

读写计数器

fifo采用传统的读写两个计数器模式,以写计数器为例进行说明,代码如下:

//==================================================================
//写入计数器
//==================================================================
reg  [DP_WD   :0]waddr;
wire             wenc;
wire             waddr_d_h;
wire [DP_WD -1:0]waddr_d_l;

assign wenc      = in_valid && in_ready;
assign waddr_d_h = (waddr[DP_WD-1:0] == DEPTH-1) ? ~waddr[DP_WD] : waddr[DP_WD];
assign waddr_d_l = (waddr[DP_WD-1:0] == DEPTH-1) ? {DP_WD{1'b0}} : waddr[DP_WD-1:0] + 1'b1;

always @(posedge clk or negedge rst_n)begin
	if(~rst_n)    waddr <= 0;
	else if(wenc) waddr <= {waddr_d_h, waddr_d_l};
end

地址指针waddr和raddr均比实际地址多一位,最高位用来指示套圈情况。当waddr和raddr的最高位相同时,fifo_cnt = waddr - raddr;当waddr和raddr的最高位相反时,fifo_cnt = DEPTH + waddr[ADDR_WIDTH-1:0] - raddr[ADDR_WIDTH-1:0]。

注意很多示意代码会让waddr一直累加翻转,这样的做法对于2^N深度的FIFO是没有问题的,而如果配置深度为非2^N深度则会出现跳转错误,因此在本代码中将addr区分为H和L两个区间,低位区间累加至DEPTH-1时高位区间翻转。

读出计数器的代码同样如此:

//==================================================================
//读出计数器
//==================================================================
reg  [DP_WD   :0]raddr;
wire             renc;
wire             raddr_d_h;
wire [DP_WD -1:0]raddr_d_l;
assign renc = out_valid && out_ready;
assign raddr_d_h = (raddr[DP_WD-1:0] == DEPTH-1) ? ~raddr[DP_WD] : raddr[DP_WD];
assign raddr_d_l = (raddr[DP_WD-1:0] == DEPTH-1) ? {DP_WD{1'b0}} : raddr[DP_WD-1:0] + 1'b1;
always @(posedge clk or negedge rst_n)begin
	if(~rst_n)    raddr <= 0;
	else if(renc) raddr <= {raddr_d_h, raddr_d_l};
end

深度计数器

通常FIFO的深度计数是通过waddr和raddr计算得到的,不过本文因为希望关键输出为寄存器输出需要用到fifo_cnt_d,因此做了一个深度计数器:

//==================================================================
//深度计数器
//==================================================================
reg  [DP_WD :0]fifo_cnt_q;

wire [DP_WD :0]waddr_d = wenc ? {waddr_d_h, waddr_d_l} : waddr;
wire [DP_WD :0]raddr_d = renc ? {raddr_d_h, raddr_d_l} : raddr;
wire [DP_WD :0]fifo_cnt_d = (waddr_d[DP_WD] == raddr_d[DP_WD]) ? (waddr_d[DP_WD-1:0] - raddr_d[DP_WD-1:0]):
															     (waddr_d[DP_WD-1:0] + DEPTH - raddr_d[DP_WD-1:0]);
wire fifo_cnt_en = (wenc ^ renc);
always @(posedge clk or negedge rst_n)begin
	if(~rst_n)    fifo_cnt_q <= 0;
	else if(fifo_cnt_en) fifo_cnt_q <= fifo_cnt_d;
end

数据寄存

数据寄存采用无复位寄存器以降低功耗,当然也可以使用sram实现,但是如果通过sram实现的话就需要考虑ram输出的多一拍延迟了,如果深度不是很深的话,建议直接通过寄存器实现吧。

//==================================================================
//数据寄存
//==================================================================
reg [WIDTH -1:0]data[DEPTH];
always @(posedge clk or negedge rst_n)begin
	if(wenc) data[waddr[DP_WD-1:0]] <= in_data;
end
assign out_data = data[raddr[DP_WD-1:0]];

对外逻辑

in_ready实际就是常用FIFO的full信号取反,out_valid就是empty的取反,因此单纯做逻辑是不难的。但是为了实现寄存器输出,最后选择了这样的逻辑:

//==================================================================
//对外逻辑
//==================================================================
//assign in_ready  = (fifo_cnt_q < DEPTH);
//assign out_valid = (fifo_cnt_q > {DP_WD{1'b0}});
wire in_ready_en;
wire in_ready_d;
reg  in_ready_q;
assign in_ready_en = (out_valid && out_ready) || in_ready;
assign in_ready_d  = (fifo_cnt_d < DEPTH);
always @(posedge clk or negedge rst_n)begin
	if(~rst_n)          in_ready_q <= 1;
	else if(in_ready_en)in_ready_q <= in_ready_d;
end

wire out_valid_en;
wire out_valid_d;
reg  out_valid_q;
assign out_valid_en = (in_valid && in_ready) || out_valid;
assign out_valid_d  = (fifo_cnt_d > {DP_WD{1'b0}});
always @(posedge clk or negedge rst_n)begin
	if(~rst_n)           out_valid_q <= 0;
	else if(out_valid_en)out_valid_q <= out_valid_d;
end

assign in_ready  = in_ready_q;
assign out_valid = out_valid_q;

好的所有代码就是这些,auto_testbench的结果:

图片

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    30

    文章

    5032

    浏览量

    117741
  • RAM
    RAM
    +关注

    关注

    7

    文章

    1322

    浏览量

    113706
  • 计数器
    +关注

    关注

    32

    文章

    2123

    浏览量

    92987
  • Mux
    Mux
    +关注

    关注

    0

    文章

    37

    浏览量

    23222
  • FIFO电路
    +关注

    关注

    1

    文章

    4

    浏览量

    4876
收藏 人收藏

    评论

    相关推荐

    同步FIFO设计详解及代码分享

    FIFO (先入先出, First In First Out )存储器,在 FPGA 和数字 IC 设计中非常常用。 根据接入的时钟信号,可以分为同步 FIFO 和异步 FIFO
    发表于 06-27 10:24 1322次阅读
    <b class='flag-5'>同步</b><b class='flag-5'>FIFO</b>设计详解及代码分享

    基于PCI接口芯片外扩FIFO的FPGA实现

    介绍了PCI 9054 接口芯片的性能及数据传输特点,提出了一种基于PCI 9054 外扩异步FIFO(先进先出)的FPGA(现场可编程门阵列)实现方法。由于PCI 9054 内部FIFO
    发表于 01-06 15:20 44次下载

    人和机器人握手同步控制

    摘要:为实现人和机器人握手运动的同步,提出基于神经振动子同步控制的方法,并将此方法应用于人和机器人握手的研究中。在现有神经振动子的基础上,设
    发表于 07-12 10:20 29次下载

    Camera Link接口的异步FIFO设计与实现

    介绍了异步FIFO在Camera Link接口中的应用,将Camera Link接口中的帧有效信号FVAL和行有效信号LVAL引入到异步FIFO的设计中。分析了FPGA中设计异步
    发表于 07-28 16:08 32次下载

    基于VHDL和FPGA的非对称同步FIFO设计实现

    本文采用VHDL描述语言,充分利用Xilinx公司Spartan II FPGA的系统资源,设计实现了一种非对称同步FIFO,它不仅提供数据缓冲,而且能进行数据总线宽度的转换。
    发表于 01-13 11:33 1778次阅读

    自定义fifo接口控制器

    自定义fifo接口控制器,利用sopc builder实现
    发表于 03-22 14:09 1次下载

    基于FIFO的高速A_D和DSP接口设计

    基于FIFO的高速A_D和DSP接口设计
    发表于 10-19 14:10 9次下载
    基于<b class='flag-5'>FIFO</b>的高速A_D和DSP<b class='flag-5'>接口</b>设计

    总线半握手跨时钟域处理

    总线半握手跨时钟域处理 简要概述: 在上一篇讲了单bit脉冲同步器跨时钟处理,本文讲述控制信号基于脉冲同步机制的总线单向握手跨时钟域处理。由于是单向
    的头像 发表于 04-04 12:32 2387次阅读
    总线半<b class='flag-5'>握手</b>跨时钟域处理

    同步FIFO之Verilog实现

    FIFO的分类根均FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO
    的头像 发表于 11-01 09:57 1358次阅读

    异步FIFO之Verilog代码实现案例

    同步FIFO的意思是说FIFO的读写时钟是同一个时钟,不同于异步FIFO,异步FIFO的读写时钟是完全异步的。
    发表于 11-01 09:58 1222次阅读

    FIFO设计—异步FIFO

    异步FIFO主要由五部分组成:写控制端、读控制端、FIFO Memory和两个时钟同步
    发表于 05-26 16:17 1050次阅读
    <b class='flag-5'>FIFO</b>设计—异步<b class='flag-5'>FIFO</b>

    一个简单的RTL同步FIFO设计

    FIFO 是FPGA设计中最有用的模块之一。FIFO 在模块之间提供简单的握手同步机制,是设计人员将数据从一个模块传输到另一个模块的常用选择。
    发表于 06-14 08:59 258次阅读

    基于寄存器的同步FIFO

      FIFO 是FPGA设计中最有用的模块之一。FIFO 在模块之间提供简单的握手同步机制,是设计人员将数据从一个模块传输到另一个模块的常用选择。 在这篇文章中,展示了一个简单的 R
    的头像 发表于 06-14 09:02 491次阅读

    同步FIFO设计分析

    模块虽小但是要有新意,首先写一个同步FIFO,这是一个烂大街的入门级项目,但是我肯定不会写的那么简单
    的头像 发表于 09-11 17:11 367次阅读
    <b class='flag-5'>同步</b><b class='flag-5'>FIFO</b>设计分析

    同步FIFO和异步FIFO的区别 同步FIFO和异步FIFO各在什么情况下应用

    简单的一种,其特点是输入和输出都与时钟信号同步,当时钟到来时,数据总是处于稳定状态,因此容易实现数据的传输和存储。 而异步FIFO则是在波形的上升沿和下降沿上进行处理,在输入输出端口处分别增加输入和输出指针,用于管理数据的读写。
    的头像 发表于 10-18 15:23 1016次阅读