3 from enum
import Enum
, auto
5 from nmigen
import (Elaboratable
, Signal
, Module
, ClockDomain
, Cat
, Record
,
7 from nmigen
.hdl
.rec
import Direction
, Layout
8 from nmigen
.tracer
import get_var_name
10 from nmigen_soc
.wishbone
import Interface
as WishboneInterface
12 from .bus
import Interface
15 "TAP", "ShiftReg", "IOType", "IOConn",
19 class _FSM(Elaboratable
):
20 """TAP subblock for the FSM"""
21 def __init__(self
, *, bus
):
24 self
.capture
= Signal()
26 self
.update
= Signal()
28 self
.posjtag
= ClockDomain("posjtag", local
=True)
29 self
.negjtag
= ClockDomain("negjtag", local
=True, clk_edge
="neg")
33 def elaborate(self
, platform
):
38 self
.posjtag
.clk
.eq(self
._bus
.tck
),
39 self
.posjtag
.rst
.eq(rst
),
40 self
.negjtag
.clk
.eq(self
._bus
.tck
),
41 self
.negjtag
.rst
.eq(rst
),
44 # Make local clock domain optionally using trst of JTAG bus as reset
45 if hasattr(self
._bus
, "trst"):
46 m
.domains
.local
= local
= ClockDomain(local
=True)
47 m
.d
.comb
+= local
.rst
.eq(self
._bus
.trst
)
49 m
.domains
.local
= local
= ClockDomain(local
=True, reset_less
=True)
50 m
.d
.comb
+= local
.clk
.eq(self
._bus
.tck
)
52 with m
.FSM(domain
="local") as fsm
:
53 with m
.State("TestLogicReset"):
54 # Be sure to reset isir, isdr
59 with m
.If(self
._bus
.tms
== 0):
60 m
.next
= "RunTestIdle"
61 with m
.State("RunTestIdle"):
62 # Be sure to reset isir, isdr
67 with m
.If(self
._bus
.tms
== 1):
68 m
.next
= "SelectDRScan"
69 with m
.State("SelectDRScan"):
70 with m
.If(self
._bus
.tms
== 0):
71 m
.d
.local
+= self
.isdr
.eq(1)
72 m
.next
= "CaptureState"
74 m
.next
= "SelectIRScan"
75 with m
.State("SelectIRScan"):
76 with m
.If(self
._bus
.tms
== 0):
77 m
.d
.local
+= self
.isir
.eq(1)
78 m
.next
= "CaptureState"
80 m
.next
= "TestLogicReset"
81 with m
.State("CaptureState"):
82 with m
.If(self
._bus
.tms
== 0):
86 with m
.State("ShiftState"):
87 with m
.If(self
._bus
.tms
== 1):
89 with m
.State("Exit1"):
90 with m
.If(self
._bus
.tms
== 0):
93 m
.next
= "UpdateState"
94 with m
.State("Pause"):
95 with m
.If(self
._bus
.tms
== 1):
97 with m
.State("Exit2"):
98 with m
.If(self
._bus
.tms
== 0):
101 m
.next
= "UpdateState"
102 with m
.State("UpdateState"):
107 with m
.If(self
._bus
.tms
== 0):
108 m
.next
= "RunTestIdle"
110 m
.next
= "SelectDRScan"
113 rst
.eq(fsm
.ongoing("TestLogicReset")),
114 self
.capture
.eq(fsm
.ongoing("CaptureState")),
115 self
.shift
.eq(fsm
.ongoing("ShiftState")),
116 self
.update
.eq(fsm
.ongoing("UpdateState")),
121 class _IRBlock(Elaboratable
):
122 """TAP subblock for handling the IR shift register"""
123 def __init__(self
, *, ir_width
, cmd_idcode
,
124 tdi
, capture
, shift
, update
,
127 self
.ir
= Signal(ir_width
, reset
=cmd_idcode
)
131 self
._capture
= capture
133 self
._update
= update
135 def elaborate(self
, platform
):
138 shift_ir
= Signal(len(self
.ir
), reset_less
=True)
140 m
.d
.comb
+= self
.tdo
.eq(self
.ir
[0])
141 with m
.If(self
._capture
):
142 m
.d
.posjtag
+= shift_ir
.eq(self
.ir
)
143 with m
.Elif(self
._shift
):
144 m
.d
.posjtag
+= shift_ir
.eq(Cat(shift_ir
[1:], self
._tdi
))
145 with m
.Elif(self
._update
):
146 # For ir we only update it on the rising edge of clock
147 # to avoid that we already have the new ir value when still in
149 m
.d
.posjtag
+= self
.ir
.eq(shift_ir
)
159 class IOConn(Record
):
160 """TAP subblock representing the interface for an JTAG IO cell.
161 It contains signal to connect to the core and to the pad
163 This object is normally only allocated and returned from ``TAP.add_io``
164 It is a Record subclass.
168 core: subrecord with signals for the core
169 i: Signal(1), present only for IOType.In and IOType.InTriOut.
170 Signal input to core with pad input value.
171 o: Signal(1), present only for IOType.Out, IOType.TriOut and IOType.InTriOut.
172 Signal output from core with the pad output value.
173 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
174 Signal output from core with the pad output enable value.
175 pad: subrecord with for the pad
176 i: Signal(1), present only for IOType.In and IOType.InTriOut
177 Output from pad with pad input value for core.
178 o: Signal(1), present only for IOType.Out, IOType.TriOut and IOType.InTriOut.
179 Input to pad with pad output value.
180 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
181 Input to pad with pad output enable value.
186 if iotype
in (IOType
.In
, IOType
.InTriOut
):
187 sigs
.append(("i", 1))
188 if iotype
in (IOType
.Out
, IOType
.TriOut
, IOType
.InTriOut
):
189 sigs
.append(("o", 1))
190 if iotype
in (IOType
.TriOut
, IOType
.InTriOut
):
191 sigs
.append(("oe", 1))
193 return Layout((("core", sigs
), ("pad", sigs
)))
195 def __init__(self
, *, iotype
, name
=None, src_loc_at
=0):
196 super().__init
__(self
.__class
__.layout(iotype
), name
=name
, src_loc_at
=src_loc_at
+1)
198 self
._iotype
= iotype
200 class _IDBypassBlock(Elaboratable
):
201 """TAP subblock for the ID shift register"""
202 def __init__(self
, *, manufacturer_id
, part_number
, version
,
203 tdi
, capture
, shift
, update
, bypass
,
206 if not isinstance(manufacturer_id
, Const
) and len(manufacturer_id
) != 11:
207 raise ValueError("manufacturer_id has to be Const of length 11")
208 if not isinstance(part_number
, Const
) and len(manufacturer_id
) != 16:
209 raise ValueError("part_number has to be Const of length 16")
210 if not isinstance(version
, Const
) and len(version
) != 4:
211 raise ValueError("version has to be Const of length 4")
212 self
._id
= Cat(Const(1,1), manufacturer_id
, part_number
, version
)
214 self
.tdo
= Signal(name
=name
+"_tdo")
217 self
._capture
= capture
219 self
._update
= update
220 self
._bypass
= bypass
222 def elaborate(self
, platform
):
225 sr
= Signal(32, reset_less
=True, name
=self
.name
+"_sr")
227 # Local signals for the module
236 _capture
.eq(self
._capture
),
237 _shift
.eq(self
._shift
),
238 _update
.eq(self
._update
),
239 _bypass
.eq(self
._bypass
),
244 m
.d
.posjtag
+= sr
.eq(self
._id
)
247 m
.d
.posjtag
+= sr
[0].eq(_tdi
)
249 m
.d
.posjtag
+= sr
.eq(Cat(sr
[1:], _tdi
))
254 class ShiftReg(Record
):
255 """Object with interface for extra shift registers on a TAP.
260 cmds : int, default=1
261 The number of corresponding JTAG instructions
263 This object is normally only allocated and returned from ``TAP.add_shiftreg``
264 It is a Record subclass.
268 i: length=sr_length, FANIN
269 The input data sampled during capture state of the TAP
270 ie: length=cmds, FANOUT
271 Indicates that data is to be sampled by the JTAG TAP and
272 should be held stable. The bit indicates the corresponding
273 instruction for which data is asked.
274 This signal is kept high for a whole JTAG TAP clock cycle
275 and may thus be kept higher for more than one clock cycle
276 on the domain where ShiftReg is used.
277 The JTAG protocol does not allow insertion of wait states
278 so data need to be provided before ie goes down. The speed
279 of the response will determine the max. frequency for the
281 o: length=sr_length, FANOUT
282 The value of the shift register.
283 oe: length=cmds, FANOUT
284 Indicates that output is stable and can be sampled downstream because
285 JTAG TAP is in the Update state. The bit indicates the corresponding
286 instruction. The bit is only kept high for one clock cycle.
288 def __init__(self
, *, sr_length
, cmds
=1, name
=None, src_loc_at
=0):
290 ("i", sr_length
, Direction
.FANIN
),
291 ("ie", cmds
, Direction
.FANOUT
),
292 ("o", sr_length
, Direction
.FANOUT
),
293 ("oe", cmds
, Direction
.FANOUT
),
295 super().__init
__(layout
, name
=name
, src_loc_at
=src_loc_at
+1)
298 class TAP(Elaboratable
):
301 self
, *, with_reset
=False, ir_width
=None,
302 manufacturer_id
=Const(0b10001111111, 11), part_number
=Const(1, 16),
304 name
=None, src_loc_at
=0
306 assert((ir_width
is None) or (isinstance(ir_width
, int) and ir_width
>= 2))
307 assert(len(version
) == 4)
310 name
= get_var_name(depth
=src_loc_at
+2, default
="TAP")
312 self
.bus
= Interface(with_reset
=with_reset
, name
=self
.name
+"_bus",
313 src_loc_at
=src_loc_at
+1)
317 self
._ir
_width
= ir_width
318 self
._manufacturer
_id
= manufacturer_id
319 self
._part
_number
= part_number
320 self
._version
= version
322 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at the end
329 def elaborate(self
, platform
):
332 # Determine ir_width if not fixed.
333 ir_max
= max(self
._ircodes
) + 1 # One extra code needed with all ones
334 ir_width
= len("{:b}".format(ir_max
))
335 if self
._ir
_width
is not None:
336 assert self
._ir
_width
>= ir_width
, "Specified JTAG IR width not big enough for allocated shiift registers"
337 ir_width
= self
._ir
_width
339 # TODO: Make commands numbers configurable
345 cmd_bypass
= 2**ir_width
- 1 # All ones
347 m
.submodules
._fsm
= fsm
= _FSM(bus
=self
.bus
)
348 m
.domains
.posjtag
= fsm
.posjtag
349 m
.domains
.negjtag
= fsm
.negjtag
352 m
.submodules
._irblock
= irblock
= _IRBlock(
353 ir_width
=ir_width
, cmd_idcode
=cmd_idcode
, tdi
=self
.bus
.tdi
,
354 capture
=(fsm
.isir
& fsm
.capture
),
355 shift
=(fsm
.isir
& fsm
.shift
),
356 update
=(fsm
.isir
& fsm
.update
),
357 name
=self
.name
+"_ir",
361 select_id
= fsm
.isdr
& ((ir
== cmd_idcode
) |
(ir
== cmd_bypass
))
362 m
.submodules
._idblock
= idblock
= _IDBypassBlock(
363 manufacturer_id
=self
._manufacturer
_id
, part_number
=self
._part
_number
,
364 version
=self
._version
, tdi
=self
.bus
.tdi
,
365 capture
=(select_id
& fsm
.capture
),
366 shift
=(select_id
& fsm
.shift
),
367 update
=(select_id
& fsm
.update
),
368 bypass
=(ir
== cmd_bypass
),
369 name
=self
.name
+"_id",
372 io_capture
= Signal()
376 io_bd2core
= Signal()
377 sample
= (ir
== cmd_extest
) |
(ir
== cmd_sample
)
378 preload
= (ir
== cmd_preload
)
379 select_io
= fsm
.isdr
& (sample | preload
)
381 io_capture
.eq(sample
& fsm
.capture
), # Don't capture if not sample (like for PRELOAD)
382 io_shift
.eq(select_io
& fsm
.shift
),
383 io_update
.eq(select_io
& fsm
.update
),
384 io_bd2io
.eq(ir
== cmd_extest
),
385 io_bd2core
.eq(ir
== cmd_intest
),
387 io_tdo
= self
._elaborate
_ios
(
389 capture
=io_capture
, shift
=io_shift
, update
=io_update
,
390 bd2io
=io_bd2io
, bd2core
=io_bd2core
,
393 tdo
= Signal(name
=self
.name
+"_tdo")
394 with m
.If(select_ir
):
395 m
.d
.comb
+= tdo
.eq(irblock
.tdo
)
396 with m
.Elif(select_id
):
397 m
.d
.comb
+= tdo
.eq(idblock
.tdo
)
398 with m
.Elif(select_io
):
399 m
.d
.comb
+= tdo
.eq(io_tdo
)
401 self
._elaborate
_shiftregs
(
402 m
, capture
=fsm
.capture
, shift
=fsm
.shift
, update
=fsm
.update
,
403 ir
=irblock
.ir
, tdo_jtag
=tdo
405 self
._elaborate
_wishbones
(m
)
410 def add_io(self
, *, iotype
, name
=None, src_loc_at
=0):
411 """Add a io cell to the boundary scan chain
414 - iotype: :class:`IOType` enum.
420 name
= "ioconn" + str(len(self
._ios
))
422 ioconn
= IOConn(iotype
=iotype
, name
=name
, src_loc_at
=src_loc_at
+1)
423 self
._ios
.append(ioconn
)
426 def _elaborate_ios(self
, *, m
, capture
, shift
, update
, bd2io
, bd2core
):
433 length
= sum(connlength
[conn
._iotype
] for conn
in self
._ios
)
435 io_sr
= Signal(length
)
436 io_bd
= Signal(length
)
440 for conn
in self
._ios
:
441 if conn
._iotype
== IOType
.In
:
442 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.pad
.i
)
444 elif conn
._iotype
== IOType
.Out
:
445 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.core
.o
)
447 elif conn
._iotype
== IOType
.TriOut
:
449 io_sr
[idx
].eq(conn
.core
.o
),
450 io_sr
[idx
+1].eq(conn
.core
.oe
),
453 elif conn
._iotype
== IOType
.InTriOut
:
455 io_sr
[idx
].eq(conn
.pad
.i
),
456 io_sr
[idx
+1].eq(conn
.core
.o
),
457 io_sr
[idx
+2].eq(conn
.core
.oe
),
461 raise("Internal error")
462 assert idx
== length
, "Internal error"
464 m
.d
.posjtag
+= io_sr
.eq(Cat(self
.bus
.tdi
, io_sr
[:-1]))
466 m
.d
.negjtag
+= io_bd
.eq(io_sr
)
469 for conn
in self
._ios
:
470 if conn
._iotype
== IOType
.In
:
471 m
.d
.comb
+= conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
))
473 elif conn
._iotype
== IOType
.Out
:
474 m
.d
.comb
+= conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
))
476 elif conn
._iotype
== IOType
.TriOut
:
478 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
)),
479 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.oe
)),
482 elif conn
._iotype
== IOType
.InTriOut
:
484 conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
)),
485 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.o
)),
486 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+2], conn
.core
.oe
)),
490 raise("Internal error")
491 assert idx
== length
, "Internal error"
496 def add_shiftreg(self
, *, ircode
, length
, domain
="sync", name
=None, src_loc_at
=0):
497 """Add a shift register to the JTAG interface
500 - ircode: code(s) for the IR; int or sequence of ints. In the latter case this
501 shiftreg is shared between different IR codes.
502 - length: the length of the shift register
503 - domain: the domain on which the signal will be used"""
509 ir_it
= ircodes
= (ircode
,)
510 for _ircode
in ir_it
:
511 if not isinstance(_ircode
, int) or _ircode
<= 0:
512 raise ValueError("IR code '{}' is not an int greater than 0".format(_ircode
))
513 if _ircode
in self
._ircodes
:
514 raise ValueError("IR code '{}' already taken".format(_ircode
))
516 self
._ircodes
.extend(ircodes
)
519 name
= "sr{}".format(len(self
._srs
))
520 sr
= ShiftReg(sr_length
=length
, cmds
=len(ircodes
), name
=name
, src_loc_at
=src_loc_at
+1)
521 self
._srs
.append((ircodes
, domain
, sr
))
525 def _elaborate_shiftregs(self
, m
, capture
, shift
, update
, ir
, tdo_jtag
):
526 # tdos is tuple of (tdo, tdo_en) for each shiftreg
528 for ircodes
, domain
, sr
in self
._srs
:
529 reg
= Signal(len(sr
.o
), name
=sr
.name
+"_reg")
530 m
.d
.comb
+= sr
.o
.eq(reg
)
532 isir
= Signal(len(ircodes
), name
=sr
.name
+"_isir")
533 sr_capture
= Signal(name
=sr
.name
+"_capture")
534 sr_shift
= Signal(name
=sr
.name
+"_shift")
535 sr_update
= Signal(name
=sr
.name
+"_update")
537 isir
.eq(Cat(ir
== ircode
for ircode
in ircodes
)),
538 sr_capture
.eq((isir
!= 0) & capture
),
539 sr_shift
.eq((isir
!= 0) & shift
),
540 sr_update
.eq((isir
!= 0) & update
),
543 # update signal is on the JTAG clockdomain, sr.oe is on `domain` clockdomain
544 # latch update in `domain` clockdomain and see when it has falling edge.
545 # At that edge put isir in sr.oe for one `domain` clockdomain
546 update_core
= Signal(name
=sr
.name
+"_update_core")
547 update_core_prev
= Signal(name
=sr
.name
+"_update_core_prev")
549 update_core
.eq(sr_update
), # This is CDC from JTAG domain to given domain
550 update_core_prev
.eq(update_core
)
552 with m
.If(update_core_prev
& ~update_core
):
553 # Falling edge of update
554 m
.d
[domain
] += sr
.oe
.eq(isir
)
556 m
.d
[domain
] += sr
.oe
.eq(0)
559 m
.d
.posjtag
+= reg
.eq(Cat(reg
[1:], self
.bus
.tdi
))
560 with m
.If(sr_capture
):
561 m
.d
.posjtag
+= reg
.eq(sr
.i
)
563 # tdo = reg[0], tdo_en = shift
564 tdos
.append((reg
[0], sr_shift
))
567 # Assign the right tdo to the bus tdo
568 for i
, (tdo
, tdo_en
) in enumerate(tdos
):
571 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
574 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
578 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
580 # Always connect tdo_jtag to
581 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
584 def add_wishbone(self
, *, ircodes
, address_width
, data_width
, granularity
=None, domain
="sync",
585 name
=None, src_loc_at
=0):
586 """Add a wishbone interface
588 In order to allow high JTAG clock speed, data will be cached. This means that if data is
589 output the value of the next address will be read automatically.
593 ircodes: sequence of three integer for the JTAG IR codes;
594 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
595 has a shift register of length 'address_width', the two other codes
596 share a shift register of length data_width.
597 address_width: width of the address
598 data_width: width of the data
601 wb: nmigen_soc.wishbone.bus.Interface
602 The Wishbone interface, is pipelined and has stall field.
604 if len(ircodes
) != 3:
605 raise ValueError("3 IR Codes have to be provided")
608 name
= "wb" + str(len(self
._wbs
))
609 sr_addr
= self
.add_shiftreg(
610 ircode
=ircodes
[0], length
=address_width
, domain
=domain
,
613 sr_data
= self
.add_shiftreg(
614 ircode
=ircodes
[1:], length
=data_width
, domain
=domain
,
618 wb
= WishboneInterface(data_width
=data_width
, addr_width
=address_width
,
619 granularity
=granularity
, features
={"stall", "lock", "err", "rty"},
620 name
=name
, src_loc_at
=src_loc_at
+1)
622 self
._wbs
.append((sr_addr
, sr_data
, wb
, domain
))
626 def _elaborate_wishbones(self
, m
):
627 for sr_addr
, sr_data
, wb
, domain
in self
._wbs
:
628 m
.d
.comb
+= sr_addr
.i
.eq(wb
.adr
)
630 if hasattr(wb
, "sel"):
632 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
634 with m
.FSM(domain
=domain
) as fsm
:
635 with m
.State("IDLE"):
636 with m
.If(sr_addr
.oe
): # WBADDR code
637 m
.d
[domain
] += wb
.adr
.eq(sr_addr
.o
)
639 with m
.Elif(sr_data
.oe
[0]): # WBREAD code
641 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
643 with m
.Elif(sr_data
.oe
[1]): # WBWRITE code
644 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
646 with m
.State("READ"):
647 with m
.If(~wb
.stall
):
649 with m
.State("READACK"):
651 # Store read data in sr_data.i and keep it there til next read
652 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
654 with m
.State("WRITEREAD"):
655 with m
.If(~wb
.stall
):
656 m
.next
= "WRITEREADACK"
657 with m
.State("WRITEREADACK"):
659 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
663 wb
.cyc
.eq(~fsm
.ongoing("IDLE")),
664 wb
.stb
.eq(fsm
.ongoing("READ") | fsm
.ongoing("WRITEREAD")),
665 wb
.we
.eq(fsm
.ongoing("WRITEREAD")),