Merge pull request #973 from mole99/sdf-interconnect

Implement SDF INTERCONNECT
This commit is contained in:
Cary R 2023-09-02 09:08:22 -07:00 committed by GitHub
commit 8e0fa024d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1477 additions and 114 deletions

View File

@ -177,6 +177,11 @@ extern bool gn_icarus_misc_flag;
is false, then skip elaboration of specify behavior. */
extern bool gn_specify_blocks_flag;
/* If this flag is true, then add input/output buffers to modules so that
VVP can insert intermodpaths inbetween. If this flag
is false, then no input/output buffers are inserted if not needed. */
extern bool gn_interconnect_flag;
/* If this flag is true, then elaborate supported assertion statements. If
this flag is false, then stub out supported assertion statements. */
extern bool gn_supported_assertions_flag;

View File

@ -95,6 +95,14 @@ simulation, and in fact can hurt performance of the
simulation. However, disabling specify blocks reduces accuracy of
full-timing simulations.
.TP 8
.B -ginterconnect\fI|\fP-gno-interconnect
Enable or disable (default) SDF interconnect support. When enabled,
input and output buffers will be added to modules, so that VVP can
add interconnection delays. This option is commonly not needed for RTL
simulation, and in fact can hurt performance of the
simulation. However, disabling interconnection delays reduces accuracy of
full-timing simulations.
.TP 8
.B -gstd-include\fI|\fP-gno-std-include
Enable (default) or disable the search of a standard installation
include directory after all other explicit include directories. This

View File

@ -125,6 +125,7 @@ char depmode = 'a';
const char*generation = "2005";
const char*gen_specify = "no-specify";
const char*gen_interconnect = "no-interconnect";
const char*gen_assertions = "assertions";
const char*gen_xtypes = "xtypes";
const char*gen_icarus = "icarus-misc";
@ -757,6 +758,12 @@ static int process_generation(const char*name)
else if (strcmp(name,"no-specify") == 0)
gen_specify = "no-specify";
else if (strcmp(name,"interconnect") == 0)
gen_interconnect = "interconnect";
else if (strcmp(name,"no-interconnect") == 0)
gen_interconnect = "no-interconnect";
else if (strcmp(name,"assertions") == 0)
gen_assertions = "assertions";
@ -821,6 +828,7 @@ static int process_generation(const char*name)
"Other generation flags:\n"
" assertions | supported-assertions | no-assertions\n"
" specify | no-specify\n"
" interconnect | no-interconnect\n"
" verilog-ams | no-verilog-ams\n"
" std-include | no-std-include\n"
" relative-include | no-relative-include\n"
@ -1368,6 +1376,7 @@ int main(int argc, char **argv)
if (mtm != 0) fprintf(iconfig_file, "-T:%s\n", mtm);
fprintf(iconfig_file, "generation:%s\n", generation);
fprintf(iconfig_file, "generation:%s\n", gen_specify);
fprintf(iconfig_file, "generation:%s\n", gen_interconnect);
fprintf(iconfig_file, "generation:%s\n", gen_assertions);
fprintf(iconfig_file, "generation:%s\n", gen_xtypes);
fprintf(iconfig_file, "generation:%s\n", gen_io_range_error);

View File

@ -1159,13 +1159,13 @@ static void convert_net(Design*des, const LineInfo *line,
}
static void isolate_and_connect(Design*des, NetScope*scope, const PGModule*mod,
NetNet*port, NetNet*sig, NetNet::PortType ptype)
NetNet*port, NetNet*sig, NetNet::PortType ptype, int idx = -1)
{
switch (ptype) {
case NetNet::POUTPUT:
{
NetBUFZ*tmp = new NetBUFZ(scope, scope->local_symbol(),
sig->vector_width(), true);
sig->vector_width(), true, idx);
tmp->set_line(*mod);
des->add_node(tmp);
connect(tmp->pin(1), port->pin(0));
@ -1598,17 +1598,21 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
}
}
if (need_bufz_for_input_port(prts)) {
NetBUFZ*tmp = new NetBUFZ(scope, scope->local_symbol(),
sig->vector_width(), true);
// Add module input buffers if needed
if (need_bufz_for_input_port(prts) || gn_interconnect_flag == true) {
// FIXME improve this for multiple module instances
NetScope* inner_scope = scope->instance_arrays[get_name()][0];
NetBUFZ*tmp = new NetBUFZ(inner_scope, inner_scope->local_symbol(),
sig->vector_width(), true, gn_interconnect_flag ? idx : -1);
tmp->set_line(*this);
des->add_node(tmp);
connect(tmp->pin(1), sig->pin(0));
netvector_t*tmp2_vec = new netvector_t(sig->data_type(),
sig->vector_width()-1,0);
NetNet*tmp2 = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, tmp2_vec);
sig->vector_width()-1,0);
NetNet*tmp2 = new NetNet(inner_scope, inner_scope->local_symbol(),
NetNet::WIRE, tmp2_vec);
tmp2->local_flag(true);
tmp2->set_line(*this);
connect(tmp->pin(0), tmp2->pin(0));
@ -1933,8 +1937,11 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
// that are a delay path destination, to avoid
// the delay being applied to other drivers of
// the external signal.
if (prts[0]->delay_paths() > 0) {
isolate_and_connect(des, scope, this, prts[0], sig, ptype);
if (prts[0]->delay_paths() > 0 || (gn_interconnect_flag == true && ptype == NetNet::POUTPUT)) {
// FIXME improve this for multiple module instances
NetScope* inner_scope = scope->instance_arrays[get_name()][0];
isolate_and_connect(des, inner_scope, this, prts[0], sig, ptype, gn_interconnect_flag ? idx : -1);
} else {
connect(prts[0]->pin(0), sig->pin(0));
}
@ -3496,14 +3503,14 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const
parm.parm);
}
// Special case: Specify blocks are turned off, and this is an
// $sdf_annotate system task. There will be nothing for $sdf
// to annotate, and the user is intending to turn the behavior
// Special case: Specify blocks and interconnects are turned off,
// and this is an $sdf_annotate system task. There will be nothing for
// $sdf to annotate, and the user is intending to turn the behavior
// off anyhow, so replace the system task invocation with a no-op.
if (gn_specify_blocks_flag == false && name == "$sdf_annotate") {
if (gn_specify_blocks_flag == false && gn_interconnect_flag == false && name == "$sdf_annotate") {
cerr << get_fileline() << ": warning: Omitting $sdf_annotate() "
<< "since specify blocks are being omitted." << endl;
<< "since specify blocks and interconnects are being omitted." << endl;
NetBlock*noop = new NetBlock(NetBlock::SEQU, scope);
noop->set_line(*this);
return noop;

View File

@ -109,6 +109,7 @@ ivl_logic_scope
ivl_logic_type
ivl_logic_udp
ivl_logic_width
ivl_logic_port_buffer
ivl_lpm_array
ivl_lpm_aset_value
@ -229,6 +230,7 @@ ivl_scope_mod_module_ports
ivl_scope_mod_module_port_name
ivl_scope_mod_module_port_type
ivl_scope_mod_module_port_width
ivl_scope_mod_module_port_buffer
ivl_scope_mod_port
ivl_scope_name
ivl_scope_param

View File

@ -1112,6 +1112,7 @@ extern ivl_drive_t ivl_logic_drive0(ivl_net_logic_t net);
extern ivl_drive_t ivl_logic_drive1(ivl_net_logic_t net);
extern unsigned ivl_logic_width(ivl_net_logic_t net);
extern unsigned ivl_logic_is_cassign(ivl_net_logic_t net);
extern unsigned ivl_logic_port_buffer(ivl_net_logic_t net);
/* DEPRECATED */
extern const char* ivl_logic_attr(ivl_net_logic_t net, const char*key);
@ -1897,6 +1898,7 @@ extern unsigned ivl_scope_mod_module_ports(ivl_scope_t net);
extern const char *ivl_scope_mod_module_port_name(ivl_scope_t net, unsigned idx );
extern ivl_signal_port_t ivl_scope_mod_module_port_type(ivl_scope_t net, unsigned idx );
extern unsigned ivl_scope_mod_module_port_width(ivl_scope_t net, unsigned idx );
extern ivl_net_logic_t ivl_scope_mod_module_port_buffer(ivl_scope_t net, unsigned idx );
extern unsigned ivl_scope_ports(ivl_scope_t net);
extern ivl_signal_t ivl_scope_port(ivl_scope_t net, unsigned idx);
@ -2432,3 +2434,4 @@ _END_DECL
#undef ENUM_UNSIGNED_INT
#endif /* IVL_ivl_target_H */

View File

@ -1,4 +1,3 @@
SDF WARNING: ivltests/br_gh889.sdf:18: loaded from vlog95.v:17: Wildcard cell instance specification (*) currently not supported.
0 A=x, B=x, Q=x
10 A=1, B=1, Q=x
11 A=1, B=1, Q=0

View File

@ -1,4 +1,3 @@
SDF WARNING: ivltests/br_gh889.sdf:18: loaded from ./ivltests/br_gh889.v:21: Wildcard cell instance specification (*) currently not supported.
0 A=x, B=x, Q=x
10 A=1, B=1, Q=x
11 A=1, B=1, Q=0

View File

@ -1,3 +1,4 @@
SDF INFO: Loading ivltests/sdf_header.sdf from ivltests/sdf_header.v:6
SDF INFO: ivltests/sdf_header.sdf:2: Version: 3.0
SDF INFO: ivltests/sdf_header.sdf:3: Design: test
SDF INFO: ivltests/sdf_header.sdf:4: Date: Wed Mar 8 12:34:56 2023

View File

@ -0,0 +1,6 @@
time=0 a=x b=x
time=5000 a=0 b=x
time=5680 a=0 b=0
time=15000 a=1 b=0
time=15680 a=1 b=1
ivltests/sdf_interconnect1.v:59: $finish called at 25000 (1ps)

View File

@ -0,0 +1,6 @@
time=0 a=x b=x
time=5000 a=0 b=x
time=5110 a=0 b=0
time=15000 a=1 b=0
time=15330 a=1 b=1
ivltests/sdf_interconnect2.v:61: $finish called at 25000 (1ps)

View File

@ -0,0 +1,20 @@
time=0 a=x b=x c=x d=x
time=10000 a=0 b=0 c=0 d=x
time=10560 a=0 b=0 c=0 d=0
time=20000 a=1 b=0 c=0 d=0
time=20560 a=1 b=0 c=0 d=1
time=30000 a=0 b=1 c=0 d=1
time=30450 a=0 b=1 c=0 d=0
time=40000 a=1 b=1 c=0 d=0
time=40560 a=1 b=1 c=0 d=1
time=50000 a=0 b=0 c=1 d=1
time=50340 a=0 b=0 c=1 d=0
time=50450 a=0 b=0 c=1 d=1
time=50560 a=0 b=0 c=1 d=0
time=60000 a=1 b=0 c=1 d=0
time=60560 a=1 b=0 c=1 d=1
time=70000 a=0 b=1 c=1 d=1
time=70450 a=0 b=1 c=1 d=0
time=80000 a=1 b=1 c=1 d=0
time=80560 a=1 b=1 c=1 d=1
ivltests/sdf_interconnect3.v:132: $finish called at 90000 (1ps)

View File

@ -0,0 +1,56 @@
(DELAYFILE
(SDFVERSION "3.0")
(DESIGN "test")
(DATE "Wed Mar 8 12:34:56 2023")
(VENDOR "No Vendor")
(PROGRAM "Human")
(VERSION "1.0.0")
(DIVIDER .)
(VOLTAGE 5.5:5.0:4.5)
(PROCESS "best=0.65:nom=1.0:worst=1.8")
(TEMPERATURE -25.0:25.0:85.0)
(TIMESCALE 1 ns)
(CELL
(CELLTYPE "my_design")
(INSTANCE)
(DELAY
(ABSOLUTE
(INTERCONNECT a buffer0.in (0.010:0.020:0.030) (0.010:0.020:0.030))
(INTERCONNECT buffer0.out buffer1.in (0.010:0.020:0.030) (0.010:0.020:0.030))
(INTERCONNECT buffer1.out buffer2.in (0.010:0.020:0.030) (0.010:0.020:0.030))
(INTERCONNECT buffer2.out b (0.010:0.020:0.030) (0.010:0.020:0.030))
)
)
)
(CELL
(CELLTYPE "buffer")
(INSTANCE buffer0)
(DELAY
(ABSOLUTE
(IOPATH in out (0.1:0.2:0.3) (0.1:0.2:0.3))
)
)
)
(CELL
(CELLTYPE "buffer")
(INSTANCE buffer1)
(DELAY
(ABSOLUTE
(IOPATH in out (0.1:0.2:0.3) (0.1:0.2:0.3))
)
)
)
(CELL
(CELLTYPE "buffer")
(INSTANCE buffer2)
(DELAY
(ABSOLUTE
(IOPATH in out (0.1:0.2:0.3) (0.1:0.2:0.3))
)
)
)
)

View File

@ -0,0 +1,68 @@
`timescale 1ns/1ps
/*
This design tests the interconnection delay
for three buffers in series
*/
module buffer (
input in,
output out
);
specify
(in => out) = (0.0:0.0:0.0);
endspecify
assign out = in;
endmodule
module my_design (
input a,
output b
);
wire w1, w2;
buffer buffer0 (
.in (a),
.out (w1)
);
buffer buffer1 (
.in (w1),
.out (w2)
);
buffer buffer2 (
.in (w2),
.out (b)
);
endmodule
module top;
initial begin
$sdf_annotate("ivltests/sdf_interconnect1.sdf", my_design_inst);
$monitor("time=%0t a=%h b=%h", $realtime, a, b);
end
reg a;
wire b;
initial begin
#5;
a <= 1'b0;
#10;
a <= 1'b1;
#10;
$finish;
end
my_design my_design_inst (
.a (a),
.b (b)
);
endmodule

View File

@ -0,0 +1,55 @@
(DELAYFILE
(SDFVERSION "3.0")
(DESIGN "test")
(DATE "Wed Mar 8 12:34:56 2023")
(VENDOR "No Vendor")
(PROGRAM "Human")
(VERSION "1.0.0")
(DIVIDER .)
(VOLTAGE 5.5:5.0:4.5)
(PROCESS "best=0.65:nom=1.0:worst=1.8")
(TEMPERATURE -25.0:25.0:85.0)
(TIMESCALE 1 ns)
(CELL
(CELLTYPE "my_design")
(INSTANCE)
(DELAY
(ABSOLUTE
(INTERCONNECT a buffer0.in (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT a buffer1.in (0.000:0.020:0.000) (0.000:0.020:0.000))
(INTERCONNECT a buffer2.in (0.000:0.030:0.000) (0.000:0.030:0.000))
)
)
)
(CELL
(CELLTYPE "buffer")
(INSTANCE buffer0)
(DELAY
(ABSOLUTE
(IOPATH in out (0.000:0.100:0.000) (0.000:0.100:0.000))
)
)
)
(CELL
(CELLTYPE "buffer")
(INSTANCE buffer1)
(DELAY
(ABSOLUTE
(IOPATH in out (0.000:0.200:0.000) (0.000:0.200:0.000))
)
)
)
(CELL
(CELLTYPE "buffer")
(INSTANCE buffer2)
(DELAY
(ABSOLUTE
(IOPATH in out (0.000:0.300:0.000) (0.000:0.300:0.000))
)
)
)
)

View File

@ -0,0 +1,70 @@
`timescale 1ns/1ps
/*
This design tests the interconnection delay
for three buffers in parallel
*/
module buffer (
input in,
output out
);
specify
(in => out) = (0.0:0.0:0.0);
endspecify
assign out = in;
endmodule
module my_design (
input a,
output b
);
wire w1, w2, w3;
buffer buffer0 (
.in (a),
.out (w1)
);
buffer buffer1 (
.in (a),
.out (w2)
);
buffer buffer2 (
.in (a),
.out (w3)
);
assign b = w1 & w2 & w3;
endmodule
module top;
initial begin
$sdf_annotate("ivltests/sdf_interconnect2.sdf", my_design_inst);
$monitor("time=%0t a=%h b=%h", $realtime, a, b);
end
reg a;
wire b;
initial begin
#5;
a <= 1'b0;
#10;
a <= 1'b1;
#10;
$finish;
end
my_design my_design_inst (
.a (a),
.b (b)
);
endmodule

View File

@ -0,0 +1,119 @@
(DELAYFILE
(SDFVERSION "3.0")
(DESIGN "test")
(DATE "Wed Mar 8 12:34:56 2023")
(VENDOR "No Vendor")
(PROGRAM "Human")
(VERSION "1.0.0")
(DIVIDER .)
(VOLTAGE 5.5:5.0:4.5)
(PROCESS "best=0.65:nom=1.0:worst=1.8")
(TEMPERATURE -25.0:25.0:85.0)
(TIMESCALE 1 ns)
(CELL
(CELLTYPE "my_design")
(INSTANCE)
(DELAY
(ABSOLUTE
(INTERCONNECT a buffer0.in (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT b my_xor0.a (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT c my_xor0.b (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT buffer0.out my_xor1.a (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT b my_xor1.b (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT my_xor0.out buffer1.in (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT my_xor1.out my_xor2.a (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT buffer1.out my_xor2.b (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT c buffer2.in (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT my_xor2.out my_xor3.a (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT buffer2.out my_xor3.b (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT my_xor3.out buffer3.in (0.000:0.010:0.000) (0.000:0.010:0.000))
(INTERCONNECT buffer3.out d (0.000:0.010:0.000) (0.000:0.010:0.000))
)
)
)
(CELL
(CELLTYPE "buffer")
(INSTANCE buffer0)
(DELAY
(ABSOLUTE
(IOPATH in out (0.0:0.1:0.0) (0.0:0.1:0.0))
)
)
)
(CELL
(CELLTYPE "buffer")
(INSTANCE buffer1)
(DELAY
(ABSOLUTE
(IOPATH in out (0.0:0.1:0.0) (0.0:0.1:0.0))
)
)
)
(CELL
(CELLTYPE "buffer")
(INSTANCE buffer2)
(DELAY
(ABSOLUTE
(IOPATH in out (0.0:0.1:0.0) (0.0:0.1:0.0))
)
)
)
(CELL
(CELLTYPE "buffer")
(INSTANCE buffer3)
(DELAY
(ABSOLUTE
(IOPATH in out (0.0:0.1:0.0) (0.0:0.1:0.0))
)
)
)
(CELL
(CELLTYPE "my_xor")
(INSTANCE my_xor0)
(DELAY
(ABSOLUTE
(IOPATH a out (0.0:0.1:0.0) (0.0:0.1:0.0))
(IOPATH b out (0.0:0.1:0.0) (0.0:0.1:0.0))
)
)
)
(CELL
(CELLTYPE "my_xor")
(INSTANCE my_xor1)
(DELAY
(ABSOLUTE
(IOPATH a out (0.0:0.1:0.0) (0.0:0.1:0.0))
(IOPATH b out (0.0:0.1:0.0) (0.0:0.1:0.0))
)
)
)
(CELL
(CELLTYPE "my_xor")
(INSTANCE my_xor2)
(DELAY
(ABSOLUTE
(IOPATH a out (0.0:0.1:0.0) (0.0:0.1:0.0))
(IOPATH b out (0.0:0.1:0.0) (0.0:0.1:0.0))
)
)
)
(CELL
(CELLTYPE "my_xor")
(INSTANCE my_xor3)
(DELAY
(ABSOLUTE
(IOPATH a out (0.0:0.1:0.0) (0.0:0.1:0.0))
(IOPATH b out (0.0:0.1:0.0) (0.0:0.1:0.0))
)
)
)
)

View File

@ -0,0 +1,143 @@
`timescale 1ns/1ps
/*
This design tests the interconnection delays
for a circuit of various buffers and xors
*/
module my_xor (
input a,
input b,
output out
);
specify
(a => out) = (0.0:0.0:0.0);
(b => out) = (0.0:0.0:0.0);
endspecify
assign out = a ^ b;
endmodule
module buffer (
input in,
output out
);
specify
(in => out) = (0.0:0.0:0.0);
endspecify
assign out = in;
endmodule
module my_design (
input a,
input b,
input c,
output d
);
wire w1, w2, w3, w4, w5, w6, w7;
buffer buffer0 (
.in (a),
.out (w1)
);
my_xor my_xor0 (
.a (b),
.b (c),
.out (w2)
);
my_xor my_xor1 (
.a (w1),
.b (b),
.out (w3)
);
buffer buffer1 (
.in (w2),
.out (w4)
);
my_xor my_xor2 (
.a (w3),
.b (w4),
.out (w5)
);
buffer buffer2 (
.in (c),
.out (w6)
);
my_xor my_xor3 (
.a (w5),
.b (w6),
.out (w7)
);
buffer buffer3 (
.in (w7),
.out (d)
);
endmodule
module top;
initial begin
$sdf_annotate("ivltests/sdf_interconnect3.sdf", my_design_inst);
$monitor("time=%0t a=%h b=%h c=%h d=%h", $realtime, a, b, c, d);
end
reg a, b, c;
wire d;
initial begin
#10;
a <= 1'b0;
b <= 1'b0;
c <= 1'b0;
#10;
a <= 1'b1;
b <= 1'b0;
c <= 1'b0;
#10;
a <= 1'b0;
b <= 1'b1;
c <= 1'b0;
#10;
a <= 1'b1;
b <= 1'b1;
c <= 1'b0;
#10;
a <= 1'b0;
b <= 1'b0;
c <= 1'b1;
#10;
a <= 1'b1;
b <= 1'b0;
c <= 1'b1;
#10;
a <= 1'b0;
b <= 1'b1;
c <= 1'b1;
#10;
a <= 1'b1;
b <= 1'b1;
c <= 1'b1;
#10;
$finish;
end
my_design my_design_inst (
.a (a),
.b (b),
.c (c),
.d (d)
);
endmodule

View File

@ -94,3 +94,6 @@ task_return_fail1 vvp_tests/task_return_fail1.json
task_return_fail2 vvp_tests/task_return_fail2.json
timing_check_syntax vvp_tests/timing_check_syntax.json
timing_check_delayed_signals vvp_tests/timing_check_delayed_signals.json
sdf_interconnect1 vvp_tests/sdf_interconnect1.json
sdf_interconnect2 vvp_tests/sdf_interconnect2.json
sdf_interconnect3 vvp_tests/sdf_interconnect3.json

View File

@ -0,0 +1,6 @@
{
"type" : "normal",
"source" : "sdf_interconnect1.v",
"iverilog-args" : [ "-Ttyp", "-ginterconnect", "-gspecify" ],
"gold" : "sdf_interconnect1"
}

View File

@ -0,0 +1,6 @@
{
"type" : "normal",
"source" : "sdf_interconnect2.v",
"iverilog-args" : [ "-Ttyp", "-ginterconnect", "-gspecify" ],
"gold" : "sdf_interconnect2"
}

View File

@ -0,0 +1,6 @@
{
"type" : "normal",
"source" : "sdf_interconnect3.v",
"iverilog-args" : [ "-Ttyp", "-ginterconnect", "-gspecify" ],
"gold" : "sdf_interconnect3"
}

12
main.cc
View File

@ -106,6 +106,7 @@ generation_t generation_flag = GN_DEFAULT;
bool gn_icarus_misc_flag = true;
bool gn_cadence_types_flag = true;
bool gn_specify_blocks_flag = true;
bool gn_interconnect_flag = true;
bool gn_supported_assertions_flag = true;
bool gn_unsupported_assertions_flag = true;
bool gn_io_range_error_flag = true;
@ -334,6 +335,12 @@ static void process_generation_flag(const char*gen)
} else if (strcmp(gen,"no-specify") == 0) {
gn_specify_blocks_flag = false;
} else if (strcmp(gen,"interconnect") == 0) {
gn_interconnect_flag = true;
} else if (strcmp(gen,"no-interconnect") == 0) {
gn_interconnect_flag = false;
} else if (strcmp(gen,"assertions") == 0) {
gn_supported_assertions_flag = true;
gn_unsupported_assertions_flag = true;
@ -1095,6 +1102,11 @@ int main(int argc, char*argv[])
else
cout << ",no-specify";
if (gn_interconnect_flag)
cout << ",interconnect";
else
cout << ",no-interconnect";
if (gn_cadence_types_flag)
cout << ",xtypes";
else

View File

@ -572,8 +572,15 @@ void NetScope::add_module_port_info( unsigned idx, perm_string name, PortType::E
info.name = name;
info.type = ptype;
info.width = width;
info.buffer = nullptr;
}
PortInfo* NetScope::get_module_port_info( unsigned idx )
{
ivl_assert(*this, type_ == MODULE);
ivl_assert(*this, ports_.size() > idx);
return &ports_[idx];
}
unsigned NetScope::module_port_nets() const
{
@ -872,3 +879,4 @@ void NetScope::add_tie_lo(Design*des)
connect(sig->pin(0), tie_lo_->pin(0));
}
}

View File

@ -1966,8 +1966,8 @@ unsigned NetSignExtend::width() const
return width_;
}
NetBUFZ::NetBUFZ(NetScope*s, perm_string n, unsigned w, bool trans)
: NetNode(s, n, 2), width_(w), transparent_(trans)
NetBUFZ::NetBUFZ(NetScope*s, perm_string n, unsigned w, bool trans, int port_info_index)
: NetNode(s, n, 2), width_(w), transparent_(trans), port_info_index_(port_info_index)
{
pin(0).set_dir(Link::OUTPUT);
pin(1).set_dir(Link::INPUT);

View File

@ -660,6 +660,7 @@ struct PortInfo
PortType::Enum type;
unsigned long width;
perm_string name;
ivl_net_logic_t buffer;
};
@ -1127,6 +1128,8 @@ class NetScope : public Definitions, public Attrib {
PortType::Enum type,
unsigned long width );
PortInfo* get_module_port_info(unsigned idx);
const std::vector<PortInfo> &module_port_info() const;
/* Scopes have their own time units and time precision. The
@ -2359,11 +2362,12 @@ class NetSubstitute : public NetNode {
class NetBUFZ : public NetNode {
public:
explicit NetBUFZ(NetScope*s, perm_string n, unsigned wid, bool transp);
explicit NetBUFZ(NetScope*s, perm_string n, unsigned wid, bool transp, int port_info_index = -1);
~NetBUFZ();
unsigned width() const;
bool transparent() const { return transparent_; }
int port_info_index() const { return port_info_index_; }
virtual void dump_node(std::ostream&, unsigned ind) const;
virtual bool emit_node(struct target_t*) const;
@ -2371,6 +2375,7 @@ class NetBUFZ : public NetNode {
private:
unsigned width_;
bool transparent_;
int port_info_index_;
};
/*

View File

@ -992,6 +992,12 @@ extern "C" unsigned ivl_logic_width(ivl_net_logic_t net)
return net->width_;
}
extern "C" unsigned ivl_logic_port_buffer(ivl_net_logic_t net)
{
assert(net);
return net->is_port_buffer;
}
extern "C" int ivl_udp_sequ(ivl_udp_t net)
{
assert(net);
@ -2315,6 +2321,11 @@ extern "C" unsigned ivl_scope_mod_module_port_width(ivl_scope_t net, unsigned id
return net->module_ports_info[idx].width;
}
extern "C" ivl_net_logic_t ivl_scope_mod_module_port_buffer(ivl_scope_t net, unsigned idx )
{
assert(net);
return (ivl_net_logic_t)net->module_ports_info[idx].buffer;
}
extern "C" unsigned ivl_scope_ports(ivl_scope_t net)
{

View File

@ -856,6 +856,7 @@ bool dll_target::bufz(const NetBUFZ*net)
obj->type_ = net->transparent()? IVL_LO_BUFT : IVL_LO_BUFZ;
obj->width_= net->width();
obj->is_cassign = 0;
obj->is_port_buffer = net->port_info_index() >= 0;
obj->npins_= 2;
obj->pins_ = new ivl_nexus_t[2];
FILE_NAME(obj, net);
@ -892,6 +893,14 @@ bool dll_target::bufz(const NetBUFZ*net)
scope_add_logic(scop, obj);
// Add bufz to the corresponding port_info entry,
// if it is an input / output buffer
// This is needed for the SDF interconnect feature
// to access the buffers directly from the port_info
if (obj->is_port_buffer) {
scop->module_ports_info[net->port_info_index()].buffer = obj;
}
return true;
}
@ -959,6 +968,7 @@ void dll_target::logic(const NetLogic*net)
struct ivl_net_logic_s *obj = new struct ivl_net_logic_s;
obj->width_ = net->width();
obj->is_port_buffer = 0;
FILE_NAME(obj, net);
@ -1427,6 +1437,7 @@ void dll_target::udp(const NetUDP*net)
struct ivl_net_logic_s *obj = new struct ivl_net_logic_s;
obj->type_ = IVL_LO_UDP;
obj->is_port_buffer = 0;
FILE_NAME(obj, net);
/* The NetUDP class hasn't learned about width yet, so we

View File

@ -530,6 +530,7 @@ struct ivl_net_logic_s {
ivl_logic_t type_;
unsigned width_;
unsigned is_cassign;
unsigned is_port_buffer;
ivl_udp_t udp;
perm_string name_;

View File

@ -308,6 +308,9 @@ int signal_is_return_value(ivl_signal_t sig)
*/
int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr)
{
// If bufz is a module input/output buffer do not elide
if (ivl_logic_port_buffer(net)) return 0;
ivl_nexus_t in_n, out_n;
unsigned idx;
@ -2400,17 +2403,20 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent)
if( ivl_scope_type(net) == IVL_SCT_MODULE ) {
// Port data for VPI: needed for vpiPorts property of vpiModule
for( idx = 0; idx < ivl_scope_mod_module_ports(net); ++idx ) {
const char *name = ivl_scope_mod_module_port_name(net,idx);
ivl_signal_port_t ptype = ivl_scope_mod_module_port_type(net,idx);
unsigned width = ivl_scope_mod_module_port_width(net,idx);
if( name == 0 )
name = "";
fprintf( vvp_out, " .port_info %u %s %u \"%s\";\n",
idx, vvp_port_info_type_str(ptype), width,
vvp_mangle_name(name) );
}
// Port data for VPI: needed for vpiPorts property of vpiModule
for( idx = 0; idx < ivl_scope_mod_module_ports(net); ++idx ) {
const char *name = ivl_scope_mod_module_port_name(net,idx);
ivl_signal_port_t ptype = ivl_scope_mod_module_port_type(net,idx);
unsigned width = ivl_scope_mod_module_port_width(net,idx);
ivl_net_logic_t buffer = ivl_scope_mod_module_port_buffer(net,idx);
if( name == 0 )
name = "";
fprintf( vvp_out, " .port_info %u %s %u \"%s\"",
idx, vvp_port_info_type_str(ptype), width,
vvp_mangle_name(name) );
if (buffer) fprintf( vvp_out, " L_%p;\n", buffer);
else fprintf( vvp_out, ";\n");
}
}
for (idx = 0 ; idx < ivl_scope_params(net) ; idx += 1) {
@ -2529,3 +2535,4 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent)
ivl_scope_children(net, (ivl_scope_f*) draw_scope, net);
return 0;
}

View File

@ -60,6 +60,11 @@ vpiHandle vpi_handle_by_index(vpiHandle ref, PLI_INT32 idx)
assert(vpip_routines);
return vpip_routines->handle_by_index(ref, idx);
}
vpiHandle vpi_handle_multi(PLI_INT32 type, vpiHandle ref1, vpiHandle ref2)
{
assert(vpip_routines);
return vpip_routines->handle_multi(type, ref1, ref2);
}
// for traversing relationships

View File

@ -42,6 +42,7 @@ char sdf_use_hchar = '.';
struct sdf_delay_s delay;
struct port_with_edge_s port_with_edge;
struct sdf_delval_list_s delval_list;
struct interconnect_port_s interconnect_port;
};
%token K_ABSOLUTE K_CELL K_CELLTYPE K_COND K_CONDELSE K_DATE K_DELAYFILE
@ -63,7 +64,9 @@ char sdf_use_hchar = '.';
%type <string_val> celltype
%type <string_val> cell_instance
%type <string_val> hierarchical_identifier
%type <string_val> port port_instance port_interconnect
%type <string_val> port port_instance
%type <interconnect_port> port_interconnect
%type <real_val> signed_real_number
%type <delay> delval rvalue_opt rvalue rtriple signed_real_number_opt
@ -157,26 +160,31 @@ program_version
hierarchy_divider
: '(' K_DIVIDER '.' ')'
{ sdf_use_hchar = '.';
if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Divider: \"%c\"\n", sdf_parse_path, @1.first_line, sdf_use_hchar);
if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Divider: \"%c\"\n",
sdf_parse_path, @1.first_line, sdf_use_hchar);
}
| '(' K_DIVIDER '/' ')'
{ sdf_use_hchar = '/';
if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Divider: \"%c\"\n", sdf_parse_path, @1.first_line, sdf_use_hchar);
if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Divider: \"%c\"\n",
sdf_parse_path, @1.first_line, sdf_use_hchar);
}
| '(' K_DIVIDER HCHAR ')'
{ /* sdf_use_hchar no-change */
if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Divider: \"%c\"\n", sdf_parse_path, @1.first_line, sdf_use_hchar);
if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Divider: \"%c\"\n",
sdf_parse_path, @1.first_line, sdf_use_hchar);
}
;
voltage
: '(' K_VOLTAGE rtriple ')'
{ /* The value must be defined. */
if (! $3.defined) {
vpi_printf("SDF ERROR: %s:%d: Chosen value not defined.\n", sdf_parse_path, @1.first_line);
}
else if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Voltage: %f\n",
sdf_parse_path, @2.first_line, $3.value);
if (! $3.defined) {
vpi_printf("SDF ERROR: %s:%d: Chosen value not defined.\n",
sdf_parse_path, @1.first_line);
} else if (sdf_flag_inform) {
vpi_printf("SDF INFO: %s:%d: Voltage: %f\n",
sdf_parse_path, @2.first_line, $3.value);
}
}
| '(' K_VOLTAGE signed_real_number ')'
{ if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Voltage: %f\n",
@ -195,11 +203,10 @@ process
temperature
: '(' K_TEMPERATURE rtriple ')'
{ /* The value must be defined. */
if (! $3.defined) {
vpi_printf("SDF ERROR: %s:%d: Chosen value not defined.\n", sdf_parse_path, @1.first_line);
}
else if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Temperature: %f\n",
sdf_parse_path, @2.first_line, $3.value);
if (! $3.defined) vpi_printf("SDF ERROR: %s:%d: Chosen value not defined.\n",
sdf_parse_path, @1.first_line);
else if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Temperature: %f\n",
sdf_parse_path, @2.first_line, $3.value);
}
| '(' K_TEMPERATURE signed_real_number ')'
{ if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Temperature: %f\n",
@ -335,15 +342,22 @@ del_def
sdf_parse_path, @2.first_line); }
/* | '(' K_INTERCONNECT port_instance port_instance delval_list ')' */
| '(' K_INTERCONNECT port_interconnect port_interconnect delval_list ')'
{ if (sdf_flag_warning) vpi_printf("SDF WARNING: %s:%d: "
"INTERCONNECT not supported.\n",
sdf_parse_path, @2.first_line);
free($3);
free($4);
{
if (sdf_flag_inform) {
vpi_printf("SDF INFO: %s:%d: INTERCONNECT with "
"port1 = %s index = %d, port2 = %s index = %d\n",
sdf_parse_path, @2.first_line, $3.name, $3.index, $4.name, $4.index);
}
sdf_interconnect_delays($3, $4, &$5, @2.first_line);
free($3.name);
free($4.name);
}
| '(' K_INTERCONNECT error ')'
{ vpi_printf("SDF ERROR: %s:%d: Invalid/malformed INTERCONNECT\n",
sdf_parse_path, @2.first_line); }
sdf_parse_path, @2.first_line);
}
;
tchk_def_list
@ -396,13 +410,13 @@ cond_edge_identifier
timing_check_condition
: port_interconnect
{ free($1); }
{ free($1.name); }
| '~' port_interconnect
{ free($2); }
{ free($2.name); }
| '!' port_interconnect
{ free($2); }
{ free($2.name); }
| port_interconnect equality_operator scalar_constant
{ free($1); }
{ free($1.name); }
;
/* This is not complete! */
@ -455,12 +469,17 @@ port
/* | hierarchical_identifier '[' INTEGER ']' */
;
/* Since INTERCONNECT is ignored we can also ignore a vector bit. */
port_interconnect
: hierarchical_identifier
{ $$ = $1; }
{
struct interconnect_port_s tmp = {$1, false, 0};
$$ = tmp;
}
| hierarchical_identifier '[' INTEGER ']'
{ $$ = $1;}
{
struct interconnect_port_s tmp = {$1, true, $3};
$$ = tmp;
}
;
port_edge

View File

@ -24,11 +24,6 @@
* used to share declarations between the parse and the lexor.
*/
struct port_with_edge_s {
int vpi_edge;
char*string_val;
};
/* Path to source for error messages. */
extern const char*sdf_parse_path;

View File

@ -20,6 +20,7 @@
*/
# include <stdio.h>
# include <stdbool.h>
/*
* Invoke the parser to parse the opened SDF file. The fd is the SDF
@ -48,10 +49,28 @@ struct sdf_delval_list_s {
struct sdf_delay_s val[12];
};
struct port_with_edge_s {
int vpi_edge;
char*string_val;
};
struct interconnect_port_s {
char* name;
bool has_index;
int index; // invalid if has_index is false
};
extern void sdf_select_instance(const char*celltype, const char*inst,
const int sdf_lineno);
extern void sdf_iopath_delays(int vpi_edge, const char*src, const char*dst,
const struct sdf_delval_list_s*delval,
const int sdf_lineno);
extern void sdf_interconnect_delays(struct interconnect_port_s port1,
struct interconnect_port_s port2,
const struct sdf_delval_list_s*delval_list,
const int sdf_lineno);
#endif /* IVL_sdf_priv_h */

View File

@ -76,8 +76,11 @@ void sdf_select_instance(const char*celltype, const char*cellinst, const int sdf
/* Test for wildcard character */
if (cellinst == NULL) {
sdf_warn_file_line(sdf_lineno);
vpi_printf("Wildcard cell instance specification (*) currently not supported.\n");
if (sdf_flag_warning) {
vpi_printf("SDF WARNING: %s:%d: sorry: "
"Wildcard cell instance specification (*) currently not supported.\n",
sdf_fname, sdf_lineno);
}
sdf_cur_cell = 0;
return;
}
@ -96,9 +99,8 @@ void sdf_select_instance(const char*celltype, const char*cellinst, const int sdf
vpiHandle tmp_scope = find_scope(scope, buffer);
if (tmp_scope == 0) {
sdf_warn_file_line(sdf_lineno);
vpi_printf("Cannot find %s in scope %s.\n",
buffer, vpi_get_str(vpiFullName, scope));
vpi_printf("SDF ERROR: %s:%d: Cannot find %s in scope %s.\n",
sdf_fname, sdf_lineno, buffer, vpi_get_str(vpiFullName, scope));
break;
}
assert(tmp_scope);
@ -113,24 +115,21 @@ void sdf_select_instance(const char*celltype, const char*cellinst, const int sdf
else
sdf_cur_cell = find_scope(scope, src);
if (sdf_cur_cell == 0) {
sdf_warn_file_line(sdf_lineno);
vpi_printf("Unable to find %s in scope %s.\n",
src, vpi_get_str(vpiFullName, scope));
vpi_printf("SDF ERROR: %s:%d: Unable to find %s in scope %s.\n",
sdf_fname, sdf_lineno, src, vpi_get_str(vpiFullName, scope));
return;
}
/* The scope that matches should be a module. */
if (vpi_get(vpiType,sdf_cur_cell) != vpiModule) {
sdf_warn_file_line(sdf_lineno);
vpi_printf("Scope %s in %s is not a module.\n",
src, vpi_get_str(vpiFullName, scope));
vpi_printf("SDF ERROR: %s:%d: Scope %s in %s is not a module.\n",
sdf_fname, sdf_lineno, src, vpi_get_str(vpiFullName, scope));
}
/* The matching scope (a module) should have the expected type. */
if (strcmp(celltype,vpi_get_str(vpiDefName,sdf_cur_cell)) != 0) {
sdf_warn_file_line(sdf_lineno);
vpi_printf("Module %s in %s is not a %s; it is a ", src,
vpi_get_str(vpiFullName, scope), celltype);
vpi_printf("SDF ERROR: %s:%d: Module %s in %s is not a %s; it is a ",
sdf_fname, sdf_lineno, src, vpi_get_str(vpiFullName, scope), celltype);
vpi_printf("%s\n", vpi_get_str(vpiDefName, sdf_cur_cell));
}
@ -147,6 +146,98 @@ static const char*edge_str(int vpi_edge)
return "edge.. ";
}
vpiHandle get_port_handle(char* port_name, const int sdf_lineno)
{
vpiHandle scope = sdf_cur_cell;
// Get occurences of '.' in the name
int submodules = 0;
for (int i=0; port_name[i] != '\0'; i++) {
if (port_name[i] == '.') submodules++;
}
// Extract the first token
char* token = strtok(port_name, ".");;
// Change scope into submodule
while (submodules--) {
scope = vpi_handle_by_name(token, scope);
if (!scope) vpi_printf("SDF ERROR: %s:%d: Submodule %s in port path not found!\n", sdf_fname, sdf_lineno, token);
// Extract next token
token = strtok(NULL, ".");
}
// Iterate over ports
vpiHandle port_i = vpi_iterate(vpiPort, scope) ;
vpiHandle port;
vpiHandle port_handle = NULL;
while ((port=vpi_scan(port_i)) != NULL) {
char *port_name_ = vpi_get_str(vpiName, port) ;
if (strcmp(port_name_, token) == 0) {
if (port_handle != NULL) {
if (sdf_flag_warning) vpi_printf("SDF WARNING: %s:%d: Found multiple matching ports for %s !\n", sdf_fname, sdf_lineno, token);
}
port_handle = port;
}
}
return port_handle;
}
void sdf_interconnect_delays(struct interconnect_port_s port1, struct interconnect_port_s port2,
const struct sdf_delval_list_s*delval_list,
const int sdf_lineno)
{
// Get handles for both ports
// After calling get_port_handle, the name is invalid
vpiHandle port1_handle = get_port_handle(port1.name, sdf_lineno);
vpiHandle port2_handle = get_port_handle(port2.name, sdf_lineno);
if (port1_handle && port2_handle) {
// Get interModPath for the two ports
vpiHandle intermodpath = vpi_handle_multi(vpiInterModPath, port1_handle, port2_handle);
if (intermodpath) {
if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Created a vpiInterModPath\n", sdf_fname, sdf_lineno);
s_vpi_delay delays;
struct t_vpi_time delay_vals[12];
// Initialize delay structure
delays.da = delay_vals;
delays.no_of_delays = delval_list->count;
delays.time_type = vpiScaledRealTime;
delays.mtm_flag = 0;
delays.append_flag = 0;
delays.pulsere_flag = 0;
vpi_get_delays(intermodpath, &delays);
for (int idx = 0 ; idx < delval_list->count ; idx += 1) {
delay_vals[idx].type = vpiScaledRealTime;
if (delval_list->val[idx].defined) {
if (sdf_flag_inform) vpi_printf("SDF INFO: %s:%d: Putting delay: %f for index %d\n",
sdf_fname, sdf_lineno, delval_list->val[idx].value, idx);
delay_vals[idx].real = delval_list->val[idx].value;
}
}
// Put the new delays
vpi_put_delays(intermodpath, &delays);
} else {
vpi_printf("SDF ERROR: %s:%d: Could not find intermodpath!\n", sdf_fname, sdf_lineno);
}
} else {
vpi_printf("SDF ERROR: %s:%d: Could not find handles for both ports!\n", sdf_fname, sdf_lineno);
}
}
void sdf_iopath_delays(int vpi_edge, const char*src, const char*dst,
const struct sdf_delval_list_s*delval_list,
const int sdf_lineno)
@ -214,10 +305,10 @@ void sdf_iopath_delays(int vpi_edge, const char*src, const char*dst,
}
if (match_count == 0) {
sdf_warn_file_line(sdf_lineno);
vpi_printf("Unable to match ModPath %s%s -> %s in %s\n",
edge_str(vpi_edge), src, dst,
vpi_get_str(vpiFullName, sdf_cur_cell));
vpi_printf("SDF ERROR: %s:%d: Unable to match ModPath %s%s -> %s in %s\n",
sdf_fname, sdf_lineno,
edge_str(vpi_edge), src, dst,
vpi_get_str(vpiFullName, sdf_cur_cell));
}
}
@ -309,6 +400,13 @@ static PLI_INT32 sys_sdf_annotate_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
return 0;
}
if (sdf_flag_inform) {
vpi_printf("SDF INFO: Loading %s from %s:%d\n",
fname,
vpi_get_str(vpiFile, callh),
(int)vpi_get(vpiLineNo, callh));
}
sdf_fd = fopen(fname, "r");
if (sdf_fd == 0) {
vpi_printf("SDF WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),

View File

@ -46,6 +46,7 @@ void vpi_get_systf_info(vpiHandle, p_vpi_systf_data) { }
vpiHandle vpi_handle_by_name(const char*, vpiHandle) { return 0; }
vpiHandle vpi_handle_by_index(vpiHandle, PLI_INT32) { return 0; }
vpiHandle vpi_handle_multi(PLI_INT32, vpiHandle, vpiHandle) { return 0; }
// for traversing relationships
@ -195,6 +196,7 @@ vpip_routines_s vpi_routines = {
.get_systf_info = vpi_get_systf_info,
.handle_by_name = vpi_handle_by_name,
.handle_by_index = vpi_handle_by_index,
.handle_multi = vpi_handle_multi,
.handle = vpi_handle,
.iterate = vpi_iterate,
.scan = vpi_scan,

View File

@ -277,6 +277,7 @@ typedef struct t_vpi_delay {
#define vpiConstant 7
#define vpiFunction 20
#define vpiIntegerVar 25
#define vpiInterModPath 26
#define vpiIterator 27
#define vpiMemory 29
#define vpiMemoryWord 30
@ -534,6 +535,7 @@ extern vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle ref);
extern vpiHandle vpi_scan(vpiHandle iter);
extern vpiHandle vpi_handle_by_index(vpiHandle ref, PLI_INT32 idx);
extern vpiHandle vpi_handle_by_name(const char*name, vpiHandle scope);
extern vpiHandle vpi_handle_multi(PLI_INT32 type, vpiHandle ref1, vpiHandle ref2);
extern void vpi_get_time(vpiHandle obj, s_vpi_time*t);
extern PLI_INT32 vpi_get(int property, vpiHandle ref);
@ -697,6 +699,7 @@ typedef struct {
void (*get_systf_info)(vpiHandle, p_vpi_systf_data);
vpiHandle (*handle_by_name)(const char*, vpiHandle);
vpiHandle (*handle_by_index)(vpiHandle, PLI_INT32);
vpiHandle (*handle_multi)(PLI_INT32, vpiHandle, vpiHandle);
vpiHandle (*handle)(PLI_INT32, vpiHandle);
vpiHandle (*iterate)(PLI_INT32, vpiHandle);
vpiHandle (*scan)(vpiHandle);

View File

@ -514,7 +514,7 @@ extern void compile_var_queue(char*label, char*name, unsigned size);
* nets connected through module ports.
*/
extern void compile_port_info( unsigned index, int vpi_port_type, unsigned width, const char *name );
extern void compile_port_info( unsigned index, int vpi_port_type, unsigned width, const char *name, char* buffer );
/*

View File

@ -1115,3 +1115,351 @@ struct __vpiModPathSrc* vpip_make_modpath_src(struct __vpiModPath*path,
return obj;
}
vvp_fun_intermodpath::vvp_fun_intermodpath(vvp_net_t*net, unsigned width)
: net_(net)
{
for (unsigned idx = 0 ; idx < 12 ; idx += 1)
delay_[idx] = 0;
cur_vec4_ = vvp_vector4_t(width, BIT4_X);
schedule_init_propagate(net_, cur_vec4_);
}
vvp_fun_intermodpath::~vvp_fun_intermodpath()
{
}
void vvp_fun_intermodpath::get_delay12(vvp_time64_t val[12]) const
{
for (unsigned idx = 0 ; idx < 12 ; idx += 1)
val[idx] = delay_[idx];
}
void vvp_fun_intermodpath::put_delay12(const vvp_time64_t val[12])
{
for (unsigned idx = 0 ; idx < 12 ; idx += 1)
delay_[idx] = val[idx];
}
/*
* FIXME: this implementation currently only uses the maximum delay
* from all the bit changes in the vectors. If there are multiple
* changes with different delays, then the results would be
* wrong. What should happen is that if there are multiple changes,
* multiple vectors approaching the result should be scheduled.
*/
void vvp_fun_intermodpath::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t)
{
/* Only the first port is used. */
if (port.port() > 0)
return;
if (cur_vec4_.eeq(bit))
return;
/* Given the scheduled output time, create an output event. */
vvp_time64_t use_delay = delay_from_edge(cur_vec4_.value(0),
bit.value(0),
delay_);
/* FIXME: This bases the edge delay on only the least
bit. This is WRONG! I need to find all the possible delays,
and schedule an event for each partial change. Hard! */
for (unsigned idx = 1 ; idx < bit.size() ; idx += 1) {
vvp_time64_t tmp = delay_from_edge(cur_vec4_.value(idx),
bit.value(idx),
delay_);
/* If the current and new bit values match then no delay
* is needed for this bit. */
if (cur_vec4_.value(idx) == bit.value(idx)) continue;
assert(tmp == use_delay);
}
cur_vec4_ = bit;
schedule_generic(this, use_delay, false);
}
void vvp_fun_intermodpath::run_run()
{
net_->send_vec4(cur_vec4_, 0);
}
/*
* All the below routines that begin with
* intermodpath_* belong the internal function
* of an vpiInterModPath object. This is used to
* make some specific delays path operations
*
*/
static int intermodpath_get(int, vpiHandle ref)
{
struct __vpiInterModPath*obj =dynamic_cast<__vpiInterModPath*>(ref);
assert(obj);
return 0;
}
static void intermodpath_get_value(vpiHandle ref, p_vpi_value)
{
struct __vpiInterModPath* intermodpath = dynamic_cast<__vpiInterModPath*>(ref);
assert(intermodpath);
return;
}
static vpiHandle intermodpath_put_value(vpiHandle ref, s_vpi_value *, int )
{
struct __vpiInterModPath* intermodpath = dynamic_cast<__vpiInterModPath*>(ref);
assert(intermodpath);
return 0;
}
static vpiHandle intermodpath_get_handle(int code, vpiHandle ref)
{
struct __vpiInterModPath*rfp = dynamic_cast<__vpiInterModPath*>(ref);
assert(rfp);
switch (code) {
case vpiScope:
return rfp->scope;
case vpiModule:
{ __vpiScope*scope = rfp->scope;
while (scope && scope->get_type_code() != vpiModule)
scope = scope->scope;
assert(scope);
return scope;
}
}
return 0;
}
static vpiHandle intermodpath_iterate(int code, vpiHandle ref)
{
struct __vpiInterModPath*rfp = dynamic_cast<__vpiInterModPath*>(ref);
assert(rfp);
// For now intermodpaths only support exactly two ports
switch (code) {
case vpiPort: {
vpiHandle*args = (vpiHandle*)calloc(2, sizeof(vpiHandle*));
args[0] = rfp->port1;
args[1] = rfp->port2;
return vpip_make_iterator(2, args, true);
}
}
return 0;
}
/*
* This routine will put specific dimension of delay[] values
* into a vpiHandle. In this case, we will put
* specific delays values in a vpiInterModPath object
*
*/
static void intermodpath_put_delays (vpiHandle ref, p_vpi_delay delays)
{
vvp_time64_t tmp[12];
int idx;
struct __vpiInterModPath * src = dynamic_cast<__vpiInterModPath*>(ref) ;
assert(src) ;
vvp_fun_intermodpath *fun = dynamic_cast<vvp_fun_intermodpath*>(src->net->fun);
assert( fun );
typedef unsigned char map_array_t[12];
// Only the first six entries are used for the less than twelve maps.
static const map_array_t map_1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static const map_array_t map_2 = {0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0};
static const map_array_t map_3 = {0, 1, 2, 0, 2, 1, 0, 0, 0, 0, 0, 0};
static const map_array_t map_6 = {0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0};
static const map_array_t map12 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
const map_array_t*use_map = 0;
switch (delays->no_of_delays) {
case 1:
use_map = &map_1;
break;
case 2:
use_map = &map_2;
break;
case 3:
use_map = &map_3;
break;
case 6:
use_map = &map_6;
break;
case 12:
use_map = &map12;
break;
default:
assert(0);
break;
}
if (delays->time_type == vpiSimTime) {
for (idx = 0 ; idx < 12 ; idx += 1) {
tmp[idx] = vpip_timestruct_to_time(delays->da+use_map[0][idx]);
}
} else {
// You cannot create a modpath with a negative delay so set it
// to zero per 1364-2005 section 14.3.1.
for (idx = 0 ; idx < delays->no_of_delays ; idx += 1) {
if (delays->da[idx].real < 0.0) delays->da[idx].real = 0.0;
}
for (idx = 0 ; idx < 12 ; idx += 1) {
tmp[idx] = vpip_scaled_real_to_time64(delays->da[use_map[0][idx]].real,
src->scope);
}
}
/* Now define the to-from-x delays if needed. */
if (delays->no_of_delays <= 6) {
/* 0->x is the minimum of 0->z and 0->1. */
tmp[DELAY_EDGE_0x] = tmp[DELAY_EDGE_0z] < tmp[DELAY_EDGE_01] ?
tmp[DELAY_EDGE_0z] : tmp[DELAY_EDGE_01];
/* x->1 is the maximum of z->1 and 0->1. */
tmp[DELAY_EDGE_x1] = tmp[DELAY_EDGE_z1] > tmp[DELAY_EDGE_01] ?
tmp[DELAY_EDGE_z1] : tmp[DELAY_EDGE_01];
/* 1->x is the minimum of 1->z and 1->0. */
tmp[DELAY_EDGE_1x] = tmp[DELAY_EDGE_1z] < tmp[DELAY_EDGE_10] ?
tmp[DELAY_EDGE_1z] : tmp[DELAY_EDGE_10];
/* x->0 is the maximum of z->0 and 1->0. */
tmp[DELAY_EDGE_x0] = tmp[DELAY_EDGE_z0] > tmp[DELAY_EDGE_10] ?
tmp[DELAY_EDGE_z0] : tmp[DELAY_EDGE_10];
/* x->z is the maximum of 1->z and 0->z. */
tmp[DELAY_EDGE_xz] = tmp[DELAY_EDGE_1z] > tmp[DELAY_EDGE_0z] ?
tmp[DELAY_EDGE_1z] : tmp[DELAY_EDGE_0z];
/* z->x is the minimum of z->1 and z->0. */
tmp[DELAY_EDGE_zx] = tmp[DELAY_EDGE_z1] < tmp[DELAY_EDGE_z0] ?
tmp[DELAY_EDGE_z1] : tmp[DELAY_EDGE_z0];
}
fun->put_delay12(tmp);
}
/*
* This routine will retrieve the delay[12] values
* of a vpiHandle. In this case, he will get an
* specific delays values from a vpiInterModPath
* object
*
*/
static void intermodpath_get_delays ( vpiHandle ref, p_vpi_delay delays )
{
struct __vpiInterModPath*src = dynamic_cast<__vpiInterModPath*>(ref) ;
assert(src);
vvp_fun_intermodpath *fun = dynamic_cast<vvp_fun_intermodpath*>(src->net->fun);
assert(fun);
int idx;
vvp_time64_t tmp[12];
fun->get_delay12(tmp);
switch (delays->no_of_delays) {
case 1:
case 2:
case 3:
case 6:
case 12:
break;
default:
assert(0);
break;
}
if (delays->time_type == vpiSimTime) {
for (idx = 0; idx < delays->no_of_delays; idx += 1) {
vpip_time_to_timestruct(delays->da+idx, tmp[idx]);
}
} else {
for (idx = 0; idx < delays->no_of_delays; idx += 1) {
delays->da[idx].real = vpip_time_to_scaled_real(tmp[idx], src->scope);
}
}
}
/*
* The __vpiInterModPath class is what the VPI client sees as a
* vpiInterModPath object.
*/
inline __vpiInterModPath::__vpiInterModPath()
{ }
int __vpiInterModPath::get_type_code(void) const
{ return vpiInterModPath; }
int __vpiInterModPath::vpi_get(int code)
{ return intermodpath_get(code, this); }
void __vpiInterModPath::vpi_get_value(p_vpi_value val)
{ intermodpath_get_value(this, val); }
vpiHandle __vpiInterModPath::vpi_put_value(p_vpi_value val, int flags)
{ return intermodpath_put_value(this, val, flags); }
vpiHandle __vpiInterModPath::vpi_handle(int code)
{ return intermodpath_get_handle(code, this); }
vpiHandle __vpiInterModPath::vpi_iterate(int code)
{ return intermodpath_iterate(code, this); }
void __vpiInterModPath::vpi_get_delays(p_vpi_delay del)
{ intermodpath_get_delays(this, del); }
void __vpiInterModPath::vpi_put_delays(p_vpi_delay del)
{ intermodpath_put_delays(this, del); }
static int intermodpath_free_object( vpiHandle ref )
{
delete ref;
return 1 ;
}
__vpiHandle::free_object_fun_t __vpiInterModPath::free_object_fun(void)
{ return &intermodpath_free_object; }
/*
* This function will construct a vpiInterModPath Object.
* give a respective "net", and will point to his
* respective functor
*/
#ifdef CHECK_WITH_VALGRIND
static struct __vpiInterModPath**imp_list = 0;
static unsigned imp_count = 0;
#endif
struct __vpiInterModPath* vpip_make_intermodpath(vvp_net_t *net, vpiPortInfo* port1, vpiPortInfo* port2)
{
struct __vpiInterModPath*obj = new __vpiInterModPath;
obj->scope = vpip_peek_current_scope ( );
obj->net = net;
obj->port1 = port1;
obj->port2 = port2;
#ifdef CHECK_WITH_VALGRIND
imp_count += 1;
imp_list = (struct __vpiInterModPath **) realloc(imp_list,
imp_count*sizeof(struct __vpiInterModPath **));
imp_list[imp_count-1] = obj;
#endif
return obj;
}
#ifdef CHECK_WITH_VALGRIND
void intermodpath_delete()
{
for (unsigned idx = 0; idx < mp_count; idx += 1) {
delete imp_list[idx];
}
free(imp_list);
imp_list = 0;
imp_count = 0;
}
#endif

View File

@ -234,4 +234,36 @@ class vvp_fun_modpath_edge : public vvp_fun_modpath_src {
bool negedge_;
};
/*
* The intermodpath is used to implement the SDF INTERCONNECT feature
* Upon a (INTERCONNECT ...) statement an intermodpath will be inserted
* between port1 and port2 and its delay can be annotated
*/
class vvp_fun_intermodpath : public vvp_net_fun_t, private vvp_gen_event_s {
public:
vvp_fun_intermodpath(vvp_net_t*net, unsigned width);
~vvp_fun_intermodpath();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t);
void get_delay12(vvp_time64_t out[12]) const;
void put_delay12(const vvp_time64_t in[12]);
private:
virtual void run_run();
private:
vvp_net_t*net_;
vvp_vector4_t cur_vec4_;
vvp_time64_t delay_[12];
private: // not implemented
vvp_fun_intermodpath(const vvp_fun_intermodpath&);
vvp_fun_intermodpath& operator= (const vvp_fun_intermodpath&);
};
#endif /* IVL_delay_H */

View File

@ -713,10 +713,13 @@ statement
/* Port information for scopes... currently this is just meta-data for VPI queries */
| K_PORT_INFO T_NUMBER port_type T_NUMBER T_STRING T_SYMBOL ';'
{ compile_port_info( $2 /* port_index */, $3, $4 /* width */,
$5 /*&name */, $6 /* buffer */ ); }
| K_PORT_INFO T_NUMBER port_type T_NUMBER T_STRING ';'
{ compile_port_info( $2 /* port_index */, $3, $4 /* width */,
$5 /*&name */ ); }
$5 /*&name */, nullptr /* buffer */ ); }
| K_TIMESCALE T_NUMBER T_NUMBER';'
{ compile_timescale($2, $3); }

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2008-2022 Stephen Williams (steve@icarus.com)
* Copyright (c) 2008-2023 Stephen Williams (steve@icarus.com)
* Copyright (c) 2023 Leo Moser (leo.moser@pm.me)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -20,6 +21,7 @@
# include "version_base.h"
# include "vpi_priv.h"
# include "schedule.h"
# include "logic.h"
#ifdef CHECK_WITH_VALGRIND
# include "vvp_cleanup.h"
#endif
@ -1553,6 +1555,140 @@ vpiHandle vpi_handle_by_name(const char *name, vpiHandle scope)
return out;
}
// Used to get intermodpath for two ports
vpiHandle vpi_handle_multi(PLI_INT32 type,
vpiHandle ref1,
vpiHandle ref2)
{
if (vpi_trace) {
fprintf(vpi_trace, "vpi_handle_multi(%d, %p, %p) -->\n",
type, ref1, ref2);
}
if (type != vpiInterModPath) {
fprintf(stderr, "sorry: vpi_handle_multi currently supports"
"only vpiInterModPath\n");
return nullptr;
}
vpiPortInfo* port1 = dynamic_cast<vpiPortInfo*>(ref1);
if (!port1) {
fprintf(stderr, "sorry: second argument of vpi_handle_multi"
"must be a vpiPort\n");
return nullptr;
}
vpiPortInfo* port2 = dynamic_cast<vpiPortInfo*>(ref2);
if (!port2) {
fprintf(stderr, "sorry: third argument of vpi_handle_multi"
"must be a vpiPort\n");
return nullptr;
}
// If both ports are vpiOutput, we have to reassign the __vpiSignal from port1
// to port2 because otherwise the non-delayed version of the signal is dumped
// even tho the intermodpath is correctly inserted
__vpiSignal* output_signal = nullptr;
if (port1->get_direction() == vpiOutput && port2->get_direction() == vpiOutput) {
vpiHandle scope_port2 = vpi_handle(vpiScope, ref2);
assert(scope_port2);
std::string port2_name(vpi_get_str(vpiName, ref2));
// Iterate over nets in the scope of port2
vpiHandle net_i = vpi_iterate(vpiNet, scope_port2) ;
vpiHandle net;
while ((net = vpi_scan(net_i)) != NULL) {
std::string net_name(vpi_get_str(vpiName, net));
// Compare whether the net matches with the port name
if (net_name == port2_name) {
output_signal = dynamic_cast<__vpiSignal*>(net);
}
}
}
vvp_net_t* net1 = port1->get_port();
vvp_net_t* net2 = port2->get_port();
if (net1 == nullptr || net2 == nullptr) {
fprintf(stderr, "Error: Could not find net. "
"Did you run iverilog with '-ginterconnect'?\n");
return nullptr;
}
if (net1 == net2) {
fprintf(stderr, "Error: Net for both ports is the same. "
"Did you pass the same port twice?\n");
return nullptr;
}
if (!dynamic_cast<vvp_fun_buft*>(net1->fun)) {
fprintf(stderr, "Error: functor of net1 must be"
"vvp_fun_buft\n");
return nullptr;
}
if (!dynamic_cast<vvp_fun_buft*>(net2->fun)) {
fprintf(stderr, "Error: functor of net2 must be"
"vvp_fun_buft\n");
return nullptr;
}
// Iterate over all nodes connected to port1
vvp_net_ptr_t cur = net1->out_;
vvp_net_ptr_t prev = vvp_net_ptr_t(nullptr, 0);
while (cur.ptr()) {
// Port2 is directly connected to port1
if (cur.ptr() == net2) {
vvp_net_t*new_net = new vvp_net_t;
// Create new node with intermodpath and connect port2 to it
int width = 1; // TODO
vvp_fun_intermodpath*obj = new vvp_fun_intermodpath(new_net, width);
new_net->fun = obj;
new_net->out_ = cur;
// Port2 is in the middle of the list
// Insert intermodpath before port2 and keep everything else intact
if (prev.ptr()) {
prev.ptr()->port[prev.port()] = vvp_net_ptr_t(new_net, 0); // Point to port 0 of vvp_fun_intermodpath
new_net->port[0] = cur.ptr()->port[cur.port()]; // Connect the next net in list
cur.ptr()->port[cur.port()] = vvp_net_ptr_t(nullptr, 0); // Only port2 is connected to intermodpath
// Port2 is first in list
// Insert intermodpath before port2 and keep everything else intact
} else {
net1->out_ = vvp_net_ptr_t(new_net, 0); // Point to port 0 of vvp_fun_intermodpath
new_net->port[0] = cur.ptr()->port[cur.port()]; // Connect the next net in list
cur.ptr()->port[cur.port()] = vvp_net_ptr_t(nullptr, 0); // Only port2 is connected to intermodpath
}
// If both ports are vpiOutput, we have to reassign the __vpiSignal
if (output_signal) {
net2->fil = net1->fil;
net1->fil = nullptr;
output_signal->node = net2;
}
// Create the VPI intermodpath object
__vpiInterModPath* intermodpath = vpip_make_intermodpath(new_net, port1, port2);
intermodpath->intermodpath = obj;
// Finally done, return the intermodpath object
return intermodpath;
}
prev = cur;
cur = cur.ptr()->port[cur.port()]; // Next net in linked list
}
fprintf(stderr, "VPI error: Could not insert intermodpath!\n");
return nullptr;
}
/*
We increment the two vpi methods to enable the
@ -1749,6 +1885,7 @@ vpip_routines_s vpi_routines = {
.get_systf_info = vpi_get_systf_info,
.handle_by_name = vpi_handle_by_name,
.handle_by_index = vpi_handle_by_index,
.handle_multi = vpi_handle_multi,
.handle = vpi_handle,
.iterate = vpi_iterate,
.scan = vpi_scan,

View File

@ -432,6 +432,34 @@ struct __vpiBit {
int get_index(void) const;
};
class vpiPortInfo : public __vpiHandle {
public:
vpiPortInfo( __vpiScope *parent,
unsigned index,
int vpi_direction,
unsigned width,
const char *name,
char* buffer );
~vpiPortInfo();
int get_type_code(void) const { return vpiPort; }
int get_direction(void) { return direction_; }
int vpi_get(int code);
char* vpi_get_str(int code);
vpiHandle vpi_handle(int code);
vvp_net_t* get_port(void) const { return ref_; }
private:
__vpiScope *parent_;
unsigned index_;
int direction_;
unsigned width_;
const char *name_;
vvp_net_t *ref_;
};
/*
* This is used by system calls to represent a bit/part select of
* a simple variable or constant array word.
@ -520,6 +548,43 @@ extern struct __vpiModPathSrc* vpip_make_modpath_src(struct __vpiModPath*path,
extern struct __vpiModPath* vpip_make_modpath(vvp_net_t *net) ;
/*
*
* The vpiInterModPath vpiHandle will define
* a vpiInterModPath of record .intermodpath as defined
* in the IEEE 1364
*
*/
struct __vpiInterModPath : public __vpiHandle {
__vpiInterModPath();
int get_type_code(void) const;
int vpi_get(int code);
void vpi_get_value(p_vpi_value val);
vpiHandle vpi_put_value(p_vpi_value val, int flags);
vpiHandle vpi_handle(int code);
vpiHandle vpi_iterate(int code);
void vpi_get_delays(p_vpi_delay del);
void vpi_put_delays(p_vpi_delay del);
free_object_fun_t free_object_fun(void);
__vpiScope *scope ;
class vvp_fun_intermodpath*intermodpath;
vvp_net_t *net;
vpiPortInfo* port1;
vpiPortInfo* port2;
};
/*
* The Function is used to create the vpiHandle
* for vpiInterModPath
*/
extern struct __vpiInterModPath* vpip_make_intermodpath(vvp_net_t *net, vpiPortInfo* port1, vpiPortInfo* port2);
/*
* These methods support the vpi creation of events. The name string

View File

@ -82,6 +82,9 @@ static void delete_sub_scopes(__vpiScope *scope)
/* The destination ModPath is cleaned up later. */
delete item;
break;
case vpiInterModPath:
delete item;
break;
case vpiNamedEvent:
named_event_delete(item);
break;
@ -659,40 +662,20 @@ unsigned vpip_add_item_to_context(automatic_hooks_s*item,
}
class vpiPortInfo : public __vpiHandle {
public:
vpiPortInfo( __vpiScope *parent,
unsigned index,
int vpi_direction,
unsigned width,
const char *name );
~vpiPortInfo();
int get_type_code(void) const { return vpiPort; }
int vpi_get(int code);
char* vpi_get_str(int code);
vpiHandle vpi_handle(int code);
private:
__vpiScope *parent_;
unsigned index_;
int direction_;
unsigned width_;
const char *name_;
};
vpiPortInfo::vpiPortInfo( __vpiScope *parent,
unsigned index,
int vpi_direction,
unsigned width,
const char *name ) :
const char *name,
char* buffer) :
parent_(parent),
index_(index),
direction_(vpi_direction),
width_(width),
name_(name)
{
if (buffer != nullptr) functor_ref_lookup(&ref_, buffer);
else ref_ = nullptr;
}
vpiPortInfo::~vpiPortInfo()
@ -757,9 +740,9 @@ vpiHandle vpiPortInfo::vpi_handle(int code)
* code-generators etc. There are no actual nets corresponding to instances of module ports
* as elaboration directly connects nets connected through module ports.
*/
void compile_port_info( unsigned index, int vpi_direction, unsigned width, const char *name )
void compile_port_info( unsigned index, int vpi_direction, unsigned width, const char *name, char* buffer )
{
vpiHandle obj = new vpiPortInfo( vpip_peek_current_scope(),
index, vpi_direction, width, name );
vpip_attach_to_current_scope(obj);
vpiHandle obj = new vpiPortInfo( vpip_peek_current_scope(),
index, vpi_direction, width, name, buffer );
vpip_attach_to_current_scope(obj);
}

View File

@ -18,6 +18,7 @@ vpi_get_vlog_info
vpi_handle
vpi_handle_by_index
vpi_handle_by_name
vpi_handle_multi
vpi_iterate
vpi_mcd_close
vpi_mcd_flush

View File

@ -1159,7 +1159,8 @@ class vvp_net_t {
public: // Method to support $countdrivers
void count_drivers(unsigned idx, unsigned counts[4]);
private:
// This needs to be public so that SDF interconnects can be inserted
public:
vvp_net_ptr_t out_;
public: // Need a better new for these objects.