e0c63db0242b32451bed08db310ae7a5e08f423b
3 from enum
import Enum
, auto
6 from nmigen
.build
import *
7 from nmigen
.lib
.io
import *
8 from nmigen
.hdl
.rec
import Direction
, Layout
9 from nmigen
.tracer
import get_var_name
11 from nmigen_soc
.wishbone
import Interface
as WishboneInterface
13 from .bus
import Interface
16 "TAP", "ShiftReg", "IOType", "IOConn",
20 class _FSM(Elaboratable
):
21 """TAP subblock for the FSM"""
22 def __init__(self
, *, bus
):
25 self
.capture
= Signal()
27 self
.update
= Signal()
29 # JTAG uses both edges of the incoming clock (TCK). set them up here
30 self
.posjtag
= ClockDomain("posjtag", local
=True)
31 self
.negjtag
= ClockDomain("negjtag", local
=True, clk_edge
="neg")
35 def elaborate(self
, platform
):
40 self
.posjtag
.clk
.eq(self
._bus
.tck
),
41 self
.posjtag
.rst
.eq(rst
),
42 self
.negjtag
.clk
.eq(self
._bus
.tck
),
43 self
.negjtag
.rst
.eq(rst
),
46 # Make local clock domain optionally using trst of JTAG bus as reset
47 if hasattr(self
._bus
, "trst"):
48 m
.domains
.local
= local
= ClockDomain(local
=True)
49 m
.d
.comb
+= local
.rst
.eq(self
._bus
.trst
)
51 m
.domains
.local
= local
= ClockDomain(local
=True, reset_less
=True)
52 m
.d
.comb
+= local
.clk
.eq(self
._bus
.tck
)
54 with m
.FSM(domain
="local") as fsm
:
55 with m
.State("TestLogicReset"):
56 # Be sure to reset isir, isdr
61 with m
.If(self
._bus
.tms
== 0):
62 m
.next
= "RunTestIdle"
63 with m
.State("RunTestIdle"):
64 # Be sure to reset isir, isdr
69 with m
.If(self
._bus
.tms
== 1):
70 m
.next
= "SelectDRScan"
71 with m
.State("SelectDRScan"):
72 with m
.If(self
._bus
.tms
== 0):
73 m
.d
.local
+= self
.isdr
.eq(1)
74 m
.next
= "CaptureState"
76 m
.next
= "SelectIRScan"
77 with m
.State("SelectIRScan"):
78 with m
.If(self
._bus
.tms
== 0):
79 m
.d
.local
+= self
.isir
.eq(1)
80 m
.next
= "CaptureState"
82 m
.next
= "TestLogicReset"
83 with m
.State("CaptureState"):
84 with m
.If(self
._bus
.tms
== 0):
88 with m
.State("ShiftState"):
89 with m
.If(self
._bus
.tms
== 1):
91 with m
.State("Exit1"):
92 with m
.If(self
._bus
.tms
== 0):
95 m
.next
= "UpdateState"
96 with m
.State("Pause"):
97 with m
.If(self
._bus
.tms
== 1):
99 with m
.State("Exit2"):
100 with m
.If(self
._bus
.tms
== 0):
101 m
.next
= "ShiftState"
103 m
.next
= "UpdateState"
104 with m
.State("UpdateState"):
109 with m
.If(self
._bus
.tms
== 0):
110 m
.next
= "RunTestIdle"
112 m
.next
= "SelectDRScan"
115 rst
.eq(fsm
.ongoing("TestLogicReset")),
116 self
.capture
.eq(fsm
.ongoing("CaptureState")),
117 self
.shift
.eq(fsm
.ongoing("ShiftState")),
118 self
.update
.eq(fsm
.ongoing("UpdateState")),
124 class _IRBlock(Elaboratable
):
125 """TAP subblock for handling the IR shift register"""
126 def __init__(self
, *, ir_width
, cmd_idcode
,
127 tdi
, capture
, shift
, update
,
130 self
.ir
= Signal(ir_width
, reset
=cmd_idcode
)
134 self
._capture
= capture
136 self
._update
= update
138 def elaborate(self
, platform
):
141 shift_ir
= Signal(len(self
.ir
), reset_less
=True)
143 m
.d
.comb
+= self
.tdo
.eq(self
.ir
[0])
144 with m
.If(self
._capture
):
145 m
.d
.posjtag
+= shift_ir
.eq(self
.ir
)
146 with m
.Elif(self
._shift
):
147 m
.d
.posjtag
+= shift_ir
.eq(Cat(shift_ir
[1:], self
._tdi
))
148 with m
.Elif(self
._update
):
149 # For ir we only update it on the rising edge of clock
150 # to avoid that we already have the new ir value when still in
152 m
.d
.posjtag
+= self
.ir
.eq(shift_ir
)
164 class IOConn(Record
):
165 """TAP subblock representing the interface for an JTAG IO cell.
166 It contains signal to connect to the core and to the pad
168 This object is normally only allocated and returned from ``TAP.add_io``
169 It is a Record subclass.
173 core: subrecord with signals for the core
174 i: Signal(1), present only for IOType.In and IOType.InTriOut.
175 Signal input to core with pad input value.
176 o: Signal(1), present only for IOType.Out, IOType.TriOut and IOType.InTriOut.
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 IOType.InTriOut.
184 Input to pad with pad output value.
185 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
186 Input to pad with pad output enable value.
191 if iotype
in (IOType
.In
, IOType
.InTriOut
):
192 sigs
.append(("i", 1))
193 if iotype
in (IOType
.Out
, IOType
.TriOut
, IOType
.InTriOut
):
194 sigs
.append(("o", 1))
195 if iotype
in (IOType
.TriOut
, IOType
.InTriOut
):
196 sigs
.append(("oe", 1))
198 return Layout((("core", sigs
), ("pad", sigs
)))
200 def __init__(self
, *, iotype
, name
=None, src_loc_at
=0):
201 super().__init
__(self
.__class
__.layout(iotype
), name
=name
, src_loc_at
=src_loc_at
+1)
203 self
._iotype
= iotype
206 class _IDBypassBlock(Elaboratable
):
207 """TAP subblock for the ID shift register"""
208 def __init__(self
, *, manufacturer_id
, part_number
, version
,
209 tdi
, capture
, shift
, update
, bypass
,
212 if not isinstance(manufacturer_id
, Const
) and len(manufacturer_id
) != 11:
213 raise ValueError("manufacturer_id has to be Const of length 11")
214 if not isinstance(part_number
, Const
) and len(manufacturer_id
) != 16:
215 raise ValueError("part_number has to be Const of length 16")
216 if not isinstance(version
, Const
) and len(version
) != 4:
217 raise ValueError("version has to be Const of length 4")
218 self
._id
= Cat(Const(1,1), manufacturer_id
, part_number
, version
)
220 self
.tdo
= Signal(name
=name
+"_tdo")
223 self
._capture
= capture
225 self
._update
= update
226 self
._bypass
= bypass
228 def elaborate(self
, platform
):
231 sr
= Signal(32, reset_less
=True, name
=self
.name
+"_sr")
233 # Local signals for the module
242 _capture
.eq(self
._capture
),
243 _shift
.eq(self
._shift
),
244 _update
.eq(self
._update
),
245 _bypass
.eq(self
._bypass
),
250 m
.d
.posjtag
+= sr
.eq(self
._id
)
253 m
.d
.posjtag
+= sr
[0].eq(_tdi
)
255 m
.d
.posjtag
+= sr
.eq(Cat(sr
[1:], _tdi
))
260 class ShiftReg(Record
):
261 """Object with interface for extra shift registers on a TAP.
266 cmds : int, default=1
267 The number of corresponding JTAG instructions
269 This object is normally only allocated and returned from ``TAP.add_shiftreg``
270 It is a Record subclass.
274 i: length=sr_length, FANIN
275 The input data sampled during capture state of the TAP
276 ie: length=cmds, FANOUT
277 Indicates that data is to be sampled by the JTAG TAP and
278 should be held stable. The bit indicates the corresponding
279 instruction for which data is asked.
280 This signal is kept high for a whole JTAG TAP clock cycle
281 and may thus be kept higher for more than one clock cycle
282 on the domain where ShiftReg is used.
283 The JTAG protocol does not allow insertion of wait states
284 so data need to be provided before ie goes down. The speed
285 of the response will determine the max. frequency for the
287 o: length=sr_length, FANOUT
288 The value of the shift register.
289 oe: length=cmds, FANOUT
290 Indicates that output is stable and can be sampled downstream because
291 JTAG TAP is in the Update state. The bit indicates the corresponding
292 instruction. The bit is only kept high for one clock cycle.
294 def __init__(self
, *, sr_length
, cmds
=1, name
=None, src_loc_at
=0):
296 ("i", sr_length
, Direction
.FANIN
),
297 ("ie", cmds
, Direction
.FANOUT
),
298 ("o", sr_length
, Direction
.FANOUT
),
299 ("oe", cmds
, Direction
.FANOUT
),
301 super().__init
__(layout
, name
=name
, src_loc_at
=src_loc_at
+1)
304 class TAP(Elaboratable
):
307 self
, *, with_reset
=False, ir_width
=None,
308 manufacturer_id
=Const(0b10001111111, 11), part_number
=Const(1, 16),
310 name
=None, src_loc_at
=0
312 assert((ir_width
is None) or (isinstance(ir_width
, int) and ir_width
>= 2))
313 assert(len(version
) == 4)
316 name
= get_var_name(depth
=src_loc_at
+2, default
="TAP")
318 self
.bus
= Interface(with_reset
=with_reset
, name
=self
.name
+"_bus",
319 src_loc_at
=src_loc_at
+1)
323 self
._ir
_width
= ir_width
324 self
._manufacturer
_id
= manufacturer_id
325 self
._part
_number
= part_number
326 self
._version
= version
328 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at the end
334 def elaborate(self
, platform
):
337 # Determine ir_width if not fixed.
338 ir_max
= max(self
._ircodes
) + 1 # One extra code needed with all ones
339 ir_width
= len("{:b}".format(ir_max
))
340 if self
._ir
_width
is not None:
341 assert self
._ir
_width
>= ir_width
, "Specified JTAG IR width not big enough for allocated shiift registers"
342 ir_width
= self
._ir
_width
344 # TODO: Make commands numbers configurable
350 cmd_bypass
= 2**ir_width
- 1 # All ones
352 m
.submodules
._fsm
= fsm
= _FSM(bus
=self
.bus
)
353 m
.domains
.posjtag
= fsm
.posjtag
354 m
.domains
.negjtag
= fsm
.negjtag
358 m
.submodules
._irblock
= irblock
= _IRBlock(
359 ir_width
=ir_width
, cmd_idcode
=cmd_idcode
, tdi
=self
.bus
.tdi
,
360 capture
=(fsm
.isir
& fsm
.capture
),
361 shift
=(fsm
.isir
& fsm
.shift
),
362 update
=(fsm
.isir
& fsm
.update
),
363 name
=self
.name
+"_ir",
368 select_id
= fsm
.isdr
& ((ir
== cmd_idcode
) |
(ir
== cmd_bypass
))
369 m
.submodules
._idblock
= idblock
= _IDBypassBlock(
370 manufacturer_id
=self
._manufacturer
_id
, part_number
=self
._part
_number
,
371 version
=self
._version
, tdi
=self
.bus
.tdi
,
372 capture
=(select_id
& fsm
.capture
),
373 shift
=(select_id
& fsm
.shift
),
374 update
=(select_id
& fsm
.update
),
375 bypass
=(ir
== cmd_bypass
),
376 name
=self
.name
+"_id",
379 # IO (Boundary scan) block
380 io_capture
= Signal()
384 io_bd2core
= Signal()
385 sample
= (ir
== cmd_extest
) |
(ir
== cmd_sample
)
386 preload
= (ir
== cmd_preload
)
387 select_io
= fsm
.isdr
& (sample | preload
)
389 io_capture
.eq(sample
& fsm
.capture
), # Don't capture if not sample (like for PRELOAD)
390 io_shift
.eq(select_io
& fsm
.shift
),
391 io_update
.eq(select_io
& fsm
.update
),
392 io_bd2io
.eq(ir
== cmd_extest
),
393 io_bd2core
.eq(ir
== cmd_intest
),
395 io_tdo
= self
._elaborate
_ios
(
397 capture
=io_capture
, shift
=io_shift
, update
=io_update
,
398 bd2io
=io_bd2io
, bd2core
=io_bd2core
,
401 # chain tdo: select as appropriate, to go into into shiftregs
402 tdo
= Signal(name
=self
.name
+"_tdo")
403 with m
.If(select_ir
):
404 m
.d
.comb
+= tdo
.eq(irblock
.tdo
)
405 with m
.Elif(select_id
):
406 m
.d
.comb
+= tdo
.eq(idblock
.tdo
)
407 with m
.Elif(select_io
):
408 m
.d
.comb
+= tdo
.eq(io_tdo
)
411 self
._elaborate
_shiftregs
(
412 m
, capture
=fsm
.capture
, shift
=fsm
.shift
, update
=fsm
.update
,
413 ir
=irblock
.ir
, tdo_jtag
=tdo
417 self
._elaborate
_wishbones
(m
)
422 def add_io(self
, *, iotype
, name
=None, src_loc_at
=0):
423 """Add a io cell to the boundary scan chain
426 - iotype: :class:`IOType` enum.
432 name
= "ioconn" + str(len(self
._ios
))
434 ioconn
= IOConn(iotype
=iotype
, name
=name
, src_loc_at
=src_loc_at
+1)
435 self
._ios
.append(ioconn
)
438 def _elaborate_ios(self
, *, m
, capture
, shift
, update
, bd2io
, bd2core
):
445 length
= sum(connlength
[conn
._iotype
] for conn
in self
._ios
)
449 io_sr
= Signal(length
)
450 io_bd
= Signal(length
)
452 # Boundary scan "capture" mode. makes I/O status available via SR
455 for conn
in self
._ios
:
456 if conn
._iotype
== IOType
.In
:
457 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.pad
.i
)
459 elif conn
._iotype
== IOType
.Out
:
460 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.core
.o
)
462 elif conn
._iotype
== IOType
.TriOut
:
464 io_sr
[idx
].eq(conn
.core
.o
),
465 io_sr
[idx
+1].eq(conn
.core
.oe
),
468 elif conn
._iotype
== IOType
.InTriOut
:
470 io_sr
[idx
].eq(conn
.pad
.i
),
471 io_sr
[idx
+1].eq(conn
.core
.o
),
472 io_sr
[idx
+2].eq(conn
.core
.oe
),
476 raise("Internal error")
477 assert idx
== length
, "Internal error"
479 # "Shift" mode (sends out captured data on tdo, sets incoming from tdi)
481 m
.d
.posjtag
+= io_sr
.eq(Cat(self
.bus
.tdi
, io_sr
[:-1]))
485 m
.d
.negjtag
+= io_bd
.eq(io_sr
)
487 # sets up IO (pad<->core) or in testing mode depending on requested
488 # mode, via Muxes controlled by bd2core and bd2io
490 for conn
in self
._ios
:
491 if conn
._iotype
== IOType
.In
:
492 m
.d
.comb
+= conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
))
494 elif conn
._iotype
== IOType
.Out
:
495 m
.d
.comb
+= conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
))
497 elif conn
._iotype
== IOType
.TriOut
:
499 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
)),
500 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.oe
)),
503 elif conn
._iotype
== IOType
.InTriOut
:
505 conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
)),
506 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.o
)),
507 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+2], conn
.core
.oe
)),
511 raise("Internal error")
512 assert idx
== length
, "Internal error"
516 def add_shiftreg(self
, *, ircode
, length
, domain
="sync", name
=None, src_loc_at
=0):
517 """Add a shift register to the JTAG interface
520 - ircode: code(s) for the IR; int or sequence of ints. In the latter case this
521 shiftreg is shared between different IR codes.
522 - length: the length of the shift register
523 - domain: the domain on which the signal will be used"""
529 ir_it
= ircodes
= (ircode
,)
530 for _ircode
in ir_it
:
531 if not isinstance(_ircode
, int) or _ircode
<= 0:
532 raise ValueError("IR code '{}' is not an int greater than 0".format(_ircode
))
533 if _ircode
in self
._ircodes
:
534 raise ValueError("IR code '{}' already taken".format(_ircode
))
536 self
._ircodes
.extend(ircodes
)
539 name
= "sr{}".format(len(self
._srs
))
540 sr
= ShiftReg(sr_length
=length
, cmds
=len(ircodes
), name
=name
, src_loc_at
=src_loc_at
+1)
541 self
._srs
.append((ircodes
, domain
, sr
))
545 def _elaborate_shiftregs(self
, m
, capture
, shift
, update
, ir
, tdo_jtag
):
546 # tdos is tuple of (tdo, tdo_en) for each shiftreg
548 for ircodes
, domain
, sr
in self
._srs
:
549 reg
= Signal(len(sr
.o
), name
=sr
.name
+"_reg")
550 m
.d
.comb
+= sr
.o
.eq(reg
)
552 isir
= Signal(len(ircodes
), name
=sr
.name
+"_isir")
553 sr_capture
= Signal(name
=sr
.name
+"_capture")
554 sr_shift
= Signal(name
=sr
.name
+"_shift")
555 sr_update
= Signal(name
=sr
.name
+"_update")
557 isir
.eq(Cat(ir
== ircode
for ircode
in ircodes
)),
558 sr_capture
.eq((isir
!= 0) & capture
),
559 sr_shift
.eq((isir
!= 0) & shift
),
560 sr_update
.eq((isir
!= 0) & update
),
563 # update signal is on the JTAG clockdomain, sr.oe is on `domain` clockdomain
564 # latch update in `domain` clockdomain and see when it has falling edge.
565 # At that edge put isir in sr.oe for one `domain` clockdomain
566 # Using this custom sync <> JTAG domain synchronization avoids the use of
567 # more generic but also higher latency CDC solutions like FFSynchronizer.
568 update_core
= Signal(name
=sr
.name
+"_update_core")
569 update_core_prev
= Signal(name
=sr
.name
+"_update_core_prev")
571 update_core
.eq(sr_update
), # This is CDC from JTAG domain to given domain
572 update_core_prev
.eq(update_core
)
574 with m
.If(update_core_prev
& ~update_core
):
575 # Falling edge of update
576 m
.d
[domain
] += sr
.oe
.eq(isir
)
578 m
.d
[domain
] += sr
.oe
.eq(0)
581 m
.d
.posjtag
+= reg
.eq(Cat(reg
[1:], self
.bus
.tdi
))
582 with m
.If(sr_capture
):
583 m
.d
.posjtag
+= reg
.eq(sr
.i
)
585 # tdo = reg[0], tdo_en = shift
586 tdos
.append((reg
[0], sr_shift
))
589 # Assign the right tdo to the bus tdo
590 for i
, (tdo
, tdo_en
) in enumerate(tdos
):
593 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
596 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
600 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
602 # Always connect tdo_jtag to
603 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
606 def add_wishbone(self
, *, ircodes
, address_width
, data_width
, granularity
=None, domain
="sync",
607 name
=None, src_loc_at
=0):
608 """Add a wishbone interface
610 In order to allow high JTAG clock speed, data will be cached. This means that if data is
611 output the value of the next address will be read automatically.
615 ircodes: sequence of three integer for the JTAG IR codes;
616 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
617 has a shift register of length 'address_width', the two other codes
618 share a shift register of length data_width.
619 address_width: width of the address
620 data_width: width of the data
623 wb: nmigen_soc.wishbone.bus.Interface
624 The Wishbone interface, is pipelined and has stall field.
626 if len(ircodes
) != 3:
627 raise ValueError("3 IR Codes have to be provided")
630 name
= "wb" + str(len(self
._wbs
))
631 sr_addr
= self
.add_shiftreg(
632 ircode
=ircodes
[0], length
=address_width
, domain
=domain
,
635 sr_data
= self
.add_shiftreg(
636 ircode
=ircodes
[1:], length
=data_width
, domain
=domain
,
640 wb
= WishboneInterface(data_width
=data_width
, addr_width
=address_width
,
641 granularity
=granularity
, features
={"stall", "lock", "err", "rty"},
642 name
=name
, src_loc_at
=src_loc_at
+1)
644 self
._wbs
.append((sr_addr
, sr_data
, wb
, domain
))
648 def _elaborate_wishbones(self
, m
):
649 for sr_addr
, sr_data
, wb
, domain
in self
._wbs
:
650 m
.d
.comb
+= sr_addr
.i
.eq(wb
.adr
)
652 if hasattr(wb
, "sel"):
654 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
656 with m
.FSM(domain
=domain
) as fsm
:
657 with m
.State("IDLE"):
658 with m
.If(sr_addr
.oe
): # WBADDR code
659 m
.d
[domain
] += wb
.adr
.eq(sr_addr
.o
)
661 with m
.Elif(sr_data
.oe
[0]): # WBREAD code
663 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
665 with m
.Elif(sr_data
.oe
[1]): # WBWRITE code
666 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
668 with m
.State("READ"):
669 with m
.If(~wb
.stall
):
671 with m
.State("READACK"):
673 # Store read data in sr_data.i and keep it there til next read
674 # This is enough to synchronize between sync and JTAG clock domain
675 # and no higher latency solutions like FFSynchronizer is needed.
676 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
678 with m
.State("WRITEREAD"):
679 with m
.If(~wb
.stall
):
680 m
.next
= "WRITEREADACK"
681 with m
.State("WRITEREADACK"):
683 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
687 wb
.cyc
.eq(~fsm
.ongoing("IDLE")),
688 wb
.stb
.eq(fsm
.ongoing("READ") | fsm
.ongoing("WRITEREAD")),
689 wb
.we
.eq(fsm
.ongoing("WRITEREAD")),