make PROGRAM=application/baremetal/helloworld SOC=gd32vf103 BOARD=gd32vf103c_longan_nano all
# 默认代码编译路径
PROGRAM :=baremetal/helloworld
# help信息
.PHONY: __help
__help:
@echo "Help about Build/Run/Debug/Clean Nuclei SDK Application"
@echo "make [PROGRAM=/path/to/app] help Show Build System Help Message"
@echo "make [EXTRA_APP_ROOTDIRS=/path/to/extraapps] cleanall Clean all the applications"
@echo "Examples:"
@echo "make PROGRAM=application/baremetal/helloworld all"
@echo "make PROGRAM=application/baremetal/helloworld SOC=gd32vf103 BOARD=gd32vf103v_rvstar clean all"
@echo "make -k cleanall"
@echo "make -k SOC=gd32vf103 BOARD=gd32vf103v_rvstar cleanall"
@echo "make -k EXTRA_APP_ROOTDIRS=soc_testcases cleanall"
# wildcard可以判读编译代码的路径是否存在,不存在返回空
VALID_PROGRAM=$(wildcard $(PROGRAM))
# 判断编译代码中是否有Makefile文件
VALID_PROGRAM_MAKEFILE=$(wildcard $(PROGRAM)/Makefile)
# make的时候,只有下面的这些命令是有效的
# Valid SDK Rules accepted by build system
VALID_SDK_RULES := all info help bin size dasm upload run_openocd run_gdb clean debug
# 指定app源码的根目录
# Default root directories to search
APP_ROOTDIRS := application test
# Extra application root directories passed by make
# 其他app 源码根目录,适合放玩家自己的app
EXTRA_APP_ROOTDIRS ?=
# TOTAL_ROOTDIRS中记录所有的app根目录
# get all the root directories for applications
TOTAL_ROOTDIRS := $(APP_ROOTDIRS) $(EXTRA_APP_ROOTDIRS)
# 字面意思就是默认的搜索模式
# 实质就是在要搜索的路径下面加上这些
# 比如搜索app/
# 那么就会搜索app/* app/*/* app/*/*/* ...
# Default search patterns
SEARCH_PATTERNS := * */* */*/* */*/*/*
# 首先说一下foreach函数 $(foreach <var>,<list>,<text>)
# 把list中的单词逐一取出放到参数var所指定的变量中,然后执行text包含的表达式
# addprefix函数就是添加前缀 $(addprefix fixstring,string1 string2 ...)
# 其中fixstring表示要添加的前缀,逗号后则是要添加前缀的字符串
# 所以这里就是分别取出application test作为前缀application/ test/
# 最后编程 application/* application/*/* ...
# test也一样
# 以上信息保存到PROGS_TO_SEARCH
PROGS_TO_SEARCH := $(foreach rootdir, $(TOTAL_ROOTDIRS), $(addprefix $(rootdir)/, $(SEARCH_PATTERNS)))
# sort函数是排序函数 $(sort LIST)
# 给字串“LIST”中的单词以首字母为准进行排序(升序),并取掉重复
# dir函数 $(dir Names)
# 从文件名序列Name中取出目录部分,也就是最后一个/之前的部分
# 比如$(dir src/test.c) 这里返回的就是 src/
# 总的意思就是application/* application/*/* 找到这些路径下面的makefile
# 并进行一个升序排序,升序后返回其所在的目录的路径
PROGS_makefile := $(foreach progdir, $(PROGS_TO_SEARCH), $(sort $(dir $(wildcard $(progdir)/makefile))))
# 这里同上面一样,不过找的是Makefile文件
PROGS_Makefile := $(foreach progdir, $(PROGS_TO_SEARCH), $(sort $(dir $(wildcard $(progdir)/Makefile))))
# 将makefile和Makefile所在目录进行升序排序
PROGS_DIRS := $(sort $(PROGS_makefile) $(PROGS_Makefile))
# 将有makefile和Makefile的目录加上__CLEAN__的前缀
# 比如之前application/baremetal/demo_dsp/
# 加上前缀后变成 __CLEAN__application/baremetal/demo_dsp/
CLEAN_DIRS_RULES := $(addprefix __CLEAN__, $(PROGS_DIRS))
# 如果要编译的源码下面找不到Makefile
ifeq ($(VALID_PROGRAM_MAKEFILE), )
# 则指定一个默认的app
APP_PROGRAM=application/$(PROGRAM)
# 判断新的app是否存在
VALID_PROGRAM=$(wildcard $(APP_PROGRAM))
# 新的app存在的话是不是有Makefile
VALID_PROGRAM_MAKEFILE=$(wildcard $(APP_PROGRAM)/Makefile)
# 如果新的app还是没有Makefile,则打印下面的信息
ifeq ($(VALID_PROGRAM_MAKEFILE), )
$(error No valid Makefile in $(PROGRAM) directory! please check!)
endif
endif
# 如果make的时候输入的是cleanall
cleanall: $(CLEAN_DIRS_RULES)
# cleanall的动作
# $(patsubst <pattern>,<replacement>,<text>)
# 模式字符串替换函数——patsubst
# 找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)
# 是否符合模式<pattern>,如果匹配的话,则以<replacement>替换
# 以$(patsubst %.c,%.o,$(dir))为例,patsubst把$(dir)中的变量符合
# 后缀是.c的全部替换成.o
# 这里就是对每个有Makefile和makefile的目录执行 make -C xxxx clean
$(CLEAN_DIRS_RULES):
make -C $(patsubst __CLEAN__%, %, $@) clean
# 如果不是cleanall其是有效的命令,则执行下面的操作
# 比如是all
# 则对要编译的源码执行 make -C xxxx all
$(VALID_SDK_RULES):
make -C $(VALID_PROGRAM) $@
# 要编译生成的目标名字是helloworld
TARGET = helloworld
# 指定sdk的根目录
NUCLEI_SDK_ROOT = ../../..
# SRCDIRS这里指定源码路径是 当前 . 和 src目录
SRCDIRS = . src
# 同上
INCDIRS = . inc
# 编译相关忽略
COMMON_FLAGS := -O2
# 导入Makefile.base
include $(NUCLEI_SDK_ROOT)/Build/Makefile.base
# 指定sdk的Build路径
NUCLEI_SDK_BUILD = $(NUCLEI_SDK_ROOT)/Build
# Include your local and global makefile variables
# SOC, DOWNLOAD, PFLOAT, NEWLIB etc.
# Makefile.global should be placed in $(NUCLEI_SDK_ROOT)/Build
# Makefile.local should be placed together with application Makefile in your own application folder
# Sample content for this Makefile.local or Makefile.global
# SOC ?= demosoc
# DOWNLOAD ?= ilm
# 这里解释一下Makefile中的 = := ?= += 这些东西
#
# = 最普通的赋值,这个赋值容易搞错,注意这个值是最后被指定的值
# 举例:
# VIR_A = 1
# VIR_B = $(VIR_A) 2
# VIR_A = 3
# 经过赋值操作后,VIR_B的值是3 2,而不是 1 2
# 这里就是最后指定的值的意思
#
# := 直接赋值,且赋予的是当前位置的值
# 举例:
# VIR_A = 1
# VIR_B = $(VIR_A) 2
# VIR_A = 3
# 经过赋值操作后,VIR_B的值是 1 2
#
# ?= 表示如果该变量没有被赋值,则赋予等号后的值
# 举例:
# VIR_A ?= 1
# 如果make的时候传入VIR_A=2
# 或者之前有定义 VIR_A := 2
# 那么VIR_A值为2,否则为1
#
# += 表示将等号后的值添加到前面的变量上
# 举例:
# VIR_A := 1
# VIR_A += 2
# 那么VIR_A的值为 1 2
#
# 这里会去helloworld下面找Makefile.local
# 以及Build下面找Makefile.global
# 如果有的话,找到后记录在EXTRA_MKS
EXTRA_MKS := $(wildcard Makefile.local $(NUCLEI_SDK_BUILD)/Makefile.global)
# strip去掉空格函数 $(strip STRINT)
# 去掉字串(若干单词,使用若干空字符分割) “STRINT”开头和结尾的
# 空字符,并将其中多个连续空字符合并为一个空字符。
# 举例:
# STR = a b c
# LOSTR = $(strip $(STR))
# 结果是"a b c"
# 这里判断如果找到了Makefile.local和Makefile.global
# 那么将其包含进来
ifneq ("$(strip $(EXTRA_MKS))", "")
$(info Obtaining addtional make variables from $(EXTRA_MKS))
include $(EXTRA_MKS)
endif
# 下面的注释比较清晰,这里就不再汉语分析
# Variables could be passed in make command
# NOTE: CORE and BOARD are defined in Makefile.soc.$(SOC)
# BOARD and SOC name should always be lower-case
## Available choices:
## The name of sub directories in $(NUCLEI_SDK_ROOT)/SoC/
SOC ?= demosoc
## Available choices:
## ilm: Program will be download into ilm/ram and run directly in ilm/ram, program lost when poweroff
## flash: Program will be download into flash, when running, program will be copied to ilm/ram and run in ilm/ram
## flashxip: Program will to be download into flash and run directly in Flash
DOWNLOAD ?= ilm
## If SIMULATION=1, it means the program is optimized for hardware simulation environment
SIMULATION ?= 0
## If V=1, it will display compiling message in verbose including compiling options
V ?=
## If SILENT=1, it will not display any compiling messsage
SILENT ?=
# Variables should be defined in Application Makefile
## Available choices:
## The name of sub directories in $(NUCLEI_SDK_ROOT)/OS/
RTOS ?=
## If PFLOAT=1, it will enable float point print when using nano newlib
PFLOAT ?= 0
## If NEWLIB=nano, it will use nano newlib, otherwise it will use normal newlib
NEWLIB ?= nano
## If NOGC=1, it will not gc any sections during compiling to save code size
NOGC ?=
# 这里指定soc和rtos的路径
# Directories for SoC and RTOS chosen
NUCLEI_SDK_SOC = $(NUCLEI_SDK_ROOT)/SoC/$(SOC)
NUCLEI_SDK_RTOS = $(NUCLEI_SDK_ROOT)/OS/$(RTOS)
## Include GNU Make Standard Library
## Website: http://gmsl.sourceforge.net/
# GNU Make 标准库 (GMSL) 是使用本机 GNU Make功能实现的函数集合,
# 这些功能提供列表和字符串操作、整数运算、关联数组、堆栈和调试工具
# 这里用到再说
include $(NUCLEI_SDK_BUILD)/gmsl/gmsl
# 下面分别分析 Makefile.misc Makefile.conf Makefile.rules
include $(NUCLEI_SDK_BUILD)/Makefile.misc
include $(NUCLEI_SDK_BUILD)/Makefile.conf
include $(NUCLEI_SDK_BUILD)/Makefile.rules
# 注意这里用一些变量代码了一些func操作
## Small Functions ##
get_csrcs = $(foreach subdir, $(1), $(wildcard $(subdir)/*.c $(subdir)/*.C))
get_asmsrcs = $(foreach subdir, $(1), $(wildcard $(subdir)/*.s $(subdir)/*.S))
get_cxxsrcs = $(foreach subdir, $(1), $(wildcard $(subdir)/*.cpp $(subdir)/*.CPP))
check_item_exist = $(strip $(if $(filter 1, $(words $(1))),$(filter $(1), $(sort $(2))),))
# 对于windows 这里忽略
###
# For Windows, in Win9x, COMSPEC is defined, WinNT, ComSpec is defined
###
ifdef ComSpec
WINCMD:=$(ComSpec)
endif
ifdef COMSPEC
WINCMD:=$(COMSPEC)
endif
ifneq "$(WINCMD)" ""
ifneq "$(findstring /cygdrive/,$(PATH))" ""
HOST_OS:=Cygwin
else
HOST_OS:=Windows
endif
else
# 这里才是非windows的情况
# shell uname, ubuntu下返回linux
HOST_OS:=$(shell uname)
endif
##
# Define one space
##
# nullstring值为一个空格
nullstring=
space=$(nullstring) # one space
# 定义一些变量
RM=rm -rf
RMD=rm -rf
ECHO=echo
CP=cp -rf
MKD = mkdir -p
PS=/$(nullstring)
NULL=/dev/null
## Check OS ##
## Check OS == Windows ##
ifeq "$(HOST_OS)" "Windows"
PS=\$(nullstring)
NULL=NUL
DOS_CMD=$(WINCMD) /C
# when OS is windows, force SHELL to be cmd
# or if in your evironment path there is
# a mingw shell, the make process will go wrong
SHELL:=$(WINCMD)
endif
## Check OS == Linux ##
ifeq "$(HOST_OS)" "Linux"
PS=/$(nullstring)
NULL=/dev/null
endif
## Check OS == Darwin ##
ifeq "$(HOST_OS)" "Darwin"
PS=/$(nullstring)
NULL=/dev/null
endif
# V是在Makefile.base中定义的
# @符合的作用
# 如果将‘@’添加到命令行前,这个命令将不被make回显出来
# 注意是命令不被回显
# 举例:
# @echo --compiling module----
# 屏幕输出 --compiling module----
# 没有@
# 屏幕输出 echo --compiling module----
## MAKEFILE COMPILE MESSAGE CONTROL ##
ifeq ($(V),1)
Q=
else
Q=@
endif
# SILENT 沉默的意思
# 就是以后使用下面的命令打印还是不打印的效果
## Suppress All Message ##
ifeq ($(SILENT), 1)
TRACE_CREATE_DIR =
TRACE_COMPILE =
TRACE_ASSEMBLE =
TRACE_LINK =
TRACE_ARCHIVE =
## Overwrite Q Value set by V option ##
override Q=@
else
TRACE_CREATE_DIR = @$(ECHO) "Creating Directory : " $(@D)
TRACE_COMPILE = @$(ECHO) "Compiling : " $<
TRACE_ASSEMBLE = @$(ECHO) "Assembling : " $<
TRACE_LINK = @$(ECHO) "Linking : " $@
TRACE_ARCHIVE = @$(ECHO) "Archiving : " $@
endif
## NUCLEI RISCV GCC COMPILER
## NUCLEI RISCV OPENOCD
#
#!< Nuclei SDK Tools Root
# 指定交叉编译器的前缀
COMPILE_PREFIX ?= riscv-nuclei-elf-
# 以下是官方期望的sdk_tool gcc openocd的路径
# 我们都没有...
NUCLEI_SDK_TOOL_ROOT ?= $(NUCLEI_SDK_ROOT)/prebuilt_tools
NUCLEI_RISCV_GCC_ROOT ?= $(NUCLEI_SDK_TOOL_ROOT)/gcc
NUCLEI_OPENOCD_ROOT ?= $(NUCLEI_SDK_TOOL_ROOT)/openocd
# 如果gcc openocd 目录都存在,则NUCLEI_SDK_TOOL_ROOT_EXIST值为1
# 否则为0
NUCLEI_SDK_TOOL_ROOT_EXIST = 0
ifneq ($(wildcard $(NUCLEI_RISCV_GCC_ROOT)),)
ifneq ($(wildcard $(NUCLEI_OPENOCD_ROOT)),)
NUCLEI_SDK_TOOL_ROOT_EXIST = 1
endif
endif
# gcc openocd 目录都存在的情况
# 找到下面这些命令的绝对路径
ifeq ($(NUCLEI_SDK_TOOL_ROOT_EXIST),1)
CC := $(abspath $(NUCLEI_RISCV_GCC_ROOT)/bin/$(COMPILE_PREFIX)gcc)
CXX := $(abspath $(NUCLEI_RISCV_GCC_ROOT)/bin/$(COMPILE_PREFIX)g++)
OBJDUMP := $(abspath $(NUCLEI_RISCV_GCC_ROOT)/bin/$(COMPILE_PREFIX)objdump)
OBJCOPY := $(abspath $(NUCLEI_RISCV_GCC_ROOT)/bin/$(COMPILE_PREFIX)objcopy)
GDB := $(abspath $(NUCLEI_RISCV_GCC_ROOT)/bin/$(COMPILE_PREFIX)gdb)
AR := $(abspath $(NUCLEI_RISCV_GCC_ROOT)/bin/$(COMPILE_PREFIX)ar)
SIZE := $(abspath $(NUCLEI_RISCV_GCC_ROOT)/bin/$(COMPILE_PREFIX)size)
OPENOCD := $(abspath $(NUCLEI_OPENOCD_ROOT)/bin/openocd)
else
# 由于我们不配合官方,所以走这里,都加上了前缀
CC := $(COMPILE_PREFIX)gcc
CXX := $(COMPILE_PREFIX)g++
OBJDUMP := $(COMPILE_PREFIX)objdump
OBJCOPY := $(COMPILE_PREFIX)objcopy
GDB := $(COMPILE_PREFIX)gdb
AR := $(COMPILE_PREFIX)ar
SIZE := $(COMPILE_PREFIX)size
OPENOCD := openocd
endif
# 导入 Makefile.soc
include $(NUCLEI_SDK_BUILD)/Makefile.soc
# 我们传入的soc是gd32vf103
ifeq ($(SOC),hbird)
$(warning SOC hbird is renamed to demosoc since Nuclei SDK 0.3.1, please use SOC=demosoc now)
override SOC := demosoc
endif
# 这里会导入 Makefile.soc.gd32vf103
include $(NUCLEI_SDK_BUILD)/Makefile.soc.$(SOC)
# BOARD是gd32vf103c_longan_nano
BOARD ?= gd32vf103v_rvstar
# variant 板子的变体 比如v1 v2等等
# Variant for Board or SoC
VARIANT ?=
# 这里指定CORE 和 DOWNLOAD
# override DOWNLOAD and CORE variable for GD32VF103 SoC
# even though it was set with a command argument
override CORE := n205
override DOWNLOAD := flashxip
# 记录板子路径和通用代码路径
GD32VF103_SDK_SOC_BOARD=$(NUCLEI_SDK_SOC)/Board/$(BOARD)
GD32VF103_SDK_SOC_COMMON=$(NUCLEI_SDK_SOC)/Common
# gd32vf103上没有ilm
# 而且板子gd32vf103c_longan_nano上有2种gd32vf103
# 分别是gd32vf103x8和gd32vf103xb
# 其中
# gd32vf103x8 是64k flash + 20k ram
# gd32vf103xb 是128k flash + 32k ram
# 手里的板子是gd32vf103xb
#no ilm on gd32vf103 SoC
ifeq ($(BOARD), gd32vf103c_longan_nano)
ifeq ($(VARIANT), lite)
LINKER_SCRIPT ?= $(GD32VF103_SDK_SOC_BOARD)/Source/GCC/gcc_gd32vf103x8_flashxip.ld
else
# 对应的链接脚本是 gcc_gd32vf103xb_flashxip.ld
LINKER_SCRIPT ?= $(GD32VF103_SDK_SOC_BOARD)/Source/GCC/gcc_gd32vf103xb_flashxip.ld
endif
else
LINKER_SCRIPT ?= $(GD32VF103_SDK_SOC_BOARD)/Source/GCC/gcc_gd32vf103_flashxip.ld
endif
# OPENCD 这里不关心
OPENOCD_CFG ?= $(GD32VF103_SDK_SOC_BOARD)/openocd_gd32vf103.cfg
# 将core值n205转换成大写U205放到CORE_UPPER中
CORE_UPPER = $(call uc, $(CORE))
RISCV_ARCH ?= rv32imac
RISCV_ABI ?= ilp32
# Extra openocd and gdb commands
GDB_UPLOAD_CMDS += -ex "monitor halt"
# 删除了gdb相关的东西
...
# 列出了所有支持的下载模式
## ilm: 程序会下载到ilm/ram中直接在ilm/ram中运行,掉电程序丢失
## flash: 程序会被下载到flash中,运行时,程序会被复制到ilm/ram并在ilm/ram中运行
## flashxip: 程序将被下载到闪存中并直接在闪存中运行
SUPPORT_DOWNLOAD_MODES=flash flashxip ilm ddr
# 之前有个gsml的东西,uc就是来自那里
# 这里的意思就是将DOWNLOAD的变量换成全部大写
# DOWNLOAD变量来自Makefile.soc 值为flashxip
# 同样这里是变成大写 FLASHXIP
DOWNLOAD_UPPER = $(call uc, $(DOWNLOAD))
# 将所有支持的模式变成大写
SUPPORT_DOWNLOAD_MODES_UPPER=$(call uc, $(SUPPORT_DOWNLOAD_MODES))
# check_item_exist是在 Makefile.misc中定义
# $(strip $(if $(filter 1, $(words $(1))),$(filter $(1), $(sort $(2))),))
# filter 过滤函数 $(filter, )
# 以模式过滤字符串中的单词,保留符合模式的单词。可以有多个模式
# words 测试了一下 $(words xxx) 返回的是xxx中字符串的个数
# 返回符合模式的字串
# $(words $(DOWNLOAD_UPPER)) 返回值是1
# $(filter 1, $(words $(DOWNLOAD_UPPER))) 这里就相当于从1中找1,返回1
# if函数 $(if <condition>,<then-part>,<else-part> )
# $(if 1, $(filter $(1), $(sort $(2))),)
# if条件满足了,才会执行逗号后面的语句
# 从升序后的$(SUPPORT_DOWNLOAD_MODES_UPPER)中过滤$(DOWNLOAD_UPPER)
# 其返回值就是FLASHXIP
# 最后 $(strip FLASHXIP)
# strip去掉空格函数,之前分析过
# 所以这里最后返回的就是FLASHXIP
VALID_DOWNLOAD = $(call check_item_exist, $(DOWNLOAD_UPPER), $(SUPPORT_DOWNLOAD_MODES_UPPER))
# 导入Makefile.files
include $(NUCLEI_SDK_BUILD)/Makefile.files
# 导入Makefile.rtos
include $(NUCLEI_SDK_BUILD)/Makefile.rtos
#添加NMSIS相关头文件路径
INCDIRS += $(NUCLEI_SDK_ROOT)/NMSIS/Core/Include
C_INCDIRS +=
CXX_INCDIRS +=
ASM_INCDIRS +=
SRCDIRS +=
C_SRCDIRS +=
CXX_SRCDIRS +=
ASM_SRCDIRS +=
C_SRCS +=
CXX_SRCS +=
ASM_SRCS +=
LIBDIRS +=
# 导入Makefile.files.gd3vf103
include $(NUCLEI_SDK_BUILD)/Makefile.files.$(SOC)
# 添加common的头文件路径
INCDIRS += $(GD32VF103_SDK_SOC_COMMON)/Include
# 添加common源文件
C_SRCDIRS += $(GD32VF103_SDK_SOC_COMMON)/Source $(GD32VF103_SDK_SOC_COMMON)/Source/Drivers \
$(GD32VF103_SDK_SOC_COMMON)/Source/Stubs
# 这里是usb相关的支持 不分析
ifeq ($(USB_DRV_SUPPORT), 1)
INCDIRS += $(GD32VF103_SDK_SOC_COMMON)/Include/Usb
C_SRCDIRS += $(GD32VF103_SDK_SOC_COMMON)/Source/Drivers/Usb
endif
# 添加汇编文件
ASM_SRCS += $(GD32VF103_SDK_SOC_COMMON)/Source/GCC/startup_gd32vf103.S \
$(GD32VF103_SDK_SOC_COMMON)/Source/GCC/intexc_gd32vf103.S
# 判断是不是有效的板子
# Add extra board related source files and header files
VALID_GD32VF103_SDK_SOC_BOARD=$(wildcard $(GD32VF103_SDK_SOC_BOARD))
# 对应有效的板子
# 添加板子的头文件和源码
ifneq ($(VALID_GD32VF103_SDK_SOC_BOARD),)
INCDIRS += $(VALID_GD32VF103_SDK_SOC_BOARD)/Include
C_SRCDIRS += $(VALID_GD32VF103_SDK_SOC_BOARD)/Source
endif
# 如果编译rtos比如 application/rtthread/demo
# 其Makefile 里面会设置RTOS的值
# 这里不深入分析
ifneq ($(RTOS),)
ifneq ($(wildcard $(NUCLEI_SDK_RTOS)),)
MAKEFILE_PREREQS += $(NUCLEI_SDK_BUILD)/Makefile.rtos
MAKEFILE_PREREQS += $(NUCLEI_SDK_BUILD)/Makefile.rtos.$(RTOS)
include $(NUCLEI_SDK_BUILD)/Makefile.rtos.$(RTOS)
# Define RTOS_$(RTOS) to show usage of RTOS, such as RTOS_FREERTOS
RTOS_UPPER = $(call uc, $(RTOS))
COMMON_FLAGS += -DRTOS_$(RTOS_UPPER)
endif
endif
# 判断下载模式是否有效
ifeq ($(VALID_DOWNLOAD),)
$(error Download mode $(DOWNLOAD) is not supported, support modes: $(SUPPORT_DOWNLOAD_MODES))
endif
DEFAULT_RISCV_ARCH ?= rv32imac
DEFAULT_RISCV_ABI ?= ilp32
# RISC_ARCH RISC_ABI的 值是在 Makefile.soc.gd32vf103设置的
ifeq ($(RISCV_ARCH),)
$(warning RISCV_ARCH is not defined, use $(DEFAULT_RISCV_ARCH) as default)
RISCV_ARCH := $(DEFAULT_RISCV_ARCH)
endif
ifeq ($(RISCV_ABI),)
$(warning RISCV_ABI is not defined, use $(DEFAULT_RISCV_ABI) as default)
RISCV_ABI := $(DEFAULT_RISCV_ABI)
endif
# 这里和gcc编译器关联较大 最好百度
MKDEP_OPT = -MMD -MT $@ -MF $@.d
# 将所有的头文件加上 -I的前缀
C_INCLUDE_OPT = $(foreach dir,$(sort $(INCDIRS) $(C_INCDIRS)),-I$(dir))
# 同上
CXX_INCLUDE_OPT = $(foreach dir,$(sort $(INCDIRS) $(CXX_INCDIRS)),-I$(dir))
# 同上
ASM_INCLUDE_OPT = $(foreach dir,$(sort $(INCDIRS) $(ASM_INCDIRS)),-I$(dir))
# 删除gcc编译器配置相关的
...
# 同样还是编译相关的
COMMON_FLAGS += -g -march=$(RISCV_ARCH) -mabi=$(RISCV_ABI) -mcmodel=medany \
$(GC_CFLAGS) -fno-common -DDOWNLOAD_MODE=DOWNLOAD_MODE_$(DOWNLOAD_UPPER)
CFLAGS += $(COMMON_FLAGS) $(C_INCLUDE_OPT) $(MKDEP_OPT)
CXXFLAGS += $(COMMON_FLAGS) $(CXX_INCLUDE_OPT) $(MKDEP_OPT)
ASMFLAGS += $(COMMON_FLAGS) $(ASM_INCLUDE_OPT) $(MKDEP_OPT)
# 加上-L的前缀
LIB_OPT = $(addprefix -L, $(sort $(LIBDIRS)))
LDFLAGS += -T $(LINKER_SCRIPT) -lstdc++ -nostartfiles -Wl,-M,-Map=$(TARGET).map \
$(GC_LDFLAGS) $(NEWLIB_LDFLAGS) --specs=nosys.specs \
$(LIB_OPT) $(LDLIBS)
# More options needed by -flto, if not passed, will fail in linking phase
LDFLAGS += -u _isatty -u _write -u _sbrk -u _read -u _close -u _fstat -u _lseek
# 将Makefile及链接文件汇总到 COMMON_PREREQS
# Prerequesties
MAKEFILE_PREREQS += $(NUCLEI_SDK_BUILD)/Makefile.base
MAKEFILE_PREREQS += $(NUCLEI_SDK_BUILD)/Makefile.soc
MAKEFILE_PREREQS += $(NUCLEI_SDK_BUILD)/Makefile.soc.$(SOC)
MAKEFILE_PREREQS += $(NUCLEI_SDK_BUILD)/Makefile.conf
MAKEFILE_PREREQS += $(NUCLEI_SDK_BUILD)/Makefile.core
MAKEFILE_PREREQS += $(NUCLEI_SDK_BUILD)/Makefile.files
MAKEFILE_PREREQS += $(NUCLEI_SDK_BUILD)/Makefile.files.$(SOC)
MAKEFILE_PREREQS += $(NUCLEI_SDK_BUILD)/Makefile.misc
MAKEFILE_PREREQS += $(NUCLEI_SDK_BUILD)/Makefile.rules
MAKEFILE_PREREQS += $(EXTRA_MKS)
MAKEFILE_PREREQS += Makefile
LINK_PREREQS += $(LINKER_SCRIPT)
COMMON_PREREQS = $(MAKEFILE_PREREQS) $(LINK_PREREQS)
TARGET_ELF = $(TARGET).elf
# 将所有的c文件 c++文件 汇编文件 分别进行升序排列
# 注意call的使用方法是$(call variable,param1,param2,...)
# 执行的时候parame1 parame2 分别赋值给临时变量 $(1) $(2)
# 以下面这个为例 $(1) 为$(SRCDIRS) 和 $(C_SRCDIRS)
ALL_CSRCS = $(sort $(C_SRCS) $(call get_csrcs, $(SRCDIRS) $(C_SRCDIRS)))
ALL_CXXSRCS = $(sort $(CXX_SRCS) $(call get_cxxsrcs, $(SRCDIRS) $(CXX_SRCDIRS)))
ALL_ASMSRCS = $(sort $(ASM_SRCS) $(call get_asmsrcs, $(SRCDIRS) $(ASM_SRCDIRS)))
ALL_ASM_OBJS := $(ALL_ASMSRCS:=.o)
ALL_C_OBJS := $(ALL_CSRCS:=.o)
ALL_CXX_OBJS := $(ALL_CXXSRCS:=.o)
ALL_OBJS += $(ALL_ASM_OBJS) $(ALL_C_OBJS) $(ALL_CXX_OBJS)
ALL_DEPS := $(ALL_OBJS:=.d)
CLEAN_OBJS += $(TARGET).elf $(TARGET).map $(TARGET).bin $(TARGET).dump $(TARGET).dasm \
$(TARGET).hex $(TARGET).verilog openocd.log $(ALL_OBJS) $(ALL_DEPS)
REAL_CLEAN_OBJS = $(subst /,$(PS), $(CLEAN_OBJS))
# 执行all的时候,默认编译elf文件
# Default goal, placed before dependency includes
all: info $(TARGET).elf
# include dependency files of application
ifneq ($(MAKECMDGOALS),clean)
-include $(ALL_DEPS)
endif
# 生成elf文件是一系列的命令
$(TARGET).elf: $(ALL_OBJS)
$(TRACE_LINK)
$(Q)$(CC) $(CFLAGS) $(ALL_OBJS) -o $@ $(LDFLAGS)
$(Q)$(SIZE) $@
# 对于下面几个有使用$(COMMON_PREREQS) 不理解
# 尝试删除 好像也可以,没有影响
$(ALL_ASM_OBJS): %.o: % $(COMMON_PREREQS)
$(TRACE_ASSEMBLE)
$(Q)$(CC) $(ASMFLAGS) -c -o $@ $<
$(ALL_C_OBJS): %.o: % $(COMMON_PREREQS)
$(TRACE_COMPILE)
$(Q)$(CC) $(CFLAGS) -c -o $@ $<
$(ALL_CXX_OBJS): %.o: % $(COMMON_PREREQS)
$(TRACE_COMPILE)
$(Q)$(CXX) $(CXXFLAGS) -c -o $@ $<
dasm: $(TARGET).elf
$(OBJDUMP) -S -d $< > $(TARGET).dump
$(OBJDUMP) -d $< > $(TARGET).dasm
$(OBJCOPY) $< -O ihex $(TARGET).hex
$(OBJCOPY) $< -O verilog $(TARGET).verilog
# 生成bin文件的在这里
bin: $(TARGET).elf
$(OBJCOPY) $< -O binary $(TARGET).bin
size: $(TARGET).elf
$(Q)$(SIZE) $<
upload: $(TARGET).elf
@$(ECHO) "Download and run $<"
$(GDB) $< -ex "set remotetimeout 240" \
-ex "target remote $(GDBREMOTE)" \
$(GDB_UPLOAD_ARGS) $(GDB_UPLOAD_CMDS)
run_openocd:
@$(ECHO) "Start openocd server"
$(OPENOCD) $(OPENOCD_PORT_ARGS) $(OPENOCD_ARGS)
run_gdb: $(TARGET).elf
@$(ECHO) "Run gdb to connect openocd server and debug"
$(GDB) $< $(GDB_ARGS) $(GDB_CMDS)
debug: $(TARGET).elf
@$(ECHO) "Download and debug $<"
$(GDB) $< -ex "set remotetimeout 240" \
-ex "target remote $(GDBREMOTE)"
clean:
@$(ECHO) "Clean all build objects"
$(Q)$(RM) $(REAL_CLEAN_OBJS)