lm4flash.c 12.1 KB
Newer Older
Fabio Utzig's avatar
Fabio Utzig committed
1
/* lm4flash - TI Stellaris Launchpad ICDI flasher
Fabio Utzig's avatar
Fabio Utzig committed
2
 * Copyright (C) 2012 Fabio Utzig <fabio@utzig.net>
3
 * Copyright (C) 2012 Peter Stuge <peter@stuge.se>
Fabio Utzig's avatar
Fabio Utzig committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

Fabio Utzig's avatar
Fabio Utzig committed
20
21
#include <stdio.h>
#include <stdlib.h>
22
23
#include <stdint.h>
#include <string.h>
Fabio Utzig's avatar
Fabio Utzig committed
24
25
26
27
28

#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

Fabio Utzig's avatar
Fabio Utzig committed
29
#include <libusb.h>
Fabio Utzig's avatar
Fabio Utzig committed
30

Fabio Utzig's avatar
Fabio Utzig committed
31
//#define DEBUG 1
32

33
34
35
#define ICDI_VID 0x1cbe
#define ICDI_PID 0x00fd

Fabio Utzig's avatar
Fabio Utzig committed
36
37
// FlashPatch Control Register: see ARM Av7mRM C1.11.3
static const uint32_t FP_CTRL  = 0xe0002000;
38

Fabio Utzig's avatar
Fabio Utzig committed
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Debug Halting Control and Status Register: see ARM Av7mRM C1.6.2
static const uint32_t DHCSR    = 0xe000edf0;

// Device Identification: see Stellaris LM4F120H5QR Microcontroller Section 5.5
static const uint32_t DID0     = 0x400fe000;
static const uint32_t DID1     = 0x400fe004;

// Device Identification: see Stellaris LM4F120H5QR Microcontroller Section 5.5
static const uint32_t DC0      = 0x400fe008;

// Run-Mode Clock Configuration: Stellaris LM4F120H5QR Microcontroller Section 5.5
static const uint32_t RCC      = 0x400fe060;

// Non-Volatile Memory Information: Stellaris LM4F120H5QR Microcontroller Section 5.6
static const uint32_t NVMSTAT  = 0x400fe1a0;
Fabio Utzig's avatar
Fabio Utzig committed
54
55

// Rom Control: see Stellaris LM4F120H5QR Microcontroller Page 531
Fabio Utzig's avatar
Fabio Utzig committed
56
static const uint32_t ROMCTL   = 0x400fe0f0;
Fabio Utzig's avatar
Fabio Utzig committed
57
58

// Flash Memory Address: see Stellaris LM4F120H5QR Microcontroller Page 497
Fabio Utzig's avatar
Fabio Utzig committed
59
static const uint32_t FMA      = 0x400fd000;
60
61
62
63
64

static const uint8_t INTERFACE_NR = 0x02;
static const uint8_t ENDPOINT_IN  = 0x83;
static const uint8_t ENDPOINT_OUT = 0x02;

65
66
67
68
69
70
71
72
73
74
75
76
#define START "$"
#define END "#"

#ifdef WIN32
#define snprintf _snprintf
#define SNPRINTF_OFFSET 1
#else
#define SNPRINTF_OFFSET 0
#endif

#define START_LEN strlen(START)
#define END_LEN (strlen(END) + 2)
77

78
#define FLASH_BLOCK_SIZE 512
79

80
81
82
83
84
85
86
87
88
89
90
/* Prefix + potentially every flash byte escaped */
#define BUF_SIZE 64 + 2*FLASH_BLOCK_SIZE

static uint8_t flash_block[FLASH_BLOCK_SIZE];
static union {
	char c[BUF_SIZE];
	uint8_t u8[BUF_SIZE];
	uint32_t u32[BUF_SIZE / 4];
} buf;

static uint32_t le32_to_cpu(const uint32_t x)
91
{
92
93
94
95
96
97
98
99
100
	union {
		uint8_t  b8[4];
		uint32_t b32;
	} _tmp;
	_tmp.b8[3] = x >> 24;
	_tmp.b8[2] = x >> 16;
	_tmp.b8[1] = x >> 8;
	_tmp.b8[0] = x & 0xff;
	return _tmp.b32;
101
102
}

Fabio Utzig's avatar
Fabio Utzig committed
103
104
static int do_verify = 0;

105
106
#define cpu_to_le32 le32_to_cpu

107
108
109
110
111
112
static int send_command(libusb_device_handle *handle, int size)
{
	int transferred = 0;
	int retval;

#ifdef DEBUG
113
114
	int i, col;

Fabio Utzig's avatar
Fabio Utzig committed
115
116
	printf("buffer:\n");
	for (i = 0, col = 1; i < size; i++, col++) {
117
		printf("%02x ", buf.u8[i]);
Fabio Utzig's avatar
Fabio Utzig committed
118
119
120
		if (col == 16) { col = 0; printf("\n"); }
	}
	printf("\n");
121
122
#endif

123
	retval = libusb_bulk_transfer(handle, ENDPOINT_OUT, buf.u8, size, &transferred, 0);
124
125
126
127
128
129
130
131
132
133
	if (retval != 0 || size != transferred) {
		printf("Error transmitting data %d\n", retval);
	}

	return retval;
}

static int wait_response(libusb_device_handle *handle, int *size)
{
	int retval;
134
#ifdef DEBUG
135
	int i;
136
#endif
137

138
	retval = libusb_bulk_transfer(handle, ENDPOINT_IN, buf.u8, BUF_SIZE, size, 0);
139
140
141
142
143
144
145
146
	if (retval != 0) {
		printf("Error receiving data %d\n", retval);
	}

#ifdef DEBUG
	printf("wait_response: size=%d\n", *size);
	printf("buffer: ");
	for (i = 0; i < *size; i++)
147
		printf("0x%02x ", buf.u8[i]);
148
149
150
151
152
153
	printf("\n");
#endif

	return retval;
}

Fabio Utzig's avatar
Fabio Utzig committed
154
static int checksum_and_send(libusb_device_handle *handle, size_t idx, int *xfer)
155
{
156
157
158
	size_t i;
	uint8_t sum = 0;
	int retval, transfered;
159

160
161
	if (idx + SNPRINTF_OFFSET + END_LEN > BUF_SIZE)
		return LIBUSB_ERROR_NO_MEM;
162

163
164
	for (i = 1; i < idx; i++)
		sum += buf.u8[i];
165

166
	idx += sprintf(buf.c + idx, END "%02x", sum);
167
168

	retval = send_command(handle, idx);
169
170
	if (retval)
		return retval;
171

172
173
174
175
	/* wait for ack (+/-) */
	retval = wait_response(handle, &transfered);
	if (retval)
		return retval;
176

177
178
	if (transfered != 1 || buf.c[0] != '+')
		return LIBUSB_ERROR_OTHER;
179

180
181
	/* wait for command response */
	retval = wait_response(handle, &transfered);
Fabio Utzig's avatar
Fabio Utzig committed
182
183
	if (xfer)
		*xfer = transfered;
184

185
	/* FIXME: validate transfered here? */
186
187
188
189

	return retval;
}

Fabio Utzig's avatar
Fabio Utzig committed
190

191
192
193
static int send_u8_hex(libusb_device_handle *handle, const char *prefix, const char *bytes, size_t num_bytes)
{
	size_t i, idx;
194

195
196
197
198
199
	/* Make sure that everything fits!
	 * START + prefix + hex bytes + END + hex checksum + '\0'
	 */
	if (START_LEN + (prefix ? strlen(prefix) : 0) + (2 * num_bytes) + END_LEN + 1 > BUF_SIZE)
		return LIBUSB_ERROR_NO_MEM;
200

201
	idx = sprintf(buf.c, START "%s", prefix);
202

203
204
	for (i = 0; bytes && i < num_bytes; i++)
		idx += sprintf(buf.c + idx, "%02x", bytes[i]);
205

Fabio Utzig's avatar
Fabio Utzig committed
206
	return checksum_and_send(handle, idx, NULL);
207
208
}

209
static int send_u8_binary(libusb_device_handle *handle, const char *prefix, const char *bytes, size_t num_bytes)
210
211
212
{
	size_t idx;

213
214
215
216
217
	/* Make sure that everything fits!
	 * START + prefix + bytes + END + hex checksum + '\0'
	 */
	if (START_LEN + (prefix ? strlen(prefix) : 0) + num_bytes + END_LEN + 1 > BUF_SIZE)
		return LIBUSB_ERROR_NO_MEM;
218

219
	idx = sprintf(buf.c, START "%s", prefix);
220

221
222
	memcpy(buf.c + idx, bytes, num_bytes);
	idx += num_bytes;
223

Fabio Utzig's avatar
Fabio Utzig committed
224
	return checksum_and_send(handle, idx, NULL);
225
226
}

227
static int send_u32(libusb_device_handle *handle, const char *prefix, const uint32_t val, const char *suffix)
228
{
229
230
231
	size_t idx = snprintf(buf.c, BUF_SIZE, START "%s%08x%s",
			prefix ? prefix : "", val,
			suffix ? suffix : "");
232

Fabio Utzig's avatar
Fabio Utzig committed
233
	return checksum_and_send(handle, idx, NULL);
234
235
}

236
static int send_u32_u32(libusb_device_handle *handle, const char *prefix, const uint32_t val1, const char *infix, const uint32_t val2, const char *suffix)
237
{
238
239
240
241
	size_t idx = snprintf(buf.c, BUF_SIZE, START "%s%08x%s%08x%s",
			prefix ? prefix : "", val1,
			infix ? infix : "", val2,
			suffix ? suffix : "");
242

Fabio Utzig's avatar
Fabio Utzig committed
243
	return checksum_and_send(handle, idx, NULL);
244
}
245
246


247
248
249
static int send_mem_write(libusb_device_handle *handle, const uint32_t addr, const uint32_t val)
{
	return send_u32_u32(handle, "X", addr, ",4:", val, NULL);
250
251
}

252
static int send_mem_read(libusb_device_handle *handle, const uint32_t addr, uint32_t *val)
Fabio Utzig's avatar
Fabio Utzig committed
253
{
254
255
256
	int retval = send_u32(handle, "x", addr, ",4");
	if (retval)
		return retval;
Fabio Utzig's avatar
Fabio Utzig committed
257

258
259
	if (val)
		*val = le32_to_cpu(buf.u32[0]);
Fabio Utzig's avatar
Fabio Utzig committed
260

261
262
	return 0;
}
Fabio Utzig's avatar
Fabio Utzig committed
263

264
265
266
267
static int send_flash_erase(libusb_device_handle *handle, const uint32_t start, const uint32_t end)
{
	return send_u32_u32(handle, "vFlashErase:", start, ",", end, NULL);
}
Fabio Utzig's avatar
Fabio Utzig committed
268

269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
static int send_flash_write(libusb_device_handle *handle, const uint32_t addr, const uint8_t *bytes, size_t len)
{
	size_t i;
	char prefix[] = "vFlashWrite:12345678:";
	char by, rawbuf[1024], *buf = rawbuf;

	sprintf(strchr(prefix, ':') + 1, "%08x:", addr);

	for (i = 0; i < len; i++)
		switch (by = bytes[i]) {
		case '#':
		case '$':
		case '}':
			*buf++ = '}';
			by ^= 0x20;
			/* fall through */
		default:
			if (buf >= rawbuf + sizeof(rawbuf))
				return LIBUSB_ERROR_NO_MEM;

			*buf++ = by;
			break;
Fabio Utzig's avatar
Fabio Utzig committed
291
292
		}

293
	i = buf - rawbuf;
Fabio Utzig's avatar
Fabio Utzig committed
294

295
	return send_u8_binary(handle, prefix, rawbuf, i) ? -1 : i;
Fabio Utzig's avatar
Fabio Utzig committed
296
297
}

Fabio Utzig's avatar
Fabio Utzig committed
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
static int send_flash_verify(libusb_device_handle *handle, const uint32_t addr, const uint8_t *bytes, size_t len)
{
	size_t i, j;
	char by, rawbuf[1024], *bp = rawbuf;
	int retval, transfered;

	size_t idx = snprintf(buf.c, BUF_SIZE, START "x%x,%x", addr, (uint32_t)len);

	retval = checksum_and_send(handle, idx, &transfered);

	for (i = 0; i < transfered; i++) {
		switch (by = buf.u8[i]) {
		case '}':
			by = buf.u8[++i] ^ 0x20;
			/* fall through */
		default:
			if (bp >= rawbuf + sizeof(rawbuf))
				return LIBUSB_ERROR_NO_MEM;

			*bp++ = by;
			break;
		}
	}

	if (strncmp(rawbuf, "$OK:", 4) != 0)
		return LIBUSB_ERROR_OTHER;

	for (i = 0, j = strlen("$OK:"); i < len; i++, j++) {
		if (bytes[i] != (uint8_t)rawbuf[j]) {
			printf("Error verifying flash\n");
			return LIBUSB_ERROR_OTHER;
		}
	}

	return 0;
}

335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
#define SEND_COMMAND(cmd) do { \
	int r = send_u8_hex(handle, "qRcmd,", (cmd), sizeof((cmd)) - 1); \
	if (r) \
		return r; \
} while (0)

#define SEND_STRING(str) do { \
	int r = send_u8_hex(handle, (str), NULL, 0); \
	if (r) \
		return r; \
} while (0)

#define MEM_WRITE(address, value) do { \
	int r = send_mem_write(handle, (address), (value)); \
	if (r) \
		return r; \
} while (0)

#define MEM_READ(address, value) do { \
	int r = send_mem_read(handle, (address), (value)); \
	if (r) \
		return r; \
} while (0)

#define FLASH_ERASE(start, end) do { \
	int r = send_flash_erase(handle, (start), (end)); \
	if (r) \
		return r; \
} while (0)

#define FLASH_WRITE(address, data, length) do { \
	int r = send_flash_write(handle, (address), (data), (length)); \
	if (r < (length)) \
		return LIBUSB_ERROR_OTHER; \
} while (0)

Fabio Utzig's avatar
Fabio Utzig committed
371
372
373
374
375
/*
 *  This flow is of commands is based on an USB capture of
 *  traffic between LM Flash Programmer and the Stellaris Launchpad
 *  when doing a firmware write
 */
376
static int write_firmware(libusb_device_handle *handle, FILE *f)
377
378
{
	uint32_t val = 0;
Fabio Utzig's avatar
Fabio Utzig committed
379
	uint32_t addr;
380
381
382
383
384
	size_t rdbytes;

	SEND_COMMAND("debug clock \0");
	SEND_STRING("qSupported");
	SEND_STRING("?");
Fabio Utzig's avatar
Fabio Utzig committed
385
386
387
	MEM_WRITE(FP_CTRL, 0x3000000);
	MEM_READ(DID0, &val);
	MEM_READ(DID1, &val);
388
	SEND_STRING("?");
Fabio Utzig's avatar
Fabio Utzig committed
389
	MEM_READ(DHCSR, &val);
390
	SEND_COMMAND("debug sreset");
Fabio Utzig's avatar
Fabio Utzig committed
391
	MEM_READ(DHCSR, &val);
392
393
	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
394
395
396
397
398
399
400
	MEM_READ(DHCSR, &val);
	MEM_READ(RCC, &val);
	MEM_READ(DID0, &val);
	MEM_READ(DID1, &val);
	MEM_READ(DC0, &val);
	MEM_READ(DID0, &val);
	MEM_READ(NVMSTAT, &val);
Fabio Utzig's avatar
Fabio Utzig committed
401
402

	/* XXX: Repeated below, why? */
403
	MEM_WRITE(FMA, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
404
	MEM_READ(DHCSR, &val);
405
406
	FLASH_ERASE(0, 0);
	SEND_COMMAND("debug creset");
Fabio Utzig's avatar
Fabio Utzig committed
407
	MEM_READ(DHCSR, &val);
Fabio Utzig's avatar
Fabio Utzig committed
408

Fabio Utzig's avatar
Fabio Utzig committed
409
	MEM_WRITE(DHCSR, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
410
411

	/* XXX: this is the same sequence of the above commands? */
412
	MEM_WRITE(FMA, 0x200);
Fabio Utzig's avatar
Fabio Utzig committed
413
	MEM_READ(DHCSR, &val);
414
415
	FLASH_ERASE(0, 0);
	SEND_COMMAND("debug creset");
Fabio Utzig's avatar
Fabio Utzig committed
416
	MEM_READ(DHCSR, &val);
417
418
419

	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
420
	MEM_READ(DHCSR, &val);
421
422
423
424
425
426
427
428
429
430
431
432

	for (addr = 0; !feof(f); addr += sizeof(flash_block)) {
		rdbytes = fread(flash_block, 1, sizeof(flash_block), f);

		if (rdbytes < sizeof(flash_block) && !feof(f)) {
			perror("fread");
			return LIBUSB_ERROR_OTHER;
		}

		FLASH_WRITE(addr, flash_block, rdbytes);
	}

Fabio Utzig's avatar
Fabio Utzig committed
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
	if (do_verify) {
		fseek(f, 0, SEEK_SET);

		for (addr = 0; !feof(f); addr += sizeof(flash_block)) {
			rdbytes = fread(flash_block, 1, sizeof(flash_block), f);

			if (rdbytes < sizeof(flash_block) && !feof(f)) {
				perror("fread");
				return LIBUSB_ERROR_OTHER;
			}

			/* On error don't return immediately... finish resetting the board */
			if (send_flash_verify(handle, addr, flash_block, rdbytes) != 0)
				break;
		}
	}

450
451
452
453
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

	/* reset board */
Fabio Utzig's avatar
Fabio Utzig committed
454
	MEM_WRITE(FP_CTRL, 0x3000000);
455
456
457
458
459
	SEND_COMMAND("debug hreset");
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

	return 0;
460
}
Fabio Utzig's avatar
Fabio Utzig committed
461
462
463

int main(int argc, char *argv[])
{
464
465
466
	libusb_context *ctx = NULL;
	libusb_device_handle *handle = NULL;
	int retval = 1;
467
	FILE *f = NULL;
Fabio Utzig's avatar
Fabio Utzig committed
468
469

	if (argc < 2) {
Fabio Utzig's avatar
Fabio Utzig committed
470
471
		printf("usage: %s [-v] <binary-file>\n", argv[0]);
		printf("\t-v : enables verification after write\n");
472
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
473
	}
Fabio Utzig's avatar
Fabio Utzig committed
474

Fabio Utzig's avatar
Fabio Utzig committed
475
476
477
	if ((argc == 3) && (strncmp(argv[1], "-v", strlen("-v")) == 0))
		do_verify = 1;

Fabio Utzig's avatar
Fabio Utzig committed
478
479
	if (libusb_init(&ctx) != 0) {
		fprintf(stderr, "Error initializing libusb\n");
480
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
481
482
	}

483
	/* FIXME: should not be using this function call! */
484
	handle = libusb_open_device_with_vid_pid(ctx, ICDI_VID, ICDI_PID);
Fabio Utzig's avatar
Fabio Utzig committed
485
	if (!handle) {
486
487
		fprintf(stderr, "No ICDI device with USB VID:PID %04x:%04x found!\n",
				ICDI_VID, ICDI_PID);
488
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
489
490
	}

491
	retval = libusb_claim_interface(handle, INTERFACE_NR);
Fabio Utzig's avatar
Fabio Utzig committed
492
493
	if (retval != 0) {
		printf("Error claiming interface %d\n", retval);
494
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
495
496
	}

Fabio Utzig's avatar
Fabio Utzig committed
497
	f = fopen(argv[argc - 1], "rb");
498
499
	if (!f) {
		perror("fopen");
500
501
		retval = 1;
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
502
503
	}

504
	retval = write_firmware(handle, f);
Fabio Utzig's avatar
Fabio Utzig committed
505

506
done:
507
508
	if (f)
		fclose(f);
509
510
511
512
513
514
	if (handle)
		libusb_close(handle);
	if (ctx)
		libusb_exit(ctx);

	return retval;
Fabio Utzig's avatar
Fabio Utzig committed
515
}