Titan-1论文阅读

Titan-1是一个开源的、高性能的 RISC-V 向量核心生成器,旨在同时扩展数据级并行性 (DLP) 和指令级并行性 (ILP)
向量处理器问题
DLP 扩展瓶颈(数据路径加宽的难题)
掩码(Mask)广播:在 RISC-V 向量架构中,v0 寄存器用作掩码。当执行“谓词执行”(predicated execution)时,需要将 v0 的值广播到所有通道,这在超宽设计中成为了一个主要的布线瓶颈 。
向量重排布线拥塞:在向量处理中,vrgather (收集)、vslide (滑动) 和 vreduce (规约) 等指令需要进行复杂且非局部(Non-local)的数据重排。例如,gather 需要根据索引从任意通道中提取数据。 实现这些复杂重排需要一个强大的交叉开关网络(或复杂的布线,让任意输出通道都能连接到任意输入通道。如果将这个复杂的交叉开关布线分散到每个计算通道内部,会导致每个通道内部的布线变得极度拥塞、复杂且面积巨大。
ILP 调度效率低下(多指令并发的难题)
标量-向量核同步:标量核心(控制单元)在发出向量指令后,常常必须停机(stall)等待向量核心完成,即使后续的标量指令是独立的,这浪费了资源 。
缺乏精细链接(Chaining):“链接”技术允许一条指令的输出元素在计算完成前就被下一条指令使用 。传统设计缺乏对这种依赖关系的精细管理,导致 ILP 不足 。
内存利用率低:向量 LSU(加载/存储单元)通常一次只能处理一条内存指令,导致读/写带宽减半 。同时,索引加载(Indexed load/store)操作的延迟极高,会阻塞整个管线 。
架构

顶层:标量核心 (Scalar Core)
负责所有指令的控制流管理。
流水线阶段: 处理指令的获取(Fetch IN, ICache, IQ)、解码(ID)、标量执行(EXEC, FP0, FP1)、内存操作(MEM)和指令提交(COMMIT)。
定序器 (Sequencer)
解码向量指令(IQ, ID),生成并分配控制信号给向量核心的各个组件。使用基于指令的记分板 来管理指令依赖。
配置融合: 支持配置指令与 VType 缓存的融合,以避免不必要的流水线停顿。
中央:排列单元
位于 T1 架构的中心位置。 实现通道之间高效的数据移动,处理所有复杂的跨通道数据重排和归约操作(如 vrgather, vreduce)
四周:向量通道 (Vector Lanes)
每个通道独立执行向量指令。
访存
HBLSU
单位步长访问,DLEN 宽数据通路, 独立的加载和存储路径
HOLSU
恒定步长和索引访问,允许后续的非依赖指令前进,利用 ILP,减轻索引访存带来的巨大延迟。
优化实现
DLP 扩展
粗粒度布局规划求解器
T1 使用一个启发式求解器来优化向量通道在芯片上的物理布局(Floorplan) 。这能最小化“widen”和“narrow”等跨通道指令的最差情况路由距离,从而降低延迟 。
采用启发式布局优化
- 目标函数: 求解器不关心芯片的美观或整齐,它唯一的优化目标是:最小化所有关键跨通道指令(如
widen,narrow)所需的最差情况路由距离。 - 输入分析: 求解器首先分析 T1 架构中所有通道之间的通信需求,识别出哪些通道需要进行高频且关键的数据交换。
- 迭代放置: 它采用一种启发式(非穷举搜索)算法,迭代地决定每个通道的物理位置。在放置新通道时,它会参考其所有依赖通道(即需要通信的邻居)的位置。
- 最小化最大距离: 求解器会选择一个位置,使得该新通道与所有已放置的依赖通道之间的最大距离最小化。这确保了没有哪条关键的通信路径被无限拉长。
后端大神
v0 掩码影子缓存
T1 在“重排单元”中为掩码寄存器 v0 维护了一个“影子副本”(即寄存器缓存)。当通道需要掩码时,直接从这个缓存中获取,利用从置换单元到通道的现有物理通道来防止预测指令的跨通道流量,从而避免了对专用通道间连接的需求,避免了代价高昂的跨通道广播。
只有当软件明确更新了 v0 寄存器时,中央控制逻辑才会更新影子缓存。由于 v0 的更新频率相对较低,这种局部缓存策略极大地减轻了持续广播的压力。
专用的重排单元
T1 在芯片中央设置了一个专用的、与数据路径同宽(DLEN-wide)的重排单元 。它高效处理所有跨通道数据重排(如 vrgather, reduce),解决了布线瓶颈 。
感觉可能重排阻塞带来性能降低,但也没见论文再分析这个问题,牺牲前端换后端
ILP 调度
基于记分板的标量-向量 OoO
T1 采用“**发射即提交 **”策略 。标量核心将向量指令“发射”到向量核心的队列后,就视其为“已提交”,然后继续执行后续的独立标量指令 。向量核心内部的专用记分板会跟踪指令的实际执行进度 。
T1 的标量和向量同步机制(向量计分板)。
| 类别 | 定义 (是否影响外部状态) | 标量核心动作 (如何发射) | 向量核心状态跟踪 | 依赖与停顿 (如何同步) |
|---|---|---|---|---|
| 类别 1:纯向量计算 | 不修改标量寄存器或控制状态寄存器(CSR)。 | 标量核心将指令发射到向量队列后,立即认为该指令已提交/退休,并继续执行后续指令。 | 向量核心在内部异步执行,无需与标量核心进行状态同步。 | 无需停顿。实现了完全的乱序执行。 |
| 类别 2:写入标量/CSR | 会修改标量寄存器或 CSR(如浮点异常标志)。 | 标量核心将指令发射后,在其自身的提交阶段记分板中为该指令预留一个条目。 | 向量核心执行完成后,向标量核心的记分板发送完成信号。 | 后续的标量指令如果需要读取这个被影响的标量寄存器/CSR,将被记分板停顿,直到向量核心完成写入。 |
| 类别 3:内存访问 | 会访问主内存,有引发异常(如访问故障)的风险。 | 标量核心将指令发射。风险管理基于地址是否可预测。 | 向量核心负责执行加载/存储,并将访问故障(如果有)的真实状态反馈给标量核心。 | 根据子类进行精细化管理: |
| 子类 3b:单元/常数步进 | 地址可预测,基于标量寄存器计算。 | 标量核心的 LSU(加载/存储单元)在提交前即可预留指令将要访问的内存地址范围。 | - | 后续的标量内存操作,如果与该预留地址冲突,则被停顿;如果不冲突,则继续执行。实现精细的内存访问乱序。 |
| 子类 3c:索引访问 | 地址不可预测,地址存储在向量寄存器中。 | 默认情况:标量核心必须保守地停顿所有后续的标量内存操作,直到向量指令完成(最保守)。 | - | T1 激进优化 (Chicken Bit): 如果设置了 CSR 中的“Chicken Bit”(即软件保证不会发生故障),则解除保守停顿,允许后续的独立指令执行,有效隐藏索引访存带来的巨大延迟。 |
真激进
精细粒度链接
当一条向量指令(A)依赖于前一条指令(B)的输出时,处理器通常会等待指令 B 处理完所有向量元素,并将所有结果写回向量寄存器文件之后,才允许指令 A 开始执行
T1 使用记分板在通道内部以极精细的粒度(例如每个元素组)管理数据依赖 。这使得依赖的指令能够紧密重叠执行,极大地提高了 ILP 。
分块执行: 向量指令在向量核心内部并非一次性完成,而是被分解成多个元素组来执行。每个组包含少量连续的向量元素。
记分板跟踪: T1 在向量核心内部为每一个向量寄存器设置了一个专用的、基于元素组的记分板。
状态位 : 这个记分板为向量寄存器中的每个元素组维护一个或多个状态位。这些状态位跟踪该元素组是否已经计算完成并可供后续指令读取。
一旦一个元素组完成,依赖它的指令就可以立即启动。这使得指令 A 的延迟(从开始到结束的时间)可以被指令 B 的执行所隐藏
内存交错
T1 的 LSU 允许一个加载(Load)和一个存储(Store)指令同时处于执行状态,从而并发使用内存的读写通道,使带宽利用率翻倍 。
独立内存端口/管线 : T1 的 LSU 被设计成拥有独立的读取路径 和 写入路径,它们可以并行操作 L1 数据缓存和 L2 缓存。
尽管 Load 和 Store 指令是从指令流中按顺序发出的,T1 允许它们进入各自的执行管线并并行访问内存。
暑假那时看到过类似的实现(虽然我自己写不出来),大概就是Load-Store 依赖检查(要走个旁路)和Store-Load 乱序要写好挺麻烦的。
配置指令融合
T1 将 vsetvl(设置向量长度)等配置指令与后续的向量指令“融合”,消除了因等待配置生效而引起的管线停顿 。