本文是对RED-V红板裸机程序编译过程的分析
Sparkfun RED-V 红板祼机调试记录
分析一下Freedom Studio
的构建过程,分析的过程是:
Makefile
- 链接脚本
- 镜像下载
- 调试方法
Makefile文件的解析
Freedome Studio
工程的makefile
指令如下:
make all CONFIGURATION=debug
那么我们来分析一下工程的Makefile
文件,这个文件位于wsFreedomStudio/red-v
工程目录下
确定编译配置
PROGRAM = sifive-welcome
TARGET = sifive-hifive1-revb
# The configuration defaults to Debug. Valid choices are:
# - debug
# - release
CONFIGURATION ?= debug
BSP_DIR ?= $(abspath bsp)
SRC_DIR ?= $(abspath src)
载入目标板的配置
# Include the BSP settings
include $(BSP_DIR)/settings.mk
ifeq ($(RISCV_LIBC),)
RISCV_LIBC=nano
endif
ifeq ($(RISCV_LIBC),nano)
# 裸机需要使用libgloss底层库
LIBMETAL_EXTRA=-lmetal-gloss
METAL_WITH_EXTRA=--with-builtin-libgloss
SPEC=nano
endif
LINK_TARGET = default
RISCV_XLEN := 32
MTIME_RATE_HZ_DEF=32768
而settings.mk
的内容如下:
RISCV_ARCH = rv32imac
RISCV_ABI = ilp32
RISCV_CMODEL = medlow
RISCV_SERIES = sifive-3-series
TARGET_TAGS = board jlink
TARGET_DHRY_ITERS = 20000000
TARGET_CORE_ITERS = 5000
TARGET_FREERTOS_WAIT_MS = 1000
TARGET_INTR_WAIT_CYCLE = 0
确定工具链相关内容
工具链
CROSS_COMPILE ?= riscv64-unknown-elf
RISCV_GCC := $(CROSS_COMPILE)-gcc
SEGGER_JLINK_EXE := JLinkExe
SEGGER_JLINK_GDB_SERVER := JLinkGDBServer
编译选项
RISCV_CFLAGS += -march=$(RISCV_ARCH) -mabi=$(RISCV_ABI) -mcmodel=$(RISCV_CMODEL)
最终的编译选项为:
-march=rv32imac -mabi=ilp32 -mcmodel=medlow -ffunction-sections -fdata-sections -I/Users/wilson/wsFreedomStudio/red-v/bsp/install/include --specs=nano.specs -DMTIME_RATE_HZ_DEF=32768 -fcommon -O0 -g
链接选项
# Turn on garbage collection for unused sections
RISCV_LDFLAGS += -Wl,--gc-sections
# Turn on linker map file generation
RISCV_LDFLAGS += -Wl,-Map,$(PROGRAM).map
# Turn off the C standard library
RISCV_LDFLAGS += -nostartfiles -nostdlib
# Find the archive files and linker scripts
RISCV_LDFLAGS += -L$(sort $(dir $(abspath $(filter %.a,$^)))) -T$(abspath $(filter %.lds,$^))
# Link to the relevant libraries
RISCV_LDLIBS += -Wl,--start-group -lc -lgcc -lm -lmetal $(LIBMETAL_EXTRA) -Wl,--end-group
最终的链接选项为:
-Wl,--gc-sections -Wl,-Map,sifive-welcome.map -nostartfiles -nostdlib -L/Users/wilson/wsFreedomStudio/red-v/bsp/install/lib/debug/ -T/Users/wilson/wsFreedomStudio/red-v/bsp/metal.default.lds sifive-welcome.c -Wl,--start-group -lc -lgcc -lm -lmetal -lmetal-gloss -Wl,--end-group
加载配置Makefile
# Load the configuration Makefile
CONFIGURATION_FILE = $(wildcard $(CONFIGURATION).mk)
include $(CONFIGURATION).mk
就是加载./debug.mk
配置文件
# Set the optimization level
RISCV_ASFLAGS += -O0
RISCV_CFLAGS += -O0
RISCV_CXXFLAGS += -O0
# Enable debug
RISCV_ASFLAGS += -g
RISCV_CFLAGS += -g
RISCV_CXXFLAGS += -g
输出目标
PROGRAM_ELF ?= $(SRC_DIR)/$(CONFIGURATION)/$(PROGRAM).elf
PROGRAM_HEX ?= $(SRC_DIR)/$(CONFIGURATION)/$(PROGRAM).hex
PROGRAM_LST ?= $(SRC_DIR)/$(CONFIGURATION)/$(PROGRAM).lst
Make目标
.PHONY: all
all: software
.PHONY: software
software: $(PROGRAM_ELF)
software: $(PROGRAM_HEX)
# 代码文件,所有src/目录下的c/h/S文件
PROGRAM_SRCS = $(wildcard $(SRC_DIR)/*.c) $(wildcard $(SRC_DIR)/*.h) $(wildcard $(SRC_DIR)/*.S)
# elf: srcs, *.a, default.lds
$(PROGRAM_ELF): \
$(PROGRAM_SRCS) \
$(BSP_DIR)/install/lib/$(CONFIGURATION)/libmetal.a \
$(BSP_DIR)/install/lib/$(CONFIGURATION)/libmetal-gloss.a \
$(BSP_DIR)/metal.$(LINK_TARGET).lds
mkdir -p $(dir $@)
$(MAKE) -C $(SRC_DIR) $(basename $(notdir $@)) \
PORT_DIR=$(PORT_DIR) \
PROGRAM=$(PROGRAM) \
AR=$(RISCV_AR) \
CC=$(RISCV_GCC) \
CXX=$(RISCV_GXX) \
ASFLAGS="$(RISCV_ASFLAGS)" \
CCASFLAGS="$(RISCV_CCASFLAGS)" \
CFLAGS="$(RISCV_CFLAGS)" \
CXXFLAGS="$(RISCV_CXXFLAGS)" \
XCFLAGS="$(RISCV_XCFLAGS)" \
LDFLAGS="$(RISCV_LDFLAGS)" \
LDLIBS="$(RISCV_LDLIBS)" \
FREERTOS_METAL_VENV_PATH="$(FREERTOS_METAL_VENV_PATH)"
mv $(SRC_DIR)/$(basename $(notdir $@)) $@
mv $(SRC_DIR)/$(basename $(notdir $@)).map $(dir $@)
touch -c $@
$(RISCV_OBJDUMP) --source --all-headers --demangle --line-numbers --wide $@ > $(PROGRAM_LST)
$(RISCV_SIZE) $@
这段内容的输出为:
mkdir -p /Users/wilson/wsFreedomStudio/red-v/src/debug/
make -C /Users/wilson/wsFreedomStudio/red-v/src sifive-welcome \
PORT_DIR= \
PROGRAM=sifive-welcome \
AR=/Applications/FreedomStudio.app/Contents/Eclipse/SiFive/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8/bin/riscv64-unknown-elf-ar \
CC=/Applications/FreedomStudio.app/Contents/Eclipse/SiFive/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8/bin/riscv64-unknown-elf-gcc \
CXX=/Applications/FreedomStudio.app/Contents/Eclipse/SiFive/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8/bin/riscv64-unknown-elf-g++ \
ASFLAGS="-march=rv32imac -mabi=ilp32 -mcmodel=medlow --specs=nano.specs -O0 -g" \
CCASFLAGS="-march=rv32imac -mabi=ilp32 -mcmodel=medlow -I/Users/wilson/wsFreedomStudio/red-v/bsp/install/include --specs=nano.specs" \
CFLAGS="-march=rv32imac -mabi=ilp32 -mcmodel=medlow -ffunction-sections -fdata-sections -I/Users/wilson/wsFreedomStudio/red-v/bsp/install/include --specs=nano.specs -DMTIME_RATE_HZ_DEF=32768 -fcommon -O0 -g" \
CXXFLAGS="-march=rv32imac -mabi=ilp32 -mcmodel=medlow -ffunction-sections -fdata-sections -I/Users/wilson/wsFreedomStudio/red-v/bsp/install/include --specs=nano.specs -DMTIME_RATE_HZ_DEF=32768 -O0 -g" \
XCFLAGS="-DMETAL_WAIT_CYCLE=0" \
LDFLAGS="-Wl,--gc-sections -Wl,-Map,sifive-welcome.map -nostartfiles -nostdlib -L/Users/wilson/wsFreedomStudio/red-v/bsp/install/lib/debug/ -T/Users/wilson/wsFreedomStudio/red-v/bsp/metal.default.lds" \
LDLIBS="-Wl,--start-group -lc -lgcc -lm -lmetal -lmetal-gloss -Wl,--end-group" \
FREERTOS_METAL_VENV_PATH="/Users/wilson/wsFreedomStudio/red-v/venv"
make[1]: Entering directory '/Users/wilson/wsFreedomStudio/red-v/src'
/Applications/FreedomStudio.app/Contents/Eclipse/SiFive/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8/bin/riscv64-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -mcmodel=medlow -ffunction-sections -fdata-sections -I/Users/wilson/wsFreedomStudio/red-v/bsp/install/include --specs=nano.specs -DMTIME_RATE_HZ_DEF=32768 -fcommon -O0 -g -Wl,--gc-sections -Wl,-Map,sifive-welcome.map -nostartfiles -nostdlib -L/Users/wilson/wsFreedomStudio/red-v/bsp/install/lib/debug/ -T/Users/wilson/wsFreedomStudio/red-v/bsp/metal.default.lds sifive-welcome.c -Wl,--start-group -lc -lgcc -lm -lmetal -lmetal-gloss -Wl,--end-group -o sifive-welcome
make[1]: Leaving directory '/Users/wilson/wsFreedomStudio/red-v/src'
mv /Users/wilson/wsFreedomStudio/red-v/src/sifive-welcome /Users/wilson/wsFreedomStudio/red-v/src/debug/sifive-welcome.elf
mv /Users/wilson/wsFreedomStudio/red-v/src/sifive-welcome.map /Users/wilson/wsFreedomStudio/red-v/src/debug/
touch -c /Users/wilson/wsFreedomStudio/red-v/src/debug/sifive-welcome.elf
/Applications/FreedomStudio.app/Contents/Eclipse/SiFive/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8/bin/riscv64-unknown-elf-objdump --source --all-headers --demangle --line-numbers --wide /Users/wilson/wsFreedomStudio/red-v/src/debug/sifive-welcome.elf > /Users/wilson/wsFreedomStudio/red-v/src/debug/sifive-welcome.lst
/Applications/FreedomStudio.app/Contents/Eclipse/SiFive/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8/bin/riscv64-unknown-elf-size /Users/wilson/wsFreedomStudio/red-v/src/debug/sifive-welcome.elf
text data bss dec hex filename
30914 2796 3256 36966 9066 /Users/wilson/wsFreedomStudio/red-v/src/debug/sifive-welcome.elf
/Applications/FreedomStudio.app/Contents/Eclipse/SiFive/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8/bin/riscv64-unknown-elf-objcopy -O ihex /Users/wilson/wsFreedomStudio/red-v/src/debug/sifive-welcome.elf /Users/wilson/wsFreedomStudio/red-v/src/debug/sifive-welcome.hex
清除目标
.PHONY: clean-software
clean-software:
$(MAKE) -C $(SRC_DIR) PORT_DIR=$(PORT_DIR) clean
rm -rf $(SRC_DIR)/$(CONFIGURATION)
.PHONY: clean
clean: clean-software
总结
结过分析,我们通过
make all CONFIGURATION=debug
生成了四个文件
wsFreedomStudio/red-v/src/debug/sifive-welcome.elf
wsFreedomStudio/red-v/src/debug/sifive-welcome.hex
wsFreedomStudio/red-v/src/debug/sifive-welcome.lst
wsFreedomStudio/red-v/src/debug/sifive-welcome.map
下载版本文件
目标:搞清下面make
的过程
make PROGRAM=sparkfun-welcome TARGET=sparkfun-redv CONFIGURATION=release upload
makefile
文件内容:
#############################################################
# Configuration
#############################################################
# Allows users to create Makefile.local or ../Makefile.project with
# configuration variables, so they don't have to be set on the command-line
# every time.
extra_configs := $(wildcard Makefile.local ../Makefile.project)
ifneq ($(extra_configs),)
$(info Obtaining additional make variables from $(extra_configs))
include $(extra_configs)
endif
TARGET_ROOT ?= $(abspath .)
PROGRAM_ROOT ?= $(abspath .)
# Allow BOARD as a synonym for TARGET
# 允许BOARD作为TARGET的同义词
ifneq ($(BOARD),)
TARGET ?= $(BOARD) # TARGET=sparkfun-redv
endif
# Default PROGRAM and TARGET
PROGRAM ?= hello # PROGRAM=sparkfun-welcome
TARGET ?= $(shell find $(TARGET_ROOT)/bsp/* -type d | head -n 1 | rev | cut -d '/' -f 1 | rev)
# The configuration defaults to Debug. Valid choices are:
# - debug
# - release
CONFIGURATION ?= debug
# Setup differences between host platforms
ifeq ($(OS),Windows_NT)
SED_RE_FLAG = -r
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
SED_RE_FLAG = -r
endif
ifeq ($(UNAME_S),Darwin)
SED_RE_FLAG = -E
endif
endif
# Default to use relase configuration For Benchmark programs, like Coremark and Dhrystone.
# 默认使用relase配置 对于基准测试程序,如Coremark和Dhrystone。
ifeq ($(PROGRAM),dhrystone)
CONFIGURATION = release
endif
# Coremark require PORT_DIR set for different OS, freedom-metal for us!
# Coremark要求为不同的操作系统设置 PORT_DIR,对我们来说是freedom-metal
ifeq ($(PROGRAM),coremark)
CONFIGURATION = release
ifeq ($(PORT_DIR),)
PORT_DIR = freedom-metal
endif
endif
# SRC_DIR = ./software/sparkfun-welcome
SRC_DIR = $(PROGRAM_ROOT)/software/$(PROGRAM)
# PROGRAM_ELF = $(SRC_DIR)/release/sparkfun-welcome.elf
PROGRAM_ELF = $(SRC_DIR)/$(CONFIGURATION)/$(PROGRAM).elf
PROGRAM_HEX = $(SRC_DIR)/$(CONFIGURATION)/$(PROGRAM).hex
PROGRAM_LST = $(SRC_DIR)/$(CONFIGURATION)/$(PROGRAM).lst
#############################################################
# BSP Loading
#############################################################
# Finds the directory in which this BSP is located, ensuring that there is
# exactly one.
# 找到bsp目录:
BSP_DIR := $(wildcard $(TARGET_ROOT)/bsp/$(TARGET))
ifeq ($(words $(BSP_DIR)),0)
$(error Unable to find BSP for $(TARGET), expected to find "bsp/$(TARGET)")
endif
ifneq ($(words $(BSP_DIR)),1)
$(error Found multiple BSPs for $(TARGET): "$(BSP_DIR)")
endif
#############################################################
# Standalone Script Include 独立的脚本包括
#############################################################
# The standalone script is included here because it needs $(SRC_DIR) and
# $(BSP_DIR) to be set.
#
# The standalone Makefile handles the following tasks:
# - Including $(BSP_DIR)/settings.mk and validating RISCV_ARCH, RISCV_ABI
# - Setting the toolchain path with CROSS_COMPILE and RISCV_PATH
# - Providing the software and $(PROGRAM_ELF) Make targets for Metal
# 独立的 Makefile 处理以下任务。
# - 包括 $(BSP_DIR)/settings.mk 并验证 RISCV_ARCH, RISCV_ABI
# - 用CROSS_COMPILE和RISCV_PATH设置工具链路径
# - 为Metal提供软件和$(PROGRAM_ELF) 制作目标
include scripts/standalone.mk
#############################################################
# Prints help message
#############################################################
.PHONY: help
help:
@echo " SiFive Freedom E Software Development Kit "
@echo " Makefile targets:"
@echo ""
@echo " software [PROGRAM=$(PROGRAM)] [TARGET=$(TARGET)]"
@echo " [CONFIGURATION=$(CONFIGURATION)]:"
@echo " Builds the requested PROGRAM for the TARGET using the"
@echo " specified build CONFIGURATION."
@echo ""
@echo " metal [TARGET=$(TARGET)] [CONFIGURATION=$(CONFIGURATION)]"
@echo " Builds the Freedom Metal library for TARGET."
@echo ""
@echo " clean [PROGRAM=$(PROGRAM)] [TARGET=$(TARGET)]"
@echo " [CONFIGURATION=$(CONFIGURATION)]:"
@echo " Cleans compiled objects for a specified "
@echo " software program."
@echo ""
@echo " upload [PROGRAM=$(PROGRAM)] [TARGET=$(TARGET)]"
@echo " [CONFIGURATION=$(CONFIGURATION)]:"
@echo " For board and FPGA TARGETs, uploads the program to the"
@echo " on-board flash."
@echo ""
@echo " debug [PROGRAM=$(PROGRAM)] [TARGET=$(TARGET)]"
@echo " [CONFIGURATION=$(CONFIGURATION)]:"
@echo " For board and FPGA TARGETs, attaches GDB to the"
@echo " running program."
@echo ""
@echo " simulate [PROGRAM=$(PROGRAM)] [TARGET=$(TARGET)]"
@echo " [CONFIGURATION=$(CONFIGURATION)]:"
@echo " Simulates the program in the QEMU emulator."
@echo ""
@echo " standalone STANDALONE_DEST=/path/to/desired/location"
@echo " [PROGRAM=$(PROGRAM)] [TARGET=$(TARGET)]:"
@echo " Exports a program for a single target into a standalone"
@echo " project directory at STANDALONE_DEST."
@echo ""
@echo " open-docs"
@echo " Opens the Freedom E SDK documentation in your HTML"
@echo " viewer of choice. The documentation can also be found"
@echo " online at"
@echo " https://sifive.github.io/freedom-e-sdk-docs/index.html"
.PHONY: open-docs
open-docs: scripts/open-docs
$^
.PHONY: clean
clean:
#############################################################
# Enumerate BSPs and Programs
#
# List all available boards and programs in a form that
# Freedom Studio knows how to parse. Do not change the
# format or fixed text of the output without consulting the
# Freedom Studio dev team.
#############################################################
# 枚举BSP和程序
#
# 列出所有可用的板卡和程序的形式,以便于
# Freedom Studio知道如何解析。 请不要改变
# 格式或固定的输出文本,除非咨询
# Freedom Studio开发团队。
#############################################################
# Find all settings.mk with TARGET_REQUIRE_TAGS in TARGET_TAGS
# 在TARGET_TAGS中找到所有带有TARGET_REQUIRE_TAGS的设置.mk
MATCHING_SETTINGS = $(shell scripts/filter-targets $(TARGET_ROOT)/bsp $(TARGET_REQUIRE_TAGS))
# Get the name of the containing directory of all matching settings.mk
# 获取所有匹配设置的包含目录的名称.mk
MATCHING_TARGETS = $(patsubst $(TARGET_ROOT)/bsp/%/,%,$(dir $(MATCHING_SETTINGS)))
.PHONY: list-targets
list-targets:
@echo bsp-list: $(sort $(MATCHING_TARGETS))
# Lists all available TARGET_TAGS
#
# 1. Find all settings.mk
# 2. Extract the TARGET_TAGS line
# 3. Extract the value of TARGET_TAGS
# 4. Split each tag onto a newline
# 5. Sort the lines
# 6. Find unique tags
#
.PHONY: list-target-tags
list-target-tags:
@echo target-tags: $(shell find $(TARGET_ROOT)/bsp -name settings.mk | \
xargs grep -he "TARGET_TAGS" | \
sed $(SED_RE_FLAG) 's/TARGET_TAGS.*=(.*)/\1/' | \
tr ' ' '\n' | \
sort | \
uniq)
# Metal programs are any submodules in the software folder
# 金属程序是软件文件夹中的任何子模块
.PHONY: list-programs
list-programs:
@echo program-list: $(shell ls $(PROGRAM_ROOT)/software)
.PHONY: list-options
list-options: list-programs list-targets
#############################################################
# Import rules to build Freedom Metal
# 在这里进行编译
#############################################################
include scripts/libmetal.mk # 编译出hex、bin等文件
#############################################################
# Standalone Project Export
#############################################################
ifeq ($(STANDALONE_DEST),)
standalone:
$(error Please provide STANDALONE_DEST to create a standalone project)
else # STANDALONE_DEST != ""
$(STANDALONE_DEST):
$(STANDALONE_DEST)/%:
mkdir -p $@
ifneq ($(filter rtl,$(TARGET_TAGS)),)
# TARGETs with the "rtl" TARGET_TAG need elf2hex in their standalone project
standalone: \
$(STANDALONE_DEST) \
$(STANDALONE_DEST)/bsp \
$(STANDALONE_DEST)/src \
$(SRC_DIR) \
freedom-metal \
debug.mk \
release.mk \
scripts/elf2hex \
scripts/standalone.mk \
scripts/libmetal.mk
cp -r $(addprefix $(BSP_DIR)/,$(filter-out build,$(shell ls $(BSP_DIR)))) $</bsp/
cp -r freedom-metal $</
find $</freedom-metal -name ".git*" | xargs rm -rf
mkdir -p $</scripts
cp -r scripts/elf2hex $</scripts
find $</scripts/elf2hex -name ".git*" | xargs rm -rf
ifeq ($(PORT_DIR),)
$(MAKE) -C $(SRC_DIR) clean
else
$(MAKE) -C $(SRC_DIR) PORT_DIR=${PORT_DIR} clean
endif
cp -r $(SRC_DIR)/* $</src/
cp debug.mk $</debug.mk
cp release.mk $</release.mk
echo "PROGRAM = $(PROGRAM)" > $</Makefile
echo "TARGET = ${TARGET}" >> $</Makefile
ifneq ($(PORT_DIR),)
echo "PORT_DIR = $(PORT_DIR)" >> $</Makefile
endif
echo "" >> $</Makefile
echo "# The configuration defaults to Debug. Valid choices are:" >> $</Makefile
echo "# - debug" >> $</Makefile
echo "# - release" >> $</Makefile
echo "CONFIGURATION ?= ${CONFIGURATION}" >> $</Makefile
echo "" >> $</Makefile
cat scripts/standalone.mk >> $</Makefile
cat scripts/libmetal.mk >> $</Makefile
else # "rtl" not in TARGET_TAGS
standalone: \
$(STANDALONE_DEST) \
$(STANDALONE_DEST)/bsp \
$(STANDALONE_DEST)/src \
$(SRC_DIR) \
freedom-metal \
debug.mk \
release.mk \
scripts/standalone.mk \
scripts/libmetal.mk
cp -r $(addprefix $(BSP_DIR)/,$(filter-out build,$(shell ls $(BSP_DIR)))) $</bsp/
cp -r freedom-metal $</
find $</freedom-metal -name ".git*" | xargs rm -rf
ifeq ($(PORT_DIR),)
$(MAKE) -C $(SRC_DIR) clean
else
$(MAKE) -C $(SRC_DIR) PORT_DIR=${PORT_DIR} clean
endif
cp -r $(SRC_DIR)/* $</src/
cp debug.mk $</debug.mk
cp release.mk $</release.mk
echo "PROGRAM = $(PROGRAM)" > $</Makefile
echo "TARGET = ${TARGET}" >> $</Makefile
ifneq ($(PORT_DIR),)
echo "PORT_DIR = $(PORT_DIR)" >> $</Makefile
endif
echo "" >> $</Makefile
echo "# The configuration defaults to Debug. Valid choices are:" >> $</Makefile
echo "# - debug" >> $</Makefile
echo "# - release" >> $</Makefile
echo "CONFIGURATION ?= ${CONFIGURATION}" >> $</Makefile
echo "" >> $</Makefile
cat scripts/standalone.mk >> $</Makefile
cat scripts/libmetal.mk >> $</Makefile
endif # rtl in TARGET_TAGS
endif # STANDALONE_DEST
#############################################################
# Upload and Debug
#############################################################
ifneq ($(RISCV_OPENOCD_PATH),)
RISCV_OPENOCD=$(RISCV_OPENOCD_PATH)/bin/openocd
else
#if RISCV_OPENOCD_PATH is not set, just look on the PATH
RISCV_OPENOCD=openocd
endif
ifneq ($(filter jlink,$(TARGET_TAGS)),)
upload: $(PROGRAM_HEX)
scripts/upload --hex $(PROGRAM_HEX) --jlink $(SEGGER_JLINK_EXE)
else
upload: $(PROGRAM_ELF)
scripts/upload --elf $(PROGRAM_ELF) --openocd $(RISCV_OPENOCD) --gdb $(RISCV_GDB) --openocd-config bsp/$(TARGET)/openocd.cfg
endif
ifneq ($(filter jlink,$(TARGET_TAGS)),)
debug: $(PROGRAM_ELF)
scripts/debug --elf $(PROGRAM_ELF) --jlink $(SEGGER_JLINK_GDB_SERVER) --gdb $(RISCV_GDB)
else
debug: $(PROGRAM_ELF)
scripts/debug --elf $(PROGRAM_ELF) --openocd $(RISCV_OPENOCD) --gdb $(RISCV_GDB) --openocd-config bsp/$(TARGET)/openocd.cfg
endif
ifeq ($(findstring qemu,$(TARGET)),qemu)
ifeq ($(findstring rv32,$(RISCV_ARCH)),rv32)
simulate: $(PROGRAM_ELF)
scripts/simulate --elf $(PROGRAM_ELF) --qemu $(QEMU_RISCV32) --qemu-config bsp/$(TARGET)/qemu.cfg
else # findstring rv32
simulate: $(PROGRAM_ELF)
scripts/simulate --elf $(PROGRAM_ELF) --qemu $(QEMU_RISCV64) --qemu-config bsp/$(TARGET)/qemu.cfg
endif
else # findstring qemu
simulate:
@echo "QEMU can't simulate target $(TARGET)!"
endif
freedom-e-sdk/scripts/libmetal.mk
#############################################################
# Compiles an instance of Metal targeted at $(TARGET)
# 编译一个针对$(TARGET)的Metal实例
#############################################################
METAL_SOURCE_PATH ?= freedom-metal
# 链接脚本:freedom-e-sdk/bsp/sparkfun-redv/metal.default.lds
METAL_LDSCRIPT = $(BSP_DIR)/metal.$(LINK_TARGET).lds
METAL_HEADER = $(BSP_DIR)/metal.h
METAL_INLINE = $(BSP_DIR)/metal-inline.h
PLATFORM_HEADER = $(BSP_DIR)/metal-platform.h
METAL_PREFIX = $(abspath $(BSP_DIR)/install)
METAL_BUILD_DIR = $(abspath $(BSP_DIR)/build/$(CONFIGURATION))
METAL_LIB_DIR = $(abspath $(BSP_DIR)/install/lib/$(CONFIGURATION))
.PHONY: metal
metal: $(METAL_LIB_DIR)/stamp
$(METAL_BUILD_DIR)/Makefile:
@rm -rf $(dir $@)
@mkdir -p $(dir $@)
cd $(dir $@) && \
CFLAGS="$(RISCV_CFLAGS)" \
$(abspath $(METAL_SOURCE_PATH)/configure) \
--host=$(CROSS_COMPILE) \
--prefix=$(METAL_PREFIX) \
--libdir=$(METAL_LIB_DIR) \
--disable-maintainer-mode \
--with-preconfigured \
--with-machine-name=$(TARGET) \
--with-machine-header=$(abspath $(METAL_HEADER)) \
--with-machine-inline=$(abspath $(METAL_INLINE)) \
--with-platform-header=$(abspath $(PLATFORM_HEADER)) \
--with-machine-ldscript=$(abspath $(METAL_LDSCRIPT)) \
--with-builtin-libgloss
touch -c $@
$(METAL_LIB_DIR)/stamp: $(BSP_DIR)/build/$(CONFIGURATION)/Makefile
$(MAKE) -C $(abspath $(BSP_DIR)/build/$(CONFIGURATION)) install
date > $@
$(METAL_LIB_DIR)/libriscv%.a: $(METAL_LIB_DIR)/stamp ;@:
$(METAL_LIB_DIR)/libmetal.a: $(METAL_LIB_DIR)/libriscv__mmachine__$(TARGET).a
cp $< $@
$(METAL_LIB_DIR)/libmetal-gloss.a: $(METAL_LIB_DIR)/libriscv__menv__metal.a
cp $< $@
# If we're cleaning the last Metal library for a TARGET, then remove
# the install directory, otherwise just remove the built libs for that
# CONFIGURATION.
ifeq ($(words $(wildcard $(METAL_PREFIX)/lib/*)),1)
METAL_CLEAN = $(METAL_PREFIX)
else
METAL_CLEAN = $(METAL_LIB_DIR)
endif
.PHONY: clean-metal
clean-metal:
rm -rf $(METAL_CLEAN)
rm -rf $(METAL_BUILD_DIR)
clean: clean-metal
metal_install: metal
$(MAKE) -C $(METAL_SOURCE_PATH) install
执行:
make PROGRAM=sparkfun-welcome TARGET=sparkfun-redv CONFIGURATION=release upload
会执行这里的metal_install
freedom-e-sdk/bsp/sparkfun-redv/metal.default.lds
从链接脚本我们可以知道,程序的入口是_enter
,以及内存的布局
OUTPUT_ARCH("riscv")
# 入口
ENTRY(_enter)
# 内存布局
MEMORY
{
flash (rxai!w) : ORIGIN = 0x20010000, LENGTH = 0x6a120
itim (wx!rai) : ORIGIN = 0x8000000, LENGTH = 0x2000
ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 0x4000
}
# 设置段的属性:表示该程序段在程序运行时应该被加载
PHDRS
{
flash PT_LOAD;
ram PT_LOAD;
ram_init PT_LOAD;
itim PT_LOAD;
itim_init PT_LOAD;
}
freedom-e-sdk/software/sparkfun-welcome/
然后我们开始看程序代码,代码很直接,就是直接执行示例的编译,
附录
内存布局
在《fe310-g002-manual-v1p0》手册中的第四章,
MEMORY
{
flash (rxai!w) : ORIGIN = 0x20010000, LENGTH = 0x6a120
itim (wx!rai) : ORIGIN = 0x8000000, LENGTH = 0x2000
ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 0x4000
}
基地址 | 尾地址 | 属性 | 说明 | 备注 |
---|---|---|---|---|
0x2000_0000 | 0x3FFF_FFFF | R XC | QSPI 0 Flash (512 MiB) | 片外非易失性存储器 |
0x4000_0000 | 0x7FFF_FFFF | 保留 | ||
0x8000_0000 | 0x8000_3FFF | RWX A | E31 DTIM (16 KiB) | 片上易变性存储器 |
0x8000_4000 | 0xFFFF_FFFF | 保留 |
内存属性:R-读,W-写,X-执行,C-可缓存,A-原子
PHDRS命令
该命令仅在产生ELF目标文件时有效。
ELF目标文件格式用program headers程序头(程序头内包含一个或多个segment程序段描述)来描述程序如何被载入内存。可以用objdump -p命令查看。
当在本地ELF系统运行ELF目标文件格式的程序时,系统加载器通过读取程序头信息以知道如何将程序加载到内存。要了解系统加载器如何解析程序头,请参考ELF ABI文档。
在连接脚本内不指定PHDRS命令时,连接器能够很好的创建程序头,但是有时需要更精确的描述程序头,那么PAHDRS命令就派上用场了。
注意:一旦在连接脚本内使用了PHDRS命令,那么连接器仅会创建PHDRS命令指定的信息,所以使用时须谨慎。
PHDRS命令文法
PHDRS
{
NAME TYPE [ FILEHDR ] [ PHDRS ] [ AT ( ADDRESS ) ]
[ FLAGS ( FLAGS ) ] ;
}
- NAME
- 为程序段名,此名字可以与符号名、section名、文件名重复,因为它在一个独立的名字空间内。
- 此名字只能在SECTIONS命令内使用。
- 一个程序段可以由多个‘可加载’的section组成。通过输出section描述的属性:PHDRS可以将输出section加入一个程序段,
- PHDRS中的PHDRS为程序段名。在一个输出section描述内可以多次使用:PHDRS命令,也即可以将一个section加入多个程序段。
- 如果在一个输出section描述内指定了:PHDRS属性,那么其后的输出section描述将默认使用该属性,除非它也定义了:PHDRS属性。显然当多个输出section属于同一程序段时可简化书写。
- TYPE
- TYPE可以是以下八种形式,
- PT_NULL 0:表示未被使用的程序段
- PT_LOAD 1:表示该程序段在程序运行时应该被加载
- PT_DYNAMIC 2:表示该程序段包含动态连接信息
- TYPE可以是以下八种形式,
Newlib
newlib,因为这个涉及到裸机编程时,如何使用c运行库。
newlib是一个面向嵌入式系统的c运行库。在裸机如果想要实现c运行库,那么newlib是最好的一个选择。而且newlib可以集成到gcc交叉编译器中,在使用gcc的时候,直接链接newlib库,生成可执行文件。
Newlib由三部分构成:libgloss、libc、libm,三者在Newlib源代码中的存储位置如下
- newlib-x.y.z
- libgloss
- newlib
- libc
- libm
libc是标准C库,libm是标准数学库,libgloss是底层库。
对于如下的代码:
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
使用aarch64-none-elf-gcc工具,直接编译的话,那么会有报错信息,提示有符号找不到
aarch64-none-elf-gcc hello.c –o hello.elf
这些符号,是使用newlib库时,需要自己实现的桩函数,包括了_write
,_close
函数等等。因为newlib库,并不知道裸机底层,是如何实现这些操作的。
这里,我们以riscv-newlib这个github上托管的开源代码库为例,来说明。该版本库的github地址为https://github.com/riscv/riscv-newlib。
以调用write函数为例,write函数,总共3个参数,第一个是文件描述符,第二个是字符串,第三个是字符串长度。
如果我们在程序中,调用了write函数,那么就会调用newlib库中的write函数。该函数实现在 newlib/libc/syscalls/syswrite.c
中。
/* connector for write */
#include <reent.h>
#include <unistd.h>
_READ_WRITE_RETURN_TYPE
write (int fd,
const void *buf,
size_t cnt)
{
return _write_r (_REENT, fd, buf, cnt);
}
在write函数中,会调用_write_r
函数。
而_write_r
函数,也实现在newlib库中。函数实现在newlib/libc/reent/writer.c
中:
_ssize_t _write_r (struct _reent *ptr,
int fd,
const void *buf,
size_t cnt)
{
_ssize_t ret;
errno = 0;
if ((ret = (_ssize_t)_write (fd, buf, cnt)) == -1 && errno != 0)
ptr->_errno = errno;
return ret;
}
_write_r
函数,带有_r
后缀,表示该函数是可重入函数,也就是无论调用多少次,结果都是一样的。该函数,内部调用了_write
函数。可重入函数,实现在reent目录下。
而_write
函数,是没有实现在libc中的,而是在libgloss中。可以这样认为,libgloss是底层的驱动实现,而main newlib是有硬件平台无关的通用功能实现。
在libgloss下,以各个硬件平台为文件夹,进行组织的。这里我们关心aarch64。在aarch64目录中,有syscalls.c文件,里面,就实现了newlib需要的各个桩函数。
在这个文件中,我们可以看到定义了newlib需要的桩函数:
/* Forward prototypes. */
int _system (const char *);
int _rename (const char *, const char *);
int _isatty (int);
int _write (int, char *, int);
我们来看_write函数的实现:
int _write (int fd, char *ptr, int len)
{
int res;
struct fdent *pfd;
pfd = findslot (fd);
if (pfd == NULL) {
errno = EBADF;
return -1;
}
res = _swiwrite (pfd->handle, ptr, len);
/* Clearly an error. */
if (res < 0) return -1;
pfd->pos += len - res;
/* We wrote 0 bytes? Retrieve errno just in case. */
if ((len - res) == 0) return error (0);
return (len - res);
}
这里调用了_swiwrite
函数。_swiwrite
函数,实现如下:
/* fh, is a valid internal file handle.
Returns the number of bytes *not* written. */
int _swiwrite (int fh, char *ptr, int len)
{
param_block_t block[3];
block[0] = fh;
block[1] = POINTER_TO_PARAM_BLOCK_T (ptr);
block[2] = len;
return checkerror (do_AngelSVC (AngelSVC_Reason_Write, block));
}
在svc.h文件中,有定义相关的do_AngelSVC
,AngelSVC_Reason_Write
的实现:
do_AngelSVC (int reason, param_block_t * arg)
{
long long value;
asm volatile ("mov w0, %w1; mov x1, %2; " AngelSVCInsn " %3; mov %0, x0"
: "=r" (value) /* Outputs */
: "r" (reason), "r" (arg), "n" (AngelSVC) /* Inputs */
: "x0", "x1", "x2", "x3", "x17", "x30", "memory", "cc"
/* Clobbers x0 and x1, and lr if in supervisor mode */);
return value;
}
用aarch64-none-elf-gcc -v hello.c -o hello.elf
命令,可以看到输出信息里面,没有带gloss。说明链接,没有使用libgloss库。
但是如果用riscv64-unknown-elf-gcc
工具编译hello.c文件,那么是可以直接成功:
riscv64-unknown-elf-gcc -v hello.c -o hello.elf
输出信息中,有gloss,说明链接,是带有libgloss库进行链接,因此链接能够成功,最终生成hello.elf。
lst文件
在使用 eclipse 进行嵌入式开发时,有时会遇到程序跳入异常服务程序的情况。
lst 文件实际是使用 objdump 反汇编 elf 文件得到的输出文件,它拥有比 map 文件更详细的信息。如果你的程序中加入了调试信息,那么你可以在 lst 中看到每一条指令的地址。借助 lst 文件,同时通过查看栈帧结构(可以通过查看相应的手册来确定栈帧的组成),通过在 lst 文件中查找 lr 的地址所在的位置,你就能立刻定位到问题。
下面是一个典型的命令行输出:
arm-none-eabi-objdump --source --all-headers --demangle --file-headers --line-numbers --wide "hello world.elf" > "hello world.lst"
--source
: 如果可能的话,显示混入反汇编的源代码。意味着-d
--all-headers
:显示所有可用的头信息,包括符号表和重定位表项。使用-x
等价于指定所有的-a -f -h -p -r -t
--demangle
:将低级符号名称解码(分解)为用户级名称- 除了删除系统开头的任何初始下划线之外,这还使C++函数名称易于阅读。
- 不同的编译器具有不同的处理样式
- 可选的demangling样式参数可用于为编译器选择适当的demangling样式。
--file-headers
:显示来自每个objfile文件的总体头的摘要信息--line-nubmers
:使用显示的目标代码或重定位符对应的文件名和源行号标记显示(使用调试信息)。只适用于-d、-D或-r。wide
:为超过80列的输出设备格式化一些行。也不要在显示符号名称时截断它们。
这个文件包含了有关编译过程的丰富信息,该文件由多个段组成,其中Symbol Listing 和 Module Information两个段对于用户分析调试程序尤其有用,下面按照各个段在
listing file中出现的先后顺序加以说明:
页头段(Page Header)
每个lst文件都有一个包含了编译器版本号、源文件名称、日期、时间、页号的头部。示例:
C51 COMPILER V7.20 MEASURE 10/01/2004 14:05:05 PAGE 1
/Users/wilson/wsFreedomStudio/red-v/src/debug/sifive-welcome.elf: file format elf32-littleriscv
/Users/wilson/wsFreedomStudio/red-v/src/debug/sifive-welcome.elf
architecture: riscv:rv32, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x20010000