From 23f29890564a3039b3a7050b905853b067f55c69 Mon Sep 17 00:00:00 2001 From: mjoekhan Date: Tue, 21 Jul 2026 19:08:05 +0500 Subject: [PATCH] SV: add class-handle $cast and static $typename Enable UVM factory-style downcasts via runtime is-a checks on the class inheritance chain, and fold $typename to documented static type strings (e.g. "class derived"). --- docs/ROADMAP.md | 2 +- docs/STATUS.md | 1 + docs/cast-typename.md | 77 +++++++++ elab_expr.cc | 148 ++++++++++++++++++ elaborate.cc | 48 ++++++ examples/cast_typename/cast_typename_basic.sv | 66 ++++++++ ivl_target.h | 2 + t-dll-api.cc | 8 + tgt-vvp/draw_class.c | 12 +- tgt-vvp/eval_vec4.c | 49 ++++++ tgt-vvp/vvp_priv.h | 4 + tgt-vvp/vvp_process.c | 9 ++ vvp/class_type.cc | 21 ++- vvp/class_type.h | 7 + vvp/codes.h | 1 + vvp/compile.cc | 1 + vvp/compile.h | 1 + vvp/parse.y | 5 + vvp/vthread.cc | 31 ++++ vvp/vvp_cobject.h | 2 + 20 files changed, 491 insertions(+), 4 deletions(-) create mode 100644 docs/cast-typename.md create mode 100644 examples/cast_typename/cast_typename_basic.sv diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 0f56e2d6c..c3f504d22 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -10,7 +10,7 @@ Each item should be a dedicated `feat/` branch, with tests/examples and a 4. **Clocking blocks** — enough for `@(vif.cb)` — **partial** (interface-local `@(bif.cb)`; see [clocking.md](clocking.md)) 5. **`mailbox` / `semaphore` builtins** (or solid class equivalents with blocking put/get) — **partial** (int mailbox + semaphore; see [mailbox-semaphore.md](mailbox-semaphore.md)) 6. **Constraints + `randomize()` / `randomize() with`** — **partial** (unconstrained `rand`/`randc` only; see [randomize.md](randomize.md)); solver / `with` still missing -7. **`$cast` / `$typename` hardening** for factory patterns +7. **`$cast` / `$typename` hardening** for factory patterns — **partial** (class-handle `$cast` + static `$typename`; see [cast-typename.md](cast-typename.md)) 8. **Covergroups** — functional coverage 9. **DPI-C** — optional but common in real flows diff --git a/docs/STATUS.md b/docs/STATUS.md index 7308993e7..4973560b1 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -19,6 +19,7 @@ Last updated: 2026-07-21 | Clocking blocks | **Partial** on `feat/clocking-blocks`: interface-local `clocking`; `@(bif.cb)`; `cb.sig` R/W with `#0` skew. See [`docs/clocking.md`](clocking.md) and [`examples/clocking`](../examples/clocking). | | Mailbox / semaphore | **Partial** on `feat/mailbox-semaphore`: compiler builtins; `mailbox #(int)` + blocking/nonblocking put/get/num; `semaphore` get/put/try_get. See [`docs/mailbox-semaphore.md`](mailbox-semaphore.md) and [`examples/mailbox_sem`](../examples/mailbox_sem). | | Constraints / randomize | **Partial** on `feat/randomize-unconstrained`: unconstrained `rand`/`randc` integral props via `obj.randomize()` (returns 1). No constraint solver / `with` yet. See [`docs/randomize.md`](randomize.md) and [`examples/randomize`](../examples/randomize). | +| `$cast` / `$typename` | **Partial** on `feat/cast-typename`: class-handle `$cast` (dynamic is-a + assign); static `$typename` → `"class "` etc. See [`docs/cast-typename.md`](cast-typename.md) and [`examples/cast_typename`](../examples/cast_typename). | | Covergroups / DPI | Missing | ## Remotes diff --git a/docs/cast-typename.md b/docs/cast-typename.md new file mode 100644 index 000000000..b4ea8a796 --- /dev/null +++ b/docs/cast-typename.md @@ -0,0 +1,77 @@ +# `$cast` / `$typename` (Tier A #7, first slice) + +Status: **partial** — class-handle `$cast` and static `$typename` for common UVM factory-style patterns. + +## Supported in this slice + +```systemverilog +class base; endclass +class derived extends base; + int x; + function new(); x = 7; endfunction +endclass + +base b; +derived d, d2; +bit ok; +string tn; + +d = new; +b = d; +ok = $cast(d2, b); // 1 if b's dynamic type is-a derived +tn = $typename(d); // "class derived" +``` + +### `$cast(dest, src)` + +| Form | Behavior | +|------|----------| +| Function `ok = $cast(dest, src)` | Returns `1` on success, `0` on failure; on success assigns `src` into `dest` | +| Task `$cast(dest, src);` | Same assign/check; return value discarded (no runtime fatal on failure yet) | + +- **Class handles only** (destination must be a simple class variable) +- Success when `src` is non-null and its **runtime** class is `dest`'s declared type or a subclass (inheritance walk) +- Failure leaves `dest` unchanged and returns `0` +- Null `src` → failure + +### `$typename(expr)` / `$typename(type)` + +Returns a **compile-time** string for the **static** type of the argument (optional second length argument is accepted and ignored, for Accellera `uvm_typename` compatibility). + +| Argument | String format | +|----------|----------------| +| Class variable / type | `class ` (e.g. `class derived`) | +| `string` | `string` | +| `real` | `real` | +| `bit` / `logic` (and other integral) | `bit` / `logic` (width not included yet) | +| Anything else | `unknown` | + +**Limitation:** `$typename` does **not** report the dynamic (runtime) type of a class handle. `$typename(b)` is `"class base"` even when `b` holds a `derived` object. Use `$cast` / virtual `get_type_name()`-style methods for dynamic discrimination. + +## Encoding + +| Layer | Role | +|-------|------| +| Elab | `$cast` → `$ivl_cast(dest, src)`; `$typename` → `NetECString` | +| Codegen | `%cast/cobj C, v` after evaluating `src` | +| Runtime | Walk `class_type` super chain (`is_a`); store handle on success | +| Class emit | `.class "name" [n], C` records inheritance for the walk | + +## Deferred (do not claim) + +- Non-class `$cast` (integrals, enums, etc.) +- Destinations that are class properties / selects (`obj.m`, `arr[i]`) +- Task-form fatal on failed cast (IEEE optional behavior) +- Dynamic `$typename` / full IEEE type-string formatting (packed ranges, enums, packages) +- `$typename` second-argument truncation + +## Example + +[`examples/cast_typename/cast_typename_basic.sv`](../examples/cast_typename/cast_typename_basic.sv) — prints `PASSED`. + +```bash +./install/bin/iverilog -g2012 -o /tmp/cast.vvp examples/cast_typename/cast_typename_basic.sv +./install/bin/vvp /tmp/cast.vvp +``` + +See also [STATUS.md](STATUS.md) and [ROADMAP.md](ROADMAP.md). diff --git a/elab_expr.cc b/elab_expr.cc index 1088589f6..f5e729d82 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1775,6 +1775,34 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, return expr_width_; } + if (name=="$cast") { + /* Function form returns bit success; args are self-determined. */ + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { + if (parms_[idx].parm) { + width_mode_t arg_mode = SIZED; + parms_[idx].parm->test_width(des, scope, arg_mode); + } + } + expr_type_ = IVL_VT_BOOL; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; + return expr_width_; + } + + if (name=="$typename") { + if (parms_.size() >= 1 && parms_[0].parm) { + width_mode_t arg_mode = SIZED; + if (! dynamic_cast(parms_[0].parm)) + parms_[0].parm->test_width(des, scope, arg_mode); + } + expr_type_ = IVL_VT_STRING; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; + return expr_width_; + } + /* Get the return type of the system function by looking it up in the sfunc_table. */ const struct sfunc_return_type*sfunc_info = lookup_sys_func(name); @@ -2411,6 +2439,126 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, return cast_to_width_(sub, expr_wid); } + /* $cast(dest, src): class-handle dynamic cast. Rewritten to + $ivl_cast so codegen can assign dest and return success. */ + if (name=="$cast") { + if (parms_.size() != 2 || !parms_[0].parm || !parms_[1].parm) { + cerr << get_fileline() << ": error: $cast takes exactly " + << "two arguments: $cast(dest, src)." << endl; + des->errors += 1; + return 0; + } + + NetExpr*dest_ex = elab_sys_task_arg(des, scope, name, 0, parms_[0].parm); + NetExpr*src_ex = elab_sys_task_arg(des, scope, name, 1, parms_[1].parm); + if (!dest_ex || !src_ex) { + delete dest_ex; + delete src_ex; + return 0; + } + + NetESignal*dest_sig = dynamic_cast(dest_ex); + const netclass_t*dest_cls = 0; + if (dest_sig && dest_sig->sig()) + dest_cls = dynamic_cast(dest_sig->sig()->net_type()); + + if (!dest_sig || !dest_cls) { + cerr << get_fileline() << ": sorry: $cast currently supports " + << "class-handle destinations only." << endl; + des->errors += 1; + delete dest_ex; + delete src_ex; + return 0; + } + + if (src_ex->expr_type() != IVL_VT_CLASS + && ! dynamic_cast(src_ex)) { + cerr << get_fileline() << ": sorry: $cast currently supports " + << "class-handle sources only." << endl; + des->errors += 1; + delete dest_ex; + delete src_ex; + return 0; + } + + NetESFunc*sys = new NetESFunc("$ivl_cast", + &netvector_t::scalar_logic, 2); + sys->set_line(*this); + sys->parm(0, dest_ex); + sys->parm(1, src_ex); + return pad_to_width(sys, expr_wid, signed_flag_, *this); + } + + /* $typename(expr): fold to a string describing the static type. + Format for classes is "class " (see docs/cast-typename.md). */ + if (name=="$typename") { + if (parms_.empty() || !parms_[0].parm) { + cerr << get_fileline() << ": error: $typename takes at least " + << "one argument." << endl; + des->errors += 1; + return 0; + } + if (parms_.size() > 2) { + cerr << get_fileline() << ": error: $typename takes one or " + << "two arguments." << endl; + des->errors += 1; + return 0; + } + + PExpr*texpr = parms_[0].parm; + string tname; + + if (const PETypename*type_expr = dynamic_cast(texpr)) { + ivl_type_t data_type = type_expr->get_type()->elaborate_type(des, scope); + if (const netclass_t*cls = dynamic_cast(data_type)) { + tname = string("class ") + cls->get_name().str(); + } else if (data_type && data_type->base_type() == IVL_VT_STRING) { + tname = "string"; + } else if (data_type && data_type->base_type() == IVL_VT_REAL) { + tname = "real"; + } else if (data_type && data_type->base_type() == IVL_VT_BOOL) { + tname = "bit"; + } else if (data_type && data_type->base_type() == IVL_VT_LOGIC) { + tname = "logic"; + } else { + tname = "unknown"; + } + } else { + NetExpr*ne = elab_sys_task_arg(des, scope, name, 0, texpr); + if (!ne) + return 0; + + if (ne->expr_type() == IVL_VT_CLASS) { + const netclass_t*cls = dynamic_cast(ne->net_type()); + if (!cls) { + if (NetESignal*sig = dynamic_cast(ne)) { + if (sig->sig()) + cls = dynamic_cast(sig->sig()->net_type()); + } + } + if (cls) + tname = string("class ") + cls->get_name().str(); + else + tname = "class"; + } else if (ne->expr_type() == IVL_VT_STRING) { + tname = "string"; + } else if (ne->expr_type() == IVL_VT_REAL) { + tname = "real"; + } else if (ne->expr_type() == IVL_VT_BOOL) { + tname = "bit"; + } else if (ne->expr_type() == IVL_VT_LOGIC) { + tname = "logic"; + } else { + tname = "unknown"; + } + delete ne; + } + + NetECString*str = new NetECString(tname); + str->set_line(*this); + return str; + } + unsigned nparms = parms_.size(); NetESFunc*fun = new NetESFunc(name, expr_type_, expr_width_, nparms, is_overridden_); diff --git a/elaborate.cc b/elaborate.cc index 22e8cb115..8f2d25a98 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -3922,6 +3922,54 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const scope->calls_sys_task(true); + /* Task form of $cast(dest, src): same runtime as the function. */ + if (name == "$cast") { + if (parm_count != 2 || !parms_[0].parm || !parms_[1].parm) { + cerr << get_fileline() << ": error: $cast takes exactly " + << "two arguments: $cast(dest, src)." << endl; + des->errors += 1; + for (unsigned idx = 0 ; idx < parm_count ; idx += 1) + delete eparms[idx]; + return 0; + } + + /* Re-elaborate dest/src with class-handle checks via a + temporary function-style call path is awkward here; do + lightweight validation and emit $ivl_cast. */ + NetExpr*dest_ex = eparms[0]; + NetExpr*src_ex = eparms[1]; + NetESignal*dest_sig = dynamic_cast(dest_ex); + const netclass_t*dest_cls = 0; + if (dest_sig && dest_sig->sig()) + dest_cls = dynamic_cast(dest_sig->sig()->net_type()); + + if (!dest_sig || !dest_cls) { + cerr << get_fileline() << ": sorry: $cast currently supports " + << "class-handle destinations only." << endl; + des->errors += 1; + for (unsigned idx = 0 ; idx < parm_count ; idx += 1) + delete eparms[idx]; + return 0; + } + if (src_ex && src_ex->expr_type() != IVL_VT_CLASS + && ! dynamic_cast(src_ex)) { + cerr << get_fileline() << ": sorry: $cast currently supports " + << "class-handle sources only." << endl; + des->errors += 1; + for (unsigned idx = 0 ; idx < parm_count ; idx += 1) + delete eparms[idx]; + return 0; + } + + vector cparms(2); + cparms[0] = dest_ex; + cparms[1] = src_ex; + NetSTask*cur = new NetSTask("$ivl_cast", + def_sfunc_as_task, cparms); + cur->set_line(*this); + return cur; + } + NetSTask*cur = new NetSTask(name, def_sfunc_as_task, eparms); cur->set_line(*this); return cur; diff --git a/examples/cast_typename/cast_typename_basic.sv b/examples/cast_typename/cast_typename_basic.sv new file mode 100644 index 000000000..ea1a0351e --- /dev/null +++ b/examples/cast_typename/cast_typename_basic.sv @@ -0,0 +1,66 @@ +// Tier A #7 smoke: $cast (class handles) + $typename (static type string). +class base; endclass +class derived extends base; + int x; + function new(); x = 7; endfunction +endclass + +module top; + base b, b2; + derived d, d2; + string tn; + bit ok; + int pass; + + initial begin + pass = 1; + + d = new; + b = d; + + ok = $cast(d2, b); + if (!ok || d2 == null || d2.x !== 7) begin + $display("FAIL: downcast ok=%0b d2.x=%0d", ok, d2 == null ? -1 : d2.x); + pass = 0; + end + + // Upcast always succeeds for a live derived object. + ok = $cast(b2, d); + if (!ok || b2 == null) begin + $display("FAIL: upcast"); + pass = 0; + end + + // Task form: same dynamic check / assign. + d2 = null; + $cast(d2, b); + if (d2 == null || d2.x !== 7) begin + $display("FAIL: task-form $cast"); + pass = 0; + end + + // Incompatible: base-only object into derived. + b2 = new; + ok = $cast(d2, b2); + if (ok) begin + $display("FAIL: expected incompatible cast to fail"); + pass = 0; + end + + tn = $typename(d); + if (tn != "class derived") begin + $display("FAIL: $typename(d)=\"%s\" want \"class derived\"", tn); + pass = 0; + end + + tn = $typename(b); + if (tn != "class base") begin + $display("FAIL: $typename(b)=\"%s\" want \"class base\"", tn); + pass = 0; + end + + $display("typename=%s", $typename(d)); + if (pass) $display("PASSED"); + else $fatal(1, "cast/typename smoke failed"); + end +endmodule diff --git a/ivl_target.h b/ivl_target.h index 732ed649e..bca4a0b6a 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -2398,6 +2398,8 @@ extern int ivl_type_packed_msb(ivl_type_t net, unsigned dim); extern unsigned ivl_type_packed_width(ivl_type_t net); extern int ivl_type_signed(ivl_type_t net); extern const char* ivl_type_name(ivl_type_t net); +/* Superclass for a class type, or 0 if none / not a class. */ +extern ivl_type_t ivl_type_super(ivl_type_t net); extern int ivl_type_properties(ivl_type_t net); extern const char* ivl_type_prop_name(ivl_type_t net, int idx); extern ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx); diff --git a/t-dll-api.cc b/t-dll-api.cc index 68a80ae89..3b5415943 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -3290,6 +3290,14 @@ extern "C" const char* ivl_type_name(ivl_type_t net) return 0; } +extern "C" ivl_type_t ivl_type_super(ivl_type_t net) +{ + if (const netclass_t*class_type = dynamic_cast(net)) + return class_type->get_super(); + + return 0; +} + extern "C" int ivl_type_properties(ivl_type_t net) { const netclass_t*class_type = dynamic_cast(net); diff --git a/tgt-vvp/draw_class.c b/tgt-vvp/draw_class.c index f5cd21d73..cdbee7751 100644 --- a/tgt-vvp/draw_class.c +++ b/tgt-vvp/draw_class.c @@ -81,8 +81,16 @@ static void show_prop_type(ivl_type_t ptype) void draw_class_in_scope(ivl_type_t classtype) { int idx; - fprintf(vvp_out, "C%p .class \"%s\" [%d]\n", - classtype, ivl_type_name(classtype), ivl_type_properties(classtype)); + ivl_type_t super = ivl_type_super(classtype); + + if (super) + fprintf(vvp_out, "C%p .class \"%s\" [%d], C%p\n", + classtype, ivl_type_name(classtype), + ivl_type_properties(classtype), super); + else + fprintf(vvp_out, "C%p .class \"%s\" [%d]\n", + classtype, ivl_type_name(classtype), + ivl_type_properties(classtype)); for (idx = 0 ; idx < ivl_type_properties(classtype) ; idx += 1) { fprintf(vvp_out, " %3d: \"%s\", ", idx, ivl_type_prop_name(classtype,idx)); diff --git a/tgt-vvp/eval_vec4.c b/tgt-vvp/eval_vec4.c index 8270148f3..27fb0f17f 100644 --- a/tgt-vvp/eval_vec4.c +++ b/tgt-vvp/eval_vec4.c @@ -94,6 +94,45 @@ void draw_ivl_randomize(ivl_expr_t obj, int leave_result) fprintf(vvp_out, " %%pushi/vec4 1, 0, 1;\n"); } +/* + * Class-handle $cast: evaluate src object, then %cast/cobj into dest. + * Leaves a 1-bit success flag when leave_result is set. + */ +void draw_ivl_cast(ivl_expr_t dest, ivl_expr_t src, int leave_result) +{ + ivl_signal_t dest_sig = 0; + ivl_type_t dest_type = 0; + + if (dest && ivl_expr_type(dest) == IVL_EX_SIGNAL) { + dest_sig = ivl_expr_signal(dest); + if (dest_sig) + dest_type = ivl_signal_net_type(dest_sig); + } + + if (src) + draw_eval_object(src); + + if (dest_sig == 0 || dest_type == 0 + || ivl_type_base(dest_type) != IVL_VT_CLASS) { + if (src) + fprintf(vvp_out, " %%pop/obj 1, 0;\n"); + if (leave_result) + fprintf(vvp_out, " %%pushi/vec4 0, 0, 1;\n"); + return; + } + + if (! src) { + /* No source: fail without touching dest. */ + if (leave_result) + fprintf(vvp_out, " %%pushi/vec4 0, 0, 1;\n"); + return; + } + + fprintf(vvp_out, " %%cast/cobj C%p, v%p_0;\n", dest_type, dest_sig); + if (! leave_result) + fprintf(vvp_out, " %%pop/vec4 1;\n"); +} + /* * Test if the draw_immediate_vec4 instruction can be used. */ @@ -1338,6 +1377,16 @@ static void draw_sfunc_vec4(ivl_expr_t expr) return; } + /* Class-handle $cast(dest, src) -> $ivl_cast */ + if (strcmp(ivl_expr_name(expr), "$ivl_cast") == 0) { + ivl_expr_t dest = (parm_count > 0) ? ivl_expr_parm(expr, 0) : 0; + ivl_expr_t src = (parm_count > 1) ? ivl_expr_parm(expr, 1) : 0; + draw_ivl_cast(dest, src, 1); + if (ivl_expr_width(expr) > 1) + fprintf(vvp_out, " %%pad/u %u;\n", ivl_expr_width(expr)); + return; + } + draw_vpi_func_call(expr); } diff --git a/tgt-vvp/vvp_priv.h b/tgt-vvp/vvp_priv.h index 6345c6059..73acddc76 100644 --- a/tgt-vvp/vvp_priv.h +++ b/tgt-vvp/vvp_priv.h @@ -213,6 +213,10 @@ extern void draw_eval_vec4(ivl_expr_t ex); * Leaves a 1-bit success flag on the vec4 stack when leave_result!=0; * otherwise pops the result (task form). */ extern void draw_ivl_randomize(ivl_expr_t obj, int leave_result); + +/* Emit class-handle $cast as $ivl_cast(dest, src). Stores src into dest + * when the dynamic type is compatible; leaves 1-bit ok when leave_result. */ +extern void draw_ivl_cast(ivl_expr_t dest, ivl_expr_t src, int leave_result); extern void resize_vec4_wid(ivl_expr_t expr, unsigned wid); /* diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index 02b2c83a2..2fbe03f01 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -2055,6 +2055,15 @@ static int show_system_task_call(ivl_statement_t net) return 0; } + if (strcmp(stmt_name, "$ivl_cast") == 0) { + ivl_expr_t dest = ivl_stmt_parm_count(net) > 0 + ? ivl_stmt_parm(net, 0) : 0; + ivl_expr_t src = ivl_stmt_parm_count(net) > 1 + ? ivl_stmt_parm(net, 1) : 0; + draw_ivl_cast(dest, src, 0); + return 0; + } + if (strcmp(stmt_name, "$ivl_semaphore$get") == 0) { ivl_expr_t sem = ivl_stmt_parm(net, 0); if (sem) draw_eval_object(sem); diff --git a/vvp/class_type.cc b/vvp/class_type.cc index 034ac768d..a4acbac6d 100644 --- a/vvp/class_type.cc +++ b/vvp/class_type.cc @@ -397,7 +397,7 @@ void property_object::copy(char*dst, char*src) /* **** */ class_type::class_type(const string&nam, size_t nprop) -: class_name_(nam), properties_(nprop) +: class_name_(nam), super_(0), properties_(nprop) { instance_size_ = 0; } @@ -408,6 +408,17 @@ class_type::~class_type() delete properties_[idx].type; } +bool class_type::is_a(const class_type*target) const +{ + if (target == 0) + return false; + for (const class_type*cur = this; cur; cur = cur->get_super()) { + if (cur == target) + return true; + } + return false; +} + void class_type::set_property(size_t idx, const string&name, const string&type, uint64_t array_size) { assert(idx < properties_.size()); @@ -596,6 +607,14 @@ void compile_class_property(unsigned idx, char*nam, char*typ, uint64_t array_siz delete[]typ; } +void compile_class_set_super(char*super_lab) +{ + assert(compile_class); + if (super_lab == 0) + return; + compile_vpi_lookup(compile_class->super_handle_ptr(), super_lab); +} + void compile_class_done(void) { __vpiScope*scope = vpip_peek_current_scope(); diff --git a/vvp/class_type.h b/vvp/class_type.h index 4a36ed6e7..83d7f14d5 100644 --- a/vvp/class_type.h +++ b/vvp/class_type.h @@ -43,6 +43,12 @@ class class_type : public __vpiHandle { // This is the name of the class type. inline const std::string&class_name(void) const { return class_name_; } + // Superclass (may be filled later via compile_vpi_lookup). + inline const class_type* get_super(void) const + { return dynamic_cast(super_); } + inline vpiHandle* super_handle_ptr(void) { return &super_; } + /* True if this type is target or derived from target. */ + bool is_a(const class_type*target) const; // Number of properties in the class definition. inline size_t property_count(void) const { return properties_.size(); } @@ -76,6 +82,7 @@ class class_type : public __vpiHandle { private: std::string class_name_; + vpiHandle super_; struct prop_t { std::string name; diff --git a/vvp/codes.h b/vvp/codes.h index dd11ad676..879678527 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -64,6 +64,7 @@ extern bool of_CASSIGN_VEC4(vthread_t thr, vvp_code_t code); extern bool of_CASSIGN_VEC4_OFF(vthread_t thr, vvp_code_t code); extern bool of_CASSIGN_WR(vthread_t thr, vvp_code_t code); extern bool of_CAST2(vthread_t thr, vvp_code_t code); +extern bool of_CAST_COBJ(vthread_t thr, vvp_code_t code); extern bool of_CAST_VEC2_DAR(vthread_t thr, vvp_code_t code); extern bool of_CAST_VEC4_DAR(vthread_t thr, vvp_code_t code); extern bool of_CAST_VEC4_STR(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index 6d8e7dd24..4eee30ef0 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -125,6 +125,7 @@ static const struct opcode_table_s opcode_table[] = { { "%cassign/vec4", of_CASSIGN_VEC4, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%cassign/vec4/off",of_CASSIGN_VEC4_OFF,2,{OA_FUNC_PTR,OA_BIT1, OA_NONE} }, { "%cassign/wr", of_CASSIGN_WR, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, + { "%cast/cobj", of_CAST_COBJ, 2, {OA_VPI_PTR, OA_FUNC_PTR2, OA_NONE} }, { "%cast/vec2/dar", of_CAST_VEC2_DAR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%cast/vec4/dar", of_CAST_VEC4_DAR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%cast/vec4/str", of_CAST_VEC4_STR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, diff --git a/vvp/compile.h b/vvp/compile.h index b73872260..70920dd09 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -585,6 +585,7 @@ extern void delete_udp_symbols(void); extern void compile_class_start(char*lab, char*nam, unsigned nprop); extern void compile_class_property(unsigned idx, char*nam, char*typ, uint64_t array_size); +extern void compile_class_set_super(char*super_lab); extern void compile_class_done(void); #endif /* IVL_compile_H */ diff --git a/vvp/parse.y b/vvp/parse.y index 5c7998080..af68ac6da 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -916,6 +916,11 @@ statement class_properties_opt ';' { compile_class_done(); } + | T_LABEL K_CLASS T_STRING '[' T_NUMBER ']' ',' T_SYMBOL + { compile_class_start($1, $3, $5); compile_class_set_super($8); } + class_properties_opt ';' + { compile_class_done(); } + | enum_type { ; } diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 112f85f14..b7d04839d 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -1750,6 +1750,37 @@ bool of_CAST2(vthread_t thr, vvp_code_t) return true; } +/* + * %cast/cobj , + * + * Pop a class object from the object stack. If its dynamic type is an + * instance of (or derived from) , store it into + * and push 1 onto the vec4 stack; otherwise leave dest unchanged and + * push 0. A null source fails the cast. + */ +bool of_CAST_COBJ(vthread_t thr, vvp_code_t cp) +{ + const class_type*dest_type = dynamic_cast(cp->handle); + assert(dest_type); + assert(cp->net2); + + vvp_object_t obj; + thr->pop_object(obj); + + int ok = 0; + if (! obj.test_nil()) { + vvp_cobject*cobj = obj.peek(); + if (cobj && cobj->get_defn() && cobj->get_defn()->is_a(dest_type)) { + vvp_net_ptr_t ptr(cp->net2, 0); + vvp_send_object(ptr, obj, thr->wt_context); + ok = 1; + } + } + + thr->push_vec4(vvp_vector4_t(1, ok ? BIT4_1 : BIT4_0)); + return true; +} + bool do_cast_vec_dar(vthread_t thr, vvp_code_t cp, bool as_vec4) { unsigned wid = cp->number; diff --git a/vvp/vvp_cobject.h b/vvp/vvp_cobject.h index 758c83dcb..c63dc0440 100644 --- a/vvp/vvp_cobject.h +++ b/vvp/vvp_cobject.h @@ -46,6 +46,8 @@ class vvp_cobject : public vvp_object { void shallow_copy(const vvp_object*that) override; + inline const class_type* get_defn(void) const { return defn_; } + private: const class_type* defn_; // For now, only support 32bit bool signed properties.