news 2026/10/3 6:01:13

用TCL脚本生成AD9361 HDL参考设计:从环境准备到工程验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用TCL脚本生成AD9361 HDL参考设计:从环境准备到工程验证

1. 先搞清楚这套TCL脚本到底在干什么

1.1 为什么ADI不直接给一个现成的.xpr工程文件

我第一次接触AD9361的HDL参考设计时,下意识去找zc706_fmcomms2.xpr或者vcu118_fmcomms2.xpr这种现成工程文件,结果翻遍整个仓库都没找到。后来才明白,ADI维护的不是“某个板卡对应的单个工程”,而是一个覆盖多款FPGA开发板、多款射频模块的大型HDL仓库。如果每发布一个版本都要手工维护几十个.xpr文件,不仅工作量爆炸,而且很容易因为IP版本、器件型号的细微差别导致工程打不开。

所以ADI选择了TCL脚本这套方案。TCL脚本本质上是一串Vivado能直接执行的命令,脚本运行时会根据你指定的板卡、器件、参考设计名,在内存中动态创建工程、添加IP、生成Block Design、写入约束文件。用一句话概括:TCL脚本生成工程的过程,像是用代码“画”了一个工程出来,而不是给你一张画好的图。

对于开发者来说,这有很实际的好处。你可以通过改参数瞬间换一块FPGA器件,或者把fmcomms2换成fmcomms4,不需要手动删除旧工程重新建。版本管理也友好,Git里只存文本类型的TCL脚本和源文件,不会出现二进制项目文件冲突。

1.2 脚本的工作流程拆解

我建议在跑脚本之前,先打开仓库里的projects/fmcomms2/zc706目录,看清这里的文件结构。通常你会看到:

  • Makefile:封装了TCL脚本的调用方式,加上参数后一条make命令就能生成工程。
  • system_project.tcl:核心脚本,负责创建顶层工程、添加源文件、调用ADI公共IP库。
  • system_top.v:顶层HDL文件,包含处理系统、射频接口、DMA等模块的例化。
  • system_bd.tcl:用来生成Block Design的脚本,会在Vivado里创建system_bd。
  • system_constr.xdc:引脚约束和时序约束。

这些TCL脚本内部会先加载library/scripts/adi_ip.tcl这类公共函数,然后注册ADI的自定义IP核,比如axi_ad9361、axi_dmac、util_pack等。之后脚本会调用create_project创建工程,调用add_files添加HDL源文件,再执行system_bd.tcl把Block Design搭建出来,最后生成约束并更新编译顺序。

理解了这条链路,再去执行脚本心里就有底了。如果中间某一步报错,你能很快判断是IP库路径问题、约束文件问题还是Block Design脚本问题,而不是对着整个Vivado日志发呆。

2. 运行脚本前必须做好的环境准备

2.1 Vivado版本与分支的匹配问题

这一步看着不起眼,但影响最大。ADI的HDL仓库会按Vivado主版本拆分分支,常见的有hdl_2021_r2、hdl_2022_r1、hdl_2023_r1等。如果你在Vivado 2022.2里硬跑hdl_2021_r2分支的脚本,大概率会遇到IP版本不支持或者某个TCL函数被移除的报错。反过来,用Vivado 2021.2跑2023分支也可能出问题。

我的做法是:先确定自己装的Vivado版本,再去仓库里git checkout对应的分支。比如本机是Vivado 2022.2,那就切换到hdl_2022_r2分支。如果遇到几个版本之间API差异的问题,通常README里会写明推荐版本。ADI官方也会在分支名上直接标注,基本不需要猜。

除了Vivado版本,还要确认安装了对应的许可证。生成工程本身不需要特别License,但后续综合和实现Vivado的IP核时,可能需要有效的Vivado License。我遇到过一次“IP Evaluation License”弹出的情况,那是没有激活正式License导致的,需要检查本机许可状态。

2.2 获取ADI HDL参考设计的正确方式

获取源码这步,我建议直接使用Git克隆。仓库地址是https://github.com/analogdevicesinc/hdl。如果你只是临时用,也可以下载zip压缩包,但后续想切换版本、看提交记录就很麻烦。克隆完成后,进入仓库根目录:

git checkout hdl_2022_r2

然后确认一下子模块是否完整。ADI仓库有时候会引用一些外部IP或库,不过大多数情况下你需要的HDL源码都直接包含在仓库里。如果后续脚本报“找不到某个文件”,优先怀疑当前分支没有拉完整。

还有一个容易忽略的点:仓库路径不能有中文、空格,Windows下尤其注意。Vivado自身对路径空格的支持时好时坏,而TCL脚本里经常用相对路径组合字符串,一旦遇到空格就会出现文件找不到或者路径解析错误。我一般把仓库放在D:\projects\hdl这种纯英文路径下,省去一堆麻烦。

2.3 检查Vivado环境变量,尤其Linux下要source settings

在Windows下,最直接的方式是打开“Vivado 2022.2 Tcl Shell”或“Vivado Tcl Console”,它会自动把工具路径配置好。但在Linux服务器环境下,如果你直接打开一个普通终端执行vivado -mode batch -source xxx.tcl,很可能会提示vivado: command not found。这时候需要先source Vivado的环境变量脚本:

source /tools/Xilinx/Vivado/2022.2/settings64.sh

然后再执行which vivado确认路径。如果你是Windows下用命令行批处理,也要注意安装目录里的settings64.bat,或者干脆使用Vivado自带的Tcl Shell,避免自己配置PATH。

此外,建议提前安装git和make。Windows下如果要用make命令,需要装MSYS2或者使用Windows Subsystem for Linux。如果你不想折腾,直接从Tcl Console执行脚本也能生成工程,make不是必须的。但我自己更推荐用make,因为它能自动设置ADI_HDL_DIR等环境变量,减少手动拼参数的错误概率。

3. 手把手运行TCL脚本生成HDL工程

3.1 从“一条make命令”到“手动执行TCL脚本”

进入对应工程目录是第一步。以ADI官方参考设计fmcomms2搭配Zynq开发板为例:

cd projects/fmcomms2/zc706 make

这个命令会读取Makefile,设置几个关键变量后调用Vivado批处理模式执行TCL脚本。整个过程中,终端会打印大量日志,最后在当前目录下出现zc706_fmcomms2文件夹,里面就是生成好的Vivado工程。

但如果你像我一样想尽量理解每一步在做什么,可以抛开Makefile,在Vivado Tcl Console里手动执行。先启动Tcl Console并进入仓库目录:

cd D:/projects/hdl/projects/fmcomms2/zc706 source ./system_project.tcl

脚本里会用set adc_dac_cores 1这类变量控制射频链路数量,还会引用公共库中的函数来添加IP核。手动执行的好处是,出错了你能在Console里看到是哪一行TCL报错,能用set直接查看当前变量值,方便调试。

3.2 关键参数的含义与典型配置

在运行TCL脚本之前,建议先扫一眼脚本顶部的变量。不同的reference design参数差异很大,但有几个是通用的:

变量名含义常见取值
adc_dac_cores射频收发链路数量,决定例化几个AD9361接口1、2
rx_dma/tx_dma是否使能RX/TX的DMA通道1、0
fpga_part目标FPGA型号xc7z045ffg900-2
board_name板卡名称,用于约束和顶层选择zc706
adi_mm2s_enable是否启用AXI MM2S接口1、0

如果不需要两条射频链路,只做单收单发,那就把adc_dac_cores设为1,可以节省大量逻辑资源。若你的处理逻辑只要接收不要发送,可以把tx_dma关掉,生成的Block Design里会减少对应的DMA通道。

3.3 自定义板卡目录的操作方法

实际项目中很少有人直接拿官方开发板做产品,多半是自研板卡。这时候不建议直接修改官方目录里已有的工程,而是把整个zc706目录复制一份,改成自己的板卡名。比如:

cp -r projects/fmcomms2/zc706 projects/fmcomms2/myboard

然后修改myboard目录下的system_project.tcl,把工程名、器件型号、板卡名改成自己的。同时要把system_constr.xdc里的引脚约束全部替换成自己板卡上AD9361和FPGA的实际连接。TCL脚本里通常会读取板卡目录下的约束文件,你只要保证myboard目录里约束文件名字和脚本里引用的名字一致就行。

需要特别提醒的是,如果自研板卡的FPGA型号和官方板卡不同,还要检查IP核版本的可用性。比如某些IP核不支持新的器件,脚本虽然能生成工程,但综合时可能报“IP not supported on target part”。遇到这种情况,优先去ADI公共IP库中查看IP核的支持列表,换一个支持的器件。

4. 生成工程后的常规处理与验证

4.1 生成XPR后先检查Block Design,不要急着综合

脚本跑完,生成了.xpr工程文件。很多人一激动就直接打开工程点“Generate Bitstream”,结果烧到板上没反应。我的习惯是先用Vivado打开system_bd,检查Block Design里的几个核心模块是否都连对了。以AD9361工程为例,最重要的几个IP是:

  • axi_ad9361:AD9361的数字接口控制器,负责LVDS/CMOS数据接收、发送、时钟同步。
  • axi_ad9361_adc_pack:把采样数据打包成AXI-Stream。
  • axi_dmac:DMA控制器,把数据从PS内存搬到PL或反向搬。
  • util_wfifo:跨时钟域FIFO,缓存数据流。
  • axi_gpio:用于配置AD9361的复位、使能等控制信号。

TCL脚本生成的Block Design一般已经很完整,但你要确认DMA的中断连接是否到了PS端的pl_ps_irq0或pl_ps_irq1,因为Linux驱动通常依赖中断来搬运数据。如果发现中断没连,需要手动连上后重新生成Wrapper。

在Block Design里修改后,记得右键设计源文件,选择“Create HDL Wrapper”,让Vivado重新生成顶层的HDL例化代码。如果你不生成Wrapper,后续综合时可能会用旧版本的顶层文件,导致修改不生效。

4.2 综合、实现和生成比特流的完整流程

确认Block Design没问题后,就可以开始综合了。这里我建议按顺序执行:

  1. 在Sources面板选中顶层文件,右键“Generate Output Products”。
  2. 等待所有IP核的输出产物生成完毕。
  3. 点击“Run Synthesis”,综合完成且无错误后,再“Run Implementation”。
  4. 最后“Generate Bitstream”。

如果你的工程文件多、器件大,建议用批处理模式来跑,避免GUI卡死。比如:

vivado -mode batch -source run_bitstream.tcl

在TCL脚本里依次执行synth_design、opt_design、place_design、route_design和write_bitstream。这种方式不光快,而且方便记录日志。

综合后经常会出现时序违例,尤其是AD9361数据接口的时序约束。这类接口直接连到FPGA的引脚上,受PCB走线长度影响很大。如果时序不过,先看是不是约束文件里set_input_delay和set_output_delay设置得偏差太大,再考虑调整综合策略,比如把phys_opt_design打开,或者对数据通路加一列寄存器。AD9361的LVDS数据速率不算非常高,正常布局下时序是可以收敛的。

4.3 如何把AD9361接口和自定义逻辑对接

生成好的工程默认是把AD9361数据通过DMA搬到PS端,靠Linux驱动或裸机程序读取。如果你不想走DMA,而是想在PL里直接对数据进行处理,比如做数字下变频、滤波,就得打断原来的数据通路。

最常用的方式是在Block Design中,把axi_ad9361_adc_pack输出的AXI-Stream接口不接到DMA,而是接到你自己的自定义IP上。你可以在Vivado里用“Add Module”添加一个HDL文件,然后手动连线。AXI-Stream的接口比较简单,核心信号是axis_tdata、axis_tvalid、axis_tready、axis_tlast。数据位宽一般是32位或64位,取决于你配置的采样位数和通道数。

我自己习惯的做法是先用ILA抓一下AD9361输出的波形,确认数据有效信号和数据内容没问题,再接后续算法模块。ILA的采样深度不用太大,深度1024足够观察链路是否正常。抓到的数据如果全是0,先检查AD9361的SPI配置是否完成,而不是怀疑数据通路。

5. 我在实际操作中踩过的坑和排查思路

5.1 TCL脚本跑到一半报“file not found”的根因分析

我曾在一次脚本执行中遇到一个特别诡异的报错,提示某个.xci文件找不到。单独检查文件确实存在,路径也正确。后来发现是TCL脚本内部用相对路径解析文件,而执行脚本时当前工作目录和我项目目录不一致。因为我是打开了Vivado GUI,在默认的Tcl Console路径下直接source了system_project.tcl,脚本里的相对路径自然就找不到了。

排查方法很简单:执行脚本前先pwd看一下当前目录,然后cd到工程目录。另外有些脚本会检查环境变量ADI_HDL_DIR,指向仓库根目录。如果没设这个变量,公共函数就找不到library下的IP脚本。这个问题在Linux下尤其常见,因为你在普通终端里source了settings后,ADI_HDL_DIR并没有设置。

建议手动设置一下:

export ADI_HDL_DIR=/path/to/hdl

或者在Windows环境变量里添加ADI_HDL_DIR。这样TCL脚本在加载library目录时就能正确定位。

5.2 脚本运行时间过长,卡在“Generating IP”

生成工程本身不算慢,但后续“Generate Output Products”会花很长时间。如果不开批处理,GUI界面还可能一直转圈,看起来像卡死了。其实Vivado是在逐个生成IP核的输出,尤其是axi_ad9361这种包含大量实例的IP,需要几分钟到十几分钟不等。

如果你的机器内存比较小,可以尝试关闭多余软件,或者使用批处理模式。另外,Vivado的IP缓存目录可能在系统盘,如果磁盘空间不足,生成IP会异常变慢。可以在Tcl Console里执行:

set_param general.maxThreads 8

这个参数可以调整Vivado使用的线程数,对于多核CPU有明显效果。但也不要无脑调太大,我试过在16核机器上设置16线程,反而因为内存不足导致编译失败,后来稳定在8线程。

5.3 明明选的AD9361,生成出来的顶层却是AD9380

这个问题看起来离谱,但我在自定义板卡时遇到过。原因是目录结构选错了。projects目录下有很多参考设计,有些是ADI视频接口,有些是射频收发。TCL脚本生成顶层时,会根据system_top.v里的宏定义或者文件列表来判断例化哪个IP。如果你把fmcomms2目录里的脚本和adv7511目录里的system_constr.xdc混在一起,工程自然就乱套了。

所以我的建议是:不要跨目录混用文件。每块板卡目录自成一个完整系统,复制后要检查所有文件是否都来自同一套参考设计。特别是有了自定义板卡目录后,要确保system_project.tcl引用的system_top.v还在这个目录下,而不是意外引用到了官方路径。

5.4 综合阶段出现“MMCM/PLL cannot lock”或时序崩溃

这个现象不是每次必现,但我遇到过一个比较隐晦的原因:工程里某个时钟管理模块的频率约束和实际引脚输入时钟不一致。AD9361参考设计通常会从板载晶振或者AD9361的时钟输出获取参考时钟,如果TCL脚本里默认的时钟频率和你开发板实际晶振频率不同,就会导致MMCM配置失败。

遇到这类问题,先打开Clocking Wizard的配置,确认输入时钟频率是否和板卡原理图一致。比如原理图上写着100MHz,但脚本里默认是50MHz,那就需要手动改一下IP配置。改完之后重新生成输出产物,再跑一遍综合,大部分问题都能解决。

6. 以这套工程为基础做二次开发的小技巧

6.1 改工程名和顶层模块后如何保持脚本可控

很多人拿到生成好的工程后,第一件事就是改工程名。直接在Vivado里“Save Project As”当然可以,但如果以后你想再次用TCL脚本重新生成工程,改过的名字和路径就被覆盖了。正确的做法是修改system_project.tcl里的工程名变量,然后删除旧工程目录,重新执行脚本生成。

我有一次把工程名从zc706_fmcomms2改成了myradio_top,结果发现TCL脚本生成后,Block Design的名字还是system_bd,顶层Wrapper也是system_wrapper。如果希望名字统一,需要在system_bd.tcl里也同步修改。建议只改工程名,保留system_bd和system_wrapper这些内部名称,改动最小,脚本也不容易坏。

6.2 加入自定义AXI-Lite寄存器逻辑

很多场景下,你需要通过PS端控制PL里的参数,比如滤波器系数、增益表。这时最方便的做法是在Block Design里加入一个自定义AXI-Lite Slave IP。Vivado自带的“Create and Package New IP”向导能帮你生成AXI-Lite接口框架,然后在Verilog里写寄存器逻辑。

以一个8路寄存器为例:

always @(posedge s_axi_aclk) begin if (s_axi_aresetn == 1'b0) begin reg0 <= 16'h0; end else if (s_axi_awvalid && s_axi_wvalid && s_axi_awready && s_axi_wready) begin if (s_axi_awaddr[3:0] == 4'd0) reg0 <= s_axi_wdata[15:0]; end end

添加完成后,需要在Block Design里手动连接这个IP的S_AXI到PS的M_AXI_GP口。记得给PS的接口打开“Enable”状态,并分配地址。地址分配时避开已有的GPIO、SPI等外设地址区间即可。这样Linux端只需要通过/dev/mem或UIO框架就能读写这些寄存器。

6.3 把整个流程纳入版本管理,用批处理模式跑工程生成

TCL脚本最大的优势是确定性。只要仓库版本固定、环境变量固定,任何人在任何机器上都能生成一模一样的工程。我现在的做法是把仓库克隆下来后,写一个build.sh脚本,里面设置好ADI_HDL_DIR、Vivado版本、目标工程目录,然后一键生成并编译。

source /tools/Xilinx/Vivado/2022.2/settings64.sh export ADI_HDL_DIR=/home/user/hdl cd /home/user/hdl/projects/fmcomms2/myboard make

这样团队成员之间只需要同步HDL仓库和脚本,不用同步动辄几个GB的Vivado工程目录。如果遇到IP版本升级,直接切分支重新生成即可,旧工程不会污染新的开发流程。

我个人建议把生成过程中所有的警告日志都打印出来固化到仓库里,方便后续对比。虽然这样会占用一点存储空间,但排查问题的时候比对日志比比对截图高效得多。尤其是TCL脚本这种“一步错步步错”的流程,一份干净的历史日志能帮你快速定位是哪次修改引入了回归。

以上就是我从零跑通ADI AD9361 TCL脚本生成HDL工程的完整记录。整套流程并不复杂,关键是理解每一步在做什么,以及准备好版本匹配的Vivado环境。如果你也在折腾AD9361和Vivado,建议先在官方板卡上跑通默认脚本,再逐步改成自定义板卡。遇到问题时,先完整读一遍TCL日志,绝大多数答案都在里面。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 6:00:53

从零搭建AI工程体系:手写神经网络与反向传播实战

1. 从零搭建AI工程体系&#xff0c;为什么我劝你别一上来就调包“ai-engineering-from-scratch”这个标题&#xff0c;第一次看到的时候我愣了一下。市面上讲AI的教程铺天盖地&#xff0c;但绝大多数都是教你import torch然后跑一个预训练模型&#xff0c;或者调个API就完事。真…

作者头像 李华
网站建设 2026/10/3 6:00:53

从零构建AI工程能力:数据、特征、训练与推理全链路实战

1. 从零搭建AI工程能力&#xff1a;这个项目到底在解决什么问题第一次看到ai-engineering-from-scratch这个标题&#xff0c;我脑子里蹦出来的第一个念头是&#xff1a;又一个教人调包的教程&#xff1f;但仔细琢磨了一下“from scratch”这个限定词&#xff0c;再结合这两年带…

作者头像 李华
网站建设 2026/10/3 6:00:37

用Vue3和CodeMirror 6自研公式编辑器:从选型到实现全攻略

做了几年后台管理系统&#xff0c;最让我头疼的需求之一就是"表单里让用户填一条计算规则"。你说它是代码吧&#xff0c;用户不认&#xff1b;你说它是纯文本吧&#xff0c;业务方又不满意&#xff0c;说没有提示、写错了也不知道。直到我尝试用 Vue3 加 CodeMirror …

作者头像 李华
网站建设 2026/10/3 6:00:19

用Dify构建hindsight:对抗后视偏差的AI复盘助手

1. hindsight这个词&#xff0c;我想把它变成一款AI应用如果说这两年我听到最多的一个英文单词&#xff0c;除了hallucination之外&#xff0c;大概就是hindsight了。hindsight直译过来是"后见之明"&#xff0c;就是我们常说的"事后诸葛亮"。但这个词在心理…

作者头像 李华
网站建设 2026/10/3 6:00:19

Univer 在线表格引擎实战:Canvas 渲染与 Node.js 协同开发指南

1. 从“univer”这个标题说起&#xff1a;它到底是什么&#xff0c;能解决什么问题第一次看到“univer”这个词&#xff0c;很多人会以为是“universe”的缩写&#xff0c;或者某个新出的前端框架。实际上&#xff0c;Univer 是一个开源的在线电子表格与文档协作引擎&#xff0…

作者头像 李华
网站建设 2026/10/3 5:59:47

STM32实战开发:从嵌入式系统到硬件控制的全流程解析

做嵌入式这行&#xff0c;不管你是准备毕业设计还是刚进公司接手 MCU 项目&#xff0c;STM32 几乎都是躲不开的一道坎。它说到底是嵌入式系统里的一个具体芯片系列&#xff0c;但真正用起来&#xff0c;你会发现难点从来不在芯片本身&#xff0c;而在怎么把外设控制、通信协议和…

作者头像 李华