2753063b0c391249297785a21c9e065867a3bfd9
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")),
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
)
161 class IOConn(Record
):
162 """TAP subblock representing the interface for an JTAG IO cell.
163 It contains signal to connect to the core and to the pad
165 This object is normally only allocated and returned from ``TAP.add_io``
166 It is a Record subclass.
170 core: subrecord with signals for the core
171 i: Signal(1), present only for IOType.In and IOType.InTriOut.
172 Signal input to core with pad input value.
173 o: Signal(1), present only for IOType.Out, IOType.TriOut and IOType.InTriOut.
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 IOType.InTriOut.
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
, src_loc_at
=src_loc_at
+1)
200 self
._iotype
= iotype
202 class _IDBypassBlock(Elaboratable
):
203 """TAP subblock for the ID shift register"""
204 def __init__(self
, *, manufacturer_id
, part_number
, version
,
205 tdi
, capture
, shift
, update
, bypass
,
208 if not isinstance(manufacturer_id
, Const
) and len(manufacturer_id
) != 11:
209 raise ValueError("manufacturer_id has to be Const of length 11")
210 if not isinstance(part_number
, Const
) and len(manufacturer_id
) != 16:
211 raise ValueError("part_number has to be Const of length 16")
212 if not isinstance(version
, Const
) and len(version
) != 4:
213 raise ValueError("version has to be Const of length 4")
214 self
._id
= Cat(Const(1,1), manufacturer_id
, part_number
, version
)
216 self
.tdo
= Signal(name
=name
+"_tdo")
219 self
._capture
= capture
221 self
._update
= update
222 self
._bypass
= bypass
224 def elaborate(self
, platform
):
227 sr
= Signal(32, reset_less
=True, name
=self
.name
+"_sr")
229 # Local signals for the module
238 _capture
.eq(self
._capture
),
239 _shift
.eq(self
._shift
),
240 _update
.eq(self
._update
),
241 _bypass
.eq(self
._bypass
),
246 m
.d
.posjtag
+= sr
.eq(self
._id
)
249 m
.d
.posjtag
+= sr
[0].eq(_tdi
)
251 m
.d
.posjtag
+= sr
.eq(Cat(sr
[1:], _tdi
))
256 class ShiftReg(Record
):
257 """Object with interface for extra shift registers on a TAP.
262 cmds : int, default=1
263 The number of corresponding JTAG instructions
265 This object is normally only allocated and returned from ``TAP.add_shiftreg``
266 It is a Record subclass.
270 i: length=sr_length, FANIN
271 The input data sampled during capture state of the TAP
272 ie: length=cmds, FANOUT
273 Indicates that data is to be sampled by the JTAG TAP and
274 should be held stable. The bit indicates the corresponding
275 instruction for which data is asked.
276 This signal is kept high for a whole JTAG TAP clock cycle
277 and may thus be kept higher for more than one clock cycle
278 on the domain where ShiftReg is used.
279 The JTAG protocol does not allow insertion of wait states
280 so data need to be provided before ie goes down. The speed
281 of the response will determine the max. frequency for the
283 o: length=sr_length, FANOUT
284 The value of the shift register.
285 oe: length=cmds, FANOUT
286 Indicates that output is stable and can be sampled downstream because
287 JTAG TAP is in the Update state. The bit indicates the corresponding
288 instruction. The bit is only kept high for one clock cycle.
290 def __init__(self
, *, sr_length
, cmds
=1, name
=None, src_loc_at
=0):
292 ("i", sr_length
, Direction
.FANIN
),
293 ("ie", cmds
, Direction
.FANOUT
),
294 ("o", sr_length
, Direction
.FANOUT
),
295 ("oe", cmds
, Direction
.FANOUT
),
297 super().__init
__(layout
, name
=name
, src_loc_at
=src_loc_at
+1)
300 class TAP(Elaboratable
):
303 self
, *, with_reset
=False, ir_width
=None,
304 manufacturer_id
=Const(0b10001111111, 11), part_number
=Const(1, 16),
306 name
=None, src_loc_at
=0
308 assert((ir_width
is None) or (isinstance(ir_width
, int) and ir_width
>= 2))
309 assert(len(version
) == 4)
312 name
= get_var_name(depth
=src_loc_at
+2, default
="TAP")
314 self
.bus
= Interface(with_reset
=with_reset
, name
=self
.name
+"_bus",
315 src_loc_at
=src_loc_at
+1)
319 self
._ir
_width
= ir_width
320 self
._manufacturer
_id
= manufacturer_id
321 self
._part
_number
= part_number
322 self
._version
= version
324 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at the end
330 def elaborate(self
, platform
):
333 # Determine ir_width if not fixed.
334 ir_max
= max(self
._ircodes
) + 1 # One extra code needed with all ones
335 ir_width
= len("{:b}".format(ir_max
))
336 if self
._ir
_width
is not None:
337 assert self
._ir
_width
>= ir_width
, "Specified JTAG IR width not big enough for allocated shiift registers"
338 ir_width
= self
._ir
_width
340 # TODO: Make commands numbers configurable
346 cmd_bypass
= 2**ir_width
- 1 # All ones
348 m
.submodules
._fsm
= fsm
= _FSM(bus
=self
.bus
)
349 m
.domains
.posjtag
= fsm
.posjtag
350 m
.domains
.negjtag
= fsm
.negjtag
354 m
.submodules
._irblock
= irblock
= _IRBlock(
355 ir_width
=ir_width
, cmd_idcode
=cmd_idcode
, tdi
=self
.bus
.tdi
,
356 capture
=(fsm
.isir
& fsm
.capture
),
357 shift
=(fsm
.isir
& fsm
.shift
),
358 update
=(fsm
.isir
& fsm
.update
),
359 name
=self
.name
+"_ir",
364 select_id
= fsm
.isdr
& ((ir
== cmd_idcode
) |
(ir
== cmd_bypass
))
365 m
.submodules
._idblock
= idblock
= _IDBypassBlock(
366 manufacturer_id
=self
._manufacturer
_id
, part_number
=self
._part
_number
,
367 version
=self
._version
, tdi
=self
.bus
.tdi
,
368 capture
=(select_id
& fsm
.capture
),
369 shift
=(select_id
& fsm
.shift
),
370 update
=(select_id
& fsm
.update
),
371 bypass
=(ir
== cmd_bypass
),
372 name
=self
.name
+"_id",
375 # IO (Boundary scan) block
376 io_capture
= Signal()
380 io_bd2core
= Signal()
381 sample
= (ir
== cmd_extest
) |
(ir
== cmd_sample
)
382 preload
= (ir
== cmd_preload
)
383 select_io
= fsm
.isdr
& (sample | preload
)
385 io_capture
.eq(sample
& fsm
.capture
), # Don't capture if not sample (like for PRELOAD)
386 io_shift
.eq(select_io
& fsm
.shift
),
387 io_update
.eq(select_io
& fsm
.update
),
388 io_bd2io
.eq(ir
== cmd_extest
),
389 io_bd2core
.eq(ir
== cmd_intest
),
391 io_tdo
= self
._elaborate
_ios
(
393 capture
=io_capture
, shift
=io_shift
, update
=io_update
,
394 bd2io
=io_bd2io
, bd2core
=io_bd2core
,
397 # chain tdo: select as appropriate, to go into into shiftregs
398 tdo
= Signal(name
=self
.name
+"_tdo")
399 with m
.If(select_ir
):
400 m
.d
.comb
+= tdo
.eq(irblock
.tdo
)
401 with m
.Elif(select_id
):
402 m
.d
.comb
+= tdo
.eq(idblock
.tdo
)
403 with m
.Elif(select_io
):
404 m
.d
.comb
+= tdo
.eq(io_tdo
)
407 self
._elaborate
_shiftregs
(
408 m
, capture
=fsm
.capture
, shift
=fsm
.shift
, update
=fsm
.update
,
409 ir
=irblock
.ir
, tdo_jtag
=tdo
413 self
._elaborate
_wishbones
(m
)
418 def add_io(self
, *, iotype
, name
=None, src_loc_at
=0):
419 """Add a io cell to the boundary scan chain
422 - iotype: :class:`IOType` enum.
428 name
= "ioconn" + str(len(self
._ios
))
430 ioconn
= IOConn(iotype
=iotype
, name
=name
, src_loc_at
=src_loc_at
+1)
431 self
._ios
.append(ioconn
)
434 def _elaborate_ios(self
, *, m
, capture
, shift
, update
, bd2io
, bd2core
):
441 length
= sum(connlength
[conn
._iotype
] for conn
in self
._ios
)
445 io_sr
= Signal(length
)
446 io_bd
= Signal(length
)
450 for conn
in self
._ios
:
451 if conn
._iotype
== IOType
.In
:
452 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.pad
.i
)
454 elif conn
._iotype
== IOType
.Out
:
455 m
.d
.posjtag
+= io_sr
[idx
].eq(conn
.core
.o
)
457 elif conn
._iotype
== IOType
.TriOut
:
459 io_sr
[idx
].eq(conn
.core
.o
),
460 io_sr
[idx
+1].eq(conn
.core
.oe
),
463 elif conn
._iotype
== IOType
.InTriOut
:
465 io_sr
[idx
].eq(conn
.pad
.i
),
466 io_sr
[idx
+1].eq(conn
.core
.o
),
467 io_sr
[idx
+2].eq(conn
.core
.oe
),
471 raise("Internal error")
472 assert idx
== length
, "Internal error"
474 m
.d
.posjtag
+= io_sr
.eq(Cat(self
.bus
.tdi
, io_sr
[:-1]))
476 m
.d
.negjtag
+= io_bd
.eq(io_sr
)
479 for conn
in self
._ios
:
480 if conn
._iotype
== IOType
.In
:
481 m
.d
.comb
+= conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
))
483 elif conn
._iotype
== IOType
.Out
:
484 m
.d
.comb
+= conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
))
486 elif conn
._iotype
== IOType
.TriOut
:
488 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
)),
489 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.oe
)),
492 elif conn
._iotype
== IOType
.InTriOut
:
494 conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
)),
495 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.o
)),
496 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+2], conn
.core
.oe
)),
500 raise("Internal error")
501 assert idx
== length
, "Internal error"
505 def add_shiftreg(self
, *, ircode
, length
, domain
="sync", name
=None, src_loc_at
=0):
506 """Add a shift register to the JTAG interface
509 - ircode: code(s) for the IR; int or sequence of ints. In the latter case this
510 shiftreg is shared between different IR codes.
511 - length: the length of the shift register
512 - domain: the domain on which the signal will be used"""
518 ir_it
= ircodes
= (ircode
,)
519 for _ircode
in ir_it
:
520 if not isinstance(_ircode
, int) or _ircode
<= 0:
521 raise ValueError("IR code '{}' is not an int 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
, src_loc_at
=src_loc_at
+1)
530 self
._srs
.append((ircodes
, domain
, sr
))
534 def _elaborate_shiftregs(self
, m
, capture
, shift
, update
, ir
, tdo_jtag
):
535 # tdos is tuple of (tdo, tdo_en) for each shiftreg
537 for ircodes
, domain
, sr
in self
._srs
:
538 reg
= Signal(len(sr
.o
), name
=sr
.name
+"_reg")
539 m
.d
.comb
+= sr
.o
.eq(reg
)
541 isir
= Signal(len(ircodes
), name
=sr
.name
+"_isir")
542 sr_capture
= Signal(name
=sr
.name
+"_capture")
543 sr_shift
= Signal(name
=sr
.name
+"_shift")
544 sr_update
= Signal(name
=sr
.name
+"_update")
546 isir
.eq(Cat(ir
== ircode
for ircode
in ircodes
)),
547 sr_capture
.eq((isir
!= 0) & capture
),
548 sr_shift
.eq((isir
!= 0) & shift
),
549 sr_update
.eq((isir
!= 0) & update
),
552 # update signal is on the JTAG clockdomain, sr.oe is on `domain` clockdomain
553 # latch update in `domain` clockdomain and see when it has falling edge.
554 # At that edge put isir in sr.oe for one `domain` clockdomain
555 update_core
= Signal(name
=sr
.name
+"_update_core")
556 update_core_prev
= Signal(name
=sr
.name
+"_update_core_prev")
558 update_core
.eq(sr_update
), # This is CDC from JTAG domain to given domain
559 update_core_prev
.eq(update_core
)
561 with m
.If(update_core_prev
& ~update_core
):
562 # Falling edge of update
563 m
.d
[domain
] += sr
.oe
.eq(isir
)
565 m
.d
[domain
] += sr
.oe
.eq(0)
568 m
.d
.posjtag
+= reg
.eq(Cat(reg
[1:], self
.bus
.tdi
))
569 with m
.If(sr_capture
):
570 m
.d
.posjtag
+= reg
.eq(sr
.i
)
572 # tdo = reg[0], tdo_en = shift
573 tdos
.append((reg
[0], sr_shift
))
576 # Assign the right tdo to the bus tdo
577 for i
, (tdo
, tdo_en
) in enumerate(tdos
):
580 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
583 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
587 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
589 # Always connect tdo_jtag to
590 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
593 def add_wishbone(self
, *, ircodes
, address_width
, data_width
, granularity
=None, domain
="sync",
594 name
=None, src_loc_at
=0):
595 """Add a wishbone interface
597 In order to allow high JTAG clock speed, data will be cached. This means that if data is
598 output the value of the next address will be read automatically.
602 ircodes: sequence of three integer for the JTAG IR codes;
603 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
604 has a shift register of length 'address_width', the two other codes
605 share a shift register of length data_width.
606 address_width: width of the address
607 data_width: width of the data
610 wb: nmigen_soc.wishbone.bus.Interface
611 The Wishbone interface, is pipelined and has stall field.
613 if len(ircodes
) != 3:
614 raise ValueError("3 IR Codes have to be provided")
617 name
= "wb" + str(len(self
._wbs
))
618 sr_addr
= self
.add_shiftreg(
619 ircode
=ircodes
[0], length
=address_width
, domain
=domain
,
622 sr_data
= self
.add_shiftreg(
623 ircode
=ircodes
[1:], length
=data_width
, domain
=domain
,
627 wb
= WishboneInterface(data_width
=data_width
, addr_width
=address_width
,
628 granularity
=granularity
, features
={"stall", "lock", "err", "rty"},
629 name
=name
, src_loc_at
=src_loc_at
+1)
631 self
._wbs
.append((sr_addr
, sr_data
, wb
, domain
))
635 def _elaborate_wishbones(self
, m
):
636 for sr_addr
, sr_data
, wb
, domain
in self
._wbs
:
637 m
.d
.comb
+= sr_addr
.i
.eq(wb
.adr
)
639 if hasattr(wb
, "sel"):
641 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
643 with m
.FSM(domain
=domain
) as fsm
:
644 with m
.State("IDLE"):
645 with m
.If(sr_addr
.oe
): # WBADDR code
646 m
.d
[domain
] += wb
.adr
.eq(sr_addr
.o
)
648 with m
.Elif(sr_data
.oe
[0]): # WBREAD code
650 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
652 with m
.Elif(sr_data
.oe
[1]): # WBWRITE code
653 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
655 with m
.State("READ"):
656 with m
.If(~wb
.stall
):
658 with m
.State("READACK"):
660 # Store read data in sr_data.i and keep it there til next read
661 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
663 with m
.State("WRITEREAD"):
664 with m
.If(~wb
.stall
):
665 m
.next
= "WRITEREADACK"
666 with m
.State("WRITEREADACK"):
668 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
672 wb
.cyc
.eq(~fsm
.ongoing("IDLE")),
673 wb
.stb
.eq(fsm
.ongoing("READ") | fsm
.ongoing("WRITEREAD")),
674 wb
.we
.eq(fsm
.ongoing("WRITEREAD")),