From 9601928032a8aa7c1980ee140988b8df83722420 Mon Sep 17 00:00:00 2001 From: mjoekhan Date: Tue, 21 Jul 2026 18:16:16 +0500 Subject: [PATCH] SV: add interface clocking-block vertical slice for UVM Parse interface-local clocking blocks, treat cb as a transparent hop for member R/W, and elaborate @(bif.cb) as the clocking event so basic driver sync works with #0 skew. --- Makefile.in | 2 +- Module.h | 4 + PClocking.cc | 26 ++++++ PClocking.h | 51 +++++++++++ PNamedItem.cc | 3 + PNamedItem.h | 4 +- Statement.h | 2 + docs/ROADMAP.md | 2 +- docs/STATUS.md | 1 + docs/clocking.md | 59 +++++++++++++ elaborate.cc | 159 ++++++++++++++++++++++++++-------- examples/clocking/cb_basic.sv | 30 +++++++ netmisc.h | 4 + parse.y | 41 ++++++++- pform.cc | 74 ++++++++++++++++ pform.h | 11 +++ symbol_search.cc | 42 +++++++++ 17 files changed, 472 insertions(+), 43 deletions(-) create mode 100644 PClocking.cc create mode 100644 PClocking.h create mode 100644 docs/clocking.md create mode 100644 examples/clocking/cb_basic.sv diff --git a/Makefile.in b/Makefile.in index c2befb0bc..ac969870b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -127,7 +127,7 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ pform_types.o \ symbol_search.o sync.o sys_funcs.o verinum.o verireal.o vpi_modules.o target.o \ Attrib.o HName.o Module.o PClass.o PDelays.o PEvent.o PExpr.o PFunction.o \ - PGate.o PGenerate.o PModport.o PNamedItem.o PPackage.o PScope.o PSpec.o PTimingCheck.o \ + PGate.o PGenerate.o PClocking.o PModport.o PNamedItem.o PPackage.o PScope.o PSpec.o PTimingCheck.o \ PTask.o PUdp.o PWire.o Statement.o AStatement.o $M $(FF) $(TT) all: dep config.h _pli_types.h version_tag.h version_base.h ivl@EXEEXT@ diff --git a/Module.h b/Module.h index cd4fde766..9cc17b1ac 100644 --- a/Module.h +++ b/Module.h @@ -36,6 +36,7 @@ class PEIdent; class PGate; class PGenerate; class PModport; +class PClocking; class PSpecPath; class PTimingCheck; class PTask; @@ -154,6 +155,9 @@ class Module : public PScopeExtra, public PNamedItem { program blocks. */ std::map modports; + /* Clocking blocks (modules and interfaces). */ + std::map clockings; + /* List for specify paths and timing checks */ std::list specify_paths; std::list timing_checks; diff --git a/PClocking.cc b/PClocking.cc new file mode 100644 index 000000000..16fc2c1fb --- /dev/null +++ b/PClocking.cc @@ -0,0 +1,26 @@ +/* + * Minimal SystemVerilog clocking-block parse representation. + */ + +# include "config.h" + +# include "PClocking.h" + +PClocking::PClocking(perm_string n) +: name_(n) +{ +} + +PClocking::~PClocking() +{ +} + +void PClocking::set_events(const std::vector&evs) +{ + events_ = evs; +} + +PNamedItem::SymbolType PClocking::symbol_type() const +{ + return CLOCKING; +} diff --git a/PClocking.h b/PClocking.h new file mode 100644 index 000000000..2550c3563 --- /dev/null +++ b/PClocking.h @@ -0,0 +1,51 @@ +#ifndef IVL_PClocking_H +#define IVL_PClocking_H +/* + * Minimal SystemVerilog clocking-block parse representation (UVM Tier A #4). + */ + +# include "PNamedItem.h" +# include "PScope.h" +# include "StringHeap.h" +# include "netlist.h" +# include +# include + +class PEEvent; +class PExpr; + +/* + * PClocking holds a parsed clocking block: the clocking event and the + * list of clocking signals (direction + optional skew expression). + * Skews beyond #0 are accepted in the grammar but ignored at runtime. + */ +class PClocking : public PNamedItem { + + public: + struct signal_t { + NetNet::PortType direction; + PExpr*skew; /* optional; ignored for v1 */ + }; + + explicit PClocking(perm_string name); + ~PClocking() override; + + perm_string name() const { return name_; } + + void set_events(const std::vector&evs); + const std::vector& events() const { return events_; } + + std::map signals; + + SymbolType symbol_type() const override; + + private: + perm_string name_; + std::vector events_; + + private: // not implemented + PClocking(const PClocking&); + PClocking& operator= (const PClocking&); +}; + +#endif /* IVL_PClocking_H */ diff --git a/PNamedItem.cc b/PNamedItem.cc index 70c33a7ca..76d28e1b3 100644 --- a/PNamedItem.cc +++ b/PNamedItem.cc @@ -78,6 +78,9 @@ std::ostream& operator << (std::ostream&o, PNamedItem::SymbolType st) case PNamedItem::MODPORT: o << "a modport"; break; + case PNamedItem::CLOCKING: + o << "a clocking block"; + break; case PNamedItem::PACKAGE: o << "a package"; break; diff --git a/PNamedItem.h b/PNamedItem.h index ba8f86e8f..917adc2e8 100644 --- a/PNamedItem.h +++ b/PNamedItem.h @@ -30,8 +30,8 @@ class PNamedItem : virtual public LineInfo { public: enum SymbolType { ANY, PARAM, NET, VAR, GENVAR, EVENT, TYPE, ENUM, CLASS, FUNCTION, TASK, BLOCK, GENBLOCK, MODPORT, - PACKAGE, MODULE, PROGRAM, INTERFACE, PRIMITIVE, - INSTANCE }; + CLOCKING, PACKAGE, MODULE, PROGRAM, INTERFACE, + PRIMITIVE, INSTANCE }; explicit PNamedItem(); virtual ~PNamedItem() override; diff --git a/Statement.h b/Statement.h index 03fee609b..ae6d79397 100644 --- a/Statement.h +++ b/Statement.h @@ -508,6 +508,8 @@ class PEventStatement : public Statement { NetProc* elaborate_wait(Design*des, NetScope*scope, NetProc*st) const; NetProc* elaborate_wait_fork(Design*des, const NetScope*scope) const; + const std::vector& get_events() const { return expr_; } + private: std::vectorexpr_; Statement*statement_; diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 3fc24f586..57bae2566 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -7,7 +7,7 @@ Each item should be a dedicated `feat/` branch, with tests/examples and a 1. **Parameterized classes** — `class C #(type T = int);` / `C#(byte)` — needed for `uvm_*#(T)`, `config_db` — **partial** (defaults; see STATUS) 2. **Associative arrays** — `int aa[string];` — **in progress / partial** (string keys only; see [assoc-array.md](assoc-array.md)) 3. **Virtual interfaces** + eventing on `vif.clk` — **partial** (see [virtual-interface.md](virtual-interface.md)) -4. **Clocking blocks** — enough for `@(vif.cb)` +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) 6. **Constraints + `randomize()` / `randomize() with`** — start unconstrained `rand`, then solver 7. **`$cast` / `$typename` hardening** for factory patterns diff --git a/docs/STATUS.md b/docs/STATUS.md index 3c4fc6a50..1dde44418 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -16,6 +16,7 @@ Last updated: 2026-07-21 | Parameterized classes | **Partial** on `feat/param-classes` (merged): ANSI `class C #(type T = int, parameter int W = 8);` parses into the class scope and elaborates with **defaults**. Explicit specializations `C#(byte)` / overrides not done yet. See [`examples/param_classes`](../examples/param_classes). | | Associative arrays | **Partial** on `feat/assoc-array`: string-keyed `int aa[string]` / `int aa[*]` with size/num/exists/delete/foreach/copy. See [`docs/assoc-array.md`](assoc-array.md) and [`examples/assoc_array`](../examples/assoc_array). | | Virtual interfaces | **Partial** on `feat/virtual-interface`: `virtual interface T` as class property / TF arg; assign interface instance; member R/W; `@(posedge vif.clk)`. See [`docs/virtual-interface.md`](virtual-interface.md) and [`examples/virtual_interface`](../examples/virtual_interface). | +| 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). | | Constraints / randomize | Missing | | Covergroups / DPI | Missing | diff --git a/docs/clocking.md b/docs/clocking.md new file mode 100644 index 000000000..8edb5c16a --- /dev/null +++ b/docs/clocking.md @@ -0,0 +1,59 @@ +# Clocking blocks (Tier A #4) + +Status: **partial** — interface-local clocking enough for basic driver sync. + +## Supported in this slice + +```systemverilog +interface bus_if(input logic clk); + logic [7:0] data; + logic req; + clocking cb @(posedge clk); + input #0 data; // #1step may be written; treated as #0 + output #0 req; + endclocking +endinterface + +module top; + logic clk; + bus_if bif(clk); + logic [7:0] x; + initial begin + bif.data = 8'h3C; + @(bif.cb); // wait on clocking event (posedge clk) + bif.cb.req <= 1'b1; // NBA to underlying req + x = bif.cb.data; // read current value of data + end +endmodule +``` + +### Pragmatic semantics (v1) + +| Construct | Behavior | +|-----------|----------| +| `@(cb)` / `@(bif.cb)` | Wait on the clocking event (`posedge clk`) | +| `cb.req <= val` | Non-blocking assign to underlying `req` (visible after NBA / next clocking event) | +| `cb.data` (read) | Current value of `data` (sampling skew = `#0`) | +| `#0` / `#1step` skew | Accepted; `#1step` aliases to `#0` if present | + +Clocking signal names are **transparent** hierarchical hops: `bif.cb.req` resolves to `bif.req`. + +## Example + +[`examples/clocking/cb_basic.sv`](../examples/clocking/cb_basic.sv) — prints `PASSED`. + +```bash +./install/bin/iverilog -g2012 -o /tmp/cb_basic.vvp examples/clocking/cb_basic.sv +./install/bin/vvp /tmp/cb_basic.vvp +``` + +## Deferred (do not claim) + +- `default clocking` +- `##` cycle delays +- Modport clocking (`modport m (clocking cb);`) +- Complex / non-zero input/output skews +- `inout` clocking drive/sample timing +- `vif.cb` through virtual interfaces (follow-on once VI member lookup covers clocking) + +See also [STATUS.md](STATUS.md) and [ROADMAP.md](ROADMAP.md). diff --git a/elaborate.cc b/elaborate.cc index 3ae2e13ff..d585b5198 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -55,6 +55,7 @@ # include "netclass.h" # include "netmisc.h" # include "PModport.h" +# include "PClocking.h" # include "util.h" # include "parse_api.h" # include "compiler.h" @@ -5408,46 +5409,61 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope, } /* Vertical-slice support: @(posedge vif.clk) where vif is a - virtual interface. Emit $ivl_vif_wait then the continuation. */ + virtual interface. Emit $ivl_vif_wait then the continuation. + Skip speculative elaborate when the path is a clocking wait. */ if (expr_.size() == 1 && dynamic_cast(expr_[0]->expr())) { - NetExpr*tmp = elab_and_eval(des, scope, expr_[0]->expr(), -1); - if (NetESFunc*sf = dynamic_cast(tmp)) { - if (strcmp(sf->name(), "$ivl_vif_get") == 0 && sf->nparms() == 2) { - int edge_code = 2; /* anyedge */ - switch (expr_[0]->type()) { - case PEEvent::POSEDGE: edge_code = 0; break; - case PEEvent::NEGEDGE: edge_code = 1; break; - case PEEvent::ANYEDGE: edge_code = 2; break; - case PEEvent::EDGE: edge_code = 2; break; - default: break; - } - NetExpr*idx_ex = sf->parm(1); - long midx = 0; - if (!eval_as_long(midx, idx_ex)) { - cerr << get_fileline() << ": error: " - << "Virtual interface wait member index " - << "must be constant." << endl; - des->errors += 1; - delete tmp; - return 0; - } - /* Dup the VI base — NetESFunc::parm(idx,0) deletes - the old expression, so we cannot steal in place. */ - NetExpr*vif_base = sf->parm(0)->dup_expr(); - delete tmp; - NetSTask*wait_task = elab_vif_wait_task(*this, vif_base, - edge_code, - static_cast(midx)); - if (!enet) - return wait_task; - NetBlock*blk = new NetBlock(NetBlock::SEQU, 0); - blk->set_line(*this); - blk->append(wait_task); - blk->append(enet); - return blk; + const PEIdent*vid = dynamic_cast(expr_[0]->expr()); + bool maybe_clocking = false; + if (vid && !vid->path().name.empty()) { + symbol_search_results csr; + if (symbol_search(this, des, scope, vid->path(), + vid->lexical_pos(), &csr) && + csr.is_scope() && csr.scope) { + perm_string tail = vid->path().name.back().name; + if (find_scope_clocking(csr.scope, tail)) + maybe_clocking = true; } } - delete tmp; + if (!maybe_clocking) { + NetExpr*tmp = elab_and_eval(des, scope, expr_[0]->expr(), -1); + if (NetESFunc*sf = dynamic_cast(tmp)) { + if (strcmp(sf->name(), "$ivl_vif_get") == 0 && sf->nparms() == 2) { + int edge_code = 2; /* anyedge */ + switch (expr_[0]->type()) { + case PEEvent::POSEDGE: edge_code = 0; break; + case PEEvent::NEGEDGE: edge_code = 1; break; + case PEEvent::ANYEDGE: edge_code = 2; break; + case PEEvent::EDGE: edge_code = 2; break; + default: break; + } + NetExpr*idx_ex = sf->parm(1); + long midx = 0; + if (!eval_as_long(midx, idx_ex)) { + cerr << get_fileline() << ": error: " + << "Virtual interface wait member index " + << "must be constant." << endl; + des->errors += 1; + delete tmp; + return 0; + } + /* Dup the VI base — NetESFunc::parm(idx,0) deletes + the old expression, so we cannot steal in place. */ + NetExpr*vif_base = sf->parm(0)->dup_expr(); + delete tmp; + NetSTask*wait_task = elab_vif_wait_task(*this, vif_base, + edge_code, + static_cast(midx)); + if (!enet) + return wait_task; + NetBlock*blk = new NetBlock(NetBlock::SEQU, 0); + blk->set_line(*this); + blk->append(wait_task); + blk->append(enet); + return blk; + } + } + delete tmp; + } } /* Create a single NetEvent and NetEvWait. Then, create a @@ -5554,7 +5570,8 @@ cerr << endl; /* If the expression is an identifier that matches a named event, then handle this case all at once and - skip the rest of the expression handling. */ + skip the rest of the expression handling. Also handle + terminal clocking-block waits: @(cb) / @(bif.cb). */ if (const PEIdent*id = dynamic_cast(expr_[idx]->expr())) { symbol_search_results sr; @@ -5586,6 +5603,72 @@ cerr << endl; } continue; } + + /* @(bif.cb): path ends at a clocking block name that + was treated as transparent during symbol_search. */ + if (sr.is_scope() && sr.scope && !id->path().name.empty()) { + perm_string cb_name = id->path().name.back().name; + const PClocking*cb = find_scope_clocking(sr.scope, cb_name); + if (cb && !cb->events().empty()) { + NetScope*cb_scope = sr.scope; + for (size_t eidx = 0 ; eidx < cb->events().size() + ; eidx += 1) { + PEEvent*cev = cb->events()[eidx]; + if (!cev || !cev->expr()) { + cerr << get_fileline() << ": error: " + "Clocking block '" << cb_name + << "' has an empty clocking event." + << endl; + des->errors += 1; + continue; + } + NetExpr*tmp = elab_and_eval(des, cb_scope, + cev->expr(), -1); + if (tmp == 0) { + cerr << get_fileline() << ": error: " + "Failed to evaluate clocking event for '" + << cb_name << "'." << endl; + des->errors += 1; + continue; + } + NetNet*expr = tmp->synthesize(des, cb_scope, tmp); + if (expr == 0) { + des->errors += 1; + delete tmp; + continue; + } + delete tmp; + + unsigned pins = (cev->type() == PEEvent::ANYEDGE) + ? expr->pin_count() : 1; + NetEvProbe*pr = 0; + switch (cev->type()) { + case PEEvent::POSEDGE: + pr = new NetEvProbe(scope, scope->local_symbol(), + ev, NetEvProbe::POSEDGE, pins); + break; + case PEEvent::NEGEDGE: + pr = new NetEvProbe(scope, scope->local_symbol(), + ev, NetEvProbe::NEGEDGE, pins); + break; + case PEEvent::EDGE: + pr = new NetEvProbe(scope, scope->local_symbol(), + ev, NetEvProbe::EDGE, pins); + break; + case PEEvent::ANYEDGE: + default: + pr = new NetEvProbe(scope, scope->local_symbol(), + ev, NetEvProbe::ANYEDGE, pins); + break; + } + for (unsigned p = 0 ; p < pr->pin_count() ; p += 1) + connect(pr->pin(p), expr->pin(p)); + des->add_node(pr); + expr_count += 1; + } + continue; + } + } } diff --git a/examples/clocking/cb_basic.sv b/examples/clocking/cb_basic.sv new file mode 100644 index 000000000..270eded11 --- /dev/null +++ b/examples/clocking/cb_basic.sv @@ -0,0 +1,30 @@ +// Clocking-block vertical slice (UVM Tier A #4). +// Interface-local clocking with @(bif.cb), NBA write, and sampled read. +interface bus_if(input logic clk); + logic [7:0] data; + logic req; + clocking cb @(posedge clk); + input #0 data; + output #0 req; + endclocking +endinterface + +module top; + logic clk; + bus_if bif(clk); + logic [7:0] x; + initial begin clk = 0; forever #5 clk = ~clk; end + initial begin + bif.data = 8'h3C; + @(bif.cb); + bif.cb.req <= 1'b1; // NBA to underlying req (#0 skew) + x = bif.cb.data; // #0 sample of data + @(bif.cb); // next clocking event: NBA is visible on bif.req + if (x !== 8'h3C || bif.req !== 1'b1) begin + $display("FAILED x=%0h req=%0b", x, bif.req); + $finish; + end + $display("PASSED"); + $finish; + end +endmodule diff --git a/netmisc.h b/netmisc.h index e8eed8b12..3dd511eed 100644 --- a/netmisc.h +++ b/netmisc.h @@ -147,6 +147,10 @@ extern bool check_interface_modport_access(const LineInfo *li, Design *des, const symbol_search_results &res, bool is_write); +class PClocking; +extern const PClocking* find_scope_clocking(const NetScope*scope, + perm_string name); + /* * This function transforms an expression by either zero or sign extending * the high bits until the expression has the desired width. This may mean diff --git a/parse.y b/parse.y index 5d0915747..331a09717 100644 --- a/parse.y +++ b/parse.y @@ -1099,7 +1099,8 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type net_type net_type_opt net_type_or_var net_type_or_var_opt %type gatetype switchtype -%type port_direction port_direction_opt +%type port_direction port_direction_opt clocking_direction +%type clocking_skew_opt %type integer_vector_type integer_vector_type_no_reg %type parameter_value_opt @@ -2521,6 +2522,37 @@ modport_tf_port | K_function data_type_or_implicit_or_void IDENTIFIER tf_port_list_parens_opt ; + /* Minimal clocking-block items: direction [#skew] signals ; */ +clocking_item_list_opt + : clocking_item_list + | + ; + +clocking_item_list + : clocking_item + | clocking_item_list clocking_item + ; + +clocking_direction + : K_input { $$ = NetNet::PINPUT; } + | K_output { $$ = NetNet::POUTPUT; } + | K_inout { $$ = NetNet::PINOUT; } + ; + +clocking_skew_opt + : '#' delay_value_simple { $$ = $2; } + | { $$ = 0; } + ; + +clocking_item + : clocking_direction clocking_skew_opt list_of_identifiers ';' + { pform_add_clocking_signals(@1, $1, $2, $3); } + | error ';' + { yyerror(@1, "error: Invalid clocking block item."); + yyerrok; + } + ; + non_integer_type /* IEEE1800-2005: A.2.2.1 */ : K_real { $$ = real_type_t::REAL; } | K_realtime { $$ = real_type_t::REAL; } @@ -5935,6 +5967,13 @@ module_item | modport_declaration + /* SystemVerilog clocking blocks (Tier A #4 vertical slice). */ + | attribute_list_opt K_clocking identifier_name event_control ';' + { pform_start_clocking(@2, $3, $4); } + clocking_item_list_opt + K_endclocking + { pform_end_clocking(@8); } + /* 1364-2001 and later allow specparam declarations outside specify blocks. */ | attribute_list_opt K_specparam diff --git a/pform.cc b/pform.cc index 5e5e7c5f8..29cfda436 100644 --- a/pform.cc +++ b/pform.cc @@ -31,6 +31,7 @@ # include "PUdp.h" # include "PGenerate.h" # include "PModport.h" +# include "PClocking.h" # include "PSpec.h" # include "PTimingCheck.h" # include "discipline.h" @@ -367,6 +368,7 @@ static list > conditional_block_names; /* This tracks the current modport list being processed. This is always within an interface. */ static PModport*pform_cur_modport = 0; +static PClocking*pform_cur_clocking = 0; static NetNet::Type pform_default_nettype = NetNet::WIRE; @@ -3554,6 +3556,78 @@ void pform_add_modport_port(const struct vlltype&loc, pform_cur_modport->simple_ports[name] = make_pair(port_type, expr); } +void pform_start_clocking(const struct vlltype&loc, const char*name, + PEventStatement*ev) +{ + Module*scope = pform_cur_module.front(); + ivl_assert(loc, scope); + ivl_assert(loc, pform_cur_clocking == 0); + + pform_requires_sv(loc, "Clocking block"); + + perm_string use_name = lex_strings.make(name); + pform_cur_clocking = new PClocking(use_name); + FILE_NAME(pform_cur_clocking, loc); + + if (ev) { + pform_cur_clocking->set_events(ev->get_events()); + /* Keep PEEvent objects; drop the statement wrapper. */ + delete ev; + } else { + cerr << loc << ": error: Clocking block '" << use_name + << "' requires a clocking event." << endl; + error_count += 1; + } + + if (scope->clockings.find(use_name) != scope->clockings.end()) { + cerr << loc << ": error: Duplicate clocking block '" + << use_name << "'." << endl; + error_count += 1; + } + + add_local_symbol(scope, use_name, pform_cur_clocking); + scope->clockings[use_name] = pform_cur_clocking; + + delete[] name; +} + +void pform_end_clocking(const struct vlltype&loc) +{ + ivl_assert(loc, pform_cur_clocking); + pform_cur_clocking = 0; +} + +void pform_add_clocking_signals(const struct vlltype&loc, + NetNet::PortType direction, + PExpr*skew, + std::list*names) +{ + ivl_assert(loc, pform_cur_clocking); + ivl_assert(loc, names); + + /* Skews other than #0 / #1step are accepted but ignored for v1. */ + (void)skew; + + for (list::iterator cur = names->begin() + ; cur != names->end() ; ++cur) { + perm_string name = cur->first; + if (pform_cur_clocking->signals.find(name) + != pform_cur_clocking->signals.end()) { + cerr << loc << ": error: Duplicate clocking signal '" + << name << "' in clocking block '" + << pform_cur_clocking->name() << "'." << endl; + error_count += 1; + continue; + } + PClocking::signal_t sig; + sig.direction = direction; + sig.skew = skew; + pform_cur_clocking->signals[name] = sig; + } + + delete names; +} + bool pform_requires_sv(const struct vlltype&loc, const char *feature) { if (gn_system_verilog()) diff --git a/pform.h b/pform.h index 6c1ed7fc1..27ac08136 100644 --- a/pform.h +++ b/pform.h @@ -235,6 +235,17 @@ extern void pform_add_modport_port(const struct vlltype&loc, NetNet::PortType port_type, perm_string name, PExpr*expr); +/* + * Clocking-block related functions (Tier A #4 vertical slice). + */ +extern void pform_start_clocking(const struct vlltype&loc, const char*name, + PEventStatement*ev); +extern void pform_end_clocking(const struct vlltype&loc); +extern void pform_add_clocking_signals(const struct vlltype&loc, + NetNet::PortType direction, + PExpr*skew, + std::list*names); + /* * This creates an identifier aware of names that may have been * imported from other packages. diff --git a/symbol_search.cc b/symbol_search.cc index 863c9717e..35ebdad2b 100644 --- a/symbol_search.cc +++ b/symbol_search.cc @@ -26,10 +26,31 @@ # include "PPackage.h" # include "PWire.h" # include "PModport.h" +# include "PClocking.h" +# include "Module.h" +# include "parse_api.h" # include "ivl_assert.h" using namespace std; +const PClocking* find_scope_clocking(const NetScope*scope, perm_string name) +{ + if (!scope || scope->type() != NetScope::MODULE) + return 0; + + map::const_iterator mod = + pform_modules.find(scope->module_name()); + if (mod == pform_modules.end()) + return 0; + + map::const_iterator cb = + mod->second->clockings.find(name); + if (cb == mod->second->clockings.end()) + return 0; + + return cb->second; +} + /* * Search for the hierarchical name. The path may have multiple components. If * that's the case, then recursively pull the path apart until we find the @@ -340,6 +361,27 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, return true; } + + /* Clocking blocks are transparent for hierarchical + descent: bif.cb.req resolves to bif.req. Terminal + @(bif.cb) is handled in event elaboration. */ + if (scope->type() == NetScope::MODULE && + find_scope_clocking(scope, path_tail.name)) { + path.push_back(path_tail); + res->scope = const_cast(scope); + res->net = 0; + res->eve = 0; + res->par_val = 0; + res->type = 0; + res->path_head = path; + if (debug_scopes || debug_elaborate) { + cerr << li->get_fileline() << ": symbol_search: " + << "Clocking '" << path_tail.name + << "' is transparent in " + << scope_path(scope) << endl; + } + return true; + } } else if (scope->find_interface_port_alias_array(path_tail.name)) { bool flag = false; hname_t path_item = eval_path_component(des, start_scope, path_tail, flag);