3baaa9553f9b3c5a95b9e51a3ed93869bed6ee96
5 from nmigen
.build
import *
6 from nmigen
.lib
.io
import *
8 from wishbone
import Wishbone
11 "PmodJTAGMasterResource",
12 "PmodJTAGMasterAResource",
13 "PmodJTAGSlaveResource",
14 "PmodJTAGSlaveAResource",
18 #TODO: Provide more documentation
20 def PmodJTAGMasterResource(name
, number
, *, pmod
, attrs
=Attrs(IOSTANDARD
="LVCMOS33")):
21 return Resource(name
, number
,
22 Subsignal("TCK", Pins("1", dir="o", conn
=("pmod", pmod
))),
23 Subsignal("TMS", Pins("2", dir="o", conn
=("pmod", pmod
))),
24 Subsignal("TDO", Pins("3", dir="o", conn
=("pmod", pmod
))),
25 Subsignal("TDI", Pins("4", dir="i", conn
=("pmod", pmod
))),
29 def PmodJTAGMasterAResource(name
, number
, *, pmod
, attrs
=Attrs(IOSTANDARD
="LVCMOS33")):
30 return Resource(name
, number
,
31 Subsignal("TCK", Pins("1", dir="o", conn
=("pmod", pmod
))),
32 Subsignal("TMS", Pins("2", dir="o", conn
=("pmod", pmod
))),
33 Subsignal("TDO", Pins("3", dir="o", conn
=("pmod", pmod
))),
34 Subsignal("TDI", Pins("4", dir="i", conn
=("pmod", pmod
))),
35 Subsignal("TRST", PinsN("7", dir="o", conn
=("pmod", pmod
))),
39 def PmodJTAGSlaveResource(name
, number
, *, pmod
, attrs
=Attrs(IOSTANDARD
="LVCMOS33")):
40 return Resource(name
, number
,
41 Subsignal("TCK", Pins("1", dir="i", conn
=("pmod", pmod
))),
42 Subsignal("TMS", Pins("2", dir="i", conn
=("pmod", pmod
))),
43 Subsignal("TDI", Pins("3", dir="i", conn
=("pmod", pmod
))),
44 Subsignal("TDO", Pins("4", dir="o", conn
=("pmod", pmod
))),
48 def PmodJTAGSlaveAResource(name
, number
, *, pmod
, attrs
=Attrs(IOSTANDARD
="LVCMOS33")):
49 return Resource(name
, number
,
50 Subsignal("TCK", Pins("1", dir="i", conn
=("pmod", pmod
))),
51 Subsignal("TMS", Pins("2", dir="i", conn
=("pmod", pmod
))),
52 Subsignal("TDI", Pins("3", dir="i", conn
=("pmod", pmod
))),
53 Subsignal("TDO", Pins("4", dir="o", conn
=("pmod", pmod
))),
54 Subsignal("TRST", PinsN("7", dir="i", conn
=("pmod", pmod
))),
59 class ShiftReg(Elaboratable
):
60 def __init__(self
, ircodes
, length
, domain
):
61 # The sr record will be returned to user code
62 self
.sr
= Record([("i", length
), ("o", length
), ("oe", len(ircodes
)), ("ack", 1)])
63 # The next attributes are for JTAG class usage only
64 self
.ir
= None # made None as width is not known yet
67 self
.tdo_en
= Signal()
68 self
.capture
= Signal()
70 self
.update
= Signal()
71 self
.jtag_cd
= None # The JTAG clock domain
75 self
._ircodes
= ircodes
78 def elaborate(self
, platform
):
79 length
= len(self
.sr
.o
)
84 m
.domains
.jtag
= self
.jtag_cd
86 sr_jtag
= Signal(length
)
88 assert isinstance(self
.ir
, Signal
)
89 isir
= Signal(len(self
._ircodes
))
94 isir
.eq(Cat(self
.ir
== ircode
for ircode
in self
._ircodes
)),
95 capture
.eq((isir
!= 0) & self
.capture
),
96 shift
.eq((isir
!= 0) & self
.shift
),
97 update
.eq((isir
!= 0) & self
.update
),
100 # On update set o, oe and wait for ack
101 # update signal is on JTAG clockdomain, latch it
102 update_core
= Signal()
103 m
.d
[domain
] += update_core
.eq(update
) # This is CDC from JTAG domain to given domain
104 with m
.FSM(domain
=domain
):
105 with m
.State("IDLE"):
106 m
.d
.comb
+= self
.sr
.oe
.eq(0)
107 with m
.If(update_core
):
108 # Latch sr_jtag cross domain but it should be stable due to latching of update_core
109 m
.d
[domain
] += self
.sr
.o
.eq(sr_jtag
)
110 # Wait one cycle to raise oe so sr.o has one more cycle to stabilize
112 with m
.State("WAIT4ACK"):
113 m
.d
.comb
+= self
.sr
.oe
.eq(isir
)
114 with m
.If(self
.sr
.ack
):
116 with m
.State("WAIT4END"):
117 m
.d
.comb
+= self
.sr
.oe
.eq(0)
118 with m
.If(~update_core
):
122 self
.tdo
.eq(sr_jtag
[0]),
123 self
.tdo_en
.eq(shift
),
127 m
.d
.jtag
+= sr_jtag
.eq(Cat(sr_jtag
[1:], self
.tdi
))
129 m
.d
.jtag
+= sr_jtag
.eq(self
.sr
.i
)
133 class JTAGWishbone(Elaboratable
):
134 def __init__(self
, sr_addr
, sr_data
, wb
, domain
):
135 self
._sr
_addr
= sr_addr
136 self
._sr
_data
= sr_data
138 self
._domain
= domain
143 def elaborate(self
, platform
):
144 sr_addr
= self
._sr
_addr
145 sr_data
= self
._sr
_data
147 domain
= self
._domain
152 if hasattr(wb
, "sel"):
154 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
158 sr_addr
.ack
.eq(sr_addr
.oe
),
159 sr_data
.ack
.eq(sr_data
.oe
!= 0),
162 with m
.FSM(domain
=domain
) as fsm
:
163 with m
.State("IDLE"):
169 with m
.If(sr_addr
.oe
): # WBADDR code
170 m
.d
[domain
] += wb
.addr
.eq(sr_addr
.o
)
172 with m
.If(sr_data
.oe
[0]): # WBREAD code
173 m
.d
[domain
] += wb
.addr
.eq(wb
.addr
+ 1)
175 with m
.If(sr_data
.oe
[1]): # WBWRITE code
176 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
178 with m
.State("READ"):
184 with m
.If(~wb
.stall
):
186 with m
.State("READACK"):
193 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
195 with m
.State("WRITEREAD"):
201 with m
.If(~wb
.stall
):
202 m
.next
= "WRITEREADACK"
203 with m
.State("WRITEREADACK"):
210 m
.d
[domain
] += wb
.addr
.eq(wb
.addr
+ 1)
216 class JTAG(Elaboratable
):
218 def _add_files(platform
, prefix
):
219 d
= os
.path
.realpath("{0}{1}{2}{1}vhdl".format(
220 os
.path
.dirname(__file__
), os
.path
.sep
, os
.path
.pardir
224 "c4m_jtag_idblock.vhdl",
225 "c4m_jtag_iocell.vhdl",
226 "c4m_jtag_ioblock.vhdl",
227 "c4m_jtag_irblock.vhdl",
228 "c4m_jtag_tap_fsm.vhdl",
229 "c4m_jtag_tap_controller.vhdl",
231 f
= open(d
+ fname
, "r")
232 platform
.add_file(prefix
+ fname
, f
)
236 def __init__(self
, io_count
, *, ir_width
=None, manufacturer_id
=Const(0b10001111111, 11),
237 part_number
=Const(1, 16), version
=Const(0, 4)
239 assert(isinstance(io_count
, int) and io_count
> 0)
240 assert((ir_width
is None) or (isinstance(ir_width
, int) and ir_width
>= 2))
241 assert(len(version
) == 4)
243 # TODO: Handle IOs with different directions
248 self
.core
= Array(Pin(1, "io") for _
in range(io_count
)) # Signals to use for core
249 self
.pad
= Array(Pin(1, "io") for _
in range(io_count
)) # Signals going to IO pads
251 self
.jtag_cd
= ClockDomain(name
="jtag", local
=True) # Own clock domain using TCK as clock signal
255 self
._io
_count
= io_count
256 self
._ir
_width
= ir_width
257 self
._manufacturer
_id
= manufacturer_id
258 self
._part
_number
= part_number
259 self
._version
= version
261 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at the end
266 def elaborate(self
, platform
):
267 JTAG
._add
_files
(platform
, "jtag" + os
.path
.sep
)
278 ir_max
= max(self
._ircodes
) + 1 # One extra code needed with all ones
279 ir_width
= len("{:b}".format(ir_max
))
280 if self
._ir
_width
is not None:
281 assert self
._ir
_width
>= ir_width
, "Specified JTAG IR width not big enough for allocated shiift registers"
282 ir_width
= self
._ir
_width
283 ir
= Signal(ir_width
)
285 core_i
= Cat(pin
.i
for pin
in self
.core
)
286 core_o
= Cat(pin
.o
for pin
in self
.core
)
287 core_oe
= Cat(pin
.oe
for pin
in self
.core
)
288 pad_i
= Cat(pin
.i
for pin
in self
.pad
)
289 pad_o
= Cat(pin
.o
for pin
in self
.pad
)
290 pad_oe
= Cat(pin
.oe
for pin
in self
.pad
)
293 "p_IOS": self
._io
_count
,
294 "p_IR_WIDTH": ir_width
,
295 "p_MANUFACTURER": self
._manufacturer
_id
,
296 "p_PART_NUMBER": self
._part
_number
,
297 "p_VERSION": self
._version
,
302 "i_TRST_N": Const(1),
304 "o_DRCAPTURE": capture
,
306 "o_DRUPDATE": update
,
309 "i_CORE_OUT": core_o
,
310 "i_CORE_EN": core_oe
,
315 m
.submodules
.tap
= Instance("c4m_jtag_tap_controller", **params
)
318 self
.jtag_cd
.clk
.eq(self
.tck
),
319 self
.jtag_cd
.rst
.eq(reset
),
322 for i
, sr
in enumerate(self
._srs
):
323 m
.submodules
["sr{}".format(i
)] = sr
327 sr
.capture
.eq(capture
),
329 sr
.update
.eq(update
),
332 if len(self
._srs
) > 0:
337 with m
.If(sr
.tdo_en
):
338 m
.d
.comb
+= self
.tdo
.eq(sr
.tdo
)
340 with m
.Elif(sr
.tdo_en
):
341 m
.d
.comb
+= self
.tdo
.eq(sr
.tdo
)
343 m
.d
.comb
+= self
.tdo
.eq(tdo_jtag
)
345 m
.d
.comb
+= self
.tdo
.eq(tdo_jtag
)
347 for i
, wb
in enumerate(self
._wbs
):
348 m
.submodules
["wb{}".format(i
)] = wb
354 def add_shiftreg(self
, ircode
, length
, domain
="sync"):
355 """Add a shift register to the JTAG interface
358 - ircode: code(s) for the IR; int or sequence of ints. In the latter case this
359 shiftreg is shared between different IR codes.
360 - length: the length of the shift register
361 - domain: the domain on which the signal will be used"""
367 ir_it
= ircodes
= (ircode
,)
368 for _ircode
in ir_it
:
369 assert(isinstance(_ircode
, int) and _ircode
> 0 and _ircode
not in self
._ircodes
)
371 sr
= ShiftReg(ircodes
, length
, domain
)
372 sr
.jtag_cd
= self
.jtag_cd
373 self
._ircodes
.extend(ircodes
)
379 def add_wishbone(self
, ircodes
, address_width
, data_width
, sel_width
=None, domain
="sync"):
380 """Add a wishbone interface
383 - ircodes: sequence of three integer for the JTAG IR codes;
384 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
385 has a shift register of length 'address_width', the two other codes
386 share a shift register of length data_width.
387 - address_width: width of the address
388 - data_width: width of the data"""
390 assert len(ircodes
) == 3
392 sr_addr
= self
.add_shiftreg(ircodes
[0], address_width
, domain
=domain
)
393 sr_data
= self
.add_shiftreg(ircodes
[1:], data_width
, domain
=domain
)
395 wb
= Wishbone(data_width
=data_width
, address_width
=address_width
, sel_width
=sel_width
, master
=True)
397 self
._wbs
.append(JTAGWishbone(sr_addr
, sr_data
, wb
, domain
))