Select Git revision
Forked from
card10 / firmware
Source project has a limited visibility.
mphalport.c 8.95 KiB
#include "epicardium.h"
#include "api/common.h"
#include "max32665.h"
#include "mxc_delay.h"
#include "tmr.h"
/* stdarg.h must be included before mpprint.h */
#include <stdarg.h>
#include "py/lexer.h"
#include "py/mpconfig.h"
#include "py/mperrno.h"
#include "py/mpstate.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "py/mpprint.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
/* Smallest integer us interval which can be reached exactly due
* to the 32768 Hz systick frequency */
#define SYSTICK_INTERVAL_US_MIN 15625
/* Target systick interval is 1 second */
#define SYSTICK_INTERVAL_US \
(SYSTICK_INTERVAL_US_MIN * (1000000 / SYSTICK_INTERVAL_US_MIN))
/*
* Copied from core_cm4.h and modified to select the
* 32768 Hz RTC crystal as the clock source */
static uint32_t systick_config(uint32_t ticks)
{
if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)
return (1); /* Reload value impossible */
SysTick->LOAD = ticks - 1; /* set reload register */
NVIC_SetPriority(
SysTick_IRQn,
(1 << __NVIC_PRIO_BITS) -
1); /* set Priority for Systick Interrupt */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL =
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
/* Initialize everything for MicroPython */
void pycardium_hal_init(void)
{
/* TMR5 is used for interrupts from Epicardium */
NVIC_EnableIRQ(TMR5_IRQn);
/*
* Enable UART RX Interrupt so Pycardium can sleep until
* a character becomes available.
*/
epic_interrupt_enable(EPIC_INT_UART_RX);
/*
* Configure SysTick timer for SYSTICK_INTERVAL_US period.
*/
systick_config(SYSTICK_INTERVAL_US * 32768LL / 1000000);
}
/******************************************************************************
* Serial Communication
*/
/* Receive single character */
int mp_hal_stdin_rx_chr(void)
{
int chr;
while ((chr = epic_uart_read_char()) < 0) {
__WFI();
}
return chr;
}
/* Send a string */
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len)
{
epic_uart_write_str(str, len);
}
static char exception_lines[2][80];
static bool exception;
static size_t exception_line_index;
static void exception_feed(char c)
{
if (c == '\n') {
exception_lines[1][exception_line_index] = 0;
if (exception_lines[1][0] == ' ') {
memcpy(exception_lines[0],
exception_lines[1],
sizeof(exception_lines[0]));
exception_line_index = 0;
} else {
exception = false;
epic_disp_open();
epic_disp_clear(0);
epic_disp_print_adv(
1, 0, 0, "Exception:", 0xF800, 0x0000
);
char buf = exception_lines[0][26];
exception_lines[0][26] = 0;
epic_disp_print_adv(
1, 0, 12, exception_lines[0], 0xFFFF, 0x0000
);
exception_lines[0][26] = buf;
if (strlen(exception_lines[0]) > 26) {
epic_disp_print_adv(
1,
0,
24,
exception_lines[0] + 26,
0xFFFF,
0x0000
);
}
buf = exception_lines[1][26];
exception_lines[1][26] = 0;
epic_disp_print_adv(
1, 0, 40, exception_lines[1], 0xF800, 0x0000
);
exception_lines[1][26] = buf;
if (strlen(exception_lines[1]) > 26) {
epic_disp_print_adv(
1,
0,
52,
exception_lines[1] + 26,
0xF800,
0x0000
);
}
epic_disp_update();
}
} else {
if (exception_line_index < sizeof(exception_lines[0]) - 1) {
exception_lines[1][exception_line_index++] = c;
}
}
}
/* Send a string, but replace \n with \n\r */
void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len)
{
/*
* Only print one line at a time. Insert `\r` between lines so
* they are properly displayed on the serial console.
*/
if (strncmp(str, "Traceback (most recent call last):\n", len) == 0) {
exception = true;
exception_line_index = 0;
} else if (exception) {
for (size_t i = 0; i < len; i++) {
exception_feed(str[i]);
}
}
size_t i, last = 0;
for (i = 0; i < len; i++) {
if (str[i] == '\n') {
epic_uart_write_str(&str[last], i - last);
epic_uart_write_str("\r", 1);
last = i;
}
}
epic_uart_write_str(&str[last], len - last);
}
/* Send a zero-terminated string */
void mp_hal_stdout_tx_str(const char *str)
{
mp_hal_stdout_tx_strn(str, strlen(str));
}
/* Used by MicroPython for debug output */
int DEBUG_printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
int ret = mp_vprintf(MP_PYTHON_PRINTER, fmt, args);
va_end(args);
return ret;
}
void __attribute__((noreturn)) sbrk_is_not_implemented___see_issue_44(void);
intptr_t _sbrk(int incr)
{
sbrk_is_not_implemented___see_issue_44();
}
void epic_isr_ctrl_c(void)
{
mp_sched_keyboard_interrupt();
}
void mp_hal_set_interrupt_char(char c)
{
if (c != '\xFF') {
mp_obj_exception_clear_traceback(
MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))
);
}
if (c == 0x03) {
epic_interrupt_enable(EPIC_INT_CTRL_C);
} else {
epic_interrupt_disable(EPIC_INT_CTRL_C);
}
}
/******************************************************************************
* SysTick timer at 1000 Hz
*/
static volatile int64_t systick_count = 0;
void SysTick_Handler(void)
{
systick_count += 1;
}
/*
* Get an absolute "timestamp" in microseconds.
*/
static int64_t systick_get_us()
{
uint32_t val, count;
uint32_t irqsaved = __get_PRIMASK();
/* The asynchronous/slow clocking of the systick means that
* its value can jump to 0 before the interrupt is triggered.
* Simply wait until it is not 0 and then read the count. */
do {
__set_PRIMASK(0);
val = SysTick->VAL;
count = systick_count;
__set_PRIMASK(irqsaved);
} while (val == 0);
int64_t us = count * SYSTICK_INTERVAL_US +
(SysTick->LOAD - val) * 1000000LL / 32768;
return us;
}
static void systick_delay_precise(uint32_t us)
{
/*
* Calculate how long the busy-spin needs to be. As the very first
* instruction, read the current timer value to ensure as little skew as
* possible.
*
* Accuracy is about 30 us (due to the 32 kHz systick)
*/
uint32_t count_to_overflow = SysTick->VAL;
uint32_t count_reload = SysTick->LOAD;
uint32_t delay_count = us * 32768 / 1000000;
/*
* Calculate the final count for both paths. Marked as volatile so the
* compiler can't move this into the branches and screw up the timing.
*/
volatile uint32_t count_final_direct = count_to_overflow - delay_count;
volatile uint32_t count_final_underflow =
count_reload - (delay_count - count_to_overflow);
if (delay_count > count_to_overflow) {
/*
* Wait for the SysTick to underflow and then count down
* to the final value.
*/
while (SysTick->VAL <= count_to_overflow ||
SysTick->VAL > count_final_underflow) {
__NOP();
}
} else {
/*
* Wait for the SysTick to count down to the final value.
*/
while (SysTick->VAL > count_final_direct) {
__NOP();
}
}
}
static void systick_delay(uint32_t us)
{
if (us == 0)
return;
/*
* For very short delays, use the systick_delay_precise() function which
* delays with a microsecond accuracy. For anything > 10 ms, use
* systick_delay_sleep() which puts the CPU to sleep when nothing is
* happening and also checks for MicroPython interrupts every now and
* then.
*/
if (us < 10000) {
systick_delay_precise(us);
} else {
int64_t now = systick_get_us();
int64_t final_time = now + us;
while (final_time - systick_get_us() > 10000) {
uint32_t sleep_time =
(final_time - systick_get_us()) / 1000;
/* We need to wake up at least in SYSTICK_INTERVAL_US to make
* sure we serve the systick interrupt. */
if (sleep_time > SYSTICK_INTERVAL_US / 1000) {
sleep_time = SYSTICK_INTERVAL_US / 1000;
}
/* Add some error margin to avoid issues with the clock accuracy
* of epicardium. We will account for the actual time via our
* (accurate) systick */
epic_sleep(sleep_time * 8 / 10);
/* epic_sleep() can return early if there was an interrupt
* coming from epicardium side.
* Give MP a chance to handle them. */
mp_handle_pending(true);
}
now = systick_get_us();
if (final_time > now) {
systick_delay_precise(final_time - now);
}
}
}
/******************************************************************************
* Time & Delay
*/
void mp_hal_delay_ms(mp_uint_t ms)
{
systick_delay(ms * 1000);
}
void mp_hal_delay_us(mp_uint_t us)
{
systick_delay(us);
}
mp_uint_t mp_hal_ticks_ms(void)
{
return (mp_uint_t)(systick_get_us() / 1000);
}
mp_uint_t mp_hal_ticks_us(void)
{
return (mp_uint_t)systick_get_us();
}
/******************************************************************************
* Fatal Errors
*/
void NORETURN nlr_jump_fail(void *val)
{
char msg[] = " >>> nlr_jump_fail <<<\r\n";
epic_uart_write_str(msg, sizeof(msg));
epic_exit(253);
}
/******************************************************************************
* CSPRNG
*/
int mp_hal_csprng_read_int(void)
{
int result;
epic_csprng_read((uint8_t *)&result, sizeof(result));
return result;
}