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全为0 | req全为0时未处理 | 添加默认情况 |
| 轮询不轮询 | last_grant更新时机错误 | 确保在有效授权时更新 |
9. 知识点总结
仲裁方法对比
| 方法 | 实现复杂度 | 公平性 | 实时性 |
|---|---|---|---|
| 固定优先级 | 低 | 差 | 好 |
| 轮询 | 中 | 好 | 中 |
| 菊花链 | 低 | 中 | 差 |
| 动态优先级 | 高 | 可配置 | 可配置 |
AXI仲裁器设计要点
- ID管理:为每个主设备分配唯一ID前缀
- 乱序支持:不同ID可以乱序完成
- 背压处理:使用FIFO缓冲请求
完成本练习后,你应该掌握:
- 仲裁器工作原理
- 固定优先级和轮询算法
- 状态机设计
- AXI总线仲裁概念
最后更新: 2026-07-14