6d983539bc
Internal flash used for the filesystem is now written (from the cache) only after a 5s delay, or when a file is closed, or when the drive is unmounted from the host. This delay means that multiple writes can accumulate in the cache, and leads to less writes to the flash, making it last longer. It's implemented by a high-priority interrupt that takes care of flash erase and write, and flushing the cache. This is still only an interim solution for the flash filesystem. It eventually needs to be replaced with something that uses less RAM for the cache, something that can use more of the flash, and something that does proper wear levelling.
237 lines
7.0 KiB
C
237 lines
7.0 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include <stm32f4xx_hal.h>
|
|
|
|
#include "nlr.h"
|
|
#include "misc.h"
|
|
#include "mpconfig.h"
|
|
#include "qstr.h"
|
|
#include "misc.h"
|
|
#include "lexer.h"
|
|
#include "parse.h"
|
|
#include "obj.h"
|
|
#include "parsehelper.h"
|
|
#include "compile.h"
|
|
#include "runtime.h"
|
|
#include "repl.h"
|
|
#include "gc.h"
|
|
#include "gccollect.h"
|
|
#include "systick.h"
|
|
#include "pybstdio.h"
|
|
#include "readline.h"
|
|
#include "pyexec.h"
|
|
#include "usb.h"
|
|
#include "build/py/py-version.h"
|
|
|
|
pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
|
|
STATIC bool repl_display_debugging_info = 0;
|
|
|
|
// parses, compiles and executes the code in the lexer
|
|
// frees the lexer before returning
|
|
bool parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, bool is_repl) {
|
|
mp_parse_error_kind_t parse_error_kind;
|
|
mp_parse_node_t pn = mp_parse(lex, input_kind, &parse_error_kind);
|
|
qstr source_name = mp_lexer_source_name(lex);
|
|
|
|
if (pn == MP_PARSE_NODE_NULL) {
|
|
// parse error
|
|
mp_parse_show_exception(lex, parse_error_kind);
|
|
mp_lexer_free(lex);
|
|
return false;
|
|
}
|
|
|
|
mp_lexer_free(lex);
|
|
|
|
mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, is_repl);
|
|
mp_parse_node_free(pn);
|
|
|
|
if (module_fun == mp_const_none) {
|
|
return false;
|
|
}
|
|
|
|
nlr_buf_t nlr;
|
|
bool ret;
|
|
uint32_t start = HAL_GetTick();
|
|
if (nlr_push(&nlr) == 0) {
|
|
usb_vcp_set_interrupt_char(VCP_CHAR_CTRL_C); // allow ctrl-C to interrupt us
|
|
mp_call_function_0(module_fun);
|
|
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
|
|
nlr_pop();
|
|
ret = true;
|
|
} else {
|
|
// uncaught exception
|
|
// FIXME it could be that an interrupt happens just before we disable it here
|
|
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
|
|
mp_obj_print_exception((mp_obj_t)nlr.ret_val);
|
|
ret = false;
|
|
}
|
|
|
|
// display debugging info if wanted
|
|
if (is_repl && repl_display_debugging_info) {
|
|
uint32_t ticks = HAL_GetTick() - start; // TODO implement a function that does this properly
|
|
printf("took %lu ms\n", ticks);
|
|
gc_collect();
|
|
// qstr info
|
|
{
|
|
uint n_pool, n_qstr, n_str_data_bytes, n_total_bytes;
|
|
qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes);
|
|
printf("qstr:\n n_pool=%u\n n_qstr=%u\n n_str_data_bytes=%u\n n_total_bytes=%u\n", n_pool, n_qstr, n_str_data_bytes, n_total_bytes);
|
|
}
|
|
|
|
// GC info
|
|
{
|
|
gc_info_t info;
|
|
gc_info(&info);
|
|
printf("GC:\n");
|
|
printf(" %lu total\n", info.total);
|
|
printf(" %lu : %lu\n", info.used, info.free);
|
|
printf(" 1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int pyexec_raw_repl(void) {
|
|
vstr_t line;
|
|
vstr_init(&line, 32);
|
|
|
|
raw_repl_reset:
|
|
stdout_tx_str("raw REPL; CTRL-B to exit\r\n");
|
|
|
|
for (;;) {
|
|
vstr_reset(&line);
|
|
stdout_tx_str(">");
|
|
for (;;) {
|
|
char c = stdin_rx_chr();
|
|
if (c == VCP_CHAR_CTRL_A) {
|
|
// reset raw REPL
|
|
goto raw_repl_reset;
|
|
} else if (c == VCP_CHAR_CTRL_B) {
|
|
// change to friendly REPL
|
|
stdout_tx_str("\r\n");
|
|
vstr_clear(&line);
|
|
pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
|
|
return 0;
|
|
} else if (c == VCP_CHAR_CTRL_C) {
|
|
// clear line
|
|
vstr_reset(&line);
|
|
} else if (c == VCP_CHAR_CTRL_D) {
|
|
// input finished
|
|
break;
|
|
} else if (c <= 127) {
|
|
// let through any other ASCII character
|
|
vstr_add_char(&line, c);
|
|
}
|
|
}
|
|
|
|
// indicate reception of command
|
|
stdout_tx_str("OK");
|
|
|
|
if (line.len == 0) {
|
|
// exit for a soft reset
|
|
stdout_tx_str("\r\n");
|
|
vstr_clear(&line);
|
|
return 1;
|
|
}
|
|
|
|
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line.buf, line.len, 0);
|
|
parse_compile_execute(lex, MP_PARSE_FILE_INPUT, false);
|
|
|
|
// indicate end of output with EOF character
|
|
stdout_tx_str("\004");
|
|
}
|
|
}
|
|
|
|
int pyexec_friendly_repl(void) {
|
|
vstr_t line;
|
|
vstr_init(&line, 32);
|
|
|
|
#if defined(USE_HOST_MODE) && MICROPY_HW_HAS_LCD
|
|
// in host mode, we enable the LCD for the repl
|
|
mp_obj_t lcd_o = mp_call_function_0(mp_load_name(qstr_from_str("LCD")));
|
|
mp_call_function_1(mp_load_attr(lcd_o, qstr_from_str("light")), mp_const_true);
|
|
#endif
|
|
|
|
friendly_repl_reset:
|
|
stdout_tx_str("Micro Python build " MICROPY_GIT_HASH " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with STM32F405RG\r\n");
|
|
stdout_tx_str("Type \"help()\" for more information.\r\n");
|
|
|
|
// to test ctrl-C
|
|
/*
|
|
{
|
|
uint32_t x[4] = {0x424242, 0xdeaddead, 0x242424, 0xdeadbeef};
|
|
for (;;) {
|
|
nlr_buf_t nlr;
|
|
printf("pyexec_repl: %p\n", x);
|
|
usb_vcp_set_interrupt_char(VCP_CHAR_CTRL_C);
|
|
if (nlr_push(&nlr) == 0) {
|
|
for (;;) {
|
|
}
|
|
} else {
|
|
printf("break\n");
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
for (;;) {
|
|
vstr_reset(&line);
|
|
int ret = readline(&line, ">>> ");
|
|
|
|
if (ret == VCP_CHAR_CTRL_A) {
|
|
// change to raw REPL
|
|
stdout_tx_str("\r\n");
|
|
vstr_clear(&line);
|
|
pyexec_mode_kind = PYEXEC_MODE_RAW_REPL;
|
|
return 0;
|
|
} else if (ret == VCP_CHAR_CTRL_B) {
|
|
// reset friendly REPL
|
|
stdout_tx_str("\r\n");
|
|
goto friendly_repl_reset;
|
|
} else if (ret == VCP_CHAR_CTRL_C) {
|
|
// break
|
|
stdout_tx_str("\r\n");
|
|
continue;
|
|
} else if (ret == VCP_CHAR_CTRL_D) {
|
|
// exit for a soft reset
|
|
stdout_tx_str("\r\n");
|
|
vstr_clear(&line);
|
|
return 1;
|
|
} else if (vstr_len(&line) == 0) {
|
|
continue;
|
|
}
|
|
|
|
while (mp_repl_continue_with_input(vstr_str(&line))) {
|
|
vstr_add_char(&line, '\n');
|
|
int ret = readline(&line, "... ");
|
|
if (ret == VCP_CHAR_CTRL_D) {
|
|
// stop entering compound statement
|
|
break;
|
|
}
|
|
}
|
|
|
|
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr_str(&line), vstr_len(&line), 0);
|
|
parse_compile_execute(lex, MP_PARSE_SINGLE_INPUT, true);
|
|
}
|
|
}
|
|
|
|
bool pyexec_file(const char *filename) {
|
|
mp_lexer_t *lex = mp_lexer_new_from_file(filename);
|
|
|
|
if (lex == NULL) {
|
|
printf("could not open file '%s' for reading\n", filename);
|
|
return false;
|
|
}
|
|
|
|
return parse_compile_execute(lex, MP_PARSE_FILE_INPUT, false);
|
|
}
|
|
|
|
mp_obj_t pyb_set_repl_info(mp_obj_t o_value) {
|
|
repl_display_debugging_info = mp_obj_get_int(o_value);
|
|
return mp_const_none;
|
|
}
|
|
|
|
MP_DEFINE_CONST_FUN_OBJ_1(pyb_set_repl_info_obj, pyb_set_repl_info);
|