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 self
.posjtag
= ClockDomain("posjtag", local
=True)
29 self
.negjtag
= ClockDomain("negjtag", local
=True, clk_edge
="neg")
33 def elaborate(self
, platform
):
38 self
.posjtag
.clk
.eq(self
._bus
.tck
),
39 self
.posjtag
.rst
.eq(rst
),
40 self
.negjtag
.clk
.eq(self
._bus
.tck
),
41 self
.negjtag
.rst
.eq(rst
),
44 # Make local clock domain optionally using trst of JTAG bus as reset
45 if hasattr(self
._bus
, "trst"):
46 m
.domains
.local
= local
= ClockDomain(local
=True)
47 m
.d
.comb
+= local
.rst
.eq(self
._bus
.trst
)
49 m
.domains
.local
= local
= ClockDomain(local
=True, reset_less
=True)
50 m
.d
.comb
+= local
.clk
.eq(self
._bus
.tck
)
52 with m
.FSM(domain
="local") as fsm
:
53 with m
.State("TestLogicReset"):
54 # Be sure to reset isir, isdr
59 with m
.If(self
._bus
.tms
== 0):
60 m
.next
= "RunTestIdle"
61 with m
.State("RunTestIdle"):
62 # Be sure to reset isir, isdr
67 with m
.If(self
._bus
.tms
== 1):
68 m
.next
= "SelectDRScan"
69 with m
.State("SelectDRScan"):
70 with m
.If(self
._bus
.tms
== 0):
71 m
.d
.local
+= self
.isdr
.eq(1)
72 m
.next
= "CaptureState"
74 m
.next
= "SelectIRScan"
75 with m
.State("SelectIRScan"):
76 with m
.If(self
._bus
.tms
== 0):
77 m
.d
.local
+= self
.isir
.eq(1)
78 m
.next
= "CaptureState"
80 m
.next
= "TestLogicReset"
81 with m
.State("CaptureState"):
82 with m
.If(self
._bus
.tms
== 0):
86 with m
.State("ShiftState"):
87 with m
.If(self
._bus
.tms
== 1):
89 with m
.State("Exit1"):
90 with m
.If(self
._bus
.tms
== 0):
93 m
.next
= "UpdateState"
94 with m
.State("Pause"):
95 with m
.If(self
._bus
.tms
== 1):
97 with m
.State("Exit2"):
98 with m
.If(self
._bus
.tms
== 0):
101 m
.next
= "UpdateState"
102 with m
.State("UpdateState"):
107 with m
.If(self
._bus
.tms
== 0):
108 m
.next
= "RunTestIdle"
110 m
.next
= "SelectDRScan"
113 rst
.eq(fsm
.ongoing("TestLogicReset")),
114 self
.capture
.eq(fsm
.ongoing("CaptureState")),
115 self
.shift
.eq(fsm
.ongoing("ShiftState")),
116 self
.update
.eq(fsm
.ongoing("UpdateState")),
121 class _IRBlock(Elaboratable
):
122 """TAP subblock for handling the IR shift register"""
123 def __init__(self
, *, ir_width
, cmd_idcode
,
124 tdi
, capture
, shift
, update
,
127 self
.ir
= Signal(ir_width
, reset
=cmd_idcode
)
131 self
._capture
= capture
133 self
._update
= update
135 def elaborate(self
, platform
):
138 shift_ir
= Signal(len(self
.ir
), reset_less
=True)
140 m
.d
.comb
+= self
.tdo
.eq(self
.ir
[0])
141 with m
.If(self
._capture
):
142 m
.d
.posjtag
+= shift_ir
.eq(self
.ir
)
143 with m
.Elif(self
._shift
):
144 m
.d
.posjtag
+= shift_ir
.eq(Cat(shift_ir
[1:], self
._tdi
))
145 with m
.Elif(self
._update
):
146 # For ir we only update it on the rising edge of clock
147 # to avoid that we already have the new ir value when still in
149 m
.d
.posjtag
+= self
.ir
.eq(shift_ir
)
159 class IOConn(Record
):
160 """TAP subblock representing the interface for an JTAG IO cell.
161 It contains signal to connect to the core and to the pad
163 This object is normally only allocated and returned from ``TAP.add_io``
164 It is a Record subclass.
168 core: subrecord with signals for the core
169 i: Signal(1), present only for IOType.In and IOType.InTriOut.
170 Signal input to core with pad input value.
171 o: Signal(1), present only for IOType.Out, IOType.TriOut and
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
181 Input to pad with pad output value.
182 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
183 Input to pad with pad output enable value.
188 if iotype
in (IOType
.In
, IOType
.InTriOut
):
189 sigs
.append(("i", 1))
190 if iotype
in (IOType
.Out
, IOType
.TriOut
, IOType
.InTriOut
):
191 sigs
.append(("o", 1))
192 if iotype
in (IOType
.TriOut
, IOType
.InTriOut
):
193 sigs
.append(("oe", 1))
195 return Layout((("core", sigs
), ("pad", sigs
)))
197 def __init__(self
, *, iotype
, name
=None, src_loc_at
=0):
198 super().__init
__(self
.__class
__.layout(iotype
), name
=name
,
199 src_loc_at
=src_loc_at
+1)
201 self
._iotype
= iotype
203 class _IDBypassBlock(Elaboratable
):
204 """TAP subblock for the ID shift register"""
205 def __init__(self
, *, manufacturer_id
, part_number
, version
,
206 tdi
, capture
, shift
, update
, bypass
,
209 if (not isinstance(manufacturer_id
, Const
) and
210 len(manufacturer_id
) != 11):
211 raise ValueError("manufacturer_id has to be Const of length 11")
212 if not isinstance(part_number
, Const
) and len(manufacturer_id
) != 16:
213 raise ValueError("part_number has to be Const of length 16")
214 if not isinstance(version
, Const
) and len(version
) != 4:
215 raise ValueError("version has to be Const of length 4")
216 self
._id
= Cat(Const(1,1), manufacturer_id
, part_number
, version
)
218 self
.tdo
= Signal(name
=name
+"_tdo")
221 self
._capture
= capture
223 self
._update
= update
224 self
._bypass
= bypass
226 def elaborate(self
, platform
):
229 sr
= Signal(32, reset_less
=True, name
=self
.name
+"_sr")
231 # Local signals for the module
240 _capture
.eq(self
._capture
),
241 _shift
.eq(self
._shift
),
242 _update
.eq(self
._update
),
243 _bypass
.eq(self
._bypass
),
248 m
.d
.posjtag
+= sr
.eq(self
._id
)
251 m
.d
.posjtag
+= sr
[0].eq(_tdi
)
253 m
.d
.posjtag
+= sr
.eq(Cat(sr
[1:], _tdi
))
258 class ShiftReg(Record
):
259 """Object with interface for extra shift registers on a TAP.
264 cmds : int, default=1
265 The number of corresponding JTAG instructions
267 This object is normally only allocated and returned from ``TAP.add_shiftreg``
268 It is a Record subclass.
272 i: length=sr_length, FANIN
273 The input data sampled during capture state of the TAP
274 ie: length=cmds, FANOUT
275 Indicates that data is to be sampled by the JTAG TAP and
276 should be held stable. The bit indicates the corresponding
277 instruction for which data is asked.
278 This signal is kept high for a whole JTAG TAP clock cycle
279 and may thus be kept higher for more than one clock cycle
280 on the domain where ShiftReg is used.
281 The JTAG protocol does not allow insertion of wait states
282 so data need to be provided before ie goes down. The speed
283 of the response will determine the max. frequency for the
285 o: length=sr_length, FANOUT
286 The value of the shift register.
287 oe: length=cmds, FANOUT
288 Indicates that output is stable and can be sampled downstream because
289 JTAG TAP is in the Update state. The bit indicates the corresponding
290 instruction. The bit is only kept high for one clock cycle.
292 def __init__(self
, *, sr_length
, cmds
=1, name
=None, src_loc_at
=0):
294 ("i", sr_length
, Direction
.FANIN
),
295 ("ie", cmds
, Direction
.FANOUT
),
296 ("o", sr_length
, Direction
.FANOUT
),
297 ("oe", cmds
, Direction
.FANOUT
),
299 super().__init
__(layout
, name
=name
, src_loc_at
=src_loc_at
+1)
302 class TAP(Elaboratable
):
304 def __init__(self
, *, with_reset
=False, ir_width
=None,
305 manufacturer_id
=Const(0b10001111111, 11),
306 part_number
=Const(1, 16),
308 name
=None, src_loc_at
=0:
309 assert((ir_width
is None) or (isinstance(ir_width
, int) and
311 assert(len(version
) == 4)
314 name
= get_var_name(depth
=src_loc_at
+2, default
="TAP")
316 self
.bus
= Interface(with_reset
=with_reset
, name
=self
.name
+"_bus",
317 src_loc_at
=src_loc_at
+1)
321 self
._ir
_width
= ir_width
322 self
._manufacturer
_id
= manufacturer_id
323 self
._part
_number
= part_number
324 self
._version
= version
326 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at 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 " \
341 "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
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",
366 select_id
= fsm
.isdr
& ((ir
== cmd_idcode
) |
(ir
== cmd_bypass
))
367 m
.submodules
._idblock
= idblock
= _IDBypassBlock(
368 manufacturer_id
=self
._manufacturer
_id
,
369 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_capture
= Signal()
382 io_bd2core
= Signal()
383 sample
= (ir
== cmd_extest
) |
(ir
== cmd_sample
)
384 preload
= (ir
== cmd_preload
)
385 select_io
= fsm
.isdr
& (sample | preload
)
387 io_capture
.eq(sample
& fsm
.capture
), # Don't capture if not sample
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 tdo
= Signal(name
=self
.name
+"_tdo")
401 with m
.If(select_ir
):
402 m
.d
.comb
+= tdo
.eq(irblock
.tdo
)
403 with m
.Elif(select_id
):
404 m
.d
.comb
+= tdo
.eq(idblock
.tdo
)
405 with m
.Elif(select_io
):
406 m
.d
.comb
+= tdo
.eq(io_tdo
)
408 self
._elaborate
_shiftregs
(
409 m
, capture
=fsm
.capture
, shift
=fsm
.shift
, update
=fsm
.update
,
410 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
)
442 io_sr
= Signal(length
)
443 io_bd
= Signal(length
)
447 for conn
in self
._ios
:
448 if conn
._iotype
== IOType
.In
:
449 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.pad
.i
)
451 elif conn
._iotype
== IOType
.Out
:
452 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.core
.o
)
454 elif conn
._iotype
== IOType
.TriOut
:
456 io_sr
[idx
].eq(conn
.core
.o
),
457 io_sr
[idx
+1].eq(conn
.core
.oe
),
460 elif conn
._iotype
== IOType
.InTriOut
:
462 io_sr
[idx
].eq(conn
.pad
.i
),
463 io_sr
[idx
+1].eq(conn
.core
.o
),
464 io_sr
[idx
+2].eq(conn
.core
.oe
),
468 raise("Internal error")
469 assert idx
== length
, "Internal error"
471 m
.d
.posjtag
+= io_sr
.eq(Cat(self
.bus
.tdi
, io_sr
[:-1]))
473 m
.d
.negjtag
+= io_bd
.eq(io_sr
)
476 for conn
in self
._ios
:
477 if conn
._iotype
== IOType
.In
:
478 m
.d
.comb
+= conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
))
480 elif conn
._iotype
== IOType
.Out
:
481 m
.d
.comb
+= conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
))
483 elif conn
._iotype
== IOType
.TriOut
:
485 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
)),
486 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.oe
)),
489 elif conn
._iotype
== IOType
.InTriOut
:
491 conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
)),
492 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.o
)),
493 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+2], conn
.core
.oe
)),
497 raise("Internal error")
498 assert idx
== length
, "Internal error"
503 def add_shiftreg(self
, *, ircode
, length
, domain
="sync", name
=None,
505 """Add a shift register to the JTAG interface
508 - ircode: code(s) for the IR; int or sequence of ints. In the latter
509 case this 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 "
521 "greater than 0".format(_ircode
))
522 if _ircode
in self
._ircodes
:
523 raise ValueError("IR code '{}' already taken".format(_ircode
))
525 self
._ircodes
.extend(ircodes
)
528 name
= "sr{}".format(len(self
._srs
))
529 sr
= ShiftReg(sr_length
=length
, cmds
=len(ircodes
), name
=name
,
530 src_loc_at
=src_loc_at
+1)
531 self
._srs
.append((ircodes
, domain
, sr
))
535 def _elaborate_shiftregs(self
, m
, capture
, shift
, update
, ir
, tdo_jtag
):
536 # tdos is tuple of (tdo, tdo_en) for each shiftreg
538 for ircodes
, domain
, sr
in self
._srs
:
539 reg
= Signal(len(sr
.o
), name
=sr
.name
+"_reg")
540 m
.d
.comb
+= sr
.o
.eq(reg
)
542 isir
= Signal(len(ircodes
), name
=sr
.name
+"_isir")
543 sr_capture
= Signal(name
=sr
.name
+"_capture")
544 sr_shift
= Signal(name
=sr
.name
+"_shift")
545 sr_update
= Signal(name
=sr
.name
+"_update")
547 isir
.eq(Cat(ir
== ircode
for ircode
in ircodes
)),
548 sr_capture
.eq((isir
!= 0) & capture
),
549 sr_shift
.eq((isir
!= 0) & shift
),
550 sr_update
.eq((isir
!= 0) & update
),
553 # update signal is on the JTAG clockdomain, sr.oe is on `domain`
554 # clockdomain latch update in `domain` clockdomain and see when
555 # it has falling edge.
556 # At that edge put isir in sr.oe for one `domain` clockdomain
557 update_core
= Signal(name
=sr
.name
+"_update_core")
558 update_core_prev
= Signal(name
=sr
.name
+"_update_core_prev")
560 update_core
.eq(sr_update
), # This is CDC from JTAG domain
562 update_core_prev
.eq(update_core
)
564 with m
.If(update_core_prev
& ~update_core
):
565 # Falling edge of update
566 m
.d
[domain
] += sr
.oe
.eq(isir
)
568 m
.d
[domain
] += sr
.oe
.eq(0)
571 m
.d
.posjtag
+= reg
.eq(Cat(reg
[1:], self
.bus
.tdi
))
572 with m
.If(sr_capture
):
573 m
.d
.posjtag
+= reg
.eq(sr
.i
)
575 # tdo = reg[0], tdo_en = shift
576 tdos
.append((reg
[0], sr_shift
))
579 # Assign the right tdo to the bus tdo
580 for i
, (tdo
, tdo_en
) in enumerate(tdos
):
583 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
586 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
590 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
592 # Always connect tdo_jtag to
593 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
596 def add_wishbone(self
, *, ircodes
, address_width
, data_width
,
597 granularity
=None, domain
="sync",
598 name
=None, src_loc_at
=0):
599 """Add a wishbone interface
601 In order to allow high JTAG clock speed, data will be cached.
602 This means that if data is output the value of the next address
603 will be read automatically.
607 ircodes: sequence of three integer for the JTAG IR codes;
608 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
609 has a shift register of length 'address_width', the two other codes
610 share a shift register of length data_width.
611 address_width: width of the address
612 data_width: width of the data
615 wb: nmigen_soc.wishbone.bus.Interface
616 The Wishbone interface, is pipelined and has stall field.
618 if len(ircodes
) != 3:
619 raise ValueError("3 IR Codes have to be provided")
622 name
= "wb" + str(len(self
._wbs
))
623 sr_addr
= self
.add_shiftreg(
624 ircode
=ircodes
[0], length
=address_width
, domain
=domain
,
627 sr_data
= self
.add_shiftreg(
628 ircode
=ircodes
[1:], length
=data_width
, domain
=domain
,
632 wb
= WishboneInterface(data_width
=data_width
, addr_width
=address_width
,
633 granularity
=granularity
,
634 features
={"stall", "lock", "err", "rty"},
635 name
=name
, src_loc_at
=src_loc_at
+1)
637 self
._wbs
.append((sr_addr
, sr_data
, wb
, domain
))
641 def _elaborate_wishbones(self
, m
):
642 for sr_addr
, sr_data
, wb
, domain
in self
._wbs
:
643 m
.d
.comb
+= sr_addr
.i
.eq(wb
.adr
)
645 if hasattr(wb
, "sel"):
647 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
649 with m
.FSM(domain
=domain
) as fsm
:
650 with m
.State("IDLE"):
651 with m
.If(sr_addr
.oe
): # WBADDR code
652 m
.d
[domain
] += wb
.adr
.eq(sr_addr
.o
)
654 with m
.Elif(sr_data
.oe
[0]): # WBREAD code
656 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
658 with m
.Elif(sr_data
.oe
[1]): # WBWRITE code
659 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
661 with m
.State("READ"):
662 with m
.If(~wb
.stall
):
664 with m
.State("READACK"):
666 # Store read data in sr_data.i
667 # and keep it there til next read
668 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
670 with m
.State("WRITEREAD"):
671 with m
.If(~wb
.stall
):
672 m
.next
= "WRITEREADACK"
673 with m
.State("WRITEREADACK"):
675 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
679 wb
.cyc
.eq(~fsm
.ongoing("IDLE")),
680 wb
.stb
.eq(fsm
.ongoing("READ") | fsm
.ongoing("WRITEREAD")),
681 wb
.we
.eq(fsm
.ongoing("WRITEREAD")),