Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: bdmtrionic.cpp
- Revision:
- 1:d5452e398b76
- Child:
- 2:bf3a2b29259a
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bdmtrionic.cpp Tue Sep 14 21:02:04 2010 +0000
@@ -0,0 +1,894 @@
+/*******************************************************************************
+
+bdmtrionic.cpp
+(c) 2010 by Sophie Dexter
+
+General purpose BDM functions for Just4Trionic by Just4pLeisure
+
+A derivative work based on:
+//-----------------------------------------------------------------------------
+// CAN/BDM adapter firmware
+// (C) Janis Silins, 2010
+// $id$
+//-----------------------------------------------------------------------------
+
+********************************************************************************
+
+WARNING: Use at your own risk, sadly this software comes with no guarantees.
+This software is provided 'free' and in good faith, but the author does not
+accept liability for any damage arising from its use.
+
+*******************************************************************************/
+
+#include "bdmtrionic.h"
+
+// structure for command address/value pairs
+struct mempair_t {
+ uint32_t addr; ///< target address
+ uint16_t val; ///< word value
+};
+
+// word write algorithm (29Fxxx)
+static const struct mempair_t am29_write [] = {
+ {0xaaaa, 0xaaaa}, {0x5554, 0x5555}, {0xaaaa, 0xa0a0},
+};
+
+// chip erase algorithms
+static const struct mempair_t am29_erase [] = {
+ {0xaaaa, 0xaaaa}, {0x5554, 0x5555}, {0xaaaa, 0x8080},
+ {0xaaaa, 0xaaaa}, {0x5554, 0x5555}, {0xaaaa, 0x1010}
+};
+
+// reset algorithms
+//static const struct mempair_t am29_reset = {0xfffe, 0xf0f0};
+static const struct mempair_t am29_reset [] = {
+ {0xaaaa, 0xaaaa}, {0x5554, 0x5555}, {0xaaaa, 0xf0f0},
+};
+
+// chip id algorithms
+static const struct mempair_t am29_id [] = {
+ {0xaaaa, 0xaaaa}, {0x5554, 0x5555}, {0xaaaa, 0x9090},
+};
+
+// ;-)
+static const struct mempair_t flash_tag [] = {
+ {0x7fe00, 0xFF4A}, {0x7fe02, 0x7573}, {0x7fe04, 0x7434}, {0x7fe06, 0x704C},
+ {0x7fe08, 0x6569}, {0x7fe0a, 0x7375}, {0x7fe0c, 0x7265}, {0x7fe0e, 0x3B29},
+};
+
+// local functions
+bool reset_am29(void);
+bool erase_am29();
+bool flash_am29(const uint32_t* addr, uint16_t value);
+bool reset_am28(void);
+bool erase_am28(const uint32_t* start_addr, const uint32_t* end_addr);
+bool flash_am28(const uint32_t* addr, uint16_t value);
+bool get_flash_id(uint8_t* make, uint8_t* type);
+
+
+//-----------------------------------------------------------------------------
+/**
+ Dumps contents of a memory block from [start_addr] up to, but not including,
+ the [end_addr] as long words (word-aligned addresses). MCU must be in
+ background mode. The operation interrupts if the break character is
+ received.
+
+ @param start_addr block start address
+ @param end_addr block end address
+
+ @return status flag
+*/
+
+
+uint8_t dump_flash(const uint32_t* start_addr, const uint32_t* end_addr) {
+
+ // check parametres
+ if (*start_addr > *end_addr) {
+ return TERM_ERR;
+ }
+
+ // dump memory contents
+ uint32_t curr_addr = *start_addr;
+ uint32_t value;
+
+ while ((curr_addr < *end_addr) && (pc.getc() != TERM_BREAK)) {
+ // read long word
+ if (curr_addr > *start_addr) {
+ if (memdump_long(&value) != TERM_OK) {
+ return TERM_ERR;
+ }
+ } else {
+ if (memread_long(&value, &curr_addr) != TERM_OK) {
+ return TERM_ERR;
+ }
+ }
+
+ // send memory value to host
+ printf("%08X", value);
+
+ // add the terminating character
+ if (curr_addr < *end_addr - 4) {
+ pc.putc(TERM_OK);
+ // light up the activity LED
+ // led_on(LED_ACT);
+ led3 = 1;
+ }
+
+ curr_addr += 4;
+ }
+
+ return TERM_OK;
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Dumps the contents of a T5 ECU to a BIN file on the mbed 'disk'
+ from [start_addr] up to, but not including, the [end_addr].
+ MCU must be in background mode.
+
+ @param start_addr block start address
+ @param end_addr block end address
+
+ @return status flag
+*/
+
+uint8_t dump_trionic() {
+
+ // Configure the MC68332 register values to prepare for flashing
+ printf("I am trying to discover what type of Trionic ECU I am connected to...\r\n");
+ prep_t5_do();
+ // Work out what type of FLASH chips we want to make a dump file for
+ uint8_t make;
+ uint8_t type;
+ get_flash_id(&make, &type);
+ // set up chip-specific functions
+ bool (*reset_func)();
+ uint32_t flash_size;
+
+ switch (type) {
+ case AMD29F400B:
+ case AMD29F400T:
+ printf("I have found AMD29F400 type FLASH chips; I must be connected to a T7 ECU :-)\r\n");
+ reset_func = &reset_am29;
+ flash_size = T7FLASHSIZE;
+ break;
+ case AMD29F010:
+ printf("I have found AMD29F010 type FLASH chips; I must be connected to a repaired T5.5 ECU :-)\r\n");
+ reset_func = &reset_am29;
+ flash_size = T55FLASHSIZE;
+ break;
+ case AMD28F010:
+ case INTEL28F010:
+ printf("I have found 28F010 type FLASH chips; I must be connected to a T5.5 ECU :-)\r\n");
+ reset_func = &reset_am28;
+ flash_size = T55FLASHSIZE;
+ break;
+ case AMD28F512:
+ case INTEL28F512:
+ printf("I have found 28F512 type FLASH chips; I must be connected to a T5.2 ECU :-)\r\n");
+ reset_func = &reset_am28;
+ flash_size = T52FLASHSIZE;
+ break;
+ default:
+ // unknown flash type
+ printf("I could not work out what FLASH chips or TRIONIC ECU I am connected to :-(\r\n");
+ return TERM_ERR;
+ }
+
+ // reset the FLASH chips
+ if (!reset_func()) return TERM_ERR;
+
+ printf("Creating FLASH dump file...\r\n");
+ FILE *fp = fopen("/local/original.bin", "w"); // Open "original.bin" on the local file system for writing
+ if (!fp) {
+ perror ("The following error occured");
+ return TERM_ERR;
+ }
+
+// dump memory contents
+ uint32_t addr = 0x00;
+ uint32_t long_value;
+
+// setup start address to dump from
+ if (memread_long_cmd(&addr) != TERM_OK) return TERM_ERR;
+
+ timer.reset();
+ timer.start();
+
+ while (addr < flash_size) {
+ uint16_t byte_count = 0;
+ while (byte_count < FILE_BUF_LENGTH) {
+ // get long word
+ if (memget_long(&long_value) != TERM_OK) return TERM_ERR;
+ addr += 4;
+ // send memory value to file_buffer before saving to mbed 'disk'
+ file_buffer[byte_count] = ((uint8_t)(long_value >> 24));
+ file_buffer[byte_count+1] = ((uint8_t)(long_value >> 16));
+ file_buffer[byte_count+2] = ((uint8_t)(long_value >> 8));
+ file_buffer[byte_count+3] = ((uint8_t)long_value);
+ byte_count +=4;
+ }
+// make the activity led twinkle
+ led3 = 1;
+ fwrite(file_buffer, 1, FILE_BUF_LENGTH, fp);
+ if (ferror (fp)) {
+ fclose (fp);
+ printf ("Error writing to the FLASH BIN file.\r\n");
+ return TERM_ERR;
+ }
+ }
+ // should 'clear' the BDM connection here but bdm_clear won't compile from here
+ // instead do a memread (or anything really) but ignore the result because it's not needed for anything
+ memread_long(&long_value, &addr);
+ timer.stop();
+ printf("Getting the FLASH dump took %#.1f seconds.\r\n",timer.read());
+ fclose(fp);
+ return TERM_OK;
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Erases the flash memory chip starting from [start_addr] up to, but not
+ including [end_addr] and optionally verifies the result; MCU must be in
+ background mode.
+
+ @param flash_type type of flash chip
+ @param start_addr flash start address
+ @param end_addr flash end address
+
+ @return status flag
+*/
+uint8_t erase_flash(const char* flash_type, const uint32_t* start_addr,
+ const uint32_t* end_addr) {
+ // AM29Fxxx chips (retrofitted to Trionic 5.x; original to T7)
+ if (strncmp(flash_type, "29f010", 6) == 0 ||
+ strncmp(flash_type, "29f400", 6) == 0) {
+ return erase_am29() ? TERM_OK : TERM_ERR;
+ }
+
+ // AM28F010 chip (Trionic 5.x original)
+ if (strncmp(flash_type, "28f010", 6) == 0) {
+ return erase_am28(start_addr, end_addr) ? TERM_OK : TERM_ERR;
+ }
+
+ return TERM_ERR;
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Writes a batch of long words to the flash starting from [start_addr]. The
+ operation interrupts if a break character is received. MCU must be in
+ background mode.
+
+ @param flash_type type of flash chip
+ @param start_addr block start address
+
+ @return status flag
+*/
+uint8_t write_flash(const char* flash_type, const uint32_t* start_addr) {
+ // set up chip-specific functions
+ bool (*reset_func)(void);
+ bool (*flash_func)(const uint32_t*, uint16_t);
+
+ // AM29Fxxx chips (retrofitted to Trionic 5.x, original to T7)
+ if (strncmp(flash_type, "29f010", 6) == 0 ||
+ strncmp(flash_type, "29f400", 6) == 0) {
+ reset_func = &reset_am29;
+ flash_func = &flash_am29;
+ } else if (strncmp(flash_type, "28f010", 6) == 0) {
+ // AM28F010 chip (Trionic 5.x original)
+ reset_func = &reset_am28;
+ flash_func = &flash_am28;
+ } else {
+ // unknown flash type
+ return TERM_ERR;
+ }
+
+ // reset the flash
+ if (!reset_func()) {
+ return TERM_ERR;
+ }
+
+ uint32_t curr_addr = *start_addr;
+ if (strncmp(flash_type, "29f010", 6) == 0) {
+ curr_addr = 0;
+ }
+
+ int rx_char = 0;
+ char rx_buf[8];
+ char* rx_ptr;
+ uint32_t long_value;
+ bool ret = true;
+
+ // ready to receive data
+ pc.putc(TERM_OK);
+
+ while (true) {
+ // receive long words from USB
+ printf("receive long words from USB\r\n");
+ rx_ptr = rx_buf;
+ do {
+ rx_char = pc.getc();
+ if (rx_char != EOF) {
+ // have got all characters for one long word
+ if (rx_ptr > &rx_buf[7]) {
+ ret = (rx_char == TERM_OK);
+ break;
+ }
+
+ // save the character
+ *rx_ptr++ = (char)rx_char;
+ }
+ } while (rx_char != TERM_OK && rx_char != TERM_BREAK);
+ // end writing
+ printf("end writing\r\n");
+ if (!ret || rx_char == TERM_BREAK) {
+ break;
+ }
+
+ // convert value to long word
+ printf("convert value to long word\r\n");
+ if (!ascii2int(&long_value, rx_buf, 8)) {
+ ret = false;
+ break;
+ }
+ printf("long value %08x \r\n", long_value);
+
+ // write the first word
+ printf("write the first word\r\n");
+ if (!flash_func(&curr_addr, (uint16_t)(long_value >> 16))) {
+ ret = false;
+ break;
+ }
+ curr_addr += 2;
+ // write the second word
+ printf("write the second word\r\n");
+ if (!flash_func(&curr_addr, (uint16_t)long_value)) {
+ ret = false;
+ break;
+ }
+ curr_addr += 2;
+
+ // light up the activity LED
+// led_on(LED_ACT);
+ led3 = 1;
+ }
+
+ // reset flash
+ return (reset_func() && ret) ? TERM_OK : TERM_ERR;
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Writes a BIN file to the flash starting from [start_addr].
+ The operation ends when no more bytes can be read from the BIN file.
+ MCU must be in background mode.
+
+ @param flash_type type of flash chip
+ @param start_addr block start address
+
+ @return status flag
+*/
+uint8_t flash_trionic() {
+ // Configure the MC68332 register values to prepare for flashing
+ printf("I am trying to discover what type of Trionic ECU I am connected to...\r\n");
+ prep_t5_do();
+ // Work out what type of FLASH chips we want to program
+ uint8_t make;
+ uint8_t type;
+ get_flash_id(&make, &type);
+ // set up chip-specific functions
+ bool (*reset_func)();
+ bool (*flash_func)(const uint32_t*, uint16_t);
+ uint32_t flash_size;
+
+ switch (type) {
+ case AMD29F400B:
+ case AMD29F400T:
+ printf("I have found AMD29F400 type FLASH chips; I must be connected to a T7 ECU :-)\r\n");
+ reset_func = &reset_am29;
+ flash_func = &flash_am29;
+ flash_size = T7FLASHSIZE;
+ break;
+ case AMD29F010:
+ printf("I have found AMD29F010 type FLASH chips; I must be connected to a repaired T5.5 ECU :-)\r\n");
+ reset_func = &reset_am29;
+ flash_func = &flash_am29;
+ flash_size = T55FLASHSIZE;
+ break;
+ case AMD28F010:
+ case INTEL28F010:
+ printf("I have found 28F010 type FLASH chips; I must be connected to a T5.5 ECU :-)\r\n");
+ reset_func = &reset_am28;
+ flash_func = &flash_am28;
+ flash_size = T55FLASHSIZE;
+ break;
+ case AMD28F512:
+ case INTEL28F512:
+ printf("I have found 28F512 type FLASH chips; I must be connected to a T5.2 ECU :-)\r\n");
+ reset_func = &reset_am28;
+ flash_func = &flash_am28;
+ flash_size = T52FLASHSIZE;
+ break;
+ default:
+ // unknown flash type
+ printf("I could not work out what FLASH chips or TRIONIC ECU I am connected to :-(\r\n");
+ return TERM_ERR;
+ }
+
+ // reset the FLASH chips
+ if (!reset_func()) return TERM_ERR;
+
+ printf("Checking the FLASH BIN file...\r\n");
+ FILE *fp = fopen("/local/modified.hex", "r"); // Open "modified.hex" on the local file system for reading
+ if (!fp) {
+ printf("Error: I could not find the BIN file MODIFIED.HEX\r\n");;
+ return TERM_ERR;
+ }
+ // obtain file size - it should match the size of the FLASH chips:
+ fseek (fp , 0 , SEEK_END);
+ uint32_t file_size = ftell (fp);
+ rewind (fp);
+
+ // read the initial stack pointer value in the BIN file - it should match the value expected for the type of ECU
+ uint8_t stack_byte = 0;
+ uint32_t stack_long = 0;
+ if (!fread(&stack_byte,1,1,fp)) return TERM_ERR;
+ stack_long |= (stack_byte << 24);
+ if (!fread(&stack_byte,1,1,fp)) return TERM_ERR;
+ stack_long |= (stack_byte << 16);
+ if (!fread(&stack_byte,1,1,fp)) return TERM_ERR;
+ stack_long |= (stack_byte << 8);
+ if (!fread(&stack_byte,1,1,fp)) return TERM_ERR;
+ stack_long |= stack_byte;
+ rewind (fp);
+
+ if (flash_size == T52FLASHSIZE && (file_size != T52FLASHSIZE || stack_long != T5POINTER)) {
+ fclose(fp);
+ printf("The BIN file does not appear to be for a T5.2 ECU :-(\r\n");
+ printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, flash_size, stack_long);
+ return TERM_ERR;
+ }
+ if (flash_size == T55FLASHSIZE && (file_size != T55FLASHSIZE || stack_long != T5POINTER)) {
+ fclose(fp);
+ printf("The BIN file does not appear to be for a T5.5 ECU :-(\r\n");
+ printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, flash_size, stack_long);
+ return TERM_ERR;
+ }
+ if (flash_size == T7FLASHSIZE && (file_size != T7FLASHSIZE || stack_long != T7POINTER)) {
+ fclose(fp);
+ printf("The BIN file does not appear to be for a T7 ECU :-(\r\n");
+ printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, flash_size, stack_long);
+ return TERM_ERR;
+ }
+
+ timer.reset();
+ timer.start();
+
+ uint32_t curr_addr = 0;
+
+ switch (type) {
+ // AM29Fxxx chips (retrofitted to Trionic 5.x; original to T7)
+ case AMD29F400B:
+ case AMD29F400T:
+ case AMD29F010:
+ printf("Erasing 29F400/010 type FLASH chips...\r\n");
+ if (erase_am29() == TERM_ERR) {
+ printf("WARNING: An error occured when I tried to erase the FLASH chips :-(\r\n");
+ return TERM_ERR;
+ }
+ break;
+ // AM28F010 chip (Trionic 5.x original)
+ case AMD28F010:
+ case INTEL28F010:
+ case AMD28F512:
+ case INTEL28F512:
+ printf("Erasing 28F010/512 type FLASH chips...\r\n");
+ if (erase_am28(&curr_addr, &flash_size) == TERM_ERR) {
+ printf("WARNING: An error occured when I tried to erase the FLASH chips :-(\r\n");
+ return TERM_ERR;
+ }
+ break;
+ default:
+ // unknown flash type - shouldn't get here hence "Starange!"
+ printf("Strange! I couldn't work out how to erase the FLASH chips in the TRIONIC ECU that I am connected to :-(\r\n");
+ return TERM_ERR;
+ }
+
+ timer.stop();
+ printf("Erasing took %#.1f seconds.\r\n",timer.read());
+
+ printf("Programming the FLASH chips...\r\n");
+
+ timer.reset();
+ timer.start();
+
+ uint16_t word_value = 0;
+ uint8_t byte_value = 0;
+// bool ret = true;
+
+// ready to receive data
+ while (curr_addr < flash_size) {
+ // receive bytes from BIN file
+ //Get a byte - break if no more bytes to get
+ if (!fread(&byte_value,1,1,fp)) {
+ fclose(fp);
+ printf("Error reading the BIN file MODIFIED.HEX");
+ break;
+ }
+ word_value = (byte_value << 8);
+ if (!fread(&byte_value,1,1,fp)) {
+ fclose(fp);
+ printf("Error reading the BIN file MODIFIED.HEX");
+ break;
+ }
+ word_value |= byte_value;
+
+ // write the word if it is not 0xffff
+ if (word_value != 0xffff) {
+ if (!flash_func(&curr_addr, word_value)) break;
+ }
+ curr_addr += 2;
+
+ // make the activity LED twinkle
+ led3 = 1;
+
+ }
+
+ timer.stop();
+ fclose(fp);
+
+ if (curr_addr == flash_size) {
+ printf("Programming took %#.1f seconds.\r\n",timer.read());
+ reset_func();
+ for (uint8_t i = 0; i < 8; ++i) {
+ memread_word(&word_value, &flash_tag[i].addr);
+ flash_func(&flash_tag[i].addr, (flash_tag[i].val & word_value));
+ }
+
+ } else {
+ printf("WARNING: Oh dear, I couldn't program the FLASH at address 0x%8x.\r\n", curr_addr);
+ }
+
+// reset flash
+ return (reset_func() && (curr_addr == flash_size)) ? TERM_OK : TERM_ERR;
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Resets an AM29Fxxx flash memory chip. MCU must be in background mode.
+
+ @param none
+
+ @return succ / fail
+*/
+bool reset_am29(void) {
+ // execute the reset command
+// uint32_t addr = 0xfffe;
+// return (memwrite_word(&addr, 0xf0f0) == TERM_OK);
+ // execute the algorithm
+ for (uint8_t i = 0; i < 3; ++i) {
+ if (memwrite_word(&am29_reset[i].addr, am29_reset[i].val) != TERM_OK) return false;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Erases an AM29Fxxx flash memory chip and verifies the result; MCU must be
+ in background mode.
+
+ @return succ / fail
+*/
+bool erase_am29() {
+ // reset flash
+ if (!reset_am29()) {
+ return false;
+ }
+
+ // execute the algorithm
+ for (uint8_t i = 0; i < 6; ++i) {
+ if (memwrite_word(&am29_erase[i].addr, am29_erase[i].val) != TERM_OK) {
+ reset_am29();
+ return false;
+ }
+ }
+
+ // verify the result
+ uint32_t addr = 0x0;
+ uint16_t verify_value;
+
+ uint8_t err_cnt = ERR_COUNT;
+ while (--err_cnt) {
+ // typical erase time = 1s
+ // Allow up to 25.5 seconds erase time
+ wait_ms(100);
+ if (memread_word(&verify_value, &addr) == TERM_OK && verify_value == 0xffff) {
+ // erase completed normally
+ reset_am29();
+ return true;
+ }
+ }
+
+ // erase failed
+ reset_am29();
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Writes a word to AM29Fxxx flash memory chip and optionally verifies the
+ result; MCU must be in background mode.
+
+ @param addr destination address
+ @param val value
+
+ @return succ / fail
+*/
+bool flash_am29(const uint32_t* addr, uint16_t value) {
+
+ // execute the algorithm
+ for (uint8_t i = 0; i < 3; ++i) {
+ if (memwrite_word(&am29_write[i].addr, am29_write[i].val) != TERM_OK) {
+ reset_am29();
+ return false;
+ }
+ }
+ // write the value
+ if (memwrite_word(addr, value) != TERM_OK) {
+ reset_am29();
+ return false;
+ }
+ // verify the result
+ uint8_t err_cnt = ERR_COUNT;
+ while (--err_cnt) {
+ // Allow up to approx 2.55 milliseconds program time (255 * ~10us BDM memread time)
+// wait_ms(10);
+ uint16_t verify_value;
+ if ((memread_word(&verify_value, addr) == TERM_OK) &&
+ (verify_value == value)) {
+ // flashing successful
+ return true;
+ }
+ }
+ // writing failed
+ reset_am29();
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Resets a AM28Fxxx flash memory chip. MCU must be in background mode.
+
+ @param start_addr flash start address
+
+ @return succ / fail
+*/
+bool reset_am28(void) {
+ uint32_t start_addr = 0x0;
+ return (memwrite_word(&start_addr, 0xffff) == TERM_OK &&
+ memwrite_word(&start_addr, 0xffff) == TERM_OK);
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Erases an AM28Fxxx flash memory chip and verifies the result; MCU must be
+ in background mode.
+
+ @param start_addr flash start address
+ @param end_addr flash end address
+
+ @return succ / fail
+*/
+bool erase_am28(const uint32_t* start_addr, const uint32_t* end_addr) {
+
+ // check the addresses
+ if (!start_addr || !end_addr) return false;
+
+ // reset flash
+ if (!reset_am28()) return false;
+
+ // write zeroes over entire flash space
+ uint32_t addr = *start_addr;
+
+ while (addr < *end_addr) {
+ if (!flash_am28(&addr, 0x0000)) return false;
+ addr += 2;
+// // feedback to host computer
+// pc.putc(TERM_OK);
+ // make the activity LED twinkle
+// led3 = (addr & 0x400);
+ led3 = 1;
+ }
+
+ // erase flash
+ addr = *start_addr;
+ uint8_t verify_value;
+
+
+ uint16_t pulse_cnt = 0;
+ if (memwrite_byte_cmd(NULL) != TERM_OK) {
+ reset_am28();
+ return false;
+ }
+ while ((++pulse_cnt < 1000) && (addr < *end_addr)) {
+ // issue the erase command
+ if (memwrite_write_byte(&addr, 0x20) != TERM_OK ||
+ memwrite_write_byte(&addr, 0x20) != TERM_OK) break;
+ wait_ms(10);
+
+ while (addr < *end_addr) {
+ // issue the verify command
+ if (memwrite_read_byte(&addr, 0xa0) != TERM_OK) break;
+// wait_us(6);
+ // check the written value
+ if (memread_write_byte(&verify_value, &addr) != TERM_OK) break;
+ if (verify_value != 0xff) break;
+ // succeeded need to check next address
+ addr++;
+ // make the activity LED twinkle
+ led3 = 1;
+ }
+ }
+ // the erase process ends with a BDM_WRITE + BDM_BYTESIZE command left in the BDM
+ // it is safe to use it to put one of the FLASH chips into read mode and thereby
+ // leave the BDM ready for the next command
+ memwrite_nop_byte(start_addr, 0x00);
+
+ reset_am28();
+ // check for success
+ return (addr == *end_addr) ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Writes a byte to AM28Fxxx flash memory chip and verifies the result
+ A so called 'mask' method checks the FLASH contents and only tries
+ to program bytes that need to be programmed.
+ MCU must be in background mode.
+
+ @param addr destination address
+ @param val value
+
+ @return succ / fail
+*/
+bool flash_am28(const uint32_t* addr, uint16_t value) {
+
+ if (!addr) return false;
+
+ uint8_t pulse_cnt = 0;
+ uint16_t verify_value = 0;
+ uint16_t mask_value = 0xffff;
+
+ // put flash into read mode and read address
+ if (memwrite_word_read_word(&verify_value, addr, 0x0000) != TERM_OK) return false;
+ // return if FLASH already has the correct value - e.g. not all of the FLASH is used and is 0xff
+ if (verify_value == value) return true;
+
+ while (++pulse_cnt < 25) {
+
+ // set a mask
+ if ((uint8_t)verify_value == (uint8_t)value) mask_value &= 0xff00;
+ if ((uint8_t)(verify_value >> 8) == (uint8_t)(value >> 8)) mask_value &= 0x00ff;
+
+ // write the new value
+ if (memwrite_word_write_word(addr, (0x4040 & mask_value), value) != TERM_OK) break;
+ // NOTE the BDM interface is slow enough that there is no need for a 10us delay before verifying
+// wait_us(10);
+ // issue the verification command
+ // NOTE the BDM interface is slow enough that there is no need for a 6us delay before reading back
+ if (memwrite_word_read_word(&verify_value, addr, (0xc0c0 & mask_value)) != TERM_OK) break;
+ // check if flashing was successful;
+ if (verify_value == value) return true;
+ }
+
+ // something went wrong; reset the flash chip and return failed
+ reset_am28();
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Does the equivalent of do prept5.do in BD32
+ Sets up all of the control registers in the MC68332 so that we can program
+ the FLASH chips
+
+ @param none
+
+ @return succ / fail
+*/
+
+uint8_t prep_t5_do(void) {
+
+ // reset and freeze the MC68332 chip
+ if (restart_chip() != TERM_OK) return TERM_ERR;
+
+ // set the 'fc' registers to allow supervisor mode access
+ uint32_t long_value = 0x05;
+ if (sysreg_write(0x0e, &long_value) != TERM_OK) return TERM_ERR;
+ if (sysreg_write(0x0f, &long_value) != TERM_OK) return TERM_ERR;
+
+ // Set MC68332 to 16 MHz (actually 16.78 MHz)
+ long_value = 0x00fffa04;
+ if (memwrite_word(&long_value, 0x7f00) != TERM_OK) return TERM_ERR;
+
+ // Disable watchdog and monitors
+ long_value = 0x00fffa21;
+ if (memwrite_byte(&long_value, 0x00) != TERM_OK) return TERM_ERR;
+
+ // Chip select pin assignments
+ long_value = 0x00fffa44;
+ if (memwrite_word(&long_value, 0x3fff) != TERM_OK) return TERM_ERR;
+
+ // Boot Chip select read only, one wait state
+ long_value = 0x00fffa48;
+ if (memwrite_word(&long_value, 0x0007) != TERM_OK) return TERM_ERR;
+ if (memfill_word(0x6870) != TERM_OK) return TERM_ERR;
+
+ // Chip select 1 and 2 upper lower bytes, zero wait states
+ long_value = 0x00fffa50;
+ if (memwrite_word(&long_value, 0x0007) != TERM_OK) return TERM_ERR;
+ if (memfill_word(0x3030) != TERM_OK) return TERM_ERR;
+ if (memfill_word(0x0007) != TERM_OK) return TERM_ERR;
+ if (memfill_word(0x5030) != TERM_OK) return TERM_ERR;
+
+ // PQS Data - turn on VPPH
+ long_value = 0x00fffc14;
+ if (memwrite_word(&long_value, 0x0040) != TERM_OK) return TERM_ERR;
+
+ // PQS Data Direction output
+ long_value = 0x00fffc17;
+ if (memwrite_byte(&long_value, 0x40) != TERM_OK) return TERM_ERR;
+ // wait for programming voltage to be ready
+ wait_ms(10);
+
+// // Enable internal 2kByte RAM of 68332 at address 0x00100000
+// long_value = 0x00fffb04;
+// if (memwrite_word(&long_value, 0x1000) != TERM_OK) return TERM_ERR;
+ return TERM_OK;
+}
+
+//-----------------------------------------------------------------------------
+/**
+ Works out what type of flash chip is fitted in the ECU by reading
+ the manufacturer byte codes.
+ It is enough to use the 29Fxxx flash id algorithm because 28Fxxx
+ FLASH chips ignore the first few writes needed by the 29Fxxx chips
+ MCU must be in background mode.
+
+ @param make (out)
+ type (out)
+
+ @return succ / fail
+*/
+bool get_flash_id(uint8_t* make, uint8_t* type) {
+
+ uint32_t addr = 0x0;
+ uint32_t value;
+ bool ret;
+ // read id bytes algorithm for 29F010/400 FLASH chips
+ for (uint8_t i = 0; i < 3; ++i) {
+ if (memwrite_word(&am29_id[i].addr, am29_id[i].val) != TERM_OK) return false;
+ }
+ if (memread_long(&value, &addr) != TERM_OK) return false;
+ *make = (uint8_t)(value >> 24);
+ *type = (uint8_t)(value >> 8);
+ printf("FLASH id bytes: %08x, make: %02x, type: %02x\r\n", value, *make, *type);
+ switch (*type) {
+ case AMD29F400B:
+ case AMD29F400T:
+ case AMD29F010:
+ case AMD28F010:
+ case INTEL28F010:
+ case AMD28F512:
+ case INTEL28F512:
+ ret = true;
+ default:
+ ret = false;
+ }
+ return ret;
+}
+
+//-----------------------------------------------------------------------------
+// EOF
+//-----------------------------------------------------------------------------