added function for enabling/disabling CPU extensions (experimental!); optimized some functions to be always inlined

This commit is contained in:
stnolting 2020-07-07 19:14:00 +02:00
parent 5e8b242911
commit e643751244
2 changed files with 107 additions and 61 deletions

View file

@ -43,14 +43,9 @@
#define neorv32_cpu_h
// prototypes
void neorv32_cpu_sleep(void);
void neorv32_cpu_eint(void);
void neorv32_cpu_dint(void);
int neorv32_cpu_switch_extension(int sel, int state);
int neorv32_cpu_irq_enable(uint8_t irq_sel);
int neorv32_cpu_irq_disable(uint8_t irq_sel);
void neorv32_cpu_sw_irq(void);
void neorv32_cpu_breakpoint(void);
void neorv32_cpu_env_call(void);
void neorv32_cpu_delay_ms(uint32_t time_ms);
@ -83,4 +78,74 @@ inline void __attribute__ ((always_inline)) neorv32_cpu_csr_write(const int csr_
asm volatile ("csrw %[input_i], %[input_j]" : : [input_i] "i" (csr_id), [input_j] "r" (csr_data));
}
/**********************************************************************//**
* Put CPU into "sleep" mode.
*
* @note This function executes the WFI insstruction.
* The WFI (wait for interrupt) instruction will make the CPU stall until
* an interupt request is detected. Interrupts have to be globally enabled
* and at least one external source must be enabled (e.g., the CLIC or the machine
* timer) to allow the CPU to wake up again. If 'Zicsr' CPU extension is disabled,
* this will permanently stall the CPU.
**************************************************************************/
inline void __attribute__ ((always_inline)) neorv32_cpu_sleep(void) {
asm volatile ("wfi");
}
/**********************************************************************//**
* Enable global CPU interrupts (via MIE flag in mstatus CSR).
**************************************************************************/
inline void __attribute__ ((always_inline)) neorv32_cpu_eint(void) {
asm volatile ("csrrsi zero, mstatus, %0" : : "i" (1 << CPU_MSTATUS_MIE));
}
/**********************************************************************//**
* Disable global CPU interrupts (via MIE flag in mstatus CSR).
**************************************************************************/
inline void __attribute__ ((always_inline)) neorv32_cpu_dint(void) {
asm volatile ("csrrci zero, mstatus, %0" : : "i" (1 << CPU_MSTATUS_MIE));
}
/**********************************************************************//**
* Trigger machine software interrupt.
*
* @note The according IRQ has to be enabled via neorv32_cpu_irq_enable(uint8_t irq_sel) and
* global interrupts must be enabled via neorv32_cpu_eint(void) to trigger an IRQ via software.
* The MSI becomes active after 3 clock cycles.
**************************************************************************/
inline void __attribute__ ((always_inline)) neorv32_cpu_sw_irq(void) {
asm volatile ("csrrsi zero, mip, %0" : : "i" (1 << CPU_MIP_MSIP));
// the MSI becomes active 3 clock cycles afters issueing
asm volatile ("nop"); // these nops are not required, they just make sure the MSI becomes active
asm volatile ("nop"); // before the "real" next operation is executed
}
/**********************************************************************//**
* Trigger breakpoint exception (via EBREAK instruction).
**************************************************************************/
inline void __attribute__ ((always_inline)) neorv32_cpu_breakpoint(void) {
asm volatile ("ebreak");
}
/**********************************************************************//**
* Trigger "environment call" exception (via ECALL instruction).
**************************************************************************/
inline void __attribute__ ((always_inline)) neorv32_cpu_env_call(void) {
asm volatile ("ecall");
}
#endif // neorv32_cpu_h

View file

@ -44,38 +44,50 @@
/**********************************************************************//**
* Put CPU into "sleep" mode.
* Enable/disable CPU extension during runtime via the 'misa' CSR.
*
* @note This function executes the WFI insstruction.
* The WFI (wait for interrupt) instruction will make the CPU stall until
* an interupt request is detected. Interrupts have to be globally enabled
* and at least one external source must be enabled (e.g., the CLIC or the machine
* timer) to allow the CPU to wake up again. If 'Zicsr' CPU extension is disabled,
* this will permanently stall the CPU.
* @warning This is still highly experimental! This function requires the Zicsr + Zifencei CPU extensions.
*
* @param[in] sel Bit to be set in misa CSR / extension to be enabled. See #NEORV32_CPU_MISA_enum.
* @param[in] state Set 1 to enable the selected extension, set 0 to disable it;
* return 0 if success, 1 if error (invalid sel or extension cannot be enabled).
**************************************************************************/
void neorv32_cpu_sleep(void) {
int neorv32_cpu_switch_extension(int sel, int state) {
asm volatile ("wfi");
}
// get current misa setting
uint32_t misa_curr = neorv32_cpu_csr_read(CSR_MISA);
uint32_t misa_prev = misa_curr;
// abort if misa.z is cleared
if ((misa_curr & (1 << CPU_MISA_Z_EXT)) == 0) {
return 1;
}
/**********************************************************************//**
* Enable global CPU interrupts (via MIE flag in mstatus CSR).
**************************************************************************/
void neorv32_cpu_eint(void) {
// out of range?
if (sel > 25) {
return 1;
}
const int mask = 1 << CPU_MSTATUS_MIE;
asm volatile ("csrrsi zero, mstatus, %0" : : "i" (mask));
}
// enable/disable selected extension
if (state & 1) {
misa_curr |= (1 << sel);
}
else {
misa_curr &= ~(1 << sel);
}
// try updating misa
neorv32_cpu_csr_write(CSR_MISA, misa_curr);
asm volatile("fence.i"); // required to flush prefetch buffers
asm volatile("nop");
/**********************************************************************//**
* Disable global CPU interrupts (via MIE flag in mstatus CSR).
**************************************************************************/
void neorv32_cpu_dint(void) {
const int mask = 1 << CPU_MSTATUS_MIE;
asm volatile ("csrrci zero, mstatus, %0" : : "i" (mask));
// dit it work?
if (neorv32_cpu_csr_read(CSR_MISA) == misa_prev) {
return 1; // nope
}
else {
return 0; // fine
}
}
@ -117,37 +129,6 @@ int neorv32_cpu_irq_disable(uint8_t irq_sel) {
}
/**********************************************************************//**
* Trigger machine software interrupt.
*
* @note The according IRQ has to be enabled via neorv32_cpu_irq_enable(uint8_t irq_sel) and
* global interrupts must be enabled via neorv32_cpu_eint(void) to trigger an IRQ via software.
**************************************************************************/
void neorv32_cpu_sw_irq(void) {
register uint32_t mask = (uint32_t)(1 << CPU_MIP_MSIP);
asm volatile ("csrrs zero, mip, %0" : : "r" (mask));
}
/**********************************************************************//**
* Trigger breakpoint exception (via EBREAK instruction).
**************************************************************************/
void neorv32_cpu_breakpoint(void) {
asm volatile ("ebreak");
}
/**********************************************************************//**
* Trigger "environment call" exception (via ECALL instruction).
**************************************************************************/
void neorv32_cpu_env_call(void) {
asm volatile ("ecall");
}
/**********************************************************************//**
* Simple delay function (not very precise) using busy wait.
*