From 48817a386839c50c8b4f7abc30611ad0e72d9526 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Fri, 9 Oct 2020 13:54:19 +0100 Subject: [PATCH] add DMI interface to JTAG TAP --- c4m/nmigen/jtag/bus.py | 14 +++++++ c4m/nmigen/jtag/tap.py | 95 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/c4m/nmigen/jtag/bus.py b/c4m/nmigen/jtag/bus.py index c6015a7..d1532bc 100644 --- a/c4m/nmigen/jtag/bus.py +++ b/c4m/nmigen/jtag/bus.py @@ -1,6 +1,20 @@ from nmigen import * from nmigen.hdl.rec import Direction + +class DMIInterface(Record): + def __init__(self, name=None, addr_wid=4, data_wid=64): + layout = [ + ('addr_i', addr_wid, Direction.FANIN), # DMI register address + ('din', data_wid, Direction.FANIN), # DMI data write in (we=1) + ('dout', data_wid, Direction.FANOUT), # DMI data read out (we=0) + ('req_i', 1, Direction.FANIN), # DMI request valid (stb) + ('we_i', 1, Direction.FANIN), # DMI write-enable + ('ack_o', 1, Direction.FANOUT), # DMI ack request + ] + super().__init__(name=name, layout=layout) + + class Interface(Record): """JTAG Interface. diff --git a/c4m/nmigen/jtag/tap.py b/c4m/nmigen/jtag/tap.py index 35e0160..acd1f45 100755 --- a/c4m/nmigen/jtag/tap.py +++ b/c4m/nmigen/jtag/tap.py @@ -9,7 +9,7 @@ from nmigen.tracer import get_var_name from nmigen_soc.wishbone import Interface as WishboneInterface -from .bus import Interface +from .bus import Interface, DMIInterface __all__ = [ "TAP", "ShiftReg", "IOType", "IOConn", @@ -328,6 +328,7 @@ class TAP(Elaboratable): self._ios = [] self._srs = [] self._wbs = [] + self._dmis = [] def elaborate(self, platform): m = Module() @@ -417,8 +418,100 @@ class TAP(Elaboratable): # wishbone self._elaborate_wishbones(m) + # DMI (Debug Memory Interface) + self._elaborate_dmis(m) + return m + def add_dmi(self, *, ircodes, address_width=8, data_width=64, + domain="sync", name=None): + """Add a DMI interface + + * writing to DMIADDR will automatically trigger a DMI READ. + the DMI address does not alter (so writes can be done at that addr) + * reading from DMIREAD triggers a DMI READ at the current DMI addr + the address is automatically incremented by 1 after. + * writing to DMIWRITE triggers a DMI WRITE at the current DMI addr + the address is automatically incremented by 1 after. + + Parameters: + ----------- + ircodes: sequence of three integer for the JTAG IR codes; + they represent resp. DMIADDR, DMIREAD and DMIWRITE. + First code has a shift register of length 'address_width', + the two other codes share a shift register of length + data_width. + + address_width: width of the address + data_width: width of the data + + Returns: + dmi: soc.debug.dmi.DMIInterface + The DMI interface + """ + if len(ircodes) != 3: + raise ValueError("3 IR Codes have to be provided") + + if name is None: + name = "dmi" + str(len(self._dmis)) + + # add 2 shift registers: one for addr, one for data. + sr_addr = self.add_shiftreg(ircode=ircodes[0], length=address_width, + domain=domain, name=name+"_addrsr") + sr_data = self.add_shiftreg(ircode=ircodes[1:], length=data_width, + domain=domain, name=name+"_datasr") + + dmi = DMIInterface(name=name) + self._dmis.append((sr_addr, sr_data, dmi, domain)) + + return dmi + + def _elaborate_dmis(self, m): + for sr_addr, sr_data, dmi, domain in self._dmis: + cd = m.d[domain] + m.d.comb += sr_addr.i.eq(dmi.addr_i) + + with m.FSM(domain=domain) as ds: + + # detect mode based on whether jtag addr or data read/written + with m.State("IDLE"): + with m.If(sr_addr.oe): # DMIADDR code + cd += dmi.addr_i.eq(sr_addr.o) + m.next = "READ" + with m.Elif(sr_data.oe[0]): # DMIREAD code + # If data is + cd += dmi.addr_i.eq(dmi.addr_i + 1) + m.next = "READ" + with m.Elif(sr_data.oe[1]): # DMIWRITE code + cd += dmi.din.eq(sr_data.o) + m.next = "WRRD" + + # req_i raises for 1 clock + with m.State("READ"): + m.next = "READACK" + + # wait for read ack + with m.State("READACK"): + with m.If(dmi.ack_o): + # Store read data in sr_data.i hold till next read + cd += sr_data.i.eq(dmi.dout) + m.next = "IDLE" + + # req_i raises for 1 clock + with m.State("WRRD"): + m.next = "WRRDACK" + + # wait for write ack + with m.State("WRRDACK"): + with m.If(dmi.ack_o): + cd += dmi.addr_i.eq(dmi.addr_i + 1) + m.next = "READ" # for readwrite + + # set DMI req and write-enable based on ongoing FSM states + m.d.comb += [ + dmi.req_i.eq(ds.ongoing("READ") | ds.ongoing("WRRD")), + dmi.we_i.eq(ds.ongoing("WRRD")), + ] def add_io(self, *, iotype, name=None, src_loc_at=0): """Add a io cell to the boundary scan chain -- 2.30.2