ether2ser 0.1.0
Ethernet <-> synchronous V.24 bridge firmware for RP2040 + W5500
Loading...
Searching...
No Matches
persistent_config.c
Go to the documentation of this file.
1/*
2 * ether2ser - Ethernet <-> synchronous V.24 (RS-232/V.28) bridge
3 *
4 * File: src/system/persistent_config.c
5 * Purpose: Flash-backed persistent configuration and memory usage reporting.
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 *
9 * Copyright (c) 2026 Florian <f.leuze@outlook.de>
10 */
11
12// Related headers
13#include "persistent_config.h"
14
15// Standard library headers
16#include <assert.h>
17#include <inttypes.h>
18#include <malloc.h>
19#include <stdbool.h>
20#include <stddef.h>
21#include <stdint.h>
22#include <string.h>
23
24// Library Headers
25#include "boards/pico.h"
26#include "hardware/flash.h"
27#include "hardware/regs/addressmap.h"
28#include "hardware/sync.h"
29
30// Project Headers
31#include "system/common.h"
32
33// Generated headers
34
35// Reserve last 4KB sector for config
36#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE)
37
38#define CONFIG_MAGIC 0xCAFEBABE
39#define BYTES_PER_KIBIBYTE 1024U
40#define RAM_TOTAL_KIBIBYTES 264U
41#define RAM_BASE_ADDRESS 0x20000000U
42#define W55RP20_FLASH_TOTAL_BYTES (2U * BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)
43
45{
46 extern char stack_limit_sym asm("__StackLimit");
47 extern char bss_end_sym asm("__bss_end__");
48 extern char heap_start_sym asm("__heap_start");
49 extern char heap_end_sym asm("__heap_end");
50 (void)stack_limit_sym;
51 (void)bss_end_sym;
52 (void)heap_start_sym;
53 (void)heap_end_sym;
54
55 // RAM starts at RAM_BASE_ADDRESS and spans RAM_TOTAL_KIBIBYTES KiB.
56 uint32_t total_ram = RAM_TOTAL_KIBIBYTES * BYTES_PER_KIBIBYTE;
57
58 // Static data (data + bss sections)
59 uint32_t static_used = (uint32_t)&bss_end_sym - RAM_BASE_ADDRESS;
60
61 // Use mallinfo for accurate heap stats
62 struct mallinfo current_mallinfo = mallinfo();
63 uint32_t heap_used = current_mallinfo.uordblks; // Bytes allocated
64
65 LOG_PLAIN("=== Memory Usage ===\n");
66 LOG_PLAIN("Static RAM (data+bss): %" PRIu32 " bytes (%.1f KB)\n", (uint32_t)static_used,
67 (double)static_used / (double)BYTES_PER_KIBIBYTE);
68 LOG_PLAIN("Heap allocated: %" PRIu32 " bytes (%.1f KB)\n", (uint32_t)heap_used,
69 (double)heap_used / (double)BYTES_PER_KIBIBYTE);
70 LOG_PLAIN("Total RAM: %" PRIu32 " bytes (%.1f KB)\n", (uint32_t)total_ram,
71 (double)total_ram / (double)BYTES_PER_KIBIBYTE);
72
73 uint32_t free_ram = (uint32_t)(total_ram - static_used - heap_used);
74 LOG_PLAIN("Approx free: %" PRIu32 " bytes (%.1f KB)\n", free_ram,
75 (double)free_ram / (double)BYTES_PER_KIBIBYTE);
76}
77
79{
80 extern char flash_binary_start_sym asm("__flash_binary_start");
81 extern char flash_binary_end_sym asm("__flash_binary_end");
82
83 uintptr_t flash_start = (uintptr_t)&flash_binary_start_sym;
84 uintptr_t flash_end = (uintptr_t)&flash_binary_end_sym;
85
86 uint32_t flash_used = (uint32_t)(flash_end - flash_start);
87 uint32_t total_flash = W55RP20_FLASH_TOTAL_BYTES;
88 uint32_t flash_free = total_flash - flash_used;
89
90 LOG_PLAIN("=== Flash Usage ===\n");
91 LOG_PLAIN("Flash used: %" PRIu32 " bytes (%.1f KB)\n", flash_used,
92 (double)flash_used / (double)BYTES_PER_KIBIBYTE);
93 LOG_PLAIN("Total flash: %" PRIu32 " bytes\n", total_flash);
94 LOG_PLAIN("Flash free: %" PRIu32 " bytes\n", flash_free);
95}
96
97// Read: just cast the flash address
98static const config_t* nonsafe_config_read(void)
99{
100 return (const config_t*)(XIP_BASE + FLASH_TARGET_OFFSET);
101}
102
104{
105 const config_t* tmp_cnfg = nonsafe_config_read();
106 if (tmp_cnfg->magic != CONFIG_MAGIC)
107 {
108 return false;
109 }
110 memcpy(cfg, tmp_cnfg, sizeof(config_t));
111 return true;
112}
113
114void config_wipe(void)
115{
116 uint8_t buf[FLASH_PAGE_SIZE] = {0}; // 256 bytes, pad with zeros
117 uint32_t ints = save_and_disable_interrupts();
118 flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);
119 flash_range_program(FLASH_TARGET_OFFSET, buf, FLASH_PAGE_SIZE);
120 restore_interrupts(ints);
121}
122
124{
125 const config_t* cfg = nonsafe_config_read();
126 return cfg->magic == CONFIG_MAGIC;
127}
128
129void dump_config(void)
130{
131 const config_t* cfg = nonsafe_config_read();
132 if (cfg->magic != CONFIG_MAGIC)
133 {
134 LOG_INFO("Config: invalid magic 0x%08" PRIX32 "\r\n", cfg->magic);
135 return;
136 }
137
138 LOG_INFO("Config:\r\n");
139 LOG_INFO(" magic=0x%08" PRIX32 " version=%" PRIu32 "\r\n", cfg->magic, cfg->version);
140 LOG_INFO(" local=%u.%u.%u.%u:%u\r\n", cfg->local_config.ip_address[0],
143 LOG_INFO(" remote=%u.%u.%u.%u:%u\r\n", cfg->remote_config.ip_address[0],
146 LOG_INFO(" net: mac=%02X:%02X:%02X:%02X:%02X:%02X ip=%u.%u.%u.%u sn=%u.%u.%u.%u "
147 "gw=%u.%u.%u.%u dns=%u.%u.%u.%u dhcp=%u\r\n",
148 cfg->net_config.net_info.mac[0], cfg->net_config.net_info.mac[1],
149 cfg->net_config.net_info.mac[2], cfg->net_config.net_info.mac[3],
150 cfg->net_config.net_info.mac[4], cfg->net_config.net_info.mac[5],
151 cfg->net_config.net_info.ip[0], cfg->net_config.net_info.ip[1],
152 cfg->net_config.net_info.ip[2], cfg->net_config.net_info.ip[3],
153 cfg->net_config.net_info.sn[0], cfg->net_config.net_info.sn[1],
154 cfg->net_config.net_info.sn[2], cfg->net_config.net_info.sn[3],
155 cfg->net_config.net_info.gw[0], cfg->net_config.net_info.gw[1],
156 cfg->net_config.net_info.gw[2], cfg->net_config.net_info.gw[3],
157 cfg->net_config.net_info.dns[0], cfg->net_config.net_info.dns[1],
158 cfg->net_config.net_info.dns[2], cfg->net_config.net_info.dns[3],
159 cfg->net_config.net_info.dhcp);
160 LOG_INFO(" broadcast=%u.%u.%u.%u\r\n", cfg->net_config.broadcast_address[0],
163 LOG_INFO(" v24: baud=%u txd_inv=%u txc_inv=%u cts_inv=%u rts_inv=%u dtr_inv=%u rxd_inv=%u "
164 "rxc_inv=%u dcd_inv=%u\r\n",
165 (unsigned)cfg->v24_config.baudrate,
174}
175
176// Write: erase then program
177void config_write(const config_t* cfg)
178{
179 assert(FLASH_PAGE_SIZE > sizeof(config_t));
180 uint8_t buf[FLASH_PAGE_SIZE] = {0}; // 256 bytes, pad with zeros
181 config_t* cfg_mut = (config_t*)cfg;
182 cfg_mut->magic = CONFIG_MAGIC;
183 memcpy(buf, cfg_mut, sizeof(config_t));
184
185 uint32_t ints = save_and_disable_interrupts();
186 flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);
187 flash_range_program(FLASH_TARGET_OFFSET, buf, FLASH_PAGE_SIZE);
188 restore_interrupts(ints);
189}
#define PRIX32
Definition common.h:68
#define LOG_INFO(...)
Definition common.h:164
#define LOG_PLAIN(...)
Definition common.h:162
#define PRIu32
Definition common.h:64
void print_flash_usage(void)
Print flash usage statistics.
void dump_config(void)
Print current configuration to console/log output.
#define RAM_TOTAL_KIBIBYTES
#define CONFIG_MAGIC
#define FLASH_TARGET_OFFSET
#define W55RP20_FLASH_TOTAL_BYTES
bool config_read(config_t *cfg)
Read configuration from flash.
#define BYTES_PER_KIBIBYTE
void config_write(const config_t *cfg)
Write configuration to flash.
void print_memory_usage(void)
Print RAM usage statistics.
void config_wipe(void)
Erase persistent configuration sector.
bool config_is_valid(void)
Check if flash configuration magic marker is valid.
#define RAM_BASE_ADDRESS
static const config_t * nonsafe_config_read(void)
wiz_NetInfo net_info
uint8_t broadcast_address[4]
uint8_t ip_address[4]
uint16_t port
V24_POLARITIES_T polarities
Definition v24_config.h:91
V24_BAUDRATE_T baudrate
Definition v24_config.h:89
V24_RX_POLARITIES_T rx_polarities
Definition v24_config.h:80
V24_TX_POLARITIES_T tx_polarities
Definition v24_config.h:79
Persistent configuration blob stored in flash.
UDP_CONFIG_T local_config
uint32_t version
NETWORK_CONFIG_T net_config
uint32_t magic
V24_CONFIG_T v24_config
UDP_CONFIG_T remote_config