From 203af85503bc416ee063585ef310390198af338b Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sun, 9 Jul 2023 09:05:28 +0100 Subject: [PATCH] Add shell event handler --- src/rt/shell.c | 88 +++++++++++++++++++++++++++++++++++++------- src/rt/shell.h | 7 ++++ test/shell/wave1.vhd | 10 +++++ test/test_shell.c | 73 ++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 test/shell/wave1.vhd diff --git a/src/rt/shell.c b/src/rt/shell.c index e1c44ec9..ea62883c 100644 --- a/src/rt/shell.c +++ b/src/rt/shell.c @@ -59,6 +59,8 @@ typedef struct { ident_t name; ident_t path; print_func_t *printer; + rt_watch_t *watch; + tcl_shell_t *owner; } shell_signal_t; typedef char *(*get_line_fn_t)(tcl_shell_t *); @@ -82,6 +84,7 @@ typedef struct _tcl_shell { get_line_fn_t getline; jit_factory_t make_jit; unit_registry_t *registry; + shell_handler_t handler; } tcl_shell_t; static __thread tcl_shell_t *rl_shell = NULL; @@ -117,7 +120,7 @@ static void shell_update_now(tcl_shell_t *sh) Tcl_UpdateLinkedVar(sh->interp, "deltas"); } -static void shell_cmd_add(tcl_shell_t *sh, const char *name, Tcl_ObjCmdProc fn, +static void shell_add_cmd(tcl_shell_t *sh, const char *name, Tcl_ObjCmdProc fn, const char *help) { shell_cmd_t cmd = { name, fn, help }; @@ -465,6 +468,57 @@ static int shell_cmd_examine(ClientData cd, Tcl_Interp *interp, Tcl_GetString(objv[0])); } +static void shell_event_cb(uint64_t now, rt_signal_t *s, rt_watch_t *w, + void *user) +{ + shell_signal_t *ss = user; + shell_handler_t *h = &(ss->owner->handler); + + if (h->signal_update != NULL) + (*h->signal_update)(ss->path, now, s, h->context); +} + +static const char add_help[] = + "Add signals and other objects to the display\n" + "\n" + "Syntax:\n" + " add wave \n" + "\n" + "Examples:\n" + " add wave /*\tAdd all signals to waveform\n"; + +static int shell_cmd_add(ClientData cd, Tcl_Interp *interp, + int objc, Tcl_Obj *const objv[]) +{ + tcl_shell_t *sh = cd; + + if (objc != 3 || strcmp(Tcl_GetString(objv[1]), "wave") != 0) + goto usage; + else if (!shell_has_model(sh)) + return TCL_ERROR; + + const char *glob = Tcl_GetString(objv[2]); + for (int i = 0; i < sh->nsignals; i++) { + shell_signal_t *ss = &(sh->signals[i]); + if (!ident_glob(ss->path, glob, -1)) + continue; + + if (sh->handler.add_wave != NULL) + (*sh->handler.add_wave)(ss->path, ss->signal, sh->handler.context); + + assert(ss->watch == NULL); + assert(find_watch(&(ss->signal->nexus), shell_event_cb) == NULL); + + ss->watch = model_set_event_cb(sh->model, ss->signal, + shell_event_cb, ss, true); + } + + return TCL_OK; + + usage: + return tcl_error(sh, "syntax error, enter $bold$help add for usage"); +} + static const char help_help[] = "Display list of commands or detailed help\n" "\n" @@ -669,18 +723,19 @@ tcl_shell_t *shell_new(jit_factory_t make_jit) atexit(Tcl_Finalize); - shell_cmd_add(sh, "help", shell_cmd_help, help_help); - shell_cmd_add(sh, "exit", shell_cmd_exit, exit_help); - shell_cmd_add(sh, "copyright", shell_cmd_copyright, copyright_help); - shell_cmd_add(sh, "find", shell_cmd_find, find_help); - shell_cmd_add(sh, "run", shell_cmd_run, run_help); - shell_cmd_add(sh, "restart", shell_cmd_restart, restart_help); - shell_cmd_add(sh, "analyse", shell_cmd_analyse, analyse_help); - shell_cmd_add(sh, "vcom", shell_cmd_analyse, analyse_help); - shell_cmd_add(sh, "elaborate", shell_cmd_elaborate, elaborate_help); - shell_cmd_add(sh, "vsim", shell_cmd_elaborate, elaborate_help); - shell_cmd_add(sh, "examine", shell_cmd_examine, examine_help); - shell_cmd_add(sh, "exa", shell_cmd_examine, examine_help); + shell_add_cmd(sh, "help", shell_cmd_help, help_help); + shell_add_cmd(sh, "exit", shell_cmd_exit, exit_help); + shell_add_cmd(sh, "copyright", shell_cmd_copyright, copyright_help); + shell_add_cmd(sh, "find", shell_cmd_find, find_help); + shell_add_cmd(sh, "run", shell_cmd_run, run_help); + shell_add_cmd(sh, "restart", shell_cmd_restart, restart_help); + shell_add_cmd(sh, "analyse", shell_cmd_analyse, analyse_help); + shell_add_cmd(sh, "vcom", shell_cmd_analyse, analyse_help); + shell_add_cmd(sh, "elaborate", shell_cmd_elaborate, elaborate_help); + shell_add_cmd(sh, "vsim", shell_cmd_elaborate, elaborate_help); + shell_add_cmd(sh, "examine", shell_cmd_examine, examine_help); + shell_add_cmd(sh, "exa", shell_cmd_examine, examine_help); + shell_add_cmd(sh, "add", shell_cmd_add, add_help); qsort(sh->cmds, sh->ncmds, sizeof(shell_cmd_t), compare_shell_cmd); @@ -740,6 +795,7 @@ static void recurse_signals(tcl_shell_t *sh, rt_scope_t *scope, shell_signal_t *ss = (*wptr)++; ss->signal = s; ss->name = ident_downcase(tree_ident(s->where)); + ss->owner = sh; tb_istr(path, ss->name); ss->path = ident_new(tb_get(path)); @@ -752,6 +808,7 @@ static void recurse_signals(tcl_shell_t *sh, rt_scope_t *scope, shell_signal_t *ss = (*wptr)++; ss->signal = a->signal; ss->name = ident_downcase(tree_ident(a->where)); + ss->owner = sh; tb_istr(path, ss->name); ss->path = ident_new(tb_get(path)); @@ -831,3 +888,8 @@ bool shell_do(tcl_shell_t *sh, const char *file) return false; } } + +void shell_set_handler(tcl_shell_t *sh, const shell_handler_t *h) +{ + sh->handler = *h; +} diff --git a/src/rt/shell.h b/src/rt/shell.h index bc9852d5..b5b05455 100644 --- a/src/rt/shell.h +++ b/src/rt/shell.h @@ -22,11 +22,18 @@ typedef jit_t *(*jit_factory_t)(unit_registry_t *); +typedef struct { + void (*add_wave)(ident_t path, rt_signal_t *s, void *ctx); + void (*signal_update)(ident_t path, uint64_t now, rt_signal_t *s, void *ctx); + void *context; +} shell_handler_t; + tcl_shell_t *shell_new(jit_factory_t make_jit); void shell_free(tcl_shell_t *sh); bool shell_eval(tcl_shell_t *sh, const char *script, const char **result); bool shell_do(tcl_shell_t *sh, const char *file); void shell_interact(tcl_shell_t *sh); void shell_reset(tcl_shell_t *sh, tree_t top); +void shell_set_handler(tcl_shell_t *sh, const shell_handler_t *h); #endif // _RT_SHELL_H diff --git a/test/shell/wave1.vhd b/test/shell/wave1.vhd new file mode 100644 index 00000000..66bb60c7 --- /dev/null +++ b/test/shell/wave1.vhd @@ -0,0 +1,10 @@ +entity wave1 is +end entity; + +architecture test of wave1 is + signal x : bit; +begin + + x <= '1' after 1 ns, '0' after 2 ns; + +end architecture; diff --git a/test/test_shell.c b/test/test_shell.c index 6f45444a..d4eb7f9c 100644 --- a/test/test_shell.c +++ b/test/test_shell.c @@ -101,6 +101,78 @@ START_TEST(test_examine1) } END_TEST +static void wave1_add_wave(ident_t path, rt_signal_t *s, void *user) +{ + int *state = user; + + switch ((*state)++) { + case 0: + ck_assert_str_eq(istr(path), "/x"); + break; + default: + ck_abort_msg("unexpected call to wave1_add_wave in state %d", *state - 1); + } +} + +static void wave1_signal_update(ident_t path, uint64_t now, rt_signal_t *s, + void *user) +{ + int *state = user; + + switch ((*state)++) { + case 1: + ck_assert_str_eq(istr(path), "/x"); + ck_assert_int_eq(now, 1000000); + break; + case 2: + ck_assert_str_eq(istr(path), "/x"); + ck_assert_int_eq(now, 2000000); + break; + default: + ck_abort_msg("unexpected call to wave1_signal_update in state %d", + *state - 1); + } +} + +START_TEST(test_wave1) +{ + const error_t expect[] = { + { -1, NULL } + }; + expect_errors(expect); + + tcl_shell_t *sh = shell_new(jit_new); + + int state = 0; + shell_handler_t handler = { + .add_wave = wave1_add_wave, + .signal_update = wave1_signal_update, + .context = &state, + }; + shell_set_handler(sh, &handler); + + const char *result = NULL; + + shell_eval(sh, "analyse " TESTDIR "/shell/wave1.vhd", &result); + ck_assert_str_eq(result, ""); + + shell_eval(sh, "elaborate wave1", &result); + ck_assert_str_eq(result, ""); + + shell_eval(sh, "add wave /x", &result); + ck_assert_str_eq(result, ""); + ck_assert_int_eq(state, 1); + + shell_eval(sh, "run", &result); + ck_assert_str_eq(result, ""); + ck_assert_int_eq(state, 3); + + shell_free(sh); + + check_expected_errors(); +} +END_TEST + Suite *get_shell_tests(void) { Suite *s = suite_create("shell"); @@ -109,6 +181,7 @@ Suite *get_shell_tests(void) tcase_add_test(tc, test_sanity); tcase_add_test(tc, test_analyse); tcase_add_test(tc, test_examine1); + tcase_add_test(tc, test_wave1); suite_add_tcase(s, tc); return s; -- 2.39.2