fe014f381182feacf050b22dbd55cb6785ce305d
[c4m-jtag.git] / sim / cocotb / 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):
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 trst_n <= 1
48 self.period = Timer(clk_period)
49
50 # Standard commands
51 # TODO: make IR length configurable; now 2 is assumed
52 self.BYPASS = [1, 1]
53 self.IDCODE = [0, 1]
54 self.SAMPLEPRELOAD = [1, 0]
55 self.EXTEST = [0, 0]
56
57 # After command we always leave the controller in reset or runidle state
58 # If value is None we will always reset the interface
59 self.state = None
60
61 # The methods of this class are coroutines. The results will be stored
62 # in the result field
63 self.result = None
64
65 @cocotb.coroutine
66 def cycle_clock(self, cycles=1):
67 if self.state == "Run" and self.tms == 1:
68 self.state = "Scan"
69 yield self.clkgen.Cycle(cycles)
70
71 @cocotb.coroutine
72 def reset(self):
73 if not self.trst_n is None:
74 # Enable reset signal for one clock period
75 self.trst_n <= 0
76 yield self.period
77 self.trst_n <= 1
78 else:
79 # 5 cycles with tms on 1 should reset the JTAG TAP controller
80 self.tms <= 1
81 yield self.cycle_clock(5)
82
83 self.state = "Reset"
84
85 self.result = None
86
87 @cocotb.coroutine
88 def change_state(self, tms_list):
89 """
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
93 if that is the case.
94 """
95 tms_copy = list(tms_list)
96 while tms_copy:
97 self.tms <= tms_copy.pop()
98 yield self.cycle_clock()
99 self.result = None
100
101 @cocotb.coroutine
102 def change_to_run(self):
103 """
104 Put TAP in RunTestIdle state
105 self.result is bool and true if TAP went through reset state
106 """
107 isreset = False
108 if self.state is None:
109 yield self.reset()
110 if self.state is "Reset":
111 isreset = True
112 self.tms <= 0
113 yield self.cycle_clock()
114 self.state = "Run"
115 assert(self.state == "Run")
116 self.result = isreset
117
118 @cocotb.coroutine
119 def load_ir(self, cmd):
120 cmd_copy = list(cmd)
121 result = BinaryValue(bits=len(cmd_copy))
122 l_result = list()
123
124 yield self.change_to_run()
125 # Go to Capture/IR
126 yield self.change_state([0, 1, 1])
127
128 # Shift the two
129 self.tms <= 0
130 while cmd_copy:
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))
136
137 # Go to RunTestIdle
138 yield self.change_state([0, 1, 1])
139 self.state = "Run"
140
141 @cocotb.coroutine
142 def idcode(self):
143 """
144 Get the IDCODE from the device
145 result will contain the 32 bit IDCODE of the device
146 """
147
148 result = BinaryValue(bits=32)
149 l_result = list()
150
151 # Keep tdi 0 for the whole run
152 self.tdi <= 0
153
154 yield self.change_to_run()
155 if not self.result:
156 # If TAP was not reset we have to load IDCODE command
157 yield self.load_ir(self.IDCODE)
158
159 # Should be again in RUN state
160 assert(self.state == "Run")
161
162 # Go to Shift/DR
163 yield self.change_state([0, 0, 1])
164
165 # Enter Shift; run for 32 cycles
166 self.tms <= 0
167 for i in range(32):
168 l_result.insert(0, str(self.tdo))
169 yield self.cycle_clock()
170 result.binstr = "".join(l_result)
171
172 # Go to RunTestIdle
173 yield self.change_state([0, 1, 1])
174 self.state = "Run"
175
176 self.result = result
177
178 @cocotb.coroutine
179 def shift_data(self, data_in):
180 """
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
185 """
186 if isinstance(data_in, BinaryValue):
187 data_copy = [int(c) for c in data_in.binstr]
188 else:
189 data_copy = list(data_in)
190 result = BinaryValue()
191 l_result = list()
192
193 yield self.change_to_run()
194 # Go to Capture/DR
195 yield self.change_state([0, 1])
196
197 # Shift data through
198 self.tms <= 0
199 while data_copy:
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)
204
205 # Go to RunTestIdle
206 yield self.change_state([0, 1, 1])
207 self.state = "Run"
208
209 self.result = result