aboutsummaryrefslogtreecommitdiff
path: root/src/jtag_scan.c
blob: f46a14819a063793d9b7a273e237fb1327dc33ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/*
 * This file is part of the Black Magic Debug project.
 *
 * Copyright (C) 2011  Black Sphere Technologies Ltd.
 * Written by Gareth McMullin <gareth@blacksphere.co.nz>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/* This file implements JTAG protocol support.  Provides functionality
 * to detect devices on the scan chain and read their IDCODEs.
 * It depends on the low-level function provided by the platform's jtagtap.c.
 */

#include "general.h"
#include "jtagtap.h"
#include "morse.h"
#include "jtag_scan.h"
#include "gdb_packet.h"
#include "adiv5.h"
#include "arm7tdmi.h"

struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1];
int jtag_dev_count;

static struct jtag_dev_descr_s {
	uint32_t idcode;
	uint32_t idmask;
	char *descr;
	void (*handler)(jtag_dev_t *dev);
} dev_descr[] = {
	{.idcode = 0x0BA00477, .idmask = 0x0FFF0FFF,
		.descr = "ARM Limited: ADIv5 JTAG-DP port.",
		.handler = adiv5_jtag_dp_handler},
	{.idcode = 0x06410041, .idmask = 0x0FFFFFFF,
		.descr = "ST Microelectronics: STM32, Medium density."},
	{.idcode = 0x06412041, .idmask = 0x0FFFFFFF,
		.descr = "ST Microelectronics: STM32, Low density."},
	{.idcode = 0x06414041, .idmask = 0x0FFFFFFF,
		.descr = "ST Microelectronics: STM32, High density."},
	{.idcode = 0x06416041, .idmask = 0x0FFFFFFF,
		.descr = "ST Microelectronics: STM32L."},
	{.idcode = 0x06418041, .idmask = 0x0FFFFFFF,
		.descr = "ST Microelectronics: STM32, Connectivity Line."},
	{.idcode = 0x06420041, .idmask = 0x0FFFFFFF,
		.descr = "ST Microelectronics: STM32, Value Line."},
	{.idcode = 0x06428041, .idmask = 0x0FFFFFFF,
		.descr = "ST Microelectronics: STM32, Value Line, High density."},
	{.idcode = 0x06411041, .idmask = 0xFFFFFFFF,
		.descr = "ST Microelectronics: STM32F2xx."},
	{.idcode = 0x06413041 , .idmask = 0xFFFFFFFF,
		.descr = "ST Microelectronics: STM32F4xx."},
	{.idcode = 0x0BB11477 , .idmask = 0xFFFFFFFF,
		.descr = "NPX: LPC11C24."},
/* Just for fun, unsupported */
	{.idcode = 0x8940303F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: ATMega16."},
	{.idcode = 0x0792603F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: AT91SAM9261."},
	{.idcode = 0x20270013, .idmask = 0xFFFFFFFF, .descr = "Intel: i80386ex."},
	{.idcode = 0, .idmask = 0, .descr = "Unknown"},
};

/* bucket of ones for don't care TDI */
static const uint8_t ones[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";

/* Scan JTAG chain for devices, store IR length and IDCODE (if present).
 * Reset TAP state machine.
 * Select Shift-IR state.
 * Each device is assumed to shift out IR at 0x01. (this may not always be true)
 * Shift in ones until we read two consecutive ones, then we have shifted out the
 * 	IRs of all devices.
 *
 * After this process all the IRs are loaded with the BYPASS command.
 * Select Shift-DR state.
 * Shift in ones and count zeros shifted out. Should be one for each device.
 * Check this against device count obtained by IR scan above.
 *
 * Reset the TAP state machine again. This should load all IRs with IDCODE.
 * For each device, shift out one bit. If this is zero IDCODE isn't present,
 *	continue to next device. If this is one shift out the remaining 31 bits
 *	of the IDCODE register.
 */
int jtag_scan(const uint8_t *irlens)
{
	int i;
	uint32_t j;

	target_list_free();

	jtag_dev_count = 0;
	memset(&jtag_devs, 0, sizeof(jtag_devs));

	/* Run throught the SWD to JTAG sequence for the case where an attached SWJ-DP is
	 * in SW-DP mode.
	 */
	DEBUG("Resetting TAP\n");
	jtagtap_init();
	if(connect_assert_srst)
		jtagtap_srst(true); /* will be deasserted after attach */
	jtagtap_reset();

	if (irlens) {
		DEBUG("Given list of IR lengths, skipping probe\n");
		DEBUG("Change state to Shift-IR\n");
		jtagtap_shift_ir();
		j = 0;
		while((jtag_dev_count <= JTAG_MAX_DEVS) &&
		      (jtag_devs[jtag_dev_count].ir_len <= JTAG_MAX_IR_LEN)) {
			uint32_t irout;
			if(*irlens == 0)
				break;
			jtagtap_tdi_tdo_seq((uint8_t*)&irout, 0, ones, *irlens);
			if (!(irout & 1)) {
				DEBUG("check failed: IR[0] != 1\n");
				return -1;
			}
			jtag_devs[jtag_dev_count].ir_len = *irlens;
			jtag_devs[jtag_dev_count].ir_prescan = j;
			jtag_devs[jtag_dev_count].dev = jtag_dev_count;
			j += *irlens;
			irlens++;
			jtag_dev_count++;
		}
	} else {
		DEBUG("Change state to Shift-IR\n");
		jtagtap_shift_ir();

		DEBUG("Scanning out IRs\n");
		if(!jtagtap_next(0, 1)) {
			DEBUG("jtag_scan: Sanity check failed: IR[0] shifted out as 0\n");
			jtag_dev_count = -1;
			return -1; /* must be 1 */
		}
		jtag_devs[0].ir_len = 1; j = 1;
		while((jtag_dev_count <= JTAG_MAX_DEVS) &&
		      (jtag_devs[jtag_dev_count].ir_len <= JTAG_MAX_IR_LEN)) {
			if(jtagtap_next(0, 1)) {
				if(jtag_devs[jtag_dev_count].ir_len == 1) break;
				jtag_devs[++jtag_dev_count].ir_len = 1;
				jtag_devs[jtag_dev_count].ir_prescan = j;
				jtag_devs[jtag_dev_count].dev = jtag_dev_count;
			} else jtag_devs[jtag_dev_count].ir_len++;
			j++;
		}
		if(jtag_dev_count > JTAG_MAX_DEVS) {
			DEBUG("jtag_scan: Maximum device count exceeded\n");
			jtag_dev_count = -1;
			return -1;
		}
		if(jtag_devs[jtag_dev_count].ir_len > JTAG_MAX_IR_LEN) {
			DEBUG("jtag_scan: Maximum IR length exceeded\n");
			jtag_dev_count = -1;
			return -1;
		}
	}

	DEBUG("Return to Run-Test/Idle\n");
	jtagtap_next(1, 1);
	jtagtap_return_idle();

	/* All devices should be in BYPASS now */

	/* Count device on chain */
	DEBUG("Change state to Shift-DR\n");
	jtagtap_shift_dr();
	for(i = 0; (jtagtap_next(0, 1) == 0) && (i <= jtag_dev_count); i++)
		jtag_devs[i].dr_postscan = jtag_dev_count - i - 1;

	if(i != jtag_dev_count) {
		DEBUG("jtag_scan: Sanity check failed: "
			"BYPASS dev count doesn't match IR scan\n");
		jtag_dev_count = -1;
		return -1;
	}

	DEBUG("Return to Run-Test/Idle\n");
	jtagtap_next(1, 1);
	jtagtap_return_idle();
	if(!jtag_dev_count) {
		morse("NO TARGETS.", 1);
		return 0;
	}

	/* Fill in the ir_postscan fields */
	for(i = jtag_dev_count - 1; i; i--)
		jtag_devs[i-1].ir_postscan = jtag_devs[i].ir_postscan +
					jtag_devs[i].ir_len;

	/* Reset jtagtap: should take all devs to IDCODE */
	jtagtap_reset();
	jtagtap_shift_dr();
	for(i = 0; i < jtag_dev_count; i++) {
		if(!jtagtap_next(0, 1)) continue;
		jtag_devs[i].idcode = 1;
		for(j = 2; j; j <<= 1)
			if(jtagtap_next(0, 1)) jtag_devs[i].idcode |= j;

	}
	DEBUG("Return to Run-Test/Idle\n");
	jtagtap_next(1, 1);
	jtagtap_return_idle();

	/* Check for known devices and handle accordingly */
	for(i = 0; i < jtag_dev_count; i++)
		for(j = 0; dev_descr[j].idcode; j++)
			if((jtag_devs[i].idcode & dev_descr[j].idmask) ==
			   dev_descr[j].idcode) {
				jtag_devs[i].current_ir = -1;
				/* Save description in table */
				jtag_devs[i].descr = dev_descr[j].descr;
				/* Call handler to initialise/probe device further */
				if(dev_descr[j].handler)
					dev_descr[j].handler(&jtag_devs[i]);
				break;
			}

	if(!target_list) morse("NO TARGETS.", 1);
	else morse(NULL, 0);

	return jtag_dev_count;
}

void jtag_dev_write_ir(jtag_dev_t *d, uint32_t ir)
{
	if(ir == d->current_ir) return;
	for(int i = 0; i < jtag_dev_count; i++)
		jtag_devs[i].current_ir = -1;
	d->current_ir = ir;

	jtagtap_shift_ir();
	jtagtap_tdi_seq(0, ones, d->ir_prescan);
	jtagtap_tdi_seq(d->ir_postscan?0:1, (void*)&ir, d->ir_len);
	jtagtap_tdi_seq(1, ones, d->ir_postscan);
	jtagtap_return_idle();
}

void jtag_dev_shift_dr(jtag_dev_t *d, uint8_t *dout, const uint8_t *din, int ticks)
{
	jtagtap_shift_dr();
	jtagtap_tdi_seq(0, ones, d->dr_prescan);
	if(dout)
		jtagtap_tdi_tdo_seq((void*)dout, d->dr_postscan?0:1, (void*)din, ticks);
	else
		jtagtap_tdi_seq(d->dr_postscan?0:1, (void*)din, ticks);
	jtagtap_tdi_seq(1, ones, d->dr_postscan);
	jtagtap_return_idle();
}