[sw/example] simplify dual-core demo

This commit is contained in:
stnolting 2025-01-10 11:21:45 +01:00
parent 053d54dcbd
commit 1e53b2a868
3 changed files with 22 additions and 154 deletions

View file

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

View file

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

View file

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