circuitpython/ports/silabs/supervisor/port.c

345 lines
9.0 KiB
C

/*
* This file is part of Adafruit for EFR32 project
*
* The MIT License (MIT)
*
* Copyright 2023 Silicon Laboratories Inc. www.silabs.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "supervisor/background_callback.h"
#include "supervisor/board.h"
#include "supervisor/port.h"
#include "shared/timeutils/timeutils.h"
#include "common-hal/microcontroller/Pin.h"
#include "shared-bindings/microcontroller/__init__.h"
#if CIRCUITPY_AUDIOPWMIO
#include "common-hal/audiopwmio/PWMAudioOut.h"
#endif
#if CIRCUITPY_BUSIO
#include "common-hal/busio/I2C.h"
#include "common-hal/busio/SPI.h"
#include "common-hal/busio/UART.h"
#endif
#if CIRCUITPY_PULSEIO
#include "common-hal/pulseio/PulseOut.h"
#include "common-hal/pulseio/PulseIn.h"
#endif
#if CIRCUITPY_PWMIO
#include "common-hal/pwmio/PWMOut.h"
#endif
#if CIRCUITPY_PULSEIO || CIRCUITPY_PWMIO
#include "peripherals/timers.h"
#endif
#if CIRCUITPY_SDIOIO
#include "common-hal/sdioio/SDCard.h"
#endif
#if CIRCUITPY_PULSEIO || CIRCUITPY_ALARM
#include "peripherals/exti.h"
#endif
#if CIRCUITPY_ALARM
#include "common-hal/alarm/__init__.h"
#endif
#if CIRCUITPY_RTC
#include "shared-bindings/rtc/__init__.h"
#endif
#if CIRCUITPY_ANALOGIO
#include "common-hal/analogio/AnalogOut.h"
#endif
#if CIRCUITPY_BLEIO
#include "common-hal/_bleio/__init__.h"
#endif
// Include headers of EFR32
#include <cmsis_os2.h>
#include "em_chip.h"
#include "sl_cmsis_os2_common.h"
#include "sl_component_catalog.h"
#include "sl_sleeptimer.h"
#include "sl_system_init.h"
#include "sl_system_kernel.h"
#if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
#include "sl_power_manager.h"
#endif // SL_CATALOG_POWER_MANAGER_PRESENT
#if !defined(SL_CATALOG_KERNEL_PRESENT)
#error "Error: Requires SL_CATALOG_KERNEL_PRESENT definition"
#endif
#define SL_CIRCUITPYTHON_TASK_STACK_EXTRA_SIZE (32)
#define SL_CIRCUITPYTHON_TASK_PRIORITY (40)
#define HEAP_SIZE (88 * 1024)
extern uint32_t __bss_start__;
extern uint32_t __bss_end__;
uint32_t _sbss;
uint32_t _ebss;
uint32_t *heap;
uint32_t heap_size;
STATIC sl_sleeptimer_timer_handle_t _tick_timer;
STATIC sl_sleeptimer_timer_handle_t _sleep_timer;
// CircuitPython stack thread
STATIC void circuitpython_thread(void *p_arg);
STATIC osThreadId_t tid_thread_circuitpython;
__ALIGNED(8)
STATIC uint8_t thread_circuitpython_stk[(CIRCUITPY_DEFAULT_STACK_SIZE +
SL_CIRCUITPYTHON_TASK_STACK_EXTRA_SIZE) &
0xFFFFFFF8u];
__ALIGNED(4)
STATIC uint8_t thread_circuitpython_cb[osThreadCbSize];
STATIC const osThreadAttr_t thread_circuitpython_attr = {
.name = "CircuitPython stack",
.stack_mem = thread_circuitpython_stk,
.stack_size = sizeof(thread_circuitpython_stk),
.cb_mem = thread_circuitpython_cb,
.cb_size = osThreadCbSize,
.priority = (osPriority_t)SL_CIRCUITPYTHON_TASK_PRIORITY
};
STATIC bool isSchedulerStarted = false;
safe_mode_t port_init(void) {
#if defined(SL_CATALOG_KERNEL_PRESENT)
if (!isSchedulerStarted) {
_sbss = __bss_start__;
_ebss = __bss_end__;
isSchedulerStarted = true;
// Initialize Silicon Labs device, system, service(s) and protocol stack(s).
// Note that if the kernel is present, processing task(s) will be created by
// this call.
sl_system_init();
// Create thread for Bluetooth stack
if (tid_thread_circuitpython == NULL) {
tid_thread_circuitpython = osThreadNew(circuitpython_thread,
NULL,
&thread_circuitpython_attr);
}
// Create mutex for Bluetooth handle
if (bluetooth_connection_mutex_id == NULL) {
bluetooth_connection_mutex_id = osMutexNew(&bluetooth_connection_mutex_attr);
}
if (tid_thread_circuitpython == NULL) {
for (;;) {
}
}
// Start the kernel. Task(s) created in app_init() will start running.
sl_system_kernel_start();
}
#endif // SL_CATALOG_KERNEL_PRESENT
if (heap == NULL) {
heap = malloc(HEAP_SIZE);
heap_size = HEAP_SIZE / sizeof(uint32_t);
}
if (heap == NULL) {
return SAFE_MODE_NO_HEAP;
}
return SAFE_MODE_NONE;
}
void reset_port(void) {
reset_all_pins();
#if CIRCUITPY_BUSIO
i2c_reset();
spi_reset();
uart_reset();
#endif
#if CIRCUITPY_PWMIO
pwmout_reset();
#endif
#if CIRCUITPY_ANALOGIO
analogout_reset();
#endif
#if CIRCUITPY_BLEIO
bleio_reset();
#endif
#if CIRCUITPY_RTC
rtc_reset();
#endif
#if CIRCUITPY_WATCHDOG
watchdog_reset();
#endif
}
void reset_to_bootloader(void) {
CHIP_Reset();
for (;;) {
}
}
void reset_cpu(void) {
CHIP_Reset();
for (;;) {
}
}
uint32_t *port_heap_get_bottom(void) {
return heap;
}
uint32_t *port_heap_get_top(void) {
return heap + heap_size;
}
bool port_has_fixed_stack(void) {
return true;
}
uint32_t *port_stack_get_limit(void) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
return (uint32_t *)thread_circuitpython_stk;
#pragma GCC diagnostic pop
}
uint32_t *port_stack_get_top(void) {
return port_stack_get_limit() + CIRCUITPY_DEFAULT_STACK_SIZE / sizeof(uint32_t);
}
uint64_t port_get_raw_ticks(uint8_t *subticks) {
uint32_t timer_freq = sl_sleeptimer_get_timer_frequency();
uint64_t all_subticks = (uint64_t)(sl_sleeptimer_get_tick_count()) * 1024;
if (subticks != NULL) {
*subticks = all_subticks % timer_freq;
}
return all_subticks / timer_freq;
}
// Periodic tick timer callback
STATIC void on_tick_timer_timeout(sl_sleeptimer_timer_handle_t *handle,
void *data) {
(void)&handle;
(void)&data;
supervisor_tick();
// CircuitPython's VM is run in a separate FreeRTOS task from timer callbacks.
// So, we have to notify the main task every time in case it's waiting for us.
osThreadFlagsSet(tid_thread_circuitpython, 0x0001);
}
// Enable 1/1024 second tick.
void port_enable_tick(void) {
uint32_t timer_freq = sl_sleeptimer_get_timer_frequency();
// Create timer for waking up the system periodically.
sl_sleeptimer_start_periodic_timer(&_tick_timer,
timer_freq / 1024,
on_tick_timer_timeout, NULL,
0,
SL_SLEEPTIMER_NO_HIGH_PRECISION_HF_CLOCKS_REQUIRED_FLAG);
}
// Disable 1/1024 second tick.
void port_disable_tick(void) {
sl_sleeptimer_stop_timer(&_tick_timer);
}
void port_wake_main_task(void) {
osThreadFlagsSet(tid_thread_circuitpython, 0x0001);
}
STATIC void on_sleep_timer_timeout(sl_sleeptimer_timer_handle_t *handle,
void *data) {
port_wake_main_task();
}
void port_interrupt_after_ticks(uint32_t ticks) {
uint32_t timer_freq = sl_sleeptimer_get_timer_frequency();
uint32_t timer_tick = (uint32_t)((((uint64_t)ticks * timer_freq) + 1023) / 1024u);
// Create one-shot timer for waking up the system.
sl_sleeptimer_start_timer(&_sleep_timer,
timer_tick,
on_sleep_timer_timeout, NULL,
0,
SL_SLEEPTIMER_NO_HIGH_PRECISION_HF_CLOCKS_REQUIRED_FLAG);
}
void port_idle_until_interrupt(void) {
if (!background_callback_pending()) {
osThreadFlagsWait(0x0001, osFlagsWaitAny, osWaitForever);
}
}
// Place the word to save just after our BSS section that gets blanked.
void port_set_saved_word(uint32_t value) {
__bss_end__ = value;
}
uint32_t port_get_saved_word(void) {
return __bss_end__;
}
#if CIRCUITPY_ALARM
// Board deinit in case boards/xxx/board.c does not provide board_deinit()
MP_WEAK void board_deinit(void) {
}
#endif
extern void main(void);
void circuitpython_thread(void *p_arg) {
(void)p_arg;
main();
}
__attribute__((used)) void BusFault_Handler(void) {
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}
}
__attribute__((used)) void UsageFault_Handler(void) {
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}
}
__attribute__((used)) void HardFault_Handler(void) {
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}
}