Merge pull request #2508 from cesanta/v307

Use V307 flash
This commit is contained in:
Sergey Lyubka 2023-12-01 00:01:20 +00:00 committed by GitHub
commit 920875ecc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1518 additions and 1064 deletions

View File

@ -52,4 +52,4 @@ endif
# Cleanup. Delete built program and all build artifacts
clean:
$(DELETE) $(PROG) $(PACK) *.o *.obj *.exe *.dSYM mbedtls
$(DELETE) $(PROG) $(PACK) *.o *.obj *.exe *.bin *.dSYM mbedtls

View File

@ -4,12 +4,34 @@
#include "mongoose.h"
#include "net.h"
#define CONFIG_FILE "settings.bin"
static int s_sig_num;
static void signal_handler(int sig_num) {
signal(sig_num, signal_handler);
s_sig_num = sig_num;
}
bool web_load_settings(void *buf, size_t len) {
bool ok = false;
size_t size = 0;
void *data = mg_file_read(&mg_fs_posix, CONFIG_FILE, &size);
if (data == NULL) {
MG_ERROR(("Error reading %s", CONFIG_FILE));
} else if (size != len) {
MG_ERROR(("%s size != %lu", CONFIG_FILE, len));
} else {
memcpy(buf, data, len);
}
free(data);
return ok;
}
bool web_save_settings(void *buf, size_t len) {
MG_INFO(("Saving to %s", CONFIG_FILE));
return mg_file_write(&mg_fs_posix, CONFIG_FILE, buf, len);
}
int main(void) {
struct mg_mgr mgr;

View File

@ -4,12 +4,13 @@
#include "net.h"
// Device settings
struct settings {
struct device_settings {
uint32_t magic;
int log_level;
bool mqtt_enabled;
char *mqtt_server_url;
char *mqtt_topic_tx;
char *mqtt_topic_rx;
char mqtt_server_url[64];
char mqtt_topic_tx[16];
char mqtt_topic_rx[16];
};
struct conndata {
@ -17,13 +18,21 @@ struct conndata {
unsigned long id; // Connection ID waiting for the Modbus response
};
static struct settings s_settings = {3, false, NULL, NULL, NULL};
static struct device_settings s_settings;
static const char *s_json_header =
"Content-Type: application/json\r\n"
"Cache-Control: no-cache\r\n";
static uint64_t s_boot_timestamp = 0; // Updated by SNTP
static void set_default_settings(struct device_settings *s) {
s->magic = SETTINGS_MAGIC;
s->log_level = MG_LL_DEBUG;
mg_snprintf(s->mqtt_server_url, sizeof(s->mqtt_server_url), "%s",
"mqtt://broker.hivemq.com:1883");
mg_snprintf(s->mqtt_topic_tx, sizeof(s->mqtt_topic_tx), "%s", "modbus1/tx");
mg_snprintf(s->mqtt_topic_rx, sizeof(s->mqtt_topic_rx), "%s", "modbus1/rx");
}
// This is for newlib and TLS (mbedTLS)
uint64_t mg_now(void) {
return mg_millis() + s_boot_timestamp;
@ -50,25 +59,28 @@ static void timer_sntp_fn(void *param) {
mg_sntp_connect(param, "udp://time.google.com:123", sfn, NULL);
}
static void setfromjson(struct mg_str json, const char *jsonpath, char **dst) {
static void setfromjson(struct mg_str json, const char *jsonpath, char *buf,
size_t len) {
char *val = mg_json_get_str(json, jsonpath);
if (val != NULL) {
free(*dst);
*dst = val;
}
if (val != NULL) mg_snprintf(buf, len, "%s", val);
free(val);
}
static void handle_settings_set(struct mg_connection *c, struct mg_str body) {
struct settings settings;
struct device_settings settings;
memset(&settings, 0, sizeof(settings));
set_default_settings(&settings);
mg_json_get_bool(body, "$.mqtt_enabled", &settings.mqtt_enabled);
settings.log_level = mg_json_get_long(body, "$.log_level", MG_LL_INFO);
setfromjson(body, "$.mqtt_server_url", &settings.mqtt_server_url);
setfromjson(body, "$.mqtt_topic_rx", &settings.mqtt_topic_rx);
setfromjson(body, "$.mqtt_topic_tx", &settings.mqtt_topic_tx);
setfromjson(body, "$.mqtt_server_url", settings.mqtt_server_url,
sizeof(settings.mqtt_server_url));
setfromjson(body, "$.mqtt_topic_rx", settings.mqtt_topic_rx,
sizeof(settings.mqtt_topic_rx));
setfromjson(body, "$.mqtt_topic_tx", settings.mqtt_topic_tx,
sizeof(settings.mqtt_topic_tx));
s_settings = settings; // TODO: save to the device flash
bool ok = true;
s_settings = settings;
bool ok = web_save_settings(&s_settings, sizeof(s_settings));
mg_http_reply(c, 200, s_json_header,
"{%m:%s,%m:%m}", //
MG_ESC("status"), ok ? "true" : "false", //
@ -146,7 +158,7 @@ static struct mg_connection *start_modbus_request(struct mg_mgr *mgr,
send16(c, reg); // Start register
send16(c, nregs); // Number of registers
if (func == 16) { // Fill in register values to write
if (func == 16) { // Fill in register values to write
send8(c, (uint8_t) (nregs * 2)); // Send number of bytes
for (uint16_t i = 0; i < nregs; i++) {
char path[20];
@ -283,10 +295,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
}
void web_init(struct mg_mgr *mgr) {
// Init default settings
s_settings.mqtt_server_url = strdup("mqtt://broker.hivemq.com:1883");
s_settings.mqtt_topic_tx = strdup("modbus1/tx");
s_settings.mqtt_topic_rx = strdup("modbus1/rx");
set_default_settings(&s_settings);
web_load_settings(&s_settings, sizeof(s_settings));
mg_http_listen(mgr, HTTP_URL, fn, NULL);
mg_http_listen(mgr, HTTPS_URL, fn, (void *) 1);

View File

@ -16,7 +16,11 @@ extern "C" {
#define HTTPS_URL "https://0.0.0.0:8443"
#endif
void web_init(struct mg_mgr *mgr);
void web_init(struct mg_mgr *mgr); // Initialise Web UI
bool web_load_settings(void *buf, size_t len); // Load from disk/flash
bool web_save_settings(void *buf, size_t len); // Save to disk/flash
#define SETTINGS_MAGIC 0xaabbccdd
#ifdef __cplusplus
}

File diff suppressed because it is too large Load Diff

View File

@ -298,13 +298,14 @@ function Settings({}) {
return html`
<div class="m-4 grid grid-cols-1 gap-4 md:grid-cols-2">
${saveResult && html`<${Notification} ok=${saveResult.status}
text=${saveResult.message} close=${() => setSaveResult(null)} />`}
<div class="py-1 divide-y border rounded bg-white flex flex-col">
<div class="font-light uppercase flex items-center text-gray-600 px-4 py-2">
MQTT Forwarding
<//>
<div class="py-2 px-5 flex-1 flex flex-col relative">
${saveResult && html`<${Notification} ok=${saveResult.status}
text=${saveResult.message} close=${() => setSaveResult(null)} />`}
<${Setting} title="Enable MQTT forwarding" value=${settings.mqtt_enabled} setfn=${mksetfn('mqtt_enabled')} type="switch" />
<${Setting} title="MQTT Server URL" value=${settings.mqtt_server_url} setfn=${mksetfn('mqtt_server_url')} type="" disabled=${!settings.mqtt_enabled} />
<${Setting} title="MQTT Topic RX" value=${settings.mqtt_topic_rx} setfn=${mksetfn('mqtt_topic_rx')} type="" disabled=${!settings.mqtt_enabled} />
@ -335,8 +336,6 @@ function Settings({}) {
Console Log
<//>
<div class="py-2 px-5 flex-1 flex flex-col relative">
${saveResult && html`<${Notification} ok=${saveResult.status}
text=${saveResult.message} close=${() => setSaveResult(null)} />`}
<${Setting} title="Log Level" value=${settings.log_level} setfn=${mksetfn('log_level')} type="select" addonLeft="0-3" options=${logOptions} />
<div class="mb-1 mt-3 flex place-content-end"><${Button} icon=${Icons.save} onclick=${onsave} title="Save Settings" /><//>
<//>

View File

@ -77,7 +77,7 @@ int main(void) {
}
MG_INFO(("Initialising application..."));
web_init(&mgr);
//web_init(&mgr);
MG_INFO(("Starting event loop"));
for (;;) {

View File

@ -1,13 +1,18 @@
ROOT ?= $(realpath $(PWD)/../../..)
PREFIX ?= riscv-none-elf
#PREFIX ?= docker run --platform linux/amd64 --rm -it -v$(ROOT):$(ROOT) -w $(PWD) mdashnet/riscv riscv64-unknown-elf
CFLAGS = -W -Wall -Wextra -Wno-error -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
CFLAGS += -ffunction-sections -fdata-sections
CFLAGS += -march=rv32imafc -mabi=ilp32f
CFLAGS += -DSYSCLK_FREQ_144MHz_HSE -I. -Ivendor -g3 -Os $(CFLAGS_EXTRA)
LDFLAGS = -T vendor/link.ld -nostartfiles --specs=nano.specs
LDFLAGS += -lc -lgcc -Wl,--gc-sections
LDFLAGS = -T link.ld -nostartfiles --specs=nano.specs
LDFLAGS += -lc -lgcc -Wl,--gc-sections #-Wl,-Map=$@.map
SOURCES = main.c mongoose.c net.c packed_fs.c vendor/system_ch32v30x.c vendor/startup_ch32v30x_D8C.S
SOURCES = main.c mongoose.c net.c packed_fs.c
SOURCES += vendor/system_ch32v30x.c vendor/startup_ch32v30x_D8C.S
CFLAGS += -DHTTP_URL=\"http://0.0.0.0/\" -DHTTPS_URL=\"https://0.0.0.0/\"
ifeq ($(OS),Windows_NT)
@ -19,14 +24,15 @@ endif
all: firmware.bin
firmware.bin: firmware.elf
riscv64-unknown-elf-objcopy -O binary $< $@
$(PREFIX)-objcopy -O binary $< $@
ls -l firmware.*
firmware.elf: $(SOURCES) hal.h Makefile
riscv64-unknown-elf-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
firmware.elf: $(SOURCES) link.ld hal.h mongoose_custom.h Makefile
$(PREFIX)-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
flash: firmware.bin
wchisp flash $<
# @echo; echo "IMPORTANT: configure device to 96k RAM / 224k Flash"
clean:
$(RM) firmware.* *.su

View File

@ -3,6 +3,8 @@
#pragma once
#define UART_DEBUG USART1
#include <ch32v30x.h>
#include <stdbool.h>
@ -58,7 +60,7 @@ static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t cfg) {
}
volatile uint32_t *r = &gpio->CFGLR;
if (no > 7) r = &gpio->CFGHR, no -= 8;
uint8_t v = (mode & 3U) | ((cfg & 3U) << 2);
uint8_t v = (uint8_t) ((mode & 3U) | ((cfg & 3U) << 2));
CLRSET(*r, 15U << (no * 4), v << (no * 4));
}
static inline void gpio_input(uint16_t pin) {
@ -127,6 +129,23 @@ static inline void ethernet_init(void) {
// NVIC_SetPriority(ETH_IRQn, 0);
}
// opt: 0: 128/192, 1: 96/224, 2: 64/256, 3: 32/288
static inline void set_ram_size(uint8_t opt) {
// Unlock flash option byte, RM 32.6.1
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xcdef89ab;
FLASH->OBKEYR = 0x45670123;
FLASH->OBKEYR = 0xcdef89ab;
FLASH->CTLR |= 1U << 4; // Enable options byte programming
unsigned val = *(uint16_t *) 0x1ffff802;
val &= ~(3U << 6);
val |= ((opt & 3U)) << 6;
FLASH->CTLR |= FLASH_CTLR_PG; // Set programming bit
*(uint16_t *) 0x1ffff802 = (uint16_t) val; // Write half-word
spin(9999);
FLASH->CTLR &= ~(1U << 4);
}
static inline void rng_init(void) {
RNG->CR |= RNG_CR_RNGEN;
}

View File

@ -0,0 +1,36 @@
ENTRY(_start);
MEMORY {
flash(rx) : ORIGIN = 0x00000000, LENGTH = 288k
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 32k
}
_estack = ORIGIN(sram) + LENGTH(sram);
SECTIONS {
.init : { _sinit = .; . = ALIGN(4); KEEP(*(SORT_NONE(.init))) . = ALIGN(4); _einit = . ; } > flash
.vector : { KEEP(*(.vector)) } > flash
.text : { *(.text .text.* .rodata .rodata.*) } > flash
.dalign : { . = ALIGN(4); PROVIDE(_data_vma = . ); } > sram AT > flash
.dlalign : { . = ALIGN(4); PROVIDE(_data_lma = . ); } > flash AT > flash
.data : {
_sdata = .;
*(.data .data.*)
. = ALIGN(8);
PROVIDE( __global_pointer$ = . + 0x800 );
*(.sdata .sdata.* .gnu.linkonce.s.*) /* for weak symbol linking */
_edata = .;
} > sram AT > flash
.bss : { _sbss = .; *(.bss .bss.* COMMON) _ebss = .; } > sram
. = ALIGN(8);
_end = .;
__stack_size = 2048;
.stack ORIGIN(sram) + LENGTH(sram) - __stack_size : {
PROVIDE( _heap_end = . );
. = ALIGN(4);
PROVIDE(_susrstack = . );
. = . + __stack_size;
PROVIDE( _eusrstack = . );
} > sram
}

View File

@ -8,7 +8,6 @@
#define LED1_PIN PIN('A', 15) // On-board red LED
#define LED2_PIN PIN('B', 4) // On-board blue LED
#define LED_PIN LED2_PIN
#define UART_DEBUG USART1
#define BLINK_PERIOD_MS 1000 // LED_PIN blinking period in millis
@ -29,10 +28,24 @@ void mg_random(void *buf, size_t len) { // Use on-board RNG
}
}
// This flash space resides at after the 0-wait 320k area
static char *s_flash_space = (char *) (0x8000000 + 320 * 1024);
bool web_load_settings(void *buf, size_t len) {
if (*(uint32_t *) s_flash_space != SETTINGS_MAGIC) return false;
memcpy(buf, s_flash_space, len);
return true;
}
bool web_save_settings(void *buf, size_t len) {
return mg_flash_write(s_flash_space, buf, len);
}
static void timer_fn(void *arg) {
gpio_toggle(LED_PIN); // Blink LED_PIN
struct mg_tcpip_if *ifp = arg; // And show
const char *names[] = {"down", "up", "req", "ready"}; // network stats
return;
MG_INFO(("Ethernet: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u",
names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent,
ifp->ndrop, ifp->nerr));
@ -53,21 +66,20 @@ static void mg_putchar(char ch, void *param) {
// https://mongoose.ws/documentation/#2-minute-integration-guide
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_HTTP_MSG) {
// The MG_EV_HTTP_MSG event means HTTP request. `hm` holds parsed request,
// see https://mongoose.ws/documentation/#struct-mg_http_message
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// If the requested URI is "/api/hi", send a simple JSON response back
if (mg_http_match_uri(hm, "/api/hi")) {
// Use mg_http_reply() API function to generate JSON response. It adds a
// Content-Length header automatically. In the response, we show
// the requested URI and HTTP body:
mg_http_reply(c, 200, "", "{%m:%m,%m:%m}\n", // See mg_snprintf doc
MG_ESC("uri"), mg_print_esc, hm->uri.len, hm->uri.ptr,
MG_ESC("body"), mg_print_esc, hm->body.len, hm->body.ptr);
if (mg_http_match_uri(hm, "/api/ram")) {
// This endpoint allows to read RAM:
// curl IP:8000/api/ram -d '{"addr":"0x20", "len": 32}'
char *s = mg_json_get_str(hm->body, "$.addr");
void *buf = (void *) (uintptr_t) (s ? strtoul(s, NULL, 0) : 0);
size_t len = mg_json_get_long(hm->body, "$.len", 4);
mg_hexdump(buf, len);
mg_http_reply(c, 200, "", "{%m:%m}\n", MG_ESC("data"), mg_print_hex, len,
buf);
free(s);
} else {
// For all other URIs, serve static content
mg_http_reply(c, 200, "", "hi tick %llu\n", s_ticks); // See mg_snprintf doc
mg_http_reply(c, 200, "", "hi tick %llu\n", s_ticks);
}
}
(void) fn_data;
@ -85,10 +97,19 @@ int main(void) {
mg_log_set(MG_LL_DEBUG); // Set log level
mg_log_set_fn(mg_putchar, UART_DEBUG);
ethernet_init(); // Initialise ethernet pins
MG_INFO(("Starting, CPU freq %g MHz", (double) SystemCoreClock / 1000000));
// MG_INFO(("RCC_RSTSCKR=%#lx", RCC->RSTSCKR));
extern char _end[], _heap_end[];
MG_INFO(("Heap size: %lu bytes", _heap_end - _end));
// Print chip RAM/Flash configuration, and set to 64/256
const char *sizes[] = {"128/192", "96/224", "64/256", "32/288"};
uint32_t mode = (FLASH->OBR >> 8) & 3U;
MG_INFO(("RAM/FLASH configuration: %s", sizes[mode]));
if (mode != 3) set_ram_size(3), mg_device_reset();
// Initialise Mongoose network stack
ethernet_init(); // Initialise ethernet pins
struct mg_tcpip_driver_stm32_data driver_data = {.mdc_cr = 1};
struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC(),
// Uncomment below for static configuration:
@ -117,17 +138,12 @@ int main(void) {
return 0;
}
// Newlib syscalls overrides. IO retargeting, and malloc
int _write(int fd, char *ptr, int len) {
if (fd == 1 || fd == 2) uart_write_buf(UART_DEBUG, ptr, (size_t) len);
return len;
}
// Newlib syscall for malloc
void *_sbrk(ptrdiff_t incr) {
extern char _end[];
extern char _heap_end[];
extern char _end[], _heap_end[];
static char *curbrk = _end;
if ((curbrk + incr < _end) || (curbrk + incr > _heap_end)) return NULL - 1;
//MG_INFO(("%p %ld", curbrk, incr));
curbrk += incr;
return curbrk - incr;
}

View File

@ -2,6 +2,8 @@
// See https://mongoose.ws/documentation/#build-options
#define MG_ARCH MG_ARCH_NEWLIB
#define MG_OTA MG_OTA_FLASH
#define MG_DEVICE MG_DEVICE_CH32V307
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_CUSTOM_MILLIS 1

View File

@ -1 +1,180 @@
ENTRY( _start ) __stack_size = 2048; PROVIDE( _stack_size = __stack_size ); MEMORY { /* CH32V30x_D8C - CH32V305RB-CH32V305FB CH32V30x_D8 - CH32V303CB-CH32V303RB */ /* FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K */ /* CH32V30x_D8C - CH32V307VC-CH32V307WC-CH32V307RC CH32V30x_D8 - CH32V303VC-CH32V303RC FLASH + RAM supports the following configuration FLASH-192K + RAM-128K FLASH-224K + RAM-96K FLASH-256K + RAM-64K FLASH-288K + RAM-32K */ FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 288K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K } SECTIONS { .init : { _sinit = .; . = ALIGN(4); KEEP(*(SORT_NONE(.init))) . = ALIGN(4); _einit = .; } >FLASH AT>FLASH .vector : { *(.vector); . = ALIGN(64); } >FLASH AT>FLASH .text : { . = ALIGN(4); *(.text) *(.text.*) *(.rodata) *(.rodata*) *(.gnu.linkonce.t.*) . = ALIGN(4); } >FLASH AT>FLASH .fini : { KEEP(*(SORT_NONE(.fini))) . = ALIGN(4); } >FLASH AT>FLASH PROVIDE( _etext = . ); PROVIDE( _eitcm = . ); .preinit_array : { PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array)) PROVIDE_HIDDEN (__preinit_array_end = .); } >FLASH AT>FLASH .init_array : { PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) PROVIDE_HIDDEN (__init_array_end = .); } >FLASH AT>FLASH .fini_array : { PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) PROVIDE_HIDDEN (__fini_array_end = .); } >FLASH AT>FLASH .ctors : { /* gcc uses crtbegin.o to find the start of the constructors, so we make sure it is first. Because this is a wildcard, it doesn't matter if the user does not actually link against crtbegin.o; the linker won't look for a file to match a wildcard. The wildcard also means that it doesn't matter which directory crtbegin.o is in. */ KEEP (*crtbegin.o(.ctors)) KEEP (*crtbegin?.o(.ctors)) /* We don't want to include the .ctor section from the crtend.o file until after the sorted ctors. The .ctor section from the crtend file contains the end of ctors marker and it must be last */ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) } >FLASH AT>FLASH .dtors : { KEEP (*crtbegin.o(.dtors)) KEEP (*crtbegin?.o(.dtors)) KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) } >FLASH AT>FLASH .dalign : { . = ALIGN(4); PROVIDE(_data_vma = .); } >RAM AT>FLASH .dlalign : { . = ALIGN(4); PROVIDE(_data_lma = .); } >FLASH AT>FLASH .data : { *(.gnu.linkonce.r.*) *(.data .data.*) *(.gnu.linkonce.d.*) . = ALIGN(8); PROVIDE( __global_pointer$ = . + 0x800 ); *(.sdata .sdata.*) *(.sdata2.*) *(.gnu.linkonce.s.*) . = ALIGN(8); *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*) . = ALIGN(4); PROVIDE( _edata = .); } >RAM AT>FLASH .bss : { . = ALIGN(4); PROVIDE( _sbss = .); *(.sbss*) *(.gnu.linkonce.sb.*) *(.bss*) *(.gnu.linkonce.b.*) *(COMMON*) . = ALIGN(4); PROVIDE( _ebss = .); } >RAM AT>FLASH PROVIDE( _end = _ebss); PROVIDE( end = . ); .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size : { PROVIDE( _heap_end = . ); . = ALIGN(4); PROVIDE(_susrstack = . ); . = . + __stack_size; PROVIDE( _eusrstack = .); } >RAM }
ENTRY( _start )
__stack_size = 2048;
PROVIDE( _stack_size = __stack_size );
MEMORY {
/* CH32V30x_D8C - CH32V305RB-CH32V305FB
CH32V30x_D8 - CH32V303CB-CH32V303RB
*/
/*
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
*/
/* CH32V30x_D8C - CH32V307VC-CH32V307WC-CH32V307RC
CH32V30x_D8 - CH32V303VC-CH32V303RC
FLASH + RAM supports the following configuration
FLASH-192K + RAM-128K
FLASH-224K + RAM-96K
FLASH-256K + RAM-64K
FLASH-288K + RAM-32K
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 288K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
*/
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 288k
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32k
}
SECTIONS
{
.init :
{
_sinit = .;
. = ALIGN(4);
KEEP(*(SORT_NONE(.init)))
. = ALIGN(4);
_einit = .;
} >FLASH AT>FLASH
.vector :
{
*(.vector);
. = ALIGN(64);
} >FLASH AT>FLASH
.text :
{
. = ALIGN(4);
*(.text)
*(.text.*)
*(.rodata)
*(.rodata*)
*(.gnu.linkonce.t.*)
. = ALIGN(4);
} >FLASH AT>FLASH
.fini :
{
KEEP(*(SORT_NONE(.fini)))
. = ALIGN(4);
} >FLASH AT>FLASH
PROVIDE( _etext = . );
PROVIDE( _eitcm = . );
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH AT>FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH AT>FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH AT>FLASH
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
} >FLASH AT>FLASH
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
} >FLASH AT>FLASH
.dalign :
{
. = ALIGN(4);
PROVIDE(_data_vma = .);
} >RAM AT>FLASH
.dlalign :
{
. = ALIGN(4);
PROVIDE(_data_lma = .);
} >FLASH AT>FLASH
.data :
{
*(.gnu.linkonce.r.*)
*(.data .data.*)
*(.gnu.linkonce.d.*)
. = ALIGN(8);
PROVIDE( __global_pointer$ = . + 0x800 );
*(.sdata .sdata.*)
*(.sdata2.*)
*(.gnu.linkonce.s.*)
. = ALIGN(8);
*(.srodata.cst16)
*(.srodata.cst8)
*(.srodata.cst4)
*(.srodata.cst2)
*(.srodata .srodata.*)
. = ALIGN(4);
PROVIDE( _edata = .);
} >RAM AT>FLASH
.bss :
{
. = ALIGN(4);
PROVIDE( _sbss = .);
*(.sbss*)
*(.gnu.linkonce.sb.*)
*(.bss*)
*(.gnu.linkonce.b.*)
*(COMMON*)
. = ALIGN(4);
PROVIDE( _ebss = .);
} >RAM AT>FLASH
PROVIDE( _end = _ebss);
PROVIDE( end = . );
.stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size :
{
PROVIDE( _heap_end = . );
. = ALIGN(4);
PROVIDE(_susrstack = . );
. = . + __stack_size;
PROVIDE( _eusrstack = .);
} >RAM
}

View File

@ -112,6 +112,88 @@ size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t dl) {
return len;
}
#ifdef MG_ENABLE_LINES
#line 1 "src/device_ch32v307.c"
#endif
#if MG_DEVICE == MG_DEVICE_CH32V307
// RM: https://www.wch-ic.com/downloads/CH32FV2x_V3xRM_PDF.html
#define FLASH_BASE 0x40022000
#define FLASH_ACTLR (FLASH_BASE + 0)
#define FLASH_KEYR (FLASH_BASE + 4)
#define FLASH_OBKEYR (FLASH_BASE + 8)
#define FLASH_STATR (FLASH_BASE + 12)
#define FLASH_CTLR (FLASH_BASE + 16)
#define FLASH_ADDR (FLASH_BASE + 20)
#define FLASH_OBR (FLASH_BASE + 28)
#define FLASH_WPR (FLASH_BASE + 32)
void *mg_flash_start(void) {
return (void *) 0x08000000;
}
size_t mg_flash_size(void) {
return 480 * 1024; // First 320k is 0-wait
}
size_t mg_flash_sector_size(void) {
return 4096;
}
size_t mg_flash_write_align(void) {
return 4;
}
int mg_flash_bank(void) {
return 0;
}
void mg_device_reset(void) {
*((volatile uint32_t *) 0xbeef0000) |= 1U << 7; // NVIC_SystemReset()
}
static void flash_unlock(void) {
static bool unlocked;
if (unlocked == false) {
MG_REG(FLASH_KEYR) = 0x45670123;
MG_REG(FLASH_KEYR) = 0xcdef89ab;
unlocked = true;
}
}
static void flash_wait(void) {
while (MG_REG(FLASH_STATR) & MG_BIT(0)) (void) 0;
}
bool mg_flash_erase(void *addr) {
//MG_INFO(("%p", addr));
flash_unlock();
flash_wait();
MG_REG(FLASH_ADDR) = (uint32_t) addr;
MG_REG(FLASH_CTLR) |= MG_BIT(1) | MG_BIT(6); // PER | STRT;
flash_wait();
return true;
}
static bool is_page_boundary(const void *addr) {
uint32_t val = (uint32_t) addr;
return (val & (mg_flash_sector_size() - 1)) == 0;
}
bool mg_flash_write(void *addr, const void *buf, size_t len) {
//MG_INFO(("%p %p %lu", addr, buf, len));
//mg_hexdump(buf, len);
flash_unlock();
const uint16_t *src = (uint16_t *) buf, *end = &src[len / 2];
uint16_t *dst = (uint16_t *) addr;
MG_REG(FLASH_CTLR) |= MG_BIT(0); // Set PG
//MG_INFO(("CTLR: %#lx", MG_REG(FLASH_CTLR)));
while (src < end) {
if (is_page_boundary(dst)) mg_flash_erase(dst);
*dst++ = *src++;
flash_wait();
}
MG_REG(FLASH_CTLR) &= ~MG_BIT(0); // Clear PG
return true;
}
#endif
#ifdef MG_ENABLE_LINES
#line 1 "src/device_dummy.c"
#endif

View File

@ -1732,10 +1732,11 @@ MG_IRAM void mg_ota_boot(void); // Bootloader function
#define MG_DEVICE_NONE 0 // Dummy system
#define MG_DEVICE_STM32H5 1 // STM32 H5
#define MG_DEVICE_STM32H7 2 // STM32 H7
#define MG_DEVICE_CUSTOM 100 // Custom implementation
#define MG_DEVICE_NONE 0 // Dummy system
#define MG_DEVICE_STM32H5 1 // STM32 H5
#define MG_DEVICE_STM32H7 2 // STM32 H7
#define MG_DEVICE_CH32V307 100 // WCH CH32V307
#define MG_DEVICE_CUSTOM 1000 // Custom implementation
#ifndef MG_DEVICE
#define MG_DEVICE MG_DEVICE_NONE

View File

@ -5,10 +5,11 @@
#include "arch.h"
#define MG_DEVICE_NONE 0 // Dummy system
#define MG_DEVICE_STM32H5 1 // STM32 H5
#define MG_DEVICE_STM32H7 2 // STM32 H7
#define MG_DEVICE_CUSTOM 100 // Custom implementation
#define MG_DEVICE_NONE 0 // Dummy system
#define MG_DEVICE_STM32H5 1 // STM32 H5
#define MG_DEVICE_STM32H7 2 // STM32 H7
#define MG_DEVICE_CH32V307 100 // WCH CH32V307
#define MG_DEVICE_CUSTOM 1000 // Custom implementation
#ifndef MG_DEVICE
#define MG_DEVICE MG_DEVICE_NONE

78
src/device_ch32v307.c Normal file
View File

@ -0,0 +1,78 @@
#include "device.h"
#include "log.h"
#if MG_DEVICE == MG_DEVICE_CH32V307
// RM: https://www.wch-ic.com/downloads/CH32FV2x_V3xRM_PDF.html
#define FLASH_BASE 0x40022000
#define FLASH_ACTLR (FLASH_BASE + 0)
#define FLASH_KEYR (FLASH_BASE + 4)
#define FLASH_OBKEYR (FLASH_BASE + 8)
#define FLASH_STATR (FLASH_BASE + 12)
#define FLASH_CTLR (FLASH_BASE + 16)
#define FLASH_ADDR (FLASH_BASE + 20)
#define FLASH_OBR (FLASH_BASE + 28)
#define FLASH_WPR (FLASH_BASE + 32)
void *mg_flash_start(void) {
return (void *) 0x08000000;
}
size_t mg_flash_size(void) {
return 480 * 1024; // First 320k is 0-wait
}
size_t mg_flash_sector_size(void) {
return 4096;
}
size_t mg_flash_write_align(void) {
return 4;
}
int mg_flash_bank(void) {
return 0;
}
void mg_device_reset(void) {
*((volatile uint32_t *) 0xbeef0000) |= 1U << 7; // NVIC_SystemReset()
}
static void flash_unlock(void) {
static bool unlocked;
if (unlocked == false) {
MG_REG(FLASH_KEYR) = 0x45670123;
MG_REG(FLASH_KEYR) = 0xcdef89ab;
unlocked = true;
}
}
static void flash_wait(void) {
while (MG_REG(FLASH_STATR) & MG_BIT(0)) (void) 0;
}
bool mg_flash_erase(void *addr) {
//MG_INFO(("%p", addr));
flash_unlock();
flash_wait();
MG_REG(FLASH_ADDR) = (uint32_t) addr;
MG_REG(FLASH_CTLR) |= MG_BIT(1) | MG_BIT(6); // PER | STRT;
flash_wait();
return true;
}
static bool is_page_boundary(const void *addr) {
uint32_t val = (uint32_t) addr;
return (val & (mg_flash_sector_size() - 1)) == 0;
}
bool mg_flash_write(void *addr, const void *buf, size_t len) {
//MG_INFO(("%p %p %lu", addr, buf, len));
//mg_hexdump(buf, len);
flash_unlock();
const uint16_t *src = (uint16_t *) buf, *end = &src[len / 2];
uint16_t *dst = (uint16_t *) addr;
MG_REG(FLASH_CTLR) |= MG_BIT(0); // Set PG
//MG_INFO(("CTLR: %#lx", MG_REG(FLASH_CTLR)));
while (src < end) {
if (is_page_boundary(dst)) mg_flash_erase(dst);
*dst++ = *src++;
flash_wait();
}
MG_REG(FLASH_CTLR) &= ~MG_BIT(0); // Clear PG
return true;
}
#endif