verilog每日一撕day1:固定优先级与轮询仲裁器

马上就要开始找工作了,晚上睡觉的时候突然想在网上记录一下自己学习经历,我在知乎上学到了很多东西,感觉写点什么也是很好的成长机会.于是今天准备开此贴来记录一下我的手撕代码练习,在找到工作之前争取不断进步!

上学期观摩了一下师兄师姐的笔试题,然后这两天做实习笔试题又遇到了仲裁器代码手撕,或多或少有点零碎的记忆,今天就借此机会把这个问题搞定吧!
我的思路来自于这个微信公众号https://mp.weixin.qq.com/s?__biz=MzI3NjI5MjU3OQ==&mid=2247483852&idx=1&sn=31b59750c623be3d6a0e8c66eaecafb8&chksm=eb76f740dc017e56465f331cac5900c661b4ab52764a3c67c36e7e647fae7a606e8fa1ba77de&scene=21#wechat_redirect
我消化了一下并自己写了代码,如果有问题,还请指出和探讨.

固定优先级仲裁器

module Simplest_Para_FP_Arb#(
    parameter N = 8
)(
    input                           clk,
    input                           rst_n,
    input       [ N-1:0 ]   req,
    output  reg [ N-1:0 ]   grant
);
wire [ N-1:0 ] gnt;
assign gnt = req & ~(req-1);
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        grant <= {N{1'b0}} ;
    end
    else begin
        grant <= gnt ;
    end
    
end
endmodule

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

具体做法就是用到这个式子
req与它的2的补码按位与.即可得到
assign gnt = req & ~(req-1)
特性:一个数与它的补码相与,得到的是独热码.
(ps:这类一些列二进制数,找1的个数,找1的位置,我已经看见过我好几次手撕代码在问了,可能后续会开一期专门写一下?)

轮询仲裁器round-robin

其实说实话我最先开一直以为轮询算法的是选中某个从机时,那它的优先级降到最低,比他低的优先级加1,比它高的优先级不变,后来发现自己理解错了(不知道有没有我这种理解的仲裁器),round-robin还是要体现round, 值得是某一个被选中时,它的优先级被降为最低,而他旁边的那一位(我一般用高一位)的那个优先级变为最高.

先上代码再上思路.

`timescale 1ns / 1ns
module round_robin #(
	parameter N = 8
	)
	(
		input 			rst_n,
		input 			clk,
		input [N-1:0]	req,
		output [N-1:0]  grant
	);
reg [N-1:0] one_hot_priority;  //   0010   => 1230   用独热码表示其优先级
reg [2*N-1:0] grant_double;

reg [N-1:0] one_hot_priority_q ;
always @(posedge clk or negedge rst_n)begin
	if(!rst_n) begin
		one_hot_priority_q <= 8'b1;
	end
	else begin
		one_hot_priority_q <= one_hot_priority;
	end
end

//在每次仲裁之后,都要算出此时变化后的优先级,所以要用组合逻辑
always @(*)begin
	if(!rst_n) begin
		one_hot_priority = 8'b1;
	end
	else if (grant != 'd0)begin
		one_hot_priority = {grant[N-2:0],grant[N-1]};
	end
	else begin
		one_hot_priority = one_hot_priority_q;
	end
end

//在下一个时钟周期输出,grant_double
always @(posedge clk or negedge rst_n)begin
	if(!rst_n) begin
		grant_double  <= 16'b0;
	end
	else begin
		grant_double  <= {req,req} & ~({req,req}-{{N{1'b0}},one_hot_priority});
	end
end
assign grant = grant_double[2*N-1:N] | grant_double[N-1:0];
endmodule

一个非常简单tb,其实我还不太会写tb.

`timescale 1ns / 1ns


module round_robin_tb();
reg rst_n;
reg clk;
reg [7:0] req;
reg empty_req;
wire [7:0] grant;

parameter N = 8;
initial begin
  clk = 1'b0;
end
always #5 clk =~clk;  //t=10ns

initial begin
	rst_n = 1'b0;
    # 95 
  rst_n = 1'b1;
end

initial begin
  empty_req = 1'b0;
    # 105 
  empty_req = 1'b1;
    # 10
  empty_req = 1'b0;
end


  always @(posedge clk or rst_n) begin
    if(~rst_n) begin
      req <= {N{1'b0}};
    end
    else if(empty_req)begin
      req <= 'b0;
    end
    else begin
      req <= $random();
    end
  end



round_robin  #( .N(N)
  )
  inst_round_robin (
	.rst_n(rst_n),
	.clk(clk),
	.req(req),
	.grant(grant)
	);
endmodule

仿真波形图如下所示

在这里插入图片描述

我的思路是:
利用固定优先级 grant = req & ~( req-1)的改进法.
引申为:grant = req & ~( req-one_hot_priority)
出现一个问题: 这只能找到 在 priority 左边的 那个第一个1,但是找不到右边的. 也就是说req 不够减,会得到一个负数.
于是用req的两倍扩展来试试
double_grant = {req,req} & ~({req,req}-one_hot_priority);
当priority所在位和其左边的数有请求req时, 直接就可以找到其grant
当priority的左边的数没有对应的请求时, 只有在右边有,那么右边的值在复制过一边后,可以得到对应的响应
这样只需要double_grant的前面和后面一半相或既可
assign grant = grant_double[2*N-1:N] | grant_double[N-1:0];
而优先级更新只需将one_hot_priority循环位移1位即可.

在这里 double_grant 是req的打一拍后才得到的值,所以grant我用了assign 也相当于是grant,我们希望每次grant后都立马更新新的优先级, 这样就不会影响下一个req的判断, 所以更新优先级需要用组合逻辑, 这样的话遇到一个问题,组合逻辑不能赋值给自己保存状态, 如果有一个时钟周期的请求没有请求那该怎么办? 于是我想了个办法,给one_hot_priority寄存,保存它的值,在更新优先级的时候如果grant == 'd0;那么说明上一拍的req是0,这样就可以将优先级更新为之前打一拍保存后的优先级.

总结

其实固定优先级仲裁器知道一个grant = req & ~( req-1)
其实轮询仲裁器 就只需要知道grant = req & ~( req-1)并将其引申到grant = req & ~( req-one_hot_priority)再引申到 复制两遍
,这样就可以将优先级更新为之前打一拍保存后的优先级.

总结

其实固定优先级仲裁器知道一个grant = req & ~( req-1)
其实轮询仲裁器 就只需要知道grant = req & ~( req-1)并将其引申到grant = req & ~( req-one_hot_priority)再引申到 复制两遍
double_grant = {req,req} & ~({req,req}-one_hot_priority); 即可.剩下的打拍什么的就写写代码对着波形慢慢调咯?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值