fix wishbone optional stall
[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
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 """TAP subblock representing the interface for an JTAG IO cell.
162 It contains signal to connect to the core and to the pad
163
164 This object is normally only allocated and returned from ``TAP.add_io``
165 It is a Record subclass.
166
167 Attributes
168 ----------
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
173 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
181 IOType.InTriOut.
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.
185 """
186 @staticmethod
187 def layout(iotype):
188 sigs = []
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))
195
196 return Layout((("core", sigs), ("pad", sigs)))
197
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)
201
202 self._iotype = iotype
203
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,
208 name):
209 self.name = name
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)
218
219 self.tdo = Signal(name=name+"_tdo")
220
221 self._tdi = tdi
222 self._capture = capture
223 self._shift = shift
224 self._update = update
225 self._bypass = bypass
226
227 def elaborate(self, platform):
228 m = Module()
229
230 sr = Signal(32, reset_less=True, name=self.name+"_sr")
231
232 # Local signals for the module
233 _tdi = Signal()
234 _capture = Signal()
235 _shift = Signal()
236 _update = Signal()
237 _bypass = Signal()
238
239 m.d.comb += [
240 _tdi.eq(self._tdi),
241 _capture.eq(self._capture),
242 _shift.eq(self._shift),
243 _update.eq(self._update),
244 _bypass.eq(self._bypass),
245 self.tdo.eq(sr[0]),
246 ]
247
248 with m.If(_capture):
249 m.d.posjtag += sr.eq(self._id)
250 with m.Elif(_shift):
251 with m.If(_bypass):
252 m.d.posjtag += sr[0].eq(_tdi)
253 with m.Else():
254 m.d.posjtag += sr.eq(Cat(sr[1:], _tdi))
255
256 return m
257
258
259 class ShiftReg(Record):
260 """Object with interface for extra shift registers on a TAP.
261
262 Parameters
263 ----------
264 sr_length : int
265 cmds : int, default=1
266 The number of corresponding JTAG instructions
267
268 This object is normally only allocated and returned from ``TAP.add_shiftreg``
269 It is a Record subclass.
270
271 Attributes
272 ----------
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
285 JTAG interface.
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.
292 """
293 def __init__(self, *, sr_length, cmds=1, name=None, src_loc_at=0):
294 layout = [
295 ("i", sr_length, Direction.FANIN),
296 ("ie", cmds, Direction.FANOUT),
297 ("o", sr_length, Direction.FANOUT),
298 ("oe", cmds, Direction.FANOUT),
299 ]
300 super().__init__(layout, name=name, src_loc_at=src_loc_at+1)
301
302
303 class TAP(Elaboratable):
304 #TODO: Document TAP
305 def __init__(self, *, with_reset=False, ir_width=None,
306 manufacturer_id=Const(0b10001111111, 11),
307 part_number=Const(1, 16),
308 version=Const(0, 4),
309 name=None, src_loc_at=0):
310 assert((ir_width is None) or (isinstance(ir_width, int) and
311 ir_width >= 2))
312 assert(len(version) == 4)
313
314 if name is None:
315 name = get_var_name(depth=src_loc_at+2, default="TAP")
316 self.name = name
317 self.bus = Interface(with_reset=with_reset, name=self.name+"_bus",
318 src_loc_at=src_loc_at+1)
319
320 ##
321
322 self._ir_width = ir_width
323 self._manufacturer_id = manufacturer_id
324 self._part_number = part_number
325 self._version = version
326
327 self._ircodes = [0, 1, 2] # Already taken codes, all ones added at end
328
329 self._ios = []
330 self._srs = []
331 self._wbs = []
332
333 def elaborate(self, platform):
334 m = Module()
335
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
343
344 # TODO: Make commands numbers configurable
345 cmd_extest = 0
346 cmd_intest = 0
347 cmd_idcode = 1
348 cmd_sample = 2
349 cmd_preload = 2
350 cmd_bypass = 2**ir_width - 1 # All ones
351
352 m.submodules._fsm = fsm = _FSM(bus=self.bus)
353 m.domains.posjtag = fsm.posjtag
354 m.domains.negjtag = fsm.negjtag
355
356 # IR block
357 select_ir = fsm.isir
358 m.submodules._irblock = irblock = _IRBlock(
359 ir_width=ir_width, cmd_idcode=cmd_idcode, tdi=self.bus.tdi,
360 capture=(fsm.isir & fsm.capture),
361 shift=(fsm.isir & fsm.shift),
362 update=(fsm.isir & fsm.update),
363 name=self.name+"_ir",
364 )
365 ir = irblock.ir
366
367 # ID block
368 select_id = fsm.isdr & ((ir == cmd_idcode) | (ir == cmd_bypass))
369 m.submodules._idblock = idblock = _IDBypassBlock(
370 manufacturer_id=self._manufacturer_id,
371 part_number=self._part_number,
372 version=self._version, tdi=self.bus.tdi,
373 capture=(select_id & fsm.capture),
374 shift=(select_id & fsm.shift),
375 update=(select_id & fsm.update),
376 bypass=(ir == cmd_bypass),
377 name=self.name+"_id",
378 )
379
380 # IO (Boundary scan) block
381 io_capture = Signal()
382 io_shift = Signal()
383 io_update = Signal()
384 io_bd2io = Signal()
385 io_bd2core = Signal()
386 sample = (ir == cmd_extest) | (ir == cmd_sample)
387 preload = (ir == cmd_preload)
388 select_io = fsm.isdr & (sample | preload)
389 m.d.comb += [
390 io_capture.eq(sample & fsm.capture), # Don't capture if not sample
391 # (like for PRELOAD)
392 io_shift.eq(select_io & fsm.shift),
393 io_update.eq(select_io & fsm.update),
394 io_bd2io.eq(ir == cmd_extest),
395 io_bd2core.eq(ir == cmd_intest),
396 ]
397 io_tdo = self._elaborate_ios(
398 m=m,
399 capture=io_capture, shift=io_shift, update=io_update,
400 bd2io=io_bd2io, bd2core=io_bd2core,
401 )
402
403 # chain tdo: select as appropriate, to go into into shiftregs
404 tdo = Signal(name=self.name+"_tdo")
405 with m.If(select_ir):
406 m.d.comb += tdo.eq(irblock.tdo)
407 with m.Elif(select_id):
408 m.d.comb += tdo.eq(idblock.tdo)
409 with m.Elif(select_io):
410 m.d.comb += tdo.eq(io_tdo)
411
412 # shiftregs block
413 self._elaborate_shiftregs(
414 m, capture=fsm.capture, shift=fsm.shift, update=fsm.update,
415 ir=irblock.ir, tdo_jtag=tdo
416 )
417
418 # wishbone
419 self._elaborate_wishbones(m)
420
421 return m
422
423
424 def add_io(self, *, iotype, name=None, src_loc_at=0):
425 """Add a io cell to the boundary scan chain
426
427 Parameters:
428 - iotype: :class:`IOType` enum.
429
430 Returns:
431 - :class:`IOConn`
432 """
433 if name is None:
434 name = "ioconn" + str(len(self._ios))
435
436 ioconn = IOConn(iotype=iotype, name=name, src_loc_at=src_loc_at+1)
437 self._ios.append(ioconn)
438 return ioconn
439
440 def _elaborate_ios(self, *, m, capture, shift, update, bd2io, bd2core):
441 connlength = {
442 IOType.In: 1,
443 IOType.Out: 1,
444 IOType.TriOut: 2,
445 IOType.InTriOut: 3,
446 }
447 length = sum(connlength[conn._iotype] for conn in self._ios)
448 if length == 0:
449 return self.bus.tdi
450
451 io_sr = Signal(length)
452 io_bd = Signal(length)
453
454 with m.If(capture):
455 idx = 0
456 for conn in self._ios:
457 if conn._iotype == IOType.In:
458 m.d.posjtag += io_sr[idx].eq(conn.pad.i)
459 idx += 1
460 elif conn._iotype == IOType.Out:
461 m.d.posjtag += io_sr[idx].eq(conn.core.o)
462 idx += 1
463 elif conn._iotype == IOType.TriOut:
464 m.d.posjtag += [
465 io_sr[idx].eq(conn.core.o),
466 io_sr[idx+1].eq(conn.core.oe),
467 ]
468 idx += 2
469 elif conn._iotype == IOType.InTriOut:
470 m.d.posjtag += [
471 io_sr[idx].eq(conn.pad.i),
472 io_sr[idx+1].eq(conn.core.o),
473 io_sr[idx+2].eq(conn.core.oe),
474 ]
475 idx += 3
476 else:
477 raise("Internal error")
478 assert idx == length, "Internal error"
479 with m.Elif(shift):
480 m.d.posjtag += io_sr.eq(Cat(self.bus.tdi, io_sr[:-1]))
481 with m.Elif(update):
482 m.d.negjtag += io_bd.eq(io_sr)
483
484 idx = 0
485 for conn in self._ios:
486 if conn._iotype == IOType.In:
487 m.d.comb += conn.core.i.eq(Mux(bd2core, io_bd[idx], conn.pad.i))
488 idx += 1
489 elif conn._iotype == IOType.Out:
490 m.d.comb += conn.pad.o.eq(Mux(bd2io, io_bd[idx], conn.core.o))
491 idx += 1
492 elif conn._iotype == IOType.TriOut:
493 m.d.comb += [
494 conn.pad.o.eq(Mux(bd2io, io_bd[idx], conn.core.o)),
495 conn.pad.oe.eq(Mux(bd2io, io_bd[idx+1], conn.core.oe)),
496 ]
497 idx += 2
498 elif conn._iotype == IOType.InTriOut:
499 m.d.comb += [
500 conn.core.i.eq(Mux(bd2core, io_bd[idx], conn.pad.i)),
501 conn.pad.o.eq(Mux(bd2io, io_bd[idx+1], conn.core.o)),
502 conn.pad.oe.eq(Mux(bd2io, io_bd[idx+2], conn.core.oe)),
503 ]
504 idx += 3
505 else:
506 raise("Internal error")
507 assert idx == length, "Internal error"
508
509 return io_sr[-1]
510
511 def add_shiftreg(self, *, ircode, length, domain="sync", name=None,
512 src_loc_at=0):
513 """Add a shift register to the JTAG interface
514
515 Parameters:
516 - ircode: code(s) for the IR; int or sequence of ints. In the latter
517 case this shiftreg is shared between different IR codes.
518 - length: the length of the shift register
519 - domain: the domain on which the signal will be used"""
520
521 try:
522 ir_it = iter(ircode)
523 ircodes = ircode
524 except TypeError:
525 ir_it = ircodes = (ircode,)
526 for _ircode in ir_it:
527 if not isinstance(_ircode, int) or _ircode <= 0:
528 raise ValueError("IR code '{}' is not an int "
529 "greater than 0".format(_ircode))
530 if _ircode in self._ircodes:
531 raise ValueError("IR code '{}' already taken".format(_ircode))
532
533 self._ircodes.extend(ircodes)
534
535 if name is None:
536 name = "sr{}".format(len(self._srs))
537 sr = ShiftReg(sr_length=length, cmds=len(ircodes), name=name,
538 src_loc_at=src_loc_at+1)
539 self._srs.append((ircodes, domain, sr))
540
541 return sr
542
543 def _elaborate_shiftregs(self, m, capture, shift, update, ir, tdo_jtag):
544 # tdos is tuple of (tdo, tdo_en) for each shiftreg
545 tdos = []
546 for ircodes, domain, sr in self._srs:
547 reg = Signal(len(sr.o), name=sr.name+"_reg")
548 m.d.comb += sr.o.eq(reg)
549
550 isir = Signal(len(ircodes), name=sr.name+"_isir")
551 sr_capture = Signal(name=sr.name+"_capture")
552 sr_shift = Signal(name=sr.name+"_shift")
553 sr_update = Signal(name=sr.name+"_update")
554 m.d.comb += [
555 isir.eq(Cat(ir == ircode for ircode in ircodes)),
556 sr_capture.eq((isir != 0) & capture),
557 sr_shift.eq((isir != 0) & shift),
558 sr_update.eq((isir != 0) & update),
559 ]
560
561 # update signal is on the JTAG clockdomain, sr.oe is on `domain`
562 # clockdomain latch update in `domain` clockdomain and see when
563 # it has falling edge.
564 # At that edge put isir in sr.oe for one `domain` clockdomain
565 update_core = Signal(name=sr.name+"_update_core")
566 update_core_prev = Signal(name=sr.name+"_update_core_prev")
567 m.d[domain] += [
568 update_core.eq(sr_update), # This is CDC from JTAG domain
569 # to given domain
570 update_core_prev.eq(update_core)
571 ]
572 with m.If(update_core_prev & ~update_core):
573 # Falling edge of update
574 m.d[domain] += sr.oe.eq(isir)
575 with m.Else():
576 m.d[domain] += sr.oe.eq(0)
577
578 with m.If(sr_shift):
579 m.d.posjtag += reg.eq(Cat(reg[1:], self.bus.tdi))
580 with m.If(sr_capture):
581 m.d.posjtag += reg.eq(sr.i)
582
583 # tdo = reg[0], tdo_en = shift
584 tdos.append((reg[0], sr_shift))
585
586
587 # Assign the right tdo to the bus tdo
588 for i, (tdo, tdo_en) in enumerate(tdos):
589 if i == 0:
590 with m.If(tdo_en):
591 m.d.comb += self.bus.tdo.eq(tdo)
592 else:
593 with m.Elif(tdo_en):
594 m.d.comb += self.bus.tdo.eq(tdo)
595
596 if len(tdos) > 0:
597 with m.Else():
598 m.d.comb += self.bus.tdo.eq(tdo_jtag)
599 else:
600 # Always connect tdo_jtag to
601 m.d.comb += self.bus.tdo.eq(tdo_jtag)
602
603
604 def add_wishbone(self, *, ircodes, address_width, data_width,
605 granularity=None, domain="sync", features=None,
606 name=None, src_loc_at=0):
607 """Add a wishbone interface
608
609 In order to allow high JTAG clock speed, data will be cached.
610 This means that if data is output the value of the next address
611 will be read automatically.
612
613 Parameters:
614 -----------
615 ircodes: sequence of three integer for the JTAG IR codes;
616 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
617 has a shift register of length 'address_width', the two other codes
618 share a shift register of length data_width.
619 address_width: width of the address
620 data_width: width of the data
621 features: features required. defaults to stall, lock, err, rty
622
623 Returns:
624 wb: nmigen_soc.wishbone.bus.Interface
625 The Wishbone interface, is pipelined and has stall field.
626 """
627 if len(ircodes) != 3:
628 raise ValueError("3 IR Codes have to be provided")
629
630 if features is None:
631 features={"stall", "lock", "err", "rty"}
632 if name is None:
633 name = "wb" + str(len(self._wbs))
634 sr_addr = self.add_shiftreg(
635 ircode=ircodes[0], length=address_width, domain=domain,
636 name=name+"_addrsr"
637 )
638 sr_data = self.add_shiftreg(
639 ircode=ircodes[1:], length=data_width, domain=domain,
640 name=name+"_datasr"
641 )
642
643 wb = WishboneInterface(data_width=data_width, addr_width=address_width,
644 granularity=granularity, features=features,
645 name=name, src_loc_at=src_loc_at+1)
646
647 self._wbs.append((sr_addr, sr_data, wb, domain))
648
649 return wb
650
651 def _elaborate_wishbones(self, m):
652 for sr_addr, sr_data, wb, domain in self._wbs:
653 m.d.comb += sr_addr.i.eq(wb.adr)
654
655 if hasattr(wb, "sel"):
656 # Always selected
657 m.d.comb += [s.eq(1) for s in wb.sel]
658
659 with m.FSM(domain=domain) as fsm:
660 with m.State("IDLE"):
661 with m.If(sr_addr.oe): # WBADDR code
662 m.d[domain] += wb.adr.eq(sr_addr.o)
663 m.next = "READ"
664 with m.Elif(sr_data.oe[0]): # WBREAD code
665 # If data is
666 m.d[domain] += wb.adr.eq(wb.adr + 1)
667 m.next = "READ"
668 with m.Elif(sr_data.oe[1]): # WBWRITE code
669 m.d[domain] += wb.dat_w.eq(sr_data.o)
670 m.next = "WRITEREAD"
671 with m.State("READ"):
672 if not hasattr(wb, "stall"):
673 m.next = "READACK"
674 else:
675 with m.If(~wb.stall):
676 m.next = "READACK"
677 with m.State("READACK"):
678 with m.If(wb.ack):
679 # Store read data in sr_data.i
680 # and keep it there til next read
681 m.d[domain] += sr_data.i.eq(wb.dat_r)
682 m.next = "IDLE"
683 with m.State("WRITEREAD"):
684 if not hasattr(wb, "stall"):
685 m.next = "WRITEREADACK"
686 else:
687 with m.If(~wb.stall):
688 m.next = "WRITEREADACK"
689 with m.State("WRITEREADACK"):
690 with m.If(wb.ack):
691 m.d[domain] += wb.adr.eq(wb.adr + 1)
692 m.next = "READ"
693
694 m.d.comb += [
695 wb.cyc.eq(~fsm.ongoing("IDLE")),
696 wb.stb.eq(fsm.ongoing("READ") | fsm.ongoing("WRITEREAD")),
697 wb.we.eq(fsm.ongoing("WRITEREAD")),
698 ]