Merge pull request #12934 from aabadie/pr/cpu/fe310_cpu_rework
cpu/fe310: several cleanup in implementation
This commit is contained in:
commit
fd248dbc3c
@ -18,104 +18,14 @@
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
@ -27,6 +27,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Xtimer configuration
|
||||
* @{
|
||||
*/
|
||||
#define XTIMER_HZ (32768UL)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Macros for controlling the on-board LEDs
|
||||
* @{
|
||||
|
||||
@ -30,18 +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)
|
||||
|
||||
/**
|
||||
* @name Xtimer configuration
|
||||
* @{
|
||||
*/
|
||||
#define XTIMER_DEV (0)
|
||||
#define XTIMER_CHAN (0)
|
||||
#define XTIMER_WIDTH (32)
|
||||
#define XTIMER_HZ (32768ul)
|
||||
#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
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -85,14 +120,6 @@ static const uart_conf_t uart_config[] = {
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GPIO configuration
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define GPIO_INTR_PRIORITY (3)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name PWM configuration
|
||||
*
|
||||
|
||||
@ -18,126 +18,14 @@
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
@ -28,6 +28,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Xtimer configuration
|
||||
* @{
|
||||
*/
|
||||
#define XTIMER_HZ (32768UL)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Macros for controlling the on-board LEDs
|
||||
* @{
|
||||
|
||||
@ -31,18 +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)
|
||||
|
||||
/**
|
||||
* @name Xtimer configuration
|
||||
* @{
|
||||
*/
|
||||
#define XTIMER_DEV (0)
|
||||
#define XTIMER_CHAN (0)
|
||||
#define XTIMER_WIDTH (32)
|
||||
#define XTIMER_HZ (32768ul)
|
||||
#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
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -86,14 +121,6 @@ static const uart_conf_t uart_config[] = {
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GPIO configuration
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#define GPIO_INTR_PRIORITY (3)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name PWM configuration
|
||||
*
|
||||
|
||||
104
cpu/fe310/clock.c
Normal file
104
cpu/fe310/clock.c
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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/prci_driver.h"
|
||||
|
||||
#if !(USE_CLOCK_HFXOSC || USE_CLOCK_HFXOSC_PLL)
|
||||
static uint32_t _cpu_frequency = 0;
|
||||
#endif
|
||||
|
||||
void clock_init(void)
|
||||
{
|
||||
/* 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
|
||||
}
|
||||
423
cpu/fe310/cpu.c
423
cpu/fe310/cpu.c
@ -17,52 +17,89 @@
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#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 "periph_conf.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)
|
||||
/*
|
||||
* 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
|
||||
|
||||
volatile int __in_isr = 0;
|
||||
/* This should work for any reasonable cpu clock value. */
|
||||
#define SCKDIV_SAFE 3
|
||||
|
||||
/* 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)
|
||||
/*
|
||||
* By default the SPI FFMT initialized as:
|
||||
* cmd_en = 1
|
||||
* addr_len = 3
|
||||
* cmd_code = 3
|
||||
* all other fields = 0
|
||||
*/
|
||||
void flash_init(void)
|
||||
{
|
||||
(void) num;
|
||||
/* 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.
|
||||
*/
|
||||
SPI0_REG(SPI_REG_SCKDIV) = SCKDIV_SAFE;
|
||||
|
||||
/* 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} */
|
||||
|
||||
/*
|
||||
* 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
|
||||
* @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 clock */
|
||||
clock_init();
|
||||
|
||||
/* Setup trap handler function */
|
||||
write_csr(mtvec, &trap_entry);
|
||||
#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'))) {
|
||||
@ -70,326 +107,12 @@ void cpu_init(void)
|
||||
write_csr(fcsr, 0); /* initialize rounding mode, undefined at reset */
|
||||
}
|
||||
|
||||
/* Clear all interrupt enables */
|
||||
write_csr(mie, 0);
|
||||
/* Initialize IRQs */
|
||||
irq_init();
|
||||
|
||||
/* Initial PLIC external interrupt controller */
|
||||
PLIC_init(PLIC_CTRL_ADDR, PLIC_NUM_INTERRUPTS, PLIC_NUM_PRIORITIES);
|
||||
/* Initialize newlib-nano library stubs */
|
||||
nanostubs_init();
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
#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);
|
||||
}
|
||||
|
||||
@ -25,8 +25,6 @@
|
||||
#ifndef CPU_H
|
||||
#define CPU_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "thread.h"
|
||||
|
||||
#include "vendor/platform.h"
|
||||
@ -41,6 +39,33 @@ extern "C" {
|
||||
*/
|
||||
void cpu_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initialization of the clock
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*
|
||||
|
||||
@ -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
|
||||
|
||||
198
cpu/fe310/irq_arch.c
Normal file
198
cpu/fe310/irq_arch.c
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
27
cpu/fe310/panic.c
Normal file
27
cpu/fe310/panic.c
Normal file
@ -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
|
||||
}
|
||||
@ -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;
|
||||
|
||||
197
cpu/fe310/thread_arch.c
Normal file
197
cpu/fe310/thread_arch.c
Normal file
@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user