2.2 高级综合 HLS:从 C 代码到硬件结构


2.2 高级综合 HLS:从 C 代码到硬件结构

本节摘要:高级综合把算法级 C 代码直接映射为周期精确的硬件结构,核心是三步:调度(决定每个操作在第几拍执行)、分配(决定每种资源造几个)、绑定(决定每个操作用哪个资源执行)。本节用同一个 FIR 滤波器例子完整走一遍三步,展示资源约束与延迟的量化换算关系。学完本节,你能对任何一段简单 C 代码手工推演出硬件结构与时延。

一段循环代码的硬件困境

逻辑综合处理的是已经有明确寄存器与组合逻辑边界的 RTL 代码,而算法工程师手里的是 C 或 C++:变量没有寄存器概念,运算没有周期概念。把一段串行 C 代码变成并行硬件,存在根本性的自由度:同一行乘法可以单独造一个乘法器在第 1 拍完成,也可以复用同一个乘法器分 8 拍轮流算 8 个数。前者快但面积大,后者省面积但慢。这个自由度无法自动判定优劣,HLS 的全部艺术就是在延迟、面积、吞吐三个目标之间替设计者做出(或帮他做出)量化权衡。

HLS 流程分三步咬合。调度(scheduling)给每个操作分配执行时钟拍,同时受两样东西约束:数据依赖(乘数没算完,加法不能开始)与资源约束(只有两个乘法器时,同拍最多做两次乘法)。分配(allocation)根据调度结果决定实例化几个加法器、乘法器、寄存器。绑定(binding)把每个具体操作指派到某个硬件单元,并决定哪些变量共享同一个寄存器——两个变量的生存期不重叠,就可以共用一个寄存器。三步中调度是主轴,分配与绑定跟随其后,本节也以调度为重点。

ASAP 与 ALAP:调度的两个极值

无资源约束时,调度的两个极值可以线性时间算出。ASAP(尽早调度):每个操作放在其所有前驱完成后的最早一拍,给出最快方案。ALAP(尽晚调度):从输出截止时间反向推每个操作的最晚一拍,给出最省寄存器的方案。两者的差称为操作的松弛(slack):松弛为零的操作在关键路径上,拖一拍整体就慢一拍。这两个极值还夹出了资源约束调度的解空间——所有可行调度都在 ASAP 与 ALAP 之间。

拿一个 4 抽头 FIR 滤波器算一遍。设乘法 1 拍、加法 1 拍,输出要在第 4 拍内给出。代码形态是 y = x0c0 + x1c1 + x2c2 + x3c3。ASAP 调度:4 个乘法全部在第 1 拍并行执行;3 个加法按依赖树在第 2、3 拍执行(两两先加,再合并);总延迟 3 拍,需要 4 个乘法器、2 个加法器。若资源收紧到每拍只能做 2 次乘法,乘法被迫分两拍执行,加法依次顺延,延迟变成 4 拍。资源再减半,延迟再涨。这个例子里的换算关系值得记住:乘法器个数每减半,延迟约加一拍到两拍——面积与时延的折中曲线就在这两条端点之间滑动。

方案 乘法器 加法器 延迟(拍) 适用场景
全并行 ASAP 4 2 3 高吞吐数据通路
乘法限 2 个 2 2 4 面积敏感中端
乘法限 1 个 1 1 7 极致省面积的慢速通路

列表调度:有约束时的工业标准

资源约束下的最优调度是 NP 难问题,工业工具清一色用列表调度(list scheduling):每一拍开始时,把所有"数据已就绪、资源有空"的操作放进候选池,按优先级排序,资源够就做,不够就留到下一拍。优先级函数最常用的是从 ALAP 算出的紧急度——松弛越小的操作越先安排。列表调度好实现、效果稳定,通常落在最优解一拍之内。

// 列表调度伪代码(按紧急度降序) ListSchedule(ops, resourceLimit) { for (t = 1; ; t++) { ready = ops 中所有前驱已完成且尚未调度的操作 sort ready by (slack 升序) // 松弛小者优先 for (op in ready) { r = op 需要的资源类型 if (used[r][t] < resourceLimit[r]) { schedule op at time t used[r][t] += 1 } } if (所有操作已调度) return schedule } }

流水线与 loop carving:吞吐的倍增器

调度还有一张隐藏的牌:不让一次迭代彻底做完再开始下一次,而是像工厂流水线一样让多次迭代交叠执行。以 4 抽头 FIR 为例,若对循环做全流水( initiation interval,II = 1),每个时钟拍都有一条新数据进入,稳态吞吐达到每拍一个输出——即便单次迭代内部延迟仍是 3 拍。HLS 工具里常见的提示"资源不够导致 II 从 1 降到 2",含义就是:想一拍进一条新数据,但乘法器被上一迭代占着,只能两拍进一条。延迟与吞吐是两个独立指标:流水线改变吞吐,几乎不改变单次延迟;资源缩减同时恶化两者。分不清这两点,是 HLS 使用者最常见的调参失误。

绑定阶段的寄存器共享同理会稍隐蔽一些:两个变量的生存期(从最后一次写入到最后一次读出)不重叠就能共享寄存器,图着色算法(生存期图上着色,色数即寄存器数)是标准解法。这与编译器的寄存器分配同构——事实上 HLS 就是一座面向硬件的编译器,第 7 章会看到 LLVM 基础设施如何成为现代 HLS 工具的前端。

拿到结果后如何判优

给出一份 HLS 报告,先看三行数字:延迟(latency)、 initiation interval、资源使用(各算子个数与 BRAM、DSP 数)。判断它是否健康的方法是拿 ASAP 极值当参照:延迟已贴近 ASAP 极值,说明调度空间用尽,再想提速只能换算法或加流水;II 大于 1,看资源占用率——资源占用高说明瓶颈在硬件量,占用低却 II 大说明瓶颈在依赖链或存储端口。改代码的方向也随之明确:前者减运算(查表替乘法、位宽裁剪),后者拆数组增端口、循环展开加资源。这套判读逻辑比背工具选项重要,选项十年一换,判读逻辑十年不变。

本节要点回顾

  • HLS 三步:调度定时间、分配定资源量、绑定定复用关系,调度是主轴。
  • ASAP 与 ALAP:无约束调度的两个极值,其差是松弛,松弛为零即关键路径。
  • FIR 折中曲线:乘法器从 4 个减到 1 个,延迟从 3 拍涨到 7 拍,面积时延互换可量化。
  • 列表调度:按松弛升序的贪心,工业标配,通常距最优一拍以内。
  • 流水线与 II:流水线改吞吐不改延迟,资源与依赖是 II 的两大约束。
  • 判读方法:拿 ASAP 当参照系,用资源占用率区分瓶颈类型,再决定改代码方向。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U