static void opt_elide_array_ref_bounds(tree_t t) { tree_t value = tree_value(t); if (tree_kind(value) != T_REF) return; tree_t decl = tree_ref(value); const int nparams = tree_params(t); for (int i = 0; i < nparams; i++) { tree_t index = tree_value(tree_param(t, i)); if (tree_kind(index) != T_REF) return; tree_t index_decl = tree_ref(index); tree_t range_var = tree_attr_tree(index_decl, range_var_i); if (range_var == NULL) return; if (range_var != decl) return; } tree_add_attr_int(t, elide_bounds_i, 1); }
static void ungroup_proc_params(tree_t t, group_nets_ctx_t *ctx) { // Ungroup any signal that is passed to a procedure as in general we // cannot guarantee anything about the procedure's behaviour const int nparams = tree_params(t); for (int i = 0; i < nparams; i++) { tree_t value = tree_value(tree_param(t, i)); tree_kind_t kind = tree_kind(value); while (kind == T_ARRAY_REF || kind == T_ARRAY_SLICE) { value = tree_value(value); kind = tree_kind(value); } if (kind != T_REF) continue; tree_t decl = tree_ref(value); if (tree_kind(decl) != T_SIGNAL_DECL) continue; const int nnets = tree_nets(decl); for (int i = 0; i < nnets; i++) group_add(ctx, tree_net(decl, i), 1); } }
END_TEST START_TEST(test_copy1) { input_from_file(TESTDIR "/elab/copy1.vhd"); const error_t expect[] = { { -1, NULL } }; expect_errors(expect); tree_t e = run_elab(); int nfuncs = 0, nshared = 0; const int ndecls = tree_decls(e); for (int i = 0; i < ndecls; i++) { tree_t t = tree_decl(e, i); if (tree_kind(t) == T_FUNC_BODY) nfuncs++; else if (tree_kind(t) == T_VAR_DECL) nshared++; } fail_unless(nfuncs == 1); fail_unless(nshared == 2); }
static tree_t opt_delete_wait_only_fn(tree_t t, void *ctx) { if (tree_kind(t) != T_PROCESS) return t; if ((tree_stmts(t) == 1) && (tree_kind(tree_stmt(t, 0)) == T_WAIT)) return NULL; else return t; }
class_t class_of(tree_t t) { switch (tree_kind(t)) { case T_VAR_DECL: return C_VARIABLE; case T_SIGNAL_DECL: return C_SIGNAL; case T_CONST_DECL: return C_CONSTANT; case T_PORT_DECL: return tree_class(t); case T_ENUM_LIT: case T_GENVAR: case T_ALIAS: case T_FIELD_DECL: return C_DEFAULT; case T_UNIT_DECL: return C_UNITS; case T_ARCH: return C_ARCHITECTURE; case T_FUNC_DECL: case T_FUNC_BODY: return C_FUNCTION; case T_PROC_DECL: case T_PROC_BODY: return C_PROCEDURE; case T_ENTITY: return C_ENTITY; case T_TYPE_DECL: return C_TYPE; case T_FILE_DECL: return C_FILE; case T_PROCESS: case T_BLOCK: case T_FOR: return C_LABEL; case T_COMPONENT: return C_COMPONENT; case T_REF: return class_of(tree_ref(t)); case T_ARRAY_REF: case T_ARRAY_SLICE: case T_RECORD_REF: return class_of(tree_value(t)); case T_PACKAGE: return C_PACKAGE; case T_LIBRARY: return C_LIBRARY; case T_ELAB: return C_ELAB; default: fatal("missing class_of for %s", tree_kind_str(tree_kind(t))); } }
bool folded_enum(tree_t t, unsigned *pos) { if (tree_kind(t) == T_REF) { tree_t decl = tree_ref(t); if (tree_kind(decl) == T_ENUM_LIT) { *pos = tree_pos(decl); return true; } } return false; }
bool folded_bool(tree_t t, bool *b) { if (tree_kind(t) == T_REF) { tree_t decl = tree_ref(t); if (tree_kind(decl) == T_ENUM_LIT && type_ident(tree_type(decl)) == std_bool_i) { *b = (tree_pos(decl) == 1); return true; } } return false; }
bool folded_bool(tree_t t, bool *b) { ident_t std_bool_i = ident_new("STD.STANDARD.BOOLEAN"); if (tree_kind(t) == T_REF) { tree_t decl = tree_ref(t); if (tree_kind(decl) == T_ENUM_LIT && type_ident(tree_type(decl)) == std_bool_i) { *b = (tree_pos(decl) == 1); return true; } } return false; }
void opt(tree_t top) { if (tree_kind(top) == T_ELAB) opt_delete_wait_only(top); tree_visit(top, opt_tag, NULL); }
static void elab_add_context(tree_t t, const elab_ctx_t *ctx) { ident_t cname = tree_ident(t); ident_t lname = ident_until(cname, '.'); lib_t lib = elab_find_lib(lname, ctx); tree_t unit = lib_get(lib, cname); if (unit == NULL) fatal_at(tree_loc(t), "cannot find unit %s", istr(cname)); else if (tree_kind(unit) == T_PACKAGE) { elab_copy_context(unit, ctx); ident_t name = tree_ident(unit); ident_t body_i = ident_prefix(name, ident_new("body"), '-'); tree_t body = lib_get(lib, body_i); if (body != NULL) elab_copy_context(unit, ctx); } // Always use real library name rather than WORK alias tree_set_ident(t, tree_ident(unit)); tree_add_context(ctx->out, t); }
static void opt_tag_last_value_fcall(tree_t t) { tree_t decl = tree_ref(t); if (tree_attr_str(decl, builtin_i) != NULL) return; // A regular subprogram call may pass parameters as class signal which // could access 'LAST_VALUE in the body const int nports = tree_ports(decl); for (int i = 0; i < nports; i++) { tree_t port = tree_port(decl, i); if (tree_class(port) != C_SIGNAL) continue; tree_t value = tree_value(tree_param(t, i)); tree_kind_t kind; while ((kind = tree_kind(value)) != T_REF) { assert((kind == T_ARRAY_REF) || (kind == T_ARRAY_SLICE)); value = tree_value(value); } tree_add_attr_int(tree_ref(value), last_value_i, 1); } }
static void group_ref(tree_t target, group_nets_ctx_t *ctx, int start, int n) { assert(tree_kind(target) == T_REF); tree_t decl = tree_ref(target); switch (tree_kind(decl)) { case T_SIGNAL_DECL: group_decl(decl, ctx, start, n); break; case T_ALIAS: group_target(tree_value(decl), ctx); break; default: break; } }
static void ungroup_name(tree_t name, group_nets_ctx_t *ctx) { switch (tree_kind(name)) { case T_ARRAY_REF: case T_ARRAY_SLICE: case T_RECORD_REF: ungroup_name(tree_value(name), ctx); break; case T_REF: ungroup_ref(name, ctx); break; default: fatal_trace("cannot handle tree type %s in ungroup_name", tree_kind_str(tree_kind(name))); } }
static tree_t get_bool_lit(tree_t t, bool v) { tree_t fdecl = tree_ref(t); assert(tree_kind(fdecl) == T_FUNC_DECL); static type_t bool_type = NULL; if (bool_type == NULL) { lib_t std = lib_find("std", true, true); assert(std != NULL); tree_t standard = lib_get(std, ident_new("STD.STANDARD")); assert(standard != NULL); const int ndecls = tree_decls(standard); for (int i = 0; (i < ndecls) && (bool_type == NULL); i++) { tree_t d = tree_decl(standard, i); if (tree_ident(d) == std_bool_i) bool_type = tree_type(d); } assert(bool_type != NULL); } tree_t lit = type_enum_literal(bool_type, v ? 1 : 0); tree_t b = tree_new(T_REF); tree_set_loc(b, tree_loc(t)); tree_set_ref(b, lit); tree_set_type(b, bool_type); tree_set_ident(b, tree_ident(lit)); return b; }
static void group_nets_visit_fn(tree_t t, void *_ctx) { group_nets_ctx_t *ctx = _ctx; switch (tree_kind(t)) { case T_SIGNAL_ASSIGN: group_target(tree_target(t), ctx); break; case T_WAIT: { const int ntriggers = tree_triggers(t); for (int i = 0; i < ntriggers; i++) group_target(tree_trigger(t, i), ctx); } break; case T_PCALL: ungroup_proc_params(t, ctx); break; case T_SIGNAL_DECL: // Ensure that no group is larger than a signal declaration group_decl(t, ctx, 0, -1); break; default: break; } }
static tree_t elab_port_to_signal(tree_t arch, tree_t port, tree_t actual) { assert(tree_kind(port) == T_PORT_DECL); ident_t name = tree_ident(port); const int ndecls = tree_decls(arch); for (int i = 0; i < ndecls; i++) { tree_t d = tree_decl(arch, i); if (tree_ident(d) == name) return d; } type_t port_type = tree_type(port); type_t actual_type = tree_type(actual); type_t type = (type_is_unconstrained(port_type)) ? actual_type : port_type; port_mode_t mode = tree_subkind(port); tree_t s = tree_new(T_SIGNAL_DECL); tree_set_ident(s, tree_ident(port)); tree_set_type(s, type); tree_add_attr_int(s, fst_dir_i, mode); tree_set_loc(s, tree_loc(port)); tree_set_flag(s, tree_flags(port) & TREE_F_LAST_VALUE); if ((mode == PORT_OUT) || (mode == PORT_INOUT) || (mode == PORT_BUFFER)) { if (tree_has_value(port)) tree_add_attr_tree(s, driver_init_i, tree_value(port)); } tree_add_decl(arch, s); return s; }
static tree_t rewrite_refs(tree_t t, void *context) { rewrite_params_t *params = context; if (tree_kind(t) != T_REF) return t; tree_t decl = tree_ref(t); for (int i = 0; i < params->count; i++) { if (decl != params->formals[i]) continue; // Do not rewrite references if they appear as formal names if (tree_attr_int(t, formal_i, 0)) continue; // Skip assignments to OPEN ports if (params->actuals[i] == NULL) continue; switch (tree_kind(params->actuals[i])) { case T_SIGNAL_DECL: case T_ENUM_LIT: tree_set_ref(t, params->actuals[i]); tree_set_type(t, tree_type(params->actuals[i])); return t; case T_LITERAL: case T_AGGREGATE: case T_REF: case T_ARRAY_SLICE: case T_ARRAY_REF: case T_FCALL: case T_CONCAT: return params->actuals[i]; case T_TYPE_CONV: // XXX: this only works in trivial cases return tree_value(tree_param(params->actuals[i], 0)); default: fatal_at(tree_loc(params->actuals[i]), "cannot handle tree kind %s " "in rewrite_refs", tree_kind_str(tree_kind(params->actuals[i]))); } } return t; }
void vcd_restart(void) { if (vcd_file == NULL) return; vcd_emit_header(); int next_key = 0; const int ndecls = tree_decls(vcd_top); for (int i = 0; i < ndecls; i++) { tree_t d = tree_decl(vcd_top, i); switch (tree_kind(d)) { case T_HIER: fprintf(vcd_file, "$scope module %s $end\n", istr(tree_ident(d))); break; case T_SIGNAL_DECL: if (wave_should_dump(d)) vcd_process_signal(d, &next_key); break; default: break; } int npop = tree_attr_int(d, ident_new("scope_pop"), 0); while (npop-- > 0) fprintf(vcd_file, "$upscope $end\n"); } fprintf(vcd_file, "$enddefinitions $end\n"); fprintf(vcd_file, "$dumpvars\n"); last_time = UINT64_MAX; for (int i = 0; i < ndecls; i++) { tree_t d = tree_decl(vcd_top, i); if (tree_kind(d) == T_SIGNAL_DECL) { vcd_data_t *data = tree_attr_ptr(d, vcd_data_i); if (likely(data != NULL)) vcd_event_cb(0, d, data->watch, data); } } fprintf(vcd_file, "$end\n"); }
bool folded_int(tree_t t, int64_t *l) { if ((tree_kind(t) == T_LITERAL) && (tree_subkind(t) == L_INT)) { *l = tree_ival(t); return true; } else return false; }
static void elab_copy_context(tree_t src, const elab_ctx_t *ctx) { const int nsrc = tree_contexts(src); for (int i = 0; i < nsrc; i++) { tree_t c = tree_context(src, i); if (tree_kind(c) == T_USE) elab_use_clause(c, ctx); } }
static void ungroup_ref(tree_t target, group_nets_ctx_t *ctx) { tree_t decl = tree_ref(target); if (tree_kind(decl) == T_SIGNAL_DECL) { const int nnets = tree_nets(decl); for (int i = 0; i < nnets; i++) group_add(ctx, tree_net(decl, i), 1); } }
bool folded_real(tree_t t, double *l) { if ((tree_kind(t) == T_LITERAL) && (tree_subkind(t) == L_REAL)) { *l = tree_dval(t); return true; } else return false; }
static int shell_cmd_watch(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { const char *help = "watch - Trace changes to a signal\n" "\n" "Usage: watch SIGNALS...\n" "\n" "Prints a message every time an update occurs to a signal listed." "\n" "Examples:\n" " watch [signals {clk}] Trace updates to all signals named clk\n"; if (show_help(objc, objv, help)) return TCL_OK; if (objc == 1) { warnf("nothing to watch (try -help for usage)"); return TCL_OK; } hash_t *decl_hash = (hash_t *)cd; for (int i = 1; i < objc; i++) { int length; if (Tcl_ListObjLength(interp, objv[i], &length) != TCL_OK) return TCL_ERROR; for (int j = 0; j < length; j++) { Tcl_Obj *obj; if (Tcl_ListObjIndex(interp, objv[i], j, &obj) != TCL_OK) return TCL_ERROR; const char *str = Tcl_GetString(obj); tree_t t = hash_get(decl_hash, ident_new(str)); if (t == NULL) return tcl_error(interp, "object not found: %s", str); if (t == NULL) return tcl_error(interp, "object not found: %s", str); else if (tree_kind(t) != T_SIGNAL_DECL) return tcl_error(interp, "not a signal: %s", str); else if (type_is_array(tree_type(t))) return tcl_error(interp, "only scalar signals may be watched"); // TODO: make this work for arrays slave_watch_msg_t msg = { .index = tree_index(t) }; slave_post_msg(SLAVE_WATCH, &msg, sizeof(msg)); } } return TCL_OK; }
int64_t assume_int(tree_t t) { switch (tree_kind(t)) { case T_LITERAL: assert(tree_subkind(t) == L_INT); return tree_ival(t); case T_REF: { tree_t ref = tree_ref(t); assert(tree_kind(ref) == T_ENUM_LIT); return tree_pos(ref); } default: fatal_at(tree_loc(t), "expression cannot be folded to " "an integer constant"); } }
VOID paste_tree(WORD dobj, WORD dx, WORD dy, WORD dup) { if (tree_kind(rcs_clipkind)) { if (ins_tree(ad_clip, ROOT, rcs_clipkind, dobj, dx, dy) != -1L) { if (!dup) clr_clip(); } } }
static bool folded(tree_t t) { tree_kind_t kind = tree_kind(t); if (kind == T_LITERAL) return true; else if (kind == T_REF) { bool dummy; return folded_bool(t, &dummy); } else return false; }
static int shell_cmd_unwatch(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { const char *help = "unwatch - Stop tracing signals\n" "\n" "Usage: unwatch SIGNALS...\n" "\n" "Clears any watch callback on SIGNALS. Note this will also stop any\n" "VCD or other waveform capture for these signals.\n" "\n" "Examples:\n" " watch [signals {clk}] Stop tracing updates to clk\n"; if (show_help(objc, objv, help)) return TCL_OK; if (objc == 1) { warnf("nothing to unwatch (try -help for usage)"); return TCL_OK; } hash_t *decl_hash = (hash_t *)cd; // TODO: refactor this code to avoid duplication with "watch" and "show" for (int i = 1; i < objc; i++) { int length; if (Tcl_ListObjLength(interp, objv[i], &length) != TCL_OK) return TCL_ERROR; for (int j = 0; j < length; j++) { Tcl_Obj *obj; if (Tcl_ListObjIndex(interp, objv[i], j, &obj) != TCL_OK) return TCL_ERROR; const char *str = Tcl_GetString(obj); tree_t t = hash_get(decl_hash, ident_new(str)); if (t == NULL) return tcl_error(interp, "object not found: %s", str); else if (tree_kind(t) != T_SIGNAL_DECL) return tcl_error(interp, "not a signal: %s", str); slave_unwatch_msg_t msg = { .index = tree_index(t) }; slave_post_msg(SLAVE_UNWATCH, &msg, sizeof(msg)); } } return TCL_OK; }
END_TEST START_TEST(test_issue19) { input_from_file(TESTDIR "/elab/issue19.vhd"); const error_t expect[] = { { -1, NULL } }; expect_errors(expect); tree_t e = run_elab(); tree_t tmp = NULL; const int ndecls = tree_decls(e); for (int i = 0; (i < ndecls) && (tmp == NULL); i++) { tree_t t = tree_decl(e, i); if (icmp(tree_ident(t), ":comp6:c1:tmp")) tmp = t; } fail_if(tmp == NULL); tree_t value = tree_value(tmp); fail_unless(tree_kind(value) == T_LITERAL); fail_unless(tree_ival(value) == 32); for (int i = 0; (i < ndecls) && (tmp == NULL); i++) { tree_t t = tree_decl(e, i); if (icmp(tree_ident(t), ":comp6:c1:tmp3")) tmp = t; } fail_if(tmp == NULL); value = tree_value(tmp); fail_unless(tree_kind(value) == T_LITERAL); fail_unless(tree_ival(value) == 32); }
static void group_target(tree_t t, group_nets_ctx_t *ctx) { switch (tree_kind(t)) { case T_REF: group_ref(t, ctx, 0, -1); break; case T_ARRAY_REF: case T_ARRAY_SLICE: case T_RECORD_REF: { type_t type = tree_type(t); if (!type_known_width(type)) ungroup_name(t, ctx); else if (!group_name(t, ctx, 0, type_width(type))) ungroup_name(t, ctx); } break; case T_LITERAL: case T_OPEN: // Constant folding can cause this to appear break; case T_AGGREGATE: { const int nassocs = tree_assocs(t); for (int i = 0; i < nassocs; i++) group_target(tree_value(tree_assoc(t, i)), ctx); } break; default: fmt_loc(stdout, tree_loc(t)); fatal_trace("Cannot handle tree kind %s in group_target", tree_kind_str(tree_kind(t))); } }
static bool elab_have_context(tree_t unit, ident_t name) { const int ndest = tree_contexts(unit); for (int i = 0; i < ndest; i++) { tree_t c2 = tree_context(unit, i); if (tree_kind(c2) != T_USE) continue; if (tree_ident(c2) == name) return true; } return false; }