commit 410ff88e267f788ae6e1b0086cd86e4b1d3d093b Author: Oleg Borodin Date: Tue Aug 30 11:02:51 2022 +0200 initial import of sources diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..053411b --- /dev/null +++ b/Makefile @@ -0,0 +1,81 @@ +# +# Copyright: Oleg Borodin 2018 +# + +.SECONDARY: + +CFLAGS+= -I. -Os -DSTM32F4 -std=c99 +#CFLAGS+= -mthumb +#CFLAGS+= -march=armv7e-m +CFLAGS+= -mfloat-abi=hard +#CFLAGS+= -mfpu=fpv4-sp-d16 +CFLAGS+= -mcpu=cortex-m4 +CFLAGS+= -fno-common -ffunction-sections -fdata-sections +CFLAGS+= -g -gdwarf-2 +CFLAGS+= -Wall + +LDFLAGS+= ${CFLAGS} +LDFLAGS+= --static +#LDFLAGS+= -nostartfiles +LDFLAGS+= -T main.ld + +LDFLAGS+= -Wl,-Map=main.map +LDFLAGS+= -Wl,--cref -Wl,--gc-sections +LDFLAGS+= -lopencm3_stm32f4 +LDFLAGS+= -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group + +TARGET= arm-eabi + +all: main.bin + +OBJS+= main.o +OBJS+= syscall.o +OBJS+= usartu.o + +main.elf: $(OBJS) + $(TARGET)-gcc $(^F) $(LDFLAGS) -o $@ + $(TARGET)-size --format=berkeley $@ + +%.o: %.c + $(TARGET)-gcc $(CFLAGS) -c -o $@ $< + +%.o: %.S + $(TARGET)-as $(ASFLAGS) -o $@ $< + +%.bin: %.elf + $(TARGET)-objcopy -O binary $< $@ + +%.elf: %.o + $(TARGET)-gcc $(^F) $(LDFLAGS) -o $@ + $(TARGET)-size --format=berkeley $@ + +clean: + rm -f *.i *.o *.elf *.bin *.map *~ *.hex *.d *.s + +flash: main.bin + @openocd \ + -c 'puts "--- START --------------------"' \ + -f 'interface/stlink.cfg' \ + -f 'target/stm32f4x.cfg' \ + -c 'puts "--- INIT --------------------"' \ + -c "init" \ + -c "reset halt" \ + -c 'puts "--- WRITE --------------------"' \ + -c "flash write_image erase $< 0x08000000"\ + -c 'puts "--- VERIFY --------------------"' \ + -c "verify_image $<" \ + -c 'puts "--- RESET --------------------"' \ + -c "reset" \ + -c 'puts "--- DONE --------------------"' \ + -c "shutdown" + +debug: main.bin + @openocd \ + -c 'puts "--- START --------------------"' \ + -f 'interface/stlink.cfg' \ + -f 'target/stm32f4x.cfg' \ + -c 'puts "--- INIT --------------------"' \ + -c "init" \ + -c "halt" \ + -c "poll" +#EOF diff --git a/README.md b/README.md new file mode 100644 index 0000000..2daf97f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ + +# Minimal STM32 F4 task sheduler + diff --git a/main.c b/main.c new file mode 100644 index 0000000..9bfb803 --- /dev/null +++ b/main.c @@ -0,0 +1,239 @@ +/* + * Copyright 2022 Oleg Borodin + */ + +#include +#include +#include + +#include +#include +#include + + +#include +#include +#include +#include + +#include "usartu.h" + +void delay(uint32_t n) { + for (volatile int i = 0; i < n * 800; i++) + __asm__("nop"); +} + +static void clock_setup(void) { + rcc_clock_setup_pll(&rcc_hse_8mhz_3v3[RCC_CLOCK_3V3_168MHZ]); + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_USART1); +} + +static void usart_setup(void) { + + usart_disable(USART1); + nvic_disable_irq(NVIC_USART1_IRQ); + + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9 | GPIO10); + gpio_set_af(GPIOA, GPIO_AF7, GPIO9 | GPIO10); + + usart_set_baudrate(USART1, 115200); + usart_set_databits(USART1, 8); + usart_set_stopbits(USART1, USART_STOPBITS_1); + usart_set_parity(USART1, USART_PARITY_NONE); + usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); + usart_set_mode(USART1, USART_MODE_TX_RX); + + usart_disable_rx_interrupt(USART1); + + usart_enable(USART1); +} + +static void systick_setup(void) { + systick_set_frequency(10000, rcc_ahb_frequency); + systick_interrupt_enable(); + systick_counter_enable(); +} + + +void scheduler_yield(void); +int g_uptime; + +void sys_tick_handler(void) { + g_uptime++; + scheduler_yield(); +} + +#define SCHEDULER_NUM_TASKS 4 +#define TASK_STACK_SIZE 2048 + +typedef struct { + void (*entry)(void); + void *stack; + unsigned stack_size; + uint32_t sp; + bool runnable; +} task_t; + + +#define TASK_COUNT_REG 9 + +typedef struct __attribute__((packed)) { + struct { + uint32_t registers[TASK_COUNT_REG]; + } sw_frame; + struct { + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r12; + uint32_t lr; + uint32_t pc; + uint32_t psr; + } nvic_frame; +} stack_frame_t; + +typedef struct { + task_t tasks[SCHEDULER_NUM_TASKS]; + uint8_t current_task; +} scheduler_t; + +static scheduler_t g_scheduler = { + .current_task = 0 +}; + + +static void task_exit_func(void) { + while (true); +} + +#define RETURN_PSR 0x21000000 +#define STACK_FILL 0xA5 + +#define SCHEDULER_MAX_TASKS 16 + +void scheduler_init(scheduler_t *scheduler) { + for (int i = 0; i < SCHEDULER_NUM_TASKS; i++) { + task_t *task = &(scheduler->tasks[i]); + task->stack = NULL; + task->stack_size = 0; + task->sp = (uint32_t)NULL; + task->runnable = false; + } + scheduler->current_task = 0; +} + +void scheduler_task(scheduler_t *scheduler, int task_no, void (*entry)(void)) { + + task_t *task = &(scheduler->tasks[task_no]); + task->stack = (void*)malloc(TASK_STACK_SIZE); + task->stack_size = TASK_STACK_SIZE; + memset(task->stack, STACK_FILL, task->stack_size); + + task->sp = (uint32_t)(task->stack + task->stack_size); + task->sp -= sizeof(stack_frame_t); + + stack_frame_t *frame = (stack_frame_t*)task->sp; + frame->nvic_frame.lr = (uint32_t)task_exit_func; + frame->nvic_frame.pc = (uint32_t)entry; + frame->nvic_frame.psr = RETURN_PSR; + + task->runnable = true; +} + +static void scheduler_switch(scheduler_t *scheduler) { + do { + scheduler->current_task = (scheduler->current_task + 1) % SCHEDULER_NUM_TASKS; + } while (!scheduler->tasks[scheduler->current_task].runnable); +} + +#define EXC_RETURN_MODE_THREAD 0x00000004 +#define RETURN_ON_PSP_THREAD 0xFFFFFFFD + + +void __attribute__((naked)) pend_sv_handler(void) { + + const uint32_t RETURN_ON_PSP = RETURN_ON_PSP_THREAD; + + __asm__("cpsid if":::"memory"); + uint32_t lr; + __asm__("mov %0, lr\n":"=r"(lr)); + + if (lr & EXC_RETURN_MODE_THREAD) { + uint32_t psp; + __asm__( + "mrs %0, psp\n" + "stmdb %0!, {r2,r4-r11}\n" + "msr psp, %0\n" : "=r"(psp) + ); + g_scheduler.tasks[g_scheduler.current_task].sp = psp; + } else { + __asm__( + "stmdb sp!, {r2,r4-r11}\n"); + } + + scheduler_switch(&g_scheduler); + + uint32_t psp = (uint32_t)g_scheduler.tasks[g_scheduler.current_task].sp; + __asm__( + "ldmfd %0!, {r2,r4-r11}\n" + "msr psp, %0\n"::"r"(psp) + ); + + __asm__("cpsie if" ::: "memory"); + __asm__("bx %0\n"::"r"(RETURN_ON_PSP)); +} + +void scheduler_yield(void) { + SCB_ICSR |= SCB_ICSR_PENDSVSET; + __asm__( + "nop\n" + "nop\n" + "nop\n" + "nop\n" + ); +} + +void task1(void) { + while (true) { + printf("task 1 %d\r\n", g_uptime); + delay(3000); + }; +} + +void task2(void) { + while (true) { + printf("task 2 %d\r\n", g_uptime); + delay(3100); + }; +} + +void task3(void) { + while (true) { + printf("task 3 %d\r\n", g_uptime); + delay(3200); + }; +} + +void task4(void) { + while (true) { + printf("task 4 %d\r\n", g_uptime); + delay(3300); + }; +} + +int main(void) { + g_uptime = 0; + clock_setup(); + usart_setup(); + scheduler_init(&g_scheduler); + scheduler_task(&g_scheduler, 0, task1); + scheduler_task(&g_scheduler, 1, task2); + scheduler_task(&g_scheduler, 2, task3); + scheduler_task(&g_scheduler, 3, task4); + + systick_setup(); + + while (true); +} diff --git a/main.ld b/main.ld new file mode 100644 index 0000000..49c4b7e --- /dev/null +++ b/main.ld @@ -0,0 +1,71 @@ + +MEMORY { + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K + SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K +} + +EXTERN (vector_table) + +ENTRY(reset_handler) + +SECTIONS +{ + .text : { + *(.vectors) + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + } >FLASH + + .preinit_array : { + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } >FLASH + + .init_array : { + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } >FLASH + + .fini_array : { + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + } >FLASH + + . = ALIGN(4); + _etext = .; + + .data : { + _data = .; + *(.data*) + . = ALIGN(4); + _edata = .; + } >SRAM AT >FLASH + + _data_loadaddr = LOADADDR(.data); + + .bss : { + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + } >SRAM + + + . = ALIGN(4); + _end = .; +} + +PROVIDE(_stack = ORIGIN(SRAM) + LENGTH(SRAM)); + +PROVIDE(_heap = _end ); +PROVIDE(_eheap = ORIGIN(SRAM) + LENGTH(SRAM)); diff --git a/syscall.c b/syscall.c new file mode 100644 index 0000000..618d8c5 --- /dev/null +++ b/syscall.c @@ -0,0 +1,141 @@ +/* + * Copyright 2022 Oleg Borodin + */ + + +#include + +#include +#include +#include +#include +#include + +#include + +#undef errno +extern int errno; + +char *__env[1] = { 0 }; + +char **environ = __env; + +int _execve(char *name, char **argv, char **env) { + errno = ENOMEM; + return -1; +} + +int _fork(void) { + errno = EAGAIN; + return -1; +} + +int _getpid(void) { + return 1; +} + +int _kill(int pid, int sig) { + errno = EINVAL; + return -1; +} + +int _exit() { + while (1); +} + +int _isatty(int file) { + return 1; +} + +int _fstat(int file, struct stat *st) { + st->st_mode = S_IFCHR; + return 0; +} + +int _link(char *old, char *new) { + errno = EMLINK; + return -1; +} + +int _lseek(int file, int ptr, int dir) { + return 0; +} + +int _open(const char *name, int flags, int mode) { + return -1; +} + +#define STDIN 0 +#define STDOUT 1 +#define STDERR 3 + + +int _read(int file, char *ptr, int len) { + int i = 0; + while (i < len) { + ptr[i++] = 0; + } + return i; +} + + +int _write(int file, char *ptr, int len) { + int i; + + if ((file == STDOUT) || (file == STDERR)) { + for (i = 0; i < len; i++) { + usart_putc(USART1, ptr[i]); + } + return len; + } + return 0; +} + +int _stat(char *file, struct stat *st) { + st->st_mode = S_IFCHR; + return 0; +} + +int _close(int file) { + return -1; +} + + +int _times(struct tms *buf) { + return -1; +} + +int _unlink(char *name) { + errno = ENOENT; + return -1; +} + +int _wait(int *status) { + errno = ECHILD; + return -1; +} + + +void *_sbrk(int incr) { + extern const void *_heap; + extern const void *_eheap; + + void *prev_heap; + static void *heap = NULL; + + if (heap == NULL) { + heap = (void *)&_heap; + } + + void* next_heap = heap + incr; + + if (next_heap >= (void*)&_eheap) { + errno = ENOMEM; + return NULL; + } + + prev_heap = heap; + heap = next_heap; + + return (void*)prev_heap; +} diff --git a/usartu.c b/usartu.c new file mode 100644 index 0000000..e0b15db --- /dev/null +++ b/usartu.c @@ -0,0 +1,19 @@ +/* + * Copyright 2022 Oleg Borodin + */ + +#include + +#include +#include + +void usart_puts(uint32_t usart, uint8_t * str) { + uint16_t i = 0; + while (str[i] != 0) { + usart_send_blocking(usart, str[i++]); + } +} + +void usart_putc(uint32_t usart, uint8_t c) { + usart_send_blocking(usart, c); +} diff --git a/usartu.h b/usartu.h new file mode 100644 index 0000000..9d1243f --- /dev/null +++ b/usartu.h @@ -0,0 +1,7 @@ +#ifndef _USARTU_H_XYZ +#define _USARTU_H_XYZ + +void usart_puts(uint32_t usart, uint8_t * str); +void usart_putc(uint32_t usart, uint8_t c); + +#endif