当前位置: 首页 > news >正文

HPM6750双核RISC-V开发实战:从固件合并到双核启动全流程解析

1. 项目概述与核心思路

最近在折腾一块基于HPM6750双核RISC-V处理器的开发板,这块板子挺有意思,它集成了两个完全独立的RISC-V核心(hart0和hart1)。在嵌入式开发里,双核系统能做的事情就多了,比如一个核跑实时控制,另一个核处理网络协议栈或者图形界面,效率能提升不少。但随之而来的一个现实问题就是:怎么把两个核心的程序(固件)烧录进去?这可不是简单的“一键下载”就能搞定的事儿。

我用的开发环境是AWorksLP SDK,它对外设做了高度抽象,同一类外设的接口是统一的,这让写跨平台应用轻松了不少。但官方文档里关于双核烧录的步骤写得比较零散,新手照着做很容易卡住。经过一番摸索和踩坑,我总算把整个流程跑通了。这篇文章,我就以SDK里自带的hello双核例程为例,手把手带你走一遍HPM6750双核工程的创建、编译、固件合并与烧录的全过程。无论你是刚接触双核MCU,还是从其他平台(比如ARM Cortex-M系列的双核)转过来,这篇详尽的实操记录应该都能帮你避开我走过的弯路。

简单来说,HPM6750的双核烧录,核心思路是“主核带从核”。hart0(核心0)是主核,它总是首先启动,并且它的程序是固化在Flash里的。hart1(核心1)是从核,它自己不能独立启动,必须由hart0来“唤醒”和加载。因此,我们的最终目标,是生成一个包含了hart0(Flash程序)和hart1(SDRAM程序)所有代码的单一固件文件,然后通过调试器(比如J-Link)一次性下载到芯片里。听起来有点绕?别急,下面我们一步步拆解。

2. 环境准备与工程结构解析

工欲善其事,必先利其器。在开始动手之前,我们需要把环境和工程结构搞清楚。

2.1 开发环境与SDK

首先,你需要准备好AWorksLP SDK和对应的集成开发环境(IDE),官方通常推荐基于Eclipse的定制版本。确保你的SDK版本支持HPM6750和双核特性。将SDK解压到一个没有中文和空格的路径下,这是避免各种奇怪编译问题的第一步。

SDK的目录结构很有讲究,理解它有助于后续操作。关键路径如下:

  • {SDK}\demos\multi-core\:这里存放了所有的双核示例工程。我们重点关注的hello例程就在里面。
  • {SDK}\platforms\platform-hpm-aworks-lp\boards\EPC6750-AWI-muti\:这是针对我们使用的“EPC6750-AWI-muti”这块多核评估板的板级支持包(BSP)目录。里面包含了该板卡的所有硬件配置文件、驱动和链接脚本。
  • {SDK}\platforms\platform-hpm-aworks-lp\boards\EPC6750-AWI-muti\source\:这个source文件夹是关键中的关键。我们后续需要把从核(hart1)编译生成的二进制文件(.bin)拷贝到这里,主核工程在编译时会来这里寻找并“打包”这个文件。

注意:务必确认你使用的板卡型号。本文以EPC6750-AWI-muti为例,如果你的板子是EPC6750-AWI-L或其他型号,部分配置(尤其是调试串口)会有所不同,需要相应调整。

2.2 双核工程本质:两个独立的项目

这是理解整个流程的基础。HPM6750的两个RISC-V核心是完全独立的,它们有各自的内存映射、中断向量表和运行上下文。因此,在软件开发层面,双核工程并不是一个“工程”里包含两个“任务”,而是两个完全独立的单核工程

hello例程目录下,你会清晰地看到两个文件夹:

  • hart0/:这是主核(Core 0)的工程目录。
  • hart1/:这是从核(Core 1)的工程目录。

你需要分别用IDE打开、配置和编译这两个工程。它们之间的关联,仅仅是通过一个“约定”:主核工程在编译的最终阶段,会把从核工程生成的二进制文件“包含”进来,合并成一个最终的可执行文件。

2.3 运行内存规划:Flash vs. SDRAM

两个核心的运行位置也不同,这是由硬件特性和启动顺序决定的:

  • hart0 (主核):其程序代码通常链接到芯片的内部Flash地址空间。芯片上电复位后,直接从Flash的起始地址开始执行hart0的代码。
  • hart1 (从核):其程序代码将被加载到外部SDRAM中运行。这是因为hart1的启动由hart0控制,hart0需要有能力将一段二进制代码搬运到SDRAM,然后跳转过去执行。SDRAM容量大、速度快,适合存放从核的应用程序。

所以,在编译配置上,两个工程也有显著区别:

  • hart0工程:编译目标通常是“Flash调试”,链接脚本指向Flash地址。
  • hart1工程:编译目标需要选择“SDRAM调试”,链接脚本指向SDRAM的特定地址(例如0x8000 0000)。并且,它的输出文件格式必须是Raw Binary (.bin),而不是默认的ELF文件,因为二进制格式更适合主核进行简单的内存搬运。

3. 实操步骤详解:从编译到烧录

理论铺垫完毕,现在进入实战环节。我们按照“先编译从核,再编译主核,最后下载”的顺序进行。

3.1 步骤一:创建与导入工程

首先,在IDE中创建或导入工程。

  1. 选择File->New->AWorksLP Project(或类似选项)。
  2. 在弹窗中,选择“从现有代码创建工程”或直接定位到{SDK}\demos\multi-core\hello目录。
  3. 关键选择:在板卡选择页面,必须选择EPC6750-AWI-muti。即使你手头的物理板子是EPC6750-AWI-L,在创建这个双核示例工程时,为了使用多核的BSP配置,也需要先选择muti版本。串口等差异我们后续再调整。
  4. 分别对hart0hart1文件夹执行上述操作,在IDE中建立两个独立的工程。

3.2 步骤二:编译hart1(从核)固件

我们先处理从核,因为它的输出是主核的“原材料”。

  1. 打开hart1工程:在IDE的工程浏览器中,切换到hart1工程。
  2. 配置编译目标
    • 找到工程属性(通常右键工程 ->Properties)。
    • 定位到C/C++ Build->Settings->Tool Settings选项卡。
    • 找到GNU RISC-V Cross Create Flash Image或类似的“生成镜像”工具设置。
    • Output file format(输出文件格式)从默认的Intel HexELF改为Raw binary。这一步至关重要,它告诉编译器生成一个纯粹的、无头信息的二进制镜像,方便主核直接加载。
  3. 选择编译配置:在IDE的工具栏,找到编译配置下拉菜单。你会看到多个配置选项,如flash_debug,sdram_hart1_debug等。
    • 为hart1工程选择sdram_hart1_debug。这个配置预定义了正确的链接脚本,将代码和数据定位到SDRAM地址。
  4. 执行编译:点击编译按钮。编译成功后,去工程目录下寻找生成的文件。路径通常是:{你的工作空间}\hello\hart1\project_eclipse\sdram_hart1_debug\在这个目录下,你应该能找到生成的二进制文件,名字类似HPM6750-MULTI-HART1.bin。请记下这个完整的文件名。

3.3 步骤三:准备hart1固件供主核使用

编译出.bin文件只是第一步,接下来要把它“交给”主核工程。

  1. 拷贝bin文件:将上一步生成的HPM6750-MULTI-HART1.bin文件,复制到SDK的板级包指定目录:{SDK}\platforms\platform-hpm-aworks-lp\boards\EPC6750-AWI-muti\source\如果source文件夹不存在,就手动创建一个。
  2. 核对汇编文件:用文本编辑器打开source文件夹下的一个关键文件:hpm_hart1_image.S。这个文件的内容通常如下:
    .section .hart1_image, "a" .global hart1_image_start .global hart1_image_end .align 4 hart1_image_start: .incbin "HPM6750-MULTI-HART1.bin" /* 确保这里的文件名与你拷贝的bin文件完全一致 */ hart1_image_end:
    你的任务就是检查.incbin指令后面的双引号内的文件名,是否与你刚刚拷贝进去的bin文件名称完全一致,包括大小写。incbin是汇编器指令,作用是在当前位置“包含”一个二进制文件。主核工程在链接时,会把这个二进制数据段链接到最终的ELF文件中。

实操心得:这是最容易出错的一步。很多人编译失败,报错找不到符号hart1_image_start,十有八九是因为这里的文件名没对上,或者bin文件没有成功拷贝到这个目录。我建议在拷贝完成后,在IDE里刷新一下工程,确保文件系统同步。

3.4 步骤四:编译hart0(主核)固件

处理好从核的“粮草”,现在来编译主力部队。

  1. 切换工程:在IDE中,将活动工程切换到hart0
  2. 选择编译配置:在编译配置下拉菜单中,为hart0工程选择flash_debug。这个配置会将主核程序链接到Flash。
  3. 执行编译:点击编译按钮。这次编译过程会做一件重要的事:链接器会读取hpm_hart1_image.S,将我们准备好的hart1的bin文件作为一段数据,整合进最终生成的hart0的ELF文件中。
  4. 定位最终文件:编译成功后,在hart0工程的输出目录(如project_eclipse\flash_debug\)下,会生成最终的可执行ELF文件(例如hello.elf)和可供烧录的二进制文件(例如hello.binhello.hex)。这个文件已经包含了两个核心的所有代码。

3.5 步骤五:硬件连接与调试串口配置

在烧录之前,需要正确连接硬件和配置调试串口,以便观察两个核心的运行打印。

  1. 硬件连接
    • 使用调试器(如J-Link)连接板子的SWD接口,用于下载程序和调试。
    • 连接板子的串口到PC。这里需要特别注意:根据板卡型号和工程配置,两个核心可能使用不同的串口。
  2. 串口配置解析:打开设备树文件:{SDK}\platforms\platform-hpm-aworks-lp\boards\EPC6750-AWI-muti\EPC6750-AWI-muti.dts在文件中搜索uart,你会找到类似下面的配置:
    &uart0 { status = "okay"; core = <0>; /* hart0 使用 uart0 */ }; &uart13 { status = "okay"; core = <1>; /* hart1 使用 uart13 */ };
    这意味着在默认的EPC6750-AWI-muti配置中:
    • hart0aw_kprintf输出到UART0
    • hart1aw_kprintf输出到UART13
  3. 针对EPC6750-AWI-L评估板的调整:如果你手头是EPC6750-AWI-L评估板,情况稍有不同。该板子的UART13硬件连接到了RS485收发器,默认不是简单的TTL UART。你有两种选择:
    • 选项A(使用UART5):修改EPC6750-AWI-muti.dts文件,将hart1的core = <1>;&uart13节点移到&uart5节点,并确保uart5status = “okay”;。UART5在板子的排针上有引出,更方便连接USB转TTL模块。
    • 选项B(使用UART13的485功能):不修改DTS,但需要确保板载的485功能已使能(可能需要跳线或GUI配置),并使用RS485转USB适配器来接收数据。个人建议新手使用选项A,修改DTS后重新编译工程,这样直接用UART5的TTL电平,接线和调试都更简单。

3.6 步骤六:烧录与运行

万事俱备,只欠烧录。

  1. 配置调试会话:在IDE中,为hart0工程创建一个调试配置(Debug Configuration)。选择正确的调试器(如J-Link),目标芯片选择HPM6750。
  2. 下载程序:启动调试会话。IDE会通过调试器,将我们最终生成的、包含了双核代码的ELF文件下载到芯片的Flash中。
  3. 复位与观察:程序下载完成后,按一下板子上的复位按钮(Reset)。这是必须的步骤,因为上电或下载后,hart1可能处于不确定状态,需要一次硬件复位让hart0重新执行启动流程。
  4. 打开串口终端:在PC上打开两个串口终端软件窗口(如Putty、MobaXterm、SecureCRT等)。
    • 终端1:连接hart0使用的串口(如UART0),配置正确的波特率(通常是115200)。
    • 终端2:连接hart1使用的串口(如修改后的UART5或原始的UART13),同样配置波特率。
  5. 查看打印信息:复位后,你应该能在两个串口终端中看到交替打印的信息:
    • hart0终端:会先打印”open multi_core ok!”,然后每隔1秒打印”hart0: hello world!”
    • hart1终端:会打印”application Start……”,然后每隔1秒打印”hart1: hello world!”

当你同时看到两个核心的“hello world”在各自的串口上有规律地打印时,恭喜你,双核烧录与运行就成功了!

4. 核心代码与机制深度解析

看到现象只是第一步,理解背后的代码逻辑才能举一反三。我们来深入看看hello例程里最关键的两段代码。

4.1 hart1(从核)程序分析

从核的程序非常简单,它就是一个独立的、无限循环的C应用。

int aw_main() { aw_kprintf("\r\napplication Start.............. \r\n"); while(1) { aw_kprintf("hart1: hello world!\n"); aw_mdelay(1000); // 延时1秒 } return 0; }
  • aw_main():这是AWorksLP应用程序的入口函数,类似于标准C的main()
  • aw_kprintf():AWorksLP提供的打印函数,输出会重定向到该核心配置的调试串口。
  • aw_mdelay():毫秒级延时函数。

关键点:从核程序完全不知道自己是被谁、如何加载到SDRAM的。它只关心从它的入口地址开始执行自己的逻辑。这个入口地址在链接脚本里定义,必须和主核加载它的目标地址(SDRAM中的地址)一致。

4.2 hart0(主核)程序分析

主核的程序承担了启动从核的重任。

static void __start_hart1(void) { int fd; fd = aw_open("/dev/multi_core", AW_O_RDWR, 0); if (fd < 0) { aw_kprintf("open error, fd: %d\n", fd); } aw_kprintf("open multi_core ok!\n"); } int aw_main() { aw_kprintf("\r\napplication Start.............. \r\n"); __start_hart1(); // 启动hart1 while(1) { aw_kprintf("hart0: hello world!\n"); aw_mdelay(1000); } return 0; }
  • __start_hart1()函数是核心。它通过aw_open(“/dev/multi_core”, …)这个操作来启动从核。
  • ”/dev/multi_core”:这是一个虚拟设备,是AWorksLP SDK为多核通信与启动抽象出来的一个设备节点。打开这个设备,底层驱动会执行一系列硬件操作:
    1. 配置hart1的启动向量地址(指向SDRAM中hart1程序的起始地址)。
    2. 将hart1从复位状态释放。
    3. hart1检测到释放信号后,开始从指定的SDRAM地址执行指令。
  • aw_open的返回值是一个文件描述符fd,这个fd在后续的多核通信(如OpenAMP或RPC)中会用到,用于在两个核心之间传递消息或数据。在简单的hello例程中,我们打开设备后并没有进行实际的数据通信。

机制总结:主核通过操作系统提供的抽象接口(aw_open)触发硬件机制来启动从核。从核的二进制镜像已经作为数据段被包含在主核的固件中,主核的启动代码会在系统初始化早期,自动将这段数据从Flash拷贝到SDRAM的指定位置,为aw_open操作做好准备。

5. 常见问题与深度排查指南

双核调试比单核复杂,遇到问题是常态。下面是我总结的一些常见坑点及其解决方案。

5.1 编译与链接问题

问题现象可能原因排查步骤与解决方案
编译hart0时,链接错误,提示undefined reference to ‘hart1_image_start’1.hpm_hart1_image.S文件中的.incbin文件名与实际的bin文件名不匹配。
2. bin文件未拷贝到…/source/目录,或目录错误。
3. 未在hart0工程的链接脚本或工程配置中包含hpm_hart1_image.S文件(通常SDK已配置好)。
1.仔细核对文件名:检查hpm_hart1_image.S中的字符串与source文件夹下bin文件的名字,确保一字不差,包括后缀.bin
2.确认路径:确认bin文件在{SDK}\…\EPC6750-AWI-muti\source\下。
3.清理并重建:尝试清理(Clean)hart0和hart1工程,然后按顺序重新编译(先hart1,拷贝bin,再hart0)。
hart1工程编译失败,提示地址溢出或链接错误hart1工程的链接地址(SDRAM地址)与硬件不符,或SDRAM未正确初始化。1.检查编译配置:确保hart1工程选择的是sdram_hart1_debug等与SDRAM相关的配置。
2.检查链接脚本:查看该配置使用的链接脚本(.ld文件),确认代码段(.text)的起始地址是否为SDRAM的有效地址(如0x80000000)。
3.确认SDRAM初始化:主核的启动代码必须包含SDRAM的初始化。确保使用的BSP和板级支持包是正确的。
编译通过,但生成的最终bin/elf文件异常小hart1的bin文件没有被成功包含进去。查看最终生成的ELF文件大小,如果只有几十KB(仅hart0代码),说明包含失败。请回到步骤三,严格检查bin文件拷贝和汇编文件引用的每一步。

5.2 运行与调试问题

问题现象可能原因排查步骤与解决方案
只有hart0串口有打印,hart1串口无任何输出1. hart1程序未成功启动。
2. hart1使用的串口配置错误或硬件连接错误。
3. hart1程序在SDRAM中运行崩溃。
1.检查启动代码:在主核aw_open(“/dev/multi_core”)后,是否打印了成功信息?如果没有,说明打开多核设备失败,检查驱动和硬件配置。
2.检查串口配置:确认设备树(.dts)中hart1核心对应的串口引脚配置是否正确,是否与你实际连接的串口线匹配。对于EPC6750-AWI-L板,强烈建议将hart1的调试串口改为UART5
3.使用调试器:尝试在hart1的入口函数(aw_main)处设置断点。如果能停住,说明启动成功,问题在串口输出;如果停不住或无法调试,问题在启动或加载环节。
两个核心的打印信息混乱,或只有一个核心在打印两个核心的串口配置成了同一个物理串口。检查设备树,确保core = <0>core = <1>分别指向不同的uart节点(如uart0和uart5)。
程序运行一段时间后死机或行为异常1. 双核访问共享资源(如内存、外设)未做同步,导致竞态条件。
2. SDRAM访问不稳定(时钟、电气问题)。
3. 堆栈空间不足。
1.隔离问题:先分别测试两个核心的单核程序是否稳定。
2.检查共享资源:在hello例程中,两个核心仅通过串口打印,没有共享变量。如果你修改了代码并引入了共享数据,必须使用互斥锁、信号量等机制进行保护。
3.调整内存配置:检查hart1工程的链接脚本,为其分配足够的堆(heap)和栈(stack)空间,尤其是在SDRAM中运行。
无法通过调试器连接到hart1进行调试调试器配置或IDE设置不支持多核调试,或hart1的调试接口未使能。1.确认IDE支持:查看你的IDE和调试插件是否支持RISC-V多核调试。可能需要特定的调试配置。
2.检查调试脚本:在调试配置中,可能需要加载特定的多核调试脚本(.cfg文件),以确保在复位后能正确识别和连接两个核心。
3.先确保hart0能调试:多核调试通常以主核为入口。确保能正常对hart0进行单步、断点调试。

5.3 高级技巧与优化建议

  1. 自定义hart1的加载地址:默认的SDRAM地址(如0x80000000)是BSP预设的。如果你需要调整,需要修改两处:

    • hart1工程的链接脚本:修改MEMORY区域定义和SECTIONS的起始地址。
    • 主核的加载代码hpm_hart1_image.S文件只是包含了数据,实际的搬运代码在BSP的底层启动文件里。你需要找到负责从Flash拷贝数据到SDRAM的函数(通常叫copy_hart1_image或类似),并修改其目标地址参数。这需要深入阅读SDK启动代码。
  2. 验证hart1固件完整性:在复杂项目中,可以在主核启动从核前,增加一个校验步骤,比如计算hart1 bin文件的CRC,并与预存值对比,确保加载到SDRAM的数据是正确的。

  3. 从核程序不限于while(1):从核可以运行一个完整的实时操作系统(RTOS)任务。AWorksLP本身支持多核,你可以在hart1上也创建一个AWorks任务,并通过OpenAMP或共享内存与hart0进行复杂通信。demos/multi-core/下的openamprpc例程就是更高级的框架。

  4. 性能考量:hart1运行在SDRAM,其访问速度比hart0的Flash快,但延迟比内部TCM高。对于极端性能要求的代码,可以考虑将hart1的关键代码段通过主核加载到内部RAM中执行,但这需要更精细的内存管理和链接脚本控制。

整个流程走下来,你会发现双核烧录的核心在于“分离编译,合并打包,主核引导”。只要理解了incbin包含机制和aw_open(“/dev/multi_core”)这个启动开关,剩下的就是细致的工程配置和问题排查了。希望这篇超详细的指南能帮你顺利打通HPM6750双核开发的任督二脉。

http://www.cnnetsun.cn/news/2484558.html

相关文章:

  • HsMod终极指南:55项功能打造你的个性化炉石传说体验
  • 想买AI漫剧制作服务?先了解这3个价格档位和真实案例
  • MCU工程迁移实战:从STM32到MSPM0L1306的完整指南
  • Perplexity作家搜索≠简单关键词匹配:从NLP意图识别到跨平台身份对齐的9层专业验证体系
  • CentOS 7服务器上NVIDIA驱动和CUDA 11.x的保姆级安装避坑指南(含Nouveau禁用与版本选择)
  • 2026年免费商用音乐素材网站TOP5深度评测:从版权合规到项目适配的全方位指南
  • 从Vue/React到移动端:用Cordova 12把你的Web项目打包成Android App实战
  • 注册培训师、咨询师——杨刚老师简介
  • 初创团队如何利用 Taotoken 以最小成本验证多个大模型能力
  • 【限时解密】Perplexity未公开的历史资料检索协议v2.3:仅开放给前500名深度用户的私有搜索语法手册
  • 信息安全工程师-网站安全主动防御体系构建与政务网站合规实践
  • 自动化测试的新趋势:AI驱动的自动化测试框架
  • Java:猜数字游戏
  • 全志MPP媒体处理平台在V853-PRO开发板上的实战应用
  • Claude Code 在大型代码库里的真实体验
  • 猫抓浏览器扩展:3分钟学会免费下载在线视频的完整指南 [特殊字符]
  • 多场景互动抽奖公众号管理系统
  • 从普通AI算法工程师到AI技术负责人:软件测试从业者的进阶之路
  • 魔百盒刷机后必做的5个设置:从开机自启到应用隐藏,让你的旧盒子焕然一新
  • 别再滥用 `runOnUiThread`!Android 主线程嵌套滥用的危害与正确用法
  • Arco Design Pro:3个痛点解决与5步快速搭建企业级中后台系统
  • 为什么你的无锁队列在压测中崩了——从 ABA 问题到 Hazard Pointer,追踪 lock-free 内存回收的生死时序
  • 二年级下册语文看图写话作文:图书借阅公约
  • 设计智能体对话界面:消息气泡、打字指示器与时间戳
  • HFSS仿真微带线损耗翻车?可能是这3个细节没做好(附PCB导入避坑指南)
  • NY378固态MT29F32T08GSLBHL8-24QA:B
  • JavaSE-14
  • 硬核实战:调用Gemini多模态管道,直击办公中的图表解析、发票识别与自动化脚本生成(国内镜像免费方案)
  • LabVIEW实战:生产者-消费者与状态机模式在测控系统中的应用
  • 2026硕士论文AIGC检测多少算合格?各校红线汇总,附降AI攻略