e3de5c30068b4b064ba25387ca5837b47c79f0ae
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
, DMIInterface
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
):
168 """TAP subblock representing the interface for an JTAG IO cell.
169 It contains signal to connect to the core and to the pad
171 This object is normally only allocated and returned from ``TAP.add_io``
172 It is a Record subclass.
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
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
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.
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))
203 return Layout((("core", sigs
), ("pad", sigs
)))
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)
209 self
._iotype
= iotype
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
,
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
)
226 self
.tdo
= Signal(name
=name
+"_tdo")
229 self
._capture
= capture
231 self
._update
= update
232 self
._bypass
= bypass
234 def elaborate(self
, platform
):
237 sr
= Signal(32, reset_less
=True, name
=self
.name
+"_sr")
239 # Local signals for the module
248 _capture
.eq(self
._capture
),
249 _shift
.eq(self
._shift
),
250 _update
.eq(self
._update
),
251 _bypass
.eq(self
._bypass
),
256 m
.d
.posjtag
+= sr
.eq(self
._id
)
259 m
.d
.posjtag
+= sr
[0].eq(_tdi
)
261 m
.d
.posjtag
+= sr
.eq(Cat(sr
[1:], _tdi
))
266 class ShiftReg(Record
):
267 """Object with interface for extra shift registers on a TAP.
272 cmds : int, default=1
273 The number of corresponding JTAG instructions
275 This object is normally only allocated and returned from ``TAP.add_shiftreg``
276 It is a Record subclass.
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
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.
300 def __init__(self
, *, sr_length
, cmds
=1, name
=None, src_loc_at
=0):
302 ("i", sr_length
, Direction
.FANIN
),
303 ("ie", cmds
, Direction
.FANOUT
),
304 ("o", sr_length
, Direction
.FANOUT
),
305 ("oe", cmds
, Direction
.FANOUT
),
307 super().__init
__(layout
, name
=name
, src_loc_at
=src_loc_at
+1)
309 class TAP(Elaboratable
):
311 def __init__(self
, *, with_reset
=False, ir_width
=None,
312 manufacturer_id
=Const(0b10001111111, 11),
313 part_number
=Const(1, 16),
315 name
=None, src_loc_at
=0):
316 assert((ir_width
is None) or (isinstance(ir_width
, int) and
318 assert(len(version
) == 4)
321 name
= get_var_name(depth
=src_loc_at
+2, default
="TAP")
323 self
.bus
= Interface(with_reset
=with_reset
, name
=self
.name
+"_bus",
324 src_loc_at
=src_loc_at
+1)
328 self
._ir
_width
= ir_width
329 self
._manufacturer
_id
= manufacturer_id
330 self
._part
_number
= part_number
331 self
._version
= version
333 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at end
340 def elaborate(self
, platform
):
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
351 # TODO: Make commands numbers configurable
357 cmd_bypass
= 2**ir_width
- 1 # All ones
359 m
.submodules
._fsm
= fsm
= _FSM(bus
=self
.bus
)
360 m
.domains
.posjtag
= fsm
.posjtag
361 m
.domains
.negjtag
= fsm
.negjtag
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",
375 select_id
= fsm
.isdr
& ((ir
== cmd_idcode
) |
(ir
== cmd_bypass
))
376 m
.submodules
._idblock
= idblock
= _IDBypassBlock(
377 manufacturer_id
=self
._manufacturer
_id
,
378 part_number
=self
._part
_number
,
379 version
=self
._version
, tdi
=self
.bus
.tdi
,
380 capture
=(select_id
& fsm
.capture
),
381 shift
=(select_id
& fsm
.shift
),
382 update
=(select_id
& fsm
.update
),
383 bypass
=(ir
== cmd_bypass
),
384 name
=self
.name
+"_id",
387 # IO (Boundary scan) block
388 io_capture
= Signal()
392 io_bd2core
= Signal()
393 sample
= (ir
== cmd_extest
) |
(ir
== cmd_sample
)
394 preload
= (ir
== cmd_preload
)
395 select_io
= fsm
.isdr
& (sample | preload
)
397 io_capture
.eq(sample
& fsm
.capture
), # Don't capture if not sample
399 io_shift
.eq(select_io
& fsm
.shift
),
400 io_update
.eq(select_io
& fsm
.update
),
401 io_bd2io
.eq(ir
== cmd_extest
),
402 io_bd2core
.eq(ir
== cmd_intest
),
404 io_tdo
= self
._elaborate
_ios
(
406 capture
=io_capture
, shift
=io_shift
, update
=io_update
,
407 bd2io
=io_bd2io
, bd2core
=io_bd2core
,
410 # chain tdo: select as appropriate, to go into into shiftregs
411 tdo
= Signal(name
=self
.name
+"_tdo")
412 with m
.If(select_ir
):
413 m
.d
.comb
+= tdo
.eq(irblock
.tdo
)
414 with m
.Elif(select_id
):
415 m
.d
.comb
+= tdo
.eq(idblock
.tdo
)
416 with m
.Elif(select_io
):
417 m
.d
.comb
+= tdo
.eq(io_tdo
)
420 self
._elaborate
_shiftregs
(
421 m
, capture
=fsm
.capture
, shift
=fsm
.shift
, update
=fsm
.update
,
422 ir
=irblock
.ir
, tdo_jtag
=tdo
426 self
._elaborate
_wishbones
(m
)
428 # DMI (Debug Memory Interface)
429 self
._elaborate
_dmis
(m
)
433 def add_dmi(self
, *, ircodes
, address_width
=8, data_width
=64,
434 domain
="sync", name
=None):
435 """Add a DMI interface
437 * writing to DMIADDR will automatically trigger a DMI READ.
438 the DMI address does not alter (so writes can be done at that addr)
439 * reading from DMIREAD triggers a DMI READ at the current DMI addr
440 the address is automatically incremented by 1 after.
441 * writing to DMIWRITE triggers a DMI WRITE at the current DMI addr
442 the address is automatically incremented by 1 after.
446 ircodes: sequence of three integer for the JTAG IR codes;
447 they represent resp. DMIADDR, DMIREAD and DMIWRITE.
448 First code has a shift register of length 'address_width',
449 the two other codes share a shift register of length
452 address_width: width of the address
453 data_width: width of the data
456 dmi: soc.debug.dmi.DMIInterface
459 if len(ircodes
) != 3:
460 raise ValueError("3 IR Codes have to be provided")
463 name
= "dmi" + str(len(self
._dmis
))
465 # add 2 shift registers: one for addr, one for data.
466 sr_addr
= self
.add_shiftreg(ircode
=ircodes
[0], length
=address_width
,
467 domain
=domain
, name
=name
+"_addrsr")
468 sr_data
= self
.add_shiftreg(ircode
=ircodes
[1:], length
=data_width
,
469 domain
=domain
, name
=name
+"_datasr")
471 dmi
= DMIInterface(name
=name
)
472 self
._dmis
.append((sr_addr
, sr_data
, dmi
, domain
))
476 def _elaborate_dmis(self
, m
):
477 for sr_addr
, sr_data
, dmi
, domain
in self
._dmis
:
479 m
.d
.comb
+= sr_addr
.i
.eq(dmi
.addr_i
)
481 with m
.FSM(domain
=domain
) as ds
:
483 # detect mode based on whether jtag addr or data read/written
484 with m
.State("IDLE"):
485 with m
.If(sr_addr
.oe
): # DMIADDR code
486 cd
+= dmi
.addr_i
.eq(sr_addr
.o
)
488 with m
.Elif(sr_data
.oe
[0]): # DMIREAD code
490 cd
+= dmi
.addr_i
.eq(dmi
.addr_i
+ 1)
492 with m
.Elif(sr_data
.oe
[1]): # DMIWRITE code
493 cd
+= dmi
.din
.eq(sr_data
.o
)
496 # req_i raises for 1 clock
497 with m
.State("READ"):
501 with m
.State("READACK"):
502 with m
.If(dmi
.ack_o
):
503 # Store read data in sr_data.i hold till next read
504 cd
+= sr_data
.i
.eq(dmi
.dout
)
507 # req_i raises for 1 clock
508 with m
.State("WRRD"):
512 with m
.State("WRRDACK"):
513 with m
.If(dmi
.ack_o
):
514 cd
+= dmi
.addr_i
.eq(dmi
.addr_i
+ 1)
515 m
.next
= "READ" # for readwrite
517 # set DMI req and write-enable based on ongoing FSM states
519 dmi
.req_i
.eq(ds
.ongoing("READ") | ds
.ongoing("WRRD")),
520 dmi
.we_i
.eq(ds
.ongoing("WRRD")),
523 def add_io(self
, *, iotype
, name
=None, src_loc_at
=0):
524 """Add a io cell to the boundary scan chain
527 - iotype: :class:`IOType` enum.
533 name
= "ioconn" + str(len(self
._ios
))
535 ioconn
= IOConn(iotype
=iotype
, name
=name
, src_loc_at
=src_loc_at
+1)
536 self
._ios
.append(ioconn
)
539 def _elaborate_ios(self
, *, m
, capture
, shift
, update
, bd2io
, bd2core
):
540 length
= sum(IOConn
.lengths
[conn
._iotype
] for conn
in self
._ios
)
544 io_sr
= Signal(length
)
545 io_bd
= Signal(length
)
547 # Boundary scan "capture" mode. makes I/O status available via SR
551 for conn
in self
._ios
:
552 # in appropriate sequence: In/TriOut has pad.i,
553 # Out.InTriOut has everything, Out and TriOut have core.o
554 if conn
._iotype
in [IOType
.In
, IOType
.InTriOut
]:
555 iol
.append(conn
.pad
.i
)
556 if conn
._iotype
in [IOType
.Out
, IOType
.InTriOut
]:
557 iol
.append(conn
.core
.o
)
558 if conn
._iotype
in [IOType
.TriOut
, IOType
.InTriOut
]:
559 iol
.append(conn
.core
.oe
)
560 # length double-check
561 idx
+= IOConn
.lengths
[conn
._iotype
] # fails if wrong type
562 assert idx
== length
, "Internal error"
563 m
.d
.posjtag
+= io_sr
.eq(Cat(*iol
)) # assigns all io_sr in one hit
565 # "Shift" mode (sends out captured data on tdo, sets incoming from tdi)
567 m
.d
.posjtag
+= io_sr
.eq(Cat(self
.bus
.tdi
, io_sr
[:-1]))
571 m
.d
.negjtag
+= io_bd
.eq(io_sr
)
573 # sets up IO (pad<->core) or in testing mode depending on requested
574 # mode, via Muxes controlled by bd2core and bd2io
576 for conn
in self
._ios
:
577 if conn
._iotype
== IOType
.In
:
578 m
.d
.comb
+= conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
))
580 elif conn
._iotype
== IOType
.Out
:
581 m
.d
.comb
+= conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
))
583 elif conn
._iotype
== IOType
.TriOut
:
585 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
)),
586 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.oe
)),
589 elif conn
._iotype
== IOType
.InTriOut
:
591 conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
)),
592 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.o
)),
593 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+2], conn
.core
.oe
)),
597 raise("Internal error")
598 assert idx
== length
, "Internal error"
602 def add_shiftreg(self
, *, ircode
, length
, domain
="sync", name
=None,
604 """Add a shift register to the JTAG interface
607 - ircode: code(s) for the IR; int or sequence of ints. In the latter
608 case this shiftreg is shared between different IR codes.
609 - length: the length of the shift register
610 - domain: the domain on which the signal will be used"""
616 ir_it
= ircodes
= (ircode
,)
617 for _ircode
in ir_it
:
618 if not isinstance(_ircode
, int) or _ircode
<= 0:
619 raise ValueError("IR code '{}' is not an int "
620 "greater than 0".format(_ircode
))
621 if _ircode
in self
._ircodes
:
622 raise ValueError("IR code '{}' already taken".format(_ircode
))
624 self
._ircodes
.extend(ircodes
)
627 name
= "sr{}".format(len(self
._srs
))
628 sr
= ShiftReg(sr_length
=length
, cmds
=len(ircodes
), name
=name
,
629 src_loc_at
=src_loc_at
+1)
630 self
._srs
.append((ircodes
, domain
, sr
))
634 def _elaborate_shiftregs(self
, m
, capture
, shift
, update
, ir
, tdo_jtag
):
635 # tdos is tuple of (tdo, tdo_en) for each shiftreg
637 for ircodes
, domain
, sr
in self
._srs
:
638 reg
= Signal(len(sr
.o
), name
=sr
.name
+"_reg")
639 m
.d
.comb
+= sr
.o
.eq(reg
)
641 isir
= Signal(len(ircodes
), name
=sr
.name
+"_isir")
642 sr_capture
= Signal(name
=sr
.name
+"_capture")
643 sr_shift
= Signal(name
=sr
.name
+"_shift")
644 sr_update
= Signal(name
=sr
.name
+"_update")
646 isir
.eq(Cat(ir
== ircode
for ircode
in ircodes
)),
647 sr_capture
.eq((isir
!= 0) & capture
),
648 sr_shift
.eq((isir
!= 0) & shift
),
649 sr_update
.eq((isir
!= 0) & update
),
652 # update signal is on the JTAG clockdomain, sr.oe is on `domain`
653 # clockdomain latch update in `domain` clockdomain and see when
654 # it has falling edge.
655 # At that edge put isir in sr.oe for one `domain` clockdomain
656 update_core
= Signal(name
=sr
.name
+"_update_core")
657 update_core_prev
= Signal(name
=sr
.name
+"_update_core_prev")
659 update_core
.eq(sr_update
), # This is CDC from JTAG domain
661 update_core_prev
.eq(update_core
)
663 with m
.If(update_core_prev
& ~update_core
):
664 # Falling edge of update
665 m
.d
[domain
] += sr
.oe
.eq(isir
)
667 m
.d
[domain
] += sr
.oe
.eq(0)
670 m
.d
.posjtag
+= reg
.eq(Cat(reg
[1:], self
.bus
.tdi
))
671 with m
.If(sr_capture
):
672 m
.d
.posjtag
+= reg
.eq(sr
.i
)
674 # tdo = reg[0], tdo_en = shift
675 tdos
.append((reg
[0], sr_shift
))
678 # Assign the right tdo to the bus tdo
679 for i
, (tdo
, tdo_en
) in enumerate(tdos
):
682 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
685 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
689 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
691 # Always connect tdo_jtag to
692 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
695 def add_wishbone(self
, *, ircodes
, address_width
, data_width
,
696 granularity
=None, domain
="sync", features
=None,
697 name
=None, src_loc_at
=0):
698 """Add a wishbone interface
700 In order to allow high JTAG clock speed, data will be cached.
701 This means that if data is output the value of the next address
702 will be read automatically.
706 ircodes: sequence of three integer for the JTAG IR codes;
707 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
708 has a shift register of length 'address_width', the two other codes
709 share a shift register of length data_width.
710 address_width: width of the address
711 data_width: width of the data
712 features: features required. defaults to stall, lock, err, rty
715 wb: nmigen_soc.wishbone.bus.Interface
716 The Wishbone interface, is pipelined and has stall field.
718 if len(ircodes
) != 3:
719 raise ValueError("3 IR Codes have to be provided")
722 features
={"stall", "lock", "err", "rty"}
724 name
= "wb" + str(len(self
._wbs
))
725 sr_addr
= self
.add_shiftreg(
726 ircode
=ircodes
[0], length
=address_width
, domain
=domain
,
729 sr_data
= self
.add_shiftreg(
730 ircode
=ircodes
[1:], length
=data_width
, domain
=domain
,
734 wb
= WishboneInterface(data_width
=data_width
, addr_width
=address_width
,
735 granularity
=granularity
, features
=features
,
736 name
=name
, src_loc_at
=src_loc_at
+1)
738 self
._wbs
.append((sr_addr
, sr_data
, wb
, domain
))
742 def _elaborate_wishbones(self
, m
):
743 for sr_addr
, sr_data
, wb
, domain
in self
._wbs
:
744 m
.d
.comb
+= sr_addr
.i
.eq(wb
.adr
)
746 if hasattr(wb
, "sel"):
748 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
750 with m
.FSM(domain
=domain
) as fsm
:
751 with m
.State("IDLE"):
752 with m
.If(sr_addr
.oe
): # WBADDR code
753 m
.d
[domain
] += wb
.adr
.eq(sr_addr
.o
)
755 with m
.Elif(sr_data
.oe
[0]): # WBREAD code
757 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
759 with m
.Elif(sr_data
.oe
[1]): # WBWRITE code
760 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
762 with m
.State("READ"):
763 if not hasattr(wb
, "stall"):
766 with m
.If(~wb
.stall
):
768 with m
.State("READACK"):
770 # Store read data in sr_data.i
771 # and keep it there til next read
772 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
774 with m
.State("WRITEREAD"):
775 if not hasattr(wb
, "stall"):
776 m
.next
= "WRITEREADACK"
778 with m
.If(~wb
.stall
):
779 m
.next
= "WRITEREADACK"
780 with m
.State("WRITEREADACK"):
782 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
786 wb
.cyc
.eq(~fsm
.ongoing("IDLE")),
787 wb
.stb
.eq(fsm
.ongoing("READ") | fsm
.ongoing("WRITEREAD")),
788 wb
.we
.eq(fsm
.ongoing("WRITEREAD")),