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")),
122 class _IRBlock(Elaboratable
):
123 """TAP subblock for handling the IR shift register"""
124 def __init__(self
, *, ir_width
, cmd_idcode
,
125 tdi
, capture
, shift
, update
,
128 self
.ir
= Signal(ir_width
, reset
=cmd_idcode
)
132 self
._capture
= capture
134 self
._update
= update
136 def elaborate(self
, platform
):
139 shift_ir
= Signal(len(self
.ir
), reset_less
=True)
141 m
.d
.comb
+= self
.tdo
.eq(self
.ir
[0])
142 with m
.If(self
._capture
):
143 m
.d
.posjtag
+= shift_ir
.eq(self
.ir
)
144 with m
.Elif(self
._shift
):
145 m
.d
.posjtag
+= shift_ir
.eq(Cat(shift_ir
[1:], self
._tdi
))
146 with m
.Elif(self
._update
):
147 # For ir we only update it on the rising edge of clock
148 # to avoid that we already have the new ir value when still in
150 m
.d
.posjtag
+= self
.ir
.eq(shift_ir
)
160 class IOConn(Record
):
161 """TAP subblock representing the interface for an JTAG IO cell.
162 It contains signal to connect to the core and to the pad
164 This object is normally only allocated and returned from ``TAP.add_io``
165 It is a Record subclass.
169 core: subrecord with signals for the core
170 i: Signal(1), present only for IOType.In and IOType.InTriOut.
171 Signal input to core with pad input value.
172 o: Signal(1), present only for IOType.Out, IOType.TriOut and IOType.InTriOut.
173 Signal output from core with the pad output value.
174 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
175 Signal output from core with the pad output enable value.
176 pad: subrecord with for the pad
177 i: Signal(1), present only for IOType.In and IOType.InTriOut
178 Output from pad with pad input value for core.
179 o: Signal(1), present only for IOType.Out, IOType.TriOut and IOType.InTriOut.
180 Input to pad with pad output value.
181 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
182 Input to pad with pad output enable value.
187 if iotype
in (IOType
.In
, IOType
.InTriOut
):
188 sigs
.append(("i", 1))
189 if iotype
in (IOType
.Out
, IOType
.TriOut
, IOType
.InTriOut
):
190 sigs
.append(("o", 1))
191 if iotype
in (IOType
.TriOut
, IOType
.InTriOut
):
192 sigs
.append(("oe", 1))
194 return Layout((("core", sigs
), ("pad", sigs
)))
196 def __init__(self
, *, iotype
, name
=None, src_loc_at
=0):
197 super().__init
__(self
.__class
__.layout(iotype
), name
=name
, src_loc_at
=src_loc_at
+1)
199 self
._iotype
= iotype
201 class _IDBypassBlock(Elaboratable
):
202 """TAP subblock for the ID shift register"""
203 def __init__(self
, *, manufacturer_id
, part_number
, version
,
204 tdi
, capture
, shift
, update
, bypass
,
207 if not isinstance(manufacturer_id
, Const
) and len(manufacturer_id
) != 11:
208 raise ValueError("manufacturer_id has to be Const of length 11")
209 if not isinstance(part_number
, Const
) and len(manufacturer_id
) != 16:
210 raise ValueError("part_number has to be Const of length 16")
211 if not isinstance(version
, Const
) and len(version
) != 4:
212 raise ValueError("version has to be Const of length 4")
213 self
._id
= Cat(Const(1,1), manufacturer_id
, part_number
, version
)
215 self
.tdo
= Signal(name
=name
+"_tdo")
218 self
._capture
= capture
220 self
._update
= update
221 self
._bypass
= bypass
223 def elaborate(self
, platform
):
226 sr
= Signal(32, reset_less
=True, name
=self
.name
+"_sr")
228 # Local signals for the module
237 _capture
.eq(self
._capture
),
238 _shift
.eq(self
._shift
),
239 _update
.eq(self
._update
),
240 _bypass
.eq(self
._bypass
),
245 m
.d
.posjtag
+= sr
.eq(self
._id
)
248 m
.d
.posjtag
+= sr
[0].eq(_tdi
)
250 m
.d
.posjtag
+= sr
.eq(Cat(sr
[1:], _tdi
))
255 class ShiftReg(Record
):
256 """Object with interface for extra shift registers on a TAP.
261 cmds : int, default=1
262 The number of corresponding JTAG instructions
264 This object is normally only allocated and returned from ``TAP.add_shiftreg``
265 It is a Record subclass.
269 i: length=sr_length, FANIN
270 The input data sampled during capture state of the TAP
271 ie: length=cmds, FANOUT
272 Indicates that data is to be sampled by the JTAG TAP and
273 should be held stable. The bit indicates the corresponding
274 instruction for which data is asked.
275 This signal is kept high for a whole JTAG TAP clock cycle
276 and may thus be kept higher for more than one clock cycle
277 on the domain where ShiftReg is used.
278 The JTAG protocol does not allow insertion of wait states
279 so data need to be provided before ie goes down. The speed
280 of the response will determine the max. frequency for the
282 o: length=sr_length, FANOUT
283 The value of the shift register.
284 oe: length=cmds, FANOUT
285 Indicates that output is stable and can be sampled downstream because
286 JTAG TAP is in the Update state. The bit indicates the corresponding
287 instruction. The bit is only kept high for one clock cycle.
289 def __init__(self
, *, sr_length
, cmds
=1, name
=None, src_loc_at
=0):
291 ("i", sr_length
, Direction
.FANIN
),
292 ("ie", cmds
, Direction
.FANOUT
),
293 ("o", sr_length
, Direction
.FANOUT
),
294 ("oe", cmds
, Direction
.FANOUT
),
296 super().__init
__(layout
, name
=name
, src_loc_at
=src_loc_at
+1)
299 class TAP(Elaboratable
):
302 self
, *, with_reset
=False, ir_width
=None,
303 manufacturer_id
=Const(0b10001111111, 11), part_number
=Const(1, 16),
305 name
=None, src_loc_at
=0
307 assert((ir_width
is None) or (isinstance(ir_width
, int) and ir_width
>= 2))
308 assert(len(version
) == 4)
311 name
= get_var_name(depth
=src_loc_at
+2, default
="TAP")
313 self
.bus
= Interface(with_reset
=with_reset
, name
=self
.name
+"_bus",
314 src_loc_at
=src_loc_at
+1)
318 self
._ir
_width
= ir_width
319 self
._manufacturer
_id
= manufacturer_id
320 self
._part
_number
= part_number
321 self
._version
= version
323 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
353 m
.submodules
._irblock
= irblock
= _IRBlock(
354 ir_width
=ir_width
, cmd_idcode
=cmd_idcode
, tdi
=self
.bus
.tdi
,
355 capture
=(fsm
.isir
& fsm
.capture
),
356 shift
=(fsm
.isir
& fsm
.shift
),
357 update
=(fsm
.isir
& fsm
.update
),
358 name
=self
.name
+"_ir",
363 select_id
= fsm
.isdr
& ((ir
== cmd_idcode
) |
(ir
== cmd_bypass
))
364 m
.submodules
._idblock
= idblock
= _IDBypassBlock(
365 manufacturer_id
=self
._manufacturer
_id
, part_number
=self
._part
_number
,
366 version
=self
._version
, tdi
=self
.bus
.tdi
,
367 capture
=(select_id
& fsm
.capture
),
368 shift
=(select_id
& fsm
.shift
),
369 update
=(select_id
& fsm
.update
),
370 bypass
=(ir
== cmd_bypass
),
371 name
=self
.name
+"_id",
374 # IO (Boundary scan) block
375 io_capture
= Signal()
379 io_bd2core
= Signal()
380 sample
= (ir
== cmd_extest
) |
(ir
== cmd_sample
)
381 preload
= (ir
== cmd_preload
)
382 select_io
= fsm
.isdr
& (sample | preload
)
384 io_capture
.eq(sample
& fsm
.capture
), # Don't capture if not sample (like for PRELOAD)
385 io_shift
.eq(select_io
& fsm
.shift
),
386 io_update
.eq(select_io
& fsm
.update
),
387 io_bd2io
.eq(ir
== cmd_extest
),
388 io_bd2core
.eq(ir
== cmd_intest
),
390 io_tdo
= self
._elaborate
_ios
(
392 capture
=io_capture
, shift
=io_shift
, update
=io_update
,
393 bd2io
=io_bd2io
, bd2core
=io_bd2core
,
396 # chain tdo: select as appropriate, to go into into shiftregs
397 tdo
= Signal(name
=self
.name
+"_tdo")
398 with m
.If(select_ir
):
399 m
.d
.comb
+= tdo
.eq(irblock
.tdo
)
400 with m
.Elif(select_id
):
401 m
.d
.comb
+= tdo
.eq(idblock
.tdo
)
402 with m
.Elif(select_io
):
403 m
.d
.comb
+= tdo
.eq(io_tdo
)
406 self
._elaborate
_shiftregs
(
407 m
, capture
=fsm
.capture
, shift
=fsm
.shift
, update
=fsm
.update
,
408 ir
=irblock
.ir
, tdo_jtag
=tdo
412 self
._elaborate
_wishbones
(m
)
417 def add_io(self
, *, iotype
, name
=None, src_loc_at
=0):
418 """Add a io cell to the boundary scan chain
421 - iotype: :class:`IOType` enum.
427 name
= "ioconn" + str(len(self
._ios
))
429 ioconn
= IOConn(iotype
=iotype
, name
=name
, src_loc_at
=src_loc_at
+1)
430 self
._ios
.append(ioconn
)
433 def _elaborate_ios(self
, *, m
, capture
, shift
, update
, bd2io
, bd2core
):
440 length
= sum(connlength
[conn
._iotype
] for conn
in self
._ios
)
444 io_sr
= Signal(length
)
445 io_bd
= Signal(length
)
449 for conn
in self
._ios
:
450 if conn
._iotype
== IOType
.In
:
451 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.pad
.i
)
453 elif conn
._iotype
== IOType
.Out
:
454 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.core
.o
)
456 elif conn
._iotype
== IOType
.TriOut
:
458 io_sr
[idx
].eq(conn
.core
.o
),
459 io_sr
[idx
+1].eq(conn
.core
.oe
),
462 elif conn
._iotype
== IOType
.InTriOut
:
464 io_sr
[idx
].eq(conn
.pad
.i
),
465 io_sr
[idx
+1].eq(conn
.core
.o
),
466 io_sr
[idx
+2].eq(conn
.core
.oe
),
470 raise("Internal error")
471 assert idx
== length
, "Internal error"
473 m
.d
.posjtag
+= io_sr
.eq(Cat(self
.bus
.tdi
, io_sr
[:-1]))
475 m
.d
.negjtag
+= io_bd
.eq(io_sr
)
478 for conn
in self
._ios
:
479 if conn
._iotype
== IOType
.In
:
480 m
.d
.comb
+= conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
))
482 elif conn
._iotype
== IOType
.Out
:
483 m
.d
.comb
+= conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
))
485 elif conn
._iotype
== IOType
.TriOut
:
487 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
)),
488 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.oe
)),
491 elif conn
._iotype
== IOType
.InTriOut
:
493 conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
)),
494 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.o
)),
495 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+2], conn
.core
.oe
)),
499 raise("Internal error")
500 assert idx
== length
, "Internal error"
504 def add_shiftreg(self
, *, ircode
, length
, domain
="sync", name
=None, src_loc_at
=0):
505 """Add a shift register to the JTAG interface
508 - ircode: code(s) for the IR; int or sequence of ints. In the latter case this
509 shiftreg is shared between different IR codes.
510 - length: the length of the shift register
511 - domain: the domain on which the signal will be used"""
517 ir_it
= ircodes
= (ircode
,)
518 for _ircode
in ir_it
:
519 if not isinstance(_ircode
, int) or _ircode
<= 0:
520 raise ValueError("IR code '{}' is not an int greater than 0".format(_ircode
))
521 if _ircode
in self
._ircodes
:
522 raise ValueError("IR code '{}' already taken".format(_ircode
))
524 self
._ircodes
.extend(ircodes
)
527 name
= "sr{}".format(len(self
._srs
))
528 sr
= ShiftReg(sr_length
=length
, cmds
=len(ircodes
), name
=name
, src_loc_at
=src_loc_at
+1)
529 self
._srs
.append((ircodes
, domain
, sr
))
533 def _elaborate_shiftregs(self
, m
, capture
, shift
, update
, ir
, tdo_jtag
):
534 # tdos is tuple of (tdo, tdo_en) for each shiftreg
536 for ircodes
, domain
, sr
in self
._srs
:
537 reg
= Signal(len(sr
.o
), name
=sr
.name
+"_reg")
538 m
.d
.comb
+= sr
.o
.eq(reg
)
540 isir
= Signal(len(ircodes
), name
=sr
.name
+"_isir")
541 sr_capture
= Signal(name
=sr
.name
+"_capture")
542 sr_shift
= Signal(name
=sr
.name
+"_shift")
543 sr_update
= Signal(name
=sr
.name
+"_update")
545 isir
.eq(Cat(ir
== ircode
for ircode
in ircodes
)),
546 sr_capture
.eq((isir
!= 0) & capture
),
547 sr_shift
.eq((isir
!= 0) & shift
),
548 sr_update
.eq((isir
!= 0) & update
),
551 # update signal is on the JTAG clockdomain, sr.oe is on `domain` clockdomain
552 # latch update in `domain` clockdomain and see when it has falling edge.
553 # At that edge put isir in sr.oe for one `domain` clockdomain
554 update_core
= Signal(name
=sr
.name
+"_update_core")
555 update_core_prev
= Signal(name
=sr
.name
+"_update_core_prev")
557 update_core
.eq(sr_update
), # This is CDC from JTAG domain to given domain
558 update_core_prev
.eq(update_core
)
560 with m
.If(update_core_prev
& ~update_core
):
561 # Falling edge of update
562 m
.d
[domain
] += sr
.oe
.eq(isir
)
564 m
.d
[domain
] += sr
.oe
.eq(0)
567 m
.d
.posjtag
+= reg
.eq(Cat(reg
[1:], self
.bus
.tdi
))
568 with m
.If(sr_capture
):
569 m
.d
.posjtag
+= reg
.eq(sr
.i
)
571 # tdo = reg[0], tdo_en = shift
572 tdos
.append((reg
[0], sr_shift
))
575 # Assign the right tdo to the bus tdo
576 for i
, (tdo
, tdo_en
) in enumerate(tdos
):
579 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
582 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
586 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
588 # Always connect tdo_jtag to
589 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
592 def add_wishbone(self
, *, ircodes
, address_width
, data_width
, granularity
=None, domain
="sync",
593 name
=None, src_loc_at
=0):
594 """Add a wishbone interface
596 In order to allow high JTAG clock speed, data will be cached. This means that if data is
597 output the value of the next address will be read automatically.
601 ircodes: sequence of three integer for the JTAG IR codes;
602 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
603 has a shift register of length 'address_width', the two other codes
604 share a shift register of length data_width.
605 address_width: width of the address
606 data_width: width of the data
609 wb: nmigen_soc.wishbone.bus.Interface
610 The Wishbone interface, is pipelined and has stall field.
612 if len(ircodes
) != 3:
613 raise ValueError("3 IR Codes have to be provided")
616 name
= "wb" + str(len(self
._wbs
))
617 sr_addr
= self
.add_shiftreg(
618 ircode
=ircodes
[0], length
=address_width
, domain
=domain
,
621 sr_data
= self
.add_shiftreg(
622 ircode
=ircodes
[1:], length
=data_width
, domain
=domain
,
626 wb
= WishboneInterface(data_width
=data_width
, addr_width
=address_width
,
627 granularity
=granularity
, features
={"stall", "lock", "err", "rty"},
628 name
=name
, src_loc_at
=src_loc_at
+1)
630 self
._wbs
.append((sr_addr
, sr_data
, wb
, domain
))
634 def _elaborate_wishbones(self
, m
):
635 for sr_addr
, sr_data
, wb
, domain
in self
._wbs
:
636 m
.d
.comb
+= sr_addr
.i
.eq(wb
.adr
)
638 if hasattr(wb
, "sel"):
640 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
642 with m
.FSM(domain
=domain
) as fsm
:
643 with m
.State("IDLE"):
644 with m
.If(sr_addr
.oe
): # WBADDR code
645 m
.d
[domain
] += wb
.adr
.eq(sr_addr
.o
)
647 with m
.Elif(sr_data
.oe
[0]): # WBREAD code
649 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
651 with m
.Elif(sr_data
.oe
[1]): # WBWRITE code
652 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
654 with m
.State("READ"):
655 with m
.If(~wb
.stall
):
657 with m
.State("READACK"):
659 # Store read data in sr_data.i and keep it there til next read
660 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
662 with m
.State("WRITEREAD"):
663 with m
.If(~wb
.stall
):
664 m
.next
= "WRITEREADACK"
665 with m
.State("WRITEREADACK"):
667 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
671 wb
.cyc
.eq(~fsm
.ongoing("IDLE")),
672 wb
.stb
.eq(fsm
.ongoing("READ") | fsm
.ongoing("WRITEREAD")),
673 wb
.we
.eq(fsm
.ongoing("WRITEREAD")),