diff --git a/.gitignore b/.gitignore index f0f84d4..713c558 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,20 @@ +# MPLab X project folder *.X/ + +# IntelliJ project folder *.idea/ +!*.mcp + +# MPLab 8 saved project settings +*.mcs +*.mptags +*.tagsrc +# intermediate build files obj/ +# built binaries bin/ -*.mcs +# temporary copy of linker script (generated by xc16-gcc) +*.gld.00 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ad47c9b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.12) +project(bootypic VERSION 1.9.3 LANGUAGES C) + +STRING(TOLOWER "pic${CMAKE_SYSTEM_PROCESSOR}" MCU_NAME) +STRING(TOLOWER "p${CMAKE_SYSTEM_PROCESSOR}" MCU_NAME_2) + +set(DEVICE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/devices/${MCU_NAME}") +set(FIRMWARE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/../Power_Board/src") + +add_executable(bootypic + bootloader.c + "${DEVICE_SRC}/boot_user.c" + "${FIRMWARE_SRC}/power.c" + "${FIRMWARE_SRC}/battery.c" + "${FIRMWARE_SRC}/stdfunctions.c" + "${FIRMWARE_SRC}/i2clib.c" + ) + +target_include_directories(bootypic + PRIVATE + "${DEVICE_SRC}" + "${FIRMWARE_SRC}" + ) + +target_compile_options(bootypic PRIVATE -O1 -ffunction-sections) +target_compile_definitions(bootypic PRIVATE BOOTYPIC) +set(APP_LINKER_SCRIPT "${DEVICE_SRC}/${MCU_NAME_2}_app.gld") +set(BOOT_LINKER_SCRIPT "${DEVICE_SRC}/${MCU_NAME_2}_boot.gld") + +set_target_properties(bootypic + PROPERTIES LINK_DEPENDS ${BOOT_LINKER_SCRIPT}) +target_link_options(bootypic + PRIVATE + "LINKER:--gc-sections" + "LINKER:--heap=2048" + "LINKER:--coresident,--defsym,__SP_init=0x800,--defsym,__SPLIM_init=0x1000,-mreserve=data@0x0:0x1000" + "LINKER:--script=${BOOT_LINKER_SCRIPT},-Map=${CMAKE_CURRENT_BINARY_DIR}/$.map,--no-isr,--save-gld,-D__DEFINE_RESET" + ) +# $ +#file(GENERATE OUTPUT filename CONTENT "$") +add_custom_command( + TARGET bootypic POST_BUILD + COMMAND "${XC16_bin2hex_EXECUTABLE}" "$" + BYPRODUCTS bootypic.hex +) diff --git a/bootloader.c b/bootloader.c index 61c261e..197cb19 100644 --- a/bootloader.c +++ b/bootloader.c @@ -2,6 +2,11 @@ #include "config.h" #include "bootloader.h" + +#if MAX_PROG_SIZE % _FLASH_ROW != 0 +#error "MAX_PROG_SIZE must be a multiple of _FLASH_ROW" +#endif + /* bootloader starting address (cannot write to addresses between * BOOTLOADER_START_ADDRESS and APPLICATION_START_ADDRESS) */ #if _FLASH_PAGE == 128 @@ -12,127 +17,101 @@ #define BOOTLOADER_START_ADDRESS 0x800 #endif -static uint8_t rxBuffer[RX_BUF_LEN]; -static uint16_t rxBufferIndex = 0; - +static uint8_t message[RX_BUF_LEN] = {0}; static uint8_t f16_sum1 = 0, f16_sum2 = 0; -static uint16_t t2Counter = 0; - -int main(void){ - /* initialize the peripherals from the user-supplied initialization functions */ - initPins(); - initOsc(); - initUart(); - initTimers(); - - /* wait until something is received on the serial port */ - while(!should_abort_boot(t2Counter)){ - ClrWdt(); - - receiveBytes(); - processReceived(); - - if(TMR2 > 50000){ - TMR2 = 0; - t2Counter++; - } - } +int main(void) { + if (pre_boot()) { + /* initialize the peripherals from the user-supplied initialization functions */ + initPins(); + initOsc(); + initUart(); + initTimers(); + + /* wait until something is received on the serial port */ + while(!should_abort_boot()){ + ClrWdt(); + receiveBytes(); + } + } startApp(APPLICATION_START_ADDRESS); return 0; } +typedef enum ParseState { + STATE_WAIT_FOR_START, ///< Have not yet received a start byte. + STATE_READ_ESCAPED, ///< Have received an escape byte, next data byte must be decoded + STATE_READ_VERBATIM, ///< Have not received an escape byte, next data byte should be read as-is + STATE_END_OF_MESSAGE, +} ParseState; + void receiveBytes(void){ - static const uint16_t TMR1_THRESHOLD = (uint16_t)(STALE_MESSAGE_TIME * (FCY / (256.0f))); - - while(U1STAbits.URXDA){ - rxBuffer[rxBufferIndex] = U1RXREG; - rxBufferIndex++; + static uint16_t messageIndex = 0; + static ParseState state = STATE_WAIT_FOR_START; + // keep reading until one of the following: + // 1. we don't have any data available in the uart buffer to read in which case we return + // and wait for this function to be called again + // 2. we find an END OF MESSAGE, in which case we call processCommand to deal with the data + // 3. we overflow the receive buffer, in which case we throw out the data + while(true) + { + uint8_t a_byte; - TMR1 = TMR2 = 0; - t2Counter = 0; - T1CONbits.TON = 1; - } - - /* if the time since the last received has expired, then - * turn off the timer and reset the buffer */ - if(TMR1 > TMR1_THRESHOLD){ - uint16_t i; - T1CONbits.TON = 0; - TMR1 = 0; - - rxBufferIndex = 0; - for(i=0; i= RX_BUF_LEN || state == STATE_END_OF_MESSAGE) { + uint16_t i; + for(i=0; i= BOOTLOADER_START_ADDRESS) && (address < APPLICATION_START_ADDRESS)) @@ -202,10 +178,8 @@ void processCommand(uint8_t* data){ /* re-initialize the bootloader start address */ if(address == 0){ - address = 0x00000000; - /* this is the GOTO BOOTLOADER instruction */ - progData[0] = 0x040000 + BOOTLOADER_START_ADDRESS; + progData[0] = 0x040000 | BOOTLOADER_START_ADDRESS; progData[1] = 0x000000; /* write the data */ @@ -215,10 +189,7 @@ void processCommand(uint8_t* data){ break; case CMD_READ_ADDR: - address = (uint32_t)data[3] - + ((uint32_t)data[4] << 8) - + ((uint32_t)data[5] << 16) - + ((uint32_t)data[6] << 24); + address = from_lendian_uint32(data + 3); progData[0] = address; progData[1] = readAddress(address); @@ -226,15 +197,12 @@ void processCommand(uint8_t* data){ break; case CMD_READ_MAX: - address = (uint32_t)data[3] - + ((uint32_t)data[4] << 8) - + ((uint32_t)data[5] << 16) - + ((uint32_t)data[6] << 24); + address = from_lendian_uint32(data + 3); progData[0] = address; for(i=0; i= BOOTLOADER_START_ADDRESS) && (address < APPLICATION_START_ADDRESS)) + break; + for(i=0; i<_FLASH_ROW; i++){ - progData[i] = (uint32_t)data[i * 4 + 7] - + ((uint32_t)data[i * 4 + 8] << 8) - + ((uint32_t)data[i * 4 + 9] << 16) - + ((uint32_t)data[i * 4 + 10] << 24); + progData[i] = from_lendian_uint32(data + 7 + i * 4); } - - /* do not allow the bootloader to be overwritten */ - if((word >= BOOTLOADER_START_ADDRESS) && (word < APPLICATION_START_ADDRESS)) - break; - + /* do not allow the reset vector to be changed by the application */ - if(word < __IVT_BASE) + if(address < __IVT_BASE) break; writeRow(address, progData); break; case CMD_WRITE_MAX_PROG_SIZE: - address = (uint32_t)data[3] - + ((uint32_t)data[4] << 8) - + ((uint32_t)data[5] << 16) - + ((uint32_t)data[6] << 24); - + address = from_lendian_uint32(data + 3); + + /* do not allow the bootloader to be overwritten */ + if((address >= BOOTLOADER_START_ADDRESS) && (address < APPLICATION_START_ADDRESS)) + break; + /* fill the progData array */ for(i=0; i #include diff --git a/devices/dspic33epXmc/32mc204/boot_user.h b/devices/dspic33epXmc/32mc204/boot_user.h index 0af52d0..fb779f6 100644 --- a/devices/dspic33epXmc/32mc204/boot_user.h +++ b/devices/dspic33epXmc/32mc204/boot_user.h @@ -1,5 +1,5 @@ -#ifndef _BOOTLOADER_H -#define _BOOTLOADER_H +#ifndef _BOOT_USER_H +#define _BOOT_USER_H #include #include diff --git a/devices/pic24fj256gb106/boot_user.c b/devices/pic24fj256gb106/boot_user.c index 739869e..88f5195 100644 --- a/devices/pic24fj256gb106/boot_user.c +++ b/devices/pic24fj256gb106/boot_user.c @@ -1,12 +1,21 @@ /// Device-specific implementation details #include "xc.h" #include "boot_user.h" +#include "power.h" -bool readBootPin(void); +bool pre_boot(){ + power_init(); + + bool should_run_bootloader; + // In a normal power-on boot, we don't need to run the bootloader + // if the firmware is corrupt, it will reset for an IOPUWR + should_run_bootloader = (RCON & ~_RCON_POR_MASK & ~_RCON_BOR_MASK); + RCON = 0; + return should_run_bootloader; +} void initOsc(void){ CLKDIV = 0; - return; } @@ -14,8 +23,9 @@ void initPins(void){ /* no analog, all digital */ AD1PCFGL = 0xffff; AD1PCFGH = 0x3; - -#if defined(BOOT_PORT_B) + +#if defined(BOOT_PORT_NONE) +#elif defined(BOOT_PORT_B) TRISB |= (1 << BOOT_PIN); #elif defined(BOOT_PORT_C) TRISC |= (1 << BOOT_PIN); @@ -32,14 +42,13 @@ void initPins(void){ #endif } -#define UART_MAP_RX(rpn) uart_map_rx(rpn) void uart_map_rx(uint16_t rpn) { // map that pin to UART RX _U1RXR = rpn; } -#define UART_MAP_TX(rpn) uart_map_tx(rpn) -void uart_map_tx(uint16_t rpn) { +static __attribute__((always_inline)) void uart_map_tx(uint16_t rpn) { + // this big case statement benefits greatly from inlining this function. #define _RPxR(x) _RP ## x ## R #define CASE(x) case x: _RPxR(x)=_RPOUT_U1TX; break; switch (rpn){ @@ -82,42 +91,19 @@ void uart_map_tx(uint16_t rpn) { void initUart(void){ U1MODE = 0; U1STA = 0x2000; + uart_map_rx(RX_PIN); + uart_map_tx(TX_PIN); if (UART_BAUD_RATE < FCY/4.0f){ U1MODEbits.BRGH = 0; // High Baud Rate Select bit = off - U1BRG = FCY / (16.0f*UART_BAUD_RATE) - 1; + U1BRG = FCY / 16 / UART_BAUD_RATE - 1; } else { U1MODEbits.BRGH = 1; - U1BRG = FCY / (4.0f*UART_BAUD_RATE) - 1; - } - -/* make the RX pin an input */ -#if defined(UART_MAP_RX) - UART_MAP_RX(RX_PIN); -#elif defined RX_PORT_A - TRISA |= (1 << RX_PIN); - ANSELA &= ~(1 << RX_PIN); -#elif defined RX_PORT_B - TRISB |= (1 << RX_PIN); - ANSELB &= ~(1 << RX_PIN); -#elif defined RX_PORT_C - TRISC |= (1 << RX_PIN); - ANSELC &= ~(1 << RX_PIN); -#endif - -/* make the TX pin an output */ -#if defined UART_MAP_TX - UART_MAP_TX(TX_PIN); -#elif defined TX_PORT_A - TRISA &= ~(1 << TX_PIN); - ANSELA &= ~(1 << TX_PIN); -#elif defined TX_PORT_B - TRISB &= ~(1 << TX_PIN); - ANSELB &= ~(1 << TX_PIN); -#elif defined TX_PORT_C - TRISC &= ~(1 << TX_PIN); - ANSELC &= ~(1 << TX_PIN); -#endif + U1BRG = FCY / 4 / UART_BAUD_RATE - 1; + } + + /* note UART module overrides the PORT, LAT, and TRIS bits, so no + * need to set them */ U1MODEbits.UARTEN = 1; /* enable UART */ U1STAbits.UTXEN = 1; /* transmit enabled */ @@ -126,24 +112,30 @@ void initUart(void){ } void initTimers(void){ - /* initialize timer1 registers - timer 1 is used for determining if the - * rx buffer should be flushed b/c of local stale data (mis-transfers, - * etc) */ - T1CON = 0x0030; /* prescaler = 256 (4.27us/tick) */ - - /* initialize timer2 registers - timer 2 is used for determining if the - * the bootloader has been engaged recently */ - TMR2 = 0; + T2CON = T3CON = 0; + TMR2 = TMR3 = 0; + PR2 = PR3 = 0xffff; + + T2CONbits.T32 = 1; // merge timer 2 and timer 3 into a 32 bit timer + T2CONbits.TCKPS = 0b11; // 256 prescale + T2CONbits.TON = 1; +} - T2CON = 0x8030; /* prescaler = 256 */ +uint32_t getTimeTicks(){ + uint32_t n_ticks = 0; + n_ticks |= (uint32_t)TMR2; + n_ticks |= ((uint32_t)TMR3HLD)<<16; + return n_ticks; } -bool should_abort_boot(uint16_t counterValue) { - if(counterValue > NUM_OF_TMR2_OVERFLOWS){ - return true; - } +bool should_abort_boot() { + static const uint32_t BOOTLOADER_TIMEOUT_TICKS = (FCY / 256.0 * BOOT_LOADER_TIME); + if(getTimeTicks() > BOOTLOADER_TIMEOUT_TICKS){ + return true; + } - #if defined(BOOT_PORT_A) + #if defined(BOOT_PORT_NONE) + #elif defined(BOOT_PORT_A) if(PORTA & (1 << BOOT_PIN)) return true; #elif defined(BOOT_PORT_B) @@ -159,6 +151,16 @@ bool should_abort_boot(uint16_t counterValue) { return false; } +bool tryRxByte(uint8_t *outbyte){ + if (U1STAbits.URXDA){ + *outbyte = U1RXREG; + return true; + } + else{ + return false; + } +} + /// Device-specific implementations of bootloader operations void eraseByAddress(uint32_t address){ uint16_t offset; diff --git a/devices/pic24fj256gb106/boot_user.h b/devices/pic24fj256gb106/boot_user.h index 231cad0..081ef3b 100644 --- a/devices/pic24fj256gb106/boot_user.h +++ b/devices/pic24fj256gb106/boot_user.h @@ -1,38 +1,21 @@ -#ifndef _BOOTLOADER_H -#define _BOOTLOADER_H +#ifndef _BOOT_USER_H +#define _BOOT_USER_H #include #include +#ifndef __PIC24FJ256GB106__ +#error "platform settings do not match header file" +#endif +#define PLATFORM_STRING "pic24fj256gb106" + /** * @brief these defines will determine the boot pin to be utilized * * When the boot pin is pulled low, then bootloader will start, otherwise * the application will start on reset. */ -#define BOOT_PORT_B -#define BOOT_PIN 0 - -/** - * @brief choose the RX and TX pins by RP function and port/pin - * - * Each pin should have three defines associated with it: - * * port - * * pin number - * * RP assignment - * - * Assign the port by defining RX_PORT_A, RX_PORT_B, etc. - * Assign the pin by defining RX_PIN 1, RX_PIN 2, etc. - * Assign the remappable input by define RX_RPNUM to an RP number - * - * Same pattern for TX pins. - * - * If the pin has not been utilized before, it may be necessary to add this - * bit to the source code as well, but in those places, it should be clear - * what the user should do in the source code to make compatible with their - * application. - */ - +#define BOOT_PORT_NONE // UART communication baud rate, in Hz #define UART_BAUD_RATE 57600 @@ -47,8 +30,6 @@ * active at startup before moving on to the application */ #define BOOT_LOADER_TIME (10.0f) -#define STALE_MESSAGE_TIME (0.05f) - /* @brief this is the maximum size that can be programmed into the microcontroller * as part of one transaction using the CMD_WRITE_MAX_PROG_SIZE command @@ -57,46 +38,24 @@ * allow faster programming operations, but will consume more RAM. */ #define MAX_PROG_SIZE 0x80 -#define APPLICATION_START_ADDRESS 0x1000 -#define TIME_PER_TMR2_50k 0.213 -#define FCY 16000000UL /* instruction clock frequency, in Hz */ +#define APPLICATION_START_ADDRESS 0x2000 +#define FCY (16000000UL) /* instruction clock frequency, in Hz */ #define _FLASH_PAGE 512 /* _FLASH_PAGE should be the maximum page (in instructions) */ #define _FLASH_ROW 64 /* _FLASH_ROW = maximum write row (in instructions) */ -#define NUM_OF_TMR2_OVERFLOWS (uint16_t)((BOOT_LOADER_TIME/TIME_PER_TMR2_50k) + 1.0) - -#if defined(__PIC24FJ256GB106__) -#define PLATFORM_STRING "pic24fj256gb106" -#else -#warning "your device may not be supported" -#endif - -/** - * @brief initializes the oscillator - */ -void initOsc(void); - -/** - * @brief initializes the pins - */ -void initPins(void); - -/** - * @brief initializes the UART - */ -void initUart(void); - /** - * @brief initializes TMR1 and TMR2 + * @brief run the very first initialization + * @return true if we should continue with the bootloader + * false if we should jump directly to the application */ -void initTimers(void); +bool pre_boot(); /** * @brief determines if the bootloader should abort * @return true if the bootloader should abort, else false */ -bool should_abort_boot(uint16_t counterValue); +bool should_abort_boot(); /** * @brief reads the value at the address @@ -122,7 +81,7 @@ void writeInstr(uint32_t address, uint32_t instruction); /** * @brief writes two instructions, starting at the address * @param address the starting address (must be even) - * @param progDataArray a 32-bit, 2-element array containing the instruction + * @param progDataArray a 32-bit, 2-element array containing the instructions * words to be written to flash */ void doubleWordWrite(uint32_t address, uint32_t* progDataArray); @@ -130,14 +89,14 @@ void doubleWordWrite(uint32_t address, uint32_t* progDataArray); /** * @brief writes an entire row of instructions, starting at the address * @param address the starting address (must start a flash row) - * @param words a buffer containing the instructions to write + * @param words a buffer containing the _FLASH_ROW instructions to write */ void writeRow(uint32_t address, uint32_t* words); /** * @brief writes the maximum number of instructions * @param address the starting address - * @param progData a 32-bit, 2-element array containing the instruction words + * @param progData a 32-bit, MAX_PROG_SIZE-element array containing the instruction words * to be written to flash */ void writeMax(uint32_t address, uint32_t* progData); diff --git a/devices/pic24fj256gb106/config.h b/devices/pic24fj256gb106/config.h index 4a35bc0..f116658 100644 --- a/devices/pic24fj256gb106/config.h +++ b/devices/pic24fj256gb106/config.h @@ -1,7 +1,7 @@ /* PIC24FJ256GB106 Configuration Bit Settings */ -#ifndef CONFIG_H -#define CONFIG_H +#ifndef _CONFIG_H +#define _CONFIG_H #pragma config JTAGEN = OFF #pragma config GCP = OFF diff --git a/devices/pic24fj256gb106/p24FJ256GB106_app.gld b/devices/pic24fj256gb106/p24FJ256GB106_app.gld index 5f7eae1..6b41c89 100644 --- a/devices/pic24fj256gb106/p24FJ256GB106_app.gld +++ b/devices/pic24fj256gb106/p24FJ256GB106_app.gld @@ -33,7 +33,6 @@ * */ - OUTPUT_ARCH("24FJ256GB106") CRT0_STARTUP(crt0_standard.o) CRT1_STARTUP(crt1_standard.o) diff --git a/devices/pic24fj256gb106/p24FJ256GB106_boot.gld b/devices/pic24fj256gb106/p24FJ256GB106_boot.gld index ec0c25d..30cd09a 100644 --- a/devices/pic24fj256gb106/p24FJ256GB106_boot.gld +++ b/devices/pic24fj256gb106/p24FJ256GB106_boot.gld @@ -60,7 +60,7 @@ MEMORY ivt : ORIGIN = 0x4, LENGTH = 0xFC _reserved : ORIGIN = 0x100, LENGTH = 0x4 aivt : ORIGIN = 0x104, LENGTH = 0xFC - program (xr) : ORIGIN = 0x400, LENGTH = 0xC00 /* reduced to ensure that the bootloader doesn't encroach on application space */ + program (xr) : ORIGIN = 0x400, LENGTH = 0x1600 /* reduced to ensure that the bootloader doesn't encroach on application space */ CONFIG3 : ORIGIN = 0x2ABFA, LENGTH = 0x2 CONFIG2 : ORIGIN = 0x2ABFC, LENGTH = 0x2 CONFIG1 : ORIGIN = 0x2ABFE, LENGTH = 0x2 diff --git a/devices/pic24fvXkm/boot_user.h b/devices/pic24fvXkm/boot_user.h index 5af97b2..b72320a 100644 --- a/devices/pic24fvXkm/boot_user.h +++ b/devices/pic24fvXkm/boot_user.h @@ -1,5 +1,5 @@ -#ifndef _BOOTLOADER_H -#define _BOOTLOADER_H +#ifndef _BOOT_USER_H +#define _BOOT_USER_H #include #include