一些轮询仲裁器方案的SystemVerilog 代码

 
 
关注、星标公众号,精彩内容每日送达
来源:网络素材

前文介绍了一个固定优先级arbiter的可综合SysyemVerilog代码,你一定注意到了该设计的一个最大缺点。存在静态优先级意味着,在某些情况下,低优先级的模块可能永远无法得到服务。这被称为饿死,在某些设计中可能没问题,但在某些设计中,我们可能需要避免这种情况。

在下面的这篇文章中介绍了很多种轮询仲裁方案。

c8e78c1874b1f747c06e07e5e2eb51e4.jpeg

「数字芯片设计【不定期更新文件】」

链接:https://pan.quark.cn/s/27331927a18e

避免它的一种方法是轮询优先级。一旦请求模块得到服务,下一个模块将获得最高优先级,以循环方式进行优先级的调整。这就是为什么这个仲裁器被称为Round Robin轮询仲裁器,它用于避免设计中的饿死。

某个模块的请求必须等待的最大时间取决于此设计中的请求者数量。

always_comb begin
 case (pointer_reg)
 2'b00 :
 if (req[0]) grant = 4'b0001;
 else if (req[1]) grant = 4'b0010;
 else if (req[2]) grant = 4'b0100;
 else if (req[3]) grant = 4'b1000;
 else grant = 4'b0000;
 2'b01 :
 if (req[1]) grant = 4'b0010;
 else if (req[2]) grant = 4'b0100;
 else if (req[3]) grant = 4'b1000;
 else if (req[0]) grant = 4'b0001;
 else grant = 4'b0000;
      2'b10 :
 if (req[2]) grant = 4'b0100;
 else if (req[3]) grant = 4'b1000;
 else if (req[0]) grant = 4'b0001;
 else if (req[1]) grant = 4'b0010;
 else grant = 4'b0000;
 2'b11 :
 if (req[3]) grant = 4'b1000;
 else if (req[0]) grant = 4'b0001;
 else if (req[1]) grant = 4'b0010;
 else if (req[2]) grant = 4'b0100;
 else grant = 4'b0000;
 endcase // case(req)
end

此代码适用于具有4位请求的RR仲裁。优先级根据变量pointer_reg进行轮换每当请求者获得服务时,pointer_reg都会发生变化。例如,如果模块0被服务,那么最高优先级将是模块1。当模块1被服务时,模块2将获得最高优先级。

我们可以写出计算pointer_reg值的逻辑,如下所示:

logic [1:0] pointer_req, next_pointer_req;
  
  always @(posedge clock) begin
    if (reset) pointer_req <= '0;
    else       pointer_req <= next_pointer_req;
  end
  
  always_comb begin
    assign next_pointer_req = 2'b00;
    casez (gnt)
      4'b0001: next_pointer_req = 2'b01;
      4'b0010: next_pointer_req = 2'b10;
      4'b0100: next_pointer_req = 2'b11;
      4'b1000: next_pointer_req = 2'b00;
    endcase
  end
就像固定优先级仲裁一样,这种设计是不可扩展的。输入请求数量较多的代码编写起来很繁琐,而且容易出错。下面介绍其他可扩展且易于参数化的设计。
The Rotate + Priority + Rotate Design

bf03ed422cd59c86e559d67c8246d3d8.jpeg

以“Rotate + Priority + Rotate”的设计为例。它使用固定优先级仲裁器,并向右或向左移动输入,以模仿优先级的旋转。假设4'b1011是第一个输入,这次没有移位,因为这是第一个输入。假设LSB具有最高优先级,当我们将其发送给固定优先级仲裁器时,此输入的输出将为4'b0001。假设下一个输入是4'b0011。通常,这种情况的输出也是4'b0001,但在RR仲裁的情况下不是。由于这是第二个输入,我们将其移动1,因此对固定优先级仲裁器的输入现在变为4'b1001,输出也变为4'b0001。但这不是我们的最终输出,因为在计算输出grant信号之前,我们将输入“向左右”移动了1,现在我们必须将其“向左”旋转1才能获得最终输出,这样输出现在变成4'b0010。这是一个简单但有效的设计。决定左右旋转的指针逻辑可以很容易地以我之前提到的方式编码。这种设计速度不是很快,因为需要大量的操作来生成grant输出,但综合时需要非常小的面积。

The MUXed parallel priority arbiters

bb726b3d8821bc284947c4b6e170d701.png

从图表中可以看出,这个设计相当大。该设计还是使用固定优先级仲裁器,每个输入请求都有一个固定优先级仲裁器。随着输入请求者数量的增加,它只会越来越大。但好的方面是,它非常快。由于所有优先级仲裁器的输出grant已经计算好了,我们只是从中选择一个输出。让我们尝试为4位输入请求总线编写此设计代码。

module muxed_rr_arb(
  input logic clock,
           reset,
  input logic [3:0] req,
  output logic [3:0] gnt
);
  
  logic [3:0] mux_ip0, mux_ip1, mux_ip2, mux_ip3;
  
  //Instantiate fixed priority arbiter and calculate the grant output for shifted priorities
  fixed_pri_arbiter inst0 (.req(req),    .gnt(mux_ip0));
  fixed_pri_arbiter inst1 (.req(req>>1), .gnt(mux_ip1));
  fixed_pri_arbiter inst2 (.req(req>>2), .gnt(mux_ip2));
  fixed_pri_arbiter inst3 (.req(req>>3), .gnt(mux_ip3));
  
  //Select line pointer calculation
  logic [1:0] pointer_req, next_pointer_req;
  
  always @(posedge clock) begin
    if (reset) pointer_req <= '0;
    else pointer_req <= next_pointer_req;
  end
  
  always_comb begin
    assign next_pointer_req = 2'b00;
    casez (gnt)
      4'b0001: next_pointer_req = 2'b01;
      4'b0010: next_pointer_req = 2'b10;
      4'b0100: next_pointer_req = 2'b11;
      4'b1000: next_pointer_req = 2'b00;
    endcase
  end
  
  //Final output
  always_comb begin
    case (pointer_req)
      2'b00: gnt = mux_ip0;
      2'b01: gnt = mux_ip1;
      2'b10: gnt = mux_ip2;
      2'b11: gnt = mux_ip3;
    endcase
  end
  
endmodule

您可以使用generate for循环来实例化固定优先级仲裁器,轻松地对此设计进行参数化。

Two Simple Priority Arbiters with a Mask

54f08c1e2a6f622d0d81d8e6fdbfcd05.jpeg

此设计使用两个固定的优先级仲裁。一个用于计算请求输入的grant输出,另一个用于计算带有屏蔽请求输入的grant输出。屏蔽请求是通过使用mask变量对输入请求进行AND来计算的,每次请求服务时,其值都会发生变化。mask逻辑如下所示:

always@(/*AUTOSENSE*/pointer_reg) begin
 case (pointer_reg) // synopsys full_case parallel_case
 2'b00: req_mask <= 4'b1111;
 2'b01: req_mask <= 4'b1110;
 2'b10: req_mask <= 4'b1100;
 2'b11: req_mask <= 4'b1000;
 endcase
end

这是一个非常有效的解决方案,在论文中提到的所有设计中,它提供了最佳性能。

Weighted RR Arbiter

这也是一个轮询仲裁,但有一点变化。例如,让我们假设请求者0的权重值为3,请求者1的权重值为2。如果在第一个周期中,输入为4'b0011,请求者0将被服务。现在请求者0已服务,其权重减少到2。请求者1的权重值仍然为2。假设输入在第二个周期中是相同的,即4'b0011。理想情况下,这次请求者1应该根据循环计划获得服务,但由于请求者0和1的权重相同,我们假设LSB具有最高的优先级,请求者0将再次获得服务,其权重将减少到1。现在,如果我们在第三个周期中看到相同的输入,请求者1的权重值更大,因此请求者1这次将得到服务。

*免责声明:本文由作者原创。文章内容系作者个人观点,转载仅为了传达一种不同的观点,不代表对该观点赞同或支持,如果有任何异议,欢迎联系。

(全文完)

图片

想要了解FPGA吗?这里有实例分享,ZYNQ设计,关注我们的公众号,探索

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值