fix(iomux): Fix port signal length (given mux size non-power of 2)
[pinmux.git] / src / spec / iomux.py
index 61e772b990b034c7ef74e8ff9a66c0cf045e9982..0351fbe064d0687bb9cc2adeb9dd6a4cc7ea65ec 100644 (file)
@@ -5,7 +5,7 @@ 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 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 math import ceil, floor
 from nmigen import Elaboratable, Module, Signal, Record, Array, Cat
 from nmigen.hdl.rec import Layout
@@ -28,18 +28,24 @@ io_layout = (("i", 1),
              ("o", 1)
             )
 
              ("o", 1)
             )
 
+# 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):
 
 class IOMuxBlockSingle(Elaboratable):
 
-    def __init__(self):
+    def __init__(self, n_ports=4):
         print("1-bit IO Mux Block")
         print("1-bit IO Mux Block")
-        self.n_banks = 4
-        self.bank = Signal(log2_int(self.n_banks))
+        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)
 
@@ -47,48 +53,128 @@ class IOMuxBlockSingle(Elaboratable):
         m = Module()
         comb, sync = m.d.comb, m.d.sync
 
         m = Module()
         comb, sync = m.d.comb, m.d.sync
 
-        bank = self.bank
-        bank_ports = self.bank_ports
+        port = self.port
+        periph_ports = self.periph_ports
         out_port = self.out_port
 
         # Connect IO Pad output port to one of the peripheral IOs
         # Connect peripheral inputs to the IO pad input
         out_port = self.out_port
 
         # 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)
+
+        comb += self.periph_ports[port].i.eq(self.out_port.i)
 
 
-        # const
-        BANK0_WB = 0
-        BANK1_P1 = 1
-        BANK2_P2 = 2
-        BANK3_P3 = 3
-
-        with m.Switch(bank):
-            with m.Case(BANK0_WB):
-                self.connect_bank_to_io(comb, BANK0_WB)
-            with m.Case(BANK1_P1):
-                self.connect_bank_to_io(comb, BANK1_P1)
-            with m.Case(BANK2_P2):
-                self.connect_bank_to_io(comb, BANK2_P2)
-            with m.Case(BANK3_P3):
-                self.connect_bank_to_io(comb, BANK3_P3)
         return m
 
         return m
 
-    def connect_bank_to_io(self, domain, bank_arg):
-        domain += self.out_port.o.eq(self.bank_ports[bank_arg].o)
-        domain += self.out_port.oe.eq(self.bank_ports[bank_arg].oe)
-        domain += self.bank_ports[bank_arg].i.eq(self.out_port.i)
+    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'},
@@ -99,16 +185,16 @@ 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)
 
     temp_traces = ('Misc', [
                       ])
         traces.append(temp_traces)
 
     temp_traces = ('Misc', [
-                    ('bank[1:0]', 'in')
+                    ('port[%d:0]' % ((n_ports-1).bit_length()-1), 'in')
                   ])
     traces.append(temp_traces)
     temp_traces = ('IO port to pad', [
                   ])
     traces.append(temp_traces)
     temp_traces = ('IO port to pad', [
@@ -122,9 +208,10 @@ def gen_gtkw_doc(module_name, n_banks, filename):
     write_gtkw(filename+".gtkw", filename+".vcd", traces, style,
                module=module_name)
 
     write_gtkw(filename+".gtkw", filename+".vcd", traces, style,
                module=module_name)
 
-def sim_iomux():
-    filename = "test_pinmux" # Doesn't include extension
-    dut = IOMuxBlockSingle()
+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)
@@ -134,91 +221,15 @@ def sim_iomux():
 
     sim = Simulator(m)
 
 
     sim = Simulator(m)
 
-    sim.add_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)
-
-# Method for toggling i/o/oe of a particular bank port,
-# while bank_sel has three different values:
-# value before, given value, value after
-# when rand is True, previous and consecutive values are
-# random (but NOT equal to given bank_sel)
-def test_single_bank(dut, bank, rand=True):
-    if rand:
-        print("Randomising the prev and next banks")
-        prev_bank=bank
-        while(prev_bank == bank):
-            prev_bank = randint(0, dut.n_banks-1)
-        next_bank=bank
-        while(next_bank == bank):
-            next_bank = randint(0, dut.n_banks-1)
-    else:
-        if bank == 0:
-            prev_bank = dut.n_banks
-        else:
-            prev_bank = bank - 1
-
-        if bank == dut.n_banks:
-            next_bank = 0
-        else:
-            next_bank = bank + 1
-
-    print("Prev={}, Given={}, Next={}".format(prev_bank, bank, next_bank))
+    gen_gtkw_doc("top.pinmux", dut.n_ports, filename)
 
 
-    yield dut.bank.eq(prev_bank)
-    yield Delay(1e-6)
-    yield dut.bank_ports[bank].o.eq(0)
-    yield dut.bank_ports[bank].oe.eq(0)
-    yield dut.out_port.i.eq(0)
-    yield Delay(1e-6)
-
-    yield dut.bank.eq(bank)
-    yield Delay(1e-6)
-
-    test_o = yield dut.out_port.o
-    test_oe = yield dut.out_port.oe
-    test_i = yield dut.bank_ports[bank].i
-    assert(test_o == 0)
-    assert(test_oe == 0)
-    assert(test_i == 0)
 
 
-    yield dut.bank_ports[bank].o.eq(1)
-    yield Delay(1e-6)
-    yield dut.bank_ports[bank].oe.eq(1)
-    yield Delay(1e-6)
-    yield dut.out_port.i.eq(1)
-    yield Delay(1e-6)
-
-    test_o = yield dut.out_port.o
-    test_oe = yield dut.out_port.oe
-    test_i = yield dut.bank_ports[bank].i
-    #print(test_o, test_oe, test_i)
-    assert(test_o == 1)
-    assert(test_oe == 1)
-    assert(test_i == 1)
-
-    yield dut.bank.eq(next_bank)
-    yield Delay(1e-6)
-    yield dut.bank_ports[bank].o.eq(0)
-    yield dut.bank_ports[bank].oe.eq(0)
-    yield dut.out_port.i.eq(0)
-    yield Delay(1e-6)
-
-def test_iomux(dut):
-    print("------START----------------------")
-    #print(dir(dut.bank_ports[0]))
-    #print(dut.bank_ports[0].fields)
-
-    yield from test_single_bank(dut, 0)
-    yield from test_single_bank(dut, 1)
-    yield from test_single_bank(dut, 2)
-    yield from test_single_bank(dut, 3)
-
-    print("Finished the 1-bit IO mux block test!")
 
 if __name__ == '__main__':
 
 if __name__ == '__main__':
-    sim_iomux()
+    sim_iomux(rand_order=True)