377 lines
11 KiB
Java
377 lines
11 KiB
Java
/*
|
|
* Copyright © 2010 Keith Packard <keithp@keithp.com>
|
|
*
|
|
* 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.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
*/
|
|
|
|
package org.altusmetrum.altoslib_14;
|
|
|
|
import java.io.*;
|
|
|
|
public class AltosFlash extends AltosProgrammer {
|
|
File file;
|
|
FileInputStream input;
|
|
AltosHexfile image;
|
|
AltosLink link;
|
|
AltosDebug debug;
|
|
AltosRomconfig rom_config;
|
|
boolean aborted;
|
|
AltosFlashListener listener;
|
|
|
|
static final byte MOV_direct_data = (byte) 0x75;
|
|
static final byte MOV_DPTR_data16 = (byte) 0x90;
|
|
static final byte MOV_A_data = (byte) 0x74;
|
|
static final byte MOVX_atDPTR_A = (byte) 0xf0;
|
|
static final byte MOVX_A_atDPTR = (byte) 0xe0;
|
|
static final byte INC_DPTR = (byte) 0xa3;
|
|
static final byte TRAP = (byte) 0xa5;
|
|
|
|
static final byte JB = (byte) 0x20;
|
|
|
|
static final byte MOV_A_direct = (byte) 0xe5;
|
|
static final byte MOV_direct1_direct2 = (byte) 0x85;
|
|
static final byte MOV_direct_A = (byte) 0xf5;
|
|
static final byte MOV_R0_data = (byte) (0x78 | 0);
|
|
static final byte MOV_R1_data = (byte) (0x78 | 1);
|
|
static final byte MOV_R2_data = (byte) (0x78 | 2);
|
|
static final byte MOV_R3_data = (byte) (0x78 | 3);
|
|
static final byte MOV_R4_data = (byte) (0x78 | 4);
|
|
static final byte MOV_R5_data = (byte) (0x78 | 5);
|
|
static final byte MOV_R6_data = (byte) (0x78 | 6);
|
|
static final byte MOV_R7_data = (byte) (0x78 | 7);
|
|
static final byte DJNZ_R0_rel = (byte) (0xd8 | 0);
|
|
static final byte DJNZ_R1_rel = (byte) (0xd8 | 1);
|
|
static final byte DJNZ_R2_rel = (byte) (0xd8 | 2);
|
|
static final byte DJNZ_R3_rel = (byte) (0xd8 | 3);
|
|
static final byte DJNZ_R4_rel = (byte) (0xd8 | 4);
|
|
static final byte DJNZ_R5_rel = (byte) (0xd8 | 5);
|
|
static final byte DJNZ_R6_rel = (byte) (0xd8 | 6);
|
|
static final byte DJNZ_R7_rel = (byte) (0xd8 | 7);
|
|
|
|
static final byte P1DIR = (byte) 0xFE;
|
|
static final byte P1 = (byte) 0x90;
|
|
|
|
/* flash controller */
|
|
static final byte FWT = (byte) 0xAB;
|
|
static final byte FADDRL = (byte) 0xAC;
|
|
static final byte FADDRH = (byte) 0xAD;
|
|
static final byte FCTL = (byte) 0xAE;
|
|
static final byte FCTL_BUSY = (byte) 0x80;
|
|
static final byte FCTL_BUSY_BIT = (byte) 7;
|
|
static final byte FCTL_SWBSY = (byte) 0x40;
|
|
static final byte FCTL_SWBSY_BIT = (byte) 6;
|
|
static final byte FCTL_CONTRD = (byte) 0x10;
|
|
static final byte FCTL_WRITE = (byte) 0x02;
|
|
static final byte FCTL_ERASE = (byte) 0x01;
|
|
static final byte FWDATA = (byte) 0xAF;
|
|
|
|
static final byte ACC = (byte) 0xE0;
|
|
|
|
/* offsets within the flash_page program */
|
|
static final int FLASH_ADDR_HIGH = 8;
|
|
static final int FLASH_ADDR_LOW = 11;
|
|
static final int RAM_ADDR_HIGH = 13;
|
|
static final int RAM_ADDR_LOW = 14;
|
|
static final int FLASH_WORDS_HIGH = 16;
|
|
static final int FLASH_WORDS_LOW = 18;
|
|
static final int FLASH_TIMING = 21;
|
|
|
|
/* sleep mode control */
|
|
static final int SLEEP = (byte) 0xbe;
|
|
static final int SLEEP_USB_EN = (byte) 0x80;
|
|
static final int SLEEP_XOSC_STB = (byte) 0x40;
|
|
static final int SLEEP_HFRC_STB = (byte) 0x20;
|
|
static final int SLEEP_RST_MASK = (byte) 0x18;
|
|
static final int SLEEP_RST_POWERON = (byte) 0x00;
|
|
static final int SLEEP_RST_EXTERNAL = (byte) 0x10;
|
|
static final int SLEEP_RST_WATCHDOG = (byte) 0x08;
|
|
static final int SLEEP_OSC_PD = (byte) 0x04;
|
|
static final int SLEEP_MODE_MASK = (byte) 0x03;
|
|
static final int SLEEP_MODE_PM0 = (byte) 0x00;
|
|
static final int SLEEP_MODE_PM1 = (byte) 0x01;
|
|
static final int SLEEP_MODE_PM2 = (byte) 0x02;
|
|
static final int SLEEP_MODE_PM3 = (byte) 0x03;
|
|
|
|
/* clock controller */
|
|
static final byte CLKCON = (byte) 0xC6;
|
|
static final byte CLKCON_OSC32K = (byte) 0x80;
|
|
static final byte CLKCON_OSC = (byte) 0x40;
|
|
static final byte CLKCON_TICKSPD = (byte) 0x38;
|
|
static final byte CLKCON_CLKSPD = (byte) 0x07;
|
|
|
|
static final byte[] flash_page_proto = {
|
|
|
|
MOV_direct_data, P1DIR, (byte) 0x02,
|
|
MOV_direct_data, P1, (byte) 0xFF,
|
|
|
|
MOV_direct_data, FADDRH, 0, /* FLASH_ADDR_HIGH */
|
|
|
|
MOV_direct_data, FADDRL, 0, /* FLASH_ADDR_LOW */
|
|
|
|
MOV_DPTR_data16, 0, 0, /* RAM_ADDR_HIGH, RAM_ADDR_LOW */
|
|
|
|
MOV_R7_data, 0, /* FLASH_WORDS_HIGH */
|
|
|
|
MOV_R6_data, 0, /* FLASH_WORDS_LOW */
|
|
|
|
|
|
MOV_direct_data, FWT, 0x20, /* FLASH_TIMING */
|
|
|
|
MOV_direct_data, FCTL, FCTL_ERASE,
|
|
/* eraseWaitLoop: */
|
|
MOV_A_direct, FCTL,
|
|
JB, ACC|FCTL_BUSY_BIT, (byte) 0xfb,
|
|
|
|
MOV_direct_data, P1, (byte) 0xfd,
|
|
|
|
MOV_direct_data, FCTL, FCTL_WRITE,
|
|
/* writeLoop: */
|
|
MOV_R5_data, 2,
|
|
/* writeWordLoop: */
|
|
MOVX_A_atDPTR,
|
|
INC_DPTR,
|
|
MOV_direct_A, FWDATA,
|
|
DJNZ_R5_rel, (byte) 0xfa, /* writeWordLoop */
|
|
/* writeWaitLoop: */
|
|
MOV_A_direct, FCTL,
|
|
JB, ACC|FCTL_SWBSY_BIT, (byte) 0xfb, /* writeWaitLoop */
|
|
DJNZ_R6_rel, (byte) 0xf1, /* writeLoop */
|
|
DJNZ_R7_rel, (byte) 0xef, /* writeLoop */
|
|
|
|
MOV_direct_data, P1DIR, (byte) 0x00,
|
|
MOV_direct_data, P1, (byte) 0xFF,
|
|
TRAP,
|
|
};
|
|
|
|
public byte[] make_flash_page(int flash_addr, int ram_addr, int byte_count) {
|
|
int flash_word_addr = flash_addr >> 1;
|
|
int flash_word_count = ((byte_count + 1) >> 1);
|
|
|
|
byte[] flash_page = new byte[flash_page_proto.length];
|
|
for (int i = 0; i < flash_page.length; i++)
|
|
flash_page[i] = flash_page_proto[i];
|
|
|
|
flash_page[FLASH_ADDR_HIGH] = (byte) (flash_word_addr >> 8);
|
|
flash_page[FLASH_ADDR_LOW] = (byte) (flash_word_addr);
|
|
flash_page[RAM_ADDR_HIGH] = (byte) (ram_addr >> 8);
|
|
flash_page[RAM_ADDR_LOW] = (byte) (ram_addr);
|
|
|
|
byte flash_words_low = (byte) (flash_word_count);
|
|
byte flash_words_high = (byte) (flash_word_count >> 8);
|
|
/* the flashing code has a minor 'bug' */
|
|
if (flash_words_low != 0)
|
|
flash_words_high++;
|
|
|
|
flash_page[FLASH_WORDS_HIGH] = (byte) flash_words_high;
|
|
flash_page[FLASH_WORDS_LOW] = (byte) flash_words_low;
|
|
return flash_page;
|
|
}
|
|
|
|
static byte[] set_clkcon_fast = {
|
|
MOV_direct_data, CLKCON, 0x00
|
|
};
|
|
|
|
static byte[] get_sleep = {
|
|
MOV_A_direct, SLEEP
|
|
};
|
|
|
|
public void clock_init() throws IOException, InterruptedException {
|
|
if (debug != null) {
|
|
debug.debug_instr(set_clkcon_fast);
|
|
|
|
byte status;
|
|
for (int times = 0; times < 20; times++) {
|
|
Thread.sleep(1);
|
|
status = debug.debug_instr(get_sleep);
|
|
if ((status & SLEEP_XOSC_STB) != 0)
|
|
return;
|
|
}
|
|
throw new IOException("Failed to initialize target clock");
|
|
}
|
|
}
|
|
|
|
void action(String s, int percent) {
|
|
if (listener != null && !aborted)
|
|
listener.position(s, percent);
|
|
}
|
|
|
|
void action(int part, int total) {
|
|
int percent = 100 * part / total;
|
|
action(String.format("%d/%d (%d%%)",
|
|
part, total, percent),
|
|
percent);
|
|
}
|
|
|
|
void altos_run(int pc) throws IOException, InterruptedException {
|
|
debug.set_pc(pc);
|
|
int set_pc = debug.get_pc();
|
|
if (pc != set_pc)
|
|
throw new IOException("Failed to set target program counter");
|
|
debug.resume();
|
|
|
|
for (int times = 0; times < 20; times++) {
|
|
byte status = debug.read_status();
|
|
if ((status & AltosDebug.STATUS_CPU_HALTED) != 0)
|
|
return;
|
|
}
|
|
|
|
throw new IOException("Failed to execute program on target");
|
|
}
|
|
|
|
public void flash() {
|
|
try {
|
|
if (!check_rom_config())
|
|
throw new IOException("Invalid rom config settings");
|
|
if (image.address + image.data.length > 0x8000)
|
|
throw new IOException(String.format("Flash image too long %d",
|
|
image.address +
|
|
image.data.length));
|
|
if ((image.address & 0x3ff) != 0)
|
|
throw new IOException(String.format("Flash image must start on page boundary (is 0x%x)",
|
|
image.address));
|
|
int ram_address = 0xf000;
|
|
int flash_prog = 0xf400;
|
|
|
|
/*
|
|
* Store desired config values into image
|
|
*/
|
|
rom_config.write(image);
|
|
/*
|
|
* Bring up the clock
|
|
*/
|
|
clock_init();
|
|
|
|
int remain = image.data.length;
|
|
int flash_addr = (int) image.address;
|
|
int image_start = 0;
|
|
|
|
action(AltosFlashListener.flash_start, 0);
|
|
action(0, image.data.length);
|
|
while (remain > 0 && !aborted) {
|
|
int this_time = remain;
|
|
if (this_time > 0x400)
|
|
this_time = 0x400;
|
|
|
|
if (debug != null) {
|
|
/* write the data */
|
|
debug.write_memory(ram_address, image.data,
|
|
image_start, this_time);
|
|
|
|
/* write the flash program */
|
|
byte[] flash_page = make_flash_page(flash_addr,
|
|
ram_address,
|
|
this_time);
|
|
debug.write_memory(flash_prog, flash_page);
|
|
|
|
altos_run(flash_prog);
|
|
byte[] check = debug.read_memory(flash_addr, this_time);
|
|
for (int i = 0; i < this_time; i++)
|
|
if (check[i] != image.data[image_start + i])
|
|
throw new IOException(String.format("Flash write failed at 0x%x (%02x != %02x)",
|
|
image.address + image_start + i,
|
|
check[i], image.data[image_start + i]));
|
|
} else {
|
|
Thread.sleep(100);
|
|
}
|
|
|
|
remain -= this_time;
|
|
flash_addr += this_time;
|
|
image_start += this_time;
|
|
|
|
action(image.data.length - remain, image.data.length);
|
|
}
|
|
if (!aborted) {
|
|
action(AltosFlashListener.flash_done, 100);
|
|
if (debug != null) {
|
|
debug.set_pc((int) image.address);
|
|
debug.resume();
|
|
}
|
|
}
|
|
if (debug != null)
|
|
debug.close();
|
|
} catch (IOException ie) {
|
|
action(ie.getMessage(), -1);
|
|
abort();
|
|
} catch (InterruptedException ie) {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
public void close() {
|
|
if (debug != null)
|
|
debug.close();
|
|
}
|
|
|
|
synchronized public void abort() {
|
|
aborted = true;
|
|
close();
|
|
}
|
|
|
|
public boolean check_rom_config() throws InterruptedException {
|
|
if (debug == null)
|
|
return true;
|
|
if (rom_config == null)
|
|
rom_config = debug.romconfig();
|
|
return rom_config != null && rom_config.valid();
|
|
}
|
|
|
|
public void set_romconfig (AltosRomconfig romconfig) {
|
|
rom_config = romconfig;
|
|
}
|
|
|
|
public AltosRomconfig target_romconfig(AltosUsbId usb_id, String usb_product) throws InterruptedException {
|
|
if (!check_rom_config())
|
|
return null;
|
|
if (rom_config.usb_id == null)
|
|
rom_config.usb_id = usb_id;
|
|
if (rom_config.usb_product == null)
|
|
rom_config.usb_product = usb_product;
|
|
return rom_config;
|
|
}
|
|
|
|
public AltosRomconfig image_romconfig() {
|
|
return new AltosRomconfig(image);
|
|
}
|
|
|
|
public AltosFlash(File file, AltosLink link, AltosFlashListener listener)
|
|
throws IOException, FileNotFoundException, InterruptedException {
|
|
this.file = file;
|
|
this.link = link;
|
|
this.listener = listener;
|
|
if (link != null)
|
|
debug = new AltosDebug(link);
|
|
input = new FileInputStream(file);
|
|
image = new AltosHexfile(input);
|
|
|
|
boolean connection_ok = true;
|
|
|
|
if (debug != null) {
|
|
try {
|
|
connection_ok = debug.check_connection();
|
|
} catch (IOException ie) {
|
|
debug.close();
|
|
throw ie;
|
|
} catch (InterruptedException ie) {
|
|
debug.close();
|
|
throw ie;
|
|
}
|
|
}
|
|
if (!connection_ok) {
|
|
debug.close();
|
|
throw new IOException("Debug port not connected");
|
|
}
|
|
}
|
|
}
|