DE0-Nano FPGA开发实战:从Verilog到Nios II软核系统搭建

📅 2026/7/18 13:13:06
DE0-Nano FPGA开发实战:从Verilog到Nios II软核系统搭建
1. 项目概述与核心价值如果你刚拿到一块DE0-Nano开发板面对FPGA、Quartus II、Verilog这些名词感到无从下手那么这篇实战指南就是为你准备的。我花了大量时间从最基础的工程创建到复杂的Nios II软核系统搭建把整个流程中容易踩的坑、必须注意的细节都梳理了出来。FPGA的魅力在于“硬件可编程”它不像单片机那样执行固定的指令流而是让你用代码“画”出一块真正的数字电路。DE0-Nano作为一款入门级但功能齐全的开发板搭载了Altera现Intel的Cyclone IV FPGA是学习数字逻辑设计、片上系统开发的绝佳平台。本文的核心价值在于提供一条清晰的、可复现的路径。我不会只告诉你“点击这里然后点击那里”而是会解释每一个操作背后的意图为什么这里要用PLL引脚分配的依据是什么SOPC Builder里每个组件的作用是什么通过一个完整的项目——从让LED闪烁的简单计数器到构建一个能运行C程序的Nios II处理器系统——你将不仅掌握工具的使用更能理解FPGA开发的核心思想。无论你是电子相关专业的学生还是希望拓展硬件技能的软件工程师这篇超过五千字的详实记录都能让你少走弯路快速上手。2. 开发环境部署与板卡初识在开始写第一行代码之前扎实的环境准备是成功的基石。这一部分我们解决“用什么”以及“怎么连”的问题。2.1 软件工具链安装与配置FPGA开发离不开厂商提供的集成环境。对于DE0-Nano我们的核心工具是Intel Quartus Prime Lite Edition早期版本叫Quartus II。我强烈建议直接从Intel官网下载最新版本的Lite版它对于Cyclone IV系列器件是免费的并且包含了我们所需的所有功能设计输入、综合、布局布线、编程下载以及SOPC Builder或更新版本的Platform Designer。安装过程比较直接但有几个关键点需要注意。首先安装路径不要包含中文或空格像C:\Altera\或D:\IntelFPGA\这样的路径是最稳妥的。安装程序可能会询问是否安装USB-Blaster驱动务必勾选。如果安装时漏了也不用担心驱动文件通常位于Quartus安装目录的drivers\usb-blaster文件夹下。当第一次通过USB线连接DE0-Nano板卡到电脑时系统会提示发现新硬件此时手动指定到这个目录安装驱动即可。驱动安装成功后在Quartus的Tools - Programmer窗口中点击Hardware Setup应该能看到USB-Blaster [USB-0]的选项这标志着你的电脑已经可以和板卡通信了。注意不同版本的Quartus界面和组件名称可能略有差异例如SOPC Builder在新版中已整合为Platform Designer但核心逻辑和流程是相通的。本文以经典的Quartus II 13.0/13.1界面为例进行讲解其原理适用于后续版本。2.2 DE0-Nano开发板硬件资源解析工欲善其事必先利其器。让我们快速浏览一下DE0-Nano板上的核心资源这对后续设计至关重要FPGA芯片核心是一颗Cyclone IV EP4CE22F17C6。EP4CE22表示Cyclone IV E系列逻辑单元约22KF17代表484脚的FBGA封装C6是速度等级。这些信息在我们创建工程选择器件时必须完全匹配。时钟源板载一颗50MHz的有源晶振连接至FPGA的全局时钟引脚R8。这是我们所有同步设计的基础时钟。用户I/O8个绿色LED连接到FPGA引脚低电平点亮共阳极接法。2个按键连接到FPGA引脚默认高电平按下时为低电平。GPIO扩展口两排共76个引脚将FPGA的I/O引出可以连接自定义外设或扩展板。配置与调试接口USB-Blaster电路用于编程FPGA和调试Nios II系统通过JTAG协议。电源可通过USB口或外部电源接口供电。理解这些资源后我们就能明白我们的设计就是要用代码去控制这些LED、读取按键状态并通过GPIO与外界交互。2.3 使用DE0-Nano System Builder快速搭建框架对于初学者手动分配所有引脚和约束是一件繁琐且易错的工作。TerasicDE0-Nano的生产商提供了一个非常实用的工具DE0-Nano System Builder。它本质上是一个图形化的引脚分配和顶层模块生成器。它的用法很简单。找到开发板资料光盘或官网下载包中的Tools\DE0_NANO_SystemBuilder目录直接运行DE0_NANO_SystemBuilder.exe即可无需安装。工具界面清晰输入项目名称例如led_blink。这将成为你顶层Verilog模块的名字。系统配置这里以复选框形式列出了板载的所有资源时钟、按键、LED、ADC、加速度计等。你只需要勾选你设计里会用到的部分。例如如果你只用到LED和按键就只勾选LED和KEY。工具会自动为你生成这些信号对应的引脚分配。GPIO扩展如果你使用了官方的扩展板如LCD、VGA等可以在这里选择对应的型号工具会生成相应的引脚定义。如果是自定义扩展板选择GPIO Default然后你需要根据自己定义的引脚功能在生成的qsf文件中手动修改信号名和方向。生成项目点击Generate按钮。工具会在当前目录下创建一个以项目名命名的文件夹里面包含project_name.v顶层Verilog文件声明了所有你用到的I/O端口。project_name.qpf和project_name.qsfQuartus项目文件和设置文件其中.qsf包含了所有自动生成的引脚位置约束。project_name.sdc时序约束文件模板通常需要你根据设计修改。project_name.htm引脚分配文档方便查阅。实操心得即使是有经验的开发者在项目初期我也推荐使用System Builder生成框架。它能确保基本的引脚分配正确无误避免因引脚号输入错误导致的硬件不工作问题。生成后你可以把.v文件作为你的顶层模块在其中实例化你自己的功能模块。3. 第一个FPGA项目从闪烁LED到用户交互现在我们抛开System Builder从头开始手动创建一个完整的Quartus项目目的是实现一个交互式LED闪烁器。通过这个项目你将熟悉Quartus开发全流程。3.1 创建Quartus II工程与器件选择启动Quartus II选择File - New Project Wizard。目录、名称与顶层实体工作目录建议用一个干净的路径例如E:\fpga_projects\my_first_fpga。项目名和顶层实体名都设为my_first_fpga。这里有个关键概念顶层实体就是你设计中最顶层的模块名Quartus将从这个模块开始综合整个设计。添加文件如果已有设计文件.v或.bdf可以在这里添加。我们从头创建直接点击Next跳过。器件选择这是至关重要的一步。在Family中选择Cyclone IV E。在Target device选项中选择Specific device selected in Available devices list。然后在过滤器中手动选择EP4CE22F17C6封装为FBGA引脚数484速度等级6。务必与板卡上的芯片丝印核对一致。EDA工具设置综合、仿真等工具暂时用Quartus自带的默认即可。完成检查总结信息点击Finish。此时一个空的Quartus项目就创建好了。3.2 设计输入混合使用原理图与Verilog HDLQuartus支持多种设计输入方式原理图、Verilog HDL、VHDL、Block Diagram等。在实际项目中通常是混合使用的。这里我们采用一种经典的学习路径用Verilog编写核心功能模块用原理图BDF作为顶层进行模块连接和I/O管理。3.2.1 编写Verilog计数器模块我们首先创建一个简单的32位计数器模块。File - New - Verilog HDL File新建一个Verilog文件。输入以下代码并保存为simple_counter.vmodule simple_counter ( input wire CLOCK_5, // 5MHz时钟输入由PLL产生 output reg [31:0] counter_out // 32位计数器输出 ); always (posedge CLOCK_5) begin counter_out counter_out 1; // 每个时钟上升沿计数器加1 end endmodule这个模块的行为非常简单当时钟信号CLOCK_5的上升沿到来时32位寄存器counter_out的值加1。由于计数器是32位的从0累加到2^32-1需要很长时间这为我们观察LED变化提供了基础。将该文件转换为符号SymbolFile - Create/Update - Create Symbol Files for Current File。这步操作会生成一个simple_counter.bsf文件允许我们在原理图中像调用一个芯片一样调用这个计数器模块。3.2.2 使用Megafunction锁相环PLL板载晶振是50MHz直接驱动计数器的话LED变化太快人眼无法分辨。我们需要一个更慢的时钟。虽然可以用计数器分频但更专业、更灵活的方式是使用锁相环PLL。PLL是FPGA内部的硬核电路可以产生频率稳定、相位可调的新时钟。新建一个Block Diagram/Schematic File (my_first_fpga.bdf)作为顶层文件。在原理图空白处右键Insert - Symbol点击Megawizard Plug-In Manager。选择Installed Plug-Ins - I/O - ALTPLL。设定输出文件为Verilog HDL命名为pll。在参数配置页面输入时钟频率inclk0设为50 MHz。在输出时钟c0配置页面我们想要一个5MHz的时钟。由于输入是50MHz我们将Clock division factor设为10即50MHz / 10 5MHz。其他选项如相位偏移保持默认。完成向导将生成的pll符号放置到原理图中。3.2.3 顶层原理图连接与I/O引脚添加现在我们在顶层原理图中将各个模块连接起来。添加输入时钟引脚从符号库primitives - pin中找到input符号放置并命名为CLOCK_50连接到PLL的inclk0输入口。这代表来自板载50MHz晶振的时钟信号。连接PLL与计数器使用画线工具将PLL的c0输出5MHz连接到计数器simple_counter的CLOCK_5输入。添加输出总线与多路选择器计数器的counter_out是一个32位总线。我们想用其中不同的位来控制LED的闪烁速度。添加一个lpm_mux多路选择器Megafunction配置为2路输入每路4位宽。将计数器的bit[26:23]连接到data1xbit[24:21]连接到data0x。这两组比特位在计数器中的位置不同因此它们的翻转速度也不同高位翻转慢低位翻转快。添加用户控制引脚添加一个input引脚命名为KEY[0]连接到多路选择器的sel选择端。这样通过按下按键就可以选择让哪一组计数器位去驱动LED。添加LED输出引脚添加一个4位宽的output引脚命名为LED[3..0]连接到多路选择器的result输出端。这里的[3..0]是Quartus原理图中表示总线位宽的语法。至此一个完整的、带用户交互的LED控制器硬件电路图就设计完成了。其功能是默认情况下LED以较慢的速度由计数器高4位控制进行二进制计数闪烁当按下KEY[0]时LED切换为较快的速度由计数器稍低的4位控制闪烁。3.3 引脚分配、时序约束与设计编译设计完成后必须告诉Quartus每个输入输出信号对应到FPGA芯片的哪个物理引脚上。引脚分配选择Assignments - Pin Planner。你会看到一个表格列出了设计中的所有I/O信号CLOCK_50,KEY[0],LED[0]~LED[3]。根据DE0-Nano的用户手册将信号分配到正确的引脚号CLOCK_50-PIN_R8KEY[0]-PIN_J15LED[0]-PIN_A15LED[1]-PIN_A13LED[2]-PIN_B13LED[3]-PIN_A11分配完成后关闭Pin Planner。这些约束会自动保存到.qsf文件中。时序约束对于同步设计必须定义时钟的周期以便时序分析工具进行优化和验证。我们创建一个简单的.sdc文件。Tools - TimeQuest Timing Analyzer。File - New SDC File。输入以下内容create_clock -period 20.000 -name CLOCK_50 [get_ports {CLOCK_50}] derive_pll_clocks derive_clock_uncertainty第一条命令定义了主时钟CLOCK_50的周期为20ns对应50MHz频率。后两条命令指示工具自动推导PLL生成的时钟并进行时钟不确定性分析。保存文件为my_first_fpga.sdc。Quartus会自动识别与顶层模块同名的SDC文件。全编译点击工具栏上的蓝色三角形按钮Start Compilation或选择Processing - Start Compilation。编译过程包括综合、布局布线、时序分析和生成编程文件。在Compilation Report中重点关注“Timing Analyzer”部分确保没有时序违规Slack为正数。对于这个简单设计通常都能满足时序要求。3.4 程序下载与硬件验证编译成功后会生成一个.sof文件SRAM Object File。用USB线连接DE0-Nano和电脑打开电源。Tools - Programmer。点击Hardware Setup选择USB-Blaster [USB-0]。点击Auto Detect软件应能识别出FPGA型号EP4CE22。点击Add File选择生成的my_first_fpga.sof文件。确保Program/Configure选项被勾选。点击Start。进度条走完后程序即下载到FPGA中。现在观察板卡四个LED应该开始缓慢地以二进制形式闪烁。按下KEY[0]按键并保持LED的闪烁速度应该明显变快。松开按键恢复慢速。这说明你的第一个FPGA设计已经在硬件上成功运行了常见问题与排查LED不亮或常亮检查引脚分配是否正确特别是LED的极性DE0-Nano是低电平点亮。检查原理图中连线是否正确总线位宽是否匹配。按键无反应检查KEY[0]的引脚分配并确认原理图中多路选择器的sel端连接正确。按键信号可能需要消抖处理但在这个简单实验中由于计数器速度相对较慢机械抖动的影响不明显。编程失败确认USB-Blaster驱动已安装线缆连接可靠。尝试给板卡重新上电。在Programmer中尝试JTAG Chain Debugger功能检查JTAG链路是否畅通。4. 进阶构建Nios II软核处理器系统让LED闪烁只是FPGA的“开胃菜”。FPGA真正的威力在于其可编程性我们甚至可以在其中“搭建”出一个CPU系统。Altera的Nios II就是一个可以嵌入到FPGA中的软核处理器。下面我们一步步在DE0-Nano上构建一个Nios II系统。4.1 SOPC Builder系统集成工具入门SOPC Builder在新版Quartus中为Platform Designer是一个图形化的系统集成工具用于将处理器、存储器控制器、外设IP核等组件连接起来构成一个完整的片上系统。新建一个Quartus工程命名为myfirst_niosii器件同样选择EP4CE22F17C6。选择Tools - SOPC Builder启动工具。系统名称我们命名为DE0_NANO_SOPC时钟输入命名为clk_5050MHz。4.2 添加与配置系统组件一个最小的可运行系统需要三个核心组件处理器、存储程序和数据的存储器、以及一个用于调试通信的接口。Nios II Processor在组件库中选择Processors - Nios II Processor。点击Add。在配置向导中选择Nios II/e经济型内核即可它占用逻辑资源最少适合DE0-Nano。其他设置保持默认点击Finish。在系统窗口中将其重命名为cpu。JTAG UART这是通过JTAG电缆实现PC与FPGA系统间串行通信的IP核是调试和打印信息的生命线。添加Interface Protocols - Serial - JTAG UART使用默认设置重命名为jtag_uart。On-Chip MemoryNios II处理器需要内存来存放程序和变量。我们使用FPGA内部的RAM资源。添加Memories and Memory Controllers - On-Chip - On-Chip Memory (RAM or ROM)。内存类型选择RAM (Writable)。数据宽度32 bits与Nios II总线位宽一致。总内存大小这里需要计算。DE0-Nano的FPGA内部RAMM9K总量是~600Kbits。我们设置为26000 bytes约25.4KB这是一个安全且充足的值确保综合不会因资源不足而失败。重命名为onchip_memory2。PIO (Parallel I/O)为了控制LED我们需要一个并行I/O核。添加Peripherals - Microcontroller Peripherals - PIO (Parallel I/O)。设置宽度为8对应8个LED。方向选择Output。重命名为pio_led。系统连接与地址分配SOPC Builder会自动用Avalon-MM总线将组件互联。我们需要手动为CPU指定启动地址。双击cpu组件进行编辑。在Reset Vector和Exception Vector的Memory下拉框中都选择onchip_memory2。这表示处理器上电复位和异常处理都从这片片上内存开始。点击System - Auto-Assign Base Addresses和Auto-Assign IRQs让工具自动分配各组件的内存映射地址和中断号。生成系统点击Generate按钮。工具会生成一个代表整个系统的Verilog模块文件DE0_NANO_SOPC.v以及一系列支持文件。生成成功后退出SOPC Builder。4.3 创建顶层模块与集成SOPC Builder生成的是一个子系统模块我们需要一个顶层Verilog文件来将它实例化并连接到FPGA的物理引脚。新建一个Verilog HDL文件输入以下代码module myfirst_niosii ( input wire CLOCK_50, // 50MHz时钟输入 output wire [7:0] LED // 8位LED输出 ); // 实例化由SOPC Builder生成的系统 DE0_NANO_SOPC DE0_NANO_SOPC_inst ( .clk_50 (CLOCK_50), // 连接50MHz时钟 .out_port_from_the_pio_led (LED), // 连接LED输出 .reset_n (1b1) // 复位信号拉高不复位 ); endmodule保存为myfirst_niosii.v并将其设置为顶层实体在Project Navigator中右键该文件选择Set as Top-Level Entity。进行引脚分配CLOCK_50分配至PIN_R8LED[7:0]依次分配至对应的LED引脚可参考原理图。同样需要创建或复用之前的.sdc文件定义50MHz的时钟约束。执行全编译。4.4 Nios II EDS与软件开发硬件系统myfirst_niosii.sof生成后我们需要为Nios II处理器编写C程序来控制LED。启动Nios II Software Build Tools for Eclipse在开始菜单或Quartus安装目录下找到并打开Nios II SBT。创建BSP工程File - New - Nios II Board Support Package。选择刚才SOPC Builder生成的.sopcinfo文件位于项目目录下。设定BSP工程名如myfirst_bsp。在Settings中特别注意stdout,stderr,stdin全部指向jtag_uart。这样printf等函数输出才能通过JTAG在Eclipse的终端显示。Timestamp timer可以指向cpu自带的定时器便于软件延时。其他保持默认点击Finish生成BSP。这个工程提供了硬件抽象层HAL驱动。创建应用工程File - New - Nios II Application。选择上一步创建的BSP工程作为基础。命名应用工程如led_controller。编写C程序在应用工程的src文件夹下新建main.c文件。#include stdio.h #include system.h #include altera_avalon_pio_regs.h int main() { unsigned int count 0; printf(Nios II LED Controller Started!\n); while (1) { // 将计数值写入PIO核从而控制LED IOWR_ALTERA_AVALON_PIO_DATA(PIO_LED_BASE, count); count; // 简单延时 usleep(200000); // 延时200ms } return 0; }这段代码不断将一个递增的count变量写入pio_led外设的寄存器从而实现LED流水灯或二进制计数的效果。usleep函数依赖于BSP中配置的定时器。构建项目右键点击应用工程选择Build Project。这会编译C代码并链接BSP库最终生成一个.elf可执行文件。4.5 系统下载与联合调试这是最后一步将硬件比特流和软件程序分别下载到FPGA中。下载硬件设计在Quartus Programmer中将myfirst_niosii.sof文件下载到FPGA。运行Nios II程序在Nios II SBT Eclipse中右键点击应用工程选择Run As - Nios II Hardware。软件会通过JTAG接口将.elf文件写入到FPGA的片上内存中并启动Nios II处理器执行。此时你应该能看到DE0-Nano板上的8个LED开始有规律地闪烁或计数。同时在Eclipse的Nios II Console视图中可以看到打印出的Nios II LED Controller Started!信息。这标志着一个完整的、包含软核处理器和自定义外设的片上系统成功在你的FPGA上运行了起来。深度解析与避坑指南资源评估在SOPC中添加组件时要时刻留意左下角的资源使用情况。Nios II系统会消耗大量的逻辑单元和内存资源。对于资源紧张的EP4CE22建议从Nios II/e经济内核开始并谨慎添加外设。地址空间冲突如果软件运行时行为异常如跑飞首先检查SOPC Builder中各组件的基地址是否有重叠。务必使用Auto-Assign Base Addresses功能。软件找不到硬件确保.sof文件已正确下载且Eclipse中Run Configuration里连接的硬件与实际一致都是USB-Blaster。有时需要重启Nios II SBT或重新连接USB线。printf不输出检查BSP设置中stdout是否指向了jtag_uart并且软件中包含了stdio.h。JTAG UART的驱动在HAL中已集成但需要正确初始化。从手动配置每一个逻辑门到搭建一个可编程的处理器系统FPGA开发的不同层次在此展现得淋漓尽致。这个过程里最深刻的体会是硬件描述语言如Verilog定义了电路的结构和行为而SOPC Builder这类工具则是在更高的抽象层次上进行系统集成。当你看到自己编写的C代码通过自己搭建的硬件系统驱动起真实的LED时那种对计算机体系结构从底层到上层的贯通感是单纯学习软件或硬件都无法给予的。后续你可以尝试添加更多外设如定时器、中断控制器甚至自定义的Avalon总线外设从而打造出真正为特定应用量身定制的片上系统。