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
174 Signal output from core with the pad output value.
175 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
176 Signal output from core with the pad output enable value.
177 pad: subrecord with for the pad
178 i: Signal(1), present only for IOType.In and IOType.InTriOut
179 Output from pad with pad input value for core.
180 o: Signal(1), present only for IOType.Out, IOType.TriOut and
182 Input to pad with pad output value.
183 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
184 Input to pad with pad output enable value.
189 if iotype
in (IOType
.In
, IOType
.InTriOut
):
190 sigs
.append(("i", 1))
191 if iotype
in (IOType
.Out
, IOType
.TriOut
, IOType
.InTriOut
):
192 sigs
.append(("o", 1))
193 if iotype
in (IOType
.TriOut
, IOType
.InTriOut
):
194 sigs
.append(("oe", 1))
196 return Layout((("core", sigs
), ("pad", sigs
)))
198 def __init__(self
, *, iotype
, name
=None, src_loc_at
=0):
199 super().__init
__(self
.__class
__.layout(iotype
), name
=name
,
200 src_loc_at
=src_loc_at
+1)
202 self
._iotype
= iotype
204 class _IDBypassBlock(Elaboratable
):
205 """TAP subblock for the ID shift register"""
206 def __init__(self
, *, manufacturer_id
, part_number
, version
,
207 tdi
, capture
, shift
, update
, bypass
,
210 if (not isinstance(manufacturer_id
, Const
) and
211 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
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 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 " \
342 "not big enough for allocated shiift registers"
343 ir_width
= self
._ir
_width
345 # TODO: Make commands numbers configurable
351 cmd_bypass
= 2**ir_width
- 1 # All ones
353 m
.submodules
._fsm
= fsm
= _FSM(bus
=self
.bus
)
354 m
.domains
.posjtag
= fsm
.posjtag
355 m
.domains
.negjtag
= fsm
.negjtag
359 m
.submodules
._irblock
= irblock
= _IRBlock(
360 ir_width
=ir_width
, cmd_idcode
=cmd_idcode
, tdi
=self
.bus
.tdi
,
361 capture
=(fsm
.isir
& fsm
.capture
),
362 shift
=(fsm
.isir
& fsm
.shift
),
363 update
=(fsm
.isir
& fsm
.update
),
364 name
=self
.name
+"_ir",
369 select_id
= fsm
.isdr
& ((ir
== cmd_idcode
) |
(ir
== cmd_bypass
))
370 m
.submodules
._idblock
= idblock
= _IDBypassBlock(
371 manufacturer_id
=self
._manufacturer
_id
,
372 part_number
=self
._part
_number
,
373 version
=self
._version
, tdi
=self
.bus
.tdi
,
374 capture
=(select_id
& fsm
.capture
),
375 shift
=(select_id
& fsm
.shift
),
376 update
=(select_id
& fsm
.update
),
377 bypass
=(ir
== cmd_bypass
),
378 name
=self
.name
+"_id",
381 # IO (Boundary scan) block
382 io_capture
= Signal()
386 io_bd2core
= Signal()
387 sample
= (ir
== cmd_extest
) |
(ir
== cmd_sample
)
388 preload
= (ir
== cmd_preload
)
389 select_io
= fsm
.isdr
& (sample | preload
)
391 io_capture
.eq(sample
& fsm
.capture
), # Don't capture if not sample
393 io_shift
.eq(select_io
& fsm
.shift
),
394 io_update
.eq(select_io
& fsm
.update
),
395 io_bd2io
.eq(ir
== cmd_extest
),
396 io_bd2core
.eq(ir
== cmd_intest
),
398 io_tdo
= self
._elaborate
_ios
(
400 capture
=io_capture
, shift
=io_shift
, update
=io_update
,
401 bd2io
=io_bd2io
, bd2core
=io_bd2core
,
404 # chain tdo: select as appropriate, to go into into shiftregs
405 tdo
= Signal(name
=self
.name
+"_tdo")
406 with m
.If(select_ir
):
407 m
.d
.comb
+= tdo
.eq(irblock
.tdo
)
408 with m
.Elif(select_id
):
409 m
.d
.comb
+= tdo
.eq(idblock
.tdo
)
410 with m
.Elif(select_io
):
411 m
.d
.comb
+= tdo
.eq(io_tdo
)
414 self
._elaborate
_shiftregs
(
415 m
, capture
=fsm
.capture
, shift
=fsm
.shift
, update
=fsm
.update
,
416 ir
=irblock
.ir
, tdo_jtag
=tdo
420 self
._elaborate
_wishbones
(m
)
425 def add_io(self
, *, iotype
, name
=None, src_loc_at
=0):
426 """Add a io cell to the boundary scan chain
429 - iotype: :class:`IOType` enum.
435 name
= "ioconn" + str(len(self
._ios
))
437 ioconn
= IOConn(iotype
=iotype
, name
=name
, src_loc_at
=src_loc_at
+1)
438 self
._ios
.append(ioconn
)
441 def _elaborate_ios(self
, *, m
, capture
, shift
, update
, bd2io
, bd2core
):
448 length
= sum(connlength
[conn
._iotype
] for conn
in self
._ios
)
452 io_sr
= Signal(length
)
453 io_bd
= Signal(length
)
457 for conn
in self
._ios
:
458 if conn
._iotype
== IOType
.In
:
459 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.pad
.i
)
461 elif conn
._iotype
== IOType
.Out
:
462 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.core
.o
)
464 elif conn
._iotype
== IOType
.TriOut
:
466 io_sr
[idx
].eq(conn
.core
.o
),
467 io_sr
[idx
+1].eq(conn
.core
.oe
),
470 elif conn
._iotype
== IOType
.InTriOut
:
472 io_sr
[idx
].eq(conn
.pad
.i
),
473 io_sr
[idx
+1].eq(conn
.core
.o
),
474 io_sr
[idx
+2].eq(conn
.core
.oe
),
478 raise("Internal error")
479 assert idx
== length
, "Internal error"
481 m
.d
.posjtag
+= io_sr
.eq(Cat(self
.bus
.tdi
, io_sr
[:-1]))
483 m
.d
.negjtag
+= io_bd
.eq(io_sr
)
486 for conn
in self
._ios
:
487 if conn
._iotype
== IOType
.In
:
488 m
.d
.comb
+= conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
))
490 elif conn
._iotype
== IOType
.Out
:
491 m
.d
.comb
+= conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
))
493 elif conn
._iotype
== IOType
.TriOut
:
495 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
)),
496 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.oe
)),
499 elif conn
._iotype
== IOType
.InTriOut
:
501 conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
)),
502 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.o
)),
503 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+2], conn
.core
.oe
)),
507 raise("Internal error")
508 assert idx
== length
, "Internal error"
512 def add_shiftreg(self
, *, ircode
, length
, domain
="sync", name
=None,
514 """Add a shift register to the JTAG interface
517 - ircode: code(s) for the IR; int or sequence of ints. In the latter
518 case this shiftreg is shared between different IR codes.
519 - length: the length of the shift register
520 - domain: the domain on which the signal will be used"""
526 ir_it
= ircodes
= (ircode
,)
527 for _ircode
in ir_it
:
528 if not isinstance(_ircode
, int) or _ircode
<= 0:
529 raise ValueError("IR code '{}' is not an int "
530 "greater than 0".format(_ircode
))
531 if _ircode
in self
._ircodes
:
532 raise ValueError("IR code '{}' already taken".format(_ircode
))
534 self
._ircodes
.extend(ircodes
)
537 name
= "sr{}".format(len(self
._srs
))
538 sr
= ShiftReg(sr_length
=length
, cmds
=len(ircodes
), name
=name
,
539 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`
563 # clockdomain latch update in `domain` clockdomain and see when
564 # it has falling edge.
565 # At that edge put isir in sr.oe for one `domain` clockdomain
566 update_core
= Signal(name
=sr
.name
+"_update_core")
567 update_core_prev
= Signal(name
=sr
.name
+"_update_core_prev")
569 update_core
.eq(sr_update
), # This is CDC from JTAG 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
,
606 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.
611 This means that if data is output the value of the next address
612 will be read automatically.
616 ircodes: sequence of three integer for the JTAG IR codes;
617 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
618 has a shift register of length 'address_width', the two other codes
619 share a shift register of length data_width.
620 address_width: width of the address
621 data_width: width of the data
624 wb: nmigen_soc.wishbone.bus.Interface
625 The Wishbone interface, is pipelined and has stall field.
627 if len(ircodes
) != 3:
628 raise ValueError("3 IR Codes have to be provided")
631 name
= "wb" + str(len(self
._wbs
))
632 sr_addr
= self
.add_shiftreg(
633 ircode
=ircodes
[0], length
=address_width
, domain
=domain
,
636 sr_data
= self
.add_shiftreg(
637 ircode
=ircodes
[1:], length
=data_width
, domain
=domain
,
641 wb
= WishboneInterface(data_width
=data_width
, addr_width
=address_width
,
642 granularity
=granularity
,
643 features
={"stall", "lock", "err", "rty"},
644 name
=name
, src_loc_at
=src_loc_at
+1)
646 self
._wbs
.append((sr_addr
, sr_data
, wb
, domain
))
650 def _elaborate_wishbones(self
, m
):
651 for sr_addr
, sr_data
, wb
, domain
in self
._wbs
:
652 m
.d
.comb
+= sr_addr
.i
.eq(wb
.adr
)
654 if hasattr(wb
, "sel"):
656 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
658 with m
.FSM(domain
=domain
) as fsm
:
659 with m
.State("IDLE"):
660 with m
.If(sr_addr
.oe
): # WBADDR code
661 m
.d
[domain
] += wb
.adr
.eq(sr_addr
.o
)
663 with m
.Elif(sr_data
.oe
[0]): # WBREAD code
665 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
667 with m
.Elif(sr_data
.oe
[1]): # WBWRITE code
668 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
670 with m
.State("READ"):
671 with m
.If(~wb
.stall
):
673 with m
.State("READACK"):
675 # Store read data in sr_data.i
676 # and keep it there til next read
677 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
679 with m
.State("WRITEREAD"):
680 with m
.If(~wb
.stall
):
681 m
.next
= "WRITEREADACK"
682 with m
.State("WRITEREADACK"):
684 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
688 wb
.cyc
.eq(~fsm
.ongoing("IDLE")),
689 wb
.stb
.eq(fsm
.ongoing("READ") | fsm
.ongoing("WRITEREAD")),
690 wb
.we
.eq(fsm
.ongoing("WRITEREAD")),