|
| 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