跳到主要内容
SoC设计

仲裁器

进阶级数字设计练习:仲裁器

SOC 进阶 仲裁器

仲裁器 完整练习

1. 模块功能说明

仲裁器(Arbiter)用于多个主设备共享资源时的访问控制。

应用场景:
- 总线仲裁(多个主设备访问同一总线)
- 内存仲裁(多个端口访问同一内存)
- DMA通道仲裁
- 中断优先级仲裁

2. 仲裁原理

多个主设备请求:
Master0 ──req[0]──→ ┌─────────┐
Master1 ──req[1]──→ │ Arbiter │──→ grant[0..N]
Master2 ──req[2]──→ └─────────┘     ↓
                               选中的主设备

为什么需要仲裁?

场景: 多个CPU共享一条总线

CPU0 ──┐
CPU1 ──┼──→ 总线 ──→ 内存
CPU2 ──┘

问题: 同一时刻只能有一个CPU访问总线
解决: 仲裁器决定哪个CPU获得访问权

3. 完整代码

arbiter.sv

//=============================================================================
// Module: arbiter
// Description: 仲裁器
pubDate: 2025-01-01
?n//              支持固定优先级和轮询仲裁
// Author: 学习笔记
// Date: 2026-07-13
//=============================================================================

module arbiter #(
    parameter NUM_MASTER = 4,           // 主设备数量
    parameter MODE = 0                  // 0=固定优先级, 1=轮询
)(
    input  logic clk,
    input  logic rst_n,
    
    // 请求信号
    input  logic [NUM_MASTER-1:0] req,      // 请求
    output logic [NUM_MASTER-1:0] grant,    // 授权
    
    // 选中设备编号
    output logic [$clog2(NUM_MASTER)-1:0] sel
);

    //=========================================================================
    // 信号声明
    //=========================================================================
    logic [$clog2(NUM_MASTER)-1:0] last_grant;  // 上次授权的设备(轮询用)

    //=========================================================================
    // 固定优先级仲裁
    //=========================================================================
    // 优先级: req[3] > req[2] > req[1] > req[0]
    //=========================================================================
    always_comb begin
        grant = '0;
        sel   = '0;
        
        if (MODE == 0) begin
            // 固定优先级:从高位到低位
            for (int i = NUM_MASTER-1; i >= 0; i--) begin
                if (req[i]) begin
                    grant[i] = 1'b1;
                    sel      = i[$clog2(NUM_MASTER)-1:0];
                end
            end
        end
    end

    //=========================================================================
    // 轮询仲裁(Round Robin)
    //=========================================================================
    // 从上次授权的下一个开始查找
    //=========================================================================
    always_comb begin
        if (MODE == 1) begin
            grant = '0;
            sel   = '0;
            
            for (int i = 0; i < NUM_MASTER; i++) begin
                automatic int idx;
                idx = (last_grant + 1 + i) % NUM_MASTER;
                
                if (req[idx]) begin
                    grant[idx] = 1'b1;
                    sel        = idx[$clog2(NUM_MASTER)-1:0];
                end
            end
        end
    end

    //=========================================================================
    // 记录上次授权(轮询用)
    //=========================================================================
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            last_grant <= '0;
        else if (|grant)
            last_grant <= sel;
    end

endmodule

4. Testbench

tb_arbiter.sv

//=============================================================================
// Testbench: tb_arbiter
// Description: 仲裁器测试平台
pubDate: 2025-01-01
?n//=============================================================================

`timescale 1ns / 1ps

module tb_arbiter;

    //=========================================================================
    // 参数
    //=========================================================================
    parameter NUM_MASTER = 4;
    parameter CLK_PERIOD = 10;

    //=========================================================================
    // 信号
    //=========================================================================
    logic clk;
    logic rst_n;
    logic [NUM_MASTER-1:0] req;
    logic [NUM_MASTER-1:0] grant;
    logic [$clog2(NUM_MASTER)-1:0] sel;

    //=========================================================================
    // 时钟生成
    //=========================================================================
    initial clk = 0;
    always #(CLK_PERIOD/2) clk = ~clk;

    //=========================================================================
    // 实例化(固定优先级模式)
    //=========================================================================
    arbiter #(
        .NUM_MASTER(NUM_MASTER),
        .MODE(0)  // 固定优先级
    ) uut (
        .clk(clk),
        .rst_n(rst_n),
        .req(req),
        .grant(grant),
        .sel(sel)
    );

    //=========================================================================
    // 测试激励
    //=========================================================================
    initial begin
        // 初始化
        rst_n = 0;
        req = 0;
        
        #100;
        rst_n = 1;
        #20;
        
        $display("=== 仲裁器测试开始(固定优先级)===");
        
        //=====================================================================
        // 测试1: 单个请求
        //=====================================================================
        $display("--- 测试1: 单个请求 ---");
        req = 4'b0001;  // 只有Master0请求
        #10;
        $display("req=%b, grant=%b, sel=%0d", req, grant, sel);
        req = 4'b0000;
        #10;
        $display("");
        
        //=====================================================================
        // 测试2: 多个请求,低优先级设备先请求
        //=====================================================================
        $display("--- 测试2: 多个请求(Master0和Master2同时请求)---");
        req = 4'b0101;  // Master0和Master2
        #10;
        $display("req=%b, grant=%b, sel=%0d (应该选中Master2)", req, grant, sel);
        req = 4'b0000;
        #10;
        $display("");
        
        //=====================================================================
        // 测试3: 所有设备请求
        //=====================================================================
        $display("--- 测试3: 所有设备请求 ---");
        req = 4'b1111;
        #10;
        $display("req=%b, grant=%b, sel=%0d (应该选中Master3)", req, grant, sel);
        req = 4'b0000;
        #10;
        $display("");
        
        //=====================================================================
        // 测试4: 高优先级设备释放
        //=====================================================================
        $display("--- 测试4: 高优先级设备释放 ---");
        req = 4'b1100;  // Master3和Master2
        #10;
        $display("req=%b, grant=%b, sel=%0d (Master3)", req, grant, sel);
        
        req = 4'b0100;  // Master3释放,只剩Master2
        #10;
        $display("req=%b, grant=%b, sel=%0d (Master2)", req, grant, sel);
        req = 4'b0000;
        #10;
        $display("");
        
        $display("=== 仲裁器测试结束 ===");
        $finish;
    end

    //=========================================================================
    // 波形输出
    //=========================================================================
    initial begin
        $dumpfile("wave_arbiter.vcd");
        $dumpvars(0, tb_arbiter);
    end

endmodule

5. 关键设计点

仲裁算法对比

算法优点缺点适用场景
固定优先级简单、响应快饿死低优先级设备实时系统
轮询公平延迟不确定通用系统
菊花链连线少延迟不确定低速外设

固定优先级实现

优先级: req[3] > req[2] > req[1] > req[0]

if (req[3])      grant = 4'b1000;
else if (req[2]) grant = 4'b0100;
else if (req[1]) grant = 4'b0010;
else if (req[0]) grant = 4'b0001;
else             grant = 4'b0000;

轮询实现

last_grant = 2  (上次授权Master2)
当前查找顺序: Master3 → Master0 → Master1 → Master2

req = 4'b1011  (Master3,1,0请求)

检查Master3: req[3]=1 → 授权Master3

6. 进阶:AXI总线仲裁器

接口定义

module axi_arbiter #(
    parameter NUM_MASTER = 4
)(
    input  logic clk,
    input  logic rst_n,
    
    // AXI Master接口(多个)
    input  logic [31:0] m_axi_awaddr  [NUM_MASTER],
    input  logic        m_axi_awvalid [NUM_MASTER],
    output logic        m_axi_awready [NUM_MASTER],
    
    // ... 其他AXI信号
    
    // AXI Slave接口(共享)
    output logic [31:0] s_axi_awaddr,
    output logic        s_axi_awvalid,
    input  logic        s_axi_awready
);

关键设计点

1. ID管理
   - 为每个主设备分配唯一ID前缀
   - 相同ID必须有序完成

2. 乱序完成
   - 不同ID可以乱序完成
   - 需要跟踪每个事务的状态

3. 背压处理
   - 从设备忙时,需要缓存请求
   - 使用FIFO缓冲

7. 练习任务

任务1:基础验证(必做)

# 编译
iverilog -o tb_arbiter tb_arbiter.sv arbiter.sv

# 运行仿真
vvp tb_arbiter

# 查看波形
gtkwave wave_arbiter.vcd

观察并记录:

  • 固定优先级是否正确?
  • 高优先级设备是否饿死低优先级?
  • grant信号是否独热码?

任务2:轮询模式测试(必做)

修改测试,使用轮询模式(MODE=1):

arbiter #(
    .NUM_MASTER(NUM_MASTER),
    .MODE(1)  // 轮询
) uut (...);

观察并记录:

  • 是否公平轮询?
  • 连续请求时,grant是否依次变化?

任务3:功能扩展(选做)

// 1. 优先级可配置
input  logic [NUM_MASTER-1:0][2:0] priority_level,

// 2. 带锁定的仲裁
input  logic lock,           // 锁定当前授权
output logic locked,         // 已锁定状态

// 3. 带超时的仲裁
parameter TIMEOUT = 100,     // 超时周期
output logic timeout_error   // 超时错误

8. 常见错误

错误原因解决方法
多个grant同时为1优先级逻辑错误检查if-else顺序
grant全为0req全为0时未处理添加默认情况
轮询不轮询last_grant更新时机错误确保在有效授权时更新

9. 知识点总结

仲裁方法对比

方法实现复杂度公平性实时性
固定优先级
轮询
菊花链
动态优先级可配置可配置

AXI仲裁器设计要点

  1. ID管理:为每个主设备分配唯一ID前缀
  2. 乱序支持:不同ID可以乱序完成
  3. 背压处理:使用FIFO缓冲请求

完成本练习后,你应该掌握:

  • 仲裁器工作原理
  • 固定优先级和轮询算法
  • 状态机设计
  • AXI总线仲裁概念

最后更新: 2026-07-14