mirror of
https://github.com/stnolting/neorv32.git
synced 2025-04-24 06:07:52 -04:00
[sw/example] simplify dual-core demo
This commit is contained in:
parent
053d54dcbd
commit
1e53b2a868
3 changed files with 22 additions and 154 deletions
|
@ -8,29 +8,38 @@
|
|||
|
||||
/**********************************************************************//**
|
||||
* @file demo_dual_core/main.c
|
||||
* @author Stephan Nolting
|
||||
* @brief Simple dual-core SMP demo program.
|
||||
**************************************************************************/
|
||||
#include <neorv32.h>
|
||||
#include "spinlock.h"
|
||||
|
||||
/** User configuration */
|
||||
#define BAUD_RATE 19200 // UART0 Baud rate
|
||||
|
||||
/** Function prototypes */
|
||||
void main_core1(void);
|
||||
void clint_mtime_handler_core0(void); // core0 MTIMER interrupt handler
|
||||
void clint_mtime_handler_core1(void); // core1 MTIMER interrupt handler
|
||||
#define BAUD_RATE 19200
|
||||
|
||||
/** Global variables */
|
||||
volatile uint8_t __attribute__ ((aligned (16))) core1_stack[2048]; // stack memory for core1
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Main function for core 1 (secondary core).
|
||||
*
|
||||
* @return Irrelevant (but can be inspected by the debugger).
|
||||
**************************************************************************/
|
||||
int main_core1(void) {
|
||||
|
||||
// setup NEORV32 runtime-environment (RTE) for _this_ core (core1)
|
||||
neorv32_rte_setup();
|
||||
|
||||
// print message from core0
|
||||
neorv32_uart0_printf("Hello world! This is core 1 running!\n");
|
||||
|
||||
return 0; // return to crt0 and halt
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Main function for core 0 (primary core).
|
||||
*
|
||||
* @attention This program requires the dual-core configuration, the CLINT, UART0
|
||||
* and the Zaamo ISA extension.
|
||||
* @attention This program requires the dual-core configuration, the CLINT and UART0.
|
||||
*
|
||||
* @return Irrelevant (but can be inspected by the debugger).
|
||||
**************************************************************************/
|
||||
|
@ -45,7 +54,7 @@ int main(void) {
|
|||
return -1;
|
||||
}
|
||||
neorv32_uart0_setup(BAUD_RATE, 0);
|
||||
neorv32_uart0_printf("\n<< NEORV32 Dual-Core SMP Demo >>\n\n");
|
||||
neorv32_uart0_printf("\n<< NEORV32 Simple SMP Dual-Core Demo >>\n\n");
|
||||
|
||||
|
||||
// check hardware/software configuration
|
||||
|
@ -57,31 +66,12 @@ int main(void) {
|
|||
neorv32_uart0_printf("[ERROR] CLINT module not available!\n");
|
||||
return -1;
|
||||
}
|
||||
if ((neorv32_cpu_csr_read(CSR_MXISA) & (1<<CSR_MXISA_ZAAMO)) == 0) { // atomic memory operations available?
|
||||
neorv32_uart0_printf("[ERROR] 'Zaamo' ISA extension not available!\n");
|
||||
return -1;
|
||||
}
|
||||
#ifndef __riscv_atomic
|
||||
#warning "Application has to be compiled with RISC-V 'A' ISA extension!"
|
||||
neorv32_uart0_printf("[ERROR] Application has to be compiled with 'A' ISA extension!\n");
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
|
||||
// initialize _global_ system timer (CLINT's machine timer)
|
||||
neorv32_clint_time_set(0);
|
||||
|
||||
// setup MTIMER interrupt for this core (core0)
|
||||
// the core-specific installation is handled entirely by the RTE
|
||||
neorv32_clint_mtimecmp_set(0); // initialize core-specific MTIMECMP
|
||||
neorv32_rte_handler_install(RTE_TRAP_MTI, clint_mtime_handler_core0); // install trap handler to RTE
|
||||
neorv32_cpu_csr_set(CSR_MIE, 1 << CSR_MIE_MTIE); // enable MTIMER interrupt source
|
||||
|
||||
|
||||
// Core one is halted in crt0 right after reset and wait for its machine-level software
|
||||
// interrupt before resuming. Before the interrupt is triggered, a launch configuration
|
||||
// for core 1 has to be provided. This launch configuration defines the entry point for
|
||||
// core 1 as well as the stack setup. All this is handle by "neorv32_rte_smp_launch()".
|
||||
// core 1 as well as the stack setup. All this is handle by "neorv32_smp_launch()".
|
||||
|
||||
neorv32_uart0_printf("Launching core1...\n");
|
||||
|
||||
|
@ -102,85 +92,7 @@ int main(void) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Core1 should be running now.
|
||||
// UART0 is used by both cores so it is a shared resource. We need to ensure exclusive
|
||||
// access. Therefore, we use a simple spinlock (based on atomic load-reservate /
|
||||
// store-conditional primitives).
|
||||
|
||||
// print message from core0
|
||||
// use spinlock to have exclusive access to UART0
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("This is a message from core 0!\n");
|
||||
spin_unlock();
|
||||
|
||||
|
||||
// enable machine-level interrupts and wait in sleep mode for the MTIMER interrupt
|
||||
neorv32_cpu_csr_set(CSR_MSTATUS, 1 << CSR_MSTATUS_MIE);
|
||||
while (1) {
|
||||
neorv32_cpu_sleep();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Main function for core 1 (secondary core).
|
||||
**************************************************************************/
|
||||
void main_core1(void) {
|
||||
|
||||
// setup NEORV32 runtime-environment (RTE) for _this_ core (core1)
|
||||
neorv32_rte_setup();
|
||||
|
||||
|
||||
// print message from core0
|
||||
// use spinlock to have exclusive access to UART0
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("Hello world! This is core1 running!\n");
|
||||
spin_unlock();
|
||||
|
||||
|
||||
// setup MTIMER interrupt for this core (core1)
|
||||
// the core-specific installation is handled entirely by the RTE
|
||||
neorv32_clint_mtimecmp_set(0); // initialize core-specific MTIMECMP
|
||||
neorv32_rte_handler_install(RTE_TRAP_MTI, clint_mtime_handler_core1); // install trap handler to RTE
|
||||
neorv32_cpu_csr_set(CSR_MIE, 1 << CSR_MIE_MTIE); // enable MTIMER interrupt source
|
||||
|
||||
|
||||
// enable machine-level interrupts and wait in sleep mode
|
||||
neorv32_cpu_csr_set(CSR_MSTATUS, 1 << CSR_MSTATUS_MIE);
|
||||
while (1) {
|
||||
neorv32_cpu_sleep();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* CLINT machine timer interrupt handler for core 0.
|
||||
**************************************************************************/
|
||||
void clint_mtime_handler_core0(void) {
|
||||
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("[core0] Primary core 1-second MTIMER interrupt. SMP is so cool!\n");
|
||||
spin_unlock();
|
||||
|
||||
// program next interrupt time (in 1 second)
|
||||
// this is automatically mapped to core0's MTIMECMP register
|
||||
neorv32_clint_mtimecmp_set(neorv32_clint_time_get() + 1*neorv32_sysinfo_get_clk());
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* CLINT machine timer interrupt handler for core 1.
|
||||
**************************************************************************/
|
||||
void clint_mtime_handler_core1(void) {
|
||||
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("[core1] Secondary core 2-seconds MTIMER interrupt. Dual-core rules!\n");
|
||||
spin_unlock();
|
||||
|
||||
// program next interrupt time (in 2 seconds)
|
||||
// this is automatically mapped to core1's MTIMECMP register
|
||||
neorv32_clint_mtimecmp_set(neorv32_clint_time_get() + 2*neorv32_sysinfo_get_clk());
|
||||
return 0; // return to crt0 and halt
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/**
|
||||
* @file spinlock.c
|
||||
* @brief Single simple spinlock based on atomic memory operations.
|
||||
*/
|
||||
#include <neorv32.h>
|
||||
|
||||
/**********************************************************************//**
|
||||
* Private spinlock locked variable. We can only use a single spinlock
|
||||
* as the processor only features a single reservation set.
|
||||
**************************************************************************/
|
||||
static volatile uint32_t __spin_locked = 0;
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Spinlock: set lock.
|
||||
*
|
||||
* @warning This function is blocking until the lock is acquired and set.
|
||||
**************************************************************************/
|
||||
void spin_lock(void) {
|
||||
|
||||
while(__sync_lock_test_and_set(&__spin_locked, -1)); // -> amoswap.w
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Spinlock: remove lock.
|
||||
**************************************************************************/
|
||||
void spin_unlock(void) {
|
||||
|
||||
//__sync_lock_release(&__spin_locked); // uses fence that is not required here
|
||||
__sync_lock_test_and_set(&__spin_locked, 0); // -> amoswap.w
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* @file spinlock.h
|
||||
* @brief Single simple spin-lock based on atomic lr/sc operations.
|
||||
*/
|
||||
|
||||
#ifndef spinlock_h
|
||||
#define spinlock_h
|
||||
|
||||
void spin_lock(void);
|
||||
void spin_unlock(void);
|
||||
|
||||
#endif // spinlock_h
|
Loading…
Add table
Add a link
Reference in a new issue