关于仲裁的那些事

仲裁简介

生活中其实有许多类似需要仲裁的例子,课堂上多人举手,老师选哪个人起来回答问题,那老师就充当了一个仲裁器;突然想到一个更贴切生活的例子;家里就剩一块雪糕了,你,你还有两个比你大的姐姐或者哥哥都想吃这块雪糕,那这块雪糕该给谁呢?有一种做法就是年龄最小的优先级最高,所以你吃到了这块雪糕。也就是这种仲裁机制下,只要你想吃就轮不到你的姐姐哥哥,这就是固定优先级的仲裁(Fix Priority Arbiter);当然不是所以家庭都这样宠爱最小的哈,有的家庭为了公平,就约定好,第一次最小你可以先吃雪糕,但是第二次就不给你了,给到了第二小的姐姐,最后才轮到最大的哥哥,这种仲裁机制就比较公平了,不会让最小的人天天吃雪糕,每个人都会轮上一轮,都会吃上雪糕,这就是轮循仲裁(Round Robin Arbiter);还有一种家庭呢就比较普遍,不会有固定的顺序,就是说这个雪糕由妈妈进行分配,这妈妈多多少少有点偏心,但不像固定优先级那样(这太偏心了),简单来说每次有雪糕了,妈妈进行分配的概率从小到大分别为50%/30%/20%。就有相应的权重,这就被称为加权轮循仲裁(Weighted Robin Arbiter);下面分别进行各仲裁机制的代码实现;

Fixed Priority Arbiter(固定优先级仲裁)

固定优先级就是优先级高的有请求(req),那么就将许可(gnt)给到优先级的高的,低优先级的就不考虑;只有在高优先级的不存在req时才考虑低优先级的;
这里我们假设有三个模块进行请求(后续可参数化设计可修改请求个数);

//fixed priority arbiter 
module fixed_pri_arb #(
parameter REQ_NUM = 3)
(
	input 		[REQ_NUM-1:0] 		req,	//输入请求,低bit位高优先级
	output	reg [REQ_NUM-1:0]		gnt	//输出对应bit位的许可
);
reg [REQ_NUM-1:0] pre_req;   //用以记录是否存在高优先级req;
always @(*)begin
	pre_req[0] = req[0];
	gnt[0]		= req[0];
	for(int i=1;i<REQ_NUM;i=i+1)begin
		pre_req[i] = pre_req[i-1] | req[i];  //只要优先级高的出现,则低优先级对应bit位的pre_req 为1,代表应该让给高优先级
		gnt[i] = pre_req[i-1] & req[i]; //当前bit有req,并且优先级高的bit没有req 
	end
end
endmodule

这是相对简单的一种理解方式,下面进行升级代码,确保自己上面的看会了哈,看懂了哈,不懂记得留言提问,代码我没有经过仿真所以肯定存在问题,多多指教;

就是将上面代码的pre_req的代码进行替换;

modue ........
.......
	pre_req[0] = req[0];
	pre_req[REQ_NUM-1:1] = pre_req[REQ_NUM-2:0] | req[REQ_NUM-2:0]; // 这句代码好好琢磨,其实就是和上面代码的pre_req一样,就是换了个写法;
	gnt = req[REQ_NUM-1:0]  & pre_req[REQ_NUM-1:0];//一样的;
........
endmodule

另外一种是之前同事有在用的一种方法,代码挺简单的,但越简单的代码,越需要理解;

modue ........
.......
assign gnt = req & (~(req-1));
........
endmodule

这里怎么理解呢,你拿1串2进制数进行演算一下就知道了;这里我给你试试;

假设req为110100,则按照我们说的固定优先级则最后的输出gnt应为000100;
req - 1 = 110010 ;
取反为 001101;
则req 之按位与即; 110100 & 001101 = 000100;
符号我们的预期;

其实也就是通过这种操作找出了最低位的那个1,因为之前没1,所以你要借位,
计算流程图
~(req-1) 其实是req(认为是负数)的补码,所以这种操作可以称之为req与它的补码按位与;有点小高级;

下次再更新另外两种仲裁机制;距离上一次更新博客都好久了,还是要养成习惯阿,大家监督监督我!!!

Round Robin Arbiter(轮循仲裁)

可调优先级ip调用

这里先思考一个点,设计出一个优先级可变的固定优先级模块ip;此后我们该路得到许可之后把该路的优先级降到最低即可;

module pri_arb 
#(parameter NUM = 4)
(
	input [NUM-1:0] req, // 输入请求
	input [NUM-1:0] pri,	//输入优先级,独热码,为1位优先级最高,
									//此后高位依次低减,左边的一位优先级最	低;
									//如4‘b0010,代表的优先级[1]>[2]>[3]>[0];
	output [NUM-1:0] gnt //输入的仲裁结果,独热码
);

wire [2*NUM-1:0] db_req;
wire [2*NUM-1:0] db_gnt;

assign db_req = {req,req};
assign db_gnt = db_req & (~(db_req-pri));
assign gnt = db_gnt[2*NUM-1:NUM] | db_gnt[NUM-1:0];

endmodule : pri_arb

原理其实与固定优先级章节差不多,可以再看看那个章节的内容;这边主要的区别就是进行了扩位宽db_req = {req,req};防止req不够减,固定优先级那边因为减法都是-1;所以不存在这个问题;这边建议没看懂的,可以试试自己给进一个req,pri看看输出的gnt是否符合预期;
下面就调用该模块进行优先级的修改得到一个轮询机制的仲裁模块;

module round_arb
#(parameter NUM = 4)
(
	input [NUM-1:0] req,
	output [NUM-1:0] gnt,
	input clk,
	input rst_n
);

reg [NUM-1:0] pri;
always @(posedge clk or negedge rst_n)begin
	if(rst_n == 1'b0)begin
		pri <= {(NUM-1){1'b0},1'b1};
	end
	else if((|req) == 1'b1)begin
		pri <= {gnt[NUM-2:0],gnt[NUM-1]};
	end
	else ;
end

pri_arb U_PRI_ARB
#(parameter NUM = NUM)
(			
		.req (req),
		.pri(pri),
		gnt(gnt)
);

endmoudle : round_arb

思路是将上次给出的gnt,进行移位得到下一次的优先级;如这一次送出去的gnt位4’b0010,那么下一次的优先级就是4‘b0100;

mask处理法

上面的处理,理解上比较好理解,但是当需仲裁位数较大时,再timing和面积上都没有下面所讲述的来的理想;
这里我们新建一个mask信号,当同一时刻的req信号进行了相应的gnt就将该路的mask拉高,此后不会再许可该比特位,当该时刻的req全都相应完之后才会将所以的mask放开,进行新一轮的mask计算;

module round_arb 
#(parameter NUM = 4)
(
input	clk,
input 	rst_n,
input 	[3:0] 	req,
output [3:0] 		gnt
);

//此段处理的是第一次req来后的计算,同固定优先级
wire [3:0] unmask_pre_req;
wire [3:0] unmask_gnt;
assign unmask_pre_req[0] = 1'b0;
assign unmask_pre_req[3:1] = unmask_pre_req[2:0] | req[2:0];
assign unmask_gnt = req & (~unmask_pre_req);

//此处计算req的后续
reg [3:0] mask;	//屏蔽信号,当送出一个gnt后,就应该将此位屏蔽;为0代表屏蔽,如4’b1100,代表屏蔽第零,第一bit位的req;
wire [3:0] mask_pre_req;
wire [3:0] mask_gnt;
wire [3:0] mask_req;
assign mask_pre_req[0] =  1'b0;
assign mask_pre_req[3:1]  = mask_pre_req[2:0] | mask_req[2:0];	
assign mask_req = req & mask;					//经过mask屏蔽处理后的req
assign mask_gnt = mask_req & (~(mask_pre_req)); //

always @(posedge clk or negedge rst_n)begin //mask屏蔽信号来源
	if(rst_n == 1'b0) begin
		mask <= 4'b0;
	end
	else if(|gnt == 1'b1)begin				//当送出一个许可后
		else if(|mask == 1'b0)begin		//如果此时mask为全屏蔽,其实就是第一次req
			mask <= unmask_pre_req;
		end
		else begin							//如果不是第一次则
			mask <= mask_pre_req;
		end
	end
	else ;
end

always@(*)begin				//根据mask信号进行选择
	if(|mask == 1'b0)begin
		gnt = unmask_gnt ;
	end
	else begin
		gnt = mask_gnt;
	end
end

endmodule

这里感觉不是很好理解,本人也是看了一遍文章后,自己思考了许久后写出的代码,不知道是否存在问题,以及bug,希望各位大佬批评指正;另外在这里贴一下原文:
https://mp.weixin.qq.com/s/r-nckE5nGz9mc5KqjPXKYg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汶.z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值