/* * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #define HEADER_ADDR (0x20000400) #define DATA_ADDR (0x20000800) #define FLASH_SR_ADDR (0x40023C0C) #define STATE_WAITING (0) #define STATE_WRITE (1) #define STATE_CRC (2) // typedef to make it easy to change the program size typedef uint8_t p_size_t; typedef struct __attribute__((__packed__)) { uint32_t state; volatile p_size_t *addr; uint32_t length; } Header; static uint8_t prv_crc8(const uint8_t *data, uint32_t data_len) { uint8_t crc = 0; // nibble lookup table for (x^8 + x^5 + x^3 + x^2 + x + 1) static const uint8_t lookup_table[] = { 0, 47, 94, 113, 188, 147, 226, 205, 87, 120, 9, 38, 235, 196, 181, 154 }; for (uint32_t i = 0; i < data_len * 2; i++) { uint8_t nibble = data[i / 2]; if (i % 2 == 0) { nibble >>= 4; } uint8_t index = nibble ^ (crc >> 4); crc = lookup_table[index & 0xf] ^ ((crc << 4) & 0xf0); } return crc; } static void prv_wait_for_flash_not_busy(void) { while ((*(volatile uint32_t *)FLASH_SR_ADDR) & (1 << 16)); // BSY flag in FLASH_SR } __attribute__((__noreturn__)) void Reset_Handler(void) { // Disable all interrupts __asm__("cpsid i" : : : "memory"); volatile uint32_t *flash_sr = (volatile uint32_t *)FLASH_SR_ADDR; volatile p_size_t *data = (volatile p_size_t *)DATA_ADDR; volatile Header *header = (volatile Header *)HEADER_ADDR; header->state = STATE_WAITING; while(1) { switch (header->state) { case STATE_WRITE: prv_wait_for_flash_not_busy(); for (uint32_t i = 0; i < header->length / sizeof(p_size_t); i++) { header->addr[i] = data[i]; __asm__("isb 0xF":::"memory"); __asm__("dsb 0xF":::"memory"); /// Wait until flash isn't busy prv_wait_for_flash_not_busy(); if (*flash_sr & (0x1f << 4)) { // error raised, set bad state header->state = *flash_sr; } if (header->addr[i] != data[i]) { header->state = 0xbd; } } header->addr += header->length / sizeof(p_size_t); header->state = STATE_WAITING; break; case STATE_CRC: *data = prv_crc8((uint8_t *)header->addr, header->length); header->state = STATE_WAITING; break; default: break; } } __builtin_unreachable(); } //! These symbols are defined in the linker script for use in initializing //! the data sections. uint8_t since we do arithmetic with section lengths. //! These are arrays to avoid the need for an & when dealing with linker symbols. extern uint8_t _estack[]; __attribute__((__section__(".isr_vector"))) const void * const vector_table[] = { _estack, Reset_Handler };