From fd9813ad5ac8d16b1d06f06a95ae03301b7c3624 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Fri, 23 Feb 2024 14:12:13 +0000 Subject: [PATCH] Allow use of triggers for more process types --- src/common.c | 2 - src/jit/jit-dump.c | 3 +- src/jit/jit-exits.c | 29 +++++++- src/jit/jit-exits.h | 2 + src/jit/jit-irgen.c | 34 ++++++++++ src/jit/jit-priv.h | 2 + src/lower.c | 136 +++++++++++++++++++++++++++++-------- src/parse.c | 2 + src/prim.h | 1 + src/rt/model.c | 137 +++++++++++++++++++++++++++++--------- src/rt/structs.h | 20 ++++-- src/vcode.c | 47 +++++++++++++ src/vcode.h | 4 ++ test/lower/trigger1.vhd | 29 +++++++- test/regress/testlist.txt | 1 + test/regress/wait27.vhd | 66 ++++++++++++++++++ test/test_lower.c | 69 +++++++++++++++++++ 17 files changed, 512 insertions(+), 72 deletions(-) create mode 100644 test/regress/wait27.vhd diff --git a/src/common.c b/src/common.c index 10fbab54..d93a1bb1 100644 --- a/src/common.c +++ b/src/common.c @@ -1388,8 +1388,6 @@ bool is_open_coded_builtin(subprogram_kind_t kind) case S_MUL_RI: case S_MUL_IR: case S_DIV_RI: - case S_RISING_EDGE: - case S_FALLING_EDGE: case S_CONCAT: case S_SCALAR_AND: case S_SCALAR_OR: diff --git a/src/jit/jit-dump.c b/src/jit/jit-dump.c index f785c4fa..79480de6 100644 --- a/src/jit/jit-dump.c +++ b/src/jit/jit-dump.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2022-2023 Nick Gasson +// Copyright (C) 2022-2024 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -88,6 +88,7 @@ const char *jit_exit_name(jit_exit_t exit) "CLEAR_EVENT", "IMPLICIT_EVENT", "ENTER_STATE", "REFLECT_VALUE", "REFLECT_SUBTYPE", "FUNCTION_TRIGGER", "ADD_TRIGGER", "TRANSFER_SIGNAL", "PORT_CONVERSION", "CONVERT_IN", "CONVERT_OUT", "BIND_FOREIGN", + "OR_TRIGGER", }; assert(exit < ARRAY_LEN(names)); return names[exit]; diff --git a/src/jit/jit-exits.c b/src/jit/jit-exits.c index f4d228ff..1f461efe 100644 --- a/src/jit/jit-exits.c +++ b/src/jit/jit-exits.c @@ -925,8 +925,8 @@ void __nvc_do_exit(jit_exit_t which, jit_anchor_t *anchor, jit_scalar_t *args, case JIT_EXIT_FUNCTION_TRIGGER: { - jit_handle_t handle = args[0].integer; - unsigned nargs = args[1].integer; + jit_handle_t handle = args[0].integer; + unsigned nargs = args[1].integer; if (jit_has_runtime(thread->jit)) args[0].pointer = x_function_trigger(handle, nargs, args + 2); @@ -935,6 +935,31 @@ void __nvc_do_exit(jit_exit_t which, jit_anchor_t *anchor, jit_scalar_t *args, } break; + case JIT_EXIT_OR_TRIGGER: + { + void *left = args[0].pointer; + void *right = args[1].pointer; + + if (jit_has_runtime(thread->jit)) + args[0].pointer = x_or_trigger(left, right); + else + args[0].pointer = NULL; // Called during constant folding + } + break; + + case JIT_EXIT_CMP_TRIGGER: + { + sig_shared_t *shared = args[0].pointer; + int32_t offset = args[1].integer; + int64_t right = args[2].integer; + + if (jit_has_runtime(thread->jit)) + args[0].pointer = x_cmp_trigger(shared, offset, right); + else + args[0].pointer = NULL; // Called during constant folding + } + break; + case JIT_EXIT_ADD_TRIGGER: { void *trigger = args[0].pointer; diff --git a/src/jit/jit-exits.h b/src/jit/jit-exits.h index 7887ad42..83d29df4 100644 --- a/src/jit/jit-exits.h +++ b/src/jit/jit-exits.h @@ -92,6 +92,8 @@ void *x_reflect_subtype(void *context, tree_t where, const jit_scalar_t *bounds); void *x_function_trigger(jit_handle_t handle, unsigned nargs, const jit_scalar_t *args); +void *x_or_trigger(void *left, void *right); +void *x_cmp_trigger(sig_shared_t *ss, uint32_t offset, int64_t right); void x_add_trigger(void *ptr); void *x_port_conversion(const ffi_closure_t *driving, const ffi_closure_t *effective); diff --git a/src/jit/jit-irgen.c b/src/jit/jit-irgen.c index fb5b993e..95db9080 100644 --- a/src/jit/jit-irgen.c +++ b/src/jit/jit-irgen.c @@ -3391,6 +3391,34 @@ static void irgen_op_function_trigger(jit_irgen_t *g, int op) g->map[vcode_get_result(op)] = j_recv(g, 0); } +static void irgen_op_or_trigger(jit_irgen_t *g, int op) +{ + jit_value_t left = irgen_get_arg(g, op, 0); + jit_value_t right = irgen_get_arg(g, op, 1); + + j_send(g, 0, left); + j_send(g, 1, right); + + macro_exit(g, JIT_EXIT_OR_TRIGGER); + + g->map[vcode_get_result(op)] = j_recv(g, 0); +} + +static void irgen_op_cmp_trigger(jit_irgen_t *g, int op) +{ + jit_value_t shared = irgen_get_arg(g, op, 0); + jit_value_t offset = jit_value_from_reg(jit_value_as_reg(shared) + 1); + jit_value_t right = irgen_get_arg(g, op, 1); + + j_send(g, 0, shared); + j_send(g, 1, offset); + j_send(g, 2, right); + + macro_exit(g, JIT_EXIT_CMP_TRIGGER); + + g->map[vcode_get_result(op)] = j_recv(g, 0); +} + static void irgen_op_add_trigger(jit_irgen_t *g, int op) { jit_value_t trigger = irgen_get_arg(g, op, 0); @@ -3857,6 +3885,12 @@ static void irgen_block(jit_irgen_t *g, vcode_block_t block) case VCODE_OP_FUNCTION_TRIGGER: irgen_op_function_trigger(g, i); break; + case VCODE_OP_OR_TRIGGER: + irgen_op_or_trigger(g, i); + break; + case VCODE_OP_CMP_TRIGGER: + irgen_op_cmp_trigger(g, i); + break; case VCODE_OP_ADD_TRIGGER: irgen_op_add_trigger(g, i); break; diff --git a/src/jit/jit-priv.h b/src/jit/jit-priv.h index 497c0582..43b456fa 100644 --- a/src/jit/jit-priv.h +++ b/src/jit/jit-priv.h @@ -165,6 +165,8 @@ typedef enum { JIT_EXIT_CONVERT_IN, JIT_EXIT_CONVERT_OUT, JIT_EXIT_BIND_FOREIGN, + JIT_EXIT_OR_TRIGGER, + JIT_EXIT_CMP_TRIGGER, } jit_exit_t; typedef uint16_t jit_reg_t; diff --git a/src/lower.c b/src/lower.c index 387d1bf9..a3a1b42d 100644 --- a/src/lower.c +++ b/src/lower.c @@ -2011,22 +2011,6 @@ static void lower_release_temp(lower_unit_t *lu, vcode_var_t tmp) APUSH(lu->free_temps, tmp); } -static vcode_reg_t lower_falling_rising_edge(lower_unit_t *lu, tree_t fcall, - subprogram_kind_t kind) -{ - tree_t p0 = tree_value(tree_param(fcall, 0)); - - vcode_reg_t nets_reg = lower_lvalue(lu, p0); - vcode_reg_t value_reg = lower_rvalue(lu, p0); - - if (kind == S_FALLING_EDGE) - value_reg = emit_not(value_reg); - - vcode_reg_t event_reg = - emit_event_flag(nets_reg, emit_const(vtype_offset(), 1)); - return emit_and(event_reg, value_reg); -} - static vcode_reg_t lower_short_circuit(lower_unit_t *lu, tree_t fcall, subprogram_kind_t builtin) { @@ -2261,8 +2245,6 @@ static vcode_reg_t lower_builtin(lower_unit_t *lu, tree_t fcall, return lower_short_circuit(lu, fcall, builtin); else if (builtin == S_CONCAT) return lower_concat(lu, fcall, VCODE_INVALID_REG, VCODE_INVALID_REG); - else if (builtin == S_RISING_EDGE || builtin == S_FALLING_EDGE) - return lower_falling_rising_edge(lu, fcall, builtin); vcode_reg_t r0 = lower_subprogram_arg(lu, fcall, 0); vcode_reg_t r1 = lower_subprogram_arg(lu, fcall, 1); @@ -10897,6 +10879,19 @@ static void lower_predef_file_open3(lower_unit_t *lu, tree_t decl) emit_return(emit_load(status_var)); } +static void lower_edge_predef(lower_unit_t *lu, tree_t decl) +{ + vcode_reg_t nets_reg = vcode_param_reg(1); + vcode_reg_t value_reg = emit_load_indirect(emit_resolved(nets_reg)); + + if (tree_subkind(decl) == S_FALLING_EDGE) + value_reg = emit_not(value_reg); + + vcode_reg_t count_reg = emit_const(vtype_offset(), 1); + vcode_reg_t event_reg = emit_event_flag(nets_reg, count_reg); + emit_return(emit_and(event_reg, value_reg)); +} + static void lower_predef(lower_unit_t *lu, object_t *obj) { tree_t decl = tree_from_object(obj); @@ -11038,6 +11033,9 @@ static void lower_predef(lower_unit_t *lu, object_t *obj) case S_FILE_POSITION: lower_foreign_predef(lu, decl, "__nvc_file_position"); break; + case S_RISING_EDGE: + case S_FALLING_EDGE: + return lower_edge_predef(lu, decl); default: fatal_trace("cannot lower predefined function %s", type_pp(type)); } @@ -11194,20 +11192,81 @@ static bool can_use_transfer_signal(tree_t proc, driver_set_t *ds) return true; } -static vcode_reg_t lower_func_trigger(lower_unit_t *lu, tree_t fcall) +static vcode_reg_t lower_trigger(lower_unit_t *lu, tree_t fcall, tree_t proc) { tree_t decl = tree_ref(fcall); - if (tree_subkind(decl) != S_USER) + const subprogram_kind_t kind = tree_subkind(decl); + if (kind == S_SCALAR_EQ) { + tree_t p0 = tree_value(tree_param(fcall, 0)), p1; + + if (is_literal(p0)) // Commute arguments + p1 = p0, p0 = tree_value(tree_param(fcall, 1)); + else + p1 = tree_value(tree_param(fcall, 1)); + + if (tree_kind(p0) != T_REF || class_of(p0) != C_SIGNAL) + return VCODE_INVALID_REG; + else if (!is_literal(p1)) + return VCODE_INVALID_REG; + + vcode_reg_t left_reg = lower_lvalue(lu, p0); + vcode_reg_t right_reg = lower_rvalue(lu, p1); + + return emit_cmp_trigger(left_reg, right_reg); + } + else if (kind == S_SCALAR_AND) { + // Testing x'event is redundant if the process is only + // sensistive to x + tree_t w = tree_stmt(proc, 1); + assert(tree_kind(w) == T_WAIT); + + if (tree_triggers(w) != 1) + return VCODE_INVALID_REG; + + tree_t p0 = tree_value(tree_param(fcall, 0)); + tree_t p1 = tree_value(tree_param(fcall, 1)); + tree_t aref, other; + + if (tree_kind(p0) == T_ATTR_REF) + aref = p0, other = p1; + else if (tree_kind(p1) == T_ATTR_REF) + aref = p1, other = p0; + else + return VCODE_INVALID_REG; + + if (tree_subkind(aref) != ATTR_EVENT) + return VCODE_INVALID_REG; + else if (tree_kind(other) != T_FCALL) + return VCODE_INVALID_REG; + + if (!same_tree(tree_name(aref), tree_trigger(w, 0))) + return VCODE_INVALID_REG; + + return lower_trigger(lu, other, proc); + } + else if (kind != S_USER && kind != S_FALLING_EDGE && kind != S_RISING_EDGE) return VCODE_INVALID_REG; else if (tree_flags(decl) & TREE_F_IMPURE) return VCODE_INVALID_REG; const int nparams = tree_params(fcall); + if (nparams != tree_ports(decl)) + return VCODE_INVALID_REG; + for (int i = 0; i < nparams; i++) { - tree_t p = tree_value(tree_param(fcall, i)); - const tree_kind_t kind = tree_kind(p); - if (kind != T_LITERAL && (kind != T_REF || class_of(p) != C_SIGNAL)) + tree_t p = tree_param(fcall, i); + tree_t value = tree_value(p); + if (is_literal(value)) + continue; + else if (tree_kind(value) == T_REF && class_of(value) == C_SIGNAL) { + // Must be passing as a signal not the resolved value + if (tree_subkind(p) != P_POS) + return VCODE_INVALID_REG; + else if (tree_class(tree_port(decl, tree_pos(p))) != C_SIGNAL) + return VCODE_INVALID_REG; + } + else return VCODE_INVALID_REG; } @@ -11224,7 +11283,9 @@ static vcode_reg_t lower_func_trigger(lower_unit_t *lu, tree_t fcall) static vcode_reg_t lower_process_trigger(lower_unit_t *lu, tree_t proc) { - if (tree_stmts(proc) != 2) + if (cover_enabled(lu->cover, COVER_MASK_BRANCH | COVER_MASK_EXPRESSION)) + return VCODE_INVALID_REG; // Would have incorrect branch coverage + else if (tree_stmts(proc) != 2) return VCODE_INVALID_REG; // Preconditions @@ -11234,14 +11295,31 @@ static vcode_reg_t lower_process_trigger(lower_unit_t *lu, tree_t proc) tree_t s0 = tree_stmt(proc, 0); if (tree_kind(s0) != T_IF) return VCODE_INVALID_REG; - else if (tree_conds(s0) != 1) - return VCODE_INVALID_REG; - tree_t value = tree_value(tree_cond(s0, 0)); - if (tree_kind(value) != T_FCALL) + const int nconds = tree_conds(s0); + if (nconds > 2) return VCODE_INVALID_REG; - return lower_func_trigger(lu, value); + vcode_reg_t branches[2] = { VCODE_INVALID_REG, VCODE_INVALID_REG }; + for (int i = 0; i < nconds; i++) { + tree_t c = tree_cond(s0, i); + if (!tree_has_value(c)) + return VCODE_INVALID_REG; + + tree_t value = tree_value(c); + if (tree_kind(value) != T_FCALL) + return VCODE_INVALID_REG; + + branches[i] = lower_trigger(lu, value, proc); + } + + if (nconds == 1) + return branches[0]; + else if (branches[0] == VCODE_INVALID_REG + || branches[1] == VCODE_INVALID_REG) + return VCODE_INVALID_REG; + else + return emit_or_trigger(branches[0], branches[1]); } void lower_process(lower_unit_t *parent, tree_t proc, driver_set_t *ds) diff --git a/src/parse.c b/src/parse.c index 8c684d6c..56df5126 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1376,11 +1376,13 @@ static void declare_predefined_ops(tree_t container, type_t t) if (t == std_bool || t == (std_bit = std_type(NULL, STD_BIT))) { tree_t d1 = builtin_fn(ident_new("RISING_EDGE"), std_bool, S_RISING_EDGE, "S", t, NULL); + mangle_func(nametab, d1); tree_set_class(tree_port(d1, 0), C_SIGNAL); tree_add_decl(container, d1); tree_t d2 = builtin_fn(ident_new("FALLING_EDGE"), std_bool, S_FALLING_EDGE, "S", t, NULL); + mangle_func(nametab, d2); tree_set_class(tree_port(d2, 0), C_SIGNAL); tree_add_decl(container, d2); diff --git a/src/prim.h b/src/prim.h index 89e7a75c..437c110b 100644 --- a/src/prim.h +++ b/src/prim.h @@ -71,6 +71,7 @@ typedef struct _rt_proc rt_proc_t; typedef struct _rt_alias rt_alias_t; typedef struct _rt_implicit rt_implicit_t; typedef struct _rt_resolution rt_resolution_t; +typedef struct _rt_trigger rt_trigger_t; typedef struct waveform waveform_t; typedef struct sens_list sens_list_t; diff --git a/src/rt/model.c b/src/rt/model.c index 48ea3557..574dce2a 100644 --- a/src/rt/model.c +++ b/src/rt/model.c @@ -2632,29 +2632,66 @@ static bool heap_delete_proc_cb(uint64_t key, void *value, void *search) return untag_pointer(value, rt_proc_t) == search; } -static void wakeup_one(rt_model_t *m, rt_wakeable_t *obj) +static bool run_trigger(rt_model_t *m, rt_trigger_t *t) { - if (obj->pending) - return; // Already scheduled + if (t->when == m->now && t->iteration == m->iteration) + return t->result.integer != 0; // Cached - if (obj->trigger != NULL) { - rt_trigger_t *t = obj->trigger; - - if (t->when != m->now || t->iteration != m->iteration) { + switch (t->kind) { + case FUNC_TRIGGER: + { tlab_t tlab = jit_null_tlab(m->jit); - jit_vfastcall(m->jit, t->handle, &t->result, t->nargs, t->args, &tlab); + if (!jit_vfastcall(m->jit, t->handle, &t->result, t->nargs, + t->args, &tlab)) + m->force_stop = true; TRACE("run trigger %p %s ==> %"PRIi64, t, istr(jit_get_name(m->jit, t->handle)), t->result.integer); + } + break; + + case OR_TRIGGER: + { + rt_trigger_t *left = t->args[0].pointer; + rt_trigger_t *right = t->args[1].pointer; + t->result.integer = run_trigger(m, left) || run_trigger(m, right); - t->when = m->now; - t->iteration = m->iteration; + TRACE("or trigger %p ==> %"PRIi64, t, t->result.integer); } + break; - if (t->result.integer == 0) - return; // Filtered + case CMP_TRIGGER: + { + rt_signal_t *s = t->args[0].pointer; + uint32_t offset = t->args[1].integer; + int64_t right = t->args[2].integer; + +#define COMPARE_SCALAR(type) do { \ + const type *data = (type *)s->shared.data; \ + t->result.integer = (data[offset] == right); \ + } while (0) + + FOR_ALL_SIZES(s->nexus.size, COMPARE_SCALAR); + + TRACE("cmp trigger %p ==> %"PRIi64, t, t->result.integer); + } + break; } + t->when = m->now; + t->iteration = m->iteration; + + return t->result.integer != 0; +} + +static void wakeup_one(rt_model_t *m, rt_wakeable_t *obj) +{ + if (obj->pending) + return; // Already scheduled + + if (obj->trigger != NULL && !run_trigger(m, obj->trigger)) + return; // Filtered + deferq_t *dq = obj->postponed ? &m->postponedq : &m->procq; switch (obj->kind) { @@ -3557,6 +3594,37 @@ int32_t *get_cover_counter(rt_model_t *m, int32_t tag) return jit_get_cover_mem(m->jit, tag + 1) + tag; } +static rt_trigger_t *new_trigger(rt_model_t *m, trigger_kind_t kind, + uint64_t hash, jit_handle_t handle, + unsigned nargs, const jit_scalar_t *args) +{ + rt_trigger_t **bucket = &(m->triggertab[hash % TRIGGER_TAB_SIZE]); + + for (rt_trigger_t *exist = *bucket; exist; exist = exist->chain) { + bool hit = exist->handle == handle + && exist->nargs == nargs + && exist->kind == kind; + + for (int i = 0; hit && i < nargs; i++) + hit &= (exist->args[i].integer == args[i].integer); + + if (hit) + return exist; + } + + const size_t argsz = nargs * sizeof(jit_scalar_t); + + rt_trigger_t *t = static_alloc(m, sizeof(rt_trigger_t) + argsz); + t->handle = handle; + t->nargs = nargs; + t->when = TIME_HIGH; + t->kind = kind; + t->chain = *bucket; + memcpy(t->args, args, argsz); + + return (*bucket = t); +} + //////////////////////////////////////////////////////////////////////////////// // Entry points from compiled code @@ -4268,33 +4336,42 @@ void *x_function_trigger(jit_handle_t handle, unsigned nargs, TRACE("function trigger %s nargs=%u hash=%"PRIx64, istr(jit_get_name(m->jit, handle)), nargs, hash); - rt_trigger_t *exist = m->triggertab[hash % TRIGGER_TAB_SIZE]; - if (exist != NULL) { - bool hit = exist->handle == handle && exist->nargs == nargs; - for (int i = 0; hit && i < nargs; i++) - hit &= (exist->args[i].integer == args[i].integer); + return new_trigger(m, FUNC_TRIGGER, hash, handle, nargs, args); +} - if (hit) - return exist; - } +void *x_or_trigger(void *left, void *right) +{ + rt_model_t *m = get_model(); - const size_t argsz = nargs * sizeof(jit_scalar_t); + uint64_t hash = mix_bits_64(left) ^ mix_bits_64(right); - rt_trigger_t *t = static_alloc(m, sizeof(rt_trigger_t) + argsz); - t->handle = handle; - t->nargs = nargs; - t->when = TIME_HIGH; - memcpy(t->args, args, argsz); + TRACE("or trigger %p %p hash=%"PRIx64, left, right, hash); - return (m->triggertab[hash % TRIGGER_TAB_SIZE] = t); + const jit_scalar_t args[] = { { .pointer = left }, { .pointer = right } }; + return new_trigger(m, OR_TRIGGER, hash, JIT_HANDLE_INVALID, 2, args); } -void x_add_trigger(void *ptr) +void *x_cmp_trigger(sig_shared_t *ss, uint32_t offset, int64_t right) { rt_model_t *m = get_model(); + rt_signal_t *s = container_of(ss, rt_signal_t, shared); + + uint64_t hash = mix_bits_64(s) ^ mix_bits_32(offset) ^ mix_bits_64(right); + + TRACE("cmp trigger %s+%d right=%"PRIi64" hash=%"PRIx64, + istr(tree_ident(s->where)), offset, right, hash); + + const jit_scalar_t args[] = { + { .pointer = s }, + { .integer = offset }, + { .integer = right } + }; + return new_trigger(m, CMP_TRIGGER, hash, JIT_HANDLE_INVALID, 3, args); +} - TRACE("add trigger %p %s", ptr, - istr(jit_get_name(m->jit, ((rt_trigger_t *)ptr)->handle))); +void x_add_trigger(void *ptr) +{ + TRACE("add trigger %p", ptr); rt_wakeable_t *obj = get_active_wakeable(); assert(obj->trigger == NULL); diff --git a/src/rt/structs.h b/src/rt/structs.h index 995ce69c..d763f8b2 100644 --- a/src/rt/structs.h +++ b/src/rt/structs.h @@ -34,13 +34,19 @@ typedef enum { typedef uint32_t wakeup_gen_t; -typedef struct { - jit_handle_t handle; - unsigned nargs; - uint64_t when; - unsigned iteration; - jit_scalar_t result; - jit_scalar_t args[0]; +typedef enum { + FUNC_TRIGGER, OR_TRIGGER, CMP_TRIGGER +} trigger_kind_t; + +typedef struct _rt_trigger { + jit_handle_t handle; + unsigned nargs; + uint64_t when; + unsigned iteration; + trigger_kind_t kind; + rt_trigger_t *chain; + jit_scalar_t result; + jit_scalar_t args[0]; } rt_trigger_t; typedef struct { diff --git a/src/vcode.c b/src/vcode.c index cbd5fac4..27ffffcc 100644 --- a/src/vcode.c +++ b/src/vcode.c @@ -683,6 +683,9 @@ void vcode_opt(void) case VCODE_OP_TRAP_MUL: case VCODE_OP_TRAP_EXP: case VCODE_OP_ALLOC: + case VCODE_OP_FUNCTION_TRIGGER: + case VCODE_OP_CMP_TRIGGER: + case VCODE_OP_OR_TRIGGER: if (uses[o->result] == -1) { vcode_dump_with_mark(j, NULL, NULL); fatal_trace("definition of r%d does not dominate all uses", @@ -963,6 +966,7 @@ const char *vcode_op_string(vcode_op_t op) "trap exp", "implicit event", "enter state", "reflect value", "reflect subtype", "function trigger", "add trigger", "transfer signal", "port conversion", "convert in", "convert out", "bind foreign", + "or trigger", "cmp trigger" }; if ((unsigned)op >= ARRAY_LEN(strs)) return "???"; @@ -2255,6 +2259,21 @@ void vcode_dump_with_mark(int mark_op, vcode_dump_fn_t callback, void *arg) } break; + case VCODE_OP_OR_TRIGGER: + case VCODE_OP_CMP_TRIGGER: + { + col += vcode_dump_reg(op->result); + col += color_printf(" := %s ", vcode_op_string(op->kind)); + col += vcode_dump_reg(op->args.items[0]); + if (op->kind == VCODE_OP_OR_TRIGGER) + col += printf(" || "); + else + col += printf(" == "); + col += vcode_dump_reg(op->args.items[1]); + vcode_dump_result_type(col, op); + } + break; + case VCODE_OP_ADD_TRIGGER: { printf("%s ", vcode_op_string(op->kind)); @@ -5886,6 +5905,34 @@ vcode_reg_t emit_function_trigger(ident_t func, const vcode_reg_t *args, return (op->result = vcode_add_reg(vtype_trigger())); } +vcode_reg_t emit_or_trigger(vcode_reg_t left, vcode_reg_t right) +{ + op_t *op = vcode_add_op(VCODE_OP_OR_TRIGGER); + vcode_add_arg(op, left); + vcode_add_arg(op, right); + + VCODE_ASSERT(vcode_reg_kind(left) == VCODE_TYPE_TRIGGER, + "or trigger left argument must be trigger"); + VCODE_ASSERT(vcode_reg_kind(right) == VCODE_TYPE_TRIGGER, + "or trigger right argument must be trigger"); + + return (op->result = vcode_add_reg(vtype_trigger())); +} + +vcode_reg_t emit_cmp_trigger(vcode_reg_t left, vcode_reg_t right) +{ + op_t *op = vcode_add_op(VCODE_OP_CMP_TRIGGER); + vcode_add_arg(op, left); + vcode_add_arg(op, right); + + VCODE_ASSERT(vcode_reg_kind(left) == VCODE_TYPE_SIGNAL, + "cmp trigger left argument must be signal"); + VCODE_ASSERT(vcode_reg_kind(right) == VCODE_TYPE_INT, + "cmp trigger right argument must be integer"); + + return (op->result = vcode_add_reg(vtype_trigger())); +} + void emit_add_trigger(vcode_reg_t trigger) { op_t *op = vcode_add_op(VCODE_OP_ADD_TRIGGER); diff --git a/src/vcode.h b/src/vcode.h index d7e92aa2..76652fa5 100644 --- a/src/vcode.h +++ b/src/vcode.h @@ -164,6 +164,8 @@ typedef enum { VCODE_OP_CONVERT_IN, VCODE_OP_CONVERT_OUT, VCODE_OP_BIND_FOREIGN, + VCODE_OP_OR_TRIGGER, + VCODE_OP_CMP_TRIGGER, } vcode_op_t; typedef enum { @@ -518,6 +520,8 @@ vcode_reg_t emit_reflect_subtype(ident_t ptype, vcode_reg_t context, vcode_reg_t locus, vcode_reg_t bounds); vcode_reg_t emit_function_trigger(ident_t func, const vcode_reg_t *args, int nargs); +vcode_reg_t emit_or_trigger(vcode_reg_t left, vcode_reg_t right); +vcode_reg_t emit_cmp_trigger(vcode_reg_t left, vcode_reg_t right); void emit_add_trigger(vcode_reg_t trigger); vcode_reg_t emit_port_conversion(vcode_reg_t driving, vcode_reg_t effective); void emit_convert_in(vcode_reg_t conv, vcode_reg_t nets, vcode_reg_t count); diff --git a/test/lower/trigger1.vhd b/test/lower/trigger1.vhd index 50a55aa8..590874ce 100644 --- a/test/lower/trigger1.vhd +++ b/test/lower/trigger1.vhd @@ -7,7 +7,12 @@ architecture test of trigger1 is return x'event and x = '1'; end function; - signal clk, x : bit; + function bad (x : bit) return boolean is + begin + return x = '1'; + end function; + + signal clk, x, y, z, rstn : bit; begin p1: process (clk) is @@ -17,4 +22,26 @@ begin end if; end process; + p2: process (clk) is + begin + if bad(clk) then + x <= not x; + end if; + end process; + + p3: process (rstn, clk) is + begin + if rstn = '0' then + y <= '0'; + elsif rising(clk) then + y <= not y; + end if; + end process; + + p4: process (clk) is + begin + if clk'event and clk = '1' then + z <= not z; + end if; + end process; end architecture; diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index c0fb4bbc..950e7045 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -943,3 +943,4 @@ issue844 normal,2008 conv14 normal,2008 issue850 shell vlog7 verilog,gold +wait27 normal diff --git a/test/regress/wait27.vhd b/test/regress/wait27.vhd new file mode 100644 index 00000000..1ce5b425 --- /dev/null +++ b/test/regress/wait27.vhd @@ -0,0 +1,66 @@ +entity wait27 is +end entity; + +architecture test of wait27 is + function rising (signal x : bit) return boolean is + begin + return x'event and x = '1'; + end function; + + function falling (signal x : bit) return boolean is + begin + return x'event and x = '0'; + end function; + + signal clk, rstn : bit := '0'; + signal count1, count2 : natural; + signal running : boolean := true; +begin + + clkgen: clk <= not clk after 5 ns when running; + + p1: process (rstn, clk) is + begin + if rstn = '0' then + count1 <= 0; + elsif rising(clk) then + count1 <= count1 + 1; + end if; + end process; + + p2: process (rstn, clk) is + begin + if rstn = '0' then + count2 <= 0; + elsif clk'event and clk = '0' then + count2 <= count1 + 1; + end if; + end process; + + p3: process (clk) is + begin + if rising(clk) then + assert now > 0 ns; + end if; + end process; + + check: process is + begin + wait until falling(clk); + rstn <= '1'; + wait for 50 ns; + assert count1 = 5; + assert count2 = 5; + running <= false; + wait for 20 ns; + assert count1 = 6; + assert count2 = 6; + rstn <= '0'; + wait for 0 ns; + wait for 0 ns; + assert count1 = 0; + assert count2 = 0; + wait; + end process; + +end architecture; diff --git a/test/test_lower.c b/test/test_lower.c index e4a39ceb..1188f971 100644 --- a/test/test_lower.c +++ b/test/test_lower.c @@ -250,6 +250,8 @@ static void check_bb(int bb, const check_bb_t *expect, int len) case VCODE_OP_PUSH_SCOPE: case VCODE_OP_POP_SCOPE: case VCODE_OP_ADD_TRIGGER: + case VCODE_OP_OR_TRIGGER: + case VCODE_OP_CMP_TRIGGER: break; case VCODE_OP_CONST_ARRAY: @@ -6058,6 +6060,73 @@ START_TEST(test_trigger1) CHECK_BB(0); } + + { + vcode_unit_t vu = find_unit("WORK.TRIGGER1.P2"); + vcode_select_unit(vu); + + EXPECT_BB(0) = { + { VCODE_OP_VAR_UPREF, .name = "X", .hops = 1 }, + { VCODE_OP_LOAD_INDIRECT }, + { VCODE_OP_CONST, .value = 1 }, + { VCODE_OP_DRIVE_SIGNAL }, + { VCODE_OP_VAR_UPREF, .name = "CLK", .hops = 1 }, + { VCODE_OP_LOAD_INDIRECT }, + { VCODE_OP_SCHED_EVENT }, + { VCODE_OP_RETURN }, + }; + + CHECK_BB(0); + } + + { + vcode_unit_t vu = find_unit("WORK.TRIGGER1.P3"); + vcode_select_unit(vu); + + EXPECT_BB(0) = { + { VCODE_OP_VAR_UPREF, .name = "Y", .hops = 1 }, + { VCODE_OP_LOAD_INDIRECT }, + { VCODE_OP_CONST, .value = 1 }, + { VCODE_OP_DRIVE_SIGNAL }, + { VCODE_OP_VAR_UPREF, .name = "RSTN", .hops = 1 }, + { VCODE_OP_LOAD_INDIRECT }, + { VCODE_OP_SCHED_EVENT }, + { VCODE_OP_VAR_UPREF, .name = "CLK", .hops = 1 }, + { VCODE_OP_LOAD_INDIRECT }, + { VCODE_OP_SCHED_EVENT }, + { VCODE_OP_CONST, .value = 0 }, + { VCODE_OP_CMP_TRIGGER }, + { VCODE_OP_CONTEXT_UPREF, .hops = 1 }, + { VCODE_OP_FUNCTION_TRIGGER, .func = "WORK.TRIGGER1.RISING(sJ)B" }, + { VCODE_OP_OR_TRIGGER }, + { VCODE_OP_ADD_TRIGGER }, + { VCODE_OP_RETURN }, + }; + + CHECK_BB(0); + } + + { + vcode_unit_t vu = find_unit("WORK.TRIGGER1.P4"); + vcode_select_unit(vu); + + EXPECT_BB(0) = { + { VCODE_OP_VAR_UPREF, .name = "Z", .hops = 1 }, + { VCODE_OP_LOAD_INDIRECT }, + { VCODE_OP_CONST, .value = 1 }, + { VCODE_OP_DRIVE_SIGNAL }, + { VCODE_OP_VAR_UPREF, .name = "CLK", .hops = 1 }, + { VCODE_OP_LOAD_INDIRECT }, + { VCODE_OP_SCHED_EVENT }, + { VCODE_OP_CONST, .value = 1 }, + { VCODE_OP_CMP_TRIGGER }, + { VCODE_OP_ADD_TRIGGER }, + { VCODE_OP_RETURN }, + }; + + CHECK_BB(0); + } + } END_TEST -- 2.39.2