465 lines
14 KiB
C
465 lines
14 KiB
C
/*
|
|
Copyright 2016 fishpepper <AT> gmail.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 3 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
author: fishpepper <AT> gmail.com
|
|
*/
|
|
#pragma noiv
|
|
|
|
#include <stdint.h>
|
|
#include "cc25xx.h"
|
|
#include "main.h"
|
|
#include "uart.h"
|
|
#include "led.h"
|
|
#include "delay.h"
|
|
#include "device.h"
|
|
#include "flash.h"
|
|
#include "io.h"
|
|
|
|
static void bootloader_init_clocks(void) {
|
|
// power up osc (?)
|
|
SLEEP &= ~CLOCKSOURCE_OSC_PD_BIT;
|
|
|
|
// wait for XOSC stable
|
|
while (!CLOCKSOURCE_XOSC_STABLE()) {}
|
|
NOP();
|
|
|
|
// start crystal osc as HS clocksource, OSC32 is int rc osc
|
|
CLKCON = 0x80;
|
|
|
|
// wait for selection to be active
|
|
while (!CLOCKSOURCE_XOSC_STABLE()) {}
|
|
NOP();
|
|
|
|
// power down the unused oscillator
|
|
SLEEP |= CLOCKSOURCE_OSC_PD_BIT;
|
|
}
|
|
|
|
static void bootloader_init(void) {
|
|
// show bootloader activity:
|
|
led_red_on();
|
|
led_green_on();
|
|
|
|
// set up clocks
|
|
bootloader_init_clocks();
|
|
|
|
// turn on cache pre-fetch mode
|
|
FCTL = 0x08;
|
|
|
|
// wait for vcc to stabilize
|
|
delay_ms(50);
|
|
}
|
|
|
|
|
|
static uint8_t bootloader_decode_address(uint16_t *address) {
|
|
uint8_t rx;
|
|
uint8_t checksum = 0;
|
|
|
|
// as we only accept 16bit addresses
|
|
// stm32 uses 0x0800xxyy as address for flash
|
|
// ignore the first two bytes
|
|
checksum ^= uart_getc();
|
|
checksum ^= uart_getc();
|
|
|
|
// high byte of 16bit
|
|
rx = uart_getc();
|
|
*address = rx;
|
|
checksum ^= rx;
|
|
|
|
// low byte of 16bit
|
|
rx = uart_getc();
|
|
*address = ((*address) << 8) | rx;
|
|
checksum ^= rx;
|
|
|
|
// read address checksum
|
|
rx = uart_getc();
|
|
|
|
// verify checksum
|
|
if (rx != checksum) {
|
|
// checksum invalid -> abort here
|
|
return 0;
|
|
}
|
|
|
|
// verify if this is within memory bounds:
|
|
if ((*address) > (FLASH_SIZE)) {
|
|
return 0;
|
|
}
|
|
|
|
// everything is fine
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void bootloader_jump_to_app(void) {
|
|
// disable all interrupts
|
|
EA = 0;
|
|
IEN0 = 0;
|
|
IEN1 = 0;
|
|
IEN2 = 0;
|
|
|
|
// jump to main app. this will never return
|
|
__asm
|
|
ljmp #(BOOTLOADER_SIZE)
|
|
sjmp .
|
|
__endasm;
|
|
}
|
|
|
|
void bootloader_main(void) {
|
|
__xdata uint8_t buffer[256+2];
|
|
uint8_t state = 0;
|
|
uint8_t command = 0;
|
|
uint8_t rx = 0;
|
|
uint16_t address;
|
|
uint8_t *data_ptr = 0;
|
|
uint8_t checksum;
|
|
uint8_t len = 0;
|
|
uint16_t len16 = 0;
|
|
uint8_t i;
|
|
myfuncptr_t jump_helper;
|
|
|
|
io_init();
|
|
led_init();
|
|
|
|
// check if we have to enter the bootloader
|
|
// or jump to the application
|
|
// wait some time for the voltage level on i/o to
|
|
// stabilize
|
|
delay_ms(25);
|
|
if (!io_bootloader_enabled()) {
|
|
// bootloader enable pin pulled low
|
|
if (*((__xdata uint8_t*)(BOOTLOADER_SIZE)) != 0xFF) {
|
|
// there is valid flash content on this address
|
|
// so it is safe to jump to main app!
|
|
bootloader_jump_to_app();
|
|
}
|
|
}
|
|
|
|
|
|
bootloader_init();
|
|
uart_init();
|
|
flash_init();
|
|
|
|
led_green_on();
|
|
led_red_off();
|
|
|
|
|
|
|
|
// the bootloader enable pin was high or
|
|
// there was no valid code uploaded yet -> enter bootloader mode
|
|
while (1) {
|
|
// uart_putc_d(state);
|
|
// do main statemachine
|
|
switch (state) {
|
|
default:
|
|
case(0):
|
|
// fetch command byte
|
|
command = uart_getc();
|
|
if (command == BOOTLOADER_COMMAND_INIT) {
|
|
// init sequence, send ack
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
} else {
|
|
// real command
|
|
state = 1;
|
|
}
|
|
break;
|
|
|
|
case(1):
|
|
// check command checksum (inverted)
|
|
rx = uart_getc();
|
|
// NOTE: ~x seems to be calculated in uint16_t !
|
|
if (rx == (command ^ 0xFF)) {
|
|
// fine, valid command -> decode
|
|
switch (command) {
|
|
// unknown or unsupported command
|
|
default:
|
|
// invalid command, abort
|
|
state = 0xFF;
|
|
break;
|
|
|
|
// all known commands
|
|
case(BOOTLOADER_COMMAND_GET):
|
|
case(BOOTLOADER_COMMAND_GET_VERSION):
|
|
case(BOOTLOADER_COMMAND_GET_ID):
|
|
case(BOOTLOADER_COMMAND_READ_MEMORY):
|
|
case(BOOTLOADER_COMMAND_GO):
|
|
case(BOOTLOADER_COMMAND_WRITE_MEMORY):
|
|
case(BOOTLOADER_COMMAND_ERASE):
|
|
// send ACK and continue with command handler
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
state = 10 + command;
|
|
break;
|
|
}
|
|
} else {
|
|
// mismatch - this was either a comm error or we are
|
|
// in the middle of a command, retry with the current byte as cmd byte:
|
|
if (rx == BOOTLOADER_COMMAND_INIT) {
|
|
// init sequence, send ack
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
state = 0;
|
|
} else {
|
|
// real command
|
|
command = rx;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// send GET response
|
|
case(10 + BOOTLOADER_COMMAND_GET):
|
|
// number of command bytes that will follow
|
|
uart_putc(7);
|
|
// version
|
|
uart_putc(BOOTLOADER_VERSION);
|
|
// send supported commands
|
|
uart_putc(BOOTLOADER_COMMAND_GET);
|
|
uart_putc(BOOTLOADER_COMMAND_GET_VERSION);
|
|
uart_putc(BOOTLOADER_COMMAND_GET_ID);
|
|
uart_putc(BOOTLOADER_COMMAND_READ_MEMORY);
|
|
uart_putc(BOOTLOADER_COMMAND_GO);
|
|
uart_putc(BOOTLOADER_COMMAND_WRITE_MEMORY);
|
|
uart_putc(BOOTLOADER_COMMAND_ERASE);
|
|
// send ack
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
// wait for next command
|
|
state = 0;
|
|
break;
|
|
|
|
// send GET_ID response
|
|
case(10 + BOOTLOADER_COMMAND_GET_ID):
|
|
// number of response bytes to follow
|
|
uart_putc(1);
|
|
// send product id of an F1 chip with the same pagesize (1024)
|
|
uart_putc(BOOTLOADER_DEVICE_ID >> 8);
|
|
uart_putc(BOOTLOADER_DEVICE_ID & 0xFF);
|
|
// send ack
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
// wait for next command
|
|
state = 0;
|
|
break;
|
|
|
|
// send GET_VERSION response
|
|
case (10 + BOOTLOADER_COMMAND_GET_VERSION):
|
|
// bootloader version
|
|
uart_putc(BOOTLOADER_VERSION);
|
|
// send option bytes
|
|
uart_putc(0x00);
|
|
uart_putc(0x00);
|
|
// send ack
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
// wait for next command
|
|
state = 0;
|
|
break;
|
|
|
|
// send READ_MEMORY response
|
|
case(10 + BOOTLOADER_COMMAND_READ_MEMORY):
|
|
if (!bootloader_decode_address(&address)) {
|
|
// abort now
|
|
state = 0xFF;
|
|
break;
|
|
}
|
|
|
|
// addresss is valid, send ack
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
|
|
// fetch data
|
|
len = uart_getc();
|
|
checksum = uart_getc();
|
|
|
|
// verify checksum
|
|
if (len != (checksum ^ 0xFF)) {
|
|
// checksum invalid -> abort here
|
|
state = 0xFF;
|
|
break;
|
|
}
|
|
|
|
// checksum test passed, send ack
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
|
|
// fetch flash content (len+1 bytes!)
|
|
flash_read(address, buffer, ((uint16_t) len) + 1);
|
|
|
|
// send len+1 bytes
|
|
data_ptr = &buffer[0];
|
|
uart_putc(*data_ptr++);
|
|
while (len--) {
|
|
uart_putc(*data_ptr++);
|
|
}
|
|
|
|
// wait for next command
|
|
state = 0;
|
|
break;
|
|
|
|
// send GO response
|
|
case(10 + BOOTLOADER_COMMAND_GO):
|
|
if (!bootloader_decode_address(&address)) {
|
|
// abort now
|
|
state = 0xFF;
|
|
break;
|
|
}
|
|
|
|
// addresss is valid, send ack
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
|
|
// now jump to user application given by address
|
|
// disable all interrupts
|
|
EA = 0;
|
|
IEN0 = 0;
|
|
IEN1 = 0;
|
|
IEN2 = 0;
|
|
jump_helper = (myfuncptr_t) address;
|
|
jump_helper();
|
|
|
|
// wait for next command
|
|
state = 0;
|
|
break;
|
|
|
|
// send WRITE_MEMORY response
|
|
case(10 + BOOTLOADER_COMMAND_WRITE_MEMORY):
|
|
if (!bootloader_decode_address(&address)) {
|
|
// abort now
|
|
state = 0xFF;
|
|
break;
|
|
}
|
|
|
|
// addresss is valid, send ack
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
|
|
// fetch len
|
|
len = uart_getc();
|
|
checksum = len;
|
|
|
|
// place to store data
|
|
data_ptr = &buffer[0];
|
|
|
|
// we will have to write len+1 bytes
|
|
len16 = ((uint16_t) len) + 1;
|
|
|
|
// we can only start the write on even addresses
|
|
if (address & 1) {
|
|
// not an even address, add a dummy write of 0xFF to the data:
|
|
address--;
|
|
*data_ptr++ = 0xFF;
|
|
len16++;
|
|
}
|
|
|
|
|
|
// retrieve N+1 data bytes
|
|
rx = uart_getc();
|
|
*data_ptr++ = rx;
|
|
checksum ^= rx;
|
|
|
|
for (i=0; i < len; i++) {
|
|
rx = uart_getc();
|
|
*data_ptr++ = rx;
|
|
checksum ^= rx;
|
|
}
|
|
|
|
// verify checksum
|
|
rx = uart_getc();
|
|
if (checksum != rx) {
|
|
// checksum invalid -> abort here
|
|
state = 0xFF;
|
|
break;
|
|
}
|
|
|
|
// we have to write an even number of bytes as well
|
|
if (len16 & 1) {
|
|
// not even, fix it by appending a dumm write of 0xFF
|
|
*data_ptr++ = 0xFF;
|
|
len16++;
|
|
}
|
|
|
|
|
|
// checksum ok - store data
|
|
if (!flash_write_data(address, buffer, len16)) {
|
|
// write failed
|
|
state = 0xFF;
|
|
break;
|
|
}
|
|
|
|
// done
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
|
|
// wait for next command
|
|
state = 0;
|
|
break;
|
|
|
|
// send ERASE response
|
|
case(10 + BOOTLOADER_COMMAND_ERASE):
|
|
// get number of pages to be erased
|
|
len = uart_getc();
|
|
checksum = len;
|
|
|
|
if (len == 0xFF) {
|
|
// special case, full flash erase
|
|
if (uart_getc() == 0x00) {
|
|
// valid command, mark all pages to be erased
|
|
len = 0;
|
|
data_ptr = &buffer[0];
|
|
for (i = PAGECOUNT_BOOTLOADER; i < PAGECOUNT_FLASH; i++) {
|
|
buffer[len] = i;
|
|
len++;
|
|
}
|
|
} else {
|
|
// checksum error, abort
|
|
state = 0xFF;
|
|
break;
|
|
}
|
|
} else {
|
|
// fetch len+1 pages to be erased
|
|
data_ptr = &buffer[0];
|
|
rx = uart_getc();
|
|
*data_ptr++ = rx;
|
|
checksum ^= rx;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
rx = uart_getc();
|
|
*data_ptr++ = rx;
|
|
checksum ^= rx;
|
|
}
|
|
|
|
// fetch checksum
|
|
rx = uart_getc();
|
|
|
|
if (rx != checksum) {
|
|
// checksum mismatch, abort
|
|
state = 0xFF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// fine, the len+1 pages to be erased are now in buffer[]
|
|
// execute the erase of len+1 pages
|
|
data_ptr = &buffer[0];
|
|
if (!flash_erase_page(*data_ptr++)) { state = 0xFF; break;}
|
|
while (len--) {
|
|
if (!flash_erase_page(*data_ptr++)) { state = 0xFF; break;}
|
|
}
|
|
|
|
// execute suceeded!
|
|
uart_putc(BOOTLOADER_RESPONSE_ACK);
|
|
|
|
// wait for next command
|
|
state = 0;
|
|
break;
|
|
|
|
// ABORT STATE - send nack and goto idle
|
|
case(0xFF):
|
|
uart_putc(BOOTLOADER_RESPONSE_NACK);
|
|
state = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|