diff --git a/ChangeLog b/ChangeLog index 695bd21..aa3a683 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2019-12-03 NIIBE Yutaka + + * mcu/gd32vf103.h: New. + * mcu/chx-gd32vf103.c: New. + * mcu/clk_gpio_init-gd32vf103.c: New. + + * chopstx-riscv32.h: New. + * chopstx-riscv32.c: New. + * entry-riscv32.c: New. + + * entry-cortex-m.c: Rename from entry.c. + 2019-12-03 NIIBE Yutaka * contrib/spi-st.c: New. diff --git a/chopstx-riscv32.c b/chopstx-riscv32.c new file mode 100644 index 0000000..eabd982 --- /dev/null +++ b/chopstx-riscv32.c @@ -0,0 +1,684 @@ +/* + * chopstx-riscv32.c - Threads and only threads: Arch specific code + * for RISC-V 32 IMAC (Bumblebee core) + * + * Copyright (C) 2019 + * Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chopstx is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * recipients of GNU GPL by a written offer. + * + */ + +/* + * Define Bumblebee specific CSRs + */ +asm ( + ".equ msubm,0x7c4\n\t" /* No use (not needed to care about) */ + ".equ mtvt,0x307\n\t" + ".equ mtvt2,0x7ec\n\t" +); + +/* Data Memory Barrier. */ +static void +chx_dmb (void) +{ + asm volatile ("fence" : : : "memory"); +} + +/* No saved registers on the stack. */ + +/* + * Constants for RISC-V. + */ +#define REG_PC 0 +#define REG_RA 1 +#define REG_SP 2 +#define REG_GP 3 +#define REG_TP 4 +#define REG_A0 10 +#define MACHINE_STATUS_INIT 0x00001880 /* Machine-mode, interrupt enabled. */ + +/* + * We keep the TP register to the thread context address. + * Also, it is kept at the MSCRATCH register. + */ +static struct chx_thread * +chx_running (void) +{ + uint32_t r; + + asm ( "mv %0,tp" : "=r" (r)); + if (r == 0) + return NULL; + return (struct chx_thread *)(r - 20); +} + +static void +chx_set_running (struct chx_thread *tp) +{ + asm ( "addi tp,%0,20\n\t" + "csrw mscratch,tp" : /* no output */ : "r" (tp) : "memory"); +} + + +/* + * Lower layer architecture specific functions. + * + * system tick and interrupt + */ + +/* + * System tick + * + * SYSTICK timer model is decreasing counter, where counter value 0 + * means the timer stop. + * + * Since RISC-V MTIME is ever increasing counter, we need a bit of + * glue code here. Fortunately, we have MSTOP register for Bumblebee + * core, so, we can use the register to stop the counter. (If no such + * register stopping MTIME, we could put possible largest value in + * MTIMECMP, so that the timer won't be fired.) + */ +/* TIMER registers. */ +struct TIMER { + volatile uint32_t mtime_lo; + volatile uint32_t mtime_hi; + volatile uint32_t mtimecmp_lo; + volatile uint32_t mtimecmp_hi; + uint32_t rsv0[1018]; + volatile uint8_t mstop; + uint8_t rsv1[3]; + volatile uint8_t msip; + uint8_t rsv2[3]; +} __attribute__ ((packed)); +static struct TIMER *const TIMER = (struct TIMER *)0xD1000000; + +/* In Chopstx, we only use the lower 32-bit of the timer. */ + +static void +chx_systick_init_arch (void) +{ + TIMER->mstop |= 1; + TIMER->mtime_hi = TIMER->mtime_lo = 0; + TIMER->mtimecmp_lo = 0xffffffff; + TIMER->mtimecmp_hi = 0; +} + +/* + * @ticks: 0 means to stop the timer, otherwise update the timer + */ +static void +chx_systick_reload (uint32_t ticks) +{ + if (!ticks) + { + TIMER->mstop |= 1; + TIMER->mtimecmp_lo = 0xffffffff; + } + else + { + TIMER->mtime_lo = 0; + TIMER->mtimecmp_lo = ticks; + if ((TIMER->mstop & 1)) + TIMER->mstop &= ~1; + } +} + +static uint32_t +chx_systick_get (void) +{ + int stopped = (TIMER->mstop & 1); + + if (stopped) + return 0; + else + { + uint32_t now = TIMER->mtime_lo; + + if (TIMER->mtimecmp_lo <= now) + return 0; + + return TIMER->mtimecmp_lo - now; + } +} + +/* TIMER runs at 1/4 of core clock. */ + +static uint32_t +usec_to_ticks (uint32_t usec) +{ + return usec * (MHZ/4); +} + +static uint32_t +ticks_to_usec (uint32_t ticks) +{ + return ticks / (MHZ/4); +} + +/* + * Interrupt Handling of (E)CLIC + */ + +/* CLIC registers. */ +struct CLIC { + volatile uint8_t cfg; + uint8_t rsv[3]; + volatile uint32_t info; + volatile uint8_t uth; + volatile uint8_t sth; + volatile uint8_t hth; + volatile uint8_t mth; +} __attribute__ ((packed)); + +static struct CLIC *const CLIC = (struct CLIC *)0xD2000000; + +struct CLIC_INT { + volatile uint8_t ip; + volatile uint8_t ie; + volatile uint8_t attr; + volatile uint8_t ctl; +} __attribute__ ((packed)); +static struct CLIC_INT *const CLIC_INT = (struct CLIC_INT *)0xD2001000; + +static void +chx_enable_intr (uint8_t irq_num) +{ + CLIC_INT[irq_num].ie |= 1; +} + +static void +chx_clr_intr (uint8_t irq_num) +{ + /* Clear pending interrupt is done automatically by the hardware. */ + (void)irq_num; +} + +static int +chx_disable_intr (uint8_t irq_num) +{ + int already_disabled = !(CLIC_INT[irq_num].ie & 1); + + CLIC_INT[irq_num].ie &= ~1; + return already_disabled; +} + +static void +chx_set_intr_prio (uint8_t irq_num) +{ + CLIC_INT[irq_num].attr = 0xc0; /* Level triggered, SHV=0 */ + /* Note: SHV: Selective Hardware Vectoring: off (use common routine). */ + + CLIC_INT[irq_num].ctl = 0xff; +} + +#define SWINT_IRQ 3 +#define TIMER_IRQ 7 +#define MEMERR_IRQ 17 /* Why on earth it's IRQ??? */ + +static void chx_handle_intr (void); +static void exception_handler (void); + +/* Alignment to 64 is needed to enable ECLIC mode. */ +static void __attribute__ ((naked,aligned(64))) +exception_handler (void) +{ + asm volatile ( +"0: j 0b" + : /* no output */); +} + +static void __attribute__ ((naked)) +memory_error (void) +{ + asm volatile ( +"0: j 0b" + : /* no output */); +} + +typedef void (*handler)(void); + +/* Not used (because we always disable SHV for all interrupts), + * Just in case, if some interrupt uses SHV=1. + */ +static const handler vector_table[] __attribute__ ((aligned(512))) = { + 0, 0, 0, chx_handle_intr, + 0, 0, 0, chx_handle_intr, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, memory_error, 0, 0, +}; + +static void +chx_interrupt_controller_init (void) +{ + asm volatile ( + "csrw mtvt,%0" + : /* no output */ : "r" (vector_table) : "memory" ); + + asm volatile ( + "csrw mtvt2,%0\n\t" + "csrsi mtvt2,1" + : /* no output */ : "r" (chx_handle_intr) : "memory"); + + asm volatile ( + "csrw mtvec,%0\n\t" + "csrsi mtvec,3" /* Enable ECLC mode */ + : /* no output */ : "r" (exception_handler) : "memory" ); + + CLIC->cfg &= 0xe1; /* NLBITS = 0 */ + CLIC->mth = 0; + + /* In Bumblebee core, timer interrupt is also handled by CLIC. */ + chx_set_intr_prio (SWINT_IRQ); + chx_enable_intr (SWINT_IRQ); + chx_set_intr_prio (TIMER_IRQ); + chx_enable_intr (TIMER_IRQ); + chx_set_intr_prio (MEMERR_IRQ); + chx_enable_intr (MEMERR_IRQ); +} + +/* Just for testing TIMER and ECLIC. No real use for now. */ +static void +chx_sw_int (int go) +{ + if (go) + TIMER->msip |= 1; + else + TIMER->msip &= ~1; +} + +static void +chx_cpu_sched_lock (void) +{ + asm volatile ("csrci mstatus,8" : : : "memory" ); +} + +static void +chx_cpu_sched_unlock (void) +{ + asm volatile ("csrsi mstatus,8" : : : "memory" ); +} + +static void +chx_init_arch (struct chx_thread *tp) +{ + memset (&tp->tc, 0, sizeof (tp->tc)); + chx_set_running (tp); +} + +#define SAVE_CALLEE_SAVE_REGISTERS \ + "# Save callee save registers\n\t" \ + "sw s0,32(sp)\n\t" \ + "sw s1,36(sp)\n\t" \ + "sw s2,72(sp)\n\t" \ + "sw s3,76(sp)\n\t" \ + "sw s4,80(sp)\n\t" \ + "sw s5,84(sp)\n\t" \ + "sw s6,88(sp)\n\t" \ + "sw s7,92(sp)\n\t" \ + "sw s8,96(sp)\n\t" \ + "sw s9,100(sp)\n\t" \ + "sw s10,104(sp)\n\t" \ + "sw s11,108(sp)\n\t" + +#define RESTORE_CALLEE_SAVE_REGISTERS \ + "# Restore callee save registers\n\t" \ + "lw s0,32(sp)\n\t" \ + "lw s1,36(sp)\n\t" \ + "lw s2,72(sp)\n\t" \ + "lw s3,76(sp)\n\t" \ + "lw s4,80(sp)\n\t" \ + "lw s5,84(sp)\n\t" \ + "lw s6,88(sp)\n\t" \ + "lw s7,92(sp)\n\t" \ + "lw s8,96(sp)\n\t" \ + "lw s9,100(sp)\n\t" \ + "lw s10,104(sp)\n\t" \ + "lw s11,108(sp)\n\t" + +#define SAVE_OTHER_REGISTERS \ + "# Save other registers\n\t" \ + "sw ra,4(sp)\n\t" \ + "sw gp,12(sp)\n\t" \ + "sw t0,20(sp)\n\t" \ + "sw t1,24(sp)\n\t" \ + "sw t2,28(sp)\n\t" \ + "sw a0,40(sp)\n\t" \ + "sw a1,44(sp)\n\t" \ + "sw a2,48(sp)\n\t" \ + "sw a3,52(sp)\n\t" \ + "sw a4,56(sp)\n\t" \ + "sw a5,60(sp)\n\t" \ + "sw a6,64(sp)\n\t" \ + "sw a7,68(sp)\n\t" \ + "sw t3,112(sp)\n\t" \ + "sw t4,116(sp)\n\t" \ + "sw t5,120(sp)\n\t" \ + "sw t6,124(sp)\n\t" + +#define RESTORE_OTHER_REGISTERS \ + "# Restore other registers\n\t" \ + "lw ra,4(sp)\n\t" \ + "lw gp,12(sp)\n\t" \ + "lw t0,20(sp)\n\t" \ + "lw t1,24(sp)\n\t" \ + "lw t2,28(sp)\n\t" \ + "lw a0,40(sp)\n\t" \ + "lw a1,44(sp)\n\t" \ + "lw a2,48(sp)\n\t" \ + "lw a3,52(sp)\n\t" \ + "lw a4,56(sp)\n\t" \ + "lw a5,60(sp)\n\t" \ + "lw a6,64(sp)\n\t" \ + "lw a7,68(sp)\n\t" \ + "lw t3,112(sp)\n\t" \ + "lw t4,116(sp)\n\t" \ + "lw t5,120(sp)\n\t" \ + "lw t6,124(sp)\n\t" +/* + * MSTATUS register: + * 31: SD + * 16-15: XS (Extra Unit state) + * 14-13: FS (Floating-point Unit state) + * 12-11: MPP (Previous Privilege) + * 7: MPIE (Previous Interrupt Enable flag) + * 3: MIE (Interrupt Enable flag) + */ +/* + * MSUBM register: + * 9-8 PTYP (Previous Type-of-execution) + * 7-6 TYP (Currunt Type-of-execution) + * 0: Normal, 1: Interrupt, 2: Excep, 3: NMI + * + * No need to store PTYP field of MSUBM in MACHINE_STATUS. + * No need to setup MSUBM for PTYP. + * Since it's always 0 when this is called. + * + * Save MPP..MPIE in MACHINE_STATUS in the thread context. + */ +#define SETUP_MSTATUS_FROM_MACHINE_STATUS \ + "lw a0,128(sp)\n\t" /* MPP..MPIE is in MACHINE_STATUS */ \ + "csrr a1,mstatus\n\t" \ + "srli a1,a1,13\n\t" \ + "slli a1,a1,13\n\t" /* SD, XS, and FS bits from MSTATUS */ \ + "or a0,a0,a1\n\t" \ + "csrw mstatus,a0\n\t" /* Note: keep MIE=0 */ + +/* It is good if ISA has a single instruction for this operation. */ +#define CATCH_AN_INTERRUPT_SYNCHRONOUSLY \ + "mv a0,zero\n" \ + "0:\n\t" \ + /* \ + * Interrupt is masked here, and it executes the WFI \ + * instruction. When an interrupt occurs, the core is waken \ + * up (even if it is masked). \ + */ \ + "csrci mstatus,8\n\t" /* Interrupt should be masked already. */ \ + /* Nevertheless, make sure it's masked. */ \ + "wfi\n\t" \ + /* \ + * It is good if MCAUSE were updated here, but it is only \ + * updated when the control goes into the interrupt service, \ + * in a step of the interrupt handling steps of the core. So, \ + * we let it go to chx_handle_intr, by unmasking. \ + */ \ + "csrsi mstatus,8\n\t" /* Unmask interrupts to catch one. */ \ + /* Just before this line, it is interrupted. */ \ + /* And interrupt is masked and a0 is set. */ \ + "beqz a0,0b\n\t" /* Just in case if not, loop. */ + +/* + * The idle function. + * + * NOTE: In this function, interrupt is masked (MIE=0) and interrupt + * is synchronously handled. + */ +static struct chx_thread * __attribute__ ((used)) +chx_idle (void) +{ + extern void chx_prepare_sleep_mode (void); + register struct chx_thread *tp_next asm ("a0") = NULL; + + while (tp_next == NULL) + { + register uint32_t irq_num asm ("a0"); + + chx_prepare_sleep_mode (); /* MCU specific sleep setup */ + + asm volatile ( + CATCH_AN_INTERRUPT_SYNCHRONOUSLY + /* + * In chx_handle_intr, a0 is set by the value of MCAUSE. + */ + "slli a0,a0,20\n\t" + "srli a0,a0,20" /* Take lower 12-bit of MCAUSE */ + : "=r" (irq_num)); + /* Note: here, interrupt is masked again. */ + + if (irq_num == SWINT_IRQ) + chx_sw_int (0); + else if (irq_num == TIMER_IRQ) + tp_next = chx_timer_expired (); + else if (irq_num == MEMERR_IRQ) + memory_error (); + else + tp_next = chx_recv_irq (irq_num); + } + + return tp_next; +} + + +static uintptr_t +voluntary_context_switch (struct chx_thread *tp_next) +{ + register uintptr_t result asm ("a0"); + + asm volatile ( + /* Here, %0 (a0) points to pointer (struct chx_thread *) to be + * switched. We get the thread context pointer adding the + * offset. + */ + "# Voluntary context switch\n\t" + "sw sp,8(tp)\n\t" + "mv sp,tp\n\t" /* Using SP, we can use C.SWSP instruction */ + "sw zero,0(sp)\n\t" + SAVE_CALLEE_SAVE_REGISTERS + "# Check if going to IDLE function\n\t" + "bnez %0,0f\n\t" + /* + * NOTE: Here, for running chx_idle, we can use + * __main_stack_end__, because neither any threads, nor + * interrupt context use that. Using __main_stack_end__, we + * can minimize stack memory usage of each thread. + */ + "# Call the IDLE function, interrupt masked.\n\t" + "mv tp,zero\n\t" + "csrw mscratch,tp\n\t" + "la sp,__main_stack_end__\n\t" + "call chx_idle\n" + ".L_V_CONTEXT_SWITCH_BEGIN:\n" + "0:\n\t" + "addi %0,%0,20\n\t" + "mv sp,%0\n\t" + RESTORE_CALLEE_SAVE_REGISTERS + /**/ + "csrw mscratch,sp\n\t" + "lw a0,0(sp)\n\t" + "beqz a0,1f\n" + ".L_RETURN_TO_PREEMPTED_THREAD:\n\t" + "csrw mepc,a0\n\t" + SETUP_MSTATUS_FROM_MACHINE_STATUS + RESTORE_OTHER_REGISTERS + "lw tp,16(sp)\n\t" /* Application is free to other use of TP */ + "lw sp,8(sp)\n\t" + "mret\n" + "1:\n\t" + "lw a0,-4(sp)\n\t" /* Get the result value */ + "mv tp,sp\n\t" + "lw sp,8(sp)\n\t" + "csrsi mstatus,8\n" /* Unmask interrupts. */ + ".L_V_CONTEXT_SWITCH_FINISH:" + : "=r" (result) + : "0" (tp_next) + : "ra", "t0", "t1", "t2", "t3", "t4", "t5", "t6", + "a1", "a2", "a3", "a4", "a5", "a6", "a7", + "memory"); + + return result; +} + +extern void cause_link_time_error_unexpected_size_of_struct_chx_thread (void); + +static struct chx_thread * +chopstx_create_arch (uintptr_t stack_addr, size_t stack_size, + voidfunc thread_entry, void *arg) +{ + struct chx_thread *tp; + void *stack; + register uint32_t gp; + + asm ( "mv %0,gp" : "=r" (gp)); + + if (CHOPSTX_THREAD_SIZE != sizeof (struct chx_thread)) + cause_link_time_error_unexpected_size_of_struct_chx_thread (); + + if (stack_size < sizeof (struct chx_thread)) + chx_fatal (CHOPSTX_ERR_THREAD_CREATE); + + stack = (void *)(stack_addr + stack_size - sizeof (struct chx_thread)); + tp = (struct chx_thread *)stack; + memset (&tp->tc, 0, sizeof (tp->tc)); + tp->tc.reg[REG_A0] = (uint32_t)arg; + tp->tc.reg[REG_RA] = (uint32_t)chopstx_exit; + tp->tc.reg[REG_PC] = (uint32_t)thread_entry; + tp->tc.reg[REG_GP] = gp; + tp->tc.reg[REG_TP] = (uint32_t)&tp->tc; + tp->tc.reg[REG_SP] = (uint32_t)stack; + tp->tc.machine_status = MACHINE_STATUS_INIT; + + return tp; +} + + +/* + * Note: Examine the assembler output carefully, because it has + * ((naked)) attribute + */ +static void __attribute__ ((naked,aligned(4))) +chx_handle_intr (void) +{ + struct chx_thread *tp_next; + uint32_t irq_num; + + /* + * stack setup to __main_stack_end__ + * save registers. + */ + asm volatile ( + "csrrw sp,mscratch,sp\n\t" /* SP to MSCRATCH, thread pointer into SP */ + "# Check if it is IDLE\n\t" + "bnez sp,0f\n\t" + /**/ + "csrrw sp,mscratch,sp\n\t" /* Recover MSCRATCH, the thread pointer */ + "li a0,0x80\n\t" + "csrrc x0,mstatus,a0\n\t" /* Clear MPIE bit to mask interrupt */ + "csrr a0,mcause\n\t" + "mret\n" + "0:\n\t" + "sw tp,16(sp)\n\t" /* Application is free to other use of TP */ + SAVE_OTHER_REGISTERS + "csrr a0,mepc\n\t" + "sw a0,0(sp)\n\t" + /**/ + "mv tp,sp\n\t" /* TP is now the thread pointer */ + "csrrw sp,mscratch,sp\n\t" /* TP to MSCRATCH, SP_old into SP */ + "sw sp,8(tp)\n\t" + "la sp,__main_stack_end__"); + + /* + * The stack at __main_stack_end__ is shared data between chx_idle + * and this handler. When it comes here, there is no active chx_idle, + * so, it is safe to use the stack. + * + * Note that when there are multiple cores, we need this stack for + * each core. + */ + + asm ( "csrr %0,mcause\n\t" + "slli %0,%0,20\n\t" + "srli %0,%0,20" /* Take lower 12-bit of MCAUSE */ + : "=r" (irq_num)); + + tp_next = NULL; + if (irq_num == SWINT_IRQ) + chx_sw_int (0); + else if (irq_num == TIMER_IRQ) + tp_next = chx_timer_expired (); + else if (irq_num == MEMERR_IRQ) + memory_error (); + else + tp_next = chx_recv_irq (irq_num); + + if (!tp_next) + asm volatile ( + "mv sp,tp\n\t" /* Using SP, we can use C.SWSP instruction */ + RESTORE_OTHER_REGISTERS + "lw tp,16(sp)\n\t" /* Application is free to other use of TP */ + "lw sp,8(sp)\n\t" + "mret"); + + tp_next = chx_running_preempted (tp_next); + + asm volatile ( + "# Involuntary context switch\n\t" + "mv sp,tp\n\t" /* Using SP, we can use C.SWSP instruction */ + SAVE_CALLEE_SAVE_REGISTERS + /* + * MACHINE_STATUS = (MSTATUS & 0x00001f80) + */ + "csrr a1,mstatus\n\t" + "andi a1,a1,-9\n\t" /* Clear MIE (bit3) */ + "slli a1,a1,19\n\t" /* Clear bit31 to bit13 */ + "srli a1,a1,19\n\t" /* MPP..MPIE from MSTATUS */ + "sw a1,128(sp)\n" + ".L_IV_CONTEXT_SWITCH_BEGIN:\n\t" + "addi %0,%0,20\n\t" + "mv sp,%0\n\t" + RESTORE_CALLEE_SAVE_REGISTERS + /**/ + "csrw mscratch,sp\n\t" + "lw a0,0(sp)\n\t" + "bnez a0,.L_RETURN_TO_PREEMPTED_THREAD\n\t" + /**/ + "lw a0,-4(sp)\n\t" /* Get the result value */ + "mv tp,sp\n\t" + "lw sp,8(sp)\n\t" + "la a1,.L_V_CONTEXT_SWITCH_FINISH\n\t" + "csrw mepc,a1\n\t" + "li a1,0x188\n\t" /* Set MPIE and MPP bits */ + "slli a1,a1,4\n\t" + "csrrs x0,mstatus,a1\n\t"/* Prev: Machine mode, enable interrupt */ + "mret" /* Return to Prev */ + : /* no output */ : "r" (tp_next) : "memory"); +} diff --git a/chopstx-riscv32.h b/chopstx-riscv32.h new file mode 100644 index 0000000..3ba4424 --- /dev/null +++ b/chopstx-riscv32.h @@ -0,0 +1,61 @@ +/* + * The thread context: specific to RISC-V 32. + * + * Calling convention matters when we implement context switch. + * + * We implement a voluntary context switch function which follows + * standard calling convension. The function doesn't need to save + * caller save registers (function arguments and temporaries) and + * fixed ones (global pointer and thread pointer) to a thread context + * struct. Only it needs to care about stack pointer and saved + * registers. + * + * For interrupt handling when it returns directly to original thread, + * we can avoid saving all registers; It can only save registers used + * in that handling. When it returns to another thread, that is, when + * it does involuntary context switch (also called "thread + * preemption"), all registers should be saved. + * + * In Chopstx, we don't use nested interrupt of machine. So, when a + * thread is interrupted, the interrupted context is always one of + * application threads or the idle thread. We have a dedicated stack + * for exception handling. + * + * Here is RISC-V calling convention. + * + * Register ABI name Description Saver other usage + * x0 zero 0, hard-wired -- + * x1 ra return address caller + * x2 sp stack pointer callEE + * x3 gp global pointer -- + * x4 tp thread pointer -- + * x5-7 t0-2 temporary caller + * x8 s0/fp saved register callEE frame pointer + * x9 s1 saved register callEE + * x10-11 a0-1 argument caller return value + * x12-17 a2-7 argument caller + * x18-27 s2-11 saved register callEE + * x28-31 t3-6 temporary caller + * + * Simply, we have entries of all registers in the thread context. We + * don't use stack to save/restore part of its context. We don't put + * any other constraints such that an application should always keep + * gp and/or tp registers; It is free for an application to + * temporarily use those registers beyond a conventional usage (e.g., + * for some specific routines like hash computation). + * + * We use the first entry, reg[0], to store PC, because x0 is always 0, + * no need to save/restore it. + * + * Core specific machine status information is saved into the last + * entry, MACHINE_STATUS. For Bumblebee core, it is composed by bits + * of mstatus and msubm. + */ +struct tcontext { + uint32_t reg[32]; + uint32_t machine_status; +}; + +typedef struct tcontext tcontext_t; + +#define CHOPSTX_THREAD_SIZE 160 diff --git a/entry.c b/entry-cortex-m.c similarity index 99% rename from entry.c rename to entry-cortex-m.c index 2d90b27..8e8b4c7 100644 --- a/entry.c +++ b/entry-cortex-m.c @@ -1,5 +1,5 @@ /* - * entry.c - Entry routine when reset and interrupt vectors. + * entry-cortex-m.c - Entry routine when reset and interrupt vectors. * * Copyright (C) 2013, 2014, 2015, 2016, 2017, 2019 * Flying Stone Technology diff --git a/entry-riscv32.c b/entry-riscv32.c new file mode 100644 index 0000000..cc5effe --- /dev/null +++ b/entry-riscv32.c @@ -0,0 +1,84 @@ +/* + * entry-riscv32.c - Entry routine when reset. + * + * Copyright (C) 2019 + * Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chopstx is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * recipients of GNU GPL by a written offer. + * + */ + +#include "board.h" +#include "mcu/clk_gpio_init-gd32vf103.c" + +/* + * Startup entry point. + */ +void __attribute__ ((naked,section(".text.startup.0"))) +entry (void) +{ + /* Start at 0x00000000 (alias 0x08000000), interrupt masked */ + asm volatile ( + "li a0,0x0800000c\n\t" + "jr a0\n\t" /* Jump to physical address */ + ".align 2\n\t" + ".option push\n" + ".option norelax\n\t" + "la gp,__global_pointer$\n" + ".option pop\n\t" + "la sp,__main_stack_end__\n\t" + "call clock_init\n\t" + /* Clear BSS section. */ + "la a0,_bss_start\n\t" + "la a1,_bss_end\n\t" + "0:\n\t" + "beq a0,a1,1f\n" + "sw zero,(a0)\n\t" + "addi a0,a0,4\n\t" + "j 0b\n" + "1:\n\t" + /* Copy data section. */ + "la a0,_textdata\n\t" + "la a1,_data\n\t" + "la a2,_edata\n" + "0:\n\t" + "beq a1,a2,1f\n\t" + "lw t0,(a0)\n\t" + "sw t0,(a1)\n\t" + "addi a0,a0,4\n\t" + "addi a1,a1,4\n\t" + "j 0b\n" + "1:\n\t" + /* Switch to application stack. */ + "la sp,__process0_stack_end__-160\n\t" + "mv a0,sp\n\t" + "call chx_init\n\t" + "call chx_systick_init\n\t" + "call gpio_init\n\t" + /* Enable interrupts. */ + "csrsi mstatus,8\n\t" + /* Call main. */ + "call main\n\t" + "3:\n\t" + "j 3b" + : /* no output */ : /* no input */ : "memory"); +} diff --git a/mcu/chx-gd32vf103.c b/mcu/chx-gd32vf103.c new file mode 100644 index 0000000..338f9dc --- /dev/null +++ b/mcu/chx-gd32vf103.c @@ -0,0 +1,22 @@ +#include +#include + +asm ( + ".equ wfe,0x810\n\t" /* Not used (yet). */ + ".equ sleepvalue,0x811" /* Not used (yet). */ +); + +extern int chx_allow_sleep; + +void +chx_sleep_mode (int how) +{ + /*TBD*/ + (void)how; +} + +void +chx_prepare_sleep_mode (void) +{ + /*TBD*/ +} diff --git a/mcu/clk_gpio_init-gd32vf103.c b/mcu/clk_gpio_init-gd32vf103.c new file mode 100644 index 0000000..75422ca --- /dev/null +++ b/mcu/clk_gpio_init-gd32vf103.c @@ -0,0 +1,77 @@ +#include +#include + +static void __attribute__((used,section(".text.startup.1"))) +clock_init (void) +{ + /* HXTAL setup */ + RCU->CTL |= RCU_CTL_HXTALEN; + while (!(RCU->CTL & RCU_CTL_HXTALSTB)) + ; + + RCU->CFG0 &= ~RCU_CFG0_AHB_APB1_APB2_MASK; + + /* CK_AHB = CK_SYS */ + RCU->CFG0 |= RCU_CFG0_AHB_CKSYS_DIV1; + /* CK_APB2 = CK_AHB */ + RCU->CFG0 |= RCU_CFG0_APB2_CKAHB_DIV1; + /* CK_APB1 = CK_AHB/2 */ + RCU->CFG0 |= RCU_CFG0_APB1_CKAHB_DIV2; + + /* CK_ADC, CK_TIMER1xxx, CK_TIMER0, CK_I2S */ + + /* PLL setup */ + RCU->CFG0 &= ~RCU_CFG0_PLLSRC_PLLMF_MASK; + RCU->CFG0 |= RCU_CFG0_PLL_MUL_VALUE | RCU_CFG0_PLLSRC_HXTAL; + + RCU->CFG1 &= ~RCU_CFG1_PREDV0SEL_MASK; + RCU->CFG1 |= RCU_CFG1_PREDV0SEL_HXTAL; + + RCU->CTL |= RCU_CTL_PLLEN; + + while (!(RCU->CTL & RCU_CTL_PLLSTB)) + ; + + /* Select PLL as system clock */ + RCU->CFG0 &= ~RCU_CFG0_SCS_MASK; + RCU->CFG0 |= RCU_CFG0_CKSYSSRC_PLL; + + /* Wait until PLL is selected as system clock */ + while (!(RCU->CFG0 & RCU_CFG0_SCSS_PLL)) + ; + + /* Stop IRC8M */ + RCU->CTL &= ~RCU_CTL_IRC8MEN; + + /* Flash setup: TBD */ +} + +static void __attribute__((used,section(".text.startup.1"))) +gpio_init (void) +{ + RCU->APB2EN |= RCU_APB2_GPIO; + RCU->APB2RST = RCU_APB2_GPIO; + RCU->APB2RST = 0; + +#ifdef AFIO_MAPR_SOMETHING + AFIO->MAPR |= AFIO_MAPR_SOMETHING; +#endif + + /* LED is mandatory. We configure it always. */ + GPIO_LED->ODR = VAL_GPIO_LED_ODR; + GPIO_LED->CRH = VAL_GPIO_LED_CRH; + GPIO_LED->CRL = VAL_GPIO_LED_CRL; + + /* If there is USB enabler pin and it's independent, we configure it. */ +#if defined(GPIO_USB) && defined(VAL_GPIO_USB_ODR) + GPIO_USB->ODR = VAL_GPIO_USB_ODR; + GPIO_USB->CRH = VAL_GPIO_USB_CRH; + GPIO_USB->CRL = VAL_GPIO_USB_CRL; +#endif + +#ifdef GPIO_OTHER + GPIO_OTHER->ODR = VAL_GPIO_OTHER_ODR; + GPIO_OTHER->CRH = VAL_GPIO_OTHER_CRH; + GPIO_OTHER->CRL = VAL_GPIO_OTHER_CRL; +#endif +} diff --git a/mcu/gd32vf103.h b/mcu/gd32vf103.h new file mode 100644 index 0000000..4e10c01 --- /dev/null +++ b/mcu/gd32vf103.h @@ -0,0 +1,56 @@ +struct RCU { + volatile uint32_t CTL; + volatile uint32_t CFG0; + volatile uint32_t INT; + volatile uint32_t APB2RST; + volatile uint32_t APB1RST; + volatile uint32_t AHBEN; + volatile uint32_t APB2EN; + volatile uint32_t APB1EN; + volatile uint32_t BDCTL; + volatile uint32_t RSTSCK; + volatile uint32_t AHBRST; + volatile uint32_t CFG1; + uint32_t rsv; + volatile uint32_t DSV; +}; +static struct RCU *const RCU = (struct RCU *)0x40021000; + +#define RCU_CTL_HXTALEN 0x00010000 +#define RCU_CTL_HXTALSTB 0x00020000 +#define RCU_CTL_PLLSTB 0x02000000 +#define RCU_CTL_PLLEN 0x01000000 +#define RCU_CTL_IRC8MEN 0x00000001 + +#define RCU_CFG0_ADC_MASK 0x0000c000 +#define RCU_CFG0_AHB_APB1_APB2_MASK 0x00003ff0 +#define RCU_CFG0_AHB_CKSYS_DIV1 0x00000000 +#define RCU_CFG0_APB2_CKAHB_DIV1 0x00000000 +#define RCU_CFG0_APB1_CKAHB_DIV2 0x00000400 +#define RCU_CFG0_PLLSRC_PLLMF_MASK 0x203d0000 +#define RCU_CFG0_PLL_MUL12 0x00280000 +#define RCU_CFG0_PLLSRC_HXTAL 0x00010000 +#define RCU_CFG0_SCS_MASK 0x00000003 +#define RCU_CFG0_SCSS_PLL 0x00000008 +#define RCU_CFG0_CKSYSSRC_PLL 0x00000002 + +#define RCU_CFG1_PREDV0SEL_MASK 0x00010000 +#define RCU_CFG1_PREDV0SEL_HXTAL 0x00000000 + +#define RCU_APB2_GPIOA 0x00000004 +#define RCU_APB2_GPIOB 0x00000008 +#define RCU_APB2_GPIOC 0x00000010 + +/* Semantics is exactly same as STM32F103. */ +struct GPIO { + volatile uint32_t CRL; + volatile uint32_t CRH; + volatile uint32_t IDR; + volatile uint32_t ODR; + volatile uint32_t BSRR; + volatile uint32_t BRR; + volatile uint32_t LCKR; +}; +static struct GPIO *const GPIOA = (struct GPIO *)0x40010800; +static struct GPIO *const GPIOB = (struct GPIO *)0x40010C00; +static struct GPIO *const GPIOC = (struct GPIO *)0x40011000; diff --git a/rules.mk b/rules.mk index b80552a..0471b95 100644 --- a/rules.mk +++ b/rules.mk @@ -1,7 +1,7 @@ # Chopstx make rules. ifeq ($(EMULATION),) -CSRC += $(CHOPSTX)/entry.c +CSRC += $(CHOPSTX)/entry-$(ARCH).c else CSRC += $(CHOPSTX)/entry-gnu-linux.c endif