5deb89a1362f05293fecfac467ca24d2baa5b63a
[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 tms_copy = list(tms_list)
88 while tms_copy:
89 self.tms <= tms_copy.pop()
90 yield self.cycle_clock()
91 self.result = None
92
93 @cocotb.coroutine
94 def change_to_run(self):
95 """
96 Put TAP in RunTestIdle state
97 self.result is bool and true if TAP went through reset state
98 """
99 isreset = False
100 if self.state is None:
101 yield self.reset()
102 if self.state is "Reset":
103 isreset = True
104 self.tms <= 0
105 yield self.cycle_clock()
106 self.state = "Run"
107 assert(self.state == "Run")
108 self.result = isreset
109
110 @cocotb.coroutine
111 def load_ir(self, cmd):
112 cmd_copy = list(cmd)
113 result = BinaryValue(bits=len(cmd_copy))
114 l_result = list()
115
116 yield self.change_to_run()
117 # Go to Capture/IR
118 yield self.change_state([0, 1, 1])
119
120 # Shift the two
121 self.tms <= 0
122 while cmd_copy:
123 # In first iteration we enter SHIFT state and tdo is made active
124 yield self.cycle_clock()
125 # For the last iteration tdi will be shifted when entering next state
126 self.tdi <= cmd_copy.pop()
127 l_result.insert(0, str(self.tdo))
128
129 # Go to RunTestIdle
130 yield self.change_state([0, 1, 1])
131 self.state = "Run"
132
133 @cocotb.coroutine
134 def idcode(self):
135 """
136 Get the IDCODE from the device
137 result will contain the 32 bit IDCODE of the device
138 """
139
140 result = BinaryValue(bits=32)
141 l_result = list()
142
143 # Keep tdi 0 for the whole run
144 self.tdi <= 0
145
146 yield self.change_to_run()
147 if not self.result:
148 # If TAP was not reset we have to load IDCODE command
149 yield self.load_ir(self.IDCODE)
150
151 # Should be again in RUN state
152 assert(self.state == "Run")
153
154 # Go to Shift/DR
155 yield self.change_state([0, 0, 1])
156
157 # Enter Shift; run for 32 cycles
158 self.tms <= 0
159 for i in range(32):
160 l_result.insert(0, str(self.tdo))
161 yield self.cycle_clock()
162 result.binstr = "".join(l_result)
163
164 # Go to RunTestIdle
165 yield self.change_state([0, 1, 1])
166 self.state = "Run"
167
168 self.result = result
169
170 @cocotb.coroutine
171 def shift_data(self, data_in):
172 """
173 Shift data in through the JTAG and capture the output
174 Input can be of type BinaryValue or an iterable value of 0 and 1s.
175 Last bit will be shifted in first.
176 result will contain the sample TDO with the same number of bits as the input
177 """
178 if isinstance(data_in, BinaryValue):
179 data_copy = [int(c) for c in data_in.binstr]
180 else:
181 data_copy = list(data_in)
182 result = BinaryValue()
183 l_result = list()
184
185 yield self.change_to_run()
186 # Go to Capture/DR
187 yield self.change_state([0, 1])
188
189 # Shift data through
190 self.tms <= 0
191 while data_copy:
192 yield self.cycle_clock()
193 self.tdi <= data_copy.pop()
194 l_result.insert(0, str(self.tdo))
195 result.binstr = "".join(l_result)
196
197 # Go to RunTestIdle
198 yield self.change_state([0, 1, 1])
199 self.state = "Run"
200
201 self.result = result