这一篇在干嘛?

这一篇不讲具体电路,而是带你把安路 TD(Anlogic TD)开发软件的完整使用流程走一遍:新建工程、写 Verilog、编译、锁引脚、下载到板子、固化到 FLASH,最后再用 ModelSim 做一次功能仿真。示例工程很简单——让核心板上的 4 个 LED 以 1 秒间隔闪烁。走完这一篇,后面所有实验的上板流程你就全部掌握了。

实验目标与准备工作

本篇基于安路 EG4S20 系列 FPGA(配套 KX-EG420 开发板),使用开发工具 Anlogic TD 标准版(TD5.6.2-64bit)。学完你将掌握:

  1. 在 Anlogic TD 中新建工程、编译工程的基本方法;
  2. 锁定引脚、制作引脚约束文件(.adc)的方法;
  3. 把工程下载到开发板、以及固化到 FLASH 的方法;
  4. 用 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 引脚
CLKR7
复位按键名称
rst_n (K1)A2
LED 名称
LED0A4
LED1A3
LED2C10
LED3B12

参考上表,把引脚号填入对应信号名的「Location」栏。具体输入方法:用鼠标点击 CLOCK 信号名 Location 下面的空白位置,输入 PIN_R7,也可以直接输入 R7,然后回车即可。注意这里必须根据原理图的硬件连接关系分配引脚,不能凭空填。

填好后如下图所示(含 IO Standard、Drive Strength 等完整属性):

NameDirectionBankLocationIOStandardDriveStrengthPullTypeSlewRateDiffResistorPCIClampHysteresisVREF
1rst_ninputbank3A2.VCMOS33NAPULLUPNANAOFFOFFNA
2LED[1]outputbank0A3.VCMOS338NONESLOWNANANANA
3LED[0]outputbank0A4.VCMOS338NONESLOWNANANANA
4LED[3]outputbank0B12.VCMOS338NONESLOWNANANANA
5LED[2]outputbank0C10.VCMOS338NONESLOWNANANANA
6CLOCKinputbank2R7.VCMOS33NAPULLUPNANAOFFOFFNA


图 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 驱动程序。流程如下:

  1. 右键桌面图标「计算机」→「属性」,打开「设备管理器」,在通用串行总线设备下会出现「USB-JTAG-Cable」:


图 1-17 设备管理器中的 USB-JTAG-Cable

  1. 右键选择「USB-JTAG-Cable」,点击「更新驱动程序」:


图 1-18 更新驱动程序

  1. 选择「浏览计算机以查找驱动程序软件」:


图 1-19 浏览计算机查找驱动

  1. 点击「浏览」,定位到 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 endmodule

TestBench 端口名必须与顶层一致

注意上面模板里实例化写的是 .clk_50M(CLOCK).led_out(LED),而我们顶层模块 cnt1s 的端口名是 CLOCKLED。实际仿真时,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 开发流水线就走通了:

  1. 新建工程——路径合法、命名与顶层一致;
  2. 输入设计——写 Verilog、保存(文件名 = 模块名);
  3. 编译工程——SynOpt 通过,清理 Error、审视 Warning;
  4. 分配引脚——IO Constraint 手动分配或 .adc 约束文件批量分配;
  5. 编译布线——Run 生成比特流;
  6. 下载程序——JTAG/SRAM 模式下载 .bit,掉电丢失;
  7. 固化程序——PROGRAM FLASH 模式写入片外 FLASH,掉电不丢;
  8. 功能仿真——ModelSim + TestBench,缩短时间参数快速验证。

现象:板上 LED 每 1 秒翻转一次,4 个灯同步闪烁;用 PROGRAM FLASH 固化后,重新上电程序依然有效。

后面每个实验都会反复使用这套流程,只是代码、引脚和现象不同。把这一篇的每一步操作练熟,是最划算的投资。

自测