Add RISC-V 32 IMAC support.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka
2019-12-03 13:27:16 +09:00
parent 51f2ca841f
commit 214066fd82
9 changed files with 998 additions and 2 deletions

View File

@@ -1,3 +1,15 @@
2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* 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 <gniibe@fsij.org> 2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* contrib/spi-st.c: New. * contrib/spi-st.c: New.

684
chopstx-riscv32.c Normal file
View File

@@ -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 <gniibe@fsij.org>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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");
}

61
chopstx-riscv32.h Normal file
View File

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

View File

@@ -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 * Copyright (C) 2013, 2014, 2015, 2016, 2017, 2019
* Flying Stone Technology * Flying Stone Technology

84
entry-riscv32.c Normal file
View File

@@ -0,0 +1,84 @@
/*
* entry-riscv32.c - Entry routine when reset.
*
* Copyright (C) 2019
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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");
}

22
mcu/chx-gd32vf103.c Normal file
View File

@@ -0,0 +1,22 @@
#include <stdint.h>
#include <mcu/gd32vf103.h>
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*/
}

View File

@@ -0,0 +1,77 @@
#include <stdint.h>
#include <mcu/gd32vf103.h>
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
}

56
mcu/gd32vf103.h Normal file
View File

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

View File

@@ -1,7 +1,7 @@
# Chopstx make rules. # Chopstx make rules.
ifeq ($(EMULATION),) ifeq ($(EMULATION),)
CSRC += $(CHOPSTX)/entry.c CSRC += $(CHOPSTX)/entry-$(ARCH).c
else else
CSRC += $(CHOPSTX)/entry-gnu-linux.c CSRC += $(CHOPSTX)/entry-gnu-linux.c
endif endif