简介:面向使用 DE1-SoC 开发板的嵌入式工程师、FPGA/ARM 学习者及高校实验教学人员,这份硬件原理图与开发手册用于解决开发板接口定位、系统配置和板载外设调试缺少权威依据的问题,适合课程实验、项目开发与硬件排错场景。压缩包仅含 1 个 PDF 文件,大小约 7.1MB,内容以英文用户手册为主体,便于在电脑或平板上随查随用。已有 1323 人学习下载,说明其对入门与工程实践均有参考价值。手册从开发套件包装清单、系统光盘和获取帮助讲起,继而解析板卡布局、组件与块图,重点覆盖 FPGA 配置模式、Cyclone V SoC FPGA 配置、LED/按键/开关等状态与复位元素、时钟电路,以及与 FPGA 相连的 7 段数码管、2×20 GPIO、24 位音频 CODEC、I2C 多路复用器、VGA、TV 解码和红外收发等外设。读者可据此快速熟悉硬件结构、核对原理图信号,并为嵌入式与 FPGA 项目设计打下基础,尤其适合需要按图核对接口与配置流程的读者。
1. 双芯同板:为什么手册的第一课是“配置模式”
做过 STM32 或者普通单片机的人,第一次翻开这份手册大概会愣一下:第三章第一节不是点灯,而是 FPGA 配置模式设置;第 8 章整章在讲怎么把 .SOF 转成 .JIC 再烧进 EPCS。一块开发板,上电之后连“程序在哪”都要先讲一章,这在单片机世界里不可想象——上电从固定地址取第一条指令,规则谁也改不了。
DE1-SoC 不一样。板上坐着两套系统:Cyclone V SoC 里的硬核双核 ARM Cortex-A9(HPS),和 85K 逻辑单元的 FPGA 阵列。FPGA 掉电即失忆,上电时是一片空白 SRAM,必须先由外部把配置数据喂进去,它才知道自己该变成什么电路。这条配置链路是整份手册真正的起点;搞不清它,后面所有外设、桥接、中断都是空中楼阁。
2. 上电失忆:FPGA 配置链路与 EPCS 的持久化逻辑
2.1 MSEL 与配置模式:上电那一刻板子听谁的
Cyclone V 的配置流程由几根专用引脚控制:nCONFIG 拉低让芯片进入复位,释放后芯片拉低 nSTATUS 表示忙,紧接着采样 MSEL[4:0] 这组模式引脚。采样结果决定它去哪里找配置数据——主动串行(AS)、被动串行(PS)、快速被动并行(FPP),还是干脆停在 JTAG 等着外部灌数据。
DE1-SoC 走的常规路径是 AS:板载一颗 EPCS128 串行配置器件,FPGA 自己当 SPI 主机,用 DCLK、ASDI、nCSO 几根专用脚去读它。整个过程中不需要任何主机参与,上电即自举。这条路径的价值在于“固化”——设备交付时不用带电脑,插电就能变成设计好的那套电路。
需要留意的是 MSEL 的采样时机。这 5 根脚只在复位释放后的很短一个窗口内被读取,之后随便怎么变都不影响本次启动。所以如果板上是用跳线帽或拨码开关设 MSEL,改完必须重新上电,热插拔无效。这一点我在实验室见过太多次:有人改完跳线直接按复位键,发现逻辑没起来,以为是烧写出错,其实芯片压根没重新采样。
JTAG 模式则是另一回事:MSEL 设成 JTAG 后,FPGA 停在等待状态,USB-Blaster II 通过 TCK、TMS、TDI、TDO 把比特流直接打进配置 SRAM。这种方式下载快、迭代方便,但掉电全丢。DE1-SoC 板上自带 USB-Blaster II,省了一根下载线,代价是 Quartus 版本和驱动必须对得上,否则识别出来的 Cable 名字会莫名其妙变成别的型号。
2.2 从 .sof 到 .jic:EPCS128 里到底存了什么
Quartus 编译的产物默认是 .sof,SRAM Object File,本质就是一份原始比特流。它能被 JTAG 直接写进 FPGA 的配置 SRAM。但 EPCS128 挂的是 FPGA 的专用配置引脚,压根没连到 USB 或者任何主机接口上,PC 无法直接对它寻址。这就产生了一个绕不开的问题:想让配置持久化,数据怎么进 EPCS。
Quartus 给的答案是 JIC——JTAG Indirect Configuration。这个格式的关键在于“间接”:USB-Blaster 先把 FPGA 通过 JTAG 置于一种特殊状态,让 FPGA 的配置引脚临时变成访问 EPCS 的通道,然后下载算法借着这条通道把数据一位一位写进 EPCS 的存储阵列。整个过程对用户透明,看起来就像“烧了个文件”。
# 1) 把编译生成的比特流转成可间接烧写 EPCS 的 JIC # -c 表示执行转换,-d 指定目标配置器件型号 quartus_cpf -c -d EPCS128 de1_soc_top.sof de1_soc_top.jic # 2) 通过 JTAG 把 JIC 写进 EPCS,掉电后仍可自举 quartus_pgm -c "USB-Blaster II" -m jtag -o "p;de1_soc_top.jic" # 3) 开发阶段想快速验证逻辑,直接把 .sof 打进 SRAM 就够了 quartus_pgm -c "USB-Blaster II" -m jtag -o "p;de1_soc_top.sof"quartus_cpf的-d参数必须和板上实际器件一致,写成 EPCS64 会直接报容量不匹配。quartus_pgm里-c指定下载器,-m指定链路类型,-o后面的p;表示 program 操作,分号后面跟文件路径。第三条命令是我平时用得最多的:改一行 Verilog 就烧一次 SRAM,几秒钟的事,不用等 EPCS 擦写那一两分钟。
EPCS128 的容量是 128 Mbit,也就是 16 MB。Cyclone V 5CSEMA5 的比特流通常只有几 MB,剩下的空间理论上有余量,但别指望拿它当通用 Flash 存数据——它是配置专用器件,访问时序和寿命都不适合。
2.3 两条启动链:FPGA 配置完了,HPS 才刚开始
板子上其实跑着两条互不干涉的启动链,很多人第一次接触会混在一起。
FPGA 这条链是:上电 → MSEL 采样 → 从 EPCS 读比特流 → 配置 SRAM → 用户逻辑开始运行。整条链在毫秒级完成,全程不需要软件介入。
HPS 那条链是:上电 → 片内 Boot ROM 执行 → 从 SD 卡(或其它介质)读 Preloader → Preloader 初始化 DDR3 和时钟 → 加载 U-Boot → 加载 Linux 内核。这条链要几百毫秒到几秒,取决于镜像大小和 DDR 训练时间。
两条链的交汇点在“桥”上。HPS 想访问 FPGA 侧的任何东西,前提是 FPGA 里已经加载了包含桥和 PIO 的比特流。如果只烧了 SD 卡镜像而 FPGA 侧是空的,HPS 里去 mmap 那段地址就会直接触发总线异常。反过来,只烧 JIC 不准备 SD 卡,串口上什么都不会打印。这两件事必须都做对,系统才算真正起来。
3. 打拍子与归零:四路时钟、两种复位和外设归属
3.1 四路 50MHz 同源时钟与 PLL 的取舍
手册 3.5 节写明板载时钟发生器提供四路 50MHz 时钟源。为什么不干脆一路扇出?原因有几层。FPGA 的全局时钟网络资源有限,一路时钟要驱动整片逻辑,扇出和偏斜都会变差;不同 IO bank 到时钟源的走线长度不同,分开布能把偏斜控制在可控范围内;板上还有音频 CODEC 这类对时钟抖动敏感的器件,独立一路能少受数字逻辑开关噪声的干扰。
50MHz 对 Cyclone V 来说只是个起点。芯片里有 6 个 fractional PLL,可以把 50MHz 倍频到几百 MHz,也可以做非整数分频。SDRAM 控制器就靠这个:64MB SDRAM 挂在 FPGA 侧,16 位数据总线,控制器时钟通常跑到 100MHz 以上,靠 PLL 从 50MHz 倍频得到。
真正麻烦的是相位。SDRAM 的数据和 DQS 之间有一个建立保持窗口,控制器输出的时钟和送到 SDRAM 芯片的时钟如果完全同相,采样点很容易落在跳变沿上。常见的做法是让 PLL 输出两路:一路 0° 给控制器内部逻辑,另一路偏移 -75° 或 -90° 送给 SDRAM 芯片,人为把采样点推进数据有效窗口的中央。这个偏移值不是拍脑袋定的,得看板级走线延迟和器件手册的时序参数,不同批次的板子可能要微调。
3.2 冷复位与热复位:HPS_RESET_n 和 HPS_WARM_RST_n 的差别
DE1-SoC 的 HPS 侧有两个复位按钮,对应两根信号:HPS_RESET_n 和 HPS_WARM_RST_n。名字只差一个词,行为差得很远。
HPS_RESET_n 是冷复位。它把整个 HPS 复位域拉低,包括 DDR3 控制器。释放后芯片重新走一遍 Boot ROM,DDR3 要重新做初始化训练,整个过程几百毫秒起步。效果等同于断电重启,只是不用拔电源。
HPS_WARM_RST_n 是热复位。它只复位 Cortex-A9 核和一部分外设,DDR3 控制器和内存里的内容都保留。这意味着启动时跳过了 DDR 训练,速度快得多,而且内存里的现场还在——调试内核崩溃时这一点特别有用,热复位之后还能从保留的内存区域里捞日志。
提示:热复位保留 DDR 内容这一点,在排查“内核 panic 后想看看崩溃瞬间的内存”时非常关键。但别指望它保留所有外设状态,UART、USB 这些通常也会被重置。
FPGA 侧没有这种区分,用户按键(KEY0~KEY3)就是普通 IO,接不接进复位逻辑完全由设计者决定。我一般会在顶层给按键加个简单的同步器和消抖,直接当异步复位用经常会在释放沿上出 metastability。
3.3 外设归属:地址空间决定一切
板子上的外设不是随便挂的,FPGA 侧和 HPS 侧的划分背后是总线结构和引脚复用的硬约束。
| 外设 | 挂载侧 | 接口与总线 | 由谁驱动 |
|---|---|---|---|
| 64MB SDRAM | FPGA | 16 位数据总线,Avalon-MM | 用户逻辑或 Nios II |
| 1GB DDR3 | HPS | 32 位数据总线,硬核内存控制器 | Linux |
| VGA DAC(8 位三路) | FPGA | 并行像素数据线 | 用户逻辑 |
| 24-bit 音频 CODEC | FPGA | 串行音频接口 | 用户逻辑 |
| TV 解码器 | FPGA | 视频输入总线 | 用户逻辑 |
| PS/2、IR 收发 | FPGA | 自定义 IO | 用户逻辑 |
| ADC(8 通道、12 位、500KSPS) | FPGA | SPI | 用户逻辑 |
| 千兆以太网 | HPS | 硬核 MAC + 外部 PHY | Linux |
| USB 2.0 Host ×2 | HPS | ULPI | Linux |
| Micro SD | HPS | SDMMC | Linux |
| G-sensor | HPS | I2C + 中断 | Linux |
| 两个 40pin 扩展口 | FPGA | 直连 IO bank | 用户自定义 |
划分逻辑很清楚:高速、协议复杂、需要成熟驱动的接口全部交给 HPS 的硬核控制器,DDR3、千兆以太网、USB、SD 卡这些在 Linux 下有现成驱动,开箱能用。需要灵活重定义、或者对时序有特殊要求的接口留给 FPGA,VGA、音频、ADC、扩展口都属于这一类,代价是每次改逻辑都得重新烧配置。
从嵌入式硬件的角度看,这个划分还照顾了信号完整性。DDR3 的走线要等长、阻抗匹配,必须尽量贴近 HPS 的专用 IO bank,走线越短越好。而 VGA 和音频这类低速接口,走线容差大得多,放到 FPGA 侧随便布。
4. HPS 看见 FPGA:轻量级桥、System Builder 与一次 mmap 的完整路径
4.1 三条桥:HPS 和 FPGA 之间的通道
Cyclone V 把两个世界的连接做成了三条独立的桥。
HPS-to-FPGA 桥是 AXI 主接口转 Avalon-MM,方向是 HPS 访问 FPGA 侧外设。它的带宽高,适合大数据量传输,比如 HPS 往 FPGA 侧的 FIFO 里灌视频数据。
FPGA-to-HPS 桥方向反过来,FPGA 逻辑可以主动读写 HPS 的 DDR 或者外设。做硬件加速时这条桥很关键,FPGA 算完的结果直接写进 Linux 能看到的内存,省一次拷贝。
FPGA-to-SDRAM 桥专门给 FPGA 侧那 64MB SDRAM 用,绕过 HPS 直接访问。
还有一条轻量级桥(Lightweight HPS-to-FPGA),32 位宽,带宽不高,专门给 PIO 和状态寄存器这类小数据量访问用。它的存在是为了不占主桥的带宽——HPS 每隔几毫秒读一次 LED 状态,走主桥纯属浪费。地址上,HPS 的内存映射里有一段固定窗口留给 FPGA 侧外设,轻量级桥的基址在 Cyclone V 上是固定的,后面各个 IP 的偏移由 Platform Designer 在系统生成时分配。
4.2 System Builder 到底生成了什么
手册第 4 章介绍的 DE1-SoC System Builder 是个配置生成工具。输入是板级选项——DDR3 的时序参数、引脚复用选择、外设使能,输出是一整套东西:Platform Designer 系统文件、顶层 HDL、Preloader 源码模板、设备树源码。
为什么必须用它?因为 Cyclone V HPS 的配置项多到离谱。DDR3 控制器光时序参数就有几十个(tRCD、tRP、tWR、刷新周期、ODT 设置……),引脚复用有上百个可选功能。手写这些配置不现实,而且错一个参数就是启动不了,还看不出错在哪。
System Builder 生成的东西里,最容易被忽略的是设备树。Linux 内核靠设备树知道“哪个地址上有哪个外设”,如果设备树和实际比特流对不上,驱动加载了也访问不到正确的寄存器。改完 FPGA 侧逻辑重新生成系统之后,设备树必须跟着更新,dtb 也要重新编译。
4.3 一次完整的 HPS 写 LED 链路
手册 7.1 节的示例叫 HPS Control LED and HEX,看起来简单,实际把整条链路都串起来了。从用户态一行代码到引脚电平,中间要穿过六层。
/* Linux 用户态直接访问 FPGA 侧 PIO:mmap 物理地址后按 32 位读写 */ #include <stdio.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> /* 轻量级 HPS-to-FPGA 桥的基址在 Cyclone V 上是固定的 */ #define LW_BRIDGE_BASE 0xFF200000UL /* 具体偏移由 System Builder 在 Platform Designer 里分配 */ #define LED_PIO_OFFSET 0x00000010UL int main(void) { int fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd < 0) { perror("open /dev/mem"); return 1; } volatile unsigned int *led_pio = (unsigned int *)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, LW_BRIDGE_BASE + LED_PIO_OFFSET); if (led_pio == MAP_FAILED) { perror("mmap"); return 1; } for (int i = 0; i < 10; i++) { *led_pio = 1u << i; /* 逐位点亮 10 个用户 LED */ usleep(200000); } munmap((void *)led_pio, 0x1000); close(fd); return 0; }O_SYNC是必须的,不加的话 write 可能停在 CPU 缓存里不落到总线上,读回来是旧值。volatile告诉编译器别优化掉这次访问——这个地址背后是硬件,不是普通内存。mmap的长度参数必须是页大小的整数倍,0x1000 是 4KB,够覆盖一个 PIO 的寄存器空间。
整条链路的顺序是:用户态写指针 → CPU 发出对虚拟地址的写 → MMU 翻译成物理地址 0xFF200000 + 偏移 → 该地址落在轻量级桥窗口内 → AXI 事务被路由到 FPGA 侧 → FPGA 里的 PIO IP 的 Avalon-MM 从接口接住 → PIO 输出寄存器更新 → 引脚电平变化 → LED 亮。
任何一个环节断掉,现象都是“LED 不亮”,但原因可能完全不同。所以排查时要从后往前:先在 Platform Designer 里确认 PIO 的偏移,再看设备树里的地址,最后才怀疑代码。
5. 避坑与排查:手册不会写在你面前的那些翻车点
5.1 配置与启动相关的坑
现象:Programmer 显示烧写成功,进度条走完,提示 100%。断电重启之后,FPGA 逻辑完全没反应,LED 不闪,逻辑分析仪上也没波形。
原因:MSEL 还停在 JTAG 模式。烧写成功只能说明数据进了 EPCS,但 FPGA 上电后如果采样到 JTAG 模式,它根本不会去读 EPCS,而是乖乖等着 JTAG 灌数据。等不到就一直是空白。
解决:把 MSEL 调到 AS 模式,重新上电。这一步做完之后建议先别急着改逻辑,烧一次 JIC、断一次电、看一次现象,确认固化路径真的通了,再往下做。
现象:串口终端一片空白,或者内核打印停在 “Waiting for root device”。
原因:Preloader 里的 UART 引脚复用和板子实际接线对不上,或者 SD 卡分区偏移和 Preloader 里写死的不一致。DE1-SoC 的 HPS 启动链是 Boot ROM → Preloader → U-Boot → Linux,前一级找不到下一级就停在那里,串口上什么都不出。
解决:用 System Builder 重新生成 Preloader,核对串口波特率和引脚复用;用分区工具确认 SPL 存放的偏移和镜像里的一致。血泪经验是:改完任何和启动相关的配置,先烧最低限度能打印一行字的镜像验证通路,别一上来就烧完整系统。
5.2 总线与地址映射相关的坑
现象:C 程序第一次往指针写数据就收到 SIGBUS,dmesg 里有 unhandled page fault。
原因:访问的物理地址不在轻量级桥窗口内,或者比特流里压根没加桥和 PIO。MMU 翻译成功不代表总线上有设备响应,没有从设备应答的访问会变成总线错误。
解决:从 System Builder 生成的 .sopcinfo 或者设备树里读出实际分配的偏移,改代码里的LED_PIO_OFFSET。别抄别人的偏移值,每个工程的分配都可能不同。
现象:网口有链路灯,但ifconfig看不到 eth0,或者能起来但收不到包。
原因:System Builder 里 PHY 的 MDIO 地址或者引脚复用选错,生成的设备树和实际硬件对不上。这种错很难从现象上判断,因为 PHY 自己上电后会亮灯,看起来一切正常。
解决:对比设备树里 ethernet 节点的 phy-handle 和手册 3.7.2 节的引脚定义,重新生成 dtb。有条件的话用示波器看一眼 MDC/MDIO 上有没有波形,没波形就是控制器根本没使能。
5.3 时序与存储相关的坑
现象:跑 SDRAM Test in Verilog 时,大部分地址读写正确,个别单元读回来全是 0 或者全是 1。
原因:PLL 的相位偏移没覆盖住 dq 的建立保持窗口,或者刷新周期配置偏大导致个别行漏刷新。这种错是概率性的,跑一遍可能过,跑十遍错一次。
解决:调 PLL 相位,从 -75° 往 -60° 和 -90° 两个方向试,同时确认 SDRAM 的时序参数和器件手册一致。测试程序要跑随机地址、连续地址、跨行地址三种模式,只跑顺序读写会漏掉很多问题。
6. 设计取舍:这块板子为什么宁可难用一点也不肯牺牲灵活
如果让我评价 DE1-SoC 的设计取向,一句话:它把“可重配置”看得比“开箱即用”重得多。
最明显的是内存的不对称分配。FPGA 侧只有 64MB SDRAM、16 位总线,HPS 侧却是 1GB DDR3、32 位。这个组合乍看很奇怪,为什么不让两边共享大内存?因为 FPGA 侧的逻辑通常不需要大容量,它要的是确定性和低延迟——每拍都能预测的访问周期比容量重要得多。而 HPS
本文还有配套的精品资源,点击获取