feat(stage2): Added PinSpec testing, not used yet
[pinmux.git] / src / spec / stage2.py
index 1e574d3f80ff12b2a5415ebc313bd262ba7812aa..3cd7fbafb424e223a8f883123801833d7bb684e5 100644 (file)
@@ -19,6 +19,8 @@ else:
     from nmigen.sim import Simulator, Settle, Delay
 
 from iomux import IOMuxBlockSingle
+from base import PinSpec
+from jtag import iotypes
 
 io_layout = (("i", 1),
              ("oe", 1),
@@ -40,85 +42,73 @@ I2C_BANK = 2
 Really basic example, uart tx/rx and i2c sda/scl pinmux
 """
 class ManPinmux(Elaboratable):
-    def __init__(self, pad_names):
+    def __init__(self, requested):
         print("Test Manual Pinmux!")
+
+        self.requested = requested
         self.n_banks = 4
-        self.iomux1 = IOMuxBlockSingle(self.n_banks)
-        self.iomux2 = IOMuxBlockSingle(self.n_banks)
-        """
-        self.pads = {}
-        for pad in pad_names:
-            self.pads[pad] = Record(name=pad, layout=io_layout)
-        self.gpio = {"0": Record(name="gp0", layout=io_layout),
-                     "1": Record(name="gp1", layout=io_layout)
-                    }
-        self.uart = Record(name="uart", layout=uart_layout)
-        self.i2c = {"sda": Record(name="sda", layout=io_layout),
-                    "scl": Record(name="scl", layout=io_layout)
-                   }
-        """
         self.bank = Signal(log2_int(self.n_banks))
-        self.pads = {pad_names[0]:{}, pad_names[1]:{}}
-        self.pads["N1"]["pad"] = Record(name=pad_names[0], layout=io_layout)
-        self.pads["N1"]["mux%d" % GPIO_BANK] = Record(name="gp0", layout=io_layout)
-        self.pads["N1"]["mux%d" % UART_BANK] = Record(name="tx", layout=uart_tx_layout)
-        self.pads["N1"]["mux%d" % I2C_BANK] = Record(name="sda", layout=io_layout)
-        self.pads["N2"]["pad"] = Record(name=pad_names[1], layout=io_layout)
-        self.pads["N2"]["mux%d" % GPIO_BANK] = Record(name="gp1", layout=io_layout)
-        self.pads["N2"]["mux%d" % UART_BANK] = Signal(name="rx") # Only one signal
-        self.pads["N2"]["mux%d" % I2C_BANK] = Record(name="scl", layout=io_layout)
+        self.pads = {}
+        self.muxes = {}
+        # Automatically create the necessary periph/pad Records/Signals
+        # depending on the given dict specification
+        for pad in self.requested.keys():
+            self.pads[pad] = {}
+            self.pads[pad]["pad"] = Record(name=pad, layout=io_layout)
+            self.muxes[pad] = IOMuxBlockSingle(self.n_banks)
+            for mux in self.requested[pad].keys():
+                periph = self.requested[pad][mux][0]
+                unit = self.requested[pad][mux][1]
+                sig = self.requested[pad][mux][2][:-1]
+                sig_type = iotypes[self.requested[pad][mux][2][-1]]
+                #print(sig, sig_type)
+                if sig_type == iotypes['*']:
+                    self.pads[pad][mux] = Record(name="%s%d" % (sig, unit),
+                                                 layout=io_layout)
+                elif sig_type == iotypes['+']:
+                    self.pads[pad][mux] = Signal(name="%s%d_o" % (sig, unit))
+                elif sig_type == iotypes['-']:
+                    self.pads[pad][mux] = Signal(name="%s%d_i" % (sig, unit))
+        print(self.pads)
 
     def elaborate(self, platform):
         m = Module()
         comb, sync = m.d.comb, m.d.sync
-        iomux1 = self.iomux1
-        iomux2 = self.iomux2
-        m.submodules.iomux1 = iomux1
-        m.submodules.iomux2 = iomux2
-
-        pads = self.pads
-        pad0 = self.pads["N1"]["pad"]
-        gp0 = self.pads["N1"]["mux%d" % GPIO_BANK]
-        gp1 = self.pads["N2"]["mux%d" % GPIO_BANK]
-        #uart = self.uart
-        #i2c = self.i2c
+        muxes = self.muxes
         bank = self.bank
+        pads = self.pads
+        for pad in pads.keys():
+            m.submodules[pad+"_mux"] = muxes[pad]
+            # all muxes controlled by the same multi-bit signal
+            comb += muxes[pad].bank.eq(bank)
 
-        comb += iomux1.bank.eq(bank)
-        comb += iomux2.bank.eq(bank)
+        # print(self.requested)
+        # print(self.pads)
 
         # ---------------------------
-        # This section is muxing only - doesn'care about pad names
+        # This section connects the periphs to the assigned banks
         # ---------------------------
-        # gpio - gpio0 on Pad1, gpio1 on Pad2
-        comb += iomux1.bank_ports[GPIO_BANK].o.eq(gp0.o)
-        comb += iomux1.bank_ports[GPIO_BANK].oe.eq(gp0.oe)
-        comb += gp0.i.eq(iomux1.bank_ports[GPIO_BANK].i)
-        comb += iomux2.bank_ports[GPIO_BANK].o.eq(gp1.o)
-        comb += iomux2.bank_ports[GPIO_BANK].oe.eq(gp1.oe)
-        comb += gp1.i.eq(iomux2.bank_ports[GPIO_BANK].i)
-        # uart Pad 1 tx, Pad 2 rx
-        #comb += iomux1.bank_ports[UART_BANK].o.eq(uart.tx)
-        #comb += iomux1.bank_ports[UART_BANK].oe.eq(uart.oe)
-        #comb += uart.rx.eq(iomux2.bank_ports[UART_BANK].i)
-        # i2c  Pad 1 sda, Pad 2 scl
-        #comb += iomux1.bank_ports[I2C_BANK].o.eq(i2c["sda"].o)
-        #comb += iomux1.bank_ports[I2C_BANK].oe.eq(i2c["sda"].oe)
-        #comb += i2c["sda"].i.eq(iomux1.bank_ports[I2C_BANK].i)
-        #comb += iomux2.bank_ports[I2C_BANK].o.eq(i2c["scl"].o)
-        #comb += iomux2.bank_ports[I2C_BANK].oe.eq(i2c["scl"].oe)
-        #comb += i2c["scl"].i.eq(iomux2.bank_ports[I2C_BANK].i)
-
+        for pad in pads.keys():
+            for mux in self.requested[pad].keys():
+                periph = self.requested[pad][mux][0]
+                num = int(mux[3])
+                sig = self.requested[pad][mux][2][:-1]
+                sig_type = iotypes[self.requested[pad][mux][2][-1]]
+                if sig_type == iotypes['*']:
+                    comb += muxes[pad].bank_ports[num].o.eq(pads[pad][mux].o)
+                    comb += muxes[pad].bank_ports[num].oe.eq(pads[pad][mux].oe)
+                    comb += pads[pad][mux].i.eq(muxes[pad].bank_ports[num].i)
+                elif sig_type == iotypes['+']:
+                    comb += muxes[pad].bank_ports[num].o.eq(pads[pad][mux])
+                elif sig_type == iotypes['-']:
+                    comb += pads[pad][mux].eq(muxes[pad].bank_ports[num].i)
         # ---------------------------
         # Here is where the muxes are assigned to the actual pads
         # ---------------------------
-        # TODO: for-loop to autoconnect muxes to pads (n_pads var?)
-        comb += pads['N1']["pad"].o.eq(iomux1.out_port.o)
-        comb += pads['N1']["pad"].oe.eq(iomux1.out_port.oe)
-        comb += iomux1.out_port.i.eq(pads['N1']["pad"].i)
-        comb += pads['N2']["pad"].o.eq(iomux2.out_port.o)
-        comb += pads['N2']["pad"].oe.eq(iomux2.out_port.oe)
-        comb += iomux2.out_port.i.eq(pads['N2']["pad"].i)
+        for pad in pads.keys():
+            comb += pads[pad]["pad"].o.eq(muxes[pad].out_port.o)
+            comb += pads[pad]["pad"].oe.eq(muxes[pad].out_port.oe)
+            comb += muxes[pad].out_port.i.eq(pads[pad]["pad"].i)
 
         return m
 
@@ -126,12 +116,12 @@ class ManPinmux(Elaboratable):
         for pad in list(self.pads.keys()):
             for field in self.pads[pad]["pad"].fields.values():
                 yield field
-        #for field in self.uart.fields.values():
-        #    yield field
-        #for field in self.i2c["sda"].fields.values():
-        #    yield field
-        #for field in self.i2c["scl"].fields.values():
-        #    yield field
+            for mux in self.pads[pad].keys():
+                if type(self.pads[pad][mux]) == Signal:
+                    yield self.pads[pad][mux]
+                else:
+                    for field in self.pads[pad][mux].fields.values():
+                        yield field
         yield self.bank
 
     def ports(self):
@@ -180,37 +170,38 @@ Sends a byte via uart tx, checked at output pad
 Then sends the same byte via input pad to uart rx
 Input and output pads are different, so must specify both
 """
-def uart_send(uart, pad_o, pad_i, byte, delay=1e-6):
+def uart_send(tx, rx, pad_tx, pad_rx, byte, delay=1e-6):
     # Drive uart tx - check the word seen at the Pad
-    yield uart.oe.eq(1)
-    yield uart.tx.eq(1)
+    print(type(tx))
+    #yield tx.oe.eq(1)
+    yield tx.eq(1)
     yield Delay(2*delay)
-    yield uart.tx.eq(0) # start bit
+    yield tx.eq(0) # start bit
     yield Delay(delay)
     read = 0
     # send one byte, lsb first
     for i in range(0, 8):
         bit = (byte >> i) & 0x1
-        yield uart.tx.eq(bit)
+        yield tx.eq(bit)
         yield Delay(delay)
-        test_bit = yield pad_o
+        test_bit = yield pad_tx.o
         read |= (test_bit << i)
-    yield uart.tx.eq(1) # stop bit
+    yield tx.eq(1) # stop bit
     yield Delay(delay)
     assert byte == read, f"UART Sent: %x | Pad Read: %x" % (byte, read)
     # Drive Pad i - check word at uart rx
-    yield pad_i.eq(1)
+    yield pad_rx.i.eq(1)
     yield Delay(2*delay)
-    yield pad_i.eq(0) # start bit
+    yield pad_rx.i.eq(0) # start bit
     yield Delay(delay)
     read2 = 0
     for i in range(0, 8):
         bit = (read >> i) & 0x1
-        yield pad_i.eq(bit)
+        yield pad_rx.i.eq(bit)
         yield Delay(delay)
-        test_bit = yield uart.rx
+        test_bit = yield rx
         read2 |= (test_bit << i)
-    yield pad_i.eq(1) # stop bit
+    yield pad_rx.i.eq(1) # stop bit
     yield Delay(delay)
     assert read == read2, f"Pad Sent: %x | UART Read: %x" % (read, read2)
 
@@ -277,25 +268,136 @@ def i2c_send(sda, scl, sda_pad, byte, delay=1e-6):
     yield sda.o.eq(1) # 'release' the SDA line
 
 # Test the GPIO/UART/I2C connectivity
-def test_man_pinmux(dut, pad_names):
+def test_man_pinmux(dut, requested):
+    # TODO: Convert to automatic
+    # [{"pad":%s, "bank":%d}, {"pad":%s, "bank":%d},...]
+    #gpios = [{"padname":"N1", "bank":GPIO_BANK},
+    #         {"padname":"N2", "bank":GPIO_BANK}]
+    # [[txPAD, MUXx, rxPAD, MUXx],...] - diff banks not supported yet
+    uarts = [{"txpadname":"N1", "rxpadname":"N2", "bank":UART_BANK}]
+    # [[sdaPAD, MUXx, sclPAD, MUXx],...] - diff banks not supported yet
+    i2cs = [{"sdapadname":"N1", "sclpadname":"N2", "bank":I2C_BANK}]
+
+    gpios = []
     delay = 1e-6
+    for pad in requested.keys():
+        for mux in requested[pad].keys():
+            periph = requested[pad][mux][0]
+
+            if periph == "gpio":
+                # [{"padname":%s, "bank": %d}, ...]
+                gpios.append({"padname":pad, "bank": int(mux[3])})
+            if periph == "uart":
+                # TODO:
+                pass
+            if periph == "i2c":
+                # TODO:
+                pass
+    print(gpios)
     # GPIO test
-    yield from set_bank(dut, GPIO_BANK)
-    yield from gpio(dut.pads["N1"]["mux0"], dut.pads["N1"]["pad"], 0x5a5)
-    yield from gpio(dut.pads["N2"]["mux0"], dut.pads["N2"]["pad"], 0x5a5)
+    for gpio_periph in gpios:
+        padname = gpio_periph["padname"]
+        gpio_bank = gpio_periph["bank"]
+        gp = dut.pads[padname]["mux%d" % gpio_bank]
+        pad = dut.pads[padname]["pad"]
+        yield from set_bank(dut, gpio_bank)
+        yield from gpio(gp, pad, 0x5a5)
+
     # UART test
-    #yield from set_bank(dut, UART_BANK)
-    #yield from uart_send(dut.uart, dut.pads['N1'].o, dut.pads['N2'].i, 0x42)
-    #yield dut.pads['N2'].i.eq(0)
-    #yield Delay(delay)
+    for uart_periph in uarts:
+        txpadname = uart_periph["txpadname"]
+        rxpadname = uart_periph["rxpadname"]
+        uart_bank = uart_periph["bank"]
+        tx = dut.pads[txpadname]["mux%d" % uart_bank]
+        rx = dut.pads[rxpadname]["mux%d" % uart_bank]
+        txpad = dut.pads[txpadname]["pad"]
+        rxpad = dut.pads[rxpadname]["pad"]
+        yield from set_bank(dut, UART_BANK)
+        yield from uart_send(tx, rx, txpad, rxpad, 0x42)
+
     # I2C test
-    #yield from set_bank(dut, I2C_BANK)
-    #yield from i2c_send(dut.i2c['sda'], dut.i2c['scl'], dut.pads['N1'], 0x67)
+    for i2c_periph in i2cs:
+        sdapadname = i2c_periph["sdapadname"]
+        sclpadname = i2c_periph["sclpadname"]
+        i2c_bank = i2c_periph["bank"]
+        sda = dut.pads[sdapadname]["mux%d" % i2c_bank]
+        scl = dut.pads[sclpadname]["mux%d" % i2c_bank]
+        sdapad = dut.pads[sdapadname]["pad"]
+
+    yield from set_bank(dut, I2C_BANK)
+    yield from i2c_send(sda, scl, sdapad, 0x67)
+
+def gen_gtkw_doc(module_name, requested, filename):
+    # GTKWave doc generation
+    style = {
+        '': {'base': 'hex'},
+        'in': {'color': 'orange'},
+        'out': {'color': 'yellow'},
+        'debug': {'module': 'top', 'color': 'red'}
+    }
+    # Create a trace list, each block expected to be a tuple()
+    traces = []
+    temp = 0
+    n_banks = 0
+    for pad in requested.keys():
+        temp = len(requested[pad].keys())
+        if n_banks < temp:
+            n_banks = temp
+        temp_traces = ("Pad %s" % pad, [])
+        # Pad signals
+        temp_traces[1].append(('%s__i' % pad, 'in'))
+        temp_traces[1].append(('%s__o' % pad, 'out'))
+        temp_traces[1].append(('%s__oe' % pad, 'out'))
+        for mux in requested[pad].keys():
+            periph = requested[pad][mux][0]
+            unit_num = requested[pad][mux][1]
+            if len(requested[pad][mux]) == 3:
+                pin = requested[pad][mux][2]
+            else:
+                pin = "io"
+
+            if periph == "gpio":
+                temp_traces[1].append(('gp%d__i' % unit_num, 'in'))
+                temp_traces[1].append(('gp%d__o' % unit_num, 'out'))
+                temp_traces[1].append(('gp%d__oe' % unit_num, 'out'))
+            elif periph == "uart":
+                if pin == "tx":
+                    temp_traces[1].append(('tx%d__o' % unit_num, 'out'))
+                    temp_traces[1].append(('tx%d__oe' % unit_num, 'out'))
+                    pass
+                elif pin == "rx":
+                    temp_traces[1].append(('rx%d' % unit_num, 'in'))
+                    pass
+            elif periph == "i2c":
+                temp_traces[1].append(('%s%d__i' % (pin, unit_num), 'in'))
+                temp_traces[1].append(('%s%d__o' % (pin, unit_num), 'out'))
+                temp_traces[1].append(('%s%d__oe' % (pin, unit_num), 'out'))
+        traces.append(temp_traces)
+
+    # master bank signal
+    temp_traces = ('Misc', [
+                    ('bank[%d:0]' % ((n_banks-1).bit_length()-1), 'in')
+                  ])
+    traces.append(temp_traces)
+
+    #print(traces)
+
+    write_gtkw(filename+".gtkw", filename+".vcd", traces, style,
+               module=module_name)
+
 
 def sim_man_pinmux():
     filename = "test_man_pinmux"
-    pad_names = ["N1", "N2"]
-    dut = ManPinmux(pad_names)
+    requested = {"N1": {"mux%d" % GPIO_BANK: ["gpio", 0, '0*'],
+                        "mux%d" % UART_BANK: ["uart", 0, 'tx+'],
+                        "mux%d" % I2C_BANK: ["i2c", 0, 'sda*']},
+                 "N2": {"mux%d" % GPIO_BANK: ["gpio", 1, '*'],
+                        "mux%d" % UART_BANK: ["uart", 0, 'rx-'],
+                        "mux%d" % I2C_BANK: ["i2c", 0, 'scl*']},
+                 "N3": {"mux%d" % GPIO_BANK: ["gpio", 2, '0*']},
+                 "N4": {"mux%d" % GPIO_BANK: ["gpio", 3, '0*']}
+                }
+    dut = ManPinmux(requested)
     vl = rtlil.convert(dut, ports=dut.ports())
     with open(filename+".il", "w") as f:
         f.write(vl)
@@ -305,11 +407,36 @@ def sim_man_pinmux():
 
     sim = Simulator(m)
 
-    sim.add_process(wrap(test_man_pinmux(dut, pad_names)))
+    sim.add_process(wrap(test_man_pinmux(dut, requested)))
     sim_writer = sim.write_vcd(filename+".vcd")
     with sim_writer:
         sim.run()
-    #gen_gtkw_doc("top.manpinmux", dut.n_banks, filename)
+    gen_gtkw_doc("top.manpinmux", dut.requested, filename)
 
 if __name__ == '__main__':
-    sim_man_pinmux()    
+    sim_man_pinmux()
+    #pinbanks = []
+    #fixedpins = []
+    #function_names = []
+    #testspec = PinSpec()
+    pinbanks = {
+        'A': (4, 4), # (num of pads, num of banks)?
+        #'B': (18, 4),
+        #'C': (24, 1),
+        #'D': (93, 1),
+    }
+    fixedpins = {
+        'POWER_GPIO': [
+            'VDD_GPIOB',
+            'GND_GPIOB',
+        ]}
+    function_names = {'TWI0': 'I2C 0',
+                      'UART0': 'UART (TX/RX) 0',
+                     }
+    ps = PinSpec(pinbanks, fixedpins, function_names)
+    ps.gpio("", ('A', 0), 0, 0, 4)
+    ps.uart("0", ('A', 0), 1)
+    ps.i2c("0", ('A', 0), 2)
+
+    print(dir(ps.gpio))
+    print(ps.gpio.pinouts, ps.gpio.bankspec, ps.gpio.pinfn, ps.gpio.fname)