当前位置:首页 > EDA > 电子设计自动化
[导读]仲裁器Arbiter是数字设计中非常常见的模块,应用也非常广泛。定义就是当有两个或两个以上的模块需要占用同一个资源的时候,我们需要由仲裁器arbiter来决定哪一个模块来占有这个资源。类比一下,老师上课问了一个问题,底下同学不止一个人举手了,老师这个时候就要扮演仲裁者的角色,来指定由哪位同学站起来回答问题。一般来说,提出占有资源的模块要产生一个请求(request),类比于学生要举手表示自己要回答问题。所有的请求送给仲裁器之后,仲裁器要返回一个许可(grant),也就是老师要选择一名同学,通过点这个同学的名字的方式,告诉这个同学可以站起来回答问题。

仲裁器Arbiter是数字设计中非常常见的模块,应用也非常广泛。定义就是当有两个或两个以上的模块需要占用同一个资源的时候,我们需要由仲裁器arbiter来决定哪一个模块来占有这个资源。类比一下,老师上课问了一个问题,底下同学不止一个人举手了,老师这个时候就要扮演仲裁者的角色,来指定由哪位同学站起来回答问题。一般来说,提出占有资源的模块要产生一个请求(request),类比于学生要举手表示自己要回答问题。所有的请求送给仲裁器之后,仲裁器要返回一个许可(grant),也就是老师要选择一名同学,通过点这个同学的名字的方式,告诉这个同学可以站起来回答问题。

仲裁器很重要的一点是只能让一个模块得到许可,因为这个资源某一时刻只能由一个模块占用。类比一下,老师不能让两个同学同时站起来回答,因为两个人同时站起来说话就谁也听不清了。在数字电路中,总线仲裁是一个常见的例子,比如多个master要占用总线来去写数据,那么需要仲裁器来许可哪个master来占用总线。

那么一个显而易见的问题就是,仲裁器应该基于什么原则来分配许可呢?这篇我们先讨论最常见的方式:固定优先级 -- fixed priority。

固定优先级,顾名思义,就是说每个模块的优先级是固定的,是提前分配好的,如果有两个模块同时产生request,那么优先级高的模块可以获得grant。还是类比老师叫同学起来回答问题。比如,在老师心中,老师可以这样安排一个优先级:学号。学号是1的同学回答优先级最高,2号其次,一直到最后一位同学N,当大家举手的时候,总是叫学号最小的那个。比如说如果1号同学举手了,那就叫1号;如果1号,2号都没有举手,3号举手了,那就叫3号,很好理解吧?当然,如果N个同学里只有1位举手,那不管这个同学是几号,都让他回答问题。如果大家都不举手,那么老师就不叫了(现实中可能不是这样,有的老师可能会点名,但是咱们就不讨论了,大家理解就行)。

你可能会说,那对学号大的同学不公平啊,每次都抢不过学号小的同学。你说的没错,仲裁器的许可算法是有很多种的,我们说的固定优先级只是一种最简单的许可算法,还有其他的算法会考虑到公平性,我们之后再讨论。

回到仲裁器本身,如果我们要设计一个fixed priority arbiter,输入是一个multibit request,每一个bit代表一个模块的request, 输出一个multibit grant,每个bit代表给对应的模块的grant信号。我们可以把优先级这样安排,最低位优先级最高,最高位优先级最低。

我们先以3个模块产生request为例,大家一般在面试的时候都会碰到给定模块数目,比如3,让你设计。咱们就直接上code来表示一种写法

1 module fixed_prio_arb

2 (

3 input [2:0] req,

4

5 output logic [2:0] grant

6 );

7

8 always_comb begin

9 case (1'b1)

10 req[0]: grant = 3'b001;

11 req[1]: grant = 3'b010;

12 req[2]: grant = 3'b100;

13 default:grant = 3'b000;

14 endcase

15 end

16

17 endmodule: fixed_prio_arb

这里的技巧是利用verilog中的case语句,可以比用if else简洁,而且利用了case里的按顺序evaluate语法规则来实现了优先级。这里多说一句给verilog的初学者,尽管verilog和C看起来很像,很多关键字都是一样的,比如case,但是verilog的case和C的case是不一样的,verilog的case自带"break",即当一个condition满足之后,就只会执行这一条冒号之后的,只有input 发生变化之后才会再次evaluate,因为这是描述硬件电路。而C语言的case里如果一条满足之后会按照顺序继续往下执行,如果下一个条件依然满足,那么就会跳到下一个条件里,所以C的case语句我们通常要加break。

好,如果这一篇老李到此为止那就太水了,下面才是真正的干货内容。面试的时候如果你能够往下发挥才能让面试官眼前一亮。

还是那个老李一直强调的,如何设计一个参数化的模块。对于上面的仲裁器来说,我们希望可以参数化产生请求的个数,即设计下面的模块

1 module priority_arbiter #(

2 parameter REQ_WIDTH = 16

3 )(

4 input [REQ_WIDTH-1:0] req,

5 output [REQ_WIDTH-1:0] gnt

6 );

这样我们可以根据不同场合产生request的模块的个数来例化同一个仲裁器,只需要改变参数即可。很明显,上面利用case的写法不能scalable,我们需要用更加巧妙的办法。

首先可以想到的办法是利用for循环,思路其实非常直接,从低位到高位依次去判断,借助一个pre_req来记录低位是否已经有了request, 如果第i位有了request,那么第i+1位一直到最高位的pre_req都是1。

1 module prior_arb #(

2 parameter REQ_WIDTH = 16

3 )(

4 input logic [REQ_WIDTH-1:0] req,

5 output logic [REQ_WIDTH-1:0] grant

6 );

7

8 logic [REQ_WIDTH-1:0] pre_req;

9

10 always_comb begin

11 pre_req[0] = req[0];

12 grant[0] = req[0];

13 for (int i = 1; i < REQ_WIDTH; i = i + 1) begin

14 grant[i] = req[i] & !pre_req[i-1]; // current req & no higher priority request

15 pre_req[i] = req[i] | pre_req[i-1]; // or all higher priority requests

16 end

17 end

18

19 endmodule

有没有更简洁的办法呢?下面老李介绍两种实现方式,code非常简洁,先来上面的设计的变体,但是不用for循环,本质上是一样的,只有3行code。

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 preq_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

下面的这种实现方式就更夸张了,就一行实现,读到这里的读者可以说是赚到了,因为老李搜过,至少在各种介绍仲裁器的公众号文章里老李从来没有见到过这种方法。老李第一次学到的时候也是惊为天人,但是它背后的思想却非常朴素,简直不超过小学一年级的知识。

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 assign gnt = req & (~(req-1));

9 endmodule

本质上,我们要做的是找req这个信号里从低到高第一个出现的1,那么我们给req减去1会得到什么?假设req的第i位是1,第0到第i-1位都是0,那么减去1之后我们知道低位不够减,得要向高位借位,直到哪一位可以借到呢?就是第一次出现1的位,即从第i位借位,第0到i-1位都变成了1,而第i位变为了0,更高位不变。然后我们再给减1之后的结果取反,然后把结果再和req本身按位与,可以得出,只有第i位在取反之后又变成了1,而其余位都是和req本身相反的,按位与之后是0,这样就提取出来了第一个为1的那一位,也就是我们需要的grant。再考虑一下特殊情况req全0,很明显,按位与之后gnt依然都是全0,没有任何问题。

聪明的同学可能已经联想到,减1再取反,这不是计算2的补码的算法吗?只不过我们书本上学到的给一个数求2的补码的方法是取反再加1,这里倒过来,减1再取反,本质上是一样的。这其实是2的补码的一个特性,即一个数和它的补码相与,得到的结果是一个独热码,独热码为1的那一位是这个数最低的1。所以这个仲裁器的设计方法用一句话概括:request和它的2的补码按位与。如果同学们在面试的时候被问到设计优先级固定仲裁器,只要记住这句话,那么你就可以惊艳面试官。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭