725e7e6321df9c5b5f511c74519de746f790916d
5 from nmigen
.build
import *
6 from nmigen
.lib
.io
import *
7 from nmigen
.tracer
import get_var_name
9 from c4m_repo
.nmigen
.lib
import Wishbone
16 class ShiftReg(Elaboratable
):
17 def __init__(self
, ircodes
, length
, domain
):
18 # The sr record will be returned to user code
19 self
.sr
= Record([("i", length
), ("o", length
), ("oe", len(ircodes
)), ("ack", 1)])
20 # The next attributes are for JTAG class usage only
21 self
.ir
= None # made None as width is not known yet
24 self
.tdo_en
= Signal()
25 self
.capture
= Signal()
27 self
.update
= Signal()
28 self
.jtag_cd
= None # The JTAG clock domain
32 self
._ircodes
= ircodes
35 def elaborate(self
, platform
):
36 length
= len(self
.sr
.o
)
41 m
.domains
.jtag
= self
.jtag_cd
43 sr_jtag
= Signal(length
)
45 assert isinstance(self
.ir
, Signal
)
46 isir
= Signal(len(self
._ircodes
))
51 isir
.eq(Cat(self
.ir
== ircode
for ircode
in self
._ircodes
)),
52 capture
.eq((isir
!= 0) & self
.capture
),
53 shift
.eq((isir
!= 0) & self
.shift
),
54 update
.eq((isir
!= 0) & self
.update
),
57 # On update set o, oe and wait for ack
58 # update signal is on JTAG clockdomain, latch it
59 update_core
= Signal()
60 m
.d
[domain
] += update_core
.eq(update
) # This is CDC from JTAG domain to given domain
61 with m
.FSM(domain
=domain
):
63 m
.d
.comb
+= self
.sr
.oe
.eq(0)
64 with m
.If(update_core
):
65 # Latch sr_jtag cross domain but it should be stable due to latching of update_core
66 m
.d
[domain
] += self
.sr
.o
.eq(sr_jtag
)
67 # Wait one cycle to raise oe so sr.o has one more cycle to stabilize
69 with m
.State("WAIT4ACK"):
70 m
.d
.comb
+= self
.sr
.oe
.eq(isir
)
71 with m
.If(self
.sr
.ack
):
73 with m
.State("WAIT4END"):
74 m
.d
.comb
+= self
.sr
.oe
.eq(0)
75 with m
.If(~update_core
):
79 self
.tdo
.eq(sr_jtag
[0]),
80 self
.tdo_en
.eq(shift
),
84 m
.d
.jtag
+= sr_jtag
.eq(Cat(sr_jtag
[1:], self
.tdi
))
86 m
.d
.jtag
+= sr_jtag
.eq(self
.sr
.i
)
90 class JTAGWishbone(Elaboratable
):
91 def __init__(self
, sr_addr
, sr_data
, wb
, domain
):
92 self
._sr
_addr
= sr_addr
93 self
._sr
_data
= sr_data
100 def elaborate(self
, platform
):
101 sr_addr
= self
._sr
_addr
102 sr_data
= self
._sr
_data
104 domain
= self
._domain
109 if hasattr(wb
, "sel"):
111 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
115 sr_addr
.ack
.eq(sr_addr
.oe
),
116 sr_data
.ack
.eq(sr_data
.oe
!= 0),
119 with m
.FSM(domain
=domain
) as fsm
:
120 with m
.State("IDLE"):
126 with m
.If(sr_addr
.oe
): # WBADDR code
127 m
.d
[domain
] += wb
.addr
.eq(sr_addr
.o
)
129 with m
.If(sr_data
.oe
[0]): # WBREAD code
130 m
.d
[domain
] += wb
.addr
.eq(wb
.addr
+ 1)
132 with m
.If(sr_data
.oe
[1]): # WBWRITE code
133 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
135 with m
.State("READ"):
141 with m
.If(~wb
.stall
):
143 with m
.State("READACK"):
150 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
152 with m
.State("WRITEREAD"):
158 with m
.If(~wb
.stall
):
159 m
.next
= "WRITEREADACK"
160 with m
.State("WRITEREADACK"):
167 m
.d
[domain
] += wb
.addr
.eq(wb
.addr
+ 1)
173 class TAP(Elaboratable
):
176 def _add_files(platform
, prefix
):
177 d
= os
.path
.realpath("{dir}{sep}{par}{sep}{par}{sep}vhdl{sep}jtag".format(
178 dir=os
.path
.dirname(__file__
), sep
=os
.path
.sep
, par
=os
.path
.pardir
182 "c4m_jtag_idblock.vhdl",
183 "c4m_jtag_iocell.vhdl",
184 "c4m_jtag_ioblock.vhdl",
185 "c4m_jtag_irblock.vhdl",
186 "c4m_jtag_tap_fsm.vhdl",
187 "c4m_jtag_tap_controller.vhdl",
189 f
= open(d
+ fname
, "r")
190 platform
.add_file(prefix
+ fname
, f
)
194 def __init__(self
, io_count
, *, ir_width
=None, manufacturer_id
=Const(0b10001111111, 11),
195 part_number
=Const(1, 16), version
=Const(0, 4)
197 assert(isinstance(io_count
, int) and io_count
> 0)
198 assert((ir_width
is None) or (isinstance(ir_width
, int) and ir_width
>= 2))
199 assert(len(version
) == 4)
201 # TODO: Handle IOs with different directions
206 self
.core
= Array(Pin(1, "io") for _
in range(io_count
)) # Signals to use for core
207 self
.pad
= Array(Pin(1, "io") for _
in range(io_count
)) # Signals going to IO pads
209 self
.jtag_cd
= ClockDomain(name
="jtag", local
=True) # Own clock domain using TCK as clock signal
213 self
._io
_count
= io_count
214 self
._ir
_width
= ir_width
215 self
._manufacturer
_id
= manufacturer_id
216 self
._part
_number
= part_number
217 self
._version
= version
219 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at the end
224 def elaborate(self
, platform
):
225 TAP
._add
_files
(platform
, "jtag" + os
.path
.sep
)
236 ir_max
= max(self
._ircodes
) + 1 # One extra code needed with all ones
237 ir_width
= len("{:b}".format(ir_max
))
238 if self
._ir
_width
is not None:
239 assert self
._ir
_width
>= ir_width
, "Specified JTAG IR width not big enough for allocated shiift registers"
240 ir_width
= self
._ir
_width
241 ir
= Signal(ir_width
)
243 core_i
= Cat(pin
.i
for pin
in self
.core
)
244 core_o
= Cat(pin
.o
for pin
in self
.core
)
245 core_oe
= Cat(pin
.oe
for pin
in self
.core
)
246 pad_i
= Cat(pin
.i
for pin
in self
.pad
)
247 pad_o
= Cat(pin
.o
for pin
in self
.pad
)
248 pad_oe
= Cat(pin
.oe
for pin
in self
.pad
)
251 "p_IOS": self
._io
_count
,
252 "p_IR_WIDTH": ir_width
,
253 "p_MANUFACTURER": self
._manufacturer
_id
,
254 "p_PART_NUMBER": self
._part
_number
,
255 "p_VERSION": self
._version
,
260 "i_TRST_N": Const(1),
262 "o_DRCAPTURE": capture
,
264 "o_DRUPDATE": update
,
267 "i_CORE_OUT": core_o
,
268 "i_CORE_EN": core_oe
,
273 m
.submodules
.tap
= Instance("c4m_jtag_tap_controller", **params
)
276 self
.jtag_cd
.clk
.eq(self
.tck
),
277 self
.jtag_cd
.rst
.eq(reset
),
280 for i
, sr
in enumerate(self
._srs
):
281 m
.submodules
["sr{}".format(i
)] = sr
285 sr
.capture
.eq(capture
),
287 sr
.update
.eq(update
),
290 if len(self
._srs
) > 0:
295 with m
.If(sr
.tdo_en
):
296 m
.d
.comb
+= self
.tdo
.eq(sr
.tdo
)
298 with m
.Elif(sr
.tdo_en
):
299 m
.d
.comb
+= self
.tdo
.eq(sr
.tdo
)
301 m
.d
.comb
+= self
.tdo
.eq(tdo_jtag
)
303 m
.d
.comb
+= self
.tdo
.eq(tdo_jtag
)
305 for i
, wb
in enumerate(self
._wbs
):
306 m
.submodules
["wb{}".format(i
)] = wb
312 def add_shiftreg(self
, ircode
, length
, domain
="sync"):
313 """Add a shift register to the JTAG interface
316 - ircode: code(s) for the IR; int or sequence of ints. In the latter case this
317 shiftreg is shared between different IR codes.
318 - length: the length of the shift register
319 - domain: the domain on which the signal will be used"""
325 ir_it
= ircodes
= (ircode
,)
326 for _ircode
in ir_it
:
327 assert(isinstance(_ircode
, int) and _ircode
> 0 and _ircode
not in self
._ircodes
)
329 sr
= ShiftReg(ircodes
, length
, domain
)
330 sr
.jtag_cd
= self
.jtag_cd
331 self
._ircodes
.extend(ircodes
)
337 def add_wishbone(self
, ircodes
, address_width
, data_width
, sel_width
=None, domain
="sync"):
338 """Add a wishbone interface
341 - ircodes: sequence of three integer for the JTAG IR codes;
342 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
343 has a shift register of length 'address_width', the two other codes
344 share a shift register of length data_width.
345 - address_width: width of the address
346 - data_width: width of the data"""
348 assert len(ircodes
) == 3
350 sr_addr
= self
.add_shiftreg(ircodes
[0], address_width
, domain
=domain
)
351 sr_data
= self
.add_shiftreg(ircodes
[1:], data_width
, domain
=domain
)
353 wb
= Wishbone(data_width
=data_width
, address_width
=address_width
, sel_width
=sel_width
, master
=True)
355 self
._wbs
.append(JTAGWishbone(sr_addr
, sr_data
, wb
, domain
))