【芯片前端】与RR调度的相爱相杀——verilog实现RR调度器1

前言

之前在做验证时候被拉着参加了一个RR调度的优化比赛,写了写验证环境我发现这没法写啊,这输出和每一拍的时序强相关,根本做不了黑盒的rm嘛。于是我直接拉过来一个golden的RR调度模块当rm做拍拍比对,果然时效果非常好O(∩_∩)O

后面呢又用过几次RR调度,直到上个项目呢在这上面翻了一次车,于是我下决心要把这个东西研究的透彻些,省的之后再出乱子。

关于RR调度的算法和行为也不必赘述了,直接从硬件实现开始吧。

verilog rr dispatch实现

要进行rr调度,第一个要解决的问题就是从一组独热码中选出第一个为1的值并将其输出。这个实现起来比较简单,只需要两行代码:

wire [WD -1:0] mask  = {req[WD -2:0] | mask[WD -2:0], 1'b0};
wire [WD -1:0] grant = ~mask & req;

mask是一个中间信号作为掩码,讲真,mask的这种写法在我第一次接触到的时候还是比较惊奇的,露出了“还能这么搞”的表情,这句代码实际的拆解就是(以WD=4为例,下同):

mask[0] = 0
mask[1] = mask[0] | req[0]
mask[2] = mask[1] | req[1]
mask[3] = mask[2] | req[2]

实际的效果看一组示例就很清晰了,使req[3:0] = 4'b1010:

i值mask[i-1]req[i-1]mask[i]
0NANA0
1000
2011
3101

得到了mask = 4'b1100,可以看到已经把最低位为1的位置锁定住了,即1 0分界点0区域的最高位置,因此通过 ~mask | req即可达到当前调度的结果grant = 4'b0010;

同时分析可以发现,mask还有另外的一个作用:区分高低优先级,mask为1的地方是接下来高优先级要去调度的区域,为0的地方是低优先级去调度的区域因为这块已经调度过了,哪怕又出现了1也要等我把高几比特调度完才可以,因此下一拍实际被调度的req值应该为4‘b1000(低两比特无论是啥值都不需要看,因为在低优先级区域),因此我们引入一个power信号用来屏蔽低优先级区域,这个信号的生成就是上一次调度过程中使用的mask信号。

RR调度中每次ack返回时会切换一次调度结果,因此按照现在的思路,可以先简单的组织一下模块的代码了:

module rr_dispatch #(parameter WD = 2)
(
	input  clk,
	input  rst_n,
	input  [WD -1:0] req,	
	input  ack,
	
	output [WD -1:0] grant
);

reg  [WD -1:0] req_power;

wire [WD -1:0] req_after_power = req & req_power;
wire [WD -1:0] mask = {req_after_power[WD -2:0] | mask[WD -2:0], 1'b0};

lways @(posedge clk or negedge rst_n)begin
	if(~rst_n)begin
		req_power <= {WD{1'b1}};
	end
    else begin
        if(ack) begin
            req_power <= mask;
        end
    end
end

assign grant = ~mask | req_power;

endmodule

req_power的复位值显然应该是全1,这样才能保证一上来调度时所有bit都能同等级的被看到。

但是这样呢就会引发一个问题,也是我之前第一次写rr调度思考了一个晚上如何处理的点(不得不说只有自己思考过才能理解深入啊哈哈),就是当一轮结束时如何无缝切换下一轮呢?先确定下一轮结束的标志,就是req_after_power = ’0。当req_after_power = ’0时我们就不能看power之后的req了,而要看req & '1的调度输入也就是说从头开始调度。一轮调度结束呢有两种情况,一是power真的为‘0了,比如说本轮的req = 4'b1000,则mask = 0000,下一轮的power自然也是4’b0000,req_after_power = 4‘b0000;二是高优先级的调度区域已经没有请求了,比如说本轮的req = 4'b0100,则mask = 1000,下一轮的power自然也是4’b1000,此时req_after_power = 4‘b0000。

因此当req_after_power = 4‘b0000时,就不能以req_after_power作为调度的输入,而是以req本身作为调度输入,因此我们完善一下代码,就用old_和new_来区分本轮调度阶段和新的一轮调度吧:

reg  [WD -1:0] req_power;
wire [WD -1:0] req_after_power = req & req_power;

wire [WD -1:0] old_mask = {req_after_power[WD -2:0] | old_mask[WD -2:0], 1'b0};
wire [WD -1:0] new_mask = {req[WD -2:0]             | new_mask[WD -2:0], 1'b0};

wire [WD -1:0] old_grant = ~old_mask & req_after_power;
wire [WD -1:0] new_grant = ~new_mask & req;

wire   old_grant_work = (|req_after_power);
assign grant = old_grant_work ? old_grant : new_grant;

这样一来,如果本轮调度结果为全0的话,那么就立刻就开始新的一轮轮询调度,避免中间出现空拍,当然了,power的更新也需要简单调整下,如果本轮是调度的old那么就把old_mask写入到power中,调度的是new那么自然就把new_mask写入到power中,只把新人作旧人了:

always @(posedge clk or negedge rst_n)begin
	if(~rst_n)begin
		req_power <= {WD{1'b1}};
	end
	else if(ack) begin
		if(old_grant_work)begin
			req_power <= old_mask;
		end
		else if(|req)begin
			req_power <= new_mask;
		end
	end
end

ok,目前的代码可以看得出是基本成型了,我们来简单仿真一下:

可以看到在第一轮req突然发生改变的时刻,调度行为也是没有发生异常的;

再一段,可以观察到调度行为还是符合预期的。 

但是吧,虽然目前看起来是符合预期的,但是其中还有一个隐忧,这个留到下次吧~~

 RTL代码

module rr_dispatch #(parameter WD = 2)
(
	input clk,
	input rst_n,
	input [WD -1:0] req,	
	input ack,
	
	output [WD -1:0] grant
);

reg  [WD -1:0] req_power;
wire [WD -1:0] req_after_power = req & req_power;

wire [WD -1:0] old_mask = {req_after_power[WD -2:0] | old_mask[WD -2:0], 1'b0};
wire [WD -1:0] new_mask = {req[WD -2:0]             | new_mask[WD -2:0], 1'b0};

wire old_grant_work = (|req_after_power);

wire [WD -1:0] old_grant = ~old_mask & req_after_power;
wire [WD -1:0] new_grant = ~new_mask & req;

always @(posedge clk or negedge rst_n)begin
	if(~rst_n)begin
		req_power <= {WD{1'b1}};
	end
	else if(ack) begin
		if(old_grant_work)begin
			req_power <= old_mask;
		end
		else if(|req)begin
			req_power <= new_mask;
		end
	end
end

assign grant = old_grant_work ? old_grant : new_grant;

endmodule

还有一个代码,是我第一次写的,行为上我观察了下其实也实现了这个过程,只不过呢把过程拆分的冗余了一些,也贴在这里吧:

module rr_dispatch #(parameter WD = 2)
(
	input clk,
	input rst_n,
	input [WD -1:0] req,	
	input ack,
	
	output [WD -1:0] grant
);

reg  [WD -1:0] req_power;
wire [WD -1:0] req_after_power = req & req_power;

wire [WD -1:0] old_mask = {req_after_power[WD -2:0] | old_mask[WD -2:0], 1'b0};
wire [WD -1:0] new_mask = {req[WD -2:0]             | new_mask[WD -2:0], 1'b0};

wire old_continue = |(old_mask & req_after_power);

wire [WD -1:0] old_grant = ~old_mask & req_after_power;
wire [WD -1:0] new_grant = ~new_mask & req;
wire old_grant_work = (|old_grant);

always @(posedge clk or negedge rst_n)begin
	if(~rst_n)begin
		req_power <= {WD{1'b1}};
	end
	else if(ack) begin
		if(old_continue)begin
			req_power <= old_mask;
		end
		else if(~old_grant_work)begin
			req_power <= new_mask;
		end
        else if(|new_mask)begin //old_continue == 0, old_grant_work == 1
            req_power <= {WD{1'b1}};
        end
		else begin
			req_power <= req_power;
		end
	end
	else begin
		req_power <= req_power;
	end
end

assign grant = old_grant_work ? old_grant : new_grant;

endmodule

  • 7
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

尼德兰的喵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值