仲裁器设计

一、固定优先级仲裁器

仲裁器设计(1)固定优先级仲裁器-CSDN博客

固定优先级的本质是找到一个多比特信号中,最开始出现的1,一般是从低位到高位的顺序. 约定好:低位代表高优先级,高位代表低优先级

轮询的思想简单来说就是屏蔽已经授权的用户,授权给还未仲裁的较高优先级用户,因此核心问题在于如何找到那个还未仲裁的较高优先级用户。这让我们很容易联想到如何找到序列里第一个1位置(由低到高)的问题,它是这样处理的。

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

modulefixed_prio_arb

(

input[2:0]req,

output logic[2:0]grant

);

always_combbegin

case(1'b1)

req[0]: grant = 3'b001;

req[1]:grant=3'b010;

req[2]: grant = 3'b100;

default:grant=3'b000;

endcase

end

endmodule: fixed_prio_arb

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

将序列减1,这样序列中从低位开始,0bit位就会因为不够减向高位借位,当前位就变为1,直到遇到序列中第一个为1的bit位,其因为低位的借位变为0,更高位则保持不变。再将得到的新seq进行按位~,此时原seq中第一个1仍然为1,再和原seq进行按位&操作,即可得到由低到高的第一个1的位置。记住req和它的2的补码按位与

req-1就是从低位找到高位找到最低位的1,然后减掉之后变成0

moduleprior_arb#(

parameter REQ_WIDTH=16

)(

input[REQ_WIDTH-1:0]req,

output[REQ_WIDTH-1:0]grant

);

assign grant = req & (~(req-1));

endmodule

二、轮询仲裁器

【数字IC设计】循环优先级仲裁器 的 verilog实现(原理、源码、仿真)_轮询仲裁器verilog代码-CSDN博客

轮询仲裁需要找到上一次授权后的最高优先级用户,由于grant本身就是独热码,所以只需要将上述中的1b1换成上一次仲裁结果即可,这里就需要使用时序逻辑。考虑到grant的二进制数值可能比req大,因此将位宽扩大一倍,如下操作。将double_req减去上一次仲裁结果后,再取反和原double_req按位&,此时就可找到当前可授权的用户,再把grant位宽恢复成N位。最后需要把当前仲裁结果进行循环左移处理,这是为了在下次仲裁时屏蔽当前已完成仲裁的用户。

总之round-robin还是要体现round, 指的是某一位被选中时,它的优先级被降为最低,而他旁边的高一位的那个优先级变为最高。

该算法每个req的优先级并不固定,会根据前一次的仲裁结果动态调整各个req的优先级。具体描述如下,同样假设有有四个req(DCBA),规定四个req的初始优先级从左到右依次变高,即A的优先级最高,D的优先级最低。Round-Robin规定,当前仲裁胜出的req的优先级在下一轮仲裁中变为最低,胜出req相邻左边的优先级变为最高。即假设在第一次仲裁时,req为4'b0110,那么B在该次仲裁中胜出,那么下一轮仲裁时,四个req的优先级顺序变为BADC(注意这里是顺序轮转,A的优先级会变得比D、C低)。

那如何将Round-Robin转换为相应的数学模型呢?本着有轮子就不新造的理念,可以试着将Fixed Priority Arbitrary升级为Round-Robin。观察可以发现,第一次的仲裁其实是一次优先级顺序为DCBAFixed Priority Arbitrary,第二次仲裁是将原本优先级顺序为DCBA的变为BADC的一次Fixed Priority Arbitrary。为了便于理解,进一步将第二次仲裁拆分为DCBADC00两个同时执行的Fixed Priority Arbitrary,如下图所示。

两个仲裁分布得到了X和Y,X和Y之间谁最终胜出就很简单了,因为在本轮仲裁中,DC的优先级高于BA,所以只要在DC中有相应的req,则Y胜出,反之则X胜出。这里提出pointer的概念,pointer所指的位置表示优先级最高,pointer的更新是基于上一次仲裁结果进行更新。pointer为分界点做两个Fixed Priority Arbitrary,再根据Pointer所指位置以及左边是否有req决定最终胜出的req。

moduleround_robin_arb(

inputclk ,

inputrst_n ,

input[3:0] req,

output[3:0] grant

);

//生成上一次仲裁结果

reg[3:0] last_state;

always@(posedgeclkornegedgerst_n)begin

if(!rst_n)

last_state <=4'b0001;//默认值,表示最低位的优先级最高

elseif(|req)

last_state <= {grant[2:0],grant[3]};//有仲裁请求,根据上一次的仲裁结果,左移1bit后选择新的优先级最高的bit

else

last_state <= last_state;//无仲裁请求时,pre_state不更新

end

//如果最左侧几个高优先级主机都为发起仲裁请求,需要从最低位开始轮询。

//此处通过两个request拼接,将右侧低位拼接到左侧,即可实现对低位的判断。

//相当于上面的例子的{DCBA,DC00}进行仲裁

wire[7:0] grant_ext;

assigngrant_ext = {req,req} & ~({req,req} - last_state);

//得到的grant_ext必定为一个独热码,但是置高位可能在代表低位的高4bit中,因此进行求或运算

assigngrant = grant_ext[3:0] | grant_ext[7:4];

endmodule

三、权重轮询仲裁器

我们在轮询的基础上加上一些权重,仲裁器虽然轮询的去serve requestor的 请求,但是完成一圈轮询后,requestor被serve的次数并不完全相同。

假设requestor有A、B、C、D三个,权值分别为4、3、2、1,假设它们的request一直为高,且从A开始轮询。则A被serve 4 次后B 才能被serve,依次类推。即weighted round robin则是要把weight计数器消耗光之后才轮换。

如果A被serve的次数不够4次,此时request被拉低了呢?

这个时候,我们不能等待A,而是要serve其他request为高的source,不然如果A后面不再发出有request,其他source的request就会永远不能被serve,就会挂死。

因此,sourcecounterweight相同,或者是正在被servesource request被拉低,则重新load权值

moduleweight_round_arb(

input clk,

input rst_n,

input[3:0]req,

output[3:0]grant

);

parameter integer WEIGHT[3:0] = {1,2,3,4};

reg [3:0] last_state;

reg [3:0] count [3:0];

wire [3:0] round_cell_en; //请求通道轮换触发信号

wire round_en;

genvar i;

generate

for(i=0; i<4; i=i+1) begin: counter

always @(posedge clk or negedge rst_n) begin

if(!rst_n)

count[i] <= 4'b0;

elseif(|grant)begin

if(grant[i])

count[i]<=count[i]+1'b1;

else

count[i] <= 4'b0;

end

end

// 1.当前节点连续占用总线的次数,达到了设定的权重上限,必须交出控制权。2.主动释放(count[i] != 0 & ~req[i]):提前换人。当前节点的额度还没用完(连庄进行中),但突然撤销了请求(比如数据发完了),必须立刻交出总线,避免浪费资源。

assign round_cell_en[i]=(count[i]==WEIGHT[i])|((count[i]!=4'd0) & (~req[i]));

end

endgenerate

//使用按位或归约运算符(|)简化你的写法

assign round_en = (|round_cell_en) & (|req);

always @(posedge clk or negedge rst_n) begin

if(!rst_n)

last_state <= 4'b0001;

elseif(round_en)

last_state<={grant[2:0],grant[3]};

end

wire[7:0]grant_ext;

assign grant_ext={req,req}&~({req,req}-last_state);

assign grant=grant_ext[3:0]|grant_ext[7:4];

endmodule

四、总结

仲裁器用于管理多个主设备对同一共享资源(如总线,存储器)的访问请求,确保在任何时刻只有一个主设备可以获得访问权限,避免资源冲突。

如果请求信号是异步的怎么办呢

就需要用同步器,可以直接用打两拍的方式,虽然是多bit信号,但是每一个请求信号都来自不同的模块

如何优化仲裁器的时序和面积呢

时序:例如对于轮询仲裁器,可以用流水线的方式,第一拍更新指针,第二拍根据指针生成最终的grant。

面积:assign grant_ext={req,req}&~({req,req}-last_state);

在一个SoC系统中,CPU,DMA和GPU都要访问DDR,你会用哪个仲裁策略,为什么呢

1.流量识别与分类:

首先需要识别系统中所有可能产生数据流的主设备(Master):CPU集群、GPU、NPU、DMA、视频编解码器、网络接口等。为这些数据流分类贴标签,通常分为几大类:

延迟敏感型:CPU取指、缓存维护、中断控制、触控反馈、音频回放。目标:低延迟。

带宽敏感型:GPU纹理渲染、视频帧传输、内存拷贝(DMA)。目标:高带宽。

实时保证型:显示控制器(必须在下个帧扫描前送出新数据)、汽车安全总线。目标:确定性延迟和带宽,绝不能有抖动。

最佳effort型:后台网络下载、磁盘碎片整理。目标:有空闲资源则处理,无任何保证。

2.制定QoS策略

根据分类,将系统资源(总线带宽、内存带宽)按优先级进行分配。

例如:

策略1:CPU的实时请求(Latency-Sensitive)优先级最高,可插队任何其他请求

策略2:GPU的带宽请求(Bandwidth-Sensitive)享有最高权重,保证其80%的可用带宽

策略3:显示DMA控制器使用固定的时间片(TDMA),保证其每秒60帧数据必须按时送达

有哪些常见的仲裁策略呢