diff --git a/kernel/atomkernel.c b/kernel/atomkernel.c index 7521c70..db9273f 100755 --- a/kernel/atomkernel.c +++ b/kernel/atomkernel.c @@ -342,17 +342,16 @@ static void atomThreadSwitch(ATOM_TCB *old_tcb, ATOM_TCB *new_tcb) /* Set the new currently-running thread pointer */ curr_tcb = new_tcb; + /** + * The context switch will shift execution to a different thread. The + * new thread is now ready to run so clear its suspend status in + * preparation for it waking up. + */ + new_tcb->suspended = FALSE; + /* Call the architecture-specific context switch */ archContextSwitch (old_tcb, new_tcb); } - - /** - * The context switch shifted execution to a different thread. By the time - * we get back here, we are running in old_tcb context again. Clear its - * suspend status now that we're back. - */ - old_tcb->suspended = FALSE; - } diff --git a/ports/cortex-m/Makefile b/ports/cortex-m/Makefile new file mode 100644 index 0000000..2a1e14f --- /dev/null +++ b/ports/cortex-m/Makefile @@ -0,0 +1,366 @@ +################################################ +# Toplevel makefile for all Cortex-M targets # +################################################ + +ifeq ($(V),) + Q := @ + # Do not print "Entering directory ...". + MAKEFLAGS += --no-print-directory +endif + +# Build directory +ifdef O + build_dir=$(shell readlink -f $(O)) +else + build_dir=$(CURDIR)/build +endif + +# Source directory +src_dir=$(CURDIR) + +# Clean object list before including board makefile +objs := +aobjs := + +# set default board if none is given +ifeq ($(BOARD),) + BOARD = nucleo-f103rb +endif + +include $(src_dir)/boards/$(BOARD)/Makefile.include + +# Make sure target MCU is set +ifndef TARGET + $(error TARGET undefined) +endif + +# Configure toolchain +CROSS_COMPILE ?= arm-none-eabi- + +CC := $(CROSS_COMPILE)gcc +CXX := $(CROSS_COMPILE)g++ +LD := $(CROSS_COMPILE)gcc +AR := $(CROSS_COMPILE)ar +AS := $(CROSS_COMPILE)as +OBJCOPY := $(CROSS_COMPILE)objcopy +OBJDUMP := $(CROSS_COMPILE)objdump +GDB := $(CROSS_COMPILE)gdb +STFLASH = $(shell which st-flash) + +LDSCRIPT ?= linker/$(TARGET).ld + +# Enable stack-checking. WARNING: the full automated test suite currently +# requires a little over 1KB RAM with stack-checking enabled. If you are +# using a device with 1KB internal SRAM and no external SRAM then you +# must disable stack-checking to run all of the automated tests. +#STACK_CHECK=true + +# Location of atomthreads sources +board_dir=$(src_dir)/boards/$(BOARD) +common_dir=$(src_dir)/common +kernel_dir=$(src_dir)/../../kernel +tests_dir=$(src_dir)/../../tests + +# 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 + +# Kernel object files +objs += atomkernel.o +objs += atomsem.o +objs += atommutex.o +objs += atomtimer.o +objs += atomqueue.o + +# Collection of built objects (excluding test applications) +build_objs = $(foreach obj,$(objs),$(build_dir)/$(obj)) + +# 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 +LIB_DIR = $(OPENCM3_DIR)/lib +SCRIPT_DIR = $(OPENCM3_DIR)/scripts + +# GCC flags +CFLAGS = -Os -g +CFLAGS += -Wall -Werror +CFLAGS += -Wredundant-decls -Wstrict-prototypes +CFLAGS += -fno-common -ffunction-sections -fdata-sections + +# Enable stack-checking (disable if not required) +ifeq ($(STACK_CHECK),true) +CFLAGS += -DATOM_STACK_CHECKING -DTESTS_LOG_STACK_USAGE +endif + +# C & C++ preprocessor common flags +CPPFLAGS += -MD +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) + +# Linker flags +LDFLAGS += --static -nostartfiles +LDFLAGS += -L$(LIB_DIR) +LDFLAGS += -T$(LDSCRIPT) +LDFLAGS += -Wl,-Map=$(build_dir)/$(*).map +LDFLAGS += -Wl,--gc-sections +ifeq ($(V),99) +LDFLAGS += -Wl,--print-gc-sections +endif + +## Used libraries +# Target specific version libopencm3 +LDLIBS += -l$(LIBNAME) + +ifneq ($(BOARD),qemu) +## Flags for newlibc. +# This uses the version of newlib optimised for size and without libnosys. +# This usually is the version of newlib you want to use, although it has some +# limitations, like printf not having support for floating point vars. +LDLIBS += -Wl,--start-group -lc_nano -lgcc -Wl,--end-group + +# This is the fully bloated version of newlib. Adds about 20k compared to +# libc_nano. +#LDLIBS += -Wl,--start-group -lc -lgcc -Wl,--end-group + +# Use small newlib with libnosys if you do not want to use the provided stubs. +# Be advised that heap management will probaly break in interesting ways. +# This is because libnosys' standard _sbrk() expects the stack to start at +# the top end of memory while the threads' stacks are positioned inside +# the BSS. +#LDLIBS += -Wl,--start-group -lc_nano -lgcc -lnosys -Wl,--end-group +else +# Special LDLIBS for qemu target to enable semi-hosting. +# TODO: Check if this is also useful for real hardware +LDLIBS += -Wl,--start-group --specs=rdimon.specs -lc -lrdimon -lgcc -Wl,--end-group +endif + +.PHONY: all +all: build_all + +# Generate a direct make target for a test application +define build_test +.PHONY: $(1) +$(1): $(build_dir)/$(1).elf +endef + +# 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"; \ + printf "\tPlease run:\n"; \ + printf "\t$$ git submodule add https://github.com/libopencm3/libopencm3.git\n"; \ + printf "\t$$ git submodule init\n"; \ + printf "\t$$ git submodule update\n"; \ + printf "\tbefore running make.\n"; \ + printf "######## ERROR ########\n"; \ + exit 1; \ + fi + $(Q)$(MAKE) -C libopencm3 V=$(V) +endif + +$(build_dir)/%.bin: $(build_dir)/%.elf + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (OBJCOPY) $(subst $(build_dir)/,,$@)") + $(Q)$(OBJCOPY) -O binary $< $@ + +$(build_dir)/%.hex: $(build_dir)/%.elf + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (OBJCOPY) $(subst $(build_dir)/,,$@)") + $(Q)$(OBJCOPY) -O ihex $< $@ + +$(build_dir)/%.srec: $(build_dir)/%.elf + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (OBJCOPY) $(subst $(build_dir)/,,$@)") + $(Q)$(OBJCOPY) -O srec $< $@ + +$(build_dir)/%.list: $(build_dir)/%.elf + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (OBJDUMP) $(subst $(build_dir)/,,$@)") + $(Q)$(OBJDUMP) -S $< > $@ + +# 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_tmobjs) $(build_dir)/$(*).o $(LDLIBS) -o $@ + +$(build_dir)/%.o: $(src_dir)/%.S Makefile $(board_dir)/Makefile.include + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (AS) $(subst $(build_dir)/,,$@)") + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -D__ASSEMBLY__ -I`dirname $<` -c $< -o $@ + +$(build_dir)/%.o: $(src_dir)/%.c Makefile $(board_dir)/Makefile.include + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (CC) $(subst $(build_dir)/,,$@)") + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -I`dirname $<` -c $< -o $@ + +$(build_dir)/%.o: $(board_dir)/%.S Makefile $(board_dir)/Makefile.include + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (AS) $(subst $(build_dir)/,,$@)") + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -D__ASSEMBLY__ -I`dirname $<` -c $< -o $@ + +$(build_dir)/%.o: $(board_dir)/%.c Makefile $(board_dir)/Makefile.include + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (CC) $(subst $(build_dir)/,,$@)") + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -I`dirname $<` -c $< -o $@ + +$(build_dir)/%.o: $(common_dir)/%.S Makefile $(board_dir)/Makefile.include + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (AS) $(subst $(build_dir)/,,$@)") + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -D__ASSEMBLY__ -I`dirname $<` -c $< -o $@ + +$(build_dir)/%.o: $(common_dir)/%.c Makefile $(board_dir)/Makefile.include + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (CC) $(subst $(build_dir)/,,$@)") + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -I`dirname $<` -c $< -o $@ + +$(build_dir)/%.o: $(kernel_dir)/%.c Makefile $(board_dir)/Makefile.include + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (CC) $(subst $(build_dir)/,,$@)") + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -I`dirname $<` -c $< -o $@ + +$(build_dir)/%.o: $(tests_dir)/%.c Makefile $(board_dir)/Makefile.include + $(Q)mkdir -p `dirname $@` + $(if $(Q), @echo " (CC) $(subst $(build_dir)/,,$@)") + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -I`dirname $<` -c $< -o $@ + +# Clean. Remove only atomthread's object files and images +.PHONY: clean +clean: + rm -rf doxygen-kernel + rm -rf doxygen-opencm3 + rm -rf $(build_dir) + +# 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 + +# Docs +.PHONY: doxygen +doxygen: + doxygen $(kernel_dir)/Doxyfile +ifeq ($(build_lib),true) + $(Q)$(MAKE) -C libopencm3 V=$(V) doc +endif + +################################################################################# +# Target flashing recipes "borrowed" from libopencm3-examples. Mostly untested. # +################################################################################# + +%.stlink-flash: $(build_dir)/%.bin + $(if $(Q), @echo " (FLASH) $(subst $(build_dir)/,,$<)") + $(Q)$(STFLASH) write $< 0x8000000 + +ifeq ($(STLINK_PORT),) +ifeq ($(BMP_PORT),) +ifeq ($(OOCD_SERIAL),) +%.flash: $(build_dir)/%.hex + $(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 $<" \ + -c "reset" \ + -c "shutdown" $(NULL) +else +%.flash: $(build_dir)/%.hex + $(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 $<" \ + -c "reset" \ + -c "shutdown" $(NULL) +endif +else +%.flash: $(build_dir)/%.elf + $(if $(Q), @echo " (GDB) $(subst $(build_dir)/,,$<)") + $(Q)$(GDB) --batch \ + -ex 'target extended-remote $(BMP_PORT)' \ + -x $(SCRIPT_DIR)/black_magic_probe_flash.scr \ + $< +endif +else +%.flash: $(build_dir)/%.elf + $(if $(Q), @echo " (GDB) $(subst $(build_dir)/,,$<)") + $(Q)$(GDB) --batch \ + -ex 'target extended-remote $(STLINK_PORT)' \ + -x $(SCRIPT_DIR)/stlink_flash.scr \ + $< +endif + +# Include auto-generated dependencies +-include $(all_objs:.o=.d) \ No newline at end of file diff --git a/ports/cortex-m/README.md b/ports/cortex-m/README.md new file mode 100644 index 0000000..53b61a3 --- /dev/null +++ b/ports/cortex-m/README.md @@ -0,0 +1,233 @@ +# ARM Cortex-M Port + +Author: Tido Klaassen + +License: BSD Revised. Needs libopencm3, which is LGPLv3. + +## Summary and Prerequisites +This port should run on any Cortex-M0/3/4/4F (M0+ not tested). +It uses RedHat's [newlib](https://sourceware.org/newlib/) and [libopencm3] +(https://github.com/libopencm3/libopencm3). You will also need an ARM compiler +toolchain. If you want to flash or debug your target, [openocd](http://openocd.org) +is also a must. For STM32 targets [stlink](https://github.com/texane/stlink) +might be helpful. + +On Debian systems, compiler, newlib and openocd can easily be installed by the +package manager (untested, not going to set up a new system just to test this): + +``` +apt-get install gcc-arm-none-eabi binutils-arm-none-eabi +apt-get install libnewlib-arm-none-eabi libnewlib-dev +apt-get install openocd +``` + +## Code Layout +The "classic" port components (code needed for task set-up and context +switching and the atomport{-private}.h headers) are residing in the +top level port directory. + +There are additional subdirectories: + +* **boards** contains subdirectories for specific hardware. Each +board needs at least a Makefile fragment, which defines certain variables +describing the hardware used, as well as a list of extra object files needed. +There will usually be at least a `board_setup.c`, which contains code to +initialise the hardware properly (clocks, UART, systick timer, GPIOs, etc.). + +* **common** contains code needed by multiple boards, such as +stub functions for newlib or routines shared by multiple boards using the +same family of target MCUs. + +* **linker** contains linker script fragments for specific target MCUs. These +just define the target's memory areas (RAM, Flash) and include libopencm3's +main linker script for the appropriate chip family. + +* **libopencm3** unless you compile and link against an external installation +of libopencm3, this will be a Git submodule containing, surprise!, libopencm3. + +* **build** this is a temporary directory where all object files end up in and +which will be removed by `make clean`. + +## Build Preparation +Unless you decide to use an external installation of libopencm3, you will have +to set up the libopencm3 sub-module: +``` +git submodule add https://github.com/libopencm3/libopencm3.git +git submodule init +git submodule update +``` +Optional: As of 2015-07-08 the libopencm3 API has not been declared stable. If +future changes break the build, you can check out the older revision used while +developing this port: +``` +cd libopencm3 +git checkout a4bb8f7e240c9f238384cf86d009002ba42a25ed +``` + +## Building and Flashing +To build the test suite, run +``` +make BOARD=*yourboard* all +``` +where *yourboard* is the name of a directory in **boards**. If no BOARD is +given, it defaults to nucleo-f103rb. + +To build with an external libopencm3, run +``` +make BOARD=*yourboard* OPENCM3_DIR=*/path/to/libopencm3* all +``` + +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* 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. +The application gets flashed and you can ignore the error. + +## Adding New Boards +To add support for a new board, you will have to provide at least two parts. +First, a `Makefile.include` to be pulled in by the main Makefile. Second, +some set-up code for your specific hardware. If there is no linker script for +your MCU, you will have to add one, too. + +### Board Makefile Fragment +The main Makefile will include the Makefile.include located in the chosen +board's sub-directory. This is where you set compile time options and additional +source files to be used. Here is the one for the nucleo-f103rb: + +``` +TARGET ?= stm32f103rb +LIBNAME ?= opencm3_stm32f1 +DEFS ?= -DSTM32F1 +DEFS += -DSTD_CON=USART2 +DEFS += -DMST_SIZE=0x400 + +FP_FLAGS ?= -msoft-float +ARCH_FLAGS ?= -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd + +OOCD ?= openocd +OOCD_INTERFACE ?= stlink-v2-1 +OOCD_BOARD ?= st_nucleo_f103rb + +objs += board_setup.o +objs += stubs.o stm32_con.o +``` + +* **TARGET** is the name for the actual MCU used on your board. It will be used +to locate the linker script file in the `linker` directory by appending +`.ld` to it. + +* **LIBNAME** is the name of the opencm3 library to link your object files +against. + +* **DEFS** are flags that will be appended to the CPPFLAGS. You will at least +have to define the opencm3 target family (STM32F1 here), so the build process +will find the right header files to include. If you are using the stub and +console functions provided in the common directory, you will also have to +define the reserved main stack size (MST_SIZE) and which UART to use for stdio +(STD_CON). + +* **FP_FLAGS** which floating point format to use. For MCUs without hardware +support for floating point (M0/3, sometimes 4), use `-msoft-float`, +otherwise use `-mfloat-abi=hard -mfpu=fpv4-sp-d16`. You could add these +directly to `ARCH_FLAGS`, but this way it is easily overridden from the +make command line. + +* **ARCH_FLAGS** specify the instruction (sub)set and CPU type to compile for, +as well as any quirk tunings needed and the `FP_FLAGS`. These flags are +handed to preprocessor, compiler, assembler and linker. + +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 configuration 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 +(*.c and *.S) in the main port directory, the board directory and the common +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 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 + +1. mask interrupts +2. set up the main clock and configure the systick timer +3. configure console UART +4. configure GPIOs + +(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 suite programs expect your board set-up code to provide a function +named `int board_setup(void)` to perform this tasks. + +### Linker Script +If you are using an hitherto unknown target MCU, you will have to provide a +linker script for it in the `linker` directory. The script's name must +be the same as given to the `TARGET` variable in your Makefile.include, +with the extension `.ld` added. It is recommended that you just define +your MCU's memory regions and include libopencm3's linker file for your +target's product family. Please look at the provided linker files for examples +on how to do this. + +## Port Internals +The Cortex-M port is different from the other architectures insofar as it makes +use of two particular features. First, it uses separate stacks for thread and +exception context. When the core enters exception mode, it first pushes xPSR, +PC, LR, r0-r3 and r12 on the currently active stack (probably the thread stack +in PSP) and then switches to the main stack stored in MSP. It also stores a +special EXC_RETURN code in LR which, when loaded into the PC, will determine +if on return program execution continues to use the MSP or switches over to +the PSP and, on cores with an FPU, whether FPU registers need to be restored. +The Cortex-M also implements a nested vectored interrupt controller (NVIC), +which means that a running ISR may be pre-empted by an exception of higher +priority. + +The use of separate stacks for thread and exception context has the nice +implication that you do not have to reserve space on every task's stack for +possible use by ISRs. But it also means that it breaks atomthreads concept +of simply swapping task stacks, regardless of if atomSched() was called from +thread or interrupt context. We would have to implement different +archContextSwitch() functions called from thread or exception context and +also do messy stack manipulations depending on whether the task to be +scheduled in was scheduled out in in the same context or not. Yuck! +And don't get me started on nested exceptions calling atomIntExit()... + +This is where the second feature comes handy, the PendSV mechanism. +PendSV is an asynchronous exception with the lowest possible priority, which +means that, when triggered, it will be called by the NVIC if there are no +other exceptions pending or running. We use it by having archContextSwitch() +set up a pointer to the TCB that should be scheduled in and then trigger the +PendSv exception. As soon as program flow leaves the critical section or +performs the outermost exception return, the pend_sv_handler() will be called +and the thread context switch takes place. diff --git a/ports/cortex-m/atomport-asm.S b/ports/cortex-m/atomport-asm.S new file mode 100644 index 0000000..6098ba3 --- /dev/null +++ b/ports/cortex-m/atomport-asm.S @@ -0,0 +1,361 @@ +/* + * 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. + */ + +.syntax unified + +/** + * Create more readable defines for usable intruction set and FPU + */ +#undef THUMB_2 +#undef WITH_FPU + +#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) +#define THUMB_2 +#endif + +#if defined(__VFP_FP__) && !defined(__SOFTFP__) +#define WITH_FPU +#endif + +/** + * Extern variables needed for context switching and first thread restore + */ +.extern CTX_SW_NFO +.extern vector_table + +/** + * Some bit masks and registers used + */ +.equ FPU_USED, 0x00000010 +.equ SCB_ICSR, 0xE000ED04 +.equ PENDSVCLR, 0x08000000 + +.text + +.global archFirstThreadRestore +.func archFirstThreadRestore +.type archFirstThreadRestore,%function +.thumb_func +archFirstThreadRestore: + /** + * Disable interrupts. They should be disabled anyway, but just + * to make sure... + */ + movs r1, #1 + msr PRIMASK, r1 + + /** + * Reset main stack pointer to initial value, which is the first entry + * in the vector table. + */ + ldr r1, = vector_table + ldr r1, [r1, #0] + msr MSP, r1 + + /* Update ctx_switch_info, set this thread as both running and next */ + ldr r1, = CTX_SW_NFO + str r0, [r1, #0] + str r0, [r1, #4] + + /* Get thread stack pointer from tcb. Conveniently the first element */ + ldr r1, [r0, #0] + msr PSP, r1 + + /** + * Set bit #1 in CONTROL. Causes switch to PSP, so we can work directly + * with SP now and use pop/push. + */ + movs r1, #2 + mrs r2, CONTROL + orrs r2, r2, r1 + msr CONTROL, r2 + + /** + * Initialise thread's register context from its stack frame. Since this + * function gets called only once at system start up, execution time is + * not critical. We can get away with using only Thumb-1 instructions that + * will work on all Cortex-M devices. + * + * Initial stack looks like this: + * xPSR + * PC + * lr + * r12 + * r3 + * r2 + * r1 + * r0 + * exc_ret <- ignored here + * r11 + * r10 + * r9 + * r8 + * r7 + * r6 + * r5 + * r4 <- thread's saved_sp points here + */ + + /** + * + * Move SP to position of r8 and restore high registers by loading + * them to r4-r7 before moving them to r8-r11 + */ + add SP, #16 + pop {r4-r7} + mov r8, r4 + mov r9, r5 + mov r10, r6 + mov r11, r7 + + /* move SP back to top of stack and load r4-r7 */ + sub SP, #32 + pop {r4-r7} + + /*load r12, lr, pc and xpsr to r0-r3 and restore r12 and lr */ + add SP, #36 + pop {r0-r3} + mov r12, r0 + mov lr, r1 + + /** + * r2 contains the PC and r3 APSR, SP is now at the bottom of the stack. We + * can't initialise APSR now because we will have to do a movs later when + * enabling interrupts, so r3 must not be touched. We also need an extra + * register holding the value that will be moved to PRIMASK. To do this, + * we build a new stack containing only the initial values of r2, r3 + * and pc. In the end this will be directly popped into the registers, + * finishing the thread restore and branching to the thread's entry point. + */ + + /* Save PC value */ + push {r2} + + /* Move values for r2 and r3 to lie directly below value for pc */ + sub SP, #20 + pop {r1-r2} + add SP, #12 + push {r1-r2} + + /* Load values for r0 and r1 from stack */ + sub SP, #20 + pop {r0-r1} + + /* Move SP to start of our new r2,r3,pc mini stack */ + add SP, #12 + + /* Restore xPSR and enable interrupts */ + movs r2, #0 + msr APSR_nzcvq, r3 + msr PRIMASK, r2 + + /* Pop r2,r3,pc from stack, thereby jumping to thread entry point */ + pop {r2,r3,pc} + nop + +.size archFirstThreadRestore, . - archFirstThreadRestore +.endfunc + +.global pend_sv_handler +.func pend_sv_handler +.type pend_sv_handler,%function +.thumb_func +pend_sv_handler: + /** + * Disable interrupts. No need to check if they were enabled because, + * well, we're an interrupt handler. Duh... + */ + movs r0, #1 + msr PRIMASK, r0 + + /** + * Clear PendSv pending bit. There seems to exist a hardware race condition + * in the NVIC that can prevent automatic clearing of the PENDSVSET. See + * http://embeddedgurus.com/state-space/2011/09/whats-the-state-of-your-cortex/ + */ + ldr r0, = SCB_ICSR + ldr r1, = PENDSVCLR + str r1, [r0, #0] + + /** + * Check if running and next thread are really different. + * From here on we have + * r0 = &ctx_switch_info + * r1 = ctx_switch_info.running_tcb + * r2 = ctx_switch_info.next_tcb + * + * If r1 == r2 we can skip the context switch. This may theoretically + * happen if the running thread gets scheduled out and in again by + * multiple nested or tail-chained ISRs before the PendSv handler + * gets called. + */ + ldr r0, = CTX_SW_NFO + ldr r1, [r0, #0] + ldr r2, [r0, #4] + cmp r1, r2 + beq no_switch + + /** + * Copy running thread's process stack pointer to r3 and use it to push + * */ + mrs r3, PSP + +#if defined(THUMB_2) + /** + * Save old thread's context on Cortex-M[34] + */ + +#if defined(WITH_FPU) + /* Check if FPU was used by thread and store registers if necessary */ + tst lr, FPU_USED + it eq + vstmdbeq r3!, {s16-s31} + + /** + * TODO: Defer stacking FPU context by disabling FPU and using a + * fault handler to store the FPU registers if another thread + * tries using it + */ +#endif // WITH_FPU + + /* Push running thread's remaining registers on stack */ + stmdb r3!, {r4-r11, lr} + +#else // !THUMB2 + + /** + * Save old thread's register context on Cortex-M0. + * Push running thread's remaining registers on stack. + * Thumb-1 can only use stm on low registers, so we + * have to do this in two steps. + */ + + /* Reserve space for r8-r11 + exc_return before storing r4-r7 */ + subs r3, r3, #36 + stmia r3!, {r4-r7} + + /** + * Move r8-r11 to low registers and use store multiple with automatic + * post-increment to push them on the stack + */ + mov r4, r8 + mov r5, r9 + mov r6, r10 + mov r7, r11 + stmia r3!, {r4-r7} + + /** + * Move lr (contains the exc_return code) to low registers and store it + * on the stack. + */ + mov r4, lr + str r4, [r3, #0] + + /* Re-adjust r3 to point at top of stack */ + subs r3, r3, #32 +#endif // !THUMB_2 + /** + * Address of running TCB still in r1. Store threads current stack top + * into its sp_save_ptr, which is the struct's first element + */ + str r3, [r1, #0] + + /** + * ctx_switch_info.next_tcb is going to become ctx_switch_info.running_tcb, + * so we update the pointer. + */ + str r2, [r0, #0] + + /** + * Fetch next thread's stack pointer from its TCB's sp_save_ptr and restore + * the thread's register context. + */ + ldr r3, [r2, #0] + +#if defined(THUMB_2) + + /* Cortex-M[34], restore thread's task stack frame */ + ldmia r3!, {r4-r11, lr} + +#if defined(WITH_FPU) + /** + * Check if FPU was used by new thread and restore registers if necessary. + */ + tst lr, FPU_USED + it eq + vldmiaeq r3!, {s16-s31} + + /** + * TODO: only restore FPU registers if FPU was used by another thread + * between this thread being scheduled out and now. + */ +#endif // WITH_FPU +#else // !THUMB_2 + + /** + * Thread restore for Cortex-M0 + * Restore thread's task stack frame. Because thumb 1 only supports + * load multiple on low register, we have to do it in two steps and + * adjust the stack pointer manually. + */ + + /* Restore high registers */ + adds r3, r3, #16 + ldmia r3!, {r4-r7} + mov r8, r4 + mov r9, r5 + mov r10, r6 + mov r11, r7 + + /* Restore lr */ + ldr r4, [r3, #0] + mov lr, r4 + subs r3, r3, #32 + + /** + * Restore r4-r7 and adjust r3 to point at the top of the exception + * stack frame. + */ + ldmia r3!, {r4-r7} + adds r3, r3, #20 +#endif // !THUMB_2 + + /* Set process stack pointer to new thread's stack*/ + msr PSP, r3 + +no_switch: + /* Re-enable interrupts */ + movs r0, #0 + msr PRIMASK, r0 + + /* Return to new thread */ + bx lr + nop +.size pend_sv_handler, . - pend_sv_handler +.endfunc diff --git a/ports/cortex-m/atomport-private.h b/ports/cortex-m/atomport-private.h new file mode 100644 index 0000000..6fe0e86 --- /dev/null +++ b/ports/cortex-m/atomport-private.h @@ -0,0 +1,117 @@ +/* + * 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. + */ + +#ifndef __ATOMPORT_PRIVATE_H_ +#define __ATOMPORT_PRIVATE_H_ + +#include "atomport.h" +#include "atom.h" + +/** + * context saved automagically by exception entry + */ +struct isr_stack { + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r12; + uint32_t lr; + uint32_t pc; + uint32_t psr; +} __attribute__((packed)); + +struct isr_fpu_stack { + uint32_t s0; + uint32_t s1; + uint32_t s2; + uint32_t s3; + uint32_t s4; + uint32_t s5; + uint32_t s6; + uint32_t s7; + uint32_t s8; + uint32_t s9; + uint32_t s10; + uint32_t s11; + uint32_t s12; + uint32_t s13; + uint32_t s14; + uint32_t s15; + uint32_t fpscr; +} __attribute__((packed)); + +/** + * remaining context saved by task switch ISR + */ +struct task_stack { + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t r7; + uint32_t r8; + uint32_t r9; + uint32_t r10; + uint32_t r11; + uint32_t exc_ret; +} __attribute__((packed)); + +struct task_fpu_stack { + uint32_t s16; + uint32_t s17; + uint32_t s18; + uint32_t s19; + uint32_t s20; + uint32_t s21; + uint32_t s22; + uint32_t s23; + uint32_t s24; + uint32_t s25; + uint32_t s26; + uint32_t s27; + uint32_t s28; + uint32_t s29; + uint32_t s30; + uint32_t s31; +} __attribute__((packed)); + +/** + * Info needed by pend_sv_handler used for delayed task switching. + * Running_tcb is a pointer to the TCB currently running (gosh, really?!) + * next_tcb is a pointer to a TCB that should be running. + * archContextSwitch() will update next_tcb and trigger a pend_sv. The + * pend_sv_handler will be called as soon as all other ISRs have returned, + * do the real context switch and update running_tcb. + */ +struct task_switch_info { + volatile struct atom_tcb *running_tcb; + volatile struct atom_tcb *next_tcb; +} __attribute__((packed)); + +#endif /* __ATOMPORT_PRIVATE_H_ */ diff --git a/ports/cortex-m/atomport-tests.h b/ports/cortex-m/atomport-tests.h new file mode 100644 index 0000000..0bc7152 --- /dev/null +++ b/ports/cortex-m/atomport-tests.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef __ATOM_PORT_TESTS_H +#define __ATOM_PORT_TESTS_H + +/* Include Atomthreads kernel API */ +#include +#include "atom.h" + +/* Default thread stack size (in bytes) */ +#define TEST_THREAD_STACK_SIZE 1024 + +/* Uncomment to enable logging of stack usage to UART */ +/* #define TESTS_LOG_STACK_USAGE */ + +#define ATOMLOG printf +#define _STR + +#endif /* __ATOM_PORT_TESTS_H */ + diff --git a/ports/cortex-m/atomport.c b/ports/cortex-m/atomport.c new file mode 100644 index 0000000..49ea73d --- /dev/null +++ b/ports/cortex-m/atomport.c @@ -0,0 +1,205 @@ +/* + * 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 +#include +#include +#include +#include + +#include "atomport.h" +#include "atomport-private.h" + +static void thread_shell(void); + +/** + * + */ +struct task_switch_info ctx_switch_info asm("CTX_SW_NFO") = +{ + .running_tcb = NULL, + .next_tcb = NULL, +}; + +/** + * We do not perform the context switch directly. Instead we mark the new tcb + * as should-be-running in ctx_switch_info and trigger a PendSv-interrupt. + * The pend_sv_handler will be called when all other pending exceptions have + * returned and perform the actual context switch. + * This way we do not have to worry if we are being called from task or + * interrupt context, which would mean messing with either main or thread + * stack format. + * + * One difference to the other architectures is that execution flow will + * actually continue in the old thread context until interrupts are enabled + * again. From a thread context this should make no difference, as the context + * switch will be performed as soon as the execution flow would return to the + * calling thread. Unless, of course, the thread called atomSched() with + * disabled interrupts, which it should not do anyways... + */ +void __attribute__((noinline)) +archContextSwitch(ATOM_TCB *old_tcb_ptr __maybe_unused, ATOM_TCB *new_tcb_ptr) +{ + if(likely(ctx_switch_info.running_tcb != NULL)){ + ctx_switch_info.next_tcb = new_tcb_ptr; + + __dmb(); + + SCB_ICSR = SCB_ICSR_PENDSVSET; + } +} + +void sys_tick_handler(void) +{ + /* Call the interrupt entry routine */ + atomIntEnter(); + + /* Call the OS system tick handler */ + atomTimerTick(); + + /* Call the interrupt exit routine */ + atomIntExit(TRUE); +} + +/** + * Put chip into infinite loop if NMI or hard fault occurs + */ +void nmi_handler(void) +{ + while(1) + ; +} + +void hard_fault_handler(void) +{ + while(1) + ; +} + + +/** + * This function is called when a new thread is scheduled in for the first + * time. It will simply call the threads entry point function. + */ +static void thread_shell(void) +{ + ATOM_TCB *task_ptr; + + /** + * We "return" to here after being scheduled in by the pend_sv_handler. + * We get a pointer to our TCB from atomCurrentContext() + */ + task_ptr = atomCurrentContext(); + + /** + * Our thread entry point and parameter are stored in the TCB. + * Call it if it is valid + */ + if(task_ptr && task_ptr->entry_point){ + task_ptr->entry_point(task_ptr->entry_param); + } + + /** + * Thread returned or entry point was not valid. + * Should never happen... Maybe we should switch MCU into debug mode here + */ + while(1) + ; +} + +/** + * Initialise a threads stack so it can be scheduled in by + * archFirstThreadRestore or the pend_sv_handler. + */ +void archThreadContextInit(ATOM_TCB *tcb_ptr, void *stack_top, + void (*entry_point)(uint32_t), uint32_t entry_param) +{ + struct isr_stack *isr_ctx; + struct task_stack *tsk_ctx; + + /** + * Enforce initial stack alignment + */ + stack_top = STACK_ALIGN(stack_top, STACK_ALIGN_SIZE); + + /** + * New threads will be scheduled from an exception handler, so we have to + * set up an exception stack frame as well as task stack frame + */ + isr_ctx = stack_top - sizeof(*isr_ctx); + tsk_ctx = stack_top - sizeof(*isr_ctx) - sizeof(*tsk_ctx); + +#if 0 + printf("[%s] tcb_ptr: %p stack_top: %p isr_ctx: %p tsk_ctx: %p entry_point: %p, entry_param: 0x%x\n", + __func__, tcb_ptr, stack_top, isr_ctx, tsk_ctx, entry_point, entry_param); + printf("[%s] isr_ctx->r0: %p isr_ctx->psr: %p tsk_ctx->r4: %p tsk_ctx->lr: %p\n", + __func__, &isr_ctx->r0, &isr_ctx->psr, &tsk_ctx->r4, &tsk_ctx->lr); +#endif + /** + * We use the exception return mechanism to jump to our thread_shell() + * function and initialise the PSR to the default value (thumb state + * flag set and nothing else) + */ + isr_ctx->psr = 0x01000000; + isr_ctx->pc = (uint32_t) thread_shell; + + /* initialise unused registers to silly value */ + isr_ctx->lr = 0xEEEEEEEE; + isr_ctx->r12 = 0xCCCCCCCC; + isr_ctx->r3 = 0x33333333; + isr_ctx->r2 = 0x22222222; + isr_ctx->r1 = 0x11111111; + isr_ctx->r0 = 0x00000000; + + /** + * We use this special EXC_RETURN code to switch from main stack to our + * thread stack on exception return + */ + tsk_ctx->exc_ret = 0xFFFFFFFD; + + /* initialise unused registers to silly value */ + tsk_ctx->r11 = 0xBBBBBBBB; + tsk_ctx->r10 = 0xAAAAAAAA; + tsk_ctx->r9 = 0x99999999; + tsk_ctx->r8 = 0x88888888; + tsk_ctx->r7 = 0x77777777; + tsk_ctx->r6 = 0x66666666; + tsk_ctx->r5 = 0x55555555; + tsk_ctx->r4 = 0x44444444; + + /** + * Stack frames have been initialised, save it to the TCB. Also set + * the thread's real entry point and param, so the thread shell knows + * what function to call. + */ + tcb_ptr->sp_save_ptr = tsk_ctx; + tcb_ptr->entry_point = entry_point; + tcb_ptr->entry_param = entry_param; +} + diff --git a/ports/cortex-m/atomport.h b/ports/cortex-m/atomport.h new file mode 100644 index 0000000..562b84e --- /dev/null +++ b/ports/cortex-m/atomport.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#ifndef __ATOM_PORT_H +#define __ATOM_PORT_H + +#include +#include +#include + +/* Required number of system ticks per second (normally 100 for 10ms tick) */ +#define SYSTEM_TICKS_PER_SEC 100 + +/* Size of each stack entry / stack alignment size (4 bytes on Cortex-M without FPU) */ +#define STACK_ALIGN_SIZE sizeof(uint32_t) + +#define ALIGN(x, a) ((x + (typeof(x))(a) - 1) & ~((typeof(x))(a) - 1)) +#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((uint32_t)(p), (a))) +#define STACK_ALIGN(p, a) (typeof(p))((typeof(a))(p) & ~((a) - 1)) + +#define POINTER void * +#define UINT32 uint32_t + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#define __maybe_unused __attribute__((unused)) + +/** + * Critical region protection: this should disable interrupts + * to protect OS data structures during modification. It must + * allow nested calls, which means that interrupts should only + * be re-enabled when the outer CRITICAL_END() is reached. + */ +#define CRITICAL_STORE bool __irq_flags +#define CRITICAL_START() __irq_flags = cm_mask_interrupts(true) +#define CRITICAL_END() (void) cm_mask_interrupts(__irq_flags) + +/* Uncomment to enable stack-checking */ +/* #define ATOM_STACK_CHECKING */ + +#endif /* __ATOM_PORT_H */ diff --git a/ports/cortex-m/boards/ek-lm4f120xl/Makefile.include b/ports/cortex-m/boards/ek-lm4f120xl/Makefile.include new file mode 100644 index 0000000..3a86c74 --- /dev/null +++ b/ports/cortex-m/boards/ek-lm4f120xl/Makefile.include @@ -0,0 +1,17 @@ +TARGET ?= lm4f120xl + +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) + +OOCD ?= openocd +OOCD_INTERFACE ?= ti-icdi +OOCD_BOARD ?= ek-lm4f120xl + +objs += board_setup.o +objs += stubs.o lm4f_con.o +# aobjs += helloworld.o \ No newline at end of file diff --git a/ports/cortex-m/boards/ek-lm4f120xl/board_setup.c b/ports/cortex-m/boards/ek-lm4f120xl/board_setup.c new file mode 100644 index 0000000..d014200 --- /dev/null +++ b/ports/cortex-m/boards/ek-lm4f120xl/board_setup.c @@ -0,0 +1,141 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include + +#include "atomport.h" + +static void uart_setup(uint32_t baud) +{ + periph_clock_enable(RCC_GPIOA); + gpio_set_af(GPIOA, 1, GPIO0 | GPIO1); + + periph_clock_enable(RCC_UART0); + + /* We need a brief delay before we can access UART config registers */ + __asm__("nop"); + + /* Disable the UART while we mess with its setings */ + uart_disable(UART0); + + /* Configure the UART clock source as precision internal oscillator */ + uart_clock_from_piosc(UART0); + + /* Set communication parameters */ + uart_set_baudrate(UART0, baud); + uart_set_databits(UART0, 8); + uart_set_parity(UART0, UART_PARITY_NONE); + uart_set_stopbits(UART0, 1); + + /* Now that we're done messing with the settings, enable the UART */ + uart_enable(UART0); +} + +/** + * initialise and start SysTick counter. This will trigger the + * sys_tick_handler() periodically once interrupts have been enabled + * by archFirstThreadRestore() + */ +static void systick_setup(void) +{ + systick_set_frequency(SYSTEM_TICKS_PER_SEC, 80000000); + + systick_interrupt_enable(); + + systick_counter_enable(); +} + +/** + * Set up the core clock to something other than the internal 16MHz PIOSC. + * Make sure that you use the same clock frequency in systick_setup(). + */ +static void clock_setup(void) +{ + /** + * set up 400MHz PLL from 16MHz crystal and divide by 5 to get 80MHz + * system clock + */ + rcc_sysclk_config(OSCSRC_MOSC, XTAL_16M, 5); +} + +/** + * Set up user LED and provide function for toggling it. This is for + * use by the test suite programs + */ +static void test_led_setup(void) +{ + /* LED is connected to GPIO1 on port F */ + periph_clock_enable(RCC_GPIOF); + gpio_mode_setup(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO1); + gpio_set_output_config(GPIOF, GPIO_OTYPE_PP, GPIO_DRIVE_2MA, GPIO1); + + gpio_set(GPIOF, GPIO1); +} + +void test_led_toggle(void) +{ + gpio_toggle(GPIOF, GPIO1); +} + +/** + * Callback from your main program to set up the board's hardware before + * the kernel is started. + */ +int board_setup(void) +{ + /* Disable interrupts. This makes sure that the sys_tick_handler will + * not be called before the first thread has been started. + * Interrupts will be enabled by archFirstThreadRestore(). + */ + cm_mask_interrupts(true); + + /* configure system clock, user LED and UART */ + gpio_enable_ahb_aperture(); + clock_setup(); + test_led_setup(); + uart_setup(115200); + + /* Initialise SysTick counter */ + systick_setup(); + + /* Set exception priority levels. Make PendSv the lowest priority and + * SysTick the second to lowest + */ + nvic_set_priority(NVIC_PENDSV_IRQ, 0xFF); + nvic_set_priority(NVIC_SYSTICK_IRQ, 0xFE); + + return 0; +} diff --git a/ports/cortex-m/boards/nucleo-f072rb/Makefile.include b/ports/cortex-m/boards/nucleo-f072rb/Makefile.include new file mode 100644 index 0000000..2c4c410 --- /dev/null +++ b/ports/cortex-m/boards/nucleo-f072rb/Makefile.include @@ -0,0 +1,17 @@ +TARGET ?= stm32f072rb + +LIBNAME ?= opencm3_stm32f0 +DEFS ?= -DSTM32F0 +DEFS += -DSTD_CON=USART2 +DEFS += -DMST_SIZE=0x400 + +FP_FLAGS ?= -msoft-float +ARCH_FLAGS ?= -mthumb -mcpu=cortex-m0 + +OOCD ?= openocd +OOCD_INTERFACE ?= stlink-v2-1 +OOCD_BOARD ?= st_nucleo_f0 + +objs += board_setup.o +objs += stubs.o stm32_con.o +# aobjs += helloworld.o \ No newline at end of file diff --git a/ports/cortex-m/boards/nucleo-f072rb/board_setup.c b/ports/cortex-m/boards/nucleo-f072rb/board_setup.c new file mode 100644 index 0000000..e3e1f4f --- /dev/null +++ b/ports/cortex-m/boards/nucleo-f072rb/board_setup.c @@ -0,0 +1,134 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include + +#include "atomport.h" + +/** + * Set up USART2. + * This one is connected via the virtual serial port on the Nucleo Board + */ +static void usart_setup(uint32_t baud) +{ + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_USART2); + + usart_disable(USART2); + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO3); + gpio_set_af(GPIOA, GPIO_AF1, GPIO2 | GPIO3); + + usart_set_baudrate(USART2, baud); + usart_set_databits(USART2, 8); + usart_set_parity(USART2, USART_PARITY_NONE); + usart_set_stopbits(USART2, USART_CR2_STOP_1_0BIT); + usart_set_mode(USART2, USART_MODE_TX_RX); + usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE); + + usart_enable(USART2); +} + +/** + * initialise and start SysTick counter. This will trigger the + * sys_tick_handler() periodically once interrupts have been enabled + * by archFirstThreadRestore() + */ +static void systick_setup(void) +{ + systick_set_frequency(SYSTEM_TICKS_PER_SEC, 48000000); + + systick_interrupt_enable(); + + systick_counter_enable(); +} + +/** + * Set up the core clock to something other than the internal 16MHz PIOSC. + * Make sure that you use the same clock frequency in systick_setup(). + */ +static void clock_setup(void) +{ + /* set core clock to 48MHz, generated from PIOSC */ + rcc_clock_setup_in_hsi_out_48mhz(); +} + +/** + * Set up user LED and provide function for toggling it. This is for + * use by the test suite programs + */ +static void test_led_setup(void) +{ + /* LED is connected to GPIO5 on port A */ + rcc_periph_clock_enable(RCC_GPIOA); + + gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO5); + gpio_set(GPIOA, GPIO5); +} + +void test_led_toggle(void) +{ + gpio_toggle(GPIOA, GPIO5); +} + +/** + * Callback from your main program to set up the board's hardware before + * the kernel is started. + */ +int board_setup(void) +{ + /* Disable interrupts. This makes sure that the sys_tick_handler will + * not be called before the first thread has been started. + * Interrupts will be enabled by archFirstThreadRestore(). + */ + cm_mask_interrupts(true); + + /* configure system clock, user LED and UART */ + clock_setup(); + test_led_setup(); + usart_setup(115200); + + /* initialise SysTick counter */ + systick_setup(); + + /* Set exception priority levels. Make PendSv the lowest priority and + * SysTick the second to lowest + */ + nvic_set_priority(NVIC_PENDSV_IRQ, 0xFF); + nvic_set_priority(NVIC_SYSTICK_IRQ, 0xFE); + + return 0; +} + diff --git a/ports/cortex-m/boards/nucleo-f103rb/Makefile.include b/ports/cortex-m/boards/nucleo-f103rb/Makefile.include new file mode 100644 index 0000000..fd66985 --- /dev/null +++ b/ports/cortex-m/boards/nucleo-f103rb/Makefile.include @@ -0,0 +1,17 @@ +TARGET ?= stm32f103rb + +LIBNAME ?= opencm3_stm32f1 +DEFS ?= -DSTM32F1 +DEFS += -DSTD_CON=USART2 +DEFS += -DMST_SIZE=0x400 + +FP_FLAGS ?= -msoft-float +ARCH_FLAGS ?= -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd + +OOCD ?= openocd +OOCD_INTERFACE ?= stlink-v2-1 +OOCD_BOARD ?= st_nucleo_f103rb + +objs += board_setup.o +objs += stubs.o stm32_con.o +# aobjs += helloworld.o \ No newline at end of file diff --git a/ports/cortex-m/boards/nucleo-f103rb/board_setup.c b/ports/cortex-m/boards/nucleo-f103rb/board_setup.c new file mode 100644 index 0000000..261bd96 --- /dev/null +++ b/ports/cortex-m/boards/nucleo-f103rb/board_setup.c @@ -0,0 +1,138 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include + +#include "atomport.h" + +/** + * Set up USART2. + * This one is connected via the virtual serial port on the Nucleo Board + */ +static void usart_setup(uint32_t baud) +{ + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_AFIO); + rcc_periph_clock_enable(RCC_USART2); + + usart_disable(USART2); + + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, + GPIO_USART2_TX | GPIO_USART2_RX); + + usart_set_baudrate(USART2, baud); + usart_set_databits(USART2, 8); + usart_set_stopbits(USART2, USART_STOPBITS_1); + usart_set_parity(USART2, USART_PARITY_NONE); + usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE); + usart_set_mode(USART2, USART_MODE_TX_RX); + + usart_enable(USART2); +} + +/** + * initialise and start SysTick counter. This will trigger the + * sys_tick_handler() periodically once interrupts have been enabled + * by archFirstThreadRestore() + */ +static void systick_setup(void) +{ + systick_set_frequency(SYSTEM_TICKS_PER_SEC, 72000000); + + systick_interrupt_enable(); + + systick_counter_enable(); +} + +/** + * Set up the core clock to something other than the internal 16MHz PIOSC. + * Make sure that you use the same clock frequency in systick_setup(). + */ +static void clock_setup(void) +{ + /* set core clock to 72MHz, generated from external 8MHz crystal */ + rcc_clock_setup_in_hse_8mhz_out_72mhz(); +} + +/** + * Set up user LED and provide function for toggling it. This is for + * use by the test suite programs + */ +static void test_led_setup(void) +{ + /* LED is connected to GPIO5 on port A */ + rcc_periph_clock_enable(RCC_GPIOA); + + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, GPIO5); + + gpio_set(GPIOA, GPIO5); +} + +void test_led_toggle(void) +{ + gpio_toggle(GPIOA, GPIO5); +} + +/** + * Callback from your main program to set up the board's hardware before + * the kernel is started. + */ +int board_setup(void) +{ + /* Disable interrupts. This makes sure that the sys_tick_handler will + * not be called before the first thread has been started. + * Interrupts will be enabled by archFirstThreadRestore(). + */ + cm_mask_interrupts(true); + + /* configure system clock, user LED and UART */ + clock_setup(); + test_led_setup(); + usart_setup(115200); + + /* initialise SysTick counter */ + systick_setup(); + + /* Set exception priority levels. Make PendSv the lowest priority and + * SysTick the second to lowest + */ + nvic_set_priority(NVIC_PENDSV_IRQ, 0xFF); + nvic_set_priority(NVIC_SYSTICK_IRQ, 0xFE); + + return 0; +} + diff --git a/ports/cortex-m/boards/nucleo-f411re/Makefile.include b/ports/cortex-m/boards/nucleo-f411re/Makefile.include new file mode 100644 index 0000000..1895295 --- /dev/null +++ b/ports/cortex-m/boards/nucleo-f411re/Makefile.include @@ -0,0 +1,17 @@ +TARGET ?= stm32f411re + +LIBNAME ?= opencm3_stm32f4 +DEFS ?= -DSTM32F4 +DEFS += -DSTD_CON=USART2 +DEFS += -DMST_SIZE=0x400 + +FP_FLAGS ?= -mfloat-abi=hard -mfpu=fpv4-sp-d16 +ARCH_FLAGS ?= -mthumb -mcpu=cortex-m4 $(FP_FLAGS) + +OOCD ?= openocd +OOCD_INTERFACE ?= stlink-v2-1 +OOCD_BOARD ?= st_nucleo_f4 + +objs += board_setup.o +objs += stubs.o stm32_con.o +# aobjs += helloworld.o \ No newline at end of file diff --git a/ports/cortex-m/boards/nucleo-f411re/board_setup.c b/ports/cortex-m/boards/nucleo-f411re/board_setup.c new file mode 100644 index 0000000..e7df081 --- /dev/null +++ b/ports/cortex-m/boards/nucleo-f411re/board_setup.c @@ -0,0 +1,136 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include + +#include "atomport.h" + +/** + * Set up USART2. + * This one is connected via the virtual serial port on the Nucleo Board + */ +static void usart_setup(uint32_t baud) +{ + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_USART2); + + usart_disable(USART2); + + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO3); + gpio_set_af(GPIOA, GPIO_AF7, GPIO2); + + usart_set_baudrate(USART2, baud); + usart_set_databits(USART2, 8); + usart_set_stopbits(USART2, USART_STOPBITS_1); + usart_set_parity(USART2, USART_PARITY_NONE); + usart_set_mode(USART2, USART_MODE_TX_RX); + usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE); + + usart_enable(USART2); +} + +/** + * initialise and start SysTick counter. This will trigger the + * sys_tick_handler() periodically once interrupts have been enabled + * by archFirstThreadRestore() + */ +static void systick_setup(void) +{ + systick_set_frequency(SYSTEM_TICKS_PER_SEC, 16000000); + + systick_interrupt_enable(); + + systick_counter_enable(); +} + +/** + * Set up the core clock to something other than the internal 16MHz PIOSC. + * Make sure that you use the same clock frequency in systick_setup(). + */ +static void clock_setup(void) +{ + /** + * Clock setting for stm32f4 is currently broken in libopencm3. + * Leave at internal 16MHz + */ + // rcc_clock_setup_hse_3v3(CLOCK_3V3_48MHZ); +} + +/** + * Set up user LED and provide function for toggling it. This is for + * use by the test suite programs + */ +static void test_led_setup(void) +{ + /* LED is connected to GPIO5 on port A */ + rcc_periph_clock_enable(RCC_GPIOA); + gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO5); + gpio_set(GPIOA, GPIO5); +} + +void test_led_toggle(void) +{ + gpio_toggle(GPIOA, GPIO5); +} + +/** + * Callback from your main program to set up the board's hardware before + * the kernel is started. + */ +int board_setup(void) +{ + /* Disable interrupts. This makes sure that the sys_tick_handler will + * not be called before the first thread has been started. + * Interrupts will be enabled by archFirstThreadRestore(). + */ + cm_mask_interrupts(true); + + /* configure system clock, user LED and UART */ + clock_setup(); + test_led_setup(); + usart_setup(115200); + + /* initialise SysTick counter */ + systick_setup(); + + /* Set exception priority levels. Make PendSv the lowest priority and + * SysTick the second to lowest + */ + nvic_set_priority(NVIC_PENDSV_IRQ, 0xFF); + nvic_set_priority(NVIC_SYSTICK_IRQ, 0xFE); + + return 0; +} diff --git a/ports/cortex-m/boards/qemu/Makefile.include b/ports/cortex-m/boards/qemu/Makefile.include new file mode 100644 index 0000000..02c7c94 --- /dev/null +++ b/ports/cortex-m/boards/qemu/Makefile.include @@ -0,0 +1,27 @@ +TARGET ?= stm32f103rb +LIBNAME ?= opencm3_stm32f1 +DEFS ?= -DSTM32F1 +DEFS += -DSTD_CON=USART2 +DEFS += -DMST_SIZE=0x400 + +FP_FLAGS ?= -msoft-float +ARCH_FLAGS ?= -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd + +OOCD ?= openocd +OOCD_INTERFACE ?= stlink-v2-1 +OOCD_BOARD ?= st_nucleo_f103rb + +objs += board_setup.o +# aobjs += helloworld.o + +# Special requirements for running automated tests with QEMU + +QEMU ?= qemu-system-gnuarmeclipse +.DEFAULT_GOAL := all +.SECONDEXPANSION: + +.PHONY: qemutests +qemutests: $$(addsuffix .sim, $$(sort $$(build_telfs))) + +%.sim: $$(basename $$@) + boards/qemu/run_test.exp $(QEMU) $(*) diff --git a/ports/cortex-m/boards/qemu/README.md b/ports/cortex-m/boards/qemu/README.md new file mode 100644 index 0000000..29889d8 --- /dev/null +++ b/ports/cortex-m/boards/qemu/README.md @@ -0,0 +1,31 @@ +This board is a quick n' dirty copy of nucleo-f103rb, stripped down to run +atomthread's test suite on qemu. Since it must be linked against newlib's +librdimon to enable semi-hosting, the stubs provided in the common directory +can not be used. Do not be surprised if malloc etc. do not work as expected. + +The [GNU ARM Eclipse](http://gnuarmeclipse.livius.net/blog/) project maintains +a fork of qemu that is able to emulate a Nucleo-F103RB board. Check out their +gnuarmeclipse-dev branch from [here] +(http://sourceforge.net/p/gnuarmeclipse/qemu/ci/gnuarmeclipse-dev/tree/). + +At time of writing (2015-07-13), I had to run configure with +`--disable-werror --target-list="gnuarmeclipse-softmmu"` to build +a usable target. + +After installing you can use it to run the test binaries like this: + +``` +qemu-system-gnuarmeclipse -nographic -monitor null \ + -semihosting --machine NUCLEO-F103RB \ + --verbose --kernel build/kern1.elf +``` + +The whole test suite can be run in an automatic way from the build system by +using the tool `expect`. + +``` +make BOARD=qemu QEMU=/path/to/your/qemu/binary qemutests +``` + +If your qemu-binary is called `qemu-system-gnuarmeclipse` and is located +in your search path, you can omit the `QEMU=...` part. diff --git a/ports/cortex-m/boards/qemu/board_setup.c b/ports/cortex-m/boards/qemu/board_setup.c new file mode 100644 index 0000000..ef4d008 --- /dev/null +++ b/ports/cortex-m/boards/qemu/board_setup.c @@ -0,0 +1,119 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include + +#include "atomport.h" + +/** + * initialise and start SysTick counter. This will trigger the + * sys_tick_handler() periodically once interrupts have been enabled + * by archFirstThreadRestore(). Since we did not change the clock source, + * the MCU is still running from 16MHz PIOSC + */ +static void systick_setup(void) +{ + systick_set_frequency(SYSTEM_TICKS_PER_SEC, 16000000); + + systick_interrupt_enable(); + + systick_counter_enable(); +} + +/** + * Set up the core clock to something other than the internal 16MHz PIOSC. + * Make sure that you use the same clock frequency in systick_setup(). + */ +/** + * Set up user LED and provide function for toggling it. This is for + * use by the test suite programs + */ +static void test_led_setup(void) +{ + /* LED is connected to GPIO5 on port A */ + rcc_periph_clock_enable(RCC_GPIOA); + + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, GPIO5); + + gpio_set(GPIOA, GPIO5); +} + +void test_led_toggle(void) +{ + static bool on = true; + + /* qemu does not seem to know how to handle gpio_toggle() */ + if(on){ + gpio_clear(GPIOA, GPIO5); + } else { + gpio_set(GPIOA, GPIO5); + } + + on = !on; +} + +/** + * Callback from your main program to set up the board's hardware before + * the kernel is started. + */ +extern void initialise_monitor_handles(void); +int board_setup(void) +{ + /* initialise semi-hosting */ + initialise_monitor_handles(); + + /* Disable interrupts. This makes sure that the sys_tick_handler will + * not be called before the first thread has been started. + * Interrupts will be enabled by archFirstThreadRestore(). + */ + cm_mask_interrupts(true); + + /* configure user LED*/ + test_led_setup(); + + /* initialise SysTick counter */ + systick_setup(); + + /* Set exception priority levels. Make PendSv the lowest priority and + * SysTick the second to lowest + */ + nvic_set_priority(NVIC_PENDSV_IRQ, 0xFF); + nvic_set_priority(NVIC_SYSTICK_IRQ, 0xFE); + + return 0; +} + diff --git a/ports/cortex-m/boards/qemu/run_test.exp b/ports/cortex-m/boards/qemu/run_test.exp new file mode 100755 index 0000000..c35ddf4 --- /dev/null +++ b/ports/cortex-m/boards/qemu/run_test.exp @@ -0,0 +1,40 @@ +#!/usr/bin/env expect + +# Expect script to run an automated test within the simulator (QEMU) and +# check for successful completion. +# +# Arguments: +# +# Returns 0 on successful test run within QEMU, 1 on failure + + +# Start the test +spawn [lindex $argv 0] -nographic -monitor null -semihosting \ + --machine NUCLEO-F103RB --kernel [lindex $argv 1] + +# Expect to see the test starting within 10 seconds +set timeout 10 + +# Wait for the test to start ("Go") +expect { + "Go" { + puts "Test started" + + # The test could take up to 3 minutes to complete once started + set timeout 180 + + # Now expect to see "Pass" or "Fail" within 3 minutes + expect { + "Pass" { puts "Test passed"; exit 0 } + "Fail" { puts "Test failed"; exit 1 } + timeout { puts "Test timed out without completing"; exit 1 } + } + } + + timeout { + # Didn't receive "Go" within 10 seconds + puts "Test failed to start ('Go' not seen)" + exit 1 + } +} + diff --git a/ports/cortex-m/common/helloworld.c b/ports/cortex-m/common/helloworld.c new file mode 100644 index 0000000..10aee37 --- /dev/null +++ b/ports/cortex-m/common/helloworld.c @@ -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 + +#include +#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); + } + +} diff --git a/ports/cortex-m/common/lm4f_con.c b/ports/cortex-m/common/lm4f_con.c new file mode 100644 index 0000000..23a7407 --- /dev/null +++ b/ports/cortex-m/common/lm4f_con.c @@ -0,0 +1,93 @@ +/* + * 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 + +#include +#include + +/** + * _read and _write for LM4F + * + * _read and _write are used by newlib's I/O routines (think printf, etc.) + * If you want to use this code in your binary, you will have to initialise + * the UART in your board's setup code and define STD_CON to your UART's + * name in your board's Makefile + */ +int _read(int fd, void *buf, size_t count) +{ + int rcvd; + char *ptr; + + if(fd <= 2){ + ptr = (char *) buf; + rcvd = 0; + + while(count > 0){ + *ptr = uart_recv_blocking(STD_CON); + if(*ptr == '\r'){ + *ptr = '\n'; + } + + ++rcvd; + --count; + } + }else{ + rcvd = -1; + errno = EIO; + } + + return rcvd; +} + +int _write(int fd, const void *buf, size_t count) +{ + int sent; + char *ptr; + + if(fd <= 2){ + sent = count; + ptr = (char *) buf; + + while(count > 0){ + if(*ptr == '\n'){ + uart_send_blocking(STD_CON, '\r'); + } + uart_send_blocking(STD_CON, *ptr++); + + ++sent; + --count; + } + }else{ + errno = EIO; + sent = -1; + } + + return sent; +} diff --git a/ports/cortex-m/common/stm32_con.c b/ports/cortex-m/common/stm32_con.c new file mode 100644 index 0000000..ffc5f27 --- /dev/null +++ b/ports/cortex-m/common/stm32_con.c @@ -0,0 +1,92 @@ +/* + * 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 + +#include +#include + +/** + * _read and _write for STM32 + * + * _read and _write are used by newlib's I/O routines (think printf, etc.) + * If you want to use this code in your binary, you will have to initialise + * the UART in your board's setup code and define STD_CON to your UART's + * name in your board's Makefile + */ +int _read(int fd, void *buf, size_t count) +{ + int rcvd; + char *ptr; + + if(fd <= 2){ + ptr = (char *) buf; + rcvd = 0; + while(count > 0){ + *ptr = usart_recv_blocking(STD_CON); + if(*ptr == '\r'){ + *ptr = '\n'; + } + ++rcvd; + --count; + } + }else{ + rcvd = -1; + errno = EIO; + } + + return rcvd; +} + +int _write(int fd, const void *buf, size_t count) +{ + int sent; + char *ptr; + + if(fd <= 2){ + sent = count; + ptr = (char *) buf; + + while(count > 0){ + if(*ptr == '\n'){ + usart_send_blocking(STD_CON, '\r'); + } + usart_send_blocking(STD_CON, *ptr++); + ++sent; + --count; + } + + }else{ + errno = EIO; + sent = -1; + } + + return sent; +} + diff --git a/ports/cortex-m/common/stubs.c b/ports/cortex-m/common/stubs.c new file mode 100644 index 0000000..4bcf302 --- /dev/null +++ b/ports/cortex-m/common/stubs.c @@ -0,0 +1,116 @@ +/* + * 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 + +#include + +#include "atomport.h" + +/** + * _sbrk is needed by newlib for heap management. + * The main stack usually starts at the top of RAM and grows downwards, + * while the heap starts at the bottom and grows upwards. To prevent + * the heap from colliding with the main stack, we reserve a certain amount + * of memory and check that growing the heap won't touch that region. + * The size of the protected area is set via the define MST_SIZE in the + * board Makefile + */ + +#ifndef MST_SIZE +#error Main stack size not defined. Please define MST_SIZE in board Makefile +#endif + +/** + * The vector table is provided by libopencm3/cm3/vector.h and contains + * the initial main stack pointer value + */ +extern vector_table_t vector_table; + +/** + * The linker provides the start address of the unused system memory. + * It is exported via the symbol 'end' and will be used as the bottom + * of the heap + */ +extern char end; + +static char *heap_end = 0; +caddr_t _sbrk(int incr) +{ + char *prev_end; + + if(heap_end == 0){ + heap_end = &end; + } + + prev_end = heap_end; + + if(heap_end + incr + MST_SIZE > (char *) vector_table.initial_sp_value){ + /* new heap size would collide with main stack area*/ + return (caddr_t) 0; + } + + heap_end += incr; + + return (caddr_t) prev_end; +} + +/** + * dummy stubs needed by newlib when not linked with libnosys + */ +int _close(int file __maybe_unused) +{ + return -1; +} + +int _fstat(int file __maybe_unused, struct stat *st) +{ + st->st_mode = S_IFCHR; + + return 0; +} + +int _isatty(int file __maybe_unused) +{ + return 1; +} + +int _lseek(int file __maybe_unused, + int ptr __maybe_unused, + int dir __maybe_unused) +{ + return 0; +} + +int _open(const char *name __maybe_unused, + int flags __maybe_unused, + int mode __maybe_unused) +{ + return -1; +} diff --git a/ports/cortex-m/linker/lm4f120xl.ld b/ports/cortex-m/linker/lm4f120xl.ld new file mode 100644 index 0000000..2675012 --- /dev/null +++ b/ports/cortex-m/linker/lm4f120xl.ld @@ -0,0 +1,10 @@ +/* Memory regions for TI Stellaris EX-LM4F120XL evaluation board. */ + +MEMORY +{ + rom (rx) : ORIGIN = 0x00000000, LENGTH = 256K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 32K +} + +/* Include main opencm3 linker script */ +INCLUDE libopencm3_lm4f.ld diff --git a/ports/cortex-m/linker/stm32f072rb.ld b/ports/cortex-m/linker/stm32f072rb.ld new file mode 100644 index 0000000..a70c131 --- /dev/null +++ b/ports/cortex-m/linker/stm32f072rb.ld @@ -0,0 +1,10 @@ +/* Memory regions for STM32F072RB, 128K flash, 16K RAM. */ + +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 16K +} + +/* Include main opencm3 linker script */ +INCLUDE libopencm3_stm32f0.ld diff --git a/ports/cortex-m/linker/stm32f103rb.ld b/ports/cortex-m/linker/stm32f103rb.ld new file mode 100644 index 0000000..86078f9 --- /dev/null +++ b/ports/cortex-m/linker/stm32f103rb.ld @@ -0,0 +1,10 @@ +/* Memory regions for STM32F103RB, 128K flash, 20K RAM. */ + +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} + +/* Include main opencm3 linker script */ +INCLUDE libopencm3_stm32f1.ld diff --git a/ports/cortex-m/linker/stm32f411re.ld b/ports/cortex-m/linker/stm32f411re.ld new file mode 100644 index 0000000..3c61d05 --- /dev/null +++ b/ports/cortex-m/linker/stm32f411re.ld @@ -0,0 +1,10 @@ +/* Memory regions for STM32F411RE, 512K flash, 128K RAM. */ + +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 512K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K +} + +/* Include main opencm3 linker script */ +INCLUDE libopencm3_stm32f4.ld diff --git a/ports/cortex-m/tests-main.c b/ports/cortex-m/tests-main.c new file mode 100644 index 0000000..2524ee8 --- /dev/null +++ b/ports/cortex-m/tests-main.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2010, Kelvin Lawson. 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 + +#include +#include "atom.h" +#include "atomport-private.h" +#include "atomtests.h" +#include "atomtimer.h" + + +/* Constants */ + +/* + * Idle thread stack size + * + * This needs to be large enough to handle any interrupt handlers + * and callbacks called by interrupt handlers (e.g. user-created + * timer callbacks) as well as the saving of all context when + * switching away from this thread. + * + * In this case, the idle stack is allocated on the BSS via the + * idle_thread_stack[] byte array. + */ +#define IDLE_STACK_SIZE_BYTES 512 + + +/* + * Main thread stack size + * + * Note that this is not a required OS kernel thread - you will replace + * this with your own application thread. + * + * In this case the Main thread is responsible for calling out to the + * test routines. Once a test routine has finished, the test status is + * printed out on the UART and the thread remains running in a loop + * flashing a LED. + * + * The Main thread stack generally needs to be larger than the idle + * thread stack, as not only does it need to store interrupt handler + * stack saves and context switch saves, but the application main thread + * will generally be carrying out more nested function calls and require + * stack for application code local variables etc. + * + * With all OS tests implemented to date on the AVR, the Main thread + * stack has not exceeded 201 bytes. To allow all tests to run we set + * a minimum main thread stack size of 204 bytes. This may increase in + * future as the codebase changes but for the time being is enough to + * cope with all of the automated tests. + */ +#define MAIN_STACK_SIZE_BYTES 1024 + + +/* + * Startup code stack + * + * Some stack space is required at initial startup for running the main() + * routine. This stack space is only temporarily required at first bootup + * and is no longer required as soon as the OS is started. By default + * GCC sets this to the top of RAM (RAMEND) and it grows down from there. + * Because we only need this temporarily, though, it would be wasteful to + * set aside a region at the top of RAM which is not used during runtime. + * + * What we do here is to reuse part of the idle thread's stack during + * initial startup. As soon as we enter the main() routine we move the + * stack pointer to half-way down the idle thread's stack. This is used + * temporarily while calls are made to atomOSInit(), atomThreadCreate() + * and atomOSStart(). Once the OS is started this stack area is no + * longer required, and can be used for its original purpose (for the + * idle thread's stack). + * + * This does mean, however, that we cannot monitor the stack usage of the + * idle thread. Stack usage is monitored by prefilling the stack with a + * known value, and we are obliterating some of that prefilled area by + * using it as our startup stack, so we cannot use the stack-checking API + * to get a true picture of idle thread stack usage. If you wish to + * monitor idle thread stack usage for your applications then you are + * free to use a different region for the startup stack (e.g. set aside + * an area permanently, or place it somewhere you know you can reuse + * later in the application). For the time being, this method gives us a + * simple way of reducing the memory consumption without having to add + * any special AVR-specific considerations to the automated test + * applications. + * + * This optimisation was required to allow some of the larger automated + * test modules to run on devices with 1KB of RAM. You should avoid doing + * this if you can afford to set aside 64 bytes or so, or if you are + * writing your own applications in which you have further control over + * where data is located. + */ + + +/* Local data */ + +/* Application threads' TCBs */ +static ATOM_TCB main_tcb; + +/* Main thread's stack area */ +static uint8_t main_thread_stack[MAIN_STACK_SIZE_BYTES]; + +/* Idle thread's stack area */ +static uint8_t idle_thread_stack[IDLE_STACK_SIZE_BYTES]; + +/* Forward declarations */ +static void main_thread_func (uint32_t data); + + +/** + * \b main + * + * Program entry point. + * + * Sets up the AVR hardware resources (system tick timer interrupt) necessary + * for the OS to be started. Creates an application thread and starts the OS. + */ +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"); + } + + /** + * Note: to protect OS structures and data during initialisation, + * interrupts must remain disabled until the first thread + * has been restored. They are reenabled at the very end of + * the first thread restore, at which point it is safe for a + * reschedule to take place. + */ + board_setup(); + + /** + * Initialise the OS before creating our threads. + * + * Note that we cannot enable stack-checking on the idle thread on + * this platform because we are already using part of the idle + * thread's stack now as our startup stack. Prefilling for stack + * checking would overwrite our current stack. + * + * If you are not reusing the idle thread's stack during startup then + * you are free to enable stack-checking here. + */ + status = atomOSInit(&idle_thread_stack[0], IDLE_STACK_SIZE_BYTES, FALSE); + if (status == ATOM_OK) + { + + /* Create an application thread */ + status = atomThreadCreate(&main_tcb, + TEST_THREAD_PRIO, main_thread_func, 0, + &main_thread_stack[0], + MAIN_STACK_SIZE_BYTES, + TRUE); + if (status == ATOM_OK) + { + /** + * First application thread successfully created. It is + * now possible to start the OS. Execution will not return + * from atomOSStart(), which will restore the context of + * our application thread and start executing it. + * + * Note that interrupts are still disabled at this point. + * They will be enabled as we restore and execute our first + * thread in archFirstThreadRestore(). + */ + atomOSStart(); + } + } + + while (1) + ; + + /* There was an error starting the OS if we reach here */ + return (0); +} + + +/** + * \b main_thread_func + * + * Entry point for main application thread. + * + * This is the first thread that will be executed when the OS is started. + * + * @param[in] data Unused (optional thread entry parameter) + * + * @return None + */ +extern void test_led_toggle(void); +static void main_thread_func (uint32_t data __maybe_unused) +{ + uint32_t test_status; + int sleep_ticks; + + + /* Put a message out on the UART */ + printf("Go\n"); + + /* Start test. All tests use the same start API. */ + test_status = test_start(); + + /* Check main thread stack usage (if enabled) */ +#ifdef ATOM_STACK_CHECKING + if (test_status == 0) + { + uint32_t used_bytes, free_bytes; + + /* Check idle thread stack usage */ + if (atomThreadStackCheck (&main_tcb, &used_bytes, &free_bytes) == ATOM_OK) + { + /* Check the thread did not use up to the end of stack */ + if (free_bytes == 0) + { + printf("Main stack overflow\n"); + test_status++; + } + + /* Log the stack usage */ +#ifdef TESTS_LOG_STACK_USAGE + printf("MainUse: %lu\n", used_bytes); +#endif + } + + } +#endif + + /* Log final status */ + if (test_status == 0) + { + printf("Pass\n"); + } + else + { + printf("Fail(%lu)\n", test_status); + } + + /* Flash LED once per second if passed, very quickly if failed */ + sleep_ticks = (test_status == 0) ? SYSTEM_TICKS_PER_SEC : (SYSTEM_TICKS_PER_SEC/8); + + /* Test finished, flash slowly for pass, fast for fail */ + while (1) + { + /* Toggle a LED (STK500-specific) */ + test_led_toggle(); + + /* Sleep then toggle LED again */ + atomTimerDelay (sleep_ticks); + } + +}