#!/bin/env python3
-import os
+import os, textwrap
from nmigen import *
from nmigen.build import *
from nmigen.hdl.rec import Direction
from nmigen.tracer import get_var_name
-from c4m_repo.nmigen.lib import Wishbone
+from nmigen_soc.wishbone import Interface as WishboneInterface
from .bus import Interface
platform.add_file(prefix + fname, f)
f.close()
+ _controller_templ = textwrap.dedent(r"""
+ library ieee;
+ use ieee.std_logic_1164.ALL;
+
+ use work.c4m_jtag.ALL;
+
+ entity {name} is
+ port (
+ -- The TAP signals
+ TCK: in std_logic;
+ TMS: in std_logic;
+ TDI: in std_logic;
+ TDO: out std_logic;
+ TRST_N: in std_logic;
+
+ -- The FSM state indicators
+ RESET: out std_logic;
+ CAPTURE: out std_logic;
+ SHIFT: out std_logic;
+ UPDATE: out std_logic;
+
+ -- The Instruction Register
+ IR: out std_logic_vector({ir_width}-1 downto 0);
+
+ -- The I/O access ports
+ CORE_IN: out std_logic_vector({ios}-1 downto 0);
+ CORE_EN: in std_logic_vector({ios}-1 downto 0);
+ CORE_OUT: in std_logic_vector({ios}-1 downto 0);
+
+ -- The pad connections
+ PAD_IN: in std_logic_vector({ios}-1 downto 0);
+ PAD_EN: out std_logic_vector({ios}-1 downto 0);
+ PAD_OUT: out std_logic_vector({ios}-1 downto 0)
+ );
+ end {name};
+
+ architecture rtl of {name} is
+ begin
+ jtag : c4m_jtag_tap_controller
+ generic map(
+ DEBUG => FALSE,
+ IR_WIDTH => {ir_width},
+ IOS => {ios},
+ MANUFACTURER => "{manufacturer:011b}",
+ PART_NUMBER => "{part:016b}",
+ VERSION => "{version:04b}"
+ )
+ port map(
+ TCK => TCK,
+ TMS => TMS,
+ TDI => TDI,
+ TDO => TDO,
+ TRST_N => TRST_N,
+ RESET => RESET,
+ CAPTURE => CAPTURE,
+ SHIFT => SHIFT,
+ UPDATE => UPDATE,
+ IR => IR,
+ CORE_IN => CORE_IN,
+ CORE_EN => CORE_EN,
+ CORE_OUT => CORE_OUT,
+ PAD_IN => PAD_IN,
+ PAD_EN => PAD_EN,
+ PAD_OUT => PAD_OUT
+ );
+ end architecture rtl;
+ """)
+ _cell_inst = 0
+ @classmethod
+ def _add_instance(cls, platform, prefix, *, ir_width, ios, manufacturer, part, version):
+ name = "jtag_controller_i{}".format(cls._cell_inst)
+ cls._cell_inst += 1
+
+ platform.add_file(
+ "{}{}.vhdl".format(prefix, name),
+ cls._controller_templ.format(
+ name=name, ir_width=ir_width, ios=ios,
+ manufacturer=manufacturer, part=part, version=version,
+ )
+ )
+
+ return name
+
def __init__(
- self, io_count, *, ir_width=None,
+ self, io_count, *, 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)
- self.name = name if name is not None else get_var_name(depth=src_loc_at+2, default="TAP")
- self.bus = Interface(name=self.name+"_bus", src_loc_at=src_loc_at+1)
+ if name is None:
+ name = get_var_name(depth=src_loc_at+2, default="TAP")
+ self.name = name
+ self.bus = Interface(with_reset=with_reset, name=self.name+"_bus",
+ src_loc_at=src_loc_at+1)
# TODO: Handle IOs with different directions
- self.core = Array(Pin(1, "io") for _ in range(io_count)) # Signals to use for core
- self.pad = Array(Pin(1, "io") for _ in range(io_count)) # Signals going to IO pads
+ self.core = Array(
+ Pin(1, "io", name=name+"_coreio"+str(i), src_loc_at=src_loc_at+1)
+ for i in range(io_count)
+ ) # Signals to use for core
+ self.pad = Array(
+ Pin(1, "io", name=name+"_padio"+str(i), src_loc_at=src_loc_at+1)
+ for i in range(io_count)
+ ) # Signals going to IO pads
##
def elaborate(self, platform):
- TAP._add_files(platform, "jtag" + os.path.sep)
+ self.__class__._add_files(platform, "jtag" + os.path.sep)
m = Module()
assert self._ir_width >= ir_width, "Specified JTAG IR width not big enough for allocated shiift registers"
ir_width = self._ir_width
+ cell = self.__class__._add_instance(
+ platform, "jtag" + os.path.sep, ir_width=ir_width, ios=self._io_count,
+ manufacturer=self._manufacturer_id.value, part=self._part_number.value,
+ version=self._version.value,
+ )
+
sigs = Record([
("capture", 1),
("shift", 1),
reset = Signal()
+ trst_n = Signal()
+ m.d.comb += trst_n.eq(~self.bus.trst if hasattr(self.bus, "trst") else Const(1))
+
core_i = Cat(pin.i for pin in self.core)
core_o = Cat(pin.o for pin in self.core)
core_oe = Cat(pin.oe for pin in self.core)
pad_o = Cat(pin.o for pin in self.pad)
pad_oe = Cat(pin.oe for pin in self.pad)
- params = {
- "p_IOS": self._io_count,
- "p_IR_WIDTH": ir_width,
- "p_MANUFACTURER": self._manufacturer_id,
- "p_PART_NUMBER": self._part_number,
- "p_VERSION": self._version,
- "i_TCK": self.bus.tck,
- "i_TMS": self.bus.tms,
- "i_TDI": self.bus.tdi,
- "o_TDO": sigs.tdo_jtag,
- "i_TRST_N": Const(1),
- "o_RESET": reset,
- "o_DRCAPTURE": sigs.capture,
- "o_DRSHIFT": sigs.shift,
- "o_DRUPDATE": sigs.update,
- "o_IR": sigs.ir,
- "o_CORE_IN": core_i,
- "i_CORE_OUT": core_o,
- "i_CORE_EN": core_oe,
- "i_PAD_IN": pad_i,
- "o_PAD_OUT": pad_o,
- "o_PAD_EN": pad_oe,
- }
- m.submodules.tap = Instance("c4m_jtag_tap_controller", **params)
+ m.submodules.tap = Instance(cell,
+ i_TCK=self.bus.tck,
+ i_TMS=self.bus.tms,
+ i_TDI=self.bus.tdi,
+ o_TDO=sigs.tdo_jtag,
+ i_TRST_N=trst_n,
+ o_RESET=reset,
+ o_CAPTURE=sigs.capture,
+ o_SHIFT=sigs.shift,
+ o_UPDATE=sigs.update,
+ o_IR=sigs.ir,
+ o_CORE_IN=core_i,
+ i_CORE_OUT=core_o,
+ i_CORE_EN=core_oe,
+ i_PAD_IN=pad_i,
+ o_PAD_OUT=pad_o,
+ o_PAD_EN=pad_oe,
+ )
# Own clock domain using TCK as clock signal
m.domains.jtag = jtag_cd = ClockDomain(name="jtag", local=True)
return m
- 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:
m.d.comb += self.bus.tdo.eq(sigs.tdo_jtag)
- def add_wishbone(self, *, ircodes, address_width, data_width, sel_width=None, domain="sync"):
+ def add_wishbone(self, *, ircodes, address_width, data_width, granularity=None, domain="sync"):
"""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.
Parameters:
+ -----------
ircodes: sequence of three integer for the JTAG IR codes;
they represent resp. WBADDR, WBREAD and WBREADWRITE. 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:
+ wb: nmigen_soc.wishbone.bus.Interface
+ The Wishbone interface, is pipelined and has stall field.
"""
if len(ircodes) != 3:
raise ValueError("3 IR Codes have to be provided")
sr_addr = self.add_shiftreg(ircodes[0], address_width, domain=domain)
sr_data = self.add_shiftreg(ircodes[1:], data_width, domain=domain)
- wb = Wishbone(data_width=data_width, address_width=address_width, sel_width=sel_width, master=True)
+ wb = WishboneInterface(data_width=data_width, addr_width=address_width,
+ granularity=granularity, features={"stall", "lock", "err", "rty"})
self._wbs.append((sr_addr, sr_data, wb, domain))
with m.FSM(domain=domain) as fsm:
with m.State("IDLE"):
- m.d.comb += [
- wb.cyc.eq(0),
- wb.stb.eq(0),
- wb.we.eq(0),
- ]
with m.If(sr_addr.oe): # WBADDR code
- m.d[domain] += wb.addr.eq(sr_addr.o)
+ m.d[domain] += wb.adr.eq(sr_addr.o)
m.next = "READ"
with m.Elif(sr_data.oe[0]): # WBREAD code
# If data is
- m.d[domain] += wb.addr.eq(wb.addr + 1)
+ m.d[domain] += wb.adr.eq(wb.adr + 1)
m.next = "READ"
-
- with m.If(sr_data.oe[1]): # WBWRITE code
+ with m.Elif(sr_data.oe[1]): # WBWRITE code
m.d[domain] += wb.dat_w.eq(sr_data.o)
m.next = "WRITEREAD"
with m.State("READ"):
- m.d.comb += [
- wb.cyc.eq(1),
- wb.stb.eq(1),
- wb.we.eq(0),
- ]
with m.If(~wb.stall):
m.next = "READACK"
with m.State("READACK"):
- m.d.comb += [
- wb.cyc.eq(1),
- wb.stb.eq(0),
- wb.we.eq(0),
- ]
with m.If(wb.ack):
# Store read data in sr_data.i and keep it there til next read
m.d[domain] += sr_data.i.eq(wb.dat_r)
m.next = "IDLE"
with m.State("WRITEREAD"):
- m.d.comb += [
- wb.cyc.eq(1),
- wb.stb.eq(1),
- wb.we.eq(1),
- ]
with m.If(~wb.stall):
m.next = "WRITEREADACK"
with m.State("WRITEREADACK"):
- m.d.comb += [
- wb.cyc.eq(1),
- wb.stb.eq(0),
- wb.we.eq(0),
- ]
with m.If(wb.ack):
- m.d[domain] += wb.addr.eq(wb.addr + 1)
+ m.d[domain] += wb.adr.eq(wb.adr + 1)
m.next = "READ"
+
+ m.d.comb += [
+ wb.cyc.eq(~fsm.ongoing("IDLE")),
+ wb.stb.eq(fsm.ongoing("READ") | fsm.ongoing("WRITEREAD")),
+ wb.we.eq(fsm.ongoing("WRITEREAD")),
+ ]