utils: Adding data-width option to mux generation

encapsulating ports into a class

Signed-off-by: Jeffrey Elms <elmsfu@freshred.net>
diff --git a/v2x/lib/mux.py b/v2x/lib/mux.py
index aa6b21d..fab33a8 100644
--- a/v2x/lib/mux.py
+++ b/v2x/lib/mux.py
@@ -59,6 +59,32 @@
         return self.value
 
 
+class ModulePort(object):
+    def __init__(self, pin_type, name, width, index, data_width=1):
+        self.name = name
+        self.pin_type = pin_type
+        self.width = width
+        self.index = index
+        self.data_width = data_width
+
+    def getParameterString(self):
+        if self.width == 1:
+            return '\tparameter [0:0] %s = 0;\n' % (self.name)
+        else:
+            return '\tparameter %s %s = 0;\n' % (self.index, self.name)
+
+    def getDefinition(self):
+        if self.width == 1:
+            if self.data_width is not None and self.data_width > 1:
+                return '\t%s [%d:0] %s;\n' % (self.pin_type.verilog(),
+                                              self.data_width - 1, self.name)
+            else:
+                return '\t%s %s;\n' % (self.pin_type.verilog(), self.name)
+        else:
+            return '\t%s %s %s;\n' % (self.pin_type.verilog(), self.index,
+                                      self.name)
+
+
 def pb_type_xml(mux_type, mux_name, pins, subckt=None, num_pb=1, comment=""):
     """Generate <pb_type> XML for a mux.
 
@@ -122,27 +148,36 @@
     if comment is not None:
         pb_type_xml.append(ET.Comment(comment))
 
-    for pin_type, pin_name, pin_width, pin_index in pins:
-        #assert pin_index < pin_width, (
-        #    "Pin index {} >= width {} for pin {} {}".format(pin_index, pin_width, pin_name, pin_type))
-        if mux_type == MuxType.ROUTING and pin_type == MuxPinType.SELECT:
+    for port in pins:
+        #assert port.index < port.width, (
+        #    "Pin index {} >= width {} for pin {} {}".format(port.index, port.width, port.name, port.pin_type))
+        if mux_type == MuxType.ROUTING and port.pin_type == MuxPinType.SELECT:
             continue
+
+        assert port.width == 1 or port.data_width == 1, 'Only one of width(%d) or data_width(%d) may > 1 for pin %s' % (
+            port.width, port.data_width, port.name)
+
+        if port.width == 1 and port.data_width > 1:
+            num_pins = port.data_width
+        else:
+            num_pins = port.width
+
         ET.SubElement(
             pb_type_xml,
-            pin_type.direction(),
+            port.pin_type.direction(),
             {
-                'name': pin_name,
-                'num_pins': str(pin_width)
+                'name': port.name,
+                'num_pins': str(num_pins)
             },
         )
 
     if mux_type == MuxType.LOGIC:
-        for ipin_type, ipin_name, ipin_width, ipin_index in pins:
-            if ipin_type not in (MuxPinType.INPUT, MuxPinType.SELECT):
+        for inport in pins:
+            if inport.pin_type not in (MuxPinType.INPUT, MuxPinType.SELECT):
                 continue
 
-            for opin_type, opin_name, opin_width, opin_index in pins:
-                if opin_type not in (MuxPinType.OUTPUT, ):
+            for outport in pins:
+                if outport.pin_type not in (MuxPinType.OUTPUT, ):
                     continue
 
                 ET.SubElement(
@@ -150,20 +185,20 @@
                     'delay_constant',
                     {
                         'max': "10e-12",
-                        'in_port': "%s.%s" % (mux_name, ipin_name),
-                        'out_port': "%s.%s" % (mux_name, opin_name),
+                        'in_port': "%s.%s" % (mux_name, inport.name),
+                        'out_port': "%s.%s" % (mux_name, outport.name),
                     },
                 )
     elif mux_type == MuxType.ROUTING:
         interconnect = ET.SubElement(pb_type_xml, 'interconnect')
 
         inputs = [
-            "{}.{}".format(mux_name, n) for t, n, _, _ in pins
-            if t in (MuxPinType.INPUT, )
+            "{}.{}".format(mux_name, port.name) for port in pins
+            if port.pin_type in (MuxPinType.INPUT, )
         ]
         outputs = [
-            "{}.{}".format(mux_name, n) for t, n, _, _ in pins
-            if t in (MuxPinType.OUTPUT, )
+            "{}.{}".format(mux_name, port.name) for port in pins
+            if port.pin_type in (MuxPinType.OUTPUT, )
         ]
         assert len(outputs) == 1
 
diff --git a/v2x/mux_gen.py b/v2x/mux_gen.py
index 4eddbc8..b37c505 100755
--- a/v2x/mux_gen.py
+++ b/v2x/mux_gen.py
@@ -34,6 +34,9 @@
 parser.add_argument('--width', type=int, default=8, help="Width of the MUX.")
 
 parser.add_argument(
+    '--data-width', type=int, default=1, help="data width of the MUX.")
+
+parser.add_argument(
     '--type',
     choices=['logic', 'routing'],
     default='logic',
@@ -155,6 +158,10 @@
     mux_mk = normpath(
         os.path.join(mydir, '..', 'common', 'make', 'mux.mk'), to=outdir)
 
+    if args.data_width > 1 and not args.split_inputs:
+        assert False, "data_width(%d) > 1 requires using split_inputs" % (
+            args.data_width)
+
     if args.name_inputs:
         assert_eq(args.name_input, parser.get_default("name_input"))
         args.name_input = None
@@ -242,6 +249,9 @@
             if args.name_input != parser.get_default('name_input'):
                 print("MUX_INPUT = {}".format(args.name_input), file=f)
 
+        if args.data_width > 1:
+            print("MUX_DATA_WIDTH = {}".format(args.data_width), file=f)
+
         if args.split_selects:
             print("MUX_SPLIT_SELECTS = 1", file=f)
             if args.name_selects != parser.get_default('name_selects'):
@@ -280,27 +290,34 @@
     for i in args.order:
         if i == 'i':
             if args.split_inputs:
-                port_names.extend((mux_lib.MuxPinType.INPUT,
-                                   args.name_inputs[j], 1, '[%i]' % j)
-                                  for j in range(args.width))
+                port_names.extend(
+                    mux_lib.ModulePort(mux_lib.MuxPinType.INPUT,
+                                       args.name_inputs[
+                                           j], 1, '[%i]' % j, args.data_width)
+                    for j in range(args.width))
             else:
                 # verilog range bounds are inclusive and convention is [<width-1>:0]
-                port_names.append((mux_lib.MuxPinType.INPUT, args.name_input,
-                                   args.width, '[%i:0]' % (args.width - 1)))
+                port_names.append(
+                    mux_lib.ModulePort(mux_lib.MuxPinType.INPUT,
+                                       args.name_input, args.width,
+                                       '[%i:0]' % (args.width - 1)))
         elif i == 's':
             if args.split_selects:
-                port_names.extend((mux_lib.MuxPinType.SELECT,
-                                   args.name_selects[j], 1, '[%i]' % j)
-                                  for j in range(args.width_bits))
+                port_names.extend(
+                    mux_lib.ModulePort(mux_lib.MuxPinType.SELECT,
+                                       args.name_selects[j], 1, '[%i]' % j)
+                    for j in range(args.width_bits))
             else:
+                # verilog range bounds are inclusive and convention is [<width-1>:0]
                 assert args.name_select is not None
-                # verilog array range bounds are inclusive and convention is [<width-1>:0]
                 port_names.append(
-                    (mux_lib.MuxPinType.SELECT, args.name_select,
-                     args.width_bits, '[%i:0]' % (args.width_bits - 1)))
+                    mux_lib.ModulePort(mux_lib.MuxPinType.SELECT,
+                                       args.name_select, args.width_bits,
+                                       '[%i:0]' % (args.width_bits - 1)))
         elif i == 'o':
-            port_names.append((mux_lib.MuxPinType.OUTPUT, args.name_output, 1,
-                               ''))
+            port_names.append(
+                mux_lib.ModulePort(mux_lib.MuxPinType.OUTPUT, args.name_output,
+                                   1, '', args.data_width))
 
     # ------------------------------------------------------------------------
     # Generate the sim.v Verilog module
@@ -311,10 +328,10 @@
     sim_pathname = os.path.join(outdir, sim_filename)
     with open(sim_pathname, "w") as f:
         module_args = []
-        for type, name, _, _ in port_names:
-            if args.type == 'routing' and type == mux_lib.MuxPinType.SELECT:
+        for port in port_names:
+            if args.type == 'routing' and port.pin_type == mux_lib.MuxPinType.SELECT:
                 continue
-            module_args.append(name)
+            module_args.append(port.name)
 
         mux_prefix = {'logic': '', 'routing': 'r'}[args.type]
         mux_class = {'logic': 'mux', 'routing': 'routing'}[args.type]
@@ -334,60 +351,68 @@
         f.write('(* blackbox *) (* CLASS="%s" *)\n' % mux_class)
         f.write("module %s(%s);\n" % (args.name_mux, ", ".join(module_args)))
         previous_type = None
-        for type, name, width, index in port_names:
-            if previous_type != type:
+        for port in port_names:
+            if previous_type != port.pin_type:
                 f.write("\n")
-                previous_type = type
-            if args.type == 'routing' and type == mux_lib.MuxPinType.SELECT:
-                if width == 1:
-                    f.write('\tparameter [0:0] %s = 0;\n' % (name))
-                else:
-                    f.write('\tparameter %s %s = 0;\n' % (index, name))
+                previous_type = port.pin_type
+            if args.type == 'routing' and port.pin_type == mux_lib.MuxPinType.SELECT:
+                f.write(port.getParameterString())
                 continue
-
-            if width == 1:
-                f.write('\t%s %s;\n' % (type.verilog(), name))
             else:
-                f.write('\t%s %s %s;\n' % (type.verilog(), index, name))
+                f.write(port.getDefinition())
 
         f.write("\n")
+        if args.data_width > 1:
+            f.write('\tgenvar\tii;\n')
+            f.write('\tfor(ii=0; ii<%d; ii++) begin: bitmux\n' %
+                    (args.data_width))
+
         f.write('\tMUX%s mux (\n' % args.width)
         for i in range(0, args.width):
             j = 0
-            for type, name, width, index in port_names:
-                if type != mux_lib.MuxPinType.INPUT:
+            for port in port_names:
+                if port.pin_type != mux_lib.MuxPinType.INPUT:
                     continue
-                if j + width <= i:
-                    j += width
+                if j + port.width <= i:
+                    j += port.width
                     continue
                 break
 
-            if width == 1:
-                f.write('\t\t.I%i(%s),\n' % (i, name))
+            if port.width == 1:
+                if args.data_width > 1:
+                    f.write('\t\t.I%i(%s[ii]),\n' % (i, port.name))
+                else:
+                    f.write('\t\t.I%i(%s),\n' % (i, port.name))
             else:
-                f.write('\t\t.I%i(%s[%i]),\n' % (i, name, i - j))
+                f.write('\t\t.I%i(%s[%i]),\n' % (i, port.name, i - j))
 
         for i in range(0, args.width_bits):
             j = 0
-            for type, name, width, index in port_names:
-                if type != mux_lib.MuxPinType.SELECT:
+            for port in port_names:
+                if port.pin_type != mux_lib.MuxPinType.SELECT:
                     continue
-                if j + width < i:
-                    j += width
+                if j + port.width < i:
+                    j += port.width
                     continue
                 break
 
-            if width == 1:
-                f.write('\t\t.S%i(%s),\n' % (i, name))
+            if port.width == 1:
+                f.write('\t\t.S%i(%s),\n' % (i, port.name))
             else:
-                f.write('\t\t.S%i(%s[%i]),\n' % (i, name, i - j))
+                f.write('\t\t.S%i(%s[%i]),\n' % (i, port.name, i - j))
 
-        for type, name, width, index in port_names:
-            if type != mux_lib.MuxPinType.OUTPUT:
+        for port in port_names:
+            if port.pin_type != mux_lib.MuxPinType.OUTPUT:
                 continue
             break
-        assert_eq(width, 1)
-        f.write('\t\t.O(%s)\n\t);\n' % name)
+        assert_eq(port.width, 1)
+        if args.data_width > 1:
+            f.write('\t\t.O(%s[ii])\n\t);\n' % port.name)
+        else:
+            f.write('\t\t.O(%s)\n\t);\n' % port.name)
+
+        if args.data_width > 1:
+            f.write('end\n')
 
         f.write('endmodule\n')
 
@@ -414,17 +439,19 @@
 
         input_ports = ET.SubElement(model_xml, 'input_ports')
         output_ports = ET.SubElement(model_xml, 'output_ports')
-        for type, name, width, index in port_names:
-            if type in (mux_lib.MuxPinType.INPUT, mux_lib.MuxPinType.SELECT):
+        for port in port_names:
+            if port.pin_type in (mux_lib.MuxPinType.INPUT,
+                                 mux_lib.MuxPinType.SELECT):
                 ET.SubElement(
                     input_ports, 'port', {
                         'name':
-                        name,
+                        port.name,
                         'combinational_sink_ports':
-                        ','.join(n for t, n, w, i in port_names
-                                 if t in (mux_lib.MuxPinType.OUTPUT, )),
+                        ','.join(
+                            port.name for port in port_names
+                            if port.pin_type in (mux_lib.MuxPinType.OUTPUT, )),
                     })
-            elif type in (mux_lib.MuxPinType.OUTPUT, ):
+            elif port.pin_type in (mux_lib.MuxPinType.OUTPUT, ):
                 ET.SubElement(output_ports, 'port', {'name': args.name_output})
 
         models_str = ET.tostring(models_xml, pretty_print=True).decode('utf-8')