Made nmigen code independent of VHDL code.
authorStaf Verhaegen <staf@stafverhaegen.be>
Mon, 6 Jan 2020 17:06:05 +0000 (18:06 +0100)
committerStaf Verhaegen <staf@stafverhaegen.be>
Mon, 6 Jan 2020 17:06:05 +0000 (18:06 +0100)
All blocks have been converted to nmigen.

c4m/nmigen/jtag/tap.py
test/nmigen/cocotb/controller/Makefile
test/nmigen/cocotb/controller/generate.py
test/nmigen/cocotb/controller/test.py

index b2bcf818f8a3a2bc5b9fe62bfa5dafd628b8321b..44cae0453869f971b9f587e5fd5abfa06cc8701d 100755 (executable)
@@ -1,10 +1,11 @@
 #!/bin/env python3
 import os, textwrap
+from enum import Enum, auto
 
 from nmigen import *
 from nmigen.build import *
 from nmigen.lib.io import *
-from nmigen.hdl.rec import Direction
+from nmigen.hdl.rec import Direction, Layout
 from nmigen.tracer import get_var_name
 
 from nmigen_soc.wishbone import Interface as WishboneInterface
@@ -12,10 +13,245 @@ from nmigen_soc.wishbone import Interface as WishboneInterface
 from .bus import Interface
 
 __all__ = [
-    "TAP", "ShiftReg",
+    "TAP", "ShiftReg", "IOType", "IOConn",
 ]
 
 
+class _FSM(Elaboratable):
+    """TAP subblock for the FSM"""
+    def __init__(self, *, bus):
+        self.isir = Signal()
+        self.isdr = Signal()
+        self.capture = Signal()
+        self.shift = Signal()
+        self.update = Signal()
+
+        self.posjtag = ClockDomain("posjtag", local=True)
+        self.negjtag = ClockDomain("negjtag", local=True, clk_edge="neg")
+
+        self._bus = bus
+
+    def elaborate(self, platform):
+        m = Module()
+
+        rst = Signal()
+        m.d.comb += [
+            self.posjtag.clk.eq(self._bus.tck),
+            self.posjtag.rst.eq(rst),
+            self.negjtag.clk.eq(self._bus.tck),
+            self.negjtag.rst.eq(rst),
+        ]
+
+        # Make local clock domain optionally using trst of JTAG bus as reset
+        if hasattr(self._bus, "trst"):
+            m.domains.local = local = ClockDomain(local=True)
+            m.d.comb += local.rst.eq(self._bus.trst)
+        else:
+            m.domains.local = local = ClockDomain(local=True, reset_less=True)
+        m.d.comb += local.clk.eq(self._bus.tck)
+
+        with m.FSM(domain="local") as fsm:
+            with m.State("TestLogicReset"):
+                # Be sure to reset isir, isdr
+                m.d.local += [
+                    self.isir.eq(0),
+                    self.isdr.eq(0),
+                ]
+                with m.If(self._bus.tms == 0):
+                    m.next = "RunTestIdle"
+            with m.State("RunTestIdle"):
+                # Be sure to reset isir, isdr
+                m.d.local += [
+                    self.isir.eq(0),
+                    self.isdr.eq(0),
+                ]
+                with m.If(self._bus.tms == 1):
+                    m.next = "SelectDRScan"
+            with m.State("SelectDRScan"):
+                with m.If(self._bus.tms == 0):
+                    m.d.local += self.isdr.eq(1)
+                    m.next = "CaptureState"
+                with m.Else():
+                    m.next = "SelectIRScan"
+            with m.State("SelectIRScan"):
+                with m.If(self._bus.tms == 0):
+                    m.d.local += self.isir.eq(1)
+                    m.next = "CaptureState"
+                with m.Else():
+                    m.next = "TestLogicReset"
+            with m.State("CaptureState"):
+                with m.If(self._bus.tms == 0):
+                    m.next = "ShiftState"
+                with m.Else():
+                    m.next = "Exit1"
+            with m.State("ShiftState"):
+                with m.If(self._bus.tms == 1):
+                    m.next = "Exit1"
+            with m.State("Exit1"):
+                with m.If(self._bus.tms == 0):
+                    m.next = "Pause"
+                with m.Else():
+                    m.next = "UpdateState"
+            with m.State("Pause"):
+                with m.If(self._bus.tms == 1):
+                    m.next = "Exit2"
+            with m.State("Exit2"):
+                with m.If(self._bus.tms == 0):
+                    m.next = "ShiftState"
+                with m.Else():
+                    m.next = "UpdateState"
+            with m.State("UpdateState"):
+                m.d.local += [
+                    self.isir.eq(0),
+                    self.isdr.eq(0),
+                ]
+                with m.If(self._bus.tms == 0):
+                    m.next = "RunTestIdle"
+                with m.Else():
+                    m.next = "SelectDRScan"
+
+            m.d.comb += [
+                rst.eq(fsm.ongoing("TestLogicReset")),
+                self.capture.eq(fsm.ongoing("CaptureState")),
+                self.shift.eq(fsm.ongoing("ShiftState")),
+                self.update.eq(fsm.ongoing("UpdateState")),
+            ]
+
+        return m
+
+class _IRBlock(Elaboratable):
+    """TAP subblock for handling the IR shift register"""
+    def __init__(self, *, ir_width, cmd_idcode,
+                 tdi, capture, shift, update,
+                 name):
+        self.name = name
+        self.ir = Signal(ir_width, reset=cmd_idcode)
+        self.tdo = Signal()
+
+        self._tdi = tdi
+        self._capture = capture
+        self._shift = shift
+        self._update = update
+
+    def elaborate(self, platform):
+        m = Module()
+
+        shift_ir = Signal(len(self.ir), reset_less=True)
+
+        m.d.comb += self.tdo.eq(self.ir[0])
+        with m.If(self._capture):
+            m.d.posjtag += shift_ir.eq(self.ir)
+        with m.Elif(self._shift):
+            m.d.posjtag += shift_ir.eq(Cat(shift_ir[1:], self._tdi))
+        with m.Elif(self._update):
+            # For ir we only update it on the rising edge of clock
+            # to avoid that we already have the new ir value when still in
+            # Update state
+            m.d.posjtag += self.ir.eq(shift_ir)
+
+        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
+
+    This object is normally only allocated and returned from ``TAP.add_io``
+    It is a Record subclass.
+
+    Attributes
+    ----------
+    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.
+            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.
+            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.
+    """
+    @staticmethod
+    def layout(iotype):
+        sigs = []
+        if iotype in (IOType.In, IOType.InTriOut):
+            sigs.append(("i", 1))
+        if iotype in (IOType.Out, IOType.TriOut, IOType.InTriOut):
+            sigs.append(("o", 1))
+        if iotype in (IOType.TriOut, IOType.InTriOut):
+            sigs.append(("oe", 1))
+
+        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)
+
+        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:
+            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")
+        if not isinstance(version, Const) and len(version) != 4:
+            raise ValueError("version has to be Const of length 4")
+        self._id = Cat(Const(1,1), manufacturer_id, part_number, version)
+
+        self.tdo = Signal(name=name+"_tdo")
+
+        self._tdi = tdi
+        self._capture = capture
+        self._shift = shift
+        self._update = update
+        self._bypass = bypass
+
+    def elaborate(self, platform):
+        m = Module()
+
+        sr = Signal(32, reset_less=True, name=self.name+"_sr")
+
+        # Local signals for the module
+        _tdi = Signal()
+        _capture = Signal()
+        _shift = Signal()
+        _update = Signal()
+        _bypass = Signal()
+
+        m.d.comb += [
+            _tdi.eq(self._tdi),
+            _capture.eq(self._capture),
+            _shift.eq(self._shift),
+            _update.eq(self._update),
+            _bypass.eq(self._bypass),
+            self.tdo.eq(sr[0]),
+        ]
+
+        with m.If(_capture):
+            m.d.posjtag += sr.eq(self._id)
+        with m.Elif(_shift):
+            with m.If(_bypass):
+                m.d.posjtag += sr[0].eq(_tdi)
+            with m.Else():
+                m.d.posjtag += sr.eq(Cat(sr[1:], _tdi))
+
+        return m
+
+
 class ShiftReg(Record):
     """Object with interface for extra shift registers on a TAP.
 
@@ -46,8 +282,8 @@ class ShiftReg(Record):
     o: length=sr_length, FANOUT
         The value of the shift register.
     oe: length=cmds, FANOUT
-        Indicates that output needs to be sampled downstream because
-        JTAG TAP in in the Update state. The bit indicated the corresponding
+        Indicates that output is stable and can be sampled downstream because
+        JTAG TAP is in the Update state. The bit indicates the corresponding
         instruction. The bit is only kept high for one clock cycle.
     """
     def __init__(self, *, sr_length, cmds=1, name=None, src_loc_at=0):
@@ -62,115 +298,12 @@ class ShiftReg(Record):
 
 class TAP(Elaboratable):
     #TODO: Document TAP
-    @staticmethod
-    def _add_files(platform, prefix):
-        d = os.path.realpath("{dir}{sep}{par}{sep}{par}{sep}vhdl{sep}jtag".format(
-            dir=os.path.dirname(__file__), sep=os.path.sep, par=os.path.pardir
-        )) + os.path.sep
-        for fname in [
-            "c4m_jtag_pkg.vhdl",
-            "c4m_jtag_idblock.vhdl",
-            "c4m_jtag_iocell.vhdl",
-            "c4m_jtag_ioblock.vhdl",
-            "c4m_jtag_irblock.vhdl",
-            "c4m_jtag_tap_fsm.vhdl",
-            "c4m_jtag_tap_controller.vhdl",
-        ]:
-            f = open(d + fname, "r")
-            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, *, with_reset=False, ir_width=None,
+        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(isinstance(io_count, int) and io_count > 0)
         assert((ir_width is None) or (isinstance(ir_width, int) and ir_width >= 2))
         assert(len(version) == 4)
 
@@ -180,33 +313,21 @@ class TAP(Elaboratable):
         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", 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
-
         ##
 
-        self._io_count = io_count
         self._ir_width = ir_width
         self._manufacturer_id = manufacturer_id
         self._part_number = part_number
         self._version = version
 
         self._ircodes = [0, 1, 2] # Already taken codes, all ones added at the end
-        self._srs = []
 
+        self._ios = []
+        self._srs = []
         self._wbs = []
 
 
     def elaborate(self, platform):
-        self.__class__._add_files(platform, "jtag" + os.path.sep)
-
         m = Module()
 
         # Determine ir_width if not fixed.
@@ -216,64 +337,163 @@ class TAP(Elaboratable):
             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,
+        # TODO: Make commands numbers configurable
+        cmd_extest = 0
+        cmd_intest = 0
+        cmd_idcode = 1
+        cmd_sample = 2
+        cmd_preload = 2
+        cmd_bypass = 2**ir_width - 1 # All ones
+
+        m.submodules._fsm = fsm = _FSM(bus=self.bus)
+        m.domains.posjtag = fsm.posjtag
+        m.domains.negjtag = fsm.negjtag
+
+        select_ir = fsm.isir
+        m.submodules._irblock = irblock = _IRBlock(
+            ir_width=ir_width, cmd_idcode=cmd_idcode, tdi=self.bus.tdi,
+            capture=(fsm.isir & fsm.capture),
+            shift=(fsm.isir & fsm.shift),
+            update=(fsm.isir & fsm.update),
+            name=self.name+"_ir",
         )
-
-        sigs = Record([
-            ("capture", 1),
-            ("shift", 1),
-            ("update", 1),
-            ("ir", ir_width),
-            ("tdo_jtag", 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_i = Cat(pin.i for pin in self.pad)
-        pad_o = Cat(pin.o for pin in self.pad)
-        pad_oe = Cat(pin.oe for pin in self.pad)
-
-        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,
+        ir = irblock.ir
+
+        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,
+            version=self._version, tdi=self.bus.tdi,
+            capture=(select_id & fsm.capture),
+            shift=(select_id & fsm.shift),
+            update=(select_id & fsm.update),
+            bypass=(ir == cmd_bypass),
+            name=self.name+"_id",
         )
 
-        # Own clock domain using TCK as clock signal
-        m.domains.jtag = jtag_cd = ClockDomain(name="jtag", local=True)
+        io_capture = Signal()
+        io_shift = Signal()
+        io_update = Signal()
+        io_bd2io = Signal()
+        io_bd2core = Signal()
+        sample = (ir == cmd_extest) | (ir == cmd_sample)
+        preload = (ir == cmd_preload)
+        select_io = fsm.isdr & (sample | preload)
         m.d.comb += [
-            jtag_cd.clk.eq(self.bus.tck),
-            jtag_cd.rst.eq(reset),
+            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),
+            io_bd2core.eq(ir == cmd_intest),
         ]
+        io_tdo = self._elaborate_ios(
+            m=m,
+            capture=io_capture, shift=io_shift, update=io_update,
+            bd2io=io_bd2io, bd2core=io_bd2core,
+        )
 
-        self._elaborate_shiftregs(m, sigs)
+        tdo = Signal(name=self.name+"_tdo")
+        with m.If(select_ir):
+            m.d.comb += tdo.eq(irblock.tdo)
+        with m.Elif(select_id):
+            m.d.comb += tdo.eq(idblock.tdo)
+        with m.Elif(select_io):
+            m.d.comb += tdo.eq(io_tdo)
+
+        self._elaborate_shiftregs(
+            m, capture=fsm.capture, shift=fsm.shift, update=fsm.update,
+            ir=irblock.ir, tdo_jtag=tdo
+        )
         self._elaborate_wishbones(m)
 
         return m
 
 
+    def add_io(self, *, iotype, name=None, src_loc_at=0):
+        """Add a io cell to the boundary scan chain
+
+        Parameters:
+        - iotype: :class:`IOType` enum.
+
+        Returns:
+        - :class:`IOConn`
+        """
+        if name is None:
+            name = "ioconn" + str(len(self._ios))
+
+        ioconn = IOConn(iotype=iotype, name=name, src_loc_at=src_loc_at+1)
+        self._ios.append(ioconn)
+        return ioconn
+
+    def _elaborate_ios(self, *, m, capture, shift, update, bd2io, bd2core):
+        connlength = {
+            IOType.In: 1,
+            IOType.Out: 1,
+            IOType.TriOut: 2,
+            IOType.InTriOut: 3,
+        }
+        length = sum(connlength[conn._iotype] for conn in self._ios)
+
+        io_sr = Signal(length)
+        io_bd = Signal(length)
+
+        with m.If(capture):
+            idx = 0
+            for conn in self._ios:
+                if conn._iotype == IOType.In:
+                    m.d.posjtag += io_sr[idx].eq(conn.pad.i)
+                    idx += 1
+                elif conn._iotype == IOType.Out:
+                    m.d.posjtag += io_sr[idx].eq(conn.core.o)
+                    idx += 1
+                elif conn._iotype == IOType.TriOut:
+                    m.d.posjtag += [
+                        io_sr[idx].eq(conn.core.o),
+                        io_sr[idx+1].eq(conn.core.oe),
+                    ]
+                    idx += 2
+                elif conn._iotype == IOType.InTriOut:
+                    m.d.posjtag += [
+                        io_sr[idx].eq(conn.pad.i),
+                        io_sr[idx+1].eq(conn.core.o),
+                        io_sr[idx+2].eq(conn.core.oe),
+                    ]
+                    idx += 3
+                else:
+                    raise("Internal error")
+            assert idx == length, "Internal error"
+        with m.Elif(shift):
+            m.d.posjtag += io_sr.eq(Cat(self.bus.tdi, io_sr[:-1]))
+        with m.Elif(update):
+            m.d.negjtag += io_bd.eq(io_sr)
+
+        idx = 0
+        for conn in self._ios:
+            if conn._iotype == IOType.In:
+                m.d.comb += conn.core.i.eq(Mux(bd2core, io_bd[idx], conn.pad.i))
+                idx += 1
+            elif conn._iotype == IOType.Out:
+                m.d.comb += conn.pad.o.eq(Mux(bd2io, io_bd[idx], conn.core.o))
+                idx += 1
+            elif conn._iotype == IOType.TriOut:
+                m.d.comb += [
+                    conn.pad.o.eq(Mux(bd2io, io_bd[idx], conn.core.o)),
+                    conn.pad.oe.eq(Mux(bd2io, io_bd[idx+1], conn.core.oe)),
+                ]
+                idx += 2
+            elif conn._iotype == IOType.InTriOut:
+                m.d.comb += [
+                    conn.core.i.eq(Mux(bd2core, io_bd[idx], conn.pad.i)),
+                    conn.pad.o.eq(Mux(bd2io, io_bd[idx+1], conn.core.o)),
+                    conn.pad.oe.eq(Mux(bd2io, io_bd[idx+2], conn.core.oe)),
+                ]
+                idx += 3
+            else:
+                raise("Internal error")
+        assert idx == length, "Internal error"
+
+        return io_sr[-1]
+
+
     def add_shiftreg(self, *, ircode, length, domain="sync", name=None, src_loc_at=0):
         """Add a shift register to the JTAG interface
 
@@ -297,13 +517,13 @@ class TAP(Elaboratable):
         self._ircodes.extend(ircodes)
 
         if name is None:
-            name = self.name + "_sr{}".format(len(self._srs))
+            name = "sr{}".format(len(self._srs))
         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
 
-    def _elaborate_shiftregs(self, m, sigs):
+    def _elaborate_shiftregs(self, m, capture, shift, update, ir, tdo_jtag):
         # tdos is tuple of (tdo, tdo_en) for each shiftreg
         tdos = []
         for ircodes, domain, sr in self._srs:
@@ -311,14 +531,14 @@ class TAP(Elaboratable):
             m.d.comb += sr.o.eq(reg)
 
             isir = Signal(len(ircodes), name=sr.name+"_isir")
-            capture = Signal(name=sr.name+"_capture")
-            shift = Signal(name=sr.name+"_shift")
-            update = Signal(name=sr.name+"_update")
+            sr_capture = Signal(name=sr.name+"_capture")
+            sr_shift = Signal(name=sr.name+"_shift")
+            sr_update = Signal(name=sr.name+"_update")
             m.d.comb += [
-                isir.eq(Cat(sigs.ir == ircode for ircode in ircodes)),
-                capture.eq((isir != 0) & sigs.capture),
-                shift.eq((isir != 0) & sigs.shift),
-                update.eq((isir != 0) & sigs.update),
+                isir.eq(Cat(ir == ircode for ircode in ircodes)),
+                sr_capture.eq((isir != 0) & capture),
+                sr_shift.eq((isir != 0) & shift),
+                sr_update.eq((isir != 0) & update),
             ]
 
             # update signal is on the JTAG clockdomain, sr.oe is on `domain` clockdomain
@@ -327,40 +547,43 @@ class TAP(Elaboratable):
             update_core = Signal(name=sr.name+"_update_core")
             update_core_prev = Signal(name=sr.name+"_update_core_prev")
             m.d[domain] += [
-                update_core.eq(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 == 0):
+            with m.If(update_core_prev & ~update_core):
                 # Falling edge of update
                 m.d[domain] += sr.oe.eq(isir)
             with m.Else():
                 m.d[domain] += sr.oe.eq(0)
 
-            with m.If(shift):
-                m.d.jtag += reg.eq(Cat(reg[1:], self.bus.tdi))
-            with m.If(capture):
-                m.d.jtag += reg.eq(sr.i)
+            with m.If(sr_shift):
+                m.d.posjtag += reg.eq(Cat(reg[1:], self.bus.tdi))
+            with m.If(sr_capture):
+                m.d.posjtag += reg.eq(sr.i)
 
             # tdo = reg[0], tdo_en = shift
-            tdos.append((reg[0], shift))
+            tdos.append((reg[0], sr_shift))
 
+
+        # Assign the right tdo to the bus tdo
         for i, (tdo, tdo_en) in enumerate(tdos):
             if i == 0:
-                with m.If(shift):
+                with m.If(tdo_en):
                     m.d.comb += self.bus.tdo.eq(tdo)
             else:
-                with m.Elif(shift):
+                with m.Elif(tdo_en):
                     m.d.comb += self.bus.tdo.eq(tdo)
 
         if len(tdos) > 0:
             with m.Else():
-                m.d.comb += self.bus.tdo.eq(sigs.tdo_jtag)
+                m.d.comb += self.bus.tdo.eq(tdo_jtag)
         else:
             # Always connect tdo_jtag to 
-            m.d.comb += self.bus.tdo.eq(sigs.tdo_jtag)
+            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",
+                     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
@@ -382,11 +605,20 @@ class TAP(Elaboratable):
         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)
+        if name is None:
+            name = "wb" + str(len(self._wbs))
+        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"
+        )
 
         wb = WishboneInterface(data_width=data_width, addr_width=address_width,
-                               granularity=granularity, features={"stall", "lock", "err", "rty"})
+                               granularity=granularity, features={"stall", "lock", "err", "rty"},
+                               name=name, src_loc_at=src_loc_at+1)
 
         self._wbs.append((sr_addr, sr_data, wb, domain))
 
@@ -394,6 +626,8 @@ class TAP(Elaboratable):
 
     def _elaborate_wishbones(self, m):
         for sr_addr, sr_data, wb, domain in self._wbs:
+            m.d.comb += sr_addr.i.eq(wb.adr)
+
             if hasattr(wb, "sel"):
                 # Always selected
                 m.d.comb += [s.eq(1) for s in wb.sel]
index 0456f3b3da5f288bc748fffb20fe3e91c4f4d42c..e19adeead15470c9da23bf457c5bf0f603078224 100644 (file)
@@ -18,14 +18,6 @@ TOPVCD := $(CURDIR)/$(TOPLEVEL).vcd
 # COCOTB
 #
 VHDL_SOURCES = \
-  $(CODEDIR)/jtag/c4m_jtag_pkg.vhdl \
-  $(CODEDIR)/jtag/c4m_jtag_tap_fsm.vhdl \
-  $(CODEDIR)/jtag/c4m_jtag_irblock.vhdl \
-  $(CODEDIR)/jtag/c4m_jtag_iocell.vhdl \
-  $(CODEDIR)/jtag/c4m_jtag_ioblock.vhdl \
-  $(CODEDIR)/jtag/c4m_jtag_idblock.vhdl \
-  $(CODEDIR)/jtag/c4m_jtag_tap_controller.vhdl \
-  $(CODEDIR)/jtag/jtag_controller_i0.vhdl \
 #VHDL_SOURCES end
 VERILOG_SOURCES = \
   $(TOPFILE) \
index fee62fe81d5d3cfe286cfe5c8eb1cdceb75cf98f..21226ef3bdad5c0e8267257f2b76020e26bda534 100755 (executable)
@@ -5,7 +5,7 @@ from nmigen import *
 from nmigen.back.verilog import convert
 from nmigen.build import Platform
 
-from c4m.nmigen.jtag import TAP
+from c4m.nmigen.jtag import TAP, IOType, IOConn
 
 class DummyPlatform(Platform):
     resources = []
@@ -16,37 +16,31 @@ class DummyPlatform(Platform):
         raise NotImplementedError
 
 class Top(Elaboratable):
+    iotypes = (IOType.In, IOType.Out, IOType.TriOut, IOType.InTriOut)
+
     def __init__(self, io_count):
-        self.tap = TAP(io_count)
-        self.core_i = Signal(io_count, name="top_corei")
-        self.core_o = Signal(io_count, name="top_coreo")
-        self.core_oe = Signal(io_count, name="top_coreoe")
-        self.pad_i = Signal(io_count, name="top_padi")
-        self.pad_o = Signal(io_count, name="top_pado")
-        self.pad_oe = Signal(io_count, name="top_padoe")
+        self.tap = tap = TAP()
+        self.ios = [tap.add_io(iotype=iotype) for iotype in self.iotypes]
 
     def elaborate(self, platform):
         m = Module()
 
         m.submodules.tap = self.tap
 
-        m.d.comb += [
-            self.core_i.eq(Cat(io.i for io in self.tap.core)),
-            Cat(io.o for io in self.tap.core).eq(self.core_o),
-            Cat(io.oe for io in self.tap.core).eq(self.core_oe),
-            Cat(io.i for io in self.tap.pad).eq(self.pad_i),
-            self.pad_o.eq(Cat(io.o for io in self.tap.pad)),
-            self.pad_oe.eq(Cat(io.oe for io in self.tap.pad)),
-        ]
-
         return m
 
 top = Top(2)
 
 p = DummyPlatform()
 
-ports = [top.tap.bus.tck, top.tap.bus.tms, top.tap.bus.tdi, top.tap.bus.tdo,
-         top.core_i, top.core_o, top.core_oe, top.pad_i, top.pad_o, top.pad_oe]
+ports = [top.tap.bus.tck, top.tap.bus.tms, top.tap.bus.tdi, top.tap.bus.tdo]
+for conn in top.ios:
+    for sig in ("i", "o", "oe"):
+        try:
+            ports += [getattr(conn.core, sig), getattr(conn.pad, sig)]
+        except:
+            pass
+
 # for io in tap.core:
 #     ports += [io.i, io.o, io.oe]
 # for io in tap.pad:
index 7b465fb7b5797a2a44d62d1a0920917b5941233e..7b3143113001007c460a8d4f053e9ac4834afba7 100644 (file)
@@ -4,6 +4,7 @@ from cocotb.binary import BinaryValue
 
 from c4m.cocotb.jtag.c4m_jtag import JTAG_Master
 
+
 @cocotb.test()
 def test01_idcode(dut):
     """
@@ -26,6 +27,7 @@ def test01_idcode(dut):
 
     assert(result1 == result2)
     
+
 @cocotb.test()
 def test02_bypass(dut):
     """
@@ -48,6 +50,7 @@ def test02_bypass(dut):
     dut._log.info("bypass out: {}".format(master.result.binstr))
     assert(master.result.binstr[:-1] == data_in.binstr[1:])
 
+
 @cocotb.test()
 def test03_sample(dut):
     """
@@ -63,32 +66,71 @@ def test03_sample(dut):
     dut._log.info("Load SAMPLEPRELOAD command")
     yield master.load_ir(master.SAMPLEPRELOAD)
 
-    data_in.binstr = "011000"
+    data_in.binstr = "0100110"
     dut._log.info("  preloading data {}".format(data_in.binstr))
 
     # Set the ios pins
-    dut.top_coreo = 2
-    dut.top_coreoe = 0
-    dut.top_padi = 1
+    dut.tap_ioconn0__pad__i = 1
+    dut.tap_ioconn1__core__o = 0
+    dut.tap_ioconn2__core__o = 1
+    dut.tap_ioconn2__core__oe = 1
+    dut.tap_ioconn3__pad__i = 0
+    dut.tap_ioconn3__core__o = 0
+    dut.tap_ioconn3__core__oe = 1
     yield master.shift_data(data_in)
     dut._log.info("  output: {}".format(master.result.binstr))
-    assert(master.result.binstr == "100010")
+    assert(master.result.binstr == "1011001")
 
+    assert dut.tap_ioconn0__core__i == 1
+    assert dut.tap_ioconn1__pad__o == 0
+    assert dut.tap_ioconn2__pad__o == 1
+    assert dut.tap_ioconn2__pad__oe == 1
+    assert dut.tap_ioconn3__core__i == 0
+    assert dut.tap_ioconn3__pad__o == 0
+    assert dut.tap_ioconn3__pad__oe == 1
 
     dut._log.info("Load EXTEST command")
     yield master.load_ir(master.EXTEST)
 
-    data_in.binstr = "100111"
+    assert dut.tap_ioconn0__core__i == 0
+    assert dut.tap_ioconn1__pad__o == 1
+    assert dut.tap_ioconn2__pad__o == 0
+    assert dut.tap_ioconn2__pad__oe == 0
+    assert dut.tap_ioconn3__core__i == 1
+    assert dut.tap_ioconn3__pad__o == 1
+    assert dut.tap_ioconn3__pad__oe == 0
+
+    data_in.binstr = "1011001"
     dut._log.info("  input data {}".format(data_in.binstr))
     
     # Set the ios pins
-    dut.top_coreo = 1
-    dut.top_coreoe = 3
-    dut.top_padi = 2
+    dut.tap_ioconn0__pad__i = 0
+    dut.tap_ioconn1__core__o = 1
+    dut.tap_ioconn2__core__o = 0
+    dut.tap_ioconn2__core__oe = 0
+    dut.tap_ioconn3__pad__i = 1
+    dut.tap_ioconn3__core__o = 1
+    dut.tap_ioconn3__core__oe = 0
     yield master.shift_data(data_in)
     dut._log.info("  output: {}".format(master.result.binstr))
-    assert(master.result.binstr == "011101")
+    assert(master.result.binstr == "0100110")
+
+    assert dut.tap_ioconn0__core__i == 1
+    assert dut.tap_ioconn1__pad__o == 0
+    assert dut.tap_ioconn2__pad__o == 1
+    assert dut.tap_ioconn2__pad__oe == 1
+    assert dut.tap_ioconn3__core__i == 0
+    assert dut.tap_ioconn3__pad__o == 0
+    assert dut.tap_ioconn3__pad__oe == 1
+
+    yield master.reset()
+
+    assert dut.tap_ioconn0__core__i == 0
+    assert dut.tap_ioconn1__pad__o == 1
+    assert dut.tap_ioconn2__pad__o == 0
+    assert dut.tap_ioconn2__pad__oe == 0
+    assert dut.tap_ioconn3__core__i == 1
+    assert dut.tap_ioconn3__pad__o == 1
+    assert dut.tap_ioconn3__pad__oe == 0
 
-    dut._log.info("Do a capture of the last loaded data")
-    yield master.shift_data([])