news 2026/6/5 6:05:33

告别‘插队’!用Verilog手把手教你实现一个公平的Round Robin仲裁器(附SystemVerilog代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
告别‘插队’!用Verilog手把手教你实现一个公平的Round Robin仲裁器(附SystemVerilog代码)

从零构建公平仲裁器:Verilog实现Round Robin轮询算法实战

在数字系统设计中,当多个主设备(如处理器、DMA控制器、外设等)需要共享同一总线或资源时,仲裁器的作用就变得至关重要。传统固定优先级仲裁方案虽然实现简单,但容易导致低优先级设备长期无法获得访问权限,这种现象被称为"饿死"(starvation)。本文将带您深入理解Round Robin仲裁机制,并通过两种典型的Verilog实现方案,手把手教您构建一个参数化、可复用的公平仲裁模块。

1. Round Robin仲裁机制核心原理

Round Robin(轮询)算法通过动态调整优先级来保证每个请求方都能公平地获得资源访问权。其核心思想是:当一个请求方获得授权后,它的优先级会在下一轮仲裁中降至最低,其他请求方则按既定顺序提升优先级。这种机制确保了所有活跃的请求方都能轮流获得服务机会。

1.1 基础概念解析

  • 请求向量(Request Vector):一组二进制信号,每位代表一个主设备的请求状态。例如,4-bit请求向量req[3:0]中,req[2]=1表示设备2正在请求总线访问权。

  • 授权向量(Grant Vector):仲裁器输出的one-hot编码信号,指示当前哪个设备获得访问权。如grant=4'b0100表示设备2获得授权。

  • 优先级轮转:每次授权后,被授权设备的优先级降至最低,其余设备优先级相应调整。例如,初始优先级为0>1>2>3,若设备1获得授权,则新优先级变为2>3>0>1。

1.2 工作流程示例

考虑四个设备(A/B/C/D)的仲裁场景,初始优先级为A>B>C>D:

周期请求原始优先级授权新优先级
1A,CA>B>C>DAB>C>D>A
2A,CB>C>D>ACD>A>B>C
3A,BD>A>B>CAB>C>D>A
4B,DB>C>D>ABC>D>A>B

注意:仅当设备实际发出请求时才会被纳入优先级排序,未请求的设备不会占用授权机会。

2. 优先级动态调整方案实现

第一种实现方案通过实时调整优先级顺序来实现Round Robin机制,其核心是维护一个表示当前最高优先级设备的寄存器。

2.1 基础优先级仲裁模块

首先实现一个可配置优先级的固定仲裁器模块:

module priority_arbiter #( parameter WIDTH = 4 )( input [WIDTH-1:0] req, input [WIDTH-1:0] base_priority, // One-hot指示当前最高优先级位 output [WIDTH-1:0] grant ); // 将请求向量复制两份以处理优先级环绕 wire [2*WIDTH-1:0] double_req = {req, req}; // 计算授权向量:找到base_priority及以上第一个置位的请求 wire [2*WIDTH-1:0] double_gnt = double_req & ~(double_req - base_priority); // 合并两个区间的授权结果 assign grant = double_gnt[WIDTH-1:0] | double_gnt[2*WIDTH-1:WIDTH]; endmodule

这个模块的关键在于base_priority输入,它指定了当前优先级最高的设备。例如,当base_priority=4'b0100时,优先级顺序为2>3>0>1。

2.2 动态优先级控制逻辑

接下来实现优先级调整的状态机:

module round_robin_arbiter #( parameter WIDTH = 4 )( input clk, input rst_n, input [WIDTH-1:0] req, output [WIDTH-1:0] grant ); reg [WIDTH-1:0] priority_reg; // 实例化基础仲裁器 priority_arbiter #(.WIDTH(WIDTH)) arb ( .req(req), .base_priority(priority_reg), .grant(grant) ); // 优先级更新逻辑 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin priority_reg <= {{(WIDTH-1){1'b0}}, 1'b1}; // 默认最低位优先级最高 end else if (|req) begin // 有请求时才更新 priority_reg <= {grant[WIDTH-2:0], grant[WIDTH-1]}; // 左移实现优先级轮转 end end endmodule

关键点说明

  1. 复位时设置设备0为最高优先级
  2. 每次授权后,将授权向量左移一位作为新的优先级基准
  3. 仅当存在有效请求时才更新优先级寄存器

2.3 方案特点分析

优势

  • 结构简单,代码直观
  • 优先级调整逻辑清晰可见

局限性

  • 基础仲裁器使用了较大的减法运算,在请求位数较多时(如64位)可能导致时序紧张
  • 需要维护全宽度的优先级寄存器

3. 请求屏蔽方案实现

第二种实现方案采用固定优先级但动态屏蔽已服务请求的策略,通过两个并行仲裁器实现高效轮询。

3.1 核心设计思想

  1. 主仲裁器:处理未被屏蔽的请求(即尚未获得服务的设备)
  2. 辅助仲裁器:当所有请求都被屏蔽时(即完成一轮轮询),处理原始请求
  3. 屏蔽逻辑:记录已被服务的设备,防止重复授权

3.2 完整实现代码

module rr_arbiter #( parameter N = 4 )( input logic clk, input logic rst_n, input logic [N-1:0] req, output logic [N-1:0] grant ); logic [N-1:0] mask; logic [N-1:0] masked_req; logic [N-1:0] higher_pri_mask; logic [N-1:0] grant_masked; logic [N-1:0] grant_unmasked; logic no_masked_reqs; // 生成屏蔽后的请求 assign masked_req = req & mask; // 主仲裁器(处理屏蔽后的请求) assign higher_pri_mask[N-1:1] = higher_pri_mask[N-2:0] | masked_req[N-2:0]; assign higher_pri_mask[0] = 1'b0; assign grant_masked = masked_req & ~higher_pri_mask; // 辅助仲裁器(处理原始请求) logic [N-1:0] higher_pri_unmask; assign higher_pri_unmask[N-1:1] = higher_pri_unmask[N-2:0] | req[N-2:0]; assign higher_pri_unmask[0] = 1'b0; assign grant_unmasked = req & ~higher_pri_unmask; // 仲裁器选择逻辑 assign no_masked_reqs = ~(|masked_req); assign grant = no_masked_reqs ? grant_unmasked : grant_masked; // 屏蔽寄存器更新 always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin mask <= {N{1'b1}}; // 初始不屏蔽任何设备 end else begin if (|masked_req) begin // 更新屏蔽:屏蔽已授权设备及其更高优先级设备 mask <= higher_pri_mask; end else if (|req) begin // 开始新一轮轮询 mask <= higher_pri_unmask; end end end endmodule

3.3 关键信号说明

信号名称描述
mask屏蔽寄存器,1表示对应设备可参与仲裁
masked_req被屏蔽后的请求向量
higher_pri_mask主仲裁器生成的优先级掩码
grant_masked基于屏蔽后请求的授权结果
grant_unmasked基于原始请求的授权结果(当所有请求被屏蔽时使用)
no_masked_reqs标志位,指示是否所有活跃请求都已被屏蔽

3.4 性能对比

两种实现方案在资源消耗和时序表现上的对比:

指标优先级调整方案请求屏蔽方案
寄存器数量N bitsN bits
组合逻辑复杂度O(2N)减法O(N)优先级编码
关键路径延迟减法器+多路选择两级优先级编码
适用位宽中小规模(≤16)大规模(≥32)

实际选择时需权衡设计复杂度与性能需求,对于FPGA实现通常16位以下两种方案差异不大

4. 测试平台构建与验证

完整的仲裁器设计必须包含充分的验证。下面给出SystemVerilog测试平台示例:

4.1 测试平台框架

module rr_arbiter_tb #(parameter N = 4); logic clk = 0; logic rst_n; logic [N-1:0] req; logic [N-1:0] grant; // 时钟生成 always #5 clk = ~clk; // 实例化被测设计 rr_arbiter #(.N(N)) dut (.*); // 测试用例 initial begin // 初始化 rst_n = 0; req = '0; #20 rst_n = 1; // 测试用例1:单设备持续请求 $display("=== Test 1: Single requester ==="); req = 4'b0001; repeat(8) @(posedge clk); // 测试用例2:多设备交替请求 $display("=== Test 2: Multiple requesters ==="); req = 4'b0101; repeat(16) @(posedge clk); // 测试用例3:随机请求 $display("=== Test 3: Random requests ==="); repeat(32) begin req = $urandom_range(0, 2**N-1); @(posedge clk); end $finish; end // 监控与断言 always @(posedge clk) begin $display("[%0t] req=%b grant=%b", $time, req, grant); // 授权应为one-hot或全零 assert ($onehot0(grant)) else $error("Invalid grant pattern"); // 授权设备必须正在请求 if (|grant) begin assert (req & grant) else $error("Granted device not requesting"); end end endmodule

4.2 典型测试场景

  1. 公平性验证

    • 让所有设备持续发出请求
    • 统计各设备获得授权的次数
    • 预期结果:各设备授权次数应基本相等
  2. 饿死现象检测

    • 设置高优先级设备持续请求
    • 低优先级设备间歇性请求
    • 验证低优先级设备最终能获得授权
  3. 边界条件测试

    • 无请求时授权应为全零
    • 单设备请求时应立即获得授权
    • 全设备请求时应轮转授权

4.3 波形分析要点

使用仿真工具(如ModelSim、VCS等)查看波形时,应重点关注:

  1. 授权信号与请求信号的时序关系
  2. 优先级/屏蔽寄存器的更新时机
  3. 从复位开始的初始状态
  4. 请求突变的处理情况

5. 实际应用优化技巧

在真实项目中使用Round Robin仲裁器时,还需要考虑以下优化点:

5.1 参数化设计增强

module flexible_rr_arbiter #( parameter WIDTH = 8, parameter TYPE = "MASK" // "PRIORITY" or "MASK" )( input clk, input rst_n, input [WIDTH-1:0] req, output [WIDTH-1:0] grant ); generate if (TYPE == "PRIORITY") begin // 实例化优先级调整方案 priority_adjust_impl #(.WIDTH(WIDTH)) impl (.*); end else begin // 实例化请求屏蔽方案 mask_based_impl #(.WIDTH(WIDTH)) impl (.*); end endgenerate endmodule

5.2 权重扩展实现

基础Round Robin算法中每个设备的权重相同。可通过以下修改实现加权轮询:

  1. 为每个设备添加权重计数器
  2. 每次授权后减少对应设备的权重计数
  3. 当计数器归零时暂时屏蔽该设备
  4. 所有计数器归零后重新加载初始权重

5.3 跨时钟域处理

当请求来自不同时钟域时,需要添加同步逻辑:

// 请求同步化处理 sync_cell #(.STAGES(2)) sync [WIDTH-1:0] ( .clk(clk), .din(async_req), .dout(sync_req) );

5.4 性能监控接口

添加统计接口用于调试和性能分析:

logic [WIDTH-1:0][31:0] grant_count; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin grant_count <= '0; end else begin for (int i = 0; i < WIDTH; i++) begin if (grant[i]) grant_count[i] <= grant_count[i] + 1; end end end

在完成基本功能验证后,建议在实际系统环境中进行集成测试,观察仲裁器在真实流量模式下的表现。特别是在高负载情况下,公平性算法对系统整体性能的影响会变得更加明显。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/5 6:04:56

【项目01】AI实战90讲 专栏目录

本文来自《AI实战90讲》——90个实战项目&#xff0c;跑出你的AI竞争力。 一、专栏简介 欢迎来到 《AI实战90讲&#xff1a;从入门到项目落地&#xff08;2026版&#xff09;》&#xff01; 这是一个「动手第一、理论辅助」的 AI 实战专栏。不讲空洞的公式推导&#xff0c;不…

作者头像 李华
网站建设 2026/6/5 6:01:16

告别提取码烦恼!3分钟掌握百度网盘资源一键获取的终极秘籍

告别提取码烦恼&#xff01;3分钟掌握百度网盘资源一键获取的终极秘籍 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 还在为百度网盘分享链接的提取码而抓狂吗&#xff1f;每次找到心仪的学习资料或工作文件&#xff0c;却卡…

作者头像 李华