mphalport.c 6.7 KB
Newer Older
Rahix's avatar
Rahix committed
1
#include "epicardium.h"
2
#include "api/common.h"
Rahix's avatar
Rahix committed
3
4
5
6
7
8

#include "max32665.h"
#include "mxc_delay.h"
#include "tmr.h"

/* stdarg.h must be included before mpprint.h */
Rahix's avatar
Rahix committed
9
#include <stdarg.h>
Rahix's avatar
Rahix committed
10
11
12
13

#include "py/lexer.h"
#include "py/mpconfig.h"
#include "py/mperrno.h"
14
#include "py/mpstate.h"
Rahix's avatar
Rahix committed
15
16
#include "py/obj.h"
#include "py/runtime.h"
17
#include "py/mpprint.h"
Rahix's avatar
Rahix committed
18

Rahix's avatar
Rahix committed
19
20
21
#include <stdint.h>
#include <stdio.h>
#include <string.h>
22

23
24
25
26
27
28
29
30
31
32
33
/* 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);
34
35
36
37
38

	/*
	 * Configure SysTick timer for 1ms period.
	 */
	SysTick_Config(SystemCoreClock / 1000);
39
40
}

Rahix's avatar
Rahix committed
41
42
43
44
45
46
47
/******************************************************************************
 * Serial Communication
 */

/* Receive single character */
int mp_hal_stdin_rx_chr(void)
{
48
49
50
51
52
	int chr;
	while ((chr = epic_uart_read_char()) < 0) {
		__WFI();
	}
	return chr;
Rahix's avatar
Rahix committed
53
54
}

55
/* Send a string */
Rahix's avatar
Rahix committed
56
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len)
Rahix's avatar
Rahix committed
57
{
Rahix's avatar
Rahix committed
58
	epic_uart_write_str(str, len);
Rahix's avatar
Rahix committed
59
60
}

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/* 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.
	 */
	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));
}

Rahix's avatar
Rahix committed
85
86
87
88
89
/* Used by MicroPython for debug output */
int DEBUG_printf(const char *fmt, ...)
{
	va_list args;
	va_start(args, fmt);
90
	int ret = mp_vprintf(MP_PYTHON_PRINTER, fmt, args);
Rahix's avatar
Rahix committed
91
92
93
94
	va_end(args);
	return ret;
}

95
96
void __attribute__((noreturn)) sbrk_is_not_implemented___see_issue_44(void);
intptr_t _sbrk(int incr)
97
{
98
	sbrk_is_not_implemented___see_issue_44();
99
100
}

101
void epic_isr_ctrl_c(void)
102
{
103
104
105
	/* Taken from lib/micropython/micropython/lib/utils/interrupt_char.c */
	MP_STATE_VM(mp_pending_exception) =
		MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception));
Rahix's avatar
Rahix committed
106
#if MICROPY_ENABLE_SCHEDULER
107
108
	if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {
		MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
109
	}
110
#endif
111
112
113
114
}

void mp_hal_set_interrupt_char(char c)
{
Rahix's avatar
Rahix committed
115
116
117
118
119
	if (c != -1) {
		mp_obj_exception_clear_traceback(
			MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))
		);
	}
120
121

	if (c == 0x03) {
122
		epic_interrupt_enable(EPIC_INT_CTRL_C);
123
	} else {
124
		epic_interrupt_disable(EPIC_INT_CTRL_C);
125
	}
126
127
}

128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/******************************************************************************
 * SysTick timer at 1000 Hz
 */

static volatile uint64_t systick_count = 0;

void SysTick_Handler(void)
{
	systick_count += 1;
}

/*
 * Get an absolute "timestamp" in microseconds.
 */
static uint64_t systick_get_us()
{
	uint32_t irqsaved = __get_PRIMASK();
	__set_PRIMASK(0);

	uint64_t counts_per_us = SystemCoreClock / 1000000;
	uint64_t us            = systick_count * 1000 +
		      (SysTick->LOAD - SysTick->VAL) / counts_per_us;

	__set_PRIMASK(irqsaved);

	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.
	 *
	 * Subtract 0.3us (constant_offset) to account for the duration of the
	 * calculations.
	 */
	uint32_t count_to_overflow = SysTick->VAL;
	uint32_t count_reload      = SysTick->LOAD;
	uint32_t clocks_per_us     = SystemCoreClock / 1000000;
	uint32_t constant_offset   = clocks_per_us * 3 / 10;
	uint32_t delay_count       = us * clocks_per_us - constant_offset;

	/*
	 * 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_sleep(uint32_t us)
{
	uint64_t final_time = systick_get_us() + (uint64_t)us - 2;

	while (1) {
		uint64_t now = systick_get_us();

		if (now >= final_time) {
			break;
		}

		/*
		 * Sleep with WFI if more than 1ms of delay is remaining.  The
		 * SysTick interrupt is guaranteed to happen within any timespan
		 * of 1ms.
		 *
		 * Use a critical section encompassing both the check and the
		 * WFI to prevent a race-condition where the interrupt happens
		 * just in between the check and WFI.
		 */
		uint32_t irqsaved = __get_PRIMASK();
		__set_PRIMASK(0);
		if ((now + 1000) < final_time) {
			__WFI();
		}
		__set_PRIMASK(irqsaved);

		/*
		 * Handle pending MicroPython 'interrupts'.  This call could
		 * potentially not return here when a handler raises an
		 * exception.  Those will propagate outwards and thus make the
		 * delay return early.
		 *
		 * One example of this happeing is the KeyboardInterrupt
		 * (CTRL+C) which will abort the running code and exit to REPL.
		 */
		mp_handle_pending();
	}
}

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 >1ms, 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 < 1000) {
		systick_delay_precise(us);
	} else {
		systick_delay_sleep(us);
	}
}

Rahix's avatar
Rahix committed
258
259
260
261
262
263
/******************************************************************************
 * Time & Delay
 */

void mp_hal_delay_ms(mp_uint_t ms)
{
264
	systick_delay(ms * 1000);
Rahix's avatar
Rahix committed
265
266
267
268
}

void mp_hal_delay_us(mp_uint_t us)
{
269
	systick_delay(us);
Rahix's avatar
Rahix committed
270
271
}

272
273
274
275
276
mp_uint_t mp_hal_ticks_ms(void)
{
	return 0;
}

Rahix's avatar
Rahix committed
277
278
279
280
/******************************************************************************
 * Fatal Errors
 */

Rahix's avatar
Rahix committed
281
extern NORETURN void *Reset_Handler(void);
282

Rahix's avatar
Rahix committed
283
void NORETURN nlr_jump_fail(void *val)
Rahix's avatar
Rahix committed
284
{
Rahix's avatar
Rahix committed
285
286
287
	char msg[] = " >>> nlr_jump_fail <<<\r\n";
	epic_uart_write_str(msg, sizeof(msg));

288
	Reset_Handler();
Rahix's avatar
Rahix committed
289
}
290
291
292
293
294
295
296
297
298
299
300

/******************************************************************************
 * TRNG
 */

int mp_hal_trng_read_int(void)
{
	int result;
	epic_trng_read((uint8_t *)&result, sizeof(result));
	return result;
}