Merge pull request #12934 from aabadie/pr/cpu/fe310_cpu_rework

cpu/fe310: several cleanup in implementation
This commit is contained in:
Alexandre Abadie 2020-01-11 09:31:28 +01:00 committed by GitHub
commit fd248dbc3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 739 additions and 620 deletions

View File

@ -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();
}

View File

@ -27,6 +27,13 @@
extern "C" {
#endif
/**
* @name Xtimer configuration
* @{
*/
#define XTIMER_HZ (32768UL)
/** @} */
/**
* @name Macros for controlling the on-board LEDs
* @{

View File

@ -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
* @{
#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.
*/
#define XTIMER_DEV (0)
#define XTIMER_CHAN (0)
#define XTIMER_WIDTH (32)
#define XTIMER_HZ (32768ul)
#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
*

View File

@ -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();
}

View File

@ -28,6 +28,13 @@
extern "C" {
#endif
/**
* @name Xtimer configuration
* @{
*/
#define XTIMER_HZ (32768UL)
/** @} */
/**
* @name Macros for controlling the on-board LEDs
* @{

View File

@ -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
* @{
#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.
*/
#define XTIMER_DEV (0)
#define XTIMER_CHAN (0)
#define XTIMER_WIDTH (32)
#define XTIMER_HZ (32768ul)
#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
View 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
}

View File

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

View File

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

View File

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

View File

@ -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
View 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);
}