cocotb improvements
[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 class JTAGException(Exception):
6 pass
7
8 class JTAG_Clock(object):
9 """
10 Class for the JTAG clock, run cycle by cycle
11 """
12 def __init__(self, signal, period):
13 self.signal = signal
14 self.t = Timer(period/4)
15
16 @cocotb.coroutine
17 def Cycle(self, cycles=1):
18 """
19 Do one or more cycles
20 Cycle start in middle of 0 pulse of the clock
21 """
22 for i in range(cycles):
23 self.signal <= 0
24 yield self.t
25 self.signal <= 1
26 yield self.t
27 yield self.t
28 self.signal <= 0
29 yield self.t
30
31 class JTAG_Master(object):
32 """
33 Class that will run JTAG commands, shift in and out data
34 """
35 #TODO: Handle a JTAG chain with more than one device
36
37 def __init__(self, tck, tms, tdi, tdo, trst_n=None, clk_period=1000, ir_width=2):
38 self.tck = tck
39 self.clkgen = JTAG_Clock(tck, clk_period)
40 tck <= 0
41 self.tms = tms
42 tms <= 1
43 self.tdi = tdi
44 tdi <= 0
45 self.tdo = tdo
46 self.trst_n = trst_n
47 if trst_n is not None:
48 trst_n <= 1
49 self.period = Timer(clk_period)
50
51 # Standard commands
52 # TODO: Make values configurable
53 self.BYPASS = [1 for _ in range(ir_width)]
54 self.IDCODE = [1 if i == ir_width-1 else 0 for i in range(ir_width)]
55 self.SAMPLEPRELOAD = [1 if i == ir_width-2 else 0 for i in range(ir_width)]
56 self.EXTEST = [0 for _ in range(ir_width)]
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 if isinstance(cmd, BinaryValue):
122 cmd_copy = [int(c) for c in cmd.binstr]
123 else:
124 cmd_copy = list(cmd)
125 result = BinaryValue(bits=len(cmd_copy))
126 l_result = list()
127
128 yield self.change_to_run()
129 # Go to Capture/IR
130 yield self.change_state([0, 1, 1])
131
132 # Shift the two
133 self.tms <= 0
134 while cmd_copy:
135 # In first iteration we enter SHIFT state and tdo is made active
136 yield self.cycle_clock()
137 # For the last iteration tdi will be shifted when entering next state
138 self.tdi <= cmd_copy.pop()
139 l_result.insert(0, str(self.tdo))
140
141 # Go to RunTestIdle
142 yield self.change_state([0, 1, 1])
143 self.state = "Run"
144
145 @cocotb.coroutine
146 def idcode(self):
147 """
148 Get the IDCODE from the device
149 result will contain the 32 bit IDCODE of the device
150 """
151
152 result = BinaryValue(bits=32)
153 l_result = list()
154
155 # Keep tdi 0 for the whole run
156 self.tdi <= 0
157
158 yield self.change_to_run()
159 if not self.result:
160 # If TAP was not reset we have to load IDCODE command
161 yield self.load_ir(self.IDCODE)
162
163 # Should be again in RUN state
164 assert(self.state == "Run")
165
166 # Go to Shift/DR
167 yield self.change_state([0, 0, 1])
168
169 # Enter Shift; run for 32 cycles
170 self.tms <= 0
171 for i in range(32):
172 l_result.insert(0, str(self.tdo))
173 yield self.cycle_clock()
174 result.binstr = "".join(l_result)
175
176 # Go to RunTestIdle
177 yield self.change_state([0, 1, 1])
178 self.state = "Run"
179
180 self.result = result
181
182 @cocotb.coroutine
183 def shift_data(self, data_in):
184 """
185 Shift data in through the JTAG and capture the output
186 Input can be of type BinaryValue or an iterable value of 0 and 1s.
187 Last bit will be shifted in first.
188 result will contain the sample TDO with the same number of bits as the input
189 """
190 if isinstance(data_in, BinaryValue):
191 data_copy = [int(c) for c in data_in.binstr]
192 else:
193 data_copy = list(data_in)
194 result = BinaryValue()
195 l_result = list()
196
197 yield self.change_to_run()
198 # Go to Capture/DR
199 yield self.change_state([0, 1])
200
201 # Shift data through
202 self.tms <= 0
203 while data_copy:
204 yield self.cycle_clock()
205 self.tdi <= data_copy.pop()
206 l_result.insert(0, str(self.tdo))
207 result.binstr = "".join(l_result)
208
209 # Go to RunTestIdle
210 yield self.change_state([0, 1, 1])
211 self.state = "Run"
212
213 self.result = result