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