仲裁器设计(二)-- Round Robin Arbiter 轮询调度算法

2023-10-26

作者:李虹江
原文:https://mp.weixin.qq.com/s/r-nckE5nGz9mc5KqjPXKYg
本文授权转自IC加油站微信号,未经作者授权,严禁二次转载。
上一篇老李讲了固定优先级仲裁器Fixed Priority Arbiter 仲裁器设计(一) – Fixed Priority Arbiter

里面提到了,固定优先级仲裁的一个问题就是公平性。以上篇文章里同学举手老师点名的例子来说,如果老师每次都叫学号小的,那学号大的同学会觉得不公平,因为被老师点到的机会小。单纯回答问题的话可能还好,如果我们假设每回答一个问题积一分,最后成绩按照回答问题的个数来计算的话,那么很显然这种方式对学号大的同学太不公平了。所以,仲裁器的公平性问题是在设计中我们必须要考虑的。

Round Robin就是考虑到公平性的一种仲裁算法。其基本思路是,当一个requestor 得到了grant许可之后,它的优先级在接下来的仲裁中就变成了最低,也就是说每个requestor的优先级不是固定的,而是会在最高(获得了grant)之后变为最低,并且根据其他requestor的许可情况进行相应的调整。这样当有多个requestor的时候,grant可以依次给每个requestor,即使之前高优先级的requestor再次有新的request,也会等前面的requestor都grant之后再轮到它。

我们以4个requestor为例来说明,下面这个表格Req[3:0]列表示实际的request,为1表示产生了request;RR Priority这一列为当前的优先级,为0表示优先级最高,为3表示优先级最低;RR Grant这一列表示根据当前Round Robin的优先级和request给出的许可;Fixed Grant表示如果是固定优先级,即按照3210,给出的grant值。
在这里插入图片描述

第一个周期,初始状态,我们假设req[0]的优先级最高,req[1]其次,req[3]最低,当req[2]和req[0]同时为1的时候,根据优先级,req[0]优先级高于req[2],grant = 0001。

第二个周期,因为req[2]在前一个周期并没有获得grant,那么它继续为1,而这个时候req[0]又来了一个新的request,这个时候就能够看出round robin和fixed priority的差别了。对于fixed priority, grant依然给0,即0001。但是round robin算法的要求是:因为上一个周期req[0]已经被grant了,那么它的优先级变为最低的3,相应的,req[1]的优先级变为最高,因为它本来就是第二高的优先级,那么当req[0]优先级变为最低了之后它自然递补到最高,那么这个时候产生的许可grant就不能给到req[0],而是要给到req[2]。

同理,第三个周期,req[2]因为在前一个周期grant过,它的优先级变为3最低,req[3]的优先级变为最高。后面的周期大家可以自己顺着分析下来。

换句话说,因为被grant的那一路优先级在下一个周期变为最低,这样让其他路request都会依次被grant到,而不会出现其中某一路在其他路有request的情况下连续被grant的情况,所以round-robin在中文中也被翻译成“轮询调度”。

好,下面我们来讲round robin的RTL 实现。老李这次就不讲特例了,直接介绍几种参数化的写法。

首先看第一种思路,即优先级是变化的,回想一下我们之前讲的Fixed Priority Design,我们都假定了从LSB到MSB优先级是由高到低排列的。那么我们有没有办法先设计一个fixed priority arbiter,它的优先级是一个输入呢?看下面的RTL

1 module arbiter_base #(parameter NUM_REQ = 4)
2 (
3 input [NUM_REQ-1:0] req,
4 input [NUM_REQ-1:0] base,
5 output [NUM_REQ-1:0] gnt
6 );
7
8 wire[2NUM_REQ-1:0] double_req = {req,req};
9
10 wire[2
NUM_REQ-1:0] double_gnt = double_req & ~(double_req - base);
11
12 assign gnt = double_gnt[NUM_REQ-1:0] | double_gnt[2*NUM_REQ-1:NUM_REQ];
13 endmodule
在这个模块中,base是一个onehot的信号,它为1的那一位表示这一位的优先级最高,然后其次是它的高位即左边的位,直到最高位后回到第0位绕回来,优先级依次降低,直到为1那一位右边的这位为最低。咱们以4位为例,如果base = 4’b0100, 那么优先级是bit[2] > bit[3] > bit[0] > bit[1]。

这个设计的思路和老李前一篇仲裁器设计(一) – Fixed Priority Arbiter最后那个1行设计的思路很像,里面double_req & ~(double_req-base)其实就是利用减法的借位去找出base以上第一个为1的那一位,只不过由于base值可能比req值要大,不够减,所以要扩展为{req, req}来去减。当base=4‘b0001的时候就是咱们上一篇里面的最后的算法。当然base=4’b0001的时候不存在req不够减的问题,所以不用扩展。

那么好了,既然有了可以根据输入给定优先级的固定优先级仲裁器(这句话有点绕,你仔细琢磨一下),那么接下来的任务就简单了,每次grant之后,我把我的优先级调整一下就可以了呗。而且这个设计妙就妙在,base要求是一个onehot signal,而且为1的那一位优先级最高。我们前面说过,grant一定是onehot,grant之后被grant的那一路优先级变为最低,它的高1位优先级变为最高,所以,我只需要一个history_reg,来去记录之前最后grant的值,然后只需要将grant的值左移一下就变成了下一个周期的base。比如说,假设我上一个周期grant为4’b0010,那么bit[2]要变为最高优先级,那只需要base是grant的左移即可。RTL代码如下

module round_robin_arbiter #(parameter NUM_REQ = 4)
(
input clk,
input rstn,
input [NUM_REQ-1:0] req,
output [NUM_REQ-1:0] gnt
);

logic [NUM_REQ-1:0] hist_q, hist_d;

always_ff@(posedge clk) begin
if(!rstn)
hist_q <= {{NUM_REQ-1{1’b0}}, 1’b1};
else
if(|req)
hist_q <= {gnt[NUM_REQ-2:0, gnt[NUM_REQ-1]};
end

arbiter_base #(
.NUM_REQ(NUM_REQ)
) arbiter(
.req (req),
.gnt (gnt),
.base (hist_q)
);

endmodule
我们注意到,和Fixed Priority Arbiter不同,Round robin arbiter不再是纯的组合逻辑电路,而是要有时钟和复位信号,因为里面必须要有个寄存器来记录之前grant的状态。

上面这个Round Robin Arbiter的设计,好处就是思路简单明了,代码行数也很短,在你理解了Fixed Priority Arbiter之后,理解这个设计就很容易。但是这个设计也有缺点,即在面积和timing上的优化不够好。相比于我们接下来要介绍的设计,在request位数大(比如64位)的时候timing和area都要差一些,所以其实老李见并没有见到公司里采用这个设计,而更多的时候采用的是下面的设计。

前面的思路是换优先级,而request不变,另一个思路是优先级不变,但是我们从request入手:当某一路request已经grant之后,我们人为地把进入fixed priority arbiter的这一路req给屏蔽掉,这样相当于只允许之前没有grant的那些路去参与仲裁,grant一路之后就屏蔽一路,等到剩余的request都依次处理完了再把屏蔽放开,重新来过。这就是利用屏蔽mask的办法来实现round robin的思路。

这个思路还是会用到前面一讲里Fixed Priority Arbiter的写法,如何来产生屏蔽信号mask呢?回看下面这段RTL

1 module prior_arb #(
2 parameter REQ_WIDTH = 16
3 )(
4 input [REQ_WIDTH-1:0] req,
5 output [REQ_WIDTH-1:0] gnt
6 );
7
8 logic [REQ_WIDTH-1:0] pre_req;
9
10 assign pre_req[0] = 1’b0;
11
12 assign pre_req[REQ_WIDTH-1:1] = req[REQ_WIDTH-2:0] | pre_req[REQ_WIDTH-2:0];
13
14 assign gnt = req & ~pre_req;
15
16 endmodule
里面的pre_req的意义是什么?就是如果第i位的req为第一个1,那么pre_req从i+1位开始每一位都是1,而第0位到第i位都是0。这其实就是我们要找的mask! 只需要把req和上一个周期的pre_req AND起来,那么我们自然就得到了一个新的request,这个request里之前grant的位以及之前的位都被mask掉了,允许通过的是在之前优先级更低的那些位,如果那些位上之前有request但是没有被grant,现在就可以轮到他们了。每次新的grant之后mask里0的位数会变多,从而mask掉更多位,直到所有的低优先级的request都被grant了一次之后,req AND mask的结果变成全0了,这个时候就说明我们已经轮询完毕,要重新来过了。

硬件实现上我们需要两个并行的Fixed Priority Arbiter,它们一个的输入是request AND mask之后的masked_request,另一个就是原本的request,然后我们在它们两个arbiter的output中选择一个grant。如下图所示

在这里插入图片描述

当masked_request不是全0,即存在没有被mask掉的request时,我们选择上面的这一路Mask Grant,否则我们选择下面这一路Unmasked Grant。

而又因为对于上面这一路来说,当masked_request为全0的时候,Mask Grant也是全0,这个时候可以把Mask Grant和Unmask Grant直接按位OR起来就行,所以其实图上最后显示的Mux可以用下面简单的AND门和OR门实现

在这里插入图片描述

下面是这个设计的代码,依然是参数化的表达,可以满足任意数目的request。

module round_robin_arbiter #(
parameter N = 16
)(
input clk,
input rst,
input [N-1:0] req,
output[N-1:0] grant
);

logic [N-1:0] req_masked;
logic [N-1:0] mask_higher_pri_reqs;
logic [N-1:0] grant_masked;
logic [N-1:0] unmask_higher_pri_reqs;
logic [N-1:0] grant_unmasked;
logic no_req_masked;
logic [N-1:0] pointer_reg;

// Simple priority arbitration for masked portion
assign req_masked = req & pointer_reg;
assign mask_higher_pri_reqs[N-1:1] = mask_higher_pri_reqs[N-2: 0] | req_masked[N-2:0];
assign mask_higher_pri_reqs[0] = 1’b0;
assign grant_masked[N-1:0] = req_masked[N-1:0] & ~mask_higher_pri_reqs[N-1:0];

// Simple priority arbitration for unmasked portion
assign unmask_higher_pri_reqs[N-1:1] = unmask_higher_pri_reqs[N-2:0] | req[N-2:0];
assign unmask_higher_pri_reqs[0] = 1’b0;
assign grant_unmasked[N-1:0] = req[N-1:0] & ~unmask_higher_pri_reqs[N-1:0];

// Use grant_masked if there is any there, otherwise use grant_unmasked.
assign no_req_masked = ~(|req_masked);
assign grant = ({N{no_req_masked}} & grant_unmasked) | grant_masked;

// Pointer update
always @ (posedge clk) begin
if (rst) begin
pointer_reg <= {N{1’b1}};
end else begin
if (|req_masked) begin // Which arbiter was used?
pointer_reg <= mask_higher_pri_reqs;
end else begin
if (|req) begin // Only update if there’s a req
pointer_reg <= unmask_higher_pri_reqs;
end else begin
pointer_reg <= pointer_reg ;
end
end
end
end

endmodule
这里稍微多讲解几句,当no_req_masked之后,pointer_reg并不是要更新到1111或是1110,而是要根据这个时候的request来,比如说这个时候request是0010,那么新的mask就要调整为1100,重新把bit[0], bit[1]都mask掉。

可以看出,这个设计利用两个N位的arbiter并行计算,critical path只比单独的fixed priority arbiter多了mask这一步和最后的mux这一级,在timing上表现是非常好的。面积上相比于前面一种做法2N的加法器也要少一些。

关于Round-robin还有其他的思路,比如将request进行rotate,从而达到改换优先级的目的,然后再根据history来rotate 回来。还有比如并行放N个fixed priority arbiter,把所有的priority order都实现了,然后再在N个grant中选择一个,这些设计在面积和时序上都各有牺牲,而且参数化设计不是很容易写,老李这里就不细讲了。感兴趣的同学可以点击原文链接,看一篇论文里的详细讲解,老李关于方法2的设计也来自于这篇论文,里面还有不同设计的面积时序比较。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

仲裁器设计(二)-- Round Robin Arbiter 轮询调度算法 的相关文章

  • Nginx解决“no resolver defined to resolve xxx.xxx”

    1 2 3 4 5 6 7 8 9 10
  • AI制作ICON展示

    作者 陈石军 撰写时间 2019年4月7日 我先做了个背景色 这个背景色我用了三种颜色 它们分别为白色 fdfdfd 蓝色 94cfe2 绿色 72c190 背景色是由一个矩形和俩个形状图形组成的 接下来就是排版了 排版有好几种 分别是靠左
  • Scratch第一讲:scratch编程软件介绍

    喜欢编程的各位小朋友们你们好呀 欢迎来到scratch小课堂 从今天起 我们要从0开始学习scratch编程 那么有的同学要问了 什么是scratch Scratch是由麻省理工学院 MIT 设计开发的一款面向少年的简易编程工具 它的功能非
  • Linux线程性能分析和CPU亲和力

    一 线程迁移和负载均衡 Linux系统在多核CPU和SMP系统上有完善的负载均衡支持 在SMP系统中 每个CPU的核都有一个迁移线程守护程序migration 一般是系统最高优先级139 实时99 以实现执行资源平衡作业 当我们调用sche
  • 5g信号云端服务器,5G基站已有11W 国内云游戏迎来春天

    目前有报道称全国已经开通了11 3W个5G网络基站 已有87万户5G签约用户 这意味着在全国范围 有关需要网络的IT产品和生活产品都将迎来春天 其中包括网络连接使用的云游戏 进入到2019年 国内5G商用全面启动 华为 小米 OPPO等手机
  • QString : 类型转换,不留神就留坑?

    QString作为Qt中内置的数据类型 功能强大且使用方便 绝对是在Qt开发过程中出场率最高的数据类型 本篇我们只重点探讨下QString转换成其他数据类型的注意事项 short toShort bool ok nullptr int ba
  • gg修改器修改数值没有用怎么办_gg修改器修改游戏数值教程_gg修改器怎么修改数值_3DM手游...

    GG修改器是很多玩家都在用的一款游戏辅助工具 使用这款软件 能够对多种游戏的数值进行随意的修改 调整成你所需要的数值 让你玩游戏玩的更爽 今天3DM小编为大家带来的是GG修改器修改游戏数值的教程 有需要的小伙伴们可以来一起了解下 GG修改器
  • Android事件分发机制及设计思路,熬了整整30天

    前言 想要成为一名优秀的Android开发 你需要一份完备的知识体系 在这里 让我们一起成长为自己所想的那样 此篇文章是初中高级工程师学习文章 知识体系较为完整 有如下特点 1 知识结构全面 2 跟随当下技术潮流实时更新 3 可用于面试 学
  • mybatis

    mybatis 起步1 之前的mybatis写法 起步2 接口式编程写法 mybatis的配置 properties settings mapUnderscoreToCamelCase typeAliases mappers 这里项目结构发
  • (三)系统与架构级低功耗设计

    前面讲解了使用EDA工具 主要是power compiler 进行功耗分析的流程 这里我们将介绍在数字IC中进行低功耗设计的方法 同时也结合EDA工具 主要是Design Compiler 如何实现 我们的讲解的低功耗设计主要是自顶向下的设
  • 笔录Flutter(十一) FloatingActionButton

    Flutter练习Demo FloatingActionButton也是经常用的 除了常见的悬浮在右下角的一个按钮 还可以利用floatingActionButtonLocation属性 控制位置的展示 floatingActionButt
  • Python:使用爬虫抓取网页中的视频并下载(完整源码)

    Python 使用爬虫抓取网页中的视频并下载 完整源码 在今天的程序开发世界中 网站是不可或缺的一部分 人们使用网站来获取有用的信息 购买商品和娱乐自己 这些网站的内容通常包含了各种类型的文件 其中最常见的就是视频 对于有经验的程序开发者来
  • 黑马JVM总结(八)

    1 StringTable面试题 1 8 1 6时 2 StringTable的位置 jvm1 6时StringTable是常量池的一部分 它随着常量池存储在永久代当中 在1 7 1 8中从永久代变成了堆中 为什么做这个更改呢 因为永久代的
  • 关于javascript md5 函数介绍

    转自 微点阅读 https www weidianyuedu com var hexcase 1 var b64pad var chrsz 8 var mode 16 模式选择 16为16位的加密 32 为32位的加密 function p
  • Eureka的常用配置讲解

    1 关闭自我保护 保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景时 一旦进入保护模式 Eureka Server将会尝试保护其服务的注册表中的信息 不在删除服务注册表中的数据 当网络故障恢复后 Eureka Se
  • 外包四年太差劲,才幡然醒悟要跳槽

    前几天有个读者过来说 程序猿 外包干了四年太差劲了 感觉和外界差距有点大 现在被动醒悟 希望你能帮我制定一下学习路线 如果不是女朋友和我提分手 我估计现在还没醒悟 大专生 18年通过校招进入湖南某软件公司 干了3年多的CRUD 今年年初 感
  • VS--屏蔽编译warning警告设置

    VS 屏蔽编译warning警告设置 在 项目 gt 属性 gt 配置属性 gt C C gt 高级 的 禁用特定警告 中添加相应的警告编号 如4819

随机推荐