ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

全志T527实战:解锁玄铁RISC-V核,从安全岛到裸机运行

全志T527实战:解锁玄铁RISC-V核,从安全岛到裸机运行 拿到Allwinner T527这块板子的第一个周末我翻芯片手册翻到CPU框图那一页时愣了一下除了常用的8个Cortex-A55大核框图角落里还缩着一颗“RISC-V Core”。全志文档里对它只用一句话带过说它是安全岛负责安全启动和密钥管理。一开始我没把这当回事直到后来在SDK的编译日志里看到它被单独编译、单独打包、单独烧录才意识到这颗核是个“可以跑程序的处理器”而不仅仅是挂在总线边上的加密引擎。这篇要聊的就是怎么把T527上的这颗玄铁RISC-V核真正解锁出来——不折腾的话它会一直躺在安全固件里折腾完之后你可以让它跑你自己编译的裸机程序、RTOS甚至把它当作一颗独立协处理器来用。整个系列我打算分几次写这篇是Part 1目标定得很低也很明确先把工具链搭好、把裸机程序编译出来、把默认的加载流程换成我们自己的固件然后在RISC-V核上看到第一行输出并且让Linux主核通过共享内存确认“它真的活了”。整个过程踩了大概两三周的坑有些坑现在回头看特别蠢但没有亲身撞一遍很难理解这颗核在整条启动链里到底处于什么位置。如果你手里正好有T527的板子或者你只是对全志平台的异构RISC-V开发好奇这篇应该能帮你省掉不少冤枉路。1. 这颗玄铁核是什么来头为什么会默认被“锁”在安全岛里1.1 T527的硬件底牌ARM主核旁边的小字T527是一颗定位工业级和车载级的SoC核心配置是8个Cortex-A55主频能做到1.8GHz级别周边接口更是给得相当大方USB 3.0、PCIe、多路CAN、千兆网口、GPU和NPU一个不缺。很多人拿到这颗芯片都在盯着它的算力组合看但很少有人细看CPU框图角落里那一行小字RISC-V Core。这颗核在全志的平台里其实不是第一次出现了之前T507等平台上也有一颗类似的协处理器属于平头哥的玄铁系列IP具体型号不同批次或不同SDK版本里标注会有出入但本质上是一颗能独立运行完整程序的通用处理器核心有自己的中断控制器、私有内存空间和总线接口。它和主核的8个A55不是同构关系组成的是典型的异构AMP非对称多处理结构主核侧跑Linux/AndroidRISC-V侧则需要单独一份固件。这颗RISC-V核从芯片设计定位上就承担“安全岛”的角色——在系统启动早期DDR训练、安全校验、密钥管理这类需要可信环境的任务会交给它让它在与主系统隔离的上下文里运行安全固件。这个设计思路本身没什么问题ARM核再强也不如一颗物理独立的处理器更适合做隔离执行环境。但对开发者来说它的存在感基本为零因为全志默认的启动流程会直接把它“分配”给安全固件用户态和内核态都没有任何入口去触碰它。1.2 “锁住”的真实含义不是硬件锁是软件分工很多人一听“解锁”就会往破解、越狱方向想这里得先澄清一下这颗核在硬件上并没有任何锁它只是被软件层面的默认分工“焊死”在安全岛岗位上而已。芯片上电后启动流程中的某个阶段会把RISC-V核从复位状态释放出来加载一份由全志预编译好的安全固件然后这颗核就开始干它的老本行——监控安全事件、管理密钥、响应主核发来的安全请求。整个过程对上层Linux完全透明开发者既看不到它的存在也无法替它换工作。真正的“解锁”本质是在BSP的开发模式下把原本加载到RISC-V核的那份固件替换成我们自己用交叉编译器编译出来的程序。这需要开发板允许关闭Secure Boot或者使用厂商提供的开发者版固件分支这是各类开发板的常规操作跟破解加密保护完全是两码事。我手里的这块板子就明确支持切换开发模式关闭签名校验之后U-Boot阶段就能自由决定喂给RISC-V核哪份镜像。你要是买来的是锁死了生产模式的量产板这套操作就不一定走得通了这点先有个心理预期。1.3 为什么要折腾这么一颗“隐藏核心”如果只是好奇其实不值得花几周时间我折腾它主要图三件事。第一这几乎是目前成本最低的RISC-V底层开发环境之一T527的开发板价格不算贵却能让你在真实SoC上摸到玄铁RISC-V核而不是只在QEMU里看寄存器第二这颗核天然适合做实时任务主核跑LinuxRISC-V侧跑RTOS或裸机一个管业务一个管控制分工清楚第三低功耗场景下可以主核休眠、RISC-V核常驻做传感器采样和唤醒判断对做电池设备的人来说这是很实际的用法。不管哪种诉求第一步都一样先让它跑起来。2. 解锁前的关键功课看懂启动链和资源布局2.1 完整启动链梳理RISC-V核到底在哪个环节被拉起做嵌入式的人都懂一个道理改启动流程之前先得把现有的启动流程完整捋一遍。T527这条链从芯片内固化ROMBROM开始上电后BROM加载Boot0到SRAMBoot0负责初始化DDR然后接管引导ATFArm Trusted Firmware和U-Boot再往后才是Linux内核和根文件系统。不同SDK版本的环节顺序会略有差异但你可以在编译日志和打包脚本里找到清晰的线索。RISC-V核被拉起的时机藏在这一串流程里的某一个环节。以全志这套BSP的习惯RISC-V固件通常会在ATF或U-Boot阶段被读取然后写入这颗核的运行地址最后释放复位。想找到具体位置最直接的办法是在SDK源码里用关键词grep搜索“riscv”“rproc”“scp”“secure”这些字样再配合编译日志里出现的固件文件名像riscv.bin、scp.bin这类不同版本叫法不同顺藤摸瓜。我第一次就是靠搜索“riscv”在U-Boot源码里找到了一处加载逻辑从此才开始真正理解这颗核的启动路径。2.2 找对三样东西入口地址、保留内存、通信机制解锁之前有三样硬件资源必须搞明白RISC-V核的运行入口地址、它使用的内存窗口、以及它和主核之间的通信手段。入口地址决定了固件要被加载到哪里内存窗口决定了你能用多大的RAM通信机制则决定了主从核之间怎么握手和传数据。以我手里的这套SDK为例内存布局里为RISC-V核预留了一段地址区域设备树里能看到reserved-memory节点的声明。给核用的内存区域必须独立保留否则裸机固件跑起来会踩到主核Linux的内存直接死机。比较稳妥的做法是在设备树里显式预留一段内存让Linux自己绕开它代码里再引用同一块物理地址。典型写法长这样reserved-memory { #address-cells 2; #size-cells 2; ranges; riscv_reserved: riscv43000000 { reg 0x0 0x43000000 0x0 0x00400000; no-map; }; };这里的0x43000000是示例地址具体数值以你SDK里的实际分配为准但思路是一样的给RISC-V核划一块4MB的专用区域主核的Linux不碰它RISC-V固件把代码、数据、栈全部放在这块区域内。通信机制方面最简单的是共享内存加轮询复杂一点则是Mailbox加中断Part 1先用共享内存验证等后面文章再聊Mailbox。2.3 电源、时钟与复位新手最容易翻车的环节光有地址和内存还不行嵌入式SoC里任何模块要跑起来前提都是电源和时钟到位。RISC-V核在默认状态下可能是被复位线按住的也可能部分时钟门控没打开。你就算把固件正确加载到了入口地址只要它的时钟没使能、复位没释放它就一直是“假死”状态你去读共享内存地址什么都等不到。这一步在SDK里通常有现成代码可以参考。全志的CCU时钟控制单元和PRCM电源复位控制模块里会有一组和RISC-V核相关的寄存器初始化序列大概率藏在ATF或U-Boot的板级代码里。我当时是直接去源码里搜“RISCV_CCU”“RISC-V”相关宏定义找到之后照抄初始化写法。这里提醒一句不要自己拍脑袋写寄存器值最好的做法是先看懂SDK原有代码怎么初始化这颗核然后照着改因为同一套寄存器偏移在不同芯片版本里可能不一样手册更新速度往往跟不上SDK。3. 从零构建RISC-V裸机“最小工程”3.1 工具链选型别先用错ABI不然起步就是地狱给RISC-V核写裸机程序第一个卡点往往不是代码本身而是编译器选错了。RISC-V的工具链主要分两支riscv64-unknown-elf-gcc和riscv64-linux-gnu-gcc。前者面向裸机不需要操作系统自带库和启动文件也更适合嵌入式场景后者面向运行Linux的程序生成的代码依赖libc和内核ABI拿来编裸机固件会有各种莫名其妙的链接问题。个人建议直接下载SiFive官方预编译的riscv64-unknown-elf工具链放出来就能用省去折腾crosstool-ng自编译的时间。如果你的开发环境网络不太方便用Linux发行版自带的riscv64-linux-gnu-gcc也可以勉强应付但编译裸机程序时一定要记得加上-nostdlib和-ffreestanding避免系统库和启动文件被带进来。自编译工具链唯一的好处是能精确控制新指令扩展和ABI但Part 1阶段完全用不到没必要自己造轮子。我在Windows的WSL里搭的编译环境编译速度很快也没遇到什么环境兼容问题。下面是工具链选项的简单对比工具链适用场景裸机适配度上手成本riscv64-unknown-elf-gcc裸机/RTOS固件高低直接下载预编译包riscv64-linux-gnu-gccLinux用户程序低需额外控制低但裸机姿势别扭crosstool-ng自编译定制工具链需求高高源码编译要一两个小时3.2 最小工程start.S main.c xuantie.lds裸机程序的结构很简单上电后先执行启动汇编设置好栈指针和C环境然后进入C语言的main函数。这里不需要任何库依赖。我习惯把内存区域分成四段入口代码、只读数据、可写数据、BSS再用一个单独的栈顶地址。链接脚本是整个工程的核心因为RISC-V核的链接地址必须和你在内存布局里预留的窗口完全一致差一个字节都不行。先看启动汇编保存为start.S.section .text.start .globl _start _start: /* 先把中断全部关掉裸机阶段不允许任何异步打断 */ csrw mie, zero csrw mstatus, zero /* 设置栈指针栈顶往内存窗口的高地址放 */ la sp, _estack /* 清BSS段保证全局变量初始为0 */ la a0, _sbss la a1, _ebss 1: bgeu a0, a1, 2f sw zero, 0(a0) addi a0, a0, 4 j 1b 2: /* 进入C语言主函数正常情况下main不会返回 */ call main 3: j 3b再看C语言侧先做一个最简单的共享内存握手程序。这颗核刚起步时没法依赖任何串口驱动最稳妥的验证方式是直接往固定内存地址写数据让Linux主核去读#include stdint.h #define SHARED_MAGIC 0x43000100UL #define SHARED_TEXT (0x43000100UL 4) static void delay(volatile int n) { while (n--) ; } int main(void) { volatile uint32_t *magic (volatile uint32_t *)SHARED_MAGIC; volatile uint32_t *text (volatile uint32_t *)SHARED_TEXT; /* 写两个固定值主核侧读到说明本核已经活着 */ *magic 0x72697363; /* risc */ *text 0x66697273; /* firs */ while (1) { /* 每轮循环递增计数主核能观察到这个值在变化 */ (*magic); delay(10000); } return 0; }对应的链接脚本xuantie.ldsOUTPUT_ARCH(riscv) ENTRY(_start) MEMORY { RAM (rwx) : ORIGIN 0x43000000, LENGTH 4M } SECTIONS { .text : { *(.text.start) *(.text*) } RAM .rodata : { *(.rodata*) } RAM .data : { *(.data*) } RAM .bss : { _sbss .; *(.bss*) *(COMMON) _ebss .; } RAM . ALIGN(16); _estack ORIGIN(RAM) LENGTH(RAM); }链接脚本里我特意把_estack定义成内存窗口的末端栈向下增长这样程序代码和栈之间留出了足够空间不会一跑就把自己的代码段顶掉。3.3 编译命令与映像检查确保入口真的对工程文件就三个start.S、main.c、xuantie.lds配一个简单的Makefile就能出镜像CROSS riscv64-unknown-elf- CC $(CROSS)gcc OBJCOPY $(CROSS)objcopy CFLAGS -nostdlib -ffreestanding -mcmodelmedany -O2 -g -Wall all: xuantie.bin xuantie.elf: start.S main.c xuantie.lds $(CC) $(CFLAGS) -T xuantie.lds -o $ start.S main.c xuantie.bin: xuantie.elf $(OBJCOPY) -O binary $ $ dump: xuantie.elf $(CROSS)objdump -d $ clean: rm -f *.elf *.bin *.o这里有个编译参数必须重点说-mcmodelmedany。RISC-V的代码模型直接影响绝对地址是怎么生成的默认small模型下如果你的链接地址超过一定范围编译器会生成带基址的绝对寻址放在裸机环境里可能因为基址寄存器没设置而挂掉。medany模型则是用PC相对地址间接生成任意地址访问更适合高地址运行的裸机固件。编译完不要急着烧录先跑一句riscv64-unknown-elf-objdump -d xuantie.elf确认入口地址是0x43000000确认_start里第一条指令是csrw而不是别的什么库函数的开头这一步能帮你排除掉绝大多数“链接配置不对”的隐形问题。4. 三条解锁路径为什么我最终选了U-Boot截胡4.1 路线一改ATF安全固件正统但调试周期太长看到全志SDK里ATF占了很大一块代码很多人第一反应就是直接改ATF把加载到RISC-V核的那份固件换成自己的等于从源头换血。这个思路技术上最正统毕竟ATF是整条安全链路的枢纽你一眼就能看到RISC-V核是怎么被初始化和加载的。但实际操作后我发现改ATF意味着要重新编译整个ATF工程可能还要处理签名、证书、打包格式等一系列流程每次改动烧录到板子上要花很久而且一旦跑飞你甚至很难判断问题是出在ATF初始化、还是DDR配置、还是你的固件本身。这个路线的定位应该是“最终产品形态”而不是前期探索阶段该走的捷径。4.2 路线二U-Boot阶段截胡我最终的选择我实际采用的是在U-Boot启动末段动手脚。全志的U-Boot里本身就有一段逻辑负责把RISC-V固件读进内存、释放复位我们只需要把这段逻辑改成读我们自己的镜像文件即可。这样做的好处是显而易见的U-Boot编译比ATF快得多错误定位也简单而且U-Boot阶段串口已经可用了主核打印和RISC-V核的状态可以放在同一个启动日志里对照观察。具体到代码层面我搜索U-Boot源码库找到了加载RISC-V固件的那段C函数里面通常有一行指定固件来源的逻辑对照SDK的宏定义替换掉固件文件名再把加载目标地址指向我们在设备树里预留的0x43000000。核心片段看起来类似这样#define RISCV_FW_BASE 0x43000000 int riscv_firmware_load(void) { /* 从boot设备读取我们自己的固件到RISC-V核运行地址 */ if (load_file(xuantie.bin, RISCV_FW_BASE) 0) { printf(Failed to load xuantie.bin\n); return -1; } /* 使能RISC-V核的时钟具体寄存器偏移以SDK为准 */ writel(readl(SUNXI_CCU_BASE RISCV_CLK_GATE) | BIT(0), SUNXI_CCU_BASE RISCV_CLK_GATE); /* 释放复位让RISC-V核从入口地址开始执行 */ writel(BIT(0), SUNXI_CCU_BASE RISCV_SRST); printf(RISC-V firmware loaded at 0x%x\n, RISCV_FW_BASE); return 0; }这里有几个细节值得展开。第一时钟使能和复位释放这两个步骤顺序不能反必须先保证时钟稳定再拉高复位释放否则核可能跑在错误的时钟频率下行为完全不可预测第二load_file这个函数名是我按通用逻辑写的你手里的SDK大概率叫别的名字比如fatload或mmc load所以定位源码识别关键字比死记函数名重要第三加载完固件之后U-Boot本身还会继续启动LinuxRISC-V核则独立运行两者互不干扰这才构成真正的AMP双系统。4.3 路线三Linux remoteproc框架优雅但Part 2才展开如果你不想碰U-Boot也有更“操作系统化”的路线在Linux侧用remoteproc/rpmsg框架运行时加载RISC-V固件。remoteproc是内核标准框架专门用来管理辅助核能实现运行时加载、停止、重启、异常上报配合rpmsg还能提供一套socket一样的通信接口是产品化场景下最正规的玩法。但这条路线有一个前提条件你的内核必须打开了remoteproc相关驱动同时设备树里要为RISC-V核准备好资源表信息告诉驱动固件放哪、内存窗口在哪、中断号是谁。光这一步就让复杂度上了一个台阶更别提T527的BSP内核不一定默认把这些东西都配好。所以我把这条路线留到系列后面专门写Part 1先保证用U-Boot截胡跑通。4.4 选型对比以及一条重要提醒三条路线放在一起看各自的定位很清楚路线改动位置优点缺点适合场景改ATF固件启动链最上游最贴近产品形态可控性最强编译慢、签名复杂、调试痛苦最终量产固化U-Boot截胡启动链中段编译快、串口可用、方便验证每次改固件要重新打包烧录前期开发验证Linux remoteproc运行期Linux侧支持运行时加载通信框架完善依赖内核配置和设备树上手门槛高产品化后的动态加载如果你和我一样是第一次碰这颗核强烈建议直接从U-Boot截胡路线开始不要一上来就钻ATF。U-Boot阶段你还能看到完整启动日志RISC-V核的固件也能快速迭代等裸机程序稳定了再考虑往remoteproc迁移也不迟。5. 实战过程从U-Boot改造到“第一行输出”5.1 定位“截胡点”的具体步骤拿到全志T527的SDK先不要急着改代码按下面这个顺序把现场摸清楚。第一步用SDK默认配置完整编译一遍U-Boot和固件包确认你能从干净状态下复现烧录和启动第二步拿串口工具抓完整启动日志搜索“riscv”或者固件文件名关键词确认RISC-V固件加载这个行为确实发生在U-Boot阶段第三步到U-Boot源码目录里grep同一个关键词把加载固件的函数定位出来。整个过程听起来简单但我第一遍做的时候栽了个跟头日志里完全搜不到“riscv”字样后来发现是全志把相关打印藏在了一个按debug级别控制的宏后面默认不打开。所以日志里看不到不代表没有直接去源码里搜关键词更靠谱。定位到函数之后把原先加载固定固件的路径改成加载我们自己的xuantie.bin文件放在和原固件相同的boot分区或者FAT分区里确保U-Boot能读得到。编译、打包、烧录一气呵成。5.2 共享内存验证用devmem读magic把固件跑起来之后第一件验证的事不是看串口而是确认这颗核到底有没有活过来。因为裸机程序里我往0x43000100写了一个自增的计数所以只需要在主核Linux侧用busybox devmem去读这个地址看看值是不是在变化。命令很简单busybox devmem 0x43000100 32连续执行几次。如果返回的值每次都在变化而且开头是0x7269开头的某个数值说明RISC-V核确实已经从0x43000000开始执行我们的代码了这不是仿真器假装的成功是真真切切的硬件在跑。我当时看到这个数值变化的时候比看到任何“Hello World”打印都激动因为它意味着整条链路全部打通了固件编译正确、加载地址正确、时钟复位正确、共享内存访问正确。这里的经验是验证异构AMP系统“共享内存计数”这种最简单的信号比打印更可靠因为它不依赖任何外设驱动的初始化状态。串口可能因为引脚复用、波特率、配置问题输出不了但内存读写只要地址对就一定能看到效果。5.3 串口输出从共享内存迈向真正的“Hello”共享内存能握手了但日常开发还是得有串口打印才方便。T527上的调试串口和这颗RISC-V核共用的是同一套UART硬件主核U-Boot阶段已经把UART初始化好了RISC-V裸机程序里不用重新初始化时钟和波特率直接往UART的发送寄存器丢字符就行。以全志常见UART控制器为例基址在芯片手册里有明确标注发送字符的轮询逻辑长这样#define UART0_BASE 0x05000000 /* 以你板卡手册为准 */ static void uart_putc(char c) { /* 等待发送保持寄存器为空 */ while ((*(volatile uint32_t *)(UART0_BASE 0x14) (1 5)) 0) ; *(volatile uint32_t *)(UART0_BASE 0x00) c; } static void uart_puts(const char *s) { while (*s) uart_putc(*s); }需要注意这颗UART的状态寄存器和发送寄存器的偏移在不同SoC上可能不同务必以芯片手册的UART章节为准。我一度在这上面浪费了半天原因是照抄了旧平台的寄存器偏移结果发送寄存器根本不对。5.4 卡了两天的大坑固件地址被打包工具重映射直到前面都跑通了还有一个坑值得专门写出来。泛泛地说就是你以为的固件加载地址和实际烧录到板子上的加载地址可能根本不是一回事。我发现这个问题是在共享内存验证通过、但串口怎么都打印不出来的时候。readelf看固件入口地址没错U-Boot打印的加载地址也没错可RISC-V核执行起来就像是在空转。后来一步步查下去才发现全志的打包工具在生成烧录镜像时会对RISC-V分区做一次重排把固件内容拷贝到镜像里一个固定偏移位置而这个偏移和我链接脚本里写的0x43000000并不是直接对应关系。换句话说镜像里的固件最终被加载到的物理地址与我预想的地址对不上程序入口乱套了。解决方法是去打包配置里找RISC-V固件对应的分区规则要么把打包偏移改成和链接地址一致要么给固件单独做一个镜像分区绕过系统默认的编排逻辑。这个问题在官方文档里几乎找不到说明只能靠对比打包日志和实际memmap来反推。把这个坑记下来的原因很简单以后你碰到“地址检查全对、但行为就是不对”的情况先去怀疑工具链再去怀疑板子最后再怀疑自己的代码。工具链里包含的隐式地址转换往往是整个调试链路上最不透明的一环。5.5 常见失败现象与排查思路整个解锁过程中我前前后后遇到过的失败基本能归成下面几类直接列成排查表供参考现象常见原因检查方式解决方向RISC-V核完全无响应时钟未使能或复位未释放U-Boot日志里看模块初始化状态在U-Boot/ATF中显式操作CCU时钟门控和复位共享内存读出全0固件没跑起来或内存窗口地址不匹配devmem读取并确认地址映射核对链接脚本地址和reserved-memory节点串口无输出但有magic计数UART寄存器偏移错或引脚MUX被复用对照芯片手册确认UART基址和状态位修正寄存器读写偏移固件跑飞magic出现但值乱跳栈指针不对或链接地址被工具链重映射检查反汇编入口和打包配置修正链接脚本栈顶地址/调整分区偏移主核Linux启动崩溃预留内存没标记no-map被Linux占用查看内核启动时的reserved-memory日志在设备树加no-map属性这张表我建议先收好等你真正开始折腾这颗核的时候对着症状查表通常能快速缩小排查范围。6. 解锁之后能玩什么以及这个系列接下来会写什么6.1 让这颗RISC-V核跑RTOS从裸机走向实时控制一旦U-Boot截胡的通道稳定下来下一步很自然的事就是让这颗RISC-V核跑一个正经的RTOS而不是光是一个空转的裸机main。RISC-V侧跑RT-Thread这类系统完全够用它可以管理线程、定时器、信号量做主核Linux之外的实时控制逻辑。比如你的产品里需要以毫秒级精度去响应外部中断Linux侧的调度抖动肯定不理想把中断处理放到RISC-V核上响应确定性会好很多。这个方向我会在系列后续的文章里展开重点讲RT-Thread在玄铁RISC-V核上的移植注意点。6.2 协处理器场景低功耗常驻与独立安全域另一个我很看好的方向是低功耗常驻。很多带电池的智能硬件场景里主核Linux为了省电需要频繁进入休眠但系统还得能随时响应外界触发事件这时候RISC-V核就能一直保持活跃状态做一些轻量的采样和判断需要主核工作了再通过Mailbox发中断把它唤醒。这样比主核唤醒整个Linux栈处理简单事件要省电得多而且逻辑上更清晰。要做到这一点通信机制就不能继续用共享内存加轮询了得把Mailbox协议和中断机制落实这也是Part 2的重点内容。6.3 写给想照着折腾的人的一些体会文章最后聊几句掏心窝的话。这次解锁T527上的玄铁RISC-V核最深的感触不是某个具体的代码技巧而是“启动链”三个字的分量。所有看起来不可思议的问题——固件加载了但没跑、跑飞了但找不到原因、地址明明对却没执行——最后都能回溯到启动链的某个环节上。你不把BROM到U-Boot再到Linux的链路读明白后面每一步都会觉得像是在迷雾里开车。我建议所有想照这篇文章操作的人拿到板子之后的第一件事不是搭编译环境而是花两天时间把SDK里的启动源码过一遍哪怕只看懂七八成后面能少踩超过一半的坑。另外一个更实际的小技巧全志的官方文档更新经常跟不上SDK所以当文档和代码打架的时候以代码为准尤其是寄存器定义和内存布局部分。SDK源码里哪怕一行小小的宏注释都可能比手册里的图表更接近芯片真实行为。Part 1就先到这里下一篇我会把Linux侧的remoteproc/rpmsg通信、Mailbox中断机制以及RISC-V核跑RTOS的移植细节一起聊透。
返回列表