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

题目描述

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

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

通过参数定义在冲突情形下,响应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) begin
    if (!rstn) begin
        grantA_reg <= 0;
        grantB_reg <= 0;
    end 
    else begin
        case({reqA,reqB})
            2'b00:begin
                grantA_reg <= 1'b0;
                grantB_reg <= 1'b0;
            end
            2'b01:begin
                grantA_reg <= 1'b0;
                grantB_reg <= 1'b1;
            end
            2'b10:begin
                grantA_reg <= 1'b1;
                grantB_reg <= 1'b0;
            end
            default:begin
                if(counter <= (A_ratio - 1) )begin
                    grantA_reg <= 1'b1;
                    grantB_reg <= 1'b0;
                end
                else begin
                    grantA_reg <= 1'b0;
                    grantB_reg <= 1'b1;
                end
            end
        endcase
    end
end

always @(posedge clk)begin
    if(!rstn)begin
        counter <= 8'd0;
    end
    if( (reqA&&reqB) && (counter <= (A_ratio + B_ratio - 2)) )begin
        counter <= counter + 1'b1;
    end
    else 
        counter <= 8'd0;
end

endmodule

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 times
    repeat(8) begin
    #10 reqA = 1;reqB = 1;
    end
    repeat(2) begin
    #10 reqA = 1;reqB = 0;
    end
    repeat(2) begin
    #10 reqA = 0;reqB = 1;
    end
    #10 reqA = 0;reqB = 0;
    // 停止测试
    #100 $finish;
end

initial begin
    $fsdbDumpfile("arbiter.fsdb");
    $fsdbDumpvars(0);
end

endmodule

波形图

在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手撕代码--题库

猜你喜欢

转载自blog.csdn.net/qq_57502075/article/details/129278408