这一篇在干嘛?
这一篇不讲具体电路,而是带你把安路 TD(Anlogic TD)开发软件的完整使用流程走一遍:新建工程、写 Verilog、编译、锁引脚、下载到板子、固化到 FLASH,最后再用 ModelSim 做一次功能仿真。示例工程很简单——让核心板上的 4 个 LED 以 1 秒间隔闪烁。走完这一篇,后面所有实验的上板流程你就全部掌握了。
实验目标与准备工作
本篇基于安路 EG4S20 系列 FPGA(配套 KX-EG420 开发板),使用开发工具 Anlogic TD 标准版(TD5.6.2-64bit)。学完你将掌握:
- 在 Anlogic TD 中新建工程、编译工程的基本方法;
- 锁定引脚、制作引脚约束文件(.adc)的方法;
- 把工程下载到开发板、以及固化到 FLASH 的方法;
- 用 ModelSim 手动仿真验证设计功能的方法。


官方教程封面(修改日期:2024 年 10 月,杭州康芯电子)
在动手之前,有一个不起眼但极其重要的准备工作:在硬盘上专门建一个文件夹用来存放你的 Anlogic 工程。
工程路径是新手第一坑
工程目录路径只能包含字母、数字和下划线,且要以字母开头,绝对不能出现中文或其他符号。文件夹必须先手动建好,否则软件会报错「路径不存在」。例如
C:/ANLU/cnt1s就是合法的路径写法。
第 1 步:新建工程
打开 Anlogic TD 软件,主界面如下图所示。

图 1-0 软件主界面
在主页面上选择【New Project】,在弹出的窗口中点击【OK】,进入新建工程向导。

图 1-1 新建工程向导
接下来确定工程名。在打开的对话框中分别选择工程路径、设置工程名字。本例工程路径是 C:/ANLU/cnt1s,工程名和顶层文件名都取 cnt1s,然后点击「OK」。

图 1-2 设置工程路径与工程名
注意工程名要与顶层模块名保持一致,这是后面保存代码文件时的硬性要求。
第 2 步:输入设计
工程建好后,需要添加设计文件。你可以添加已有的 .v 文件(事先把要用的文件拷贝到工程文件夹下,TD 会自动识别顶层并设置为「Top」),也可以新建一个设计文件。下面走新建的路线。
在工程界面的选项区域中,右击【Hierarchy】→【New Source】。

图 1-3 新建源文件入口
在弹出的页面中选择文件格式。作为工程顶层设计文件,可以选择「Verilog」或「VHDL」,并勾选【Add to project】(把文件加入工程),单击 OK 完成创建。

图 1-4 选择源文件类型
新建文件的三种方式
除了右键菜单,你还可以把鼠标悬停在工具栏图标上查看功能提示(用法和 Word 工具栏一样),或者直接按快捷键【Ctrl】+【N】来新建工程顶层文件。文件名输入
cnt1s后,工程目录下会自动创建同名工程文件。
进入主编辑窗口后,会看到一个空白的可编辑文件,在里面输入实现实验功能的 Verilog 代码——让核心板上的 4 个 LED 灯以 1 秒间隔闪烁:

图 1-5 主编辑窗口
1 module cnt1s
2 (
3 input CLOCK,
4 input rst_n,
5 output reg [3 : 0] LED );
6 localparam T1S = 27'd49_999_999; //时钟为 50M, 故分频次数设为 50M-1
7 reg [26 : 0] cnt_value;
8 9 always @(posedge CLOCK or negedge rst_n)begin
10 if(!rst_n)
11 cnt_value <= 0;
12 else if(cnt_value == T1S)
13 cnt_value <= 0;
14 else
15 cnt_value <= cnt_value + 1'b1;
16 end
17 always @(posedge CLOCK or negedge rst_n)begin
18 if(!rst_n)
19 LED <= 4'HF ;
20 else if(cnt_value == T1S) //两次(cnt_value == T1S)之间刚刚隔了 1S
21 LED <= ~LED;
22 end
23 endmodule这段代码的原理一句话就能说清:板载时钟是 50 MHz(周期 20 ns),要凑出 1 秒需要数够 50 000 000 个时钟周期,所以分频计数器设为 50M - 1 = 49_999_999。第一个 always 块让 cnt_value 从 0 数到 T1S 再归零;第二个 always 块在每次数满的瞬间把 4 位 LED 输出取反(~LED),于是灯的状态每 1 秒翻转一次,看起来就是 1 秒间隔的闪烁。复位时 LED <= 4'HF,即 4 个灯全灭(低电平点亮时 F 表示全灭)。
输入完代码后,按快捷键【Ctrl】+【S】或选择【File】→【Save】保存。保存的文件名必须和代码里的模块名 cnt1s 一致,通常采用默认设置直接保存即可。

图 1-6 保存代码
第 3 步:编译工程
工程创建和设计输入完成后,先验证一下代码语法是否正确:双击【FPGA Flow】下的【SynOpt】→【Read Design】,对整个工程做一次全编译。

图 1-7 编译通过显示对勾
编译如果没有错误,通常【Syn Opt】左边会显示对勾,表示编译通过。然后查看打印窗口「Console」里的日志:

图 1-8 Console 打印日志
日志分三类,对待方式完全不同:
- Error:必须关注。意味着代码有语法错误,后续编译无法继续,必须修好才能往下走;
- Warning:不一定是致命的,但往往「暗藏玄机」,很多潜在问题(比如信号没驱动、位宽不匹配)都能从这些条目里找到蛛丝马迹;
- Critical:严重级别介于两者之间,同样建议逐条排查。
别把 Warning 一律无视
如果你确认这些 Warning 符合设计要求、对结果没有影响,才可以忽略它。养成「先扫一眼 Warning 再点下载」的习惯,能省掉大量上板调试时间。
第 4 步:分配引脚
编译(和仿真)通过后,就要给工程的输入、输出端口分配管脚了。管脚分配有两种方法:
- 方法一:手动分配。对照管脚分配表,把引脚号逐个输入到对应端口。适合端口数量少的情况;
- 方法二:物理约束文件(.adc)分配。制作脚本文件批量约束。适合端口多或需要经常改分配的情况。
方法一:IO Constraint 图形界面手动分配
使用菜单栏【Tools】→【IO Constraint】打开 I/O 引脚分配界面。

打开后可以看到三个端口组:时钟、复位和 LED。
分配界面里没有端口信号?
如果你打开管脚分配页面,页面中没有出现顶层文件的端口信号,请先对整个工程执行一次全编译,端口列表就会出现。
本例程需要锁定 CLOCK、4 个 LED 管脚和复位脚 rst_n。使用的 KX-EG420 核心板如下图:

图 1-10 HXEG420 核心板
经查核心板手册,CLOCK、4 个 LED 和复位引脚对应关系如下:
| 引脚名称 | FPGA 引脚 |
|---|---|
| CLK | R7 |
| 复位按键名称 | |
| rst_n (K1) | A2 |
| LED 名称 | |
| LED0 | A4 |
| LED1 | A3 |
| LED2 | C10 |
| LED3 | B12 |
参考上表,把引脚号填入对应信号名的「Location」栏。具体输入方法:用鼠标点击 CLOCK 信号名 Location 下面的空白位置,输入 PIN_R7,也可以直接输入 R7,然后回车即可。注意这里必须根据原理图的硬件连接关系分配引脚,不能凭空填。
填好后如下图所示(含 IO Standard、Drive Strength 等完整属性):

| Name | Direction | Bank | Location | IOStandard | DriveStrength | PullType | SlewRate | DiffResistor | PCIClamp | Hysteresis | VREF | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | rst_n | input | bank3 | A2 | .VCMOS33 | NA | PULLUP | NA | NA | OFF | OFF | NA |
| 2 | LED[1] | output | bank0 | A3 | .VCMOS33 | 8 | NONE | SLOW | NA | NA | NA | NA |
| 3 | LED[0] | output | bank0 | A4 | .VCMOS33 | 8 | NONE | SLOW | NA | NA | NA | NA |
| 4 | LED[3] | output | bank0 | B12 | .VCMOS33 | 8 | NONE | SLOW | NA | NA | NA | NA |
| 5 | LED[2] | output | bank0 | C10 | .VCMOS33 | 8 | NONE | SLOW | NA | NA | NA | NA |
| 6 | CLOCK | input | bank2 | R7 | .VCMOS33 | NA | PULLUP | NA | NA | OFF | OFF | NA |

图 1-12 IO Constraint 分配界面
点击保存后,Anlogic 会把图形化设置保存为 xxx.adc 文件(文件名需自定义),并自动将该 adc 文件(Anlogic Design Constrain)添加到工程中。
方法二:直接编辑 .adc 约束文件
端口很多或者要经常改分配时,手动分配显然太繁琐。更好的办法是直接修改 adc 文件,再手动把 adc 文件添加到其他工程复用。刚才图形界面生成的 adc 文件内容如下:
set_pin_assignment { CLOCK } { LOCATION = R7; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { LED[0] } { LOCATION = A4; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { LED[1] } { LOCATION = A3; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { LED[2] } { LOCATION = C10; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { LED[3] } { LOCATION = B12; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { rst_n } { LOCATION = A2; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }使用时的几个要点:
- adc 文件无法凭空自行创建——必须先通过 IO 分配界面锁定引脚生成一次,保存后它会自动加入工程;
- 之后你可以根据新工程手动修改文件里的网络名和 IO 位置,另存一份给其他工程使用;
- Anlogic 的约束文件是以
.adc为后缀的文本文件,里面存的就是一条条set_pin_assignment约束命令。
在工程界面选择【Constraints】→【Add ADC File(s)】添加已有的约束文件:

图 1-13 添加 ADC 约束文件
选中 .adc 文件,点击打开:

图 1-14 选择约束文件
然后再进行一次编译,约束就生效了。可以重新打开 IO 引脚分配界面,核对所编辑的引脚是否正确。
第 5 步:编译布线
要下载设计,首先要把工程变成能写进 FPGA 的比特流文件。在 FPGA Flow 界面点击【Run】,跑完整个编译布线流程即可。

图 1-15 点击 Run 生成比特流
第 6 步:下载程序
编译完成后,就可以给核心板下载程序来验证了。先连接好开发板:打开红色按钮电源开关,插好下载线(核心板 USB 接口见图 1-10)。
在工具栏上找到【Download】,点击进入下载界面:

图 1-16 Download 下载入口
安装 USB-JTAG 驱动(首次使用必做)
首次使用该开发板时,需要安装 USB-JTAG 驱动程序。流程如下:
- 右键桌面图标「计算机」→「属性」,打开「设备管理器」,在通用串行总线设备下会出现「USB-JTAG-Cable」:

图 1-17 设备管理器中的 USB-JTAG-Cable
- 右键选择「USB-JTAG-Cable」,点击「更新驱动程序」:

图 1-18 更新驱动程序
- 选择「浏览计算机以查找驱动程序软件」:

图 1-19 浏览计算机查找驱动
- 点击「浏览」,定位到 TD 安装文件夹下
driver里面的win8_10_64文件夹,并勾选「包括子文件夹」,点击「下一步」:


图 1-21 驱动安装完成对话框
出现上面的对话框就表明驱动安装成功了。装好后再看设备管理器,USB-JTAG 设备应当正常识别:

图 1-22 驱动装好后的设备管理器
驱动带黄色叹号怎么办
有的电脑装好后,Anlogic USB Cable 上仍有一个黄色叹号,导致 TD 软件不识别硬件。此时需要先在 BIOS 里禁用「Secure Boot」,然后进入 Windows 禁用 WIN10 的驱动程序数字签名(这样每次 Windows 重启都不再检测驱动程序的数字签名),驱动才能正常加载。
JTAG 下载 .bit 文件
在弹出的下载页面确认已自动识别到芯片,选择下载速度:

图 1-23 下载界面识别芯片
添加 .bit 文件,注意核对文件路径:

图 1-24 添加 bit 文件
选中对应 bit 流,点击 Run 下载程序:

图 1-25 点击 Run 开始下载
为了直接对 FPGA 进行下载(配置),在设备配置窗口的编程模式 SRAM Mode 中选择 JTAG(默认)。务必仔细核对下载文件的路径与文件名,确定就是当前工程生成的编程文件——它所在的路径文件夹是 cnt1s\cnt1s_Runs\phy_1。如果此文件没有出现,检查所在文件夹或重新编译。当下载进度显示 100% 时,表示编程成功。

图 1-26 下载进度 100%
下载完成之后,就能看到开发板上的 LED 指示灯欢快地亮了起来——每 1 秒翻转一次状态。
第 7 步:固化程序到 FLASH
可能已经有同学发现:下载 .bit 文件到 FPGA 后,核心板重新上电,配置程序就丢了,还得再用 JTAG 下载一次。这岂不麻烦!
原因在于:JTAG/SRAM 模式下载的内容只存在 FPGA 的 SRAM 配置单元里,掉电即失。解决办法是把程序固化到开发板上的 FLASH 中——我们的开发板上有一颗 8 Pin、64 Mbit 的 FLASH,专门用来存储配置程序。
操作很简单:在设备配置界面把 Mode 选择为【PROGRAM FLASH】模式,其余选项保持默认,然后点击【Run】即可:

图 1-26 PROGRAM FLASH 固化模式
固化失败先擦除
如果曾经意外断电,可能导致片外 FLASH 中的配置文件异常,FPGA 不能正确加载。这时需要先用 Erase Flash 模式把片外 FLASH 擦除一遍,再重新固化下载。
第 8 步:用 ModelSim 做功能仿真
上板前还可以(也应该)先仿真验证。先弄清两个概念:
- 前仿真(功能仿真):纯粹验证电路功能是否符合设计要求,不考虑电路的门延迟与线延迟;
- 后仿真(时序仿真):电路已映射到特定工艺环境,综合考虑路径延迟与门延迟的影响,验证电路在一定时序条件下是否满足设计构想。
ModelSim 的使用分两种:通过 Anlogic 软件调用 ModelSim 的「自动仿真」,以及直接打开 ModelSim 的「手动仿真」。本教程介绍手动仿真(Anlogic TD 中无法直接调用 ModelSim 做自动仿真)。
2.1 编写 TestBench 仿真文件
仿真前需要自行编写 TestBench(测试激励)文件。在模板里添加我们需要的测试代码(激励),修改完毕的代码如下:
`timescale 1 ns/ 1 ns
module tb_cnt1s( );
reg CLOCK;
reg rst_n;
wire LED;
cnt1s i1 (
.clk_50M(CLOCK),
.led_out(LED),
.rst_n(rst_n)
);
initial
begin
$display("Running testbench");
CLOCK = 0;
rst_n = 0;20 #20 rst_n = 1;
22 end
24 always
25 begin
26 #10 CLOCK = ~CLOCK;
27 end
29 endmoduleTestBench 端口名必须与顶层一致
注意上面模板里实例化写的是
.clk_50M(CLOCK)和.led_out(LED),而我们顶层模块cnt1s的端口名是CLOCK和LED。实际仿真时,TestBench 中连接的端口名要与顶层模块的端口定义完全一致,否则编译报错。这正是原文提醒「如果修改了端口的输入和输出,最好重新全编译一次,并重新生成仿真模板文件进行修改」的原因。
TestBench 的激励逻辑是:CLOCK 初值为低电平,之后用 always #10 CLOCK = ~CLOCK 产生周期 20 ns(对应 50 MHz)的时钟;rst_n 在开头 20 ns 内保持低电平复位,之后拉高释放复位。
2.2 配置仿真工程
打开 ModelSim,点击【File】→【New】→【Project】新建一个工程:

图 2-1 ModelSim 新建工程
输入工程名 cnt1s,把 ModelSim 工程保存在 cnt1s 工程例子文件夹下面,点击【OK】:

图 2-2 设置仿真工程名与路径
在弹出的界面点击【Add Existing File】,把工程源文件与 TestBench 仿真文件添加进工程:

图 2-3 添加已有文件

图 2-4 选择要添加的文件
在工程界面可以看到两个源文件。点击【Compile】→【Compile All】编译它们,编译通过后,状态问号会变成对勾:

图 2-5 编译工程文件

图 2-6 编译通过显示对勾
2.3 开始功能仿真
仿真前先「缩短」1 秒
我们在 Anlogic 工程里实现的是 1 秒间隔闪烁,想仿真这个功能,仿真软件至少要跑 2 秒。这对人是短短两秒,可对以 ns 为单位运行的仿真器来说太长了。为了便于仿真,需要把 Verilog 代码中的
localparam T1S = 27'd49_999_999;临时修改为localparam T1S = 27'd5;,让翻转周期缩短到几个时钟周期。这是功能仿真里非常常用的「参数缩比」技巧。
修改完毕后,在 ModelSim 菜单栏的【Library】→【Work】下找到 tb_cnt1s,右击选择【Simulate】:


启动仿真后出现波形窗口
右击选择【tb_cnt1s】→【Add To】→【Wave】→【All items in region】,把所有信号添加进波形窗口:

图 2-8 添加信号到 Wave
在弹出的界面下的副本窗口输入仿真时长「1000ns」然后按回车:

图 2-9 输入仿真时长 1000ns
此时就可以看到 ModelSim 仿真出的波形(图 2-10),软件会自动完成仿真并给出波形。
接下来看波形判断功能是否正常。为了便于观察,先认识几个 ModelSim 常用小工具:

图 2-11 ModelSim 常用工具栏
- 前面几个放大镜模样的工具是放大、缩小等功能,鼠标放到图标上会显示快捷键;
- 中间的黄色图标用来在波形图上做标记;
- 后面的按钮用于运行仿真、暂停仿真、重新运行等。
用这些工具处理后的波形界面如图 2-12 所示:

图 2-12 处理后的仿真波形
对照波形读结果:CLOCK 初值为低电平、周期 20 ns,rst_n 在第一个周期内为低电平、之后变高,与 TestBench 代码一致。rst_n 置高之后,随后的第一个 CLOCK 上升沿到来时 cnt_value 加 1,一直数到等于 5;当下一个 CLOCK 上升沿到来时,cnt_value 归零,同时 LED 的值发生反转——与 Verilog 代码预期完全一致,功能仿真通过。
改代码后要不要重新编译工程?
如果只修改 Verilog 程序代码和仿真文件代码(前提是不修改端口信号),不需要重新编译整个工程——只要关闭 ModelSim 仿真软件,再一次点击开始仿真即可。但如果修改了端口的输入和输出,就最好重新全编译一次,并重新生成仿真模板文件进行修改。
现象与总结
至此,一条完整的 FPGA 开发流水线就走通了:
- 新建工程——路径合法、命名与顶层一致;
- 输入设计——写 Verilog、保存(文件名 = 模块名);
- 编译工程——SynOpt 通过,清理 Error、审视 Warning;
- 分配引脚——IO Constraint 手动分配或 .adc 约束文件批量分配;
- 编译布线——Run 生成比特流;
- 下载程序——JTAG/SRAM 模式下载 .bit,掉电丢失;
- 固化程序——PROGRAM FLASH 模式写入片外 FLASH,掉电不丢;
- 功能仿真——ModelSim + TestBench,缩短时间参数快速验证。
现象:板上 LED 每 1 秒翻转一次,4 个灯同步闪烁;用 PROGRAM FLASH 固化后,重新上电程序依然有效。
后面每个实验都会反复使用这套流程,只是代码、引脚和现象不同。把这一篇的每一步操作练熟,是最划算的投资。
自测
自测
- 新建工程时,工程路径为什么不能包含中文和特殊符号?文件夹应该什么时候创建?
- 编译日志中的 Error 和 Warning 分别意味着什么?为什么 Warning 也不能完全无视?
- 50 MHz 时钟下要让 LED 每 1 秒翻转一次,分频计数器的终值
T1S为什么设为 49_999_999 而不是 50_000_000?- JTAG 下载和 PROGRAM FLASH 固化有什么本质区别?如果意外断电导致 FLASH 内容异常,应该先用什么模式处理?
- 用 ModelSim 仿真 1 秒闪烁前,为什么要把
T1S改成27'd5?如果只改了内部逻辑(不改端口),需要重新全编译工程吗?