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