[core] Split lt_api.c into separate units

This commit is contained in:
Kuba Szczodrzyński
2023-06-22 18:30:14 +02:00
parent b38a4d5d46
commit e38e53bac0
45 changed files with 947 additions and 865 deletions

View File

@@ -25,7 +25,8 @@ jobs:
run: |
mkdir -p site/
boardgen ltci
python docs/scripts/update_docs.py
python docs/scripts/write_boards.py
python docs/scripts/write_apis.py
python docs/scripts/prepare_doxygen.py
python docs/scripts/build_json.py
cp *.json site/

2
.gitignore vendored
View File

@@ -264,3 +264,5 @@ docs/status/supported_*.md
docs/status/unsupported_boards_*.md
boards/**/*.svg
boards/**/*.md
# other generated files
docs/contrib/lt-api-functions.md

View File

@@ -48,8 +48,9 @@
* [File list](ltapi/files.md)
* 👷 Contributor's manual (WIP)
* [Porting new families](docs/contrib/porting.md)
* [📁 Project structure](docs/contrib/project-structure.md)
* [API functions guide](docs/contrib/lt-api.md)
* [C standard library](docs/contrib/stdlib.md)
* [📁 Project structure](docs/contrib/project-structure.md)
* [✈️ OTA format](docs/contrib/ota/README.md)
* [uf2ota.py tool](docs/contrib/ota/uf2ota.md)
* [uf2ota.h library](docs/contrib/ota/library.md)

View File

@@ -46,6 +46,7 @@ def env_add_core_sources(env: Environment, queue, name: str, path: str) -> bool:
base_dir=path,
srcs=[
"+<*.c*>",
"+<api/*.c>",
"+<common/*.c*>",
"+<compat/*.c*>",
"+<port/*.c*>",

View File

@@ -0,0 +1,13 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
lt_cpu_model_t lt_cpu_get_model() {
uint8_t chipId = *(uint8_t *)(SCTRL_CHIP_ID);
return CPU_MODEL_ENUM(FAMILY, chipId);
}
const char *lt_cpu_get_core_type() {
return "ARM968E-S (ARMv5TE)";
}

View File

@@ -0,0 +1,41 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
void lt_get_device_mac(uint8_t *mac) {
cfg_load_mac(mac);
}
void lt_reboot() {
bk_reboot();
}
bool lt_reboot_download_mode() {
bk_reboot();
return true;
}
lt_reboot_reason_t lt_get_reboot_reason() {
switch (bk_misc_get_start_type()) {
case RESET_SOURCE_POWERON:
return REBOOT_REASON_POWER;
case RESET_SOURCE_REBOOT:
return REBOOT_REASON_SOFTWARE;
case RESET_SOURCE_WATCHDOG:
return REBOOT_REASON_WATCHDOG;
case RESET_SOURCE_CRASH_XAT0:
case RESET_SOURCE_CRASH_UNDEFINED:
case RESET_SOURCE_CRASH_PREFETCH_ABORT:
case RESET_SOURCE_CRASH_DATA_ABORT:
case RESET_SOURCE_CRASH_UNUSED:
case RESET_SOURCE_CRASH_PER_XAT0:
return REBOOT_REASON_CRASH;
case RESET_SOURCE_DEEPPS_GPIO:
case RESET_SOURCE_DEEPPS_RTC:
case RESET_SOURCE_DEEPPS_USB:
return REBOOT_REASON_SLEEP;
default:
return REBOOT_REASON_UNKNOWN;
}
}

View File

@@ -0,0 +1,26 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
// can't include <flash.h> as it collides with <Flash.h> on Windows -_-
#define REG_FLASH_BASE 0x00803000
#define REG_FLASH_OPERATE_SW (REG_FLASH_BASE + 0 * 4)
#define REG_FLASH_RDID (REG_FLASH_BASE + 4 * 4)
#define FLASH_BUSY_SW (0x01UL << 31)
#define FLASH_WP_VALUE (0x01UL << 30)
#define FLASH_OP_SW (0x01UL << 29)
#define FLASH_OP_TYPE_POS 24
#define FLASH_OP_RDID 20
lt_flash_id_t lt_flash_get_id() {
uint32_t data = (FLASH_OP_RDID << FLASH_OP_TYPE_POS) | FLASH_OP_SW | FLASH_WP_VALUE;
REG_WRITE(REG_FLASH_OPERATE_SW, data);
while (REG_READ(REG_FLASH_OPERATE_SW) & FLASH_BUSY_SW) {}
lt_flash_id_t id = {
.manufacturer_id = REG_RD8(REG_FLASH_RDID, 2),
.chip_id = REG_RD8(REG_FLASH_RDID, 1),
.chip_size_id = REG_RD8(REG_FLASH_RDID, 0),
};
return id;
}

View File

@@ -0,0 +1,9 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
void lt_init_family() {
// set default UART output port
uart_print_port = LT_UART_DEFAULT_PORT - 1;
}

View File

@@ -0,0 +1,21 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
uint32_t lt_ram_get_size() {
return 256 * 1024;
}
uint32_t lt_heap_get_size() {
#if configDYNAMIC_HEAP_SIZE
extern unsigned char _empty_ram;
#if CFG_SOC_NAME == SOC_BK7231N
return (0x00400000 + 192 * 1024) - (uint32_t)(&_empty_ram);
#else
return (0x00400000 + 256 * 1024) - (uint32_t)(&_empty_ram);
#endif
#else
return configTOTAL_HEAP_SIZE;
#endif
}

View File

@@ -0,0 +1,39 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
lt_ota_type_t lt_ota_get_type() {
return OTA_TYPE_SINGLE;
}
bool lt_ota_is_valid(uint8_t index) {
if (index != 0)
return false;
// check download RBL
// TODO: maybe check header CRC or even binary hashes
uint32_t magic;
lt_flash_read(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4);
return magic == 0x004C4252; // "RBL\0", little-endian
}
uint8_t lt_ota_dual_get_current() {
return 0;
}
uint8_t lt_ota_dual_get_stored() {
return 0;
}
bool lt_ota_switch(bool revert) {
if (!lt_ota_is_valid(0))
// no valid "download" image
// - return false when trying to activate
// - return true when trying to revert
return revert;
if (revert) {
// there's a valid "download" image, which has to be removed
return lt_flash_erase_block(FLASH_DOWNLOAD_OFFSET);
}
return true;
}

View File

@@ -0,0 +1,18 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
bool lt_wdt_enable(uint32_t timeout) {
wdt_ctrl(WCMD_SET_PERIOD, &timeout);
wdt_ctrl(WCMD_POWER_UP, NULL);
return true;
}
void lt_wdt_disable() {
wdt_ctrl(WCMD_POWER_DOWN, NULL);
}
void lt_wdt_feed() {
wdt_ctrl(WCMD_RELOAD_PERIOD, NULL);
}

View File

@@ -1,184 +0,0 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
// can't include <flash.h> as it collides with <Flash.h> on Windows -_-
#define REG_FLASH_BASE 0x00803000
#define REG_FLASH_OPERATE_SW (REG_FLASH_BASE + 0 * 4)
#define REG_FLASH_RDID (REG_FLASH_BASE + 4 * 4)
#define FLASH_BUSY_SW (0x01UL << 31)
#define FLASH_WP_VALUE (0x01UL << 30)
#define FLASH_OP_SW (0x01UL << 29)
#define FLASH_OP_TYPE_POS 24
#define FLASH_OP_RDID 20
void lt_init_family() {
// set default UART output port
uart_print_port = LT_UART_DEFAULT_PORT - 1;
}
/* _____ _____ _ _
/ ____| __ \| | | |
| | | |__) | | | |
| | | ___/| | | |
| |____| | | |__| |
\_____|_| \____*/
lt_cpu_model_t lt_cpu_get_model() {
uint8_t chipId = *(uint8_t *)(SCTRL_CHIP_ID);
return CPU_MODEL_ENUM(FAMILY, chipId);
}
const char *lt_cpu_get_core_type() {
return "ARM968E-S (ARMv5TE)";
}
/*_____ _
| __ \ (_)
| | | | _____ ___ ___ ___
| | | |/ _ \ \ / / |/ __/ _ \
| |__| | __/\ V /| | (_| __/
|_____/ \___| \_/ |_|\___\__*/
void lt_get_device_mac(uint8_t *mac) {
cfg_load_mac(mac);
}
void lt_reboot() {
bk_reboot();
}
bool lt_reboot_download_mode() {
bk_reboot();
return true;
}
lt_reboot_reason_t lt_get_reboot_reason() {
switch (bk_misc_get_start_type()) {
case RESET_SOURCE_POWERON:
return REBOOT_REASON_POWER;
case RESET_SOURCE_REBOOT:
return REBOOT_REASON_SOFTWARE;
case RESET_SOURCE_WATCHDOG:
return REBOOT_REASON_WATCHDOG;
case RESET_SOURCE_CRASH_XAT0:
case RESET_SOURCE_CRASH_UNDEFINED:
case RESET_SOURCE_CRASH_PREFETCH_ABORT:
case RESET_SOURCE_CRASH_DATA_ABORT:
case RESET_SOURCE_CRASH_UNUSED:
case RESET_SOURCE_CRASH_PER_XAT0:
return REBOOT_REASON_CRASH;
case RESET_SOURCE_DEEPPS_GPIO:
case RESET_SOURCE_DEEPPS_RTC:
case RESET_SOURCE_DEEPPS_USB:
return REBOOT_REASON_SLEEP;
default:
return REBOOT_REASON_UNKNOWN;
}
}
/*______ _ _
| ____| | | |
| |__ | | __ _ ___| |__
| __| | |/ _` / __| '_ \
| | | | (_| \__ \ | | |
|_| |_|\__,_|___/_| |*/
lt_flash_id_t lt_flash_get_id() {
uint32_t data = (FLASH_OP_RDID << FLASH_OP_TYPE_POS) | FLASH_OP_SW | FLASH_WP_VALUE;
REG_WRITE(REG_FLASH_OPERATE_SW, data);
while (REG_READ(REG_FLASH_OPERATE_SW) & FLASH_BUSY_SW) {}
lt_flash_id_t id = {
.manufacturer_id = REG_RD8(REG_FLASH_RDID, 2),
.chip_id = REG_RD8(REG_FLASH_RDID, 1),
.chip_size_id = REG_RD8(REG_FLASH_RDID, 0),
};
return id;
}
/*__ __
| \/ |
| \ / | ___ _ __ ___ ___ _ __ _ _
| |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | |
| | | | __/ | | | | | (_) | | | |_| |
|_| |_|\___|_| |_| |_|\___/|_| \__, |
__/ |
|__*/
uint32_t lt_ram_get_size() {
return 256 * 1024;
}
uint32_t lt_heap_get_size() {
#if configDYNAMIC_HEAP_SIZE
extern unsigned char _empty_ram;
#if CFG_SOC_NAME == SOC_BK7231N
return (0x00400000 + 192 * 1024) - (uint32_t)(&_empty_ram);
#else
return (0x00400000 + 256 * 1024) - (uint32_t)(&_empty_ram);
#endif
#else
return configTOTAL_HEAP_SIZE;
#endif
}
/* ____ _______
/ __ \__ __|/\
| | | | | | / \
| | | | | | / /\ \
| |__| | | |/ ____ \
\____/ |_/_/ \*/
lt_ota_type_t lt_ota_get_type() {
return OTA_TYPE_SINGLE;
}
bool lt_ota_is_valid(uint8_t index) {
if (index != 0)
return false;
// check download RBL
// TODO: maybe check header CRC or even binary hashes
uint32_t magic;
lt_flash_read(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4);
return magic == 0x004C4252; // "RBL\0", little-endian
}
uint8_t lt_ota_dual_get_current() {
return 0;
}
uint8_t lt_ota_dual_get_stored() {
return 0;
}
bool lt_ota_switch(bool revert) {
if (!lt_ota_is_valid(0))
// no valid "download" image
// - return false when trying to activate
// - return true when trying to revert
return revert;
if (revert) {
// there's a valid "download" image, which has to be removed
return lt_flash_erase_block(FLASH_DOWNLOAD_OFFSET);
}
return true;
}
/*_ __ _ _ _
\ \ / / | | | | | |
\ \ /\ / /_ _| |_ ___| |__ __| | ___ __ _
\ \/ \/ / _` | __/ __| '_ \ / _` |/ _ \ / _` |
\ /\ / (_| | || (__| | | | (_| | (_) | (_| |
\/ \/ \__,_|\__\___|_| |_|\__,_|\___/ \__, |
__/ |
|___*/
bool lt_wdt_enable(uint32_t timeout) {
wdt_ctrl(WCMD_SET_PERIOD, &timeout);
wdt_ctrl(WCMD_POWER_UP, NULL);
return true;
}
void lt_wdt_disable() {
wdt_ctrl(WCMD_POWER_DOWN, NULL);
}
void lt_wdt_feed() {
wdt_ctrl(WCMD_RELOAD_PERIOD, NULL);
}

View File

@@ -0,0 +1,58 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "lt_cpu.h"
#if LT_HAS_FREERTOS
#include <FreeRTOS.h>
#include <task.h>
#endif
lt_cpu_family_t lt_cpu_get_family() {
return FAMILY;
}
const char *lt_cpu_get_family_name() {
return STRINGIFY_MACRO(FAMILY) + 2;
}
__attribute__((weak)) lt_cpu_model_t lt_cpu_get_model() {
return MCU;
}
const char *lt_cpu_get_model_name() {
return STRINGIFY_MACRO(MCU);
}
const char *lt_cpu_get_model_code() {
return STRINGIFY_MACRO(MCULC);
}
__attribute__((weak)) uint32_t lt_cpu_get_unique_id() {
return lt_cpu_get_mac_id();
}
__attribute__((weak)) uint32_t lt_cpu_get_mac_id() {
uint8_t mac[6];
lt_get_device_mac(mac);
return (mac[3] << 0) | (mac[4] << 8) | (mac[5] << 16);
}
__attribute__((weak)) uint8_t lt_cpu_get_core_count() {
return 1;
}
#if LT_HAS_FREERTOS
__attribute__((weak)) uint32_t lt_cpu_get_freq() {
return configCPU_CLOCK_HZ;
}
#endif
uint32_t lt_cpu_get_freq_mhz() {
return lt_cpu_get_freq() / 1000000;
}
#if LT_HAS_FREERTOS
__attribute__((weak)) uint32_t lt_cpu_get_cycle_count() {
return xTaskGetTickCount() * (configCPU_CLOCK_HZ / configTICK_RATE_HZ);
}
#endif

View File

@@ -0,0 +1,80 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "lt_device.h"
static char *device_name = NULL;
const char *lt_get_version() {
return LT_VERSION_STR;
}
const char *lt_get_board_code() {
return LT_BOARD_STR;
}
const char *lt_get_device_name() {
if (device_name)
return device_name;
uint32_t chip_id = lt_cpu_get_mac_id();
uint8_t *id = (uint8_t *)&chip_id;
const char *model = lt_cpu_get_model_code();
uint8_t model_len = strlen(model);
device_name = (char *)malloc(3 + model_len + 1 + 6 + 1);
sprintf(device_name, "LT-%s-%02x%02x%02x", model, id[0], id[1], id[2]);
return device_name;
}
__attribute__((weak)) void lt_reboot() {
// The Watchdog Way
lt_wdt_enable(1L);
while (1) {}
}
__attribute__((weak)) bool lt_reboot_wdt() {
if (!lt_wdt_enable(1L))
return false;
while (1) {}
}
__attribute__((weak)) bool lt_reboot_download_mode() {
return false;
}
__attribute__((weak)) lt_reboot_reason_t lt_get_reboot_reason() {
return REBOOT_REASON_UNKNOWN;
}
const char *lt_get_reboot_reason_name(lt_reboot_reason_t reason) {
if (!reason)
reason = lt_get_reboot_reason();
switch (reason) {
case REBOOT_REASON_POWER:
return "Power-On";
case REBOOT_REASON_BROWNOUT:
return "Brownout";
case REBOOT_REASON_HARDWARE:
return "HW Reboot";
case REBOOT_REASON_SOFTWARE:
return "SW Reboot";
case REBOOT_REASON_WATCHDOG:
return "WDT Reset";
case REBOOT_REASON_CRASH:
return "Crash";
case REBOOT_REASON_SLEEP:
return "Sleep Wakeup";
case REBOOT_REASON_DEBUGGER:
return "Debugger";
default:
return "Unknown";
}
}
__attribute__((weak)) bool lt_set_debug_mode(lt_debug_mode_t mode) {
return false;
}
__attribute__((weak)) void lt_gpio_recover() {
lt_set_debug_mode(DEBUG_MODE_OFF);
}

View File

@@ -0,0 +1,39 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "lt_flash.h"
#include <fal.h>
__attribute__((weak)) uint32_t lt_flash_get_size() {
lt_flash_id_t id = lt_flash_get_id();
if (id.chip_size_id >= 0x14 && id.chip_size_id <= 0x19) {
return (1 << id.chip_size_id);
}
#ifdef FLASH_LENGTH
return FLASH_LENGTH;
#else
return 0;
#endif
}
bool lt_flash_erase(uint32_t offset, size_t length) {
return fal_partition_erase(fal_root_part, offset, length) >= 0;
}
bool lt_flash_erase_block(uint32_t offset) {
return fal_partition_erase(fal_root_part, offset, 1) >= 0;
}
uint32_t lt_flash_read(uint32_t offset, uint8_t *data, size_t length) {
int ret = fal_partition_read(fal_root_part, offset, data, length);
if (ret == -1)
return 0;
return ret;
}
uint32_t lt_flash_write(uint32_t offset, const uint8_t *data, size_t length) {
int ret = fal_partition_write(fal_root_part, offset, data, length);
if (ret == -1)
return 0;
return ret;
}

View File

@@ -0,0 +1,26 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "lt_mem.h"
#if LT_HAS_FREERTOS
#include <FreeRTOS.h>
#include <task.h>
#endif
#if LT_HAS_FREERTOS
__attribute__((weak)) uint32_t lt_heap_get_size() {
return configTOTAL_HEAP_SIZE;
}
__attribute__((weak)) uint32_t lt_heap_get_free() {
return xPortGetFreeHeapSize();
}
__attribute__((weak)) uint32_t lt_heap_get_min_free() {
return xPortGetMinimumEverFreeHeapSize();
}
#endif
__attribute__((weak)) uint32_t lt_heap_get_max_alloc() {
return 0;
}

View File

@@ -0,0 +1,18 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "lt_ota.h"
bool lt_ota_can_rollback() {
if (lt_ota_get_type() != OTA_TYPE_DUAL)
return false;
uint8_t current = lt_ota_dual_get_current();
return lt_ota_is_valid(current ^ 0b11);
}
uf2_ota_scheme_t lt_ota_get_uf2_scheme() {
if (lt_ota_get_type() == OTA_TYPE_SINGLE)
return UF2_SCHEME_DEVICE_SINGLE;
uint8_t current = lt_ota_dual_get_current();
// UF2_SCHEME_DEVICE_DUAL_1 or UF2_SCHEME_DEVICE_DUAL_2
return (uf2_ota_scheme_t)(current ^ 0b11);
}

View File

@@ -0,0 +1,41 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "lt_utils.h"
void lt_rand_bytes(uint8_t *buf, size_t len) {
int *data = (int *)buf;
size_t i;
for (i = 0; len >= sizeof(int); len -= sizeof(int)) {
data[i++] = rand();
}
if (len) {
int rem = rand();
unsigned char *pRem = (unsigned char *)&rem;
memcpy(buf + i * sizeof(int), pRem, len);
}
}
void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width) {
uint16_t pos = 0;
while (pos < len) {
// print hex offset
printf("%06lx ", offset + pos);
// calculate current line width
uint8_t lineWidth = MIN(width, len - pos);
// print hexadecimal representation
for (uint8_t i = 0; i < lineWidth; i++) {
if (i % 8 == 0) {
printf(" ");
}
printf("%02x ", buf[pos + i]);
}
// print ascii representation
printf(" |");
for (uint8_t i = 0; i < lineWidth; i++) {
char c = buf[pos + i];
putchar((c >= 0x20 && c <= 0x7f) ? c : '.');
}
puts("|\r");
pos += lineWidth;
}
}

View File

@@ -0,0 +1,11 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "lt_wdt.h"
__attribute__((weak)) bool lt_wdt_enable(uint32_t timeout) {
return false;
}
__attribute__((weak)) void lt_wdt_disable() {}
__attribute__((weak)) void lt_wdt_feed() {}

View File

@@ -1,296 +0,0 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "lt_api.h"
#include <fal.h>
#if LT_HAS_FREERTOS
#include <FreeRTOS.h>
#include <task.h>
#endif
/* _____ _____ _ _
/ ____| __ \| | | |
| | | |__) | | | |
| | | ___/| | | |
| |____| | | |__| |
\_____|_| \____*/
lt_cpu_family_t lt_cpu_get_family() {
return FAMILY;
}
const char *lt_cpu_get_family_name() {
return STRINGIFY_MACRO(FAMILY) + 2;
}
__attribute__((weak)) lt_cpu_model_t lt_cpu_get_model() {
return MCU;
}
const char *lt_cpu_get_model_name() {
return STRINGIFY_MACRO(MCU);
}
const char *lt_cpu_get_model_code() {
return STRINGIFY_MACRO(MCULC);
}
__attribute__((weak)) uint32_t lt_cpu_get_unique_id() {
return lt_cpu_get_mac_id();
}
__attribute__((weak)) uint32_t lt_cpu_get_mac_id() {
uint8_t mac[6];
lt_get_device_mac(mac);
return (mac[3] << 0) | (mac[4] << 8) | (mac[5] << 16);
}
__attribute__((weak)) uint8_t lt_cpu_get_core_count() {
return 1;
}
#if LT_HAS_FREERTOS
__attribute__((weak)) uint32_t lt_cpu_get_freq() {
return configCPU_CLOCK_HZ;
}
#endif
uint32_t lt_cpu_get_freq_mhz() {
return lt_cpu_get_freq() / 1000000;
}
#if LT_HAS_FREERTOS
__attribute__((weak)) uint32_t lt_cpu_get_cycle_count() {
return xTaskGetTickCount() * (configCPU_CLOCK_HZ / configTICK_RATE_HZ);
}
#endif
/*_____ _
| __ \ (_)
| | | | _____ ___ ___ ___
| | | |/ _ \ \ / / |/ __/ _ \
| |__| | __/\ V /| | (_| __/
|_____/ \___| \_/ |_|\___\__*/
static char *device_name = NULL;
const char *lt_get_version() {
return LT_VERSION_STR;
}
const char *lt_get_board_code() {
return LT_BOARD_STR;
}
const char *lt_get_device_name() {
if (device_name)
return device_name;
uint32_t chip_id = lt_cpu_get_mac_id();
uint8_t *id = (uint8_t *)&chip_id;
const char *model = lt_cpu_get_model_code();
uint8_t model_len = strlen(model);
device_name = (char *)malloc(3 + model_len + 1 + 6 + 1);
sprintf(device_name, "LT-%s-%02x%02x%02x", model, id[0], id[1], id[2]);
return device_name;
}
__attribute__((weak)) void lt_reboot() {
// The Watchdog Way
lt_wdt_enable(1L);
while (1) {}
}
__attribute__((weak)) bool lt_reboot_wdt() {
if (!lt_wdt_enable(1L))
return false;
while (1) {}
}
__attribute__((weak)) bool lt_reboot_download_mode() {
return false;
}
__attribute__((weak)) lt_reboot_reason_t lt_get_reboot_reason() {
return REBOOT_REASON_UNKNOWN;
}
const char *lt_get_reboot_reason_name(lt_reboot_reason_t reason) {
if (!reason)
reason = lt_get_reboot_reason();
switch (reason) {
case REBOOT_REASON_POWER:
return "Power-On";
case REBOOT_REASON_BROWNOUT:
return "Brownout";
case REBOOT_REASON_HARDWARE:
return "HW Reboot";
case REBOOT_REASON_SOFTWARE:
return "SW Reboot";
case REBOOT_REASON_WATCHDOG:
return "WDT Reset";
case REBOOT_REASON_CRASH:
return "Crash";
case REBOOT_REASON_SLEEP:
return "Sleep Wakeup";
case REBOOT_REASON_DEBUGGER:
return "Debugger";
default:
return "Unknown";
}
}
__attribute__((weak)) bool lt_set_debug_mode(lt_debug_mode_t mode) {
return false;
}
__attribute__((weak)) void lt_gpio_recover() {
lt_set_debug_mode(DEBUG_MODE_OFF);
}
/*______ _ _
| ____| | | |
| |__ | | __ _ ___| |__
| __| | |/ _` / __| '_ \
| | | | (_| \__ \ | | |
|_| |_|\__,_|___/_| |*/
__attribute__((weak)) uint32_t lt_flash_get_size() {
lt_flash_id_t id = lt_flash_get_id();
if (id.chip_size_id >= 0x14 && id.chip_size_id <= 0x19) {
return (1 << id.chip_size_id);
}
#ifdef FLASH_LENGTH
return FLASH_LENGTH;
#else
return 0;
#endif
}
bool lt_flash_erase(uint32_t offset, size_t length) {
return fal_partition_erase(fal_root_part, offset, length) >= 0;
}
bool lt_flash_erase_block(uint32_t offset) {
return fal_partition_erase(fal_root_part, offset, 1) >= 0;
}
uint32_t lt_flash_read(uint32_t offset, uint8_t *data, size_t length) {
int ret = fal_partition_read(fal_root_part, offset, data, length);
if (ret == -1)
return 0;
return ret;
}
uint32_t lt_flash_write(uint32_t offset, const uint8_t *data, size_t length) {
int ret = fal_partition_write(fal_root_part, offset, data, length);
if (ret == -1)
return 0;
return ret;
}
/*__ __
| \/ |
| \ / | ___ _ __ ___ ___ _ __ _ _
| |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | |
| | | | __/ | | | | | (_) | | | |_| |
|_| |_|\___|_| |_| |_|\___/|_| \__, |
__/ |
|__*/
#if LT_HAS_FREERTOS
__attribute__((weak)) uint32_t lt_heap_get_size() {
return configTOTAL_HEAP_SIZE;
}
__attribute__((weak)) uint32_t lt_heap_get_free() {
return xPortGetFreeHeapSize();
}
__attribute__((weak)) uint32_t lt_heap_get_min_free() {
return xPortGetMinimumEverFreeHeapSize();
}
#endif
__attribute__((weak)) uint32_t lt_heap_get_max_alloc() {
return 0;
}
/* ____ _______
/ __ \__ __|/\
| | | | | | / \
| | | | | | / /\ \
| |__| | | |/ ____ \
\____/ |_/_/ \*/
bool lt_ota_can_rollback() {
if (lt_ota_get_type() != OTA_TYPE_DUAL)
return false;
uint8_t current = lt_ota_dual_get_current();
return lt_ota_is_valid(current ^ 0b11);
}
uf2_ota_scheme_t lt_ota_get_uf2_scheme() {
if (lt_ota_get_type() == OTA_TYPE_SINGLE)
return UF2_SCHEME_DEVICE_SINGLE;
uint8_t current = lt_ota_dual_get_current();
// UF2_SCHEME_DEVICE_DUAL_1 or UF2_SCHEME_DEVICE_DUAL_2
return (uf2_ota_scheme_t)(current ^ 0b11);
}
/*_ _ _ _ _
| | | | | (_) |
| | | | |_ _| |___
| | | | __| | / __|
| |__| | |_| | \__ \
\____/ \__|_|_|__*/
void lt_rand_bytes(uint8_t *buf, size_t len) {
int *data = (int *)buf;
size_t i;
for (i = 0; len >= sizeof(int); len -= sizeof(int)) {
data[i++] = rand();
}
if (len) {
int rem = rand();
unsigned char *pRem = (unsigned char *)&rem;
memcpy(buf + i * sizeof(int), pRem, len);
}
}
void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width) {
uint16_t pos = 0;
while (pos < len) {
// print hex offset
printf("%06lx ", offset + pos);
// calculate current line width
uint8_t lineWidth = MIN(width, len - pos);
// print hexadecimal representation
for (uint8_t i = 0; i < lineWidth; i++) {
if (i % 8 == 0) {
printf(" ");
}
printf("%02x ", buf[pos + i]);
}
// print ascii representation
printf(" |");
for (uint8_t i = 0; i < lineWidth; i++) {
char c = buf[pos + i];
putchar((c >= 0x20 && c <= 0x7f) ? c : '.');
}
puts("|\r");
pos += lineWidth;
}
}
/*_ __ _ _ _
\ \ / / | | | | | |
\ \ /\ / /_ _| |_ ___| |__ __| | ___ __ _
\ \/ \/ / _` | __/ __| '_ \ / _` |/ _ \ / _` |
\ /\ / (_| | || (__| | | | (_| | (_) | (_| |
\/ \/ \__,_|\__\___|_| |_|\__,_|\___/ \__, |
__/ |
|___*/
__attribute__((weak)) bool lt_wdt_enable(uint32_t timeout) {
return false;
}
__attribute__((weak)) void lt_wdt_disable() {}
__attribute__((weak)) void lt_wdt_feed() {}

View File

@@ -3,7 +3,7 @@
#pragma once
// This file collects all LibreTiny C API includes.
// The functions are implemented in lt_api.c, which is located
// The functions are implemented in api/*.c units, which are located
// in the common core, and in the family cores.
#ifdef __cplusplus

View File

@@ -0,0 +1,14 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-23. */
#include <libretiny.h>
#include <sdk_private.h>
lt_flash_id_t lt_flash_get_id() {
lt_flash_id_t id;
uint8_t idBytes[3];
flash_read_id(&lt_flash_obj, idBytes, 3);
id.manufacturer_id = idBytes[0];
id.chip_id = idBytes[1];
id.chip_size_id = idBytes[2];
return id;
}

View File

@@ -0,0 +1,18 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-23. */
#include <libretiny.h>
#include <sdk_private.h>
bool lt_wdt_enable(uint32_t timeout) {
watchdog_init(timeout);
watchdog_start();
return true;
}
void lt_wdt_disable() {
watchdog_stop();
}
void lt_wdt_feed() {
watchdog_refresh();
}

View File

@@ -1,42 +0,0 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-23. */
#include <libretiny.h>
#include <sdk_private.h>
/*______ _ _
| ____| | | |
| |__ | | __ _ ___| |__
| __| | |/ _` / __| '_ \
| | | | (_| \__ \ | | |
|_| |_|\__,_|___/_| |*/
lt_flash_id_t lt_flash_get_id() {
lt_flash_id_t id;
uint8_t idBytes[3];
flash_read_id(&lt_flash_obj, idBytes, 3);
id.manufacturer_id = idBytes[0];
id.chip_id = idBytes[1];
id.chip_size_id = idBytes[2];
return id;
}
/*_ __ _ _ _
\ \ / / | | | | | |
\ \ /\ / /_ _| |_ ___| |__ __| | ___ __ _
\ \/ \/ / _` | __/ __| '_ \ / _` |/ _ \ / _` |
\ /\ / (_| | || (__| | | | (_| | (_) | (_| |
\/ \/ \__,_|\__\___|_| |_|\__,_|\___/ \__, |
__/ |
|___*/
bool lt_wdt_enable(uint32_t timeout) {
watchdog_init(timeout);
watchdog_start();
return true;
}
void lt_wdt_disable() {
watchdog_stop();
}
void lt_wdt_feed() {
watchdog_refresh();
}

View File

@@ -0,0 +1,34 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
lt_cpu_model_t lt_cpu_get_model() {
uint8_t chipId;
EFUSE_OneByteReadROM(9902, 0xF8, &chipId, L25EOUTVOLTAGE);
return CPU_MODEL_ENUM(FAMILY, chipId);
}
uint32_t lt_cpu_get_mac_id() {
uint32_t chipId = 0;
uint8_t *id = (uint8_t *)&chipId;
// 9902 was extracted from ROM disassembly, probably not needed
/* EFUSE_OneByteReadROM(9902, 0x3B, id + 0, L25EOUTVOLTAGE);
EFUSE_OneByteReadROM(9902, 0x3C, id + 1, L25EOUTVOLTAGE);
EFUSE_OneByteReadROM(9902, 0x3D, id + 2, L25EOUTVOLTAGE); */
// new method, based on EFUSE logical map
uint8_t *efuse = (uint8_t *)malloc(512);
// TODO do what EFUSE_LogicalMapRead() does, and read only the used data
EFUSE_LogicalMap_Read(efuse);
memcpy(id, efuse + 0x11A + 3, 3);
free(efuse);
return chipId;
}
const char *lt_cpu_get_core_type() {
return "ARM Cortex-M4F (ARMv7E-M)";
}
uint32_t lt_cpu_get_freq() {
return CPU_ClkGet(false);
}

View File

@@ -0,0 +1,39 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
void lt_get_device_mac(uint8_t *mac) {
uint8_t *efuse = (uint8_t *)malloc(512);
EFUSE_LogicalMap_Read(efuse);
memcpy(mac, efuse + 0x11A, 6);
free(efuse);
}
bool lt_reboot_download_mode() {
// mww 0x40000138 0x8
HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_NORESET_FF, 0x08);
// reboot it the ugly way
sys_reset();
while (1) {}
return true;
}
bool lt_set_debug_mode(lt_debug_mode_t mode) {
uint32_t *swd;
switch (mode) {
case DEBUG_MODE_OFF:
sys_jtag_off();
Pinmux_Config(PA_14, PINMUX_FUNCTION_GPIO);
Pinmux_Config(PA_15, PINMUX_FUNCTION_GPIO);
return true;
case DEBUG_MODE_SWD:
Pinmux_Config(PA_14, PINMUX_FUNCTION_SWD);
Pinmux_Config(PA_15, PINMUX_FUNCTION_SWD);
uint32_t *swd = (uint32_t *)0x400000A4;
*swd |= 0x1000;
return true;
default:
return false;
}
}

View File

@@ -0,0 +1,14 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
lt_flash_id_t lt_flash_get_id() {
lt_flash_id_t id;
uint8_t idBytes[3];
flash_read_id(NULL, idBytes, 3);
id.manufacturer_id = idBytes[0];
id.chip_id = idBytes[1];
id.chip_size_id = idBytes[2];
return id;
}

View File

@@ -0,0 +1,16 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
extern uint32_t GlobalDebugEnable;
extern uint16_t GlobalDebugLevel;
extern uint8_t GlobalPrivateLog;
extern uint8_t lt_uart_port;
void lt_init_family() {
// make the SDK less verbose by default
GlobalDebugEnable = 0;
GlobalPrivateLog = 0;
lt_uart_port = LT_UART_DEFAULT_PORT;
}

View File

@@ -0,0 +1,8 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
uint32_t lt_ram_get_size() {
return 256 * 1024;
}

View File

@@ -0,0 +1,78 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
lt_ota_type_t lt_ota_get_type() {
return OTA_TYPE_DUAL;
}
bool lt_ota_is_valid(uint8_t index) {
uint32_t offset;
switch (index) {
case 1:
offset = FLASH_OTA1_OFFSET;
break;
case 2:
offset = FLASH_OTA2_OFFSET;
break;
default:
return false;
}
uint8_t *address = (uint8_t *)(SPI_FLASH_BASE + offset);
return memcmp(address, "81958711", 8) == 0;
}
uint8_t lt_ota_dual_get_current() {
// RTL8710B is XIP, so check the code offset in flash
uint32_t addr = (uint32_t)lt_log;
uint32_t offs = addr - SPI_FLASH_BASE;
return offs > FLASH_OTA2_OFFSET ? 2 : 1;
}
uint8_t lt_ota_dual_get_stored() {
uint32_t *ota_address = (uint32_t *)0x8009000;
if (*ota_address == 0xFFFFFFFF)
return 1;
uint32_t ota_counter = *((uint32_t *)0x8009004);
// even count of zero-bits means OTA1, odd count means OTA2
// this allows to switch OTA images by simply clearing next bits,
// without needing to erase the flash
uint8_t count = 0;
for (uint8_t i = 0; i < 32; i++) {
if ((ota_counter & (1 << i)) == 0)
count++;
}
return 1 + (count % 2);
}
bool lt_ota_switch(bool revert) {
uint8_t current = lt_ota_dual_get_current();
uint8_t stored = lt_ota_dual_get_stored();
if ((current == stored) == revert)
return true;
if (!lt_ota_is_valid(stored ^ 0b11))
return false;
// - read current OTA switch value from 0x9004
// - reset OTA switch to 0xFFFFFFFE if it's 0x0
// - else check first non-zero bit of OTA switch
// - write OTA switch with first non-zero bit cleared
uint32_t value = HAL_READ32(SPI_FLASH_BASE, FLASH_SYSTEM_OFFSET + 4);
if (value == 0) {
uint8_t *system = (uint8_t *)malloc(64);
lt_flash_read(FLASH_SYSTEM_OFFSET, system, 64);
// reset OTA switch
((uint32_t *)system)[1] = -2;
lt_flash_erase_block(FLASH_SYSTEM_OFFSET);
return lt_flash_write(FLASH_SYSTEM_OFFSET, system, 64);
}
// clear first non-zero bit
value <<= 1;
// write OTA switch to flash
flash_write_word(NULL, FLASH_SYSTEM_OFFSET + 4, value);
return true;
}

View File

@@ -1,201 +0,0 @@
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
#include <libretiny.h>
#include <sdk_private.h>
extern uint32_t GlobalDebugEnable;
extern uint16_t GlobalDebugLevel;
extern uint8_t GlobalPrivateLog;
extern uint8_t lt_uart_port;
void lt_init_family() {
// make the SDK less verbose by default
GlobalDebugEnable = 0;
GlobalPrivateLog = 0;
lt_uart_port = LT_UART_DEFAULT_PORT;
}
/* _____ _____ _ _
/ ____| __ \| | | |
| | | |__) | | | |
| | | ___/| | | |
| |____| | | |__| |
\_____|_| \____*/
lt_cpu_model_t lt_cpu_get_model() {
uint8_t chipId;
EFUSE_OneByteReadROM(9902, 0xF8, &chipId, L25EOUTVOLTAGE);
return CPU_MODEL_ENUM(FAMILY, chipId);
}
uint32_t lt_cpu_get_mac_id() {
uint32_t chipId = 0;
uint8_t *id = (uint8_t *)&chipId;
// 9902 was extracted from ROM disassembly, probably not needed
/* EFUSE_OneByteReadROM(9902, 0x3B, id + 0, L25EOUTVOLTAGE);
EFUSE_OneByteReadROM(9902, 0x3C, id + 1, L25EOUTVOLTAGE);
EFUSE_OneByteReadROM(9902, 0x3D, id + 2, L25EOUTVOLTAGE); */
// new method, based on EFUSE logical map
uint8_t *efuse = (uint8_t *)malloc(512);
// TODO do what EFUSE_LogicalMapRead() does, and read only the used data
EFUSE_LogicalMap_Read(efuse);
memcpy(id, efuse + 0x11A + 3, 3);
free(efuse);
return chipId;
}
const char *lt_cpu_get_core_type() {
return "ARM Cortex-M4F (ARMv7E-M)";
}
uint32_t lt_cpu_get_freq() {
return CPU_ClkGet(false);
}
/*_____ _
| __ \ (_)
| | | | _____ ___ ___ ___
| | | |/ _ \ \ / / |/ __/ _ \
| |__| | __/\ V /| | (_| __/
|_____/ \___| \_/ |_|\___\__*/
void lt_get_device_mac(uint8_t *mac) {
uint8_t *efuse = (uint8_t *)malloc(512);
EFUSE_LogicalMap_Read(efuse);
memcpy(mac, efuse + 0x11A, 6);
free(efuse);
}
bool lt_reboot_download_mode() {
// mww 0x40000138 0x8
HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_NORESET_FF, 0x08);
// reboot it the ugly way
sys_reset();
while (1) {}
return true;
}
bool lt_set_debug_mode(lt_debug_mode_t mode) {
uint32_t *swd;
switch (mode) {
case DEBUG_MODE_OFF:
sys_jtag_off();
Pinmux_Config(PA_14, PINMUX_FUNCTION_GPIO);
Pinmux_Config(PA_15, PINMUX_FUNCTION_GPIO);
return true;
case DEBUG_MODE_SWD:
Pinmux_Config(PA_14, PINMUX_FUNCTION_SWD);
Pinmux_Config(PA_15, PINMUX_FUNCTION_SWD);
uint32_t *swd = (uint32_t *)0x400000A4;
*swd |= 0x1000;
return true;
default:
return false;
}
}
/*______ _ _
| ____| | | |
| |__ | | __ _ ___| |__
| __| | |/ _` / __| '_ \
| | | | (_| \__ \ | | |
|_| |_|\__,_|___/_| |*/
lt_flash_id_t lt_flash_get_id() {
lt_flash_id_t id;
uint8_t idBytes[3];
flash_read_id(NULL, idBytes, 3);
id.manufacturer_id = idBytes[0];
id.chip_id = idBytes[1];
id.chip_size_id = idBytes[2];
return id;
}
/*__ __
| \/ |
| \ / | ___ _ __ ___ ___ _ __ _ _
| |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | |
| | | | __/ | | | | | (_) | | | |_| |
|_| |_|\___|_| |_| |_|\___/|_| \__, |
__/ |
|__*/
uint32_t lt_ram_get_size() {
return 256 * 1024;
}
/* ____ _______
/ __ \__ __|/\
| | | | | | / \
| | | | | | / /\ \
| |__| | | |/ ____ \
\____/ |_/_/ \*/
lt_ota_type_t lt_ota_get_type() {
return OTA_TYPE_DUAL;
}
bool lt_ota_is_valid(uint8_t index) {
uint32_t offset;
switch (index) {
case 1:
offset = FLASH_OTA1_OFFSET;
break;
case 2:
offset = FLASH_OTA2_OFFSET;
break;
default:
return false;
}
uint8_t *address = (uint8_t *)(SPI_FLASH_BASE + offset);
return memcmp(address, "81958711", 8) == 0;
}
uint8_t lt_ota_dual_get_current() {
// RTL8710B is XIP, so check the code offset in flash
uint32_t addr = (uint32_t)lt_log;
uint32_t offs = addr - SPI_FLASH_BASE;
return offs > FLASH_OTA2_OFFSET ? 2 : 1;
}
uint8_t lt_ota_dual_get_stored() {
uint32_t *ota_address = (uint32_t *)0x8009000;
if (*ota_address == 0xFFFFFFFF)
return 1;
uint32_t ota_counter = *((uint32_t *)0x8009004);
// even count of zero-bits means OTA1, odd count means OTA2
// this allows to switch OTA images by simply clearing next bits,
// without needing to erase the flash
uint8_t count = 0;
for (uint8_t i = 0; i < 32; i++) {
if ((ota_counter & (1 << i)) == 0)
count++;
}
return 1 + (count % 2);
}
bool lt_ota_switch(bool revert) {
uint8_t current = lt_ota_dual_get_current();
uint8_t stored = lt_ota_dual_get_stored();
if ((current == stored) == revert)
return true;
if (!lt_ota_is_valid(stored ^ 0b11))
return false;
// - read current OTA switch value from 0x9004
// - reset OTA switch to 0xFFFFFFFE if it's 0x0
// - else check first non-zero bit of OTA switch
// - write OTA switch with first non-zero bit cleared
uint32_t value = HAL_READ32(SPI_FLASH_BASE, FLASH_SYSTEM_OFFSET + 4);
if (value == 0) {
uint8_t *system = (uint8_t *)malloc(64);
lt_flash_read(FLASH_SYSTEM_OFFSET, system, 64);
// reset OTA switch
((uint32_t *)system)[1] = -2;
lt_flash_erase_block(FLASH_SYSTEM_OFFSET);
return lt_flash_write(FLASH_SYSTEM_OFFSET, system, 64);
}
// clear first non-zero bit
value <<= 1;
// write OTA switch to flash
flash_write_word(NULL, FLASH_SYSTEM_OFFSET + 4, value);
return true;
}

View File

@@ -0,0 +1,21 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-22. */
#include <libretiny.h>
#include <sdk_private.h>
lt_cpu_model_t lt_cpu_get_model() {
uint32_t *addr = (uint32_t *)0x40000038;
uint8_t flash_mode = (addr[0] >> 5) & 0b11;
uint32_t chip_id = 0;
hal_get_chip_id(&chip_id);
chip_id <<= 2;
return CPU_MODEL_ENUM(FAMILY, (chip_id & 0xFF) | flash_mode);
}
const char *lt_cpu_get_core_type() {
return "ARM Cortex-M33 (ARMv8-M)";
}
uint32_t lt_cpu_get_freq() {
return hal_syson_query_sys_clk();
}

View File

@@ -0,0 +1,61 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-22. */
#include <libretiny.h>
#include <sdk_private.h>
void lt_get_device_mac(uint8_t *mac) {
efuse_logical_read(0x11A, 6, mac);
}
void lt_reboot() {
sys_cpu_reset();
while (1) {}
}
bool lt_reboot_download_mode() {
sys_uart_download_mode();
while (1) {}
return true;
}
lt_reboot_reason_t lt_get_reboot_reason() {
hal_reset_reason_t reason = -1;
rtl8710c_reset_reason_get(&reason);
switch (reason) {
case HAL_RESET_REASON_POWER_ON:
return REBOOT_REASON_POWER;
case HAL_RESET_REASON_SOFTWARE:
return REBOOT_REASON_SOFTWARE;
case HAL_RESET_REASON_WATCHDOG:
return REBOOT_REASON_WATCHDOG;
case HAL_RESET_REASON_JTAG:
return REBOOT_REASON_DEBUGGER;
default:
return REBOOT_REASON_UNKNOWN;
}
}
bool lt_set_debug_mode(lt_debug_mode_t mode) {
switch (mode) {
case DEBUG_MODE_OFF:
if (hal_misc_jtag_pin_ctrl(0) != HAL_OK)
return false;
if (hal_misc_swd_pin_ctrl(0) != HAL_OK)
return false;
return true;
case DEBUG_MODE_JTAG:
if (hal_misc_swd_pin_ctrl(0) != HAL_OK)
return false;
if (hal_misc_jtag_pin_ctrl(1) != HAL_OK)
return false;
return true;
case DEBUG_MODE_SWD:
if (hal_misc_jtag_pin_ctrl(0) != HAL_OK)
return false;
if (hal_misc_swd_pin_ctrl(1) != HAL_OK)
return false;
return true;
default:
return false;
}
}

View File

@@ -0,0 +1,14 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-22. */
#include <libretiny.h>
#include <sdk_private.h>
extern uint8_t lt_uart_port;
void lt_init_family() {
// make the SDK less verbose by default
ConfigDebugErr = _DBG_MISC_ | _DBG_FAULT_ | _DBG_BOOT_;
ConfigDebugWarn = 0;
ConfigDebugInfo = 0;
lt_uart_port = LT_UART_DEFAULT_PORT;
}

View File

@@ -0,0 +1,8 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-22. */
#include <libretiny.h>
#include <sdk_private.h>
uint32_t lt_ram_get_size() {
return 256 * 1024;
}

View File

@@ -0,0 +1,24 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-22. */
#include <libretiny.h>
#include <sdk_private.h>
lt_ota_type_t lt_ota_get_type() {
return OTA_TYPE_DUAL;
}
bool lt_ota_is_valid(uint8_t index) {
return false;
}
uint8_t lt_ota_dual_get_current() {
return 0;
}
uint8_t lt_ota_dual_get_stored() {
return 0;
}
bool lt_ota_switch(bool revert) {
return false;
}

View File

@@ -1,138 +0,0 @@
/* Copyright (c) Kuba Szczodrzyński 2023-05-22. */
#include <libretiny.h>
#include <sdk_private.h>
extern uint8_t lt_uart_port;
void lt_init_family() {
// make the SDK less verbose by default
ConfigDebugErr = _DBG_MISC_ | _DBG_FAULT_ | _DBG_BOOT_;
ConfigDebugWarn = 0;
ConfigDebugInfo = 0;
lt_uart_port = LT_UART_DEFAULT_PORT;
}
/* _____ _____ _ _
/ ____| __ \| | | |
| | | |__) | | | |
| | | ___/| | | |
| |____| | | |__| |
\_____|_| \____*/
lt_cpu_model_t lt_cpu_get_model() {
uint32_t *addr = (uint32_t *)0x40000038;
uint8_t flash_mode = (addr[0] >> 5) & 0b11;
uint32_t chip_id = 0;
hal_get_chip_id(&chip_id);
chip_id <<= 2;
return CPU_MODEL_ENUM(FAMILY, (chip_id & 0xFF) | flash_mode);
}
const char *lt_cpu_get_core_type() {
return "ARM Cortex-M33 (ARMv8-M)";
}
uint32_t lt_cpu_get_freq() {
return hal_syson_query_sys_clk();
}
/*_____ _
| __ \ (_)
| | | | _____ ___ ___ ___
| | | |/ _ \ \ / / |/ __/ _ \
| |__| | __/\ V /| | (_| __/
|_____/ \___| \_/ |_|\___\__*/
void lt_get_device_mac(uint8_t *mac) {
efuse_logical_read(0x11A, 6, mac);
}
void lt_reboot() {
sys_cpu_reset();
while (1) {}
}
bool lt_reboot_download_mode() {
sys_uart_download_mode();
while (1) {}
return true;
}
lt_reboot_reason_t lt_get_reboot_reason() {
hal_reset_reason_t reason = -1;
rtl8710c_reset_reason_get(&reason);
switch (reason) {
case HAL_RESET_REASON_POWER_ON:
return REBOOT_REASON_POWER;
case HAL_RESET_REASON_SOFTWARE:
return REBOOT_REASON_SOFTWARE;
case HAL_RESET_REASON_WATCHDOG:
return REBOOT_REASON_WATCHDOG;
case HAL_RESET_REASON_JTAG:
return REBOOT_REASON_DEBUGGER;
default:
return REBOOT_REASON_UNKNOWN;
}
}
bool lt_set_debug_mode(lt_debug_mode_t mode) {
switch (mode) {
case DEBUG_MODE_OFF:
if (hal_misc_jtag_pin_ctrl(0) != HAL_OK)
return false;
if (hal_misc_swd_pin_ctrl(0) != HAL_OK)
return false;
return true;
case DEBUG_MODE_JTAG:
if (hal_misc_swd_pin_ctrl(0) != HAL_OK)
return false;
if (hal_misc_jtag_pin_ctrl(1) != HAL_OK)
return false;
return true;
case DEBUG_MODE_SWD:
if (hal_misc_jtag_pin_ctrl(0) != HAL_OK)
return false;
if (hal_misc_swd_pin_ctrl(1) != HAL_OK)
return false;
return true;
default:
return false;
}
}
/*__ __
| \/ |
| \ / | ___ _ __ ___ ___ _ __ _ _
| |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | |
| | | | __/ | | | | | (_) | | | |_| |
|_| |_|\___|_| |_| |_|\___/|_| \__, |
__/ |
|__*/
uint32_t lt_ram_get_size() {
return 256 * 1024;
}
/* ____ _______
/ __ \__ __|/\
| | | | | | / \
| | | | | | / /\ \
| |__| | | |/ ____ \
\____/ |_/_/ \*/
lt_ota_type_t lt_ota_get_type() {
return OTA_TYPE_DUAL;
}
bool lt_ota_is_valid(uint8_t index) {
return false;
}
uint8_t lt_ota_dual_get_current() {
return 0;
}
uint8_t lt_ota_dual_get_stored() {
return 0;
}
bool lt_ota_switch(bool revert) {
return false;
}

13
docs/contrib/lt-api.md Normal file
View File

@@ -0,0 +1,13 @@
# API functions guide
The [LibreTiny C API](../dev/lt-api.md) functions are split between three types: common, weak and family.
- Common functions are implemented in the base, common core and are the same between all families.
- Weak functions are provided in the common core, but can (and sometimes should) be overridden by family cores. They sometimes provide usable default implementations (which *can* be overriden to provide e.g. a better way to do something), otherwise they're empty (e.g. if a family doesn't support such a feature).
- Family functions are not provided in the common core and have to be implemented in the family core.
A quick outline of all available functions and their types:
{%
include-markdown "lt-api-functions.md"
%}

View File

@@ -31,6 +31,7 @@ Here's what has to be done to make that work:
5. Add base core code.
- `lt_defs.h`, `lt_family.h` and `lt_api.c` files need to be created, and initialized with (even empty) functions and definitions.
- The list of family functions can be found [here](lt-api.md).
- Make the SDK call `lt_main()` as the entrypoint. If needed, use fixups.
6. Write a binary manipulation tool.

6
docs/script.js Normal file
View File

@@ -0,0 +1,6 @@
document$.subscribe(function () {
var tables = document.querySelectorAll("article table:not([class])")
tables.forEach(function (table) {
new Tablesort(table)
})
})

View File

@@ -1,7 +1,7 @@
import json
from ltchiptool import Board
from update_docs import board_obj_sort
from write_boards import board_obj_sort
boards = map(Board, Board.get_list())
boards = list(sorted(boards, key=board_obj_sort))

125
docs/scripts/write_apis.py Normal file
View File

@@ -0,0 +1,125 @@
# Copyright (c) Kuba Szczodrzyński 2023-06-22.
import re
from glob import glob
from os.path import dirname, join
import colorama
from colorama import Fore, Style
from markdown import Markdown
if __name__ == "__main__":
colorama.init()
api_path = join(dirname(__file__), "..", "..", "cores/common/base/api/lt_*.*")
out_path = join(dirname(__file__), "..", "contrib")
declaration = ""
implementation = ""
for file in glob(api_path):
with open(file, "r") as f:
data = f.read()
if file.endswith(".h"):
declaration += data
elif file.endswith(".c"):
implementation += data
block_comment_regex = r"\/\*[\d\D]+?\*\/"
line_comment_regex = r"\/\/.+?$"
macro_regex = r"#(?:[^\n\\]|\\\n)+$"
line_regex = r"\n+"
declaration = re.sub(block_comment_regex, "", declaration)
declaration = re.sub(line_comment_regex, "", declaration, flags=re.MULTILINE)
declaration = re.sub(macro_regex, "", declaration, flags=re.MULTILINE)
declaration = re.sub(line_regex, "\n", declaration)
implementation = re.sub(block_comment_regex, "", implementation)
implementation = re.sub(line_comment_regex, "", implementation, flags=re.MULTILINE)
implementation = re.sub(macro_regex, "", implementation, flags=re.MULTILINE)
implementation = re.sub(line_regex, "\n", implementation)
declaration_regex = r"^([\d\w\s]+ \*?)([\S]+?)\(.*?\);$"
implementation_regex = r"^(__attribute__\(\(weak\)\) )?([\w\d* ]+?)([\w\d]+)\(.+?{"
function_types = {}
decl_functions = set()
impl_functions = set()
weak_functions = set()
for match in re.finditer(
pattern=declaration_regex,
string=declaration,
flags=re.DOTALL | re.MULTILINE,
):
function_type = match[1].strip()
function_name = match[2].strip()
if function_types.get(function_name, function_type) != function_type:
print(
Fore.YELLOW
+ "WARNING: Wrong return type: "
+ f"'{function_types[function_name]} {function_name}'"
+ f"vs '{function_type} {function_name}'"
+ Style.RESET_ALL
)
function_types[function_name] = function_type
decl_functions.add(function_name)
for match in re.finditer(
pattern=implementation_regex,
string=implementation,
flags=re.DOTALL | re.MULTILINE,
):
is_weak = match[1]
function_type = match[2].strip()
function_name = match[3].strip()
function_types[function_name] = function_type
if function_types.get(function_name, function_type) != function_type:
print(
Fore.YELLOW
+ "WARNING: Wrong return type: "
+ f"'{function_types[function_name]} {function_name}'"
+ f"vs '{function_type} {function_name}'"
+ Style.RESET_ALL
)
if is_weak:
weak_functions.add(function_name)
else:
impl_functions.add(function_name)
common_functions = impl_functions.union(weak_functions)
family_functions = decl_functions - common_functions
undecl_functions = common_functions - decl_functions
if undecl_functions:
print(Fore.RED + "ERROR: Undeclared functions: " + ", ".join(undecl_functions))
exit(1)
md = Markdown(out_path, "lt-api-functions")
header = [
"Type",
"Function",
"Common",
"Weak",
"Family",
]
rows = []
for function in (
sorted(family_functions) + sorted(weak_functions) + sorted(impl_functions)
):
rows.append(
[
f"`{function_types[function]}`",
f"{function}()",
"✔️" if function in impl_functions else "",
"✔️" if function in weak_functions else "",
"✔️" if function not in common_functions else "",
]
)
md.add_table(header, *rows)
md.write()

View File

@@ -39,6 +39,10 @@ plugins:
extra_css:
- docs/style.css
extra_javascript:
- https://unpkg.com/tablesort@5.3.0/dist/tablesort.min.js
- docs/script.js
markdown_extensions:
- md_in_html
- admonition