1543 lines
46 KiB
C
1543 lines
46 KiB
C
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <pico/time.h>
|
|
#include <pico/timeout_helper.h>
|
|
#include <hardware/gpio.h>
|
|
|
|
#include "tool78_defs.h"
|
|
#include "tool78_hw.h"
|
|
#include "tool78_cmds.h"
|
|
|
|
static uint8_t databuf[255];
|
|
|
|
// variable name | /fRH=/8MHz us us
|
|
// 78k RL78 | 78k0 78k0r rl78
|
|
// -----------------+----------------------------------------------------------
|
|
// tCOM tSDNx/tMB | 106 13.2 62 (good enough)
|
|
// tSF | 215 X X
|
|
// tWT0 tCS1 | 172 0 255
|
|
// tWT1 (tSD8) |857883+128*44160 (1420100+128*281100) 212
|
|
// tWT2 tCS3 |214714*512+128*44160 (3300+271600+128*275000) (248862+299307)
|
|
// tWT3 tCS5 | 1506 0 1432
|
|
// tWT4 tDS5 |893355/2 149900 (309870+488315)
|
|
// tWT5 tSS5 |100407 1187500 (1732+58+(17403+29293)*128+(184+44))
|
|
// tWT6 tCS2 | 686 0 351
|
|
// tWT7 tDS2 | 12827 0 11981
|
|
// tWT8 tCS4 | 55044 18000 (3805+168+(5035+1110)*128+(5827+318))
|
|
// tWT9 (tCS8) | 1238 X 154
|
|
// tWT10 tCS6 | X 379.2 4735
|
|
// tWT11 tCS11 | 1233 0 111
|
|
// tWT12 (tCS9) | 252 0 (146110+534723+(5035+1110)*128+(203+57))
|
|
// tWT13 tCS7 | 975 0 168
|
|
// tWT14 tDS7 |66005812/2 70.2 (277095+1075967)
|
|
// tWT15 |66018156/2 1152.3 X
|
|
// tWT16 tCS10 | 583 0 219
|
|
// tFD1 tSD10 | 54368 0 (72+128*30720)
|
|
// tFD2 tSD11 | 321 11.4 512
|
|
// tFD3 tSD2 | 163 416.4 41
|
|
// tFD3 tSD5 | 163 416.4 41
|
|
// tFD4 tSD7 | 163 985.8 32
|
|
// ~tCS8~ |
|
|
// ~tSD8~ |
|
|
// ~tCS9~ |
|
|
|
|
struct delayvalues { int none; int k0; int k0r; int rl78; };
|
|
#define D(a,b,c) ((struct delayvalues){.none=1000, .k0=(a), .k0r=(b), .rl78=(c)})
|
|
static const struct delayvalues delays[23] = {
|
|
D(106, 14, 62), // tCOM
|
|
D(215, -1, -1), // tSF (78k0 SPI only)
|
|
D(172, 0, 255), // tWT0
|
|
D(857883+128*44160, (1420100+128*281100), 212), // tWT1
|
|
D(214714*512+128*44160, (3300+271600+128*275000),(248862+299307)), // tWT2
|
|
D(1506,0,1432), // tWT3
|
|
D(893355/2, 149900, (309870+488315)), // tWT4
|
|
D(100407 , 1187500, (1732+58+(17403+29293)*128+(184+44))), // tWT5
|
|
D(686,0,351), // tWT6
|
|
D(12827, 0, 11981), // tWT7
|
|
D(55044, 1800, (3805+168+(5035+1110)*128+(5827+318))), // tWT8
|
|
D(1238, -1, 154), // tWT9,
|
|
D(-1, 380, 4735), // tWT10,
|
|
D(1233, 0, 111), // tWT11,
|
|
D(252, 0, (146110+534723+(5035+1110)*128+(203+57))), // tWT12
|
|
D(975, 0, 168), // tWT13
|
|
D(66005812/2 ,71, (277095+1075967)), // tWT14
|
|
D(66018156/2 ,1153, -1), // tWT15
|
|
D(583, 0, 219), // tWT16
|
|
D(54368, 0, (72+128*30720)), // tFD1
|
|
D(321, 12, 512), // tFD2
|
|
D(163, 417, 41), // tFD3
|
|
D(163, 986, 32), // tFD4
|
|
};
|
|
// SD5 SS5 SD2 CS4 SD7 CS8 SD8 CS9
|
|
#define tCOM (((&delays[ 0].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tSF (((&delays[ 1].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT0 (((&delays[ 2].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT1 (((&delays[ 3].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT2 (((&delays[ 4].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT3 (((&delays[ 5].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT4 (((&delays[ 6].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT5 (((&delays[ 7].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT6 (((&delays[ 8].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT7 (((&delays[ 9].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT8 (((&delays[10].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT9 (((&delays[11].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT10 (((&delays[12].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT11 (((&delays[13].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT12 (((&delays[14].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT13 (((&delays[15].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT14 (((&delays[16].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT15 (((&delays[17].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tWT16 (((&delays[18].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tFD1 (((&delays[19].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tFD2 (((&delays[20].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tFD3 (((&delays[21].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tFD4 (((&delays[22].none)[(hw->target & tool78_mcu_mask) >> 4]))
|
|
#define tSD5 tFD3
|
|
#define tSS5 tWT5
|
|
#define tSD2 tFD3
|
|
#define tCS4 tWT8
|
|
#define tSD7 tFD4
|
|
#define tCS8 tWT9
|
|
#define tSD8 tWT1
|
|
#define tCS9 tWT12
|
|
#define tG23 (1000*1000) /* RL78/G23 datasheet just says "everything 1ms lol" */
|
|
|
|
// G10 proto
|
|
#define tDTR (3*1000)
|
|
#define tDRT 20
|
|
#define tCRC 1000
|
|
#define tPRO 1000
|
|
#define tVER 20000
|
|
#define tERA (350*1000)
|
|
#define tDT 1
|
|
/*#define tDR 1*/
|
|
|
|
|
|
static enum tool78_stat tool78_data_send(struct tool78_hw* hw,
|
|
int len, const uint8_t* data, bool final_block) {
|
|
//printf("datasend: len=%d data=%02x %02x %02x ...\n", len, data[0], data[1], data[2]);
|
|
if (len == 0) return tool78_stat_ack;
|
|
if (len > 0x100) return tool78_stat_internal_error;
|
|
uint8_t lenfield = len & 0xff;
|
|
|
|
uint8_t pre[2], // stx, len
|
|
post[2]; // cksum, etx/etb
|
|
pre[0] = tool78_frame_stx;
|
|
pre[1] = lenfield;
|
|
post[0] = 0; // checksum
|
|
post[1] = final_block ? tool78_frame_etx : tool78_frame_etb;
|
|
|
|
uint8_t r = 0;
|
|
r = tool78_digest_checksum8(r, 1, &pre[1]); // len
|
|
if (len) r = tool78_digest_checksum8(r, len, data);
|
|
post[0] = r;
|
|
|
|
//printf("data cksum=%02x\n", r);
|
|
|
|
int rr = hw->send(2, pre, -1);
|
|
if (rr != 2) return tool78_stat_internal_error;
|
|
|
|
if (len) {
|
|
rr = hw->send(len, data, -1);
|
|
if (rr != len) {
|
|
//printf("data not sent properly: %d\n", r);
|
|
return tool78_stat_internal_error;
|
|
}
|
|
}
|
|
|
|
rr = hw->send(2, post, -1);
|
|
if (rr != 2) return tool78_stat_internal_error;
|
|
|
|
return tool78_stat_ack;
|
|
}
|
|
|
|
static enum tool78_stat tool78_cmd_send(struct tool78_hw* hw, uint8_t cmd,
|
|
int len, const uint8_t* data) {
|
|
if (len >= 0x100) return tool78_stat_internal_error;
|
|
uint8_t lenfield = len &= 0xff;
|
|
|
|
busy_wait_us_32(tCOM); // aka tSN6(/tDN6?) (reset?) or tMB (others?)
|
|
|
|
uint8_t pre[3], // soh, len, cmd
|
|
post[2]; // cksum, etx
|
|
pre[0] = tool78_frame_soh;
|
|
pre[1] = lenfield + 1;
|
|
pre[2] = cmd;
|
|
post[0] = 0; // checksum
|
|
post[1] = tool78_frame_etx;
|
|
|
|
uint8_t r = 0;
|
|
r = tool78_digest_checksum8(r, 2, &pre[1]); // len and cmd
|
|
if (len) r = tool78_digest_checksum8(r, len, data);
|
|
post[0] = r;
|
|
|
|
int rr = hw->send(3, pre, -1);
|
|
if (rr != 3) return tool78_stat_internal_error;
|
|
|
|
if (len) {
|
|
rr = hw->send(len, data, -1);
|
|
if (rr != len) return tool78_stat_internal_error;
|
|
}
|
|
|
|
rr = hw->send(2, post, -1);
|
|
if (rr != 2) return tool78_stat_internal_error;
|
|
|
|
return tool78_stat_ack;
|
|
}
|
|
|
|
static enum tool78_stat tool78_data_recv(struct tool78_hw* hw, uint8_t* buf,
|
|
int expected_len, int timeout_us_first) {
|
|
uint8_t pre[10]; // stx, len
|
|
uint8_t post[2]; // cksum, etx/etb
|
|
//printf("recv to=%d\n", timeout_us_first);
|
|
|
|
int rr = hw->recv(1, &pre[0], timeout_us_first);
|
|
//printf("data recv rr=%d\n", rr);
|
|
if (rr == 0) return tool78_stat_busy;
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
//printf("pre[0]=0x%02x\n", pre[0]);
|
|
if (hw->target == tool78k0_spi && pre[0] == tool78_stat_busy)
|
|
return tool78_stat_busy;
|
|
if (pre[0] != tool78_frame_stx)
|
|
return (hw->flags & tool78_hw_flag_done_reset)
|
|
? tool78_stat_busy : tool78_stat_protocol_error;
|
|
|
|
rr = hw->recv(1, &pre[1], 100*100);
|
|
//printf("rr=%d (0x%08lx) pre[1]=0x%02x\n", rr, (uint32_t)rr, pre[1]);
|
|
if (rr != 1) {
|
|
/*for (int i = 0; i < rr && rr < 256; ++i)
|
|
printf("pre[%d]=0x%02x\n", i+1, pre[i+1]);
|
|
printf("interr\n");*/
|
|
return tool78_stat_internal_error;
|
|
}
|
|
|
|
int len = pre[1];
|
|
if (!len) len = 256;
|
|
|
|
if (hw->target == tool78k0_spi && pre[1] == tool78_stat_busy)
|
|
return tool78_stat_busy;
|
|
if (expected_len >= 0 && len != expected_len) {
|
|
return tool78_stat_protocol_error;
|
|
}
|
|
|
|
rr = hw->recv(len, buf, 1000*len*100);
|
|
if (rr != len) return tool78_stat_internal_error;
|
|
|
|
rr = hw->recv(2, post, 200*100);
|
|
if (rr != 2) return tool78_stat_internal_error;
|
|
if (post[1] != tool78_frame_etx && post[1] != tool78_frame_etb) {
|
|
return tool78_stat_protocol_error;
|
|
}
|
|
|
|
uint8_t ck = tool78_calc_checksum8(len, buf);
|
|
ck = tool78_digest_checksum8(ck, 1, &pre[1]);
|
|
if (ck != post[0]) {
|
|
return tool78_stat_protocol_error;
|
|
}
|
|
|
|
return tool78_stat_ack;
|
|
}
|
|
|
|
/*#define tool78_wait_status(hw, l, timeout_us, ...) \
|
|
tool78_wait_status__impl(hw, l, timeout_us, __VA_OPT__(1 ? __VA_ARGS__ : ) -1) \
|
|
*/
|
|
static enum tool78_stat tool78_wait_status/*__impl*/(struct tool78_hw* hw, int l, int timeout_us/*, int minl*/) {
|
|
/*if (hw->target == tool78k0_spi && timeout_us >= 0) {
|
|
busy_wait_us_32(timeout_us);
|
|
timeout_us *= 2;
|
|
}*/
|
|
|
|
enum tool78_stat st;
|
|
bool blockinf = timeout_us < 0;
|
|
absolute_time_t at = make_timeout_time_us(timeout_us);
|
|
timeout_state_t ts = {0};
|
|
check_timeout_fn ct = NULL;
|
|
if (!blockinf) ct = init_single_timeout_until(&ts, at);
|
|
|
|
for (int i = 0; (blockinf && i < 64) || !ct(&ts); ++i) {
|
|
if (hw->target == tool78k0_spi) {
|
|
// with SPI, a status request must be sent manually
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_status, 0, NULL);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
busy_wait_us_32(tSF);
|
|
}
|
|
|
|
st = tool78_data_recv(hw, databuf, l, timeout_us);
|
|
//printf("recv: 0x%02x\n", st);
|
|
if (st == tool78_stat_busy) goto cont;
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = databuf[0];
|
|
//printf("recvdat: 0x%02x\n", st);
|
|
if (st == tool78_stat_busy) goto cont;
|
|
|
|
return st;
|
|
|
|
cont:
|
|
busy_wait_us_32(timeout_us/64);
|
|
}
|
|
|
|
printf("wait t/o\n");
|
|
return tool78_stat_timeout_error;
|
|
}
|
|
|
|
// ----
|
|
|
|
enum tool78_stat tool78_do_reset(struct tool78_hw* hw) {
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_reset, 0, NULL);
|
|
//printf("send st=%02x\n", st);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tWT0); // aka tCS1
|
|
//printf("wait st=%02x\n", st);
|
|
if (st != tool78_stat_ack) return st;
|
|
//printf("reset cmd sent\n");
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_baud_rate_set(struct tool78_hw* hw) {
|
|
uint8_t d0x[5];
|
|
enum tool78_stat st;
|
|
|
|
switch (hw->target & tool78_mcu_mask) {
|
|
case tool78_mcu_78k0r:
|
|
// on the 78K0R/Kx3-L, baudrate set also switches to a fixed 115200 baud
|
|
// line, much like the 78K0/Kx2 interface
|
|
d0x[0] = 0x00; // uC correction mode
|
|
d0x[1] = 0x00; // fixed value
|
|
d0x[2] = 0x0a; // 115.2k
|
|
d0x[3] = 0x00; // noise filter off (0x01: noise filter on)
|
|
d0x[4] = 0x00; // we're most likely in full-speed mode (Vdd in 2.7V..5.5V
|
|
// range, needs to be 0x01 to also support down to 1.8V)
|
|
|
|
st = tool78_cmd_send(hw, tool78_cmd_baud_rate_set, 5, d0x);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
busy_wait_us_32(tWT10);
|
|
|
|
// now at 115.2kbaud, so let the physical layer switch to it
|
|
hw->set_baudrate(115200);
|
|
|
|
st = tool78_do_reset(hw);
|
|
break;
|
|
case tool78_mcu_rl78:
|
|
// TODO: don't hardcode baudrate? idk
|
|
d0x[0] = 0x00; // 0=115.2k 1=250k 2=500k 3=1M
|
|
d0x[1] = 33; // 3.3V, will switch to full-speed mode
|
|
|
|
st = tool78_cmd_send(hw, tool78_cmd_baud_rate_set, 2, d0x);
|
|
//printf("brs send=0x%02x\n", st);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, -1, tWT10); // aka tCS6 // -1 because can be 1 (OCD mode, denied) or 3
|
|
//printf("brs stat=0x%02x\n", st);
|
|
if (st != tool78_stat_ack) return st;
|
|
// let's ignore the two data bytes for now
|
|
|
|
|
|
// now at 115.2kbaud, so let the physical layer switch to it
|
|
hw->set_baudrate(115200);
|
|
|
|
hw->flags |= tool78_hw_flag_done_reset;
|
|
|
|
if (hw->flags & tool78_hw_flag_do_ocd) {
|
|
//printf("brs ok, wait for ocd...\n");
|
|
// wait for ack 0x00 byte
|
|
uint8_t byte = 0xff;
|
|
int rr = hw->recv(1, &byte, 10000);
|
|
//printf("ocd rr=%d, v=%02x\n", rr, byte);
|
|
if (rr != 1) st = tool78_stat_timeout_error;
|
|
else if (byte != 0x00) st = tool78_stat_protocol_error;
|
|
else st = tool78_stat_ack;
|
|
} else {
|
|
st = tool78_do_reset(hw);
|
|
}
|
|
break;
|
|
case tool78_mcu_78k0: default:
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_osc_freq_set(struct tool78_hw* hw) {
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_78k0)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
// OFS specifies the frequency of the EXTCLK signal (see 78k0_uart2_extclk)
|
|
// otherwise kinda irrelevant, EXCEPT THAT IS ALSO SWITCHES THE BAUDRATE TO
|
|
// A FIXED VALUE OF 115200 BAUD!
|
|
// so for now we kinda hardcode it to the value for the EXTCLK frequency to
|
|
// the one also used in 78k0_uart2_extclk.c, aka 8 MHz
|
|
uint8_t d0x[4];
|
|
// one-digit BCD, last element is an exponent
|
|
// i.e. value is (d00*100 + d01*10 + d02) * 10^d03 in Hz
|
|
d0x[0] = 8;
|
|
d0x[1] = 0;
|
|
d0x[2] = 0;
|
|
d0x[3] = 4;
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_osc_freq_set, 4, d0x);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
// now at 115.2kbaud, so let the physical layer switch to it
|
|
if (hw->target != tool78k0_spi) hw->set_baudrate(115200);
|
|
|
|
st = tool78_wait_status(hw, 1, tWT9);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
return st;
|
|
}
|
|
|
|
// -
|
|
|
|
enum tool78_stat tool78_do_chip_erase(struct tool78_hw* hw) {
|
|
if ((hw->target & tool78_mcu_mask) == tool78_mcu_rl78)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_chip_erase, 0, NULL);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
return tool78_wait_status(hw, 1, tWT1);
|
|
}
|
|
|
|
enum tool78_stat tool78_do_block_erase(struct tool78_hw* hw, uint32_t start, uint32_t end) {
|
|
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
|
if (isrl78) end = 0xffffff;
|
|
if (start >= end) return 0;
|
|
|
|
uint8_t data[6];
|
|
// on the rl78, the end address is ignored/unused and it's little-endian
|
|
if (isrl78) {
|
|
data[0] = (start >> 0) & 0xff;
|
|
data[1] = (start >> 8) & 0xff;
|
|
data[2] = (start >> 16) & 0xff;
|
|
} else {
|
|
data[0] = (start >> 16) & 0xff;
|
|
data[1] = (start >> 8) & 0xff;
|
|
data[2] = (start >> 0) & 0xff;
|
|
data[3] = (end >> 16) & 0xff;
|
|
data[4] = (end >> 8) & 0xff;
|
|
data[5] = (end >> 0) & 0xff;
|
|
}
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_block_erase,
|
|
isrl78 ? 3 : 6, data);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tWT2); // aka tCS3
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
//st = tool78_wait_status(hw, 1, tWTx); // TODO: ?
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_programming(struct tool78_hw* hw, uint32_t start,
|
|
uint32_t end, const uint8_t* src) {
|
|
if (start >= end) return 0;
|
|
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
|
|
|
uint8_t data[6];
|
|
if (isrl78) {
|
|
data[0] = (start >> 0) & 0xff;
|
|
data[1] = (start >> 8) & 0xff;
|
|
data[2] = (start >> 16) & 0xff;
|
|
data[3] = (end >> 0) & 0xff;
|
|
data[4] = (end >> 8) & 0xff;
|
|
data[5] = (end >> 16) & 0xff;
|
|
} else {
|
|
data[0] = (start >> 16) & 0xff;
|
|
data[1] = (start >> 8) & 0xff;
|
|
data[2] = (start >> 0) & 0xff;
|
|
data[3] = (end >> 16) & 0xff;
|
|
data[4] = (end >> 8) & 0xff;
|
|
data[5] = (end >> 0) & 0xff;
|
|
}
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_programming, 6, data);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tWT3); // aka tCS5
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
size_t nbytes = end - start + 1;
|
|
int nblocks = ((nbytes + 255) / 256);
|
|
for (size_t off = 0; off < nbytes; off += 256) {
|
|
busy_wait_us_32(tSD5); // aka tFD3
|
|
|
|
size_t todo = nbytes - off;
|
|
if (todo > 256) todo = 256;
|
|
bool finalblk = off + 256 >= nbytes;
|
|
//printf("prgmdata@%zu = %02x %02x %02x ...\n", off, src[off], src[off+1], src[off+2]);
|
|
st = tool78_data_send(hw, todo, &src[off], finalblk);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 2, tWT4*10); // aka tDS5
|
|
printf("prgm %zu st=%02x %02x fin=%c\n", off, st, databuf[1], finalblk?'t':'f');
|
|
if (st != tool78_stat_ack) return st;
|
|
st = databuf[1];
|
|
if (st != tool78_stat_ack) return st;
|
|
}
|
|
|
|
st = tool78_wait_status(hw, 1, isrl78 ? (tSS5*10) : (tWT5 * nblocks));
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_verify(struct tool78_hw* hw, uint32_t start,
|
|
uint32_t end, const uint8_t* src) {
|
|
if (start >= end) return 0;
|
|
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
|
|
|
uint8_t data[6];
|
|
if (isrl78) {
|
|
data[0] = (start >> 0) & 0xff;
|
|
data[1] = (start >> 8) & 0xff;
|
|
data[2] = (start >> 16) & 0xff;
|
|
data[3] = (end >> 0) & 0xff;
|
|
data[4] = (end >> 8) & 0xff;
|
|
data[5] = (end >> 16) & 0xff;
|
|
} else {
|
|
data[0] = (start >> 16) & 0xff;
|
|
data[1] = (start >> 8) & 0xff;
|
|
data[2] = (start >> 0) & 0xff;
|
|
data[3] = (end >> 16) & 0xff;
|
|
data[4] = (end >> 8) & 0xff;
|
|
data[5] = (end >> 0) & 0xff;
|
|
}
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_verify, 6, data);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tWT6); // aka tCS2
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
size_t nbytes = end - start + 1;
|
|
//int nblocks = ((nbytes + 255) / 256);
|
|
for (size_t off = 0; off < end - start; off += 256) {
|
|
busy_wait_us_32(tSD2); // aka tFD3
|
|
|
|
size_t todo = nbytes - off;
|
|
if (todo > 256) todo = 256;
|
|
bool finalblk = off + 256 >= nbytes;
|
|
st = tool78_data_send(hw, todo, &src[off], finalblk);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 2, tWT7); // aka tDS2
|
|
if (st != tool78_stat_ack) return st;
|
|
st = databuf[1];
|
|
if (st != tool78_stat_ack) return st;
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_block_blank_check(struct tool78_hw* hw,
|
|
uint32_t start, uint32_t end, bool check_flash_opt /* RL78 only */) {
|
|
if (start >= end) return 0;
|
|
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
|
|
|
uint8_t data[7];
|
|
if (isrl78) {
|
|
data[0] = (start >> 0) & 0xff;
|
|
data[1] = (start >> 8) & 0xff;
|
|
data[2] = (start >> 16) & 0xff;
|
|
data[3] = (end >> 0) & 0xff;
|
|
data[4] = (end >> 8) & 0xff;
|
|
data[5] = (end >> 16) & 0xff;
|
|
data[6] = check_flash_opt ? 1 : 0; // RL78 only
|
|
} else {
|
|
data[0] = (start >> 16) & 0xff;
|
|
data[1] = (start >> 8) & 0xff;
|
|
data[2] = (start >> 0) & 0xff;
|
|
data[3] = (end >> 16) & 0xff;
|
|
data[4] = (end >> 8) & 0xff;
|
|
data[5] = (end >> 0) & 0xff;
|
|
}
|
|
|
|
size_t nbytes = end - start + 1;
|
|
int nblocks = ((nbytes + 255) / 256);
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_block_blank_check,
|
|
isrl78 ? 7 : 6, data);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, isrl78 ? tCS4 : (tWT8 * nblocks));
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
return st;
|
|
}
|
|
|
|
static bool check_parity(uint8_t v) {
|
|
uint8_t parity = (v >> 7) & 1;
|
|
uint8_t calc = 1; // odd parity
|
|
for (size_t i = 0; i < 7; ++i) calc ^= (v >> i) & 1;
|
|
|
|
return calc == parity;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_silicon_signature(struct tool78_hw* hw,
|
|
tool78_silicon_sig_t* sig_dest) {
|
|
int nbyte = 0;
|
|
switch (hw->target & tool78_mcu_mask) {
|
|
case tool78_mcu_78k0: nbyte = 19; break;
|
|
case tool78_mcu_78k0r: nbyte = 27; break;
|
|
case tool78_mcu_rl78: nbyte = 22; break;
|
|
default: // oops
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
}
|
|
|
|
/*gpio_set_function(18, GPIO_FUNC_SIO);
|
|
gpio_set_dir(18, true);
|
|
gpio_put(18, true);*/
|
|
|
|
memset(sig_dest, 0, 27);
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_silicon_signature, 0, NULL);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
//gpio_put(18, false);
|
|
st = tool78_wait_status(hw, 1, tWT11); // aka tCS11
|
|
//gpio_put(18, true);
|
|
//printf("silisig: status 0x%02x\n", st);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
//gpio_put(18, false);
|
|
st = tool78_data_recv(hw, (uint8_t*)sig_dest, nbyte, tFD2); // aka tSD11
|
|
//gpio_put(18, true);
|
|
//printf("silisig: data 0x%02x\n", st);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
switch (hw->target & tool78_mcu_mask) {
|
|
case tool78_mcu_78k0: nbyte = 18; break;
|
|
case tool78_mcu_78k0r: nbyte = 6; break;
|
|
case tool78_mcu_rl78: nbyte = 0; break;
|
|
}
|
|
|
|
for (int i = 0; i < nbyte; ++i) {
|
|
if (!check_parity(((const uint8_t*)sig_dest)[i]))
|
|
return tool78_stat_parity_error;
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_version_get(struct tool78_hw* hw, tool78_version_t* vout) {
|
|
if ((hw->target & tool78_mcu_mask) == tool78_mcu_rl78)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_version_get, 0, NULL);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tWT12);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
memset(vout, 0, 6);
|
|
st = tool78_data_recv(hw, (uint8_t*)vout, 6, tFD2);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_checksum(struct tool78_hw* hw, uint32_t start,
|
|
uint32_t end, uint16_t* ckout) {
|
|
if (end <= start) return 0;
|
|
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
|
|
|
uint8_t data[6];
|
|
if (isrl78) {
|
|
data[0] = (start >> 0) & 0xff;
|
|
data[1] = (start >> 8) & 0xff;
|
|
data[2] = (start >> 16) & 0xff;
|
|
data[3] = (end >> 0) & 0xff;
|
|
data[4] = (end >> 8) & 0xff;
|
|
data[5] = (end >> 16) & 0xff;
|
|
} else {
|
|
data[0] = (start >> 16) & 0xff;
|
|
data[1] = (start >> 8) & 0xff;
|
|
data[2] = (start >> 0) & 0xff;
|
|
data[3] = (end >> 16) & 0xff;
|
|
data[4] = (end >> 8) & 0xff;
|
|
data[5] = (end >> 0) & 0xff;
|
|
}
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_checksum, 6, data);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tWT16); // aka tCS10
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
*ckout = 0;
|
|
st = tool78_data_recv(hw, (uint8_t*)ckout, 2, tFD1); // aka tSD10
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_security_set(struct tool78_hw* hw,
|
|
const struct tool78_security* sec) {
|
|
uint8_t data[8];
|
|
|
|
// 78k0/78k0r
|
|
data[1] = data[0] = 0;
|
|
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_security_set,
|
|
isrl78 ? 0 : 2, data);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tWT13); // aka tCS7
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
busy_wait_us_32(tSD7); // aka tFD3
|
|
switch (hw->target & tool78_mcu_mask) {
|
|
case tool78_mcu_78k0:
|
|
data[0] = sec->flg;
|
|
data[1] = 0x03; // 78k0: fixed value
|
|
st = tool78_data_send(hw, 2, data, true);
|
|
break;
|
|
case tool78_mcu_78k0r:
|
|
case tool78_mcu_rl78:
|
|
data[0] = sec->flg;
|
|
data[1] = isrl78 ? sec->bot : 0x03; // 78k0r: fixed value
|
|
// yes they switched around the endianness. sigh.
|
|
if (isrl78) {
|
|
data[2] = (sec->fsws >> 0) & 0xff;
|
|
data[3] = (sec->fsws >> 8) & 0xff;
|
|
data[4] = (sec->fswe >> 0) & 0xff;
|
|
data[5] = (sec->fswe >> 8) & 0xff;
|
|
} else {
|
|
data[2] = (sec->fsws >> 8) & 0xff;
|
|
data[3] = (sec->fsws >> 0) & 0xff;
|
|
data[4] = (sec->fswe >> 8) & 0xff;
|
|
data[5] = (sec->fswe >> 0) & 0xff;
|
|
}
|
|
data[7] = data[6] = 0xff; // fixed/reserved
|
|
st = tool78_data_send(hw, 8, data, true);
|
|
break;
|
|
default: st = tool78_stat_bad_mcu_for_cmd;
|
|
}
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tWT14); // aka tDS7
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
if (!isrl78) { // yeah...
|
|
st = tool78_wait_status(hw, 1, tWT15);
|
|
if (st != tool78_stat_ack) return st;
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_security_get(struct tool78_hw* hw,
|
|
struct tool78_security* sec) {
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
uint8_t data[8];
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_security_get, 0, NULL);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tCS8);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_data_recv(hw, data, 8, tSD8);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
sec->flg = data[0];
|
|
sec->bot = data[1];
|
|
sec->fsws = data[2] | ((uint16_t)data[3]<<8);
|
|
sec->fswe = data[4] | ((uint16_t)data[5]<<8);
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_security_release(struct tool78_hw* hw) {
|
|
// basically does the same as chip erase... oh well
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_security_release, 0, NULL);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tCS9);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
// TODO: needs a modeset/re-entry sequence now?
|
|
|
|
return st;
|
|
}
|
|
|
|
// new RL78/G23 stuff
|
|
enum tool78_stat tool78_do_g23_security_set(struct tool78_hw* hw,
|
|
const struct tool78_security* sec) {
|
|
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
|
if (!isrl78)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
uint8_t data[2];
|
|
data[0] = sec->flg | 0xe9;
|
|
data[1] = sec->flg2 | 0xfa;
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_security_set, 2, data);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tWT13 + tWT14);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_g23_security_get(struct tool78_hw* hw,
|
|
struct tool78_security* sec) {
|
|
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
|
if (!isrl78)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
uint8_t data[2];
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_security_get, 0, NULL);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tCS8);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_data_recv(hw, data, 2, tSD8);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
sec->flg = data[0];
|
|
sec->flg2 = data[1];
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_security_idauth(struct tool78_hw* hw,
|
|
const uint8_t passwd[static 10]) {
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_security_idauth, 10, passwd);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tG23);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_exopt_set(struct tool78_hw* hw,
|
|
const uint8_t exopt[static 14]) {
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_exopts_set, 14, exopt);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tG23);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_flash_rdp_set(struct tool78_hw* hw,
|
|
uint16_t block_start, uint16_t block_end, bool rdp_rw_allow) {
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
uint8_t data[4];
|
|
data[0] = block_start & 0xff;
|
|
data[1] = (block_start >> 8) | 0xfe;
|
|
data[2] = block_end & 0xff;
|
|
data[3] = ((block_end >> 8) & 1) | 0x7e | (rdp_rw_allow ? 0x80 : 0);
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_flash_rdp_set, 4, data);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tG23);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_fsw_set(struct tool78_hw* hw,
|
|
const struct tool78_fsw_settings* fswopt) {
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
uint8_t data[4];
|
|
data[0] = fswopt->block_start & 0xff;
|
|
data[1] = ((fswopt->block_start >> 8) & 1) | 0x7e
|
|
| (fswopt->fswopt_rw_allow ? 0x80 : 0);
|
|
data[2] = fswopt->block_end & 0xff;
|
|
data[3] = ((fswopt->block_end >> 8) & 1) | 0x7e
|
|
| (fswopt->fsw_area_invert ? 0x80 : 0);
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_fsw_set, 4, data);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tG23);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_do_fsw_get(struct tool78_hw* hw,
|
|
struct tool78_fsw_settings* fswopt) {
|
|
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
|
if (!isrl78)
|
|
return tool78_stat_bad_mcu_for_cmd;
|
|
|
|
uint8_t data[4];
|
|
|
|
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_fsw_get, 0, NULL);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_wait_status(hw, 1, tG23);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
st = tool78_data_recv(hw, data, 4, tG23);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
fswopt->block_start = data[0] | ((data[1] & 1) << 8);
|
|
fswopt->block_end = data[2] | ((data[3] & 1) << 8);
|
|
fswopt->fswopt_rw_allow = data[1] & 0x80;
|
|
fswopt->fsw_area_invert = data[3] & 0x80;
|
|
|
|
return st;
|
|
}
|
|
|
|
// ----
|
|
|
|
enum tool78_stat tool78_do_g10_get_flash_size(struct tool78_hw* hw,
|
|
enum tool78_g10_flash_size* fsz) {
|
|
busy_wait_us_32(tDTR);
|
|
|
|
// start crc calc cmd
|
|
uint8_t data[3];
|
|
data[0] = tool78_cmd_crc_check;
|
|
int rr = hw->send(1, data, -1);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
|
|
data[1] = data[2] = 0;
|
|
rr = hw->recv(3, data, (tDT+tDRT)*100); // cmd is echoed back
|
|
//printf("gfsz rr=%d data=%02x %02x %02x\n", rr, data[0], data[1], data[2]);
|
|
if (rr != 3) return tool78_stat_timeout_error;
|
|
if (data[1] != tool78_stat_ack) return data[1];
|
|
|
|
*fsz = data[2];
|
|
|
|
busy_wait_us_32(tDTR);
|
|
data[0] = tool78_stat_nak; // should send ack here, but send nak to stop crc calc
|
|
rr = hw->send(1, data, -1);
|
|
//printf("send nak: rr=%d data=%02x\n", rr, data[0]);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
|
|
rr = hw->recv(3, data, tCRC);
|
|
//printf("recv rr=%d data=%02x %02x %02x\n", rr, data[0], data[1], data[2]);
|
|
// should answer with 0x04 but let's not care here
|
|
|
|
return tool78_stat_ack;
|
|
}
|
|
enum tool78_stat tool78_do_g10_check_crc(struct tool78_hw* hw,
|
|
enum tool78_g10_flash_size fsz, uint16_t* crc) {
|
|
busy_wait_us_32(tDTR);
|
|
|
|
uint8_t data[3];
|
|
data[0] = tool78_cmd_crc_check;
|
|
int rr = hw->send(1, data, -1);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
|
|
rr = hw->recv(3, data, (tDT+tDRT)*100);
|
|
if (rr != 3) return tool78_stat_timeout_error;
|
|
if (data[1] != tool78_stat_ack) return data[1];
|
|
|
|
busy_wait_us_32(tDTR);
|
|
if (data[2] != fsz) {
|
|
data[0] = tool78_stat_nak; // should send ack here, but send nak to stop crc calc
|
|
rr = hw->send(1, data, -1);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
|
|
rr = hw->recv(3, data, tCRC);
|
|
// should answer with 0x04 but let's not care here
|
|
|
|
return tool78_stat_bad_flash_size;
|
|
} else {
|
|
data[0] = tool78_stat_ack;
|
|
rr = hw->send(1, data, -1);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
|
|
rr = hw->recv(2, data, tCRC*2);
|
|
if (rr != 2) return tool78_stat_timeout_error;
|
|
if (data[0] == 0xff) { // framing error bullshit
|
|
data[0] = data[1];
|
|
rr = hw->recv(1, &data[1], tCRC);
|
|
if (rr != 1) return tool78_stat_timeout_error;
|
|
}
|
|
//printf("recv1 rr=%d data=%02x %02x\n", rr, data[0], data[1]);
|
|
enum tool78_stat st = data[1];
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
rr = hw->recv(2, data, tDT*1000);
|
|
//printf("recv2 rr=%d data=%02x %02x %02x\n", rr, data[0], data[1], data[2]);
|
|
if (rr != 2) return tool78_stat_timeout_error;
|
|
if (data[0] == 0xff) {
|
|
data[0] = data[1];
|
|
rr = hw->recv(1, &data[1], tDT*500);
|
|
if (rr != 1) return tool78_stat_timeout_error;
|
|
}
|
|
//printf("recv2 rr=%d data=%02x %02x %02x\n", rr, data[0], data[1], data[2]);
|
|
|
|
*crc = data[0] | ((uint16_t)data[1] << 8);
|
|
|
|
return tool78_stat_ack;
|
|
}
|
|
}
|
|
enum tool78_stat tool78_do_g10_erase_then_write(struct tool78_hw* hw,
|
|
enum tool78_g10_flash_size fsz, const uint8_t* data_to_wr, size_t datalen) {
|
|
busy_wait_us_32(tDTR);
|
|
|
|
uint8_t data[5];
|
|
data[0] = tool78_cmd_write_after_erase;
|
|
int rr = hw->send(1, data, -1);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
|
|
rr = hw->recv(3, data, (tDT+tDRT)*100);
|
|
printf("recv1 rr=%d data=%02x %02x %02x\n", rr, data[0], data[1], data[2]);
|
|
if (rr != 3) return tool78_stat_timeout_error;
|
|
if (data[1] != tool78_stat_ack) return data[1];
|
|
|
|
busy_wait_us_32(tDTR);
|
|
if (data[2] != fsz) {
|
|
data[0] = tool78_stat_nak; // should send ack here, but send nak to stop crc calc
|
|
rr = hw->send(1, data, -1);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
|
|
rr = hw->recv(3, data, tERA);
|
|
// should answer with 0x04 but let's not care here
|
|
|
|
return tool78_stat_bad_flash_size;
|
|
} else {
|
|
data[0] = tool78_stat_ack;
|
|
rr = hw->send(1, data, -1);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
|
|
rr = hw->recv(2, data, tERA);
|
|
if (rr != 2) return tool78_stat_timeout_error;
|
|
if (data[0] == 0xff) { // framing error bullshit
|
|
data[0] = data[1];
|
|
rr = hw->recv(1, &data[1], tERA);
|
|
if (rr != 1) return tool78_stat_timeout_error;
|
|
}
|
|
printf("recv2 rr=%d data=%02x %02x\n", rr, data[0], data[1]);
|
|
enum tool78_stat st = data[1];
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
size_t flash_sz = (fsz + 1) * 256;
|
|
for (size_t i = 0; i < flash_sz; i += 4) {
|
|
data[0] = ((i+0) < datalen) ? data_to_wr[i+0] : 0xff;
|
|
data[1] = ((i+1) < datalen) ? data_to_wr[i+1] : 0xff;
|
|
data[2] = ((i+2) < datalen) ? data_to_wr[i+2] : 0xff;
|
|
data[3] = ((i+3) < datalen) ? data_to_wr[i+3] : 0xff;
|
|
|
|
busy_wait_us_32(tDTR);
|
|
rr = hw->send(1, &data[0], -1);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
busy_wait_us_32(tDRT*20);
|
|
rr = hw->send(1, &data[1], -1);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
busy_wait_us_32(tDRT*20);
|
|
rr = hw->send(1, &data[2], -1);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
busy_wait_us_32(tDRT*20);
|
|
rr = hw->send(1, &data[3], -1);
|
|
if (rr != 1) return tool78_stat_internal_error;
|
|
|
|
rr = hw->recv(5, data, tPRO);
|
|
printf("recv3 i=%04zx rr=%d data=%02x %02x %02x %02x %02x\n", i, rr, data[0],
|
|
data[1], data[2], data[3], data[4]);
|
|
if (rr != 5) return tool78_stat_timeout_error;
|
|
st = data[4];
|
|
if (st != tool78_stat_ack) return st;
|
|
}
|
|
|
|
rr = hw->recv(2, data, tVER*100);
|
|
printf("recv4 rr=%d data=%02x %02x\n", rr, data[0], data[1]);
|
|
if (rr != 2) return tool78_stat_timeout_error;
|
|
st = data[1];
|
|
|
|
return st;
|
|
}
|
|
}
|
|
// ----
|
|
|
|
int tool78_ocd_version(struct tool78_hw* hw, uint16_t* ver) {
|
|
bool is_g10 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78g10;
|
|
if (is_g10) return -5;
|
|
|
|
busy_wait_ms(1);
|
|
|
|
uint8_t bytes[3];
|
|
bytes[0] = tool78ocd_cmd_version;
|
|
int rr = hw->send(1, &bytes[0], -1);
|
|
|
|
// bytes[0] is tool78ocd_cmd_version echoed back
|
|
rr = hw->recv(3, bytes, 1000);
|
|
if (rr != 3) {
|
|
/*printf("OCDver rr: %d\n", rr);
|
|
for (int iii = 0; iii < rr; ++iii) {
|
|
printf("0x%02x%c", bytes[iii], (iii==rr-1)?'\n':' ');
|
|
}*/
|
|
return -1;
|
|
}
|
|
|
|
if (rr == 3) {
|
|
*ver = bytes[1] | ((uint16_t)bytes[2] << 8);
|
|
} /*else {
|
|
*ver = bytes[0] | ((uint16_t)bytes[1] << 8);
|
|
}*/
|
|
//printf("ver=%04x\n", *ver);
|
|
return 0;
|
|
}
|
|
int tool78_ocd_connect(struct tool78_hw* hw, const uint8_t passwd[10]) {
|
|
busy_wait_ms(1);
|
|
uint8_t stuff[11];
|
|
uint8_t connst;
|
|
bool is_g10 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78g10;
|
|
stuff[0] = is_g10 ? tool78ocd_cmdg10_connect : tool78ocd_cmd_connect;
|
|
int rr = hw->send(1, &stuff[0], -1);
|
|
|
|
// stuff[1] is tool78ocd_cmd_connect echoed back
|
|
rr = hw->recv(2, &stuff[1], 1000);
|
|
if (rr != 2) {
|
|
/*printf("OCDconn rr: %d\n", rr);
|
|
for (int iii = 0; iii < rr; ++iii) {
|
|
printf("0x%02x%c", stuff[1+iii], (iii==rr-1)?'\n':' ');
|
|
}*/
|
|
return -1;
|
|
}
|
|
connst=stuff[2];
|
|
//printf("connst 1=%02x\n", connst);
|
|
if (connst == 0xff) {
|
|
rr = hw->recv(1, &connst, 1000);
|
|
if (rr != 1) return -1;
|
|
}
|
|
//printf("connst 2=%02x\n", connst);
|
|
if (connst == 0xf0 || connst == 0xf4) return connst;
|
|
// connst==0xf1
|
|
|
|
memcpy(stuff, passwd, 10);
|
|
if (!is_g10) {
|
|
stuff[10] = tool78_calc_ocd_checksum8(10, passwd);
|
|
}
|
|
|
|
busy_wait_us_32(100);
|
|
rr = hw->send(is_g10 ? 10 : 11, stuff, -1);
|
|
|
|
// stuff[9] is the checksum byte echoed back
|
|
rr = hw->recv(2, &stuff[9], 100000);
|
|
//printf("ocdpw 2 = %02x %02x\n", stuff[9], stuff[10]);
|
|
if (rr != 2) {
|
|
/*printf("OCDconn2 rr: %d\n", rr);
|
|
for (int iii = 0; iii < rr; ++iii) {
|
|
printf("0x%02x%c", stuff[9+iii], (iii==rr-1)?'\n':' ');
|
|
}*/
|
|
return -2;
|
|
}
|
|
if (stuff[9] == 0xff && is_g10) { // framing error bullshit
|
|
stuff[9] = stuff[10];
|
|
rr = hw->recv(1, &stuff[10], 100000);
|
|
if (rr != 1) return -2;
|
|
}
|
|
//printf("ocdpw 3 = %02x %02x\n", stuff[9], stuff[10]);
|
|
while (stuff[10] == 0) {
|
|
rr = hw->recv(1, &stuff[10], 100000);
|
|
if (rr != 1) return -2;
|
|
}
|
|
//printf("ocdpw 4 = %02x %02x\n", stuff[9], stuff[10]);
|
|
|
|
/*printf("cksum=0x%02x conn=%02x pw=%02x\n",
|
|
cksum, connst, stuff[10]);*/
|
|
|
|
//printf("connst=%u stuff=%u\n", connst, stuff[10]);
|
|
return stuff[10];//(uint32_t)connst | ((uint32_t)stuff[10] << 8);
|
|
}
|
|
int tool78_ocd_read(struct tool78_hw* hw, uint16_t off, uint8_t len,
|
|
uint8_t* data) {
|
|
busy_wait_ms(1);
|
|
|
|
bool is_g10 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78g10;
|
|
|
|
uint8_t hdr[4];
|
|
int rr;
|
|
|
|
if (is_g10) {
|
|
if (len == 2) {
|
|
hdr[0] = tool78ocd_cmdg10_setptr;
|
|
hdr[1] = off & 0xff; // l
|
|
hdr[2] = off >> 8; // h
|
|
hdr[3] = 1; // b
|
|
rr = hw->send(sizeof hdr, hdr, -1);
|
|
if (rr != sizeof hdr) return -1;
|
|
|
|
// command echoed back
|
|
rr = hw->recv(4, hdr, 1000);
|
|
if (rr != 4) return -2;
|
|
if (hdr[0] == 0xff) { // frame error bullshit
|
|
hdr[0] = hdr[1];
|
|
hdr[1] = hdr[2];
|
|
hdr[2] = hdr[3];
|
|
rr = hw->recv(1, &hdr[3], 1000);
|
|
if (rr != 1) return -2;
|
|
}
|
|
|
|
// status
|
|
rr = hw->recv(1, hdr, 1000);
|
|
if (rr != 1) return -1;
|
|
if (hdr[0] != 0x00) return -3;
|
|
|
|
busy_wait_us_32(100);
|
|
hdr[0] = tool78ocd_cmdg10_read_word;
|
|
rr = hw->send(1, hdr, -1);
|
|
if (rr != 1) return -1;
|
|
|
|
rr = hw->recv(1, hdr, 1000); // command echoed back
|
|
if (rr != 1) return -1;
|
|
|
|
rr = hw->recv(2, data, 1000*len);
|
|
if (rr != 2) return -2;
|
|
return 0;
|
|
} else {
|
|
hdr[0] = tool78ocd_cmdg10_setptr;
|
|
hdr[1] = off & 0xff; // l
|
|
hdr[2] = off >> 8; // h
|
|
hdr[3] = len; // b
|
|
rr = hw->send(sizeof hdr, hdr, -1);
|
|
if (rr != sizeof hdr) return -1;
|
|
//printf("sent hdr\n");
|
|
|
|
rr = hw->recv(4, hdr, 1000);
|
|
//printf("stat rr=%d hdr=%02x %02x %02x %02x\n", rr, hdr[0], hdr[1], hdr[2], hdr[3]);
|
|
if (rr != 4) return -2;
|
|
if (hdr[0] == 0xff) { // frame error bullshit
|
|
hdr[0] = hdr[1];
|
|
hdr[1] = hdr[2];
|
|
hdr[2] = hdr[3];
|
|
rr = hw->recv(1, &hdr[3], 1000);
|
|
if (rr != 1) return -2;
|
|
}
|
|
rr = hw->recv(1, hdr, 1000);
|
|
//printf("stat rr=%d hdr=%02x\n", rr, hdr[0]);
|
|
if (rr != 1) return -1;
|
|
if (hdr[0] != 0x00) return -3;
|
|
|
|
busy_wait_us_32(100);
|
|
hdr[0] = tool78ocd_cmdg10_read_raw;
|
|
rr = hw->send(1, hdr, -1);
|
|
if (rr != 1) return -1;
|
|
//printf("sent rdcmd\n");
|
|
|
|
rr = hw->recv(1, hdr, 1000); // command echoed back
|
|
//printf("recv dd=%d hdr=%02x\n", rr, hdr[0]);
|
|
if (rr != 1) return -1;
|
|
|
|
rr = hw->recv(len, data, 1000*len);
|
|
//printf("got data rr=%d data=%02x\n", rr, data[0]);
|
|
if (rr != len) return -2;
|
|
return 0;
|
|
}
|
|
} else {
|
|
hdr[0] = tool78ocd_cmd_read;
|
|
hdr[2] = off >> 8;
|
|
hdr[1] = off & 0xff;
|
|
hdr[3] = len;
|
|
//printf("rd len=%u\n", len);
|
|
|
|
int rr = hw->send(sizeof hdr, hdr, -1);
|
|
if (rr != sizeof hdr) return -1;
|
|
rr = hw->recv(1, &hdr[2], 1000);
|
|
// last byte of header sent echoed back
|
|
if (rr != 1 || hdr[2] != hdr[3]) {
|
|
//printf("rd recv=%02x\n", hdr[2]);
|
|
return -2;
|
|
}
|
|
|
|
rr = hw->recv(len, data, 1000*len);
|
|
//printf("ocd read %d\n", rr);
|
|
return (rr == len) ? 0 : -1;
|
|
}
|
|
}
|
|
int tool78_ocd_write(struct tool78_hw* hw, uint16_t addr, uint8_t len,
|
|
const uint8_t* data) {
|
|
if (len == 0) return -1;
|
|
busy_wait_ms(1);
|
|
|
|
bool is_g10 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78g10;
|
|
|
|
uint8_t hdr[4];
|
|
int rr;
|
|
|
|
if (is_g10) {
|
|
if (len == 2) {
|
|
hdr[0] = tool78ocd_cmdg10_setptr;
|
|
hdr[1] = addr & 0xff; // l
|
|
hdr[2] = addr >> 8; // h
|
|
hdr[3] = data[1]; // b
|
|
|
|
rr = hw->send(sizeof hdr, hdr, -1);
|
|
if (rr != sizeof hdr) return -1;
|
|
|
|
// command echoed back
|
|
rr = hw->recv(4, hdr, 1000);
|
|
if (rr != 4) return -2;
|
|
if (hdr[0] == 0xff) { // frame error bullshit
|
|
hdr[0] = hdr[1];
|
|
hdr[1] = hdr[2];
|
|
hdr[2] = hdr[3];
|
|
rr = hw->recv(1, &hdr[3], 1000);
|
|
if (rr != 1) return -2;
|
|
}
|
|
|
|
// status
|
|
rr = hw->recv(1, hdr, 1000);
|
|
if (rr != 1) return -1;
|
|
if (hdr[0] != 0x00) return -3;
|
|
|
|
hdr[0] = tool78ocd_cmdg10_write_word;
|
|
hdr[1] = data[0]; // a
|
|
|
|
busy_wait_us_32(100);
|
|
rr = hw->send(2, hdr, -1);
|
|
if (rr != 2) return -1;
|
|
|
|
rr = hw->recv(3, hdr, 1000); // command echoed back + ack
|
|
if (rr != 3) return -1;
|
|
|
|
return (hdr[2] == 0) ? 0 : -2;
|
|
} else {
|
|
hdr[0] = tool78ocd_cmdg10_setptr;
|
|
hdr[1] = addr & 0xff; // l
|
|
hdr[2] = addr >> 8; // h
|
|
hdr[3] = len; // b
|
|
|
|
rr = hw->send(sizeof hdr, hdr, -1);
|
|
if (rr != sizeof hdr) return -1;
|
|
|
|
// command echoed back
|
|
rr = hw->recv(4, hdr, 1000);
|
|
printf("wr p1: rr=%d %02x %02x %02x %02x\n", rr, hdr[0],
|
|
hdr[1], hdr[2], hdr[3]);
|
|
if (rr != 4) return -2;
|
|
if (hdr[0] == 0xff) { // frame error bullshit
|
|
hdr[0] = hdr[1];
|
|
hdr[1] = hdr[2];
|
|
hdr[2] = hdr[3];
|
|
rr = hw->recv(1, &hdr[3], 1000);
|
|
if (rr != 1) return -2;
|
|
}
|
|
printf("wr p1: rr=%d %02x %02x %02x %02x\n", rr, hdr[0],
|
|
hdr[1], hdr[2], hdr[3]);
|
|
|
|
// status
|
|
rr = hw->recv(1, hdr, 1000);
|
|
printf("wr p2: rr=%d %02x %02x %02x %02x\n", rr, hdr[0],
|
|
hdr[1], hdr[2], hdr[3]);
|
|
if (rr != 1) return -1;
|
|
if (hdr[0] == len) {
|
|
rr = hw->recv(1, hdr, 1000);
|
|
printf("wr p2: rr=%d %02x %02x %02x %02x\n", rr, hdr[0],
|
|
hdr[1], hdr[2], hdr[3]);
|
|
if (rr != 1) return -1;
|
|
}
|
|
if (hdr[0] != 0x00) return -3;
|
|
|
|
hdr[0] = tool78ocd_cmdg10_write_raw;
|
|
|
|
busy_wait_us_32(100);
|
|
rr = hw->send(1, hdr, -1);
|
|
if (rr != 1) return -1;
|
|
rr = hw->recv(1, hdr, 1000); // sigh
|
|
printf("wr p3: rr=%d %02x %02x %02x %02x\n", rr, hdr[0],
|
|
hdr[1], hdr[2], hdr[3]);
|
|
if (rr != 1) return -1;
|
|
|
|
for (int i = 0; i < len; i += 8) {
|
|
int todo = len - i;
|
|
if (todo > 8) todo = 8;
|
|
|
|
busy_wait_us_32(100);
|
|
rr = hw->send(todo, &data[i], -1);
|
|
if (rr != todo) return -1;
|
|
rr = hw->recv(todo, NULL, 1000*todo); // sigh
|
|
printf("wr p4: rr=%d %02x %02x %02x %02x\n", rr, hdr[0],
|
|
hdr[1], hdr[2], hdr[3]);
|
|
if (rr != todo) return -1;
|
|
}
|
|
|
|
rr = hw->recv(1, hdr, 1000);
|
|
printf("wr p5: rr=%d %02x %02x %02x %02x\n", rr, hdr[0],
|
|
hdr[1], hdr[2], hdr[3]);
|
|
printf("recv %d: %02x %02x\n", rr, hdr[0], hdr[1]);
|
|
if (rr != 1) return -1;
|
|
|
|
return (hdr[0] == 0) ? 0 : -2;
|
|
}
|
|
} else {
|
|
hdr[0] = tool78ocd_cmd_write;
|
|
hdr[1] = addr & 0xff;
|
|
hdr[2] = addr >> 8;
|
|
hdr[3] = len;
|
|
|
|
int rr = hw->send(sizeof hdr, hdr, -1);
|
|
if (rr != sizeof hdr) return -1;
|
|
rr = hw->send(len, data, -1);
|
|
if (rr != len) return -1;
|
|
|
|
// last data byte echoed back
|
|
rr = hw->recv(2, hdr, 1000*len);
|
|
if (rr != 2) return -1;
|
|
///printf("ocd write 0x%02x\n", hdr[1]);
|
|
return (hdr[1] == tool78ocd_cmd_write) ? 0 : -2;
|
|
}
|
|
}
|
|
int tool78_ocd_exec(struct tool78_hw* hw) {
|
|
busy_wait_ms(1);
|
|
|
|
bool is_g10 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78g10;
|
|
|
|
uint8_t hdr[2], cmd;
|
|
cmd = hdr[0] = is_g10 ? tool78ocd_cmdg10_exec : tool78ocd_cmd_exec;
|
|
|
|
int rr = hw->send(1, hdr, -1);
|
|
|
|
// G13 sends status before exec, G10 sends it after
|
|
rr = hw->recv(is_g10 ? 1 : 2, hdr, 1000);
|
|
if (rr != 1 && rr != 2) return -1;
|
|
printf("exec r=%d: h[0]=0x%02x, h[1]=0x%02x\n", rr, hdr[0], hdr[1]);
|
|
|
|
return 0;//(hdr[0] == cmd) ? 0 : -2;
|
|
}
|
|
int tool78_ocd_leave(struct tool78_hw* hw, bool exit_to_ram) {
|
|
busy_wait_ms(1);
|
|
|
|
bool is_g10 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78g10;
|
|
|
|
uint8_t hdr[2];
|
|
|
|
if (is_g10) {
|
|
hdr[0] = tool78ocd_cmdg10_exit_reti;
|
|
} else {
|
|
hdr[0] = exit_to_ram ? tool78ocd_cmd_exit_ram : tool78ocd_cmd_exit_reti;
|
|
}
|
|
|
|
int rr = hw->send(1, hdr, -1);
|
|
|
|
if (is_g10) {
|
|
rr = hw->recv(2, &hdr[0], 1000);
|
|
if (rr != 2) return -1;
|
|
|
|
return 0;
|
|
} else {
|
|
rr = hw->recv(1, &hdr[1], 1000);
|
|
if (rr != 1) return -1;
|
|
|
|
return (hdr[0] == hdr[1]) ? 0 : -2;
|
|
}
|
|
}
|
|
|
|
// ----
|
|
|
|
static enum tool78_stat tool78_init_common(struct tool78_hw* hw) {
|
|
if (!hw) return tool78_stat_internal_error;
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78g10) {
|
|
hw->flags &= ~tool78_hw_flag_done_reset;
|
|
} else {
|
|
hw->flags |= tool78_hw_flag_done_reset;
|
|
}
|
|
|
|
enum tool78_stat st = tool78_stat_ack;
|
|
for (size_t i = 0; i < 16; ++i) {
|
|
if (!hw->init()) return tool78_stat_fatal_hw_error;
|
|
//printf("init %zu ok\n", i);
|
|
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78
|
|
&& (hw->target & tool78_mcu_mask) != tool78_mcu_rl78g10) {
|
|
st = tool78_do_reset(hw);
|
|
//printf("done reset st=0x%02x\n", st);
|
|
}
|
|
if (st == tool78_stat_timeout_error) {
|
|
hw->deinit();
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (st != tool78_stat_ack) return st;
|
|
//printf("hw inited\n");
|
|
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78g10) {
|
|
st = tool78_do_generic_baudrate(hw);
|
|
//printf("baudrate result=0x%02x\n", st);
|
|
if (st != tool78_stat_ack) return st;
|
|
} else {
|
|
// 0x3a/0xc5 is echoed back as well
|
|
uint8_t data[5];
|
|
int rr = hw->recv(2, data, 1000);
|
|
st = data[1];
|
|
//printf("init rr:%d st=%02x\n", rr, st);
|
|
if (rr != 2) return tool78_stat_timeout_error;
|
|
|
|
if (st == 0x06) {
|
|
// should be 5 zero bytes, but let's not check
|
|
rr = hw->recv(5, data, 5*1000);
|
|
if (rr != 5) return tool78_stat_no_ocd_status;
|
|
}
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_init_sfp(struct tool78_hw* hw, tool78_silicon_sig_t* sig) {
|
|
hw->flags &= ~tool78_hw_flag_do_ocd;
|
|
|
|
enum tool78_stat st = tool78_init_common(hw);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78g10) {
|
|
st = tool78_do_silicon_signature(hw, sig);
|
|
//printf("sig result=0x%02x\n", st);
|
|
if (st != tool78_stat_ack) return st;
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
enum tool78_stat tool78_init_ocd(struct tool78_hw* hw, uint16_t* ver,
|
|
const uint8_t passwd[10]) {
|
|
hw->flags |= tool78_hw_flag_do_ocd;
|
|
int st = tool78_init_common(hw);
|
|
//printf("common st=%d\n", st);
|
|
if (st != tool78_stat_ack) return st;
|
|
|
|
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78g10) {
|
|
st = tool78_ocd_version(hw, ver);
|
|
//printf("ocdver st=%d\n", st);
|
|
if (st) return st;
|
|
}
|
|
|
|
st = tool78_ocd_connect(hw, passwd);
|
|
//printf("conn st=%d\n", st);
|
|
/*uint8_t i = 0;
|
|
do {
|
|
st = tool78_ocd_connect(hw, passwd, i);
|
|
++i;
|
|
|
|
if (i == 0) break;
|
|
} while ((st & 0xff00) == 0xf300 && (st & 0xff) == 0xf1);*/
|
|
|
|
return st;
|
|
}
|
|
|