【芯片前端】保持代码手感——握手型同步FIFO设计

简介: 【芯片前端】保持代码手感——握手型同步FIFO设计

前言

工作中做了太久方案,还是要继续对代码手感的保持,这次完成握手型同步FIFO的RTL设计,设计规格如下:


  • 写入与读出均采用握手型接口;
  • 支持2的整数次与非整数次深度;
  • 对外输出逻辑时序较优;

RTL设计

对外接口

握手型同步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


根据我的设计要求,对外输出的in_ready和out_valid必须为寄存器输出,以保证时序最优。out_data最好也要作为寄存器输出,但是out_data必然会有MUX逻辑,在深度较浅时时序会比较好,深度太深MUX逻辑带来的时序就不能忽略了。


逻辑设计

同步FIFO的设计思路在 【芯片前端】保持代码手感——同步FIFO 中已经盘过了,这次注重握手逻辑的规划,对于涉及过得设计思路不再赘述。


写入/读出ADDR逻辑

写入计数器主要调整了触发器en端的逻辑,把输入握手作为使能信号:

//==================================================================
//写入计数器
//==================================================================
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) ? 0 : waddr[DP_WD-1:0] + 1;
always @(posedge clk or negedge rst_n)begin
  if(~rst_n)    waddr <= 0;
  else if(wenc) waddr <= {waddr_d_h, waddr_d_l};
end


读出计数器同理:

//==================================================================
//读出计数器
//==================================================================
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) ? 0 : raddr[DP_WD-1:0] + 1;
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深度计数器

fifo深度的计算我做了一些调整,fifi_cnt也作为寄存器输出,主要是我需要用fifi_cnt_d这个变量,所以是在数据读写的当拍产生fifi_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


数据寄存

数据寄存,寄存器不复位降功耗,出口有一个多路的MUX,WIDTH*DEPTH越大,逻辑越深绕线越难:

//==================================================================
//数据寄存
//==================================================================
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_verification进行仿真,得到波形图:



相关文章
|
1月前
|
缓存 前端开发 JavaScript
利用代码分割优化前端性能:策略与实践
在现代Web开发中,代码分割是提升页面加载性能的有效手段。本文介绍代码分割的概念、重要性及其实现策略,包括动态导入、路由分割等方法,并探讨在React、Vue、Angular等前端框架中的具体应用。
|
20天前
|
缓存 监控 前端开发
探索前端性能优化:关键策略与代码实例
本文深入探讨前端性能优化的关键策略,结合实际代码示例,帮助开发者提升网页加载速度和用户体验,涵盖资源压缩、懒加载、缓存机制等技术。
|
1月前
|
Web App开发 缓存 监控
前端性能优化实战:从代码到部署的全面策略
前端性能优化实战:从代码到部署的全面策略
28 1
|
1月前
|
Web App开发 前端开发 JavaScript
前端性能优化实战:从代码到部署的全面指南
前端性能优化实战:从代码到部署的全面指南
32 1
|
1月前
|
缓存 监控 前端开发
前端性能优化:从代码到部署的全面策略
前端性能优化:从代码到部署的全面策略
|
2月前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
184 2
|
2月前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
49 0
|
2月前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。
|
2月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
2月前
|
机器学习/深度学习 弹性计算 自然语言处理
前端大模型应用笔记(二):最新llama3.2小参数版本1B的古董机测试 - 支持128K上下文,表现优异,和移动端更配
llama3.1支持128K上下文,6万字+输入,适用于多种场景。模型能力超出预期,但处理中文时需加中英翻译。测试显示,其英文支持较好,中文则需改进。llama3.2 1B参数量小,适合移动端和资源受限环境,可在阿里云2vCPU和4G ECS上运行。
127 1