导语:
前四篇把 Ross 的"四大件"讲完了:Overview、Vivado MCP Server、Knowledge Base(doc_search)、Agent Skills 的安装与维护。从这一篇开始进入实战环节 ------真刀真枪跑官方仓库 xilinx/ross-ai-assistant 里的参考设计。
这一篇走 HLS 产品线 ,案例是官方 examples/edge-detection:一个 43 行的 MATLAB Sobel 边缘检测算法,目标是搬到 Zynq UltraScale+ 上跑到 4400 FPS @ 3.3ns(≈303MHz) 。驱动它的是 Ross 的 HLS skill 链:hls-matlab-to-cpp → hls-architect → hls-optimize。
与下一篇的 Vivado IP 配置案例相比,这条线回答的是另一个问题:算法工程师的 MATLAB 代码,怎么变成可综合、可验证、性能达标的硬件。
先说结果:C++ 与 MATLAB golden 最大误差 0.0043(容差 0.01),HLS 综合全循环 II=1,帧间隔 66317 周期 ≈ 4568 FPS @ 303MHz,时序收敛(slack +0.01ns),LUT 仅占 4%。
一、演示任务:43 行 MATLAB,4400 FPS 的考题
官方案例的算法很简单(rgbEdgeDetector.m,全文 43 行):
matlab
gray = 0.299*R + 0.587*G + 0.114*B; % BT.601 灰度
gx = imfilter(gray, sobelX, 'replicate', 'conv');
gy = imfilter(gray, sobelY, 'replicate', 'conv');
gradMag = sqrt(gx.^2 + gy.^2);
测试图像是经典的 256×256 cameraman。官方 README 给出的设计目标:
| 指标 | 目标 |
|---|---|
| 吞吐 | 4400 FPS(256×256 帧) |
| 时钟 | 3.3 ns(≈303 MHz) |
| 器件 | xczu9eg-ffvb1156-2-e(Zynq UltraScale+) |
| 验证 | 与 MATLAB golden 二进制比对 |
算一笔账:一帧 65536 像素,4400 FPS ≈ 每帧 2.89 亿像素/秒 ÷ 303MHz ≈ 每时钟 1 像素(II=1) ------也就是说,这个目标等价于"流水线必须做到 II=1"。算法简单,但指标逼着你要么流式、要么出局,这正是 HLS 方法论的典型教学场景。
二、驱动这个案例的,是一整条 HLS skill 链
第 4 篇讲过 Skill 的安装与维护,这一篇看它们怎么协同干活。edge-detection 案例涉及三个 skill,各管一段:
| 阶段 | Skill | 干什么 |
|---|---|---|
| MATLAB → C++ | hls-matlab-to-cpp |
跑 MATLAB 存 golden → 插桩测数值范围 → 定 ap_fixed 类型 → 两步重构(sample_based / frame_based),每步都有 PASS 门禁 |
| 帧循环 → 架构 | hls-architect |
重构为 load→compute→store 的 dataflow 架构,compute 内部再分解为多级流水(本案例:rgb2gray→sobel→magnitude) |
| 性能达标 | hls-optimize + hls-perf-pragma |
根据吞吐目标算 target_ti,放置 performance pragma |
这套链的精髓不在于"生成代码",而在于每一步都有验证门禁:C++ 不过 golden 比对,不准进入架构重构;架构版不过 g++ 验证,不准进 HLS 综合。错误被拦在离它最近的那一站。
三、实跑过程:五个里程碑,全部有真数据
环境:MATLAB R2023b(生成 golden)、Vitis HLS 2025.2(综合)、Vitis 自带 clang++(C++ 功能验证)。器件与官方目标一致:xczu9eg-ffvb1156-2-e。
里程碑 1:MATLAB golden + 数值范围插桩。
skill 的第一步不是翻译,而是让 MATLAB 自己把数据交出来:跑官方 testbench,生成 4 个二进制 golden 文件(输入 / gx / gy / gradMag,列主序 double),同时插桩打印每个中间量的数值范围:
text
gx range: [-3.529412, 3.458824]
gy range: [-3.376471, 3.501961]
gradMag range: [0.000000, 3.586540]
gray range: [0.027451, 0.992157]
这张表就是定点化的依据------位宽不是拍的,是测的 :gray ∈ [0,1) → ap_fixed<12,2>;gx/gy ∈ [-4,4) → ap_fixed<16,4>;gradMag ∈ [0,4) → ap_fixed<16,4>。
里程碑 2:refactor_1(逐行翻译版)------PASS。
把 MATLAB 逐行翻成 C++(sample_based/),保留原始算法结构,只替换数据类型。与 golden 比对结果:
text
Call 1: gx max_err=0.003830 gy max_err=0.003845 gradMag max_err=0.004271
Call 2: gx max_err=0.003830 gy max_err=0.003845 gradMag max_err=0.004271
PASS
两次调用结果完全一致(确定性)、最大误差 0.0043 远小于容差 0.01------误差全部来自 ap_fixed 量化,在预期内。
里程碑 3:refactor_2(帧循环版)------PASS。
skill 要求第二步重构:列主序 → 行主序(MATLAB 数组是列主序,流式硬件要行主序)、算法改写成帧级循环结构。但注意------skill 明确禁止在这一步做滑窗优化,那是架构阶段的活儿。验证同样 PASS,max_err=0.004271。
里程碑 4:dataflow 架构版------PASS。
hls-architect 的重构产物(rearchitect/v1/):
text
load_input → compute → store_output (顶层一个 #pragma HLS DATAFLOW)
compute 内部再分解:rgb2gray → sobel → magnitude(内部 DATAFLOW)
sobel 级采用经典的 line buffer + 3×3 滑窗:两行 line buffer 缓存灰度行,每时钟读 1 像素、输出 1 个梯度。g++ 功能验证 PASS(max_err=0.004314)。
里程碑 5:Vitis HLS 综合。
vitis-run --mode hls --tcl 跑 csynth + csim(part=xczu9eg-ffvb1156-2-e,clock=3.3ns),全流程 2 分 32 秒。综合报告关键数据:
text
模块 循环间隔(Interval) II 流水线 BRAM DSP FF LUT
rgbEdgeDetector 66317 - dataflow 2 5 4166 11138 (4%)
├ load_input 65536 1 yes
└ compute 66317 - dataflow
├ rgb2gray 65536 1 yes 3
├ sobel 66316 1 yes 2
└ magnitude 65536 1 yes 2
时钟 3.3ns,最差 slack +0.01ns(收敛);csim:PASS(两帧,0 失配)
算回设计目标:帧间隔 66317 周期 × 3.3ns ≈ 218.9µs/帧 → ≈4568 FPS ≥ 4400 FPS,达标。资源上 BRAM 2 块(就是 sobel 的两行 line buffer)、DSP 5 个、LUT 只占 4%------一颗 xczu9eg 上放十几路这样的流水线都绰绰有余。

再看视觉效果------MATLAB 边缘图 vs HLS C++ 边缘图(同阈值 0.3),差异像素 50 个,占 0.076%:

四、过程里的四个技术坑(这才是干货)
坑 1:MATLAB imfilter(..., 'conv') 的"conv"是个负号陷阱。
imfilter 默认是相关运算,加 'conv' 就是卷积------卷积会把 kernel 翻转 180° 。Sobel 核是反对称的,翻不翻刚好差一个负号。gradMag 不受影响,但 gx/gy 的符号会整体反掉,和 golden 一比就是全军覆没。正确做法:C++ 侧用翻转后的核 Kx = [1 0 -1; 2 0 -2; 1 0 -1](等效于 MATLAB 的 conv 语义)。
坑 2:列主序 vs 行主序------golden 文件的物理布局。
MATLAB 序列化数组是列主序(img(:) 按列展开),testbench 读 golden 时必须做 matlab_idx = col*ROWS + row 的映射,否则图像被"转置",比对必挂。skill 的 testbench 模板里专门强调了这一点。
坑 3:流式架构里的 'replicate' 边界------最容易被牺牲的一圈像素。
MATLAB 用 'replicate' 边界(边缘像素向外复制)。流式滑窗里,第 0 行/列和最后一行/列的 3×3 邻域是残缺的,很多 HLS 教程的做法是"边界填零、比对跳过"。我们选择了精确复刻 :对窗口 tap 的索引做 clamp(i0=(r==0)?1:0 等四个边界开关),让硬件行为和 MATLAB 完全一致------全帧 65536 个像素一个不漏地通过比对,不用在 testbench 里写"跳过边界"这种自欺欺人的豁免。代价只是循环多跑一行一列的 flush 周期(约 0.4% 开销)。
坑 4:为什么 testbench 要把 kernel 跑两遍?
skill 模板要求 testbench 调用 kernel 两次------这不是冗余:cosim 测 II(启动间隔)需要至少两次调用才能观察到"第二帧能否在前一帧的流水线上紧跟进入"。功能验证顺手也确认了两帧结果一致(无状态污染)。
五、有 Ross vs 没有 Ross:HLS 场景的日常差距
| 环节 | 没有 Ross | 有 Ross |
|---|---|---|
| 定点化 | 位宽靠经验拍,溢出/精度不够再回来调 | RANGE 插桩先测范围,ap_fixed 类型有据可依 |
| MATLAB→C++ | 手工翻译,imfilter 语义、主序布局全靠踩坑学 | skill 模板自带映射表(imfilter→滑窗、/255→定点系数) |
| 架构重构 | line buffer、边界处理、dataflow 分解是 HLS 最劝退的部分 | hls-architect 给出固定三段式 + 分解规则 + pragma 白名单 |
| 质量门禁 | 翻完直接综合,错了在 HLS 报告里找半天 | 每步 g++ 验证 PASS 才能往下走,错误就地拦截 |
| pragma | 翻 UG1399 几百页,PIPELINE/UNROLL 挨个试 | perf 目标换算 target_ti,performance pragma 让工具自己推导微优化 |
一句话:没有 Ross 时,MATLAB 到 FPGA 的距离是"经验";有 Ross 时,这个距离被固化成了一条带门禁的流水线。
六、想自己跑一遍?
案例在官方仓库 examples/edge-detection,只需 4 个文件(README、算法、testbench、runme)。复现路径:
bash
# 1. MATLAB 生成 golden(另存 4 个 .bin + 3 张 PNG)
matlab -batch "testbench_edgeDetector"
# 2. C++ 功能验证(三个版本同一份 testbench 逻辑)
clang++ -std=c++14 -I$XILINX_VITIS/include -o verify kernel.cpp testbench.cpp && ./verify
# 3. Vitis HLS 综合(2025.2 组件流程)
vitis-run --mode hls --tcl run_hls.tcl # open_component / set_part / create_clock / csynth_design
提醒两点:golden 二进制是列主序 ,C++ 读取要做索引映射;'conv' 语义记得翻转 Sobel 核,否则 gx/gy 差一个负号。
七、小结
- 案例:官方 edge-detection,43 行 MATLAB Sobel → Zynq UltraScale+,目标 4400 FPS @ 3.3ns;
- 结果:三版 C++ 全部 PASS(max_err 0.0043,容差 0.01),边界精确复刻、全帧无豁免,II=1 达成绩效目标;
- 方法论:HLS skill 链把"MATLAB→定点 C++→dataflow 架构→pragma 优化"固化成带 PASS 门禁的流水线,定点位宽靠插桩测量、边界行为逐像素对齐 MATLAB;
- 下一篇预告 :第 6 篇回到 Vivado 本体------用 Ross 控制 Vivado 完成 ISP 硬核配置:一段话需求 → 429 个参数的正确组合,以及官方"有无 skill"对照实验的数据。
参考链接:
- 本案例源码:https://github.com/xilinx/ross-ai-assistant/tree/main/examples/edge-detection
- HLS skill 链(matlab-to-cpp / hls-architect / hls-optimize):https://github.com/xilinx/ross-ai-assistant/tree/main/skills
- Vitis HLS 用户指南 UG1399:https://docs.amd.com/r/en-US/ug1399-vitis-hls
- 上一篇:《AMD Ross 深度系列 4:Agent Skills ------ 安装、更新与创建全攻略》
- 第 3 篇:《AMD Ross 深度系列 3:Knowledge Base 与 doc_search ------ RAG 原理、云与本地的抉择、自建知识库实战》
- 第 2 篇:《AMD Ross 深度系列 2:VS Code 插件与 MCP Server ------ 安装、能力与差异全解》
- 系列首篇:《AMD Ross 深度系列 1:Overview ------ 什么是 AMD Ross》