From: Andrey Miroshnikov Date: Tue, 27 Sep 2022 19:47:06 +0000 (+0000) Subject: feat(stage2): Moved up a directory to work with the PinSpec imports X-Git-Url: https://git.libre-soc.org/?p=pinmux.git;a=commitdiff_plain;h=b42bd1363750d42fc0f6f0e4b4b51a44fc01a0fb feat(stage2): Moved up a directory to work with the PinSpec imports --- diff --git a/src/spec/stage2.py b/src/spec/stage2.py deleted file mode 100644 index 2219616..0000000 --- a/src/spec/stage2.py +++ /dev/null @@ -1,478 +0,0 @@ -#!/usr/bin/env python3 -""" -pinmux documented here https://libre-soc.org/docs/pinmux/ -""" -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 nmutil.util import wrap -#from soc.bus.test.wb_rw import wb_read, wb_write - -from nmutil.gtkw import write_gtkw - -cxxsim = False -if cxxsim: - from nmigen.sim.cxxsim import Simulator, Settle, Delay -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), - ("o", 1) - ) - -uart_layout = (("rx", 1), - ("tx", 1), - ("oe", 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, requested): - print("Test Manual Pinmux!") - - self.requested = requested - self.n_banks = 4 - self.bank = Signal(log2_int(self.n_banks)) - 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 - 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) - - # print(self.requested) - # print(self.pads) - - # --------------------------- - # This section connects the periphs to the assigned banks - # --------------------------- - 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 - # --------------------------- - 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 - - def __iter__(self): - for pad in list(self.pads.keys()): - for field in self.pads[pad]["pad"].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): - 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(tx, rx, pad_tx, pad_rx, byte, delay=1e-6): - # Drive uart tx - check the word seen at the Pad - print(type(tx)) - #yield tx.oe.eq(1) - yield tx.eq(1) - yield Delay(2*delay) - 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 tx.eq(bit) - yield Delay(delay) - test_bit = yield pad_tx.o - read |= (test_bit << i) - 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_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 - read2 |= (test_bit << i) - yield pad_rx.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, 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 - 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 - 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 - 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" - 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) - - m = Module() - m.submodules.manpinmux = dut - - sim = Simulator(m) - - 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.requested, filename) - -if __name__ == '__main__': - 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) - # Unit number, (Bank, pin #), mux, start, num # pins - 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) - """ - desc_dict_keys = ['UART0', 'TWI0', 'GPIOA_A0', 'GPIOA_A1', 'GPIOA_A2', 'GPIOA_A3'] - eint = [] - pwm = [] - desc = {'UART0': 'Basic serial TX/RX serial port', - 'TWI0': 'I2C interface', - 'GPIOA_A0': 'Test GPIO0', - 'GPIOA_A1': 'Test GPIO1', - 'GPIOA_A2': 'Test GPIO2', - 'GPIOA_A3': 'Test GPIO3'} - ps.add_scenario("Test Manual Pinmux", desc_dict_keys, eint, pwm, desc) - """ - print("---------------------------------") - #with open("test.mdwn", "w") as of: - # pinout, bankspec, pin_spec, fixedpins = ps.write(of) - #print("---------------------------------") - - bk = ps.pinbanks.keys() - for bank in bk: - muxwidth = ps.muxwidths[bank] - print(bank, muxwidth) - pinidx = sorted(ps.keys()) - for pin in pinidx: - print("---------------------------------") - print(pin) - pdata = ps.get(pin) - for mux in range(muxwidth): - if mux not in pdata: - print("Mux %d : NC" % mux) - else: - name, assigned_bank = pdata[mux] - print("Mux %d : %s" % (mux, name)) - - - diff --git a/src/stage2.py b/src/stage2.py new file mode 100644 index 0000000..1d9374f --- /dev/null +++ b/src/stage2.py @@ -0,0 +1,478 @@ +#!/usr/bin/env python3 +""" +pinmux documented here https://libre-soc.org/docs/pinmux/ +""" +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 nmutil.util import wrap +#from soc.bus.test.wb_rw import wb_read, wb_write + +from nmutil.gtkw import write_gtkw + +cxxsim = False +if cxxsim: + from nmigen.sim.cxxsim import Simulator, Settle, Delay +else: + from nmigen.sim import Simulator, Settle, Delay + +from spec.iomux import IOMuxBlockSingle +from spec.base import PinSpec +from spec.jtag import iotypes + +io_layout = (("i", 1), + ("oe", 1), + ("o", 1) + ) + +uart_layout = (("rx", 1), + ("tx", 1), + ("oe", 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, requested): + print("Test Manual Pinmux!") + + self.requested = requested + self.n_banks = 4 + self.bank = Signal(log2_int(self.n_banks)) + 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 + 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) + + # print(self.requested) + # print(self.pads) + + # --------------------------- + # This section connects the periphs to the assigned banks + # --------------------------- + 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 + # --------------------------- + 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 + + def __iter__(self): + for pad in list(self.pads.keys()): + for field in self.pads[pad]["pad"].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): + 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(tx, rx, pad_tx, pad_rx, byte, delay=1e-6): + # Drive uart tx - check the word seen at the Pad + print(type(tx)) + #yield tx.oe.eq(1) + yield tx.eq(1) + yield Delay(2*delay) + 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 tx.eq(bit) + yield Delay(delay) + test_bit = yield pad_tx.o + read |= (test_bit << i) + 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_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 + read2 |= (test_bit << i) + yield pad_rx.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, 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 + 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 + 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 + 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" + 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) + + m = Module() + m.submodules.manpinmux = dut + + sim = Simulator(m) + + 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.requested, filename) + +if __name__ == '__main__': + 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) + # Unit number, (Bank, pin #), mux, start, num # pins + 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) + """ + desc_dict_keys = ['UART0', 'TWI0', 'GPIOA_A0', 'GPIOA_A1', 'GPIOA_A2', 'GPIOA_A3'] + eint = [] + pwm = [] + desc = {'UART0': 'Basic serial TX/RX serial port', + 'TWI0': 'I2C interface', + 'GPIOA_A0': 'Test GPIO0', + 'GPIOA_A1': 'Test GPIO1', + 'GPIOA_A2': 'Test GPIO2', + 'GPIOA_A3': 'Test GPIO3'} + ps.add_scenario("Test Manual Pinmux", desc_dict_keys, eint, pwm, desc) + """ + print("---------------------------------") + #with open("test.mdwn", "w") as of: + # pinout, bankspec, pin_spec, fixedpins = ps.write(of) + #print("---------------------------------") + + bk = ps.pinbanks.keys() + for bank in bk: + muxwidth = ps.muxwidths[bank] + print(bank, muxwidth) + pinidx = sorted(ps.keys()) + for pin in pinidx: + print("---------------------------------") + print(pin) + pdata = ps.get(pin) + for mux in range(muxwidth): + if mux not in pdata: + print("Mux %d : NC" % mux) + else: + name, assigned_bank = pdata[mux] + print("Mux %d : %s" % (mux, name)) + + +