Skip to content

Commit f65cca3

Browse files
Merge pull request #137 from sebastien-riou/master
basic OSCAN1 decode
2 parents 55bcb5f + cfd80ca commit f65cca3

File tree

2 files changed

+333
-0
lines changed

2 files changed

+333
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
##
2+
## This file is part of the libsigrokdecode project.
3+
##
4+
## Copyright (C) 2012 Uwe Hermann <[email protected]>
5+
##
6+
## This program is free software; you can redistribute it and/or modify
7+
## it under the terms of the GNU General Public License as published by
8+
## the Free Software Foundation; either version 2 of the License, or
9+
## (at your option) any later version.
10+
##
11+
## This program is distributed in the hope that it will be useful,
12+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
## GNU General Public License for more details.
15+
##
16+
## You should have received a copy of the GNU General Public License
17+
## along with this program; if not, write to the Free Software
18+
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
##
20+
21+
'''
22+
JTAG (Joint Test Action Group), a.k.a. "IEEE 1149.1: Standard Test Access Port
23+
and Boundary-Scan Architecture", is a protocol used for testing, debugging,
24+
and flashing various digital ICs.
25+
26+
Details:
27+
https://en.wikipedia.org/wiki/Joint_Test_Action_Group
28+
http://focus.ti.com/lit/an/ssya002c/ssya002c.pdf
29+
30+
This decoders handles a tiny part of IEEE 1149.7, the so called CJTAG OSCAN1
31+
format
32+
http://developers-club.com/posts/237885/
33+
'''
34+
35+
from .pd import Decoder
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
##
2+
## Copyright (C) 2018 Sebastien Riou
3+
##
4+
## This program is free software; you can redistribute it and/or modify
5+
## it under the terms of the GNU General Public License as published by
6+
## the Free Software Foundation; either version 2 of the License, or
7+
## (at your option) any later version.
8+
##
9+
## This program is distributed in the hope that it will be useful,
10+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
## GNU General Public License for more details.
13+
##
14+
## You should have received a copy of the GNU General Public License
15+
## along with this program; if not, write to the Free Software
16+
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
##
18+
19+
import sigrokdecode as srd
20+
21+
jtag_states = [
22+
# Intro "tree"
23+
'TEST-LOGIC-RESET', 'RUN-TEST/IDLE',
24+
# DR "tree"
25+
'SELECT-DR-SCAN', 'CAPTURE-DR', 'UPDATE-DR', 'PAUSE-DR',
26+
'SHIFT-DR', 'EXIT1-DR', 'EXIT2-DR',
27+
# IR "tree"
28+
'SELECT-IR-SCAN', 'CAPTURE-IR', 'UPDATE-IR', 'PAUSE-IR',
29+
'SHIFT-IR', 'EXIT1-IR', 'EXIT2-IR',
30+
]
31+
32+
oscan1_phases = ['nTDI','TMS','TDO']
33+
34+
class Decoder(srd.Decoder):
35+
api_version = 2
36+
id = 'cjtag_oscan1'
37+
name = 'CJTAG OSCAN1'
38+
longname = 'Joint Test Action Group (IEEE 1149.7 OSCAN1)'
39+
desc = 'Protocol for testing, debugging, and flashing ICs.'
40+
license = 'gplv2+'
41+
inputs = ['logic']
42+
outputs = ['jtag']
43+
channels = (
44+
{'id': 'tck', 'name': 'TCK', 'desc': 'Test clock'},
45+
{'id': 'tms', 'name': 'TMS', 'desc': 'Test mode select'},
46+
)
47+
annotations = tuple([tuple([s.lower(), s]) for s in oscan1_phases]) + tuple([tuple([s.lower(), s]) for s in jtag_states])
48+
others = ( \
49+
('bit-tdi', 'Bit (TDI)'),
50+
('bit-tdo', 'Bit (TDO)'),
51+
('bitstring-tdi', 'Bitstring (TDI)'),
52+
('bitstring-tdo', 'Bitstring (TDO)'),
53+
)
54+
annotation_rows = (
55+
# ('bits-tdi', 'Bits (TDI)', (16,)),
56+
# ('bits-tdo', 'Bits (TDO)', (17,)),
57+
# ('bitstrings-tdi', 'Bitstring (TDI)', (18,)),
58+
# ('bitstrings-tdo', 'Bitstring (TDO)', (19,)),
59+
('oscan1-phase', 'OSCAN1 phase', tuple(range(0,0+3)) ),
60+
('states', 'States', tuple(range(3,3+15+1))),
61+
62+
)
63+
64+
def __init__(self):
65+
self.state = 'RUN-TEST/IDLE'
66+
self.phase = 'nTDI'
67+
self.oldstate = None
68+
self.oldpins = (-1, -1, -1, -1)
69+
self.oldtck = -1
70+
self.bits_tdi = []
71+
self.bits_tdo = []
72+
self.bits_samplenums_tdi = []
73+
self.bits_samplenums_tdo = []
74+
self.samplenum = 0
75+
self.ss_item = self.es_item = None
76+
self.ss_bitstring = self.es_bitstring = None
77+
self.last_clock_samplenum = None
78+
self.saved_item = None
79+
self.first = True
80+
self.first_bit = True
81+
self.bits_cnt = 0
82+
self.data_ready = False
83+
84+
def start(self):
85+
self.out_python = self.register(srd.OUTPUT_PYTHON)
86+
self.out_ann = self.register(srd.OUTPUT_ANN)
87+
88+
def putx(self, data):
89+
self.put(self.ss_item, self.es_item, self.out_ann, data)
90+
91+
def putp(self, data):
92+
self.put(self.ss_item, self.es_item, self.out_python, data)
93+
94+
def putx_bs(self, data):
95+
self.put(self.ss_bitstring, self.es_bitstring, self.out_ann, data)
96+
97+
def putp_bs(self, data):
98+
self.put(self.ss_bitstring, self.es_bitstring, self.out_python, data)
99+
100+
def advance_state_machine(self, tms):
101+
self.oldstate = self.state
102+
103+
# Intro "tree"
104+
if self.state == 'TEST-LOGIC-RESET':
105+
# self.state = 'TEST-LOGIC-RESET' if (tms) else 'RUN-TEST/IDLE'
106+
# if we reach this state we are not in OSCAN1 anymore. Since we don't handle anything else we stay in this state to show clearly the failure
107+
self.state = 'TEST-LOGIC-RESET'
108+
elif self.state == 'RUN-TEST/IDLE':
109+
self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
110+
111+
# DR "tree"
112+
elif self.state == 'SELECT-DR-SCAN':
113+
self.state = 'SELECT-IR-SCAN' if (tms) else 'CAPTURE-DR'
114+
elif self.state == 'CAPTURE-DR':
115+
self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR'
116+
elif self.state == 'SHIFT-DR':
117+
self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR'
118+
elif self.state == 'EXIT1-DR':
119+
self.state = 'UPDATE-DR' if (tms) else 'PAUSE-DR'
120+
elif self.state == 'PAUSE-DR':
121+
self.state = 'EXIT2-DR' if (tms) else 'PAUSE-DR'
122+
elif self.state == 'EXIT2-DR':
123+
self.state = 'UPDATE-DR' if (tms) else 'SHIFT-DR'
124+
elif self.state == 'UPDATE-DR':
125+
self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
126+
127+
# IR "tree"
128+
elif self.state == 'SELECT-IR-SCAN':
129+
self.state = 'TEST-LOGIC-RESET' if (tms) else 'CAPTURE-IR'
130+
elif self.state == 'CAPTURE-IR':
131+
self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR'
132+
elif self.state == 'SHIFT-IR':
133+
self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR'
134+
elif self.state == 'EXIT1-IR':
135+
self.state = 'UPDATE-IR' if (tms) else 'PAUSE-IR'
136+
elif self.state == 'PAUSE-IR':
137+
self.state = 'EXIT2-IR' if (tms) else 'PAUSE-IR'
138+
elif self.state == 'EXIT2-IR':
139+
self.state = 'UPDATE-IR' if (tms) else 'SHIFT-IR'
140+
elif self.state == 'UPDATE-IR':
141+
self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
142+
143+
def handle_rising_tck_edge(self, tck, tms):
144+
145+
if self.phase == 'nTDI':
146+
self.tdi = 1-tms
147+
if self.first:
148+
# Save the start sample and item for later (no output yet).
149+
self.ss_item = self.samplenum
150+
self.first = False
151+
elif self.phase == 'TMS':
152+
self.tms = tms
153+
elif self.phase == 'TDO':
154+
self.tdo = tms
155+
self.advance_state_machine(self.tms)
156+
157+
# Output the saved item (from the last CLK edge to the current).
158+
self.es_item = self.samplenum
159+
if self.ss_item is not None:
160+
# Output the old state (from last rising TCK edge to current one).
161+
self.putx([3+jtag_states.index(self.oldstate), [self.oldstate]])
162+
# self.putp(['NEW STATE', self.state])
163+
164+
self.ss_item = self.samplenum
165+
166+
if 0:
167+
# Upon SHIFT-IR/SHIFT-DR collect the current TDI/TDO values.
168+
if self.state.startswith('SHIFT-'):
169+
if self.bits_cnt > 0:
170+
if self.bits_cnt == 1:
171+
self.ss_bitstring = self.samplenum
172+
173+
if self.bits_cnt > 1:
174+
self.putx([16, [str(self.bits_tdi[0])]])
175+
self.putx([17, [str(self.bits_tdo[0])]])
176+
# Use self.samplenum as ES of the previous bit.
177+
self.bits_samplenums_tdi[0][1] = self.samplenum
178+
self.bits_samplenums_tdo[0][1] = self.samplenum
179+
180+
self.bits_tdi.insert(0, tdi)
181+
self.bits_tdo.insert(0, tdo)
182+
183+
# Use self.samplenum as SS of the current bit.
184+
self.bits_samplenums_tdi.insert(0, [self.samplenum, -1])
185+
self.bits_samplenums_tdo.insert(0, [self.samplenum, -1])
186+
187+
self.bits_cnt = self.bits_cnt + 1
188+
189+
# Output all TDI/TDO bits if we just switched from SHIFT-* to EXIT1-*.
190+
if self.oldstate.startswith('SHIFT-') and \
191+
self.state.startswith('EXIT1-'):
192+
193+
#self.es_bitstring = self.samplenum
194+
if self.bits_cnt > 0:
195+
if self.bits_cnt == 1: # Only shift one bit
196+
self.ss_bitstring = self.samplenum
197+
self.bits_tdi.insert(0, tdi)
198+
self.bits_tdo.insert(0, tdo)
199+
## Use self.samplenum as SS of the current bit.
200+
self.bits_samplenums_tdi.insert(0, [self.samplenum, -1])
201+
self.bits_samplenums_tdo.insert(0, [self.samplenum, -1])
202+
else:
203+
### ----------------------------------------------------------------
204+
self.putx([16, [str(self.bits_tdi[0])]])
205+
self.putx([17, [str(self.bits_tdo[0])]])
206+
### Use self.samplenum as ES of the previous bit.
207+
self.bits_samplenums_tdi[0][1] = self.samplenum
208+
self.bits_samplenums_tdo[0][1] = self.samplenum
209+
210+
self.bits_tdi.insert(0, tdi)
211+
self.bits_tdo.insert(0, tdo)
212+
213+
## Use self.samplenum as SS of the current bit.
214+
self.bits_samplenums_tdi.insert(0, [self.samplenum, -1])
215+
self.bits_samplenums_tdo.insert(0, [self.samplenum, -1])
216+
## ----------------------------------------------------------------
217+
218+
self.data_ready = True
219+
220+
self.first_bit = True
221+
self.bits_cnt = 0
222+
if self.oldstate.startswith('EXIT'):
223+
if self.data_ready:
224+
self.data_ready = False
225+
self.es_bitstring = self.samplenum
226+
t = self.state[-2:] + ' TDI'
227+
b = ''.join(map(str, self.bits_tdi))
228+
h = ' (0x%X' % int('0b' + b, 2) + ')'
229+
s = t + ': ' + h + ', ' + str(len(self.bits_tdi)) + ' bits' #b +
230+
self.putx_bs([18, [s]])
231+
self.bits_samplenums_tdi[0][1] = self.samplenum # ES of last bit.
232+
self.putp_bs([t, [b, self.bits_samplenums_tdi]])
233+
self.putx([16, [str(self.bits_tdi[0])]]) # Last bit.
234+
self.bits_tdi = []
235+
self.bits_samplenums_tdi = []
236+
237+
t = self.state[-2:] + ' TDO'
238+
b = ''.join(map(str, self.bits_tdo))
239+
h = ' (0x%X' % int('0b' + b, 2) + ')'
240+
s = t + ': ' + h + ', ' + str(len(self.bits_tdo)) + ' bits' #+ b
241+
self.putx_bs([19, [s]])
242+
self.bits_samplenums_tdo[0][1] = self.samplenum # ES of last bit.
243+
self.putp_bs([t, [b, self.bits_samplenums_tdo]])
244+
self.putx([17, [str(self.bits_tdo[0])]]) # Last bit.
245+
self.bits_tdo = []
246+
self.bits_samplenums_tdo = []
247+
248+
249+
def handle_falling_tck_edge(self, tck, tms):
250+
251+
if self.phase == 'nTDI':
252+
next_phase = 'TMS'
253+
elif self.phase == 'TMS':
254+
next_phase = 'TDO'
255+
elif self.phase == 'TDO':
256+
next_phase = 'nTDI'
257+
258+
if self.last_clock_samplenum is not None:
259+
self.put(self.last_clock_samplenum, self.samplenum, self.out_ann, [0+oscan1_phases.index(self.phase), [self.phase]])
260+
261+
self.last_clock_samplenum = self.samplenum
262+
self.phase = next_phase
263+
264+
def decode(self, ss, es, logic):
265+
for (self.samplenum, pins) in logic:
266+
logic.logic_mask = 0b11
267+
logic.cur_pos = self.samplenum
268+
logic.edge_index = -1
269+
270+
if self.last_clock_samplenum is None:
271+
self.last_clock_samplenum = self.samplenum
272+
elif self.last_clock_samplenum >= self.samplenum:
273+
continue
274+
275+
# If none of the pins changed, there's nothing to do.
276+
if self.oldpins == pins:
277+
continue
278+
279+
# Store current pin values for the next round.
280+
self.oldpins = pins
281+
282+
# Get individual pin values into local variables.
283+
# Unused channels will have a value of > 1.
284+
( tck, tms) = pins
285+
286+
# We only care about TCK edges (either rising or falling).
287+
if (self.oldtck == tck):
288+
continue
289+
290+
# Store start/end sample for later usage.
291+
self.ss, self.es = ss, es
292+
293+
if (self.oldtck == 0 and tck == 1):
294+
self.handle_rising_tck_edge( tck, tms)
295+
elif (self.oldtck == 1 and tck == 0):
296+
self.handle_falling_tck_edge( tck, tms)
297+
298+
self.oldtck = tck

0 commit comments

Comments
 (0)