Verilog|权重轮询仲裁器(Weight_Round_Robin_Arbiter)语言: Verilog工具Vcs和Verdi本文内容为博主当前做的交换机开源项目所用IP对交换芯片设计感兴趣的朋友可以关注博主git博主git:Atom(100M以太网交换机未经作者允许禁止转载文中部分代码思想借鉴了IC加油站的内容分享侵权必删文章目录Verilog|权重轮询仲裁器(Weight_Round_Robin_Arbiter)一、仲裁器1固定优先级仲裁器(Fixed_Priority_Arbiter)2轮询仲裁器(Round_Robin_Arbiter)3权重轮询仲裁器(Weight_Round_Robin_Arbiter)二、分析1.仲裁原理2.硬件实现1基于grant2基于request三、RTL1.基于grant设计2.基于request设计一、仲裁器在数字电路当中经常会出现多个源或用户共享同一资源总线的情况这时就需要某种仲裁形式使得所有用户基于一定的规则或算法得到获取或访问共享资源的机会。例如共享总线上可以连接多个总线用户。还有交换芯片中的端口仲裁当多个入口希望通过某一个出口输出数据时需要使用一定的端口仲裁机制来选择某一时刻允许哪一个入口发送数据。工业上常用的经典仲裁方案有如下三种固定优先级仲裁器、轮询仲裁器和权重轮询仲裁器。其中固定优先级仲裁器和轮询仲裁器比较常见并且相关资料介绍也很多本文不做过多叙述重点讨论权重轮询仲裁器。1固定优先级仲裁器(Fixed_Priority_Arbiter)顾名思义发送请求的用户(agent)有着固定的优先级优先级高的用户只要保持请求就会持续得到授权而低优先级的用户只能等待。随着优先级的降低用户得到授权的机会也会随之下降。例如有4个用户需要共享数据总线req[0]优先级最高req[3]优先级最低。当req1001时仲裁结果为0001当req为1100时仲裁结果为0100。2轮询仲裁器(Round_Robin_Arbiter)固定优先级仲裁策略能够根据用户的重要性提供相应服务但低优先级的用户也可能因此长期得不到服务而被“饿死”。比如在交换业务当中如果端口采用了固定优先级的仲裁策略那么就很容易出现网络延迟甚至业务被挂死的可能。因此我们希望每个用户都能公平地获得授权而轮询仲裁器就可以满足这个需求。轮询仲裁器在初始阶段可以任意选择用户顺序在一个轮询周期内所有发出请求的用户都能公平得到授权机会。仲裁器每次仲裁时会记住上一次被授权的用户当该用户的操作完成后将其优先级置于最低。接着依序轮询其他用户的请求是否有效如果一个用户的请求无效那么将按顺序查看下一个用户。并且当某个用户得到授权后可以长期使用总线或者占用资源直到当前业务处理完成仲裁器才会授权其他用户进行操作。这种方案适用于基于数据包的协议如以太网交换或PCIe交换机。另外还有一种机制每个用户获得授权后占用资源的时间长度受到约束不能超过规定的长度否则即便当前用户没有完成操作仲裁器也会收回授权并轮询后续用户。该方案适用于突发操作如AMBA、AHB总线。以交换业务为例假设有4个端口即req[3:0]。初始阶段优先授权低位req1001,因此grant0001接着req变成1101后等到req[0]完成操作释放授权信号switch_to_next后,仲裁结果变为0100。再次释放授权req已变为1100则此时仲裁结果为1000。由此可体现出公平的轮询机制。3权重轮询仲裁器(Weight_Round_Robin_Arbiter)当用户之间仍然存在着优先级高低之分时优先级高的用户希望能够获得更多的授权机会但同时也需要考虑其他用户的情况下权重轮询仲裁器就诞生了。这也是本文所要讨论的主要内容将在下文进行分析阐述并附上rtl代码。二、分析1.仲裁原理权重轮询仲裁器从名称上来看首先是基于轮询仲裁机制的但同时又不是公平的轮询机制每个用户之间都有对应的权重关系。举个例子说明假设班级里有个ABC三位同学其中A的成绩最好B次之C的成绩最差。在课堂上老师想要提问希望三位同学都能够主动举手参与进来因此不能总是让一个人回答。当提出第一个问题并且有同学举手之后老师先按成绩选择一位同学回答假设为A。A同学回答完之后再次提出问题时若B和C举手了那么老师则优先选择让B来回答问题若B和C只有一个人举手则谁举手谁回答此时不考虑A的情况如果B和C都没有人举手并且A举手了这时老师才考虑继续让A同学回答。这是公平轮询的机制。同样的情况老师上课提问有意想要鼓励成绩较差的同学多回答一些问题给A、B、C同学的回答次数分别为123次。第一次提问时A举手的话则选择A作答此时已经A已经用完了回答次数。第二次提问若B和C有人举手则不允许A回答假设第二次是B回答了问题那么B还剩一次机会。第三次提问B和C又同时举手按照公平轮询的策略此时应该是C回答但是由于B同学还有一次回答机会因此这时仍然由B来回答问题。同理C同学也是如此。需要注意的是如果第三次提问B没有举手A和C同学举手了意味着B还有一次机会A同学已经用完了所有回答机会因此不能回答同时根据轮询原则此时应该轮到C回答。C回答完之后还剩2次机会。再下一个轮询周期B和C用完剩下的机会后即便举手老师也要提问下一个同学。这就表示权重轮询并不是仲裁一次之后就轮询下一个用户而是要综合权重来处理。CyclesABCgrant(ABC)cycle0111100cycle1111010cycle2011010cycle3011001cycle4110100cycle5101001cycle6101001cycle70110102.硬件实现从上述例子中可以很好地理解权重轮询的机制那么如何用HDL去描述该机制或者说如何用硬件电路去实现该机制是接下来需要考虑的问题。硬件实现权重仲轮询裁机制一般来说有两种思路一种是基于grant进行轮询另外一种则是基于request的方法。1基于grant轮询的思想简单来说就是屏蔽已经授权的用户授权给还未仲裁的较高优先级用户因此核心问题在于如何找到那个还未仲裁的较高优先级用户。这让我们很容易联想到如何找到序列里第一个1位置由低到高的问题它是这样处理的。将序列减1这样序列中从低位开始为0的bit位就会因为不够减向高位借位当前位就变为1直到遇到序列中第一个为1的bit位其因为低位的借位变为0更高位则保持不变。再将得到的新seq进行按位~此时原seq中第一个1仍然为1再和原seq进行按位操作,即可得到由低到高的第一个1的位置。result[N-1:0]seq[N-1:0]~(seq[N-1:0]-1b1)而轮询仲裁需要找到上一次授权后的最高优先级用户由于grant本身就是独热码所以只需要将上述中的1‘b1换成上一次仲裁结果即可这里就需要使用时序逻辑。考虑到grant的二进制数值可能比req大因此将位宽扩大一倍如下操作。将double_req减去上一次仲裁结果后再取反和原double_req按位此时就可找到当前可授权的用户再把grant位宽恢复成N位。最后需要把当前仲裁结果进行循环左移处理这是为了在下次仲裁时屏蔽当前已完成仲裁的用户。reg[N-1:0]grant_base;wire[N-1:0]grant;wire[2*N-1:0]double_req;wire[2*N-1:0]double_grant;always(posedge clk or negedge rst_n)beginif(!rst_n)grant_base[N-1:0]{{N-1}{1b0},1b1};elseif(|req[N-1:0])grant_base[N-1:0]{grant[N-2:0],grant[N-1]};assign double_req[2*N-1:0]{req,req};assign double_grant[2*N-1:0]double_req~(double_req-grant_base);assign grant[N-1:0]double_grant[2*N-1:N]|double_grant[N-1:0];上文说过权重轮询需要考虑权重的因素常规做法是给每个用户固定的仲裁次数然后各用户每仲裁一次计数一次。因此完成一次仲裁后并不能马上对grant进行循环移位而是判断当前用户可仲裁次数是否已用完若已用完则grant_base进行跳转反之如果当前用户停止请求仲裁并且有其他用户请求了仍然跳转否则保持不变。需要注意的是当仲裁器用于数据包的传输时一次仲裁可能会占用很长时间需要一个仲裁释放信号(switch_to_next)来开始下一次仲裁。因此仲裁的触发信号可以由两个信号或操作形成一种是当前没有仲裁且有新的req输入表示第一次仲裁另外一种则是由switch_to_next触发的。这是当前许多博客中没有提及的部分也更加符合实际使用的需求。为了增加IP的普适性满足不同设计的需求因此rtl采取参数化设计具体的代码会在分享在下文。由于该设计比较简单大家可以根据自己的需求和想法自行设计test bench进行验证。2基于request前面的方法很好地利用了grant为独热码的特性达到了屏蔽的效果。而本方法则是通过给request主动加上mask的方式屏蔽掉已经仲裁过的用户。起初允许所有的用户接受仲裁授权即有一个为全1的指示信号设为mask[N-1:0] {N{1b1}}。接着按照req由低到高的顺序每仲裁一次将mask对应的bit位信号拉低只保留未仲裁的用户以便下一次仲裁时进行屏蔽。当有某个用户申请仲裁时将mask和req进行按位操作得到req_mask这时req中为1的bit位在req_mask中仍然为1。接下来如何找到req_mask中的第一个1仍是关键这里我们可以从格雷码转化成二进制码的思想中得到启发按照如下方式操作。由于默认req仲裁顺序由低到高如果我们首先将mask_hi_pri_req的最低位设为0然后将req_mask[N-2:0]和mask_hi_pri_req[N-2:0]按位|作为mask_hi_pri_req[N-1:1]。这样的话假设req_mask中由低往高的第一个请求仲裁的用户为第i位则在mask_hi_pri_req中0-i位都为0第i1位到最高位都为1这同时也就得到了下次仲裁时的mask。然后再将其按位~后跟req_mask进行按位操作即可得到grant也就找到了req_mask中的第一个1。此时可能有人会问如果当前一轮都仲裁完了那么按照上述方法mask将会变成全0 这样就没法进行仲裁选择。因此我们需要另外开一路并行的unmask的仲裁选择跟前面mask方法唯一的区别就是req没有经过mask。当mask为全0时一轮已经仲裁完毕下一次mask应该为全1,也就是req本身。因此可以通过一个选择器的形式来选择输出最终的grant这里的选择器亦可用或门代替。//gray to binassign bin[N-1]gray[N];assign bin[N-2:0]bin[N-1:1]^gray[N-2:0];//arbitration for maskassign req_mask[N-1:0]req[N-1:0]mask[N-1:0];assign mask_hi_pri_req[0]1b0;assign mask_hi_pri_req[N-1:1]req_mask[N-2:0]|mask_hi_pri_req[N-2:0];assign mask_grant[N-1:0]req_mask[N-1:0]~mask_hi_pri_req[N-1:0];//arbitration for unmaskassign unmask_hi_pri_req[0]1b0;assign unmask_hi_pri_req[N-1:1]req[N-2:0]|unmask_hi_pri_req[N-2:0];assign mask_grant[N-1:0]req[N-1:0]~unmask_hi_pri_req[N-1:0];//Use grant_masked if there is any there, otherwise use grant_unmaskedassign no_req_masked~(|req_masked);assign grant({REQ_CNT{no_req_masked}}grant_unmasked)|grant_masked;到这里已经完成了一大步最后还需要将权重因素考虑进来。权重不会影响仲裁选择的方法仅仅只会改变mask跳转的判断条件。和上文中提到的一样需要给每个用户设计一个计数器cnt用来记录授权次数当授权次数用完时则把cnt_over信号拉高将所有用户的cnt_over按位|起来得到round_en以此作为mask的跳转使能。跳转时如果当前mask不为0则把mask_hi_pri_req作为下一次的mask反之若有用户请求仲裁则将unmask_hi_pri_req作为下一次的mask若没有请求则保持不变。写到这里博主发现了一个问题。假设有四名用户初始mask4‘b1111且权重由高到低位为3:2:3:4如果第一次req4’b0110则grant4‘b0010此时mask仍然为4‘b1111。如果在释放授权前req变为4’b0111则下次grant将变为4’b0001这就违反了权重轮询的规则。因此需要对第一次仲裁后的mask进行特别处理如果req_mask不全为0则将req_mask跟mask_hi_pri_req按位|起来之后作为mask。对于刚才这个例子来说req第一次仲裁后mask变为4’b1110req变为4‘b0111后req_mask4’b0110mask_hi_pri_req4‘b1100grant4‘b0010符合仲裁规则。如果req_mask全为0则对req和unmask_hi_pri_req作上述处理。至此基于两种方法的权重轮询仲裁都已经介绍完毕。两种方法都支持参数化设计rtl代码放在下面。相较之下基于grant的方法虽然更好理解但其在双倍位宽下使用了减法器对于用户较多的场景下组合逻辑时延和面积会较大而基于request的方法仅仅使用了与或非的逻辑在时序上和面积上都有更好的表现。因此博主更加推荐基于request的仲裁方法。三、RTL1.基于grant设计moduleweight_rr_arbiter(clk,rst_n,req,weight,grant,grant_vld,grant_ff,grant_ff_vld,switch_to_next);parameter REQ_CNT4;parameter GRANT_WIDTH5;parameter INIT_GRANT{{REQ_CNT-1{1b0}}, 1b1};parameter WEIGHT_WIDTHGRANT_WIDTH*REQ_CNT;input clk;input rst_n;input[REQ_CNT-1:0]req;input[WEIGHT_WIDTH-1:0]weight;input switch_to_next;output[REQ_CNT-1:0]grant;output grant_vld;output[REQ_CNT-1:0]grant_ff;output grant_ff_vld;reg[REQ_CNT-1:0]grant_base;wire[REQ_CNT*2-1:0]double_req;wire[REQ_CNT*2-1:0]double_grant;wire[REQ_CNT-1:0]grant;wire grant_vld;reg[REQ_CNT-1:0]grant_ff;reg grant_ff_vld;reg[GRANT_WIDTH-1:0]priority_cnt[REQ_CNT-1:0];wire[REQ_CNT-1:0]cnt_over;wire[REQ_CNT-1:0]round_en;wire req_change;wire no_req;wire no_grant;wire first_grant;wire arb_trig;assign no_req~(|req);assign no_grant~(|grant_ff);assign first_grantno_grant~no_req;assign arb_trigfirst_grant||switch_to_next;assign req_change~(|(grant_ffreq))|req;generate genvar i;for(i0;iREQ_CNT;ii1)begin assign cnt_over[i]priority_cnt[i]weight[GRANT_WIDTH*(i1)-1-:GRANT_WIDTH];assign round_en[i]cnt_over[i]||(priority_cnt[i]!0req_change);always (posedge clk or negedge rst_n)beginif(!rst_n)priority_cnt[i]{GRANT_WIDTH{1b0}};elseif(cnt_over[i]arb_trig)priority_cnt[i]{GRANT_WIDTH{1b0}};elseif(grant[i])priority_cnt[i]priority_cnt[i]1b1;end end endgenerate always (posedge clk or negedge rst_n)beginif(!rst_n)grant_base[REQ_CNT-1:0]INIT_GRANT;elseif(|round_en~no_grant)grant_base[REQ_CNT-1:0]{grant_ff[REQ_CNT-2:0],grant_ff[REQ_CNT-1]};end assign double_req[REQ_CNT*2-1:0]{req,req};assign double_grant[REQ_CNT*2-1:0]double_req(~(double_req-grant_base));assign grantarb_trig?(double_grant[REQ_CNT*2-1:REQ_CNT]|double_grant[REQ_CNT-1:0]):{REQ_CNT{1b0}};assign grant_vld(arb_trig~no_req)?1b1 : 1b0;always (posedge clk or negedge rst_n)beginif(!rst_n)grant_ff{REQ_CNT{1b0}};elseif(arb_trig)grant_ffgrant;elsegrant_ffgrant_ff;end always (posedge clk or negedge rst_n)beginif(!rst_n)grant_ff_vld1b0;elseif(arb_trig)grant_ff_vldno_req?1b0 : 1b1;end endmodule2.基于request设计moduleweight_rr_arbiter(clk,rst_n,req,weight,grant,grant_vld,grant_ff,grant_ff_vld,switch_to_next);parameter REQ_CNT4;parameter GRANT_WIDTH5;parameter INIT_GRANT{{REQ_CNT-1{1b0}}, 1b1};parameter WEIGHT_WIDTHGRANT_WIDTH*REQ_CNT;input clk;input rst_n;input[REQ_CNT-1:0]req;input[WEIGHT_WIDTH-1:0]weight;input switch_to_next;output[REQ_CNT-1:0]grant;output grant_vld;output[REQ_CNT-1:0]grant_ff;output grant_ff_vld;wire[REQ_CNT-1:0]grant;wire grant_vld;reg[REQ_CNT-1:0]grant_ff;reg grant_ff_vld;wire no_req;wire no_grant;wire first_grant;wire arb_trig;reg[GRANT_WIDTH-1:0]priority_cnt[REQ_CNT-1:0];wire[REQ_CNT-1:0]cnt_over;wire round_en;assign no_req~(|req);assign no_grant~(|grant_ff);assign first_grant~no_reqno_grant;assign arb_trigfirst_grant||switch_to_next;assign round_en|cnt_over[REQ_CNT-1:0];wire[REQ_CNT-1:0]req_masked;wire[REQ_CNT-1:0]mask_higher_pri_reqs;wire[REQ_CNT-1:0]grant_masked;wire[REQ_CNT-1:0]unmask_higher_pri_reqs;wire[REQ_CNT-1:0]grant_unmasked;wire no_req_masked;reg[REQ_CNT-1:0]mask_next;//Simple priority arbitration for masked portionassign req_masked[REQ_CNT-1:0]reqmask_next;assign mask_higher_pri_reqs[0]1b0;assign mask_higher_pri_reqs[REQ_CNT-1:1]req_masked[REQ_CNT-2:0]|mask_higher_pri_reqs[REQ_CNT-2:0];assign grant_masked[REQ_CNT-1:0]req_masked[REQ_CNT-1:0]~mask_higher_pri_reqs[REQ_CNT-1:0];//Simple priority arbitration for unmasked portionassign unmask_higher_pri_reqs[0]1b0;assign unmask_higher_pri_reqs[REQ_CNT-1:1]req[REQ_CNT-2:0]|unmask_higher_pri_reqs[REQ_CNT-2:0];assign grant_unmasked[REQ_CNT-1:0]req[REQ_CNT-1:0]~unmask_higher_pri_reqs[REQ_CNT-1:0];//Use grant_masked if there is any there, otherwise use grant_unmaskedassign no_req_masked~(|req_masked);assign grant({REQ_CNT{no_req_masked}}grant_unmasked)|grant_masked;assign grant_vld(arb_trig|req)?1b1 : 1b0;//round cntgenerate genvar i;for(i0;iREQ_CNT;ii1)begin assign cnt_over[i](priority_cnt[i]weight[GRANT_WIDTH*(i1)-1-:GRANT_WIDTH]-1b1);always (posedge clk or negedge rst_n)beginif(!rst_n)priority_cnt[i]{GRANT_WIDTH{1b0}};elseif(cnt_over[i])priority_cnt[i]{GRANT_WIDTH{1b0}};elseif(grant[i]grant_vld)priority_cnt[i]priority_cnt[i]1b1;end end endgenerate//pointer updatealways (posedge clk or negedge rst_n)beginif(!rst_n)mask_next{REQ_CNT{1b1}};elsebegincase({first_grant,round_en})2b10:beginif(|req_masked)mask_nextreq_masked|mask_higher_pri_reqs;elsemask_nextreq|unmask_higher_pri_reqs;end2b01,2b11:beginif(|req_masked)mask_nextmask_higher_pri_reqs;elsebeginif(|req)mask_nextunmask_higher_pri_reqs;elsemask_nextmask_next;end enddefault:mask_nextmask_next;endcase end end always (posedge clk or negedge rst_n)beginif(!rst_n)begin grant_ff{REQ_CNT{1b0}};grant_ff_vld1b0;endelseif(arb_trig)begin grant_ffgrant;grant_ff_vldno_req?1b0 : 1b1;endelsebegin grant_ffgrant_ff;grant_ff_vldgrant_ff_vld;end end endmodule由于博主水平有限内容如有不当之处欢迎指教
企业数字化 ERP 产品动态
相关推荐
【数据分析八步法】识别并处理缺失、重复、异常与关联错误 小周按前一篇的来源台账取回支付订单、退款和门店历史。第一眼看,数据量足够,字段也齐全;合并后却出现一个奇怪结果:某门店的订单数没有变,收入突然翻倍。追查发现,一张订单关联了两条退款记录,直接连接后订单金额被重复计算。若只盯着“缺失值有多少”,这类关联错误会… · 2026/9/25 2:10:16
【数据分析八步法】通过统计与图表建立事实基础 经过来源核对和质量检查,小周终于拿到可以分析的门店数据。运营经理仍在等一个直观回答:“是不是所有门店都在下滑?”如果只展示总收入的一条折线,小周可能回答“整体下降”,却看不出变化来自哪家门店;如果一上来做复杂模型,又可能在最基本的事实尚未看清时过度解释。
… · 2026/9/25 2:10:16
Springboot 集成 Ehcache操作数据库显示SQL语句设置 Springboot 集成 Ehcache操作数据库显示SQL语句设置2023-09-13 23:33:35.030 INFO 6124 --- [ task-1] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-09-13 23:33:35.124 INFO 6124 --- [ t… · 2026/9/25 2:10:16
CiLocks的keyevent速查表:8个Android按键码如何操控锁屏界面 CiLocks的keyevent速查表:8个Android按键码如何操控锁屏界面 【免费下载链接】CiLocks Crack Interface lockscreen, Metasploit and More Android/IOS Hacking 项目地址: https://gitcode.com/GitHub_Trending/ci/CiLocks
CiLocks 是一款开源的 Android 锁屏… · 2026/9/25 2:36:01
PaddleSpeech 标点恢复实战:基于 ERNIE 的 IWSLT2012-中文标点预测全流程指南 人工智能语音音频NLP媒体生成 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation … · 2026/9/25 2:36:01
wheel-picker-cj 源码拆解:双 Scroll 同步实现滚轮吸附的底层原理 wheel-picker-cj 源码拆解:双 Scroll 同步实现滚轮吸附的底层原理 【免费下载链接】wheel-picker-cj 滚轮选择UI组件 项目地址: https://gitcode.com/Cangjie-TPC/wheel-picker-cj
wheel-picker-cj 是一款基于仓颉(Cangjie)语言开发的… · 2026/9/25 2:36:01
阿里云部署OpenClaw:79元/年搭24小时AI代理,TaoToken统一Key接入配置指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 2:36:01
Comp AI CRM 渲染性能实践:用 useTransition 取代手动加载状态,告别多余重渲染 后端前端CRM人工智能AI Agent 【免费下载链接】crm Comp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM. 项目地址: https://gitcode.com/gh_mirrors/crm48/crm 点击查看 免费下载 本指南基于 Comp AI CRM 仓库内置的 Vercel React 最… · 2026/9/25 2:35:55
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37