From f9926a0337653b96bc099cdde7d0c4fea4e160de Mon Sep 17 00:00:00 2001 From: Balazs Racz Date: Tue, 14 Dec 2021 23:51:24 +0100 Subject: [PATCH 1/3] Adds bootloader application for STM32-F091 (#587) * Adds F091 bootloader. * Fixes bootloader hardware pinout. Adds bootloader appentry pointer to interrupt vector table. Adds rflash command. * Renames the directory because the pinout is different actually. --- .../BootloaderHal.cxx | 1 + .../BootloaderHal.hxx | 1 + .../Makefile | 64 +++ .../NodeId.cxx | 15 + .../bootloader_startup.c | 1 + .../memory_map.ld | 1 + .../target.ld | 1 + .../st-stm32f091rc-nucleo/BootloaderHal.cxx | 182 +++++++++ boards/st-stm32f091rc-nucleo/Makefile | 6 + boards/st-stm32f091rc-nucleo/memory_map.ld | 9 +- .../BootloaderHal.hxx | 313 +++++++++++++++ .../st-stm32f0x1_x2_x8-generic/bootloader.ld | 178 ++++++++ .../bootloader_startup.c | 379 ++++++++++++++++++ boards/st-stm32f0x1_x2_x8-generic/startup.c | 2 +- 14 files changed, 1151 insertions(+), 2 deletions(-) create mode 120000 applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/BootloaderHal.cxx create mode 120000 applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/BootloaderHal.hxx create mode 100644 applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/Makefile create mode 100644 applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/NodeId.cxx create mode 120000 applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/bootloader_startup.c create mode 120000 applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/memory_map.ld create mode 120000 applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/target.ld create mode 100644 boards/st-stm32f091rc-nucleo/BootloaderHal.cxx create mode 100644 boards/st-stm32f0x1_x2_x8-generic/BootloaderHal.hxx create mode 100644 boards/st-stm32f0x1_x2_x8-generic/bootloader.ld create mode 100644 boards/st-stm32f0x1_x2_x8-generic/bootloader_startup.c diff --git a/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/BootloaderHal.cxx b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/BootloaderHal.cxx new file mode 120000 index 000000000..99b8babee --- /dev/null +++ b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/BootloaderHal.cxx @@ -0,0 +1 @@ +../../../../boards/st-stm32f091rc-nucleo/BootloaderHal.cxx \ No newline at end of file diff --git a/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/BootloaderHal.hxx b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/BootloaderHal.hxx new file mode 120000 index 000000000..9843f233c --- /dev/null +++ b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/BootloaderHal.hxx @@ -0,0 +1 @@ +../../../../boards/st-stm32f0x1_x2_x8-generic/BootloaderHal.hxx \ No newline at end of file diff --git a/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/Makefile b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/Makefile new file mode 100644 index 000000000..23764dd39 --- /dev/null +++ b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/Makefile @@ -0,0 +1,64 @@ +APP_PATH ?= $(realpath ../..) +-include $(APP_PATH)/config.mk +-include local_config.mk + +OPENMRNPATH ?= $(shell \ +sh -c "if [ \"X`printenv OPENMRNPATH`\" != \"X\" ]; then printenv OPENMRNPATH; \ + elif [ -d /opt/openmrn/src ]; then echo /opt/openmrn; \ + elif [ -d ~/openmrn/src ]; then echo ~/openmrn; \ + elif [ -d ../../../src ]; then echo ../../..; \ + else echo OPENMRNPATH not found; fi" \ +) + +-include $(OPENMRNPATH)/etc/config.mk +LINKCORELIBS = -lopenlcb -lutils + +include $(OPENMRNPATH)/etc/stm32cubef0.mk + +CFLAGSEXTRA += -DSTM32F091xC +CXXFLAGSEXTRA += -DSTM32F091xC +SYSLIBRARIES += -lfreertos_drivers_stm32cubef091xc +OPENOCDARGS = -f board/st_nucleo_f0.cfg + +export TARGET := bare.armv6m + +include $(OPENMRNPATH)/etc/prog.mk + +ifndef DEFAULT_ADDRESS +DEFAULT_ADDRESS=0xff +endif + +include $(OPENMRNPATH)/etc/node_id.mk + +SYSLIB_SUBDIRS= +OBJEXTRA= +LDFLAGSEXTRA += -nostartfiles + +all: $(EXECUTABLE).bin + +# How to use: make multibin ADDRESS=0x20 ADDRHIGH=0x45 NUM=3 +# starting address, high bits (user range), count +multibin: + for i in $$(seq 1 $(NUM)) ; do $(MAKE) $(EXECUTABLE).bin ADDRESS=$$(printf 0x%02x $$(($(ADDRESS)+$$i))) ; cp $(EXECUTABLE).bin $(EXECUTABLE).$(MCU_SHORT).$$(printf %02x%02x $(ADDRHIGH) $$(($(ADDRESS)+$$i-1))).bin ; done + +ifeq ($(call find_missing_deps,OPENOCDPATH OPENOCDSCRIPTSPATH),) + +flash: $(EXECUTABLE)$(EXTENTION) $(EXECUTABLE).bin $(EXECUTABLE).lst + @if ps ax -o comm | grep -q openocd ; then echo openocd already running. quit existing first. ; exit 1 ; fi + $(GDB) $< -ex "target remote | $(OPENOCDPATH)/openocd -c \"gdb_port pipe\" --search $(OPENOCDSCRIPTSPATH) $(OPENOCDARGS)" -ex "monitor reset halt" -ex "load" -ex "monitor reset init" -ex "monitor reset run" -ex "detach" -ex "quit" + +lflash: $(EXECUTABLE).bin $(EXECUTABLE).lst + @if ps ax -o comm | grep -q openocd ; then echo openocd already running. quit existing first. ; exit 1 ; fi + $(GDB) $< -ex "target remote | $(OPENOCDPATH)/openocd -c \"gdb_port pipe\" --search $(OPENOCDSCRIPTSPATH) $(OPENOCDARGS)" -ex "monitor reset halt" -ex "monitor program $< 0x08000000 verify reset exit" -ex "monitor reset run" -ex "detach" -ex "quit" + +gdb: + @if ps ax -o comm | grep -q openocd ; then echo openocd already running. quit existing first. ; exit 1 ; fi + $(GDB) $(EXECUTABLE)$(EXTENTION) -ex "target remote | $(OPENOCDPATH)/openocd -c \"gdb_port pipe\" --search $(OPENOCDSCRIPTSPATH) $(OPENOCDARGS)" -ex "continue" # -ex "monitor reset halt" + +else + +flash gdb: + echo OPENOCD not found ; exit 1 + +endif + diff --git a/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/NodeId.cxx b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/NodeId.cxx new file mode 100644 index 000000000..1b0457070 --- /dev/null +++ b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/NodeId.cxx @@ -0,0 +1,15 @@ +#include "openlcb/If.hxx" +#include "address.h" + +#ifndef NODEID_HIGH_BITS +#define NODEID_HIGH_BITS 0x18 +#endif + +extern const openlcb::NodeID NODE_ID; +const openlcb::NodeID NODE_ID = 0x050101010000ULL | (NODEID_HIGH_BITS << 8) | NODEID_LOW_BITS; +extern const uint16_t DEFAULT_ALIAS; +const uint16_t DEFAULT_ALIAS = 0x400 | NODEID_LOW_BITS; + +//#define BOOTLOADER_STREAM +//#define BOOTLOADER_DATAGRAM +//#include "openlcb/Bootloader.hxx" diff --git a/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/bootloader_startup.c b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/bootloader_startup.c new file mode 120000 index 000000000..893503fbe --- /dev/null +++ b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/bootloader_startup.c @@ -0,0 +1 @@ +../../../../boards/st-stm32f0x1_x2_x8-generic/bootloader_startup.c \ No newline at end of file diff --git a/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/memory_map.ld b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/memory_map.ld new file mode 120000 index 000000000..eb8a16a86 --- /dev/null +++ b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/memory_map.ld @@ -0,0 +1 @@ +../../../../boards/st-stm32f091rc-nucleo/memory_map.ld \ No newline at end of file diff --git a/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/target.ld b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/target.ld new file mode 120000 index 000000000..127e976d7 --- /dev/null +++ b/applications/bootloader/targets/bare.armv6m.st-stm32f091rc-nucleo-dev-board/target.ld @@ -0,0 +1 @@ +../../../../boards/st-stm32f0x1_x2_x8-generic/bootloader.ld \ No newline at end of file diff --git a/boards/st-stm32f091rc-nucleo/BootloaderHal.cxx b/boards/st-stm32f091rc-nucleo/BootloaderHal.cxx new file mode 100644 index 000000000..86fa2a5d8 --- /dev/null +++ b/boards/st-stm32f091rc-nucleo/BootloaderHal.cxx @@ -0,0 +1,182 @@ +#include + +#define BOOTLOADER_STREAM +//#define BOOTLOADER_DATAGRAM + +#include "BootloaderHal.hxx" +#include "bootloader_hal.h" + +#include "nmranet_config.h" +#include "openlcb/Defs.hxx" +#include "Stm32Gpio.hxx" +#include "openlcb/Bootloader.hxx" +#include "openlcb/If.hxx" +#include "utils/GpioInitializer.hxx" + +const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; +const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4}; +const uint32_t HSEValue = 8000000; + +int g_death_lineno = 0; + +extern "C" { + +GPIO_PIN(LED_GREEN, LedPin, A, 5); +GPIO_PIN(SW1, GpioInputPU, C, 13); + +static constexpr unsigned clock_hz = 48000000; + +void bootloader_hw_set_to_safe(void) +{ + SW1_Pin::hw_set_to_safe(); + LED_GREEN_Pin::hw_set_to_safe(); +} + +extern void bootloader_reset_segments(void); +//extern unsigned long cm3_cpu_clock_hz; + +/** Setup the system clock */ +static void clock_setup(void) +{ + /* reset clock configuration to default state */ + RCC->CR = RCC_CR_HSITRIM_4 | RCC_CR_HSION; + while (!(RCC->CR & RCC_CR_HSIRDY)) + ; + +#define USE_EXTERNAL_8_MHz_CLOCK_SOURCE 1 +/* configure PLL: 8 MHz * 6 = 48 MHz */ +#if USE_EXTERNAL_8_MHz_CLOCK_SOURCE + RCC->CR |= RCC_CR_HSEON | RCC_CR_HSEBYP; + while (!(RCC->CR & RCC_CR_HSERDY)) + ; + RCC->CFGR = RCC_CFGR_PLLMUL6 | RCC_CFGR_PLLSRC_HSE_PREDIV | RCC_CFGR_SW_HSE; + while (!((RCC->CFGR & RCC_CFGR_SWS) == RCC_CFGR_SWS_HSE)) + ; +#else + RCC->CFGR = RCC_CFGR_PLLMUL6 | RCC_CFGR_PLLSRC_HSI_PREDIV | RCC_CFGR_SW_HSI; + while (!((RCC->CFGR & RCC_CFGR_SWS) == RCC_CFGR_SWS_HSI)) + ; +#endif + /* enable PLL */ + RCC->CR |= RCC_CR_PLLON; + while (!(RCC->CR & RCC_CR_PLLRDY)) + ; + + /* set PLL as system clock */ + RCC->CFGR = (RCC->CFGR & (~RCC_CFGR_SW)) | RCC_CFGR_SW_PLL; + while (!((RCC->CFGR & RCC_CFGR_SWS) == RCC_CFGR_SWS_PLL)) + ; +} + +void bootloader_hw_init() +{ + /* Globally disables interrupts until the FreeRTOS scheduler is up. */ + asm("cpsid i\n"); + + /* these FLASH settings enable opertion at 48 MHz */ + __HAL_FLASH_PREFETCH_BUFFER_ENABLE(); + __HAL_FLASH_SET_LATENCY(FLASH_LATENCY_1); + + /* Reset HSI14 bit */ + RCC->CR2 &= (uint32_t)0xFFFFFFFEU; + + /* Disable all interrupts */ + RCC->CIR = 0x00000000U; + + clock_setup(); + + /* enable peripheral clocks */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_CAN1_CLK_ENABLE(); + + /* setup pinmux */ + GPIO_InitTypeDef gpio_init; + memset(&gpio_init, 0, sizeof(gpio_init)); + + /* CAN pinmux on PB8 and PB9 */ + gpio_init.Mode = GPIO_MODE_AF_PP; + // Disables pull-ups because this is a 5V tolerant pin. + gpio_init.Pull = GPIO_NOPULL; + gpio_init.Speed = GPIO_SPEED_FREQ_HIGH; + gpio_init.Alternate = GPIO_AF4_CAN; + gpio_init.Pin = GPIO_PIN_8; + HAL_GPIO_Init(GPIOB, &gpio_init); + gpio_init.Pin = GPIO_PIN_9; + HAL_GPIO_Init(GPIOB, &gpio_init); + + LED_GREEN_Pin::hw_init(); + SW1_Pin::hw_init(); + + /* disable sleep, enter init mode */ + CAN->MCR = CAN_MCR_INRQ; + + /* Time triggered tranmission off + * Bus off state is left automatically + * Auto-Wakeup mode disabled + * automatic re-transmission enabled + * receive FIFO not locked on overrun + * TX FIFO mode on + */ + CAN->MCR |= (CAN_MCR_ABOM | CAN_MCR_TXFP); + + /* Setup timing. + * 125,000 Kbps = 8 usec/bit + */ + CAN->BTR = (CAN_BS1_5TQ | CAN_BS2_2TQ | CAN_SJW_1TQ | + ((clock_hz / 1000000) - 1)); + + /* enter normal mode */ + CAN->MCR &= ~CAN_MCR_INRQ; + + /* Enter filter initialization mode. Filter 0 will be used as a single + * 32-bit filter, ID Mask Mode, we accept everything, no mask. + */ + CAN->FMR |= CAN_FMR_FINIT; + CAN->FM1R = 0; + CAN->FS1R = 0x000000001; + CAN->FFA1R = 0; + CAN->sFilterRegister[0].FR1 = 0; + CAN->sFilterRegister[0].FR2 = 0; + + /* Activeate filter and exit initialization mode. */ + CAN->FA1R = 0x000000001; + CAN->FMR &= ~CAN_FMR_FINIT; +} + +void bootloader_led(enum BootloaderLed id, bool value) +{ + switch(id) + { + case LED_ACTIVE: + LED_GREEN_Pin::set(value); + return; + case LED_WRITING: + LED_GREEN_Pin::set(value); + return; + case LED_CSUM_ERROR: + return; + case LED_REQUEST: + return; + case LED_FRAME_LOST: + return; + default: + /* ignore */ + break; + } +} + +bool request_bootloader() +{ + extern uint32_t __bootloader_magic_ptr; + if (__bootloader_magic_ptr == REQUEST_BOOTLOADER) { + __bootloader_magic_ptr = 0; + LED_GREEN_Pin::set(true); + return true; + } + LED_GREEN_Pin::set(SW1_Pin::get()); + return !SW1_Pin::get(); +} + +} // extern "C" diff --git a/boards/st-stm32f091rc-nucleo/Makefile b/boards/st-stm32f091rc-nucleo/Makefile index bff14cf9b..51d512bf7 100644 --- a/boards/st-stm32f091rc-nucleo/Makefile +++ b/boards/st-stm32f091rc-nucleo/Makefile @@ -52,3 +52,9 @@ flash gdb: echo OPENOCD not found ; exit 1 endif + +BLOAD_HOST ?= localhost + +rflash: $(EXECUTABLE).bin + $(OPENMRNPATH)/applications/bootloader_client/targets/linux.x86/bootloader_client -w 100 -W 100 -c tiva123 -i $(BLOAD_HOST) -r -n 0x0501010118$$(printf %02x $(ADDRESS)) -f $(EXECUTABLE).bin + cp -f $(EXECUTABLE)$(EXTENTION) $(EXECUTABLE)-$$(printf %02x $(ADDRESS))$(EXTENTION) diff --git a/boards/st-stm32f091rc-nucleo/memory_map.ld b/boards/st-stm32f091rc-nucleo/memory_map.ld index 8e704deeb..6ea720a70 100644 --- a/boards/st-stm32f091rc-nucleo/memory_map.ld +++ b/boards/st-stm32f091rc-nucleo/memory_map.ld @@ -1,9 +1,14 @@ +___ram_for_bootloader_api = 8; + MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 240K EEPROMEMU (r) : ORIGIN = 0x0803C000, LENGTH = 8K BOOTLOADER (rx) : ORIGIN = 0x0803E000, LENGTH = 8K - RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K + RAM (rwx) : ORIGIN = 0x20000000, + LENGTH = 32K - ___ram_for_bootloader_api + BOOTLOADERAPI (rw) : ORIGIN = 0x20000000 + 32K - ___ram_for_bootloader_api, + LENGTH = ___ram_for_bootloader_api } __flash_start = ORIGIN(FLASH); @@ -12,4 +17,6 @@ __eeprom_start = ORIGIN(EEPROMEMU); __eeprom_end = ORIGIN(EEPROMEMU) + LENGTH(EEPROMEMU); __bootloader_start = ORIGIN(BOOTLOADER); __app_header_offset = 0x270; +__app_header_address = ORIGIN(FLASH) + __app_header_offset; __bootloader_magic_ptr = ORIGIN(RAM); +__application_node_id = ORIGIN(BOOTLOADERAPI); diff --git a/boards/st-stm32f0x1_x2_x8-generic/BootloaderHal.hxx b/boards/st-stm32f0x1_x2_x8-generic/BootloaderHal.hxx new file mode 100644 index 000000000..ed687cee3 --- /dev/null +++ b/boards/st-stm32f0x1_x2_x8-generic/BootloaderHal.hxx @@ -0,0 +1,313 @@ +/** \copyright + * Copyright (c) 2014, Balazs Racz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \file BootloaderHal.hxx + * + * STM-specific implementation of the HAL (Hardware Abstraction Layer) used by + * the OpenLCB bootloader. + * + * Usage: You have to define in your makefile a symbol like + * CXXFLAGSEXTRA+= -DSTM32F091xC + * + * @author Balazs Racz + * @date 31 January 2015 + */ + +#ifndef _BOARDS_STM32F0_BOOTLOADERHAL_HXX_ +#define _BOARDS_STM32F0_BOOTLOADERHAL_HXX_ + +#include + +#include "bootloader_hal.h" +#include "stm32f0xx_hal_conf.h" + +#include "nmranet_config.h" +#include "openlcb/Defs.hxx" +#include "utils/Crc.hxx" +#include "Stm32Gpio.hxx" + +extern "C" { + +void get_flash_boundaries(const void **flash_min, const void **flash_max, + const struct app_header **app_header) +{ + extern char __flash_start; + extern char __flash_end; + extern struct app_header __app_header_address; + *flash_min = &__flash_start; + *flash_max = &__flash_end; + *app_header = &__app_header_address; +} + +void checksum_data(const void *data, uint32_t size, uint32_t *checksum) +{ + extern uint8_t __flash_start; + if (static_cast(data) == &__flash_start) + { + // ignores the reset vector for checksum calculations. + data = static_cast(data) + 8; + size -= 8; + } + memset(checksum, 0, 16); + crc3_crc16_ibm(data, size, reinterpret_cast(checksum)); +} + +extern const openlcb::NodeID NODE_ID; + +uint16_t nmranet_alias() +{ + /// TODO: we should probably read this from someplace else + return 0x400 ^ (NODE_ID & 0xFFF); +} + +uint64_t nmranet_nodeid() +{ + /// TODO(balazs.racz): read some form of EEPROM instead. + return NODE_ID; +} + +bool read_can_frame(struct can_frame *can_frame) +{ + if (!(CAN->RF0R & CAN_RF0R_FMP0)) + { + return false; + } + + /* Read a message from CAN and clear the interrupt source */ + if (CAN->sFIFOMailBox[0].RIR & CAN_RI0R_IDE) + { + /* extended frame */ + can_frame->can_id = CAN->sFIFOMailBox[0].RIR >> 3; + can_frame->can_eff = 1; + } + else + { + /* standard frame */ + can_frame->can_id = CAN->sFIFOMailBox[0].RIR >> 21; + can_frame->can_eff = 0; + } + if (CAN->sFIFOMailBox[0].RIR & CAN_RI0R_RTR) + { + /* remote frame */ + can_frame->can_rtr = 1; + can_frame->can_dlc = 0; + } + else + { + /* data frame */ + can_frame->can_rtr = 0; + can_frame->can_dlc = CAN->sFIFOMailBox[0].RDTR & CAN_RDT0R_DLC; + can_frame->data[0] = (CAN->sFIFOMailBox[0].RDLR >> 0) & 0xFF; + can_frame->data[1] = (CAN->sFIFOMailBox[0].RDLR >> 8) & 0xFF; + can_frame->data[2] = (CAN->sFIFOMailBox[0].RDLR >> 16) & 0xFF; + can_frame->data[3] = (CAN->sFIFOMailBox[0].RDLR >> 24) & 0xFF; + can_frame->data[4] = (CAN->sFIFOMailBox[0].RDHR >> 0) & 0xFF; + can_frame->data[5] = (CAN->sFIFOMailBox[0].RDHR >> 8) & 0xFF; + can_frame->data[6] = (CAN->sFIFOMailBox[0].RDHR >> 16) & 0xFF; + can_frame->data[7] = (CAN->sFIFOMailBox[0].RDHR >> 24) & 0xFF; + } + /* release FIFO */; + CAN->RF0R |= CAN_RF0R_RFOM0; + return true; +} + +bool try_send_can_frame(const struct can_frame &can_frame) +{ + /* load the next message to transmit */ + volatile CAN_TxMailBox_TypeDef *mailbox; + if (CAN->TSR & CAN_TSR_TME0) + { + mailbox = CAN->sTxMailBox + 0; + } + else if (CAN->TSR & CAN_TSR_TME1) + { + mailbox = CAN->sTxMailBox + 1; + } + else if (CAN->TSR & CAN_TSR_TME2) + { + mailbox = CAN->sTxMailBox + 2; + } + else + { + // no buffer available + return false; + } + + /* setup frame */ + if (can_frame.can_eff) + { + mailbox->TIR = (can_frame.can_id << 3) | CAN_TI0R_IDE; + } + else + { + mailbox->TIR = can_frame.can_id << 21; + } + if (can_frame.can_rtr) + { + mailbox->TIR |= CAN_TI0R_RTR; + } + else + { + mailbox->TDTR = can_frame.can_dlc; + mailbox->TDLR = (can_frame.data[0] << 0) | + (can_frame.data[1] << 8) | + (can_frame.data[2] << 16) | + (can_frame.data[3] << 24); + mailbox->TDHR = (can_frame.data[4] << 0) | + (can_frame.data[5] << 8) | + (can_frame.data[6] << 16) | + (can_frame.data[7] << 24); + } + + /* request transmission */ + mailbox->TIR |= CAN_TI0R_TXRQ; + + return true; +} + +void bootloader_reboot(void) +{ + /* wait for TX messages to all go out */ + while (!(CAN->TSR & CAN_TSR_TME0)); + while (!(CAN->TSR & CAN_TSR_TME1)); + while (!(CAN->TSR & CAN_TSR_TME2)); + + bootloader_hw_set_to_safe(); + + HAL_NVIC_SystemReset(); +} + +void application_entry(void) +{ + bootloader_hw_set_to_safe(); + /* Globally disables interrupts. */ + asm("cpsid i\n"); + extern uint64_t __application_node_id; + __application_node_id = nmranet_nodeid(); + extern char __flash_start; + // We store the application reset in interrupt vecor 13, which is reserved + // / unused on all Cortex-M0 processors. + asm volatile(" mov r3, %[flash_addr] \n" + : + : [flash_addr] "r"(&__flash_start)); + asm volatile(" ldr r0, [r3]\n" + " mov sp, r0\n" + " ldr r0, [r3, #52]\n" + " bx r0\n"); +} + +void raw_erase_flash_page(const void *address) +{ + bootloader_led(LED_ACTIVE, 0); + bootloader_led(LED_WRITING, 1); + + FLASH_EraseInitTypeDef erase_init; + erase_init.TypeErase = FLASH_TYPEERASE_PAGES; + erase_init.PageAddress = (uint32_t)address; + erase_init.NbPages = 1; + + uint32_t page_error; + HAL_FLASH_Unlock(); + HAL_FLASHEx_Erase(&erase_init, &page_error); + HAL_FLASH_Lock(); + + bootloader_led(LED_WRITING, 0); + bootloader_led(LED_ACTIVE, 1); +} + +void erase_flash_page(const void *address) +{ + raw_erase_flash_page(address); + + extern char __flash_start; + if (static_cast(address) == &__flash_start) { + // If we erased page zero, we ensure to write back the reset pointer + // immiediately or we brick the bootloader. + extern unsigned long *__stack; + extern void reset_handler(void); + uint32_t bootdata[2]; + bootdata[0] = reinterpret_cast(&__stack); + bootdata[1] = reinterpret_cast(&reset_handler); + raw_write_flash(address, bootdata, sizeof(bootdata)); + } +} + +void raw_write_flash(const void *address, const void *data, uint32_t size_bytes) +{ + uint32_t *data_ptr = (uint32_t*)data; + uint32_t addr_ptr = (uintptr_t)address; + bootloader_led(LED_ACTIVE, 0); + bootloader_led(LED_WRITING, 1); + + HAL_FLASH_Unlock(); + while (size_bytes) + { + HAL_FLASH_Program((uint32_t)FLASH_TYPEPROGRAM_WORD, + (uint32_t)addr_ptr, *data_ptr); + size_bytes -= sizeof(uint32_t); + addr_ptr += sizeof(uint32_t); + ++data_ptr; + } + HAL_FLASH_Lock(); + + bootloader_led(LED_WRITING, 0); + bootloader_led(LED_ACTIVE, 1); +} + +void write_flash(const void *address, const void *data, uint32_t size_bytes) +{ + extern char __flash_start; + if (address == &__flash_start) { + address = static_cast(address) + 8; + data = static_cast(data) + 8; + size_bytes -= 8; + } + raw_write_flash(address, (uint32_t*)data, (size_bytes + 3) & ~3); +} + +void get_flash_page_info(const void *address, const void **page_start, + uint32_t *page_length_bytes) +{ + // STM32F091 has 2 KB flash pages. + uint32_t value = (uint32_t)address; + value &= ~(FLASH_PAGE_SIZE - 1); + *page_start = (const void *)value; + *page_length_bytes = FLASH_PAGE_SIZE; +} + +uint16_t flash_complete(void) +{ + return 0; +} + +void ignore_fn(void) +{ +} + +} // extern "C" + + +#endif // _BOARDS_STM32F0_BOOTLOADERHAL_HXX_ diff --git a/boards/st-stm32f0x1_x2_x8-generic/bootloader.ld b/boards/st-stm32f0x1_x2_x8-generic/bootloader.ld new file mode 100644 index 000000000..1683c0842 --- /dev/null +++ b/boards/st-stm32f0x1_x2_x8-generic/bootloader.ld @@ -0,0 +1,178 @@ +OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +ENTRY(reset_handler) +SEARCH_DIR(.) +GROUP(-lgcc -lc) + +/* include device specific memory map */ +INCLUDE memory_map.ld + +__top_RAM = ORIGIN(RAM) + LENGTH(RAM); + +__cs3_heap_end = __top_RAM; + +__start_ram = ORIGIN(RAM); +__end_ram = __top_RAM; + +SECTIONS +{ + /* INTERRUPT VECTORS */ + .interrupt_vector : + { + FILL(0xff) + KEEP(*(.interrupt_vector)) + + /* This will force the app header to be cleared if the bootloader is + flashed on top of an application. */ + . = __app_header_offset; + QUAD(0); /* App header: checksum for vector table. */ + QUAD(0); + LONG(0); /* App header: data size */ + QUAD(0); /* App header: checksum for payload. */ + QUAD(0); + } > FLASH + + + /* MAIN TEXT SECTION */ + .text : ALIGN(4) + { + + /* Global Section Table */ + . = ALIGN(4) ; + __section_table_start = .; + __data_section_table = .; + LONG(LOADADDR(.data)); + LONG( ADDR(.data)) ; + LONG( SIZEOF(.data)); + __data_section_table_end = .; + __bss_section_table = .; + LONG( ADDR(.bss)); + LONG( SIZEOF(.bss)); + __bss_section_table_end = .; + __section_table_end = . ; + /* End of Global Section Table */ + + + *(.after_vectors*) + + *(SORT(.text*)) + *(.rodata) + *(SORT(.rodata.*)) + . = ALIGN(4); + + /* C++ constructors etc */ + . = ALIGN(4); + KEEP(*(.init)) + + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + + /* KEEP(*(.fini)); */ + + . = ALIGN(4); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + + . = ALIGN(4); + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + /* End C++ */ + + __text_section_guard = .; + LONG( 0 ); + } > BOOTLOADER + + /* + * for exception handling/unwind - some Newlib functions (in common + * with C++ and STDC++) use this. + */ + .ARM.extab : ALIGN(4) + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > BOOTLOADER + __exidx_start = .; + + .ARM.exidx : ALIGN(4) + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > BOOTLOADER + __exidx_end = .; + + _etext = .; + + + /* MAIN DATA SECTION */ + + .uninit_RESERVED(NOLOAD) : ALIGN(4) + { + __bootloader_magic_ptr_actual = .; + LONG( 0 ); /* The magic pointer is in the uninit part. */ + KEEP(*(.bss.$RESERVED*)) + . = ALIGN(4) ; + _end_uninit_RESERVED = .; + } > RAM + + ASSERT(__bootloader_magic_ptr_actual == __bootloader_magic_ptr, "Bootloader magic ptr is not at the expected location.") + + + /* It seems that in order for LPCXpresso to properly flash the data + section, an alignment of 256 bytes is necessary. Otherwise the separate + flashing of the data section will corrupt the end of the text section. */ + .data : ALIGN(256) + { + FILL(0xff) + _data = .; + *(vtable) + __impure_data_start = .; + *(.data.impure_data) + __impure_data_end = .; + *(.data*) + + /* this magic is needed for the device tables of openMRN */ + . = ALIGN (8); + KEEP(*( SORT (.device.table.*))) ; + . = ALIGN (4); + + _edata = .; + } > RAM AT>BOOTLOADER + + + /* MAIN BSS SECTION */ + .bss : ALIGN(4) + { + _bss = .; + *(.bss*) + *(COMMON) + . = ALIGN(4) ; + _ebss = .; + PROVIDE(end = .); + } > RAM + + + /* DEFAULT NOINIT SECTION */ + .noinit (NOLOAD): ALIGN(4) + { + _noinit = .; + *(.noinit*) + . = ALIGN(4) ; + _end_noinit = .; + } > RAM + + PROVIDE(_pvHeapStart = .); + PROVIDE(__cs3_heap_start = .); + /* This pointer will be written to the SP register at reset. */ + PROVIDE(__stack = __top_RAM); + + PROVIDE(__impure_data_size = __impure_data_end - __impure_data_start); +} diff --git a/boards/st-stm32f0x1_x2_x8-generic/bootloader_startup.c b/boards/st-stm32f0x1_x2_x8-generic/bootloader_startup.c new file mode 100644 index 000000000..eb4160980 --- /dev/null +++ b/boards/st-stm32f0x1_x2_x8-generic/bootloader_startup.c @@ -0,0 +1,379 @@ +/** @copyright + * Copyright (c) 2019, Stuart W Baker + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @file bootloader_startup.c + * This file sets up the runtime environment for TI Stellaris/Tiva MCUs. + * + * @author Stuart W. Baker + * @date 15 September 2019 + */ + +#include + +#include "bootloader_hal.h" +/* we define this our selves because TivaWare forces us to otherwise bring in + * a device specific header to define this. We want to keep this file generic + * to all Cortex-M based TI MCU's + */ +#define NVIC_INT_CTRL_R (*((volatile uint32_t *)0xE000ED04)) + +/* prototypes */ +extern unsigned long *__stack; +extern void reset_handler(void); +extern void bootloader_entry(void); +static void nmi_handler(void); +static void hard_fault_handler(void); +extern void SVC_Handler(void); +extern void PendSV_Handler(void); +extern void SysTick_Handler(void); + +extern void __libc_init_array(void); + +extern int main(int argc, char *argv[]); + +extern void watchdog_interrupt_handler(void); +extern void pvd_vddio2_interrupt_handler(void); +extern void rtc_interrupt_handler(void); +extern void flash_interrupt_handler(void); +extern void rcc_crs_interrupt_handler(void); +extern void external0_1_interrupt_handler(void); +extern void external2_3_interrupt_handler(void); +extern void external4_15_interrupt_handler(void); +extern void touch_interrupt_handler(void); +extern void dma_ch1_interrupt_handler(void); +extern void dma_ch2_3_dma2_ch1_2_interrupt_handler(void); +extern void dma_ch4_5_6_7_dma2_ch3_4_5_interrupt_handler(void); +extern void adc_comp_interrupt_handler(void); +extern void timer1_break_update_trigger_commutation_interrupt_handler(void); +extern void timer1_cc_interrupt_handler(void); +extern void timer2_interrupt_handler(void); +extern void timer3_interrupt_handler(void); +extern void timer6_dac_interrupt_handler(void); +extern void timer7_interrupt_handler(void); +extern void timer14_interrupt_handler(void); +extern void timer15_interrupt_handler(void); +extern void timer16_interrupt_handler(void); +extern void timer17_interrupt_handler(void); +extern void i2c1_interrupt_handler(void); +extern void i2c2_interrupt_handler(void); +extern void spi1_interrupt_handler(void); +extern void spi2_interrupt_handler(void); +extern void uart1_interrupt_handler(void); +extern void uart2_interrupt_handler(void); +extern void uart3_4_5_6_7_8_interrupt_handler(void); +extern void cec_can_interrupt_handler(void); +extern void usb_interrupt_handler(void); + +extern void ignore_fn(void); + +extern const unsigned long cpu_clock_hz; +/** CPU clock speed. */ +const unsigned long cpu_clock_hz = 48000000; +uint32_t SystemCoreClock = 48000000; + +/** Exception table */ +__attribute__ ((section(".interrupt_vector"))) +void (* const __interrupt_vector[])(void) = +{ + (void (*)(void))(&__stack), /**< 0 initial stack pointer */ + reset_handler, /**< 1 reset vector */ + nmi_handler, /**< 2 non-maskable interrupt */ + hard_fault_handler, /**< 3 hard fault */ + 0, /**< 4 reserved */ + 0, /**< 5 reserved */ + 0, /**< 6 reserved */ + 0, /**< 7 reserved */ + 0, /**< 8 reserved */ + 0, /**< 9 reserved */ + 0, /**< 10 reserved */ + SVC_Handler, /**< 11 SV call */ + 0, /**< 12 reservedd */ + reset_handler, /**< 13 reserved -- bootloader appentry */ + PendSV_Handler, /**< 14 pend SV */ + SysTick_Handler, /**< 15 system tick */ + watchdog_interrupt_handler, /**< 16 watchdog timer */ + + /** PVD and VDDIO2 supply comparator + EXTI lines[31,16] */ + pvd_vddio2_interrupt_handler, /**< 17 */ + + /** real time clock + EXTI lines[19,17,20] */ + rtc_interrupt_handler, /**< 18 */ + flash_interrupt_handler, /**< 19 flash global interrupt */ + rcc_crs_interrupt_handler, /**< 20 RCC and CRS global interrupt */ + external0_1_interrupt_handler, /**< 21 EXTI line[1:0] */ + external2_3_interrupt_handler, /**< 22 EXTI line[3:2] */ + external4_15_interrupt_handler, /**< 23 EXTI line[15:4] */ + touch_interrupt_handler, /**< 24 touch sensing */ + dma_ch1_interrupt_handler, /**< 25 DMA channel 1 */ + + /** DMA channel 2 and 3, DMA2 channel 1 and 2 */ + dma_ch2_3_dma2_ch1_2_interrupt_handler, /* 26 */ + + /** DMA channel 4, 5, 6, and 7, DMA2 channel 3, 4, and 5 */ + dma_ch4_5_6_7_dma2_ch3_4_5_interrupt_handler, /* 27 */ + adc_comp_interrupt_handler, /**< 28 ADC and COMP + EXTI line[22:21] */ + + /** timer 1 break, update, trigger, and commutation */ + timer1_break_update_trigger_commutation_interrupt_handler, /* 29 */ + timer1_cc_interrupt_handler, /**< 30 timer 1 capture compare */ + timer2_interrupt_handler, /**< 31 timer 2 */ + timer3_interrupt_handler, /**< 32 timer 3 */ + timer6_dac_interrupt_handler, /**< 33 timer 6 and DAC underrun */ + timer7_interrupt_handler, /**< 34 timer 7 */ + timer14_interrupt_handler, /**< 35 timer 14 */ + timer15_interrupt_handler, /**< 36 timer 15 */ + timer16_interrupt_handler, /**< 37 timer 16 */ + timer17_interrupt_handler, /**< 38 timer 17 */ + i2c1_interrupt_handler, /**< 39 I2C1 + EXTI line[23] */ + i2c2_interrupt_handler, /**< 40 I2C2 */ + spi1_interrupt_handler, /**< 41 SPI1 */ + spi2_interrupt_handler, /**< 42 SPI2 */ + uart1_interrupt_handler, /**< 43 UART1 + EXTI line[25] */ + uart2_interrupt_handler, /**< 44 UART2 + EXTI line[26] */ + + /** UART3, UART4, UART5, UART6, UART7, UART8 + EXTI line[28] */ + uart3_4_5_6_7_8_interrupt_handler, /* 45 */ + cec_can_interrupt_handler, /**< 46 CEC and CAN + EXTI line[27] */ + usb_interrupt_handler, /**< 47 USB + EXTI line[18] */ + + ignore_fn /**< forces the linker to add this fn */ +}; + +extern unsigned long __data_section_table; +extern unsigned long __data_section_table_end; +extern unsigned long __bss_section_table; +extern unsigned long __bss_section_table_end; + +/** Get the system clock requency. + * @return SystemCoreClock +*/ +uint32_t HAL_RCC_GetSysClockFreq(void) +{ + return cpu_clock_hz; +} + + +void reset_handler(void) +{ + bootloader_entry(); + for ( ; /* forever */ ;) + { + /* if we ever return from main, loop forever */ + } +} + + +/** Startup the C/C++ runtime environment. + */ +void bootloader_reset_segments(void) +{ + unsigned long *section_table_addr = &__data_section_table; + + /* copy ram load sections from flash to ram */ + while (section_table_addr < &__data_section_table_end) + { + unsigned long *src = (unsigned long *)*section_table_addr++; + unsigned long *dst = (unsigned long *)*section_table_addr++; + long len = (long) *section_table_addr++; + + for ( ; len > 0; len -= 4) + { + *dst++ = *src++; + } + } + + /* zero initialize bss segment(s) */ + while (section_table_addr < &__bss_section_table_end) + { + unsigned long *zero = (unsigned long *)*section_table_addr++; + long len = (long) *section_table_addr++; + + for ( ; len > 0; len -= 4) + { + *zero++ = 0; + } + } + + /* call static constructors */ + __libc_init_array(); +} + + + +volatile uint32_t r0; +volatile uint32_t r1; +volatile uint32_t r2; +volatile uint32_t r3; +volatile uint32_t r12; +volatile uint32_t lr; /* Link register. */ +volatile uint32_t pc; /* Program counter. */ +volatile uint32_t psr;/* Program status register. */ + +/* These are volatile to try and prevent the compiler/linker optimising them + away as the variables never actually get used. */ +volatile unsigned long stacked_r0 ; +volatile unsigned long stacked_r1 ; +volatile unsigned long stacked_r2 ; +volatile unsigned long stacked_r3 ; +volatile unsigned long stacked_r12 ; +volatile unsigned long stacked_lr ; +volatile unsigned long stacked_pc ; +volatile unsigned long stacked_psr ; +volatile unsigned long _CFSR ; +volatile unsigned long _HFSR ; +volatile unsigned long _DFSR ; +volatile unsigned long _AFSR ; +volatile unsigned long _BFAR ; +volatile unsigned long _MMAR ; + +/** Decode the stack state prior to an exception occuring. This code is + * inspired by FreeRTOS. + * @param address address of the stack + */ +__attribute__((optimize("-O0"))) void hard_fault_handler_c( unsigned long *hardfault_args ) +{ + bootloader_hw_set_to_safe(); + + stacked_r0 = ((unsigned long)hardfault_args[0]) ; + stacked_r1 = ((unsigned long)hardfault_args[1]) ; + stacked_r2 = ((unsigned long)hardfault_args[2]) ; + stacked_r3 = ((unsigned long)hardfault_args[3]) ; + stacked_r12 = ((unsigned long)hardfault_args[4]) ; + stacked_lr = ((unsigned long)hardfault_args[5]) ; + stacked_pc = ((unsigned long)hardfault_args[6]) ; + stacked_psr = ((unsigned long)hardfault_args[7]) ; + + // Configurable Fault Status Register + // Consists of MMSR, BFSR and UFSR + _CFSR = (*((volatile unsigned long *)(0xE000ED28))) ; + + // Hard Fault Status Register + _HFSR = (*((volatile unsigned long *)(0xE000ED2C))) ; + + // Debug Fault Status Register + _DFSR = (*((volatile unsigned long *)(0xE000ED30))) ; + + // Auxiliary Fault Status Register + _AFSR = (*((volatile unsigned long *)(0xE000ED3C))) ; + + // Read the Fault Address Registers. These may not contain valid values. + // Check BFARVALID/MMARVALID to see if they are valid values + // MemManage Fault Address Register + _MMAR = (*((volatile unsigned long *)(0xE000ED34))) ; + // Bus Fault Address Register + _BFAR = (*((volatile unsigned long *)(0xE000ED38))) ; + + __asm("BKPT #0\n") ; // Break into the debugger + + /* When the following line is hit, the variables contain the register values. */ + if (stacked_r0 || stacked_r1 || stacked_r2 || stacked_r3 || stacked_r12 || + stacked_lr || stacked_pc || stacked_psr || _CFSR || _HFSR || _DFSR || + _AFSR || _MMAR || _BFAR) + { + //resetblink(BLINK_DIE_HARDFAULT); + for( ;; ); + } +} + +/** The fault handler implementation. This code is inspired by FreeRTOS. + */ +static void hard_fault_handler(void) +{ + __asm volatile + ( + "MOVS R0, #4 \n" + "MOV R1, LR \n" + "TST R0, R1 \n" + "BEQ _MSP \n" + "MRS R0, PSP \n" + "B hard_fault_handler_c \n" + "_MSP: \n" + "MRS R0, MSP \n" + "B hard_fault_handler_c \n" + "BX LR\n" + ); +} + +static void nmi_handler(void) +{ + for ( ; /* forever */ ; ) + { + } +} + + +volatile unsigned long _INTCTRL = 0; + +/** This is the default handler for exceptions not defined by the application. + */ +void default_interrupt_handler(void) __attribute__ ((weak)); +void default_interrupt_handler(void) +{ + _INTCTRL = NVIC_INT_CTRL_R; + while(1); + //diewith(BLINK_DIE_UNEXPIRQ); +} + + +extern void SVC_Handler(void); +extern void PendSV_Handler(void); +extern void SysTick_Handler(void); + +void SVC_Handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void PendSV_Handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void SysTick_Handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void watchdog_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void pvd_vddio2_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void rtc_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void flash_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void rcc_crs_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void external0_1_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void external2_3_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void external4_15_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void touch_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void dma_ch1_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void dma_ch2_3_dma2_ch1_2_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void dma_ch4_5_6_7_dma2_ch3_4_5_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void adc_comp_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void timer1_break_update_trigger_commutation_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void timer1_cc_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void timer2_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void timer3_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void timer6_dac_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void timer7_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void timer14_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void timer15_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void timer16_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void timer17_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void i2c1_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void i2c2_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void spi1_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void spi2_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void uart1_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void uart2_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void uart3_4_5_6_7_8_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void cec_can_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); +void usb_interrupt_handler(void) __attribute__ ((weak, alias ("default_interrupt_handler"))); diff --git a/boards/st-stm32f0x1_x2_x8-generic/startup.c b/boards/st-stm32f0x1_x2_x8-generic/startup.c index 7f5f55cdd..6a1dc518a 100644 --- a/boards/st-stm32f0x1_x2_x8-generic/startup.c +++ b/boards/st-stm32f0x1_x2_x8-generic/startup.c @@ -107,7 +107,7 @@ void (* const __interrupt_vector[])(void) = 0, /**< 10 reserved */ SVC_Handler, /**< 11 SV call */ 0, /**< 12 reserved */ - 0, /**< 13 reserved -- bootloader appentry */ + reset_handler, /**< 13 reserved -- bootloader appentry */ PendSV_Handler, /**< 14 pend SV */ SysTick_Handler, /**< 15 system tick */ watchdog_interrupt_handler, /**< 16 watchdog timer */ From 24ca60ba3cd22f3e14e4d4a3656ea169fbfbbb56 Mon Sep 17 00:00:00 2001 From: Balazs Racz Date: Sun, 19 Dec 2021 23:43:59 +0100 Subject: [PATCH 2/3] Refactor bootloader client application (#588) - The main.cxx from linux.x86 was forked into main.hxx before this PR - Removes most of the code from linux.x86/main.cxx - Removes most of the code from js.emscripten/main.cxx - Uses the shared code that is now in main.hxx This unifies the feature set that is in the linux and js.emscripten version of bootloader_client. Misc fixes - adds package.json to js version - adds support for serial port to js version - adds commands to build self-sufficient binaries for js version === * bootloader_client: Adds release script to build js binary * Rename main.cxx to main.hxx so that we can include it in real main.cxx. * Implements write_String_to_file for emscripten. * Refactors the main files for bootloader client to remove duplicate code between linux and js targets. This unifies the supported features between them. * Adds package.json configuration. * Fix whitespace. * Adds a feature to specify the second word for the cc3200 checksum algorithm. --- applications/bootloader_client/main.hxx | 127 +++---- .../targets/js.emscripten/Makefile | 17 +- .../targets/js.emscripten/main.cxx | 220 +----------- .../targets/js.emscripten/package.json | 13 + .../targets/linux.x86/main.cxx | 325 ++---------------- src/utils/FileUtils.cxx | 10 + 6 files changed, 129 insertions(+), 583 deletions(-) create mode 100644 applications/bootloader_client/targets/js.emscripten/package.json diff --git a/applications/bootloader_client/main.hxx b/applications/bootloader_client/main.hxx index d6496bbb1..e80c98fc5 100644 --- a/applications/bootloader_client/main.hxx +++ b/applications/bootloader_client/main.hxx @@ -24,7 +24,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * \file main.cxx + * \file main.hxx * * An application for updating the firmware of a remote node on the bus. * @@ -32,13 +32,6 @@ * @date 3 Aug 2013 */ -#include -#include -#include -#include - -#include - #include "os/os.h" #include "utils/constants.hxx" #include "utils/Hub.hxx" @@ -93,12 +86,13 @@ bool request_reboot_after = true; bool skip_pip = false; long long stream_timeout_nsec = 3000; uint32_t hardware_magic = 0x73a92bd1; +uint32_t hardware_magic2 = 0x5a5a55aa; void usage(const char *e) { fprintf(stderr, "Usage: %s ([-i destination_host] [-p port] | [-d device_path]) [-s " - "memory_space_id] [-c csum_algo [-m hw_magic]] [-r] [-t] [-x] " + "memory_space_id] [-c csum_algo [-m hw_magic] [-M hw_magic2]] [-r] [-t] [-x] " "[-w dg_timeout] [-W stream_timeout] [-D dump_filename] " "(-n nodeid | -a alias) -f filename\n", e); @@ -112,30 +106,38 @@ void usage(const char *e) "(also in GridConnect protocol) found at device_path. Device takes " "precedence over TCP host:port specification."); fprintf(stderr, "The default target is localhost:12021.\n"); - fprintf(stderr, "nodeid should be a 12-char hex string with 0x prefix and " - "no separators, like '-b 0x05010101141F'\n"); - fprintf(stderr, "alias should be a 3-char hex string with 0x prefix and no " - "separators, like '-a 0x3F9'\n"); - fprintf(stderr, "memory_space_id defines which memory space to write the " - "data into. Default is '-s 0xF0'.\n"); - fprintf(stderr, "csum_algo defines the checksum algorithm to use. If " - "omitted, no checksumming is done before writing the " - "data. hw_magic is an argument to the checksum.\n"); fprintf(stderr, - "-r request the target to enter bootloader mode before sending data\n"); - fprintf(stderr, "Unless -t is specified the target will be rebooted after " - "flashing complete.\n"); - fprintf(stderr, "-x skips the PIP request and uses streams.\n"); + "\n\tnodeid should be a 12-char hex string with 0x prefix and " + "no separators, like '-b 0x05010101141F'\n"); + fprintf(stderr, + "\n\talias should be a 3-char hex string with 0x prefix and no " + "separators, like '-a 0x3F9'\n"); + fprintf(stderr, + "\n\tmemory_space_id defines which memory space to write the " + "data into. Default is '-s 0xEF'.\n"); + fprintf(stderr, + "\n\tcsum_algo defines the checksum algorithm to use. If " + "omitted, no checksumming is done before writing the " + "data. hw_magic and hw_magic2 are arguments to the checksum.\n"); + fprintf(stderr, + "\n\t-r request the target to enter bootloader mode before sending " + "data\n"); + fprintf(stderr, + "\n\tUnless -t is specified the target will be rebooted after " + "flashing complete.\n"); + fprintf(stderr, "\n\t-x skips the PIP request and uses streams.\n"); + fprintf(stderr, + "\n\t-w dg_timeout sets how many seconds to wait for a datagram " + "reply.\n"); fprintf(stderr, - "-w dg_timeout sets how many seconds to wait for a datagram reply.\n"); - fprintf(stderr, "-D filename writes the checksummed payload to the given file.\n"); + "\n\t-D filename writes the checksummed payload to the given file.\n"); exit(1); } void parse_args(int argc, char *argv[]) { int opt; - while ((opt = getopt(argc, argv, "hp:i:rtd:n:a:s:f:c:m:xw:W:D:")) >= 0) + while ((opt = getopt(argc, argv, "hp:i:rtd:n:a:s:f:c:m:M:xw:W:D:")) >= 0) { switch (opt) { @@ -178,6 +180,9 @@ void parse_args(int argc, char *argv[]) case 'm': hardware_magic = strtol(optarg, nullptr, 0); break; + case 'M': + hardware_magic2 = strtol(optarg, nullptr, 0); + break; case 'r': request_reboot = true; break; @@ -263,15 +268,16 @@ void maybe_checksum(string *firmware) crc3_crc16_ibm( firmware->data(), offset, (uint16_t *)hdr.checksum_pre); hdr.checksum_pre[2] = hardware_magic; - hdr.checksum_pre[3] = 0x5a5a55aa; + hdr.checksum_pre[3] = hardware_magic2; crc3_crc16_ibm(&(*firmware)[offset + sizeof(hdr)], (firmware->size() - offset - sizeof(hdr)) & ~3, (uint16_t *)hdr.checksum_post); hdr.checksum_post[2] = hardware_magic; - hdr.checksum_post[3] = 0x5a5a55aa; + hdr.checksum_post[3] = hardware_magic2; memcpy(&(*firmware)[offset], &hdr, sizeof(hdr)); - printf("Checksummed firmware with algorithm cc3200\n"); + printf("Checksummed firmware with algorithm cc3200 (0x%08x 0x%08x)\n", + (unsigned)hardware_magic, (unsigned)hardware_magic2); } else if (algo == "esp8266") { @@ -305,42 +311,11 @@ void maybe_checksum(string *firmware) } } -/** Entry point to application. - * @param argc number of command line arguments - * @param argv array of command line arguments - * @return 0, should never return - */ -int appl_main(int argc, char *argv[]) +Buffer *fill_request() { - parse_args(argc, argv); - if (!dump_filename) - { - int conn_fd = 0; - if (device_path) - { - conn_fd = ::open(device_path, O_RDWR); - } - else - { - conn_fd = ConnectSocket(host, port); - } - HASSERT(conn_fd >= 0); - create_gc_port_for_can_hub(&can_hub0, conn_fd); - - g_if_can.add_addressed_message_support(); - // Bootstraps the alias allocation process. - g_if_can.alias_allocator()->send(g_if_can.alias_allocator()->alloc()); - - g_executor.start_thread("g_executor", 0, 1024); - usleep(400000); - } - - SyncNotifiable n; - BarrierNotifiable bn(&n); Buffer *b; mainBufferPool->alloc(&b); - b->set_done(&bn); b->data()->dst.alias = destination_alias; b->data()->dst.id = destination_nodeid; b->data()->memory_space = memory_space_id; @@ -350,26 +325,24 @@ int appl_main(int argc, char *argv[]) b->data()->request_reboot_after = request_reboot_after ? 1 : 0; b->data()->skip_pip = skip_pip ? 1 : 0; b->data()->data = read_file_to_string(filename); - - printf("Read %" PRIdPTR - " bytes from file %s. Writing to memory space 0x%02x\n", - b->data()->data.size(), filename, memory_space_id); + printf("Read %" PRIdPTR " bytes from file %s.\n", b->data()->data.size(), + filename); maybe_checksum(&b->data()->data); - if (dump_filename) - { - write_string_to_file(dump_filename, b->data()->data); - exit(0); - } + return b; +} - bootloader_client.send(b); - n.wait_for_notification(); - printf("Result: %04x %s\n", response.error_code, - response.error_details.c_str()); - if (response.error_code != 0) +bool process_dump() +{ + if (!dump_filename) { - exit(1); + return false; } - exit(0); - return 0; + string d = read_file_to_string(filename); + printf("Read %" PRIdPTR " bytes from file %s.\n", d.size(), filename); + maybe_checksum(&d); + write_string_to_file(dump_filename, d); + printf("Written data to %s.\n", dump_filename); + + return true; } diff --git a/applications/bootloader_client/targets/js.emscripten/Makefile b/applications/bootloader_client/targets/js.emscripten/Makefile index bdf68a5bb..1675ca81a 100644 --- a/applications/bootloader_client/targets/js.emscripten/Makefile +++ b/applications/bootloader_client/targets/js.emscripten/Makefile @@ -1,3 +1,18 @@ -include ../../config.mk include $(OPENMRNPATH)/etc/prog.mk -LDFLAGS += --bind +LDFLAGS += --bind -s WASM=0 + + +# How to prepare for releasing this: +# as administrator do +# npm install -g pkg +# then you can call make release +release: + pkg -C Brotli . + + +clean: clean-wasm + + +clean-wasm: + rm -f $(EXECUTABLE).{wasm,wast} diff --git a/applications/bootloader_client/targets/js.emscripten/main.cxx b/applications/bootloader_client/targets/js.emscripten/main.cxx index 67bf86272..eef8445c8 100644 --- a/applications/bootloader_client/targets/js.emscripten/main.cxx +++ b/applications/bootloader_client/targets/js.emscripten/main.cxx @@ -32,192 +32,14 @@ * @date 3 Aug 2013 */ +#include "main.hxx" + #include #include #include -#include - -#include "executor/Executor.hxx" -#include "executor/Service.hxx" -#include "executor/StateFlow.hxx" -#include "freertos/bootloader_hal.h" -#include "openlcb/AliasAllocator.hxx" -#include "openlcb/BootloaderClient.hxx" -#include "openlcb/DatagramCan.hxx" -#include "openlcb/DefaultNode.hxx" -#include "openlcb/If.hxx" -#include "openlcb/IfCan.hxx" -#include "openlcb/NodeInitializeFlow.hxx" -#include "os/os.h" -#include "utils/Crc.hxx" -#include "utils/GridConnectHub.hxx" -#include "utils/Hub.hxx" +#include "utils/JSSerialPort.hxx" #include "utils/JSTcpClient.hxx" -#include "utils/FileUtils.hxx" -#include "utils/constants.hxx" - -NO_THREAD nt; -Executor<1> g_executor(nt); -Service g_service(&g_executor); -CanHubFlow can_hub0(&g_service); - -static const openlcb::NodeID NODE_ID = 0x05010101181FULL; - -openlcb::IfCan g_if_can(&g_executor, &can_hub0, 3, 3, 2); -openlcb::InitializeFlow g_init_flow{&g_service}; -openlcb::CanDatagramService g_datagram_can(&g_if_can, 10, 2); -static openlcb::AddAliasAllocator g_alias_allocator(NODE_ID, &g_if_can); -openlcb::DefaultNode g_node(&g_if_can, NODE_ID); - -namespace openlcb -{ -Pool *const g_incoming_datagram_allocator = mainBufferPool; -} - -int port = 12021; -const char *host = "localhost"; - -const char *filename = nullptr; -uint64_t destination_nodeid = 0; -uint64_t destination_alias = 0; -int memory_space_id = openlcb::MemoryConfigDefs::SPACE_FIRMWARE; -const char *checksum_algorithm = nullptr; -bool request_reboot = false; -bool request_reboot_after = true; - -OVERRIDE_CONST(gc_generate_newlines, 1); - -void usage(const char *e) -{ - fprintf(stderr, "Usage: %s [-i destination_host] [-p port] [-s " - "memory_space_id] [-c csum_algo] [-r] [-t] (-n nodeid | -a " - "alias) -f filename\n", - e); - - fprintf(stderr, "Connects to an openlcb bus and performs the " - "bootloader protocol on openlcb node with id nodeid with " - "the contents of a given file.\n"); - fprintf(stderr, "The bus connection will be through an OpenLCB HUB on " - "destination_host:port with OpenLCB over TCP " - "(in GridConnect format) protocol."); - fprintf(stderr, "The default target is localhost:12021.\n"); - fprintf(stderr, "nodeid should be a 12-char hex string with 0x prefix and " - "no separators, like '-b 0x05010101141F'\n"); - fprintf(stderr, "alias should be a 3-char hex string with 0x prefix and no " - "separators, like '-a 0x3F9'\n"); - fprintf(stderr, "memory_space_id defines which memory space to write the " - "data into. Default is '-s 0xEF'.\n"); - fprintf(stderr, "csum_algo defines the checksum algorithm to use. If " - "omitted, no checksumming is done before writing the " - "data.\n"); - fprintf(stderr, - "-r request the target to enter bootloader mode before sending data\n"); - fprintf(stderr, "Unless -t is specified the target will be rebooted after " - "flashing complete.\n"); - exit(1); -} - -void parse_args(int argc, char *argv[]) -{ - int opt; - while ((opt = getopt(argc, argv, "hp:rtn:a:s:f:c:")) >= 0) - { - switch (opt) - { - case 'h': - usage(argv[0]); - break; - case 'p': - port = atoi(optarg); - break; - case 'i': - host = optarg; - break; - case 'f': - filename = optarg; - break; - case 'n': - destination_nodeid = strtoll(optarg, nullptr, 16); - break; - case 'a': - destination_alias = strtoul(optarg, nullptr, 16); - break; - case 's': - memory_space_id = strtol(optarg, nullptr, 16); - break; - case 'c': - checksum_algorithm = optarg; - break; - case 'r': - request_reboot = true; - break; - case 't': - request_reboot_after = false; - break; - default: - fprintf(stderr, "Unknown option %c\n", opt); - usage(argv[0]); - } - } - if (!filename || (!destination_nodeid && !destination_alias)) - { - usage(argv[0]); - } -} - -void maybe_checksum(string *firmware) -{ - if (!checksum_algorithm) - return; - string algo = checksum_algorithm; - if (algo == "tiva123") - { - struct app_header hdr; - memset(&hdr, 0, sizeof(hdr)); - // magic constant that comes from the size of the interrupt table. The - // actual target has this in memory_map.ld. - uint32_t offset = 0x270; - if (firmware->size() < offset + sizeof(hdr)) - { - fprintf(stderr, "Failed to checksum: firmware too small.\n"); - exit(1); - } - if (memcmp(&hdr, &(*firmware)[offset], sizeof(hdr))) - { - fprintf(stderr, - "Failed to checksum: location of checksum is not empty.\n"); - exit(1); - } - hdr.app_size = firmware->size(); - crc3_crc16_ibm( - &(*firmware)[8], (offset - 8) & ~3, (uint16_t *)hdr.checksum_pre); - crc3_crc16_ibm(&(*firmware)[offset + sizeof(hdr)], - (firmware->size() - offset - sizeof(hdr)) & ~3, - (uint16_t *)hdr.checksum_post); - memcpy(&(*firmware)[offset], &hdr, sizeof(hdr)); - printf("Checksummed firmware with algorithm tiva123\n"); - uint32_t reset_handler; - memcpy(&reset_handler, firmware->data() + 52, 4); - if (!reset_handler) - { - fprintf(stderr, - "Firmware does not contain any entry vector at offset 52.\n"); - exit(1); - } - } - else - { - fprintf(stderr, - "Unknown checksumming algo %s. Known algorithms are: tiva123.\n", - checksum_algorithm); - exit(1); - } -} - -openlcb::BootloaderClient bootloader_client( - &g_node, &g_datagram_can, &g_if_can); -openlcb::BootloaderResponse response; class BootloaderClientStateFlow : public StateFlowBase { @@ -231,29 +53,14 @@ class BootloaderClientStateFlow : public StateFlowBase private: Action wait_for_boot() { - return sleep_and_call(&timer_, MSEC_TO_NSEC(400), STATE(fill_request)); + return sleep_and_call(&timer_, MSEC_TO_NSEC(400), STATE(send_request)); } - Action fill_request() + Action send_request() { - Buffer *b; - mainBufferPool->alloc(&b); + Buffer *b = fill_request(); b->set_done(bn_.reset(this)); - b->data()->dst.alias = destination_alias; - b->data()->dst.id = destination_nodeid; - b->data()->memory_space = memory_space_id; - b->data()->offset = 0; - b->data()->response = &response; - b->data()->request_reboot = request_reboot ? 1 : 0; - b->data()->request_reboot_after = request_reboot_after ? 1 : 0; - b->data()->data = read_file_to_string(filename); - - printf("Read %" PRIdPTR - " bytes from file %s. Writing to memory space 0x%02x\n", - b->data()->data.size(), filename, memory_space_id); - maybe_checksum(&b->data()->data); - bootloader_client.send(b); return wait_and_call(STATE(bootload_done)); } @@ -281,9 +88,20 @@ class BootloaderClientStateFlow : public StateFlowBase int appl_main(int argc, char *argv[]) { parse_args(argc, argv); + if (process_dump()) + { + return 0; + } + std::unique_ptr dev; std::unique_ptr client; - client.reset(new JSTcpClient(&can_hub0, host, port)); - + if (device_path) + { + dev.reset(new JSSerialPort(&can_hub0, device_path)); + } + else + { + client.reset(new JSTcpClient(&can_hub0, host, port)); + } g_if_can.add_addressed_message_support(); // Bootstraps the alias allocation process. g_if_can.alias_allocator()->send(g_if_can.alias_allocator()->alloc()); diff --git a/applications/bootloader_client/targets/js.emscripten/package.json b/applications/bootloader_client/targets/js.emscripten/package.json new file mode 100644 index 000000000..e20a9b4e7 --- /dev/null +++ b/applications/bootloader_client/targets/js.emscripten/package.json @@ -0,0 +1,13 @@ +{ + "name": "openmrn-bootloader-client", + "version": "1.0.0", + "dependencies": { + "serialport": "^9.2.7" + }, + "bin": "bootloader_client.js", + "pkg": { + "assets": [ + "./node_modules/@serialport/bindings/build/Release/bindings.node" + ] + } +} diff --git a/applications/bootloader_client/targets/linux.x86/main.cxx b/applications/bootloader_client/targets/linux.x86/main.cxx index d6496bbb1..f86c5b816 100644 --- a/applications/bootloader_client/targets/linux.x86/main.cxx +++ b/applications/bootloader_client/targets/linux.x86/main.cxx @@ -39,271 +39,7 @@ #include -#include "os/os.h" -#include "utils/constants.hxx" -#include "utils/Hub.hxx" -#include "utils/GridConnectHub.hxx" -#include "utils/GcTcpHub.hxx" -#include "utils/Crc.hxx" -#include "utils/FileUtils.hxx" -#include "executor/Executor.hxx" -#include "executor/Service.hxx" - -#include "openlcb/IfCan.hxx" -#include "openlcb/DatagramCan.hxx" -#include "openlcb/BootloaderClient.hxx" -#include "openlcb/If.hxx" -#include "openlcb/AliasAllocator.hxx" -#include "openlcb/DefaultNode.hxx" -#include "openlcb/NodeInitializeFlow.hxx" -#include "utils/socket_listener.hxx" - -#include "freertos/bootloader_hal.h" - -NO_THREAD nt; -Executor<1> g_executor(nt); -Service g_service(&g_executor); -CanHubFlow can_hub0(&g_service); - -static const openlcb::NodeID NODE_ID = 0x05010101181FULL; - -openlcb::IfCan g_if_can(&g_executor, &can_hub0, 3, 3, 2); -openlcb::InitializeFlow g_init_flow{&g_service}; -openlcb::CanDatagramService g_datagram_can(&g_if_can, 10, 2); -static openlcb::AddAliasAllocator g_alias_allocator(NODE_ID, &g_if_can); -openlcb::DefaultNode g_node(&g_if_can, NODE_ID); - -namespace openlcb -{ -Pool *const g_incoming_datagram_allocator = mainBufferPool; -extern long long DATAGRAM_RESPONSE_TIMEOUT_NSEC; -} - -int port = 12021; -const char *host = "localhost"; -const char *device_path = nullptr; -const char *filename = nullptr; -const char *dump_filename = nullptr; -uint64_t destination_nodeid = 0; -uint64_t destination_alias = 0; -int memory_space_id = openlcb::MemoryConfigDefs::SPACE_FIRMWARE; -const char *checksum_algorithm = nullptr; -bool request_reboot = false; -bool request_reboot_after = true; -bool skip_pip = false; -long long stream_timeout_nsec = 3000; -uint32_t hardware_magic = 0x73a92bd1; - -void usage(const char *e) -{ - fprintf(stderr, - "Usage: %s ([-i destination_host] [-p port] | [-d device_path]) [-s " - "memory_space_id] [-c csum_algo [-m hw_magic]] [-r] [-t] [-x] " - "[-w dg_timeout] [-W stream_timeout] [-D dump_filename] " - "(-n nodeid | -a alias) -f filename\n", - e); - fprintf(stderr, "Connects to an openlcb bus and performs the " - "bootloader protocol on openlcb node with id nodeid with " - "the contents of a given file.\n"); - fprintf(stderr, - "The bus connection will be through an OpenLCB HUB on " - "destination_host:port with OpenLCB over TCP " - "(in GridConnect format) protocol, or through the CAN-USB device " - "(also in GridConnect protocol) found at device_path. Device takes " - "precedence over TCP host:port specification."); - fprintf(stderr, "The default target is localhost:12021.\n"); - fprintf(stderr, "nodeid should be a 12-char hex string with 0x prefix and " - "no separators, like '-b 0x05010101141F'\n"); - fprintf(stderr, "alias should be a 3-char hex string with 0x prefix and no " - "separators, like '-a 0x3F9'\n"); - fprintf(stderr, "memory_space_id defines which memory space to write the " - "data into. Default is '-s 0xF0'.\n"); - fprintf(stderr, "csum_algo defines the checksum algorithm to use. If " - "omitted, no checksumming is done before writing the " - "data. hw_magic is an argument to the checksum.\n"); - fprintf(stderr, - "-r request the target to enter bootloader mode before sending data\n"); - fprintf(stderr, "Unless -t is specified the target will be rebooted after " - "flashing complete.\n"); - fprintf(stderr, "-x skips the PIP request and uses streams.\n"); - fprintf(stderr, - "-w dg_timeout sets how many seconds to wait for a datagram reply.\n"); - fprintf(stderr, "-D filename writes the checksummed payload to the given file.\n"); - exit(1); -} - -void parse_args(int argc, char *argv[]) -{ - int opt; - while ((opt = getopt(argc, argv, "hp:i:rtd:n:a:s:f:c:m:xw:W:D:")) >= 0) - { - switch (opt) - { - case 'h': - usage(argv[0]); - break; - case 'p': - port = atoi(optarg); - break; - case 'i': - host = optarg; - break; - case 'd': - device_path = optarg; - break; - case 'f': - filename = optarg; - break; - case 'D': - dump_filename = optarg; - break; - case 'n': - destination_nodeid = strtoll(optarg, nullptr, 16); - break; - case 'a': - destination_alias = strtoul(optarg, nullptr, 16); - break; - case 's': - memory_space_id = strtol(optarg, nullptr, 16); - break; - case 'w': - openlcb::DATAGRAM_RESPONSE_TIMEOUT_NSEC = SEC_TO_NSEC(strtoul(optarg, nullptr, 10)); - break; - case 'W': - openlcb::g_bootloader_timeout_sec = atoi(optarg); - break; - case 'c': - checksum_algorithm = optarg; - break; - case 'm': - hardware_magic = strtol(optarg, nullptr, 0); - break; - case 'r': - request_reboot = true; - break; - case 'x': - skip_pip = true; - break; - case 't': - request_reboot_after = false; - break; - default: - fprintf(stderr, "Unknown option %c\n", opt); - usage(argv[0]); - } - } - if (!filename || (!destination_nodeid && !destination_alias && !dump_filename)) - { - usage(argv[0]); - } -} - -openlcb::BootloaderClient bootloader_client( - &g_node, &g_datagram_can, &g_if_can); -openlcb::BootloaderResponse response; - -void maybe_checksum(string *firmware) -{ - if (!checksum_algorithm) - return; - string algo = checksum_algorithm; - if (algo == "tiva123") - { - struct app_header hdr; - memset(&hdr, 0, sizeof(hdr)); - // magic constant that comes from the size of the interrupt table. The - // actual target has this in memory_map.ld. - uint32_t offset = 0x270; - if (firmware->size() < offset + sizeof(hdr)) - { - fprintf(stderr, "Failed to checksum: firmware too small.\n"); - exit(1); - } - if (memcmp(&hdr, &(*firmware)[offset], sizeof(hdr))) - { - fprintf(stderr, - "Failed to checksum: location of checksum is not empty.\n"); - exit(1); - } - hdr.app_size = firmware->size(); - crc3_crc16_ibm( - &(*firmware)[8], (offset - 8) & ~3, (uint16_t *)hdr.checksum_pre); - crc3_crc16_ibm(&(*firmware)[offset + sizeof(hdr)], - (firmware->size() - offset - sizeof(hdr)) & ~3, - (uint16_t *)hdr.checksum_post); - memcpy(&(*firmware)[offset], &hdr, sizeof(hdr)); - printf("Checksummed firmware with algorithm tiva123\n"); - uint32_t reset_handler; - memcpy(&reset_handler, firmware->data() + 52, 4); - if (!reset_handler) { - fprintf(stderr, - "Firmware does not contain any entry vector at offset 52.\n"); - exit(1); - } - } - else if (algo == "cc3200") - { - struct app_header hdr; - memset(&hdr, 0, sizeof(hdr)); - // magic constant that comes from the size of the interrupt table. The - // actual target has this in memory_map.ld. - uint32_t offset = 0x270; - if (firmware->size() < offset + sizeof(hdr)) - { - fprintf(stderr, "Failed to checksum: firmware too small.\n"); - exit(1); - } - if (memcmp(&hdr, &(*firmware)[offset], sizeof(hdr))) - { - fprintf(stderr, - "Failed to checksum: location of checksum is not empty.\n"); - exit(1); - } - hdr.app_size = firmware->size(); - crc3_crc16_ibm( - firmware->data(), offset, (uint16_t *)hdr.checksum_pre); - hdr.checksum_pre[2] = hardware_magic; - hdr.checksum_pre[3] = 0x5a5a55aa; - crc3_crc16_ibm(&(*firmware)[offset + sizeof(hdr)], - (firmware->size() - offset - sizeof(hdr)) & ~3, - (uint16_t *)hdr.checksum_post); - hdr.checksum_post[2] = hardware_magic; - hdr.checksum_post[3] = 0x5a5a55aa; - - memcpy(&(*firmware)[offset], &hdr, sizeof(hdr)); - printf("Checksummed firmware with algorithm cc3200\n"); - } - else if (algo == "esp8266") - { - struct app_header hdr; - memset(&hdr, 0, sizeof(hdr)); - string nfirmware(4096, 0); - nfirmware += *firmware; - - hdr.app_size = firmware->size() + 4096; - crc3_crc16_ibm( - nullptr, 0, (uint16_t *)hdr.checksum_pre); - hdr.checksum_pre[2] = hardware_magic; - hdr.checksum_pre[3] = 0x5a5a55aa; - - unsigned post_size = hdr.app_size - sizeof(hdr); - crc3_crc16_ibm( - &nfirmware[sizeof(hdr)], post_size, (uint16_t *)hdr.checksum_post); - hdr.checksum_post[2] = hardware_magic; - hdr.checksum_post[3] = 0x5a5a55aa; - - memcpy(&nfirmware[0], &hdr, sizeof(hdr)); - swap(*firmware, nfirmware); - printf("Checksummed firmware with algorithm esp8266 (p sz %u\n", post_size); - } - else - { - fprintf(stderr, "Unknown checksumming algo %s. Known algorithms are: " - "tiva123, cc3200, esp8266.\n", - checksum_algorithm); - exit(1); - } -} +#include "main.hxx" /** Entry point to application. * @param argc number of command line arguments @@ -313,55 +49,36 @@ void maybe_checksum(string *firmware) int appl_main(int argc, char *argv[]) { parse_args(argc, argv); - if (!dump_filename) + if (process_dump()) + { + return 0; + } + int conn_fd = 0; + if (device_path) + { + conn_fd = ::open(device_path, O_RDWR); + } + else { - int conn_fd = 0; - if (device_path) - { - conn_fd = ::open(device_path, O_RDWR); - } - else - { - conn_fd = ConnectSocket(host, port); - } - HASSERT(conn_fd >= 0); - create_gc_port_for_can_hub(&can_hub0, conn_fd); + conn_fd = ConnectSocket(host, port); + } + HASSERT(conn_fd >= 0); + create_gc_port_for_can_hub(&can_hub0, conn_fd); - g_if_can.add_addressed_message_support(); - // Bootstraps the alias allocation process. - g_if_can.alias_allocator()->send(g_if_can.alias_allocator()->alloc()); + g_if_can.add_addressed_message_support(); + // Bootstraps the alias allocation process. + g_if_can.alias_allocator()->send(g_if_can.alias_allocator()->alloc()); - g_executor.start_thread("g_executor", 0, 1024); - usleep(400000); - } + g_executor.start_thread("g_executor", 0, 1024); + usleep(400000); SyncNotifiable n; BarrierNotifiable bn(&n); - Buffer *b; - mainBufferPool->alloc(&b); + Buffer *b = fill_request(); b->set_done(&bn); - b->data()->dst.alias = destination_alias; - b->data()->dst.id = destination_nodeid; - b->data()->memory_space = memory_space_id; - b->data()->offset = 0; - b->data()->response = &response; - b->data()->request_reboot = request_reboot ? 1 : 0; - b->data()->request_reboot_after = request_reboot_after ? 1 : 0; - b->data()->skip_pip = skip_pip ? 1 : 0; - b->data()->data = read_file_to_string(filename); - - printf("Read %" PRIdPTR - " bytes from file %s. Writing to memory space 0x%02x\n", - b->data()->data.size(), filename, memory_space_id); maybe_checksum(&b->data()->data); - if (dump_filename) - { - write_string_to_file(dump_filename, b->data()->data); - exit(0); - } - bootloader_client.send(b); n.wait_for_notification(); printf("Result: %04x %s\n", response.error_code, diff --git a/src/utils/FileUtils.cxx b/src/utils/FileUtils.cxx index 979769910..3201674d0 100644 --- a/src/utils/FileUtils.cxx +++ b/src/utils/FileUtils.cxx @@ -51,6 +51,16 @@ string read_file_to_string(const string &filename) return contents; } +void write_string_to_file(const string &filename, const string &data) +{ + using emscripten::val; + EM_ASM(var fs = require('fs'); Module.fs = fs;); + val fs = val::module_property("fs"); + fs.call("writeFileSync", string(filename), + emscripten::typed_memory_view(data.size(), (uint8_t *)data.data()), + string("binary")); +} + #else #include From 9770c9bd169463e7e69b6b245c9e260b6b4608e7 Mon Sep 17 00:00:00 2001 From: Balazs Racz Date: Sun, 19 Dec 2021 23:46:31 +0100 Subject: [PATCH 3/3] Adds POM commands to the dcc debug print routine. (#589) * Adds POM commands to the dcc debug print routine. * fix mixed up printf --- src/dcc/DccDebug.cxx | 51 +++++++++++++++++++++++++++++++++++++++- src/dcc/DccDebug.cxxtest | 32 +++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/dcc/DccDebug.cxx b/src/dcc/DccDebug.cxx index e0b1ccbc0..22f09f7c3 100644 --- a/src/dcc/DccDebug.cxx +++ b/src/dcc/DccDebug.cxx @@ -229,7 +229,56 @@ string packet_to_string(const DCCPacket &pkt, bool bin_payload) else if (cmd == 0 && is_idle_packet) { } - + else if ((cmd >> 4) == 0b1110) + { + // POM command + options += " POM CV"; + unsigned kk = (cmd >> 2) & 3; + unsigned cv = (cmd & 3) << 8; + cv |= pkt.payload[ofs]; + ofs++; + options += StringPrintf("%d", cv + 1); + uint8_t d = pkt.payload[ofs++]; + + switch (kk) + { + case 0b00: + { + options += StringPrintf(" resvd %02x", d); + break; + } + case 0b01: + { + options += StringPrintf(" read/verify %d", d); + break; + } + case 0b11: + { + options += StringPrintf(" write = %d", d); + break; + } + case 0b10: + { + unsigned bit = d & 7; + unsigned value = (d >> 3) & 1; + if ((d & 0xE0) != 0xE0) + { + options += StringPrintf(" bit manipulate unknown (%02x)", d); + break; + } + if ((d & 0x10) == 0x10) + { + options += StringPrintf(" bit %d write = %d", bit, value); + } + else + { + options += StringPrintf(" bit %d verify ?= %d", bit, value); + } + break; + } + } + } + // checksum of packet if (ofs == pkt.dlc && pkt.packet_header.skip_ec == 0) { diff --git a/src/dcc/DccDebug.cxxtest b/src/dcc/DccDebug.cxxtest index a1e09ac9d..cab803ac1 100644 --- a/src/dcc/DccDebug.cxxtest +++ b/src/dcc/DccDebug.cxxtest @@ -125,6 +125,38 @@ TEST(DccDebug, F21_28) EXPECT_EQ("[dcc] Short Address 3 F[21-28]=01101001", packet_to_string(pkt)); } +TEST(DccDebug, POMWrite) +{ + Packet pkt; + pkt.add_dcc_address(DccShortAddress(3)); + pkt.add_dcc_pom_write1(13, 67); + EXPECT_EQ( + "[dcc] Short Address 3 POM CV14 write = 67", packet_to_string(pkt)); +} + +TEST(DccDebug, POMWriteHighCV) +{ + Packet pkt; + pkt.add_dcc_address(DccShortAddress(3)); + pkt.add_dcc_pom_write1(950, 255); + EXPECT_EQ( + "[dcc] Short Address 3 POM CV951 write = 255", packet_to_string(pkt)); + pkt.clear(); + pkt.add_dcc_address(DccShortAddress(3)); + pkt.add_dcc_pom_write1(1023, 0); + EXPECT_EQ( + "[dcc] Short Address 3 POM CV1024 write = 0", packet_to_string(pkt)); +} + +TEST(DccDebug, POMRead) +{ + Packet pkt; + pkt.add_dcc_address(DccShortAddress(3)); + pkt.add_dcc_pom_read1(13); + EXPECT_EQ( + "[dcc] Short Address 3 POM CV14 read/verify 0", packet_to_string(pkt)); +} + TEST(DccDebug, Accy) { Packet pkt;