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 IOType.InTriOut.
176 Signal output from core with the pad output value.
177 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
178 Signal output from core with the pad output enable value.
179 pad: subrecord with for the pad
180 i: Signal(1), present only for IOType.In and IOType.InTriOut
181 Output from pad with pad input value for core.
182 o: Signal(1), present only for IOType.Out, IOType.TriOut and IOType.InTriOut.
183 Input to pad with pad output value.
184 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
185 Input to pad with pad output enable value.
190 if iotype
in (IOType
.In
, IOType
.InTriOut
):
191 sigs
.append(("i", 1))
192 if iotype
in (IOType
.Out
, IOType
.TriOut
, IOType
.InTriOut
):
193 sigs
.append(("o", 1))
194 if iotype
in (IOType
.TriOut
, IOType
.InTriOut
):
195 sigs
.append(("oe", 1))
197 return Layout((("core", sigs
), ("pad", sigs
)))
199 def __init__(self
, *, iotype
, name
=None, src_loc_at
=0):
200 super().__init
__(self
.__class
__.layout(iotype
), name
=name
, src_loc_at
=src_loc_at
+1)
202 self
._iotype
= iotype
205 class _IDBypassBlock(Elaboratable
):
206 """TAP subblock for the ID shift register"""
207 def __init__(self
, *, manufacturer_id
, part_number
, version
,
208 tdi
, capture
, shift
, update
, bypass
,
211 if not isinstance(manufacturer_id
, Const
) and len(manufacturer_id
) != 11:
212 raise ValueError("manufacturer_id has to be Const of length 11")
213 if not isinstance(part_number
, Const
) and len(manufacturer_id
) != 16:
214 raise ValueError("part_number has to be Const of length 16")
215 if not isinstance(version
, Const
) and len(version
) != 4:
216 raise ValueError("version has to be Const of length 4")
217 self
._id
= Cat(Const(1,1), manufacturer_id
, part_number
, version
)
219 self
.tdo
= Signal(name
=name
+"_tdo")
222 self
._capture
= capture
224 self
._update
= update
225 self
._bypass
= bypass
227 def elaborate(self
, platform
):
230 sr
= Signal(32, reset_less
=True, name
=self
.name
+"_sr")
232 # Local signals for the module
241 _capture
.eq(self
._capture
),
242 _shift
.eq(self
._shift
),
243 _update
.eq(self
._update
),
244 _bypass
.eq(self
._bypass
),
249 m
.d
.posjtag
+= sr
.eq(self
._id
)
252 m
.d
.posjtag
+= sr
[0].eq(_tdi
)
254 m
.d
.posjtag
+= sr
.eq(Cat(sr
[1:], _tdi
))
259 class ShiftReg(Record
):
260 """Object with interface for extra shift registers on a TAP.
265 cmds : int, default=1
266 The number of corresponding JTAG instructions
268 This object is normally only allocated and returned from ``TAP.add_shiftreg``
269 It is a Record subclass.
273 i: length=sr_length, FANIN
274 The input data sampled during capture state of the TAP
275 ie: length=cmds, FANOUT
276 Indicates that data is to be sampled by the JTAG TAP and
277 should be held stable. The bit indicates the corresponding
278 instruction for which data is asked.
279 This signal is kept high for a whole JTAG TAP clock cycle
280 and may thus be kept higher for more than one clock cycle
281 on the domain where ShiftReg is used.
282 The JTAG protocol does not allow insertion of wait states
283 so data need to be provided before ie goes down. The speed
284 of the response will determine the max. frequency for the
286 o: length=sr_length, FANOUT
287 The value of the shift register.
288 oe: length=cmds, FANOUT
289 Indicates that output is stable and can be sampled downstream because
290 JTAG TAP is in the Update state. The bit indicates the corresponding
291 instruction. The bit is only kept high for one clock cycle.
293 def __init__(self
, *, sr_length
, cmds
=1, name
=None, src_loc_at
=0):
295 ("i", sr_length
, Direction
.FANIN
),
296 ("ie", cmds
, Direction
.FANOUT
),
297 ("o", sr_length
, Direction
.FANOUT
),
298 ("oe", cmds
, Direction
.FANOUT
),
300 super().__init
__(layout
, name
=name
, src_loc_at
=src_loc_at
+1)
303 class TAP(Elaboratable
):
306 self
, *, with_reset
=False, ir_width
=None,
307 manufacturer_id
=Const(0b10001111111, 11), part_number
=Const(1, 16),
309 name
=None, src_loc_at
=0
311 assert((ir_width
is None) or (isinstance(ir_width
, int) and ir_width
>= 2))
312 assert(len(version
) == 4)
315 name
= get_var_name(depth
=src_loc_at
+2, default
="TAP")
317 self
.bus
= Interface(with_reset
=with_reset
, name
=self
.name
+"_bus",
318 src_loc_at
=src_loc_at
+1)
322 self
._ir
_width
= ir_width
323 self
._manufacturer
_id
= manufacturer_id
324 self
._part
_number
= part_number
325 self
._version
= version
327 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at the end
333 def elaborate(self
, platform
):
336 # Determine ir_width if not fixed.
337 ir_max
= max(self
._ircodes
) + 1 # One extra code needed with all ones
338 ir_width
= len("{:b}".format(ir_max
))
339 if self
._ir
_width
is not None:
340 assert self
._ir
_width
>= ir_width
, "Specified JTAG IR width not big enough for allocated shiift registers"
341 ir_width
= self
._ir
_width
343 # TODO: Make commands numbers configurable
349 cmd_bypass
= 2**ir_width
- 1 # All ones
351 m
.submodules
._fsm
= fsm
= _FSM(bus
=self
.bus
)
352 m
.domains
.posjtag
= fsm
.posjtag
353 m
.domains
.negjtag
= fsm
.negjtag
357 m
.submodules
._irblock
= irblock
= _IRBlock(
358 ir_width
=ir_width
, cmd_idcode
=cmd_idcode
, tdi
=self
.bus
.tdi
,
359 capture
=(fsm
.isir
& fsm
.capture
),
360 shift
=(fsm
.isir
& fsm
.shift
),
361 update
=(fsm
.isir
& fsm
.update
),
362 name
=self
.name
+"_ir",
367 select_id
= fsm
.isdr
& ((ir
== cmd_idcode
) |
(ir
== cmd_bypass
))
368 m
.submodules
._idblock
= idblock
= _IDBypassBlock(
369 manufacturer_id
=self
._manufacturer
_id
, part_number
=self
._part
_number
,
370 version
=self
._version
, tdi
=self
.bus
.tdi
,
371 capture
=(select_id
& fsm
.capture
),
372 shift
=(select_id
& fsm
.shift
),
373 update
=(select_id
& fsm
.update
),
374 bypass
=(ir
== cmd_bypass
),
375 name
=self
.name
+"_id",
378 # IO (Boundary scan) block
379 io_capture
= Signal()
383 io_bd2core
= Signal()
384 sample
= (ir
== cmd_extest
) |
(ir
== cmd_sample
)
385 preload
= (ir
== cmd_preload
)
386 select_io
= fsm
.isdr
& (sample | preload
)
388 io_capture
.eq(sample
& fsm
.capture
), # Don't capture if not sample (like for PRELOAD)
389 io_shift
.eq(select_io
& fsm
.shift
),
390 io_update
.eq(select_io
& fsm
.update
),
391 io_bd2io
.eq(ir
== cmd_extest
),
392 io_bd2core
.eq(ir
== cmd_intest
),
394 io_tdo
= self
._elaborate
_ios
(
396 capture
=io_capture
, shift
=io_shift
, update
=io_update
,
397 bd2io
=io_bd2io
, bd2core
=io_bd2core
,
400 # chain tdo: select as appropriate, to go into into shiftregs
401 tdo
= Signal(name
=self
.name
+"_tdo")
402 with m
.If(select_ir
):
403 m
.d
.comb
+= tdo
.eq(irblock
.tdo
)
404 with m
.Elif(select_id
):
405 m
.d
.comb
+= tdo
.eq(idblock
.tdo
)
406 with m
.Elif(select_io
):
407 m
.d
.comb
+= tdo
.eq(io_tdo
)
410 self
._elaborate
_shiftregs
(
411 m
, capture
=fsm
.capture
, shift
=fsm
.shift
, update
=fsm
.update
,
412 ir
=irblock
.ir
, tdo_jtag
=tdo
416 self
._elaborate
_wishbones
(m
)
421 def add_io(self
, *, iotype
, name
=None, src_loc_at
=0):
422 """Add a io cell to the boundary scan chain
425 - iotype: :class:`IOType` enum.
431 name
= "ioconn" + str(len(self
._ios
))
433 ioconn
= IOConn(iotype
=iotype
, name
=name
, src_loc_at
=src_loc_at
+1)
434 self
._ios
.append(ioconn
)
437 def _elaborate_ios(self
, *, m
, capture
, shift
, update
, bd2io
, bd2core
):
444 length
= sum(connlength
[conn
._iotype
] for conn
in self
._ios
)
448 io_sr
= Signal(length
)
449 io_bd
= Signal(length
)
451 # Boundary scan "capture" mode. makes I/O status available via SR
454 for conn
in self
._ios
:
455 if conn
._iotype
== IOType
.In
:
456 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.pad
.i
)
458 elif conn
._iotype
== IOType
.Out
:
459 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.core
.o
)
461 elif conn
._iotype
== IOType
.TriOut
:
463 io_sr
[idx
].eq(conn
.core
.o
),
464 io_sr
[idx
+1].eq(conn
.core
.oe
),
467 elif conn
._iotype
== IOType
.InTriOut
:
469 io_sr
[idx
].eq(conn
.pad
.i
),
470 io_sr
[idx
+1].eq(conn
.core
.o
),
471 io_sr
[idx
+2].eq(conn
.core
.oe
),
475 raise("Internal error")
476 assert idx
== length
, "Internal error"
478 # "Shift" mode (sends out captured data on tdo, sets incoming from tdi)
480 m
.d
.posjtag
+= io_sr
.eq(Cat(self
.bus
.tdi
, io_sr
[:-1]))
484 m
.d
.negjtag
+= io_bd
.eq(io_sr
)
486 # sets up IO (pad<->core) or in testing mode depending on requested
487 # mode, via Muxes controlled by bd2core and bd2io
489 for conn
in self
._ios
:
490 if conn
._iotype
== IOType
.In
:
491 m
.d
.comb
+= conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
))
493 elif conn
._iotype
== IOType
.Out
:
494 m
.d
.comb
+= conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
))
496 elif conn
._iotype
== IOType
.TriOut
:
498 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
)),
499 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.oe
)),
502 elif conn
._iotype
== IOType
.InTriOut
:
504 conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
)),
505 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.o
)),
506 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+2], conn
.core
.oe
)),
510 raise("Internal error")
511 assert idx
== length
, "Internal error"
515 def add_shiftreg(self
, *, ircode
, length
, domain
="sync", name
=None, src_loc_at
=0):
516 """Add a shift register to the JTAG interface
519 - ircode: code(s) for the IR; int or sequence of ints. In the latter case this
520 shiftreg is shared between different IR codes.
521 - length: the length of the shift register
522 - domain: the domain on which the signal will be used"""
528 ir_it
= ircodes
= (ircode
,)
529 for _ircode
in ir_it
:
530 if not isinstance(_ircode
, int) or _ircode
<= 0:
531 raise ValueError("IR code '{}' is not an int greater than 0".format(_ircode
))
532 if _ircode
in self
._ircodes
:
533 raise ValueError("IR code '{}' already taken".format(_ircode
))
535 self
._ircodes
.extend(ircodes
)
538 name
= "sr{}".format(len(self
._srs
))
539 sr
= ShiftReg(sr_length
=length
, cmds
=len(ircodes
), name
=name
, src_loc_at
=src_loc_at
+1)
540 self
._srs
.append((ircodes
, domain
, sr
))
544 def _elaborate_shiftregs(self
, m
, capture
, shift
, update
, ir
, tdo_jtag
):
545 # tdos is tuple of (tdo, tdo_en) for each shiftreg
547 for ircodes
, domain
, sr
in self
._srs
:
548 reg
= Signal(len(sr
.o
), name
=sr
.name
+"_reg")
549 m
.d
.comb
+= sr
.o
.eq(reg
)
551 isir
= Signal(len(ircodes
), name
=sr
.name
+"_isir")
552 sr_capture
= Signal(name
=sr
.name
+"_capture")
553 sr_shift
= Signal(name
=sr
.name
+"_shift")
554 sr_update
= Signal(name
=sr
.name
+"_update")
556 isir
.eq(Cat(ir
== ircode
for ircode
in ircodes
)),
557 sr_capture
.eq((isir
!= 0) & capture
),
558 sr_shift
.eq((isir
!= 0) & shift
),
559 sr_update
.eq((isir
!= 0) & update
),
562 # update signal is on the JTAG clockdomain, sr.oe is on `domain` clockdomain
563 # latch update in `domain` clockdomain and see when it has falling edge.
564 # At that edge put isir in sr.oe for one `domain` clockdomain
565 # Using this custom sync <> JTAG domain synchronization avoids the use of
566 # more generic but also higher latency CDC solutions like FFSynchronizer.
567 update_core
= Signal(name
=sr
.name
+"_update_core")
568 update_core_prev
= Signal(name
=sr
.name
+"_update_core_prev")
570 update_core
.eq(sr_update
), # This is CDC from JTAG domain to given domain
571 update_core_prev
.eq(update_core
)
573 with m
.If(update_core_prev
& ~update_core
):
574 # Falling edge of update
575 m
.d
[domain
] += sr
.oe
.eq(isir
)
577 m
.d
[domain
] += sr
.oe
.eq(0)
580 m
.d
.posjtag
+= reg
.eq(Cat(reg
[1:], self
.bus
.tdi
))
581 with m
.If(sr_capture
):
582 m
.d
.posjtag
+= reg
.eq(sr
.i
)
584 # tdo = reg[0], tdo_en = shift
585 tdos
.append((reg
[0], sr_shift
))
588 # Assign the right tdo to the bus tdo
589 for i
, (tdo
, tdo_en
) in enumerate(tdos
):
592 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
595 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
599 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
601 # Always connect tdo_jtag to
602 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
605 def add_wishbone(self
, *, ircodes
, address_width
, data_width
, granularity
=None, domain
="sync",
606 name
=None, src_loc_at
=0):
607 """Add a wishbone interface
609 In order to allow high JTAG clock speed, data will be cached. This means that if data is
610 output the value of the next address will be read automatically.
614 ircodes: sequence of three integer for the JTAG IR codes;
615 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
616 has a shift register of length 'address_width', the two other codes
617 share a shift register of length data_width.
618 address_width: width of the address
619 data_width: width of the data
622 wb: nmigen_soc.wishbone.bus.Interface
623 The Wishbone interface, is pipelined and has stall field.
625 if len(ircodes
) != 3:
626 raise ValueError("3 IR Codes have to be provided")
629 name
= "wb" + str(len(self
._wbs
))
630 sr_addr
= self
.add_shiftreg(
631 ircode
=ircodes
[0], length
=address_width
, domain
=domain
,
634 sr_data
= self
.add_shiftreg(
635 ircode
=ircodes
[1:], length
=data_width
, domain
=domain
,
639 wb
= WishboneInterface(data_width
=data_width
, addr_width
=address_width
,
640 granularity
=granularity
, features
={"stall", "lock", "err", "rty"},
641 name
=name
, src_loc_at
=src_loc_at
+1)
643 self
._wbs
.append((sr_addr
, sr_data
, wb
, domain
))
647 def _elaborate_wishbones(self
, m
):
648 for sr_addr
, sr_data
, wb
, domain
in self
._wbs
:
649 m
.d
.comb
+= sr_addr
.i
.eq(wb
.adr
)
651 if hasattr(wb
, "sel"):
653 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
655 with m
.FSM(domain
=domain
) as fsm
:
656 with m
.State("IDLE"):
657 with m
.If(sr_addr
.oe
): # WBADDR code
658 m
.d
[domain
] += wb
.adr
.eq(sr_addr
.o
)
660 with m
.Elif(sr_data
.oe
[0]): # WBREAD code
662 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
664 with m
.Elif(sr_data
.oe
[1]): # WBWRITE code
665 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
667 with m
.State("READ"):
668 with m
.If(~wb
.stall
):
670 with m
.State("READACK"):
672 # Store read data in sr_data.i and keep it there til next read
673 # This is enough to synchronize between sync and JTAG clock domain
674 # and no higher latency solutions like FFSynchronizer is needed.
675 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
677 with m
.State("WRITEREAD"):
678 with m
.If(~wb
.stall
):
679 m
.next
= "WRITEREADACK"
680 with m
.State("WRITEREADACK"):
682 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
686 wb
.cyc
.eq(~fsm
.ongoing("IDLE")),
687 wb
.stb
.eq(fsm
.ongoing("READ") | fsm
.ongoing("WRITEREAD")),
688 wb
.we
.eq(fsm
.ongoing("WRITEREAD")),