构建系统与工作区
本页介绍 fw-AC63_GP_MCU 固件仓库的工作区目录组织与基于 Makefile 的多芯片构建体系,包括顶层总控 sdk/Makefile、各芯片 BSP 子工程 Makefile 的配置区(工具链、编译/链接参数、宏定义、源文件清单)、构建产物输出以及烧录后处理流程。
Purpose and Scope
本页覆盖的内容:
- 仓库工作区的顶层目录结构(
sdk/、apps/、bsp/及各芯片子工程)与各目录的职责边界; - 顶层
sdk/Makefile如何统一调度ac636n / ac638n / ac632n / ac635n四个芯片子工程; - 单个 BSP 子工程
Makefile的完整构建机制:平台工具链探测、clang(pi32v2 目标)编译参数、宏定义、源文件与静态库清单、LTO 链接参数; - 构建中间产物(
objs/)与最终产物(output/sdk.elf / sdk.map / sdk.ld)的生成路径; - 构建后的烧录下载脚本(
tools/download.sh/download.bat)的接入方式。
本页不涉及具体的外设驱动实现、应用业务逻辑、SDK 组件(VM、充电、升级等静态库)的内部机制——这些属于各自的目录页(例如 BSP 驱动、应用层、SDK 组件)。构建系统只是把这些模块"装配"起来的手段,本页聚焦装配过程本身。
Overview
fw-AC63_GP_MCU 是杰理(Jieli)AC63 系列通用 MCU 固件仓库,支持 AC632N、AC635N、AC636N、AC638N 四款芯片。整个固件采用 “单仓库、多芯片子工程” 的工作区布局:sdk/bsp/ 下每个芯片一个子目录,各自携带独立的 Makefile、板级文件(board/)、驱动源码(src/)和构建输出目录(output/),而 sdk/Makefile 作为唯一入口统一调度它们。
构建体系的关键设计意图:
- 多芯片复用同一套构建规则:四份子工程 Makefile 结构完全相同(差异只在源文件清单、头文件路径与芯片静态库),降低维护成本,也便于新增芯片时复制模板;
- 以 clang/LTO 为核心的现代工具链:目标架构为杰理私有 pi32v2,
mcpu=r3,全程开启 LTO(-flto),链接时通过lto-wrapper的--plugin-opt系列参数做全局优化、段合并(--gc-sections)、内联阈值控制,以适配 MCU 的有限 Flash/RAM; - 双平台支持:同一份 Makefile 通过
OS变量区分 Windows(C:/JL/pi32/bin,clang.exe)与 Linux(/opt/jieli/pi32v2/bin,clang),并自动切换烧录脚本为.bat或.sh; - 静态库裁剪定制:芯片厂商提供的功能(
printf.a、mkey.a、resfile.a、update.a、vm.a、chargestore.a以及agreement.a、memory.a、sfc_flash.a、efuse.a、clock.a、power.a、dac.a)以预编译.a形式参与链接,应用只需通过--start-group/--end-group按需引用。
使用本构建系统,开发者只需在仓库根目录的 sdk/ 下执行 make ac636n(或 ac638n / ac632n / ac635n)即可完成对应芯片固件的编译与下载;make clean 一键清理全部子工程的临时文件。
Architecture
下图展示工作区目录结构、构建调度关系与工具链/产物流向(节点均对应仓库中实际存在的文件或目录):
flowchart TD
Dev["开发者终端"] -->|"make ac636n / ac638n / ac632n / ac635n"| TopMK["sdk/Makefile<br/>顶层总控"]
TopMK -->|"make -C bsp/AC636N -f Makefile"| BSPMK["bsp/AC636N/Makefile<br/>子工程构建"]
TopMK -.->|"相同方式调度"| BSPMK2["bsp/AC638N / AC632N / AC635N<br/>子工程"]
subgraph sg_BSP["BSP 子工程 Makefile 配置区"]
TC["工具链路径<br/>clang / lto-wrapper / lto-ar"]
CFLG["编译参数<br/>-target pi32v2 -mcpu=r3 -flto -Oz"]
DEF["宏定义<br/>CONFIG_FREE_RTOS_ENABLE 等"]
SRC["源文件清单<br/>apps/main.c + board/ + src/"]
LFLG["链接参数<br/>LTO 插件选项 + sdk.ld 分散加载"]
end
BSPMK --> TC
BSPMK --> CFLG
BSPMK --> DEF
BSPMK --> SRC
BSPMK --> LFLG
TC -->|"调用"| CC["pi32v2 工具链<br/>/opt/jieli/pi32v2/bin 或 C:/JL/pi32/bin"]
CFLG --> CC
SRC --> OBJS["objs/ 中间对象 .o / 依赖 .d"]
LFLG --> LD2["lto-wrapper 链接<br/>--start-group 静态库 --end-group"]
OBJS --> LD2
LD2 --> OUT["output/sdk.elf / sdk.map / sdk.ld"]
OUT -->|"tools/download.sh / download.bat"| FLASH["烧录下载"]
架构要点说明:
- 顶层总控(
sdk/Makefile):仅做两件事——把all/clean目标拆分为四个芯片目标,并用$(MAKE) -C bsp/<chip> -f Makefile递归进入子工程。它本身不定义任何编译规则,是纯粹的任务编排层。 - 子工程 Makefile(
sdk/bsp/<chip>/Makefile):定义全部构建细节,自上而下依次为:工具链路径 → 输出文件 → 编译路径 → 编译参数(CFLAGS/CXXFLAGS)→ 宏定义(DEFINES)→ 头文件路径(INCLUDES)→ 各语言源文件清单 → 链接参数(LFLAGS)→ 系统库 → 对象/依赖文件推导规则。 - 工具链与产物:无论 Linux 还是 Windows,最终都使用同一套 pi32v2 clang 工具链;编译产物统一落在各子工程自己的
output/(链接脚本、ELF、MAP)与objs/(中间对象)下,互不干扰。 - 烧录后处理:链接完成后由
download.sh(Linux)或download.bat(Windows)接管,实现"编译即下载"的一键流程。
工作区目录结构
仓库根目录仅包含 LICENSE、README.md、README-en.md 与 sdk/ 目录;全部固件源码都位于 sdk/ 之下。结合顶层 Makefile 的源文件清单与包含路径,可以还原出如下实际布局:
| 路径 | 角色 | 依据(Makefile 中的引用) |
|---|---|---|
sdk/Makefile | 顶层总控,调度四个芯片子工程 | 全部 -C bsp/<chip> 规则 |
sdk/apps/main.c | 应用层入口(与具体芯片无关) | c_SRC_FILES 首项 ../../apps/main.c |
sdk/bsp/include/ | 跨芯片共享头文件 | INCLUDES 的 -I../../bsp/include |
sdk/bsp/include/liba/<chip>/ | 芯片厂商预编译静态库(printf.a、mkey.a、resfile.a、update.a、vm.a、chargestore.a) | LFLAGS 中 --start-group 的库路径 |
sdk/bsp/<chip>/Makefile | 芯片子工程构建脚本 | 顶层 -f Makefile 调用 |
sdk/bsp/<chip>/board/ | 板级文件(board_demo.c、test.c) | c_SRC_FILES 引用 |
sdk/bsp/<chip>/src/ | 芯片驱动源码(adc.c、boot.c、uart.c、spi.c、timer.c、wdt.c 等)与汇编入口(startup.S、upgrade.S) | c_SRC_FILES / S_SRC_FILES |
sdk/bsp/<chip>/src/*.a | 芯片级链接库(agreement.a、memory.a、sfc_flash.a、efuse.a、clock.a、power.a、dac.a) | LFLAGS --start-group 列表 |
sdk/bsp/<chip>/output/ | 构建产物:sdk.elf、sdk.map、sdk.ld(链接脚本) | OUT_ELF 与 -T/-M 参数 |
sdk/bsp/<chip>/tools/ | 烧录后处理脚本 download.sh / download.bat | POST_SCRIPT / RUN_POST_SCRIPT |
sdk/bsp/dual_bank_update.c、sdk/bsp/msg.c、sdk/bsp/sdk_version.c | 跨芯片共享的 BSP 层源码 | c_SRC_FILES 尾部引用 |
设计意图:把"与芯片无关"的代码(apps/、bsp/ 顶层共享文件)与"芯片相关"的代码(bsp/<chip>/)分离,使新增芯片时只需复制一个子工程目录并调整 Makefile 中的清单,应用层与共享 BSP 无需改动。
顶层构建入口(sdk/Makefile)
顶层 Makefile 是一个纯调度器。它声明了 10 个 PHONY 目标:all、clean 以及四个芯片目标(ac636n、ac638n、ac632n、ac635n)和对应的 clean_<chip>。核心实现如下:
.PHONY: all clean ac636n ac638n ac632n ac635n clean_ac636n clean_ac638n clean_ac632n clean_ac635n
all: ac636n ac638n ac632n ac635n
@echo +ALL DONE
clean: clean_ac636n clean_ac638n clean_ac632n clean_ac635n
@echo +CLEAN DONE
ac636n:
$(MAKE) -C bsp/AC636N -f Makefile
clean_ac636n:
$(MAKE) -C bsp/AC636N -f Makefile clean
Source: sdk/Makefile
要点解读:
all与clean都是并行无关的聚合目标:all顺序构建全部四个芯片,clean逐个清理,最后各打印+ALL DONE/+CLEAN DONE作为完成标记;- 单芯片构建使用
$(MAKE) -C bsp/<chip> -f Makefile递归调用——-C切换工作目录,-f显式指定 Makefile 文件名,子进程 make 继承环境(PATH等由子工程 Makefile 自行导出); - 顶部注释明确了 Linux 下的前置条件:从
http://pkgman.jieliapp.com/doc/all下载工具链并解压到/opt/jieli,保证/opt/jieli/common/bin/clang存在;且ulimit -n需大于 8096,否则链接阶段会因打开文件数超限失败。
核心构建流程
一次 make ac636n 的完整执行路径如下(依据 sdk/Makefile 与 sdk/bsp/AC636N/Makefile 的实际代码):
flowchart TD
Start([make ac636n]) --> Top["sdk/Makefile 目标 ac636n"]
Top --> Sub["make -C bsp/AC636N -f Makefile"]
Sub --> OS{"OS == Windows_NT ?"}
OS -->|"是"| Win["TOOL_DIR=C:/JL/pi32/bin<br/>CC=clang.exe LD=pi32v2-lto-wrapper.exe<br/>POST_SCRIPT=tools/download.bat"]
OS -->|"否"| Lin["TOOL_DIR=/opt/jieli/pi32v2/bin<br/>CC=clang LD=lto-wrapper<br/>EXT_CFLAGS=-D__SHELL__<br/>POST_SCRIPT=tools/download.sh"]
Win --> Cmp["clang -target pi32v2 -mcpu=r3 -flto -Oz 编译<br/>c/S/cpp 各源文件 → objs/ 下 .o 与 .d"]
Lin --> Cmp
Cmp --> Lnk["lto-wrapper 链接<br/>--start-group 静态库 --end-group<br/>-T output/sdk.ld -M output/sdk.map"]
Lnk --> Elf["生成 output/sdk.elf"]
Elf --> Dn{"RUN_POST_SCRIPT 存在?"}
Dn -->|"是"| Dl["执行 download.sh / download.bat 烧录"]
Dn -->|"否"| Done([结束])
Dl --> Done
流程说明:
- 入口:
make ac636n命中顶层目标,递归进入bsp/AC636N; - 平台分支:子工程 Makefile 用
ifeq ($(OS), Windows_NT)区分平台——这决定了工具链目录、可执行文件后缀(.exe)、系统库目录(pi32v2-lib/r3)以及烧录脚本类型,同时 Windows 分支不追加-D__SHELL__(注释明确说明该宏用于 Linux 下正确处理download.c); - 编译:每个源文件独立编译为
objs/下的对象文件,路径映射规则为$(addprefix $(BUILD_DIR)/, $(OBJS:$(ROOT_PREFIX)/%=%)),即去掉../../前缀后平铺到objs/,同时生成.d依赖文件; - 链接:
lto-wrapper按LFLAGS执行 LTO 全局优化、--gc-sections裁剪未用段、按--start-group/--end-group解析静态库循环依赖,产出sdk.elf、sdk.map(内存映射,供排查 Flash/RAM 占用)与链接脚本sdk.ld; - 后处理:若
RUN_POST_SCRIPT已定义(两个平台都会定义),链接后自动执行烧录脚本完成下载。
BSP 子工程 Makefile 深度剖析
以 sdk/bsp/AC636N/Makefile 为例(其余三芯片结构相同),自上而下剖析六个配置区。这是整个构建系统的核心。
1. 工具链路径与平台探测
# 工具路径设置
ifeq ($(OS), Windows_NT)
# Windows 下工具链位置
TOOL_DIR := C:/JL/pi32/bin
CC := clang.exe
CXX := clang.exe
LD := pi32v2-lto-wrapper.exe
AR := llvm-ar.exe
MKDIR := mkdir_win -p
RM := rm -rf
SYS_LIB_DIR := C:/JL/pi32/pi32v2-lib/r3
SYS_INC_DIR := C:/JL/pi32/pi32v2-include
EXT_CFLAGS := # Windows 下不需要 -D__SHELL__
export PATH:=$(TOOL_DIR);$(PATH)
Source: sdk/bsp/AC636N/Makefile
设计意图:
- 依赖环境变量
OS而非 make 内置变量:make 在 Windows 下同样可以定义OS=Windows_NT(由 cmd 环境注入),这是最廉价的跨平台判定方式; - 目录统一在变量层收敛:
TOOL_DIR、SYS_LIB_DIR、SYS_INC_DIR全部在文件头一次性确定,后续 CFLAGS/INCLUDES/LIBPATHS 直接引用,避免散落的硬编码路径; export PATH:把工具链目录注入子进程环境,使download.sh等后处理脚本也能直接找到工具;- 编译完成后统一追加绝对路径前缀:
CC := $(TOOL_DIR)/$(CC),保证即使 PATH 被污染也使用正确工具。
Linux 分支(sdk/bsp/AC636N/Makefile 第 34-54 行)使用 /opt/jieli/pi32v2/bin,并额外导出 OBJDUMP、OBJCOPY、OBJSIZEDUMP 三个工具供链接后分析使用,同时追加 EXT_CFLAGS := -D__SHELL__。
2. 编译参数(CFLAGS / CXXFLAGS)
CFLAGS := \
-target pi32v2 \
-mcpu=r3 \
-integrated-as \
-flto \
-Wuninitialized \
-Wno-invalid-noreturn \
-fno-common \
-Oz \
-g \
-fallow-pointer-null \
-fprefer-gnu-section \
-Wno-shift-negative-value \
-Wframe-larger-than=256 \
-Werror=implicit-function-declaration \
-Werror=macro-redefined \
-Werror=return-type \
-Werror=int-conversion \
-Werror=incompatible-pointer-types \
-Werror=undef \
-fms-extensions
Source: sdk/bsp/AC636N/Makefile
参数意图逐条说明:
| 参数 | 作用 |
|---|---|
-target pi32v2 -mcpu=r3 | 指定杰理私有 32 位 DSP 架构及 R3 内核变体,clang 据此生成指令集 |
-integrated-as | 使用 clang 内置汇编器,无需外部 as,保证与 LTO 中间表示兼容 |
-flto | 全程链接时优化(配合 lto-wrapper),跨编译单元内联/常量传播,显著压缩代码体积 |
-Oz | 以代码尺寸为优先的优化等级——MCU Flash 有限,体积优先于速度 |
-g | 保留调试信息,配合 sdk.map 定位问题 |
-fno-common | 未初始化全局变量不合并为 common,避免链接期符号冲突 |
-fprefer-gnu-section | 每个函数/变量独立成节,是 --gc-sections 能裁剪的前提 |
-Wframe-larger-than=256 | 栈帧超过 256 字节告警,MCU RAM 紧张的早期预警 |
-Werror=... 系列 | 隐式函数声明、宏重定义、返回类型、整型转换、指针类型不匹配、未定义宏一律升级为错误,把"野指针/类型错误"挡在编译期 |
-fms-extensions | 兼容 MS 风格扩展语法(如匿名 struct/union),匹配厂商 SDK 代码风格 |
3. 宏定义(DEFINES)
DEFINES := \
-DSUPPORT_MS_EXTENSIONS \
-D__GCC_PI32V2__ \
-DCONFIG_OS_ENABLE=0 \
-DAPP_USE_SOFT_SPI_ACCESS_VM \
-DCONFIG_FREE_RTOS_ENABLE \
-DCONFIG_RELEASE_ENABLE \
-DCONFIG_MMU_ENABLE
DEFINES += $(EXT_CFLAGS) # 额外的一些定义
Source: sdk/bsp/AC636N/Makefile
这些宏是芯片级功能开关:CONFIG_FREE_RTOS_ENABLE 决定是否启用 FreeRTOS(同时 CONFIG_OS_ENABLE=0 表示关闭厂商自有 OS)、CONFIG_MMU_ENABLE 开启 MMU、CONFIG_RELEASE_ENABLE 进入发布模式、APP_USE_SOFT_SPI_ACCESS_VM 表示用软件 SPI 访问 VM 存储。注意 DEFINES += $(EXT_CFLAGS) 把平台分支追加的 -D__SHELL__ 合入,即 Linux 构建天然多一个编译宏——这是平台差异在源码层的体现,改动宏定义即可切换芯片/功能组合,而无需改动源码。
4. 源文件清单与对象推导
c_SRC_FILES 列出应用入口(apps/main.c)、板级文件(board/board_demo.c、board/test.c)、芯片驱动(src/ 下约 30 个 .c)以及跨芯片共享的 bsp/dual_bank_update.c、bsp/msg.c、bsp/sdk_version.c;S_SRC_FILES 仅两项汇编入口 src/startup.S 与 src/upgrade.S(启动与升级引导)。随后统一推导对象与依赖文件:
c_OBJS := $(c_SRC_FILES:%.c=%.c.o)
S_OBJS := $(S_SRC_FILES:%.S=%.S.o)
OBJS := $(c_OBJS) $(S_OBJS) $(s_OBJS) $(cpp_OBJS) $(cxx_OBJS) $(cc_OBJS)
DEP_FILES := $(OBJS:%.o=%.d)
OBJS := $(addprefix $(BUILD_DIR)/, $(OBJS:$(ROOT_PREFIX)/%=%))
DEP_FILES := $(addprefix $(BUILD_DIR)/, $(DEP_FILES:$(ROOT_PREFIX)/%=%))
Source: sdk/bsp/AC636N/Makefile
机制说明:六类源文件(c/S/s/cpp/cc/cxx)分别做模式替换生成对象名(.c → .c.o,避免同名 .c 与 .cpp 冲突),再统一推导 .d 依赖文件;最后用 $(addprefix $(BUILD_DIR)/, ...) 与 $(ROOT_PREFIX)/%=%(去掉 ../../ 前缀)把对象平铺映射到 objs/ 目录下——这样无论源文件在哪个子目录,中间产物都集中在构建目录,make clean 只需删除 objs/。
5. 链接参数(LFLAGS)与静态库装配
LFLAGS := \
--plugin-opt=-pi32v2-always-use-itblock=false \
--plugin-opt=-enable-ipra=true \
--plugin-opt=-pi32v2-merge-max-offset=4096 \
--plugin-opt=-pi32v2-enable-simd=true \
--plugin-opt=mcpu=r3 \
--plugin-opt=-global-merge-on-const \
--plugin-opt=-inline-threshold=5 \
--plugin-opt=-inline-max-allocated-size=32 \
--plugin-opt=-inline-normal-into-special-section=true \
--plugin-opt=-dont-used-symbol-list=malloc,free,sprintf,printf,puts,putchar \
--plugin-opt=save-temps \
--plugin-opt=-pi32v2-enable-rep-memop \
--plugin-opt=-warn-stack-size=256 \
--sort-common \
--dont-complain-call-overflow \
--gc-sections \
--start-group \
../../bsp/include/liba/AC636N/printf.a \
../../bsp/include/liba/AC636N/mkey.a \
../../bsp/include/liba/AC636N/resfile.a \
../../bsp/include/liba/AC636N/update.a \
../../bsp/include/liba/AC636N/vm.a \
../../bsp/include/liba/AC636N/chargestore.a \
../../bsp/AC636N/src/agreement.a \
../../bsp/AC636N/src/memory.a \
../../bsp/AC636N/src/sfc_flash.a \
../../bsp/AC636N/src/efuse.a \
../../bsp/AC636N/src/clock.a \
../../bsp/AC636N/src/power.a \
../../bsp/AC636N/src/dac.a \
--end-group \
-T../../bsp/AC636N/output/sdk.ld \
-M=../../bsp/AC636N/output/sdk.map \
--plugin-opt=mcpu=r3 \
--plugin-opt=-mattr=+fprev1
Source: sdk/bsp/AC636N/Makefile
设计意图:
--plugin-opt系列是 LTO 插件的细粒度控制:-enable-ipra(跨过程寄存器分配)、-global-merge-on-const(常量段全局合并)、-inline-threshold=5与-inline-max-allocated-size=32(严格限制内联规模,防止代码膨胀)、-warn-stack-size=256(链接期栈用量预警)、-pi32v2-merge-max-offset=4096(寻址偏移合并,配合 R3 内核的 12 位立即数寻址);--dont-used-symbol-list:显式告知链接器malloc/free/sprintf/printf/puts/putchar等标准库函数即使被引用也不作 LTO 内联,避免厂商库与 libc 的符号冲突;--start-group/--end-group:让 13 个静态库互相解析循环引用(例如vm.a依赖chargestore.a,agreement.a又引用update.a),这是厂商库装配的标准手法;-T sdk.ld -M sdk.map:使用芯片分散加载脚本(output/sdk.ld)布局 Flash/RAM 段,并输出内存映射表供分析;- 系统库(
libm.a、libc.a、libcompiler-rt.a)来自SYS_LIB_DIR(-L指定),与厂商库分开管理。
6. 构建产物与清理
产物统一输出到 ../../bsp/AC636N/output/:
| 产物 | 说明 |
|---|---|
sdk.elf | 最终固件 ELF(含调试信息) |
sdk.ld | 链接脚本(分散加载描述,位于 output 目录) |
sdk.map | 链接内存映射,用于排查 Flash/RAM 占用与符号地址 |
objs/*.o / *.d | 中间对象与自动依赖文件(make clean 删除) |
Makefile 头部注释同时声明了三个常用目标:make(编译并下载)、make VERBOSE=1(显示编译详细过程)、make clean(清除编译临时文件)。
使用示例
示例 1:编译并下载指定芯片固件
在 sdk/ 目录下执行(all 目标会依次构建全部四款芯片):
.PHONY: all clean ac636n ac638n ac632n ac635n clean_ac636n clean_ac638n clean_ac632n clean_ac635n
all: ac636n ac638n ac632n ac635n
@echo +ALL DONE
ac636n:
$(MAKE) -C bsp/AC636N -f Makefile
clean_ac636n:
$(MAKE) -C bsp/AC636N -f Makefile clean
Source: sdk/Makefile
# 编译并下载 AC636N 固件(等价于进入 bsp/AC636N 执行 make)
make ac636n
# 显示详细编译过程(透传 VERBOSE 到子工程)
make VERBOSE=1
# 只编译不下载/清理
make -C bsp/AC636N -f Makefile clean
# 一次构建全部芯片,或全部清理
make all
make clean
示例 2:Linux/Windows 双平台工具链分支
同一份 Makefile 依据 OS 变量选择工具链目录与烧录脚本,这是整个构建系统跨平台工作的核心:
ifeq ($(OS), Windows_NT)
# Windows 下工具链位置
TOOL_DIR := C:/JL/pi32/bin
CC := clang.exe
CXX := clang.exe
LD := pi32v2-lto-wrapper.exe
AR := llvm-ar.exe
...
POST_SCRIPT := ../../bsp/AC636N/tools/download.bat
RUN_POST_SCRIPT := ..\..\bsp\AC636N\tools\download.bat
else
# Linux 下工具链位置
TOOL_DIR := /opt/jieli/pi32v2/bin
CC := clang
CXX := clang
LD := lto-wrapper
AR := lto-ar
...
POST_SCRIPT := ../../bsp/AC636N/tools/download.sh
RUN_POST_SCRIPT := bash $(POST_SCRIPT)
endif
Source: sdk/bsp/AC636N/Makefile
差异汇总:Windows 使用 C:/JL/pi32/bin、clang.exe、llvm-ar.exe、download.bat,且不追加 -D__SHELL__;Linux 使用 /opt/jieli/pi32v2/bin、clang、lto-ar、download.sh,追加 -D__SHELL__ 并额外导出 OBJDUMP/OBJCOPY/OBJSIZEDUMP。
示例 3:通过宏定义切换功能组合
修改 DEFINES 即可在编译期切换芯片功能,无需改动任何 C 源码:
DEFINES := \
-DSUPPORT_MS_EXTENSIONS \
-D__GCC_PI32V2__ \
-DCONFIG_OS_ENABLE=0 \
-DAPP_USE_SOFT_SPI_ACCESS_VM \
-DCONFIG_FREE_RTOS_ENABLE \
-DCONFIG_RELEASE_ENABLE \
-DCONFIG_MMU_ENABLE
DEFINES += $(EXT_CFLAGS) # 额外的一些定义
Source: sdk/bsp/AC636N/Makefile
例如:去掉 -DCONFIG_FREE_RTOS_ENABLE 可编译无 RTOS 的裸机版本;平台分支自动注入的 -D__SHELL__ 会让 download.c 在 Linux 下走正确的下载逻辑。
配置选项
以下为 sdk/bsp/<chip>/Makefile 中可直接修改的构建配置(变量名即选项名,均为字符串/路径类型):
| 选项 | 类型 | 默认值(AC636N / Linux) | 说明 |
|---|---|---|---|
TOOL_DIR | 路径 | /opt/jieli/pi32v2/bin(Win: C:/JL/pi32/bin) | 工具链根目录 |
CC / CXX | 命令 | clang(Win: clang.exe) | C/C++ 编译器 |
LD | 命令 | lto-wrapper(Win: pi32v2-lto-wrapper.exe) | LTO 链接器 |
AR | 命令 | lto-ar(Win: llvm-ar.exe) | 归档工具 |
SYS_LIB_DIR | 路径 | /opt/jieli/pi32v2/lib/r3 | 系统库目录(libc/libm/compiler-rt) |
SYS_INC_DIR | 路径 | /opt/jieli/pi32v2/include | 系统头文件目录 |
EXT_CFLAGS | 宏 | -D__SHELL__(Win: 空) | 平台附加宏定义 |
OUT_ELF | 路径 | ../../bsp/AC636N/output/sdk.elf | ELF 输出路径 |
BUILD_DIR | 路径 | objs | 中间对象目录 |
ROOT_PREFIX | 路径 | ../.. | 源路径前缀(对象映射时剥离) |
CFLAGS | 参数列表 | -target pi32v2 -mcpu=r3 -flto -Oz -g ... | 编译参数 |
DEFINES | 宏列表 | CONFIG_FREE_RTOS_ENABLE、CONFIG_MMU_ENABLE、CONFIG_RELEASE_ENABLE 等 | 功能开关 |
INCLUDES | 路径列表 | -I../../bsp/include -I../../bsp/AC636N/include -I$(SYS_INC_DIR) | 头文件搜索路径 |
c_SRC_FILES / S_SRC_FILES 等 | 文件列表 | 见上节清单 | 参与编译的源文件 |
LFLAGS | 参数列表 | LTO 插件选项 + --gc-sections + 库组 + -T sdk.ld -M sdk.map | 链接参数 |
LIBS | 文件列表 | libm.a、libc.a、libcompiler-rt.a | 链接的系统库 |
POST_SCRIPT / RUN_POST_SCRIPT | 命令 | tools/download.sh / bash $(POST_SCRIPT) | 构建后烧录脚本 |
顶层 sdk/Makefile 的配置面更小:新增芯片时只需仿照现有目标追加一个 <chip> 与 clean_<chip> PHONY 目标(并加入 all/clean 依赖列表)。
命令参考
命令(在 sdk/ 下执行) | 行为 |
|---|---|
make ac636n / make ac638n / make ac632n / make ac635n | 编译并烧录对应芯片 |
make all | 依次构建全部四款芯片 |
make clean | 清理全部芯片的临时文件 |
make clean_ac636n | 仅清理 AC636N 子工程 |
make VERBOSE=1 | 显示详细编译过程(透传至子工程) |
make -C bsp/AC636N -f Makefile clean | 绕过顶层直接操作子工程 |
失败模式与边界情况
以下问题均能在 Makefile 注释或参数中直接找到依据:
工具链缺失或路径错误
- 现象:
make报clang: command not found或找不到/opt/jieli/pi32v2/bin/clang; - 依据:
sdk/Makefile顶部注释要求工具链从http://pkgman.jieliapp.com/doc/all下载并解压到/opt/jieli,且必须保证/opt/jieli/common/bin/clang存在(注意目录层次); - 处置:检查
TOOL_DIR是否正确,Linux 下确认解压层次为/opt/jieli/pi32v2/bin/clang。
链接期打开文件数超限
- 现象:链接阶段失败,报 "too many open files" 类错误;
- 依据:顶层 Makefile 注释明确要求
ulimit -n大于 8096,原因在于 LTO 链接需要同时打开大量中间文件; - 处置:执行
ulimit -n 8096(或更大)后重新make。
平台分支不一致
- 现象:Windows 下烧录脚本执行失败,或 Linux 下
download.c行为异常; - 依据:Makefile 通过
ifeq ($(OS), Windows_NT)分支,且EXT_CFLAGS在 Linux 下追加-D__SHELL__(注释说明"Linux 下需要这个保证正确处理 download.c"); - 边界:若在非 Windows 环境误设
OS=Windows_NT,会选错工具链后缀与脚本类型,导致构建失败——平台切换只应通过真实环境变量进行。
编译警告升级为错误
- 现象:
implicit-function-declaration、macro-redefined、return-type、int-conversion、incompatible-pointer-types、undef任一出现即终止编译; - 依据:
CFLAGS中-Werror=系列参数; - 设计意图:MCU 固件对代码正确性要求苛刻,这类错误一旦混入固件会导致难以调试的运行时崩溃,因此从源头阻断。
栈/内存超限预警
- 现象:编译告警
-Wframe-larger-than=256、链接告警--plugin-opt=-warn-stack-size=256; - 依据:两个阈值均为 256 字节;
- 说明:这只是告警(除非配合
-Werror),提示栈帧可能超出 R3 内核的可用栈空间,属于早期风险信号而非硬失败。
性能与运维注意事项
- 全量 LTO 的代价:
-flto+--gc-sections+ 13 个静态库的--start-group解析,使链接阶段计算量大、打开文件多——这正是ulimit -n必须调大的原因;增量开发建议只构建目标芯片(make ac636n)而非make all; - 中间产物可复用:
objs/下的.o/.d支持增量编译,make clean只清理该目录;output/下的sdk.elf/sdk.map是链接产物,建议纳入版本管理审查以跟踪固件体积变化; - 体积优化是显式目标:
-Oz、-fprefer-gnu-section、-global-merge-on-const、-inline-threshold=5组合表明该平台以 Flash 容量为首要约束;引入新代码时优先复用include/liba/<chip>/中的厂商库,避免重复实现导致体积膨胀; - 后处理脚本可旁路:若只需编译不烧录,可临时注释
RUN_POST_SCRIPT或在链接后中断,避免开发机上反复占用下载工具。
扩展点
- 新增芯片子工程:复制
bsp/AC636N为bsp/ACxxxxN,替换output/sdk.ld链接脚本、board/板级文件、src/驱动与include/liba/<chip>/库路径,再在顶层sdk/Makefile追加acxxxxn:/clean_acxxxxn:目标并登记到all/clean(注意同步AC636N→ 新芯片名的路径替换)。 - 追加源文件:向
c_SRC_FILES(或S_SRC_FILES/cpp_SRC_FILES等)追加路径即可,对象映射规则会自动把它平铺到objs/并生成.d依赖;跨芯片共享代码应放在bsp/顶层(如dual_bank_update.c、msg.c),芯片相关代码放bsp/<chip>/src/。 - 调整功能宏:增删
DEFINES中的CONFIG_*宏切换 RTOS/MMU/Release 等特性;新增平台相关宏可仿照EXT_CFLAGS机制按OS分支注入。 - 替换/扩展烧录流程:
POST_SCRIPT与RUN_POST_SCRIPT是两个独立变量——前者记录脚本路径,后者定义实际执行命令(Linux 为bash $(POST_SCRIPT)),可替换为自定义脚本或 CI 上传命令。
Related Links
- sdk/Makefile(顶层构建总控)
- sdk/bsp/AC636N/Makefile(子工程构建模板)
- sdk/bsp/AC638N/Makefile
- sdk/bsp/AC632N/Makefile
- sdk/bsp/AC635N/Makefile
- 仓库根目录 README.md 与 README-en.md
相关主题提示:芯片驱动实现(
bsp/<chip>/src/)与 SDK 组件(VM、充电、升级等静态库)的内部机制不属于本页范围,详见各自的 BSP 驱动与 SDK 组件页面。