简介:本资源是Xilinx官方《Vivado HLS教程》UG871中文版(2020.1版)PDF文档,面向FPGA开发初学者及Zynq平台工程师,聚焦高层次综合(HLS)技术落地——解决用C/C++快速实现硬件逻辑、缩短FPGA开发周期的核心痛点。文档共1个PDF文件,大小20.08MB,内容结构完整:涵盖HLS项目创建、Tcl命令接口实操、解决方案级优化、C/C++验证(含任意精度类型)、块级I/O协议等6大实验模块,每章均含设计说明与分步指导,适合作为系统性学习与工程参考手册。已有342人学习下载,读者可直接获取Xilinx权威方法论、标准实验流程、典型调试要点及版本修订说明,无需二次整理即可用于课程教学、项目预研或HLS入门实践。
1. Vivado HLS 教程 UG871 中文版:不是“看懂语法”,而是让 FIR 滤波器在 Zynq 上跑出 200MHz 吞吐量的实操手册
你手头有一段 C 写的 FIR 滤波器,想把它变成 FPGA 上真正能吞吐数据的硬件模块——但不想从 Verilog 状态机开始画波形、调时序、改约束。Vivado HLS 教程 UG871(v2020.1)中文版,就是 Xilinx 官方为你铺好的那条“C 代码直通 RTL”的硬核通道。它不是泛泛讲 HLS 概念的科普读物,而是一套带完整 lab 文件、逐键截图、参数可复现的工程化训练包:从创建 fir_prj 项目、跑通 C 仿真(csim)、生成 RTL、做 RTL co-simulation,到最终把 IP 塞进 Zynq PS/PL 架构里,让 ARM 核通过 AXI4-Lite 控制它、用 AXI4-Stream 推流喂数据——全链路闭环。这份文档特别适合 FPGA 初学者刚啃完 Verilog 基础、正卡在“怎么把算法逻辑落地成可部署模块”这个坎上;也适合已有 Zynq 项目经验的工程师,想系统补全 HLS 流水线优化、任意精度建模、AXI 接口定制等关键能力。它不教你怎么写 Hello World,它教你怎么让一个 FIR 在 xc7z020 或 xcvu9p 上,面积压到 3200 LUTs 以内、时钟跑到 200MHz 以上、且输出波形和 MATLAB 金标完全对齐。
2. 创建第一个 HLS 项目:从 fir.c 到可综合 RTL 的七步闭环
UG871 第二章的实验 1 是整套教程的锚点——它定义了“一个可交付 HLS 设计”的最小完备流程。很多初学者卡在第一步就放弃,不是因为不会点按钮,而是没理解每个操作背后的技术契约:为什么必须指定fir为 top function?为什么out.gold.dat必须和 testbench 放一起?为什么xcvu9p-flgb2104-1e这个器件选型会直接影响后续 pipeline 指令的生效边界?本节不只带你走完 GUI 流程,更拆解每一步背后的编译器行为与硬件映射逻辑。
2.1 项目初始化:路径、顶层函数与测试台的三重绑定
Vivado HLS 对路径敏感是血泪经验。UG871 明确要求解压目录为C:\Vivado_HLS_Tutorial,否则后续所有 Tcl 脚本、相对路径引用(如#include "fir.h")都会崩。这不是矫情,而是 HLS 工具链在 csim 阶段就依赖绝对路径解析头文件和数据文件:
# Windows 下必须确保路径无空格、无中文、无特殊符号 # 正确示例: C:\Vivado_HLS_Tutorial\Introduction\lab1\fir.c C:\Vivado_HLS_Tutorial\Introduction\lab1\fir_test.c C:\Vivado_HLS_Tutorial\Introduction\lab1\out.gold.dat # 错误示例(会导致 csim 报错 fopen failed): C:\我的文档\Vivado_HLS_教程\lab1\out.gold.dat提示:Linux 用户需将路径中的反斜杠
\全部替换为正斜杠/,且Vivado_HLS_Tutorial目录权限必须为rwx(chmod -R 755 Vivado_HLS_Tutorial),否则vivado_hls命令启动后无法读取子目录。
指定顶层函数fir是 HLS 编译器的入口契约。HLS 不会自动推导主函数,它只认你显式声明的#pragma HLS INTERFACE所标注的函数。fir.c中的函数签名必须严格匹配:
// fir.c void fir( int in, // 输入数据流(单点) int *coeff, // 系数数组(外部 RAM) int *out, // 输出数据(单点) int *ce, // 使能信号(控制是否采样) int *reset // 复位信号 );注意:*coeff和*out是指针,意味着 HLS 默认将其综合为 AXI4-Memory-Mapped 接口;若想改为 FIFO 或 AXI4-Stream,必须在后续步骤中加 pragma 指令。这是新手最容易忽略的“接口默认行为陷阱”。
测试台fir_test.c的结构决定了 C 仿真的可信度。UG871 给出的模板包含自检机制:
// fir_test.c 关键片段 int main() { int in_data[100], out_data[100], gold_data[100]; FILE *fp_in = fopen("in.dat", "r"); FILE *fp_gold = fopen("out.gold.dat", "r"); // 黄金数据源 // ... 读入数据、调用 fir()、写入 out.dat ... if (compare_files("out.dat", "out.gold.dat") == 0) { printf("PASS: Output matches golden reference.\n"); return 0; // csim 成功返回 0 } else { printf("FAIL: Output mismatch.\n"); return 1; // csim 失败返回 1 → HLS GUI 会标红报错 } }参数说明:
compare_files()是 HLS 自带的比对工具,它逐字节比较二进制文件。out.gold.dat必须是用 MATLAB 或 Python 生成的、与fir.c算法完全一致的浮点/定点参考输出。若此处比对失败,后续所有 RTL 验证都失去意义——先确保 C 层逻辑正确,这是 HLS 流程的铁律。
2.2 解决方案配置:时钟周期、器件选型与多解决方案管理
UG871 图 2-6 的“Solution Configuration”窗口看似简单,实则埋着三个关键决策点:
Clock Period(10ns):这直接决定综合后 RTL 的目标频率(100MHz)。但注意:10ns 是约束目标,不是保证结果。实际能否达到,取决于
fir函数内部循环的 Critical Path Delay。若你后续加了#pragma HLS PIPELINE II=1,而逻辑延迟 >10ns,综合会报Timing violation并降频到 8ns(125MHz)或更低。所以 10ns 是起点,不是终点。Part Selection(xcvu9p-flgb2104-1e):UltraScale+ 器件的 DSP Slice 数量、BRAM Block Size、LUT 类型(LUT6 vs LUT5)会显著影响
coeff数组的存储方式。例如:coeff有 64 个 int,若用 BRAM 存储,xcvu9p 可用 36Kb BRAM 块实现单端口 RAM;若用分布式 RAM,则消耗 LUTRAM 资源。器件选型不同,syn报告里的Utilization Estimates表格会完全不同。Multiple Solutions:一个项目可建多个 solution(如
sol_10ns,sol_5ns,sol_area_opt),它们共享同一套 C 源码,但独立配置 clock、part、directive。这是 HLS 的核心优势——无需改 C 代码,仅通过 solution 切换就能对比面积/速度权衡。UG871 实验 3 就基于此设计。
创建 solution 后,HLS GUI 自动在项目目录下生成solution1/子文件夹,其结构为:
solution1/ ├── csim/ # C 仿真输出:out.dat, wave.dat(波形数据) ├── syn/ # 综合输出:verilog/(RTL 源码)、report/(synthesis.rpt) ├── sim/ # RTL 仿真输出:wave.shm(Vivado Simulator 波形) └── impl/ # IP 打包输出:component.xml, fir_v1_0.zip(IP 归档包)逻辑说明:
csim阶段验证算法逻辑;syn阶段生成 RTL 并评估资源;sim阶段用 testbench 驱动 RTL,比对out.dat与out.gold.dat;impl阶段生成标准 IP-XACT 描述,供 Vivado IP Integrator 调用。四步缺一不可,漏掉sim就等于没验证硬件行为。
2.3 GUI 界面功能区解耦:哪些窗格真有用,哪些只是摆设
UG871 图 2-8 的 GUI 截图信息量极大,但新手常被“眼花缭乱”的窗格搞晕。一线工程师的真实工作流中,只有四个区域高频使用:
| 窗格名 | 使用频率 | 关键用途 | 血泪经验 |
|---|---|---|---|
| Explorer(资源管理器) | ★★★★★ | 展开Source,Test Bench,Solutions,右键Run C Simulation/Run Synthesis | 双击fir.c后,Outline辅助窗格自动显示函数列表,Ctrl+Click 可跳转定义 |
| Console(控制台) | ★★★★★ | 实时输出csim/syn日志,错误行号精准定位(如ERROR: [HLS 200-10] Line 42: 'coeff' not declared) | 所有报错第一眼盯 Console,别去翻 report 文件——report 是 summary,Console 是 raw log |
| Report(报告窗格) | ★★★★☆ | syn/report/csynth.rpt显示 latency、interval、resource utilization;sim/report/behavioral.rpt显示波形比对结果 | Latency= 总 cycle 数,Interval= 启动间隔;若Interval > 1,说明函数不能连续启动,需加PIPELINE |
| Waveform(波形窗格) | ★★☆☆☆ | csim生成wave.dat,sim生成wave.shm,用于调试时序关系 | 新手易陷误区:以为波形好看就 OK。实际要重点看ce与out的建立/保持时间,是否满足10ns约束 |
其他窗格如Messages(冗余 Console)、Tcl Console(高级用户用)可暂时折叠。记住:HLS GUI 是工具,不是玩具;聚焦 Explorer + Console + Report,效率提升 3 倍。
3. C 验证与任意精度建模:为什么int在 FPGA 上永远不是“32 位安全”的
UG871 第三章直击 HLS 最隐蔽的雷区:C 语言类型与硬件实现的语义鸿沟。当你写int coeff[64],HLS 默认综合成 32-bit 二进制补码,但真实 FIR 设计中,系数往往只需 16-bit 定点、甚至 12-bit;用 32-bit 不仅浪费 BRAM,还会让乘法器变大、时序恶化。UG871 实验 2 和 3 引入ap_int<>和ap_fixed<>,这才是 FPGA 工程师该掌握的“精度可控建模”能力。本节不讲抽象理论,只告诉你ap_fixed<16,2>怎么算、ap_int<12>如何省资源、以及为什么ap_uint<8>比unsigned char更可靠。
3.1 C 验证失败的三大根源:数据类型溢出、指针越界、时序假设错位
C 仿真(csim)通过 ≠ 硬件行为正确。UG871 第三章实验 1 的fir_test.c能 PASS,不代表fir.c在 RTL 中不出错。常见 csim 通过但 RTL 失败的场景:
隐式类型提升溢出
// fir.c 片段(危险写法) int sum = 0; for(int i=0; i<64; i++) { sum += coeff[i] * in; // coeff[i] 和 in 都是 int(32bit),乘积是 64bit,但 sum 是 32bit → 溢出! }C 仿真中
sum溢出可能被编译器静默处理(wrap-around),但 RTL 中会生成 64-bit 加法器再截断,结果与 C 不一致。解决:显式用ap_int<48>声明sum,或加#pragma HLS STABLE variable=sum。指针解引用越界
fir_test.c中若for(i=0; i<=64; i++),C 仿真可能因内存保护未触发而侥幸通过,但 RTL 中coeff[64]访问会触发 AXI 总线SLVERR响应。解决:在fir.c顶部加断言#ifdef __SYNTHESIS__,或用ap_uint<6>作数组索引(强制 0~63)。时序假设错位
C 代码假设coeff数组读取是“瞬时”的,但 RTL 中coeff若综合为 BRAM,读取有 1-cycle 延迟。若fir()内部逻辑未插入寄存器同步,out会采样到旧数据。解决:加#pragma HLS ARRAY_PARTITION variable=coeff block factor=8,将 64-element 数组分块,降低 BRAM 端口竞争。
参数说明:
ARRAY_PARTITION的block模式将数组切分为多个独立 BRAM 块,factor=8表示每块 8 个元素,共需 8 个 BRAM port。这牺牲面积换时序,是 Zynq SoC 设计中平衡 PS/PL 带宽的常用手法。
3.2 任意精度类型实战:ap_fixed<16,2>的位宽计算与资源映射
UG871 实验 2 的核心是把原int coeff[64]替换为ap_fixed<16,2> coeff[64]。这不是简单替换,而是重新定义数据语义:
ap_fixed<W,I>:W=总位宽,I=整数位宽(含符号位),小数位宽 = W-Iap_fixed<16,2>:16-bit 总宽,2-bit 整数(范围 -2 ~ +1.999...),14-bit 小数(精度 2^-14 ≈ 0.000061)
FIR 系数通常归一化到 [-1,1),故ap_fixed<16,2>完美覆盖:整数位 2-bit 足够表示 ±1,小数位 14-bit 提供足够信噪比。
资源映射上,ap_fixed<16,2>比int节省显著:
- 存储:
int占 32-bit BRAM,ap_fixed<16,2>占 16-bit → BRAM 使用量减半 - 运算:
int * int需 32x32=1024-bit 乘法器,ap_fixed<16,2> * ap_fixed<16,2>只需 16x16=256-bit 乘法器 → DSP Slice 占用从 4 个降到 1 个
验证方法:修改fir.c后,必须重跑csim,并检查csim/report/中的csim.log是否有WARNING: [HLS 200-40] Implicit cast from 'int' to 'ap_fixed<16,2>' may lose precision。若有,说明某处int赋值给ap_fixed未加显式转换:
// 正确:显式构造 coeff[i] = ap_fixed<16,2>(float_coeff[i]); // float_coeff[i] 是 MATLAB 生成的 float 值 // 错误:隐式转换(触发 warning) coeff[i] = float_coeff[i]; // HLS 会截断,但不报错,结果不可控逻辑说明:
ap_fixed构造函数的float参数会按 IEEE-754 规则舍入到最近的ap_fixed表示值。若float_coeff[i] = 0.999999,ap_fixed<16,2>(0.999999)结果是0.99993896484375(2^-14 精度限制),这是定点建模的固有误差,需在算法层补偿。
3.3 C++ 任意精度与 SystemC 混合建模:为什么ap_int<12>比short更可靠
UG871 实验 3 演示 C++ 模式,核心是ap_int<W>的无符号/有符号控制比 C 原生类型更精细。例如 FIR 输入in通常为 12-bit ADC 数据,范围 0~4095:
// C++ 模式(推荐) #include "ap_int.h" void fir( ap_uint<12> in, // 明确 12-bit 无符号,范围 0~4095 ap_fixed<16,2> *coeff, ap_int<32> *out, // 32-bit 有符号,容纳累加和 ap_uint<1> *ce, ap_uint<1> *reset );对比 C 原生unsigned short in:
unsigned short在不同编译器下可能是 16-bit 或 32-bit,HLS 无法保证宽度ap_uint<12>强制综合为 12-bit wire,RTL 中无冗余位,节省 LUT
更关键的是ap_int<32>的溢出行为可控。C 中int sum += a*b溢出是未定义行为(UB),而ap_int<32> sum += a*b会按模 2^32 截断,且 HLS 报告中syn/report/csynth.rpt的Resource Utilization表会明确列出DSP48E2使用数(因ap_int<32>乘法映射到 DSP Slice)。
避坑 / 常见问题 / 排查
现象 1:csimPASS,但syn报错ERROR: [HLS 200-70] Cannot allocate array 'coeff' to BRAM: size exceeds device capacity
原因:ap_fixed<16,2>数组过大(如 1024 元素),而所选器件 BRAM 总量不足。xc7z020有 270 个 36Kb BRAM,但ap_fixed<16,2>单元素占 16-bit,1024 元素需 16384-bit = 0.45 个 36Kb BRAM,理论上够用;报错实因 HLS 默认尝试用单端口 BRAM,而 1024 深度需 10-bit 地址线,超出单端口 BRAM 地址宽度。
解决:加#pragma HLS RESOURCE variable=coeff core=RAM_2P强制双端口 BRAM,或#pragma HLS ARRAY_PARTITION variable=coeff cyclic factor=4分块。现象 2:
sim波形中out信号在ce==1时始终为 0
原因:ap_int<32> sum初始化为 0,但fir()函数内未重置sum,导致累加值残留。C 仿真中局部变量每次调用自动清零,RTL 中sum是寄存器,需显式复位。
解决:在fir()开头加if(*reset) sum = 0;,并确保reset信号在 RTL 中连接到ap_int<32>的 reset port。现象 3:
ap_fixed<16,2>系数加载后,out结果与 MATLAB 金标偏差 >1%
原因:MATLAB 生成的coeff是 double 精度,直接赋值给ap_fixed<16,2>时,HLS 默认舍入模式为AP_RND(四舍五入),而 MATLAB 可能用floor。
解决:在fir_test.c中用ap_fixed<16,2>::to_ap_int()显式指定舍入:coeff[i] = ap_fixed<16,2>(float_coeff[i], AP_RND_ZERO);
4. 接口综合:把void fir(...)变成 AXI4-Stream 的四步指令链
UG871 第四章是 HLS 从“能跑”到“能用”的分水岭。fir.c默认综合出的 AXI4-Memory-Mapped 接口,需要 CPU 逐地址读写coeff和out,吞吐率极低。而真实 Zynq SoC 场景中,ADC 数据是连续流,FIR 必须接 AXI4-Stream。本节教你用四条 pragma 指令,把fir()改造成axis_firIP:输入in和ce合并为in_axis,输出out和tlast合并为out_axis,系数coeff用 AXI4-Lite 配置,整个模块支持背压(ready/valid 握手)。这不是配置技巧,而是硬件协议建模的底层逻辑。
4.1 块级 I/O 协议:#pragma HLS INTERFACE s_axilite与#pragma HLS INTERFACE axis
UG871 实验 1 的fir()默认接口是s_axilite(AXI4-Lite),即 CPU 通过读写寄存器控制。要升级为流式,必须重定义in和out的接口协议:
// fir.c 修改后(关键 pragma) void fir( int in, // 原始输入 → 改为 stream int *coeff, // 系数 → 保留 axi-lite 配置 int *out, // 原始输出 → 改为 stream int *ce, int *reset ) { #pragma HLS INTERFACE s_axilite port=return bundle=CTRL #pragma HLS INTERFACE s_axilite port=coeff bundle=CTRL #pragma HLS INTERFACE s_axilite port=ce bundle=CTRL #pragma HLS INTERFACE s_axilite port=reset bundle=CTRL #pragma HLS INTERFACE axis port=in register_mode=both #pragma HLS INTERFACE axis port=out register_mode=both // ... 函数体不变 ... }s_axilite port=coeff:coeff数组作为 AXI4-Lite 从设备,CPU 可通过0x10000等地址写入系数axis port=in:in参数综合为 AXI4-Stream 主设备接口,生成in_tdata,in_tvalid,in_tready信号register_mode=both:在 stream 接口两侧加寄存器,解决跨时钟域问题(如 PS 100MHz 与 PL 200MHz)
参数说明:
bundle=CTRL将所有s_axilite端口打包到同一个 AXI4-Lite 总线(S_AXI_CTRL),减少 Vivado IP Integrator 中连线数量。若不加bundle,每个s_axilite端口会生成独立 AXI 总线,IP 封装后需手动连 5 条总线,极易出错。
4.2 端口 I/O 协议:#pragma HLS INTERFACE ap_fifo与#pragma HLS DATAFLOW
当in和out都是 stream,fir()内部需缓冲数据。UG871 实验 2 演示用ap_fifo替代数组:
#include "ap_axi_sdata.h" void fir( hls::stream<int>& in_stream, // hls::stream 是 AXI4-Stream 封装类 hls::stream<int>& out_stream, int *coeff, int *ce, int *reset ) { #pragma HLS INTERFACE axis port=in_stream #pragma HLS INTERFACE axis port=out_stream #pragma HLS INTERFACE s_axilite port=return #pragma HLS INTERFACE s_axilite port=coeff #pragma HLS INTERFACE s_axilite port=ce #pragma HLS INTERFACE s_axilite port=reset static hls::stream<int> coeff_stream; #pragma HLS STREAM variable=coeff_stream depth=64 // DATAFLOW 启用流水线:三个进程并发 #pragma HLS DATAFLOW load_coeff(coeff, coeff_stream); // 进程1:从 coeff 数组灌入 stream fir_filter(in_stream, coeff_stream, out_stream); // 进程2:滤波主逻辑 output_ctrl(out_stream, ce, reset); // 进程3:控制输出使能 }hls::stream<int>:HLS 提供的 FIFO 封装,depth=64指定内部 BRAM 深度#pragma HLS DATAFLOW:这是关键!它告诉 HLS 编译器:三个函数可并行执行,load_coeff写coeff_stream,fir_filter读coeff_stream,output_ctrl读out_stream,三者通过 FIFO 解耦,消除顺序依赖,大幅提升吞吐率
逻辑说明:
DATAFLOW模式下,fir_filter不再等待load_coeff完成才启动,而是只要coeff_stream有数据就处理。这模拟了真实硬件中“配置通道”与“数据通道”分离的架构,是 Zynq SoC 高性能设计的核心范式。
4.3 数组到 RTL 接口:#pragma HLS ARRAY_PARTITION与#pragma HLS RESOURCE
coeff数组若不加指令,HLS 默认综合为单端口 RAM,fir_filter每 cycle 只能读一个系数,严重拖慢滤波速度。UG871 实验 3 展示如何用ARRAY_PARTITION拆分:
// fir_filter() 内部 #pragma HLS ARRAY_PARTITION variable=coeff complete dim=1 // 或更精细的 #pragma HLS ARRAY_PARTITION variable=coeff cyclic factor=8complete dim=1:将 64-element 数组完全展开为 64 个独立寄存器,每个 cycle 可并行读所有系数 → 占用 64 个 LUTRAM,面积爆炸cyclic factor=8:将数组分 8 组,每组 8 个元素共享一个 BRAM port → 需 8 个 BRAM port,面积/速度平衡
#pragma HLS RESOURCE指定硬件原语:
#pragma HLS RESOURCE variable=coeff core=RAM_2P_BRAM // 强制用双端口 BRAM,支持同时读 coeff[i] 和 coeff[j]避坑 / 常见问题 / 排查
现象 1:syn报错ERROR: [HLS 200-10] Port 'in_stream' has no interface directive
原因:hls::stream类型必须显式加#pragma HLS INTERFACE axis,不能省略。#pragma HLS INTERFACE s_axilite对 stream 无效。
解决:确认in_stream和out_stream参数前都有axispragma,且函数签名中类型为hls::stream<int>&(引用传递)。现象 2:
sim波形中out_stream.tvalid一直为 0
原因:DATAFLOW模式下,若load_coeff进程未完成(coeff_stream为空),fir_filter会阻塞,out_stream无数据输出。
解决:在load_coeff()中加#pragma HLS DEPENDENCE variable=coeff_stream inter false告诉 HLS “写 FIFO 不依赖读”,或确保coeff数组在load_coeff前已由 CPU 配置好。现象 3:AXI4-Stream 接口
in_tready信号在 Vivado 中始终为 0,导致数据流被掐断
原因:in_tready由fir_filter内部逻辑驱动,若fir_filter未加#pragma HLS PIPELINE,其 latency >1,in_tready无法及时响应。
解决:在fir_filter()函数体开头加#pragma HLS PIPELINE II=1,并确保循环内无长延迟操作(如未展开的乘法)。
5. 设计分析与优化:从 DCT 函数看 latency/interval 的本质区别
UG871 第六、七章用 DCT(离散余弦变换)和矩阵乘法两个经典案例,撕开 HLS 优化的黑匣子。很多工程师以为“加#pragma HLS PIPELINE就万事大吉”,结果综合后Interval仍是 10,吞吐率上不去。本节用 DCT 的csynth.rpt报告,手把手教你读懂Latency(执行一次的总 cycle)、Interval(连续启动的最小间隔)、Initiation Interval(II)这三个数字背后的硬件真相,并给出可落地的优化路径:什么时候该UNROLL,什么时候该PARTITION,什么时候必须REMAP。
5.1 DCT 函数的瓶颈定位:从csynth.rpt的Loop Latency表格开始
UG871 第六章的 DCT 示例是一个 8x8 矩阵变换,核心是双重嵌套循环:
void dct_2d( int in[64], int out[64] ) { #pragma HLS ARRAY_PARTITION variable=in complete dim=1 #pragma HLS ARRAY_PARTITION variable=out complete dim=1 int temp[64]; #pragma HLS ARRAY_PARTITION variable=temp complete dim=1 // 行变换 row_dct(in, temp); // 列变换 col_dct(temp, out); } void row_dct(int in[64], int temp[64]) { for(int i=0; i<8; i++) { // 外层循环:8 行 for(int j=0; j<8; j++) { // 内层循环:8 列 int sum = 0; for(int k=0; k<8; k++) { // DCT 核心:8 点乘加 sum += in[i*8+k] * coeff[k][j]; } temp[i*8+j] = sum; } } }运行synthesis后,打开solution1/syn/report/csynth.rpt,定位Loop Latency表格:
+------------------+---------+---------+---------+---------+ | Loop Name | Latency | Iteration Latency | Iterations | Pipeline | +------------------+---------+---------+---------+---------+ | row_dct.j | 128 | 16 | 8 | yes | | row_dct.k | 16 | 2 | 8 | yes | | row_dct.i | 1024 | 128 | 8 | no | +------------------+---------+---------+---------+---------+row_dct.k循环:Latency=16,Iteration Latency=2,Iterations=8→ 每次迭代 2 cycle,共 8 次,总 16 cyclerow_dct.j循环:包裹k,Latency=128,Iteration Latency=16,Iterations=8→ 每次j迭代需 16 cycle(即k循环耗时),共 8 次,总 128 cyclerow_dct.i循环:Pipeline=no→ 未流水,Latency=1024,Iteration Latency=128,Iterations=8→ 每次i迭代 128 cycle,串行执行
关键洞察:row_dct.i未流水,是最大瓶颈。Interval为 1024,意味着每 1024 cycle 才能启动一次dct_2d,吞吐率极低。
5.2 优化指令组合拳:PIPELINE+UNROLL+PARTITION的协同效应
UG871 第七章实验 1 给出优化路径。不是盲目加指令,而是按层次推进:
Step 1:先解row_dct.i的串行锁
void row_dct(int in[64], int temp[64]) { <p> <a href="https://download.csdn.net/download/qq_52465629/90168074" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>