fix(iomux): Fix port signal length (given mux size non-power of 2)
[pinmux.git] / src / spec / iomux.py
index e310ed95d1fc2b40e4fd6772c3274f413eb36694..0351fbe064d0687bb9cc2adeb9dd6a4cc7ea65ec 100644 (file)
@@ -5,95 +5,176 @@ testing, however it could also be used as an actual GPIO peripheral
 
 Modified for use with pinmux, will probably change the class name later.
 """
 
 Modified for use with pinmux, will probably change the class name later.
 """
-from random import randint
-from math import ceil, floor
+from random import randint, shuffle
+#from math import ceil, floor
 from nmigen import Elaboratable, Module, Signal, Record, Array, Cat
 from nmigen.hdl.rec import Layout
 from nmigen.utils import log2_int
 from nmigen.cli import rtlil
 from nmigen import Elaboratable, Module, Signal, Record, Array, Cat
 from nmigen.hdl.rec import Layout
 from nmigen.utils import log2_int
 from nmigen.cli import rtlil
-from soc.minerva.wishbone import make_wb_layout
+#from soc.minerva.wishbone import make_wb_layout
 from nmutil.util import wrap
 from nmutil.util import wrap
-from soc.bus.test.wb_rw import wb_read, wb_write
+#from soc.bus.test.wb_rw import wb_read, wb_write
 
 from nmutil.gtkw import write_gtkw
 
 cxxsim = False
 if cxxsim:
 
 from nmutil.gtkw import write_gtkw
 
 cxxsim = False
 if cxxsim:
-    from nmigen.sim.cxxsim import Simulator, Settle
+    from nmigen.sim.cxxsim import Simulator, Settle, Delay
 else:
 else:
-    from nmigen.sim import Simulator, Settle
+    from nmigen.sim import Simulator, Settle, Delay
 
 io_layout = (("i", 1),
              ("oe", 1),
              ("o", 1)
             )
 
 
 io_layout = (("i", 1),
              ("oe", 1),
              ("o", 1)
             )
 
-class IOMuxBlock(Elaboratable):
+# This block produces an N-to-1 mux with N 3-bit periph ports and one pad port.
+# The peripheral ports are intended to be wired to peripheral functions,
+# while the pad port will connect to the I/O pad.
+# Peripheral and output ports have o/oe/i signals, and the port signal is used
+# to select between the peripheral ports.
+class IOMuxBlockSingle(Elaboratable):
 
 
-    def __init__(self):
-        print("IO Mux Block")
-        self.n_banks = 4
-        self.bank = Signal(log2_int(self.n_banks))
+    def __init__(self, n_ports=4):
+        print("1-bit IO Mux Block")
+        self.n_ports = n_ports
+        portsize = n_ports.bit_length()
+        self.port = Signal(portsize)
 
         temp = []
 
         temp = []
-        for i in range(self.n_banks):
-            temp_str = "bank{}".format(i)
-            temp.append(Record(name=temp_str, layout=io_layout))
-        self.bank_ports = Array(temp)
+        for i in range(self.n_ports):
+            name = "port%d" % i
+            temp.append(Record(name=name, layout=io_layout))
+        self.periph_ports = Array(temp)
 
         self.out_port = Record(name="IO", layout=io_layout)
 
 
         self.out_port = Record(name="IO", layout=io_layout)
 
-        #self.b0 = Record(name="b0", layout=io_layout)
-        #self.b1 = Record(name="b1", layout=io_layout)
-
     def elaborate(self, platform):
         m = Module()
         comb, sync = m.d.comb, m.d.sync
 
     def elaborate(self, platform):
         m = Module()
         comb, sync = m.d.comb, m.d.sync
 
-        bank = self.bank
-        bank_ports = self.bank_ports
-        #b0 = self.b0
-        #b1 = self.b1
+        port = self.port
+        periph_ports = self.periph_ports
         out_port = self.out_port
 
         out_port = self.out_port
 
-        sync += out_port.o.eq(bank_ports[0].o)
-        sync += out_port.oe.eq(bank_ports[0].oe)
-        sync += bank_ports[0].i.eq(out_port.i)
-
         # Connect IO Pad output port to one of the peripheral IOs
         # Connect peripheral inputs to the IO pad input
         # Connect IO Pad output port to one of the peripheral IOs
         # Connect peripheral inputs to the IO pad input
+        comb += self.out_port.o.eq(self.periph_ports[port].o)
+        comb += self.out_port.oe.eq(self.periph_ports[port].oe)
 
 
-        bank_range = range(self.n_banks)
-
-        #with m.Switch(bank):
-
-        for cur_bank in bank_range:
-            sync += out_port.o.eq(bank_ports[cur_bank].o)
-            sync += out_port.oe.eq(bank_ports[cur_bank].oe)
-            sync += bank_ports[cur_bank].i.eq(out_port.i)
-
-            temp_list = list(bank_range)
-            temp_list.pop(temp_list.index(cur_bank))
-            print("Banks with input hardwired to zero: {}".format(temp_list))
-            for j in range(len(temp_list)):
-                unused_bank = temp_list[j]
-                sync += bank_ports[unused_bank].i.eq(0)
+        comb += self.periph_ports[port].i.eq(self.out_port.i)
 
         return m
 
 
         return m
 
+    def connect_port_to_io(self, domain, port_arg):
+        domain += self.out_port.o.eq(self.periph_ports[port_arg].o)
+        domain += self.out_port.oe.eq(self.periph_ports[port_arg].oe)
+        domain += self.periph_ports[port_arg].i.eq(self.out_port.i)
+
     def __iter__(self):
         """ Get member signals for Verilog form. """
         for field in self.out_port.fields.values():
             yield field
     def __iter__(self):
         """ Get member signals for Verilog form. """
         for field in self.out_port.fields.values():
             yield field
-        for bank in range(len(self.bank_ports)):
-            for field in self.bank_ports[bank].fields.values():
+        for port in range(self.n_ports):
+            for field in self.periph_ports[port].fields.values():
                 yield field
                 yield field
-        yield self.bank
+        yield self.port
 
     def ports(self):
         return list(self)
 
 
     def ports(self):
         return list(self)
 
-def gen_gtkw_doc(module_name, n_banks, filename):
+# Method to test a particular peripheral port
+# when rand_order is True, previous and consecutive ports are
+# random (but NOT equal to given port)
+def test_single_port(dut, port, rand_order=True, delay=1e-6):
+    if rand_order:
+        print("Randomising the prev and next ports")
+        prev_port=port
+        while(prev_port == port):
+            prev_port = randint(0, dut.n_ports-1)
+        next_port=port
+        while(next_port == port):
+            next_port = randint(0, dut.n_ports-1)
+    else:
+        # Set the prev and next ports as consecutive ports
+        if port == 0:
+            prev_port = dut.n_ports - 1
+        else:
+            prev_port = port - 1
+
+        if port == dut.n_ports:
+            next_port = 0
+        else:
+            next_port = port + 1
+
+    print("Prev=%d, Given=%d, Next=%d" % (prev_port, port, next_port))
+
+    # Clear o/oe, delay, set port i
+    # Set to previous port, delay
+    # Assert port i == 0
+    # Set to desired port
+    # Assert port i == 1
+    # Set o/oe, delay
+    # Assert o, oe == 1
+    # Set to next port, delay
+    # Assert port i == 0
+    yield dut.periph_ports[port].o.eq(0)
+    yield Delay(delay)
+    yield dut.periph_ports[port].oe.eq(0)
+    yield Delay(delay)
+    yield dut.out_port.i.eq(1)
+    yield Delay(delay)
+
+    yield dut.port.eq(prev_port)
+    yield Delay(delay)
+
+    test_i = yield dut.periph_ports[port].i
+    assert(test_i == 0)
+
+    yield dut.port.eq(port)
+    yield Delay(delay)
+
+    test_o = yield dut.out_port.o
+    test_oe = yield dut.out_port.oe
+    test_i = yield dut.periph_ports[port].i
+    assert(test_o == 0)
+    assert(test_oe == 0)
+    assert(test_i == 1)
+
+    yield dut.periph_ports[port].o.eq(1)
+    yield Delay(delay)
+    yield dut.periph_ports[port].oe.eq(1)
+    yield Delay(delay)
+
+    test_o = yield dut.out_port.o
+    test_oe = yield dut.out_port.oe
+    assert(test_o == 1)
+    assert(test_oe == 1)
+
+    yield dut.port.eq(next_port)
+    yield Delay(delay)
+
+    test_i = yield dut.periph_ports[port].i
+    assert(test_i == 0)
+
+def test_iomux(dut, rand_order=True):
+    print("------START----------------------")
+    #print(dir(dut.periph_ports[0]))
+    #print(dut.periph_ports[0].fields)
+
+    # Produce a test list of port values
+    test_port_vec = list(range(0, dut.n_ports))
+    #print(test_port_vec)
+    # Randomise for wider testing
+    if rand_order:
+        shuffle(test_port_vec)
+        #print(test_port_vec)
+    for i in range(dut.n_ports):
+        yield from test_single_port(dut, test_port_vec[i], rand_order)
+
+    print("Finished the 1-bit IO mux block test!")
+
+def gen_gtkw_doc(module_name, n_ports, filename):
     # GTKWave doc generation
     style = {
         '': {'base': 'hex'},
     # GTKWave doc generation
     style = {
         '': {'base': 'hex'},
@@ -104,21 +185,33 @@ def gen_gtkw_doc(module_name, n_banks, filename):
 
     # Create a trace list, each block expected to be a tuple()
     traces = []
 
     # Create a trace list, each block expected to be a tuple()
     traces = []
-    for bank in range(0, n_banks):
-        temp_traces = ('Bank{}'.format(bank), [
-                        ('bank{}__i'.format(bank), 'in'),
-                        ('bank{}__o'.format(bank), 'out'),
-                        ('bank{}__oe'.format(bank), 'out')
+    for port in range(0, n_ports):
+        temp_traces = ('Bank%d' % port, [
+                        ('port%d__i' % port, 'in'),
+                        ('port%d__o' % port, 'out'),
+                        ('port%d__oe' % port, 'out')
                       ])
         traces.append(temp_traces)
                       ])
         traces.append(temp_traces)
+
+    temp_traces = ('Misc', [
+                    ('port[%d:0]' % ((n_ports-1).bit_length()-1), 'in')
+                  ])
+    traces.append(temp_traces)
+    temp_traces = ('IO port to pad', [
+                    ('IO__i', 'in'),
+                    ('IO__o', 'out'),
+                    ('IO__oe', 'out')
+                  ])
+    traces.append(temp_traces)
     #print(traces)
 
     write_gtkw(filename+".gtkw", filename+".vcd", traces, style,
                module=module_name)
 
     #print(traces)
 
     write_gtkw(filename+".gtkw", filename+".vcd", traces, style,
                module=module_name)
 
-def sim_iomux():
-    filename = "test_pinmux" # Doesn't include extension
-    dut = IOMuxBlock()
+def sim_iomux(rand_order=True):
+    filename = "test_iomux" # Doesn't include extension
+    n_ports = 8
+    dut = IOMuxBlockSingle(n_ports)
     vl = rtlil.convert(dut, ports=dut.ports())
     with open(filename+".il", "w") as f:
         f.write(vl)
     vl = rtlil.convert(dut, ports=dut.ports())
     with open(filename+".il", "w") as f:
         f.write(vl)
@@ -127,50 +220,16 @@ def sim_iomux():
     m.submodules.pinmux = dut
 
     sim = Simulator(m)
     m.submodules.pinmux = dut
 
     sim = Simulator(m)
-    sim.add_clock(1e-6)
 
 
-    sim.add_sync_process(wrap(test_iomux(dut)))
+    sim.add_process(wrap(test_iomux(dut, rand_order)))
     sim_writer = sim.write_vcd(filename+".vcd")
     with sim_writer:
         sim.run()
 
     sim_writer = sim.write_vcd(filename+".vcd")
     with sim_writer:
         sim.run()
 
-    gen_gtkw_doc("top.pinmux", dut.n_banks, filename)
+    gen_gtkw_doc("top.pinmux", dut.n_ports, filename)
+
 
 
-def test_iomux(dut):
-    print("------START----------------------")
-    #print(dir(dut.bank_ports[0]))
-    #print(dut.bank_ports[0].fields)
-    #for (name, signal) in dut.b0.fields.items():
-    #    print(name, signal)
-    #print(dir(dut.bank_ports))
-    #print(dut.bank_ports.__len__())
-    #for bank in range(len(dut.bank_ports)):
-    #    for values in dut.bank_ports[bank].fields.values():
-    #        print(values)
-    yield dut.bank_ports[0].o.eq(1)
-    yield dut.bank.eq(0)
-    yield
-    yield dut.bank_ports[0].o.eq(1)
-    yield
-    yield
-    yield dut.bank_ports[1].o.eq(1)
-    yield
-    yield dut.bank_ports[0].oe.eq(1)
-    yield
-    yield dut.bank.eq(1)
-    yield
-
-    yield dut.bank_ports[0].o.eq(0)
-    yield
-    yield dut.bank_ports[1].o.eq(0)
-    yield
-    yield dut.bank_ports[1].oe.eq(1)
-    yield
-    yield dut.bank.eq(0)
-    yield
-
-    print("Finished the IO mux block test!")
 
 if __name__ == '__main__':
 
 if __name__ == '__main__':
-    sim_iomux()
+    sim_iomux(rand_order=True)