summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--stmhal/pyexec.c60
-rw-r--r--stmhal/readline.c3
-rw-r--r--stmhal/usbd_cdc_interface.c7
-rwxr-xr-x[-rw-r--r--]tools/pyboard.py111
4 files changed, 138 insertions, 43 deletions
diff --git a/stmhal/pyexec.c b/stmhal/pyexec.c
index 08a5866a04..3fd797c0d3 100644
--- a/stmhal/pyexec.c
+++ b/stmhal/pyexec.c
@@ -54,31 +54,47 @@
pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
STATIC bool repl_display_debugging_info = 0;
+#define EXEC_FLAG_PRINT_EOF (1)
+#define EXEC_FLAG_ALLOW_DEBUGGING (2)
+#define EXEC_FLAG_IS_REPL (4)
+
// parses, compiles and executes the code in the lexer
// frees the lexer before returning
-int parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, bool is_repl) {
+// EXEC_FLAG_PRINT_EOF prints 2 EOF chars: 1 after normal output, 1 after exception output
+// EXEC_FLAG_ALLOW_DEBUGGING allows debugging info to be printed after executing the code
+// EXEC_FLAG_IS_REPL is used for REPL inputs (flag passed on to mp_compile)
+STATIC int parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, int exec_flags) {
+ int ret = 0;
+
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);
+ // check for parse error
if (pn == MP_PARSE_NODE_NULL) {
- // parse error
+ if (exec_flags & EXEC_FLAG_PRINT_EOF) {
+ stdout_tx_strn("\x04", 1);
+ }
mp_parse_show_exception(lex, parse_error_kind);
mp_lexer_free(lex);
- return 0;
+ goto finish;
}
mp_lexer_free(lex);
- mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, is_repl);
+ mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, exec_flags & EXEC_FLAG_IS_REPL);
+ // check for compile error
if (mp_obj_is_exception_instance(module_fun)) {
+ if (exec_flags & EXEC_FLAG_PRINT_EOF) {
+ stdout_tx_strn("\x04", 1);
+ }
mp_obj_print_exception(module_fun);
- return 0;
+ goto finish;
}
+ // execute code
nlr_buf_t nlr;
- int 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
@@ -86,10 +102,17 @@ int parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, boo
usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
nlr_pop();
ret = 1;
+ if (exec_flags & EXEC_FLAG_PRINT_EOF) {
+ stdout_tx_strn("\x04", 1);
+ }
} 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
+ // print EOF after normal output
+ if (exec_flags & EXEC_FLAG_PRINT_EOF) {
+ stdout_tx_strn("\x04", 1);
+ }
// check for SystemExit
if (mp_obj_is_subclass_fast(mp_obj_get_type((mp_obj_t)nlr.ret_val), &mp_type_SystemExit)) {
// at the moment, the value of SystemExit is unused
@@ -101,7 +124,7 @@ int parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, boo
}
// display debugging info if wanted
- if (is_repl && repl_display_debugging_info) {
+ if ((exec_flags & EXEC_FLAG_ALLOW_DEBUGGING) && 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();
@@ -123,6 +146,11 @@ int parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, boo
}
}
+finish:
+ if (exec_flags & EXEC_FLAG_PRINT_EOF) {
+ stdout_tx_strn("\x04", 1);
+ }
+
return ret;
}
@@ -171,16 +199,13 @@ raw_repl_reset:
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line.buf, line.len, 0);
if (lex == NULL) {
- printf("MemoryError\n");
+ printf("\x04MemoryError\n\x04");
} else {
- int ret = parse_compile_execute(lex, MP_PARSE_FILE_INPUT, false);
+ int ret = parse_compile_execute(lex, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF);
if (ret & PYEXEC_FORCED_EXIT) {
return ret;
}
}
-
- // indicate end of output with EOF character
- stdout_tx_str("\004");
}
}
@@ -217,6 +242,7 @@ friendly_repl_reset:
*/
for (;;) {
+ input_restart:
vstr_reset(&line);
int ret = readline(&line, ">>> ");
@@ -246,7 +272,11 @@ friendly_repl_reset:
while (mp_repl_continue_with_input(vstr_str(&line))) {
vstr_add_char(&line, '\n');
int ret = readline(&line, "... ");
- if (ret == VCP_CHAR_CTRL_D) {
+ if (ret == VCP_CHAR_CTRL_C) {
+ // cancel everything
+ stdout_tx_str("\r\n");
+ goto input_restart;
+ } else if (ret == VCP_CHAR_CTRL_D) {
// stop entering compound statement
break;
}
@@ -256,7 +286,7 @@ friendly_repl_reset:
if (lex == NULL) {
printf("MemoryError\n");
} else {
- int ret = parse_compile_execute(lex, MP_PARSE_SINGLE_INPUT, true);
+ int ret = parse_compile_execute(lex, MP_PARSE_SINGLE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL);
if (ret & PYEXEC_FORCED_EXIT) {
return ret;
}
@@ -272,7 +302,7 @@ int pyexec_file(const char *filename) {
return false;
}
- return parse_compile_execute(lex, MP_PARSE_FILE_INPUT, false);
+ return parse_compile_execute(lex, MP_PARSE_FILE_INPUT, 0);
}
mp_obj_t pyb_set_repl_info(mp_obj_t o_value) {
diff --git a/stmhal/readline.c b/stmhal/readline.c
index 5ef86c44fa..4b1f48baae 100644
--- a/stmhal/readline.c
+++ b/stmhal/readline.c
@@ -86,6 +86,9 @@ int readline(vstr_t *line, const char *prompt) {
} else if (c == VCP_CHAR_CTRL_A) {
// CTRL-A with non-empty line is go-to-start-of-line
goto home_key;
+ } else if (c == VCP_CHAR_CTRL_C) {
+ // CTRL-C with non-empty line is cancel
+ return c;
} else if (c == VCP_CHAR_CTRL_E) {
// CTRL-E is go-to-end-of-line
goto end_key;
diff --git a/stmhal/usbd_cdc_interface.c b/stmhal/usbd_cdc_interface.c
index 28f06067bb..e9b4863f6b 100644
--- a/stmhal/usbd_cdc_interface.c
+++ b/stmhal/usbd_cdc_interface.c
@@ -364,6 +364,8 @@ static int8_t CDC_Itf_Receive(uint8_t* Buf, uint32_t *Len) {
for (; src < buf_top; src++) {
if (*src == user_interrupt_char) {
char_found = true;
+ // raise exception when interrupts are finished
+ pendsv_nlr_jump(user_interrupt_data);
} else {
if (char_found) {
*dest = *src;
@@ -372,11 +374,6 @@ static int8_t CDC_Itf_Receive(uint8_t* Buf, uint32_t *Len) {
}
}
- if (char_found) {
- // raise exception when interrupts are finished
- pendsv_nlr_jump(user_interrupt_data);
- }
-
// length of remaining characters
delta_len = dest - Buf;
}
diff --git a/tools/pyboard.py b/tools/pyboard.py
index 40f2ac934c..d5a01720b7 100644..100755
--- a/tools/pyboard.py
+++ b/tools/pyboard.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
"""
pyboard interface
@@ -19,10 +21,15 @@ To run a script from the local machine on the board and print out the results:
This script can also be run directly. To execute a local script, use:
+ ./pyboard.py test.py
+
+Or:
+
python pyboard.py test.py
"""
+import sys
import time
import serial
@@ -31,21 +38,26 @@ class PyboardError(BaseException):
class Pyboard:
def __init__(self, serial_device):
- self.serial = serial.Serial(serial_device)
+ self.serial = serial.Serial(serial_device, baudrate=115200, interCharTimeout=1)
def close(self):
self.serial.close()
- def read_until(self, min_num_bytes, ending, timeout=10):
+ def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None):
data = self.serial.read(min_num_bytes)
+ if data_consumer:
+ data_consumer(data)
timeout_count = 0
while True:
- if self.serial.inWaiting() > 0:
- data = data + self.serial.read(self.serial.inWaiting())
- time.sleep(0.01)
- timeout_count = 0
- elif data.endswith(ending):
+ if data.endswith(ending):
break
+ elif self.serial.inWaiting() > 0:
+ new_data = self.serial.read(1)
+ data = data + new_data
+ if data_consumer:
+ data_consumer(new_data)
+ #time.sleep(0.01)
+ timeout_count = 0
else:
timeout_count += 1
if timeout_count >= 10 * timeout:
@@ -54,8 +66,12 @@ class Pyboard:
return data
def enter_raw_repl(self):
- self.serial.write(b'\r\x03') # ctrl-C: interrupt any running program
+ self.serial.write(b'\r\x03\x03') # ctrl-C twice: interrupt any running program
self.serial.write(b'\r\x01') # ctrl-A: enter raw REPL
+ data = self.read_until(1, b'to exit\r\n>')
+ if not data.endswith(b'raw REPL; CTRL-B to exit\r\n>'):
+ print(data)
+ raise PyboardError('could not enter raw repl')
self.serial.write(b'\x04') # ctrl-D: soft reset
data = self.read_until(1, b'to exit\r\n>')
if not data.endswith(b'raw REPL; CTRL-B to exit\r\n>'):
@@ -65,31 +81,51 @@ class Pyboard:
def exit_raw_repl(self):
self.serial.write(b'\r\x02') # ctrl-B: enter friendly REPL
- def eval(self, expression):
- ret = self.exec('print({})'.format(expression))
- ret = ret.strip()
- return ret
+ def follow(self, data_consumer=False):
+ # wait for normal output
+ data = self.read_until(1, b'\x04', data_consumer=data_consumer)
+ if not data.endswith(b'\x04'):
+ raise PyboardError('timeout waiting for first EOF reception')
+ data = data[:-1]
- def exec(self, command):
+ # wait for error output
+ data_err = self.read_until(2, b'\x04>')
+ if not data_err.endswith(b'\x04>'):
+ raise PyboardError('timeout waiting for second EOF reception')
+ data_err = data_err[:-2]
+
+ # return normal and error output
+ return data, data_err
+
+ def exec_raw(self, command, data_consumer=False):
if isinstance(command, bytes):
command_bytes = command
else:
command_bytes = bytes(command, encoding='ascii')
+
+ # write command
for i in range(0, len(command_bytes), 32):
self.serial.write(command_bytes[i:min(i+32, len(command_bytes))])
time.sleep(0.01)
self.serial.write(b'\x04')
+
+ # check if we could exec command
data = self.serial.read(2)
if data != b'OK':
raise PyboardError('could not exec command')
- data = self.read_until(2, b'\x04>')
- if not data.endswith(b'\x04>'):
- print(data)
- raise PyboardError('timeout waiting for EOF reception')
- if data.startswith(b'Traceback') or data.startswith(b' File '):
- print(data)
- raise PyboardError('command failed')
- return data[:-2]
+
+ return self.follow(data_consumer)
+
+ def eval(self, expression):
+ ret = self.exec('print({})'.format(expression))
+ ret = ret.strip()
+ return ret
+
+ def exec(self, command):
+ ret, ret_err = self.exec_raw(command)
+ if ret_err:
+ raise PyboardError('exception', ret, ret_err)
+ return ret
def execfile(self, filename):
with open(filename) as f:
@@ -175,8 +211,37 @@ def main():
if args.test:
run_test(device=args.device)
- for file in args.files:
- execfile(file, device=args.device)
+ if len(args.files) == 0:
+ try:
+ pyb = Pyboard(args.device)
+ ret, ret_err = pyb.follow(data_consumer=lambda d:print(str(d, encoding='ascii'), end=''))
+ pyb.close()
+ except PyboardError as er:
+ print(er)
+ sys.exit(1)
+ except KeyboardInterrupt:
+ sys.exit(1)
+ if ret_err:
+ print(str(ret_err, encoding='ascii'), end='')
+ sys.exit(1)
+
+ for filename in args.files:
+ try:
+ pyb = Pyboard(args.device)
+ pyb.enter_raw_repl()
+ with open(filename) as f:
+ pyfile = f.read()
+ ret, ret_err = pyb.exec_raw(pyfile, data_consumer=lambda d:print(str(d, encoding='ascii'), end=''))
+ pyb.exit_raw_repl()
+ pyb.close()
+ except PyboardError as er:
+ print(er)
+ sys.exit(1)
+ except KeyboardInterrupt:
+ sys.exit(1)
+ if ret_err:
+ print(str(ret_err, encoding='ascii'), end='')
+ sys.exit(1)
if __name__ == "__main__":
main()