CPU 平台与运行库
AD16N(UC03 内核)的 CPU 平台层是整个 BSP 的最底层运行支撑:定义基础数据类型与平台宏、提供中断/临界区/自旋锁等并发原语、硬件随机数、以及基于 PLL 的时钟树管理和延时函数;并与预编译运行库 cpu_lib.a(含中断向量、任务切换、看门狗等固化代码)协同工作,为上层的驱动、音频、解码器等模块提供统一的 CPU 抽象。
Purpose and Scope
本页覆盖 AD16N 工程中与 CPU 平台和运行库直接相关的部分:
sdk/apps/include_lib/cpu/uc03/cpu.h:基础类型、平台常量(大小端、核数、中断嵌套深度)、任务切换宏、中断状态查询、原子读、自旋锁、临界区、断言宏。sdk/apps/app/bsp/cpu/uc03/clock.c:系统时钟源选择、PLL 分频表、电压-频率上限表、udelay/mdelay延时、总线时钟计算、Flash 控制器挂起。sdk/apps/app/bsp/cpu/uc03/下的外设驱动层(ADC、IIC、LEDC、MCPWM、充电、LCD 段码、音频 ADC/DAC 等)的整体组织方式。- 预编译运行库
sdk/apps/include_lib/liba/uc03/mbox_flash/cpu_lib.a的角色与接入方式。
不在此页范围:音频编解码(见解码器相关页面)、电源/充电策略细节、具体外设协议(IIC/SPI/UART 时序)的逐字节说明、以及 bd49 芯片平台的差异(仅在必要时交叉引用)。这些主题各自有独立的目录页。
相关页面指引:外设驱动细节参见「外设驱动」类目录页;音频通路参见「音频子系统」类目录页;系统启动与内存布局参见「启动与引导」类目录页。
Overview
AD16N 平台采用单核 DSP 架构(CPU_CORE_NUM = 1,即 q32DSP 单核),小端序(CPU_ENDIAN = LITTLE_ENDIAN),并支持 2 级中断嵌套(CPU_INT_NESTING = 2)。这意味着:
- 单核模型下
current_cpu_id()恒为 0,代码中仍保留了多核判断(#if CPU_CORE_NUM > 1)以备平台扩展; - 并发保护不需要 SMP 语义的复杂锁,自旋锁实现退化为基于
csync(缓存同步指令)的简单标志位互斥; - 临界区直接通过关闭/打开全局中断实现,开销极小,适合音频等对实时性敏感的场景。
运行库部分以静态库 cpu_lib.a 形式提供,包含 wdt_clear()(喂狗)、中断服务入口、以及由 CPU_TASK_SW 宏触发的任务切换辅助代码等固化实现,链接期随应用一并编入固件。
时钟子系统是 CPU 平台层的核心:系统上电后由 16 MHz 默认时钟(sys_clock)起步,clock.c 根据电压档位(1.20V/1.32V/1.38V)和 PLL IVCO 档位查表选择最高可用频率,并通过 CLK_CON1/SYS_DIV 寄存器实现 PLL→HSB→LSB 的分频树,最终由 sys_clock_get() 提供给全系统(延时、外设时序、功耗计算)使用。
Architecture
flowchart TD
subgraph sg_App["应用层"]
App["App / 业务模块"]
end
subgraph sg_BSP["BSP 平台层 (bsp/cpu/uc03)"]
Clock["clock.c 时钟管理"]
Adc["adc_api.c"]
Iic["iic_api.c / iic_hw / iic_soft"]
Ledc["ledc.c"]
Mcpwm["mcpwm.c"]
Charge["charge.c"]
Audio["audio_adc/dac_cpu.c"]
Dev["device.c"]
end
subgraph sg_Cpu["CPU 平台抽象 (include_lib/cpu/uc03)"]
CpuH["cpu.h 类型/宏/并发原语"]
CpuLib["cpu_lib.a 预编译运行库<br/>(中断向量/任务切换/喂狗)"]
end
subgraph sg_Hw["硬件寄存器层"]
Regs["JL_CLOCK / JL_PLL0 / JL_RAND / JL_FPGA<br/>CSFR 寄存器"]
end
App --> Clock
App --> Adc
App --> Iic
App --> Ledc
App --> Mcpwm
App --> Charge
App --> Audio
App --> Dev
Clock --> CpuH
Adc --> CpuH
Iic --> CpuH
Audio --> CpuH
Dev --> CpuH
CpuH --> Regs
CpuLib --> Regs
CpuLib --> CpuH
Clock --> Regs
架构说明:cpu.h 是所有 C 代码的公共依赖——即使只是取用 u8/u32 类型定义,也必须包含它。cpu_lib.a 与 cpu.h 中的宏(CPU_TASK_SW、wdt_clear)配套使用:宏负责写入 ILAT_SET 触发任务切换,库内固化代码负责实际的中断现场保存与恢复。clock.c 是唯一直接驱动 JL_CLOCK/JL_PLL0 寄存器并向外暴露 sys_clock_get() 的模块,所有依赖时间概念的模块(udelay、外设波特率、音频采样率)都间接依赖它。
主内容:CPU 平台抽象(cpu.h)
基础类型与平台常量
cpu.h 在 #ifndef __ASSEMBLY__ 保护下定义了一套与编译器宽度绑定的定长整型,供汇编与 C 两侧共享平台宏:
typedef unsigned char u8;
typedef char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned int u32;
typedef signed int s32;
typedef unsigned long long u64;
typedef u32 FOURCC;
typedef long long s64;
#define CPU_ENDIAN LITTLE_ENDIAN // 0x4576
#define CPU_CORE_NUM 1 // 单核
#define CPU_INT_NESTING 2 // 支持 2 级中断嵌套
来源:cpu.h
设计意图:全工程(BSP、音频、解码器、应用)统一使用这些类型,避免 int 宽度随编译目标漂移;FOURCC 用于四字符文件格式标签。CPU_ENDIAN 以魔数而非 0/1 表示大小端,可在编译期检测配置与硬件是否一致。
任务切换与中断深度
#define CPU_TASK_SW(a) \
do { \
extern void wdt_clear(); \
q32DSP(0)->ILAT_SET |= BIT(7-a); \
wdt_clear(); \
} while (0)
#define CPU_INT_NESTING 2
来源:cpu.h
CPU_TASK_SW(a) 是协作式任务切换的原语:向 ILAT_SET 置位触发软件中断,中断服务程序(固化在 cpu_lib.a)保存当前任务上下文并切换到任务 a。宏内同时调用 wdt_clear() 喂狗,防止切换路径上的看门狗误复位——这是把"喂狗"与"调度"耦合的务实设计:任何长时间运行的任务切换都不会饿死看门狗。
中断状态查询与临界区
static inline int cpu_in_irq()
{
int flag;
__asm__ volatile("%0 = icfg" : "=r"(flag));
return flag & 0xff;
}
static inline int cpu_irq_disabled()
{
int flag;
__asm__ volatile("%0 = icfg" : "=r"(flag));
return (flag & 0x300) != 0x300;
}
#define CPU_CRITICAL_ENTER() do { local_irq_disable(); } while(0)
#define CPU_CRITICAL_EXIT() do { local_irq_enable(); } while(0)
来源:cpu.h
通过读 icfg 特殊寄存器判断当前是否处于中断上下文(低 8 位)以及中断是否被屏蔽(0x300 掩码位)。CPU_CRITICAL_ENTER/EXIT 是平台统一临界区接口,底层就是 local_irq_disable/enable()。由于 CPU_INT_NESTING = 2,中断服务程序内再进入临界区是被支持的,但临界区内应避免长时间占用,否则会推迟 2 级中断响应。
原子读与自旋锁
#define arch_atomic_read(v) \
({ \
__asm_csync(); \
(*(volatile int *)&(v)->counter); \
})
#define arch_spin_lock(lock) \
do { \
int ret = false; \
__asm_csync(); \
if (!(lock)->rwlock) { \
ret = true; \
(lock)->rwlock = 1; \
} \
if (ret) \
break; \
} while(1)
#define arch_spin_unlock(lock) \
do { (lock)->rwlock = 0; } while(0)
来源:cpu.h
__asm_csync() 是 DSP 的缓存同步指令,保证后续内存访问看到一致视图。单核平台上自旋锁实际只是"标志位互斥"——因为没有第二个核会同时竞争,锁的作用是防止中断上下文与任务上下文并发访问共享数据(配合临界区使用)。arch_spin_trylock 与 arch_spin_lock 的唯一区别是前者不等候、一次尝试即返回。
硬件随机数
static inline u32 rand32() { return JL_RAND->R64L; }
static inline u32 rand32_2() { return JL_RAND->R64H; }
来源:cpu.h
芯片内置硬件真随机数发生器(JL_RAND 寄存器组),64 位随机值分为低/高 32 位两次读取。相比软件 LCG,硬件 RNG 适合用于安全校验、配对码生成等场景;注意两个调用之间 RNG 会持续刷新,连续读取两次得到的是不同的随机数。
断言宏
#define ASSERT(a,...) \
do { \
if(!(a)){ \
printf("ASSERT: "#a" "__VA_ARGS__); \
} \
}while(0);
来源:cpu.h
平台断言直接输出到 printf 而非触发复位,便于在日志中定位失败条件;#a 字符串化保证日志打印原始表达式。
主内容:时钟系统(clock.c)
时钟源与频率上限
#define SPI_TSHSL 80
static u32 LSB_MAX_CLK = 96 * MHz_UNIT;
static u32 SPI_MAX_CLK = 50 * MHz_UNIT;
static u32 OTP_MAX_CLK = 21 * MHz_UNIT;
static u32 sys_clock = 16000000; // 上电默认 16MHz
const u32 pll_clock_tab0[4][6] = {
//0 d3p5 d2p5 d2p0 d1p5 d1p0
{0, 27400000L, 38400000L, 48000000L, 64000000L, 96000000L},
{0, 41100000L, 57600000L, 72000000L, 96000000L, 144000000L},
{0, 54800000L, 76800000L, 96000000L, 128000000L, 192000000L},
{0, 68600000L, 96000000L, 120000000L, 160000000L, 240000000L}
};
const u32 pll_max_clock_tab[MAX_RANGE][4] = {
//dvdd sys max lsb max otp max
{SYSVDD_VOL_SEL_120V, 128000000L, 64000000L, 21333334L}, // 1.20v
{SYSVDD_VOL_SEL_132V, 160000000L, 80000000L, 20000000L}, // 1.32v
{SYSVDD_VOL_SEL_138V, 192000000L, 96000000L, 19200000L} // 1.38v
};
来源:clock.c
设计意图:电压决定频率上限。pll_max_clock_tab 把系统最高频率与 SYSVDD 电压档位绑定——电压越高可跑得越快,代价是功耗上升。pll_clock_tab0 是 PLL 输出频率查找表,行索引为 IVCO 档位(从 PLL_CON0 读出),列索引为 PLL 分频选择(d3p5/d2p5/d2p0/d1p5/d1p0)。clock_sys_src_use_lrc_hw = 1 表示系统时钟源硬件上使用 LRC 作为低速基准。
时钟读取与分频树
int get_sys_pll_sel() { return JL_CLOCK->CLK_CON1 & 0x7; }
u32 sys_clock_get(void) { return sys_clock; }
u32 get_sys_pll_clk()
{
u32 t_sel;
u32 clock = 0;
u8 pll_ivco = (JL_PLL0->PLL_CON0 >> 11) & 0x07;
if (pll_ivco > 2) {
pll_ivco /= 2;
}
t_sel = get_sys_pll_sel();
clock = pll_clock_tab0[pll_ivco][t_sel];
if (0 == clock) {
log_error(" sys clock info err\n");
return 0;
}
u32 t_diva = div_taba[(JL_CLOCK->CLK_CON1 >> 4) & 0x3]; // {1,3,5,7}
u32 t_divb = div_tabb[(JL_CLOCK->CLK_CON1 >> 6) & 0x3]; // {1,2,4,8}
return clock / (t_diva * t_divb);
}
u32 sys_clock_peration(void)
{
u32 hsb_div = (JL_CLOCK->SYS_DIV & 0xff) + 1;
u32 pll_sys_clk = get_sys_pll_clk();
u32 hsb_clk = pll_sys_clk / (hsb_div);
return hsb_clk;
}
u32 lsb_clk_get(void)
{
u32 div42 = ((JL_CLOCK->SYS_DIV >> 8) & 0xf) + 1;
u32 lsb_clk = sys_clock_get() / (div42);
return lsb_clk;
}
来源:clock.c
分频树为 PLL → HSB(高速总线)→ LSB(低速总线):get_sys_pll_clk() 反推 PLL 实际输出(IVCO 档位 × 分频选择 ÷ div_a×div_b),sys_clock_peration() 计算 HSB 时钟(再除 hsb_div),lsb_clk_get() 计算 LSB 时钟。外设驱动根据目标频率(如 SPI 50MHz、LSB 96MHz、OTP 21MHz 上限)选择合适的除数和电压档位。
延时函数
AT(.clock.text.cache.L1)
void udelay(u32 us)
{
u32 mips = us * (sys_clock / 1000000 / 8);
while (mips--) {
asm("nop");
}
}
AT(.clock.text.cache.L1)
void mdelay(u32 ms)
{
for (int i = 0; i < ms; i++) {
udelay(1000);
}
}
来源:clock.c
udelay 是软件忙等延时:根据当前 sys_clock 换算需要的 NOP 循环次数,因此对时钟精度敏感——任何修改 sys_clock 的代码必须同步更新该变量。AT(.clock.text.cache.L1) 将延时代码固定放置在 L1 指令缓存区,避免延时过程中因缓存未命中导致的抖动(延时本身被用于外设时序,如 SPI Flash 时序 SPI_TSHSL=80)。
主内容:外设驱动层与预编译运行库
bsp/cpu/uc03/ 是 CPU 平台下的外设驱动集合,每个模块面向一个外设族:
| 文件 | 职责 |
|---|---|
adc_api.c/h | 通用 ADC 采样 API |
audio_adc_cpu.c、audio_dac_cpu.c、audio_dac_api.c | 音频 ADC/DAC 底层(与音频子系统页面呼应) |
iic_api.c、iic_hw.c、iic_soft.c、iic_eeprom.c | IIC 总线 API + 硬件/软件两种实现 + EEPROM 封装 |
ledc.c | LED 恒流驱动(PWM 调光) |
mcpwm.c | 电机控制 PWM(无刷电机驱动) |
charge.c/h | 充电管理 |
lcd_seg4x8_driver.c | 4x8 段码 LCD 驱动 |
device.c | 设备抽象/注册 |
periph_demo/ | IIC/SPI/UART 外设使用示例 |
预编译运行库 sdk/apps/include_lib/liba/uc03/mbox_flash/cpu_lib.a 以静态库形式随 SDK 发布,配合 cpu.h 中的宏一起工作:CPU_TASK_SW 中的 wdt_clear()、中断入口、local_irq_enable/disable() 的汇编实现、任务上下文切换等固化逻辑都在该库内。应用代码通过声明 extern(如 extern void wdt_clear();)调用,链接期由构建脚本将 .a 与用户代码合并为最终固件。同一平台目录下还存在 include_lib/cpu/bd49/cpu.h,表明 SDK 同时支持 bd49 芯片平台,两个平台的 cpu.h 接口保持一致以便 BSP 上层代码可移植。
核心流程:时钟初始化与系统启动
sequenceDiagram
participant Boot as "启动代码 (cpu_lib.a)"
participant Clock as "clock.c"
participant CpuH as "cpu.h 原语"
participant Pll as "JL_PLL0/JL_CLOCK 寄存器"
participant App as "应用/外设驱动"
Boot->>Clock: 调用时钟初始化 (读 efuse/电压档位)
Clock->>Pll: 读 PLL_CON0 得到 IVCO 档位
Clock->>Pll: 读 CLK_CON1 得到分频选择
Clock->>Clock: 查 pll_max_clock_tab 按电压限频
Clock->>Clock: 更新全局 sys_clock 变量
Clock-->>Boot: 返回当前系统时钟
Boot->>CpuH: CPU_CRITICAL_ENTER() 配置外设
CpuH->>CpuH: local_irq_disable() (写 icfg)
Boot->>App: 外设初始化 (读 sys_clock_get / lsb_clk_get)
App->>Clock: udelay/mdelay 忙等延时
Clock->>Clock: 按 sys_clock 换算 NOP 次数
App->>CpuH: CPU_CRITICAL_EXIT() 恢复中断
流程说明:
- 上电后
cpu_lib.a中的启动代码先建立最小运行环境,再调用clock.c完成时钟树配置; clock.c通过寄存器反推 PLL 当前输出,并按电压档位(SYSVDD_VOL_SEL_*)限制最高频率,防止超压超频损坏芯片;- 全局
sys_clock变量被同步更新,此后udelay/mdelay/sys_clock_get等所有依赖时钟的接口都以此为准; - 外设初始化期间使用
CPU_CRITICAL_ENTER/EXIT保护寄存器配置序列,避免中断插入破坏配置一致性; - 运行时任务切换通过
CPU_TASK_SW触发ILAT_SET软件中断,中断现场由运行库保存/恢复,宏内附带喂狗保证调度路径不触发看门狗。
用法示例
示例 1:临界区保护共享数据(中断与任务并发)
#define CPU_CRITICAL_ENTER() \
do { \
local_irq_disable(); \
}while(0)
#define CPU_CRITICAL_EXIT() \
do { \
local_irq_enable(); \
}while(0)
来源:cpu.h
典型用法:任务上下文修改被中断服务程序读取的计数/状态变量时,先 CPU_CRITICAL_ENTER() 再修改再 CPU_CRITICAL_EXIT();由于平台支持 2 级中断嵌套,临界区应尽量短小。
示例 2:读取系统时钟与硬件随机数
__attribute__((always_inline))
u32 sys_clock_get(void)
{
return sys_clock;
}
static inline u32 rand32()
{
return JL_RAND->R64L;
}
sys_clock_get() 是内联的全局变量读取,零开销,任何模块(如波特率计算、音频采样率校准)都可直接调用;rand32() 从硬件 RNG 读取 64 位随机值的低半部分,用于配对/校验场景。
示例 3:总线时钟推导(外设频率规划)
u32 lsb_clk_get(void)
{
u32 div42 = ((JL_CLOCK->SYS_DIV >> 8) & 0xf) + 1;
u32 lsb_clk = sys_clock_get() / (div42);
return lsb_clk;
}
来源:clock.c
外设驱动在初始化时应以 lsb_clk_get()/sys_clock_peration() 的返回值作为波特率/分频计算基准,而不是硬编码频率——这样系统降频或超频后外设时序仍然正确。
配置选项
CPU 平台层的"配置"以源码常量和查表为主,构建期决定:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
CPU_CORE_NUM | 宏 | 1 | 核数;为 1 时 current_cpu_id() 恒返回 0 |
CPU_INT_NESTING | 宏 | 2 | 允许的中断嵌套深度 |
CPU_ENDIAN | 宏 | LITTLE_ENDIAN | 平台字节序(0x4576 魔数) |
sys_clock | 全局变量 | 16000000 | 上电默认系统时钟(Hz),时钟初始化后更新 |
LSB_MAX_CLK | 静态变量 | 96 * MHz_UNIT | LSB 总线频率上限 |
SPI_MAX_CLK | 静态变量 | 50 * MHz_UNIT | SPI 外设频率上限 |
OTP_MAX_CLK | 静态变量 | 21 * MHz_UNIT | OTP 读取频率上限 |
SPI_TSHSL | 宏 | 80 | SPI Flash 时序参数(片选建立/保持) |
pll_max_clock_tab | 查表 | 见下表 | 电压档位 → 系统/LSB/OTP 最高频率 |
clock_sys_src_use_lrc_hw | 常量 | 1 | 系统时钟源硬件使用 LRC 基准 |
电压-频率上限表(pll_max_clock_tab):
| 电压档位 | 系统最高频率 | LSB 最高频率 | OTP 最高频率 |
|---|---|---|---|
SYSVDD_VOL_SEL_120V (1.20V) | 128 MHz | 64 MHz | 21.3 MHz |
SYSVDD_VOL_SEL_132V (1.32V) | 160 MHz | 80 MHz | 20 MHz |
SYSVDD_VOL_SEL_138V (1.38V) | 192 MHz | 96 MHz | 19.2 MHz |
API 参考
平台原语(cpu.h,均为 static inline 或宏)
| 接口 | 签名 | 说明 |
|---|---|---|
current_cpu_id | int current_cpu_id(void) | 返回当前核 ID;单核平台恒为 0 |
cpu_in_irq | int cpu_in_irq(void) | 读 icfg 低 8 位,非 0 表示处于中断上下文 |
cpu_irq_disabled | int cpu_irq_disabled(void) | 检查 icfg 的 0x300 掩码位,判断中断是否被屏蔽 |
rand32 | u32 rand32(void) | 硬件 RNG 低 32 位随机数 |
rand32_2 | u32 rand32_2(void) | 硬件 RNG 高 32 位随机数 |
CPU_CRITICAL_ENTER/EXIT | 宏 | 关/开全局中断进入/退出临界区 |
arch_atomic_read | 宏 | 带 csync 的原子读(counter 字段) |
arch_spin_lock/unlock/trylock | 宏 | 自旋锁三件套(基于 rwlock 标志位) |
ASSERT(a, ...) | 宏 | 条件失败时 printf 打印表达式与附加信息 |
CPU_TASK_SW(a) | 宏 | 触发 ILAT_SET 软件中断切换至任务 a,并喂狗 |
时钟接口(clock.c)
| 接口 | 签名 | 返回/说明 |
|---|---|---|
sys_clock_get | u32 sys_clock_get(void) | 当前系统时钟 Hz(内联读全局变量) |
get_sys_pll_sel | int get_sys_pll_sel(void) | CLK_CON1 低 3 位 PLL 分频选择 |
get_sys_pll_clk | u32 get_sys_pll_clk(void) | 反推 PLL 实际输出频率;查表为 0 时打错误日志并返回 0 |
sys_clock_peration | u32 sys_clock_peration(void) | HSB 总线时钟 = PLL ÷ (hsb_div+1) |
lsb_clk_get | u32 lsb_clk_get(void) | LSB 总线时钟 = sys_clock ÷ (div42+1) |
udelay | void udelay(u32 us) | 软件忙等微秒延时(L1 缓存驻留) |
mdelay | void mdelay(u32 ms) | 毫秒延时,内部循环调用 udelay(1000) |
sfc_suspend | void sfc_suspend(u32 enable_spi) | 挂起 SPI Flash 控制器(时钟切换期间保护 Flash 时序) |
故障模式、边界与并发
时钟配置错误
get_sys_pll_clk() 查表得到 0(IVCO 档位或分频选择非法)时,记录 log_error(" sys clock info err\n") 并返回 0。上游风险:若调用方未检查返回值而继续做除法,会得到 0 频率的系统时钟,导致所有基于 sys_clock 的延时和波特率失效。实际代码中该错误通常意味着 efuse/寄存器损坏或配置表与芯片版本不匹配。
电压-频率越界
pll_max_clock_tab 明确给出各电压档位下系统/LSB/OTP 的频率上限。超过上限运行属于未定义行为(时序违例),典型表现为 Flash 读错、外设偶发失败。OTP 上限随电压升高反而降低(21.3→20→19.2 MHz),说明 OTP 读取时序与电压弱相关,规划 OTP 频率时不能只按系统频率上限推断。
并发与中断嵌套
- 临界区非可重入:
CPU_CRITICAL_ENTER直接关中断,不支持嵌套计数;嵌套使用会导致提前开中断。由于CPU_INT_NESTING = 2,中断服务程序中再次进入临界区是合法的,但必须保证成对退出。 - 自旋锁无等待上限:
arch_spin_lock是忙等循环,若持有者在关中断状态下长时间运行,其他任务/中断会饥饿。使用原则:锁内只做短操作,配合临界区保护。 - 延时函数中断敏感:
udelay是纯 NOP 忙等,不依赖定时器,因此不会被中断打断而"超时",但耗时与sys_clock强相关——任何动态变频必须同步更新全局sys_clock,否则延时偏差。
单核假设
CPU_CORE_NUM = 1 下 current_cpu_id() 恒为 0;代码保留了多核编译分支(读 cnum 寄存器),若未来芯片核数变化,仅需修改该宏并保证 cpu_lib.a 配套升级。
性能与运维要点
- 延时精度:
udelay/mdelay被放置在 L1 缓存段(AT(.clock.text.cache.L1)),避免取指抖动,适用于对时序敏感的外设初始化(如 SPI FlashSPI_TSHSL)。但忙等不省电,长延时场景应改用任务睡眠。 - 热路径:
sys_clock_get、lsb_clk_get、arch_atomic_read均为内联/宏实现,零函数调用开销,可在音频中断等热路径中直接使用。 - 随机数:硬件 RNG 读取为寄存器访问,开销低;但 RNG 模块在上电后需要稳定时间,量产测试中应在初始化完成后延时再取数(配合
mdelay)。 - 运行库版本匹配:
cpu_lib.a与cpu.h必须来自同一 SDK 版本,宏展开与库内汇编(中断向量、任务切换)接口不一致会导致难以排查的链接期/运行期故障。
扩展点
- 新增外设驱动:在
bsp/cpu/uc03/下新增xxx_api.c/h,对外暴露xxx_init/xxx_read/xxx_write,内部使用cpu.h临界区原语保护寄存器操作,频率计算一律通过sys_clock_get/lsb_clk_get推导。 - 多核适配:修改
CPU_CORE_NUM并实现current_cpu_id()的多核分支(读cnum),同时需要运行库提供多核调度支持。 - 频率策略:替换/扩展
pll_max_clock_tab可定制电压-频率策略(如低功耗模式限制最大频率);clock_sys_src_use_lrc_hw可切换时钟源硬件策略。 - 断言策略:
ASSERT目前只打印不复位;可按产品需求改为打印后while(1)(源码中cpu_reset的注释实现即示例),或接入日志系统持久化。
相关链接
- cpu.h(UC03 平台头文件)
- clock.c(时钟管理实现)
- bsp/cpu/uc03 外设驱动目录
- cpu_lib.a(预编译运行库)
- bd49 平台 cpu.h(跨平台对比)
- cpu_config.c(mbox_flash 场景下的 CPU 配置)
- 外设驱动细节 → 参见「外设驱动」目录页
- 音频通路与解码 → 参见「音频子系统」「解码器」目录页