1
0
mirror of git://projects.qi-hardware.com/wernermisc.git synced 2024-12-01 10:59:42 +02:00
wernermisc/bacon/prog/picpen.c

745 lines
14 KiB
C
Raw Normal View History

/*
* picpen.c - PIC (18F{2,4}xJxx) Programmer/Emulator for Nanonote
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* 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.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include "gpio-xburst.h"
#define POWER_OFF 3, 2 /* PD02 */
#define nMCLR 3, 13 /* PD13 DAT3 */
#define PGD 3, 8 /* PD09 CMD */
#define PGC 3, 9 /* PD08 CLK */
#define ICSP_KEY 0x4d434850
#define P5_US 1 /* Delay Between 4-Bit Command and Command Operand */
#define P5A_US 1 /* Delay Between 4-Bit Command Operand and Next 4-Bit
Command */
#define P6_US 1 /* Delay Between Last PGC down of Command Byte
to First PGC up of Read of Data Word */
#define P9_US 1200 /* Delay to allow Block Programming to Occur */
#define P10_US 54000 /* Delay to allow Row Erase to Occur */
#define P11_US 524000 /* Delay to allow Bulk Erase to Occur */
#define P12_US 400 /* Input Data Hold Time from nMCLR up */
#define P13_US 1 /* VDD up Setup Time to nMCLR up */
#define P16_US 1 /* Delay Between Last PGC down and nMCLR down */
#define P17_US 3 /* nMCLR down to VDD down */
#define P19_US 4000 /* Delay from First nMCLR down to First PGC up for
Key Sequence on PGD */
#define P20_US 1 /* Delay from Last PGC down for Key Sequence on
PGD to Second nMCLR up */
#define CMD_INSN 0
#define CMD_TABLAT 0x2
#define CMD_READ_INC 0x9 /* post-increment by 1 */
#define CMD_WRITE 0xc
#define CMD_WRITE_INC 0xd /* post-increment by 2 */
#define CMD_WRITE_START 0xf
#define INSN_MOVLW 0x0e
#define INSN_MOVWF 0x6e
#define INSN_MOVF_W_0 0x50
#define INSN_MOVFF1 0xcf
#define INSN_MOVFF2 0xff
#define REG_TABLAT 0xf5
#define REG_TBLPTRL 0xf6
#define REG_TBLPTRH 0xf7
#define REG_TBLPTRU 0xf8
#define REG_ANCON0 0x48 /* banked ! */
#define REG_ANCON1 0x49 /* banked ! */
#define REG_PORTA 0x80
#define REG_PORTC 0x82
#define REG_LATA 0x89
#define REG_TRISA 0x92 /* 1 = in */
#define REG_EECON1 0xa6
#define REG_CTMUICON 0xb1
#define REG_CTMUCONL 0xb2
#define REG_CTMUCONH 0xb3
#define REG_ADCON1 0xc1
#define REG_ADCON0 0xc2
#define REG_ADRESL 0xc3
#define REG_ADRESH 0xc4
#define BLOCK 64
struct rec {
uint32_t addr;
uint8_t *data;
int len;
struct rec *next;
};
static int kb;
/* The multiplier (100) is a wild guess. Tested down to 0. */
static inline void delay(int us)
{
uint32_t i;
if (us < 10)
for (i = 0; i != us*100; i++)
asm("");
else
usleep(us);
}
static void icsp_begin(int power)
{
int i;
gpio_init();
gpio_high(nMCLR);
gpio_high(PGD);
gpio_high(PGC);
if (power) {
gpio_output(POWER_OFF);
gpio_output(PGD);
gpio_output(PGC);
gpio_output(nMCLR);
delay(100*1000); /* precharge */
}
gpio_low(PGD);
gpio_low(PGC);
gpio_low(nMCLR);
if (power)
gpio_low(POWER_OFF);
delay(P13_US);
gpio_high(nMCLR);
delay(1);
gpio_low(nMCLR);
delay(P19_US);
for (i = 31; i >= 0; i--) {
if ((ICSP_KEY >> i) & 1)
gpio_high(PGD);
else
gpio_low(PGD);
gpio_high(PGC);
gpio_low(PGC);
}
delay(P20_US);
gpio_high(nMCLR);
delay(P12_US);
}
static void icsp_end(void)
{
gpio_low(PGD);
gpio_low(PGC);
delay(P16_US);
gpio_low(nMCLR);
delay(P17_US);
gpio_high(nMCLR);
// gpio_high(POWER_OFF);
gpio_input(PGD);
gpio_input(PGC);
}
static void icsp_send(uint8_t v, int n)
{
int i;
for (i = 0; i != n; i++) {
if ((v >> i) & 1)
gpio_high(PGD);
else
gpio_low(PGD);
gpio_high(PGC);
gpio_low(PGC);
}
}
static uint8_t icsp_recv(int n)
{
uint8_t v = 0;
int i;
for (i = 0; i != n; i++) {
gpio_high(PGC);
gpio_low(PGC);
if (gpio_get(PGD))
v |= 1 << i;
}
return v;
}
static void icsp_write(uint8_t cmd, uint8_t a, uint8_t b)
{
icsp_send(cmd, 4);
delay(P5_US);
icsp_send(b, 8);
icsp_send(a, 8);
delay(P5A_US);
}
static void write_reg(uint8_t addr, uint8_t v)
{
icsp_write(CMD_INSN, INSN_MOVLW, v);
icsp_write(CMD_INSN, INSN_MOVWF, addr);
}
static uint8_t read_reg(uint8_t addr)
{
uint8_t v;
icsp_write(CMD_INSN, 0x50, addr);
/* for some strange reason we need two writes */
icsp_write(CMD_INSN, INSN_MOVWF, REG_TABLAT);
icsp_write(CMD_INSN, INSN_MOVWF, REG_TABLAT);
icsp_send(CMD_TABLAT, 4);
delay(P5_US);
icsp_send(0, 8);
delay(P6_US);
gpio_input(PGD);
v = icsp_recv(8);
delay(P5A_US);
gpio_output(PGD);
return v;
}
static void set_tblptr(uint32_t addr)
{
write_reg(REG_TBLPTRU, addr >> 16);
write_reg(REG_TBLPTRH, addr >> 8);
write_reg(REG_TBLPTRL, addr);
}
static void read_mem(uint32_t addr, uint8_t *buf, int len)
{
set_tblptr(addr);
while (len--) {
icsp_send(CMD_READ_INC, 4);
delay(P5_US);
icsp_send(0, 8);
delay(P6_US);
gpio_input(PGD);
*buf++ = icsp_recv(8);
delay(P5A_US);
gpio_output(PGD);
}
}
static void bulk_erase(void)
{
set_tblptr(0x3c0005);
icsp_write(CMD_WRITE, 0x01, 0x01);
set_tblptr(0x3c0004);
icsp_write(CMD_WRITE, 0x80, 0x80);
icsp_write(CMD_INSN, 0, 0);
icsp_send(0, 4);
delay(P11_US+P10_US);
icsp_send(0, 16);
delay(P5A_US); /* guess*/
}
static void write_mem(uint32_t addr, const uint8_t *buf, int len)
{
int i;
uint8_t first;
assert(!(addr & (BLOCK-1)));
assert(!(len & (BLOCK-1)));
/* BSF EECON1, WREN */
icsp_write(CMD_INSN, 0x84, REG_EECON1);
while (len) {
set_tblptr(addr);
for (i = 0; i != BLOCK/2-1; i++) {
first = *buf++;
icsp_write(CMD_WRITE_INC, *buf++, first);
}
first = *buf++;
icsp_write(CMD_WRITE_START, *buf++, first);
icsp_send(0, 3);
gpio_low(PGD);
gpio_high(PGC);
delay(P9_US);
gpio_low(PGC);
delay(P5_US);
icsp_send(0, 16);
delay(P5A_US); /* guess*/
addr += BLOCK;
len -= BLOCK;
}
}
/* ----- Flash-level operations -------------------------------------------- */
static void flash_record(const struct rec *rec)
{
uint8_t *tmp;
int off, padded;
off = rec->addr & (BLOCK-1);
padded = (rec->len+off+BLOCK-1) & ~(BLOCK-1);
if (!off && padded == rec->len) {
write_mem(rec->addr, rec->data, rec->len);
return;
}
tmp = malloc(padded);
if (!tmp) {
perror("malloc");
exit(1);
}
memset(tmp, 0xff, off);
memcpy(tmp+off, rec->data, rec->len);
memset(tmp+off+rec->len, 0xff, padded-rec->len-off);
write_mem(rec->addr-off, tmp, padded);
}
static void verify_record(const struct rec *rec)
{
uint8_t *tmp;
int i;
tmp = malloc(rec->len);
if (!tmp) {
perror("malloc");
exit(1);
}
read_mem(rec->addr, tmp, rec->len);
for (i = 0; i != rec->len; i++)
if (rec->data[i] != tmp[i]) {
fprintf(stderr,
"%04x: wrote %02x != read %02x\n",
rec->addr+i, rec->data[i], tmp[i]);
}
}
static void flash_file(const struct rec *recs)
{
const struct rec *rec;
for (rec = recs; rec; rec = rec->next)
if (rec->addr+rec->len > kb*1024) {
fprintf(stderr,
"record 0x%x+0x%x ends outside Flash of %d bytes\n",
rec->addr, rec->len, kb*1024-8);
exit(1);
}
bulk_erase();
for (rec = recs; rec; rec = rec->next)
flash_record(rec);
for (rec = recs; rec; rec = rec->next)
verify_record(rec);
}
static void dump_flash(void)
{
uint8_t *tmp, c;
int i, j;
tmp = malloc(kb*1024);
if (!tmp) {
perror("malloc");
exit(1);
}
read_mem(0, tmp, kb*1024);
for (i = 0; i != kb*1024; i += 16) {
printf("%04X: ", i);
for (j = 0; j != 16; j++)
printf("%02X ", tmp[i+j]);
for (j = 0; j != 16; j++) {
c = tmp[i+j];
printf("%c", c >= ' ' && c <= '~' ? c : '.');
}
printf("\n");
}
}
/* ----- Experiments ------------------------------------------------------- */
#if 0
static void blink(void)
{
write_reg(REG_TRISA, 0xfc);
while (1) {
write_reg(REG_LATA, 1);
gpio_high(PGD);
delay(200*1000);
write_reg(REG_LATA, 2);
gpio_high(PGD);
delay(200*1000);
}
}
static void adc(void)
{
int i;
#if 0
for (i = 0; i != 256; i++) {
// write_reg(REG_ANCON0, 0xff);
// write_reg(REG_TABLAT, i);
// write_reg(REG_TABLAT, i);
printf("%02x ", read_reg(REG_PORTC));
fflush(stdout);
}
#endif
write_reg(REG_TRISA, 0xfc);
// write_reg(REG_ANCON0, 0x10); /* AN4 is analog */
write_reg(REG_ADCON0, 0x20); /* AN4, Vss to AVdd */
write_reg(REG_ADCON1, 0x80); /* Tad = 0, Fosc/2, right just. */
write_reg(REG_ADCON0, 0x21); /* Enable ADC module */
while (1) {
uint8_t hi, lo;
write_reg(REG_ADCON0, 0x23); /* GO */
while (read_reg(REG_ADCON0) & 2)
write(2, ".", 1);
hi = read_reg(REG_ADRESH);
lo = read_reg(REG_ADRESL);
printf("\r%02x %02x (%u)\n", hi, lo, hi << 8 | lo);
}
#if 0
while (1) {
write_reg(
}
#endif
}
static void cap(void)
{
write_reg(REG_CTMUCONH, 0x00); /* page 405 */
write_reg(REG_CTMUCONL, 0x90);
write_reg(REG_CTMUICON, 0x01);
write_reg(REG_TRISA, 0xfc);
write_reg(REG_ADCON0, 0x20); /* AN4, Vss to AVdd */
// write_reg(REG_ADCON1, 0x8e); /* Tad = 2, Fosc/32, right just. */
write_reg(REG_ADCON1, 0x88); /* Tad = 2, Fosc/2, right just. */
write_reg(REG_ADCON0, 0x21); /* Enable ADC module */
/* bandgap ?!? */
while (1) {
uint8_t hi, lo;
write_reg(REG_CTMUCONH, 0x80); /* enable CTMU */
write_reg(REG_CTMUCONL, 0x90); /* clear EDGxSTAT */
write_reg(REG_CTMUCONH, 0x82); /* ground output */
delay(1);
write_reg(REG_CTMUCONH, 0x80); /* end drain */
write_reg(REG_CTMUCONL, 0x91); /* current on */
write_reg(REG_CTMUCONL, 0x90); /* current off */
write_reg(REG_ADCON0, 0x23); /* GO */
while (read_reg(REG_ADCON0) & 2)
write(2, ".", 1);
hi = read_reg(REG_ADRESH);
lo = read_reg(REG_ADRESL);
printf("\r%02x %02x (%u)\n", hi, lo, hi << 8 | lo);
}
}
static void dump(uint32_t addr)
{
uint8_t buf[BLOCK];
int i;
read_mem(addr, buf, BLOCK);
for (i = 0; i != BLOCK; i++) {
if (!(i & 15))
printf("%04X:", i);
printf(" %02X", buf[i]);
if ((i & 15) == 15)
printf("\n");
}
}
static void rerwr(void)
{
uint8_t buf[BLOCK];
int i;
dump(0x3fffc0);
dump(0);
bulk_erase();
dump(0);
for (i = 0; i != BLOCK; i++)
buf[i] = i;
write_mem(0, buf, BLOCK);
dump(0x3fffc0);
dump(0);
}
#endif
/* ----- Chip identification ----------------------------------------------0 */
struct chip {
const char *name;
uint8_t id2, id1;
int kb;
} chips[] = {
{ "PIC18F24J50", 0x4c, 0x00, 16 },
{ NULL }
};
#define DEVID1 0x3ffffe
static void identify(void)
{
uint8_t id[2];
const struct chip *chip;
read_mem(DEVID1, id, 2);
for (chip = chips; chip->name; chip++) {
if ((chip->id1 ^ id[0]) & 0xe0)
continue;
if (chip->id2 == id[1])
break;
}
fprintf(stderr, "ID = 0x%02x%02x (%s)\n", id[1], id[0],
chip->name ? chip->name : "?");
if (!chip->name)
exit(1);
fprintf(stderr, "%d kB Flash\n", chip->kb);
kb = chip->kb;
}
/* ----- Intel HEX file reader --------------------------------------------- */
static int hex(char c, int lineno)
{
if (c >= '0' && c <= '9')
return c-'0';
if (c >= 'A' && c <= 'F')
return c-'A'+10;
fprintf(stderr, "non-hex character \"%c\" in line %d\n", c, lineno);
exit(1);
}
/* http://en.wikipedia.org/wiki/Intel_HEX */
static struct rec *load_file(const char *name)
{
struct rec *recs = NULL, *last = NULL, *rec;
const struct rec *other;
FILE *file;
int lineno = 1;
char line[1000];
const char *s;
uint8_t buf[sizeof(line)/2];
uint8_t *p, *t, sum;
uint32_t xaddr = 0, addr;
file = fopen(name, "r");
if (!file) {
perror(name);
exit(1);
}
while (fgets(line, sizeof(line), file)) {
if (*line != ':') {
fprintf(stderr, "line %d doesn't start with colon\n",
lineno);
exit(1);
}
p = buf;
for (s = line+1; *s > ' '; s += 2)
*p++ = hex(s[0], lineno) << 4 | hex(s[1], lineno);
if (p-buf < 5) {
fprintf(stderr, "short record in line %d\n", lineno);
exit(1);
}
lineno++;
sum = 0;
for (t = buf; t != p-1; t++)
sum += *t;
if (0x100-sum != p[-1]) {
fprintf(stderr,
"checksum error (0x%02x vs. 0x%02x) in line %d\n",
p[-1], 0x100-sum, lineno);
exit(1);
}
switch (buf[3]) {
case 0: /* Data record */
if (p-buf != buf[0]+5) {
fprintf(stderr,
"data record of %d bytes has length %d\n",
p-buf, buf[0]);
exit(1);
}
addr = xaddr << 16 | buf[1] << 8 | buf[2];
if (last && last->addr+last->len == addr) {
last->data = realloc(last->data,
last->len+buf[0]);
if (!last->data) {
perror("realloc");
exit(1);
}
memcpy(last->data+last->len, buf+4, buf[0]);
last->len += buf[0];
break;
}
rec = malloc(sizeof(struct rec));
rec->addr = xaddr << 16 | buf[1] << 8 | buf[2];
rec->len = buf[0];
rec->data = malloc(rec->len);
if (!rec->data) {
perror("realloc");
exit(1);
}
rec->next = NULL;
memcpy(rec->data, buf+4, rec->len);
if (last)
last->next = rec;
else
recs = rec;
last = rec;
break;
case 1: /* End Of File record */
goto end;
case 4: /* Extended Linear Address record */
xaddr = buf[4] << 8 | buf[5];
break;
default:
fprintf(stderr,
"unrecognized record type 0x%02x in line %d\n",
buf[3], lineno);
exit(1);
}
}
end:
for (rec = recs; rec; rec = rec->next)
for (other = rec->next; other; other = other->next)
if (rec->addr < other->addr+other->len &&
rec->addr+rec->len > other->addr) {
fprintf(stderr,
"overlapping address ranges 0x%x+0x%x "
"and 0x%x+0x%x\n",
rec->addr, rec->len, other->addr,
other->len);
}
fclose(file);
return recs;
}
/* ----- Command-line parsing ---------------------------------------------- */
static void usage(const char *name)
{
fprintf(stderr,
"usage: %s [-n] [file.bin]\n", name);
exit(1);
}
int main(int argc, char **argv)
{
const struct rec *recs = NULL;
int power = 1;
int c;
while ((c = getopt(argc, argv, "n")) != EOF)
switch (c) {
case 'n':
power = 0;
break;
default:
usage(*argv);
}
switch (argc-optind) {
case 0:
break;
case 1:
recs = load_file(argv[optind]);
break;
default:
usage(*argv);
}
icsp_begin(power);
identify();
if (recs)
flash_file(recs);
else
dump_flash();
icsp_end();
return 0;
}