Merge branch 'tidklaas-master'

This commit is contained in:
Kelvin Lawson
2015-08-08 01:37:40 +01:00
29 changed files with 2961 additions and 8 deletions

View File

@@ -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;
}

366
ports/cortex-m/Makefile Normal file
View File

@@ -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)

233
ports/cortex-m/README.md Normal file
View File

@@ -0,0 +1,233 @@
# ARM Cortex-M Port
Author: Tido Klaassen <tido@4gh.eu>
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.

View File

@@ -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

View File

@@ -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_ */

View File

@@ -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 <stdio.h>
#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 */

205
ports/cortex-m/atomport.c Normal file
View File

@@ -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 <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>
#include <libopencm3/cm3/cortex.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/cm3/sync.h>
#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;
}

67
ports/cortex-m/atomport.h Normal file
View File

@@ -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 <stdint.h>
#include <stddef.h>
#include <libopencm3/cm3/cortex.h>
/* 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 */

View File

@@ -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

View File

@@ -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 <stdbool.h>
#include <libopencm3/lm4f/rcc.h>
#include <libopencm3/lm4f/gpio.h>
#include <libopencm3/lm4f/uart.h>
#include <libopencm3/cm3/systick.h>
#include <libopencm3/cm3/cortex.h>
#include <libopencm3/cm3/nvic.h>
#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;
}

View File

@@ -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

View File

@@ -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 <stdbool.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/systick.h>
#include <libopencm3/cm3/cortex.h>
#include <libopencm3/cm3/nvic.h>
#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;
}

View File

@@ -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

View File

@@ -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 <stdbool.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/systick.h>
#include <libopencm3/cm3/cortex.h>
#include <libopencm3/cm3/nvic.h>
#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;
}

View File

@@ -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

View File

@@ -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 <stdbool.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/systick.h>
#include <libopencm3/cm3/cortex.h>
#include <libopencm3/cm3/nvic.h>
#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;
}

View File

@@ -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) $(*)

View File

@@ -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.

View File

@@ -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 <stdbool.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/systick.h>
#include <libopencm3/cm3/cortex.h>
#include <libopencm3/cm3/nvic.h>
#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;
}

View File

@@ -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: <path_to_qemu> <test_elf_file>
#
# 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
}
}

View File

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

View File

@@ -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 <errno.h>
#include <libopencm3/cm3/cortex.h>
#include <libopencm3/lm4f/uart.h>
/**
* _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;
}

View File

@@ -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 <errno.h>
#include <libopencm3/cm3/cortex.h>
#include <libopencm3/stm32/usart.h>
/**
* _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;
}

View File

@@ -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 <sys/stat.h>
#include <libopencm3/cm3/vector.h>
#include "atomport.h"
/**
* _sbrk is needed by newlib for heap management.
* The main stack usually starts at the top of RAM and grows downwards,
* 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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

283
ports/cortex-m/tests-main.c Normal file
View File

@@ -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 <stdio.h>
#include <libopencm3/cm3/nvic.h>
#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);
}
}