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 # JTAG uses both edges of the incoming clock (TCK). set them up here
29 self
.posjtag
= ClockDomain("posjtag", local
=True)
30 self
.negjtag
= ClockDomain("negjtag", local
=True, clk_edge
="neg")
34 def elaborate(self
, platform
):
39 self
.posjtag
.clk
.eq(self
._bus
.tck
),
40 self
.posjtag
.rst
.eq(rst
),
41 self
.negjtag
.clk
.eq(self
._bus
.tck
),
42 self
.negjtag
.rst
.eq(rst
),
45 # Make local clock domain optionally using trst of JTAG bus as reset
46 if hasattr(self
._bus
, "trst"):
47 m
.domains
.local
= local
= ClockDomain(local
=True)
48 m
.d
.comb
+= local
.rst
.eq(self
._bus
.trst
)
50 m
.domains
.local
= local
= ClockDomain(local
=True, reset_less
=True)
51 m
.d
.comb
+= local
.clk
.eq(self
._bus
.tck
)
53 with m
.FSM(domain
="local") as fsm
:
54 with m
.State("TestLogicReset"):
55 # Be sure to reset isir, isdr
60 with m
.If(self
._bus
.tms
== 0):
61 m
.next
= "RunTestIdle"
62 with m
.State("RunTestIdle"):
63 # Be sure to reset isir, isdr
68 with m
.If(self
._bus
.tms
== 1):
69 m
.next
= "SelectDRScan"
70 with m
.State("SelectDRScan"):
71 with m
.If(self
._bus
.tms
== 0):
72 m
.d
.local
+= self
.isdr
.eq(1)
73 m
.next
= "CaptureState"
75 m
.next
= "SelectIRScan"
76 with m
.State("SelectIRScan"):
77 with m
.If(self
._bus
.tms
== 0):
78 m
.d
.local
+= self
.isir
.eq(1)
79 m
.next
= "CaptureState"
81 m
.next
= "TestLogicReset"
82 with m
.State("CaptureState"):
83 with m
.If(self
._bus
.tms
== 0):
87 with m
.State("ShiftState"):
88 with m
.If(self
._bus
.tms
== 1):
90 with m
.State("Exit1"):
91 with m
.If(self
._bus
.tms
== 0):
94 m
.next
= "UpdateState"
95 with m
.State("Pause"):
96 with m
.If(self
._bus
.tms
== 1):
98 with m
.State("Exit2"):
99 with m
.If(self
._bus
.tms
== 0):
100 m
.next
= "ShiftState"
102 m
.next
= "UpdateState"
103 with m
.State("UpdateState"):
108 with m
.If(self
._bus
.tms
== 0):
109 m
.next
= "RunTestIdle"
111 m
.next
= "SelectDRScan"
114 rst
.eq(fsm
.ongoing("TestLogicReset")),
115 self
.capture
.eq(fsm
.ongoing("CaptureState")),
116 self
.shift
.eq(fsm
.ongoing("ShiftState")),
117 self
.update
.eq(fsm
.ongoing("UpdateState")),
123 class _IRBlock(Elaboratable
):
124 """TAP subblock for handling the IR shift register"""
125 def __init__(self
, *, ir_width
, cmd_idcode
,
126 tdi
, capture
, shift
, update
,
129 self
.ir
= Signal(ir_width
, reset
=cmd_idcode
)
133 self
._capture
= capture
135 self
._update
= update
137 def elaborate(self
, platform
):
140 shift_ir
= Signal(len(self
.ir
), reset_less
=True)
142 m
.d
.comb
+= self
.tdo
.eq(self
.ir
[0])
143 with m
.If(self
._capture
):
144 m
.d
.posjtag
+= shift_ir
.eq(self
.ir
)
145 with m
.Elif(self
._shift
):
146 m
.d
.posjtag
+= shift_ir
.eq(Cat(shift_ir
[1:], self
._tdi
))
147 with m
.Elif(self
._update
):
148 # For ir we only update it on the rising edge of clock
149 # to avoid that we already have the new ir value when still in
151 m
.d
.posjtag
+= self
.ir
.eq(shift_ir
)
163 class IOConn(Record
):
164 """TAP subblock representing the interface for an JTAG IO cell.
165 It contains signal to connect to the core and to the pad
167 This object is normally only allocated and returned from ``TAP.add_io``
168 It is a Record subclass.
172 core: subrecord with signals for the core
173 i: Signal(1), present only for IOType.In and IOType.InTriOut.
174 Signal input to core with pad input value.
175 o: Signal(1), present only for IOType.Out, IOType.TriOut and
177 Signal output from core with the pad output value.
178 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
179 Signal output from core with the pad output enable value.
180 pad: subrecord with for the pad
181 i: Signal(1), present only for IOType.In and IOType.InTriOut
182 Output from pad with pad input value for core.
183 o: Signal(1), present only for IOType.Out, IOType.TriOut and
185 Input to pad with pad output value.
186 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
187 Input to pad with pad output enable value.
192 if iotype
in (IOType
.In
, IOType
.InTriOut
):
193 sigs
.append(("i", 1))
194 if iotype
in (IOType
.Out
, IOType
.TriOut
, IOType
.InTriOut
):
195 sigs
.append(("o", 1))
196 if iotype
in (IOType
.TriOut
, IOType
.InTriOut
):
197 sigs
.append(("oe", 1))
199 return Layout((("core", sigs
), ("pad", sigs
)))
201 def __init__(self
, *, iotype
, name
=None, src_loc_at
=0):
202 super().__init
__(self
.__class
__.layout(iotype
), name
=name
,
203 src_loc_at
=src_loc_at
+1)
205 self
._iotype
= iotype
208 class _IDBypassBlock(Elaboratable
):
209 """TAP subblock for the ID shift register"""
210 def __init__(self
, *, manufacturer_id
, part_number
, version
,
211 tdi
, capture
, shift
, update
, bypass
,
214 if (not isinstance(manufacturer_id
, Const
) and
215 len(manufacturer_id
) != 11):
216 raise ValueError("manufacturer_id has to be Const of length 11")
217 if not isinstance(part_number
, Const
) and len(manufacturer_id
) != 16:
218 raise ValueError("part_number has to be Const of length 16")
219 if not isinstance(version
, Const
) and len(version
) != 4:
220 raise ValueError("version has to be Const of length 4")
221 self
._id
= Cat(Const(1,1), manufacturer_id
, part_number
, version
)
223 self
.tdo
= Signal(name
=name
+"_tdo")
226 self
._capture
= capture
228 self
._update
= update
229 self
._bypass
= bypass
231 def elaborate(self
, platform
):
234 sr
= Signal(32, reset_less
=True, name
=self
.name
+"_sr")
236 # Local signals for the module
245 _capture
.eq(self
._capture
),
246 _shift
.eq(self
._shift
),
247 _update
.eq(self
._update
),
248 _bypass
.eq(self
._bypass
),
253 m
.d
.posjtag
+= sr
.eq(self
._id
)
256 m
.d
.posjtag
+= sr
[0].eq(_tdi
)
258 m
.d
.posjtag
+= sr
.eq(Cat(sr
[1:], _tdi
))
263 class ShiftReg(Record
):
264 """Object with interface for extra shift registers on a TAP.
269 cmds : int, default=1
270 The number of corresponding JTAG instructions
272 This object is normally only allocated and returned from ``TAP.add_shiftreg``
273 It is a Record subclass.
277 i: length=sr_length, FANIN
278 The input data sampled during capture state of the TAP
279 ie: length=cmds, FANOUT
280 Indicates that data is to be sampled by the JTAG TAP and
281 should be held stable. The bit indicates the corresponding
282 instruction for which data is asked.
283 This signal is kept high for a whole JTAG TAP clock cycle
284 and may thus be kept higher for more than one clock cycle
285 on the domain where ShiftReg is used.
286 The JTAG protocol does not allow insertion of wait states
287 so data need to be provided before ie goes down. The speed
288 of the response will determine the max. frequency for the
290 o: length=sr_length, FANOUT
291 The value of the shift register.
292 oe: length=cmds, FANOUT
293 Indicates that output is stable and can be sampled downstream because
294 JTAG TAP is in the Update state. The bit indicates the corresponding
295 instruction. The bit is only kept high for one clock cycle.
297 def __init__(self
, *, sr_length
, cmds
=1, name
=None, src_loc_at
=0):
299 ("i", sr_length
, Direction
.FANIN
),
300 ("ie", cmds
, Direction
.FANOUT
),
301 ("o", sr_length
, Direction
.FANOUT
),
302 ("oe", cmds
, Direction
.FANOUT
),
304 super().__init
__(layout
, name
=name
, src_loc_at
=src_loc_at
+1)
307 class TAP(Elaboratable
):
309 def __init__(self
, *, with_reset
=False, ir_width
=None,
310 manufacturer_id
=Const(0b10001111111, 11),
311 part_number
=Const(1, 16),
313 name
=None, src_loc_at
=0):
314 assert((ir_width
is None) or (isinstance(ir_width
, int) and
316 assert(len(version
) == 4)
319 name
= get_var_name(depth
=src_loc_at
+2, default
="TAP")
321 self
.bus
= Interface(with_reset
=with_reset
, name
=self
.name
+"_bus",
322 src_loc_at
=src_loc_at
+1)
326 self
._ir
_width
= ir_width
327 self
._manufacturer
_id
= manufacturer_id
328 self
._part
_number
= part_number
329 self
._version
= version
331 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at end
337 def elaborate(self
, platform
):
340 # Determine ir_width if not fixed.
341 ir_max
= max(self
._ircodes
) + 1 # One extra code needed with all ones
342 ir_width
= len("{:b}".format(ir_max
))
343 if self
._ir
_width
is not None:
344 assert self
._ir
_width
>= ir_width
, "Specified JTAG IR width " \
345 "not big enough for allocated shiift registers"
346 ir_width
= self
._ir
_width
348 # TODO: Make commands numbers configurable
354 cmd_bypass
= 2**ir_width
- 1 # All ones
356 m
.submodules
._fsm
= fsm
= _FSM(bus
=self
.bus
)
357 m
.domains
.posjtag
= fsm
.posjtag
358 m
.domains
.negjtag
= fsm
.negjtag
362 m
.submodules
._irblock
= irblock
= _IRBlock(
363 ir_width
=ir_width
, cmd_idcode
=cmd_idcode
, tdi
=self
.bus
.tdi
,
364 capture
=(fsm
.isir
& fsm
.capture
),
365 shift
=(fsm
.isir
& fsm
.shift
),
366 update
=(fsm
.isir
& fsm
.update
),
367 name
=self
.name
+"_ir",
372 select_id
= fsm
.isdr
& ((ir
== cmd_idcode
) |
(ir
== cmd_bypass
))
373 m
.submodules
._idblock
= idblock
= _IDBypassBlock(
374 manufacturer_id
=self
._manufacturer
_id
,
375 part_number
=self
._part
_number
,
376 version
=self
._version
, tdi
=self
.bus
.tdi
,
377 capture
=(select_id
& fsm
.capture
),
378 shift
=(select_id
& fsm
.shift
),
379 update
=(select_id
& fsm
.update
),
380 bypass
=(ir
== cmd_bypass
),
381 name
=self
.name
+"_id",
384 # IO (Boundary scan) block
385 io_capture
= Signal()
389 io_bd2core
= Signal()
390 sample
= (ir
== cmd_extest
) |
(ir
== cmd_sample
)
391 preload
= (ir
== cmd_preload
)
392 select_io
= fsm
.isdr
& (sample | preload
)
394 io_capture
.eq(sample
& fsm
.capture
), # Don't capture if not sample
396 io_shift
.eq(select_io
& fsm
.shift
),
397 io_update
.eq(select_io
& fsm
.update
),
398 io_bd2io
.eq(ir
== cmd_extest
),
399 io_bd2core
.eq(ir
== cmd_intest
),
401 io_tdo
= self
._elaborate
_ios
(
403 capture
=io_capture
, shift
=io_shift
, update
=io_update
,
404 bd2io
=io_bd2io
, bd2core
=io_bd2core
,
407 # chain tdo: select as appropriate, to go into into shiftregs
408 tdo
= Signal(name
=self
.name
+"_tdo")
409 with m
.If(select_ir
):
410 m
.d
.comb
+= tdo
.eq(irblock
.tdo
)
411 with m
.Elif(select_id
):
412 m
.d
.comb
+= tdo
.eq(idblock
.tdo
)
413 with m
.Elif(select_io
):
414 m
.d
.comb
+= tdo
.eq(io_tdo
)
417 self
._elaborate
_shiftregs
(
418 m
, capture
=fsm
.capture
, shift
=fsm
.shift
, update
=fsm
.update
,
419 ir
=irblock
.ir
, tdo_jtag
=tdo
423 self
._elaborate
_wishbones
(m
)
428 def add_io(self
, *, iotype
, name
=None, src_loc_at
=0):
429 """Add a io cell to the boundary scan chain
432 - iotype: :class:`IOType` enum.
438 name
= "ioconn" + str(len(self
._ios
))
440 ioconn
= IOConn(iotype
=iotype
, name
=name
, src_loc_at
=src_loc_at
+1)
441 self
._ios
.append(ioconn
)
444 def _elaborate_ios(self
, *, m
, capture
, shift
, update
, bd2io
, bd2core
):
451 length
= sum(connlength
[conn
._iotype
] for conn
in self
._ios
)
455 io_sr
= Signal(length
)
456 io_bd
= Signal(length
)
458 # Boundary scan "capture" mode. makes I/O status available via SR
461 for conn
in self
._ios
:
462 if conn
._iotype
== IOType
.In
:
463 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.pad
.i
)
465 elif conn
._iotype
== IOType
.Out
:
466 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.core
.o
)
468 elif conn
._iotype
== IOType
.TriOut
:
470 io_sr
[idx
].eq(conn
.core
.o
),
471 io_sr
[idx
+1].eq(conn
.core
.oe
),
474 elif conn
._iotype
== IOType
.InTriOut
:
476 io_sr
[idx
].eq(conn
.pad
.i
),
477 io_sr
[idx
+1].eq(conn
.core
.o
),
478 io_sr
[idx
+2].eq(conn
.core
.oe
),
482 raise("Internal error")
483 assert idx
== length
, "Internal error"
485 # "Shift" mode (sends out captured data on tdo, sets incoming from tdi)
487 m
.d
.posjtag
+= io_sr
.eq(Cat(self
.bus
.tdi
, io_sr
[:-1]))
491 m
.d
.negjtag
+= io_bd
.eq(io_sr
)
493 # sets up IO (pad<->core) or in testing mode depending on requested
494 # mode, via Muxes controlled by bd2core and bd2io
496 for conn
in self
._ios
:
497 if conn
._iotype
== IOType
.In
:
498 m
.d
.comb
+= conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
))
500 elif conn
._iotype
== IOType
.Out
:
501 m
.d
.comb
+= conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
))
503 elif conn
._iotype
== IOType
.TriOut
:
505 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
)),
506 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.oe
)),
509 elif conn
._iotype
== IOType
.InTriOut
:
511 conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
)),
512 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.o
)),
513 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+2], conn
.core
.oe
)),
517 raise("Internal error")
518 assert idx
== length
, "Internal error"
522 def add_shiftreg(self
, *, ircode
, length
, domain
="sync", name
=None,
524 """Add a shift register to the JTAG interface
527 - ircode: code(s) for the IR; int or sequence of ints. In the latter
528 case this shiftreg is shared between different IR codes.
529 - length: the length of the shift register
530 - domain: the domain on which the signal will be used"""
536 ir_it
= ircodes
= (ircode
,)
537 for _ircode
in ir_it
:
538 if not isinstance(_ircode
, int) or _ircode
<= 0:
539 raise ValueError("IR code '{}' is not an int "
540 "greater than 0".format(_ircode
))
541 if _ircode
in self
._ircodes
:
542 raise ValueError("IR code '{}' already taken".format(_ircode
))
544 self
._ircodes
.extend(ircodes
)
547 name
= "sr{}".format(len(self
._srs
))
548 sr
= ShiftReg(sr_length
=length
, cmds
=len(ircodes
), name
=name
,
549 src_loc_at
=src_loc_at
+1)
550 self
._srs
.append((ircodes
, domain
, sr
))
554 def _elaborate_shiftregs(self
, m
, capture
, shift
, update
, ir
, tdo_jtag
):
555 # tdos is tuple of (tdo, tdo_en) for each shiftreg
557 for ircodes
, domain
, sr
in self
._srs
:
558 reg
= Signal(len(sr
.o
), name
=sr
.name
+"_reg")
559 m
.d
.comb
+= sr
.o
.eq(reg
)
561 isir
= Signal(len(ircodes
), name
=sr
.name
+"_isir")
562 sr_capture
= Signal(name
=sr
.name
+"_capture")
563 sr_shift
= Signal(name
=sr
.name
+"_shift")
564 sr_update
= Signal(name
=sr
.name
+"_update")
566 isir
.eq(Cat(ir
== ircode
for ircode
in ircodes
)),
567 sr_capture
.eq((isir
!= 0) & capture
),
568 sr_shift
.eq((isir
!= 0) & shift
),
569 sr_update
.eq((isir
!= 0) & update
),
572 # update signal is on the JTAG clockdomain, sr.oe is on `domain`
573 # clockdomain latch update in `domain` clockdomain and see when
574 # it has falling edge.
575 # At that edge put isir in sr.oe for one `domain` clockdomain
576 # Using this custom sync <> JTAG domain synchronization avoids
577 # the use of more generic but also higher latency CDC solutions
578 # like FFSynchronizer.
579 update_core
= Signal(name
=sr
.name
+"_update_core")
580 update_core_prev
= Signal(name
=sr
.name
+"_update_core_prev")
582 update_core
.eq(sr_update
), # This is CDC from JTAG domain
584 update_core_prev
.eq(update_core
)
586 with m
.If(update_core_prev
& ~update_core
):
587 # Falling edge of update
588 m
.d
[domain
] += sr
.oe
.eq(isir
)
590 m
.d
[domain
] += sr
.oe
.eq(0)
593 m
.d
.posjtag
+= reg
.eq(Cat(reg
[1:], self
.bus
.tdi
))
594 with m
.If(sr_capture
):
595 m
.d
.posjtag
+= reg
.eq(sr
.i
)
597 # tdo = reg[0], tdo_en = shift
598 tdos
.append((reg
[0], sr_shift
))
601 # Assign the right tdo to the bus tdo
602 for i
, (tdo
, tdo_en
) in enumerate(tdos
):
605 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
608 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
612 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
614 # Always connect tdo_jtag to
615 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
618 def add_wishbone(self
, *, ircodes
, address_width
, data_width
,
619 granularity
=None, domain
="sync", features
=None,
620 name
=None, src_loc_at
=0):
621 """Add a wishbone interface
623 In order to allow high JTAG clock speed, data will be cached.
624 This means that if data is output the value of the next address
625 will be read automatically.
629 ircodes: sequence of three integer for the JTAG IR codes;
630 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
631 has a shift register of length 'address_width', the two other codes
632 share a shift register of length data_width.
633 address_width: width of the address
634 data_width: width of the data
635 features: features required. defaults to stall, lock, err, rty
638 wb: nmigen_soc.wishbone.bus.Interface
639 The Wishbone interface, is pipelined and has stall field.
641 if len(ircodes
) != 3:
642 raise ValueError("3 IR Codes have to be provided")
645 features
={"stall", "lock", "err", "rty"}
647 name
= "wb" + str(len(self
._wbs
))
648 sr_addr
= self
.add_shiftreg(
649 ircode
=ircodes
[0], length
=address_width
, domain
=domain
,
652 sr_data
= self
.add_shiftreg(
653 ircode
=ircodes
[1:], length
=data_width
, domain
=domain
,
657 wb
= WishboneInterface(data_width
=data_width
, addr_width
=address_width
,
658 granularity
=granularity
, features
=features
,
659 name
=name
, src_loc_at
=src_loc_at
+1)
661 self
._wbs
.append((sr_addr
, sr_data
, wb
, domain
))
665 def _elaborate_wishbones(self
, m
):
666 for sr_addr
, sr_data
, wb
, domain
in self
._wbs
:
667 m
.d
.comb
+= sr_addr
.i
.eq(wb
.adr
)
669 if hasattr(wb
, "sel"):
671 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
673 with m
.FSM(domain
=domain
) as fsm
:
674 with m
.State("IDLE"):
675 with m
.If(sr_addr
.oe
): # WBADDR code
676 m
.d
[domain
] += wb
.adr
.eq(sr_addr
.o
)
678 with m
.Elif(sr_data
.oe
[0]): # WBREAD code
680 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
682 with m
.Elif(sr_data
.oe
[1]): # WBWRITE code
683 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
685 with m
.State("READ"):
686 if not hasattr(wb
, "stall"):
689 with m
.If(~wb
.stall
):
691 with m
.State("READACK"):
693 # Store read data in sr_data.i
694 # and keep it there til next read.
695 # This is enough to synchronize between sync and JTAG
696 # clock domain and no higher latency solutions like
697 # FFSynchronizer is needed.
698 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
700 with m
.State("WRITEREAD"):
701 if not hasattr(wb
, "stall"):
702 m
.next
= "WRITEREADACK"
704 with m
.If(~wb
.stall
):
705 m
.next
= "WRITEREADACK"
706 with m
.State("WRITEREADACK"):
708 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
712 wb
.cyc
.eq(~fsm
.ongoing("IDLE")),
713 wb
.stb
.eq(fsm
.ongoing("READ") | fsm
.ongoing("WRITEREAD")),
714 wb
.we
.eq(fsm
.ongoing("WRITEREAD")),