feat(stage2.py): Converted iomux pad/perith connections to for-loop
[pinmux.git] / src / spec / stage2.py
index 4855f0b86b600831f211426b92f74beed3e08a4c..635ec6812e6d738cf55a551bb745175729e691d2 100644 (file)
@@ -29,79 +29,120 @@ uart_layout = (("rx", 1),
                ("tx", 1),
                ("oe", 1)
               )
-
-UART_BANK = 0
-I2C_BANK = 1
+uart_tx_layout = (("o", 1),
+                 ("oe", 1)
+                 )
+GPIO_BANK = 0
+UART_BANK = 1
+I2C_BANK = 2
 
 """
 Really basic example, uart tx/rx and i2c sda/scl pinmux
 """
 class ManPinmux(Elaboratable):
-    def __init__(self):
+    def __init__(self, pad_names):
         print("Test Manual Pinmux!")
-        self.n_banks = 2
-        self.iomux1 = IOMuxBlockSingle(self.n_banks)
-        self.iomux2 = IOMuxBlockSingle(self.n_banks)
-        self.pad1 = Record(name="Pad1", layout=io_layout)
-        self.pad2 = Record(name="Pad2", 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.requested = {"N1": {"mux%d" % GPIO_BANK: ["gpio", 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"]}
+                         }
+        self.n_banks = 4
         self.bank = Signal(log2_int(self.n_banks))
+        self.pads = {}
+        self.muxes = {}
+        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_num = self.requested[pad][mux][1]
+                if len(self.requested[pad][mux]) == 3:
+                    pin = self.requested[pad][mux][2]
+                else:
+                    pin = "io"
+                if periph == "gpio":
+                    self.pads[pad][mux] = Record(name="gp%d" % unit_num,
+                                                 layout=io_layout)
+                elif periph == "uart":
+                    if pin == "tx":
+                        self.pads[pad][mux] = Record(name="tx%d" % unit_num,
+                                                     layout=uart_tx_layout)
+                    elif pin == "rx":
+                        self.pads[pad][mux] = Signal(name="rx%d" % unit_num)
+                elif periph == "i2c":
+                    if pin == "sda":
+                        self.pads[pad][mux] = Record(name="sda%d" % unit_num,
+                                                     layout=io_layout)
+                    elif pin == "scl":
+                        self.pads[pad][mux] = Record(name="scl%d" % unit_num,
+                                                     layout=io_layout)
 
     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
-
-        pad1 = self.pad1
-        pad2 = self.pad2
-        uart = self.uart
-        i2c = self.i2c
+        muxes = self.muxes
         bank = self.bank
+        # TODO: replace with pin specific
+        iomux1 = muxes["N1"]
+        iomux2 = muxes["N2"]
+        for pad in self.pads.keys():
+            m.submodules[pad+"_mux"] = muxes[pad]
+            # all muxes controlled by the same multi-bit signal
+            comb += muxes[pad].bank.eq(bank)
+        pads = self.pads
+        # print(self.requested)
+        # print(self.pads)
 
-        comb += iomux1.bank.eq(bank)
-        comb += iomux2.bank.eq(bank)
-
-        # uart connected to bank 0 - 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 connected to bank 1 - 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)
-
-        comb += pad1.o.eq(iomux1.out_port.o)
-        comb += pad1.oe.eq(iomux1.out_port.oe)
-        comb += iomux1.out_port.i.eq(pad1.i)
-        comb += pad2.o.eq(iomux2.out_port.o)
-        comb += pad2.oe.eq(iomux2.out_port.oe)
-        comb += iomux2.out_port.i.eq(pad2.i)
-
-        #temp for testing - connect pad rx-tx
-        #comb += pad2.i.eq(pad1.o)
+        # ---------------------------
+        # This section connects the periphs to the assigned banks
+        # ---------------------------
+        for pad in self.pads.keys():
+            for mux in self.requested[pad].keys():
+                periph = self.requested[pad][mux][0]
+                num = int(mux[3])
+                if len(self.requested[pad][mux]) == 3:
+                    pin = self.requested[pad][mux][2]
+                else:
+                    pin = "io"
+                if periph == "gpio" or periph == "i2c":
+                    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 periph == "uart":
+                    if pin == "tx":
+                        comb += muxes[pad].bank_ports[num].o.eq(
+                                                           pads[pad][mux].o)
+                        comb += muxes[pad].bank_ports[num].oe.eq(
+                                                           pads[pad][mux].oe)
+                    elif pin == "rx":
+                        comb += pads[pad][mux].eq(muxes[pad].bank_ports[num].i)
+
+        # ---------------------------
+        # Here is where the muxes are assigned to the actual pads
+        # ---------------------------
+        for pad in self.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
 
     def __iter__(self):
-        for field in self.pad1.fields.values():
-            yield field
-        for field in self.pad2.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 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
         yield self.bank
 
     def ports(self):
@@ -111,40 +152,167 @@ def set_bank(dut, bank, delay=1e-6):
     yield dut.bank.eq(bank)
     yield Delay(delay)
 
-def uart_send(tx, rx, byte, oe=None, delay=1e-6):
-    if oe is not None:
-        yield oe.eq(1)
-    yield tx.eq(1)
+"""
+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(tx, rx, pad_tx, pad_rx, byte, delay=1e-6):
+    # Drive uart tx - check the word seen at the Pad
+    yield tx.oe.eq(1)
+    yield tx.o.eq(1)
     yield Delay(2*delay)
-    yield tx.eq(0) # start bit
+    yield tx.o.eq(0) # start bit
     yield Delay(delay)
-    result = 0
+    read = 0
     # send one byte, lsb first
     for i in range(0, 8):
         bit = (byte >> i) & 0x1
-        yield tx.eq(bit)
+        yield tx.o.eq(bit)
+        yield Delay(delay)
+        test_bit = yield pad_tx.o
+        read |= (test_bit << i)
+    yield tx.o.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_rx.i.eq(1)
+    yield Delay(2*delay)
+    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_rx.i.eq(bit)
         yield Delay(delay)
         test_bit = yield rx
-        result |= (test_bit << i)
-    yield tx.eq(1) # stop bit
+        read2 |= (test_bit << i)
+    yield pad_rx.i.eq(1) # stop bit
     yield Delay(delay)
-    if result == byte:
-        print("Received: %x | Sent: %x" % (byte, result))
-    else:
-        print("Received: %x does NOT match sent: %x" % (byte, result))
+    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
 
-def test_man_pinmux(dut):
+# 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.pads["N1"]["mux%d" % GPIO_BANK],
+                    dut.pads["N1"]["pad"], 0x5a5)
+    yield from gpio(dut.pads["N2"]["mux%d" % GPIO_BANK],
+                    dut.pads["N2"]["pad"], 0x5a5)
+    # UART test
     yield from set_bank(dut, UART_BANK)
-    yield from uart_send(dut.uart.tx, dut.pad1.o, 0x42, oe=dut.uart.oe)
-    yield dut.pad1.i.eq(1)
-    yield from uart_send(dut.pad2.i, dut.uart.rx, 0x5A)
-
+    yield from uart_send(dut.pads["N1"]["mux%d" % UART_BANK],
+                         dut.pads["N2"]["mux%d" % UART_BANK],
+                         dut.pads['N1']["pad"], dut.pads['N2']["pad"], 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.pads["N1"]["mux%d" % I2C_BANK],
+                        dut.pads["N2"]["mux%d" % I2C_BANK],
+                        dut.pads['N1']["pad"], 0x67)
 
 def sim_man_pinmux():
     filename = "test_man_pinmux"
-    dut = ManPinmux()
+    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)
@@ -154,7 +322,7 @@ def sim_man_pinmux():
 
     sim = Simulator(m)
 
-    sim.add_process(wrap(test_man_pinmux(dut)))
+    sim.add_process(wrap(test_man_pinmux(dut, pad_names)))
     sim_writer = sim.write_vcd(filename+".vcd")
     with sim_writer:
         sim.run()