1. 项目概述与核心思路最近在折腾一块基于HPM6750双核RISC-V处理器的开发板这块板子挺有意思它集成了两个完全独立的RISC-V核心hart0和hart1。在嵌入式开发里双核系统能做的事情就多了比如一个核跑实时控制另一个核处理网络协议栈或者图形界面效率能提升不少。但随之而来的一个现实问题就是怎么把两个核心的程序固件烧录进去这可不是简单的“一键下载”就能搞定的事儿。我用的开发环境是AWorksLP SDK它对外设做了高度抽象同一类外设的接口是统一的这让写跨平台应用轻松了不少。但官方文档里关于双核烧录的步骤写得比较零散新手照着做很容易卡住。经过一番摸索和踩坑我总算把整个流程跑通了。这篇文章我就以SDK里自带的hello双核例程为例手把手带你走一遍HPM6750双核工程的创建、编译、固件合并与烧录的全过程。无论你是刚接触双核MCU还是从其他平台比如ARM Cortex-M系列的双核转过来这篇详尽的实操记录应该都能帮你避开我走过的弯路。简单来说HPM6750的双核烧录核心思路是“主核带从核”。hart0核心0是主核它总是首先启动并且它的程序是固化在Flash里的。hart1核心1是从核它自己不能独立启动必须由hart0来“唤醒”和加载。因此我们的最终目标是生成一个包含了hart0Flash程序和hart1SDRAM程序所有代码的单一固件文件然后通过调试器比如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中创建或导入工程。选择File-New-AWorksLP Project或类似选项。在弹窗中选择“从现有代码创建工程”或直接定位到{SDK}\demos\multi-core\hello目录。关键选择在板卡选择页面必须选择EPC6750-AWI-muti。即使你手头的物理板子是EPC6750-AWI-L在创建这个双核示例工程时为了使用多核的BSP配置也需要先选择muti版本。串口等差异我们后续再调整。分别对hart0和hart1文件夹执行上述操作在IDE中建立两个独立的工程。3.2 步骤二编译hart1从核固件我们先处理从核因为它的输出是主核的“原材料”。打开hart1工程在IDE的工程浏览器中切换到hart1工程。配置编译目标找到工程属性通常右键工程 -Properties。定位到C/C Build-Settings-Tool Settings选项卡。找到GNU RISC-V Cross Create Flash Image或类似的“生成镜像”工具设置。将Output file format输出文件格式从默认的Intel Hex或ELF改为Raw binary。这一步至关重要它告诉编译器生成一个纯粹的、无头信息的二进制镜像方便主核直接加载。选择编译配置在IDE的工具栏找到编译配置下拉菜单。你会看到多个配置选项如flash_debug,sdram_hart1_debug等。为hart1工程选择sdram_hart1_debug。这个配置预定义了正确的链接脚本将代码和数据定位到SDRAM地址。执行编译点击编译按钮。编译成功后去工程目录下寻找生成的文件。路径通常是{你的工作空间}\hello\hart1\project_eclipse\sdram_hart1_debug\在这个目录下你应该能找到生成的二进制文件名字类似HPM6750-MULTI-HART1.bin。请记下这个完整的文件名。3.3 步骤三准备hart1固件供主核使用编译出.bin文件只是第一步接下来要把它“交给”主核工程。拷贝bin文件将上一步生成的HPM6750-MULTI-HART1.bin文件复制到SDK的板级包指定目录{SDK}\platforms\platform-hpm-aworks-lp\boards\EPC6750-AWI-muti\source\如果source文件夹不存在就手动创建一个。核对汇编文件用文本编辑器打开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主核固件处理好从核的“粮草”现在来编译主力部队。切换工程在IDE中将活动工程切换到hart0。选择编译配置在编译配置下拉菜单中为hart0工程选择flash_debug。这个配置会将主核程序链接到Flash。执行编译点击编译按钮。这次编译过程会做一件重要的事链接器会读取hpm_hart1_image.S将我们准备好的hart1的bin文件作为一段数据整合进最终生成的hart0的ELF文件中。定位最终文件编译成功后在hart0工程的输出目录如project_eclipse\flash_debug\下会生成最终的可执行ELF文件例如hello.elf和可供烧录的二进制文件例如hello.bin或hello.hex。这个文件已经包含了两个核心的所有代码。3.5 步骤五硬件连接与调试串口配置在烧录之前需要正确连接硬件和配置调试串口以便观察两个核心的运行打印。硬件连接使用调试器如J-Link连接板子的SWD接口用于下载程序和调试。连接板子的串口到PC。这里需要特别注意根据板卡型号和工程配置两个核心可能使用不同的串口。串口配置解析打开设备树文件{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配置中hart0的aw_kprintf输出到UART0。hart1的aw_kprintf输出到UART13。针对EPC6750-AWI-L评估板的调整如果你手头是EPC6750-AWI-L评估板情况稍有不同。该板子的UART13硬件连接到了RS485收发器默认不是简单的TTL UART。你有两种选择选项A使用UART5修改EPC6750-AWI-muti.dts文件将hart1的core 1;从uart13节点移到uart5节点并确保uart5的status “okay”;。UART5在板子的排针上有引出更方便连接USB转TTL模块。选项B使用UART13的485功能不修改DTS但需要确保板载的485功能已使能可能需要跳线或GUI配置并使用RS485转USB适配器来接收数据。个人建议新手使用选项A修改DTS后重新编译工程这样直接用UART5的TTL电平接线和调试都更简单。3.6 步骤六烧录与运行万事俱备只欠烧录。配置调试会话在IDE中为hart0工程创建一个调试配置Debug Configuration。选择正确的调试器如J-Link目标芯片选择HPM6750。下载程序启动调试会话。IDE会通过调试器将我们最终生成的、包含了双核代码的ELF文件下载到芯片的Flash中。复位与观察程序下载完成后按一下板子上的复位按钮Reset。这是必须的步骤因为上电或下载后hart1可能处于不确定状态需要一次硬件复位让hart0重新执行启动流程。打开串口终端在PC上打开两个串口终端软件窗口如Putty、MobaXterm、SecureCRT等。终端1连接hart0使用的串口如UART0配置正确的波特率通常是115200。终端2连接hart1使用的串口如修改后的UART5或原始的UART13同样配置波特率。查看打印信息复位后你应该能在两个串口终端中看到交替打印的信息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为多核通信与启动抽象出来的一个设备节点。打开这个设备底层驱动会执行一系列硬件操作配置hart1的启动向量地址指向SDRAM中hart1程序的起始地址。将hart1从复位状态释放。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.清理并重建尝试清理Cleanhart0和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 高级技巧与优化建议自定义hart1的加载地址默认的SDRAM地址如0x80000000是BSP预设的。如果你需要调整需要修改两处hart1工程的链接脚本修改MEMORY区域定义和SECTIONS的起始地址。主核的加载代码hpm_hart1_image.S文件只是包含了数据实际的搬运代码在BSP的底层启动文件里。你需要找到负责从Flash拷贝数据到SDRAM的函数通常叫copy_hart1_image或类似并修改其目标地址参数。这需要深入阅读SDK启动代码。验证hart1固件完整性在复杂项目中可以在主核启动从核前增加一个校验步骤比如计算hart1 bin文件的CRC并与预存值对比确保加载到SDRAM的数据是正确的。从核程序不限于while(1)从核可以运行一个完整的实时操作系统RTOS任务。AWorksLP本身支持多核你可以在hart1上也创建一个AWorks任务并通过OpenAMP或共享内存与hart0进行复杂通信。demos/multi-core/下的openamp和rpc例程就是更高级的框架。性能考量hart1运行在SDRAM其访问速度比hart0的Flash快但延迟比内部TCM高。对于极端性能要求的代码可以考虑将hart1的关键代码段通过主核加载到内部RAM中执行但这需要更精细的内存管理和链接脚本控制。整个流程走下来你会发现双核烧录的核心在于“分离编译合并打包主核引导”。只要理解了incbin包含机制和aw_open(“/dev/multi_core”)这个启动开关剩下的就是细致的工程配置和问题排查了。希望这篇超详细的指南能帮你顺利打通HPM6750双核开发的任督二脉。
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来“唤醒”和加载。因此我们的最终目标是生成一个包含了hart0Flash程序和hart1SDRAM程序所有代码的单一固件文件然后通过调试器比如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中创建或导入工程。选择File-New-AWorksLP Project或类似选项。在弹窗中选择“从现有代码创建工程”或直接定位到{SDK}\demos\multi-core\hello目录。关键选择在板卡选择页面必须选择EPC6750-AWI-muti。即使你手头的物理板子是EPC6750-AWI-L在创建这个双核示例工程时为了使用多核的BSP配置也需要先选择muti版本。串口等差异我们后续再调整。分别对hart0和hart1文件夹执行上述操作在IDE中建立两个独立的工程。3.2 步骤二编译hart1从核固件我们先处理从核因为它的输出是主核的“原材料”。打开hart1工程在IDE的工程浏览器中切换到hart1工程。配置编译目标找到工程属性通常右键工程 -Properties。定位到C/C Build-Settings-Tool Settings选项卡。找到GNU RISC-V Cross Create Flash Image或类似的“生成镜像”工具设置。将Output file format输出文件格式从默认的Intel Hex或ELF改为Raw binary。这一步至关重要它告诉编译器生成一个纯粹的、无头信息的二进制镜像方便主核直接加载。选择编译配置在IDE的工具栏找到编译配置下拉菜单。你会看到多个配置选项如flash_debug,sdram_hart1_debug等。为hart1工程选择sdram_hart1_debug。这个配置预定义了正确的链接脚本将代码和数据定位到SDRAM地址。执行编译点击编译按钮。编译成功后去工程目录下寻找生成的文件。路径通常是{你的工作空间}\hello\hart1\project_eclipse\sdram_hart1_debug\在这个目录下你应该能找到生成的二进制文件名字类似HPM6750-MULTI-HART1.bin。请记下这个完整的文件名。3.3 步骤三准备hart1固件供主核使用编译出.bin文件只是第一步接下来要把它“交给”主核工程。拷贝bin文件将上一步生成的HPM6750-MULTI-HART1.bin文件复制到SDK的板级包指定目录{SDK}\platforms\platform-hpm-aworks-lp\boards\EPC6750-AWI-muti\source\如果source文件夹不存在就手动创建一个。核对汇编文件用文本编辑器打开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主核固件处理好从核的“粮草”现在来编译主力部队。切换工程在IDE中将活动工程切换到hart0。选择编译配置在编译配置下拉菜单中为hart0工程选择flash_debug。这个配置会将主核程序链接到Flash。执行编译点击编译按钮。这次编译过程会做一件重要的事链接器会读取hpm_hart1_image.S将我们准备好的hart1的bin文件作为一段数据整合进最终生成的hart0的ELF文件中。定位最终文件编译成功后在hart0工程的输出目录如project_eclipse\flash_debug\下会生成最终的可执行ELF文件例如hello.elf和可供烧录的二进制文件例如hello.bin或hello.hex。这个文件已经包含了两个核心的所有代码。3.5 步骤五硬件连接与调试串口配置在烧录之前需要正确连接硬件和配置调试串口以便观察两个核心的运行打印。硬件连接使用调试器如J-Link连接板子的SWD接口用于下载程序和调试。连接板子的串口到PC。这里需要特别注意根据板卡型号和工程配置两个核心可能使用不同的串口。串口配置解析打开设备树文件{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配置中hart0的aw_kprintf输出到UART0。hart1的aw_kprintf输出到UART13。针对EPC6750-AWI-L评估板的调整如果你手头是EPC6750-AWI-L评估板情况稍有不同。该板子的UART13硬件连接到了RS485收发器默认不是简单的TTL UART。你有两种选择选项A使用UART5修改EPC6750-AWI-muti.dts文件将hart1的core 1;从uart13节点移到uart5节点并确保uart5的status “okay”;。UART5在板子的排针上有引出更方便连接USB转TTL模块。选项B使用UART13的485功能不修改DTS但需要确保板载的485功能已使能可能需要跳线或GUI配置并使用RS485转USB适配器来接收数据。个人建议新手使用选项A修改DTS后重新编译工程这样直接用UART5的TTL电平接线和调试都更简单。3.6 步骤六烧录与运行万事俱备只欠烧录。配置调试会话在IDE中为hart0工程创建一个调试配置Debug Configuration。选择正确的调试器如J-Link目标芯片选择HPM6750。下载程序启动调试会话。IDE会通过调试器将我们最终生成的、包含了双核代码的ELF文件下载到芯片的Flash中。复位与观察程序下载完成后按一下板子上的复位按钮Reset。这是必须的步骤因为上电或下载后hart1可能处于不确定状态需要一次硬件复位让hart0重新执行启动流程。打开串口终端在PC上打开两个串口终端软件窗口如Putty、MobaXterm、SecureCRT等。终端1连接hart0使用的串口如UART0配置正确的波特率通常是115200。终端2连接hart1使用的串口如修改后的UART5或原始的UART13同样配置波特率。查看打印信息复位后你应该能在两个串口终端中看到交替打印的信息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为多核通信与启动抽象出来的一个设备节点。打开这个设备底层驱动会执行一系列硬件操作配置hart1的启动向量地址指向SDRAM中hart1程序的起始地址。将hart1从复位状态释放。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.清理并重建尝试清理Cleanhart0和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 高级技巧与优化建议自定义hart1的加载地址默认的SDRAM地址如0x80000000是BSP预设的。如果你需要调整需要修改两处hart1工程的链接脚本修改MEMORY区域定义和SECTIONS的起始地址。主核的加载代码hpm_hart1_image.S文件只是包含了数据实际的搬运代码在BSP的底层启动文件里。你需要找到负责从Flash拷贝数据到SDRAM的函数通常叫copy_hart1_image或类似并修改其目标地址参数。这需要深入阅读SDK启动代码。验证hart1固件完整性在复杂项目中可以在主核启动从核前增加一个校验步骤比如计算hart1 bin文件的CRC并与预存值对比确保加载到SDRAM的数据是正确的。从核程序不限于while(1)从核可以运行一个完整的实时操作系统RTOS任务。AWorksLP本身支持多核你可以在hart1上也创建一个AWorks任务并通过OpenAMP或共享内存与hart0进行复杂通信。demos/multi-core/下的openamp和rpc例程就是更高级的框架。性能考量hart1运行在SDRAM其访问速度比hart0的Flash快但延迟比内部TCM高。对于极端性能要求的代码可以考虑将hart1的关键代码段通过主核加载到内部RAM中执行但这需要更精细的内存管理和链接脚本控制。整个流程走下来你会发现双核烧录的核心在于“分离编译合并打包主核引导”。只要理解了incbin包含机制和aw_open(“/dev/multi_core”)这个启动开关剩下的就是细致的工程配置和问题排查了。希望这篇超详细的指南能帮你顺利打通HPM6750双核开发的任督二脉。