2 from cocotb
.triggers
import Timer
3 from cocotb
.binary
import BinaryValue
5 class JTAGException(Exception):
8 class JTAG_Clock(object):
10 Class for the JTAG clock, run cycle by cycle
12 def __init__(self
, signal
, period
):
14 self
.t
= Timer(period
/4)
17 def Cycle(self
, cycles
=1):
20 Cycle start in middle of 0 pulse of the clock
22 for i
in range(cycles
):
31 class JTAG_Master(object):
33 Class that will run JTAG commands, shift in and out data
35 #TODO: Handle a JTAG chain with more than one device
37 def __init__(self
, tck
, tms
, tdi
, tdo
, trst_n
=None, clk_period
=1000):
39 self
.clkgen
= JTAG_Clock(tck
, clk_period
)
48 self
.period
= Timer(clk_period
)
51 # TODO: make IR length configurable; now 2 is assumed
54 self
.SAMPLEPRELOAD
= [1, 0]
57 # After command we always leave the controller in reset or runidle state
58 # If value is None we will always reset the interface
61 # The methods of this class are coroutines. The results will be stored
66 def cycle_clock(self
, cycles
=1):
67 if self
.state
== "Run" and self
.tms
== 1:
69 yield self
.clkgen
.Cycle(cycles
)
73 if not self
.trst_n
is None:
74 # Enable reset signal for one clock period
79 # 5 cycles with tms on 1 should reset the JTAG TAP controller
81 yield self
.cycle_clock(5)
88 def change_state(self
, tms_list
):
90 Put TAP in other state by giving a TMS sequence
91 This function does not detect if one ends up in reset or run
92 state afterwards, self.state has to be updated by caller
95 tms_copy
= list(tms_list
)
97 self
.tms
<= tms_copy
.pop()
98 yield self
.cycle_clock()
102 def change_to_run(self
):
104 Put TAP in RunTestIdle state
105 self.result is bool and true if TAP went through reset state
108 if self
.state
is None:
110 if self
.state
is "Reset":
113 yield self
.cycle_clock()
115 assert(self
.state
== "Run")
116 self
.result
= isreset
119 def load_ir(self
, cmd
):
121 result
= BinaryValue(bits
=len(cmd_copy
))
124 yield self
.change_to_run()
126 yield self
.change_state([0, 1, 1])
131 # In first iteration we enter SHIFT state and tdo is made active
132 yield self
.cycle_clock()
133 # For the last iteration tdi will be shifted when entering next state
134 self
.tdi
<= cmd_copy
.pop()
135 l_result
.insert(0, str(self
.tdo
))
138 yield self
.change_state([0, 1, 1])
144 Get the IDCODE from the device
145 result will contain the 32 bit IDCODE of the device
148 result
= BinaryValue(bits
=32)
151 # Keep tdi 0 for the whole run
154 yield self
.change_to_run()
156 # If TAP was not reset we have to load IDCODE command
157 yield self
.load_ir(self
.IDCODE
)
159 # Should be again in RUN state
160 assert(self
.state
== "Run")
163 yield self
.change_state([0, 0, 1])
165 # Enter Shift; run for 32 cycles
168 l_result
.insert(0, str(self
.tdo
))
169 yield self
.cycle_clock()
170 result
.binstr
= "".join(l_result
)
173 yield self
.change_state([0, 1, 1])
179 def shift_data(self
, data_in
):
181 Shift data in through the JTAG and capture the output
182 Input can be of type BinaryValue or an iterable value of 0 and 1s.
183 Last bit will be shifted in first.
184 result will contain the sample TDO with the same number of bits as the input
186 if isinstance(data_in
, BinaryValue
):
187 data_copy
= [int(c
) for c
in data_in
.binstr
]
189 data_copy
= list(data_in
)
190 result
= BinaryValue()
193 yield self
.change_to_run()
195 yield self
.change_state([0, 1])
200 yield self
.cycle_clock()
201 self
.tdi
<= data_copy
.pop()
202 l_result
.insert(0, str(self
.tdo
))
203 result
.binstr
= "".join(l_result
)
206 yield self
.change_state([0, 1, 1])