选用 Basys3 FPGA开发板进行 RISC-V 嵌入式开发是一个具有挑战性但也非常有教育意义的过程。Basys3 的核心是一颗 Xilinx Artix-7 XC7A35T-1CPG236C FPGA,资源相对有限(约 33k 逻辑单元),因此你需要精心设计或选择一个轻量级的 RISC-V 软核。
以下是需要完成的主要工作和步骤:
? 核心工作流程
选择或设计 RISC-V 软核处理器:
- 关键决策: Basys3的资源限制了你的选择。你需要一个非常精简(RV32I/E MCU 级别)的软核。
- 推荐选择:
- PicoRV32: 极其精简(~1k LUTs),支持 RV32I/E,速度适中(通常可达 50-100MHz 在 Artix-7 上),非常适合 Basys3。是最主流和最推荐的选择。
- VexRiscv: 高度可配置,你可以配置一个非常小的版本(接近 PicoRV32 的大小)。优点是后期扩展性好(如添加乘法器、中断等),但初始配置稍复杂。
- NEORV32: 设计精良,功能相对丰富(RV32I/E[C]M[U] Zicsr Zifencei),但需要更多资源(通常 > 5k LUTs)。在 Basys3 上勉强可行(资源占用率会很高,留给应用的空间小),需仔细裁剪。如果你需要比 PicoRV32 稍多的功能(如硬件乘除、中断、定时器、UART 集成),可以考虑。
- 自己设计: 仅推荐用于深入学习的场景。从最基础的 RV32I 核心开始。
- 考虑因素: 指令集支持(RV32I/E, 是否需要乘除扩展等)、中断处理能力、外设集成度(如是否内置 UART、Timer 等)、性能需求、资源占用。
设置开发环境:
- FPGA 工具链: Xilinx Vivado HLx Design Suite (WebPACK 免费版)。这是必备的,用于综合、布局布线、生成比特流文件并烧录到 Basys3。
- RISC-V 软件工具链:
- 编译器/汇编器/链接器:
riscv-gnu-toolchain (支持 Newlib 或 GCC)。
- 调试器:
riscv-openocd (配合 GDB)。
- 仿真工具 (可选但强烈推荐): Verilator (开源, 命令行), Icarus Verilog (开源), 或 Vivado 自带的 XSim (Vivado simulator)。
- 文本编辑器/IDE: VS Code (推荐, 配合 RISC-V 插件), Vivado SDK/Vitis (较重量级), 或其他你熟悉的编辑器。
集成软核与存储器系统 (硬件设计):
- 存储器:
- 指令存储器 (ROM/IMEM): 通常用 FPGA 内部的 Block RAM (BRAM) 实现,存放固件程序。需要设计一个初始化机制(通过比特流或外部加载)。
- 数据存储器 (RAM/DMEM): 同样主要用 BRAM。大小需要根据软核需求和可用资源设定(Basys3 总 BRAM 约 1.8Mb)。需要定义好地址空间映射。
- 存储器控制器: 软核通过总线(如 AXI4-Lite, Wishbone, 或自定义总线)访问 BRAM。软核通常自带总线接口模块或需要你实现。
- 外设接口:
- 必备外设:
- UART (串口): 用于调试信息输出和基本输入。Basys3 上有 USB-UART 桥接芯片连接到 FPGA。
- GPIO: 控制板载 LED 和开关,以及 PMOD 接口。
- 可能需要的其他外设:
- 系统定时器: 用于产生中断或延时。
- 中断控制器 (PLIC): 如果软核支持中断且你计划使用多个中断源。
- 实现: 需要编写或集成这些外设的 HDL 代码(Verilog/VHDL),并连接到软核的总线上。开源项目通常提供这些外设模块。定义清晰的外设寄存器映射。
- 时钟与复位: 使用 Basys3 的 100MHz 主时钟,可能需要分频以适配软核速度(或软核内部处理)。实现可靠的复位电路(按键复位)。
硬件设计 - 集成与实现(在 Vivado 中):
- 创建 Vivado 项目,选择 Basys3 FPGA 型号。
- 添加所有 HDL 源文件(软核、存储器控制器、外设、顶层设计)。
- 编写顶层模块:实例化 FPGA 芯片、软核系统、时钟管理、复位逻辑、I/O 引脚约束。
- 编写约束文件 (.xdc): 极其关键! 指定时钟引脚、复位引脚、所有 GPIO 引脚(LEDs, Switches, Buttons, UART TX/RX, PMODs 等)的物理位置(FPGA Pin Number)和电气属性(I/O Standard, 通常是 LVCMOS33)。Vivado 示例项目或 Basys3 手册提供基础约束。
- 综合、实现(布局布线)。
- 生成比特流文件 (.bit)。
编写和编译软件 (固件):
- 启动代码 (Startup Code / Crt0): 汇编编写,设置初始栈指针、清零
.bss 段、复制 .data 段到 RAM、调用 main 函数。
- 链接器脚本 (.ld): 非常重要! 定义内存布局(ROM/RAM 的起始地址和大小)、代码段
.text、数据段 .data/.bss、堆栈 .stack 的存放位置。必须与你硬件设计的地址映射完全一致。
- C 程序: 主应用程序代码。使用标准库(Newlib)函数(如
printf 最终会通过 _write syscall 指向你的 UART 驱动)。
- 设备驱动: 实现访问硬件外设寄存器的函数(通常是内存映射 IO)。例如 UART 发送/接收字符的函数、GPIO 读写函数。
- 编译: 使用
riscv-none-elf-gcc (或类似前缀) 交叉编译工具链编译 C/汇编代码。
- 链接: 使用
riscv-none-elf-ld (或 gcc 链接) 配合你的链接器脚本生成 ELF 文件。
- 生成固件映像: 使用
riscv-none-elf-objcopy 将 ELF 文件转换为可供存储器初始化使用的格式(通常是二进制 .bin 或 Intel Hex .hex)。
将固件加载到硬件:
- 方法 1: 比特流初始化 BRAM (一次性):
- 在 Vivado 中,将固件
.bin 或 .hex 文件作为 BRAM 模块(通常是 blk_mem_gen IP)的初始化文件。
- 重新生成比特流。烧录此比特流时,固件自动加载到 BRAM 中。
- 优点: 启动最快。
- 缺点: 修改固件需要重新综合整个 FPGA 设计,速度慢。
- 方法 2: 通过 UART/外部加载器 (推荐用于开发):
- 编写一个小的引导加载程序放在初始化的 BRAM 中。
- 引导加载程序上电后,通过 UART 或其他接口(如 SPI Flash)接收新的应用程序固件映像,并将其写入主程序存储区域(通常是另一块 BRAM)。
- 然后跳转到新固件执行。
- 需要一个运行在 PC 上的工具(如
litex_term 或自定义 Python 脚本)通过串口发送固件文件。
- 优点: 更新固件无需重新综合 FPGA,开发调试效率高。
- 缺点: 需要额外编写引导加载程序,启动稍慢(需加载时间)。
- 方法 3: 外部 SPI Flash (Persistent):
- 利用 Basys3 板载的 16Mb SPI Flash 存储比特流和固件。
- 配置 FPGA 从 SPI Flash 加载比特流(包含初始化的软核系统)。
- 软核启动后,软核上的固件再从 SPI Flash 的特定地址读取应用程序代码到 RAM 中执行(也需要引导加载程序)。
- 优点: 断电后固件保留。
- 缺点: 实现相对复杂,需要 SPI Flash 控制器驱动。
烧录 FPGA 配置:
- 使用 Vivado Hardware Manager。
- 将 Basys3 通过 USB 线连接到电脑(USB-JTAG 接口)。
- 打开 Hardware Manager,扫描硬件,找到 Basys3。
- 将生成的
.bit 文件烧录到 FPGA 中。配置是易失性的,断电即丢失。
- (可选)烧录到 SPI Flash: 在 Hardware Manager 中,将
.bit 文件(有时需要转换为 .mcs 或 .bin)编程到板载 SPI Flash 永久存储。设置开发板从 SPI Flash 启动(JP1 跳线)。
调试:
- 串口调试: 最基础有效。在 PC 上使用串口终端工具(PuTTY, Tera Term,
screen, minicom)连接 Basys3 的 USB-UART 端口(查看设备管理器/lsusb 确定 COM 端口/Linux dev)。在程序中通过 UART 输出打印信息和变量值。
- LED/开关: 最简单直接的调试方式,点亮 LED 指示状态变化或错误代码。
- 逻辑分析仪(Vivado ILA):
- 在 Vivado 设计中插入 ILA (Integrated Logic Analyzer) IP 核。
- 连接到你想观测的内部信号(如 CPU 的关键控制信号、总线信号、外设寄存器读写)。
- 综合实现生成带 ILA 的比特流并烧录。
- 在 Vivado Hardware Manager 中设置触发条件并捕获波形。这是调试复杂硬件时序问题的利器。
- 软件调试器 (GDB + OpenOCD):
- 需要一个 JTAG 调试器(幸运的是,Basys3 的 USB-JTAG 接口可以直接用于调试 FPGA 逻辑!)。
- 在 FPGA 设计中实例化 JTAG TAP 控制器(如 Xilinx BSCANE2)并将其连接到软核的调试接口(如果软核支持,如 PicoRV32 需要额外添加调试模块)。
- 运行
openocd (配置好 Xilinx BSCAN_SPI 或类似接口)。
- 使用
riscv-none-elf-gdb 加载 ELF 文件,连接到 openocd。
- 可以设置断点、单步执行、查看寄存器/内存变量。这是调试软件最强大的方式。
? 总结关键步骤
- 选软核: PicoRV32(首选)、VexRiscv(精简配置)、NEORV32(小心资源)。
- 搭环境: Vivado, RISC-V GCC Toolchain。
- 建系统: HDL 编码(软核+BRAM 内存+外设如 UART/GPIO+总线互联+时钟复位)+ 顶层模块 + XDC 约束。
- 写软件: 启动代码(汇编) + 链接脚本(匹配硬件地址) + C 应用代码 + 驱动 + 编译链接。
- 做集成: 将编译好的固件集成到比特流(初始化 BRAM)或实现引导加载程序(UART/SPI Flash)。
- 烧录跑: Vivado 烧录 FPGA (.bit) 或可选烧录 SPI Flash (.mcs)。
- 调问题: UART 打印、LED 指示、Vivado ILA 抓波形、GDB+OpenOCD 调试。
⚠️ 重要提示
- 资源限制: Basys3 的 Artix-7 35T 是入门级 FPGA。你的 RISC-V 系统(软核 + 存储器 + 外设)必须非常精简。目标是一个功能有限但完整的微控制器系统。
- 约束文件: .xdc 文件错误会导致设计无法正确工作(引脚不对、电平不对)。仔细检查!参考官方约束模板。
- 链接器脚本: .ld 文件错误会导致程序跑飞或数据错乱。务必确保地址映射与硬件设计一致。
- 调试耐心: 硬件软件协同调试充满挑战。善用串口打印和 ILA 波形调试硬件问题。GDB 调试软件逻辑。从小测试程序开始(如点亮 LED)。
? 学习资源
- Digilent Basys3 官方资源: 参考手册、原理图、约束文件示例。
- 所选 RISC-V 软核的文档和代码仓库: (PicoRV32, VexRiscv, NEORV32 官网/GitHub)。
- Xilinx Vivado 教程: Digilent 和 Xilinx 官网有很多基础教程。
- RISC-V 工具链文档: riscv-gnu-toolchain, openocd 文档。
- 社区和论坛: Xilinx 论坛、Reddit /r/FPGA、GitHub Issues 等。
这个过程虽然复杂,但完成后你会对计算机底层架构(CPU、内存、总线、外设)、RISC-V ISA、FPGA 开发、嵌入式系统软硬件协同设计有极其深入的理解!祝你成功!?
|
|
|
2025-11-10 17:12:55
评论
举报
|
|
|
|