Merge pull request #29 from antmicro/io_class Added support for defining IO cells
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/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_pbtype.py b/v2x/vlog_to_pbtype.py index 8164c63..d109dfb 100755 --- a/v2x/vlog_to_pbtype.py +++ b/v2x/vlog_to_pbtype.py
@@ -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: @@ -784,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