Merge pull request #9 from muhammadjawadkhan/feat/clocking-blocks
SV: interface clocking-block vertical slice (Tier A #4)
This commit is contained in:
commit
8504b349f8
|
|
@ -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@
|
||||
|
|
|
|||
4
Module.h
4
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<perm_string,PModport*> modports;
|
||||
|
||||
/* Clocking blocks (modules and interfaces). */
|
||||
std::map<perm_string,PClocking*> clockings;
|
||||
|
||||
/* List for specify paths and timing checks */
|
||||
std::list<PSpecPath*> specify_paths;
|
||||
std::list<PTimingCheck*> timing_checks;
|
||||
|
|
|
|||
|
|
@ -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<PEEvent*>&evs)
|
||||
{
|
||||
events_ = evs;
|
||||
}
|
||||
|
||||
PNamedItem::SymbolType PClocking::symbol_type() const
|
||||
{
|
||||
return CLOCKING;
|
||||
}
|
||||
|
|
@ -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 <map>
|
||||
# include <vector>
|
||||
|
||||
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<PEEvent*>&evs);
|
||||
const std::vector<PEEvent*>& events() const { return events_; }
|
||||
|
||||
std::map<perm_string,signal_t> signals;
|
||||
|
||||
SymbolType symbol_type() const override;
|
||||
|
||||
private:
|
||||
perm_string name_;
|
||||
std::vector<PEEvent*> events_;
|
||||
|
||||
private: // not implemented
|
||||
PClocking(const PClocking&);
|
||||
PClocking& operator= (const PClocking&);
|
||||
};
|
||||
|
||||
#endif /* IVL_PClocking_H */
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<PEEvent*>& get_events() const { return expr_; }
|
||||
|
||||
private:
|
||||
std::vector<PEEvent*>expr_;
|
||||
Statement*statement_;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Each item should be a dedicated `feat/<name>` 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
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
159
elaborate.cc
159
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<PEIdent*>(expr_[0]->expr())) {
|
||||
NetExpr*tmp = elab_and_eval(des, scope, expr_[0]->expr(), -1);
|
||||
if (NetESFunc*sf = dynamic_cast<NetESFunc*>(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<int>(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<PEIdent*>(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<NetESFunc*>(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<int>(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<PEIdent*>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
41
parse.y
41
parse.y
|
|
@ -1099,7 +1099,8 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
|
|||
|
||||
%type <nettype> net_type net_type_opt net_type_or_var net_type_or_var_opt
|
||||
%type <gatetype> gatetype switchtype
|
||||
%type <porttype> port_direction port_direction_opt
|
||||
%type <porttype> port_direction port_direction_opt clocking_direction
|
||||
%type <expr> clocking_skew_opt
|
||||
%type <vartype> integer_vector_type integer_vector_type_no_reg
|
||||
%type <parmvalue> 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
|
||||
|
|
|
|||
74
pform.cc
74
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<set<perm_string> > 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<pform_ident_t>*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<pform_ident_t>::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())
|
||||
|
|
|
|||
11
pform.h
11
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<pform_ident_t>*names);
|
||||
|
||||
/*
|
||||
* This creates an identifier aware of names that may have been
|
||||
* imported from other packages.
|
||||
|
|
|
|||
|
|
@ -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<perm_string,Module*>::const_iterator mod =
|
||||
pform_modules.find(scope->module_name());
|
||||
if (mod == pform_modules.end())
|
||||
return 0;
|
||||
|
||||
map<perm_string,PClocking*>::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<NetScope*>(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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue