别再死记硬背仲裁器了!用Verilog手搓一个AHB总线仲裁器(附固定/轮询两种实现源码)

张开发
2026/5/26 7:45:26 15 分钟阅读
别再死记硬背仲裁器了!用Verilog手搓一个AHB总线仲裁器(附固定/轮询两种实现源码)
从零构建AHB总线仲裁器Verilog实战与设计哲学第一次接触AHB总线仲裁器时我盯着教科书上那些复杂的信号交互图发愣——箭头交错如蛛网时序波形像心电图而示例代码里那些神秘的信号名更是让人望而生畏。直到有一天导师扔给我一块FPGA开发板说别光看理论自己写个仲裁器试试看。三天的debug地狱后当第一个正确的仲裁波形终于出现在示波器上时那些抽象的概念突然变得无比清晰。这就是硬件设计的魅力理解来自实践而非背诵。1. 为什么需要仲裁器总线争用的现实困境想象一个开放式办公室里的打印机争夺战。当五个同事同时点击打印按钮时如果没有调度机制要么打印机卡死要么文档混杂成一团糟。AHB总线上的主设备Master面临同样的困境——多个主设备如CPU、DMA、GPU等可能同时需要访问共享的存储或外设资源。仲裁器的核心使命是解决三个关键问题冲突预防确保任一时刻只有一个主设备控制总线公平调度合理分配总线使用权根据预设策略效率优化最小化仲裁延迟对系统性能的影响典型的AHB仲裁信号包括信号名称方向描述HBUSREQx输入主设备总线请求信号位向量HGRANTx输出总线授权信号位向量HMASTER输出当前获得总线的主设备编号HREADY输入从设备就绪信号在Verilog中我们可以这样定义仲裁器模块接口module AHB_Arbiter #( parameter REQ_WIDTH 4, parameter ID_WIDTH 2 )( input wire HCLK, input wire HRESETn, input wire [REQ_WIDTH-1:0] HBUSREQx, output reg [REQ_WIDTH-1:0] HGRANTx, output reg [ID_WIDTH-1:0] HMASTER ); // 仲裁逻辑将在这里实现 endmodule注意实际工程中还需考虑HTRANS、HBURST等协议信号但为聚焦核心逻辑我们先简化接口2. 固定优先级仲裁器硬件界的论资排辈固定优先级仲裁就像医院急诊科的分诊制度——无论何时心脏骤停患者永远优先于普通感冒患者。这种策略实现简单但可能导致低优先级主设备饿死。2.1 优先级编码的硬件魔法固定优先级仲裁的核心是找出最低有效位(LSB)为1的位置。Verilog中有多种实现方式方法一使用补码技巧wire [REQ_WIDTH-1:0] one_hot HBUSREQx (-HBUSREQx);这个看似神奇的表达式利用了二进制补码的特性-HBUSREQx等价于~HBUSREQx 1按位与操作会保留原数字中最右边的1方法二case语句直译always (*) begin casez (HBUSREQx) 4b???1: begin HMASTER 0; HGRANTx 4b0001; end 4b??10: begin HMASTER 1; HGRANTx 4b0010; end 4b?100: begin HMASTER 2; HGRANTx 4b0100; end 4b1000: begin HMASTER 3; HGRANTx 4b1000; end default: begin HMASTER 0; HGRANTx 4b0000; end endcase end提示casez语句中的?表示不关心该位值这比if-else链更简洁高效2.2 参数化设计的工程智慧优秀的硬件工程师像乐高大师设计时应考虑模块的复用性。下面是一个完全参数化的实现module AHB_Arbiter_Fix #( parameter REQ_WIDTH 4, parameter ID_WIDTH 2 )( // 端口声明同上 ); // 优先级编码器 wire [REQ_WIDTH-1:0] one_hot; wire [ID_WIDTH-1:0] master_id; assign one_hot HBUSREQx (-HBUSREQx); // 二进制编码转换 generate if (REQ_WIDTH 8) begin // 小规模设计使用查找表 always (*) begin case (one_hot) 8h01: master_id 0; 8h02: master_id 1; 8h04: master_id 2; // ...其他case项 default: master_id 0; endcase end end else begin // 大规模设计使用对数编码器 integer i; always (*) begin master_id 0; for (i0; iREQ_WIDTH; ii1) if (one_hot[i]) master_id i; end end endgenerate // 输出寄存器 always (posedge HCLK or negedge HRESETn) begin if (!HRESETn) begin HGRANTx 0; HMASTER 0; end else begin HGRANTx one_hot; HMASTER master_id; end end endmodule性能对比表实现方式面积开销延迟适用场景补码case中等1周期中等规模(4-8主设备)补码循环编码低多周期大规模(16主设备)优先级编码器IP高1周期超高速系统3. 轮询仲裁器民主决策的硬件实现轮询仲裁就像圆桌会议每个主设备都有机会轮流发言。这种策略更公平但实现复杂度稍高。3.1 状态机硬件中的决策大脑4主设备轮询仲裁的状态转换图S0(0123) → S1(1230) → S2(2301) → S3(3012) ↑_________________________________________↓Verilog实现采用经典的三段式状态机module AHB_Arbiter_RR #( parameter REQ_WIDTH 4 )( // 端口声明同上 ); // 状态定义 typedef enum logic [1:0] { S0 2b00, // 优先级顺序 0123 S1 2b01, // 1230 S2 2b10, // 2301 S3 2b11 // 3012 } state_t; state_t current_state, next_state; // 状态转移逻辑 always (*) begin case (current_state) S0: next_state (HGRANTx[0]) ? S1 : (HGRANTx[1]) ? S1 : (HGRANTx[2]) ? S2 : (HGRANTx[3]) ? S3 : S0; // 其他状态类似 endcase end // 状态寄存器 always (posedge HCLK or negedge HRESETn) begin if (!HRESETn) current_state S0; else current_state next_state; end // 仲裁逻辑 always (*) begin case (current_state) S0: begin if (HBUSREQx[0]) HGRANTx 4b0001; else if (HBUSREQx[1]) HGRANTx 4b0010; // 其他优先级判断 end // 其他状态类似 endcase end endmodule3.2 验证策略制造总线战争好的测试平台应该像严格的裁判能制造各种极端场景。以下测试用例特别重要饥饿测试持续保持高优先级主设备请求观察低优先级是否最终能获得授权冲突测试多个主设备同时发出请求检查仲裁结果是否符合轮询规则边界测试在状态转换边界注入请求验证无授权丢失// 典型测试序列示例 initial begin // 初始状态 HBUSREQx 4b0000; #100; // 测试场景1单主设备持续请求 HBUSREQx 4b0001; repeat(10) (posedge HCLK); // 测试场景2多主设备交替请求 HBUSREQx 4b0101; repeat(20) begin (posedge HCLK); if ($time 500) HBUSREQx ~HBUSREQx; end // 测试场景3全冲突测试 HBUSREQx 4b1111; repeat(15) (posedge HCLK); end4. 进阶设计混合仲裁策略与性能优化实际芯片设计中纯粹的固定或轮询策略往往不能满足复杂需求。我曾参与的一个图像处理SoC项目就遇到了这样的挑战——DMA需要保证最低延迟而CPU需要公平性。最终的解决方案是4.1 分级仲裁架构第一级紧急请求如中断控制器→ 固定优先级 第二级普通请求 → 轮询优先级 第三级后台任务 → 权重轮询Verilog实现关键片段// 紧急请求处理 wire urgent_grant; assign urgent_grant (URGENT_REQ ! 0); always (*) begin if (urgent_grant) begin // 固定优先级处理 casez (URGENT_REQ) 4b???1: begin /* 紧急级别0 */ end 4b??10: begin /* 紧急级别1 */ end // ... endcase end else begin // 普通轮询逻辑 case (rr_state) // 轮询状态处理 endcase end end4.2 时序优化技巧流水线仲裁将仲裁决策分为两个周期提高时钟频率预解码在当前授权周期预测下一周期可能的请求优先级缓存缓存最近几次仲裁结果优化连续访问// 流水线仲裁示例 reg [REQ_WIDTH-1:0] predecode_req; always (posedge HCLK) begin // 第一阶段请求采样 predecode_req HBUSREQx ~HGRANTx; // 第二阶段仲裁决策 case (arb_mode) FIXED: HGRANTx fixed_arb(predecode_req); ROUND: HGRANTx rr_arb(predecode_req); endcase end性能优化前后对比指标基础实现优化实现提升幅度最大时钟频率200MHz350MHz75%仲裁延迟2周期1周期50%面积开销1200LUT1800LUT50%5. 调试实战那些年我踩过的坑第一次实现轮询仲裁器时仿真波形显示授权信号偶尔会消失一个周期。经过三天追踪发现问题出在状态转换条件上——原来的设计在无请求时也会推进状态机导致活跃主设备的优先级被意外降低。教训总结状态转换必须与有效授权严格同步无请求时应保持当前状态不变所有条件分支都需要测试覆盖修正后的状态转移逻辑always (*) begin next_state current_state; // 默认保持状态 if (|HGRANTx) begin // 仅当有实际授权时才改变状态 case (current_state) S0: if (HGRANTx[0]) next_state S1; else if (HGRANTx[1]) next_state S1; // ... endcase end end另一个常见问题是仲裁器与总线协议的状态同步。记得添加HREADY信号处理always (posedge HCLK) begin if (HREADY) begin // 只有当前传输完成时才更新授权 active_grant HGRANTx; end end

更多文章