feat(interface): broaden interface port binding
This commit is contained in:
parent
c963809709
commit
39072cd452
48
elaborate.cc
48
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"
|
||||
|
|
@ -1261,12 +1262,14 @@ bool PGModule::match_module_ports_(Design*des, const Module*rmod,
|
|||
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) {
|
||||
if (sr.net != 0 ||
|
||||
(rmod->ports[j]->is_interface_port() &&
|
||||
sr.scope != 0 && sr.scope->is_interface())) {
|
||||
pins_fromwc[j] = true;
|
||||
pins[j] = new PEIdent(rmod->ports[j]->name, UINT_MAX, true);
|
||||
pins[j]->set_lineno(get_lineno());
|
||||
pins[j]->set_file(get_file());
|
||||
|
|
@ -1326,7 +1329,7 @@ bool PGModule::bind_interface_ports_(Design*des, const Module*rmod,
|
|||
NetScope*parent_scope,
|
||||
NetScope*instance_scope,
|
||||
const vector<PExpr*>&pins,
|
||||
const vector<bool>&pins_fromwc) const
|
||||
const vector<bool>&) const
|
||||
{
|
||||
bool flag = true;
|
||||
|
||||
|
|
@ -1335,15 +1338,6 @@ bool PGModule::bind_interface_ports_(Design*des, const Module*rmod,
|
|||
if (!port || !port->is_interface_port())
|
||||
continue;
|
||||
|
||||
if (pins_fromwc[idx]) {
|
||||
cerr << get_fileline() << ": sorry: Wildcard connection "
|
||||
"to interface port `" << port->name
|
||||
<< "' is not yet supported." << endl;
|
||||
des->errors += 1;
|
||||
flag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pins[idx]) {
|
||||
cerr << get_fileline() << ": error: Interface port `"
|
||||
<< port->name << "' of module " << rmod->mod_name()
|
||||
|
|
@ -1367,8 +1361,15 @@ bool PGModule::bind_interface_ports_(Design*des, const Module*rmod,
|
|||
|
||||
perm_string actual_name = actual_ident->path().name.front().name;
|
||||
NetScope*actual_scope = parent_scope->child(hname_t(actual_name));
|
||||
if (!actual_scope)
|
||||
actual_scope = parent_scope->find_interface_port_alias_scope(actual_name);
|
||||
const PModport*actual_modport = 0;
|
||||
if (!actual_scope) {
|
||||
const NetScope::interface_port_alias_t*actual_alias =
|
||||
parent_scope->find_interface_port_alias(actual_name);
|
||||
if (actual_alias) {
|
||||
actual_scope = actual_alias->actual_scope;
|
||||
actual_modport = actual_alias->modport;
|
||||
}
|
||||
}
|
||||
|
||||
if (!actual_scope || !actual_scope->is_interface()) {
|
||||
cerr << pins[idx]->get_fileline() << ": error: Actual for "
|
||||
|
|
@ -1391,14 +1392,27 @@ bool PGModule::bind_interface_ports_(Design*des, const Module*rmod,
|
|||
|
||||
map<perm_string,Module*>::const_iterator mod =
|
||||
pform_modules.find(port->interface_type);
|
||||
const PModport*modport = 0;
|
||||
if (mod != pform_modules.end()) {
|
||||
const PModport*formal_modport = 0;
|
||||
if (port->modport_name.str() && mod != pform_modules.end()) {
|
||||
map<perm_string,PModport*>::const_iterator mp =
|
||||
mod->second->modports.find(port->modport_name);
|
||||
if (mp != mod->second->modports.end())
|
||||
modport = mp->second;
|
||||
formal_modport = mp->second;
|
||||
}
|
||||
|
||||
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_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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_positional_unconnected_fail.v:7: error: Interface port `bus' of module bus_user is not connected.
|
||||
Elaboration failed
|
||||
|
|
@ -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,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,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,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
|
||||
|
|
@ -276,6 +276,15 @@ sv_interface_port_non_interface_actual_fail vvp_tests/sv_interface_port_non_inte
|
|||
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_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_mixed_assign1 vvp_tests/sv_mixed_assign1.json
|
||||
sv_mixed_assign2 vvp_tests/sv_mixed_assign2.json
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_forwarding.v",
|
||||
"iverilog-args" : [ "-g2012" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_forwarding_restrict_fail.v",
|
||||
"gold" : "sv_interface_port_forwarding_restrict_fail",
|
||||
"iverilog-args" : [ "-g2012" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_plain_ansi_regression.v",
|
||||
"iverilog-args" : [ "-g2012" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_positional.v",
|
||||
"iverilog-args" : [ "-g2012" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type" : "CE",
|
||||
"source" : "sv_interface_port_positional_unconnected_fail.v",
|
||||
"gold" : "sv_interface_port_positional_unconnected_fail",
|
||||
"iverilog-args" : [ "-g2012" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_typedef_ansi_regression.v",
|
||||
"iverilog-args" : [ "-g2012" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_unmodported_basic.v",
|
||||
"iverilog-args" : [ "-g2012" ]
|
||||
}
|
||||
|
|
@ -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" : [ "-g2012" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "sv_interface_port_wildcard.v",
|
||||
"iverilog-args" : [ "-g2012" ]
|
||||
}
|
||||
13
parse.y
13
parse.y
|
|
@ -468,11 +468,13 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
|
|||
pform_requires_sv(loc, "Interface port declaration");
|
||||
|
||||
Module::port_t *port = pform_module_interface_port_reference(
|
||||
loc, lex_strings.make(type), lex_strings.make(modport),
|
||||
loc, lex_strings.make(type),
|
||||
modport ? lex_strings.make(modport) : perm_string(),
|
||||
lex_strings.make(id));
|
||||
|
||||
delete[] type;
|
||||
delete[] modport;
|
||||
if (modport)
|
||||
delete[] modport;
|
||||
delete[] id;
|
||||
|
||||
pform_module_define_interface_port(loc, port, attributes);
|
||||
|
|
@ -4658,6 +4660,9 @@ port_declaration
|
|||
| attribute_list_opt IDENTIFIER '.' IDENTIFIER IDENTIFIER
|
||||
{ $$ = module_declare_interface_port(@5, $2, $4, $5, $1);
|
||||
}
|
||||
| attribute_list_opt IDENTIFIER IDENTIFIER
|
||||
{ $$ = module_declare_interface_port(@3, $2, 0, $3, $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,
|
||||
|
|
@ -5755,6 +5760,10 @@ port
|
|||
{ $$ = module_declare_interface_port(@4, $1, $3, $4, 0);
|
||||
}
|
||||
|
||||
| IDENTIFIER IDENTIFIER
|
||||
{ $$ = module_declare_interface_port(@2, $1, 0, $2, 0);
|
||||
}
|
||||
|
||||
/* This syntax attaches an external name to the port reference so
|
||||
that the caller can bind by name to non-trivial port
|
||||
references. The port_t object gets its PWire from the
|
||||
|
|
|
|||
Loading…
Reference in New Issue