杰理 SDK 文档中心
首页
首页
  • 项目概览与快速开始

    • 项目概述与芯片支持
    • 环境搭建与工具链
    • 工程与构建系统
    • 烧录与升级工具
    • 文档与硬件资料
  • 系统架构与芯片平台

    • 芯片平台与启动流程
    • 预编译库与头文件体系
    • 消息、定时器与中断服务
    • 通用外设驱动
  • 存储与文件系统

    • 文件系统实现
    • 存储设备驱动
    • VM 参数存储系统
  • 音频处理

    • 音频解码器
    • 音频编码器
    • MIDI 合成与播放
    • 音效、变速变调与降噪
  • 语音玩具应用

    • 应用框架与状态机
    • 音乐播放与外部音源
    • MIDI 乐器模式
    • 录音应用
    • 待机、电源管理与 USB 从机
  • 小音箱应用

    • 应用框架与模式管理
    • 播放源:音乐、FM、录音与 LineIn
  • 应用层与示例工程

    • 通用 MCU 应用
  • 固件更新与补丁

    • 固件升级机制
    • AD14N 主动降噪补丁

工程与构建系统

本文介绍 fw-AD15N_GP-MCU_SDK 的工程组织方式与 Makefile 构建系统,涵盖顶层 Makefile 的目标分发机制、各芯片/产品工程的构建配置、交叉编译工具链、编译/链接/后处理完整流程以及常用构建参数。

Purpose and Scope

本页面向希望在 Linux 或 Windows 环境下编译、烧录 SDK 固件的开发者,以及需要新增目标工程或修改构建参数的平台工程师,说明:

  • SDK 仓库中 sdk/ 目录下多个 Makefile 的分工与调用关系;
  • Makefile.ad15n_mcu 等目标工程 Makefile 的内部结构:工具链路径、编译参数、宏定义、头文件搜索路径、源文件列表、链接脚本生成与下载脚本生成;
  • 从 make 到生成 sdk.elf 再到执行烧录脚本的完整构建链路;
  • 常用构建目标、VERBOSE、LINK_AT 等参数的含义与默认值。

以下主题属于其他目录页的范畴,本文不展开:芯片外设驱动 API(见芯片 BSP 相关页面)、include_lib 静态库的接口说明(见库 API 页面)、USB 升级工具的安装与烧录细节(见烧录与升级页面)。

Overview

该 SDK 是杰理(Jieli)AD1NN 系列 MCU 的固件开发套件,采用 Makefile + Clang/LLVM 交叉编译工具链 构建。整个构建系统由两层组成:

  1. 顶层聚合 Makefile(sdk/Makefile):本身不直接编译任何代码,只负责把 make <目标名> 请求转发给对应的目标工程 Makefile(例如 make ad15n_mcu 等价于 make -f Makefile.ad15n_mcu)。
  2. 目标工程 Makefile(如 Makefile.ad15n_mcu、Makefile.ad15n_voice_toy、Makefile.ad17n_mcu 等):每个文件对应一个芯片型号(AC104N/AD14N/AD15N/AD17N/AD18N)与一种产品形态(mcu 纯 MCU 应用 / voice_toy 语音玩具应用),完整定义该目标的编译、链接、后处理规则。

构建流程分三个阶段:

  • pre_build(预构建):用 clang 的预处理器把 app_ld.c 展开生成链接脚本 app.ld,把 download_bat.c 展开生成下载脚本(Windows 为 download.bat,Linux 为 download.sh);
  • compile + link(编译链接):逐文件编译(clang -target pi32 ... -Oz -flto)生成目标文件,再由 lto-wrapper 一次性链接所有目标文件、静态库与系统库,输出 sdk.elf 与 app.map;
  • post_build(后处理):执行下载脚本完成烧录或固件后处理。

选择这套设计的原因:所有目标共享同一套 clang/LLVM 工具链与 pi32 指令集目标,通过“一个工程一个 Makefile + 顶层统一入口”的方式,既能按产品快速切换构建配置,又能保持多目标并行编译(make all)与统一清理(make clean)的便利性。

Architecture

flowchart TD
    subgraph sg_Top["sdk/Makefile(顶层入口)"]
        Top["make ad15n_mcu"]
    end

    subgraph sg_Target["目标工程 Makefile"]
        MF["Makefile.ad15n_mcu"]
        CC["clang(pi32 交叉编译)"]
        LD["lto-wrapper(链接)"]
        AR["lto-ar / llvm-ar"]
    end

    subgraph sg_Sources["源码与依赖"]
        SRC["app/src/mcu + app/bsp/*.c"]
        INC["include_lib 头文件"]
        LIB["include_lib/liba 静态库"]
        SYS["系统 libc.a / libm.a"]
    end

    subgraph sg_Out["构建产物"]
        LD_FILE["app.ld(由 app_ld.c 预处理生成)"]
        ELF["app/post_build/sh55/sdk.elf"]
        MAP["app.map"]
        POST["download.bat / download.sh"]
    end

    Top --> MF
    MF --> CC
    MF --> LD
    MF --> AR
    CC --> SRC
    CC --> INC
    LD --> LIB
    LD --> SYS
    LD --> LD_FILE
    LD --> ELF
    LD --> MAP
    MF --> POST
    POST --> ELF

各节点的职责:

  • sdk/Makefile:顶层聚合入口,声明 all/clean 及 9 个目标的 .PHONY 规则,逐个转发子目标。它的设计意图是让用户只记 make <产品名> 一个命令,而不需要关心具体 Makefile 文件名。
  • 目标工程 Makefile:每个文件是自包含的构建配方——定义工具链、编译参数、宏、头文件路径、源文件清单、链接参数与产物路径。Makefile.ad15n_mcu 是所有目标中结构最典型的一份。
  • clang / lto-wrapper / lto-ar:杰理工具链中的编译器、链接器与归档器。所有目标文件用 -flto 生成 LLVM 位码,链接阶段做全程序优化,这与后文 LFLAGS 里大量 --plugin-opt=-pi32-* 参数一一对应。
  • 源码与依赖:应用源码(app/src/mcu、app/bsp)、发布库 include_lib/liba/sh55/mcu/*.a、系统库 libc.a/libm.a/libcompiler-rt.a 共同构成链接输入。
  • 构建产物:app.ld(链接脚本)、sdk.elf(固件)、app.map(内存映射,用于排查内存占用)、download.bat/sh(下载/后处理脚本)。脚本本身也是由 C 文件预处理生成的,这是该 SDK 构建系统的一个显著特点。

顶层 Makefile:多目标分发机制

sdk/Makefile 位于仓库根目录的 sdk/ 子目录中,是所有构建的入口。它的注释明确列出了支持的 9 个目标,并在 .PHONY 声明中同时声明了每个目标的 clean_ 版本:

.PHONY: all clean ac104n_mbox_mg ad17n_mcu ad15n_mcu ad14n_mcu ad15n_voice_toy ad18n_voice_toy ad14n_voice_toy ad17n_voice_toy ad18n_mcu clean_ac104n_mbox_mg clean_ad17n_mcu clean_ad15n_mcu clean_ad14n_mcu clean_ad15n_voice_toy clean_ad18n_voice_toy clean_ad14n_voice_toy clean_ad17n_voice_toy clean_ad18n_mcu

all: ac104n_mbox_mg ad17n_mcu ad15n_mcu ad14n_mcu ad15n_voice_toy ad18n_voice_toy ad14n_voice_toy ad17n_voice_toy ad18n_mcu
	@echo +ALL DONE

clean: clean_ac104n_mbox_mg clean_ad17n_mcu clean_ad15n_mcu clean_ad14n_mcu clean_ad15n_voice_toy clean_ad18n_voice_toy clean_ad14n_voice_toy clean_ad17n_voice_toy clean_ad18n_mcu
	@echo +CLEAN DONE

Source: sdk/Makefile

每个目标的规则只是递归调用对应的工程 Makefile,例如:

ad15n_mcu:
	$(MAKE) -C . -f Makefile.ad15n_mcu

clean_ad15n_mcu:
	$(MAKE) -C . -f Makefile.ad15n_mcu clean

Source: sdk/Makefile

设计意图:

  • 统一入口:用户只需执行 make ad15n_mcu,不必记忆 Makefile.ad15n_mcu 这个文件名;
  • 批量构建:make all 会依次构建全部 9 个目标,适合 CI 或发布前全量验证;
  • 递归 make 的代价:每个目标都是独立进程调用子 make,-j 并行度需要由用户在外层指定(如 make -j4),顶层文件本身没有传递并行参数。

目标工程 Makefile 结构(以 Makefile.ad15n_mcu 为例)

跨平台工具链设置

构建系统同时支持 Windows 与 Linux,通过 $(OS) 变量区分。Windows 下工具链位于 C:/JL/pi32/bin,Linux 下位于 /opt/jieli/pi32/bin:

ifeq ($(OS), Windows_NT)
# Windows 下工具链位置
TOOL_DIR := C:/JL/pi32/bin
CC    := clang.exe
CXX   := clang.exe
LD    := lto-wrapper.exe
AR    := llvm-ar.exe
MKDIR := mkdir_win -p
RM    := rm -rf

SYS_LIB_DIR := C:/JL/pi32/libc
SYS_INC_DIR := C:/JL/pi32/include/libc
EXT_CFLAGS  := # Windows 下不需要 -D__SHELL__
export PATH:=$(TOOL_DIR);$(PATH)
else
# Linux 下工具链位置
TOOL_DIR := /opt/jieli/pi32/bin
CC    := clang
CXX   := clang
LD    := lto-wrapper
AR    := lto-ar
MKDIR := mkdir -p
RM    := rm -rf
export OBJDUMP := $(TOOL_DIR)/objdump
export OBJCOPY := $(TOOL_DIR)/objcopy
export OBJSIZEDUMP := $(TOOL_DIR)/objsizedump

SYS_LIB_DIR := $(TOOL_DIR)/../lib
SYS_INC_DIR := $(TOOL_DIR)/../include
EXT_CFLAGS  := -D__SHELL__ # Linux 下需要这个保证正确处理 download.c
export PATH:=$(TOOL_DIR):$(PATH)
endif

Source: sdk/Makefile.ad15n_mcu

两个平台的关键差异:

差异点WindowsLinux原因
工具链根目录C:/JL/pi32/opt/jieli/pi32安装约定不同
ARllvm-ar.exelto-arLTO 模式下归档器需支持 LLVM 位码
EXT_CFLAGS空-D__SHELL__保证 download.c 等脚本源文件在 Linux 下被正确处理
后处理脚本download.bat(fixbat.exe 转 GBK 编码)download.sh(bash 执行,touch 占位)平台脚本差异,fixbat 仅 Windows 需要

编译参数(CFLAGS)

CFLAGS 面向 pi32 目标做了大量针对性优化,核心是 -Oz(极致体积优化)与 -flto(全程序链接期优化):

CFLAGS := \
	-target pi32 \
	-integrated-as \
	-fno-builtin \
	-mllvm -pi32-memreg-opt \
	-mllvm -pi32-mem-offset-adj-opt \
	-mllvm -pi32-const-spill \
	-mllvm -pi32-enable-jz \
	-mllvm -pi32-tailcall-opt \
	-mllvm -inline-threshold=5 \
	-mllvm -pi32-enable-itblock=1 \
	-Oz \
	-flto \
	-g \
	-fprefer-gnu-section \
	-fms-extensions \
	-Wno-empty-body \
	-Wcast-align \
	-Wundef

Source: sdk/Makefile.ad15n_mcu

要点解读:

  • -target pi32:指定交叉编译目标架构为 pi32(杰理私有 32 位 DSP/MCU 内核,SH55 等 CPU 核基于它);
  • -mllvm -pi32-*:一系列 pi32 后端的机器级优化开关(内存寄存器优化、偏移调整、常量 spill、条件跳转、尾调用、IT 块),-inline-threshold=5 控制内联阈值,兼顾代码体积;
  • -Oz 与 -flto 配合:MCU 片内 Flash/RAM 资源紧张,体积优先;LTO 让链接器能看到全部程序后做跨模块优化;
  • -fprefer-gnu-section:每个函数/数据放入独立 section,配合链接时的 --gc-sections 删除未使用代码。

宏定义(DEFINES)

DEFINES 按产品形态裁剪功能,Makefile.ad15n_mcu 是纯 MCU 形态,因此大量外设功能被关闭:

DEFINES := \
	-DD_TOY_SDK=1 \
	-DD_APP_TOY=1 \
	-DFPGA=0 \
	-DCPU_SH55=1 \
	-DFLASH_CACHE_ENABLE=0 \
	-DMKEY_CHECK_ENABLE=1 \
	-DAUDIO_ADC_EN=0 \
	-DHAS_USB_EN=0 \
	-DHAS_UPDATE_EN=0 \
	-DSIMPLE_FATFS_ENABLE=0 \
	-DSYS_VM_EN=0 \
	-DNOFLOAT

DEFINES += $(EXT_CFLAGS) # 额外的一些定义

Source: sdk/Makefile.ad15n_mcu

这些宏在 app_config.c、app_ld.c、download_bat.c 等源文件中驱动条件编译:例如 HAS_USB_EN=0 关闭 USB 升级相关代码、SYS_VM_EN=0 关闭 VM 掉电存储、NOFLOAT 禁用浮点运算以减小代码体积。对比 Makefile.ad15n_voice_toy 会看到另一组宏(如音频、Flash 缓存使能),这正是“一个工程一个 Makefile”所承载的产品差异化。

源文件清单与头文件路径

源文件显式列出,全部位于 app/ 目录下:

c_SRC_FILES := \
	app/bsp/common/rtc/rtc.c \
	app/bsp/common/vm/vm_api.c \
	app/bsp/cpu/sh55/clock.c \
	app/bsp/cpu/sh55/port_init.c \
	app/bsp/cpu/sh55/power_api.c \
	app/bsp/cpu/sh55/uart.c \
	app/bsp/lib/common.c \
	app/src/mcu/app_config.c \
	app/src/mcu/mcu_app.c \
	app/src/mcu/sh55/init.c \
	app/src/mcu/sh55/main.c

Source: sdk/Makefile.ad15n_mcu

头文件搜索路径覆盖 app/bsp/cpu/sh55、include_lib 及其子目录(common/cpu/fs/msg/device 等)、app/post_build/sh55 等,最后追加系统头文件目录 $(SYS_INC_DIR)(见 sdk/Makefile.ad15n_mcu#L116-L141)。include_lib 是发布库的公共头文件目录,应用代码通过这些头文件调用库 API,库的二进制实现放在 include_lib/liba/ 下,编译时由链接器引用。

链接参数(LFLAGS)

链接阶段把 LTO 位码与静态库、系统库合并,并通过 @$(OBJ_FILE) 传递目标文件列表以规避命令行长度限制:

LFLAGS := \
	--plugin-opt=save-temps \
	--plugin-opt=-dont-used-symbol-list=malloc,free,sprintf,printf,puts,putchar,getchar \
	--gc-sections \
	--start-group \
	include_lib/liba/sh55/mcu/cpu_lib.a \
	include_lib/liba/sh55/mcu/efuse_trim_value_lib.a \
	--end-group \
	-Tapp/post_build/sh55/app.ld \
	-M=app/post_build/sh55/app.map \
	--plugin-opt=-pi32-memreg-opt \
	--plugin-opt=-pi32-mem-offset-adj-opt \
	--plugin-opt=-pi32-const-spill \
	--plugin-opt=-pi32-enable-jz \
	--plugin-opt=-pi32-tailcall-opt \
	--plugin-opt=-inline-threshold=5 \
	--plugin-opt=-pi32-enable-itblock=1

Source: sdk/Makefile.ad15n_mcu

要点:

  • --start-group/--end-group:允许 cpu_lib.a 与 efuse_trim_value_lib.a 之间循环引用符号;
  • -T app.ld:使用 pre_build 阶段生成的链接脚本;
  • -M=app.map:输出完整内存映射文件,用于排查 RAM/Flash 占用;
  • --plugin-opt=-dont-used-symbol-list=...:保留 malloc/free/printf 等符号,避免 --gc-sections 误删库中仍被动态使用的实现;
  • LIBS 在 Windows 下显式给出 C:/JL/pi32/lib/ 下的 libc.a、libm.a、libcompiler-rt.a,配合 LIBPATHS 的 -L 使用。

构建流水线:pre_build → 编译 → 链接 → post_build

all 目标把整条流水线串起来,并在完成后执行下载脚本:

all: pre_build $(OUT_ELF)
	$(info +POST-BUILD)
	$(QUITE) $(RUN_POST_SCRIPT) sdk

pre_build:
	$(info +PRE-BUILD)
	$(QUITE) $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -D__LD__ -E -P app/post_build/sh55/mcu/app_ld.c -o app/post_build/sh55/app.ld
	$(QUITE) $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -D__LD__ -E -P app/post_build/sh55/mcu/download_bat.c -o $(POST_SCRIPT)
	$(QUITE) $(FIXBAT) $(POST_SCRIPT)

Source: sdk/Makefile.ad15n_mcu

pre_build 是这套系统最有特色的机制:链接脚本 app.ld 和下载脚本 download.bat/sh 不是手写的静态文件,而是由 C 源文件(app_ld.c、download_bat.c)经过 clang -E -P -D__LD__ 预处理生成。这样做的好处是脚本内容可以直接引用 DEFINES 里的宏(如 Flash 起始地址、分区大小、是否使能 USB 升级),当产品配置变化时脚本自动随之调整,避免脚本与配置漂移。

编译规则采用模式规则,每种源文件后缀对应一条规则,统一加上 -MMD -MF 生成 .d 依赖文件:

$(BUILD_DIR)/%.c.o : %.c
	$(info +CC $<)
	$(QUITE) $(MKDIR) $(@D)
	$(QUITE) $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -MMD -MF $(@:.o=.d) -c $< -o $@

Source: sdk/Makefile.ad15n_mcu

同一 Makefile 还提供了 .S、.s(汇编)、.cpp、.cxx、.cc(C++)共 6 条模式规则(见 sdk/Makefile.ad15n_mcu#L279-L302),其中 .S/.s 走汇编流程、C++ 源文件追加 $(CXXFLAGS)。-include $(DEP_FILES) 在文件末尾引入所有 .d 文件,实现头文件变更后的增量重编译。

链接规则把目标文件列表写入临时文件再传给 lto-wrapper,避免参数过长:

ifeq ($(LINK_AT), 1)
$(OUT_ELF): $(OBJS)
	$(info +LINK $@)
	$(shell $(MKDIR) $(@D))
	$(file >$(OBJ_FILE), $(OBJS))
	$(QUITE) $(LD) -o $(OUT_ELF) @$(OBJ_FILE) $(LFLAGS) $(LIBPATHS) $(LIBS)
else
$(OUT_ELF): $(OBJS)
	$(info +LINK $@)
	$(shell $(MKDIR) $(@D))
	$(QUITE) $(LD) -o $(OUT_ELF) $(OBJS) $(LFLAGS) $(LIBPATHS) $(LIBS)
endif

Source: sdk/Makefile.ad15n_mcu

LINK_AT 默认值为 1(见 sdk/Makefile.ad15n_mcu#L233),使用 GNU make 的 $(file >...) 函数。注释说明:部分旧版 make 不支持 file 函数,此时需用 make LINK_AT=0 回退到直接传参方式。目标文件统一输出到 ad15n_mcu_objs/(BUILD_DIR),与源码目录隔离,make clean 只需删除 $(OUT_ELF) 与 $(BUILD_DIR) 即可(见 sdk/Makefile.ad15n_mcu#L254-L256)。

产物路径约定:OUT_ELF := app/post_build/sh55/sdk.elf,即所有目标最终固件都落在对应 CPU 核(SH55)的 app/post_build/sh55/ 目录下,与链接脚本、下载脚本、map 文件同目录,方便烧录工具统一查找。

核心构建流程

sequenceDiagram
    participant Dev as 开发者
    participant Top as sdk/Makefile
    participant MF as Makefile.ad15n_mcu
    participant CC as clang(pi32)
    participant LD as lto-wrapper
    participant Script as 下载/后处理脚本

    Dev->>Top: make ad15n_mcu(或 make all)
    Top->>MF: $(MAKE) -C . -f Makefile.ad15n_mcu
    MF->>CC: pre_build:-E -P app_ld.c → app.ld
    MF->>CC: pre_build:-E -P download_bat.c → download.bat/sh
    loop 每个 c_SRC_FILES / S_SRC_FILES
        MF->>CC: -MMD -Oz -flto -c 源文件
        CC-->>MF: ad15n_mcu_objs/*.c.o + *.d
    end
    MF->>LD: 写 @objs.txt,链接 sdk.elf
    LD-->>MF: sdk.elf + app.map
    MF->>Script: 执行 $(RUN_POST_SCRIPT) sdk
    Script-->>Dev: 烧录/后处理完成

完整构建过程中的关键决策点:

  1. 入口选择:直接 make ad15n_mcu 或先进入 sdk/ 目录执行 make -f Makefile.ad15n_mcu all 效果等价;顶层 make all 则串行构建 9 个目标。
  2. pre_build 生成物:app.ld 与下载脚本都依赖 $(DEFINES),任何宏变更都会触发脚本重新生成(因为 pre_build 始终执行、无增量判断)。
  3. 增量编译:模式规则依赖 .d 文件,头文件变化只重编受影响的目标文件;链接阶段始终全量执行。
  4. post_build:RUN_POST_SCRIPT 在 Windows 为 app\post_build\sh55\download.bat,在 Linux 为 bash $(POST_SCRIPT);执行 download 时传入参数 sdk,脚本据此找到 sdk.elf 完成烧录。

使用示例

示例 1:Linux 命令行编译 AD15N MCU 工程

README 中给出了推荐的命令行编译方式(进入 sdk/ 目录后执行):

# 选择对应的 Makefile 进行编译
make -f Makefile.ad15n_voice_toy all -j4
# 或者 AD15N MCU 工程
make -f Makefile.ad15n_mcu all -j4

Source: README.md

示例 2:通过顶层 Makefile 按目标名编译

不指定 -f,直接用顶层入口(在 sdk/ 目录下):

make ad15n_mcu        # 编译 AD15N MCU 工程并执行下载脚本
make VERBOSE=1 ad15n_mcu   # 显示每条编译/链接命令的详细过程
make clean_ad15n_mcu  # 清理该工程的临时文件与产物
make all              # 依次编译全部 9 个目标
make clean            # 清理全部目标的产物

Source: sdk/Makefile

示例 3:Windows 下的工程构建

Windows 推荐使用 Code::Blocks IDE 打开对应的 .cbp 工程文件(Build → Build,Ctrl+F9)编译(见 README.md#L151-L157);若使用命令行,工具链会自动切换为 C:/JL/pi32/bin 下的 clang.exe 等可执行文件,后处理脚本为 download.bat,并用 fixbat.exe 修正脚本编码。两种平台下 Makefile 会通过 ifeq ($(OS), Windows_NT) 自动选择正确的工具与路径,无需手工修改。

配置选项

make 变量

变量类型默认值说明
VERBOSEint0置为 1 时显示完整编译/链接命令(去掉 @ 静默前缀),用于排查编译错误
LINK_ATint1使用 make file 函数把目标文件列表写入临时文件后以 @file 方式链接;旧版 make 不支持时置 0 回退为直接传参
OSstring环境变量Windows_NT 时走 Windows 工具链分支,否则走 Linux 分支
TOOL_DIRpath平台相关Windows:C:/JL/pi32/bin;Linux:/opt/jieli/pi32/bin
BUILD_DIRpathad15n_mcu_objs编译中间产物目录(各目标 Makefile 各不相同)
OUT_ELFpathapp/post_build/sh55/sdk.elf最终固件输出路径

顶层 make 目标

目标作用对应工程 Makefile
all构建全部 9 个目标依次调用各目标
clean清理全部目标依次调用各 clean 目标
ad15n_mcu / clean_ad15n_mcu构建/清理 AD15N MCU 工程Makefile.ad15n_mcu
ad15n_voice_toy / clean_ad15n_voice_toy构建/清理 AD15N 语音玩具工程Makefile.ad15n_voice_toy
ad14n_mcu、ad17n_mcu、ad18n_mcu构建 AD14N/AD17N/AD18N MCU 工程对应 Makefile.adXXn_mcu
ad14n_voice_toy、ad17n_voice_toy、ad18n_voice_toy构建对应语音玩具工程对应 Makefile.adXXn_voice_toy
ac104n_mbox_mg构建 AC104N 音箱(mbox)管理工程Makefile.ac104n_mbox_mg

关键宏定义(Makefile.ad15n_mcu)

宏值作用
CPU_SH551目标 CPU 核为 SH55
D_TOY_SDK / D_APP_TOY1玩具 SDK / 玩具应用模式
FPGA0非 FPGA 验证平台
FLASH_CACHE_ENABLE0关闭 Flash 缓存
MKEY_CHECK_ENABLE1使能按键(MKEY)检测
AUDIO_ADC_EN0关闭音频 ADC(纯 MCU 形态)
HAS_USB_EN0关闭 USB 功能
HAS_UPDATE_EN0关闭升级功能
SIMPLE_FATFS_ENABLE0关闭简易 FATFS 文件系统
SYS_VM_EN0关闭 VM 掉电存储
NOFLOAT—禁用浮点运算,减小代码体积

环境准备与前置条件

Makefile 注释(sdk/Makefile.ad15n_mcu#L6-L11)与 README(README.md#L89-L92)明确了 Linux 环境要求:

  1. 从 http://pkgman.jieliapp.com/doc/all 下载杰理编译工具链,解压到 /opt/jieli,保证 /opt/jieli/common/bin/clang 存在(注意目录层次);
  2. 确认 ulimit -n 足够大(建议大于 8096),否则链接可能因打开文件过多而失败,可用 ulimit -n 8096 调大;
  3. Windows 环境则需安装杰理工具链(默认路径 C:/JL/pi32),并推荐使用 Code::Blocks IDE。

失败模式与边界情况

基于 Makefile 源码可以确认以下常见失败场景及其规避方式:

  • 链接期“打开文件过多”失败:LTO 全程序链接会把所有目标文件同时读入内存并打开大量文件句柄,当 ulimit -n 偏小时链接会失败。Makefile 注释明确建议 ulimit -n 8096。这是构建系统最典型的运维坑,属于系统资源限制而非代码错误。
  • 旧版 make 不支持 file 函数:LINK_AT=1(默认)依赖 GNU make 的 $(file >...);若报语法错误,需以 make LINK_AT=0 回退到把目标文件直接放在命令行上的方式(命令行长度受限,目标文件较多时可能溢出,但可作为兜底)。
  • 平台差异导致脚本编码错误:Windows 下 download.bat 由 clang -E -P 生成时输出为 UTF-8,而 cmd 需要 GBK 编码,因此用 fixbat.exe 转换;若跳过 pre_build 或手动编辑脚本,可能出现中文乱码或无法执行。Linux 下无此问题(FIXBAT := touch 占位)。
  • -D__SHELL__ 缺失:Linux 分支通过 EXT_CFLAGS 注入该宏,保证 download.c(及其生成脚本)在 Linux 下行为正确;若自行裁剪 CFLAGS 时遗漏,生成的 download.sh 可能不兼容。
  • --gc-sections 误删运行时符号:dont-used-symbol-list 显式保留 malloc/free/sprintf/printf/puts/putchar/getchar,防止这些被库间接使用的符号被回收导致链接或运行期问题。
  • 并行构建的资源竞争:顶层 make all 串行调用各子 make;但若用户直接对多个目标并行执行(如 make -j4 于顶层),各子工程使用各自独立的 BUILD_DIR,产物互不覆盖,因此并行是安全的;真正共享的 app/post_build/sh55/ 目录下各目标以 sdk.elf 同名输出,串行调用时后构建者覆盖先构建者,属预期行为(烧录哪个目标取决于最后一次构建)。

性能与运维注意事项

  • 增量编译:.d 依赖文件机制保证头文件变更后只重编受影响单元;但 pre_build 无增量判断、每次 make 都重新生成 app.ld 与下载脚本——这是有意为之,因为脚本内容依赖宏配置,无法可靠判断“未变化”。
  • LTO 链接耗时:-flto 让链接阶段承担全程序优化,目标文件越多链接越慢;LINK_AT=1 的 @file 方式避免了命令行过长,但无法缩短链接本身耗时。构建机建议具备足够内存(LTO 需要同时持有全部位码)。
  • -Oz 与 -g 并存:产物包含调试信息,objdump/objcopy/objsizedump 等工具被导出到环境供后处理与大小分析使用;正式发布时可评估是否需要裁剪调试段。
  • 清理策略:make clean 只删除 sdk.elf 与 BUILD_DIR,app.map、app.ld、下载脚本等生成物保留在 app/post_build/sh55/ 下;彻底重置需手动删除这些生成物。
  • 文件句柄与路径长度:Windows 下工具链路径 C:/JL/pi32/bin 较短,但深层嵌套的 BUILD_DIR 目录(镜像了源码相对路径)可能触达路径长度限制;如遇“路径太长”错误,可考虑缩短源码目录深度或迁移工具链安装位置。

扩展点

构建系统为新增目标/产品提供了清晰的扩展模式:

  • 新增产品目标:仿照现有文件复制一份 Makefile.<target>,修改四类内容——(1) BUILD_DIR 与产物路径;(2) DEFINES 宏按产品形态裁剪(参考 mcu 与 voice_toy 两套宏的差异);(3) c_SRC_FILES/S_SRC_FILES 源文件清单;(4) 链接参数中 include_lib/liba/<cpu>/<app>/ 下的静态库。然后在顶层 sdk/Makefile 的 .PHONY、all、clean 及各目标规则中加入新目标名即可。
  • 修改链接脚本/下载脚本:不直接编辑 app.ld 或 download.bat,而是编辑其生成源 app/post_build/sh55/mcu/app_ld.c 与 download_bat.c,让宏驱动的条件编译继续生效。
  • 切换 CPU 核:INCLUDES、c_SRC_FILES 中大量引用 sh55 目录(app/bsp/cpu/sh55、include_lib/cpu/sh55),换核需要同步替换这些路径,并调整 -DCPU_SH55=1 等宏。
  • 调试辅助:make VERBOSE=1 输出完整命令;生成的 app.map 用于分析内存布局;Linux 下 OBJDUMP/OBJCOPY/OBJSIZEDUMP 已导出,可直接用于反汇编与固件大小统计。

测试与验证

仓库以示例工程形式交付(README 中说明“包含 SDK Release 版本代码及示例工程”),构建系统的正确性验证方式是端到端编译 + 烧录:make <target> all 成功生成 sdk.elf 并执行下载脚本,即视为通过。make clean 后再构建可验证干净环境下的可复现性。由于 app_ld.c 与 download_bat.c 由同一套宏驱动,修改配置后应重新执行完整构建(而非仅增量编译),以确保脚本与固件一致。

相关链接

  • sdk/Makefile(顶层构建入口)
  • sdk/Makefile.ad15n_mcu(AD15N MCU 工程构建配置)
  • sdk/Makefile.ad15n_voice_toy(AD15N 语音玩具工程构建配置)
  • README.md(SDK 总览与编译/烧录指南)
  • 环境搭建与工具链安装:杰理官方开发环境文档(doc.zh-jieli.com)
  • 烧录与升级细节:见“烧录与升级”页面
Prev
环境搭建与工具链
Next
烧录与升级工具