当前位置: 首页 > news >正文

【芯片前端】延迟一拍出数的握手型ram结构的一次探索

思路和代码并不一定正确,仅仅是记录一次有意思的结构思考过程,如有疏漏错误,请不吝赐教。

一般而言生成的ram都是通过wenc/renc信号来控制读写,并且读使能后一拍读数据返回,当然,当ram深度较大时可能会延时。这次根据实际场景,ram本身是双口的一拍读取RAM。

用下面的代码来代替ram内部的代码:

module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk	,
	 input wenc	,
	 input [$clog2(DEPTH)-1:0] waddr	,
	 input [WIDTH-1:0] wdata	,
	 input rclk	,
	 input renc	,
	 input [$clog2(DEPTH)-1:0] raddr	,
	 output reg [WIDTH-1:0] rdata
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

我的需求是一个握手型的ram,通过握手信号来控制ram的读写,对应的接口如下:

module hand_dp_ram #(
	parameter DEPTH = 16,
	parameter WIDTH = 8
)(
	input clk,
	input rst_n,
	
	input  					   wvalid,
	output 					   wready,
	input  [$clog2(DEPTH) -1:0]waddr,
	input  [WIDTH         -1:0]wdata,
	
	input  					   arvalid,
	output 					   arready,
	input  [$clog2(DEPTH) -1:0]araddr,
	
	output					   rvalid,
	input					   rready,
	output [WIDTH         -1:0]rdata	
);

Feature List:

  1. wvalid和wready同时有效时,将wdata写入到ram地址中,写入的数据在下一拍生效;
  2. arvalid和arready同时有效时,读取araddr对应ram数据,数据最早在下一拍读出;
  3. 只有当rvalid和rready同时有效时,rdata会被取出,否则rdata需要一直保持。

需求就是这么简单,那么接下来对实现进行分析。

对于dual_port_ram(实际上叫double_port_ram比较好,我理解的dual_port_ram是两个口都可以独立的读写),读取的逻辑时序图如下:

显然,arvalid&&arready有效时,应该发起renc信号进行读取,下一拍rvalid必然是为1。但问题在于,当下一拍数据被读出后,ready的状态是未知的,如果恰好rready为0,那么数据将会丢失。

由于之前没有写过这块代码,也没有深入研究过,因此只能凭借我自己的感觉规划了三种思路:

  1. 在确保下拍没有反压(即下一拍的ready一定为1)时才置高arready;
  2. 在ram后面跟pipe来保持数据;
  3. 当数据没有被取出时,连续发起renc,直到该地址的数据被读取;

我不确认有没有更好的办法,所以只能对自己的这三个思路进行分析。

第一个思路呢,如果想获取到下一拍rready的状态,那么分类一想就会发现,只有当上一拍没有renc且当拍的rready==1才能保证下一拍的rready==1。这样就会造成一个读取效率的问题即只能隔一拍读取一次,因此单纯的这样处理一定是有问题的。

第三个思路呢,我举得功能上应该是可以实现的,但是可能会导致rvalid有效但是没有握手过程中,rdata一直在变化,这不符合握手协议要求,因此暂时把这个思路放在了一边。

最后选定的是第二种思路,通过在ram后面增加一个pipe来进行数据保持。那么应该选择什么类型的pipe呢?需求上,希望rdata从ram中读出来了就尽快发出,在无法发出时保持在pipe中,因此必然是选择backword pipe。

于是,整体的结构就定下来了:dual_port_ram —— bw_pipe。

整体结构定下来后,就又回到了开始的问题,如何预知下一拍ram的出口一定可以接数据呢?经过一个很长时间的探索(一晚上+一早上),我发现这个问题就简化为了一句话:下一拍pipe_in_ready为1时,这拍的arready才能为1。

那么怎么预知下一拍pipe_in_ready为1呢?这不就是bw_pipe里的逻辑么?!

【芯片前端】保持代码手感——握手协议ready打拍时序优化

wire out_ready_en = data_in_valid || data_out_ready;
wire out_ready_d  = data_out_ready;
wire out_ready_q;
dffse #(.WIDTH(1), .VALUE(1'b1))
u_out_ready_dffse(
	.clk(clk),
	.rst_n(rst_n),
	.d(out_ready_d),
	.en(out_ready_en),
	.q(out_ready_q)
);
assign data_in_ready  = out_ready_q;

最后归纳出最后最后的逻辑,显然就是(data_out_ready == 1)|| (data_in_valid == 0 && data_in_ready == 1)。正好对着两个场景:

  1. 下游现在可以接,哪怕你现在pipe里有数,下一拍pipe里也一定是空的,至少保证下一拍的rdata能进pipe,或者直接被下游接走;
  2. 下游现在不能接,但是pipe是空的,且这一拍没有数据要被读到pipe里;

基于这个思路,最后读通道的代码组织形式就出来了,放一下完整的代码:

module hand_dp_ram #(
	parameter DEPTH = 16,
	parameter WIDTH = 8
)(
	input clk,
	input rst_n,
	
	input  					   wvalid,
	output 					   wready,
	input  [$clog2(DEPTH) -1:0]waddr,
	input  [WIDTH         -1:0]wdata,
	
	input  					   arvalid,
	output 					   arready,
	input  [$clog2(DEPTH) -1:0]araddr,
	
	output					   rvalid,
	input					   rready,
	output [WIDTH         -1:0]rdata	
);

//***********************************************
// ram inst
//***********************************************
wire 					 ram_wenc;
wire [$clog2(DEPTH) -1:0]ram_waddr;
wire [WIDTH         -1:0]ram_wdata;
wire 					 ram_renc;
wire [$clog2(DEPTH) -1:0]ram_raddr;
wire [WIDTH         -1:0]ram_rdata;

dual_port_RAM #(
	.DEPTH(DEPTH),
	.WIDTH(WIDTH))
u_ram(
	 .wclk(clk),
	 .wenc(ram_wenc),
	 .waddr(ram_waddr),
	 .wdata(ram_wdata),
	 .rclk(clk),
	 .renc(ram_renc),
	 .raddr(ram_raddr),
	 .rdata(ram_rdata)
);

//***********************************************
// write path
//***********************************************
assign ram_wenc  = wvalid && wready;
assign ram_waddr = waddr;
assign ram_wdata = wdata;

//***********************************************
// read path
//***********************************************
assign ram_renc  = arvalid && arready;
assign ram_raddr = araddr;

//***********************************************
// read pipe
//***********************************************
wire ram_renc_ff;
dffr #(.WIDTH(1)) u_renc_ff(
	.clk(clk),
	.rst_n(rst_n),
	.d(ram_renc),
	.q(ram_renc_ff)
);

wire pipe_in_valid = ram_renc_ff;
wire pipe_in_ready;
bw_pipe #(
	.WIDTH(WIDTH))
u_pipe(
	.clk(clk),
	.rst_n(rst_n),
	
	.data_in(ram_rdata),
	.data_in_valid(pipe_in_valid),
	.data_in_ready(pipe_in_ready),
	
	.data_out(rdata),
	.data_out_valid(rvalid),
	.data_out_ready(rready)
);

//***********************************************
// out logic
//***********************************************
assign wready  = 1'b1;
assign arready = rready || (~pipe_in_valid && pipe_in_ready);

endmodule

进行了简单的仿真:

功能与预期是一致的。

PS.

在组织代码的过程中波形一直和预想的不一样。分析了半天后发现bw_pipe一四有问题于是愤怒的加了一笔逻辑就对了。然后赶紧查自己的博客有没有写错,发现博客写的跟添了一笔的代码一摸一样,gitee上的代码留错了/(ㄒoㄒ)/~~白花了2小时。

相关文章:

  • 计算机毕业设计php+vue基于微信小程序的高校新生报到管理小程序
  • jq/h5 实现实时获取大文件下载进度
  • 【从零开始的Java开发】2-9-3 油画商城静态网页案例
  • 计算机毕业设计ssm+vue基本微信小程序的个人健康管理系统
  • 【PTHREAD】线程互斥与同步之互斥锁
  • vscode自动生成testbench
  • 【流放之路-装备制作篇】
  • BLE错误码全面解析连接失败原因错误码解析BLE Disconnect Reason
  • Tensorflow pb模型转tflite,并量化
  • 【PTHREAD】线程状态
  • 网易云音乐项目————项目准备
  • 计算机网络——应用层の选择题整理
  • LabVIEW通过网络传输数据
  • 【PTHREAD】线程属性
  • 如何做好项目管理?项目管理和团队协作是关键
  • 【347天】每日项目总结系列085(2018.01.18)
  • Create React App 使用
  • JavaScript类型识别
  • Javascript设计模式学习之Observer(观察者)模式
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • node-glob通配符
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • SwizzleMethod 黑魔法
  • -- 查询加强-- 使用如何where子句进行筛选,% _ like的使用
  • 基于Android乐音识别(2)
  • 近期前端发展计划
  • 前端之Sass/Scss实战笔记
  • 少走弯路,给Java 1~5 年程序员的建议
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 移动端解决方案学习记录
  • 硬币翻转问题,区间操作
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • NLPIR智能语义技术让大数据挖掘更简单
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • ​人工智能书单(数学基础篇)
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (转)Linux NTP配置详解 (Network Time Protocol)
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • .NET正则基础之——正则委托
  • /usr/bin/env: node: No such file or directory
  • @Conditional注解详解
  • [Ariticle] 厚黑之道 一 小狐狸听故事
  • [AX]AX2012开发新特性-禁止表或者表字段
  • [BUUCTF]-PWN:wustctf2020_number_game解析(补码,整数漏洞)
  • [BZOJ1008][HNOI2008]越狱