diff options
Diffstat (limited to 'tools/proxyclient/m1n1/gpiola.py')
| -rw-r--r-- | tools/proxyclient/m1n1/gpiola.py | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/tools/proxyclient/m1n1/gpiola.py b/tools/proxyclient/m1n1/gpiola.py new file mode 100644 index 0000000..bbfd71e --- /dev/null +++ b/tools/proxyclient/m1n1/gpiola.py @@ -0,0 +1,254 @@ +# SPDX-License-Identifier: MIT +import os, sys, struct, time + +from .utils import * +from . import asm +from .proxy import REGION_RX_EL1 +from .sysreg import * + +class GPIOLogicAnalyzer(Reloadable): + def __init__(self, u, node=None, pins={}, regs={}, div=1, cpu=1, on_pin_change=True, on_reg_change=True): + self.u = u + self.p = u.proxy + self.iface = u.iface + self.cpu = cpu + self.base = 0 + if node is not None: + self.base = u.adt[node].get_reg(0)[0] + else: + on_pin_change=False + self.node = node + self.pins = pins + self.regs = regs + assert len(pins) <= 32 + assert div > 0 + self.div = div + self.cbuf = self.u.malloc(0x1000) + self.dbuf = None + self.on_pin_change = on_pin_change + self.on_reg_change = on_reg_change + self.p.mmu_init_secondary(cpu) + self.tfreq = u.mrs(CNTFRQ_EL0) + + def load_regmap(self, regmap, skip=set(), regs=set()): + base = regmap._base + for name, (addr, rcls) in regmap._namemap.items(): + if name not in skip and (not regs or name in regs): + self.regs[name] = base + addr, rcls + + def start(self, ticks, bufsize=0x10000): + self.bufsize = bufsize + if self.dbuf: + self.u.free(self.dbuf) + self.dbuf = self.u.malloc(bufsize) + + text = f""" + trace: + mov x16, x2 + add x3, x3, x2 + add x2, x2, #4 + mov x12, #-8 + mov x10, x2 + mov x6, #-1 + mov x7, #0 + ldr x8, ={self.base} + mrs x4, CNTPCT_EL0 + isb + 1: + ldr w15, [x16] + cmp w15, #1 + b.eq done + add x4, x4, x1 + 2: + mrs x5, CNTPCT_EL0 + isb + """ + if self.div > 1: + text += f""" + cmp x5, x4 + b.lo 2b + """ + + for idx, pin in enumerate(self.pins.values()): + text += f""" + ldr w9, [x8, #{pin * 4}] + bfi x7, x9, #{idx}, #1 + """ + + if self.on_pin_change: + text += f""" + cmp x7, x6 + b.eq 3f + mov x6, x7 + """ + if self.on_reg_change: + text += f""" + mov x11, x2 + """ + + text += f""" + str w5, [x2], #4 + str w7, [x2], #4 + """ + if self.on_reg_change: + text += f""" + mov x13, #0 + add x14, x12, #8 + """ + + for reg in self.regs.values(): + if isinstance(reg, tuple): + reg = reg[0] + text += f""" + ldr x9, ={reg} + ldr w9, [x9] + str w9, [x2], #4 + """ + if self.on_reg_change: + text += f""" + eor w15, w9, #1 + cmp x14, #0 + b.eq 4f + ldr w15, [x14], #4 + 4: + eor w15, w15, w9 + orr w13, w13, w15 + """ + + if self.on_reg_change: + text += f""" + cmp x13, #0 + b.ne 4f + mov x2, x11 + mov x11, x12 + b 3f + 4: + """ + text += f""" + mov x12, x11 + cmp x2, x3 + b.hs done + 3: + sub x0, x0, #1 + cbnz x0, 1b + done: + sub x0, x2, x10 + ret + """ + + code = asm.ARMAsm(text, self.cbuf) + self.iface.writemem(self.cbuf, code.data) + self.p.dc_cvau(self.cbuf, len(code.data)) + self.p.ic_ivau(self.cbuf, len(code.data)) + + self.p.write32(self.dbuf, 0) + + self.p.smp_call(self.cpu, code.trace | REGION_RX_EL1, ticks, self.div, self.dbuf, bufsize - (8 + 4 * len(self.regs))) + + def complete(self): + self.p.write32(self.dbuf, 1) + wrote = self.p.smp_wait(self.cpu) + assert wrote <= self.bufsize + data = self.iface.readmem(self.dbuf + 4, wrote) + self.u.free(self.dbuf) + self.dbuf = None + + stride = 2 + len(self.regs) + + #chexdump(data) + + self.data = [struct.unpack("<" + "I" * stride, + data[i:i + 4 * stride]) + for i in range(0, len(data), 4 * stride)] + + def vcd(self): + off = self.data[0][0] + if False: #len(self.data) > 1: + off2 = max(0, ((self.data[1][0] - off) & 0xffffffff) - 5000) + else: + off2 = 0 + + #print(off, off2) + + vcd = [] + vcd.append(""" +$timescale 1ns $end +$scope module gpio $end +""") + sym = 0 + keys = [] + rkeys = [] + + for name in self.pins: + keys.append(f"s{sym}") + vcd.append(f"$var wire 1 s{sym} {name} $end\n") + sym += 1 + for name, reg in self.regs.items(): + vcd.append(f"$var reg 32 s{sym} {name} [31:0] $end\n") + if isinstance(reg, tuple): + subkeys = {} + rcls = reg[1] + rkeys.append((f"s{sym}", rcls, subkeys)) + sym += 1 + for fname in rcls().fields.keys(): + fdef = getattr(rcls, fname) + if isinstance(fdef, tuple): + width = fdef[0] - fdef[1] + 1 + else: + width = 1 + vcd.append(f"$var reg {width} s{sym} {name}.{fname} [{width-1}:0] $end\n") + subkeys[fname] = (width, f"s{sym}") + sym += 1 + else: + rkeys.append((f"s{sym}", None, None)) + sym += 1 + vcd.append(""" +$enddefinitions $end +$dumpvars +""") + + for v in self.data: + ts = v[0] + val = v[1] + regs = v[2:] + ts = ((ts - off) & 0xffffffff) - off2 + ns = max(0, 1000000000 * ts // self.tfreq) + vcd.append(f"#{ns}\n") + vcd.append("\n".join(f"{(val>>i) & 1}{k}" for i, k in enumerate(keys)) + "\n") + for (key, rcls, subkeys), v in zip(rkeys, regs): + vcd.append(f"b{v:032b} {key}\n") + if rcls: + rval = rcls(v) + for field, (width, key) in subkeys.items(): + v = getattr(rval, field) + vcd.append(f"b{v:0{width}b} {key}\n") + + + ns += ns//10 + vcd.append(f"#{ns}\n" + "\n".join(f"{(val>>i) & 1}{k}" for i, k in enumerate(keys)) + "\n") + + return "".join(vcd) + + def show(self): + with open("/tmp/dump.vcd", "w") as fd: + fd.write(self.vcd()) + + gtkw = (""" +[dumpfile] "/tmp/dump.vcd" +[timestart] 0 +[size] 3063 1418 +[pos] -1 -1 +*-17.000000 2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[sst_width] 288 +[signals_width] 197 +[sst_expanded] 1 +[sst_vpaned_height] 421 +@23 +""" + + "\n".join("gpio." + k for k in self.pins) + "\n" + + "\n".join("gpio." + k + "[31:0]" for k in self.regs) + "\n") + + with open("/tmp/dump.gtkw", "w") as fd: + fd.write(gtkw) + + os.system("gtkwave /tmp/dump.gtkw&") |
