简介:面向SmartFusion2初学者的完整开发例程源码包,围绕Microsemi M2S010芯片中可编程逻辑与ARM微控制器并存的复合架构,提供首个可编译、可下载验证的工程模板,适合嵌入式工程师与FPGA学习者快速上手片上系统开发。压缩包共含46个文件,核心代码包括21个头文件、10个C源文件,另有5个链接脚本、3个汇编启动文件及Libero工程相关文件,配合说明文档可完整还原开发环境,理清启动代码、堆栈指针与中断向量的初始化流程。包体约10.2MB,已有920人浏览学习。工程目录按固件、驱动与硬件抽象层等模块划分,结构清晰;例程覆盖时钟配置、通用输入输出控制、FPGA比特流加载等基础操作,并用LED闪烁、串口通信示例展示微控制器与FPGA协同工作的完整链路。整套代码可作为二次开发的项目骨架,帮助读者在物联网、工业控制或安全加密等场景中快速展开后续设计。
1. 从 FirstProj 认识 M2S010:SmartFusion2 例程源码的入口价值
SmartFusion2开发例程源码里最容易被低估的一套,就是配套 M2S010-MKR-KIT 板卡的 FirstProj。它不是什么炫技工程,而是一个把硬核 Cortex-M3 与 FPGA 逻辑阵列同时拉起来的最小闭环:上电、时钟初始化、位流加载、串口打印、点亮一颗 LED。对于第一次拿到 MKR-KIT 板子的人,跑通这个工程等于一次性验证了 Libero 环境、FlashPro 连接、启动介质和时钟树四条链路。它适合正在评估 SmartFusion2 器件、或者准备在这块板子上做软硬件协同开发的工程师。这篇笔记我按“上电顺序 → 工具链操作 → 翻车点”拆开讲,新手能照步骤走,熟手能直接对参数。
2. 上电后谁先醒:M2S010 的启动顺序与时钟树
2.1 系统控制器、Cortex-M3 与 FPGA Fabric 的关系
很多从普通 FPGA 转过来的人会默认“上电先加载位流,然后再跑逻辑”,但 M2S010 不是这么工作的。SmartFusion2 是一颗真正的 SoC FPGA:一边是硬核 ARM Cortex-M3 和整套 MSS 外设(UART、SPI、I2C、DMA、GPIO),另一边是传统的 FPGA 逻辑阵列(fabric),两者通过 AXI 总线相连。这意味着上电那一刻,最先跑起来的既不是你的 C 代码,也不是 fabric 里的逻辑,而是芯片内部一颗独立的系统控制器(System Controller)。
这颗系统控制器负责读启动源,从 eNVM、外部 SPI Flash 或 SD 卡中取出系统镜像,做校验,配置 MSS 的静态参数,等整颗芯片的状态稳定后才释放 Cortex-M3 的复位。所以你在 main() 里看到的初始化代码,其实是第二层初始化:PLL 是否锁定、总线和外设时钟是否就绪、fabric 位流要不要由应用层再次加载,这些才是 FirstProj 真正演示的东西。看不懂这个顺序,后面改板子一定会踩坑。
这种设计的好处是启动行为可控且安全。坏处是它不像普通 MCU 那样“上电就进 main”,也不像普通 FPGA 那样“上电就加载 bitstream”。Fabric 的位流可以封在系统镜像里由系统控制器一次性加载,也可以由 M3 启动后的驱动代码从外部存储搬进 fabric,两种方式在 FirstProj 里都有可能出现。判断方式很简单:在源码里搜 fpga_config、fpga_upload 这类关键字,看它是在系统镜像阶段生效还是在 main 函数里被调用。
2.2 时钟树解剖:REF_CLK、MSS_CLK 与 FAB_CLK 谁先稳定
时钟配置是 SmartFusion2 例程里最像“黑匣子”的部分。FirstProj 之所以值得跑,就是因为它把一套能工作的时钟参数直接摆在你面前。典型配置里存在三个主要时钟域:
| 时钟信号 | 常见来源 | 典型频率 | 负责对象 |
|---|---|---|---|
| REF_CLK | 板上晶振或内部 RC 振荡器 | 以板卡丝印为准,常见 24/50MHz | PLL 参考输入 |
| MSS_CLK | PLL 输出 | 100–166MHz | Cortex-M3、AHB/APB 总线、UART 等外设 |
| FAB_CLK | PLL/DLL 再分频 | 几十 MHz 到百 MHz 级 | FPGA fabric 内同步逻辑与 AXI 桥 |
启动顺序上,系统控制器先让内部振荡器工作,随后再根据配置切换到外部 REF_CLK 并启动 PLL。只有 MSS_CLK 稳定后,Cortex-M3 才能真正跑起来;FAB_CLK 则可以晚于 MSS_CLK,因为它依赖 fabric 完成配置。这个先后关系直接决定了你排查问题的方向:如果串口有输出但 fabric 里的逻辑不动,问题大概率不在时钟而在位流加载;如果串口乱码或完全无输出,那就要先怀疑时钟树的 PLL 分频参数和外设时钟使能。
我拿到任何 SmartFusion2 例程源码的第一件事,就是把时钟配置页面截图,然后和板卡原理图上的晶振丝印对照。例程里的时钟参数是“答案”,但只对原板卡有效。把 FirstProj 迁到自绘板上时,REF_CLK 的频点一变,整个 MSS_CLK、UART 波特率、FAB_CLK 全部跟着失效。这个动作我后面每块新板子都会强制重做一遍。
2.3 FirstProj 源码包里装着什么:Libero 工程与 SoftConsole 工程的协作
打开 M2S010-MKR-KIT 的 FirstProj 源码包,看到的通常不是单个工程,而是两套工具链的产物。第一套是 Libero SoC,负责 M2S010 的 MSS 配置、IO 分配、fabric 逻辑设计、综合布局布线和位流生成;第二套是 SoftConsole 或等效的 GNU ARM 工具链,负责 Cortex-M3 侧 C 代码的编译,产出 .elf 可执行文件。最后 Libero 会把 .elf 和 FPGA 位流合进同一个系统镜像,烧进 eNVM 或 SPI Flash。
| 源码包中的常见内容 | 归属工具链 | 作用 |
|---|---|---|
| Libero 工程文件(.prjx 等) | Libero SoC | MSS 配置、fabric 逻辑、约束、位流生成 |
| main.c、system_m2s010.c、mss_uart/mss_gpio 驱动 | SoftConsole/GNU ARM | M3 侧初始化与业务代码 |
| 时序约束与物理约束(.sdc、.pdc) | Libero SoC | 定义时钟周期、管脚分配 |
| 链接脚本与启动汇编 | SoftConsole/GNU ARM | 决定 M3 代码在存储器的布局 |
这个结构是第一轮接触 SmartFusion2 的人最容易懵的地方。不少人以为在 Libero 里写完 Verilog 就能上板,实际上 M3 的 C 代码必须用独立的 ARM 工具链编译,再由 Libero 打包。FirstProj 的价值在于它把这条双工具链流水线完整打通了,你不需要自己摸索集成流程,只要照着源码包里的脚本和说明走一遍,就能看到 .elf 和位流是怎么合体的。这也是我建议把它当基线工程而不只是“参考代码”的原因。
3. 在 Libero SoC 里把 FirstProj 跑起来:从建工程到 FlashPro 烧写
3.1 打开工程前先对齐四样东西
不要在拿到源码包后直接双击工程文件。我先做四步确认,能避免后面所有匪夷所思的报错。
第一,Libero SoC 版本。不同大版本之间工程文件不保证互开,如果 Open Project 报“工程由更高版本创建”,别硬开。第二,器件型号。New Project 向导里 Family 必须选 SmartFusion2,Device 选 M2S010,封装和速度等级以你板卡丝印为准,同一颗 M2S010 有不同封装,选错了后续引脚约束全部错位。第三,FlashPro 烧写器型号。Libero Programmer 能识别到的目标链由实际连接的烧写器决定,MKR-KIT 如果板载了调试通道就不需要外接。第四,启动介质。看例程文档或烧写配置里选的是 eNVM 还是 SPI Flash,这决定镜像烧进去之后能不能正常引导。
如果例程工程实在打不开,我的一般做法是新建空工程,然后把源码包里的 HDL、MSS 配置和约束文件逐个加进来。这个过程多花二十分钟,但比在错误的工程文件上反复尝试要可靠得多。记住:源码包里的 .prjx 是给别人看的答案,不是对你当前环境必然有效的承诺。
3.2 从源码到烧写文件的四个关键步骤
编译流程看起来和普通 FPGA 差不多,但 SmartFusion2 多了一个心思:最终生成的编程文件里同时包含 MSS 配置、FPGA 位流和 M3 程序三段内容。Libero 的图形界面把这条链拆成了 Synthesis、Place & Route、Generate FPGA Array Bitstream、Generate Programming Data 几个节点,每个节点都可以单独右键执行。
# FirstProj 批量编译骨架,适合在 Libero Tcl Console 里逐行执行 open_project ./FirstProj.prjx run_tool -name {SYNTHESIZE} run_tool -name {PLACEROUTE} run_tool -name {GENERATE_PROGRAMMING_DATA} export_programming_file -input ./programmer/output_file.stp这段脚本的逻辑是按工程依赖顺序依次触发工具。SYNTHESIZE 对应综合,把 Verilog/VHDL 变成网表;PLACEROUTE 对应布局布线,确定逻辑在 fabric 里的物理位置并跑时序;GENERATE_PROGRAMMING_DATA 是关键,它会同时处理 MSS 配置和 fabric 位流,生成可烧写的镜像;最后 export 出 .stp 编程文件。不同小版本的 Libero 对工具节点的命名可能略有差异,如果脚本里某个名字不识别,就回到 GUI 里对着工程树右键菜单确认一遍名称再改脚本。
这里要特别说明参数含义:.stp 是 Microsemi 系列烧写器通用的编程文件格式,烧写时 Programmer 会解析里面的安全头、地址段和位流段。如果你只改了 C 代码没改 FPGA 逻辑,理论上可以只重新生成启动镜像而跳过综合布局布线,但 FirstProj 阶段我不建议做这种优化,全流程跑一遍能暴露更多问题。
3.3 烧写与首次上电验证路径
用 FlashPro 连接板卡 JTAG 链,给板子独立供电,在 Libero Programmer 里加载 .stp 后点 Program。烧写成功后别急着欢呼,先做三个验证。
第一,串口工具参数不要乱猜。打开 main.c 看 mss_uart_init 的第一个参数,波特率可能不是默认的 115200,可能是 57600 或者别的值,按代码里的实参设置串口工具。数据位 8、无校验、1 停止位、无流控是多数例程的默认组合。第二,观察 LED 行为。FirstProj 通常会让某个 GPIO 控制的 LED 按固定节奏闪烁,如果 LED 动了说明 M3 代码已经跑起来。第三,看串口打印内容是否完整。打印内容一般会包含版本信息或初始化完成标志,看到它才说明时钟、总线和外设初始化全部通过。
如果源码包里附带了预编译的烧写文件,最快路径是先烧这个现成镜像,跳过 Libero 全流程。它能确认你的烧写链路和硬件连接没有问题,之后再改源码重新编译,出问题时才容易定位。
4. 避坑:M2S010 例程跑不通时我按什么顺序排查
4.1 串口无输出,终端完全黑屏
现象:烧写报告成功,复位后串口工具里一个字符都收不到。
原因有两种最常见的可能。第一种,UART 外设时钟没使能,或者 PLL 分频参数与 UART 波特率参数不匹配,总线时钟频率不是驱动预期值,初始化函数可能直接跳过了波特率配置。第二种,接线问题,USB 转串口模块接到了 RS232 电平口,或者 TX/RX 接反、没共地。
解决:先拿万用表确认板卡 UART 引脚和 USB 转串口模块都是 3.3V TTL 电平,然后打开时钟配置确认 MSS_UART 的时钟源和分频系数,最后对照 mss_uart_init 的实参设置串口工具。我的习惯是遇到黑屏先看时钟再看接线,因为电平接反时偶尔还能收到乱码,黑屏反而更像是时钟没起来。
4.2 Fabric 里的 LED 不亮,但 M3 串口打印正常
现象:串口有输出,main 函数明显在运行,但 FPGA 逻辑阵列里那些由 Verilog 驱动的 LED 或计数器完全不动。
原因:位流没有被加载进 fabric。SmartFusion2 的位流加载不完全依赖系统控制器,可以由图 2.1 里提到的应用层代码触发。如果 FirstProj 里这段加载逻辑被条件判断跳过,或加载完成后没有释放 fabric 复位,fabric 就一直处于空白状态。
解决:在源码里搜索 fpga_config、fpga_upload、fabric_init 这类关键字,确认它确实被调用,并且检查加载完成标志位有没有被等待。硬件上,如果板子引出了 fabric 的 DONE 信号,可以用示波器确认配置完成后引脚是否拉高。这个坑最迷惑人的地方在于:M3 本身跑得好好的,让人下意识觉得整颗芯片都没问题。
4.3 在 MKR-KIT 上正常,迁到自绘板后串口乱码、LED 时序全偏
现象:同一份 FirstProj 源码,换了板卡之后串口输出乱码,LED 闪烁频率明显不对。
原因:REF_CLK 频点变了。例程里的 PLL 参数是基于原板晶振算好的,自绘板换了一颗不同频率的晶振后,MSS_CLK 和 UART 波特率全部偏离预期。这不是源码 bug,是配置没跟着硬件走。
解决:在 Libero 的时钟配置里把参考时钟频率改成新板卡实际晶振值,重新生成 MSS 配置,再同步检查 UART 波特率参数。不要试图用串口工具的“特殊波特率”去迁就,根因在时钟树,迁就只会掩盖问题。从那以后我每次换板子第一件事就是看晶振丝印,再决定要不要改时钟配置。
4.4 Libero 打开例程工程报器件不匹配或版本不兼容
现象:Open Project 报错,提示当前 Libero 版本太旧,或者工程内器件封装与当前安装的器件库不匹配。
原因:源码包是从某个特定版本的 Libero 里导出的,工程文件记录了当时的器件型号、封装和工具版本。你本机环境不完全一致时,工程文件不一定能正确加载。
解决:不跟工程文件较劲。新建一个 SmartFusion2、M2S010 的空工程,把它识别的封装选成板卡实物一致,然后把源码包里的 HDL 源码、MSS 配置 XML、约束文件手动添加进去,重新走一遍综合布局布线。这样虽然多花几分钟,但能确保所有配置都在当前版本工具里重新生成过。
4.5 FlashPro 烧写报 JTAG ID mismatch
现象:Programmer 找不到目标器件,报 JTAG ID mismatch 或无法扫描到链上的 M2S010。
原因:最常见是板卡没有独立供电,调试器虽然能通过 JTAG 口给一小部分电路供电,但 M2S010 整颗芯片的工作电流撑不住。其次是 FlashPro 线序接反,或者板上复位按键被按住,芯片一直处于复位状态,扫描不到 ID。
解决:先给板卡接上独立电源,再插 FlashPro。确认线序时以板卡丝印标注为准,不要想当然。如果板上有多个 JTAG 器件,检查目标器件是不是被配置成 bypass。这个坑通常和代码无关,排错顺序放在硬件检查的第一步。
5. 从 FirstProj 改成自己的最小系统:三个改动点和一个验证习惯
FirstProj 跑通之后,最值得做的改造是把它改成自己的“最小系统”:换一颗 LED、改一个波特率、加一段自定义 UART 打印。这里有一套我常用的三板斧。
第一板斧,在 SoftConsole 里找到 main.c,用 MSS 驱动替换例程自带的外设操作。MSS 的 GPIO 和 UART 驱动是 Microsemi 官方 HAL 的一部分,函数命名相对固定,改动点非常集中。第二板斧,调整波特率参数。注意 mss_uart_init 的第二个参数是波特率,后面的参数是位掩码组合,改完波特率要重新编译 .elf,再重新走一遍 Libero 打包流程。第三板斧,把 fabric 里原来的演示逻辑替换成你自己的模块,但暂时保留 AXI 桥的握手逻辑,先让 M3 能访问到你的自定义寄存器,再扩展功能。
#include "mss_uart.h" #include "mss_gpio.h" /* 以 mss 驱动为准,API 名称以本工程头文件声明为准 */ int main(void) { uint8_t msg[] = "\r\nM2S010 FirstProj OK\r\n"; /* 参数1:外设实例;参数2:波特率;参数3:数据位/校验/停止位组合 */ mss_uart_init(MSS_UART0, 115200, MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY | MSS_UART_ONE_STOP_BIT); /* GPIO 初始化为输出模式,驱动板载 LED */ mss_gpio_init(MSS_GPIO_0); mss_gpio_config(MSS_GPIO_0, MSS_GPIO_OUTPUT_MODE); mss_uart_polled_tx_string(MSS_UART0, msg); while (1) { mss_gpio_set_output(MSS_GPIO_0, 1); delay(); mss_gpio_set_output(MSS_GPIO_0, 0); delay(); } }这段代码的逻辑很直白:先初始化串口并打印一条确认信息,再配置 GPIO 输出模式并循环翻转,驱动一颗 LED。关键参数在 mss_uart_init 那一行,波特率必须和时钟配置算出来的总线时钟匹配,这也是图 2.2 时钟树知识点的落地验证。GPIO 的实例号 MSS_GPIO_0 对应 Libero IO Editor 里分配的具体管脚,如果你的板子 LED 不在那个管脚上,改这里就行,不用动 Verilog。delay 函数用软件循环还是定时器取决于例程自带实现,初期用软件循环足够。
每次改完例程,我都会强制自己走一遍验证清单:先查 Memory Map 确认 eNVM 分区足够容纳新镜像,再编译 .elf,然后进 Libero 重新生成编程文件,最后烧写并盯住串口输出。有一年我把 eNVM 镜像分区调小想省容量,结果位流放不进去,烧到一半 Programmer 直接报错,从那以后我再也没跳过 Memory Map 这一步。这条验证路径比任何现成的配置都靠得住,希望帮到你。
本文还有配套的精品资源,点击获取