Skip to content

Instantly share code, notes, and snippets.

@RickKimball
Last active March 22, 2016 20:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RickKimball/3173f5ca73bc6dcdb7bc to your computer and use it in GitHub Desktop.
Save RickKimball/3173f5ca73bc6dcdb7bc to your computer and use it in GitHub Desktop.
tiny preemptive msp430 tasker from Tony Philipsson ported to msp430-gcc

tonyp12 ( Tony Philipsson ) posted code for a small preemptive multi tasking kernel on 43oh.com

http://forum.43oh.com/topic/9450-tiny-msp430-preemptive-multitasking-system/?p=71458

That code was written for IAR. As I'm not in the deep pockets IAR camp, I ported it to msp430-gcc and msp430-elf-gcc.

License for tony's code:

"Code is free for non-commercial use, for commerical use a negotiated donation amount. Though commercial use will be after I added more features."

-rick

/*
* common.h
*/
#ifndef COMMON_H_
#define COMMON_H_
#include <msp430.h>
#include "sys.h"
#ifndef FIXED_REG
#if __GNUC__ == 4 && __GNUC_MINOR__ == 6
#define FIXED_REG "r4"
#else
#define FIXED_REG "R4"
#endif
#endif
#define __task __attribute__((naked, used, noreturn))
__task extern void task1(void);
__task extern void task2(void);
__task extern void task3(void);
__task extern void task4(void);
typedef void (*taskptr)(void);
#endif
-- license.txt --
This file really needs to be fleshed out. When I asked what the license for the code
was I got this answer from Tony:
"Code is free for non-commercial use, for commerical use a negotiated donation amount.
Though commercial use will be after I added more features."
//=========================(C) Tony Philipsson 2016 =======================
//
// tiny msp430 preemptive multitasking system
//
// http://forum.43oh.com/topic/9450-tiny-msp430-preemptive-multitasking-system/?p=71458
//
// Changes:
// 2016/03/18 Rick Kimball port to msp430-gcc MSP430G2553 and MSP430FR5969
//
#include "common.h"
#define SAVED_STACK_WORDS 11
#define MIN_STACK_WORDS (SAVED_STACK_WORDS+1+1)
#define tasks (sizeof(taskpnt)/sizeof(taskpnt[0]))
static taskptr const taskpnt[] = {
task1, task2, task3
};
static const unsigned stacksize[tasks] = {
MIN_STACK_WORDS+4+4+2+10, MIN_STACK_WORDS+4+4+2+10, MIN_STACK_WORDS+4+4+2+10
};
//=========================================================================
static unsigned taskstackpnt[tasks];
volatile systick_t systick;
register unsigned *taskrun asm(FIXED_REG);
int main( void )
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
BCSCTL1 = CALBC1_8MHZ; // Set DCO to factory calibrated 8MHz
DCOCTL = CALDCO_8MHZ;
DCOCTL += 13; // overclock 103% or 8.22MHz
#define MEASURE_ISR_OVHEAD 0
#if MEASURE_ISR_OVHEAD
P1DIR |= BIT4; P1SEL |= BIT4; // measure DCO
P2DIR |= BIT0; P2OUT &= ~BIT0; // toggles pin during wdt isr
#endif
taskrun = taskstackpnt;
register unsigned * multistack;
register unsigned i;
multistack = (unsigned *)__get_SP_register();
i=0;
while( i < tasks-1) {
multistack -= stacksize[i]; // adjust stack to accomodate n stack words
*(multistack) = (unsigned)taskpnt[++i]; // prefill in PC
*(multistack-1) = GIE; // prefill in SR
taskstackpnt[i] = (unsigned)
(multistack-((MIN_STACK_WORDS)-1)); // point at dummy stack words
}
WDTCTL = WDT_MDLY_8; // 1ms tick using MCLK
IE1 |= WDTIE;
__enable_interrupt();
asm volatile (" br 0(%0)\n":: "r" (taskpnt)); // indirect jmp to first task
}
//============= TASK SWITCHER ISR =============
__attribute__((interrupt(WDT_VECTOR), naked, used))
void taskswitcher(void);
void taskswitcher(void)
{
#if MEASURE_ISR_OVHEAD
asm("xor.b #1, %0" :: "m" (P2OUT)); // toggle pin to measure overhead
#endif
__asm__ volatile (
"push R15\n push R14\n push R13\n push R12\n"
"push R11\n push R10\n push R9\n push R8\n"
"push R7\n push R6\n"
"push R5\n"
);
#if 1
__asm__ volatile (" inc %A0\n" :: "m" (systick));
__asm__ volatile (" adc %B0\n" :: "m" (systick));
#else
++systick;
#endif
__asm__ volatile (
" mov r1, 0(%2)\n"
" incd %2\n"
" cmp %0,%2\n"
" jnz 1f\n"
" mov %1, %2\n"
"1:\n"
" mov 0(%2), r1\n"
::"i" (taskstackpnt+tasks)
,"i" (taskstackpnt)
,"r" (taskrun)
);
__asm__ volatile (
"pop R5\n"
"pop R6\n pop R7\n"
"pop R8\n pop R9\n pop R10\n pop R11\n"
"pop R12\n pop R13\n pop R14\n pop R15"
);
#if MEASURE_ISR_OVHEAD
asm("xor.b #1, %0" :: "m" (P2OUT)); // toggle pin to measure overhead
#endif
asm("reti"); // we need to add because of naked attribute
}
//========================= Copyright (c) 2016 Tony Philipsson =======================
// main.c - tiny msp430 preemptive multitasking system
//
// See:
// http://forum.43oh.com/topic/9450-tiny-msp430-preemptive-multitasking-system/?p=71458
//
// Changes:
// 2016/03/18 Rick Kimball port to msp430-gcc MSP430G2553 and MSP430FR5969
// 2016/03/20 Rick Kimball version specific for FR5969 using FRAM as stack
//
#include <stdint.h>
#include "common.h"
#define SAVED_STACK_WORDS 11
#define MIN_STACK_WORDS (SAVED_STACK_WORDS+1+1)
#define tasks (sizeof(taskpnt)/sizeof(taskpnt[0]))
static taskptr const taskpnt[] = {
task1, task2, task3, task4
};
#define STACK_WORD_SIZE (1024+128+256+64)
static const unsigned stacksize[tasks] = {
1024, 128, 256, 64
};
static unsigned taskstackpnt[tasks];
unsigned *alt_stack_space;
volatile systick_t systick;
register unsigned *taskrun asm(FIXED_REG);
//=========================================================================
void main( void )
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
PM5CTL0 &= ~LOCKLPM5; // Unlock Pin Registers
CSCTL0_H = CSKEY >> 8; // Unlock CS registers
CSCTL1 = DCOFSEL_6; // Set DCO to 8MHz
CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK; // SMCLK/MCLK DCO and VLO
CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1; // Set all dividers to 1
CSCTL0_H = 0; // Lock CS registers
P3DIR |= BIT0; P3OUT &= ~BIT0;
taskrun = taskstackpnt;
alt_stack_space = (unsigned *) 0xff00-(STACK_WORD_SIZE);
register unsigned *multistack = &alt_stack_space[STACK_WORD_SIZE];
register unsigned *p = multistack;
register unsigned i=0;
while( i < tasks-1 ) {
multistack -= stacksize[i]; // adjust stack to accomodate n stack words
*(multistack) = (unsigned)taskpnt[++i]; // prefill in PC
*(multistack-1) = GIE; // prefill in SR
taskstackpnt[i] = (unsigned)
(multistack-((MIN_STACK_WORDS)-1)); // point at dummy stack words
}
//WDTCTL = WDT_MDLY_32; // 4ms tick using MCLK
//WDTCTL = WDT_MDLY_8;; // 1ms tick using MCLK
WDTCTL = WDT_MDLY_0_5; // 1/16ms tick using MCLK
SFRIE1 |= WDTIE;
#if 1
__enable_interrupt();
#else
__asm__ volatile (" eint\n nop\n");
#endif
__asm__ volatile (
" mov %1, r1\n" // set stack pointer to fram
" br 0(%0)\n" // indirect jmp to first task
::
"r" (taskpnt)
,"r" (p)
);
}
//============= TASK SWITCHER ISR =============
__attribute__((interrupt(WDT_VECTOR), naked, used))
void taskswitcher(void);
void taskswitcher(void)
{
__asm__ volatile ("xor.b #1,%0" :: "m" (P3OUT) ); // measure overhead
// push r15-r5
__asm__ volatile ("pushm %0, r15" :: "i" (SAVED_STACK_WORDS));
#if 0
++systick;
#else
__asm__ volatile (" inc %A0\n" :: "m" (systick));
__asm__ volatile (" adc %B0\n" :: "m" (systick));
#endif
__asm__ volatile (
" mov r1, 0(%2)\n"
" incd %2\n"
" cmp %0, %2\n"
" jnz 1f\n"
" mov %1, %2\n"
"1:\n"
" mov 0(%2), r1\n"
::"i" (taskstackpnt+tasks)
,"i" (taskstackpnt)
,"r" (taskrun)
);
// pop r5-r15
__asm__ volatile ("popm %0, r15" :: "i" (SAVED_STACK_WORDS));
__asm__ volatile ("xor.b #1,%0" :: "m" (P3OUT) );
__asm__ volatile ("reti\n");
}
#--------------------------------------------------------------------
# Makefile - for tiny msp430 preemptive kernel
TARGET = tinyos.elf
# point this to where your CCS is installed
CCS_PATH =? $(HOME)/ti/ccsv6
#ARCH ?= msp430-elf-
ARCH ?= msp430-
CPU ?= msp430g2553
OPTIMIZE ?= s
VERBOSE ?=
COMM_OBJS = task1.o task2.o task3.o sys.o
ALL_OBJS = $(COMM_OBJS) task4.o main.o main_fr5969.o
ifeq ($(CPU),msp430g2553)
DEBUG_TARGET ?= rf2500
HWMULT = none
OBJS = main.o
else
DEBUG_TARGET ?= tilib
HWMULT ?= f5series
OBJS = task4.o main_fr5969.o
endif
CC = $(ARCH)gcc
LD = $(ARCH)gcc
SIZE = $(ARCH)size
OBJDUMP = $(ARCH)objdump
READELF = $(ARCH)readelf
TARGETBASE = $(patsubst %.elf,%,$(TARGET))
LSSFILE = $(TARGETBASE).lss
CFLAGS = $(VERBOSE) -g -O$(OPTIMIZE) -mmcu=$(CPU)
CFLAGS += -fdata-sections -ffunction-sections
CFLAGS += -Wall -Wno-main
ifeq ($(ARCH),msp430-elf-)
CFLAGS += -gdwarf-3 -gstrict-dwarf
CFLAGS += -I $(CCS_PATH)/ccs_base/msp430/include_gcc
CFLAGS += -I $(CCS_PATH)/tools/compiler/gcc_msp430_4.9.14r1_467/msp430-elf/include
CFLAGS += -mhwmult=$(HWMULT)
CFLAGS += -fomit-frame-pointer
CFLAGS += -ffixed-R4
CFLAGS += -minrt
else
CFLAGS += -D__get_SP_register=__read_stack_pointer
CFLAGS += -D__set_SP_register=__write_stack_pointer
CFLAGS += -fomit-frame-pointer
CFLAGS += -ffixed-r4
endif
LDFLAGS = -mmcu=$(CPU)
ifeq ($(ARCH),msp430-elf-)
LDFLAGS += -Wl,-Map,"$(TARGETBASE).map"
LDFLAGS += -L /mnt/vbox/shared/ti_62/ccsv6/ccs_base/msp430/include_gcc
LDFLAGS += -T $(CPU).ld
LDFLAGS += -gdwarf-3 -gstrict-dwarf
LDFLAGS += $(VERBOSE)
else
LDFLAGS += -Wl,-gc-section,-Map,"$(TARGETBASE).map"
LDFLAGS += -mdisable-watchdog
endif
.phony: all clean install debug showmacros updatefw
all: $(TARGET)
$(SIZE) $^
$(TARGET): $(COMM_OBJS) $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
$(OBJDUMP) -CS $@ > $(LSSFILE)
$(READELF) -x .rodata $@ >> $(LSSFILE)
clean:
rm -f $(TARGET) $(ALL_OBJS) $(LSSFILE)
rm -f $(TARGETBASE).hex $(TARGETBASE).cycles $(TARGETBASE).map
install: tinyos.elf
mspdebug $(DEBUG_TARGET) "prog $?"
debug:
xterm -e "$(ARCH)gdb -ex 'target remote :2000' $(TARGET)" &
xterm -e "mspdebug $(DEBUG_TARGET) gdb" &
updatefw:
mspdebug --allow-fw-update tilib
showmacros:
$(CC) $(CFLAGS) -include "msp430.h" -v -dM -E - </dev/null | sort | less
/*
* sys_xxx - helper routines
*
* Rick Kimball (C) 2016
*/
#include "common.h"
extern volatile systick_t systick;
inline systick_t sys_tick_count(void) { return systick; }
void sys_delay(systick_t msecs)
{
systick_t start = sys_tick_count();
//msecs <<= 4;
while ( (sys_tick_count() - start) < msecs) {
;
}
}
/*
* sys.h
*/
#ifndef SYS_H_
#define SYS_H_
typedef unsigned long systick_t;
void sys_delay(systick_t msecs);
#endif
#include "common.h"
void task1(void) {
volatile unsigned count=0; // 4 bytes on stack
P1DIR |= BIT0;
P1OUT &= ~BIT0;
while(1){
P1OUT ^= BIT0;
#if 1
__delay_cycles(50*(4000000/1000));
#else
sys_delay(50);
#endif
P1OUT ^= BIT0;
#if 1
__delay_cycles(450*(4000000/1000));
#else
sys_delay(450); // 4+2 bytes of stack used
#endif
++count;
}
}
#include "common.h"
#ifdef __MSP430FR5969__
#define PORTDIR P4DIR
#define PORTOUT P4OUT
#else
#define PORTDIR P1DIR
#define PORTOUT P1OUT
#endif
void task2(void){
volatile unsigned long count=0; // 4 bytes of stack
PORTDIR |= BIT6;
while(1){
PORTOUT |= BIT6;
sys_delay(25); // 4+2 bytes of stack
PORTOUT &=~BIT6;
sys_delay(100); // 4+2 bytes of stack
++count;
}
}
#include "common.h"
unsigned int fibo(int);
void task3(void){
int temp = 0;
while(1){
fibo(++temp);
}
}
unsigned int fibo(int n){
if (n < 2)
return n;
else
return (fibo(n-1) + fibo(n-2));
}
#include "common.h"
void task4(void) {
volatile unsigned count=0;
P2DIR |= BIT4;
P2OUT &= ~BIT4;
while(1){
P2OUT ^= BIT4;
sys_delay(10);
P2OUT ^= BIT4;
sys_delay(10);
++count;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment