> 文章列表 > 数字IC手撕代码--联发科(总线访问仲裁)

数字IC手撕代码--联发科(总线访问仲裁)

数字IC手撕代码--联发科(总线访问仲裁)

题目描述

当A、B两组的信号请求访问某个模块时,为了保证正确的访问,需要对这些信号进行仲裁。请用Verilog实现一个仲裁器,对两组请求信号进行仲后,要求:

协议如图所示,请求方发送req(request)信号1表示有请求给仲裁器,仲裁器响应grant信号为1表示请求成功:

数字IC手撕代码--联发科(总线访问仲裁)

通过参数定义在冲突情形下,响应A/B的比例

(举例,一段时间内,有若干次A请求和若干次B请求,其中A&B发生冲突的有N次,这N次中先响应A 3次,后响应B 1次,循环反复。举例中的3和1可配置。);

添加必要的注释,增加代码可读性。

解题思路

根据题目描述,很容易想到模块arbiter的端口应该如下:

module arbiter (input wire clk,         // 时钟信号input wire rst,         // 复位信号input wire reqA,        // A组请求信号input wire reqB,        // B组请求信号output reg grantA,      // A组响应信号output reg grantB       // B组响应信号
);

时钟、复位信号。两个请求信号和两个相应响应信号。只有在冲突时,AB冲突响应比为3:1。其他情况下,正常响应。所以可以写一个计数器,仅在冲突时加1。计数器=0,1,2时响应A,计数器=3时响应B,于此同时将计数器置0。

对于仲裁部分,可以将总线的请求信号reqA和reqB拼接成一个2bit信号,这样使用case语句就能避免多级if-else嵌套导致的长组合逻辑链。

在case语句中,把{reqA, reqB}的所有可能2'b00,2'b01,2'b10,2'b11。全都规划到就行,当{reqA, reqB}==2'b11时,判断冲突计数器范围,落在[0,A_ratio-1],则冲突时A获得总线;若counter落在[A_ratio,A_ratio+B_ratio-2]时,冲突时B获得总线。这样模块满足题目要求可自定义冲突分配比例。

代码

module arbiter #(parameter [7:0] A_ratio = 3 ,   // A grant ratio parameter [7:0] B_ratio = 1     // B grant ratio
)(input   wire    clk     ,       // 时钟信号input   wire    rstn    ,       // 复位信号input   wire    reqA    ,       // A组请求信号input   wire    reqB    ,       // B组请求信号output          grantA  ,       // A组响应信号output          grantB          // B组响应信号
);// 定义计数器和比例参数
reg [7:0] counter = 0;             // belongs to [0, A_ratio + B_ratio - 1]
reg grantA_reg,grantB_reg;assign grantA = grantA_reg;
assign grantB = grantB_reg;always @(posedge clk) beginif (!rstn) begingrantA_reg <= 0;grantB_reg <= 0;end else begincase({reqA,reqB})2'b00:begingrantA_reg <= 1'b0;grantB_reg <= 1'b0;end2'b01:begingrantA_reg <= 1'b0;grantB_reg <= 1'b1;end2'b10:begingrantA_reg <= 1'b1;grantB_reg <= 1'b0;enddefault:beginif(counter <= (A_ratio - 1) )begingrantA_reg <= 1'b1;grantB_reg <= 1'b0;endelse begingrantA_reg <= 1'b0;grantB_reg <= 1'b1;endendendcaseend
endalways @(posedge clk)beginif(!rstn)begincounter <= 8'd0;endif( (reqA&&reqB) && (counter <= (A_ratio + B_ratio - 2)) )begincounter <= counter + 1'b1;endelse counter <= 8'd0;
endendmodule

tb

module arbiter_tb;// 定义时钟和复位信号
reg clk;
reg rstn;// 定义A组和B组请求信号
reg reqA;
reg reqB;// 定义A组和B组响应信号
wire grantA;
wire grantB;// 实例化被测试的模块
arbiter dut (.clk(clk),.rstn(rstn),.reqA(reqA),.reqB(reqB),.grantA(grantA),.grantB(grantB)
);// 时钟信号发生器
always #5 clk = ~clk;// 测试用例1:A组优先
initial begin// 初始化信号rstn = 0;clk  = 0;reqA = 0;reqB = 0;// 复位#15 rstn = 1;#1 // 发送A组请求#10 reqA = 1;// 发送B组请求#10 reqA = 0;reqB = 1;// 发送A and B组请求 eight timesrepeat(8) begin#10 reqA = 1;reqB = 1;endrepeat(2) begin#10 reqA = 1;reqB = 0;endrepeat(2) begin#10 reqA = 0;reqB = 1;end#10 reqA = 0;reqB = 0;// 停止测试#100 $finish;
endinitial begin$fsdbDumpfile("arbiter.fsdb");$fsdbDumpvars(0);
endendmodule

波形图

数字IC手撕代码--联发科(总线访问仲裁)

在tb里,先分别让A、B各请求总线一次,然后让他们出现请求冲突8次,最后再让A、B分别请求总线两次,从图中可以看到,在A、B请求冲突的时候,A_grant、B_grant拿到总线的比例是3:1,我们在module定义开头给了两个parameter,定义了A_grant、B_grant拿到总线的比例A_ratio和B_ratio,如果要修改模块代码,修改module传入的parameter的值即可。

更多手撕代码题可以前往 数字IC手撕代码--题库

大数据