Merge pull request #1348 from jotego/interface-ports
Support SystemVerilog interface-typed module ports
This commit is contained in:
commit
00325f3efb
62
Module.cc
62
Module.cc
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1998-2022 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 1998-2026 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
|
|
@ -21,13 +21,65 @@
|
|||
|
||||
# include "Module.h"
|
||||
# include "PGate.h"
|
||||
# include "PModport.h"
|
||||
# include "PWire.h"
|
||||
# include "parse_api.h"
|
||||
# include "ivl_assert.h"
|
||||
# include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
list<Module::named_expr_t> Module::user_defparms;
|
||||
|
||||
Module::port_t::port_t()
|
||||
: port_kind(P_SIGNAL), default_value(0), interface_unpacked_dimensions(0), lexical_pos(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool resolve_interface_formal_port(const LineInfo*li, Design*des,
|
||||
const Module::port_t*port,
|
||||
interface_formal_port_t&res,
|
||||
bool emit_errors)
|
||||
{
|
||||
ivl_assert(*li, port);
|
||||
ivl_assert(*li, port->is_interface_port());
|
||||
|
||||
res = interface_formal_port_t();
|
||||
|
||||
map<perm_string,Module*>::const_iterator mod =
|
||||
pform_modules.find(port->interface_type);
|
||||
if (mod == pform_modules.end() || !mod->second->is_interface) {
|
||||
if (emit_errors) {
|
||||
cerr << li->get_fileline() << ": error: Interface port "
|
||||
<< port->name << " uses unknown interface type `"
|
||||
<< port->interface_type << "'." << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
res.module = mod->second;
|
||||
|
||||
if (port->modport_name.str()) {
|
||||
map<perm_string,PModport*>::const_iterator mp =
|
||||
mod->second->modports.find(port->modport_name);
|
||||
if (mp == mod->second->modports.end()) {
|
||||
if (emit_errors) {
|
||||
cerr << li->get_fileline() << ": error: Interface port "
|
||||
<< port->name << " uses unknown modport `"
|
||||
<< port->modport_name << "' of interface `"
|
||||
<< port->interface_type << "'." << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
res.modport = mp->second;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* n is a permallocated string. */
|
||||
Module::Module(LexicalScope*parent, perm_string n)
|
||||
: PScopeExtra(n, parent)
|
||||
|
|
@ -63,12 +115,18 @@ const vector<PEIdent*>& Module::get_port(unsigned idx) const
|
|||
ivl_assert(*this, idx < ports.size());
|
||||
static const vector<PEIdent*> zero;
|
||||
|
||||
if (ports[idx])
|
||||
if (ports[idx] && !ports[idx]->is_interface_port())
|
||||
return ports[idx]->expr;
|
||||
else
|
||||
return zero;
|
||||
}
|
||||
|
||||
const Module::port_t* Module::get_port_info(unsigned idx) const
|
||||
{
|
||||
ivl_assert(*this, idx < ports.size());
|
||||
return ports[idx];
|
||||
}
|
||||
|
||||
unsigned Module::find_port(const char*name) const
|
||||
{
|
||||
ivl_assert(*this, name != 0);
|
||||
|
|
|
|||
32
Module.h
32
Module.h
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef IVL_Module_H
|
||||
#define IVL_Module_H
|
||||
/*
|
||||
* Copyright (c) 1998-2025 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 1998-2026 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
|
|
@ -43,6 +43,7 @@ class PFunction;
|
|||
class PWire;
|
||||
class PProcess;
|
||||
class Design;
|
||||
class LineInfo;
|
||||
class NetScope;
|
||||
|
||||
/*
|
||||
|
|
@ -65,9 +66,25 @@ class Module : public PScopeExtra, public PNamedItem {
|
|||
default value. */
|
||||
public:
|
||||
struct port_t {
|
||||
enum port_kind_t { P_SIGNAL, P_INTERFACE };
|
||||
|
||||
port_t();
|
||||
|
||||
port_kind_t port_kind;
|
||||
perm_string name;
|
||||
std::vector<PEIdent*> expr;
|
||||
PExpr*default_value;
|
||||
|
||||
/* Interface formal port metadata. For signal ports these
|
||||
fields are empty/zero. The modport name is optional in the
|
||||
representation, although the parser initially only accepts
|
||||
the explicit interface_type.modport form. */
|
||||
perm_string interface_type;
|
||||
perm_string modport_name;
|
||||
std::list<pform_range_t>*interface_unpacked_dimensions;
|
||||
unsigned lexical_pos;
|
||||
|
||||
bool is_interface_port() const { return port_kind == P_INTERFACE; }
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
@ -148,6 +165,7 @@ class Module : public PScopeExtra, public PNamedItem {
|
|||
|
||||
unsigned port_count() const;
|
||||
const std::vector<PEIdent*>& get_port(unsigned idx) const;
|
||||
const port_t* get_port_info(unsigned idx) const;
|
||||
unsigned find_port(const char*name) const;
|
||||
|
||||
// Return port name ("" for undeclared port)
|
||||
|
|
@ -181,4 +199,16 @@ class Module : public PScopeExtra, public PNamedItem {
|
|||
Module& operator= (const Module&);
|
||||
};
|
||||
|
||||
struct interface_formal_port_t {
|
||||
interface_formal_port_t() : module(0), modport(0) { }
|
||||
|
||||
const Module*module;
|
||||
const PModport*modport;
|
||||
};
|
||||
|
||||
extern bool resolve_interface_formal_port(const LineInfo*li, Design*des,
|
||||
const Module::port_t*port,
|
||||
interface_formal_port_t&res,
|
||||
bool emit_errors);
|
||||
|
||||
#endif /* IVL_Module_H */
|
||||
|
|
|
|||
9
PGate.h
9
PGate.h
|
|
@ -241,6 +241,15 @@ class PGModule : public PGate {
|
|||
void elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const;
|
||||
void elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const;
|
||||
bool elaborate_sig_mod_(Design*des, NetScope*scope, const Module*mod) const;
|
||||
bool bind_interface_ports_(Design*des, const Module*mod,
|
||||
NetScope*parent_scope, NetScope*instance_scope,
|
||||
const std::vector<PExpr*>&pins,
|
||||
const std::vector<bool>&pins_fromwc) const;
|
||||
bool match_module_ports_(Design*des, const Module*mod,
|
||||
NetScope*scope,
|
||||
std::vector<PExpr*>&pins,
|
||||
std::vector<bool>&pins_fromwc,
|
||||
std::vector<bool>&pins_is_explicitly_not_connected) const;
|
||||
// Not currently used.
|
||||
#if 0
|
||||
bool elaborate_sig_udp_(Design*des, NetScope*scope, PUdp*udp) const;
|
||||
|
|
|
|||
|
|
@ -4733,6 +4733,9 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope,
|
|||
// If the identifier names a signal (a variable or a net)
|
||||
// then create a NetESignal node to handle it.
|
||||
if (sr.net != 0) {
|
||||
if (!check_interface_modport_access(this, des, sr, false))
|
||||
return 0;
|
||||
|
||||
if (NEED_CONST & flags) {
|
||||
cerr << get_fileline() << ": error: A reference to a net "
|
||||
"or variable (`" << path_ << "') is not allowed in "
|
||||
|
|
|
|||
|
|
@ -563,6 +563,9 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!check_interface_modport_access(this, des, sr, true))
|
||||
return 0;
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": " << __func__ << ": "
|
||||
<< "Found l-value path_=" << path_
|
||||
|
|
|
|||
16
elab_sig.cc
16
elab_sig.cc
|
|
@ -44,6 +44,7 @@
|
|||
# include "netqueue.h"
|
||||
# include "netscalar.h"
|
||||
# include "util.h"
|
||||
# include "parse_api.h"
|
||||
# include "ivl_assert.h"
|
||||
|
||||
using namespace std;
|
||||
|
|
@ -298,6 +299,12 @@ bool Module::elaborate_sig(Design*des, NetScope*scope) const
|
|||
if (pp == 0)
|
||||
continue;
|
||||
|
||||
if (pp->is_interface_port()) {
|
||||
interface_formal_port_t formal;
|
||||
resolve_interface_formal_port(this, des, pp, formal, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
// The port has a name and an array of expressions. The
|
||||
// expression are all identifiers that should reference
|
||||
// wires within the scope.
|
||||
|
|
@ -456,6 +463,12 @@ bool PGModule::elaborate_sig_mod_(Design*des, NetScope*scope,
|
|||
|
||||
NetScope::scope_vec_t instance = scope->instance_arrays[get_name()];
|
||||
|
||||
vector<PExpr*>pins (rmod->port_count());
|
||||
vector<bool>pins_fromwc (rmod->port_count(), false);
|
||||
vector<bool>pins_is_explicitly_not_connected (rmod->port_count(), false);
|
||||
flag &= match_module_ports_(des, rmod, scope, pins, pins_fromwc,
|
||||
pins_is_explicitly_not_connected);
|
||||
|
||||
for (unsigned idx = 0 ; idx < instance.size() ; idx += 1) {
|
||||
// I know a priori that the elaborate_scope created the scope
|
||||
// already, so just look it up as a child of the current scope.
|
||||
|
|
@ -471,6 +484,9 @@ bool PGModule::elaborate_sig_mod_(Design*des, NetScope*scope,
|
|||
}
|
||||
ivl_assert(*this, my_scope->parent() == scope);
|
||||
|
||||
if (!bind_interface_ports_(des, rmod, scope, my_scope, pins, pins_fromwc))
|
||||
flag = false;
|
||||
|
||||
if (! rmod->elaborate_sig(des, my_scope))
|
||||
flag = false;
|
||||
|
||||
|
|
|
|||
534
elaborate.cc
534
elaborate.cc
|
|
@ -51,6 +51,7 @@
|
|||
# include "netscalar.h"
|
||||
# include "netclass.h"
|
||||
# include "netmisc.h"
|
||||
# include "PModport.h"
|
||||
# include "util.h"
|
||||
# include "parse_api.h"
|
||||
# include "compiler.h"
|
||||
|
|
@ -1248,6 +1249,432 @@ void elaborate_unpacked_port(Design *des, NetScope *scope, NetNet *port_net,
|
|||
assign_unpacked_with_bufz(des, scope, port_net, port_net, expr_net);
|
||||
}
|
||||
|
||||
bool PGModule::match_module_ports_(Design*des, const Module*rmod,
|
||||
NetScope*scope,
|
||||
vector<PExpr*>&pins,
|
||||
vector<bool>&pins_fromwc,
|
||||
vector<bool>&pins_is_explicitly_not_connected) const
|
||||
{
|
||||
// If the instance has a pins_ member, then we know we are
|
||||
// binding by name. Therefore, make up a pins array that
|
||||
// reflects the positions of the named ports.
|
||||
if (pins_) {
|
||||
unsigned nexp = rmod->port_count();
|
||||
|
||||
// Scan the bindings, matching them with port names.
|
||||
for (unsigned idx = 0 ; idx < npins_ ; idx += 1) {
|
||||
// Handle wildcard named port.
|
||||
if (pins_[idx].name[0] == '*') {
|
||||
for (unsigned j = 0 ; j < nexp ; j += 1) {
|
||||
if (rmod->ports[j] && !pins[j] && !pins_is_explicitly_not_connected[j]) {
|
||||
pins_fromwc[j] = true;
|
||||
pform_name_t path_;
|
||||
path_.push_back(name_component_t(rmod->ports[j]->name));
|
||||
symbol_search_results sr;
|
||||
symbol_search(this, des, scope, path_, UINT_MAX, &sr);
|
||||
if (sr.net != 0 ||
|
||||
(rmod->ports[j]->is_interface_port() &&
|
||||
sr.scope != 0 && sr.scope->is_interface())) {
|
||||
pins[j] = new PEIdent(rmod->ports[j]->name, UINT_MAX, true);
|
||||
pins[j]->set_lineno(get_lineno());
|
||||
pins[j]->set_file(get_file());
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Given a binding, look at the module port names
|
||||
// for the position that matches the binding name.
|
||||
unsigned pidx = rmod->find_port(pins_[idx].name);
|
||||
|
||||
// If the port name doesn't exist, the find_port
|
||||
// method will return the port count. Detect that
|
||||
// as an error.
|
||||
if (pidx == nexp) {
|
||||
cerr << get_fileline() << ": error: port ``" <<
|
||||
pins_[idx].name << "'' is not a port of "
|
||||
<< get_name() << "." << endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If I am overriding a wildcard port, delete and
|
||||
// override it.
|
||||
if (pins_fromwc[pidx]) {
|
||||
delete pins[pidx];
|
||||
pins_fromwc[pidx] = false;
|
||||
|
||||
// If I already explicitly bound something to
|
||||
// this port, then the pins array will already
|
||||
// have a pointer value where I want to place this
|
||||
// expression.
|
||||
} else if (pins[pidx]) {
|
||||
cerr << get_fileline() << ": error: port ``" <<
|
||||
pins_[idx].name << "'' already bound." <<
|
||||
endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// OK, do the binding by placing the expression in
|
||||
// the right place.
|
||||
pins[pidx] = pins_[idx].parm;
|
||||
if (!pins[pidx])
|
||||
pins_is_explicitly_not_connected[pidx] = true;
|
||||
}
|
||||
|
||||
} else if (pin_count() == 0) {
|
||||
/* Handle the special case that no ports are
|
||||
connected. It is possible that this is an empty
|
||||
connect-by-name list, so we'll allow it and assume
|
||||
that is the case. */
|
||||
for (unsigned idx = 0 ; idx < rmod->port_count() ; idx += 1)
|
||||
pins[idx] = 0;
|
||||
|
||||
} else {
|
||||
/* Otherwise, this is a positional list of port
|
||||
connections. Use as many ports as provided. Trailing
|
||||
missing ports will be left unconnect or use the default
|
||||
value if one is available. */
|
||||
if (pin_count() > rmod->port_count()) {
|
||||
cerr << get_fileline() << ": error: Wrong number "
|
||||
"of ports. Expecting at most " << rmod->port_count() <<
|
||||
", got " << pin_count() << "."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::copy(get_pins().begin(), get_pins().end(), pins.begin());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct interface_actual_scope_t {
|
||||
interface_actual_scope_t() : scope(nullptr), modport(nullptr) { }
|
||||
|
||||
NetScope*scope;
|
||||
const PModport*modport;
|
||||
perm_string display_name;
|
||||
};
|
||||
|
||||
struct interface_actual_array_t {
|
||||
std::map<long,NetScope::interface_port_alias_t> elements;
|
||||
perm_string display_name;
|
||||
};
|
||||
|
||||
static long interface_array_index(unsigned idx, long left, long right)
|
||||
{
|
||||
long low = left < right ? left : right;
|
||||
long high = left < right ? right : left;
|
||||
return low < high ? low + idx : low - idx;
|
||||
}
|
||||
|
||||
static bool interface_formal_range(const Module::port_t*port,
|
||||
Design*des, NetScope*scope,
|
||||
const LineInfo*li,
|
||||
long&left, long&right,
|
||||
unsigned&count)
|
||||
{
|
||||
if (!port->interface_unpacked_dimensions) {
|
||||
left = 0;
|
||||
right = 0;
|
||||
count = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (port->interface_unpacked_dimensions->size() != 1) {
|
||||
cerr << li->get_fileline() << ": sorry: Interface port `"
|
||||
<< port->name << "' uses a multidimensional array, which "
|
||||
"is not supported." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!evaluate_range(des, scope, li,
|
||||
port->interface_unpacked_dimensions->front(),
|
||||
left, right))
|
||||
return false;
|
||||
|
||||
count = left > right ? left - right + 1 : right - left + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool resolve_interface_actual_scope(const PExpr*actual,
|
||||
NetScope*parent_scope,
|
||||
Design*des,
|
||||
interface_actual_scope_t&res)
|
||||
{
|
||||
res = interface_actual_scope_t();
|
||||
|
||||
const PEIdent*actual_ident = dynamic_cast<const PEIdent*>(actual);
|
||||
if (!actual_ident || actual_ident->path().package ||
|
||||
actual_ident->path().name.size() != 1 ||
|
||||
actual_ident->path().name.front().index.size() > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const name_component_t&comp = actual_ident->path().name.front();
|
||||
res.display_name = comp.name;
|
||||
|
||||
if (!comp.index.empty()) {
|
||||
if (comp.index.front().sel != index_component_t::SEL_BIT) {
|
||||
cerr << actual->get_fileline() << ": sorry: Interface array "
|
||||
<< "slice actuals are not supported." << endl;
|
||||
des->errors += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool error_flag = false;
|
||||
hname_t actual_name = eval_path_component(des, parent_scope,
|
||||
comp, error_flag);
|
||||
if (error_flag)
|
||||
return true;
|
||||
|
||||
if (NetScope*child = parent_scope->child(actual_name))
|
||||
res.scope = child;
|
||||
else if (actual_name.has_numbers()) {
|
||||
const NetScope::interface_port_alias_t*alias =
|
||||
parent_scope->find_interface_port_alias_element(
|
||||
comp.name, actual_name.peek_number(0));
|
||||
if (alias) {
|
||||
res.scope = alias->actual_scope;
|
||||
res.modport = alias->modport;
|
||||
}
|
||||
}
|
||||
|
||||
if (!res.scope) {
|
||||
symbol_search_results sr;
|
||||
symbol_search(actual, des, parent_scope, actual_ident->path(),
|
||||
actual_ident->lexical_pos(), &sr);
|
||||
res.scope = sr.scope;
|
||||
if (sr.through_interface_alias())
|
||||
res.modport = sr.interface_alias_modport;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
symbol_search_results sr;
|
||||
symbol_search(actual, des, parent_scope, actual_ident->path(),
|
||||
actual_ident->lexical_pos(), &sr);
|
||||
|
||||
res.scope = sr.scope;
|
||||
if (sr.through_interface_alias())
|
||||
res.modport = sr.interface_alias_modport;
|
||||
else if (NetScope*child = parent_scope->child(hname_t(res.display_name)))
|
||||
res.scope = child;
|
||||
else if (const NetScope::interface_port_alias_t*alias =
|
||||
parent_scope->find_interface_port_alias(res.display_name)) {
|
||||
res.scope = alias->actual_scope;
|
||||
res.modport = alias->modport;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool resolve_interface_actual_array(const PExpr*actual,
|
||||
NetScope*parent_scope,
|
||||
interface_actual_array_t&res)
|
||||
{
|
||||
res = interface_actual_array_t();
|
||||
|
||||
const PEIdent*actual_ident = dynamic_cast<const PEIdent*>(actual);
|
||||
if (!actual_ident || actual_ident->path().package ||
|
||||
actual_ident->path().name.size() != 1 ||
|
||||
!actual_ident->path().name.front().index.empty())
|
||||
return false;
|
||||
|
||||
perm_string name = actual_ident->path().name.front().name;
|
||||
res.display_name = name;
|
||||
|
||||
for (NetScope*scope = parent_scope ; scope ; scope = scope->parent()) {
|
||||
auto arr = scope->instance_arrays.find(name);
|
||||
if (arr != scope->instance_arrays.end()) {
|
||||
for (unsigned idx = 0 ; idx < arr->second.size() ; idx += 1) {
|
||||
NetScope*inst = arr->second[idx];
|
||||
if (!inst)
|
||||
return false;
|
||||
hname_t hname = inst->fullname();
|
||||
if (!hname.has_numbers())
|
||||
return false;
|
||||
res.elements[hname.peek_number(0)] =
|
||||
NetScope::interface_port_alias_t(inst, nullptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const map<long,NetScope::interface_port_alias_t>*alias_arr =
|
||||
scope->find_interface_port_alias_array(name);
|
||||
if (alias_arr) {
|
||||
res.elements = *alias_arr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PGModule::bind_interface_ports_(Design*des, const Module*rmod,
|
||||
NetScope*parent_scope,
|
||||
NetScope*instance_scope,
|
||||
const vector<PExpr*>&pins,
|
||||
const vector<bool>&) const
|
||||
{
|
||||
bool flag = true;
|
||||
|
||||
for (unsigned idx = 0 ; idx < rmod->port_count() ; idx += 1) {
|
||||
const Module::port_t*port = rmod->get_port_info(idx);
|
||||
if (!port || !port->is_interface_port())
|
||||
continue;
|
||||
|
||||
if (!pins[idx]) {
|
||||
cerr << get_fileline() << ": error: Interface port `"
|
||||
<< port->name << "' of module " << rmod->mod_name()
|
||||
<< " is not connected." << endl;
|
||||
des->errors += 1;
|
||||
flag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
long formal_left = 0;
|
||||
long formal_right = 0;
|
||||
unsigned formal_count = 1;
|
||||
if (!interface_formal_range(port, des, instance_scope, pins[idx],
|
||||
formal_left, formal_right, formal_count)) {
|
||||
flag = false;
|
||||
continue;
|
||||
}
|
||||
bool formal_is_array = port->interface_unpacked_dimensions != nullptr;
|
||||
|
||||
interface_formal_port_t formal;
|
||||
resolve_interface_formal_port(pins[idx], des, port, formal, false);
|
||||
if (!formal.module)
|
||||
continue;
|
||||
|
||||
if (formal_is_array) {
|
||||
interface_actual_array_t actual_array;
|
||||
if (!resolve_interface_actual_array(pins[idx], parent_scope, actual_array)) {
|
||||
cerr << pins[idx]->get_fileline() << ": error: Interface "
|
||||
<< "array port `" << port->name << "' must be "
|
||||
"connected to an interface instance array." << endl;
|
||||
des->errors += 1;
|
||||
flag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (actual_array.elements.size() != formal_count) {
|
||||
cerr << pins[idx]->get_fileline() << ": error: Interface "
|
||||
<< "array port `" << port->name << "' expects "
|
||||
<< formal_count << " element(s) but actual `"
|
||||
<< actual_array.display_name << "' has "
|
||||
<< actual_array.elements.size() << " element(s)." << endl;
|
||||
des->errors += 1;
|
||||
flag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned pos = 0;
|
||||
bool array_ok = true;
|
||||
for (auto cur = actual_array.elements.begin()
|
||||
; cur != actual_array.elements.end() ; ++cur, ++pos) {
|
||||
NetScope*actual_scope = cur->second.actual_scope;
|
||||
if (!actual_scope || !actual_scope->is_interface()) {
|
||||
cerr << pins[idx]->get_fileline() << ": error: Actual "
|
||||
<< "element for interface array port `"
|
||||
<< port->name << "' is not an interface instance." << endl;
|
||||
des->errors += 1;
|
||||
array_ok = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (actual_scope->module_name() != formal.module->mod_name()) {
|
||||
cerr << pins[idx]->get_fileline() << ": error: Interface "
|
||||
<< "array port `" << port->name
|
||||
<< "' expects interface type `" << port->interface_type
|
||||
<< "' but actual `" << actual_array.display_name
|
||||
<< "' has element type `" << actual_scope->module_name()
|
||||
<< "'." << endl;
|
||||
des->errors += 1;
|
||||
array_ok = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur->second.modport && formal.modport &&
|
||||
cur->second.modport->name() != formal.modport->name()) {
|
||||
cerr << pins[idx]->get_fileline() << ": error: Interface "
|
||||
<< "array port `" << port->name
|
||||
<< "' cannot forward actual `" << actual_array.display_name
|
||||
<< "' restricted by modport `" << cur->second.modport->name()
|
||||
<< "' to formal modport `" << formal.modport->name()
|
||||
<< "'." << endl;
|
||||
des->errors += 1;
|
||||
array_ok = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const PModport*modport = formal.modport?
|
||||
formal.modport : cur->second.modport;
|
||||
long formal_index = interface_array_index(pos, formal_left, formal_right);
|
||||
instance_scope->add_interface_port_alias_element(
|
||||
port->name, formal_index, actual_scope, modport);
|
||||
}
|
||||
|
||||
flag = flag && array_ok;
|
||||
continue;
|
||||
}
|
||||
|
||||
interface_actual_scope_t actual;
|
||||
if (!resolve_interface_actual_scope(pins[idx], parent_scope, des, actual)) {
|
||||
cerr << pins[idx]->get_fileline() << ": error: Interface port `"
|
||||
<< port->name << "' must be connected to a simple "
|
||||
"interface instance name." << endl;
|
||||
des->errors += 1;
|
||||
flag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!actual.scope || !actual.scope->is_interface()) {
|
||||
cerr << pins[idx]->get_fileline() << ": error: Actual for "
|
||||
"interface port `" << port->name
|
||||
<< "' is not an interface instance." << endl;
|
||||
des->errors += 1;
|
||||
flag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (actual.scope->module_name() != formal.module->mod_name()) {
|
||||
cerr << pins[idx]->get_fileline() << ": error: Interface port `"
|
||||
<< port->name << "' expects interface type `"
|
||||
<< port->interface_type << "' but actual `" << actual.display_name
|
||||
<< "' has type `" << actual.scope->module_name() << "'." << endl;
|
||||
des->errors += 1;
|
||||
flag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (actual.modport && formal.modport &&
|
||||
actual.modport->name() != formal.modport->name()) {
|
||||
cerr << pins[idx]->get_fileline() << ": error: Interface port `"
|
||||
<< port->name << "' cannot forward actual `" << actual.display_name
|
||||
<< "' restricted by modport `" << actual.modport->name()
|
||||
<< "' to formal modport `" << formal.modport->name()
|
||||
<< "'." << endl;
|
||||
des->errors += 1;
|
||||
flag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const PModport*modport = formal.modport? formal.modport : actual.modport;
|
||||
instance_scope->add_interface_port_alias(port->name, actual.scope,
|
||||
modport);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
/*
|
||||
* Instantiate a module by recursively elaborating it. Set the path of
|
||||
* the recursive elaboration so that signal names get properly
|
||||
|
|
@ -1274,103 +1701,9 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
|
|||
vector<bool>pins_fromwc (rmod->port_count(), false);
|
||||
vector<bool>pins_is_explicitly_not_connected (rmod->port_count(), false);
|
||||
|
||||
// If the instance has a pins_ member, then we know we are
|
||||
// binding by name. Therefore, make up a pins array that
|
||||
// reflects the positions of the named ports.
|
||||
if (pins_) {
|
||||
unsigned nexp = rmod->port_count();
|
||||
|
||||
// Scan the bindings, matching them with port names.
|
||||
for (unsigned idx = 0 ; idx < npins_ ; idx += 1) {
|
||||
|
||||
// Handle wildcard named port
|
||||
if (pins_[idx].name[0] == '*') {
|
||||
for (unsigned j = 0 ; j < nexp ; j += 1) {
|
||||
if (rmod->ports[j] && !pins[j] && !pins_is_explicitly_not_connected[j]) {
|
||||
pins_fromwc[j] = true;
|
||||
pform_name_t path_;
|
||||
path_.push_back(name_component_t(rmod->ports[j]->name));
|
||||
symbol_search_results sr;
|
||||
symbol_search(this, des, scope, path_, UINT_MAX, &sr);
|
||||
if (sr.net != 0) {
|
||||
pins[j] = new PEIdent(rmod->ports[j]->name, UINT_MAX, true);
|
||||
pins[j]->set_lineno(get_lineno());
|
||||
pins[j]->set_file(get_file());
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Given a binding, look at the module port names
|
||||
// for the position that matches the binding name.
|
||||
unsigned pidx = rmod->find_port(pins_[idx].name);
|
||||
|
||||
// If the port name doesn't exist, the find_port
|
||||
// method will return the port count. Detect that
|
||||
// as an error.
|
||||
if (pidx == nexp) {
|
||||
cerr << get_fileline() << ": error: port ``" <<
|
||||
pins_[idx].name << "'' is not a port of "
|
||||
<< get_name() << "." << endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If I am overriding a wildcard port, delete and
|
||||
// override it
|
||||
if (pins_fromwc[pidx]) {
|
||||
delete pins[pidx];
|
||||
pins_fromwc[pidx] = false;
|
||||
|
||||
// If I already explicitly bound something to
|
||||
// this port, then the pins array will already
|
||||
// have a pointer value where I want to place this
|
||||
// expression.
|
||||
} else if (pins[pidx]) {
|
||||
cerr << get_fileline() << ": error: port ``" <<
|
||||
pins_[idx].name << "'' already bound." <<
|
||||
endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// OK, do the binding by placing the expression in
|
||||
// the right place.
|
||||
pins[pidx] = pins_[idx].parm;
|
||||
if (!pins[pidx])
|
||||
pins_is_explicitly_not_connected[pidx] = true;
|
||||
}
|
||||
|
||||
|
||||
} else if (pin_count() == 0) {
|
||||
|
||||
/* Handle the special case that no ports are
|
||||
connected. It is possible that this is an empty
|
||||
connect-by-name list, so we'll allow it and assume
|
||||
that is the case. */
|
||||
|
||||
for (unsigned idx = 0 ; idx < rmod->port_count() ; idx += 1)
|
||||
pins[idx] = 0;
|
||||
|
||||
} else {
|
||||
|
||||
/* Otherwise, this is a positional list of port
|
||||
connections. Use as many ports as provided. Trailing
|
||||
missing ports will be left unconnect or use the default
|
||||
value if one is available */
|
||||
|
||||
if (pin_count() > rmod->port_count()) {
|
||||
cerr << get_fileline() << ": error: Wrong number "
|
||||
"of ports. Expecting at most " << rmod->port_count() <<
|
||||
", got " << pin_count() << "."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
std::copy(get_pins().begin(), get_pins().end(), pins.begin());
|
||||
}
|
||||
if (!match_module_ports_(des, rmod, scope, pins, pins_fromwc,
|
||||
pins_is_explicitly_not_connected))
|
||||
return;
|
||||
|
||||
// Elaborate these instances of the module. The recursive
|
||||
// elaboration causes the module to generate a netlist with
|
||||
|
|
@ -1403,6 +1736,13 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
|
|||
bool using_default = false;
|
||||
|
||||
perm_string port_name = rmod->get_port_name(idx);
|
||||
const Module::port_t*port_info = rmod->get_port_info(idx);
|
||||
if (port_info && port_info->is_interface_port()) {
|
||||
for (unsigned inst = 0 ; inst < instance.size() ; inst += 1)
|
||||
instance[inst]->add_module_port_info(idx, port_name,
|
||||
PortType::PIMPLICIT, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the port is unconnected, substitute the default
|
||||
// value. The parser ensures that a default value only
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
ivltests/sv_interface_port_array_modport_restrict_fail.v:10: error: Cannot assign to input modport member `value' through interface port `bus'.
|
||||
1 error(s) during elaboration.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ivltests/sv_interface_port_array_size_mismatch_fail.v:12: error: Interface array port `bus' expects 2 element(s) but actual `buses' has 1 element(s).
|
||||
Elaboration failed
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
ivltests/sv_interface_port_forwarding_restrict_fail.v:23: error: Interface member `hidden' is not listed in modport `consumer'.
|
||||
ivltests/sv_interface_port_forwarding_restrict_fail.v:23: error: Unable to elaborate r-value: bus.hidden
|
||||
2 error(s) during elaboration.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ivltests/sv_interface_port_missing_modport_fail.v:18: error: Interface port bus uses unknown modport `consumer' of interface `bus_if'.
|
||||
1 error(s) during elaboration.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ivltests/sv_interface_port_missing_type_fail.v:7: error: Interface port bus uses unknown interface type `missing_if'.
|
||||
1 error(s) during elaboration.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ivltests/sv_interface_port_modport_input_write_fail.v:21: error: Cannot assign to input modport member `value' through interface port `bus'.
|
||||
1 error(s) during elaboration.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ivltests/sv_interface_port_non_interface_actual_fail.v:9: error: Actual for interface port `bus' is not an interface instance.
|
||||
Elaboration failed
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ivltests/sv_interface_port_positional_unconnected_fail.v:7: error: Interface port `bus' of module bus_user is not connected.
|
||||
Elaboration failed
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
ivltests/sv_interface_port_unlisted_member_fail.v:24: error: Interface member `hidden' is not listed in modport `consumer'.
|
||||
ivltests/sv_interface_port_unlisted_member_fail.v:24: error: Unable to elaborate r-value: bus.hidden
|
||||
2 error(s) during elaboration.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ivltests/sv_interface_port_unmodported_missing_type_fail.v:7: error: Interface port bus uses unknown interface type `missing_if'.
|
||||
1 error(s) during elaboration.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ivltests/sv_interface_port_wrong_type_fail.v:9: error: Interface port `bus' expects interface type `bus_if' but actual `bus' has type `other_if'.
|
||||
Elaboration failed
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// This tests a one-dimensional interface formal array connected to a
|
||||
// whole interface instance array, then indexed inside the receiving
|
||||
// module.
|
||||
|
||||
interface bus_if ();
|
||||
logic [7:0] value;
|
||||
modport producer(output value);
|
||||
modport consumer(input value);
|
||||
endinterface
|
||||
|
||||
module drive(input [7:0] val, bus_if.producer bus);
|
||||
assign bus.value = val;
|
||||
endmodule
|
||||
|
||||
module sample(output [7:0] y, bus_if.consumer bus);
|
||||
assign y = bus.value;
|
||||
endmodule
|
||||
|
||||
module child_array(output [7:0] y0, output [7:0] y1,
|
||||
bus_if.consumer bus[2]);
|
||||
sample c0(.y(y0), .bus(bus[0]));
|
||||
sample c1(.y(y1), .bus(bus[1]));
|
||||
endmodule
|
||||
|
||||
module test;
|
||||
bus_if buses[2]();
|
||||
wire [7:0] y0;
|
||||
wire [7:0] y1;
|
||||
|
||||
drive d0(8'd21, buses[0]);
|
||||
drive d1(8'd42, buses[1]);
|
||||
child_array dut(.bus(buses), .y0(y0), .y1(y1));
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
if (y0 !== 8'd21) begin
|
||||
$display("FAILED: y0=%0d", y0);
|
||||
$finish;
|
||||
end
|
||||
if (y1 !== 8'd42) begin
|
||||
$display("FAILED: y1=%0d", y1);
|
||||
$finish;
|
||||
end
|
||||
$display("PASSED");
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// This tests that modport restrictions apply through indexed interface
|
||||
// formal array elements.
|
||||
|
||||
interface bus_if ();
|
||||
logic value;
|
||||
modport consumer(input value);
|
||||
endinterface
|
||||
|
||||
module bad(bus_if.consumer bus[1]);
|
||||
assign bus[0].value = 1'b1;
|
||||
endmodule
|
||||
|
||||
module test;
|
||||
bus_if buses[1]();
|
||||
bad dut(.bus(buses));
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// This tests rejection of a whole interface array actual whose size
|
||||
// does not match the formal interface array size.
|
||||
|
||||
interface bus_if ();
|
||||
endinterface
|
||||
|
||||
module child(bus_if bus[2]);
|
||||
endmodule
|
||||
|
||||
module test;
|
||||
bus_if buses[1]();
|
||||
child dut(.bus(buses));
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// This tests a SystemVerilog interface-typed module port with an
|
||||
// explicit modport and a named actual interface instance connection.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
logic [7:0] lhs;
|
||||
logic [7:0] rhs;
|
||||
|
||||
bus_if bus();
|
||||
|
||||
add_if dut(.bus(bus));
|
||||
|
||||
assign bus.lhs = lhs;
|
||||
assign bus.rhs = rhs;
|
||||
|
||||
initial begin
|
||||
lhs = 8'd5;
|
||||
rhs = 8'd7;
|
||||
#1;
|
||||
if (bus.sum !== 9'd12) begin
|
||||
$display("FAILED");
|
||||
$finish;
|
||||
end
|
||||
$display("PASSED");
|
||||
end
|
||||
endmodule
|
||||
|
||||
interface bus_if #(parameter WIDTH = 8) ();
|
||||
logic [WIDTH-1:0] lhs;
|
||||
logic [WIDTH-1:0] rhs;
|
||||
logic [WIDTH:0] sum;
|
||||
|
||||
modport consumer(
|
||||
input lhs,
|
||||
input rhs,
|
||||
output sum
|
||||
);
|
||||
|
||||
endinterface
|
||||
|
||||
module add_if #(parameter WIDTH = 8) (
|
||||
bus_if.consumer bus
|
||||
);
|
||||
assign bus.sum = bus.lhs + bus.rhs;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// This tests forwarding an interface-typed formal port to a child
|
||||
// module while preserving the parent-facing modport view.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
bus_if bus();
|
||||
|
||||
assign bus.value = 1'b1;
|
||||
parent dut(.bus(bus));
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
if (bus.mirror !== 1'b1) begin
|
||||
$display("FAILED");
|
||||
$finish;
|
||||
end
|
||||
$display("PASSED");
|
||||
end
|
||||
endmodule
|
||||
|
||||
module parent(
|
||||
bus_if.consumer bus
|
||||
);
|
||||
child u_child(.bus(bus));
|
||||
endmodule
|
||||
|
||||
module child(
|
||||
bus_if bus
|
||||
);
|
||||
assign bus.mirror = bus.value;
|
||||
endmodule
|
||||
|
||||
interface bus_if ();
|
||||
logic value;
|
||||
logic mirror;
|
||||
logic hidden;
|
||||
|
||||
modport consumer(input value, output mirror);
|
||||
endinterface
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// This tests that forwarding an interface formal cannot widen access
|
||||
// beyond the parent-facing modport restriction.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
bus_if bus();
|
||||
parent dut(.bus(bus));
|
||||
endmodule
|
||||
|
||||
module parent(
|
||||
bus_if.consumer bus
|
||||
);
|
||||
child u_child(.bus(bus));
|
||||
endmodule
|
||||
|
||||
module child(
|
||||
bus_if bus
|
||||
);
|
||||
logic sample;
|
||||
|
||||
assign sample = bus.hidden;
|
||||
endmodule
|
||||
|
||||
interface bus_if ();
|
||||
logic value;
|
||||
logic hidden;
|
||||
|
||||
modport consumer(input value);
|
||||
endinterface
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// This tests connecting a scalar interface-typed formal to one element
|
||||
// of an interface instance array.
|
||||
|
||||
interface bus_if ();
|
||||
logic [7:0] value;
|
||||
modport producer(output value);
|
||||
modport consumer(input value);
|
||||
endinterface
|
||||
|
||||
module drive(input [7:0] val, bus_if.producer bus);
|
||||
assign bus.value = val;
|
||||
endmodule
|
||||
|
||||
module sample(output [7:0] y, bus_if.consumer bus);
|
||||
assign y = bus.value;
|
||||
endmodule
|
||||
|
||||
module test;
|
||||
bus_if buses[2]();
|
||||
wire [7:0] y;
|
||||
|
||||
drive d0(8'd37, buses[0]);
|
||||
sample s0(.bus(buses[0]), .y(y));
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
if (y !== 8'd37) begin
|
||||
$display("FAILED: y=%0d", y);
|
||||
$finish;
|
||||
end
|
||||
$display("PASSED");
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// This tests connecting scalar interface-typed formals to interface
|
||||
// array elements selected by generated constant indices.
|
||||
|
||||
interface bus_if ();
|
||||
logic [7:0] value;
|
||||
modport producer(output value);
|
||||
modport consumer(input value);
|
||||
endinterface
|
||||
|
||||
module drive(input [7:0] val, bus_if.producer bus);
|
||||
assign bus.value = val;
|
||||
endmodule
|
||||
|
||||
module sample(output [7:0] y, bus_if.consumer bus);
|
||||
assign y = bus.value;
|
||||
endmodule
|
||||
|
||||
module test;
|
||||
bus_if buses[2]();
|
||||
wire [7:0] y[2];
|
||||
|
||||
genvar i;
|
||||
generate
|
||||
for (i = 0; i < 2; i = i + 1) begin : gen
|
||||
localparam [7:0] VAL = 8'd11 + i;
|
||||
drive d(VAL, buses[i]);
|
||||
sample s(.y(y[i]), .bus(buses[i]));
|
||||
end
|
||||
endgenerate
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
if (y[0] !== 8'd11) begin
|
||||
$display("FAILED: y[0]=%0d", y[0]);
|
||||
$finish;
|
||||
end
|
||||
if (y[1] !== 8'd12) begin
|
||||
$display("FAILED: y[1]=%0d", y[1]);
|
||||
$finish;
|
||||
end
|
||||
$display("PASSED");
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// This tests the diagnostic path for an interface-typed module port
|
||||
// that names a modport missing from the interface definition.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
bus_if bus();
|
||||
bus_user dut(.bus(bus));
|
||||
endmodule
|
||||
|
||||
interface bus_if ();
|
||||
logic value;
|
||||
|
||||
modport producer(output value);
|
||||
endinterface
|
||||
|
||||
module bus_user(
|
||||
bus_if.consumer bus
|
||||
);
|
||||
initial $display("FAILED");
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// This tests the diagnostic path for an interface-typed module port
|
||||
// whose interface type name is not declared.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module bus_user(
|
||||
missing_if.consumer bus
|
||||
);
|
||||
initial $display("FAILED");
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// This tests rejection of an assignment through a member declared input
|
||||
// by the selected interface modport.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
bus_if bus();
|
||||
bus_user dut(.bus(bus));
|
||||
endmodule
|
||||
|
||||
interface bus_if ();
|
||||
logic value;
|
||||
|
||||
modport consumer(input value);
|
||||
endinterface
|
||||
|
||||
module bus_user(
|
||||
bus_if.consumer bus
|
||||
);
|
||||
assign bus.value = 1'b1;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// This tests rejection of a non-interface actual connected to an
|
||||
// interface-typed module port.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
logic value;
|
||||
bus_user dut(.bus(value));
|
||||
endmodule
|
||||
|
||||
interface bus_if ();
|
||||
logic value;
|
||||
|
||||
modport consumer(input value);
|
||||
endinterface
|
||||
|
||||
module bus_user(
|
||||
bus_if.consumer bus
|
||||
);
|
||||
initial $display("FAILED");
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// This protects ordinary partial ANSI port parsing near interface
|
||||
// formal grammar.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
logic [3:0] value;
|
||||
logic [3:0] result;
|
||||
|
||||
plain dut(value, result);
|
||||
|
||||
initial begin
|
||||
value = 4'ha;
|
||||
#1;
|
||||
if (result !== 4'ha) begin
|
||||
$display("FAILED");
|
||||
$finish;
|
||||
end
|
||||
$display("PASSED");
|
||||
end
|
||||
endmodule
|
||||
|
||||
module plain(
|
||||
input logic [3:0] value,
|
||||
output logic [3:0] result
|
||||
);
|
||||
assign result = value;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// This tests positional binding of an interface-typed module port.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
bus_if bus();
|
||||
|
||||
assign bus.value = 1'b1;
|
||||
bus_user dut(bus);
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
if (bus.sample !== 1'b1) begin
|
||||
$display("FAILED");
|
||||
$finish;
|
||||
end
|
||||
$display("PASSED");
|
||||
end
|
||||
endmodule
|
||||
|
||||
module bus_user(
|
||||
bus_if.consumer bus
|
||||
);
|
||||
assign bus.sample = bus.value;
|
||||
endmodule
|
||||
|
||||
interface bus_if ();
|
||||
logic value;
|
||||
logic sample;
|
||||
|
||||
modport consumer(input value, output sample);
|
||||
endinterface
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// This tests rejection of an unconnected positional interface port.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
bus_user dut();
|
||||
endmodule
|
||||
|
||||
module bus_user(
|
||||
bus_if.consumer bus
|
||||
);
|
||||
endmodule
|
||||
|
||||
interface bus_if ();
|
||||
logic value;
|
||||
|
||||
modport consumer(input value);
|
||||
endinterface
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// This protects typedef-based ANSI ports from being interpreted as
|
||||
// interface-typed formals.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
typedef logic [3:0] nibble_t;
|
||||
|
||||
module test;
|
||||
nibble_t value;
|
||||
nibble_t result;
|
||||
|
||||
typed dut(value, result);
|
||||
|
||||
initial begin
|
||||
value = 4'h6;
|
||||
#1;
|
||||
if (result !== 4'h6) begin
|
||||
$display("FAILED");
|
||||
$finish;
|
||||
end
|
||||
$display("PASSED");
|
||||
end
|
||||
endmodule
|
||||
|
||||
module typed(
|
||||
input nibble_t value,
|
||||
output nibble_t result
|
||||
);
|
||||
assign result = value;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// This tests rejection of access to an interface member that is not
|
||||
// listed in the selected modport.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
bus_if bus();
|
||||
bus_user dut(.bus(bus));
|
||||
endmodule
|
||||
|
||||
interface bus_if ();
|
||||
logic visible;
|
||||
logic hidden;
|
||||
|
||||
modport consumer(input visible);
|
||||
endinterface
|
||||
|
||||
module bus_user(
|
||||
bus_if.consumer bus
|
||||
);
|
||||
logic sample;
|
||||
|
||||
assign sample = bus.hidden;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// This tests a concrete interface-typed module port without an explicit
|
||||
// modport restriction.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
logic [7:0] lhs;
|
||||
logic [7:0] rhs;
|
||||
|
||||
bus_if bus();
|
||||
|
||||
add_if dut(.bus(bus));
|
||||
|
||||
assign bus.lhs = lhs;
|
||||
assign bus.rhs = rhs;
|
||||
|
||||
initial begin
|
||||
lhs = 8'd9;
|
||||
rhs = 8'd4;
|
||||
#1;
|
||||
if (bus.sum !== 9'd13) begin
|
||||
$display("FAILED");
|
||||
$finish;
|
||||
end
|
||||
$display("PASSED");
|
||||
end
|
||||
endmodule
|
||||
|
||||
module add_if(
|
||||
bus_if bus
|
||||
);
|
||||
assign bus.sum = bus.lhs + bus.rhs;
|
||||
endmodule
|
||||
|
||||
interface bus_if ();
|
||||
logic [7:0] lhs;
|
||||
logic [7:0] rhs;
|
||||
logic [8:0] sum;
|
||||
endinterface
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// This tests the diagnostic path for an unmodported interface-typed
|
||||
// module port whose interface type name is not declared.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module bus_user(
|
||||
missing_if bus
|
||||
);
|
||||
initial $display("FAILED");
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// This tests wildcard binding of an interface-typed module port.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
bus_if bus();
|
||||
|
||||
assign bus.value = 1'b1;
|
||||
bus_user dut(.*);
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
if (bus.sample !== 1'b1) begin
|
||||
$display("FAILED");
|
||||
$finish;
|
||||
end
|
||||
$display("PASSED");
|
||||
end
|
||||
endmodule
|
||||
|
||||
module bus_user(
|
||||
bus_if.consumer bus
|
||||
);
|
||||
assign bus.sample = bus.value;
|
||||
endmodule
|
||||
|
||||
interface bus_if ();
|
||||
logic value;
|
||||
logic sample;
|
||||
|
||||
modport consumer(input value, output sample);
|
||||
endinterface
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// This tests rejection of an actual interface instance whose type does
|
||||
// not match the interface type of the formal module port.
|
||||
//
|
||||
// This file is placed into the Public Domain, for any use, without
|
||||
// warranty.
|
||||
|
||||
module test;
|
||||
other_if bus();
|
||||
bus_user dut(.bus(bus));
|
||||
endmodule
|
||||
|
||||
interface bus_if ();
|
||||
logic value;
|
||||
|
||||
modport consumer(input value);
|
||||
endinterface
|
||||
|
||||
interface other_if ();
|
||||
logic value;
|
||||
|
||||
modport consumer(input value);
|
||||
endinterface
|
||||
|
||||
module bus_user(
|
||||
bus_if.consumer bus
|
||||
);
|
||||
initial $display("FAILED");
|
||||
endmodule
|
||||
|
|
@ -273,6 +273,27 @@ sv_default_port_value3 vvp_tests/sv_default_port_value3.json
|
|||
sv_foreach9 vvp_tests/sv_foreach9.json
|
||||
sv_foreach10 vvp_tests/sv_foreach10.json
|
||||
sv_interface vvp_tests/sv_interface.json
|
||||
sv_interface_port_basic vvp_tests/sv_interface_port_basic.json
|
||||
sv_interface_port_missing_type_fail vvp_tests/sv_interface_port_missing_type_fail.json
|
||||
sv_interface_port_missing_modport_fail vvp_tests/sv_interface_port_missing_modport_fail.json
|
||||
sv_interface_port_non_interface_actual_fail vvp_tests/sv_interface_port_non_interface_actual_fail.json
|
||||
sv_interface_port_wrong_type_fail vvp_tests/sv_interface_port_wrong_type_fail.json
|
||||
sv_interface_port_modport_input_write_fail vvp_tests/sv_interface_port_modport_input_write_fail.json
|
||||
sv_interface_port_unlisted_member_fail vvp_tests/sv_interface_port_unlisted_member_fail.json
|
||||
sv_interface_port_unmodported_basic vvp_tests/sv_interface_port_unmodported_basic.json
|
||||
sv_interface_port_unmodported_missing_type_fail vvp_tests/sv_interface_port_unmodported_missing_type_fail.json
|
||||
sv_interface_port_forwarding vvp_tests/sv_interface_port_forwarding.json
|
||||
sv_interface_port_forwarding_restrict_fail vvp_tests/sv_interface_port_forwarding_restrict_fail.json
|
||||
sv_interface_port_positional vvp_tests/sv_interface_port_positional.json
|
||||
sv_interface_port_positional_unconnected_fail vvp_tests/sv_interface_port_positional_unconnected_fail.json
|
||||
sv_interface_port_wildcard vvp_tests/sv_interface_port_wildcard.json
|
||||
sv_interface_port_indexed_actual vvp_tests/sv_interface_port_indexed_actual.json
|
||||
sv_interface_port_indexed_actual_generate vvp_tests/sv_interface_port_indexed_actual_generate.json
|
||||
sv_interface_port_array_basic vvp_tests/sv_interface_port_array_basic.json
|
||||
sv_interface_port_array_modport_restrict_fail vvp_tests/sv_interface_port_array_modport_restrict_fail.json
|
||||
sv_interface_port_array_size_mismatch_fail vvp_tests/sv_interface_port_array_size_mismatch_fail.json
|
||||
sv_interface_port_plain_ansi_regression vvp_tests/sv_interface_port_plain_ansi_regression.json
|
||||
sv_interface_port_typedef_ansi_regression vvp_tests/sv_interface_port_typedef_ansi_regression.json
|
||||
sv_literals vvp_tests/sv_literals.json
|
||||
sv_lval_concat_class_fail1 vvp_tests/sv_lval_concat_class_fail1.json
|
||||
sv_lval_concat_class_fail2 vvp_tests/sv_lval_concat_class_fail2.json
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_array_basic.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_array_modport_restrict_fail.v",
|
||||
"gold" : "sv_interface_port_array_modport_restrict_fail",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_array_size_mismatch_fail.v",
|
||||
"gold" : "sv_interface_port_array_size_mismatch_fail",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_basic.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_forwarding.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_forwarding_restrict_fail.v",
|
||||
"gold" : "sv_interface_port_forwarding_restrict_fail",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_indexed_actual.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_indexed_actual_generate.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_missing_modport_fail.v",
|
||||
"gold" : "sv_interface_port_missing_modport_fail",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_missing_type_fail.v",
|
||||
"gold" : "sv_interface_port_missing_type_fail",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_modport_input_write_fail.v",
|
||||
"gold" : "sv_interface_port_modport_input_write_fail",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_non_interface_actual_fail.v",
|
||||
"gold" : "sv_interface_port_non_interface_actual_fail",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_plain_ansi_regression.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_positional.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_positional_unconnected_fail.v",
|
||||
"gold" : "sv_interface_port_positional_unconnected_fail",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_typedef_ansi_regression.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_unlisted_member_fail.v",
|
||||
"gold" : "sv_interface_port_unlisted_member_fail",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_unmodported_basic.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_unmodported_missing_type_fail.v",
|
||||
"gold" : "sv_interface_port_unmodported_missing_type_fail",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_wildcard.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_wrong_type_fail.v",
|
||||
"gold" : "sv_interface_port_wrong_type_fail",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
49
lexor.lex
49
lexor.lex
|
|
@ -120,6 +120,8 @@ static list<int> keyword_mask_stack;
|
|||
static int comment_enter;
|
||||
static bool in_module = false;
|
||||
static bool in_UDP = false;
|
||||
static bool in_module_port_list = false;
|
||||
static bool module_port_list_start = false;
|
||||
bool in_celldefine = false;
|
||||
UCDriveType uc_drive = UCD_NONE;
|
||||
static int ts_state = 0;
|
||||
|
|
@ -139,6 +141,12 @@ void lex_in_package_scope(PPackage*pkg)
|
|||
in_package_scope = pkg;
|
||||
}
|
||||
|
||||
void lex_in_module_port_list(bool flag)
|
||||
{
|
||||
in_module_port_list = flag;
|
||||
module_port_list_start = flag;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%x CCOMMENT
|
||||
|
|
@ -160,6 +168,7 @@ void lex_in_package_scope(PPackage*pkg)
|
|||
%x REAL_SCALE
|
||||
|
||||
W [ \t\b\f\r]+
|
||||
ID [a-zA-Z_][a-zA-Z0-9$_]*
|
||||
|
||||
S [afpnumkKMGT]
|
||||
|
||||
|
|
@ -335,7 +344,7 @@ TU [munpf]
|
|||
<EDGES>"z0" { return K_edge_descriptor; }
|
||||
<EDGES>"z1" { return K_edge_descriptor; }
|
||||
|
||||
[a-zA-Z_][a-zA-Z0-9$_]* {
|
||||
{ID} {
|
||||
int rc = lexor_keyword_code(yytext, yyleng);
|
||||
switch (rc) {
|
||||
case IDENTIFIER:
|
||||
|
|
@ -417,6 +426,13 @@ TU [munpf]
|
|||
}
|
||||
}
|
||||
|
||||
/* If this identifier names a previously declared interface, then
|
||||
return this as an INTERFACE_IDENTIFIER instead. */
|
||||
if (rc == IDENTIFIER && gn_system_verilog()) {
|
||||
if (pform_test_interface_identifier(yylval.text))
|
||||
rc = INTERFACE_IDENTIFIER;
|
||||
}
|
||||
|
||||
/* If this identifier names a previously declared type, then
|
||||
return this as a TYPE_IDENTIFIER instead. */
|
||||
if (rc == IDENTIFIER && gn_system_verilog()) {
|
||||
|
|
@ -427,6 +443,22 @@ TU [munpf]
|
|||
}
|
||||
}
|
||||
|
||||
if (rc == IDENTIFIER && gn_system_verilog() &&
|
||||
in_module_port_list && module_port_list_start) {
|
||||
char save_ch = *yy_c_buf_p;
|
||||
*yy_c_buf_p = yy_hold_char;
|
||||
const char*cp = yy_c_buf_p;
|
||||
while (*cp == ' ' || *cp == '\t' || *cp == '\b' ||
|
||||
*cp == '\f' || *cp == '\r' || *cp == '\n')
|
||||
cp += 1;
|
||||
if (*cp == '.' || isalpha(static_cast<unsigned char>(*cp)) ||
|
||||
*cp == '_' || *cp == '\\')
|
||||
rc = INTERFACE_IDENTIFIER;
|
||||
*yy_c_buf_p = save_ch;
|
||||
}
|
||||
|
||||
if (in_module_port_list)
|
||||
module_port_list_start = false;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -442,6 +474,10 @@ TU [munpf]
|
|||
return PACKAGE_IDENTIFIER;
|
||||
}
|
||||
}
|
||||
if (gn_system_verilog()) {
|
||||
if (pform_test_interface_identifier(yylval.text))
|
||||
return INTERFACE_IDENTIFIER;
|
||||
}
|
||||
if (gn_system_verilog()) {
|
||||
if (typedef_t*type = pform_test_type_identifier(yylloc, yylval.text)) {
|
||||
yylval.type_identifier.text = yylval.text;
|
||||
|
|
@ -895,7 +931,16 @@ TU [munpf]
|
|||
`{W} { VLerror(yylloc, "error: Stray tic (`) here. Perhaps you put white "
|
||||
"space between the tic and preprocessor directive?"); }
|
||||
|
||||
. { return yytext[0]; }
|
||||
. {
|
||||
if (in_module_port_list) {
|
||||
if (yytext[0] == '(' || yytext[0] == ',')
|
||||
module_port_list_start = true;
|
||||
else if (yytext[0] != ')' && yytext[0] != '[' &&
|
||||
yytext[0] != ']' && yytext[0] != ':')
|
||||
module_port_list_start = false;
|
||||
}
|
||||
return yytext[0];
|
||||
}
|
||||
|
||||
/* Final catchall. something got lost or mishandled. */
|
||||
/* XXX Should we tell the user something about the lexical state? */
|
||||
|
|
|
|||
58
net_scope.cc
58
net_scope.cc
|
|
@ -812,6 +812,60 @@ const NetScope* NetScope::child(const hname_t&name) const
|
|||
return cur->second;
|
||||
}
|
||||
|
||||
void NetScope::add_interface_port_alias(perm_string formal_name,
|
||||
NetScope*actual_scope,
|
||||
const PModport*modport)
|
||||
{
|
||||
ivl_assert(*this, actual_scope);
|
||||
interface_port_aliases_[formal_name] = interface_port_alias_t(actual_scope, modport);
|
||||
}
|
||||
|
||||
const NetScope::interface_port_alias_t*
|
||||
NetScope::find_interface_port_alias(perm_string formal_name) const
|
||||
{
|
||||
map<perm_string,interface_port_alias_t>::const_iterator cur;
|
||||
cur = interface_port_aliases_.find(formal_name);
|
||||
if (cur == interface_port_aliases_.end())
|
||||
return 0;
|
||||
|
||||
return &cur->second;
|
||||
}
|
||||
|
||||
void NetScope::add_interface_port_alias_element(perm_string formal_name,
|
||||
long index,
|
||||
NetScope*actual_scope,
|
||||
const PModport*modport)
|
||||
{
|
||||
ivl_assert(*this, actual_scope);
|
||||
interface_port_alias_arrays_[formal_name][index] =
|
||||
interface_port_alias_t(actual_scope, modport);
|
||||
}
|
||||
|
||||
const NetScope::interface_port_alias_t*
|
||||
NetScope::find_interface_port_alias_element(perm_string formal_name,
|
||||
long index) const
|
||||
{
|
||||
auto arr = interface_port_alias_arrays_.find(formal_name);
|
||||
if (arr == interface_port_alias_arrays_.end())
|
||||
return 0;
|
||||
|
||||
auto cur = arr->second.find(index);
|
||||
if (cur == arr->second.end())
|
||||
return 0;
|
||||
|
||||
return &cur->second;
|
||||
}
|
||||
|
||||
const map<long,NetScope::interface_port_alias_t>*
|
||||
NetScope::find_interface_port_alias_array(perm_string formal_name) const
|
||||
{
|
||||
auto cur = interface_port_alias_arrays_.find(formal_name);
|
||||
if (cur == interface_port_alias_arrays_.end())
|
||||
return 0;
|
||||
|
||||
return &cur->second;
|
||||
}
|
||||
|
||||
/* Helper function to see if the given scope is defined in a class and if
|
||||
* so return the class scope. */
|
||||
const NetScope* NetScope::get_class_scope() const
|
||||
|
|
@ -867,6 +921,10 @@ bool NetScope::symbol_exists(perm_string sym)
|
|||
return true;
|
||||
if (find_event(sym))
|
||||
return true;
|
||||
if (find_interface_port_alias(sym))
|
||||
return true;
|
||||
if (find_interface_port_alias_array(sym))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
28
netlist.h
28
netlist.h
|
|
@ -79,6 +79,7 @@ class NetEvWait;
|
|||
class PClass;
|
||||
class PExpr;
|
||||
class PFunction;
|
||||
class PModport;
|
||||
class PPackage;
|
||||
class PTaskFunc;
|
||||
class PWire;
|
||||
|
|
@ -1043,6 +1044,31 @@ class NetScope : public Definitions, public Attrib {
|
|||
const NetScope* parent() const { return up_; }
|
||||
const NetScope* child(const hname_t&name) const;
|
||||
|
||||
struct interface_port_alias_t {
|
||||
interface_port_alias_t() : actual_scope(nullptr), modport(nullptr) { }
|
||||
interface_port_alias_t(NetScope*actual, const PModport*mp)
|
||||
: actual_scope(actual), modport(mp) { }
|
||||
|
||||
NetScope*actual_scope;
|
||||
const PModport*modport;
|
||||
};
|
||||
|
||||
/* Interface-typed module formals are represented as aliases to
|
||||
concrete interface instance scopes. These are deliberately kept
|
||||
out of the real child-scope map; only alias-aware lookup paths
|
||||
should traverse them. */
|
||||
void add_interface_port_alias(perm_string formal_name,
|
||||
NetScope*actual_scope,
|
||||
const PModport*modport);
|
||||
const interface_port_alias_t* find_interface_port_alias(perm_string formal_name) const;
|
||||
void add_interface_port_alias_element(perm_string formal_name,
|
||||
long index,
|
||||
NetScope*actual_scope,
|
||||
const PModport*modport);
|
||||
const interface_port_alias_t* find_interface_port_alias_element(perm_string formal_name,
|
||||
long index) const;
|
||||
const std::map<long,interface_port_alias_t>* find_interface_port_alias_array(perm_string formal_name) const;
|
||||
|
||||
/* A helper function to find the enclosing class scope. */
|
||||
const NetScope* get_class_scope() const;
|
||||
|
||||
|
|
@ -1347,6 +1373,8 @@ class NetScope : public Definitions, public Attrib {
|
|||
NetScope*unit_;
|
||||
NetScope*up_;
|
||||
std::map<hname_t,NetScope*> children_;
|
||||
std::map<perm_string,interface_port_alias_t> interface_port_aliases_;
|
||||
std::map<perm_string,std::map<long,interface_port_alias_t> > interface_port_alias_arrays_;
|
||||
|
||||
unsigned lcounter_;
|
||||
bool need_const_func_, is_const_func_, is_auto_, is_cell_, calls_stask_;
|
||||
|
|
|
|||
21
netmisc.h
21
netmisc.h
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef IVL_netmisc_H
|
||||
#define IVL_netmisc_H
|
||||
/*
|
||||
* Copyright (c) 1999-2025 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 1999-2026 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
|
|
@ -50,6 +50,9 @@ struct symbol_search_results {
|
|||
type = 0;
|
||||
eve = 0;
|
||||
decl_after_use = 0;
|
||||
interface_alias_scope = 0;
|
||||
interface_alias_target = 0;
|
||||
interface_alias_modport = 0;
|
||||
}
|
||||
|
||||
inline bool is_scope() const {
|
||||
|
|
@ -76,6 +79,10 @@ struct symbol_search_results {
|
|||
return "nothing found";
|
||||
}
|
||||
|
||||
inline bool through_interface_alias() const {
|
||||
return interface_alias_target != 0;
|
||||
}
|
||||
|
||||
// Scope where symbol was located. This is set in all cases,
|
||||
// assuming the search succeeded.
|
||||
NetScope*scope;
|
||||
|
|
@ -93,6 +100,14 @@ struct symbol_search_results {
|
|||
// one is retained.
|
||||
const LineInfo*decl_after_use;
|
||||
|
||||
// If lookup traversed an interface-typed formal port alias, these
|
||||
// fields describe the alias edge. The resolved object remains in the
|
||||
// normal scope/net/parameter/event fields.
|
||||
NetScope*interface_alias_scope;
|
||||
perm_string interface_alias_name;
|
||||
NetScope*interface_alias_target;
|
||||
const PModport*interface_alias_modport;
|
||||
|
||||
// Store bread crumbs of the search here. The path_tail is the parts
|
||||
// of the original path that were not found, or are after an object
|
||||
// (and so are probably members or methods).
|
||||
|
|
@ -128,6 +143,10 @@ extern bool symbol_search(const LineInfo *li, Design *des, NetScope *scope,
|
|||
const pform_scoped_name_t &path, unsigned lexical_pos,
|
||||
struct symbol_search_results*res);
|
||||
|
||||
extern bool check_interface_modport_access(const LineInfo *li, Design *des,
|
||||
const symbol_search_results &res,
|
||||
bool is_write);
|
||||
|
||||
/*
|
||||
* This function transforms an expression by either zero or sign extending
|
||||
* the high bits until the expression has the desired width. This may mean
|
||||
|
|
|
|||
84
parse.y
84
parse.y
|
|
@ -461,6 +461,32 @@ Module::port_t *module_declare_port(const YYLTYPE&loc, char *id,
|
|||
return port;
|
||||
}
|
||||
|
||||
Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
|
||||
char *modport, char *id,
|
||||
std::list<pform_range_t> *udims,
|
||||
std::list<named_pexpr_t> *attributes)
|
||||
{
|
||||
pform_requires_sv(loc, "Interface port declaration");
|
||||
|
||||
Module::port_t *port = pform_module_interface_port_reference(
|
||||
loc, lex_strings.make(type),
|
||||
modport ? lex_strings.make(modport) : perm_string(),
|
||||
lex_strings.make(id), udims);
|
||||
|
||||
delete[] type;
|
||||
if (modport)
|
||||
delete[] modport;
|
||||
delete[] id;
|
||||
|
||||
pform_module_define_interface_port(loc, port, attributes);
|
||||
|
||||
port_declaration_context.port_type = NetNet::NOT_A_PORT;
|
||||
port_declaration_context.port_net_type = NetNet::NONE;
|
||||
port_declaration_context.data_type = nullptr;
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%union {
|
||||
|
|
@ -575,7 +601,7 @@ Module::port_t *module_declare_port(const YYLTYPE&loc, char *id,
|
|||
enum typedef_t::basic_type typedef_basic_type;
|
||||
};
|
||||
|
||||
%token <text> IDENTIFIER SYSTEM_IDENTIFIER STRING TIME_LITERAL
|
||||
%token <text> IDENTIFIER INTERFACE_IDENTIFIER SYSTEM_IDENTIFIER STRING TIME_LITERAL
|
||||
%token <type_identifier> TYPE_IDENTIFIER
|
||||
%token <package> PACKAGE_IDENTIFIER
|
||||
%token <discipline> DISCIPLINE_IDENTIFIER
|
||||
|
|
@ -4598,10 +4624,20 @@ list_of_port_declarations
|
|||
{ std::vector<Module::port_t*> *ports = $1;
|
||||
|
||||
Module::port_t* port;
|
||||
port = module_declare_port(@4, $4, port_declaration_context.port_type,
|
||||
port_declaration_context.port_net_type,
|
||||
port_declaration_context.data_type,
|
||||
$5, $6, $3);
|
||||
if (port_declaration_context.port_type == NetNet::NOT_A_PORT) {
|
||||
yyerror(@4, "error: Incomplete interface port declaration.");
|
||||
delete[]$4;
|
||||
delete $5;
|
||||
delete $6;
|
||||
delete $3;
|
||||
port = 0;
|
||||
} else {
|
||||
port = module_declare_port(@4, $4,
|
||||
port_declaration_context.port_type,
|
||||
port_declaration_context.port_net_type,
|
||||
port_declaration_context.data_type,
|
||||
$5, $6, $3);
|
||||
}
|
||||
ports->push_back(port);
|
||||
$$ = ports;
|
||||
}
|
||||
|
|
@ -4617,6 +4653,12 @@ port_declaration
|
|||
: attribute_list_opt port_direction net_type_or_var_opt data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt
|
||||
{ $$ = module_declare_port(@5, $5, $2, $3, $4, $6, $7, $1);
|
||||
}
|
||||
| attribute_list_opt INTERFACE_IDENTIFIER '.' IDENTIFIER IDENTIFIER dimensions_opt
|
||||
{ $$ = module_declare_interface_port(@5, $2, $4, $5, $6, $1);
|
||||
}
|
||||
| attribute_list_opt INTERFACE_IDENTIFIER IDENTIFIER dimensions_opt
|
||||
{ $$ = module_declare_interface_port(@3, $2, 0, $3, $4, $1);
|
||||
}
|
||||
| attribute_list_opt net_type_or_var data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt
|
||||
{ pform_requires_sv(@4, "Partial ANSI port declaration");
|
||||
$$ = module_declare_port(@4, $4, port_declaration_context.port_type,
|
||||
|
|
@ -4734,9 +4776,11 @@ module
|
|||
port_declaration_context_init(); }
|
||||
module_package_import_list_opt
|
||||
module_parameter_port_list_opt
|
||||
{ lex_in_module_port_list(true); }
|
||||
module_port_list_opt
|
||||
{ lex_in_module_port_list(false); }
|
||||
module_attribute_foreign ';'
|
||||
{ pform_module_set_ports($8); }
|
||||
{ pform_module_set_ports($9); }
|
||||
timeunits_declaration_opt
|
||||
{ pform_set_scope_timescale(@2); }
|
||||
module_item_list_opt
|
||||
|
|
@ -4759,16 +4803,16 @@ module
|
|||
}
|
||||
// Check that program/endprogram and module/endmodule
|
||||
// keywords match.
|
||||
if ($2 != $15) {
|
||||
if ($2 != $17) {
|
||||
switch ($2) {
|
||||
case K_module:
|
||||
yyerror(@15, "error: module not closed by endmodule.");
|
||||
yyerror(@17, "error: module not closed by endmodule.");
|
||||
break;
|
||||
case K_program:
|
||||
yyerror(@15, "error: program not closed by endprogram.");
|
||||
yyerror(@17, "error: program not closed by endprogram.");
|
||||
break;
|
||||
case K_interface:
|
||||
yyerror(@15, "error: interface not closed by endinterface.");
|
||||
yyerror(@17, "error: interface not closed by endinterface.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -4784,13 +4828,13 @@ module
|
|||
// module.
|
||||
switch ($2) {
|
||||
case K_module:
|
||||
check_end_label(@17, "module", $4, $17);
|
||||
check_end_label(@19, "module", $4, $19);
|
||||
break;
|
||||
case K_program:
|
||||
check_end_label(@17, "program", $4, $17);
|
||||
check_end_label(@19, "program", $4, $19);
|
||||
break;
|
||||
case K_interface:
|
||||
check_end_label(@17, "interface", $4, $17);
|
||||
check_end_label(@19, "interface", $4, $19);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -5160,6 +5204,13 @@ module_item
|
|||
delete[]$2;
|
||||
}
|
||||
|
||||
| attribute_list_opt
|
||||
INTERFACE_IDENTIFIER parameter_value_opt gate_instance_list ';'
|
||||
{ perm_string tmp1 = lex_strings.make($2);
|
||||
pform_make_modgates(@2, tmp1, $3, $4, $1);
|
||||
delete[]$2;
|
||||
}
|
||||
|
||||
| attribute_list_opt
|
||||
IDENTIFIER parameter_value_opt error ';'
|
||||
{ yyerror(@2, "error: Invalid module instantiation");
|
||||
|
|
@ -5167,6 +5218,13 @@ module_item
|
|||
if ($1) delete $1;
|
||||
}
|
||||
|
||||
| attribute_list_opt
|
||||
INTERFACE_IDENTIFIER parameter_value_opt error ';'
|
||||
{ yyerror(@2, "error: Invalid module instantiation");
|
||||
delete[]$2;
|
||||
if ($1) delete $1;
|
||||
}
|
||||
|
||||
/* Continuous assignment can have an optional drive strength, then
|
||||
an optional delay3 that applies to all the assignments in the
|
||||
cont_assign_list. */
|
||||
|
|
|
|||
14
parse_misc.h
14
parse_misc.h
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef IVL_parse_misc_H
|
||||
#define IVL_parse_misc_H
|
||||
/*
|
||||
* Copyright (c) 1998-2024 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 1998-2026 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
|
|
@ -83,6 +83,13 @@ extern UCDriveType uc_drive;
|
|||
*/
|
||||
extern void lex_in_package_scope(PPackage*pkg);
|
||||
|
||||
/*
|
||||
* The parser signals when the lexor is scanning a module/interface/program
|
||||
* port list so that ambiguous SystemVerilog interface formals can be
|
||||
* tokenized without depending on declaration order.
|
||||
*/
|
||||
extern void lex_in_module_port_list(bool flag);
|
||||
|
||||
/*
|
||||
* Test if this identifier is a type identifier in the current
|
||||
* context. The pform code needs to help the lexor here because the
|
||||
|
|
@ -92,6 +99,11 @@ extern void lex_in_package_scope(PPackage*pkg);
|
|||
extern typedef_t* pform_test_type_identifier(const YYLTYPE&loc, const char*txt);
|
||||
extern typedef_t* pform_test_type_identifier(PPackage*pkg, const char*txt);
|
||||
|
||||
/*
|
||||
* Test if this identifier is a previously declared interface name.
|
||||
*/
|
||||
extern bool pform_test_interface_identifier(const char*txt);
|
||||
|
||||
/*
|
||||
* Test if this identifier is a package name. The pform needs to help
|
||||
* the lexor here because the parser detects packages and saves them.
|
||||
|
|
|
|||
37
pform.cc
37
pform.cc
|
|
@ -926,6 +926,14 @@ typedef_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool pform_test_interface_identifier(const char*txt)
|
||||
{
|
||||
perm_string name = lex_strings.make(txt);
|
||||
map<perm_string,Module*>::const_iterator cur = pform_modules.find(name);
|
||||
|
||||
return cur != pform_modules.end() && cur->second->is_interface;
|
||||
}
|
||||
|
||||
PECallFunction* pform_make_call_function(const struct vlltype&loc,
|
||||
const pform_name_t&name,
|
||||
const list<named_pexpr_t> &parms)
|
||||
|
|
@ -1386,6 +1394,35 @@ Module::port_t* pform_module_port_reference(const struct vlltype&loc,
|
|||
return ptmp;
|
||||
}
|
||||
|
||||
Module::port_t* pform_module_interface_port_reference(
|
||||
const struct vlltype&loc,
|
||||
perm_string interface_type,
|
||||
perm_string modport_name,
|
||||
perm_string name,
|
||||
list<pform_range_t>*udims)
|
||||
{
|
||||
Module::port_t*ptmp = new Module::port_t;
|
||||
|
||||
ptmp->port_kind = Module::port_t::P_INTERFACE;
|
||||
ptmp->name = name;
|
||||
ptmp->interface_type = interface_type;
|
||||
ptmp->modport_name = modport_name;
|
||||
ptmp->interface_unpacked_dimensions = udims;
|
||||
ptmp->lexical_pos = loc.lexical_pos;
|
||||
|
||||
return ptmp;
|
||||
}
|
||||
|
||||
void pform_module_define_interface_port(const struct vlltype&loc,
|
||||
Module::port_t*port,
|
||||
list<named_pexpr_t>*attr)
|
||||
{
|
||||
ivl_assert(loc, port);
|
||||
ivl_assert(loc, port->is_interface_port());
|
||||
|
||||
delete attr;
|
||||
}
|
||||
|
||||
void pform_module_set_ports(vector<Module::port_t*>*ports)
|
||||
{
|
||||
assert(! pform_cur_module.empty());
|
||||
|
|
|
|||
9
pform.h
9
pform.h
|
|
@ -167,6 +167,15 @@ extern void pform_module_define_port(const struct vlltype&li,
|
|||
|
||||
extern Module::port_t* pform_module_port_reference(const struct vlltype&loc,
|
||||
perm_string name);
|
||||
extern Module::port_t* pform_module_interface_port_reference(
|
||||
const struct vlltype&loc,
|
||||
perm_string interface_type,
|
||||
perm_string modport_name,
|
||||
perm_string name,
|
||||
std::list<pform_range_t>*udims = 0);
|
||||
extern void pform_module_define_interface_port(const struct vlltype&loc,
|
||||
Module::port_t*port,
|
||||
std::list<named_pexpr_t>*attr);
|
||||
extern void pform_endmodule(const char*, bool inside_celldefine,
|
||||
Module::UCDriveType uc_drive_def);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
# include "compiler.h"
|
||||
# include "PPackage.h"
|
||||
# include "PWire.h"
|
||||
# include "PModport.h"
|
||||
# include "ivl_assert.h"
|
||||
|
||||
using namespace std;
|
||||
|
|
@ -75,6 +76,25 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope,
|
|||
if (! flag)
|
||||
return false;
|
||||
|
||||
if (res->net && res->path_tail.empty() && !res->path_head.empty()) {
|
||||
name_component_t prefix_tail = res->path_head.back();
|
||||
if (prefix_tail.index.empty() &&
|
||||
res->scope->child_byname(prefix_tail.name)) {
|
||||
bool eval_flag = false;
|
||||
hname_t path_item = eval_path_component(des, start_scope,
|
||||
prefix_tail, eval_flag);
|
||||
if (eval_flag) {
|
||||
cerr << li->get_fileline() << ": XXXXX: Errors evaluating scope index" << endl;
|
||||
} else if (NetScope*chld = res->scope->child(path_item)) {
|
||||
if (chld->is_interface()) {
|
||||
res->scope = chld;
|
||||
res->net = 0;
|
||||
res->type = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The prefix is found to be something besides a scope. Put the
|
||||
// tail into the path_tail of the result, and return success. The
|
||||
// caller needs to deal with that tail bit. Note that the
|
||||
|
|
@ -301,6 +321,52 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope,
|
|||
}
|
||||
}
|
||||
|
||||
if (path_tail.index.empty()) {
|
||||
if (const NetScope::interface_port_alias_t*alias =
|
||||
scope->find_interface_port_alias(path_tail.name)) {
|
||||
path.push_back(path_tail);
|
||||
res->scope = alias->actual_scope;
|
||||
res->path_head = path;
|
||||
res->interface_alias_scope = scope;
|
||||
res->interface_alias_name = path_tail.name;
|
||||
res->interface_alias_target = alias->actual_scope;
|
||||
res->interface_alias_modport = alias->modport;
|
||||
|
||||
if (debug_scopes || debug_elaborate) {
|
||||
cerr << li->get_fileline() << ": symbol_search: "
|
||||
<< "Interface alias " << path_tail.name
|
||||
<< " -> " << scope_path(alias->actual_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);
|
||||
if (!flag && path_item.has_numbers() == 1) {
|
||||
if (const NetScope::interface_port_alias_t*alias =
|
||||
scope->find_interface_port_alias_element(path_tail.name,
|
||||
path_item.peek_number(0))) {
|
||||
path.push_back(path_tail);
|
||||
res->scope = alias->actual_scope;
|
||||
res->path_head = path;
|
||||
res->interface_alias_scope = scope;
|
||||
res->interface_alias_name = path_tail.name;
|
||||
res->interface_alias_target = alias->actual_scope;
|
||||
res->interface_alias_modport = alias->modport;
|
||||
|
||||
if (debug_scopes || debug_elaborate) {
|
||||
cerr << li->get_fileline() << ": symbol_search: "
|
||||
<< "Interface alias " << path_tail.name
|
||||
<< "[" << path_item.peek_number(0) << "]"
|
||||
<< " -> " << scope_path(alias->actual_scope) << endl;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't scan up if we are searching within a prefixed scope.
|
||||
if (prefix_scope)
|
||||
break;
|
||||
|
|
@ -396,3 +462,34 @@ bool symbol_search(const LineInfo *li, Design *des, NetScope *scope,
|
|||
return symbol_search(li, des, search_scope, path.name, lexical_pos,
|
||||
res, search_scope, prefix_scope);
|
||||
}
|
||||
|
||||
bool check_interface_modport_access(const LineInfo *li, Design *des,
|
||||
const symbol_search_results &res,
|
||||
bool is_write)
|
||||
{
|
||||
if (!res.through_interface_alias() || !res.interface_alias_modport || !res.net)
|
||||
return true;
|
||||
|
||||
const PModport *modport = res.interface_alias_modport;
|
||||
perm_string member = res.net->name();
|
||||
map<perm_string,PModport::simple_port_t>::const_iterator cur =
|
||||
modport->simple_ports.find(member);
|
||||
|
||||
if (cur == modport->simple_ports.end()) {
|
||||
cerr << li->get_fileline() << ": error: Interface member `"
|
||||
<< member << "' is not listed in modport `"
|
||||
<< modport->name() << "'." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_write && cur->second.first == NetNet::PINPUT) {
|
||||
cerr << li->get_fileline() << ": error: Cannot assign to input "
|
||||
"modport member `" << member << "' through interface port `"
|
||||
<< res.interface_alias_name << "'." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue