lm4flash.c 12.4 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>
RickKimball's avatar
RickKimball committed
24
#include <ctype.h>
Fabio Utzig's avatar
Fabio Utzig committed
25
26
27
28
29

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

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

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

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

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

Fabio Utzig's avatar
Fabio Utzig committed
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 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
55
56

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

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

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

66
67
68
69
70
71
72
73
74
75
76
77
#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)
78

79
#define FLASH_BLOCK_SIZE 512
80

81
82
83
84
85
86
87
88
89
90
91
/* 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)
92
{
93
94
95
96
97
98
99
100
101
	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;
102
103
}

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

106
107
#define cpu_to_le32 le32_to_cpu

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

#ifdef DEBUG
RickKimball's avatar
RickKimball committed
114
	{
115
	int i, col;
RickKimball's avatar
RickKimball committed
116
	char ascbuf[32];
117

RickKimball's avatar
RickKimball committed
118
	printf("send_command: size=%d\n",size);
Fabio Utzig's avatar
Fabio Utzig committed
119
	printf("buffer:\n");
RickKimball's avatar
RickKimball committed
120
	memset(ascbuf,0,sizeof(ascbuf));
Fabio Utzig's avatar
Fabio Utzig committed
121
	for (i = 0, col = 1; i < size; i++, col++) {
122
		printf("%02x ", buf.u8[i]);
RickKimball's avatar
RickKimball committed
123
124
125
126
127
128
129
130
131
		ascbuf[i % 16] = isprint(buf.u8[i]) ? buf.u8[i] : '.';

		if (col == 16) { col = 0; printf("%s\n",ascbuf); memset(ascbuf,0,sizeof ascbuf); }
	}

	while(col++ < 17) {
		printf("   ");
	}
	printf("%s\n",ascbuf);
Fabio Utzig's avatar
Fabio Utzig committed
132
	}
133
134
#endif

135
	retval = libusb_bulk_transfer(handle, ENDPOINT_OUT, buf.u8, size, &transferred, 0);
136
137
138
139
140
141
142
143
144
145
146
	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;

147
	retval = libusb_bulk_transfer(handle, ENDPOINT_IN, buf.u8, BUF_SIZE, size, 0);
148
149
150
151
152
	if (retval != 0) {
		printf("Error receiving data %d\n", retval);
	}

#ifdef DEBUG
RickKimball's avatar
RickKimball committed
153
154
155
	{
	int i;

156
157
158
	printf("wait_response: size=%d\n", *size);
	printf("buffer: ");
	for (i = 0; i < *size; i++)
159
		printf("0x%02x ", buf.u8[i]);
RickKimball's avatar
RickKimball committed
160
161
162
163
164
	putchar('\'');
	for (i = 0; i < *size; i++)
		putchar(isprint(buf.u8[i]) ? buf.u8[i] : '.' );
	printf("'\n");
	}
165
166
167
168
169
#endif

	return retval;
}

Fabio Utzig's avatar
Fabio Utzig committed
170
static int checksum_and_send(libusb_device_handle *handle, size_t idx, int *xfer)
171
{
172
173
174
	size_t i;
	uint8_t sum = 0;
	int retval, transfered;
175

176
177
	if (idx + SNPRINTF_OFFSET + END_LEN > BUF_SIZE)
		return LIBUSB_ERROR_NO_MEM;
178

179
180
	for (i = 1; i < idx; i++)
		sum += buf.u8[i];
181

182
	idx += sprintf(buf.c + idx, END "%02x", sum);
183
184

	retval = send_command(handle, idx);
185
186
	if (retval)
		return retval;
187

188
189
190
191
	/* wait for ack (+/-) */
	retval = wait_response(handle, &transfered);
	if (retval)
		return retval;
192

193
194
	if (transfered != 1 || buf.c[0] != '+')
		return LIBUSB_ERROR_OTHER;
195

196
197
	/* wait for command response */
	retval = wait_response(handle, &transfered);
Fabio Utzig's avatar
Fabio Utzig committed
198
199
	if (xfer)
		*xfer = transfered;
200

201
	/* FIXME: validate transfered here? */
202
203
204
205

	return retval;
}

Fabio Utzig's avatar
Fabio Utzig committed
206

207
208
209
static int send_u8_hex(libusb_device_handle *handle, const char *prefix, const char *bytes, size_t num_bytes)
{
	size_t i, idx;
210

211
212
213
214
215
	/* 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;
216

217
	idx = sprintf(buf.c, START "%s", prefix);
218

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

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

225
static int send_u8_binary(libusb_device_handle *handle, const char *prefix, const char *bytes, size_t num_bytes)
226
227
228
{
	size_t idx;

229
230
231
232
233
	/* 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;
234

235
	idx = sprintf(buf.c, START "%s", prefix);
236

237
238
	memcpy(buf.c + idx, bytes, num_bytes);
	idx += num_bytes;
239

Fabio Utzig's avatar
Fabio Utzig committed
240
	return checksum_and_send(handle, idx, NULL);
241
242
}

243
static int send_u32(libusb_device_handle *handle, const char *prefix, const uint32_t val, const char *suffix)
244
{
245
246
247
	size_t idx = snprintf(buf.c, BUF_SIZE, START "%s%08x%s",
			prefix ? prefix : "", val,
			suffix ? suffix : "");
248

Fabio Utzig's avatar
Fabio Utzig committed
249
	return checksum_and_send(handle, idx, NULL);
250
251
}

252
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)
253
{
254
255
256
257
	size_t idx = snprintf(buf.c, BUF_SIZE, START "%s%08x%s%08x%s",
			prefix ? prefix : "", val1,
			infix ? infix : "", val2,
			suffix ? suffix : "");
258

Fabio Utzig's avatar
Fabio Utzig committed
259
	return checksum_and_send(handle, idx, NULL);
260
}
261
262


263
264
265
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);
266
267
}

268
static int send_mem_read(libusb_device_handle *handle, const uint32_t addr, uint32_t *val)
Fabio Utzig's avatar
Fabio Utzig committed
269
{
270
271
272
	int retval = send_u32(handle, "x", addr, ",4");
	if (retval)
		return retval;
Fabio Utzig's avatar
Fabio Utzig committed
273

274
275
	if (val)
		*val = le32_to_cpu(buf.u32[0]);
Fabio Utzig's avatar
Fabio Utzig committed
276

277
278
	return 0;
}
Fabio Utzig's avatar
Fabio Utzig committed
279

280
281
282
283
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
284

285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
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
307
308
		}

309
	i = buf - rawbuf;
Fabio Utzig's avatar
Fabio Utzig committed
310

311
	return send_u8_binary(handle, prefix, rawbuf, i) ? -1 : i;
Fabio Utzig's avatar
Fabio Utzig committed
312
313
}

Fabio Utzig's avatar
Fabio Utzig committed
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
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;
}

351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
#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
387
388
389
390
391
/*
 *  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
 */
392
static int write_firmware(libusb_device_handle *handle, FILE *f)
393
394
{
	uint32_t val = 0;
Fabio Utzig's avatar
Fabio Utzig committed
395
	uint32_t addr;
396
397
398
399
400
	size_t rdbytes;

	SEND_COMMAND("debug clock \0");
	SEND_STRING("qSupported");
	SEND_STRING("?");
Fabio Utzig's avatar
Fabio Utzig committed
401
402
403
	MEM_WRITE(FP_CTRL, 0x3000000);
	MEM_READ(DID0, &val);
	MEM_READ(DID1, &val);
404
	SEND_STRING("?");
Fabio Utzig's avatar
Fabio Utzig committed
405
	MEM_READ(DHCSR, &val);
406
	SEND_COMMAND("debug sreset");
Fabio Utzig's avatar
Fabio Utzig committed
407
	MEM_READ(DHCSR, &val);
408
409
	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
410
411
412
413
414
415
416
	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
417
418

	/* XXX: Repeated below, why? */
419
	MEM_WRITE(FMA, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
420
	MEM_READ(DHCSR, &val);
421
422
	FLASH_ERASE(0, 0);
	SEND_COMMAND("debug creset");
Fabio Utzig's avatar
Fabio Utzig committed
423
	MEM_READ(DHCSR, &val);
Fabio Utzig's avatar
Fabio Utzig committed
424

Fabio Utzig's avatar
Fabio Utzig committed
425
	MEM_WRITE(DHCSR, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
426
427

	/* XXX: this is the same sequence of the above commands? */
428
	MEM_WRITE(FMA, 0x200);
Fabio Utzig's avatar
Fabio Utzig committed
429
	MEM_READ(DHCSR, &val);
430
431
	FLASH_ERASE(0, 0);
	SEND_COMMAND("debug creset");
Fabio Utzig's avatar
Fabio Utzig committed
432
	MEM_READ(DHCSR, &val);
433
434
435

	MEM_READ(ROMCTL, &val);
	MEM_WRITE(ROMCTL, 0x0);
Fabio Utzig's avatar
Fabio Utzig committed
436
	MEM_READ(DHCSR, &val);
437
438
439
440
441
442
443
444
445
446
447
448

	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
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
	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;
		}
	}

466
467
468
469
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

	/* reset board */
Fabio Utzig's avatar
Fabio Utzig committed
470
	MEM_WRITE(FP_CTRL, 0x3000000);
471
472
473
474
475
	SEND_COMMAND("debug hreset");
	SEND_COMMAND("set vectorcatch 0");
	SEND_COMMAND("debug disable");

	return 0;
476
}
Fabio Utzig's avatar
Fabio Utzig committed
477
478
479

int main(int argc, char *argv[])
{
480
481
482
	libusb_context *ctx = NULL;
	libusb_device_handle *handle = NULL;
	int retval = 1;
483
	FILE *f = NULL;
Fabio Utzig's avatar
Fabio Utzig committed
484
485

	if (argc < 2) {
Fabio Utzig's avatar
Fabio Utzig committed
486
487
		printf("usage: %s [-v] <binary-file>\n", argv[0]);
		printf("\t-v : enables verification after write\n");
488
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
489
	}
Fabio Utzig's avatar
Fabio Utzig committed
490

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

Fabio Utzig's avatar
Fabio Utzig committed
494
495
	if (libusb_init(&ctx) != 0) {
		fprintf(stderr, "Error initializing libusb\n");
496
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
497
498
	}

499
	/* FIXME: should not be using this function call! */
500
	handle = libusb_open_device_with_vid_pid(ctx, ICDI_VID, ICDI_PID);
Fabio Utzig's avatar
Fabio Utzig committed
501
	if (!handle) {
502
503
		fprintf(stderr, "No ICDI device with USB VID:PID %04x:%04x found!\n",
				ICDI_VID, ICDI_PID);
504
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
505
506
	}

507
	retval = libusb_claim_interface(handle, INTERFACE_NR);
Fabio Utzig's avatar
Fabio Utzig committed
508
509
	if (retval != 0) {
		printf("Error claiming interface %d\n", retval);
510
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
511
512
	}

Fabio Utzig's avatar
Fabio Utzig committed
513
	f = fopen(argv[argc - 1], "rb");
514
515
	if (!f) {
		perror("fopen");
516
517
		retval = 1;
		goto done;
Fabio Utzig's avatar
Fabio Utzig committed
518
519
	}

520
	retval = write_firmware(handle, f);
Fabio Utzig's avatar
Fabio Utzig committed
521

522
done:
523
524
	if (f)
		fclose(f);
525
526
527
528
529
530
	if (handle)
		libusb_close(handle);
	if (ctx)
		libusb_exit(ctx);

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