Rev. | 3c7279492996a0bba0b98d175e6db6e3f1437ba0 |
---|---|
Tamaño | 20,376 octetos |
Tiempo | 2021-06-05 00:10:06 |
Autor | Yoshinori Sato |
Log Message | fix some error.
|
/*
* Renesas CPU On-chip Flash memory writer
* target communication
*
* Yoshinori Sato <ysato@users.sourceforge.jp>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License version 2.1 (or later).
*/
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include "h8flash.h"
#define ACK 0x06
#define QUERY_DEVICE 0x20
#define QUERY_DEVICE_RES 0x30
#define QUERY_CLOCKMODE 0x21
#define QUERY_CLOCKMODE_RES 0x31
#define QUERY_MULTIRATE 0x22
#define QUERY_MULTIRATE_RES 0x32
#define QUERY_FREQ 0x23
#define QUERY_FREQ_RES 0x33
#define QUERY_BOOT_AREA 0x24
#define QUERY_BOOT_AREA_RES 0x34
#define QUERY_USER_AREA 0x25
#define QUERY_USER_AREA_RES 0x35
#define QUERY_WRITESIZE 0x27
#define QUERY_WRITESIZE_RES 0x37
#define SELECT_DEVICE 0x10
#define SET_CLOCKMODE 0x11
#define SET_BITRATE 0x3f
#define WRITEMODE 0x40
#define WRITE_USERBOOT 0x42
#define WRITE_USER 0x43
#define BLANKCHECK_USERBOOT 0x4c
#define BLANKCHECK_USER 0x4d
#define WRITE 0x50
struct devinfo_t {
char code[4];
char name[256];
};
struct devicelist_t {
int numdevs;
struct devinfo_t devs[0];
};
struct clockmode_t {
int nummode;
int mode[0];
};
struct multirate_t {
int numrate;
int rate[0];
};
struct multilist_t {
int nummulti;
struct multirate_t *muls[0];
};
struct freq_t {
int min;
int max;
};
struct freqlist_t {
int numfreq;
struct freq_t freq[0];
};
/* NAK answer list */
const unsigned char naktable[] = {0x80, 0x90, 0x91, 0xbf, 0xc0, 0xc2, 0xc3, 0xc8,
0xcc, 0xcd, 0xd0, 0xd2, 0xd8};
/* big endian to cpu endian convert 32bit */
static __inline__ int getlong(unsigned char *p)
{
return (*p << 24) | (*(p+1) << 16) | (*(p+2) << 8) | *(p+3);
}
/* big endian to cpu endian convert 16bit */
static __inline__ short getword(unsigned char *p)
{
return (*p << 8) | *(p+1);
}
/* cpu endian to big endian 32bit */
static __inline__ void setlong(unsigned char *buf, unsigned long val)
{
*(buf + 0) = (val >> 24) & 0xff;
*(buf + 1) = (val >> 16) & 0xff;
*(buf + 2) = (val >> 8) & 0xff;
*(buf + 3) = (val ) & 0xff;
}
/* send multibyte command */
static void send(struct port_t *p, unsigned char *data, int len)
{
unsigned char sum;
p->send_data(data, len);
if (len > 1) {
for(sum = 0; len > 0; len--, data++)
sum += *data;
sum = 0x100 - sum;
p->send_data(&sum, 1);
}
}
/* receive answer */
static int receive(struct port_t *p, unsigned char *data)
{
int len;
unsigned char *rxptr;
unsigned char sum;
rxptr = data;
if (p->receive_byte(rxptr) != 1)
return -1;
rxptr++;
/* ACK */
if (*data == ACK) {
return 1;
}
/* NAK */
if (memchr(naktable, *data, sizeof(naktable))) {
if (p->receive_byte(rxptr) != 1)
return -1;
else
return 2;
}
/* multibyte response */
if (p->receive_byte(rxptr) != 1)
return -1;
rxptr++;
len = *(data + 1) + 1;
for(; len > 0; len--) {
if (p->receive_byte(rxptr) != 1)
return -1;
rxptr++;
}
/* 0 byte body */
if (*(data + 1) == 0)
return 0;
/* sum check */
for (sum = 0, rxptr = data, len = *(data + 1) + 3; len > 0; len--, rxptr++)
sum += *rxptr;
if (sum != 0)
return -1;
return *(data + 1);
}
/* get target device list */
static struct devicelist_t *get_devicelist(struct port_t *port)
{
unsigned char rxbuf[255+3];
unsigned char *devp;
struct devicelist_t *devlist;
int devno;
rxbuf[0] = QUERY_DEVICE;
send(port, rxbuf, 1);
if (receive(port, rxbuf) == -1)
return NULL;
if (rxbuf[0] != QUERY_DEVICE_RES)
return NULL;
devno = rxbuf[2];
devlist = (struct devicelist_t *)malloc(sizeof(struct devicelist_t) +
sizeof(struct devinfo_t) * devno);
if (devlist == NULL)
return NULL;
devlist->numdevs = devno;
devp = &rxbuf[3];
for(devno = 0; devno < devlist->numdevs; devno++) {
memcpy(devlist->devs[devno].code, devp + 1, 4);
memcpy(devlist->devs[devno].name, devp + 5, *devp-4);
devlist->devs[devno].name[*devp - 4] = 0;
devp += *devp;
}
return devlist;
}
/* set target device ID */
static int select_device(struct port_t *port, const char *code)
{
unsigned char buf[6] = {SELECT_DEVICE, 0x04, 0x00, 0x00, 0x00, 0x00};
memcpy(&buf[2], code, 4);
send(port, buf, sizeof(buf));
if (receive(port, buf) != 1)
return 0;
return 1;
}
/* get target clock mode */
static struct clockmode_t *get_clockmode(struct port_t *port)
{
unsigned char rxbuf[255+3];
unsigned char *clkmdp;
struct clockmode_t *clocks;
int numclock;
rxbuf[0] = QUERY_CLOCKMODE;
send(port, rxbuf, 1);
if (receive(port, rxbuf) == -1)
return NULL;
if (rxbuf[0] != QUERY_CLOCKMODE_RES)
return NULL;
numclock = rxbuf[2];
if (numclock == 0) {
numclock = 1;
rxbuf[3] = 0;
}
clocks = (struct clockmode_t *)malloc(sizeof(struct clockmode_t) +
sizeof(int) * numclock);
if (clocks == NULL)
return NULL;
clocks->nummode = numclock;
clkmdp = &rxbuf[3];
for(numclock = 0; numclock < clocks->nummode; numclock++)
clocks->mode[numclock] = *clkmdp++;
return clocks;
}
/* set target clock mode */
static int set_clockmode(struct port_t *port, int mode)
{
unsigned char buf[3] = {SET_CLOCKMODE, 0x01, 0x00};
buf[2] = mode;
send(port, buf, sizeof(buf));
if (receive(port, buf) != 1)
return 0;
else
return 1;
}
/* get target multiplier/divider rate */
static struct multilist_t *get_multirate(struct port_t *port)
{
unsigned char rxbuf[255+3];
unsigned char *mulp;
struct multilist_t *multilist;
struct multirate_t *rate;
int nummulti;
int numrate;
int listsize;
rxbuf[0] = QUERY_MULTIRATE;
send(port, rxbuf, 1);
if (receive(port, rxbuf) == -1)
return NULL;
if (rxbuf[0] != QUERY_MULTIRATE_RES)
return NULL;
/* calc multilist size */
nummulti = rxbuf[2];
listsize = sizeof(struct multilist_t) + sizeof(struct multirate_t *) * nummulti;
mulp = &rxbuf[3];
for(; nummulti > 0; nummulti--) {
listsize += sizeof(struct multirate_t) + sizeof(int) * *mulp;
mulp += *mulp;
}
multilist = (struct multilist_t *)malloc(listsize);
if (multilist == NULL)
return NULL;
/* setup list */
multilist->nummulti = rxbuf[2];
rate = (struct multirate_t *)&multilist->muls[multilist->nummulti];
mulp = &rxbuf[3];
for (nummulti = 0; nummulti < multilist->nummulti; nummulti++) {
multilist->muls[nummulti] = rate;
rate->numrate = *mulp++;
for (numrate = 0; numrate < rate->numrate; numrate++) {
rate->rate[numrate] = *mulp++;
}
rate = (struct multirate_t *)&rate->rate[numrate];
}
return multilist;
}
/* get target operation frequency list */
static struct freqlist_t *get_freqlist(struct port_t *port)
{
unsigned char rxbuf[255+3];
unsigned char *freqp;
struct freqlist_t *freqlist;
int numfreq;
rxbuf[0] = QUERY_FREQ;
send(port, rxbuf, 1);
if (receive(port, rxbuf) == -1)
return NULL;
if (rxbuf[0] != QUERY_FREQ_RES)
return NULL;
numfreq = rxbuf[2];
freqlist = (struct freqlist_t *)malloc(sizeof(struct freqlist_t) +
sizeof(struct freq_t) * numfreq);
if (freqlist == NULL)
return NULL;
freqlist->numfreq = numfreq;
freqp = &rxbuf[3];
for(numfreq = 0; numfreq < freqlist->numfreq; numfreq++) {
freqlist->freq[numfreq].min = getword(freqp);
freqlist->freq[numfreq].max = getword(freqp+2);
freqp += 4;
}
return freqlist;
}
/* get target rom mapping */
/* get write page size */
static int get_writesize(struct port_t *port)
{
unsigned char rxbuf[5];
unsigned short size;
rxbuf[0] = QUERY_WRITESIZE;
send(port, rxbuf, 1);
if (receive(port, rxbuf) == -1)
return -1;
if (rxbuf[0] != QUERY_WRITESIZE_RES)
return -1;
if (rxbuf[1] != 2)
return -1;
size = rxbuf[2] << 8 | rxbuf[3];
return size;
}
static struct arealist_t *get_arealist(struct port_t *port, enum mat_t mat)
{
char ans;
unsigned char rxbuf[255+3];
unsigned char *areap;
struct arealist_t *arealist;
int numarea;
int wsize;
wsize = get_writesize(port);
switch(mat) {
case user:
rxbuf[0] = QUERY_USER_AREA;
ans = QUERY_USER_AREA_RES;
break;
case userboot:
rxbuf[0] = QUERY_BOOT_AREA;
ans = QUERY_BOOT_AREA_RES;
break;
default:
return NULL;
}
send(port, rxbuf, 1);
if (receive(port, rxbuf) == -1)
return NULL;
if (rxbuf[0] != ans)
return NULL;
numarea = rxbuf[2];
arealist = (struct arealist_t *)malloc(sizeof(struct arealist_t) +
sizeof(struct area_t) * numarea);
if (arealist == NULL)
return NULL;
arealist->areas = numarea;
areap = &rxbuf[3];
for(numarea = 0; numarea < arealist->areas; numarea++) {
int size;
arealist->area[numarea].start = getlong(areap);
arealist->area[numarea].end = getlong(areap+4);
arealist->area[numarea].size = wsize;
areap += 8;
size = arealist->area[numarea].end - arealist->area[numarea].start;
if (!(arealist->area[numarea].image = malloc(size)))
return NULL;
memset(arealist->area[numarea].image, 0xff, size);
}
return arealist;
}
/* bitrate candidate list */
static const int rate_list[]={1152,576,384,192,96};
/* bitrate error margine (%) */
#define ERR_MARGIN 4
/* select communication bitrate */
static int adjust_bitrate(int p_freq)
{
int brr;
int errorrate;
int rate_no;
for (rate_no = 0; rate_no < sizeof(rate_list) / sizeof(int); rate_no++) {
brr = (p_freq * 100) / (32 * rate_list[rate_no]);
errorrate = abs((p_freq * 10000) / ((brr + 1) * rate_list[rate_no] * 32) - 100);
if (errorrate <= ERR_MARGIN)
return rate_list[rate_no];
}
return 0;
}
/* set target bitrate */
static int set_bitrate(struct port_t *p, int bitrate, int freq, int coremul, int peripheralmul)
{
unsigned char buf[9] = {SET_BITRATE, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
buf[2] = (bitrate >> 8) & 0xff;
buf[3] = bitrate & 0xff;
buf[4] = (freq >> 8) & 0xff;
buf[5] = freq & 0xff;
buf[6] = (peripheralmul == 0)?1:2;
buf[7] = coremul;
buf[8] = peripheralmul;
send(p, buf, sizeof(buf));
if (receive(p, buf) != 1)
return 0;
if (p->setbaud) {
if (!p->setbaud(bitrate))
return 0;
}
usleep(10000);
buf[0] = ACK;
send(p, buf, 1);
if (receive(p, buf) != 1)
return 0;
else
return 1;
}
#define C_MULNO 0
#define P_MULNO 1
#define C_FREQNO 0
#define P_FREQNO 1
/* change communicate bitrate */
static int change_bitrate(struct port_t *p, int in_freq,
struct multilist_t *multi, struct freqlist_t *freq)
{
int rateno;
int core_mul, peripheral_mul;
int core_freq, peripheral_freq;
int clock;
int rate;
core_mul = 0;
peripheral_mul = 0;
/* select cpu core clock frequency */
for (rateno = 0, core_freq = -1; rateno < multi->muls[C_MULNO]->numrate; rateno++) {
if (multi->muls[C_MULNO]->rate[rateno] > 0)
clock = in_freq * multi->muls[C_MULNO]->rate[rateno];
else
clock = in_freq / -multi->muls[C_MULNO]->rate[rateno];
if (!(clock >= freq->freq[C_FREQNO].min && clock <= freq->freq[C_FREQNO].max))
continue;
if (core_freq < clock) {
core_mul = multi->muls[C_MULNO]->rate[rateno];
core_freq = clock;
}
}
/* select peripheral clock freqency */
if (multi->nummulti > P_MULNO) {
for (rateno = 0, peripheral_freq = -1;
rateno < multi->muls[P_MULNO]->numrate; rateno++) {
if (multi->muls[P_MULNO]->rate[rateno] > 0)
clock = in_freq * multi->muls[P_MULNO]->rate[rateno];
else
clock = in_freq / -multi->muls[P_MULNO]->rate[rateno];
if (clock < freq->freq[P_FREQNO].min ||
clock > freq->freq[P_FREQNO].max)
continue;
if (peripheral_freq < clock) {
peripheral_mul = multi->muls[P_MULNO]->rate[rateno];
peripheral_freq = clock;
}
}
} else {
peripheral_mul = 0;
peripheral_freq = core_freq;
}
/* select clock check */
if (core_freq == -1 || peripheral_freq == -1) {
fprintf(stderr,"input frequency (%d.%d MHz) is out of range\n",
in_freq / 100, in_freq % 100);
return 0;
}
VERBOSE_PRINT("core multiple rate=%d, freq=%d.%d MHz\n",
core_mul, core_freq / 100, core_freq % 100);
VERBOSE_PRINT("peripheral multiple rate=%d, freq=%d.%d MHz\n",
peripheral_mul, peripheral_freq / 100, peripheral_freq % 100);
/* select bitrate from peripheral cock*/
rate = adjust_bitrate(peripheral_freq);
if (rate == 0)
return 0;
VERBOSE_PRINT("bitrate %d bps\n",rate * 100);
/* setup host/target bitrate */
return set_bitrate(p, rate, in_freq, core_mul, peripheral_mul);
}
/* check blank page */
static int skipcheck(unsigned char *data, unsigned short size)
{
unsigned char r = 0xff;
int c;
for (c = 0; c < size; c++)
r &= *data++;
return (r == 0xff);
}
/* write rom image */
static int write_rom(struct port_t *port, struct arealist_t *arealist, enum mat_t mat)
{
unsigned char *buf = NULL;
unsigned char cmdbuf[32];
unsigned int romaddr;
int i;
struct area_t *area;
puts("Erase flash...");
/* enter writemode */
buf = cmdbuf;
buf[0] = WRITEMODE;
send(port, buf, 1);
if (receive(port, buf) != 1) {
printf("%02x ", buf[0]);
fputs(PROGNAME ": writemode start failed\n", stderr);
goto error;
}
/* mat select */
switch (mat) {
case user:
buf[0] = WRITE_USER;
break;
case userboot:
buf[0] = WRITE_USERBOOT;
break;
}
send(port, buf, 1);
if (receive(port, buf) != 1) {
printf("%02x ", buf[0]);
fputs(PROGNAME ": writemode start failed\n", stderr);
goto error;
}
/* writing loop */
for (i = 0; i < arealist->areas; i++) {
area = &arealist->area[i];
for (romaddr = area->start;
romaddr < area->end;
romaddr += area->size) {
if (skipcheck(area->image + romaddr - area->start,
area->size)) {
if (verbose)
printf("skip - %08x\n",romaddr);
else {
printf("writing %d/%d byte\r",
romaddr - area->start,
area->end - area->start);
fflush(stdout);
}
continue;
}
buf = malloc(area->size + 5);
if (buf == NULL)
goto error;
/* set write data */
*(buf + 0) = WRITE;
setlong(buf + 1, romaddr);
memcpy(buf + 5,
area->image + romaddr - area->start,
area->size);
/* write */
send(port, buf, 5 + area->size);
free(buf);
if (receive(port, buf) != 1) {
fprintf(stderr, PROGNAME ": write data %08x failed.", romaddr);
goto error;
}
if (verbose)
printf("write - %08x\n",romaddr);
else {
printf("writing %d/%d byte\r",
romaddr - area->start,
area->end - area->start);
fflush(stdout);
}
}
}
/* write finish */
buf = cmdbuf;
*(buf + 0) = WRITE;
memset(buf + 1, 0xff, 4);
send(port, buf, 5);
if (receive(port, buf) != 1) {
fputs(PROGNAME ": writemode exit failed", stderr);
goto error;
}
if (!verbose)
putc('\n', stdout);
return 0;
error:
return 1;
}
/* connect to target chip */
static int setup_connection(struct port_t *p, int input_freq, char endian)
{
int c;
int r = -1;
struct devicelist_t *devicelist = NULL;
struct clockmode_t *clockmode = NULL;
struct multilist_t *multilist = NULL;
struct freqlist_t *freqlist = NULL;
/* query target infomation */
devicelist = get_devicelist(p);
if (devicelist == NULL) {
if (errno != 0)
perror(PROGNAME);
else
fputs("devicelist error\n", stderr);
goto error;
}
if (verbose) {
char codes[5];
printf("Support devices: %d\n", devicelist->numdevs);
for (c = 0; c < devicelist->numdevs; c++) {
memcpy(codes, devicelist->devs[c].code, 4);
codes[4] = '\0';
printf("%d: %s - %s\n", c+1, codes, devicelist->devs[c].name);
}
}
/* query target clockmode */
clockmode = get_clockmode(p);
if (clockmode == NULL) {
if (errno != 0)
perror(PROGNAME);
else
fputs("clockmode error\n",stderr);
goto error;
}
if (verbose) {
if (clockmode->nummode > 0) {
printf("Support clock modes %d:", clockmode->nummode);
for (c = 0; c < clockmode->nummode; c++) {
printf(" %02x", clockmode->mode[c]);
}
printf("\n");
} else
printf("no clockmode support\n");
}
/* SELDEV devicetype select */
if (devicelist->numdevs < SELDEV) {
fprintf(stderr, "Select Device (%d) not supported.\n", SELDEV);
goto error;
}
if (!select_device(p, devicelist->devs[SELDEV].code)) {
fputs("device select error", stderr);
goto error;
}
/* SELCLK clockmode select */
if (clockmode->nummode > 0) {
if (clockmode->nummode < SELCLK) {
fprintf(stderr, "Select clock (%d) not supported.\n", SELCLK);
goto error;
}
if (!set_clockmode(p, clockmode->mode[SELCLK])) {
fputs("clock select error", stderr);
goto error;
}
} else {
if (!set_clockmode(p, 0)) {
fputs("clock select error", stderr);
goto error;
}
}
/* query multiplier/devider rate */
multilist = get_multirate(p);
if (multilist == NULL) {
if (errno != 0)
perror(PROGNAME);
else
fputs("multilist error\n",stderr);
goto error;
}
if (verbose) {
int c1,c2;
printf("Support multiple rate: %d\n", multilist->nummulti);
for (c1 = 0; c1 < multilist->nummulti; c1++) {
printf("%d:", c1 + 1);
for (c2 = 0; c2 < multilist->muls[c1]->numrate; c2++)
printf(" %d", multilist->muls[c1]->rate[c2]);
printf("\n");
}
}
/* query operation frequency range */
freqlist = get_freqlist(p);
if (freqlist == NULL) {
if (errno != 0)
perror(PROGNAME);
else
fputs("freqlist error\n",stderr);
goto error;
}
if (verbose) {
printf("operation frequencies: %d\n", freqlist->numfreq);
for (c = 0; c < freqlist->numfreq; c++) {
printf("%d: %d.%d - %d.%d\n", c + 1,
freqlist->freq[c].min / 100,freqlist->freq[c].min % 100,
freqlist->freq[c].max / 100,freqlist->freq[c].max % 100);
}
}
/* set writeing bitrate */
if (!change_bitrate(p, input_freq, multilist, freqlist)) {
fputs("set bitrate failed\n",stderr);
goto error;
}
r = 0;
error:
free(devicelist);
free(clockmode);
free(multilist);
free(freqlist);
return r;
}
/* connect to target chip */
static void dump_configs(struct port_t *p)
{
struct devicelist_t *devicelist = NULL;
struct clockmode_t *clockmode = NULL;
struct multilist_t *multilist = NULL;
struct freqlist_t *freqlist = NULL;
int dev;
int clk;
int c1,c2;
/* query target infomation */
devicelist = get_devicelist(p);
if (devicelist == NULL) {
if (errno != 0)
perror(PROGNAME);
else
fputs("devicelist error\n", stderr);
goto error;
}
/* query target clockmode */
clockmode = get_clockmode(p);
if (clockmode == NULL) {
if (errno != 0)
perror(PROGNAME);
else
fputs("clockmode error\n",stderr);
goto error;
}
for(dev = 0; dev < devicelist->numdevs; dev++) {
if (!select_device(p, devicelist->devs[dev].code)) {
fputs("device select error", stderr);
goto error;
}
for (clk = 0; clk < clockmode->nummode; clk++) {
if (!set_clockmode(p, clockmode->mode[clk])) {
fputs("clock select error", stderr);
goto error;
}
printf("dev: %s - clock: %d\n", devicelist->devs[dev].name, clk);
multilist = get_multirate(p);
if (multilist == NULL) {
if (errno != 0)
perror(PROGNAME);
else
fputs("multilist error\n",stderr);
goto error;
}
printf("multiple / divide rate\n");
for (c1 = 0; c1 < multilist->nummulti; c1++) {
for (c2 = 0; c2 < multilist->muls[c1]->numrate; c2++)
printf(" %d", (int)multilist->muls[c1]->rate[c2]);
printf("\n");
}
/* query operation frequency range */
freqlist = get_freqlist(p);
if (freqlist == NULL) {
if (errno != 0)
perror(PROGNAME);
else
fputs("freqlist error\n",stderr);
goto error;
}
printf("operation frequency (MHz)\n");
for (c1 = 0; c1 < freqlist->numfreq; c1++) {
printf("%d.%d - %d.%d\n",
freqlist->freq[c1].min / 100,freqlist->freq[c1].min % 100,
freqlist->freq[c1].max / 100,freqlist->freq[c1].max % 100);
}
free(multilist);
free(freqlist);
multilist = NULL;
freqlist = NULL;
}
}
error:
free(multilist);
free(freqlist);
free(devicelist);
free(clockmode);
}
static struct comm_t v1 = {
.get_arealist = get_arealist,
.write_rom = write_rom,
.setup_connection = setup_connection,
.dump_configs = dump_configs,
};
struct comm_t *comm_v1(void)
{
return &v1;
}