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.
from nmigen_soc.wishbone import Interface as WishboneInterface
-from .bus import Interface
+from .bus import Interface, DMIInterface
__all__ = [
"TAP", "ShiftReg", "IOType", "IOConn",
self._ios = []
self._srs = []
self._wbs = []
+ self._dmis = []
def elaborate(self, platform):
m = Module()
# 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