add code-comments regarding potential use of FFSynchroniser
[c4m-jtag.git] / c4m / nmigen / jtag / tap.py
1 #!/usr/bin/env python3
2 import os, textwrap
3 from enum import Enum, auto
4
5 from nmigen import (Elaboratable, Signal, Module, ClockDomain, Cat, Record,
6 Const, Mux)
7 from nmigen.hdl.rec import Direction, Layout
8 from nmigen.tracer import get_var_name
9
10 from nmigen_soc.wishbone import Interface as WishboneInterface
11
12 from .bus import Interface, DMIInterface
13
14 __all__ = [
15 "TAP", "ShiftReg", "IOType", "IOConn",
16 ]
17
18
19 class _FSM(Elaboratable):
20 """TAP subblock for the FSM"""
21 def __init__(self, *, bus):
22 self.isir = Signal()
23 self.isdr = Signal()
24 self.capture = Signal()
25 self.shift = Signal()
26 self.update = Signal()
27
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")
31
32 self._bus = bus
33
34 def elaborate(self, platform):
35 m = Module()
36
37 rst = Signal()
38 m.d.comb += [
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),
43 ]
44
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)
49 else:
50 m.domains.local = local = ClockDomain(local=True, reset_less=True)
51 m.d.comb += local.clk.eq(self._bus.tck)
52
53 with m.FSM(domain="local") as fsm:
54 with m.State("TestLogicReset"):
55 # Be sure to reset isir, isdr
56 m.d.local += [
57 self.isir.eq(0),
58 self.isdr.eq(0),
59 ]
60 with m.If(self._bus.tms == 0):
61 m.next = "RunTestIdle"
62 with m.State("RunTestIdle"):
63 # Be sure to reset isir, isdr
64 m.d.local += [
65 self.isir.eq(0),
66 self.isdr.eq(0),
67 ]
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"
74 with m.Else():
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"
80 with m.Else():
81 m.next = "TestLogicReset"
82 with m.State("CaptureState"):
83 with m.If(self._bus.tms == 0):
84 m.next = "ShiftState"
85 with m.Else():
86 m.next = "Exit1"
87 with m.State("ShiftState"):
88 with m.If(self._bus.tms == 1):
89 m.next = "Exit1"
90 with m.State("Exit1"):
91 with m.If(self._bus.tms == 0):
92 m.next = "Pause"
93 with m.Else():
94 m.next = "UpdateState"
95 with m.State("Pause"):
96 with m.If(self._bus.tms == 1):
97 m.next = "Exit2"
98 with m.State("Exit2"):
99 with m.If(self._bus.tms == 0):
100 m.next = "ShiftState"
101 with m.Else():
102 m.next = "UpdateState"
103 with m.State("UpdateState"):
104 m.d.local += [
105 self.isir.eq(0),
106 self.isdr.eq(0),
107 ]
108 with m.If(self._bus.tms == 0):
109 m.next = "RunTestIdle"
110 with m.Else():
111 m.next = "SelectDRScan"
112
113 m.d.comb += [
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")),
118 ]
119
120 return m
121
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,
126 name):
127 self.name = name
128 self.ir = Signal(ir_width, reset=cmd_idcode)
129 self.tdo = Signal()
130
131 self._tdi = tdi
132 self._capture = capture
133 self._shift = shift
134 self._update = update
135
136 def elaborate(self, platform):
137 m = Module()
138
139 shift_ir = Signal(len(self.ir), reset_less=True)
140
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
149 # Update state
150 m.d.posjtag += self.ir.eq(shift_ir)
151
152 return m
153
154 class IOType(Enum):
155 In = auto()
156 Out = auto()
157 TriOut = auto()
158 InTriOut = auto()
159
160 class IOConn(Record):
161 lengths = {
162 IOType.In: 1,
163 IOType.Out: 1,
164 IOType.TriOut: 2,
165 IOType.InTriOut: 3,
166 }
167
168 """TAP subblock representing the interface for an JTAG IO cell.
169 It contains signal to connect to the core and to the pad
170
171 This object is normally only allocated and returned from ``TAP.add_io``
172 It is a Record subclass.
173
174 Attributes
175 ----------
176 core: subrecord with signals for the core
177 i: Signal(1), present only for IOType.In and IOType.InTriOut.
178 Signal input to core with pad input value.
179 o: Signal(1), present only for IOType.Out, IOType.TriOut and
180 IOType.InTriOut.
181 Signal output from core with the pad output value.
182 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
183 Signal output from core with the pad output enable value.
184 pad: subrecord with for the pad
185 i: Signal(1), present only for IOType.In and IOType.InTriOut
186 Output from pad with pad input value for core.
187 o: Signal(1), present only for IOType.Out, IOType.TriOut and
188 IOType.InTriOut.
189 Input to pad with pad output value.
190 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
191 Input to pad with pad output enable value.
192 """
193 @staticmethod
194 def layout(iotype):
195 sigs = []
196 if iotype in (IOType.In, IOType.InTriOut):
197 sigs.append(("i", 1))
198 if iotype in (IOType.Out, IOType.TriOut, IOType.InTriOut):
199 sigs.append(("o", 1))
200 if iotype in (IOType.TriOut, IOType.InTriOut):
201 sigs.append(("oe", 1))
202
203 return Layout((("core", sigs), ("pad", sigs)))
204
205 def __init__(self, *, iotype, name=None, src_loc_at=0):
206 super().__init__(self.__class__.layout(iotype), name=name,
207 src_loc_at=src_loc_at+1)
208
209 self._iotype = iotype
210
211 class _IDBypassBlock(Elaboratable):
212 """TAP subblock for the ID shift register"""
213 def __init__(self, *, manufacturer_id, part_number, version,
214 tdi, capture, shift, update, bypass,
215 name):
216 self.name = name
217 if (not isinstance(manufacturer_id, Const) and
218 len(manufacturer_id) != 11):
219 raise ValueError("manufacturer_id has to be Const of length 11")
220 if not isinstance(part_number, Const) and len(manufacturer_id) != 16:
221 raise ValueError("part_number has to be Const of length 16")
222 if not isinstance(version, Const) and len(version) != 4:
223 raise ValueError("version has to be Const of length 4")
224 self._id = Cat(Const(1,1), manufacturer_id, part_number, version)
225
226 self.tdo = Signal(name=name+"_tdo")
227
228 self._tdi = tdi
229 self._capture = capture
230 self._shift = shift
231 self._update = update
232 self._bypass = bypass
233
234 def elaborate(self, platform):
235 m = Module()
236
237 sr = Signal(32, reset_less=True, name=self.name+"_sr")
238
239 # Local signals for the module
240 _tdi = Signal()
241 _capture = Signal()
242 _shift = Signal()
243 _update = Signal()
244 _bypass = Signal()
245
246 m.d.comb += [
247 _tdi.eq(self._tdi),
248 _capture.eq(self._capture),
249 _shift.eq(self._shift),
250 _update.eq(self._update),
251 _bypass.eq(self._bypass),
252 self.tdo.eq(sr[0]),
253 ]
254
255 with m.If(_capture):
256 m.d.posjtag += sr.eq(self._id)
257 with m.Elif(_shift):
258 with m.If(_bypass):
259 m.d.posjtag += sr[0].eq(_tdi)
260 with m.Else():
261 m.d.posjtag += sr.eq(Cat(sr[1:], _tdi))
262
263 return m
264
265
266 class ShiftReg(Record):
267 """Object with interface for extra shift registers on a TAP.
268
269 Parameters
270 ----------
271 sr_length : int
272 cmds : int, default=1
273 The number of corresponding JTAG instructions
274
275 This object is normally only allocated and returned from ``TAP.add_shiftreg``
276 It is a Record subclass.
277
278 Attributes
279 ----------
280 i: length=sr_length, FANIN
281 The input data sampled during capture state of the TAP
282 ie: length=cmds, FANOUT
283 Indicates that data is to be sampled by the JTAG TAP and
284 should be held stable. The bit indicates the corresponding
285 instruction for which data is asked.
286 This signal is kept high for a whole JTAG TAP clock cycle
287 and may thus be kept higher for more than one clock cycle
288 on the domain where ShiftReg is used.
289 The JTAG protocol does not allow insertion of wait states
290 so data need to be provided before ie goes down. The speed
291 of the response will determine the max. frequency for the
292 JTAG interface.
293 o: length=sr_length, FANOUT
294 The value of the shift register.
295 oe: length=cmds, FANOUT
296 Indicates that output is stable and can be sampled downstream because
297 JTAG TAP is in the Update state. The bit indicates the corresponding
298 instruction. The bit is only kept high for one clock cycle.
299 """
300 def __init__(self, *, sr_length, cmds=1, name=None, src_loc_at=0):
301 layout = [
302 ("i", sr_length, Direction.FANIN),
303 ("ie", cmds, Direction.FANOUT),
304 ("o", sr_length, Direction.FANOUT),
305 ("oe", cmds, Direction.FANOUT),
306 ]
307 super().__init__(layout, name=name, src_loc_at=src_loc_at+1)
308
309 class TAP(Elaboratable):
310 #TODO: Document TAP
311 def __init__(self, *, with_reset=False, ir_width=None,
312 manufacturer_id=Const(0b10001111111, 11),
313 part_number=Const(1, 16),
314 version=Const(0, 4),
315 name=None, src_loc_at=0):
316 assert((ir_width is None) or (isinstance(ir_width, int) and
317 ir_width >= 2))
318 assert(len(version) == 4)
319
320 if name is None:
321 name = get_var_name(depth=src_loc_at+2, default="TAP")
322 self.name = name
323 self.bus = Interface(with_reset=with_reset, name=self.name+"_bus",
324 src_loc_at=src_loc_at+1)
325
326 ##
327
328 self._ir_width = ir_width
329 self._manufacturer_id = manufacturer_id
330 self._part_number = part_number
331 self._version = version
332
333 self._ircodes = [0, 1, 2] # Already taken codes, all ones added at end
334
335 self._ios = []
336 self._srs = []
337 self._wbs = []
338 self._dmis = []
339
340 def elaborate(self, platform):
341 m = Module()
342
343 # Determine ir_width if not fixed.
344 ir_max = max(self._ircodes) + 1 # One extra code needed with all ones
345 ir_width = len("{:b}".format(ir_max))
346 if self._ir_width is not None:
347 assert self._ir_width >= ir_width, "Specified JTAG IR width " \
348 "not big enough for allocated shiift registers"
349 ir_width = self._ir_width
350
351 # TODO: Make commands numbers configurable
352 cmd_extest = 0
353 cmd_intest = 0
354 cmd_idcode = 1
355 cmd_sample = 2
356 cmd_preload = 2
357 cmd_bypass = 2**ir_width - 1 # All ones
358
359 m.submodules.fsm = fsm = _FSM(bus=self.bus)
360 m.domains.posjtag = fsm.posjtag
361 m.domains.negjtag = fsm.negjtag
362
363 # IR block
364 select_ir = fsm.isir
365 m.submodules.irblock = irblock = _IRBlock(
366 ir_width=ir_width, cmd_idcode=cmd_idcode, tdi=self.bus.tdi,
367 capture=(fsm.isir & fsm.capture),
368 shift=(fsm.isir & fsm.shift),
369 update=(fsm.isir & fsm.update),
370 name=self.name+"_ir",
371 )
372 ir = irblock.ir
373
374 # ID block
375 select_id = Signal()
376 id_bypass = Signal()
377 m.d.comb += select_id.eq(fsm.isdr &
378 ((ir == cmd_idcode) | (ir == cmd_bypass)))
379 m.d.comb += id_bypass.eq(ir == cmd_bypass)
380 m.submodules.idblock = idblock = _IDBypassBlock(
381 manufacturer_id=self._manufacturer_id,
382 part_number=self._part_number,
383 version=self._version, tdi=self.bus.tdi,
384 capture=(select_id & fsm.capture),
385 shift=(select_id & fsm.shift),
386 update=(select_id & fsm.update),
387 bypass=id_bypass,
388 name=self.name+"_id",
389 )
390
391 # IO (Boundary scan) block
392 io_capture = Signal()
393 io_shift = Signal()
394 io_update = Signal()
395 io_bd2io = Signal()
396 io_bd2core = Signal()
397 sample = (ir == cmd_extest) | (ir == cmd_sample)
398 preload = (ir == cmd_preload)
399 select_io = fsm.isdr & (sample | preload)
400 m.d.comb += [
401 io_capture.eq(sample & fsm.capture), # Don't capture if not sample
402 # (like for PRELOAD)
403 io_shift.eq(select_io & fsm.shift),
404 io_update.eq(select_io & fsm.update),
405 io_bd2io.eq(ir == cmd_extest),
406 io_bd2core.eq(ir == cmd_intest),
407 ]
408 io_tdo = self._elaborate_ios(
409 m=m,
410 capture=io_capture, shift=io_shift, update=io_update,
411 bd2io=io_bd2io, bd2core=io_bd2core,
412 )
413
414 # chain tdo: select as appropriate, to go into into shiftregs
415 tdo = Signal(name=self.name+"_tdo")
416 with m.If(select_ir):
417 m.d.comb += tdo.eq(irblock.tdo)
418 with m.Elif(select_id):
419 m.d.comb += tdo.eq(idblock.tdo)
420 with m.Elif(select_io):
421 m.d.comb += tdo.eq(io_tdo)
422
423 # shiftregs block
424 self._elaborate_shiftregs(
425 m, capture=fsm.capture, shift=fsm.shift, update=fsm.update,
426 ir=irblock.ir, tdo_jtag=tdo
427 )
428
429 # wishbone
430 self._elaborate_wishbones(m)
431
432 # DMI (Debug Memory Interface)
433 self._elaborate_dmis(m)
434
435 return m
436
437 def add_dmi(self, *, ircodes, address_width=8, data_width=64,
438 domain="sync", name=None):
439 """Add a DMI interface
440
441 * writing to DMIADDR will automatically trigger a DMI READ.
442 the DMI address does not alter (so writes can be done at that addr)
443 * reading from DMIREAD triggers a DMI READ at the current DMI addr
444 the address is automatically incremented by 1 after.
445 * writing to DMIWRITE triggers a DMI WRITE at the current DMI addr
446 the address is automatically incremented by 1 after.
447
448 Parameters:
449 -----------
450 ircodes: sequence of three integer for the JTAG IR codes;
451 they represent resp. DMIADDR, DMIREAD and DMIWRITE.
452 First code has a shift register of length 'address_width',
453 the two other codes share a shift register of length
454 data_width.
455
456 address_width: width of the address
457 data_width: width of the data
458
459 Returns:
460 dmi: soc.debug.dmi.DMIInterface
461 The DMI interface
462 """
463 if len(ircodes) != 3:
464 raise ValueError("3 IR Codes have to be provided")
465
466 if name is None:
467 name = "dmi" + str(len(self._dmis))
468
469 # add 2 shift registers: one for addr, one for data.
470 sr_addr = self.add_shiftreg(ircode=ircodes[0], length=address_width,
471 domain=domain, name=name+"_addrsr")
472 sr_data = self.add_shiftreg(ircode=ircodes[1:], length=data_width,
473 domain=domain, name=name+"_datasr")
474
475 dmi = DMIInterface(name=name)
476 self._dmis.append((sr_addr, sr_data, dmi, domain))
477
478 return dmi
479
480 def _elaborate_dmis(self, m):
481 for sr_addr, sr_data, dmi, domain in self._dmis:
482 cd = m.d[domain]
483 m.d.comb += sr_addr.i.eq(dmi.addr_i)
484
485 with m.FSM(domain=domain) as ds:
486
487 # detect mode based on whether jtag addr or data read/written
488 with m.State("IDLE"):
489 with m.If(sr_addr.oe): # DMIADDR code
490 cd += dmi.addr_i.eq(sr_addr.o)
491 m.next = "READ"
492 with m.Elif(sr_data.oe[0]): # DMIREAD code
493 # If data is
494 cd += dmi.addr_i.eq(dmi.addr_i + 1)
495 m.next = "READ"
496 with m.Elif(sr_data.oe[1]): # DMIWRITE code
497 cd += dmi.din.eq(sr_data.o)
498 m.next = "WRRD"
499
500 # req_i raises for 1 clock
501 with m.State("READ"):
502 m.next = "READACK"
503
504 # wait for read ack
505 with m.State("READACK"):
506 with m.If(dmi.ack_o):
507 # Store read data in sr_data.i hold till next read
508 # Note: could use FFSynchroniser
509 cd += sr_data.i.eq(dmi.dout)
510 m.next = "IDLE"
511
512 # req_i raises for 1 clock
513 with m.State("WRRD"):
514 m.next = "WRRDACK"
515
516 # wait for write ack
517 with m.State("WRRDACK"):
518 with m.If(dmi.ack_o):
519 cd += dmi.addr_i.eq(dmi.addr_i + 1)
520 m.next = "READ" # for readwrite
521
522 # set DMI req and write-enable based on ongoing FSM states
523 m.d.comb += [
524 dmi.req_i.eq(ds.ongoing("READ") | ds.ongoing("WRRD")),
525 dmi.we_i.eq(ds.ongoing("WRRD")),
526 ]
527
528 def add_io(self, *, iotype, name=None, src_loc_at=0):
529 """Add a io cell to the boundary scan chain
530
531 Parameters:
532 - iotype: :class:`IOType` enum.
533
534 Returns:
535 - :class:`IOConn`
536 """
537 if name is None:
538 name = "ioconn" + str(len(self._ios))
539
540 ioconn = IOConn(iotype=iotype, name=name, src_loc_at=src_loc_at+1)
541 self._ios.append(ioconn)
542 return ioconn
543
544 def _elaborate_ios(self, *, m, capture, shift, update, bd2io, bd2core):
545 length = sum(IOConn.lengths[conn._iotype] for conn in self._ios)
546 if length == 0:
547 return self.bus.tdi
548
549 io_sr = Signal(length)
550 io_bd = Signal(length)
551
552 # Boundary scan "capture" mode. makes I/O status available via SR
553 with m.If(capture):
554 iol = []
555 idx = 0
556 for conn in self._ios:
557 # in appropriate sequence: In/TriOut has pad.i,
558 # Out.InTriOut has everything, Out and TriOut have core.o
559 if conn._iotype in [IOType.In, IOType.InTriOut]:
560 iol.append(conn.pad.i)
561 if conn._iotype in [IOType.Out, IOType.InTriOut]:
562 iol.append(conn.core.o)
563 if conn._iotype in [IOType.TriOut, IOType.InTriOut]:
564 iol.append(conn.core.oe)
565 # length double-check
566 idx += IOConn.lengths[conn._iotype] # fails if wrong type
567 assert idx == length, "Internal error"
568 m.d.posjtag += io_sr.eq(Cat(*iol)) # assigns all io_sr in one hit
569
570 # "Shift" mode (sends out captured data on tdo, sets incoming from tdi)
571 with m.Elif(shift):
572 m.d.posjtag += io_sr.eq(Cat(self.bus.tdi, io_sr[:-1]))
573
574 # "Update" mode
575 with m.Elif(update):
576 m.d.negjtag += io_bd.eq(io_sr)
577
578 # sets up IO (pad<->core) or in testing mode depending on requested
579 # mode, via Muxes controlled by bd2core and bd2io
580 idx = 0
581 for conn in self._ios:
582 if conn._iotype == IOType.In:
583 m.d.comb += conn.core.i.eq(Mux(bd2core, io_bd[idx], conn.pad.i))
584 idx += 1
585 elif conn._iotype == IOType.Out:
586 m.d.comb += conn.pad.o.eq(Mux(bd2io, io_bd[idx], conn.core.o))
587 idx += 1
588 elif conn._iotype == IOType.TriOut:
589 m.d.comb += [
590 conn.pad.o.eq(Mux(bd2io, io_bd[idx], conn.core.o)),
591 conn.pad.oe.eq(Mux(bd2io, io_bd[idx+1], conn.core.oe)),
592 ]
593 idx += 2
594 elif conn._iotype == IOType.InTriOut:
595 m.d.comb += [
596 conn.core.i.eq(Mux(bd2core, io_bd[idx], conn.pad.i)),
597 conn.pad.o.eq(Mux(bd2io, io_bd[idx+1], conn.core.o)),
598 conn.pad.oe.eq(Mux(bd2io, io_bd[idx+2], conn.core.oe)),
599 ]
600 idx += 3
601 else:
602 raise("Internal error")
603 assert idx == length, "Internal error"
604
605 return io_sr[-1]
606
607 def add_shiftreg(self, *, ircode, length, domain="sync", name=None,
608 src_loc_at=0):
609 """Add a shift register to the JTAG interface
610
611 Parameters:
612 - ircode: code(s) for the IR; int or sequence of ints. In the latter
613 case this shiftreg is shared between different IR codes.
614 - length: the length of the shift register
615 - domain: the domain on which the signal will be used"""
616
617 try:
618 ir_it = iter(ircode)
619 ircodes = ircode
620 except TypeError:
621 ir_it = ircodes = (ircode,)
622 for _ircode in ir_it:
623 if not isinstance(_ircode, int) or _ircode <= 0:
624 raise ValueError("IR code '{}' is not an int "
625 "greater than 0".format(_ircode))
626 if _ircode in self._ircodes:
627 raise ValueError("IR code '{}' already taken".format(_ircode))
628
629 self._ircodes.extend(ircodes)
630
631 if name is None:
632 name = "sr{}".format(len(self._srs))
633 sr = ShiftReg(sr_length=length, cmds=len(ircodes), name=name,
634 src_loc_at=src_loc_at+1)
635 self._srs.append((ircodes, domain, sr))
636
637 return sr
638
639 def _elaborate_shiftregs(self, m, capture, shift, update, ir, tdo_jtag):
640 # tdos is tuple of (tdo, tdo_en) for each shiftreg
641 tdos = []
642 for ircodes, domain, sr in self._srs:
643 reg = Signal(len(sr.o), name=sr.name+"_reg")
644 m.d.comb += sr.o.eq(reg)
645
646 isir = Signal(len(ircodes), name=sr.name+"_isir")
647 sr_capture = Signal(name=sr.name+"_capture")
648 sr_shift = Signal(name=sr.name+"_shift")
649 sr_update = Signal(name=sr.name+"_update")
650 m.d.comb += [
651 isir.eq(Cat(ir == ircode for ircode in ircodes)),
652 sr_capture.eq((isir != 0) & capture),
653 sr_shift.eq((isir != 0) & shift),
654 sr_update.eq((isir != 0) & update),
655 ]
656
657 # update signal is on the JTAG clockdomain, sr.oe is on `domain`
658 # clockdomain latch update in `domain` clockdomain and see when
659 # it has falling edge.
660 # At that edge put isir in sr.oe for one `domain` clockdomain
661 # Note: could use FFSynchroniser instead
662 update_core = Signal(name=sr.name+"_update_core")
663 update_core_prev = Signal(name=sr.name+"_update_core_prev")
664 m.d[domain] += [
665 update_core.eq(sr_update), # This is CDC from JTAG domain
666 # to given domain
667 update_core_prev.eq(update_core)
668 ]
669 with m.If(update_core_prev & ~update_core):
670 # Falling edge of update
671 m.d[domain] += sr.oe.eq(isir)
672 with m.Else():
673 m.d[domain] += sr.oe.eq(0)
674
675 with m.If(sr_shift):
676 m.d.posjtag += reg.eq(Cat(reg[1:], self.bus.tdi))
677 with m.If(sr_capture):
678 # could also use FFSynchroniser here too
679 m.d.posjtag += reg.eq(sr.i)
680
681 # tdo = reg[0], tdo_en = shift
682 tdos.append((reg[0], sr_shift))
683
684
685 # Assign the right tdo to the bus tdo
686 for i, (tdo, tdo_en) in enumerate(tdos):
687 if i == 0:
688 with m.If(tdo_en):
689 m.d.comb += self.bus.tdo.eq(tdo)
690 else:
691 with m.Elif(tdo_en):
692 m.d.comb += self.bus.tdo.eq(tdo)
693
694 if len(tdos) > 0:
695 with m.Else():
696 m.d.comb += self.bus.tdo.eq(tdo_jtag)
697 else:
698 # Always connect tdo_jtag to
699 m.d.comb += self.bus.tdo.eq(tdo_jtag)
700
701
702 def add_wishbone(self, *, ircodes, address_width, data_width,
703 granularity=None, domain="sync", features=None,
704 name=None, src_loc_at=0):
705 """Add a wishbone interface
706
707 In order to allow high JTAG clock speed, data will be cached.
708 This means that if data is output the value of the next address
709 will be read automatically.
710
711 Parameters:
712 -----------
713 ircodes: sequence of three integer for the JTAG IR codes;
714 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
715 has a shift register of length 'address_width', the two other codes
716 share a shift register of length data_width.
717 address_width: width of the address
718 data_width: width of the data
719 features: features required. defaults to stall, lock, err, rty
720
721 Returns:
722 wb: nmigen_soc.wishbone.bus.Interface
723 The Wishbone interface, is pipelined and has stall field.
724 """
725 if len(ircodes) != 3:
726 raise ValueError("3 IR Codes have to be provided")
727
728 if features is None:
729 features={"stall", "lock", "err", "rty"}
730 if name is None:
731 name = "wb" + str(len(self._wbs))
732 sr_addr = self.add_shiftreg(
733 ircode=ircodes[0], length=address_width, domain=domain,
734 name=name+"_addrsr"
735 )
736 sr_data = self.add_shiftreg(
737 ircode=ircodes[1:], length=data_width, domain=domain,
738 name=name+"_datasr"
739 )
740
741 wb = WishboneInterface(data_width=data_width, addr_width=address_width,
742 granularity=granularity, features=features,
743 name=name, src_loc_at=src_loc_at+1)
744
745 self._wbs.append((sr_addr, sr_data, wb, domain))
746
747 return wb
748
749 def _elaborate_wishbones(self, m):
750 for sr_addr, sr_data, wb, domain in self._wbs:
751 m.d.comb += sr_addr.i.eq(wb.adr)
752
753 if hasattr(wb, "sel"):
754 # Always selected
755 m.d.comb += [s.eq(1) for s in wb.sel]
756
757 with m.FSM(domain=domain) as fsm:
758 with m.State("IDLE"):
759 with m.If(sr_addr.oe): # WBADDR code
760 m.d[domain] += wb.adr.eq(sr_addr.o)
761 m.next = "READ"
762 with m.Elif(sr_data.oe[0]): # WBREAD code
763 # If data is
764 m.d[domain] += wb.adr.eq(wb.adr + 1)
765 m.next = "READ"
766 with m.Elif(sr_data.oe[1]): # WBWRITE code
767 m.d[domain] += wb.dat_w.eq(sr_data.o)
768 m.next = "WRITEREAD"
769 with m.State("READ"):
770 if not hasattr(wb, "stall"):
771 m.next = "READACK"
772 else:
773 with m.If(~wb.stall):
774 m.next = "READACK"
775 with m.State("READACK"):
776 with m.If(wb.ack):
777 # Store read data in sr_data.i and keep it there
778 # til next read. could use FFSynchroniser (see above)
779 m.d[domain] += sr_data.i.eq(wb.dat_r)
780 m.next = "IDLE"
781 with m.State("WRITEREAD"):
782 if not hasattr(wb, "stall"):
783 m.next = "WRITEREADACK"
784 else:
785 with m.If(~wb.stall):
786 m.next = "WRITEREADACK"
787 with m.State("WRITEREADACK"):
788 with m.If(wb.ack):
789 m.d[domain] += wb.adr.eq(wb.adr + 1)
790 m.next = "READ"
791
792 if hasattr(wb, "stall"):
793 m.d.comb += wb.stb.eq(fsm.ongoing("READ") |
794 fsm.ongoing("WRITEREAD"))
795 m.d.comb += wb.we.eq(fsm.ongoing("WRITEREAD"))
796 else:
797 # non-stall is single-cycle (litex), must assert stb
798 # until ack is sent
799 m.d.comb += wb.stb.eq(fsm.ongoing("READ") |
800 fsm.ongoing("WRITEREAD") |
801 fsm.ongoing("READACK") |
802 fsm.ongoing("WRITEREADACK"))
803 m.d.comb += wb.we.eq(fsm.ongoing("WRITEREAD") |
804 fsm.ongoing("WRITEREADACK"))
805 m.d.comb += wb.cyc.eq(~fsm.ongoing("IDLE"))