From 8d2c541504905928f08eb05702eae991cba15003 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 16 Jan 2024 21:58:10 +0000 Subject: [PATCH] Finish implementation of guarded blocks and disconnection specification Fixes #829 --- NEWS.md | 2 ++ src/parse.c | 39 ++++++++++++++++++++++--- src/rt/model.c | 60 ++++++++++++++------------------------- src/sem.c | 2 +- src/simp.c | 15 ++++++---- src/tree.c | 6 +++- src/tree.h | 1 + test/regress/issue829.vhd | 49 ++++++++++++++++++++++++++++++++ test/regress/testlist.txt | 1 + test/test_parse.c | 2 +- test/test_sem.c | 3 ++ 11 files changed, 130 insertions(+), 50 deletions(-) create mode 100644 test/regress/issue829.vhd diff --git a/NEWS.md b/NEWS.md index bbeaabb5..17c71fb8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,8 @@ - The `--jit` elaboration option no longer requires `--no-save`. - Fixed a crash when subtype bounds depend on a package instance generic (#815). +- Fixed various issues in the implementation of guarded blocks and + disconnection specifications (#829). ## Version 1.11.2 - 2024-01-04 - Fixed an incorrect length check in the equivalent process for diff --git a/src/parse.c b/src/parse.c index 497840bb..e13c4e41 100644 --- a/src/parse.c +++ b/src/parse.c @@ -2426,6 +2426,25 @@ static type_t name_to_type_mark(tree_t name) return tree_type(decl); } +static void find_disconnect_specification(tree_t guard, tree_t target) +{ + if (tree_kind(target) != T_REF) + return; + else if (!tree_has_ref(target)) + return; + + tree_t decl = tree_ref(target); + + // TODO: use insert_spec for this + ident_t name = ident_prefix(tree_ident(decl), ident_new("disconnect"), '$'); + + tree_t spec = NULL; + query_name(nametab, name, &spec); + + if (spec != NULL) + tree_set_spec(guard, spec); +} + //////////////////////////////////////////////////////////////////////////////// // Parser rules @@ -7276,6 +7295,10 @@ static void p_disconnection_specification(tree_t container) tree_set_delay(d, delay); tree_set_ref(d, resolve_name(nametab, CURRENT_LOC, it->ident)); + // TODO: we should use insert_spec for this + ident_t name = ident_prefix(it->ident, ident_new("disconnect"), '$'); + insert_name(nametab, d, name); + tree_add_decl(container, d); sem_check(d, nametab); } @@ -10305,8 +10328,12 @@ static void p_options(tree_t *reject, tree_t *guard) tree_t decl = NULL; name_mask_t mask = query_name(nametab, ident_new("GUARD"), &decl); if ((mask & N_OBJECT) && decl != NULL) { - *guard = make_ref(decl); - tree_set_loc(*guard, CURRENT_LOC); + tree_t g = tree_new(T_GUARD); + tree_set_loc(g, CURRENT_LOC); + tree_set_ref(g, decl); + tree_set_type(g, tree_type(decl)); + + *guard = g; } else parse_error(CURRENT_LOC, "guarded assignment has no visible " @@ -10370,8 +10397,10 @@ static tree_t p_conditional_signal_assignment(tree_t name) tree_t reject = NULL, guard = NULL; p_options(&reject, &guard); - if (guard != NULL) + if (guard != NULL) { tree_set_guard(conc, guard); + find_disconnect_specification(guard, target); + } p_conditional_waveforms(stmt, target, NULL); @@ -10452,8 +10481,10 @@ static tree_t p_selected_signal_assignment(void) tree_t reject = NULL, guard = NULL; p_options(&reject, &guard); - if (guard != NULL) + if (guard != NULL) { tree_set_guard(conc, guard); + find_disconnect_specification(guard, target); + } p_selected_waveforms(stmt, target, reject); consume(tSEMI); diff --git a/src/rt/model.c b/src/rt/model.c index 2b9797e1..85293c87 100644 --- a/src/rt/model.c +++ b/src/rt/model.c @@ -53,7 +53,6 @@ typedef enum { EVENT_TIMEOUT, EVENT_DRIVER, EVENT_PROCESS, - EVENT_DISCONNECT, } event_kind_t; #define MEMBLOCK_LINE_SZ 64 @@ -167,7 +166,6 @@ static void async_update_driver(rt_model_t *m, void *arg); static void async_fast_driver(rt_model_t *m, void *arg); static void async_fast_all_drivers(rt_model_t *m, void *arg); static void async_update_driving(rt_model_t *m, void *arg); -static void async_disconnect(rt_model_t *m, void *arg); static void async_deposit(rt_model_t *m, void *arg); static void async_transfer_signal(rt_model_t *m, void *arg); @@ -902,7 +900,7 @@ static void deltaq_insert_proc(rt_model_t *m, uint64_t delta, rt_proc_t *proc) } static void deltaq_insert_driver(rt_model_t *m, uint64_t delta, - rt_nexus_t *nexus, rt_source_t *source) + rt_source_t *source) { if (delta == 0) { deferq_do(&m->delta_driverq, async_update_driver, source); @@ -926,19 +924,6 @@ static void deltaq_insert_deposit(rt_model_t *m, rt_deposit_t *deposit) m->next_is_delta = true; } -static void deltaq_insert_disconnect(rt_model_t *m, uint64_t delta, - rt_source_t *source) -{ - if (delta == 0) { - deferq_do(&m->delta_driverq, async_disconnect, source); - m->next_is_delta = true; - } - else { - void *e = tag_pointer(source, EVENT_DISCONNECT); - heap_insert(m->eventq_heap, m->now + delta, e); - } -} - static void reset_process(rt_model_t *m, rt_proc_t *proc) { TRACE("reset process %s", istr(proc->name)); @@ -1555,7 +1540,7 @@ static void clone_source(rt_model_t *m, rt_nexus_t *nexus, split_value(nexus, &w_new->value, &w_old->value, offset); assert(w_old->when >= m->now); - deltaq_insert_driver(m, w_new->when - m->now, nexus, new); + deltaq_insert_driver(m, w_new->when - m->now, new); } } break; @@ -2452,7 +2437,7 @@ static inline bool insert_transaction(rt_model_t *m, rt_nexus_t *nexus, // delete the current transaction assert(it->when >= m->now); if (it->when >= when - reject - && (w == NULL || !cmp_values(nexus, it->value, w->value))) { + && !cmp_values(nexus, it->value, w->value)) { waveform_t *next = it->next; last->next = next; free_value(nexus, it->value); @@ -2543,7 +2528,7 @@ static void sched_driver(rt_model_t *m, rt_nexus_t *nexus, uint64_t after, copy_value_ptr(nexus, &w->value, value); if (!insert_transaction(m, nexus, d, w, w->when, reject)) - deltaq_insert_driver(m, after, nexus, d); + deltaq_insert_driver(m, after, d); } } @@ -2555,8 +2540,16 @@ static void sched_disconnect(rt_model_t *m, rt_nexus_t *nexus, uint64_t after, const uint64_t when = m->now + after; - insert_transaction(m, nexus, d, NULL, when, reject); - deltaq_insert_disconnect(m, after, d); + // Need update_driver to clear disconnected flag + nexus->flags &= ~NET_F_FAST_DRIVER; + + waveform_t *w = alloc_waveform(m); + w->when = -when; // Use sign bit to represent null + w->next = NULL; + w->value.qword = 0; + + if (!insert_transaction(m, nexus, d, w, when, reject)) + deltaq_insert_driver(m, after, d); } static void async_watch_callback(rt_model_t *m, void *arg) @@ -2807,15 +2800,20 @@ static void update_driver(rt_model_t *m, rt_nexus_t *nexus, rt_source_t *source) waveform_t *w_now = &(source->u.driver.waveforms); waveform_t *w_next = w_now->next; - if (likely((w_next != NULL) && (w_next->when == m->now))) { + if (likely(w_next != NULL && w_next->when == m->now)) { free_value(nexus, w_now->value); *w_now = *w_next; free_waveform(m, w_next); source->disconnected = 0; update_driving(m, nexus, driving_value(nexus)); } - else - assert(w_now != NULL); + else if (unlikely(w_next != NULL && w_next->when == -m->now)) { + // Disconnect source due to null transaction + *w_now = *w_next; + free_waveform(m, w_next); + source->disconnected = 1; + update_driving(m, nexus, driving_value(nexus)); + } } else // Update due to force/release update_driving(m, nexus, driving_value(nexus)); @@ -2890,14 +2888,6 @@ static void async_fast_all_drivers(rt_model_t *m, void *arg) fast_update_all_drivers(m, signal); } -static void async_disconnect(rt_model_t *m, void *arg) -{ - rt_source_t *src = arg; - - src->disconnected = 1; - update_driver(m, src->u.driver.nexus, NULL); -} - static void async_update_driving(rt_model_t *m, void *arg) { rt_nexus_t *nexus = arg; @@ -3066,12 +3056,6 @@ static void model_cycle(rt_model_t *m) deferq_do(&m->driverq, async_timeout_callback, cb); } break; - case EVENT_DISCONNECT: - { - rt_source_t *source = untag_pointer(e, rt_source_t); - deferq_do(&m->driverq, async_disconnect, source); - } - break; } if (heap_size(m->eventq_heap) == 0) diff --git a/src/sem.c b/src/sem.c index 0d1b7e32..68d27be4 100644 --- a/src/sem.c +++ b/src/sem.c @@ -2647,7 +2647,7 @@ static bool sem_check_signal_assign(tree_t t, nametab_t *tab) static bool sem_check_guard(tree_t t, nametab_t *tab) { - assert(tree_kind(t) == T_REF); + assert(tree_kind(t) == T_GUARD); if (!sem_check_type(t, std_type(NULL, STD_BOOLEAN), tab)) sem_error(t, "guard signal must have BOOLEAN type but found %s", diff --git a/src/simp.c b/src/simp.c index fbf88a95..26ef3237 100644 --- a/src/simp.c +++ b/src/simp.c @@ -999,14 +999,13 @@ static tree_t simp_guard(tree_t container, tree_t t, tree_t wait, tree_t s0) tree_t c0 = tree_new(T_COND_STMT); tree_add_cond(g_if, c0); - tree_t guard_ref = tree_guard(t); + tree_t guard = tree_guard(t); + assert(tree_kind(guard) == T_GUARD); + + tree_t guard_ref = make_ref(tree_ref(guard)); tree_set_value(c0, guard_ref); tree_add_trigger(wait, guard_ref); - // TODO: handle disconnection specifications here - // - // For now just use the default "disconnect S : T after 0 ns;" - if (tree_kind(s0) == T_SIGNAL_ASSIGN) { tree_t target = tree_target(s0); tree_t ref = name_to_ref(target); @@ -1018,6 +1017,12 @@ static tree_t simp_guard(tree_t container, tree_t t, tree_t wait, tree_t s0) tree_t w0 = tree_new(T_WAVEFORM); tree_set_loc(w0, tree_loc(t)); + if (tree_has_spec(guard)) { + tree_t spec = tree_spec(guard); + assert(tree_kind(spec) == T_DISCONNECT); + tree_set_delay(w0, tree_delay(spec)); + } + tree_add_waveform(d, w0); tree_t c1 = tree_new(T_COND_STMT); diff --git a/src/tree.c b/src/tree.c index 79b6096c..803bc7f3 100644 --- a/src/tree.c +++ b/src/tree.c @@ -362,6 +362,9 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { // T_DUMMY_DRIVER (I_TARGET | I_IDENT), + + // T_GUARD + (I_REF | I_SPEC | I_TYPE), }; static const char *kind_text_map[T_LAST_TREE_KIND] = { @@ -402,6 +405,7 @@ static const char *kind_text_map[T_LAST_TREE_KIND] = { "T_VIEW_DECL", "T_PACKAGE_MAP", "T_COND_EXPR", "T_COND_VALUE", "T_COND_RETURN", "T_VIEW_ELEMENT", "T_MATCH_SELECT", "T_PROT_DECL", "T_DUMMY_DRIVER", + "T_GUARD", }; static const change_allowed_t change_allowed[] = { @@ -1198,7 +1202,7 @@ tree_t tree_guard(tree_t t) void tree_set_guard(tree_t t, tree_t g) { - assert(g->object.kind == T_REF); + assert(g->object.kind == T_GUARD); lookup_item(&tree_object, t, I_GUARD)->object = &(g->object); object_write_barrier(&(t->object), &(g->object)); } diff --git a/src/tree.h b/src/tree.h index 0622647f..d42e268e 100644 --- a/src/tree.h +++ b/src/tree.h @@ -366,6 +366,7 @@ typedef enum tree_kind { T_MATCH_SELECT, T_PROT_DECL, T_DUMMY_DRIVER, + T_GUARD, T_LAST_TREE_KIND } tree_kind_t; diff --git a/test/regress/issue829.vhd b/test/regress/issue829.vhd new file mode 100644 index 00000000..2b17b8b3 --- /dev/null +++ b/test/regress/issue829.vhd @@ -0,0 +1,49 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity issue829 is +end entity; + +architecture sim of issue829 is + signal a : std_logic bus; + disconnect a : std_logic after 5 ns; + signal b : boolean; + signal c : std_logic := '1'; +begin + + c <= not c after 5 ns when now < 30 ns; + + BL : block (b) is + begin + a <= guarded c; + end block; + + process + begin + assert a = 'U'; + wait for 0 ns; + assert a = 'U'; + wait for 5 ns; + assert a = 'Z'; + wait for 10 ns; + assert a = 'Z'; + b <= true; + wait for 0 ns; + assert a = 'Z'; + wait for 5 ns; + assert a = '0'; + wait for 5 ns; + assert a = '1'; + wait for 5 ns; + assert a = '0'; + wait for 10 ns; + b <= false; + assert a = '1'; + wait for 0 ns; + assert a = '1'; + wait for 5 ns; + assert a = 'Z'; + wait; + end process; + +end architecture; diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index 6c0fe0ee..80a6dc64 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -923,3 +923,4 @@ issue826 normal,2008 issue827 normal,2008 cover22 shell issue831 normal,2008 +issue829 normal diff --git a/test/test_parse.c b/test/test_parse.c index 16ba7f8b..7a3afbb5 100644 --- a/test/test_parse.c +++ b/test/test_parse.c @@ -3210,7 +3210,7 @@ START_TEST(test_guarded) fail_unless(tree_has_guard(s0)); tree_t gref = tree_guard(s0); - fail_unless(tree_kind(gref) == T_REF); + fail_unless(tree_kind(gref) == T_GUARD); fail_unless(tree_ref(gref) == g); tree_t s1 = tree_stmt(b2, 1); diff --git a/test/test_sem.c b/test/test_sem.c index 2db6f165..90f60c7e 100644 --- a/test/test_sem.c +++ b/test/test_sem.c @@ -930,8 +930,11 @@ START_TEST(test_signal) { 69, "guarded signal must have resolved subtype" }, { 78, "disconnection specification must denote a guarded signal" }, { 79, "disconnection specification must denote a guarded signal" }, + { 80, "S$disconnect already declared in this region" }, // XXX { 80, "type of declared signal RBIT does not match type BIT_VECTOR in" }, + { 81, "S$disconnect already declared in this region" }, // XXX { 81, "disconnection specification must have type TIME but found RBIT" }, + { 83, "S$disconnect already declared in this region" }, // XXX { 83, "time expression in disconnection specificiation must be static" }, { 88, "invalid use of entity E" }, { -1, NULL } -- 2.39.2