add DMI interface to JTAG TAP
[c4m-jtag.git] / c4m / nmigen / jtag / tap.py
index 35e01606d478e90164b1a0138268b3cc3f4a95e6..acd1f4532d0f4c16b1c9b096813e6b4d42b6eb22 100755 (executable)
@@ -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