Renamed jtag.py -> tap.py.
authorStaf Verhaegen <staf@stafverhaegen.be>
Thu, 5 Dec 2019 19:48:18 +0000 (20:48 +0100)
committerStaf Verhaegen <staf@stafverhaegen.be>
Fri, 6 Dec 2019 19:15:45 +0000 (20:15 +0100)
c4m/nmigen/jtag/__init__.py
c4m/nmigen/jtag/jtag.py [deleted file]
c4m/nmigen/jtag/tap.py [new file with mode: 0755]

index 9e6cc09dc22b8494e64fe39b77da1630dec3f747..5cafc60b86b0402e857ba627c67ead051c4d2ce4 100644 (file)
@@ -1,2 +1,3 @@
 from .pmod import *
 from .bus import *
 from .pmod import *
 from .bus import *
+from .tap import *
diff --git a/c4m/nmigen/jtag/jtag.py b/c4m/nmigen/jtag/jtag.py
deleted file mode 100755 (executable)
index 0e4de64..0000000
+++ /dev/null
@@ -1,357 +0,0 @@
-#!/bin/env python3
-import os
-
-from nmigen import *
-from nmigen.build import *
-from nmigen.lib.io import *
-
-from wishbone import Wishbone
-
-__all__ = [
-    "JTAG",
-]
-
-#TODO: Provide more documentation
-
-
-class ShiftReg(Elaboratable):
-    def __init__(self, ircodes, length, domain):
-        # The sr record will be returned to user code
-        self.sr = Record([("i", length), ("o", length), ("oe", len(ircodes)), ("ack", 1)])
-        # The next attributes are for JTAG class usage only
-        self.ir = None # made None as width is not known yet
-        self.tdi = Signal()
-        self.tdo = Signal()
-        self.tdo_en = Signal()
-        self.capture = Signal()
-        self.shift = Signal()
-        self.update = Signal()
-        self.jtag_cd = None # The JTAG clock domain
-
-        ##
-
-        self._ircodes = ircodes
-        self._domain = domain
-
-    def elaborate(self, platform):
-        length = len(self.sr.o)
-        domain = self._domain
-
-        m = Module()
-
-        m.domains.jtag = self.jtag_cd
-
-        sr_jtag = Signal(length)
-
-        assert isinstance(self.ir, Signal)
-        isir = Signal(len(self._ircodes))
-        capture = Signal()
-        shift = Signal()
-        update = Signal()
-        m.d.comb += [
-            isir.eq(Cat(self.ir == ircode for ircode in self._ircodes)),
-            capture.eq((isir != 0) & self.capture),
-            shift.eq((isir != 0) & self.shift),
-            update.eq((isir != 0) & self.update),
-        ]
-
-        # On update set o, oe and wait for ack
-        # update signal is on JTAG clockdomain, latch it
-        update_core = Signal()
-        m.d[domain] += update_core.eq(update) # This is CDC from JTAG domain to given domain
-        with m.FSM(domain=domain):
-            with m.State("IDLE"):
-                m.d.comb += self.sr.oe.eq(0)
-                with m.If(update_core):
-                    # Latch sr_jtag cross domain but it should be stable due to latching of update_core
-                    m.d[domain] += self.sr.o.eq(sr_jtag)
-                    # Wait one cycle to raise oe so sr.o has one more cycle to stabilize
-                    m.next = "WAIT4ACK"
-            with m.State("WAIT4ACK"):
-                m.d.comb += self.sr.oe.eq(isir)
-                with m.If(self.sr.ack):
-                    m.next = "WAIT4END"
-            with m.State("WAIT4END"):
-                m.d.comb += self.sr.oe.eq(0)
-                with m.If(~update_core):
-                    m.next = "IDLE"
-
-        m.d.comb += [
-            self.tdo.eq(sr_jtag[0]),
-            self.tdo_en.eq(shift),
-        ]
-
-        with m.If(shift):
-            m.d.jtag += sr_jtag.eq(Cat(sr_jtag[1:], self.tdi))
-        with m.If(capture):
-            m.d.jtag += sr_jtag.eq(self.sr.i)
-
-        return m
-
-class JTAGWishbone(Elaboratable):
-    def __init__(self, sr_addr, sr_data, wb, domain):
-        self._sr_addr = sr_addr
-        self._sr_data = sr_data
-        self._wb = wb
-        self._domain = domain
-
-        # To be set by JTAG
-        self._ir = None
-
-    def elaborate(self, platform):
-        sr_addr = self._sr_addr
-        sr_data = self._sr_data
-        wb = self._wb
-        domain = self._domain
-        ir = self._ir
-
-        m = Module()
-
-        if hasattr(wb, "sel"):
-            # Always selected
-            m.d.comb += [s.eq(1) for s in wb.sel]
-
-        # Immediately ack oe
-        m.d[domain] += [
-            sr_addr.ack.eq(sr_addr.oe),
-            sr_data.ack.eq(sr_data.oe != 0),
-        ]
-
-        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.next = "READ"
-                with m.If(sr_data.oe[0]): # WBREAD code
-                    m.d[domain] += wb.addr.eq(wb.addr + 1)
-                    m.next = "READ"
-                with m.If(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):
-                    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.next = "READ"
-
-        return m
-
-
-class JTAG(Elaboratable):
-    @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()
-
-
-    def __init__(self, io_count, *, ir_width=None, manufacturer_id=Const(0b10001111111, 11),
-                 part_number=Const(1, 16), version=Const(0, 4)
-    ):
-        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)
-
-        # TODO: Handle IOs with different directions
-        self.tck  = Signal()
-        self.tms  = Signal()
-        self.tdo  = Signal()
-        self.tdi  = Signal()
-        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.jtag_cd = ClockDomain(name="jtag", local=True) # Own clock domain using TCK as clock signal
-
-        ##
-
-        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._wbs = []
-
-    def elaborate(self, platform):
-        JTAG._add_files(platform, "jtag" + os.path.sep)
-
-        m = Module()
-
-        tdo_jtag = Signal()
-        reset = Signal()
-        capture = Signal()
-        shift = Signal()
-        update = Signal()
-
-
-        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"
-            ir_width = self._ir_width
-        ir = Signal(ir_width)
-
-        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)
-
-        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.tck,
-            "i_TMS": self.tms,
-            "i_TDI": self.tdi,
-            "o_TDO": tdo_jtag,
-            "i_TRST_N": Const(1),
-            "o_RESET": reset,
-            "o_DRCAPTURE": capture,
-            "o_DRSHIFT": shift,
-            "o_DRUPDATE": update,
-            "o_IR": 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.d.comb += [
-            self.jtag_cd.clk.eq(self.tck),
-            self.jtag_cd.rst.eq(reset),
-        ]
-
-        for i, sr in enumerate(self._srs):
-            m.submodules["sr{}".format(i)] = sr
-            sr.ir = ir
-            m.d.comb += [
-                sr.tdi.eq(self.tdi),
-                sr.capture.eq(capture),
-                sr.shift.eq(shift),
-                sr.update.eq(update),
-            ]
-
-        if len(self._srs) > 0:
-            first = True
-            for sr in self._srs:
-                if first:
-                    first = False
-                    with m.If(sr.tdo_en):
-                        m.d.comb += self.tdo.eq(sr.tdo)
-                else:
-                    with m.Elif(sr.tdo_en):
-                        m.d.comb += self.tdo.eq(sr.tdo)
-            with m.Else():
-                m.d.comb += self.tdo.eq(tdo_jtag)
-        else:
-            m.d.comb += self.tdo.eq(tdo_jtag)
-
-        for i, wb in enumerate(self._wbs):
-            m.submodules["wb{}".format(i)] = wb
-            wb._ir = ir
-
-        return m
-
-
-    def add_shiftreg(self, ircode, length, domain="sync"):
-        """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.
-        - length: the length of the shift register
-        - domain: the domain on which the signal will be used"""
-
-        try:
-            ir_it = iter(ircode)
-            ircodes = ircode
-        except TypeError:
-            ir_it = ircodes = (ircode,)
-        for _ircode in ir_it:
-            assert(isinstance(_ircode, int) and _ircode > 0 and _ircode not in self._ircodes)
-
-        sr = ShiftReg(ircodes, length, domain)
-        sr.jtag_cd = self.jtag_cd
-        self._ircodes.extend(ircodes)
-        self._srs.append(sr)
-
-        return sr.sr
-
-
-    def add_wishbone(self, ircodes, address_width, data_width, sel_width=None, domain="sync"):
-        """Add a wishbone interface
-
-        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"""
-
-        assert len(ircodes) == 3
-
-        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)
-
-        self._wbs.append(JTAGWishbone(sr_addr, sr_data, wb, domain))
-
-        return wb
diff --git a/c4m/nmigen/jtag/tap.py b/c4m/nmigen/jtag/tap.py
new file mode 100755 (executable)
index 0000000..0e4de64
--- /dev/null
@@ -0,0 +1,357 @@
+#!/bin/env python3
+import os
+
+from nmigen import *
+from nmigen.build import *
+from nmigen.lib.io import *
+
+from wishbone import Wishbone
+
+__all__ = [
+    "JTAG",
+]
+
+#TODO: Provide more documentation
+
+
+class ShiftReg(Elaboratable):
+    def __init__(self, ircodes, length, domain):
+        # The sr record will be returned to user code
+        self.sr = Record([("i", length), ("o", length), ("oe", len(ircodes)), ("ack", 1)])
+        # The next attributes are for JTAG class usage only
+        self.ir = None # made None as width is not known yet
+        self.tdi = Signal()
+        self.tdo = Signal()
+        self.tdo_en = Signal()
+        self.capture = Signal()
+        self.shift = Signal()
+        self.update = Signal()
+        self.jtag_cd = None # The JTAG clock domain
+
+        ##
+
+        self._ircodes = ircodes
+        self._domain = domain
+
+    def elaborate(self, platform):
+        length = len(self.sr.o)
+        domain = self._domain
+
+        m = Module()
+
+        m.domains.jtag = self.jtag_cd
+
+        sr_jtag = Signal(length)
+
+        assert isinstance(self.ir, Signal)
+        isir = Signal(len(self._ircodes))
+        capture = Signal()
+        shift = Signal()
+        update = Signal()
+        m.d.comb += [
+            isir.eq(Cat(self.ir == ircode for ircode in self._ircodes)),
+            capture.eq((isir != 0) & self.capture),
+            shift.eq((isir != 0) & self.shift),
+            update.eq((isir != 0) & self.update),
+        ]
+
+        # On update set o, oe and wait for ack
+        # update signal is on JTAG clockdomain, latch it
+        update_core = Signal()
+        m.d[domain] += update_core.eq(update) # This is CDC from JTAG domain to given domain
+        with m.FSM(domain=domain):
+            with m.State("IDLE"):
+                m.d.comb += self.sr.oe.eq(0)
+                with m.If(update_core):
+                    # Latch sr_jtag cross domain but it should be stable due to latching of update_core
+                    m.d[domain] += self.sr.o.eq(sr_jtag)
+                    # Wait one cycle to raise oe so sr.o has one more cycle to stabilize
+                    m.next = "WAIT4ACK"
+            with m.State("WAIT4ACK"):
+                m.d.comb += self.sr.oe.eq(isir)
+                with m.If(self.sr.ack):
+                    m.next = "WAIT4END"
+            with m.State("WAIT4END"):
+                m.d.comb += self.sr.oe.eq(0)
+                with m.If(~update_core):
+                    m.next = "IDLE"
+
+        m.d.comb += [
+            self.tdo.eq(sr_jtag[0]),
+            self.tdo_en.eq(shift),
+        ]
+
+        with m.If(shift):
+            m.d.jtag += sr_jtag.eq(Cat(sr_jtag[1:], self.tdi))
+        with m.If(capture):
+            m.d.jtag += sr_jtag.eq(self.sr.i)
+
+        return m
+
+class JTAGWishbone(Elaboratable):
+    def __init__(self, sr_addr, sr_data, wb, domain):
+        self._sr_addr = sr_addr
+        self._sr_data = sr_data
+        self._wb = wb
+        self._domain = domain
+
+        # To be set by JTAG
+        self._ir = None
+
+    def elaborate(self, platform):
+        sr_addr = self._sr_addr
+        sr_data = self._sr_data
+        wb = self._wb
+        domain = self._domain
+        ir = self._ir
+
+        m = Module()
+
+        if hasattr(wb, "sel"):
+            # Always selected
+            m.d.comb += [s.eq(1) for s in wb.sel]
+
+        # Immediately ack oe
+        m.d[domain] += [
+            sr_addr.ack.eq(sr_addr.oe),
+            sr_data.ack.eq(sr_data.oe != 0),
+        ]
+
+        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.next = "READ"
+                with m.If(sr_data.oe[0]): # WBREAD code
+                    m.d[domain] += wb.addr.eq(wb.addr + 1)
+                    m.next = "READ"
+                with m.If(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):
+                    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.next = "READ"
+
+        return m
+
+
+class JTAG(Elaboratable):
+    @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()
+
+
+    def __init__(self, io_count, *, ir_width=None, manufacturer_id=Const(0b10001111111, 11),
+                 part_number=Const(1, 16), version=Const(0, 4)
+    ):
+        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)
+
+        # TODO: Handle IOs with different directions
+        self.tck  = Signal()
+        self.tms  = Signal()
+        self.tdo  = Signal()
+        self.tdi  = Signal()
+        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.jtag_cd = ClockDomain(name="jtag", local=True) # Own clock domain using TCK as clock signal
+
+        ##
+
+        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._wbs = []
+
+    def elaborate(self, platform):
+        JTAG._add_files(platform, "jtag" + os.path.sep)
+
+        m = Module()
+
+        tdo_jtag = Signal()
+        reset = Signal()
+        capture = Signal()
+        shift = Signal()
+        update = Signal()
+
+
+        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"
+            ir_width = self._ir_width
+        ir = Signal(ir_width)
+
+        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)
+
+        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.tck,
+            "i_TMS": self.tms,
+            "i_TDI": self.tdi,
+            "o_TDO": tdo_jtag,
+            "i_TRST_N": Const(1),
+            "o_RESET": reset,
+            "o_DRCAPTURE": capture,
+            "o_DRSHIFT": shift,
+            "o_DRUPDATE": update,
+            "o_IR": 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.d.comb += [
+            self.jtag_cd.clk.eq(self.tck),
+            self.jtag_cd.rst.eq(reset),
+        ]
+
+        for i, sr in enumerate(self._srs):
+            m.submodules["sr{}".format(i)] = sr
+            sr.ir = ir
+            m.d.comb += [
+                sr.tdi.eq(self.tdi),
+                sr.capture.eq(capture),
+                sr.shift.eq(shift),
+                sr.update.eq(update),
+            ]
+
+        if len(self._srs) > 0:
+            first = True
+            for sr in self._srs:
+                if first:
+                    first = False
+                    with m.If(sr.tdo_en):
+                        m.d.comb += self.tdo.eq(sr.tdo)
+                else:
+                    with m.Elif(sr.tdo_en):
+                        m.d.comb += self.tdo.eq(sr.tdo)
+            with m.Else():
+                m.d.comb += self.tdo.eq(tdo_jtag)
+        else:
+            m.d.comb += self.tdo.eq(tdo_jtag)
+
+        for i, wb in enumerate(self._wbs):
+            m.submodules["wb{}".format(i)] = wb
+            wb._ir = ir
+
+        return m
+
+
+    def add_shiftreg(self, ircode, length, domain="sync"):
+        """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.
+        - length: the length of the shift register
+        - domain: the domain on which the signal will be used"""
+
+        try:
+            ir_it = iter(ircode)
+            ircodes = ircode
+        except TypeError:
+            ir_it = ircodes = (ircode,)
+        for _ircode in ir_it:
+            assert(isinstance(_ircode, int) and _ircode > 0 and _ircode not in self._ircodes)
+
+        sr = ShiftReg(ircodes, length, domain)
+        sr.jtag_cd = self.jtag_cd
+        self._ircodes.extend(ircodes)
+        self._srs.append(sr)
+
+        return sr.sr
+
+
+    def add_wishbone(self, ircodes, address_width, data_width, sel_width=None, domain="sync"):
+        """Add a wishbone interface
+
+        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"""
+
+        assert len(ircodes) == 3
+
+        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)
+
+        self._wbs.append(JTAGWishbone(sr_addr, sr_data, wb, domain))
+
+        return wb