IDCODE:
[c4m-jtag.git] / sim / cocotb / c4m_jtag.py
1 import cocotb
2 from cocotb.triggers import Timer
3 from cocotb.utils import get_sim_steps
4 from cocotb.binary import BinaryValue
5
6 class JTAGException(Exception):
7 pass
8
9 class JTAG_Clock(object):
10 """
11 Class for the JTAG clock, run cycle by cycle
12 """
13 def __init__(self, signal, period):
14 self.signal = signal
15 self.t = Timer(period/4)
16
17 @cocotb.coroutine
18 def Cycle(self, cycles=1):
19 """
20 Do one or more cycles
21 Cycle start in middle of 0 pulse of the clock
22 """
23 for i in range(cycles):
24 self.signal <= 0
25 yield self.t
26 self.signal <= 1
27 yield self.t
28 yield self.t
29 self.signal <= 0
30 yield self.t
31
32 class JTAG_Master(object):
33 """
34 Class that will run JTAG commands, shift in and out data
35 """
36 #TODO: Handle a JTAG chain with more than one device
37
38 def __init__(self, tck, tms, tdi, tdo, trst_n=None, clk_period=1000):
39 self.tck = tck
40 self.clkgen = JTAG_Clock(tck, clk_period)
41 tck <= 0
42 self.tms = tms
43 tms <= 1
44 self.tdi = tdi
45 tdi <= 0
46 self.tdo = tdo
47 self.trst_n = trst_n
48 trst_n <= 1
49 self.period = Timer(clk_period)
50
51 # Standard commands
52 # TODO: make IR length configurable; now 2 is assumed
53 self.BYPASS = [1, 1]
54 self.IDCODE = [0, 1]
55 self.SAMPLEPRELOAD = [1, 0]
56 self.EXTEST = [0, 0]
57
58 # After command we always leave the controller in reset or runidle state
59 # If value is None we will always reset the interface
60 self.state = None
61
62 # The methods of this class are coroutines. The results will be stored
63 # in the result field
64 self.result = None
65
66 @cocotb.coroutine
67 def cycle_clock(self, cycles=1):
68 if self.state == "Run" and self.tms == 1:
69 self.state = "Scan"
70 yield self.clkgen.Cycle(cycles)
71
72 @cocotb.coroutine
73 def reset(self):
74 if not self.trst_n is None:
75 # Enable reset signal for one clock period
76 self.trst_n <= 0
77 yield self.period
78 self.trst_n <= 1
79 else:
80 # 5 cycles with tms on 1 should reset the JTAG TAP controller
81 self.tms <= 1
82 yield self.cycle_clock(5)
83
84 self.state = "Reset"
85
86 self.result = None
87
88 @cocotb.coroutine
89 def change_state(self, tms_list):
90 """
91 Put TAP in other state by giving a TMS sequence
92 This function does not detect if one ends up in reset or run
93 state afterwards, self.state has to be updated by caller
94 if that is the case.
95 """
96 tms_copy = list(tms_list)
97 while tms_copy:
98 self.tms <= tms_copy.pop()
99 yield self.cycle_clock()
100 self.result = None
101
102 @cocotb.coroutine
103 def change_to_run(self):
104 """
105 Put TAP in RunTestIdle state
106 self.result is bool and true if TAP went through reset state
107 """
108 isreset = False
109 if self.state is None:
110 yield self.reset()
111 if self.state is "Reset":
112 isreset = True
113 self.tms <= 0
114 yield self.cycle_clock()
115 self.state = "Run"
116 assert(self.state == "Run")
117 self.result = isreset
118
119 @cocotb.coroutine
120 def load_ir(self, cmd):
121 cmd_copy = list(cmd)
122 result = BinaryValue(bits=len(cmd_copy))
123 l_result = list()
124
125 yield self.change_to_run()
126 # Go to Capture/IR
127 yield self.change_state([0, 1, 1])
128
129 # Shift the two
130 self.tms <= 0
131 while cmd_copy:
132 # In first iteration we enter SHIFT state and tdo is made active
133 yield self.cycle_clock()
134 # For the last iteration tdi will be shifted when entering next state
135 self.tdi <= cmd_copy.pop()
136 l_result.insert(0, str(self.tdo))
137
138 # Go to RunTestIdle
139 yield self.change_state([0, 1, 1])
140 self.state = "Run"
141
142 @cocotb.coroutine
143 def idcode(self):
144 """
145 Get the IDCODE from the device
146 result will contain the 32 bit IDCODE of the device
147 """
148
149 result = BinaryValue(bits=32)
150 l_result = list()
151
152 # Keep tdi 0 for the whole run
153 self.tdi <= 0
154
155 yield self.change_to_run()
156 if not self.result:
157 # If TAP was not reset we have to load IDCODE command
158 yield self.load_ir(self.IDCODE)
159
160 # Should be again in RUN state
161 assert(self.state == "Run")
162
163 # Go to Shift/DR
164 yield self.change_state([0, 0, 1])
165
166 # Enter Shift; run for 32 cycles
167 self.tms <= 0
168 for i in range(32):
169 l_result.insert(0, str(self.tdo))
170 yield self.cycle_clock()
171 result.binstr = "".join(l_result)
172
173 # Go to RunTestIdle
174 yield self.change_state([0, 1, 1])
175 self.state = "Run"
176
177 self.result = result
178
179 @cocotb.coroutine
180 def shift_data(self, data_in):
181 """
182 Shift data in through the JTAG and capture the output
183 Input can be of type BinaryValue or an iterable value of 0 and 1s.
184 Last bit will be shifted in first.
185 result will contain the sample TDO with the same number of bits as the input
186 """
187 if isinstance(data_in, BinaryValue):
188 data_copy = [int(c) for c in data_in.binstr]
189 else:
190 data_copy = list(data_in)
191 result = BinaryValue()
192 l_result = list()
193
194 yield self.change_to_run()
195 # Go to Capture/DR
196 yield self.change_state([0, 1])
197
198 # Shift data through
199 self.tms <= 0
200 while data_copy:
201 yield self.cycle_clock()
202 self.tdi <= data_copy.pop()
203 l_result.insert(0, str(self.tdo))
204 result.binstr = "".join(l_result)
205
206 # Go to RunTestIdle
207 yield self.change_state([0, 1, 1])
208 self.state = "Run"
209
210 self.result = result