feat: I2C demo test now loopback
[pinmux.git] / src / spec / stage2.py
index d8f840087034f84acc7507fc8019bf688971ea33..2d809e7fce1fc60166071b2d68f617314466cf79 100644 (file)
 """
 pinmux documented here https://libre-soc.org/docs/pinmux/
 """
-from nmigen.build.dsl import Resource, Subsignal, Pins
-from nmigen.build.plat import TemplatedPlatform
-from nmigen.build.res import ResourceManager, ResourceError
+from nmigen import Elaboratable, Module, Signal, Record, Array, Cat
 from nmigen.hdl.rec import Layout
-from nmigen import Elaboratable, Signal, Module, Instance
-from collections import OrderedDict
-from jtag import JTAG, resiotypes, iotypes, scanlens
-from copy import deepcopy
+from nmigen.utils import log2_int
 from nmigen.cli import rtlil
-import sys
+from soc.minerva.wishbone import make_wb_layout
+from nmutil.util import wrap
+#from soc.bus.test.wb_rw import wb_read, wb_write
 
-# extra dependencies for jtag testing (?)
-#from soc.bus.sram import SRAM
+from nmutil.gtkw import write_gtkw
 
-#from nmigen import Memory
-from nmigen.sim import Simulator, Delay, Settle, Tick, Passive
+cxxsim = False
+if cxxsim:
+    from nmigen.sim.cxxsim import Simulator, Settle, Delay
+else:
+    from nmigen.sim import Simulator, Settle, Delay
 
-from nmutil.util import wrap
+from iomux import IOMuxBlockSingle
 
-from nmutil.gtkw import write_gtkw
+io_layout = (("i", 1),
+             ("oe", 1),
+             ("o", 1)
+            )
 
-# from soc.debug.jtagutils import (jtag_read_write_reg,
-#                                 jtag_srv, jtag_set_reset,
-#                                 jtag_set_ir, jtag_set_get_dr)
-
-from soc.debug.test.test_jtag_tap import (jtag_read_write_reg,
-                                          jtag_set_reset,
-                                          jtag_set_shift_ir,
-                                          jtag_set_shift_dr,
-                                          jtag_set_run,
-                                          jtag_set_idle,
-                                          tms_data_getset)
-
-def dummy_pinset():
-    # sigh this needs to come from pinmux.
-    gpios = []
-    for i in range(4):
-        gpios.append("%d*0" % i) # gpios to mux 0
-    return {'uart': ['tx+1', 'rx-1'],
-            'gpio': gpios,
-            # 'jtag': ['tms-', 'tdi-', 'tdo+', 'tck+'],
-            'i2c': ['sda*2', 'scl+2']}
+uart_layout = (("rx", 1),
+               ("tx", 1),
+               ("oe", 1)
+              )
 
+GPIO_BANK = 0
+UART_BANK = 1
+I2C_BANK = 2
 
 """
-a function is needed which turns the results of dummy_pinset()
-into:
-
-[UARTResource("uart", 0, tx=..., rx=..),
- I2CResource("i2c", 0, scl=..., sda=...),
- Resource("gpio", 0, Subsignal("i"...), Subsignal("o"...)
- Resource("gpio", 1, Subsignal("i"...), Subsignal("o"...)
- ...
-]
+Really basic example, uart tx/rx and i2c sda/scl pinmux
 """
-# TODO: move to suitable location
-class Pins:
-    """declare a list of pins, including name and direction.  grouped by fn
-    the pin dictionary needs to be in a reliable order so that the JTAG
-    Boundary Scan is also in a reliable order
-    """
-    def __init__(self, pindict=None):
-        if pindict is None:
-            pindict = {}
-        self.io_names = OrderedDict()
-        if isinstance(pindict, OrderedDict):
-            self.io_names.update(pindict)
-        else:
-            keys = list(pindict.keys())
-            keys.sort()
-            for k in keys:
-                self.io_names[k] = pindict[k]
+class ManPinmux(Elaboratable):
+    def __init__(self, pad_names):
+        print("Test Manual Pinmux!")
+        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))
+
+    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
+        gpio = self.gpio
+        uart = self.uart
+        i2c = self.i2c
+        bank = self.bank
+
+        comb += iomux1.bank.eq(bank)
+        comb += iomux2.bank.eq(bank)
+
+        # ---------------------------
+        # This section is muxing only - doesn'care about pad names
+        # ---------------------------
+        # gpio - gpio0 on Pad1, gpio1 on Pad2
+        comb += iomux1.bank_ports[GPIO_BANK].o.eq(gpio["0"].o)
+        comb += iomux1.bank_ports[GPIO_BANK].oe.eq(gpio["0"].oe)
+        comb += gpio["0"].i.eq(iomux1.bank_ports[GPIO_BANK].i)
+        comb += iomux2.bank_ports[GPIO_BANK].o.eq(gpio["1"].o)
+        comb += iomux2.bank_ports[GPIO_BANK].oe.eq(gpio["1"].oe)
+        comb += gpio["1"].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)
+
+        # ---------------------------
+        # 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'].o.eq(iomux1.out_port.o)
+        comb += pads['N1'].oe.eq(iomux1.out_port.oe)
+        comb += iomux1.out_port.i.eq(pads['N1'].i)
+        comb += pads['N2'].o.eq(iomux2.out_port.o)
+        comb += pads['N2'].oe.eq(iomux2.out_port.oe)
+        comb += iomux2.out_port.i.eq(pads['N2'].i)
+
+        #temp for testing - connect pad rx-tx
+        #comb += pad2.i.eq(pad1.o)
+
+        return m
 
     def __iter__(self):
-        # start parsing io_names and enumerate them to return pin specs
-        scan_idx = 0
-        for fn, pins in self.io_names.items():
-            for pin in pins:
-                # decode the pin name and determine the c4m jtag io type
-                name, pin_type, bank = pin[:-2], pin[-2], pin[-1]
-                iotype = iotypes[pin_type]
-                pin_name = "%s_%s" % (fn, name)
-                yield (fn, name, iotype, pin_name, scan_idx, bank)
-                scan_idx += scanlens[iotype] # inc boundary reg scan offset
+        for pad in list(self.pads.keys()):
+            for field in self.pads[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
+        yield self.bank
+
+    def ports(self):
+        return list(self)
+
+def set_bank(dut, bank, delay=1e-6):
+    yield dut.bank.eq(bank)
+    yield Delay(delay)
+
+"""
+GPIO test function
+Set the gpio output based on given data sequence, checked at pad.o
+Then sends the same byte via pad.i to gpio input
+"""
+def gpio(gpio, pad, data, delay=1e-6):
+    # Output test - Control GPIO output
+    yield gpio.oe.eq(1)
+    yield Delay(delay)
+    n_bits = len(bin(data)[2:])
+    read = 0
+    for i in range(0, n_bits):
+        bit = (data >> i) & 0x1
+        yield gpio.o.eq(bit)
+        yield Delay(delay)
+        temp = yield pad.o
+        read |= (temp << i)
+    assert data == read, f"GPIO Sent: %x | Pad Read: %x" % (data, read)
+    # Input test - Control Pad input
+    yield gpio.oe.eq(0)
+    yield Delay(delay)
+    read2 = 0
+    for i in range(0, n_bits):
+        bit = (read >> i) & 0x1
+        yield pad.i.eq(bit)
+        yield Delay(delay)
+        temp = yield gpio.i
+        read2 |= (temp << i)
+    assert read2 == read, f"Pad Sent: %x | GPIO Read: %x" % (data, read)
+    # reset input signal
+    yield pad.i.eq(0)
+    yield Delay(delay)
+
+"""
+UART test function
+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):
+    # Drive uart tx - check the word seen at the Pad
+    yield uart.oe.eq(1)
+    yield uart.tx.eq(1)
+    yield Delay(2*delay)
+    yield uart.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 Delay(delay)
+        test_bit = yield pad_o
+        read |= (test_bit << i)
+    yield uart.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 Delay(2*delay)
+    yield pad_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 Delay(delay)
+        test_bit = yield uart.rx
+        read2 |= (test_bit << i)
+    yield pad_i.eq(1) # stop bit
+    yield Delay(delay)
+    assert read == read2, f"Pad Sent: %x | UART Read: %x" % (read, read2)
+
+"""
+I2C test function
+Sends a byte via SDA.o (peripheral side), checked at output pad
+Then sends the same byte via input pad to master SDA.i
+This transaction doesn't make the distinction between read/write bit.
+"""
+def i2c_send(sda, scl, sda_pad, byte, delay=1e-6):
+    # No checking yet
+    # No pull-up on line implemented, set high instead
+    yield sda.oe.eq(1)
+    yield sda.o.eq(1)
+    yield scl.oe.eq(1)
+    yield scl.o.eq(1)
+    yield sda_pad.i.eq(1)
+    yield Delay(delay)
+    read = 0
+    yield sda.o.eq(0) # start bit
+    yield Delay(delay)
+    for i in range(0, 8):
+        bit = (byte >> i) & 0x1
+        yield sda.o.eq(bit)
+        yield scl.o.eq(0)
+        yield Delay(delay/2)
+        yield scl.o.eq(1)
+        temp = yield sda_pad.o
+        read |= (temp << i)
+        yield Delay(delay/2)
+    yield sda.o.eq(1) # Master releases SDA line
+    yield sda.oe.eq(0)
+    assert byte == read, f"I2C Sent: %x | Pad Read: %x" % (byte, read)
+    # Slave ACK
+    yield sda_pad.i.eq(0)
+    yield scl.o.eq(0)
+    yield Delay(delay/2)
+    yield scl.o.eq(1)
+    yield Delay(delay/2)
+    # Send byte back to master
+    read2 = 0
+    for i in range(0, 8):
+        bit = (read >> i) & 0x1
+        yield sda_pad.i.eq(bit)
+        yield scl.o.eq(0)
+        yield Delay(delay/2)
+        yield scl.o.eq(1)
+        temp = yield sda.i
+        read2 |= (temp << i)
+        yield Delay(delay/2)
+    assert read == read2, f"Pad Sent: %x | I2C Read: %x" % (read, read2)
+    # Master ACK
+    yield sda.oe.eq(1)
+    yield sda.o.eq(0)
+    yield scl.o.eq(0)
+    yield Delay(delay/2)
+    yield scl.o.eq(1)
+    yield Delay(delay/2)
+    # Stop condition - SDA line high after SCL high
+    yield scl.o.eq(0)
+    yield Delay(delay/2)
+    yield scl.o.eq(1)
+    yield Delay(delay/2)
+    yield sda.o.eq(1) # 'release' the SDA line
+
+# Test the GPIO/UART/I2C connectivity
+def test_man_pinmux(dut, pad_names):
+    delay = 1e-6
+    # GPIO test
+    yield from set_bank(dut, GPIO_BANK)
+    yield from gpio(dut.gpio['0'], dut.pads['N1'], 0x5a5)
+    yield from gpio(dut.gpio['1'], dut.pads['N2'], 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)
+    # I2C test
+    yield from set_bank(dut, I2C_BANK)
+    yield from i2c_send(dut.i2c['sda'], dut.i2c['scl'], dut.pads['N1'], 0x67)
+
+    yield dut.gpio['0'].oe.eq(1)
+    yield Delay(delay)
+    yield dut.gpio['0'].oe.eq(0)
+    yield Delay(delay)
+
+def sim_man_pinmux():
+    filename = "test_man_pinmux"
+    pad_names = ["N1", "N2"]
+    dut = ManPinmux(pad_names)
+    vl = rtlil.convert(dut, ports=dut.ports())
+    with open(filename+".il", "w") as f:
+        f.write(vl)
+
+    m = Module()
+    m.submodules.manpinmux = dut
+
+    sim = Simulator(m)
 
+    sim.add_process(wrap(test_man_pinmux(dut, pad_names)))
+    sim_writer = sim.write_vcd(filename+".vcd")
+    with sim_writer:
+        sim.run()
+    #gen_gtkw_doc("top.manpinmux", dut.n_banks, filename)
 
 if __name__ == '__main__':
-    #pname = "test"
-    pinset = dummy_pinset()
-    #print()
-    #pin_test = Pins(Pins(pname+"_oe", dir="o", assert_width=1))
-    pin_test = Pins(pinset)
-    print(dir(pin_test))
-    print(pin_test.io_names)
-    for fn, name, iotype, pin_name, scan_idx, bank in pin_test:
-        print(fn, name, iotype, pin_name, scan_idx, "Bank %s" % bank)
+    sim_man_pinmux()