pebble/python_libs/pblprog/loader/src/main.c
2025-01-27 11:38:16 -08:00

112 lines
3.2 KiB
C

/*
* 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 <stdint.h>
#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
};