Final clean-up (hopefully)

Improved dependency tracking in build system
Added ability to build single target application
Added abililty to build non-testsuite applications
Added 'helloworld' example application
This commit is contained in:
Tido Klaassen
2015-07-14 18:56:15 +02:00
parent 9d41fccc86
commit 00a339e3fe
14 changed files with 263 additions and 80 deletions

View File

@@ -61,7 +61,18 @@ common_dir=$(src_dir)/common
kernel_dir=$(src_dir)/../../kernel
tests_dir=$(src_dir)/../../tests
# Object files
# Check if user wants to use external opencm3 lib or if we have to build
# it ourselves
ifeq ($(OPENCM3_DIR),)
OPENCM3_DIR = $(src_dir)/libopencm3
build_lib = true
endif
ifneq ($(V),)
$(info Using $(OPENCM3_DIR) as path to opencm3 library)
endif
# Object files needed by all applications
objs += atomport.o
objs += atomport-asm.o
@@ -72,31 +83,13 @@ objs += atommutex.o
objs += atomtimer.o
objs += atomqueue.o
objs += tests-main.o
# Collection of built objects (excluding test applications)
build_objs = $(foreach obj,$(objs),$(build_dir)/$(obj))
# Target application filenames .elf for each test object
tobjs = $(notdir $(patsubst %.c,%.o,$(wildcard $(tests_dir)/*.c)))
telfs = $(patsubst %.o,%.elf,$(tobjs))
tbins = $(patsubst %.o,%.bin,$(tobjs))
thexs = $(patsubst %.o,%.hex,$(tobjs))
build_tobjs = $(foreach tobj,$(tobjs),$(build_dir)/$(tobj))
build_telfs = $(foreach telf,$(telfs),$(build_dir)/$(telf))
build_tbins = $(foreach tbin,$(tbins),$(build_dir)/$(tbin))
build_thexs = $(foreach thex,$(thexs),$(build_dir)/$(thex))
# Check if user wants to use external opencm3 lib or if we have to build
# it ourselves
ifeq ($(OPENCM3_DIR),)
OPENCM3_DIR = $(src_dir)/libopencm3
lib_needed = build_lib
endif
ifneq ($(V),)
$(info Using $(OPENCM3_DIR) as path to opencm3 library)
endif
# Object needed by all test applications, but not user apps
tmobjs = tests-main.o
build_tmobjs = $(foreach obj,$(tmobjs),$(build_dir)/$(obj))
build_tmobjs += $(build_objs)
# Set up search paths for libopencm3
INCLUDE_DIR = $(OPENCM3_DIR)/include
@@ -105,7 +98,7 @@ SCRIPT_DIR = $(OPENCM3_DIR)/scripts
# GCC flags
CFLAGS = -Os -g
CFLAGS += -Wall -Wshadow -Wimplicit-function-declaration
CFLAGS += -Wall -Werror
CFLAGS += -Wredundant-decls -Wstrict-prototypes
CFLAGS += -fno-common -ffunction-sections -fdata-sections
@@ -116,7 +109,7 @@ endif
# C & C++ preprocessor common flags
CPPFLAGS += -MD
CPPFLAGS += -Wall -Wundef
CPPFLAGS += -Wall -Wundef -Werror
CPPFLAGS += -I$(INCLUDE_DIR) $(DEFS)
CPPFLAGS += -I$(board_dir) -I$(common_dir) -I$(src_dir) -I$(kernel_dir) -I$(tests_dir)
@@ -157,15 +150,65 @@ else
LDLIBS += -Wl,--start-group --specs=rdimon.specs -lc -lrdimon -lgcc -Wl,--end-group
endif
# All
.PHONY: all
all: $(LIB_DIR)/lib$(LIBNAME).a $(build_tbins) $(build_thexs) $(build_telfs) $(build_tobjs) $(build_objs) Makefile $(board_dir)/Makefile.include
all: build_all
# add build dependency for local libopencm3 if no external libopencm3 is used
$(LIB_DIR)/lib$(LIBNAME).a: $(lib_needed)
# Generate a direct make target for a test application
define build_test
.PHONY: $(1)
$(1): $(build_dir)/$(1).elf
endef
.PHONY: build_lib
build_lib:
# Target application filenames .elf for each test object
tobjs = $(notdir $(patsubst %.c,%.o,$(wildcard $(tests_dir)/*.c)))
telfs = $(patsubst %.o,%.elf,$(tobjs))
tbins = $(patsubst %.o,%.bin,$(tobjs))
thexs = $(patsubst %.o,%.hex,$(tobjs))
build_tobjs = $(foreach tobj,$(tobjs),$(build_dir)/$(tobj))
build_telfs = $(foreach telf,$(telfs),$(build_dir)/$(telf))
build_tbins = $(foreach tbin,$(tbins),$(build_dir)/$(tbin))
build_thexs = $(foreach thex,$(thexs),$(build_dir)/$(thex))
# Add a direct make target for every test app
$(foreach test,$(patsubst %.o,%,$(tobjs)),$(eval $(call build_test,$(test))))
# Define an explicit target and linker rule for an application not in the
# test suite. This will be a stand-alone applications located in the board
# or common directory
define build_app
.PHONY: $(1)
$(1): $(build_dir)/$(1).elf
$(build_dir)/$(1).elf $(build_dir)/$(1).map: $(LIB_DIR)/lib$(LIBNAME).a $(build_dir)/$(1).o $(build_objs) $(LDSCRIPT)
$$(Q)mkdir -p `dirname $$@`
$$(if $$(Q), @echo " (ELF) $$(subst $$(build_dir)/,,$$@)")
$$(Q)$$(LD) $$(LDFLAGS) $$(ARCH_FLAGS) $$(build_objs) $$(build_dir)/$(1).o $(LDLIBS) -o $$@
endef
# Target application filenames .elf for each user app object
aelfs = $(patsubst %.o,%.elf,$(aobjs))
abins = $(patsubst %.o,%.bin,$(aobjs))
ahexs = $(patsubst %.o,%.hex,$(aobjs))
build_aobjs = $(foreach aobj,$(aobjs),$(build_dir)/$(aobj))
build_aelfs = $(foreach aelf,$(aelfs),$(build_dir)/$(aelf))
build_abins = $(foreach abin,$(abins),$(build_dir)/$(abin))
build_ahexs = $(foreach ahex,$(ahexs),$(build_dir)/$(ahex))
# add a direct make target for every standalone app
$(foreach app,$(patsubst %.o,%,$(aobjs)),$(eval $(call build_app,$(app))))
# Build all test and user applications
all_bins = $(build_tbins) $(build_abins)
all_hexs = $(build_thexs) $(build_ahexs)
all_elfs = $(build_telfs) $(build_aelfs)
all_objs = $(build_tobjs) $(build_aobjs) $(build_tmobjs)
.PHONY: build_all
build_all: $(LIB_DIR)/lib$(LIBNAME).a $(all_bins) $(all_hexs) $(all_elfs) $(all_objs) Makefile $(board_dir)/Makefile.include
# Add build dependency for local libopencm3 if no external libopencm3 is used
ifeq ($(build_lib),true)
$(LIB_DIR)/lib$(LIBNAME).a:
$(Q)if [ ! -d libopencm3 ] ; then \
printf "######## ERROR ########\n"; \
printf "\tlibopencm3 is not initialized.\n"; \
@@ -177,7 +220,8 @@ build_lib:
printf "######## ERROR ########\n"; \
exit 1; \
fi
$(Q)$(MAKE) -C libopencm3
$(Q)$(MAKE) -C libopencm3 V=$(V)
endif
$(build_dir)/%.bin: $(build_dir)/%.elf
$(Q)mkdir -p `dirname $@`
@@ -199,10 +243,13 @@ $(build_dir)/%.list: $(build_dir)/%.elf
$(if $(Q), @echo " (OBJDUMP) $(subst $(build_dir)/,,$@)")
$(Q)$(OBJDUMP) -S $< > $@
$(build_dir)/%.elf $(build_dir)/%.map: $(build_dir)/%.o $(build_objs) $(LDSCRIPT) $(LIB_DIR)/lib$(LIBNAME).a
# This is the default rule for linking the test suite applications.
# User applications defined in a board Makefile fragment are linked
# by an explicitly generated rule.
$(build_dir)/%.elf $(build_dir)/%.map: $(LIB_DIR)/lib$(LIBNAME).a $(build_dir)/%.o $(build_tmobjs) $(LDSCRIPT)
$(Q)mkdir -p `dirname $@`
$(if $(Q), @echo " (ELF) $(subst $(build_dir)/,,$@)")
$(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(build_objs) $(build_dir)/$(*).o $(LDLIBS) -o $@
$(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(build_tmobjs) $(build_dir)/$(*).o $(LDLIBS) -o $@
$(build_dir)/%.o: $(src_dir)/%.S Makefile $(board_dir)/Makefile.include
$(Q)mkdir -p `dirname $@`
@@ -244,13 +291,17 @@ $(build_dir)/%.o: $(tests_dir)/%.c Makefile $(board_dir)/Makefile.include
$(if $(Q), @echo " (CC) $(subst $(build_dir)/,,$@)")
$(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -I`dirname $<` -c $< -o $@
# Clean
# Clean. Remove only atomthread's object files and images
.PHONY: clean
clean:
rm -rf doxygen-kernel
rm -rf doxygen-opencm3
rm -rf $(build_dir)
ifneq ($(lib_needed),)
# Real clean. Also clean libopencm3 if it was built in tree
.PHONY: realclean
realclean: clean
ifeq ($(build_lib),true)
$(Q)$(MAKE) -C libopencm3 V=$(V) clean
endif
@@ -258,56 +309,58 @@ endif
.PHONY: doxygen
doxygen:
doxygen $(kernel_dir)/Doxyfile
doxygen ./Doxyfile
ifeq ($(build_lib),true)
$(Q)$(MAKE) -C libopencm3 V=$(V) doc
endif
################################################################
# target flashing recipes "borrowed" from libopencm3-examples #
################################################################
flash: $(BINARY).flash
#################################################################################
# Target flashing recipes "borrowed" from libopencm3-examples. Mostly untested. #
#################################################################################
%.stlink-flash: $(build_dir)/%.bin
@printf " FLASH $<\n"
$(Q)$(STFLASH) write $(build_dir)/$(*).bin 0x8000000
$(if $(Q), @echo " (FLASH) $(subst $(build_dir)/,,$<)")
$(Q)$(STFLASH) write $< 0x8000000
ifeq ($(STLINK_PORT),)
ifeq ($(BMP_PORT),)
ifeq ($(OOCD_SERIAL),)
%.flash: $(build_dir)/%.hex
@printf " FLASH $<\n"
$(if $(Q), @echo " (FLASH) $(subst $(build_dir)/,,$<)")
@# IMPORTANT: Don't use "resume", only "reset" will work correctly!
-$(Q)$(OOCD) -f interface/$(OOCD_INTERFACE).cfg \
-f board/$(OOCD_BOARD).cfg \
-c "init" -c "reset init" \
-c "flash write_image erase $(build_dir)/$(*).hex" \
-c "flash write_image erase $<" \
-c "reset" \
-c "shutdown" $(NULL)
else
%.flash: $(build_dir)/%.hex
@printf " FLASH $<\n"
$(if $(Q), @echo " (FLASH) $(subst $(build_dir)/,,$<)")
@# IMPORTANT: Don't use "resume", only "reset" will work correctly!
-$(Q)$(OOCD) -f interface/$(OOCD_INTERFACE).cfg \
-f board/$(OOCD_BOARD).cfg \
-c "ft2232_serial $(OOCD_SERIAL)" \
-c "init" -c "reset init" \
-c "flash write_image erase $(build_dir)/$(*).hex" \
-c "flash write_image erase $<" \
-c "reset" \
-c "shutdown" $(NULL)
endif
else
%.flash: $(build_dir)/%.elf
@printf " GDB $(build_dir)/$(*).elf (flash)\n"
$(if $(Q), @echo " (GDB) $(subst $(build_dir)/,,$<)")
$(Q)$(GDB) --batch \
-ex 'target extended-remote $(BMP_PORT)' \
-x $(SCRIPT_DIR)/black_magic_probe_flash.scr \
$(build_dir)/$(*).elf
$<
endif
else
%.flash: $(build_dir)/%.elf
@printf " GDB $(build_dir)/$(*).elf (flash)\n"
$(if $(Q), @echo " (GDB) $(subst $(build_dir)/,,$<)")
$(Q)$(GDB) --batch \
-ex 'target extended-remote $(STLINK_PORT)' \
-x $(SCRIPT_DIR)/stlink_flash.scr \
$(build_dir)/$(*).elf
$<
endif
# Include auto-generated dependencies
-include $(all_objs:.o=.d)

View File

@@ -76,11 +76,20 @@ To build with an external libopencm3, run
```
make BOARD=*yourboard* OPENCM3_DIR=*/path/to/libopencm3* all
```
If your board Makefile also set up the necessary openocd variables, you can
use it to flash the application image:
Instead of building the whole test suite you can also just build a specific
application image by giving its base name. If, for example, you only want
to build the `queue2` test app, you can do that like this:
```
make BOARD=*yourboard* BINARY=*yourimage* flash
make BOARD=*yourboard* queue2
```
If your board Makefile also sets up the necessary openocd variables, you can
use it to flash the application image by appending `.flash` to its base name.
```
make BOARD=*yourboard* *yourimage*.flash
```
N.B.: with the ek-lm4f120xl board openocd will report an error after flashing.
I think it is because it can not handle the changed core clock after the
application starts, but I simply can't be bothered to further investigate this.
@@ -144,9 +153,9 @@ The following flags are only used for flashing the target with openocd:
* **OOCD** binary to call. Give full path if it is not in your PATH environment
variable.
* **OOCD_INTERFACE** tells open which interface config to use
* **OOCD_INTERFACE** tells open which interface configuration to use
* **OOCD_BOARD** tells openocd which board config file to use.
* **OOCD_BOARD** tells openocd which board configuration file to use.
* **objs** here you _append_ object files to include into _all_ binaries
built for this board. The main Makefile will search for matching source files
@@ -155,12 +164,19 @@ directory. You will usually have at least a `board_setup.c` specific to
your hardware and pull in the system and stdio stubs provided in the
`common` directory.
* **aobjs** the build system will build one application for each object you
add to this variable. It will do so by linking each of these objects with all
of the kernel-, port- and board-objects (but none of the testsuite-objects).
The source for the app-object should be located in either the board or
common directory and must contain the application's `main()` function.
The `helloworld.c` file in the common directory is provided as an example.
As you will probably know, variables assigned with the `?=` operator are
only altered if they have not been already set. This way you can easily test
different options for a build by providing the variables on the make command
line.
### Board-Specific Setup
### Board-Specific Set-up
All hardware needs to be initialised to some degree before it is ready to run
atomthread's kernel. At the very least you will have to
@@ -172,7 +188,7 @@ atomthread's kernel. At the very least you will have to
(Steps 3. and 4. might be optional if you do not plan to let your board
interact with the outside world in any way...)
The test suit programs expect your board set-up code to provide a function
The test suite programs expect your board set-up code to provide a function
named `int board_setup(void)` to perform this tasks.
### Linker Script

View File

@@ -1,16 +1,17 @@
TARGET ?= lm4f120xl
TARGET ?= lm4f120xl
LIBNAME ?= opencm3_lm4f
DEFS ?= -DLM4F
DEFS += -DSTD_CON=UART0
DEFS += -DMST_SIZE=0x400
LIBNAME ?= opencm3_lm4f
DEFS ?= -DLM4F
DEFS += -DSTD_CON=UART0
DEFS += -DMST_SIZE=0x400
FP_FLAGS ?= -mfloat-abi=hard -mfpu=fpv4-sp-d16
ARCH_FLAGS ?= -mthumb -mcpu=cortex-m4 $(FP_FLAGS)
FP_FLAGS ?= -mfloat-abi=hard -mfpu=fpv4-sp-d16
ARCH_FLAGS ?= -mthumb -mcpu=cortex-m4 $(FP_FLAGS)
OOCD ?= openocd
OOCD ?= openocd
OOCD_INTERFACE ?= ti-icdi
OOCD_BOARD ?= ek-lm4f120xl
OOCD_BOARD ?= ek-lm4f120xl
objs += board_setup.o
objs += stubs.o lm4f_con.o
objs += board_setup.o
objs += stubs.o lm4f_con.o
# aobjs += helloworld.o

View File

@@ -93,7 +93,7 @@ static void clock_setup(void)
/**
* Set up user LED and provide function for toggling it. This is for
* use by the test suit programs
* use by the test suite programs
*/
static void test_led_setup(void)
{

View File

@@ -1,4 +1,5 @@
TARGET ?= stm32f072rb
LIBNAME ?= opencm3_stm32f0
DEFS ?= -DSTM32F0
DEFS += -DSTD_CON=USART2
@@ -13,3 +14,4 @@ OOCD_BOARD ?= st_nucleo_f0
objs += board_setup.o
objs += stubs.o stm32_con.o
# aobjs += helloworld.o

View File

@@ -87,7 +87,7 @@ static void clock_setup(void)
/**
* Set up user LED and provide function for toggling it. This is for
* use by the test suit programs
* use by the test suite programs
*/
static void test_led_setup(void)
{

View File

@@ -1,4 +1,5 @@
TARGET ?= stm32f103rb
LIBNAME ?= opencm3_stm32f1
DEFS ?= -DSTM32F1
DEFS += -DSTD_CON=USART2
@@ -13,3 +14,4 @@ OOCD_BOARD ?= st_nucleo_f103rb
objs += board_setup.o
objs += stubs.o stm32_con.o
# aobjs += helloworld.o

View File

@@ -89,7 +89,7 @@ static void clock_setup(void)
/**
* Set up user LED and provide function for toggling it. This is for
* use by the test suit programs
* use by the test suite programs
*/
static void test_led_setup(void)
{

View File

@@ -1,4 +1,5 @@
TARGET ?= stm32f411re
LIBNAME ?= opencm3_stm32f4
DEFS ?= -DSTM32F4
DEFS += -DSTD_CON=USART2
@@ -13,3 +14,4 @@ OOCD_BOARD ?= st_nucleo_f4
objs += board_setup.o
objs += stubs.o stm32_con.o
# aobjs += helloworld.o

View File

@@ -91,7 +91,7 @@ static void clock_setup(void)
/**
* Set up user LED and provide function for toggling it. This is for
* use by the test suit programs
* use by the test suite programs
*/
static void test_led_setup(void)
{

View File

@@ -12,3 +12,4 @@ OOCD_INTERFACE ?= stlink-v2-1
OOCD_BOARD ?= st_nucleo_f103rb
objs += board_setup.o
# aobjs += helloworld.o

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2015, Tido Klaassen. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. No personal names or organizations' names associated with the
* Atomthreads project may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <libopencm3/cm3/nvic.h>
#include "atom.h"
#include "atomport-private.h"
#include "atomtimer.h"
#define STACK_SIZE 1024
#define THREAD_PRIO 42
static ATOM_TCB main_tcb;
static uint8_t thread_stacks[2][STACK_SIZE];
static void main_thread_func(uint32_t data);
/**
* Example for a stand-alone board application
*/
extern int board_setup(void);
int main(void)
{
int8_t status;
uint32_t loop;
/**
* Brief delay to give the debugger a chance to stop the core before we
* muck around with the chip's configuration.
*/
for(loop = 0;loop < 1000000;++loop){
__asm__("nop");
}
board_setup();
/**
* Initialise OS and set up idle thread
*/
status = atomOSInit(&thread_stacks[0][0], STACK_SIZE, FALSE);
if(status == ATOM_OK){
/* Set up main thread */
status = atomThreadCreate(&main_tcb, THREAD_PRIO, main_thread_func, 0,
&thread_stacks[1][0], STACK_SIZE, TRUE);
if(status == ATOM_OK){
atomOSStart();
}
}
while(1)
;
/* We will never get here */
return 0;
}
extern void test_led_toggle(void);
static void main_thread_func(uint32_t data __maybe_unused)
{
/* Print message */
printf("Hello, world!\n");
/* Loop forever and blink the LED */
while(1){
test_led_toggle();
atomTimerDelay(SYSTEM_TICKS_PER_SEC);
}
}

View File

@@ -31,6 +31,8 @@
#include <libopencm3/cm3/vector.h>
#include "atomport.h"
/**
* _sbrk is needed by newlib for heap management.
* The main stack usually starts at the top of RAM and grows downwards,
@@ -82,29 +84,33 @@ caddr_t _sbrk(int incr)
/**
* dummy stubs needed by newlib when not linked with libnosys
*/
int _close(int file)
int _close(int file __maybe_unused)
{
return -1;
}
int _fstat(int file, struct stat *st)
int _fstat(int file __maybe_unused, struct stat *st)
{
st->st_mode = S_IFCHR;
return 0;
}
int _isatty(int file)
int _isatty(int file __maybe_unused)
{
return 1;
}
int _lseek(int file, int ptr, int dir)
int _lseek(int file __maybe_unused,
int ptr __maybe_unused,
int dir __maybe_unused)
{
return 0;
}
int _open(const char *name, int flags, int mode)
int _open(const char *name __maybe_unused,
int flags __maybe_unused,
int mode __maybe_unused)
{
return -1;
}

View File

@@ -220,7 +220,7 @@ int main ( void )
* @return None
*/
extern void test_led_toggle(void);
static void main_thread_func (uint32_t data)
static void main_thread_func (uint32_t data __maybe_unused)
{
uint32_t test_status;
int sleep_ticks;