Merge pull request #27 from antmicro/no_comb
Added NO_COMB annotation.
diff --git a/README.md b/README.md
index 29ebfac..37dac98 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,9 @@
# python-symbiflow-v2x
-Tool for converting specialized annotated Verilog models into XML needed for Verilog to Routing flow.
+
+[](https://python-symbiflow-v2x.readthedocs.io/en/latest/?badge=latest) [](https://travis-ci.com/SymbiFlow/python-symbiflow-v2x)
+
+Tool for converting specialized annotated Verilog models into XML needed for
+[Verilog to Routing flow](https://docs.verilogtorouting.org/en/latest/arch/reference/).
+
+Documentation can be found at https://python-symbiflow-v2x.readthedocs.io/en/latest/
+
diff --git a/tests/internal_conn/README.rst b/tests/internal_conn/README.rst
new file mode 100644
index 0000000..27067d3
--- /dev/null
+++ b/tests/internal_conn/README.rst
@@ -0,0 +1,13 @@
+A test for cells with "passthrough" modes and direct pin-to-pin connections.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+This test demonstrates two use cases.
+
+1. A non-primitive cell that has direct input to output connections inside.
+
+ Such a cell has child cells that are connected to its ports. But it also contains direct connection(s) between its input and output ports. In such a case the direct connections have to be expressed within its interconnect along with regular connections to child cells.
+
+
+2. A cell with modes in which one of them is a "passthrough"
+
+ A passthrough mode defines a direct input to output connection and no child cells. In such a case no pb_type is to be generated for the "passthrough" mode. Instead, an interconnect defining the direect connection is placed directly under the "mode" tag.
diff --git a/tests/internal_conn/child/child.model.xml b/tests/internal_conn/child/child.model.xml
new file mode 100644
index 0000000..90cfeb8
--- /dev/null
+++ b/tests/internal_conn/child/child.model.xml
@@ -0,0 +1,10 @@
+<models xmlns:xi="http://www.w3.org/2001/XInclude">
+ <model name="CHILD">
+ <input_ports>
+ <port name="I"/>
+ </input_ports>
+ <output_ports>
+ <port name="O"/>
+ </output_ports>
+ </model>
+</models>
diff --git a/tests/internal_conn/child/child.pb_type.xml b/tests/internal_conn/child/child.pb_type.xml
new file mode 100644
index 0000000..0fda113
--- /dev/null
+++ b/tests/internal_conn/child/child.pb_type.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='utf-8'?>
+<pb_type xmlns:xi="http://www.w3.org/2001/XInclude" num_pb="1" name="CHILD">
+ <blif_model>.subckt CHILD</blif_model>
+ <input name="I" num_pins="1"/>
+ <output name="O" num_pins="1"/>
+</pb_type>
diff --git a/tests/internal_conn/child/child.sim.v b/tests/internal_conn/child/child.sim.v
new file mode 100644
index 0000000..e6aee44
--- /dev/null
+++ b/tests/internal_conn/child/child.sim.v
@@ -0,0 +1,7 @@
+(* blackbox *)
+module CHILD(
+ input wire I,
+ output wire O
+);
+
+endmodule
diff --git a/tests/internal_conn/golden.model.xml b/tests/internal_conn/golden.model.xml
new file mode 100644
index 0000000..9ca9fbd
--- /dev/null
+++ b/tests/internal_conn/golden.model.xml
@@ -0,0 +1,3 @@
+<models xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="child/child.model.xml" xpointer="xpointer(models/child::node())"/>
+</models>
diff --git a/tests/internal_conn/golden.pb_type.xml b/tests/internal_conn/golden.pb_type.xml
new file mode 100644
index 0000000..646785c
--- /dev/null
+++ b/tests/internal_conn/golden.pb_type.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<pb_type xmlns:xi="http://www.w3.org/2001/XInclude" name="PARENT" num_pb="1">
+ <input name="I0" num_pins="1"/>
+ <input name="I1" num_pins="1"/>
+ <output name="O0" num_pins="1"/>
+ <output name="O1" num_pins="1"/>
+ <pb_type blif_model=".subckt CHILD" name="child" num_pb="1">
+ <input name="I" num_pins="1"/>
+ <output name="O" num_pins="1"/>
+ </pb_type>
+ <interconnect>
+ <direct input="child.O" name="PARENT-O0" output="PARENT.O0"/>
+ <direct input="PARENT.I1" name="PARENT-O1" output="PARENT.O1"/>
+ <direct input="PARENT.I0" name="child-I" output="child.I"/>
+ </interconnect>
+</pb_type>
diff --git a/tests/internal_conn/parent.sim.v b/tests/internal_conn/parent.sim.v
new file mode 100644
index 0000000..f837c7b
--- /dev/null
+++ b/tests/internal_conn/parent.sim.v
@@ -0,0 +1,18 @@
+`include "./child/child.sim.v"
+
+module PARENT (
+ input wire I0,
+ input wire I1,
+ output wire O0,
+ output wire O1
+);
+
+ CHILD child (
+ .I(I0),
+ .O(O0)
+ );
+
+ // An direct connection from an input to the output pins.
+ assign O1 = I1;
+
+endmodule
diff --git a/tests/io/README.rst b/tests/io/README.rst
new file mode 100644
index 0000000..20f15d6
--- /dev/null
+++ b/tests/io/README.rst
@@ -0,0 +1,7 @@
+Tests for modelling I/O primitives
+++++++++++++++++++++++++++++++++++
+
+In VPR top-level I/O pins are represented using `.input` and `.output` BLIF models that are implemented by leaf pb_types. In order to model those using verilog you need to assing the specific `CLASS` attribute so the V2X would know that it has to use either `.input` or `.output` BLIF model for a cell.
+
+The I/O BLIF models are built-in into the VPR so there is no need for generating models for them. By specifying that a cell is of an I/O class the V2X won't generate model for it.
+
diff --git a/tests/io/input/golden.model.xml b/tests/io/input/golden.model.xml
new file mode 100644
index 0000000..07a7652
--- /dev/null
+++ b/tests/io/input/golden.model.xml
@@ -0,0 +1,3 @@
+<models xmlns:xi="http://www.w3.org/2001/XInclude">
+ <!--this file is intentionally left blank-->
+</models>
diff --git a/tests/io/input/golden.pb_type.xml b/tests/io/input/golden.pb_type.xml
new file mode 100644
index 0000000..77a41cb
--- /dev/null
+++ b/tests/io/input/golden.pb_type.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='utf-8'?>
+<pb_type xmlns:xi="http://www.w3.org/2001/XInclude" name="IPAD" num_pb="1">
+ <blif_model>.input</blif_model>
+ <output name="inpad" num_pins="1"/>
+</pb_type>
diff --git a/tests/io/input/ipad.sim.v b/tests/io/input/ipad.sim.v
new file mode 100644
index 0000000..5288d42
--- /dev/null
+++ b/tests/io/input/ipad.sim.v
@@ -0,0 +1,5 @@
+(* CLASS="input" *)
+module IPAD(inpad);
+ output wire inpad;
+
+endmodule
diff --git a/tests/io/output/golden.model.xml b/tests/io/output/golden.model.xml
new file mode 100644
index 0000000..07a7652
--- /dev/null
+++ b/tests/io/output/golden.model.xml
@@ -0,0 +1,3 @@
+<models xmlns:xi="http://www.w3.org/2001/XInclude">
+ <!--this file is intentionally left blank-->
+</models>
diff --git a/tests/io/output/golden.pb_type.xml b/tests/io/output/golden.pb_type.xml
new file mode 100644
index 0000000..437e760
--- /dev/null
+++ b/tests/io/output/golden.pb_type.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='utf-8'?>
+<pb_type xmlns:xi="http://www.w3.org/2001/XInclude" name="OPAD" num_pb="1">
+ <blif_model>.output</blif_model>
+ <input name="outpad" num_pins="1"/>
+</pb_type>
diff --git a/tests/io/output/opad.sim.v b/tests/io/output/opad.sim.v
new file mode 100644
index 0000000..9ce27e1
--- /dev/null
+++ b/tests/io/output/opad.sim.v
@@ -0,0 +1,5 @@
+(* CLASS="output" *)
+module OPAD(outpad);
+ input wire outpad;
+
+endmodule
diff --git a/tests/modes/golden.model.xml b/tests/modes/golden.model.xml
new file mode 100644
index 0000000..36811d0
--- /dev/null
+++ b/tests/modes/golden.model.xml
@@ -0,0 +1,3 @@
+<models xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="not/not.model.xml" xpointer="xpointer(models/child::node())"/>
+</models>
diff --git a/tests/modes/golden.pb_type.xml b/tests/modes/golden.pb_type.xml
new file mode 100644
index 0000000..d97e5ad
--- /dev/null
+++ b/tests/modes/golden.pb_type.xml
@@ -0,0 +1,43 @@
+<?xml version='1.0' encoding='utf-8'?>
+<pb_type xmlns:xi="http://www.w3.org/2001/XInclude" num_pb="1" name="INV">
+ <input name="I" num_pins="1"/>
+ <output name="O" num_pins="1"/>
+ <mode name="PASSTHROUGH">
+ <interconnect>
+ <direct>
+ <port name="I" type="input"/>
+ <port name="O" type="output"/>
+ </direct>
+ </interconnect>
+ </mode>
+ <mode name="INVERT">
+ <pb_type num_pb="1" name="INVERT">
+ <input name="I" num_pins="1"/>
+ <output name="O" num_pins="1"/>
+ <pb_type num_pb="1" name="inverter">
+ <!--old_name NOT-->
+ <xi:include href="not/not.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/>
+ </pb_type>
+ <interconnect>
+ <direct>
+ <port name="I" type="input"/>
+ <port name="I" type="output" from="inverter"/>
+ </direct>
+ <direct>
+ <port name="O" type="input" from="inverter"/>
+ <port name="O" type="output"/>
+ </direct>
+ </interconnect>
+ </pb_type>
+ <interconnect>
+ <direct>
+ <port name="I" type="input"/>
+ <port name="I" type="output" from="INVERT"/>
+ </direct>
+ <direct>
+ <port name="O" type="input" from="INVERT"/>
+ <port name="O" type="output"/>
+ </direct>
+ </interconnect>
+ </mode>
+</pb_type>
diff --git a/tests/modes/inv.sim.v b/tests/modes/inv.sim.v
new file mode 100644
index 0000000..45fd3df
--- /dev/null
+++ b/tests/modes/inv.sim.v
@@ -0,0 +1,19 @@
+`include "./not/not.sim.v"
+
+(* MODES="PASSTHROUGH;INVERT" *)
+module INV(I, O);
+ input wire I;
+ output wire O;
+
+ parameter MODE="PASSTHROUGH";
+
+ // Passthrough (no inversion) mode
+ generate if (MODE == "PASSTHROUGH") begin
+ assign O = I;
+
+ // Inversion with placeable inverter
+ end else if (MODE == "INVERT") begin
+ NOT inverter(I, O);
+
+ end endgenerate
+endmodule
diff --git a/tests/modes/not/not.model.xml b/tests/modes/not/not.model.xml
new file mode 100644
index 0000000..e135dda
--- /dev/null
+++ b/tests/modes/not/not.model.xml
@@ -0,0 +1,10 @@
+<models xmlns:xi="http://www.w3.org/2001/XInclude">
+ <model name="NOT">
+ <input_ports>
+ <port name="I" combinational_sink_ports="O"/>
+ </input_ports>
+ <output_ports>
+ <port name="O"/>
+ </output_ports>
+ </model>
+</models>
diff --git a/tests/modes/not/not.pb_type.xml b/tests/modes/not/not.pb_type.xml
new file mode 100644
index 0000000..5a6798f
--- /dev/null
+++ b/tests/modes/not/not.pb_type.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<pb_type xmlns:xi="http://www.w3.org/2001/XInclude" num_pb="1" name="NOT">
+ <blif_model>.subckt NOT</blif_model>
+ <input name="I" num_pins="1"/>
+ <output name="O" num_pins="1"/>
+ <delay_constant in_port="I" out_port="O" max="1e-10"/>
+</pb_type>
diff --git a/tests/modes/not/not.sim.v b/tests/modes/not/not.sim.v
new file mode 100644
index 0000000..c4b4d1d
--- /dev/null
+++ b/tests/modes/not/not.sim.v
@@ -0,0 +1,10 @@
+(* whitebox *)
+module NOT (I, O);
+
+ input wire I;
+ (* DELAY_CONST_I="1e-10" *)
+ output wire O;
+
+ assign O = ~I;
+
+endmodule
diff --git a/tests/net_attr/README.rst b/tests/net_attr/README.rst
new file mode 100644
index 0000000..d0d98ed
--- /dev/null
+++ b/tests/net_attr/README.rst
@@ -0,0 +1,4 @@
+Test for handling nets consisting of multiple wires
++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+This test verifies correct handling of nets that pass through multiple wires. In such cases Yosys assigns multiple names to that net.
\ No newline at end of file
diff --git a/tests/net_attr/child/child.model.xml b/tests/net_attr/child/child.model.xml
new file mode 100644
index 0000000..90cfeb8
--- /dev/null
+++ b/tests/net_attr/child/child.model.xml
@@ -0,0 +1,10 @@
+<models xmlns:xi="http://www.w3.org/2001/XInclude">
+ <model name="CHILD">
+ <input_ports>
+ <port name="I"/>
+ </input_ports>
+ <output_ports>
+ <port name="O"/>
+ </output_ports>
+ </model>
+</models>
diff --git a/tests/net_attr/child/child.pb_type.xml b/tests/net_attr/child/child.pb_type.xml
new file mode 100644
index 0000000..0cba455
--- /dev/null
+++ b/tests/net_attr/child/child.pb_type.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='utf-8'?>
+<pb_type xmlns:xi="http://www.w3.org/2001/XInclude" name="CHILD" num_pb="1">
+ <blif_model>.subckt CHILD</blif_model>
+ <input name="I" num_pins="1"/>
+ <output name="O" num_pins="1"/>
+</pb_type>
diff --git a/tests/net_attr/child/child.sim.v b/tests/net_attr/child/child.sim.v
new file mode 100644
index 0000000..e09db94
--- /dev/null
+++ b/tests/net_attr/child/child.sim.v
@@ -0,0 +1,6 @@
+module CHILD(
+ input wire I,
+ output wire O
+);
+
+endmodule
diff --git a/tests/net_attr/golden.model.xml b/tests/net_attr/golden.model.xml
new file mode 100644
index 0000000..0d2461a
--- /dev/null
+++ b/tests/net_attr/golden.model.xml
@@ -0,0 +1,3 @@
+<models xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="./child/child.model.xml" xpointer="xpointer(models/child::node())"/>
+</models>
diff --git a/tests/net_attr/golden.pb_type.xml b/tests/net_attr/golden.pb_type.xml
new file mode 100644
index 0000000..354b299
--- /dev/null
+++ b/tests/net_attr/golden.pb_type.xml
@@ -0,0 +1,19 @@
+<?xml version='1.0' encoding='utf-8'?>
+<pb_type xmlns:xi="http://www.w3.org/2001/XInclude" name="PARENT" num_pb="1">
+ <input name="I" num_pins="1"/>
+ <output name="O" num_pins="1"/>
+ <pb_type name="child" num_pb="1">
+ <!--old_name CHILD-->
+ <xi:include href="./child/child.pb_type.xml" xpointer="xpointer(pb_type/child::node())"/>
+ </pb_type>
+ <interconnect>
+ <direct>
+ <port name="I" type="input"/>
+ <port from="child" name="I" type="output"/>
+ </direct>
+ <direct>
+ <port from="child" name="O" type="input"/>
+ <port name="O" type="output"/>
+ </direct>
+ </interconnect>
+</pb_type>
diff --git a/tests/net_attr/parent.sim.v b/tests/net_attr/parent.sim.v
new file mode 100644
index 0000000..599456a
--- /dev/null
+++ b/tests/net_attr/parent.sim.v
@@ -0,0 +1,15 @@
+`include "./child/child.sim.v"
+
+module PARENT(
+ input wire I,
+ output wire O
+);
+
+ wire hop1 = I;
+
+ CHILD child (
+ .I(hop1),
+ .O(O)
+ );
+
+endmodule
diff --git a/v2x/vlog_to_model.py b/v2x/vlog_to_model.py
index 42b6ef2..a4df516 100755
--- a/v2x/vlog_to_model.py
+++ b/v2x/vlog_to_model.py
@@ -162,7 +162,7 @@
), "Leaf model names should be all uppercase!"
modclass = tmod.attr("CLASS", "")
- if modclass not in ("lut", "routing", "flipflop"):
+ if modclass not in ("input", "output", "lut", "routing", "flipflop"):
model_xml = ET.SubElement(models_xml, "model", {'name': topname})
ports = tmod.ports
diff --git a/v2x/vlog_to_pbtype.py b/v2x/vlog_to_pbtype.py
index 66f100e..d109dfb 100755
--- a/v2x/vlog_to_pbtype.py
+++ b/v2x/vlog_to_pbtype.py
@@ -9,8 +9,8 @@
This will also set the BLIF model to be `.subckt <name>` unless CLASS is
also specified.
- - `(* CLASS="lut|routing|mux|flipflop|mem" *)` : specify the class of an
- given instance.
+ - `(* CLASS="input|output|lut|routing|mux|flipflop|mem" *)` : specify
+ the class of an given instance.
- `(* MODES="mode1; mode2; ..." *)` : specify that the module has more
than one functional mode, each with a given name. The module will be
@@ -212,7 +212,7 @@
smod = yj.module(sink_type)
potential_attrs.append(filter_src(smod.port_attrs(sink_pin)))
- net_attrs = filter_src(mod.net_attrs(mod.net_name(netid)))
+ net_attrs = filter_src(mod.net_attrs_by_netid(netid))
copy_attrs(net_attrs, potential_attrs)
return net_attrs
@@ -283,6 +283,8 @@
A dictionary containing with a list of sink pins for each driver pin.
"""
interconn = defaultdict(list)
+
+ # Cell connections
for cname, ctype in mod.cells:
pb_name = strip_name(cname)
assert pb_name in valid_names
@@ -328,6 +330,36 @@
)
interconn[(pb_name, pin)].append(((None, sink_pin), net_attr))
+ # Passthrough connections. Get ports along with connections
+ inp_ports = [p for p in mod.ports if p[3] == "input"]
+ out_ports = [p for p in mod.ports if p[3] == "output"]
+
+ # Loop over outputs and assign them with connected inputs
+ for out_port in out_ports:
+ for out_bit, out_net in enumerate(out_port[2]):
+
+ # Format full output port name
+ if out_port[1] == 1:
+ out_name = out_port[0]
+ else:
+ out_name = "{}[{}]".format(out_port[0], out_bit)
+
+ # Find input
+ for inp_port in inp_ports:
+ for inp_bit, inp_net in enumerate(inp_port[2]):
+
+ # Format full input port name
+ if inp_port[1] == 1:
+ inp_name = inp_port[0]
+ else:
+ inp_name = "{}[{}]".format(inp_port[0], inp_bit)
+
+ # Find matching nets
+ if out_net == inp_net:
+ key = (None, inp_name)
+ val = ((None, out_name), {})
+ interconn[key].append(val)
+
import pprint
pprint.pprint(list(interconn.values()))
@@ -344,13 +376,23 @@
return interconn
-def mode_interconnects(mod, mode_name) -> List[(CellPin)]:
- interconn = []
+def mode_interconnects(mod, mode_name) -> Dict[CellPin, List[CellPin]]:
+ """
+ This function returns a definition of an interconnect used to connect
+ a child pb_type for the given mode with its parent pb_type that provides
+ the modes.
+
+ The returned dict is indexed by tuples containing source (driver) mode
+ names and pin names. Its values contain lists of sink modes and pin names
+ that are driven by the driver. If the mode name is None then the connection
+ refers to the parent pb_type.
+ """
+ interconn = {}
for name, width, bits, iodir in mod.ports:
if iodir == "input":
- interconn.append(((None, name), (mode_name, name)))
+ interconn[(None, name)] = [((mode_name, name,), {},)]
else:
- interconn.append(((mode_name, name), (None, name)))
+ interconn[(mode_name, name)] = [((None, name,), {},)]
return interconn
@@ -624,7 +666,9 @@
def make_leaf_pb(outfile, yj, mod, mod_pname, pb_type_xml):
- # As leaf node, need to generate timing information
+
+ # As leaf node with "blif_model" set is a site., need to generate timing
+ # information.
def process_clocked_tmg(tmgspec, port, iodir, xmltype, xml_parent):
"""Add a suitable timing spec if necessary to the pb_type"""
if tmgspec is not None:
@@ -715,7 +759,11 @@
), "Model name should be uppercase. {}".format(model_name)
mod_cls = mod.CLASS
if mod_cls is not None:
- if mod_cls == "lut":
+ if mod_cls == "input":
+ pb_attrs["blif_model"] = ".input"
+ elif mod_cls == "output":
+ pb_attrs["blif_model"] = ".output"
+ elif mod_cls == "lut":
pb_attrs["blif_model"] = ".names"
pb_attrs["class"] = "lut"
elif mod_cls == "routing":
@@ -780,23 +828,33 @@
)
)
mode_mod = mode_yj.module(mod.name)
- make_pb_type(infiles, outfile, mode_yj, mode_mod,
- True, mode_xml, smode)
- # if mode pb_type contains interconnect tag,
- # add new connctions there
+ inter = {}
+
+ # The mode has no children. Don't generate a pb_type then. Make
+ # only the interconnect instead.
+ if len(mode_mod.cells) == 0:
+ inter.update(get_interconnects(
+ mode_yj, mode_mod, smode, [smode]))
+
+ # The mode has children, recurse
+ else:
+ make_pb_type(infiles, outfile, mode_yj, mode_mod,
+ True, mode_xml, smode)
+ inter.update(mode_interconnects(mod, smode))
+
+ # Add or update the interconnect.
ic_xml = mode_xml.find("interconnect")
- print("ic_xml is", ic_xml, file=sys.stderr)
if ic_xml is None:
ic_xml = ET.SubElement(mode_xml, "interconnect")
- for (driver_cell,
- driver_pin), (sink_cell,
- sink_pin) in mode_interconnects(mod, smode):
- make_direct_conn(
- ic_xml, (driver_cell, driver_pin), (sink_cell, sink_pin),
- {}
- )
+ for (driv_cell, driv_pin), sinks in inter.items():
+ for (sink_cell, sink_pin), attrs in sinks:
+ make_direct_conn(
+ ic_xml,
+ (driv_cell, driv_pin),
+ (sink_cell, sink_pin),
+ attrs)
if not modes or mode_processing:
routing = children = []
diff --git a/v2x/yosys/json.py b/v2x/yosys/json.py
index 7278773..93a32da 100755
--- a/v2x/yosys/json.py
+++ b/v2x/yosys/json.py
@@ -273,10 +273,49 @@
for n, props in self.data["netnames"].items():
if netid in props['bits']:
names.append(n)
+ # FIXME: This fails when there are two wires with different names
+ # connected to the same netid (yes, that's possible). Which name
+ # should be returned in that case ?
if len(names) != 1:
raise KeyError("Net id {} not found".format(netid))
return names[0]
+ def net_attrs_by_netid(self, netid):
+ """
+ Returns attributes of a given netid. Raises RuntimeError if the same
+ attribute is defined for two or more wires belonging to the same net.
+ The attribute 'src' is an exception, 'src' strings are concatenated
+ when defined for more than one wire.
+
+ Returns dict:
+ -------
+ netid : int
+ """
+
+ attributes = {}
+
+ for name, data in self.data["netnames"].items():
+ if netid in data['bits']:
+
+ # Join attributes
+ for attr, value in data['attributes'].items():
+
+ # Allow multiple definitions of the same attribute only
+ # for the 'src'. Otherwise raise an exception
+ if attr in attributes and attr != 'src':
+ raise RuntimeError(
+ "Conflicting attributes '{}' on netid {}".format(
+ attr, netid))
+
+ # Join 'src' attribute strings
+ if attr in attributes and attr == 'src':
+ attributes[attr] += ";{}".format(value)
+ # Store the value
+ else:
+ attributes[attr] = value
+
+ return attributes
+
def net_drivers(self, net):
"""Returns a list of drivers of a given net, both top level inputs.
and cell outputs. "cell" is set to the name of the module for top level