GSoC23 — Workweek 8
Introduction
I am currently in the middle of implementing the SDF INTERCONNECT feature and while there was fast progress early on things have now stalled.
What is the issue? Let me explain this by showing you how I intended to annotate the design.
Interconnect - VPI
We start at the VPI side, where $sdf_annotate
is called to start the process of SDF annotation. Once a INTERCONNECT
token is parsed, we call sdf_interconnect_delays
with the appropriate parameters.
(INTERCONNECT A0 input1.A (0.014:0.014:0.014) (0.006:0.006:0.006))
(An example of an interconnect annotation)
This function finds the port handles in the respective scopes and requests an intermodpath via vpi_handle_multi
and the ports as arguments. Once a vpiInterModPath
is returned, the delays are put with vpi_put_delays
and the annotation is complete.
So far no issues, this all works well. But all the magic lies in vpi_handle_multi
which is implemented on the VVP side.
Interconnect - VVP
We received two port handles via vpi_handle_multi
and have to find the common net between those (if it even exists). How do we do this?
This is the approach which I have used until now:
We can't go from vpiPort
to its net, as this information is missing. But we know that the corresponding net in the module has the same name as the port. Therefore iterate over all nets in each module vpi_iterate(vpiNet, mod);
and find the matching nets for the ports.
Okay, now we have two vpiNets
as vpiHandle
s, we can cast them to a __vpiSignal
, the internal representation of a vpiNet.
struct __vpiSignal*net_node1 = dynamic_cast<__vpiSignal*>(net_handle1);
We can then directly access the vvp_net_t
like this: vvp_net_t* my_node1 = net_node1->node;
Now we have both vvp_net_t
pointers for both ports, we can just compare the addresses if they are the same.
Now we need to somehow insert our delay element without breaking the network.
So we take whatever is connected to the vvp_net_t
and connect it to our new vvp_net_t
with vvp_fun_intermodpath
. The new net is then connected to the first net to complete the network.
Okay, vvp_fun_intermodpath
is where we place our delays. This means we must be able to reference it later on. For this we create the __vpiInterModPath
class which holds the reference to its vvp_fun_intermodpath
. We instantiate such an object and return it from vpi_handle_multi
.
Now, when vpi_put_delays
is called with a __vpiInterModPath
, then we just set the delay of the associated vvp_fun_intermodpath
. Done!
The Issue with the Current Approach
So why does this approach have issues?
Actually I was able to annotate a simple design with three buffers in series just fine. The network looks like this:
(reg a;) (buffer instance) (buffer instance) (buffer instance)
vvp_fun_signal_vec ---> vvp_fun_bufz ---> vvp_fun_bufz ---> vvp_fun_bufz --->
Three vvp_fun_intermodpath
s get successfully inserted between the nodes.
But if you have two ore more of these buffers in parallel things fall apart:
(reg a;) (buffer instances)
vvp_fun_signal_vec ---> vvp_fun_bufz
|-> vvp_fun_bufz
|-> vvp_fun_bufz
You see, we only get a reference to vvp_fun_signal_vec
with this approach. Therefore we don't know which vvp_fun_signal_vec
<-> vvp_fun_bufz
intermodpath we are currently annotating
This becomes even more complicated with specify
blocks enabled:
(reg a;) (modpaths and buffers)
vvp_fun_signal_vec ---> vvp_fun_modpath_src
|-> vvp_fun_modpath_src
|-> vvp_fun_modpath_src
|-> vvp_fun_bufz
|-> vvp_fun_bufz
|-> vvp_fun_bufz
It's just not clear with the information available to us.
Proposal
My idea to solve this particular problem is to add a vvp_net_t
for each input and output of a module with a vvp_fun_port
functor.
This will improve the following points:
- The lookup from vpiPort to
vvp_net_t
becomes much easier, as the information to whichvvp_net_t
the port is connected can be stored invpiPortInfo
. - I can easily check whether the two port functors are connected to each other just by looking at the output ptr of the first one.
- If there is a
vvp_fun_intermodpath
after the first port and the second port is connected to this samevvp_fun_intermodpath
, I know that I have already inserted avvp_fun_intermodpath
and can simply put the delay. - As a bonus, we can easily implement PORT delays by putting a delay in
vvp_fun_port
.
The netlist would then look like this:
(reg a;) (input port (input port
my_design) buffers)
vvp_fun_signal_vec ---> vvp_fun_port ---> vvp_fun_port ---> vvp_fun_modpath_src
| |-> vvp_fun_bufz
|
|-> vvp_fun_port ---> vvp_fun_modpath_src
| |-> vvp_fun_bufz
|
|-> vvp_fun_port ---> vvp_fun_modpath_src
|-> vvp_fun_bufz
As you can see, the connection to each module is through a vvp_fun_port
. This would solve the problem from before where I didn't know which vvp_fun_modpath_src
and vvp_fun_bufz
belong to the given port.
The VVP code generated for one of the buffers currently looks like this:
S_0x55fe0ca10970 .scope module, "buffer0" "buffer" 2 21, 2 3 0, S_0x55fe0ca10790;
.timescale -9 -12;
.port_info 0 /INPUT 1 "in";
.port_info 1 /OUTPUT 1 "out";
L_0x55fe0ca25c30 .functor BUFZ 1, v0x55fe0ca25ac0_0, C4<0>, C4<0>, C4<0>;
v0x55fe0ca10b50_0 .net "in", 0 0, v0x55fe0ca25ac0_0; alias, 1 drivers
v0x55fe0ca24d70_0 .net "out", 0 0, L_0x55fe0ca25c30; alias, 1 drivers
I would propose to change it to this:
S_0x556f997bd970 .scope module, "buffer0" "buffer" 2 21, 2 3 0, S_0x556f997bd790;
.timescale -9 -12;
.port_info 0 /INPUT 1 "in" L_0x123412341234;
.port_info 1 /OUTPUT 1 "out" L_0x234234234234;
L_0x123412341234 .functor PORT 1, v0x556f997d2ac0_0, C4<0>, C4<0>, C4<0>;
L_0x234234234234 .functor PORT 1, L_0x556f997d2c30, C4<0>, C4<0>, C4<0>;
L_0x556f997d2c30 .functor BUFZ 1, L_0x123412341234, C4<0>, C4<0>, C4<0>;
v0x556f997bdb50_0 .net "in", 0 0, v0x556f997d2ac0_0; alias, 1 drivers
v0x556f997d1d70_0 .net "out", 0 0, L_0x556f997d2c30; alias, 1 drivers
All input/output signals go through the PORT
functor, also the .port_info
statement would get an additional parameter, the label for the corresponding PORT
functor. Also I would have to make sure that the modpaths are connected correctly when -gspecify
is enabled.
The drawbacks to this new approach are:
-
Small performance overhead, because all signals going from or to a module must now pass through an additional buffer.
-
The connections inside the modules have to be drastically changed, which means a lot of work in IVL.
Summary
I am currently awaiting a reply from one of the core developers whether this is the right way to go.
Until then I am trying to think of different solutions and try to familiarize myself more with IVL.
See you next week!