简介:《ISE软件应用实例》面向FPGA学习和开发者,围绕Xilinx ISE与Spartan-3E平台整理,通过六个递进实例完整演示从项目创建、HDL编码、综合约束、仿真验证到芯片配置、硬件调试的开发链路,适合需要上手ISE工具或巩固FPGA设计流程的读者。资源包内涵盖2000个文件,以vhd、v等硬件语言源码为主,搭配ucf约束、xise/gise工程文件、ngc/bit综合配置结果以及仿真日志等过程文件,类型齐全且压缩后仅21.63MB,便于下载使用。目前已有199人学习下载,适合作为FPGA入门实践参考。读者可从中获得基本逻辑门、计数器、有限状态机、DSP模块、SPI/I2C接口及存储器接口等实例源码,对照工程结构和约束设置,加深对ISE各环节操作与FPGA资源利用的理解。
1. 用 ISE 和 Spartan-3E 复现实例:为什么老工具反而更顺手
手里还有一片 Spartan-3E 的板子,想跑个串口、以太网或者简单的图像采集,最先要过的就是 ISE 这道门槛。ISE 虽然是老工具,但对 Spartan-3E 的支撑比 Vivado 完整得多,库、核、文档都齐。我整理过一套 ISE 应用实例工程,从建工程、ModelSim 仿真到 ChipScope 抓信号,再到 Linux 下的 14.7 安装,都有可以直接照抄的配置。这套记录就把我复现过程中最值得记的步骤和踩过的坑拆给你,适合手里有旧板子、想低成本验证逻辑的工程师,也适合刚接触 FPGA 但不想在工具上浪费时间的新手。最好把实例工程放在手边,边看边试,遇到问题才能立刻对上号。
2. 建一个 Spartan-3E 工程:从选型到烧录的完整链路
2.1 Spartan-3E 的资源边界与选型判断
Spartan-3E 是 Xilinx 早期低成本 FPGA,和后来的 7 系列最大的区别,是没有内部高速串行收发器,也没有硬核 DDR 控制器,能用的是 Block RAM、DCM(数字时钟管理器)和普通 I/O。很多人选型时只盯着逻辑单元,实际上这个片子真正卡脖子的是 BRAM 和用户 IO 数量。
以常见的 XC3S500E 为例,封装 FG320、速度等级 -4。这个片子放一个串口、一个 SPI Flash 控制器、一个 8 位软核,再加一个 512 深度的 ILA 调试核是没问题的;但要塞两个以太网 MAC,BRAM 就非常紧张。所以在动手写 RTL 之前,先看板上芯片具体型号,再对照数据手册确认资源,能省掉后面一半的返工。
选型的时候可以把下面这张表当作检查单,不用死记门数:
| 关注项 | 对设计的影响 |
|---|---|
| Block RAM 数量 | 决定 FIFO、ILA、缓存和数据通路能不能铺开 |
| DCM 数量 | 决定能生成几个独立时钟、相移或分频时钟 |
| 用户 IO 数 | 决定总线宽度、外设引脚是否够用 |
| 封装/速度等级 | 影响板级布线难度和最高运行频率 |
另一个必须想清楚的选型点是工具链。Spartan-3E 在 Vivado 里不支持,只能走 ISE 14.7。ISE 14.7 是 Xilinx 最后一个支持 3E 器件族的版本,对 Windows 和 Linux 都有安装包。对比下来,ISE 和 Vivado 的差异非常直接:
| 对比项 | ISE 14.7 | Vivado |
|---|---|---|
| Spartan-3E 支持 | 完整支持 | 不支持 |
| 约束方式 | UCF | XDC |
| 综合工具 | XST | Vivado Synthesis |
| 常用仿真器 | 自带 ISim + ModelSim 联合 | XSim |
Spartan-3E 核心电压是 1.2V,IO bank 电压一般接 3.3V 或 2.5V。这意味着你写 UCF 时 IOSTANDARD 必须和板卡实际电压一致,最常见的是LVCMOS33。如果板子上某个 bank 接的是 2.5V,你却约束成 3.3V,轻则采样错误,重则长期运行后损伤引脚。
2.2 最小工程搭建:Verilog 源码、UCF 约束与 bit 生成
建工程时,打开 ISE 后选 New Project,器件族选 Spartan3E,具体型号按板子选,比如 XC3S500E-FG320-4。综合工具必须选 XST,仿真语言选 Verilog,这样后续流程和 ModelSim 联合仿真才不会别着。
第一个例子建议做 LED 翻转,代码短,但能把时钟约束、复位、管脚分配、烧录全链路打通。下面这段代码可以直接放进实例工程的 led_blink 目录:
// led_blink.v module led_blink( input wire clk, // 板上晶振输入 input wire rst_n, // 低有效复位 output reg led // 驱动 LED ); reg [23:0] cnt; always @(posedge clk or negedge rst_n) begin if (!rst_n) cnt <= 24'd0; else cnt <= cnt + 1'b1; end always @(posedge clk or negedge rst_n) begin if (!rst_n) led <= 1'b0; else if (cnt == 24'd0) led <= ~led; // 计数溢出时翻转 end endmodule这里cnt是 24 位计数器,50MHz 时钟下计数周期约 335ms,LED 以约 1.5Hz 频率翻转,肉眼能看到明显闪烁。如果你板子时钟不是 50MHz,把timescale和PERIOD一起改掉。代码里rst_n是异步复位,和实际工程习惯一致。
管脚约束 UCF 文件是 ISE 的老写法,和 Vivado 的 XDC 完全不一样。下面是一个最小约束示例:
# led.ucf NET "clk" LOC = "C9" | IOSTANDARD = LVCMOS33; NET "rst_n" LOC = "H2" | IOSTANDARD = LVCMOS33; NET "led" LOC = "AA9" | IOSTANDARD = LVCMOS33; NET "clk" PERIOD = 20 ns; # 50 MHz这里的引脚位置是我手头板子的,不同板卡差异很大,直接照抄大概率翻车。你需要查板卡原理图,找到晶振、按钮和 LED 实际连接的 FPGA 引脚号再改。PERIOD约束表示 clk 周期 20ns,对应 50MHz。综合器拿到这个约束后会据此布局布线,但PERIOD本身不会生成时钟,时钟是从外部引脚进来的。
新建好源文件后,在 Process 窗口里依次双击:Synthesize – XST、Implement Design、Generate Programming File。注意 Implement 里包含 Translate、Map、Place 和 Route 三个子步骤,全部跑完才生成 bit。跑完打开 Timing Report,如果Timing constraint: PERIOD一栏的 Slack 是正数,说明时序收敛;如果是负数,说明布局布线后时钟频率达不到要求。
2.3 烧录与验证:iMPACT 的使用
生成 bit 后,用 ISE 自带 iMPACT 烧录。连接 USB 下载线,双击 Configure Target Device,选 Boundary Scan,iMPACT 会自动扫描 JTAG 链。如果只连了一片 FPGA,链上就只有一个器件,右键 Assign File 指定 top.bit,然后 Program。
烧录失败时,先检查三件事:板卡电源是否上电、JTAG 线是否插反、下载线驱动是否装好。排除这三个因素后,还可以在 Cable Setup 里降低 TCK 频率,有些老板子对高频 JTAG 很敏感,降到 5MHz 以下就稳定了。
如果你需要在产线上批量烧录,iMPACT 也支持命令行批处理,写一个 program.cmd:
setMode -bs setCable -port auto identify assignFile -p 1 -file top.bit program -p 1 quit然后执行:
impact -batch program.cmd-batch参数会静默执行,日志输出到标准输出,适合集成到 CI 脚本里。这个步骤能跑通,说明 ISE 的完整链路已经没问题了,后面所有调试都能站在这个基础之上。
3. ModelSim 联合仿真:ISE 里的仿真到底怎么跑通
3.1 为什么用 ModelSim 而不是 ISim
ISE 自带 ISim,但对于复杂波形调试和回归测试,很多人还是习惯用 ModelSim。高频问题“ise仿真用modelsim怎么完成”,核心在于:ModelSim 本身不认识 Xilinx 原语,比如BUFG、IBUFG、DCM_SP。如果 RTL 里直接例化了这些原语,或者网表仿真时带上了 Xilinx 库,就必须先把 ISE 安装目录下的仿真库编译进 ModelSim,否则仿真器一跑就报Unknown identifier。
ISim 虽然开箱即用,但脚本化能力弱,多个 testbench 回归时效率不高。ModelSim 的优势是命令行友好、波形查看流畅,配合.do脚本可以一键完成编译、仿真、检查。下面所有命令都基于 ModelSim 命令行,GUI 操作也是同一个逻辑。
3.2 编译 Xilinx 仿真库:vlib/vmap/vlog 脚本
在 ModelSim 里,所谓“配置 Xilinx 库”,就是三件事:建库、映射库、编译库。我一般写一个 TCL 脚本,放在实例工程的sim目录下,内容如下:
# xilinx_modelsim_lib.tcl set ise_root "/opt/Xilinx/14.7/ISE_DS/ISE" set lib_root "/opt/fpga_lib/msim_xilinx" vlib $lib_root/unisim vlib $lib_root/unimacro vlib $lib_root/simprim vmap unisim $lib_root/unisim vmap unimacro $lib_root/unimacro vmap simprim $lib_root/simprim vlog -work unisim $ise_root/verilog/src/unisim_comp.v vlog -work unisim $ise_root/verilog/src/unisim_retarget_comp.v vlog -work unimacro $ise_root/verilog/src/unimacro_comp.v vlog -work simprim $ise_root/verilog/src/simprim_comp.v三个库的职责不同:unisim是通用原语功能模型,simprim用于布线后时序仿真,unimacro是复杂宏模块库。脚本里路径是 Linux 写法,Windows 下把/opt/Xilinx改成D:/Xilinx/14.7/ISE_DS/ISE即可。
执行方式有两种:在 ModelSim Transcript 窗口输入source xilinx_modelsim_lib.tcl,或者在命令行直接跑:
vsim -c -do xilinx_modelsim_lib.tcl编译过程通常会持续一两分钟。编译完成后,lib_root目录下会出现 unisim、unimacro、simprim 三个子目录,里面的_info文件就是映射索引。
3.3 编译设计文件和 testbench 并跑波形
库编好后,新建工程或者直接在 ModelSim 里编译源文件。对 led_blink 这个最小例子,命令如下:
vlib work vlog +incdir+src src/led_blink.v src/led_blink_tb.v vsim -L unisim -L simprim work.led_blink_tb-L unisim和-L simprim是联合仿真的关键参数,告诉 ModelSim 在编译的库中查找 Xilinx 原语。如果设计里只用了普通 Verilog,不加也能跑;但只要 RTL 里例化了 IBUFG/BUFG,少一个库就报错。
testbench 处理的是时钟和复位,代码如下:
// led_blink_tb.v `timescale 1ns/1ps module led_blink_tb; reg clk; reg rst_n; wire led; led_blink dut( .clk (clk), .rst_n(rst_n), .led (led) ); initial begin clk = 0; forever #10 clk = ~clk; // 50MHz end initial begin rst_n = 0; #100 rst_n = 1; #5000 $finish; end endmodule波形窗口里加信号并运行:
add wave -divider "dut" sim:/led_blink_tb/dut/* run 20us如果仿真正确,你会看到cnt周期性地回到 0,led跟着翻转。为了方便肉眼观察,可以把cnt临时改成 8 位再仿真,看到的变化更直观。仿真环境和板卡环境共用同一份 UCF,但注意PERIOD约束在 RTL 仿真阶段不生效,仿真速度完全由 testbench 的#10决定。
3.4 仿真常见问题排查
ModelSim 联合仿真遇到的坑,大多集中在库编译和库加载两个环节。下面三条是我反复遇到的。
现象 1:vlog -work unisim编译过程中报Unknown compiler directive或者大量乱码。
原因:ModelSim 版本太老,对 ISE 14.7 的 Verilog 文件语法兼容不好。
解决:使用 ModelSim 10.5 及以上版本,或者换用 ISE 自带的 ModelSim XE。老版本 ModelSim 编译 14.7 的库时,确实会出现语法不兼容,不是代码的问题。
现象 2:vsim -L unisim后依然报Unknown identifier "BUFG"。
原因:vmap映射没有生效,ModelSim 在默认 work 库找不到原语,也没有识别到-L指定的库。
解决:先在 Transcript 窗口敲vmap unisim确认映射路径,再查看lib_root/unisim下是否有编译产物。如果映射指向了不存在的目录,重新执行vlib/vmap/vlog三步。
现象 3:仿真一直不翻转,波形里信号全是蓝色或高阻。
原因:复位没有正确释放,或者forever #10的时钟没有在 initial 块里启动。
解决:把run 100ns,在波形里检查rst_n和clk是否正常;如果rst_n一直为低,检查 testbench 里#100是不是放在begin...end外面导致语法歧义。
4. ISE 里加程序抓信号:ChipScope 在线调试实战
4.1 ChipScope 到底在帮你抓什么
仿真能解决逻辑问题,但真实板卡上的信号还是要在线看。ISE 时代的方案就是 ChipScope,它把逻辑分析仪塞进 FPGA。所谓“ise加程序抓信号”,本质是在综合后的网表里插入 ICON(控制器)和 ILA(逻辑分析仪核),通过 JTAG 把内部信号实时回传到电脑。
和外部逻辑分析仪相比,ChipScope 能抓到芯片内部任意布线节点,只要这个信号没有被综合优化掉。代价是它占 BRAM 和布线资源,插入过多调试核会直接影响布局布线,所以设计跑通基本功能后再加抓信号,是个好习惯。
4.2 给信号做保留标记并例化调试核
很多信号在综合优化时会被当作中间变量处理掉,不加标记根本抓不到。在 RTL 里给信号加上合成属性:
(* MARK_DEBUG = "TRUE" *) reg [3:0] state_cnt; (* MARK_DEBUG = "TRUE" *) wire uart_rdy;然后通过 Core Generator 生成一个 ILA 核,配置数据深度 1024,probe 宽度按信号数量定,生成的模块在顶层例化。这里假设生成的模块叫ila_128:
ila_128 chipscope_ila ( .clk (clk), .probe0 (state_cnt), // 4 bit .probe1 (uart_rdy) // 1 bit );.clk必须接设计里的全局时钟,最好是最快的时钟,这样采样不会漏掉快沿信号。probe0、probe1的顺序和宽度,要和 Core Generator 里配置的 probe 列表一一对应,否则 Analyzer 里看到的信号就会错位。
综合完以后,打开 Synthesize 报告,搜索state_cnt。如果报告里出现signal "state_cnt" removed或optimized out,说明还是被综合器优化了。这时需要改用:
(* KEEP = "TRUE" *) reg [3:0] state_cnt;KEEP会保留这条信号到网表,但综合后的名称可能带_reg后缀,Analyzer 里选信号时注意模糊匹配。
4.3 用 ChipScope Analyzer 抓波形
加载 bit 后,打开 ChipScope Analyzer,等待 JTAG 链路扫描到设备,然后加载目标 bit。在 Trigger Setup 里设置触发条件,比如state_cnt == 4'b1010,或者直接用trigger on rising edge。设置完成后,点击 Arm 按钮,等待触发。
触发位置参数直接影响你能看到多少历史数据。数据深度 1024、触发位置设为 512,就表示能看到触发前 512 个采样点、触发后 512 个采样点。如果设置成触发位置 0,则只能看到触发生成之后的数据,前因全丢了。
触发成功后,波形窗口里会显示被采样信号的时间图。你可以直接导出 CSV 或者以.vcd格式保存,拿到 Python 或 MATLAB 里继续处理。
4.4 抓信号时最容易翻车的三个边界
边界 1:信号被综合优化掉。前面已经说了,用MARK_DEBUG或KEEP保留。但也要注意,KEEP会让信号保留到布局布线网表,可能带来不必要的布线拥塞。只对需要调试的信号加,不要全军覆盖。
边界 2:采样时钟与被测信号不同步。ILA 只有一个采样时钟,如果被测信号来自另一个异步时钟域,抓到的波形会抖动甚至出现毛刺。这时候要么把采样时钟改成较快的那个时钟域,要么在 RTL 里先用同步器把信号打两拍再送进 ILA。
边界 3:加了 ILA 后时序不再收敛。ILA 会占用全局时钟资源,插入 probe 信号后,被采样信号路径上会多一段路由延迟。原来能跑到 100MHz 的设计,可能只能跑到 85MHz。如果发现加完 ILA 后模块莫名其妙不工作,先跑一次 MAP 后的时序报告,和没加 ILA 前对比,大概率是全局时钟缓冲被 ILA 挤占,布线质量下降。
5. Linux 下 ISE 14.7 安装、授权与避坑记录
5.1 为什么要在 Linux 上装 ISE 14.7
常看到有人搜“linux中ise 14.7安装包下载”,因为很多服务器是 Linux,ISE 在 Linux 下的命令行综合比 Windows 图形界面更适合批量回归。安装包一般是Xilinx_ISE_DS_14.7_P_64_3_0.tar.gz,这个版本是最后一个支持 Spartan-3E 的版本,装完以后既能跑 GUI,也能跑xst、ngdbuild、impact全套命令行工具。
Linux 上装 ISE 的难点不在安装本身,而在依赖库和权限。下面这套步骤在 Ubuntu 20.04 和 CentOS 7 都验证过,但不同发行版包名略有差异。
5.2 安装过程:依赖、路径与环境变量
先装依赖库,ISE 是 32 位程序,64 位系统必须准备 32 位兼容库:
sudo apt-get install -y libncurses5 lib32ncurses5 libncurses5-dev \ libstdc++6:i386 lib32z1 libgtk2.0-0:i386 libglu1-mesa:i386 \ libusb-dev libusb-1.0-0-dev fxload解压并运行安装器:
tar -xf Xilinx_ISE_DS_14.7_P_64_3_0.tar.gz cd Xilinx_ISE_DS_14.7_P_64_3_0 sudo ./xsetup安装目录建议放在/opt/Xilinx。装完后,每次打开终端第一件事是加载环境变量:
source /opt/Xilinx/14.7/ISE_DS/settings64.sh把这一行写进~/.bashrc,以后新终端自动加载。如果不执行,ise、xst、impact命令都会提示command not found。注意 ISE 14.7 自带的脚本是settings64.sh和settings32.sh,64 位系统用前者。
5.3 License 和 USB-JTAG 权限
License 文件可以放在任意位置,关键是环境变量指向它:
export XILINXD_LICENSE_FILE=/opt/Xilinx/14.7/ISE_DS/common/license/Xilinx.lic sudo chmod -R a+rX /opt/Xilinx/14.7如果 license 放在你自己的用户目录,sudo打开 iMPACT 时读不到,因为权限不足。我一般把 license 直接复制到/opt/Xilinx/14.7/ISE_DS/common/license/下,省去后面所有权限问题。
USB-JTAG 下载线在 Linux 下需要 udev 规则,否则普通用户调用impact会提示找不到 cable:
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="03fd", MODE="0666"' | sudo tee /etc/udev/rules.d/99-xilinx.rules sudo udevadm control --reload-rules03fd是 Xilinx 下载线的 USB Vendor ID,部分兼容下载线用的是 FTDI 芯片,ID 是0403。如果手上的线插上后lsusb显示0403:6014,规则里就要写ATTRS{idVendor}=="0403"。两条规则都写也不冲突。
5.4 避坑记录:现象、原因、解决
现象 1:./xsetup运行后直接报error while loading shared libraries: libXm.so.3。
原因:缺少 Motif 运行库,ISE 安装界面和部分工具依赖它。
解决:Ubuntu 下安装libmotif-dev或libxm4:i386;CentOS 下对应openmotif。如果包管理器里找不到,搜索对应发行版的libXm.so.3包名手动安装。
现象 2:ISE 启动后图形界面白屏,窗口一片空白,只有标题栏。
原因:缺少 32 位 GTK 和 OpenGL 库,或者显卡驱动过新导致旧 Tcl/Tk 渲染异常。
解决:装完libgtk2.0-0:i386 libglu1-mesa:i386后重启终端。还是白屏,就用命令行方式跑综合,别和 GUI 斗争了。
现象 3:impact命令行连接 JTAG 时提示Cannot find cable。
原因:当前用户的 USB 设备权限不足,或下载线驱动没有加载。
解决:插上下载线后执行lsusb,确认设备枚举正常,然后按上面 udev 规则操作。部分内核新版本还需要加载fxload,依赖安装列表里已经有。
现象 4:Windows 工程拷到 Linux 后,xst报大量File not found。
原因:ISE 工程文件.ise里存的是 Windows 绝对路径和反斜杠分隔符,Linux 下直接不认。
解决:不要在工程目录之间直接复制.ise,而是新建工程,把源码和 UCF 文件手动加进去。更彻底的方式是放弃 GUI 工程,用.prj源文件列表加命令行综合,这样跨平台就没有路径污染。
现象 5:综合时 license 报Feature is expired,但 Windows 下同一个 license 正常。
原因:Linux 下 license 文件路径不对,或者当前用户对 license 文件没有读权限。
解决:检查XILINXD_LICENSE_FILE是否指向真实存在的文件,然后chmod a+r给 license 文件加读权限。ISE 的 license 通常绑定网卡 MAC,服务器有多个网卡时,也可能需要设置LM_AVAIL或关掉非业务网卡。
6. 从能跑到跑好:命令行流程、时序约束与固化程序
6.1 用命令行完成综合、实现、生成 bit
实例工程里我建议保留一套命令行脚本,好处是不管 Windows 还是 Linux,重装完工具后一条命令就能复现。先建一个top.prj源文件列表:
verilog work led_blink.v verilog work led_control.vverilog关键字后面是逻辑库名和文件路径,文件顺序按依赖关系从底层到顶层。然后写一个top.xst综合脚本:
run -ifn top.prj -ifmt MIXED -ofn top.ngc -p xc3s500e-fg320-4 -top top接着按顺序执行:
xst -ifn top.xst ngdbuild -uc led.ucf top.ngc top.ngd map -p xc3s500e-fg320-4 -o mapped.ncd top.ngd par -w mapped.ncd routed.ncd bitgen -w routed.ncd top.bitngdbuild会读 UCF 并把逻辑网表和管脚约束绑定到一起,map做逻辑映射,par做布局布线,bitgen生成最终 bit。任何一步失败,日志都写在当前目录的.log里,比 GUI 弹框好排查得多。
6.2 用 trce 报告验证时序是否收敛
跑完par后,打开routed.twr,重点看Timing constraint: PERIOD这一栏。如果 Slack 是正数或 0,说明这个时钟频率下设计能跑稳;负数则说明时序没有收敛,需要降频或改 RTL。
有时候这份报告不够细,可以单独生成:
trce -e 3 routed.ncd -o report.twr-e 3表示报告每个时序约束的完整分析,包含 setup 和 hold。看到 hold 负 slack 时,先检查是不是复位异步释放导致的数据路径短,再考虑加约束。时序问题在 Spartan-3E 上比新器件更敏感,因为布线延迟占比高,不能只看 LUT 延迟。
6.3 固化到 SPI Flash:让程序掉电不丢
调试用 JTAG 下载,最终产品必须固化。Spartan-3E 支持从 SPI Flash 引导,先用 promgen 把 bit 转成.mcs:
promgen -w -spi -c -p xcf08s -o top.mcs top.bit然后回到 iMPACT,切到 Flash 编程模式,选择对应型号,加载.mcs,点击 Program。烧完后断电重启,FPGA 会主动从 SPI Flash 读取配置,LED 直接闪起来,不再需要下载线。
固化前的检查项我一般有三条:bit 文件是不是最后一次综合的产物、SPI Flash 型号和 promgen 参数是否匹配、板卡上 FPGA 的配置模式跳线有没有拨到SPI一侧。漏了任何一条,固化结果是复位后还是白屏。
从那以后,我每拿到一块新板子的习惯都是先跑一遍这个 LED 工程,用 ModelSim 仿真确认分频逻辑,再用 ChipScope 抓一下cnt信号,最后才敢上真实业务逻辑。每次固化前都会把 bit 文件和约束扔进命令行流程重跑一遍,保证任何时候重做都能复现。这套实例里的三个工程我也放了同样的脚本,照着一遍走下来,你大概率能躲掉我当年踩过的坑。希望帮到你。
本文还有配套的精品资源,点击获取