import os, textwrap
from enum import Enum, auto
-from nmigen import *
-from nmigen.build import *
-from nmigen.lib.io import *
+from nmigen import (Elaboratable, Signal, Module, ClockDomain, Cat, Record,
+ Const, Mux)
from nmigen.hdl.rec import Direction, Layout
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",
return m
+
class _IRBlock(Elaboratable):
"""TAP subblock for handling the IR shift register"""
def __init__(self, *, ir_width, cmd_idcode,
return m
+
class IOType(Enum):
In = auto()
Out = auto()
TriOut = auto()
InTriOut = auto()
+
class IOConn(Record):
"""TAP subblock representing the interface for an JTAG IO cell.
It contains signal to connect to the core and to the pad
core: subrecord with signals for the core
i: Signal(1), present only for IOType.In and IOType.InTriOut.
Signal input to core with pad input value.
- o: Signal(1), present only for IOType.Out, IOType.TriOut and IOType.InTriOut.
+ o: Signal(1), present only for IOType.Out, IOType.TriOut and
+ IOType.InTriOut.
Signal output from core with the pad output value.
oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
Signal output from core with the pad output enable value.
pad: subrecord with for the pad
i: Signal(1), present only for IOType.In and IOType.InTriOut
Output from pad with pad input value for core.
- o: Signal(1), present only for IOType.Out, IOType.TriOut and IOType.InTriOut.
+ o: Signal(1), present only for IOType.Out, IOType.TriOut and
+ IOType.InTriOut.
Input to pad with pad output value.
oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
Input to pad with pad output enable value.
return Layout((("core", sigs), ("pad", sigs)))
def __init__(self, *, iotype, name=None, src_loc_at=0):
- super().__init__(self.__class__.layout(iotype), name=name, src_loc_at=src_loc_at+1)
+ super().__init__(self.__class__.layout(iotype), name=name,
+ src_loc_at=src_loc_at+1)
self._iotype = iotype
+
class _IDBypassBlock(Elaboratable):
"""TAP subblock for the ID shift register"""
def __init__(self, *, manufacturer_id, part_number, version,
tdi, capture, shift, update, bypass,
name):
self.name = name
- if not isinstance(manufacturer_id, Const) and len(manufacturer_id) != 11:
+ if (not isinstance(manufacturer_id, Const) and
+ len(manufacturer_id) != 11):
raise ValueError("manufacturer_id has to be Const of length 11")
if not isinstance(part_number, Const) and len(manufacturer_id) != 16:
raise ValueError("part_number has to be Const of length 16")
class TAP(Elaboratable):
#TODO: Document TAP
- def __init__(
- self, *, with_reset=False, ir_width=None,
- manufacturer_id=Const(0b10001111111, 11), part_number=Const(1, 16),
- version=Const(0, 4),
- name=None, src_loc_at=0
- ):
- assert((ir_width is None) or (isinstance(ir_width, int) and ir_width >= 2))
+ def __init__(self, *, with_reset=False, ir_width=None,
+ manufacturer_id=Const(0b10001111111, 11),
+ part_number=Const(1, 16),
+ version=Const(0, 4),
+ name=None, src_loc_at=0):
+ assert((ir_width is None) or (isinstance(ir_width, int) and
+ ir_width >= 2))
assert(len(version) == 4)
if name is None:
self._part_number = part_number
self._version = version
- self._ircodes = [0, 1, 2] # Already taken codes, all ones added at the end
+ self._ircodes = [0, 1, 2] # Already taken codes, all ones added at end
self._ios = []
self._srs = []
self._wbs = []
+ self._dmis = []
def elaborate(self, platform):
m = Module()
ir_max = max(self._ircodes) + 1 # One extra code needed with all ones
ir_width = len("{:b}".format(ir_max))
if self._ir_width is not None:
- assert self._ir_width >= ir_width, "Specified JTAG IR width not big enough for allocated shiift registers"
+ assert self._ir_width >= ir_width, "Specified JTAG IR width " \
+ "not big enough for allocated shiift registers"
ir_width = self._ir_width
# TODO: Make commands numbers configurable
# ID block
select_id = fsm.isdr & ((ir == cmd_idcode) | (ir == cmd_bypass))
m.submodules._idblock = idblock = _IDBypassBlock(
- manufacturer_id=self._manufacturer_id, part_number=self._part_number,
+ manufacturer_id=self._manufacturer_id,
+ part_number=self._part_number,
version=self._version, tdi=self.bus.tdi,
capture=(select_id & fsm.capture),
shift=(select_id & fsm.shift),
preload = (ir == cmd_preload)
select_io = fsm.isdr & (sample | preload)
m.d.comb += [
- io_capture.eq(sample & fsm.capture), # Don't capture if not sample (like for PRELOAD)
+ io_capture.eq(sample & fsm.capture), # Don't capture if not sample
+ # (like for PRELOAD)
io_shift.eq(select_io & fsm.shift),
io_update.eq(select_io & fsm.update),
io_bd2io.eq(ir == cmd_extest),
# 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
io_sr = Signal(length)
io_bd = Signal(length)
+ # Boundary scan "capture" mode. makes I/O status available via SR
with m.If(capture):
idx = 0
for conn in self._ios:
else:
raise("Internal error")
assert idx == length, "Internal error"
+
+ # "Shift" mode (sends out captured data on tdo, sets incoming from tdi)
with m.Elif(shift):
m.d.posjtag += io_sr.eq(Cat(self.bus.tdi, io_sr[:-1]))
+
+ # "Update" mode
with m.Elif(update):
m.d.negjtag += io_bd.eq(io_sr)
+ # sets up IO (pad<->core) or in testing mode depending on requested
+ # mode, via Muxes controlled by bd2core and bd2io
idx = 0
for conn in self._ios:
if conn._iotype == IOType.In:
return io_sr[-1]
- def add_shiftreg(self, *, ircode, length, domain="sync", name=None, src_loc_at=0):
+ def add_shiftreg(self, *, ircode, length, domain="sync", name=None,
+ src_loc_at=0):
"""Add a shift register to the JTAG interface
Parameters:
- - ircode: code(s) for the IR; int or sequence of ints. In the latter case this
- shiftreg is shared between different IR codes.
+ - ircode: code(s) for the IR; int or sequence of ints. In the latter
+ case this shiftreg is shared between different IR codes.
- length: the length of the shift register
- domain: the domain on which the signal will be used"""
ir_it = ircodes = (ircode,)
for _ircode in ir_it:
if not isinstance(_ircode, int) or _ircode <= 0:
- raise ValueError("IR code '{}' is not an int greater than 0".format(_ircode))
+ raise ValueError("IR code '{}' is not an int "
+ "greater than 0".format(_ircode))
if _ircode in self._ircodes:
raise ValueError("IR code '{}' already taken".format(_ircode))
if name is None:
name = "sr{}".format(len(self._srs))
- sr = ShiftReg(sr_length=length, cmds=len(ircodes), name=name, src_loc_at=src_loc_at+1)
+ sr = ShiftReg(sr_length=length, cmds=len(ircodes), name=name,
+ src_loc_at=src_loc_at+1)
self._srs.append((ircodes, domain, sr))
return sr
sr_update.eq((isir != 0) & update),
]
- # update signal is on the JTAG clockdomain, sr.oe is on `domain` clockdomain
- # latch update in `domain` clockdomain and see when it has falling edge.
+ # update signal is on the JTAG clockdomain, sr.oe is on `domain`
+ # clockdomain latch update in `domain` clockdomain and see when
+ # it has falling edge.
# At that edge put isir in sr.oe for one `domain` clockdomain
+ # Using this custom sync <> JTAG domain synchronization avoids
+ # the use of more generic but also higher latency CDC solutions
+ # like FFSynchronizer.
update_core = Signal(name=sr.name+"_update_core")
update_core_prev = Signal(name=sr.name+"_update_core_prev")
m.d[domain] += [
- update_core.eq(sr_update), # This is CDC from JTAG domain to given domain
+ update_core.eq(sr_update), # This is CDC from JTAG domain
+ # to given domain
update_core_prev.eq(update_core)
]
with m.If(update_core_prev & ~update_core):
with m.Else():
m.d.comb += self.bus.tdo.eq(tdo_jtag)
else:
- # Always connect tdo_jtag to
+ # Always connect tdo_jtag to
m.d.comb += self.bus.tdo.eq(tdo_jtag)
- def add_wishbone(self, *, ircodes, address_width, data_width, granularity=None, domain="sync",
+ def add_wishbone(self, *, ircodes, address_width, data_width,
+ granularity=None, domain="sync", features=None,
name=None, src_loc_at=0):
"""Add a wishbone interface
- In order to allow high JTAG clock speed, data will be cached. This means that if data is
- output the value of the next address will be read automatically.
+ In order to allow high JTAG clock speed, data will be cached.
+ This means that if data is output the value of the next address
+ will be read automatically.
Parameters:
-----------
share a shift register of length data_width.
address_width: width of the address
data_width: width of the data
+ features: features required. defaults to stall, lock, err, rty
Returns:
wb: nmigen_soc.wishbone.bus.Interface
if len(ircodes) != 3:
raise ValueError("3 IR Codes have to be provided")
+ if features is None:
+ features={"stall", "lock", "err", "rty"}
if name is None:
name = "wb" + str(len(self._wbs))
sr_addr = self.add_shiftreg(
)
wb = WishboneInterface(data_width=data_width, addr_width=address_width,
- granularity=granularity, features={"stall", "lock", "err", "rty"},
+ granularity=granularity, features=features,
name=name, src_loc_at=src_loc_at+1)
self._wbs.append((sr_addr, sr_data, wb, domain))
m.d[domain] += wb.dat_w.eq(sr_data.o)
m.next = "WRITEREAD"
with m.State("READ"):
- with m.If(~wb.stall):
+ if not hasattr(wb, "stall"):
m.next = "READACK"
+ else:
+ with m.If(~wb.stall):
+ m.next = "READACK"
with m.State("READACK"):
with m.If(wb.ack):
- # Store read data in sr_data.i and keep it there til next read
+ # Store read data in sr_data.i
+ # and keep it there til next read.
+ # This is enough to synchronize between sync and JTAG
+ # clock domain and no higher latency solutions like
+ # FFSynchronizer is needed.
m.d[domain] += sr_data.i.eq(wb.dat_r)
m.next = "IDLE"
with m.State("WRITEREAD"):
- with m.If(~wb.stall):
+ if not hasattr(wb, "stall"):
m.next = "WRITEREADACK"
+ else:
+ with m.If(~wb.stall):
+ m.next = "WRITEREADACK"
with m.State("WRITEREADACK"):
with m.If(wb.ack):
m.d[domain] += wb.adr.eq(wb.adr + 1)