korena's blog

6. Build system & version control setup

Since we're getting closer to the point of 'platform independent' code, I thought it's time to setup a git repository for the car dashboard project before it gets out of hand. This required rewriting the Makefile and restructuring directories to accommodate BL2 and a simple operating system. BL2 will be the bootloader that is responsible for loading the OS. In a more productive setup, I would not be writing Makefiles by hand, and will probably use Autotools with nested Makefiles for recursive building, but I promised in the introductory post of this series that I will only use make for building the whole project, besides, I believe knowing your way around Makefiles is an essential skill that would come in handy when your make based build system starts acting up.

Project Structure

The directories are intuitively named, but you have to understand that the central piece of the software stack is BL1, the following command shows the directories structure:

korena@korena-solid:~/Development/Projects/Embedded/tiny210$ ls -R
 
.:
 
asm  bl2  host_bin  host_scripts  host_tools  include  linker.lds  Makefile  src  target_bin
 
 
 
./asm:
 
startup.s
 
 
 
./bl2:
 
asm  BL2_linker.lds
 
 
 
./bl2/asm:
 
bl2_asm.s
 
 
 
./host_bin:
 
 
 
./host_scripts:
 
fuseBin.sh
 
 
 
./host_tools:
 
imageMaker.c  readFileBytes.c
 
 
 
./include:
 
S5PV210.inc
 
 
 
./src:
 
 
 
./target_bin:

The top level source directories belong to BL1, because it is the primary focus here. A sub directory was created for BL2, and another will be created for the operating system when we come to the OS part of the project, the reason why we treat BL2 and the OS as sub parts of the project is because we plan to use u-boot as bootloader, which will replace BL2, and Linux as a substitute for the simple OS we'll build, the only part that will be kept from this project setup is BL1.
Finally, a Git repository was created for version control, you can clone the project from my public github account.

Makefile

Only a single top level Makefile is used here, which is not what you'd want to use in a similar real life project, because of the fact that this project encompasses three separate modules, and it would be wiser to have a Makefile for each sub module (BL1, BL2 and OS), preferably using an auto build system. The reason I went with a single Makefile is the fact that maintaining Makefiles manually is something I dread.

Makefile:

# for home
 
#CROSS_COMPILE= /home/korena/Development/Compilers/gcc-arm-none-eabi-4_7-2013q1/bin/arm-none-eabi-
 
# for work
 
CROSS_COMPILE =arm-none-eabi-
AS        = $(CROSS_COMPILE)as
LD        = $(CROSS_COMPILE)ld
CC        = $(CROSS_COMPILE)gcc
AR        = $(CROSS_COMPILE)ar
NM        = $(CROSS_COMPILE)nm
STRIP     = $(CROSS_COMPILE)strip
OBJCOPY   = $(CROSS_COMPILE)objcopy
OBJDUMP   = $(CROSS_COMPILE)objdump
HEX  = $(OBJCOPY) -O ihex
BIN  = $(OBJCOPY) -O binary -S
 
PRJ_ROOT_DIR := $(shell pwd)
PROJECT_HOME := $(shell pwd)
ASM_SRC_DIR = $(PROJECT_HOME)/asm
SRC_DIR = $(PROJECT_HOME)/src
HOST_TOOLS_DIR= $(PROJECT_HOME)/host_tools
HOST_SCRIPTS_DIR=$(PROJECT_HOME)/host_scripts
# add include directories here ...
INCLUDES_DIR = $(PROJECT_HOME)/include
##### bootloader related (second stage) #####
BL2_ROOT_DIR = $(PROJECT_HOME)/bl2
BL2_ASM_DIR = $(BL2_ROOT_DIR)/asm
BL2_INC_DIR = $(BL2_ROOT_DIR)/include
BL2_C_SRC_DIR = $(BL2_ROOT_DIR)/src
TARGET_BIN_DIR = $(PROJECT_HOME)/target_bin
HOST_BIN_DIR = $(PROJECT_HOME)/host_bin
 
# Target Name
TARGET_NAME := Tiny210_SDK
PROJECT_TYPE := embedded_app
 
# Version Number
MAJOR := 1
MINOR := 00

IMGMAKE  =$(HOST_BIN_DIR)/imageMaker
BL1_BIN  = $(TARGET_BIN_DIR)/BL1.bin
BL1_ELF  = $(TARGET_BIN_DIR)/BL1.elf
BL1_LDS   = linker.lds
BL2_BIN = $(TARGET_BIN_DIR)/BL2.bin
BL2_ELF = $(TARGET_BIN_DIR)/BL2.elf
BL2_LDS   = $(BL2_ROOT_DIR)/BL2_linker.lds
 
#BL1 source files expressions ... 
BL1_SRCs_ASM += $(wildcard $(ASM_SRC_DIR)/*.s)
BL1_SRCs_C += $(wildcard $(SRC_DIR)/*.c)
OBJS_BL1 = $(BL1_SRCs_ASM:.s=.o) $(BL1_SRCs_C:.c=.o)
 
# BL1 include files expression ...
 
ASM_INCLUDES += $(wildcard $(INCLUDES_DIR)/*.inc)
C_INCLUDES += $(wildcard $(INCLUDES_DIR)/*.h)

#BL2 source files expressions ...
BL2_SRCs_ASM += $(wildcard $(BL2_ASM_DIR)/*.s)
BL2_SRCs_C += $(wildcard $(BL2_C_SRC_DIR)/*.c)
OBJS_BL2 = $(BL2_SRCs_ASM:.s=.o) $(BL2_SRCs_C:.c=.o)
CFLAGS =  -Os -nostdlib
CFLAGS_HOST = -Os -Wall
 
all:  $(IMGMAKE) $(BL1_BIN).boot  $(BL1_BIN) $(BL2_BIN) 
$(BL1_BIN):      $(OBJS_BL1)
      $(LD) -T $(BL1_LDS) -o $(BL1_ELF) -Map BL1.map $(OBJS_BL1)
      $(OBJCOPY) -O binary $(BL1_ELF) $(BL1_BIN)
$(BL1_BIN).boot: $(BL1_BIN) $(IMGMAKE)
      $(IMGMAKE) ./$(BL1_BIN) ./$(BL1_BIN).boot
$(BL2_BIN):   $(OBJS_BL2)
       $(LD) -T $(BL2_LDS) -o $(BL2_ELF) -Map $(BL2_ROOT_DIR)/BL2.map $(OBJS_BL2)
       $(OBJCOPY) -O binary $(BL2_ELF) $(BL2_BIN)
$(IMGMAKE):
       gcc $(CFLAGS_HOST) -o $(IMGMAKE) $(HOST_TOOLS_DIR)/imageMaker.c
fuse: $(BL1_BIN).boot $(BL2_BIN)
       . $(HOST_SCRIPTS_DIR)/fuseBin.sh
%.o: %.c
       $(CC) -c $(CFLAGS) -I . $(INCLUDES_DIR)  $< -o $@
%.o: %.s
       $(AS) -c $(AFLAGS) -I  $(ASM_INCLUDES)  $< -o $@
%elf: $(OBJS_BL1)
       $(CC) $(OBJS_BL1) $(LDFLAGS) $(LIBS) -o $@
%hex: %elf
       $(HEX) $< $@
%bin: %elf
       $(BIN)  $< $@
readFileBytes: 
       gcc $(CFLAGS_HOST) -o $(HOST_BIN_DIR)/readBytes $(HOST_TOOLS_DIR)/readFileBytes.c
clean:
 
       rm -rf $(OBJS)  $(HOST_BIN_DIR)/*  $(TARGET_BIN_DIR)/*.bin $(TARGET_BIN_DIR)/*.elf $(TARGET_BIN_DIR)/*.boot $(TARGET_BIN_DIR)/*.o *.map *.o $(ASM_SRC_DIR)/*.o SRC_DIR/*.o $(BL2_ASM_DIR)/*.o $(BL2_ROOT_DIR)/*.map

There are a few things to note about this makefile, it maintains host tools that are compiled to run on the host machine, in addition to the compilation of binaries to run on the target. It also runs a script that is used to fuse binaries into the MMC card.
All intermediate objects that are produced through the compilation process reside in the same directory as their sources, but the final result of target binaries are produced in the target_bin directory for target binaries, and host_bin for host tools. Some Makefile targets are for testing purposes, and are not used in the build process.
Operating system build rules are missing, and will be completed when we need them, some BL2 rules are also missing, and will be completed as we progress through the next couple of posts.
Last but not least, a great deal of attention should be paid to the $(HOST_BIN_DIR)/* bit under clean: target, if you misspell the variable name, or provide a variable name that holds no value, then the simple command make clean will recursively and forcefully remove your root directory and its contents, this is basically death.
Finally, some files related to BL2 are added for testing purposes, they will be modified soon, we are now focusing on building BL1.
Next, we'll go back to programming. We'll initialize the UART subsystem to assist us in finishing the low level initialization of the system.