From 39cf4d38c1dd5d9bfe4a4fd59cbae848cb259bf8 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Thu, 5 Dec 2019 18:54:09 +0100 Subject: [PATCH 1/8] boards/hifive1*: move xtimer definitions to board.h --- boards/hifive1/include/board.h | 7 +++++++ boards/hifive1/include/periph_conf.h | 10 ---------- boards/hifive1b/include/board.h | 7 +++++++ boards/hifive1b/include/periph_conf.h | 10 ---------- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/boards/hifive1/include/board.h b/boards/hifive1/include/board.h index 9efd7c1a211c..756e1d78aa53 100644 --- a/boards/hifive1/include/board.h +++ b/boards/hifive1/include/board.h @@ -27,6 +27,13 @@ extern "C" { #endif +/** + * @name Xtimer configuration + * @{ + */ +#define XTIMER_HZ (32768UL) +/** @} */ + /** * @name Macros for controlling the on-board LEDs * @{ diff --git a/boards/hifive1/include/periph_conf.h b/boards/hifive1/include/periph_conf.h index 673184fddad6..20a3d12706ab 100644 --- a/boards/hifive1/include/periph_conf.h +++ b/boards/hifive1/include/periph_conf.h @@ -34,16 +34,6 @@ extern "C" { #define CLOCK_CORECLOCK (200000000ul) /** @} */ -/** - * @name Xtimer configuration - * @{ - */ -#define XTIMER_DEV (0) -#define XTIMER_CHAN (0) -#define XTIMER_WIDTH (32) -#define XTIMER_HZ (32768ul) -/** @} */ - /** * @name Timer configuration * diff --git a/boards/hifive1b/include/board.h b/boards/hifive1b/include/board.h index 4898fc400a20..84b3b70fe725 100644 --- a/boards/hifive1b/include/board.h +++ b/boards/hifive1b/include/board.h @@ -28,6 +28,13 @@ extern "C" { #endif +/** + * @name Xtimer configuration + * @{ + */ +#define XTIMER_HZ (32768UL) +/** @} */ + /** * @name Macros for controlling the on-board LEDs * @{ diff --git a/boards/hifive1b/include/periph_conf.h b/boards/hifive1b/include/periph_conf.h index 53986a311a88..6379869e7652 100644 --- a/boards/hifive1b/include/periph_conf.h +++ b/boards/hifive1b/include/periph_conf.h @@ -35,16 +35,6 @@ extern "C" { #define CLOCK_CORECLOCK (200000000ul) /** @} */ -/** - * @name Xtimer configuration - * @{ - */ -#define XTIMER_DEV (0) -#define XTIMER_CHAN (0) -#define XTIMER_WIDTH (32) -#define XTIMER_HZ (32768ul) -/** @} */ - /** * @name Timer configuration * From 97e1c7ba7e76b662e93ed96a54fa43ab16c0c30e Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Tue, 10 Dec 2019 11:29:27 +0100 Subject: [PATCH 2/8] cpu/fe310: reorganize files and includes --- cpu/fe310/clock.c | 52 +++++ cpu/fe310/cpu.c | 400 ++++----------------------------- cpu/fe310/include/cpu.h | 22 +- cpu/fe310/include/periph_cpu.h | 20 +- cpu/fe310/irq_arch.c | 204 +++++++++++++++++ cpu/fe310/panic.c | 27 +++ cpu/fe310/thread_arch.c | 197 ++++++++++++++++ 7 files changed, 548 insertions(+), 374 deletions(-) create mode 100644 cpu/fe310/clock.c create mode 100644 cpu/fe310/irq_arch.c create mode 100644 cpu/fe310/panic.c create mode 100644 cpu/fe310/thread_arch.c diff --git a/cpu/fe310/clock.c b/cpu/fe310/clock.c new file mode 100644 index 000000000000..6e0548a86bdf --- /dev/null +++ b/cpu/fe310/clock.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017, 2019 Ken Rabold, JP Bonn + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_fe310 + * @{ + * + * @file cpu.c + * @brief Implementation of the clock initialization for SiFive FE310 + * + * @author Ken Rabold + * @} + */ + +#include "cpu.h" +#include "periph_conf.h" + +#include "vendor/encoding.h" +#include "vendor/platform.h" +#include "vendor/prci_driver.h" + +void clock_init(void) +{ + /* In case we are executing from QSPI, (which is quite likely) we need to + * set the QSPI clock divider appropriately before boosting the clock + * frequency. PRCI_set_hfrosctrim_for_f_cpu() tries multiple clocks + * so choose a safe value that should work for all frequencies. + */ + SPI0_REG(SPI_REG_SCKDIV) = SCKDIV_SAFE; + + /* Note: The range is limited to ~100MHz and depends on PLL settings */ + PRCI_set_hfrosctrim_for_f_cpu(CPU_DESIRED_FREQ, PRCI_FREQ_UNDERSHOOT); + + /* begin{code-style-ignore} */ + SPI0_REG(SPI_REG_FFMT) = /* setup "Fast Read Dual I/O" */ + SPI_INSN_CMD_EN | /* Enable memory-mapped flash */ + SPI_INSN_ADDR_LEN(3) | /* 25LP03D read commands have 3 address bytes */ + SPI_INSN_PAD_CNT(4) | /* 25LP03D Table 6.11 Read Dummy Cycles = 4 */ + SPI_INSN_CMD_PROTO(SPI_PROTO_S) | /* 25LP03D Table 8.1 "Instruction */ + SPI_INSN_ADDR_PROTO(SPI_PROTO_D) | /* Set" shows mode for cmd, addr, and */ + SPI_INSN_DATA_PROTO(SPI_PROTO_D) | /* data protocol for given instruction */ + SPI_INSN_CMD_CODE(0xBB) | /* Set the instruction to "Fast Read Dual I/O" */ + SPI_INSN_PAD_CODE(0x00); /* Dummy cycle sends 0 value bits */ + /* end{code-style-ignore} */ + + SPI0_REG(SPI_REG_SCKDIV) = SCKDIV; +} diff --git a/cpu/fe310/cpu.c b/cpu/fe310/cpu.c index c7a7e7c84884..2cd43fdd510c 100644 --- a/cpu/fe310/cpu.c +++ b/cpu/fe310/cpu.c @@ -17,379 +17,65 @@ * @} */ -#include -#include -#include -#include - -#include "thread.h" -#include "irq.h" -#include "sched.h" -#include "thread.h" -#include "irq.h" #include "cpu.h" -#include "context_frame.h" -#include "periph_cpu.h" #include "periph/init.h" -#include "panic.h" -#include "vendor/encoding.h" -#include "vendor/platform.h" -#include "vendor/plic_driver.h" - -/* Default state of mstatus register */ -#define MSTATUS_DEFAULT (MSTATUS_MPP | MSTATUS_MPIE) - -volatile int __in_isr = 0; - -/* ISR trap vector */ -void trap_entry(void); - -/* PLIC external ISR function list */ -static external_isr_ptr_t _ext_isrs[PLIC_NUM_INTERRUPTS]; - -/* NULL interrupt handler */ -void null_isr(int num) -{ - (void) num; -} /** - * @brief Initialize the CPU, set IRQ priorities, clocks + * @brief Initialize the CPU, set IRQ priorities, clocks, peripheral */ void cpu_init(void) { - volatile uint64_t *mtimecmp = - (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIMECMP); + /* Initialize IRQs */ + irq_init(); - /* Setup trap handler function */ - write_csr(mtvec, &trap_entry); + /* Initialize clock */ + clock_init(); - /* Enable FPU if present */ - if (read_csr(misa) & (1 << ('F' - 'A'))) { - write_csr(mstatus, MSTATUS_FS); /* allow FPU instructions without trapping */ - write_csr(fcsr, 0); /* initialize rounding mode, undefined at reset */ - } + /* Initialize newlib-nano library stubs */ + nanostubs_init(); - /* Clear all interrupt enables */ - write_csr(mie, 0); - - /* Initial PLIC external interrupt controller */ - PLIC_init(PLIC_CTRL_ADDR, PLIC_NUM_INTERRUPTS, PLIC_NUM_PRIORITIES); - - /* Initialize ISR function list */ - for (int i = 0; i < PLIC_NUM_INTERRUPTS; i++) { - _ext_isrs[i] = null_isr; - } - - /* Set mtimecmp to largest value to avoid spurious timer interrupts */ - *mtimecmp = 0xFFFFFFFFFFFFFFFF; - - /* Enable SW, timer and external interrupts */ - set_csr(mie, MIP_MSIP); - set_csr(mie, MIP_MTIP); - set_csr(mie, MIP_MEIP); - - /* Set default state of mstatus */ - set_csr(mstatus, MSTATUS_DEFAULT); - - /* trigger static peripheral initialization */ + /* Initialize static peripheral */ periph_init(); } -/** - * @brief Enable all maskable interrupts - */ -unsigned int irq_enable(void) -{ - /* Enable all interrupts */ - set_csr(mstatus, MSTATUS_MIE); - return read_csr(mstatus); -} - -/** - * @brief Disable all maskable interrupts - */ -unsigned int irq_disable(void) -{ - unsigned int state = read_csr(mstatus); - - /* Disable all interrupts */ - clear_csr(mstatus, MSTATUS_MIE); - return state; -} - -/** - * @brief Restore the state of the IRQ flags - */ -void irq_restore(unsigned int state) -{ - /* Restore all interrupts to given state */ - write_csr(mstatus, state); -} - -/** - * @brief See if the current context is inside an ISR - */ -int irq_is_in(void) -{ - return __in_isr; -} - -/** - * @brief Set External ISR callback - */ -void set_external_isr_cb(int intNum, external_isr_ptr_t cbFunc) -{ - if ((intNum > 0) && (intNum < PLIC_NUM_INTERRUPTS)) { - _ext_isrs[intNum] = cbFunc; - } -} - -/** - * @brief External interrupt handler - */ -void external_isr(void) -{ - plic_source intNum = PLIC_claim_interrupt(); - - if ((intNum > 0) && (intNum < PLIC_NUM_INTERRUPTS)) { - _ext_isrs[intNum]((uint32_t) intNum); - } - - PLIC_complete_interrupt(intNum); -} - -/** - * @brief Global trap and interrupt handler - */ -void handle_trap(unsigned int mcause, unsigned int mepc, unsigned int mtval) -{ -#ifndef DEVELHELP - (void) mepc; - (void) mtval; -#endif - /* Tell RIOT to set sched_context_switch_request instead of - * calling thread_yield(). */ - __in_isr = 1; - - /* Check for INT or TRAP */ - if ((mcause & MCAUSE_INT) == MCAUSE_INT) { - /* Cause is an interrupt - determine type */ - switch (mcause & MCAUSE_CAUSE) { - case IRQ_M_SOFT: - /* Handle software interrupt - flag for context switch */ - sched_context_switch_request = 1; - CLINT_REG(0) = 0; - break; - -#ifdef MODULE_PERIPH_TIMER - case IRQ_M_TIMER: - /* Handle timer interrupt */ - timer_isr(); - break; -#endif - case IRQ_M_EXT: - /* Handle external interrupt */ - external_isr(); - break; - - default: - /* Unknown interrupt */ - core_panic(PANIC_GENERAL_ERROR, "Unhandled interrupt"); - break; - } - } - else { -#ifdef DEVELHELP - printf("Unhandled trap:\n"); - printf(" mcause: 0x%08x\n", mcause); - printf(" mepc: 0x%08x\n", mepc); - printf(" mtval: 0x%08x\n", mtval); -#endif - /* Unknown trap */ - core_panic(PANIC_GENERAL_ERROR, "Unhandled trap"); - } - - /* Check if context change was requested */ - if (sched_context_switch_request) { - sched_run(); - } - - /* ISR done - no more changes to thread states */ - __in_isr = 0; -} - -void panic_arch(void) -{ -#ifdef DEVELHELP - while (1) {} -#endif -} - -/** - * @brief Noticeable marker marking the beginning of a stack segment - * - * This marker is used e.g. by *thread_start_threading* to identify the - * stacks beginning. +/* + * By default the SPI FFMT initialized as: + * cmd_en = 1 + * addr_len = 3 + * cmd_code = 3 + * all other fields = 0 */ -#define STACK_MARKER (0x77777777) -/** - * @brief Initialize a thread's stack - * - * RIOT saves the tasks registers on the stack, not in the task control - * block. thread_stack_init() is responsible for allocating space for - * the registers on the stack and adjusting the stack pointer to account for - * the saved registers. - * - * The stack_start parameter is the bottom of the stack (low address). The - * return value is the top of stack: stack_start + stack_size - space reserved - * for thread context save - space reserved to align stack. - * - * thread_stack_init is called for each thread. - * - * RISCV ABI is here: https://github.com/riscv/riscv-elf-psabi-doc - * From ABI: - * The stack grows downwards and the stack pointer shall be aligned to a - * 128-bit boundary upon procedure entry, except for the RV32E ABI, where it - * need only be aligned to 32 bits. In the standard ABI, the stack pointer - * must remain aligned throughout procedure execution. Non-standard ABI code - * must realign the stack pointer prior to invoking standard ABI procedures. - * The operating system must realign the stack pointer prior to invoking a - * signal handler; hence, POSIX signal handlers need not realign the stack - * pointer. In systems that service interrupts using the interruptee's stack, - * the interrupt service routine must realign the stack pointer if linked - * with any code that uses a non-standard stack-alignment discipline, but - * need not realign the stack pointer if all code adheres to the standard ABI. - * - * @param[in] task_func pointer to the thread's code - * @param[in] arg argument to task_func - * @param[in] stack_start pointer to the start address of the thread - * @param[in] stack_size the maximum size of the stack - * - * @return pointer to the new top of the stack (128bit aligned) - * - */ -char *thread_stack_init(thread_task_func_t task_func, - void *arg, - void *stack_start, - int stack_size) +__attribute__ ((section (".ramfunc"))) +void init_flash(void) { - struct context_switch_frame *sf; - uint32_t *stk_top; - - /* calculate the top of the stack */ - stk_top = (uint32_t *)((uintptr_t)stack_start + stack_size); - - /* Put a marker at the top of the stack. This is used by - * thread_stack_print to determine where to stop dumping the - * stack. + /* Update the QSPI interface to adjust to the CPU speed + * This function needs to execute from the RAM + * when the QSPI interface is being reconfigured because the flash + * can't be accessed during this time */ - stk_top--; - *stk_top = STACK_MARKER; - - /* per ABI align stack pointer to 16 byte boundary. */ - stk_top = (uint32_t *)(((uint32_t)stk_top) & ~((uint32_t)0xf)); - - /* reserve space for the stack frame. */ - stk_top = (uint32_t *)((uint8_t *) stk_top - sizeof(*sf)); - - /* populate the stack frame with default values for starting the thread. */ - sf = (struct context_switch_frame *) stk_top; - - /* Clear stack frame */ - memset(sf, 0, sizeof(*sf)); - - /* set initial reg values */ - sf->pc = (uint32_t) task_func; - sf->a0 = (uint32_t) arg; - - /* if the thread exits go to sched_task_exit() */ - sf->ra = (uint32_t) sched_task_exit; - - return (char *) stk_top; -} - -void thread_print_stack(void) -{ - int count = 0; - uint32_t *sp = (uint32_t *) ((sched_active_thread) ? sched_active_thread->sp : NULL); - - if (sp == NULL) { - return; - } - - printf("printing the current stack of thread %" PRIkernel_pid "\n", - thread_getpid()); - -#ifdef DEVELHELP - printf("thread name: %s\n", sched_active_thread->name); - printf("stack start: 0x%08x\n", (unsigned int)(sched_active_thread->stack_start)); - printf("stack end : 0x%08x\n", (unsigned int)(sched_active_thread->stack_start + sched_active_thread->stack_size)); -#endif - - printf(" address: data:\n"); - - do { - printf(" 0x%08x: 0x%08x\n", (unsigned int) sp, (unsigned int) *sp); - sp++; - count++; - } while (*sp != STACK_MARKER); - - printf("current stack size: %i words\n", count); -} - -int thread_isr_stack_usage(void) -{ - return 0; -} - -void *thread_isr_stack_pointer(void) -{ - return NULL; -} - -void *thread_isr_stack_start(void) -{ - return NULL; -} - -/** - * @brief Call context switching at thread exit - * - * This is called is two situations: 1) after the initial main and idle threads - * have been created and 2) when a thread exits. - * - */ -void cpu_switch_context_exit(void) -{ - /* enable interrupts */ - irq_enable(); - - /* force a context switch to another thread */ - thread_yield_higher(); - UNREACHABLE(); -} - -void thread_yield_higher(void) -{ - /* Use SW intr to schedule context switch */ - CLINT_REG(CLINT_MSIP) = 1; - - /* Latency of SW intr can be 4-7 cycles; wait for the SW intr */ - __asm__ volatile ("wfi"); -} - -/** - * @brief Print heap statistics - */ -void heap_stats(void) -{ - extern char _heap_start; /* defined in linker script */ - extern char _heap_end; /* defined in linker script */ - long int heap_size = &_heap_end - &_heap_start; - struct mallinfo minfo = mallinfo(); - printf("heap: %ld (used %u, free %ld) [bytes]\n", - heap_size, minfo.uordblks, heap_size - minfo.uordblks); + /* Disable SPI flash mode */ + SPI0_REG(SPI_REG_FCTRL) &= ~SPI_FCTRL_EN; + + /* Enable QPI mode by sending command to flash */ + SPI0_REG(SPI_REG_TXFIFO) = 0x35; + + /* begin{code-style-ignore} */ + SPI0_REG(SPI_REG_FFMT) = /* setup "Fast Read Quad I/O (QPI mode)" */ + SPI_INSN_CMD_EN | /* Enable memory-mapped flash */ + SPI_INSN_ADDR_LEN(3) | /* 25LP03D read commands have 3 address bytes */ + SPI_INSN_PAD_CNT(6) | /* 25LP03D Table 6.11 Read Dummy Cycles = 6 */ + SPI_INSN_CMD_PROTO(SPI_PROTO_Q) | /* 25LP03D Table 8.1 "Instruction */ + SPI_INSN_ADDR_PROTO(SPI_PROTO_Q) | /* Set" shows mode for cmd, addr, and */ + SPI_INSN_DATA_PROTO(SPI_PROTO_Q) | /* data protocol for given instruction */ + SPI_INSN_CMD_CODE(0xEB) | /* Set the instruction to "Fast Read Quad I/O" */ + SPI_INSN_PAD_CODE(0x00); /* Dummy cycle sends 0 value bits */ + /* end{code-style-ignore} */ + + /* Re-enable SPI flash mode */ + SPI0_REG(SPI_REG_FCTRL) |= SPI_FCTRL_EN; + + /* Adjust the SPI clk divider for to boost flash speed */ + // SPI0_REG(SPI_REG_SCKDIV) = SCKDIV; } diff --git a/cpu/fe310/include/cpu.h b/cpu/fe310/include/cpu.h index cffe0084cbb9..4c075eae53ca 100644 --- a/cpu/fe310/include/cpu.h +++ b/cpu/fe310/include/cpu.h @@ -25,8 +25,6 @@ #ifndef CPU_H #define CPU_H -#include - #include "thread.h" #include "vendor/platform.h" @@ -41,6 +39,26 @@ extern "C" { */ void cpu_init(void); +/** + * @brief Initialization of the clock + */ +void clock_init(void); + +/** + * @brief Initialization of interrupts + */ +void irq_init(void); + +/** + * @brief External ISR callback + */ +typedef void (*external_isr_ptr_t)(int intNum); + +/** + * @brief Set External ISR callback + */ +void set_external_isr_cb(int intNum, external_isr_ptr_t cbFunc); + /** * @brief Print the last instruction's address * diff --git a/cpu/fe310/include/periph_cpu.h b/cpu/fe310/include/periph_cpu.h index 9e87c91b256f..165606f3fea5 100644 --- a/cpu/fe310/include/periph_cpu.h +++ b/cpu/fe310/include/periph_cpu.h @@ -50,6 +50,11 @@ typedef uint8_t gpio_t; */ #define GPIO_PIN(x, y) (x | y) +/** + * @brief GPIO interrupt priority + */ +#define GPIO_INTR_PRIORITY (3) + /** * @brief Structure for UART configuration data */ @@ -70,21 +75,6 @@ typedef struct { */ #define PERIPH_TIMER_PROVIDES_SET -/** - * @brief Timer ISR - */ -void timer_isr(void); - -/** - * @brief External ISR callback - */ -typedef void (*external_isr_ptr_t)(int intNum); - -/** - * @brief Set External ISR callback - */ -void set_external_isr_cb(int intNum, external_isr_ptr_t cbFunc); - #ifdef __cplusplus } #endif diff --git a/cpu/fe310/irq_arch.c b/cpu/fe310/irq_arch.c new file mode 100644 index 000000000000..965651b99463 --- /dev/null +++ b/cpu/fe310/irq_arch.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2017, 2019 Ken Rabold, JP Bonn + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_fe310 + * @{ + * + * @file cpu.c + * @brief Implementation of the CPU IRQ management for SiFive FE310 + * + * @author Ken Rabold + * @} + */ + +#include +#include +#include + +#include "cpu.h" +#include "irq.h" +#include "panic.h" +#include "sched.h" + +#include "vendor/encoding.h" +#include "vendor/platform.h" +#include "vendor/plic_driver.h" + +/* Default state of mstatus register */ +#define MSTATUS_DEFAULT (MSTATUS_MPP | MSTATUS_MPIE) + +volatile int fe310_in_isr = 0; + +/* PLIC external ISR function list */ +static external_isr_ptr_t _ext_isrs[PLIC_NUM_INTERRUPTS]; + +/** + * @brief ISR trap vector + */ +void trap_entry(void); + +/** + * @brief Timer ISR + */ +void timer_isr(void); + +void irq_init(void) +{ + volatile uint64_t *mtimecmp = + (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIMECMP); + + /* Setup trap handler function */ + write_csr(mtvec, &trap_entry); + + /* Enable FPU if present */ + if (read_csr(misa) & (1 << ('F' - 'A'))) { + write_csr(mstatus, MSTATUS_FS); /* allow FPU instructions without trapping */ + write_csr(fcsr, 0); /* initialize rounding mode, undefined at reset */ + } + + /* Clear all interrupt enables */ + write_csr(mie, 0); + + /* Initial PLIC external interrupt controller */ + PLIC_init(PLIC_CTRL_ADDR, PLIC_NUM_INTERRUPTS, PLIC_NUM_PRIORITIES); + + /* Set mtimecmp to largest value to avoid spurious timer interrupts */ + *mtimecmp = 0xFFFFFFFFFFFFFFFF; + + /* Enable SW, timer and external interrupts */ + set_csr(mie, MIP_MSIP); + set_csr(mie, MIP_MTIP); + set_csr(mie, MIP_MEIP); + + /* Set default state of mstatus */ + set_csr(mstatus, MSTATUS_DEFAULT); +} + +/** + * @brief Enable all maskable interrupts + */ +unsigned int irq_enable(void) +{ + /* Enable all interrupts */ + set_csr(mstatus, MSTATUS_MIE); + return read_csr(mstatus); +} + +/** + * @brief Disable all maskable interrupts + */ +unsigned int irq_disable(void) +{ + unsigned int state = read_csr(mstatus); + + /* Disable all interrupts */ + clear_csr(mstatus, MSTATUS_MIE); + return state; +} + +/** + * @brief Restore the state of the IRQ flags + */ +void irq_restore(unsigned int state) +{ + /* Restore all interrupts to given state */ + write_csr(mstatus, state); +} + +/** + * @brief See if the current context is inside an ISR + */ +int irq_is_in(void) +{ + return fe310_in_isr; +} + +/** + * @brief Set External ISR callback + */ +void set_external_isr_cb(int intNum, external_isr_ptr_t cbFunc) +{ + assert((intNum > 0) && (intNum < PLIC_NUM_INTERRUPTS)); + + _ext_isrs[intNum] = cbFunc; +} + +/** + * @brief External interrupt handler + */ +void external_isr(void) +{ + uint32_t intNum = (uint32_t)PLIC_claim_interrupt(); + + if ((intNum > 0) && (intNum < PLIC_NUM_INTERRUPTS) && (_ext_isrs[intNum] != NULL)) { + _ext_isrs[intNum](intNum); + } + + PLIC_complete_interrupt(intNum); +} + +/** + * @brief Global trap and interrupt handler + */ +void handle_trap(unsigned int mcause, unsigned int mepc, unsigned int mtval) +{ +#ifndef DEVELHELP + (void) mepc; + (void) mtval; +#endif + /* Tell RIOT to set sched_context_switch_request instead of + * calling thread_yield(). */ + fe310_in_isr = 1; + + /* Check for INT or TRAP */ + if ((mcause & MCAUSE_INT) == MCAUSE_INT) { + /* Cause is an interrupt - determine type */ + switch (mcause & MCAUSE_CAUSE) { + case IRQ_M_SOFT: + /* Handle software interrupt - flag for context switch */ + sched_context_switch_request = 1; + CLINT_REG(0) = 0; + break; + +#ifdef MODULE_PERIPH_TIMER + case IRQ_M_TIMER: + /* Handle timer interrupt */ + timer_isr(); + break; +#endif + case IRQ_M_EXT: + /* Handle external interrupt */ + external_isr(); + break; + + default: + /* Unknown interrupt */ + core_panic(PANIC_GENERAL_ERROR, "Unhandled interrupt"); + break; + } + } + else { +#ifdef DEVELHELP + printf("Unhandled trap:\n"); + printf(" mcause: 0x%08x\n", mcause); + printf(" mepc: 0x%08x\n", mepc); + printf(" mtval: 0x%08x\n", mtval); +#endif + /* Unknown trap */ + core_panic(PANIC_GENERAL_ERROR, "Unhandled trap"); + } + + /* Check if context change was requested */ + if (sched_context_switch_request) { + sched_run(); + } + + /* ISR done - no more changes to thread states */ + fe310_in_isr = 0; +} diff --git a/cpu/fe310/panic.c b/cpu/fe310/panic.c new file mode 100644 index 000000000000..45f76ee80e9d --- /dev/null +++ b/cpu/fe310/panic.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017, 2019 Ken Rabold, JP Bonn + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_fe310 + * @{ + * + * @file cpu.c + * @brief Implementation of the CPU panic for SiFive FE310 + * + * @author Ken Rabold + * @} + */ + +#include "panic.h" + +void panic_arch(void) +{ +#ifdef DEVELHELP + while (1) {} +#endif +} diff --git a/cpu/fe310/thread_arch.c b/cpu/fe310/thread_arch.c new file mode 100644 index 000000000000..3a54e1db1f40 --- /dev/null +++ b/cpu/fe310/thread_arch.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2017, 2019 Ken Rabold, JP Bonn + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_fe310 + * @{ + * + * @file cpu.c + * @brief Implementation of the CPU thread management for SiFive FE310 + * + * @author Ken Rabold + * @} + */ + +#include +#include +#include + +#include "irq.h" +#include "thread.h" +#include "sched.h" +#include "context_frame.h" + +#include "vendor/platform.h" + +/** + * @brief Noticeable marker marking the beginning of a stack segment + * + * This marker is used e.g. by *thread_start_threading* to identify the + * stacks beginning. + */ +#define STACK_MARKER (0x77777777) + +/** + * @brief Initialize a thread's stack + * + * RIOT saves the tasks registers on the stack, not in the task control + * block. thread_stack_init() is responsible for allocating space for + * the registers on the stack and adjusting the stack pointer to account for + * the saved registers. + * + * The stack_start parameter is the bottom of the stack (low address). The + * return value is the top of stack: stack_start + stack_size - space reserved + * for thread context save - space reserved to align stack. + * + * thread_stack_init is called for each thread. + * + * RISCV ABI is here: https://github.com/riscv/riscv-elf-psabi-doc + * From ABI: + * The stack grows downwards and the stack pointer shall be aligned to a + * 128-bit boundary upon procedure entry, except for the RV32E ABI, where it + * need only be aligned to 32 bits. In the standard ABI, the stack pointer + * must remain aligned throughout procedure execution. Non-standard ABI code + * must realign the stack pointer prior to invoking standard ABI procedures. + * The operating system must realign the stack pointer prior to invoking a + * signal handler; hence, POSIX signal handlers need not realign the stack + * pointer. In systems that service interrupts using the interruptee's stack, + * the interrupt service routine must realign the stack pointer if linked + * with any code that uses a non-standard stack-alignment discipline, but + * need not realign the stack pointer if all code adheres to the standard ABI. + * + * @param[in] task_func pointer to the thread's code + * @param[in] arg argument to task_func + * @param[in] stack_start pointer to the start address of the thread + * @param[in] stack_size the maximum size of the stack + * + * @return pointer to the new top of the stack (128bit aligned) + * + */ +char *thread_stack_init(thread_task_func_t task_func, + void *arg, + void *stack_start, + int stack_size) +{ + struct context_switch_frame *sf; + uint32_t *stk_top; + + /* calculate the top of the stack */ + stk_top = (uint32_t *)((uintptr_t)stack_start + stack_size); + + /* Put a marker at the top of the stack. This is used by + * thread_stack_print to determine where to stop dumping the + * stack. + */ + stk_top--; + *stk_top = STACK_MARKER; + + /* per ABI align stack pointer to 16 byte boundary. */ + stk_top = (uint32_t *)(((uint32_t)stk_top) & ~((uint32_t)0xf)); + + /* reserve space for the stack frame. */ + stk_top = (uint32_t *)((uint8_t *) stk_top - sizeof(*sf)); + + /* populate the stack frame with default values for starting the thread. */ + sf = (struct context_switch_frame *) stk_top; + + /* Clear stack frame */ + memset(sf, 0, sizeof(*sf)); + + /* set initial reg values */ + sf->pc = (uint32_t) task_func; + sf->a0 = (uint32_t) arg; + + /* if the thread exits go to sched_task_exit() */ + sf->ra = (uint32_t) sched_task_exit; + + return (char *) stk_top; +} + +void thread_print_stack(void) +{ + int count = 0; + uint32_t *sp = (uint32_t *) ((sched_active_thread) ? sched_active_thread->sp : NULL); + + if (sp == NULL) { + return; + } + + printf("printing the current stack of thread %" PRIkernel_pid "\n", + thread_getpid()); + +#ifdef DEVELHELP + printf("thread name: %s\n", sched_active_thread->name); + printf("stack start: 0x%08x\n", (unsigned int)(sched_active_thread->stack_start)); + printf("stack end : 0x%08x\n", (unsigned int)(sched_active_thread->stack_start + sched_active_thread->stack_size)); +#endif + + printf(" address: data:\n"); + + do { + printf(" 0x%08x: 0x%08x\n", (unsigned int) sp, (unsigned int) *sp); + sp++; + count++; + } while (*sp != STACK_MARKER); + + printf("current stack size: %i words\n", count); +} + +int thread_isr_stack_usage(void) +{ + return 0; +} + +void *thread_isr_stack_pointer(void) +{ + return NULL; +} + +void *thread_isr_stack_start(void) +{ + return NULL; +} + +/** + * @brief Call context switching at thread exit + * + * This is called is two situations: 1) after the initial main and idle threads + * have been created and 2) when a thread exits. + * + */ +void cpu_switch_context_exit(void) +{ + /* enable interrupts */ + irq_enable(); + + /* force a context switch to another thread */ + thread_yield_higher(); + UNREACHABLE(); +} + +void thread_yield_higher(void) +{ + /* Use SW intr to schedule context switch */ + CLINT_REG(CLINT_MSIP) = 1; + + /* Latency of SW intr can be 4-7 cycles; wait for the SW intr */ + __asm__ volatile ("wfi"); +} + +/** + * @brief Print heap statistics + */ +void heap_stats(void) +{ + extern char _heap_start; /* defined in linker script */ + extern char _heap_end; /* defined in linker script */ + + long int heap_size = &_heap_end - &_heap_start; + struct mallinfo minfo = mallinfo(); + printf("heap: %ld (used %u, free %ld) [bytes]\n", + heap_size, minfo.uordblks, heap_size - minfo.uordblks); +} From fc911bf6c5decb310fedac62a8cb02a229941124 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Wed, 11 Dec 2019 19:44:56 +0100 Subject: [PATCH 3/8] cpu/fe310: rework clock initialization --- cpu/fe310/clock.c | 102 ++++++++++++++++++++++++++++++---------- cpu/fe310/include/cpu.h | 7 +++ 2 files changed, 84 insertions(+), 25 deletions(-) diff --git a/cpu/fe310/clock.c b/cpu/fe310/clock.c index 6e0548a86bdf..bca3449d8920 100644 --- a/cpu/fe310/clock.c +++ b/cpu/fe310/clock.c @@ -20,33 +20,85 @@ #include "cpu.h" #include "periph_conf.h" -#include "vendor/encoding.h" -#include "vendor/platform.h" #include "vendor/prci_driver.h" +#if !(USE_CLOCK_HFXOSC || USE_CLOCK_HFXOSC_PLL) +static uint32_t _cpu_frequency = 0; +#endif + void clock_init(void) { - /* In case we are executing from QSPI, (which is quite likely) we need to - * set the QSPI clock divider appropriately before boosting the clock - * frequency. PRCI_set_hfrosctrim_for_f_cpu() tries multiple clocks - * so choose a safe value that should work for all frequencies. - */ - SPI0_REG(SPI_REG_SCKDIV) = SCKDIV_SAFE; - - /* Note: The range is limited to ~100MHz and depends on PLL settings */ - PRCI_set_hfrosctrim_for_f_cpu(CPU_DESIRED_FREQ, PRCI_FREQ_UNDERSHOOT); - - /* begin{code-style-ignore} */ - SPI0_REG(SPI_REG_FFMT) = /* setup "Fast Read Dual I/O" */ - SPI_INSN_CMD_EN | /* Enable memory-mapped flash */ - SPI_INSN_ADDR_LEN(3) | /* 25LP03D read commands have 3 address bytes */ - SPI_INSN_PAD_CNT(4) | /* 25LP03D Table 6.11 Read Dummy Cycles = 4 */ - SPI_INSN_CMD_PROTO(SPI_PROTO_S) | /* 25LP03D Table 8.1 "Instruction */ - SPI_INSN_ADDR_PROTO(SPI_PROTO_D) | /* Set" shows mode for cmd, addr, and */ - SPI_INSN_DATA_PROTO(SPI_PROTO_D) | /* data protocol for given instruction */ - SPI_INSN_CMD_CODE(0xBB) | /* Set the instruction to "Fast Read Dual I/O" */ - SPI_INSN_PAD_CODE(0x00); /* Dummy cycle sends 0 value bits */ - /* end{code-style-ignore} */ - - SPI0_REG(SPI_REG_SCKDIV) = SCKDIV; + /* Ensure that we aren't running off the PLL before we mess with it. */ + if (PRCI_REG(PRCI_PLLCFG) & PLL_SEL(1)) { + /* Make sure the HFROSC is running at its default setting */ + /* It is OK to change this even if we are running off of it.*/ + PRCI_REG(PRCI_HFROSCCFG) = (ROSC_DIV(4) | ROSC_TRIM(16) | ROSC_EN(1)); + + /* Wait for HFROSC to be ready */ + while ((PRCI_REG(PRCI_HFROSCCFG) & ROSC_RDY(1)) == 0); + + /* Don't use PLL clock source */ + PRCI_REG(PRCI_PLLCFG) &= ~PLL_SEL(PLL_SEL_PLL); + } + +#if USE_CLOCK_HFXOSC || USE_CLOCK_HFXOSC_PLL + /* Ensure HFXOSC is enabled */ + PRCI_REG(PRCI_HFXOSCCFG) = XOSC_EN(1); + + /* Wait for HFXOSC to become ready */ + while ((PRCI_REG(PRCI_HFXOSCCFG) & XOSC_RDY(1)) == 0); + + /* Select HFXOSC as reference frequency and bypass PLL */ + PRCI_REG(PRCI_PLLCFG) = PLL_REFSEL(PLL_REFSEL_HFXOSC) | PLL_BYPASS(1); + +#if USE_CLOCK_HFXOSC_PLL + /* Divide final output frequency by 1 */ + PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0)); + + /* Configure PLL */ + PRCI_REG(PRCI_PLLCFG) |= PLL_R(CLOCK_PLL_R) | PLL_F(CLOCK_PLL_F) | PLL_Q(CLOCK_PLL_Q); + + /* Disable PLL Bypass */ + PRCI_REG(PRCI_PLLCFG) &= ~PLL_BYPASS(1); + + /* Now it is safe to check for PLL Lock */ + while ((PRCI_REG(PRCI_PLLCFG) & PLL_LOCK(1)) == 0); +#endif + + /* Switch over to PLL Clock source */ + PRCI_REG(PRCI_PLLCFG) |= PLL_SEL(PLL_SEL_PLL); + + /* Turn off the HFROSC */ + PRCI_REG(PRCI_HFROSCCFG) &= ~ROSC_EN(1); +#elif USE_CLOCK_HFROSC_PLL + PRCI_set_hfrosctrim_for_f_cpu(CLOCK_DESIRED_FREQUENCY, PRCI_FREQ_UNDERSHOOT); +#else /* Clock HFROSC */ + /* Disable Bypass */ + PRCI_REG(PRCI_PLLCFG) &= ~PLL_BYPASS(1); + + /* Configure trim and divider values of HFROSC */ + PRCI_REG(PRCI_HFROSCCFG) = (ROSC_DIV(CLOCK_HFROSC_DIV) | ROSC_TRIM(CLOCK_HFROSC_TRIM) | ROSC_EN(1)); + + /* Wait for HFROSC to be ready */ + while ((PRCI_REG(PRCI_HFROSCCFG) & ROSC_RDY(1)) == 0); + + /* Don't use PLL clock source */ + PRCI_REG(PRCI_PLLCFG) &= ~PLL_SEL(PLL_SEL_PLL); +#endif +} + +uint32_t cpu_freq(void) +{ +#if USE_CLOCK_HFXOSC || USE_CLOCK_HFXOSC_PLL + return CLOCK_CORECLOCK; +#else /* Clock frequency with HFROSC cannot be determined precisely from + settings */ + /* If not done already, estimate the CPU frequency */ + if (_cpu_frequency == 0) { + /* Ignore the first run (for icache reasons) */ + _cpu_frequency = PRCI_measure_mcycle_freq(3000, RTC_FREQ); + _cpu_frequency = PRCI_measure_mcycle_freq(3000, RTC_FREQ); + } + return _cpu_frequency; +#endif } diff --git a/cpu/fe310/include/cpu.h b/cpu/fe310/include/cpu.h index 4c075eae53ca..bd2992379633 100644 --- a/cpu/fe310/include/cpu.h +++ b/cpu/fe310/include/cpu.h @@ -44,6 +44,13 @@ void cpu_init(void); */ void clock_init(void); +/** + * @brief Get and eventually compute the current CPU core clock frequency + * + * @return the cpu core clock frequency in Hz + */ +uint32_t cpu_freq(void); + /** * @brief Initialization of interrupts */ From 97ef5d53de2e53bd7a413c8514e1d07263153918 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Tue, 10 Dec 2019 11:46:35 +0100 Subject: [PATCH 4/8] boards/hifive1*: rework clock configuration --- boards/hifive1/include/periph_conf.h | 49 +++++++++++++++++++++++++-- boards/hifive1b/include/periph_conf.h | 49 +++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/boards/hifive1/include/periph_conf.h b/boards/hifive1/include/periph_conf.h index 20a3d12706ab..cae44fcfdff3 100644 --- a/boards/hifive1/include/periph_conf.h +++ b/boards/hifive1/include/periph_conf.h @@ -30,8 +30,53 @@ extern "C" { * @name Core Clock configuration * @{ */ -/* As defined in boards/hifive1/board.c CPU_DESIRED_FREQ **/ -#define CLOCK_CORECLOCK (200000000ul) +#define USE_CLOCK_HFXOSC_PLL (1) +#define USE_CLOCK_HFXOSC (0) +#define USE_CLOCK_HFROSC_PLL (0) + +#if USE_CLOCK_HFROSC_PLL && (USE_CLOCK_HFXOSC_PLL || USE_CLOCK_HFXOSC) +#error "Cannot use HFROSC_PLL with HFXOSC based configurations" +#endif + +#if USE_CLOCK_HFXOSC_PLL && USE_CLOCK_HFXOSC +#error "Cannot use HFXOSC with HFXOSC_PLL" +#endif + +#if USE_CLOCK_HFXOSC_PLL +#define CLOCK_PLL_R (1) /* Divide input clock by 2, mandatory with HFXOSC */ +#define CLOCK_PLL_F (39) /* Multiply REFR by 80, e.g 2 * (39 + 1) */ +#define CLOCK_PLL_Q (1) /* Divide VCO by 2, e.g 2^1 */ +#define CLOCK_PLL_INPUT_CLOCK (16000000UL) +#define CLOCK_PLL_REFR (CLOCK_PLL_INPUT_CLOCK / (CLOCK_PLL_R + 1)) +#define CLOCK_PLL_VCO (CLOCK_PLL_REFR * (2 * (CLOCK_PLL_F + 1))) +#define CLOCK_PLL_OUT (CLOCK_PLL_VCO / (1 << CLOCK_PLL_Q)) +#define CLOCK_CORECLOCK (CLOCK_PLL_OUT) /* 320000000Hz with the values used above */ + +/* Check PLL settings */ +#if CLOCK_PLL_REFR != 8000000 +#error "Only R=2 can be used when using HFXOSC" +#endif +#if (CLOCK_PLL_VCO < 384000000) || (CLOCK_PLL_VCO > 768000000) +#error "VCO frequency must be in the range [384MHz - 768MHz], check the CLOCK_PLL_F value" +#endif +#if (CLOCK_PLL_OUT < 48000000) || (CLOCK_PLL_OUT > 384000000) +#error "PLL output frequency must be in the range [48MHz - 384MHz], check the CLOCK_PLL_Q value" +#endif + +#elif USE_CLOCK_HFXOSC +#define CLOCK_CORECLOCK (16000000UL) + +/* + When using HFROSC input clock, the core clock cannot be computed from settings, + call cpu_freq() to get the configured CPU frequency. +*/ +#elif USE_CLOCK_HFROSC_PLL +#define CLOCK_DESIRED_FREQUENCY (320000000UL) + +#else +#define CLOCK_HFROSC_TRIM (6) /* ~72000000Hz input freq */ +#define CLOCK_HFROSC_DIV (1) /* Divide by 2 */ +#endif /** @} */ /** diff --git a/boards/hifive1b/include/periph_conf.h b/boards/hifive1b/include/periph_conf.h index 6379869e7652..749f450bbed8 100644 --- a/boards/hifive1b/include/periph_conf.h +++ b/boards/hifive1b/include/periph_conf.h @@ -31,8 +31,53 @@ extern "C" { * @name Core Clock configuration * @{ */ -/* As defined in boards/hifive1/board.c CPU_DESIRED_FREQ **/ -#define CLOCK_CORECLOCK (200000000ul) +#define USE_CLOCK_HFXOSC_PLL (1) +#define USE_CLOCK_HFXOSC (0) +#define USE_CLOCK_HFROSC_PLL (0) + +#if USE_CLOCK_HFROSC_PLL && (USE_CLOCK_HFXOSC_PLL || USE_CLOCK_HFXOSC) +#error "Cannot use HFROSC_PLL with HFXOSC based configurations" +#endif + +#if USE_CLOCK_HFXOSC_PLL && USE_CLOCK_HFXOSC +#error "Cannot use HFXOSC with HFXOSC_PLL" +#endif + +#if USE_CLOCK_HFXOSC_PLL +#define CLOCK_PLL_R (1) /* Divide input clock by 2, mandatory with HFXOSC */ +#define CLOCK_PLL_F (39) /* Multiply REFR by 80, e.g 2 * (39 + 1) */ +#define CLOCK_PLL_Q (1) /* Divide VCO by 2, e.g 2^1 */ +#define CLOCK_PLL_INPUT_CLOCK (16000000UL) +#define CLOCK_PLL_REFR (CLOCK_PLL_INPUT_CLOCK / (CLOCK_PLL_R + 1)) +#define CLOCK_PLL_VCO (CLOCK_PLL_REFR * (2 * (CLOCK_PLL_F + 1))) +#define CLOCK_PLL_OUT (CLOCK_PLL_VCO / (1 << CLOCK_PLL_Q)) +#define CLOCK_CORECLOCK (CLOCK_PLL_OUT) /* 320000000Hz with the values used above */ + +/* Check PLL settings */ +#if CLOCK_PLL_REFR != 8000000 +#error "Only R=2 can be used when using HFXOSC" +#endif +#if (CLOCK_PLL_VCO < 384000000) || (CLOCK_PLL_VCO > 768000000) +#error "VCO frequency must be in the range [384MHz - 768MHz], check the CLOCK_PLL_F value" +#endif +#if (CLOCK_PLL_OUT < 48000000) || (CLOCK_PLL_OUT > 384000000) +#error "PLL output frequency must be in the range [48MHz - 384MHz], check the CLOCK_PLL_Q value" +#endif + +#elif USE_CLOCK_HFXOSC +#define CLOCK_CORECLOCK (16000000UL) + +/* + When using HFROSC input clock, the core clock cannot be computed from settings, + call cpu_freq() to get the configured CPU frequency. +*/ +#elif USE_CLOCK_HFROSC_PLL +#define CLOCK_DESIRED_FREQUENCY (320000000UL) + +#else +#define CLOCK_HFROSC_TRIM (6) /* ~72000000Hz input freq */ +#define CLOCK_HFROSC_DIV (1) /* Divide by 2 */ +#endif /** @} */ /** From 473f1d7147a7279f4638740485819da6ce30e180 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Tue, 10 Dec 2019 11:29:53 +0100 Subject: [PATCH 5/8] boards/hifive1*: cleanup board initialization --- boards/hifive1/board.c | 93 -------------------------------- boards/hifive1b/board.c | 115 ---------------------------------------- 2 files changed, 208 deletions(-) diff --git a/boards/hifive1/board.c b/boards/hifive1/board.c index 68352ed51fe8..9da9afe15082 100644 --- a/boards/hifive1/board.c +++ b/boards/hifive1/board.c @@ -18,104 +18,14 @@ * @} */ -#include -#include - #include "cpu.h" #include "board.h" #include "periph/gpio.h" -#include "vendor/encoding.h" -#include "vendor/platform.h" -#include "vendor/prci_driver.h" - -/* - * Configure the memory mapped flash for faster throughput - * to minimize interrupt latency on an I-Cache miss and refill - * from flash. Alternatively (and faster) the interrupt - * routine could be put in SRAM. The linker script supports - * code in SRAM using the ".hotcode" section. - - * The flash chip on the HiFive1 is the ISSI 25LP128 - * http://www.issi.com/WW/pdf/25LP128.pdf - * The maximum frequency it can run at is 133MHz in - * "Fast Read Dual I/O" mode. - * Note the updated data sheet: - * https://static.dev.sifive.com/SiFive-FE310-G000-datasheet-v1.0.4.pdf - * states "Address and write data using DQ[3] for transmission will not - * function properly." This rules out QPI for the XIP memory mapped flash. - * #define MAX_FLASH_FREQ 133000000 - * On forum SiFive says "safe" operation would be 40MHz. 50MHz seems to work - * fine. - */ -#define MAX_FLASH_FREQ 50000000 - -/* - * CPU max is 320MHz+ according to datasheet but - * the relationship between cpu clock and spi clock is determined - * by SCKDIV. Given we're trying to achieve maximum I-cache refill - * for the flash we let MAX_FLASH_FREQ dictate the CPU clock. - */ -#define CPU_DESIRED_FREQ 200000000 - -/* - * The relationship between the input clock and SCK is given - * by the following formula (Fin is processor/tile-link clock): - * Fsck = Fin/(2(div + 1)) - * FYI - For 320MHZ it seems to be tolerating a faster SPI clock (56MHz) - */ -#define SCKDIV ((CPU_DESIRED_FREQ - 1) / (MAX_FLASH_FREQ * 2)) - -/* This should work for any reasonable cpu clock value. */ -#define SCKDIV_SAFE 3 - -/* - * By default the SPI initialized as: - * https://github.com/sifive/sifive-blocks/blob/master/src/main/scala/devices/spi/SPIFlash.scala - * insn.cmd.en := Bool(true) - * insn.cmd.code := Bits(0x03) - * insn.cmd.proto := SPIProtocol.Single - * insn.addr.len := UInt(3) - * insn.addr.proto := SPIProtocol.Single - * insn.pad.cnt := UInt(0) - * insn.pad.code := Bits(0) - * insn.data.proto := SPIProtocol.Single - * - * 25LP128 appears to left in post-reset default state. Boot code - * does not modify it. We change the SPI configuration here. - */ - -void board_init_clock(void) -{ - /* In case we are executing from QSPI, (which is quite likely) we need to - * set the QSPI clock divider appropriately before boosting the clock - * frequency. PRCI_set_hfrosctrim_for_f_cpu() tries multiple clocks - * so choose a safe value that should work for all frequencies. - */ - SPI0_REG(SPI_REG_SCKDIV) = SCKDIV_SAFE; - - /* Note: The range is limited to ~100MHz and depends on PLL settings */ - PRCI_set_hfrosctrim_for_f_cpu(CPU_DESIRED_FREQ, PRCI_FREQ_UNDERSHOOT); - - /* begin{code-style-ignore} */ - SPI0_REG(SPI_REG_FFMT) = /* setup "Fast Read Dual I/O" 1-1-2 */ - SPI_INSN_CMD_EN | /* Enable memory-mapped flash */ - SPI_INSN_ADDR_LEN(3) | /* 25LP128 read commands have 3 address bytes */ - SPI_INSN_PAD_CNT(4) | /* 25LP128 Table 6.9 Read Dummy Cycles P4,P3=0,0 */ - SPI_INSN_CMD_PROTO(SPI_PROTO_S) | /* 25LP128 Table 8.1 "Instruction */ - SPI_INSN_ADDR_PROTO(SPI_PROTO_D) | /* Set" shows mode for cmd, addr, and */ - SPI_INSN_DATA_PROTO(SPI_PROTO_D) | /* data protocol for given instruction */ - SPI_INSN_CMD_CODE(0xbb) | /* Set the instruction to "Fast Read Dual I/O" */ - SPI_INSN_PAD_CODE(0x00); /* Dummy cycle sends 0 value bits */ - /* end{code-style-ignore} */ - - SPI0_REG(SPI_REG_SCKDIV) = SCKDIV; -} void board_init(void) { /* Initialize CPU and clocks */ cpu_init(); - board_init_clock(); /* Configure GPIOs for LEDs */ gpio_init(LED0_PIN, GPIO_OUT); @@ -126,7 +36,4 @@ void board_init(void) LED0_OFF; LED1_OFF; LED2_OFF; - - /* Initialize newlib-nano library stubs */ - nanostubs_init(); } diff --git a/boards/hifive1b/board.c b/boards/hifive1b/board.c index b16b00d7eaae..f119cb3b3585 100644 --- a/boards/hifive1b/board.c +++ b/boards/hifive1b/board.c @@ -18,126 +18,14 @@ * @} */ -#include -#include - #include "cpu.h" #include "board.h" #include "periph/gpio.h" -#include "vendor/encoding.h" -#include "vendor/platform.h" -#include "vendor/prci_driver.h" - -/* - * Configure the memory mapped flash for faster throughput - * to minimize interrupt latency on an I-Cache miss and refill - * from flash. Alternatively (and faster) the interrupt - * routine could be put in SRAM. - - * The flash chip on the HiFive1b is the ISSI 25LP03D - * http://www.issi.com/WW/pdf/25LP-WP032D.pdf - * The maximum frequency it can run at is 115MHz in - * "Fast Read Dual I/O" mode. - * #define MAX_FLASH_FREQ 115000000 - * - * FYI - Like the FE310-G000, the G002 has problems with reading flash - * faster than 50MHz - */ -#define MAX_FLASH_FREQ 50000000 - -/* - * CPU max is 320MHz+ according to datasheet but - * the relationship between cpu clock and spi clock is determined - * by SCKDIV. Given we're trying to achieve maximum I-cache refill - * for the flash we let MAX_FLASH_FREQ dictate the CPU clock. - */ -#define CPU_DESIRED_FREQ 320000000 - -/* - * The relationship between the input clock and SCK is given - * by the following formula (Fin is processor/tile-link clock): - * Fsck = Fin/(2(div + 1)) - */ -#define SCKDIV ((CPU_DESIRED_FREQ - 1) / (MAX_FLASH_FREQ * 2)) - -/* This should work for any reasonable cpu clock value. */ -#define SCKDIV_SAFE 3 - -/* - * By default the SPI FFMT initialized as: - * cmd_en = 1 - * addr_len = 3 - * cmd_code = 3 - * all other fields = 0 - */ - -void board_init_clock(void) -{ - /* In case we are executing from QSPI, (which is quite likely) we need to - * set the QSPI clock divider appropriately before boosting the clock - * frequency. PRCI_set_hfrosctrim_for_f_cpu() tries multiple clocks - * so choose a safe value that should work for all frequencies. - */ - SPI0_REG(SPI_REG_SCKDIV) = SCKDIV_SAFE; - - /* Note: The range is limited to ~100MHz and depends on PLL settings */ - PRCI_set_hfrosctrim_for_f_cpu(CPU_DESIRED_FREQ, PRCI_FREQ_UNDERSHOOT); - - /* begin{code-style-ignore} */ - SPI0_REG(SPI_REG_FFMT) = /* setup "Fast Read Dual I/O" */ - SPI_INSN_CMD_EN | /* Enable memory-mapped flash */ - SPI_INSN_ADDR_LEN(3) | /* 25LP03D read commands have 3 address bytes */ - SPI_INSN_PAD_CNT(4) | /* 25LP03D Table 6.11 Read Dummy Cycles = 4 */ - SPI_INSN_CMD_PROTO(SPI_PROTO_S) | /* 25LP03D Table 8.1 "Instruction */ - SPI_INSN_ADDR_PROTO(SPI_PROTO_D) | /* Set" shows mode for cmd, addr, and */ - SPI_INSN_DATA_PROTO(SPI_PROTO_D) | /* data protocol for given instruction */ - SPI_INSN_CMD_CODE(0xBB) | /* Set the instruction to "Fast Read Dual I/O" */ - SPI_INSN_PAD_CODE(0x00); /* Dummy cycle sends 0 value bits */ - /* end{code-style-ignore} */ - - SPI0_REG(SPI_REG_SCKDIV) = SCKDIV; -} - -__attribute__ ((section (".ramfunc"))) -void board_init_flash(void) -{ - /* Update the QSPI interface to adjust to the CPU speed - * This function needs to execute from the RAM - * when the QSPI interface is being reconfigured because the flash - * can't be accessed during this time - */ - - /* Disable SPI flash mode */ - SPI0_REG(SPI_REG_FCTRL) &= ~SPI_FCTRL_EN; - - /* Enable QPI mode by sending command to flash */ - SPI0_REG(SPI_REG_TXFIFO) = 0x35; - - /* begin{code-style-ignore} */ - SPI0_REG(SPI_REG_FFMT) = /* setup "Fast Read Quad I/O (QPI mode)" */ - SPI_INSN_CMD_EN | /* Enable memory-mapped flash */ - SPI_INSN_ADDR_LEN(3) | /* 25LP03D read commands have 3 address bytes */ - SPI_INSN_PAD_CNT(6) | /* 25LP03D Table 6.11 Read Dummy Cycles = 6 */ - SPI_INSN_CMD_PROTO(SPI_PROTO_Q) | /* 25LP03D Table 8.1 "Instruction */ - SPI_INSN_ADDR_PROTO(SPI_PROTO_Q) | /* Set" shows mode for cmd, addr, and */ - SPI_INSN_DATA_PROTO(SPI_PROTO_Q) | /* data protocol for given instruction */ - SPI_INSN_CMD_CODE(0xEB) | /* Set the instruction to "Fast Read Quad I/O" */ - SPI_INSN_PAD_CODE(0x00); /* Dummy cycle sends 0 value bits */ - /* end{code-style-ignore} */ - - /* Re-enable SPI flash mode */ - SPI0_REG(SPI_REG_FCTRL) |= SPI_FCTRL_EN; - - /* Adjust the SPI clk divider for to boost flash speed */ - // SPI0_REG(SPI_REG_SCKDIV) = SCKDIV; -} void board_init(void) { /* Initialize CPU and clocks */ cpu_init(); - board_init_clock(); - // board_init_flash(); /* Configure GPIOs for LEDs */ gpio_init(LED0_PIN, GPIO_OUT); @@ -148,7 +36,4 @@ void board_init(void) LED0_OFF; LED1_OFF; LED2_OFF; - - /* Initialize newlib-nano library stubs */ - nanostubs_init(); } From 3f29eb9efb68c8ec276b04c8ff093993139bc4a1 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Fri, 13 Dec 2019 14:33:17 +0100 Subject: [PATCH 6/8] cpu/fe310: use CLOCK_CORECLOCK macro to get cpu freq --- cpu/fe310/periph/uart.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/cpu/fe310/periph/uart.c b/cpu/fe310/periph/uart.c index 80b390777478..7cc2581d741f 100644 --- a/cpu/fe310/periph/uart.c +++ b/cpu/fe310/periph/uart.c @@ -86,13 +86,8 @@ int uart_init(uart_t dev, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) /* Power on the device */ uart_poweron(dev); - /* Calculate baudrate divisor given current CPU clk rate - * Ignore the first run (icache needs to be warm) */ - uartDiv = PRCI_measure_mcycle_freq(1000, RTC_FREQ); - /* cppcheck-suppress redundantAssignment - * (reason: should ignore first cycle to get correct values) */ - uartDiv = PRCI_measure_mcycle_freq(1000, RTC_FREQ); - uartDiv = uartDiv / baudrate; + /* Calculate baudrate divisor given current CPU clk rate */ + uartDiv = cpu_freq() / baudrate; /* Enable UART 8-N-1 at given baudrate */ _REG32(uart_config[dev].addr, UART_REG_DIV) = uartDiv; From b42cf186febe6cf8f06d8a6b9a27aa4f6ee65cb0 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Tue, 17 Dec 2019 08:10:26 +0100 Subject: [PATCH 7/8] boards/hifive1*: remove gpio interrupt definition This macro is now defined in periph_cpu.h --- boards/hifive1/include/periph_conf.h | 8 -------- boards/hifive1b/include/periph_conf.h | 8 -------- 2 files changed, 16 deletions(-) diff --git a/boards/hifive1/include/periph_conf.h b/boards/hifive1/include/periph_conf.h index cae44fcfdff3..f64cf553318e 100644 --- a/boards/hifive1/include/periph_conf.h +++ b/boards/hifive1/include/periph_conf.h @@ -120,14 +120,6 @@ static const uart_conf_t uart_config[] = { /** @} */ -/** - * @name GPIO configuration - * - * @{ - */ -#define GPIO_INTR_PRIORITY (3) -/** @} */ - /** * @name PWM configuration * diff --git a/boards/hifive1b/include/periph_conf.h b/boards/hifive1b/include/periph_conf.h index 749f450bbed8..dd5679a51451 100644 --- a/boards/hifive1b/include/periph_conf.h +++ b/boards/hifive1b/include/periph_conf.h @@ -121,14 +121,6 @@ static const uart_conf_t uart_config[] = { /** @} */ -/** - * @name GPIO configuration - * - * @{ - */ -#define GPIO_INTR_PRIORITY (3) -/** @} */ - /** * @name PWM configuration * From a953b74bc7d57f478795f87a40a9a180069966ce Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Tue, 17 Dec 2019 19:38:53 +0100 Subject: [PATCH 8/8] cpu/fe310: restore flash initialization in cpu_init --- cpu/fe310/cpu.c | 115 ++++++++++++++++++++++++++++--------------- cpu/fe310/irq_arch.c | 6 --- 2 files changed, 76 insertions(+), 45 deletions(-) diff --git a/cpu/fe310/cpu.c b/cpu/fe310/cpu.c index 2cd43fdd510c..bede3f4915c5 100644 --- a/cpu/fe310/cpu.c +++ b/cpu/fe310/cpu.c @@ -19,24 +19,33 @@ #include "cpu.h" #include "periph/init.h" +#include "periph_conf.h" -/** - * @brief Initialize the CPU, set IRQ priorities, clocks, peripheral - */ -void cpu_init(void) -{ - /* Initialize IRQs */ - irq_init(); - - /* Initialize clock */ - clock_init(); +#include "vendor/encoding.h" +#include "vendor/plic_driver.h" - /* Initialize newlib-nano library stubs */ - nanostubs_init(); +/* + * Configure the memory mapped flash for faster throughput + * to minimize interrupt latency on an I-Cache miss and refill + * from flash. Alternatively (and faster) the interrupt + * routine could be put in SRAM. The linker script supports + * code in SRAM using the ".hotcode" section. + * The flash chip on the HiFive1 is the ISSI 25LP128 + * http://www.issi.com/WW/pdf/IS25LP128.pdf + * The maximum frequency it can run at is 133MHz in + * "Fast Read Dual I/O" mode. + * Note the updated data sheet: + * https://static.dev.sifive.com/SiFive-FE310-G000-datasheet-v1.0.4.pdf + * states "Address and write data using DQ[3] for transmission will not + * function properly." This rules out QPI for the XIP memory mapped flash. + * #define MAX_FLASH_FREQ 133000000 + * On forum SiFive says "safe" operation would be 40MHz. 50MHz seems to work + * fine. + */ +#define MAX_FLASH_FREQ 50000000 - /* Initialize static peripheral */ - periph_init(); -} +/* This should work for any reasonable cpu clock value. */ +#define SCKDIV_SAFE 3 /* * By default the SPI FFMT initialized as: @@ -45,37 +54,65 @@ void cpu_init(void) * cmd_code = 3 * all other fields = 0 */ - -__attribute__ ((section (".ramfunc"))) -void init_flash(void) +void flash_init(void) { - /* Update the QSPI interface to adjust to the CPU speed - * This function needs to execute from the RAM - * when the QSPI interface is being reconfigured because the flash - * can't be accessed during this time + /* In case we are executing from QSPI, (which is quite likely) we need to + * set the QSPI clock divider appropriately before boosting the clock + * frequency. */ - - /* Disable SPI flash mode */ - SPI0_REG(SPI_REG_FCTRL) &= ~SPI_FCTRL_EN; - - /* Enable QPI mode by sending command to flash */ - SPI0_REG(SPI_REG_TXFIFO) = 0x35; + SPI0_REG(SPI_REG_SCKDIV) = SCKDIV_SAFE; /* begin{code-style-ignore} */ - SPI0_REG(SPI_REG_FFMT) = /* setup "Fast Read Quad I/O (QPI mode)" */ + SPI0_REG(SPI_REG_FFMT) = /* setup "Fast Read Dual I/O" 1-1-2 */ SPI_INSN_CMD_EN | /* Enable memory-mapped flash */ - SPI_INSN_ADDR_LEN(3) | /* 25LP03D read commands have 3 address bytes */ - SPI_INSN_PAD_CNT(6) | /* 25LP03D Table 6.11 Read Dummy Cycles = 6 */ - SPI_INSN_CMD_PROTO(SPI_PROTO_Q) | /* 25LP03D Table 8.1 "Instruction */ - SPI_INSN_ADDR_PROTO(SPI_PROTO_Q) | /* Set" shows mode for cmd, addr, and */ - SPI_INSN_DATA_PROTO(SPI_PROTO_Q) | /* data protocol for given instruction */ - SPI_INSN_CMD_CODE(0xEB) | /* Set the instruction to "Fast Read Quad I/O" */ + SPI_INSN_ADDR_LEN(3) | /* 25LP128 read commands have 3 address bytes */ + SPI_INSN_PAD_CNT(4) | /* 25LP128 Table 6.9 Read Dummy Cycles P4,P3=0,0 */ + SPI_INSN_CMD_PROTO(SPI_PROTO_S) | /* 25LP128 Table 8.1 "Instruction */ + SPI_INSN_ADDR_PROTO(SPI_PROTO_D) | /* Set" shows mode for cmd, addr, and */ + SPI_INSN_DATA_PROTO(SPI_PROTO_D) | /* data protocol for given instruction */ + SPI_INSN_CMD_CODE(0xbb) | /* Set the instruction to "Fast Read Dual I/O" */ SPI_INSN_PAD_CODE(0x00); /* Dummy cycle sends 0 value bits */ /* end{code-style-ignore} */ - /* Re-enable SPI flash mode */ - SPI0_REG(SPI_REG_FCTRL) |= SPI_FCTRL_EN; + /* + * The relationship between the input clock and SCK is given + * by the following formula (Fin is processor/tile-link clock): + * Fsck = Fin/(2(div + 1)) + */ + uint32_t freq = cpu_freq(); + uint32_t sckdiv = (freq - 1) / (MAX_FLASH_FREQ * 2); + if (sckdiv > SCKDIV_SAFE) { + SPI0_REG(SPI_REG_SCKDIV) = sckdiv; + } +} + +/** + * @brief Initialize the CPU, set IRQ priorities, clocks, peripheral + */ +void cpu_init(void) +{ + /* Initialize clock */ + clock_init(); - /* Adjust the SPI clk divider for to boost flash speed */ - // SPI0_REG(SPI_REG_SCKDIV) = SCKDIV; +#if USE_CLOCK_HFROSC_PLL + /* Initialize flash memory, only when using the PLL: in this + case the CPU core clock can be configured to be so fast that the SPI + flash frequency needs to be adjusted accordingly. */ + flash_init(); +#endif + + /* Enable FPU if present */ + if (read_csr(misa) & (1 << ('F' - 'A'))) { + write_csr(mstatus, MSTATUS_FS); /* allow FPU instructions without trapping */ + write_csr(fcsr, 0); /* initialize rounding mode, undefined at reset */ + } + + /* Initialize IRQs */ + irq_init(); + + /* Initialize newlib-nano library stubs */ + nanostubs_init(); + + /* Initialize static peripheral */ + periph_init(); } diff --git a/cpu/fe310/irq_arch.c b/cpu/fe310/irq_arch.c index 965651b99463..b4eceb741d42 100644 --- a/cpu/fe310/irq_arch.c +++ b/cpu/fe310/irq_arch.c @@ -56,12 +56,6 @@ void irq_init(void) /* Setup trap handler function */ write_csr(mtvec, &trap_entry); - /* Enable FPU if present */ - if (read_csr(misa) & (1 << ('F' - 'A'))) { - write_csr(mstatus, MSTATUS_FS); /* allow FPU instructions without trapping */ - write_csr(fcsr, 0); /* initialize rounding mode, undefined at reset */ - } - /* Clear all interrupt enables */ write_csr(mie, 0);