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


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

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

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

    1. module dual_port_RAM #(parameter DEPTH = 16,
    2. parameter WIDTH = 8)(
    3. input wclk ,
    4. input wenc ,
    5. input [$clog2(DEPTH)-1:0] waddr ,
    6. input [WIDTH-1:0] wdata ,
    7. input rclk ,
    8. input renc ,
    9. input [$clog2(DEPTH)-1:0] raddr ,
    10. output reg [WIDTH-1:0] rdata
    11. );
    12. reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
    13. always @(posedge wclk) begin
    14. if(wenc)
    15. RAM_MEM[waddr] <= wdata;
    16. end
    17. always @(posedge rclk) begin
    18. if(renc)
    19. rdata <= RAM_MEM[raddr];
    20. end
    21. endmodule

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

    1. module hand_dp_ram #(
    2. parameter DEPTH = 16,
    3. parameter WIDTH = 8
    4. )(
    5. input clk,
    6. input rst_n,
    7. input wvalid,
    8. output wready,
    9. input [$clog2(DEPTH) -1:0]waddr,
    10. input [WIDTH -1:0]wdata,
    11. input arvalid,
    12. output arready,
    13. input [$clog2(DEPTH) -1:0]araddr,
    14. output rvalid,
    15. input rready,
    16. output [WIDTH -1:0]rdata
    17. );

    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打拍时序优化

    1. wire out_ready_en = data_in_valid || data_out_ready;
    2. wire out_ready_d = data_out_ready;
    3. wire out_ready_q;
    4. dffse #(.WIDTH(1), .VALUE(1'b1))
    5. u_out_ready_dffse(
    6. .clk(clk),
    7. .rst_n(rst_n),
    8. .d(out_ready_d),
    9. .en(out_ready_en),
    10. .q(out_ready_q)
    11. );
    12. 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里;

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

    1. module hand_dp_ram #(
    2. parameter DEPTH = 16,
    3. parameter WIDTH = 8
    4. )(
    5. input clk,
    6. input rst_n,
    7. input wvalid,
    8. output wready,
    9. input [$clog2(DEPTH) -1:0]waddr,
    10. input [WIDTH -1:0]wdata,
    11. input arvalid,
    12. output arready,
    13. input [$clog2(DEPTH) -1:0]araddr,
    14. output rvalid,
    15. input rready,
    16. output [WIDTH -1:0]rdata
    17. );
    18. //***********************************************
    19. // ram inst
    20. //***********************************************
    21. wire ram_wenc;
    22. wire [$clog2(DEPTH) -1:0]ram_waddr;
    23. wire [WIDTH -1:0]ram_wdata;
    24. wire ram_renc;
    25. wire [$clog2(DEPTH) -1:0]ram_raddr;
    26. wire [WIDTH -1:0]ram_rdata;
    27. dual_port_RAM #(
    28. .DEPTH(DEPTH),
    29. .WIDTH(WIDTH))
    30. u_ram(
    31. .wclk(clk),
    32. .wenc(ram_wenc),
    33. .waddr(ram_waddr),
    34. .wdata(ram_wdata),
    35. .rclk(clk),
    36. .renc(ram_renc),
    37. .raddr(ram_raddr),
    38. .rdata(ram_rdata)
    39. );
    40. //***********************************************
    41. // write path
    42. //***********************************************
    43. assign ram_wenc = wvalid && wready;
    44. assign ram_waddr = waddr;
    45. assign ram_wdata = wdata;
    46. //***********************************************
    47. // read path
    48. //***********************************************
    49. assign ram_renc = arvalid && arready;
    50. assign ram_raddr = araddr;
    51. //***********************************************
    52. // read pipe
    53. //***********************************************
    54. wire ram_renc_ff;
    55. dffr #(.WIDTH(1)) u_renc_ff(
    56. .clk(clk),
    57. .rst_n(rst_n),
    58. .d(ram_renc),
    59. .q(ram_renc_ff)
    60. );
    61. wire pipe_in_valid = ram_renc_ff;
    62. wire pipe_in_ready;
    63. bw_pipe #(
    64. .WIDTH(WIDTH))
    65. u_pipe(
    66. .clk(clk),
    67. .rst_n(rst_n),
    68. .data_in(ram_rdata),
    69. .data_in_valid(pipe_in_valid),
    70. .data_in_ready(pipe_in_ready),
    71. .data_out(rdata),
    72. .data_out_valid(rvalid),
    73. .data_out_ready(rready)
    74. );
    75. //***********************************************
    76. // out logic
    77. //***********************************************
    78. assign wready = 1'b1;
    79. assign arready = rready || (~pipe_in_valid && pipe_in_ready);
    80. endmodule

    进行了简单的仿真:

    功能与预期是一致的。

    PS.

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

  • 相关阅读:
    Python中tuple()函数的用法
    路由器二次开发一步一步把工业路由器变成一个高端的可指定出网、节点和链路的路由器,包含详细过程及快捷脚本(二)
    [免费专栏] Android安全之Android Fragment注入
    【10.26】集美大学第九届程序设计竞赛(同步赛)
    网络链接失败怀疑是服务器处于非正常状态?如何用本地电脑查看服务器是否正常?
    【3D图像分割】基于Pytorch的 VNet 3D 图像分割4(改写数据流篇)
    数据库系统工程师难考吗?
    测试集为什么不能当做验证集使用?
    Python 文件读写操作区别案例(r、r+、rb、w、w+、wb、a、a+、ab)
    大学生网页设计制作作业实例代码 (全网最全,建议收藏) HTML+CSS+JS
  • 原文地址:https://blog.csdn.net/moon9999/article/details/126669052