Added support for Cortex-M0

Added board nucleo-f072rb
This commit is contained in:
Tido Klaassen
2015-07-11 20:08:50 +02:00
parent 68e3ed69d6
commit ca81d186a7
9 changed files with 401 additions and 69 deletions

View File

@@ -197,32 +197,32 @@ $(build_dir)/%.elf $(build_dir)/%.map: $(build_dir)/%.o $(build_objs) $(LDSCRIPT
$(if $(Q), @echo " (ELF) $(subst $(build_dir)/,,$@)")
$(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(build_objs) $(build_dir)/$(*).o $(LDLIBS) -o $@
$(build_dir)/%.o: $(src_dir)/%.S
$(build_dir)/%.o: $(src_dir)/%.S Makefile
$(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
$(build_dir)/%.o: $(src_dir)/%.c Makefile
$(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)/%.c
$(build_dir)/%.o: $(board_dir)/%.c Makefile
$(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)/%.c
$(build_dir)/%.o: $(common_dir)/%.c Makefile
$(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
$(build_dir)/%.o: $(kernel_dir)/%.c Makefile
$(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
$(build_dir)/%.o: $(tests_dir)/%.c Makefile
$(Q)mkdir -p `dirname $@`
$(if $(Q), @echo " (CC) $(subst $(build_dir)/,,$@)")
$(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -I`dirname $<` -c $< -o $@

View File

@@ -1,7 +1,7 @@
# ARM Cortex-M Port
## Summary and Prerequisites
This port should run on any Cortex-M3/4/4F (but not on M0, sorry). It uses
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)

View File

@@ -29,11 +29,24 @@
.syntax unified
#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 CTX_SW_NFO
.extern _stack
.extern vector_table
.equ FPU_USED, 0x00000010
.equ FPU_USED, 0x00000010
.equ SCB_ICSR, 0xE000ED04
.equ PENDSVCLR, 0x08000000
.text
@@ -46,7 +59,7 @@ archFirstThreadRestore:
* Disable interrupts. They should be disabled anyways, but just
* to make sure...
*/
mov r1, #1
movs r1, #1
msr PRIMASK, r1
/**
@@ -63,38 +76,101 @@ archFirstThreadRestore:
str r0, [r1, #4]
/* Get thread stack pointer from tcb. Conveniently the first element */
ldr r2, [r0, #0]
ldr r1, [r0, #0]
msr PSP, r1
/* Discard dummy thread stack frame */
add r2, r2, #36
/**
* New threads can't have used the FPU, so no need to check for
* FPU stack frame here
* 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
*/
/**
* Load exception stack frame to registers r3-r10. xPSR ends up in r10,
* pc in r9
*
* Move SP to position of r8 and restore high registers by loading
* them to r4-r7 before moving them to r8-r11
*/
ldmia r2!, {r3-r10}
add SP, #16
pop {r4-r7}
mov r8, r4
mov r9, r5
mov r10, r6
mov r11, r7
/* initialise xPSR and store store adjusted stack pointer in PSP */
msr APSR_nzcvq, r10
msr PSP, r2
/* move SP back to top of stack and load r4-r7 */
sub SP, #32
pop {r4-r7}
/* set bit #1 in CONTROL. Causes switch to PSP */
mrs r1, CONTROL
orr r1, r1, #2
msr CONTROL, r1
/*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
/* enable interrupts */
mov r1, #0
msr PRIMASK, r1
/* branch to thread entry point */
bx r9
/* Pop r2,r3,pc from stack, thereby jumping to thread entry point */
pop {r2,r3,pc}
nop
.size archFirstThreadRestore, . - archFirstThreadRestore
.endfunc
@@ -107,13 +183,47 @@ pend_sv_handler:
* Disable interrupts. No need to check if they were enabled because,
* well, we're an interrupt handler. Duh...
*/
mov r0, #1
movs r0, #1
msr PRIMASK, r0
/* Get running thread's stack pointer*/
/**
* 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(__VFP_FP__) && !defined(__SOFTFP__))
#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
@@ -124,31 +234,68 @@ pend_sv_handler:
* fault handler to store the FPU registers if another thread
* tries using it
*/
#endif
#endif // WITH_FPU
/* Push running thread's remaining registers on stack */
stmdb r3!, {r4-r11, lr}
/* Fetch running thread's TCB and store its current stack pointer */
ldr r2, = CTX_SW_NFO
ldr r1, [r2, #0]
#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]
/**
* Fetch pointer to next thread's tcb from ctx_switch_info.next_tcb and
* use it to update ctx_switch_info.running_tcb.
* ctx_switch_info.next_tcb is going to become ctx_switch_info.running_tcb,
* so we update the pointer.
*/
ldr r1, [r2, #4]
str r1, [r2, #0]
str r2, [r0, #0]
/**
* Fetch next thread's stack pointer from its TCB and restore
* Fetch next thread's stack pointer from its TCB's sp_save_ptr and restore
* the thread's register context.
*/
ldr r3, [r1, #0]
ldr r3, [r2, #0]
#if defined(THUMB_2)
/* Cortex-M[34], restore thread's task stack frame */
ldmia r3!, {r4-r11, lr}
#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
#if defined(WITH_FPU)
/**
* Check if FPU was used by new thread and restore registers if necessary.
*/
@@ -160,13 +307,43 @@ pend_sv_handler:
* TODO: only restore FPU registers if FPU was used by another thread
* between this thread being scheduled out and now.
*/
#endif
#endif // WITH_FPU
#else // !THUMB_2
/* Restore process stack pointer */
/**
* 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 */
mov r0, #0
movs r0, #0
msr PRIMASK, r0
/* Return to new thread */

View File

@@ -45,7 +45,7 @@ struct isr_stack {
uint32_t lr;
uint32_t pc;
uint32_t psr;
};
} __attribute__((packed));
struct isr_fpu_stack {
uint32_t s0;
@@ -65,7 +65,7 @@ struct isr_fpu_stack {
uint32_t s14;
uint32_t s15;
uint32_t fpscr;
};
} __attribute__((packed));
/**
* remaining context saved by task switch ISR
@@ -79,8 +79,8 @@ struct task_stack {
uint32_t r9;
uint32_t r10;
uint32_t r11;
uint32_t lr;
};
uint32_t exc_ret;
} __attribute__((packed));
struct task_fpu_stack {
uint32_t s16;
@@ -99,7 +99,7 @@ struct task_fpu_stack {
uint32_t s29;
uint32_t s30;
uint32_t s31;
};
} __attribute__((packed));
/**
* Info needed by pend_sv_handler used for delayed task switching.
@@ -112,6 +112,6 @@ struct task_fpu_stack {
struct task_switch_info {
volatile struct atom_tcb *running_tcb;
volatile struct atom_tcb *next_tcb;
};
} __attribute__((packed));
#endif /* __ATOMPORT_PRIVATE_H_ */

View File

@@ -165,28 +165,28 @@ void archThreadContextInit(ATOM_TCB *tcb_ptr, void *stack_top,
isr_ctx->pc = (uint32_t) thread_shell;
/* initialise unused registers to silly value */
isr_ctx->lr = 0xDEADBEEF;
isr_ctx->r12 = 0xDEADBEEF;
isr_ctx->r3 = 0xDEADBEEF;
isr_ctx->r2 = 0xDEADBEEF;
isr_ctx->r1 = 0xDEADBEEF;
isr_ctx->r0 = 0xDEADBEEF;
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->lr = 0xFFFFFFFD;
tsk_ctx->exc_ret = 0xFFFFFFFD;
/* initialise unused registers to silly value */
tsk_ctx->r11 = 0xDEADBEEF;
tsk_ctx->r10 = 0xDEADBEEF;
tsk_ctx->r9 = 0xDEADBEEF;
tsk_ctx->r8 = 0xDEADBEEF;
tsk_ctx->r7 = 0xDEADBEEF;
tsk_ctx->r6 = 0xDEADBEEF;
tsk_ctx->r5 = 0xDEADBEEF;
tsk_ctx->r4 = 0xDEADBEEF;
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

View File

@@ -0,0 +1,15 @@
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

View File

@@ -0,0 +1,127 @@
/*
* 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 "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 72MHz, generated from external 8MHz crystal */
rcc_clock_setup_in_hsi_out_48mhz();
}
/**
* Set up user LED and provide function for toggling it. This is for
* use by the test suit 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();
return 0;
}

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

@@ -29,6 +29,7 @@
#include <stdio.h>
#include <libopencm3/cm3/nvic.h>
#include "atom.h"
#include "atomport-private.h"
#include "atomtests.h"
@@ -144,6 +145,8 @@ int main ( void )
{
int8_t status;
nvic_set_priority(NVIC_PENDSV_IRQ, 0xFF);
nvic_set_priority(NVIC_SYSTICK_IRQ, 0xFE);
/**
* Note: to protect OS structures and data during initialisation,