Systemverilog实现参数化的Round-Robin Arbiter Tree

news2024/11/19 0:43:16

本篇内容涉及的rtl代码为开源组织PLUP的common cell仓库中的源代码,本文只是对其进行些许解读。源码链接如下:[https://github.com/pulp-platform/common_cells/blob/dc555643226419b7a602f0aa39d449545ea4c1f2/src/rr_arb_tree.sv]
“想要快速提升编程能力的一个捷径是阅读优秀的源码” —— 忘记谁说的了

简介

  • 首先Round Robin是考虑到公平性的一种仲裁算法。
  • 基本思路:当一个req得到了grant许可之后,它的优先级在下一次仲裁时就会调整为最低
  • 目的:每个req的优先级不固定,在被grant之后降至最低,保证所有req都能轮流被grant。
  • 范例代码:PLUP开源仓库的common cell中的rr_arb_tree IP

功能

  1. 支持外部输入优先级
  2. 支持req锁存
  3. 内部优先级支持两种模式,一种是自动调整轮询顺序, 另一种是固定轮询顺序

代码分析

    1. 外部输入优先级
    if (ExtPrio) begin : gen_ext_rr
      assign rr_q       = rr_i;
      assign req_d      = req_i;
    end else begin : gen_int_rr
    ...
    end
  • 如果使能了ExtPrio参数,则最高优先级序号rr_q将由输入的rr_i指定。

  • 这里的rr_q是最高优先级的index, 例如输入的req序列有8bit, 我们希望初始时第3个req源具有最高优先级,则只需要把ExtPrio宏打开,并给rr_i输入3’d2即可。

  • 选择外部优先级时不支持输入锁存和调整轮询顺序,也就是说此时该模块需要依赖外部来调整优先级,模块只会根据输入的优先级输出仲裁结果。

    1. req锁存:
      if (LockIn) begin : gen_lock
        logic  lock_d, lock_q;
        logic [NumIn-1:0] req_q;

        assign lock_d     = req_o & ~gnt_i;
        assign req_d      = (lock_q) ? req_q : req_i;

        always_ff @(posedge clk_i or negedge rst_ni) begin : p_lock_reg
          if (!rst_ni) begin
            lock_q <= '0;
          end else begin
            if (flush_i) begin
              lock_q <= '0;
            end else begin
              lock_q <= lock_d;
            end
          end
        end
  • req_i中存在1,也就是有待仲裁的request时,如果仲裁开始信号gnt_i还没拉高,那么会锁存此时的request序列,所以仲裁结果也不会改变。
  • 直到仲裁开始信号gnt_i拉高后,才开始输出当前的request输入序列的仲裁结果
  • 简单来说就是gnt_i信号为低, 但是有req请求时锁存上一次的仲裁结果。

3. 自动调整轮询顺序:

if (FairArb) begin : gen_fair_arb
        logic [NumIn-1:0] upper_mask,  lower_mask;
        idx_t             upper_idx,   lower_idx,   next_idx;
        logic             upper_empty, lower_empty;

        for (genvar i = 0; i < NumIn; i++) begin : gen_mask
          assign upper_mask[i] = (i >  rr_q) ? req_d[i] : 1'b0;
          assign lower_mask[i] = (i <= rr_q) ? req_d[i] : 1'b0;
        end

        lzc #(
          .WIDTH ( NumIn ),
          .MODE  ( 1'b0  )
        ) i_lzc_upper (
          .in_i    ( upper_mask  ),
          .cnt_o   ( upper_idx   ),
          .empty_o ( upper_empty )
        );

        lzc #(
          .WIDTH ( NumIn ),
          .MODE  ( 1'b0  )
        ) i_lzc_lower (
          .in_i    ( lower_mask  ),
          .cnt_o   ( lower_idx   ),
          .empty_o ( /*unused*/  )
        );

        assign next_idx = upper_empty      ? lower_idx : upper_idx;
        assign rr_d     = (gnt_i && req_o) ? next_idx  : rr_q;
  • 通过FairArb参数使能自动调整轮询顺序。
  • 如下图所示,假设此时rr=1, 也就是第2个req为最高优先级。构造两个mask,upper_mask过滤出第3-8个req, low_mask过滤出第1-2个req. 两组mask分别求第一个1所在的位置。如果upper mask中存在1,则下一个最高优先级就是upper mask中第一个1的index;如果upper mask中没有1, 则下一个最高优先级就是low mask组中的第一个1的index。
  • 当仲裁开始信号gnt_i拉高并且req给出了结果后,本次的仲裁结束,就会将rr_d赋值为新的next_rr
  • Round-Robin Arbiter Tree-autorr
  1. 仲裁器的树型实现
localparam int unsigned NumLevels = unsigned'($clog2(NumIn));
idx_t    [2**NumLevels-2:0] index_nodes; // used to propagate the indices
DataType [2**NumLevels-2:0] data_nodes;  // used to propagate the data
logic    [2**NumLevels-2:0] gnt_nodes;   // used to propagate the grant to masters
logic[2**Nu	mLevels-2:0] req_nodes;   // used to propagate the requests to slave
// the final arbitration decision can be taken from the root of the tree
assign req_o        = req_nodes[0];
assign data_o       = data_nodes[0];
assign idx_o        = index_nodes[0];

assign gnt_nodes[0] = gnt_i;
// arbiter tree
for (genvar level = 0; unsigned'(level) < NumLevels; level++) begin : gen_levels
  for (genvar l = 0; l < 2**level; l++) begin : gen_level
	// local select signal
	logic sel;
	// index calcs
	localparam int unsigned Idx0 = 2**level-1+l;// current node
	localparam int unsigned Idx1 = 2**(level+1)-1+l*2;
	//
	// uppermost level where data is fed in from the inputs
	if (unsigned'(level) == NumLevels-1) begin : gen_first_level
	  // if two successive indices are still in the vector...
	  if (unsigned'(l) * 2 < NumIn-1) begin : gen_reduce
		assign req_nodes[Idx0]   = req_d[l*2] | req_d[l*2+1];

		// arbitration: round robin
		assign sel =  ~req_d[l*2] | req_d[l*2+1] & rr_q[NumLevels-1-level];

		assign index_nodes[Idx0] = idx_t'(sel);
		assign data_nodes[Idx0]  = (sel) ? data_i[l*2+1] : data_i[l*2];
		assign gnt_o[l*2]        = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2])   & ~sel;
		assign gnt_o[l*2+1]      = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2+1]) & sel;
	  end
	  // if only the first index is still in the vector...
	  if (unsigned'(l) * 2 == NumIn-1) begin : gen_first
		assign req_nodes[Idx0]   = req_d[l*2];
		assign index_nodes[Idx0] = '0;// always zero in this case
		assign data_nodes[Idx0]  = data_i[l*2];
		assign gnt_o[l*2]        = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2]);
	  end
	  // if index is out of range, fill up with zeros (will get pruned)
	  if (unsigned'(l) * 2 > NumIn-1) begin : gen_out_of_range
		assign req_nodes[Idx0]   = 1'b0;
		assign index_nodes[Idx0] = idx_t'('0);
		assign data_nodes[Idx0]  = DataType'('0);
	  end
	//
	// general case for other levels within the tree
	end else begin : gen_other_levels
	  assign req_nodes[Idx0]   = req_nodes[Idx1] | req_nodes[Idx1+1];

	  // arbitration: round robin
	  assign sel =  ~req_nodes[Idx1] | req_nodes[Idx1+1] & rr_q[NumLevels-1-level];

	  assign index_nodes[Idx0] = (sel) ?
		idx_t'({1'b1, index_nodes[Idx1+1][NumLevels-unsigned'(level)-2:0]}) :
		idx_t'({1'b0, index_nodes[Idx1][NumLevels-unsigned'(level)-2:0]});

	  assign data_nodes[Idx0]  = (sel) ? data_nodes[Idx1+1] : data_nodes[Idx1];
	  assign gnt_nodes[Idx1]   = gnt_nodes[Idx0] & ~sel;
	  assign gnt_nodes[Idx1+1] = gnt_nodes[Idx0] & sel;
	end
  end
end	

  • 初看这段代码有点吓人,两层for genvar外加这么多if生成语句。但是实际上可以暂时先忽略第2和第3个if。
  • 这段代码主要实现了几个功能:
  1. 根据输入的req_i输出req_o. req_o其实就是req_i按位或,只是用二叉树关键路径更短。
  2. 根据rr_q, 也就是当前最高优先级的index生成sel选择信号。sel信号表示每次二叉时选左边还是右边。下图就是生成req_node和sel信号的一个例子,其中当前的rr_q = ‘b001.
  1. 根据sel信号生成gnt_o仲裁信号,gnt_o是onehot格式的。根据每一层的sel信号,可以逐步将gnt_i信号顺着二叉树放到gnt_o中对应的bit位置,下图是一个实际例子:
  1. 在生成gnt_o中,实际也顺便根据sel信号生成了最终仲裁结果的index. 如果需要req源还附带数据通道,需要给出仲裁后的data,则也可以顺便逐级送出被仲裁的data到data_o。
  2. 在for genvar循环的第一个if,也就是gen_first_level逻辑中,除了主要代码段gen_reduce之外,还有两个if,分别是gen_first和gen_out_of_range两个生成器。
  3. gen_first:由于二叉树的输入需要是2N的格式,才能正常往下(上)生长,所以当输入req数不是2N时,就需要进行特殊处理。例如req个数为7时,则树有三层。最顶层有7个节点,二分后第七个一定会落单,所以gen_first判断当前的节点是否是这种情况,也就是if (unsigned’(l) * 2 == NumIn-1),如果匹配的话,这个落单的节点将单独向下生长节点。
  4. gen_out_of_range: gen_first是处理落单的节点,而gen_out_of_range就是处理多余的节点。例如req个数为5,NumLevels = unsigned’($clog2(NumIn))=3, 树仍然有三层,此时第一层除了有第五个落单节点之外,在for循环遍历时还会遍历到7,8两个不存在的节点,因此需要虚构出这两个节点并将其赋为0.


知乎:flappylyc - 知乎 (zhihu.com)
博客园: love小酒窝 - 博客园 (cnblogs.com)
CSDN:love小酒窝的CSDN博客-IC领域博主
公众号已开,了解更多相关内容可以扫一扫下方二维码~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/107041.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于springboot的公司人事管理系统

1 简介 今天向大家介绍一个帮助往届学生完成的毕业设计项目&#xff0c;公司人事管理系统。 计算机毕业生设计,课程设计需要帮助的可以找我 源码获取------》 链接&#xff1a;https://pan.baidu.com/s/1CdxrlV7GeRRmsT9UWEMtJg 提取码&#xff1a;cygy 2 设计概要 21世纪…

测试人不得不知的 HTTP 状态码知识

HTTP协议是当前使用最广泛的一种通信协议&#xff0c;在性能测试中&#xff0c;也使用的非常广泛。但是&#xff0c;确有很多人在调试性能测试脚本的时候&#xff0c;弄不明白HTTP状态码&#xff0c;不能通过HTTP状态码做些基本判断&#xff0c;今天&#xff0c;就来给大家好好…

客户终身价值(CLTV)计算和回归预测模型(Python)

内容介绍 本文整理了客户终身价值&#xff08;CLV或者CLTV&#xff09;的相关概念&#xff0c;并对一家英国线上零售公司的一年交易数据进行分析&#xff0c;计算该公司所有客户的CLV并且建立回归预测模型。 一、客户生命周期价值 用户生命周期价值Customer Lifetime value(…

常见实用的锁策略详解

&#x1f388;专栏链接:多线程相关知识详解 目录 1.乐观锁VS悲观锁 2.读写锁VS普通互斥锁 3.轻量级锁VS重量级锁 4.自旋锁VS挂起等待锁 5. 公平锁VS非公平锁 6.可重入锁VS不可重入锁 7.关于synchronized的锁策略以及自适应 1.乐观锁VS悲观锁 乐观锁:预测锁…

Windows中安装配置RabbitMQ

本次安装环境win10&#xff0c;采用版本 OTP 25.0.3https://github.com/erlang/otp/releases/tag/OTP-25.0.3RabbitMQ 3.10.13 Release RabbitMQ 3.10.13 rabbitmq/rabbitmq-server GitHubOpen source RabbitMQ: core server and tier 1 (built-in) plugins - Release Rabbi…

[N1CTF 2018]eating_cms parse_url绕过

index.php <?php require_once "function.php"; if(isset($_SESSION[login] )){Header("Location: user.php?pageinfo"); } else{include "templates/index.html"; } ?> function.php <?php session_start(); require_once &q…

Kafka Producer 自定义 拦截器 序列化

Kafka Producer 拦截器 & 序列化 前言 文章中的版本信息、maven依赖如下 JDK17 kafka_2.13-3.3.1 pom文件 <dependencies><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>…

NR HARQ (四)Type-2 HARQ-ACK codebook

微信同步更新欢迎关注同名modem协议笔记 上篇提到type-1 HARQ-ACK codebook&#xff0c;即semi-static codebook&#xff0c;UE要为每个PDSCH候选位置生成反馈&#xff0c;也会包含实际没有下行传输的PDSCH&#xff0c;再加上配置CBG的场景&#xff0c;HARQ-ACK 码本中包含的无…

【Linux 内核 内存管理】物理内存组织结构

一、 UMA和NUMA两种模型 共享存储型多处理机有两种模型 一致内存访问&#xff08;Uniform-Memory-Access&#xff0c;简称UMA&#xff09;模型 非一致内存访问&#xff08;Nonuniform-Memory-Access&#xff0c;简称NUMA&#xff09;模型 UMA模型 物理存储器被所有处理器件均…

超标量处理器设计——第八章_发射

超标量处理器设计——第八章_发射 参考《超标量处理器》姚永斌著 文章目录超标量处理器设计——第八章_发射8.1 简述8.1.1 集中式 VS. 分布式8.1.2 数据捕捉 VS. 非数据捕捉8.1.3 压缩 VS. 非压缩8.2 发射过程的流水线8.2.1 非数据捕捉结构的流水线8.2.2 数据捕捉结构的流水线8…

随手写系列——写一个凯撒密码转换页面

文章目录先展示效果H5编写C3编写JS编写——方法一&#xff1a;过程版JS编写——方法二&#xff1a;对象版代码获取先展示效果 &#xff08;因为主要是实现功能&#xff0c;所以CSS写的很粗糙&#xff09; H5编写 基础结构如下&#xff1a; 先构成最外面的大盒子.box&#…

【Flutter】之便于提高开发效率的周边库和轮子

GetX 状态管理 GetX包含很多功能&#xff0c;各种弹出widget、路由管理、国际化、Utils、状态管理等。 基于路由管理 1. 添加到项目中 1.1. 将此添加到pubspec.yaml文件中。 get: 4.1.4 1.2. 在命令行中运行 flutter packages get 1.3. 在MaterialApp前面加上 “Get”&…

centos7 yum安装postgreSQL

安装环境centos7.6 安装步骤&#xff1a; 1、安装postgresql&#xff1a; yum install postgresql-server 2、安装postgresql 扩展包&#xff1a; yum install postgresql-contrib 3、初始化&#xff1a; postgresql-setup initdb 4、启动开机自启动&#xff1a; systemc…

说话人识别神经网络推理方式

概述 说话人识别是一个序列总结&#xff08;Sequence Summarization&#xff09;任务&#xff0c;输入是音频&#xff08;或者说&#xff0c;声学特征的序列&#xff09;&#xff0c;输出是说话人的嵌入码&#xff0c;有的神经网络可以输入一对音频&#xff0c;直接输出这对音…

java微信支付v3系列——9.微信支付之商家转账API

这个功能就比较复杂了&#xff0c;首先是得有90天的资金流水才能开通&#xff0c;其次开通后还需要在官网进行配置&#xff0c;不能直接调用&#xff0c;并且限制了IP地址。 如下图所示&#xff0c;首先需要进行产品设置&#xff0c;将里面都设置好后才能进行开发&#xff0c;…

feign 调用常见问题避坑指南!

摘要&#xff1a;主要是总结了一下这段时间在使用 feign 的过程中的遇到的一些坑点。一、Get请求自动转化成POST的问题1、client 请求参数没有加上 RequestParam 注解问题代码&#xff1a;GetMapping("/showName") String showName(String name);错误提示&#xff1a…

让 APISpace 告诉你什么场景使用什么API

Q1&#xff1a;某商家打算搞年底促销活动&#xff0c;需要将活动信息通过短信的形式通知给用户&#xff0c;这个场景可以用什么接口&#xff1f; 发送通知类的短信&#xff0c;可以使用 通知短信 API~ 通知短信&#xff0c;支持三大运营商&#xff0c;虚拟运营商短信发送&…

第14章 并发控制与恢复

第14章 并发控制与恢复 考试范围&#xff1a; 14.1-14.3, 14.8-14.11 考试题型&#xff1a; 事务操作 考试内容&#xff1a; 1、锁/共享锁/排它锁的概念 2、多粒度锁 Multiple Granularity 3、两阶段封锁协议 The Two-Phase Locking Protocol 两段锁协议是指同一事务对任何…

2.前端笔记-JS-JS3种书写位置、注释、输入输出

书写位置 行内式嵌入式外部文件引入 1、行内式JS 可以将单行或少量的JS代码写在HTML标签的事件属性中&#xff08;以on开头的属性&#xff09;&#xff0c;如onclick单双引号使用&#xff1a;HTML中推荐双引号&#xff0c;JS中推荐单引号&#xff0c;如 <input type&quo…

vue实现将自己网站(h5链接)分享到微信中形成小卡片(超详细)

大家好&#xff0c;我是雄雄。 前言 我们在分享公众号信息到微信或者群中的时候&#xff0c;会出现一个小卡片&#xff0c;如下所示&#xff1a; 但是呢&#xff0c;这种小卡片只能走微信的接口来实现&#xff0c;比如我们从公众号、小程序中分享的内容可以是这样的。如果我们…