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 "cpu.h"
|
||||||
#include "board.h"
|
#include "board.h"
|
||||||
#include "periph/gpio.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)
|
void board_init(void)
|
||||||
{
|
{
|
||||||
/* Initialize CPU and clocks */
|
/* Initialize CPU and clocks */
|
||||||
cpu_init();
|
cpu_init();
|
||||||
board_init_clock();
|
|
||||||
|
|
||||||
/* Configure GPIOs for LEDs */
|
/* Configure GPIOs for LEDs */
|
||||||
gpio_init(LED0_PIN, GPIO_OUT);
|
gpio_init(LED0_PIN, GPIO_OUT);
|
||||||
@ -126,7 +36,4 @@ void board_init(void)
|
|||||||
LED0_OFF;
|
LED0_OFF;
|
||||||
LED1_OFF;
|
LED1_OFF;
|
||||||
LED2_OFF;
|
LED2_OFF;
|
||||||
|
|
||||||
/* Initialize newlib-nano library stubs */
|
|
||||||
nanostubs_init();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,13 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Xtimer configuration
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define XTIMER_HZ (32768UL)
|
||||||
|
/** @} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name Macros for controlling the on-board LEDs
|
* @name Macros for controlling the on-board LEDs
|
||||||
* @{
|
* @{
|
||||||
|
|||||||
@ -30,18 +30,53 @@ extern "C" {
|
|||||||
* @name Core Clock configuration
|
* @name Core Clock configuration
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
/* As defined in boards/hifive1/board.c CPU_DESIRED_FREQ **/
|
#define USE_CLOCK_HFXOSC_PLL (1)
|
||||||
#define CLOCK_CORECLOCK (200000000ul)
|
#define USE_CLOCK_HFXOSC (0)
|
||||||
/** @} */
|
#define USE_CLOCK_HFROSC_PLL (0)
|
||||||
|
|
||||||
/**
|
#if USE_CLOCK_HFROSC_PLL && (USE_CLOCK_HFXOSC_PLL || USE_CLOCK_HFXOSC)
|
||||||
* @name Xtimer configuration
|
#error "Cannot use HFROSC_PLL with HFXOSC based configurations"
|
||||||
* @{
|
#endif
|
||||||
*/
|
|
||||||
#define XTIMER_DEV (0)
|
#if USE_CLOCK_HFXOSC_PLL && USE_CLOCK_HFXOSC
|
||||||
#define XTIMER_CHAN (0)
|
#error "Cannot use HFXOSC with HFXOSC_PLL"
|
||||||
#define XTIMER_WIDTH (32)
|
#endif
|
||||||
#define XTIMER_HZ (32768ul)
|
|
||||||
|
#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
|
* @name PWM configuration
|
||||||
*
|
*
|
||||||
|
|||||||
@ -18,126 +18,14 @@
|
|||||||
* @}
|
* @}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "board.h"
|
#include "board.h"
|
||||||
#include "periph/gpio.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)
|
void board_init(void)
|
||||||
{
|
{
|
||||||
/* Initialize CPU and clocks */
|
/* Initialize CPU and clocks */
|
||||||
cpu_init();
|
cpu_init();
|
||||||
board_init_clock();
|
|
||||||
// board_init_flash();
|
|
||||||
|
|
||||||
/* Configure GPIOs for LEDs */
|
/* Configure GPIOs for LEDs */
|
||||||
gpio_init(LED0_PIN, GPIO_OUT);
|
gpio_init(LED0_PIN, GPIO_OUT);
|
||||||
@ -148,7 +36,4 @@ void board_init(void)
|
|||||||
LED0_OFF;
|
LED0_OFF;
|
||||||
LED1_OFF;
|
LED1_OFF;
|
||||||
LED2_OFF;
|
LED2_OFF;
|
||||||
|
|
||||||
/* Initialize newlib-nano library stubs */
|
|
||||||
nanostubs_init();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,13 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Xtimer configuration
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define XTIMER_HZ (32768UL)
|
||||||
|
/** @} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name Macros for controlling the on-board LEDs
|
* @name Macros for controlling the on-board LEDs
|
||||||
* @{
|
* @{
|
||||||
|
|||||||
@ -31,18 +31,53 @@ extern "C" {
|
|||||||
* @name Core Clock configuration
|
* @name Core Clock configuration
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
/* As defined in boards/hifive1/board.c CPU_DESIRED_FREQ **/
|
#define USE_CLOCK_HFXOSC_PLL (1)
|
||||||
#define CLOCK_CORECLOCK (200000000ul)
|
#define USE_CLOCK_HFXOSC (0)
|
||||||
/** @} */
|
#define USE_CLOCK_HFROSC_PLL (0)
|
||||||
|
|
||||||
/**
|
#if USE_CLOCK_HFROSC_PLL && (USE_CLOCK_HFXOSC_PLL || USE_CLOCK_HFXOSC)
|
||||||
* @name Xtimer configuration
|
#error "Cannot use HFROSC_PLL with HFXOSC based configurations"
|
||||||
* @{
|
#endif
|
||||||
*/
|
|
||||||
#define XTIMER_DEV (0)
|
#if USE_CLOCK_HFXOSC_PLL && USE_CLOCK_HFXOSC
|
||||||
#define XTIMER_CHAN (0)
|
#error "Cannot use HFXOSC with HFXOSC_PLL"
|
||||||
#define XTIMER_WIDTH (32)
|
#endif
|
||||||
#define XTIMER_HZ (32768ul)
|
|
||||||
|
#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
|
* @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 "cpu.h"
|
||||||
#include "context_frame.h"
|
|
||||||
#include "periph_cpu.h"
|
|
||||||
#include "periph/init.h"
|
#include "periph/init.h"
|
||||||
#include "panic.h"
|
#include "periph_conf.h"
|
||||||
|
|
||||||
#include "vendor/encoding.h"
|
#include "vendor/encoding.h"
|
||||||
#include "vendor/platform.h"
|
|
||||||
#include "vendor/plic_driver.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);
|
* By default the SPI FFMT initialized as:
|
||||||
|
* cmd_en = 1
|
||||||
/* PLIC external ISR function list */
|
* addr_len = 3
|
||||||
static external_isr_ptr_t _ext_isrs[PLIC_NUM_INTERRUPTS];
|
* cmd_code = 3
|
||||||
|
* all other fields = 0
|
||||||
/* NULL interrupt handler */
|
*/
|
||||||
void null_isr(int num)
|
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)
|
void cpu_init(void)
|
||||||
{
|
{
|
||||||
volatile uint64_t *mtimecmp =
|
/* Initialize clock */
|
||||||
(uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
|
clock_init();
|
||||||
|
|
||||||
/* Setup trap handler function */
|
#if USE_CLOCK_HFROSC_PLL
|
||||||
write_csr(mtvec, &trap_entry);
|
/* 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 */
|
/* Enable FPU if present */
|
||||||
if (read_csr(misa) & (1 << ('F' - 'A'))) {
|
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 */
|
write_csr(fcsr, 0); /* initialize rounding mode, undefined at reset */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear all interrupt enables */
|
/* Initialize IRQs */
|
||||||
write_csr(mie, 0);
|
irq_init();
|
||||||
|
|
||||||
/* Initial PLIC external interrupt controller */
|
/* Initialize newlib-nano library stubs */
|
||||||
PLIC_init(PLIC_CTRL_ADDR, PLIC_NUM_INTERRUPTS, PLIC_NUM_PRIORITIES);
|
nanostubs_init();
|
||||||
|
|
||||||
/* Initialize ISR function list */
|
/* Initialize static peripheral */
|
||||||
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 */
|
|
||||||
periph_init();
|
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
|
#ifndef CPU_H
|
||||||
#define CPU_H
|
#define CPU_H
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
|
|
||||||
#include "vendor/platform.h"
|
#include "vendor/platform.h"
|
||||||
@ -41,6 +39,33 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
void cpu_init(void);
|
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
|
* @brief Print the last instruction's address
|
||||||
*
|
*
|
||||||
|
|||||||
@ -50,6 +50,11 @@ typedef uint8_t gpio_t;
|
|||||||
*/
|
*/
|
||||||
#define GPIO_PIN(x, y) (x | y)
|
#define GPIO_PIN(x, y) (x | y)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief GPIO interrupt priority
|
||||||
|
*/
|
||||||
|
#define GPIO_INTR_PRIORITY (3)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Structure for UART configuration data
|
* @brief Structure for UART configuration data
|
||||||
*/
|
*/
|
||||||
@ -70,21 +75,6 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
#define PERIPH_TIMER_PROVIDES_SET
|
#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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#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 */
|
/* Power on the device */
|
||||||
uart_poweron(dev);
|
uart_poweron(dev);
|
||||||
|
|
||||||
/* Calculate baudrate divisor given current CPU clk rate
|
/* Calculate baudrate divisor given current CPU clk rate */
|
||||||
* Ignore the first run (icache needs to be warm) */
|
uartDiv = cpu_freq() / baudrate;
|
||||||
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;
|
|
||||||
|
|
||||||
/* Enable UART 8-N-1 at given baudrate */
|
/* Enable UART 8-N-1 at given baudrate */
|
||||||
_REG32(uart_config[dev].addr, UART_REG_DIV) = uartDiv;
|
_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