• 【芯片前端】根据数据有效选择输出的握手型FIFO结构探究


    前言

    之前要做一个一读多写的fifo,也就是master写入数据到fifo中,多个slave读取数据,结构如下图所示:

    由于slave需要的数据一致,fifo内只需要例化一个ram以节约空间。这个fifo的具体结构下次博客中再来讨论。在这个fifo之后,又衍生出一个新的需求,就是master写入的数据并非每个slave都需要读取,而是需要甄选,比如trc0是slave0/1/2均需要读取,trc1只有slave1/2需要slave0将知丢弃即可。这个需求也不难,在fifo的出口根据每个slave_en对握手进行处理即可。

    而后,叒衍生了一个需求,就是如果某一slave连续不需要数据,那么需要直接读出下一个需要的数据,中间不要有空泡,对于某一个slave来说简单的示意图如下:

    归结一下需求(不考虑一对多的场景,一对多只需要在一对一的外面裹一些逻辑,暂不展开), 就是fifo写入的数据不是每一个都需要输出,只需要输出需要的数据(令power=1为需要的数据),那么我们由需求得到了下面的接口:

    1. module bypass_fifo #(
    2. parameter DEPTH = 8,
    3. parameter WIDTH = 128
    4. )(
    5. input clk,
    6. input rst_n,
    7. input in_valid,
    8. input [WIDTH -1:0] in_data,
    9. input in_power,
    10. output in_ready,
    11. output out_valid,
    12. output [WIDTH -1:0] out_data,
    13. input out_ready
    14. );

    代码实现

    这个结构的难点在于,读指针不是连续加1,而是需要跳跃性的去找下一个有效的点:

    因此我最开始的做法是写一个for-if结构,if找到了下一个power就停止否则一直找到wr_ptr所在的位置,而确实我也完成了RTL代码并确认了这个写法可以综合。但是后来我进一步的思考,发现这就是一个”找首1”的的结构:

     而“找首1”的结构是有固定套路的因此又往前推进了一点点。接下来还是继续思考这个问题,我记起来为什么我会写“找首1”呢?因为之前写了一个rr调度的代码,里面需要有这个逻辑:

    【芯片前端】与RR调度的相爱相杀——verilog实现RR调度器

     因此我突然灵光乍现,这东西不就是个RR调度吗?再仔细看这个结构,实际上每个power为1对应的项就是需要被调度的一路,为0的项等价于没有调度请求。rd_ptr从0 -> 2的过程,实际就是使第1项被丢弃的过程。

     那么有一个关键问题需要解决,是用keep型的rr调度还是非keep型的呢?keep型与非keep型主要的差别在于ack返回前调度逻辑看到的req是否会改变。在这个场景下个人认为都可以的,因为fifo本身读写指针的控制机智(读指针不能跨越写指针,写指针也不能跨越读指针)以及数据输入的顺序,应该是不用专门使用keep型的调度器。因此我选择了非keep型的调度器。rr调度的接口与行为就不赘述了,直接分析下如何组织外部的逻辑:

    1. //==================================================================
    2. //bitmap维护
    3. //==================================================================
    4. reg [BM_WD -1:0]power_bitmap_q;
    5. wire power_bitmap_en = in_hand_en || out_inner_hand_en;
    6. wire [BM_WD -1:0]power_bitmap_d;
    7. wire [BM_WD -1:0]power_bitmap_in;
    8. wire [BM_WD -1:0]power_bitmap_out;
    9. wire [BM_WD -1:0]power_bitmap_waddr;
    10. wire [BM_WD -1:0]power_bitmap;
    11. assign power_bitmap_in = ({{(BM_WD-1){1'b0}}, (in_hand_en & in_power & 1'b1)} << waddr);//0010
    12. assign power_bitmap_out = ~({{(BM_WD-1){1'b0}}, (out_inner_hand_en & 1'b1)} << raddr);//1011
    13. assign power_bitmap_d = (power_bitmap_q & power_bitmap_out) | power_bitmap_in;
    14. always @(posedge clk or negedge rst_n)begin
    15. if(~rst_n)
    16. power_bitmap_q <= {BM_WD{1'b0}};
    17. else if(power_bitmap_en)
    18. power_bitmap_q <= power_bitmap_d;
    19. end
    20. assign power_bitmap_waddr = ({{(BM_WD-1){1'b0}}, 1'b1} << waddr);
    21. assign power_bitmap = power_bitmap_q | power_bitmap_waddr;

    说一下大概的一个思路:

    1. 有数据写入时(in_hand_en==1),把对应power根据rd_addr写到对应的req位置上去;
    2. 有数据被读出时(out_inner_hand_en),把对应req的位置清为0,因为握手型(我这个)fifo的不会发生同一拍对同一个位置进行读写,因此没有竞争;
    3. waddr所在的那个位置,req必须是1,要不raddr直接跨过去waddr索引到后面肯定是不行的,所以最终的bitmap(也就是req)必须要或一下;

    因此进一步例化rr调度器就可以了:

    1. wire [BM_WD -1:0]grant;
    2. rr_dispatch #(.WD(BM_WD), .KEEP_MODE(0))
    3. u_rr
    4. (
    5. .clk(clk),
    6. .rst_n(rst_n),
    7. .req(power_bitmap),
    8. .ack(out_inner_hand_en),
    9. .grant(grant)
    10. );

    通过grant来获得raddr:

    1. //==================================================================
    2. //读出计数器
    3. //==================================================================
    4. reg [DP_WD :0]raddr_d;
    5. wire renc;
    6. always @* begin: RADDR_D
    7. integer i;
    8. for(i=0; i<BM_WD; i=i+1)begin
    9. if(grant[i] == 1'b1) begin
    10. raddr_d = i;
    11. end
    12. end
    13. end
    14. assign raddr = raddr_d;

    最后的输出逻辑要进一步组织下:

    1. //==================================================================
    2. //输出逻辑
    3. //==================================================================
    4. wire inner_out_real = power_bitmap[raddr];
    5. assign inner_out_valid = (raddr != waddr);
    6. assign inner_out_ready = out_ready || (~inner_out_real);
    7. assign out_valid = inner_out_valid && inner_out_real;
    8. assign in_ready = !((raddr[DP_WD -1:0] == waddr[DP_WD -1:0]) && (raddr[DP_WD] != waddr[DP_WD]));

    什么时候才是真正有效的输出呢(out_valid = 1),必须是power_bitmap[raddr]==1(这里我认为用power_bitmap_q[raddr]更为准确,不过下面用raddr != waddr做了处理,逻辑上也是没有问题的),同时raddr != waddr。对于in_ready而言,和普通的握手型fifo没有区别。

    仿真结果

    截取了部分时间的仿真波形,可以看到如d/11/16/18这些数据并没有输出,且输出数据ready可接时没有空泡。

    完整代码

    1. module bypass_fifo_new #(
    2. parameter DEPTH = 8,
    3. parameter WIDTH = 128
    4. )(
    5. input clk,
    6. input rst_n,
    7. input in_valid,
    8. input [WIDTH -1:0] in_data,
    9. input in_power,
    10. output in_ready,
    11. output out_valid,
    12. output [WIDTH -1:0] out_data,
    13. input out_ready
    14. );
    15. localparam DP_WD = DEPTH == 1 ? 1 : $clog2(DEPTH);
    16. localparam BM_WD = DEPTH*2;
    17. //==================================================================
    18. //公用信号
    19. //==================================================================
    20. wire inner_out_valid;
    21. wire inner_out_ready;
    22. wire in_hand_en = in_valid && in_ready;
    23. wire out_hand_en = out_valid && out_ready;
    24. wire out_inner_hand_en = inner_out_valid && inner_out_ready;
    25. reg [DP_WD :0]waddr;
    26. //reg [DP_WD :0]raddr;
    27. wire [DP_WD :0]raddr;
    28. //==================================================================
    29. //写入计数器
    30. //==================================================================
    31. wire wenc;
    32. wire waddr_d_h;
    33. wire [DP_WD -1:0]waddr_d_l;
    34. assign wenc = in_hand_en;
    35. assign waddr_d_h = (waddr[DP_WD-1:0] == DEPTH-1) ? ~waddr[DP_WD] : waddr[DP_WD];
    36. assign waddr_d_l = (waddr[DP_WD-1:0] == DEPTH-1) ? 0 : waddr[DP_WD-1:0] + 1;
    37. always @(posedge clk or negedge rst_n)begin
    38. if(~rst_n) waddr <= 0;
    39. else if(wenc) waddr <= {waddr_d_h, waddr_d_l};
    40. end
    41. //==================================================================
    42. //bitmap维护
    43. //==================================================================
    44. reg [BM_WD -1:0]power_bitmap_q;
    45. wire power_bitmap_en = in_hand_en || out_inner_hand_en;
    46. wire [BM_WD -1:0]power_bitmap_d;
    47. wire [BM_WD -1:0]power_bitmap_in;
    48. wire [BM_WD -1:0]power_bitmap_out;
    49. wire [BM_WD -1:0]power_bitmap_waddr;
    50. wire [BM_WD -1:0]power_bitmap;
    51. assign power_bitmap_in = ({{(BM_WD-1){1'b0}}, (in_hand_en & in_power & 1'b1)} << waddr);//0010
    52. assign power_bitmap_out = ~({{(BM_WD-1){1'b0}}, (out_inner_hand_en & 1'b1)} << raddr);//1011
    53. assign power_bitmap_d = (power_bitmap_q & power_bitmap_out) | power_bitmap_in;
    54. always @(posedge clk or negedge rst_n)begin
    55. if(~rst_n)
    56. power_bitmap_q <= {BM_WD{1'b0}};
    57. else if(power_bitmap_en)
    58. power_bitmap_q <= power_bitmap_d;
    59. end
    60. assign power_bitmap_waddr = ({{(BM_WD-1){1'b0}}, 1'b1} << waddr);
    61. assign power_bitmap = power_bitmap_q | power_bitmap_waddr;
    62. //==================================================================
    63. //
    64. //==================================================================
    65. wire [BM_WD -1:0]grant;
    66. rr_dispatch #(.WD(BM_WD), .KEEP_MODE(0))
    67. u_rr
    68. (
    69. .clk(clk),
    70. .rst_n(rst_n),
    71. .req(power_bitmap),
    72. .ack(out_inner_hand_en),
    73. .grant(grant)
    74. );
    75. //==================================================================
    76. //读出计数器
    77. //==================================================================
    78. reg [DP_WD :0]raddr_d;
    79. wire renc;
    80. always @* begin: RADDR_D
    81. integer i;
    82. for(i=0; i
    83. if(grant[i] == 1'b1) begin
    84. raddr_d = i;
    85. end
    86. end
    87. end
    88. assign raddr = raddr_d;
    89. //==================================================================
    90. //输出逻辑
    91. //==================================================================
    92. wire inner_out_real = power_bitmap[raddr];
    93. assign inner_out_valid = (raddr != waddr);
    94. assign inner_out_ready = out_ready || (~inner_out_real);
    95. assign out_valid = inner_out_valid && inner_out_real;
    96. assign in_ready = !((raddr[DP_WD -1:0] == waddr[DP_WD -1:0]) && (raddr[DP_WD] != waddr[DP_WD]));
    97. //==================================================================
    98. //数据寄存
    99. //==================================================================
    100. reg [WIDTH -1:0]data[DEPTH -1:0];
    101. always @(posedge clk)begin
    102. if(wenc) data[waddr[DP_WD-1:0]] <= in_data;
    103. end
    104. assign out_data = data[raddr[DP_WD-1:0]];
    105. endmodule

     

  • 相关阅读:
    网络观察方法
    【k8s】1、基础概念和架构及组件
    编写Android.mk / Android.bp 引用三方 jar 包,aar包,so 库
    MySQL练习题,记录
    ssm和springboot整合
    【PTA 题目详解】 7-10 猴子吃桃
    Charles-ios无法抓包原因之一证书
    js实现鼠标拖拽改变div大小的同时另一个div宽度也变化
    Web 页面性能衡量指标-以用户为中心的效果指标
    SpringBoot 项目使用 Sa-Token 完成登录认证
  • 原文地址:https://blog.csdn.net/moon9999/article/details/126692866