Workaround for POS (Power on self-test) failure messages(s) not output on "native" USB connect.

Fixed handling of NVS buffer allocation, size was not increased as expected when physical NVS is capable of holding more than 2 Kbytes.
This could lead to POS failure or plugins not initializing when a large tool table is configured.
This commit is contained in:
Terje Io
2025-10-23 10:40:01 +02:00
parent ba222d16aa
commit 720f6bd552
7 changed files with 157 additions and 77 deletions

View File

@@ -1,5 +1,24 @@
## grblHAL changelog
<a name="20251023">Build 20251023
Core:
* Workaround for POS (Power on self-test) failure messages(s) not output on "native" USB connect.
* Fixed handling of NVS buffer allocation, size was not increased as expected when physical NVS is capable of holding more than 2 Kbytes.
This could lead to POS failure or plugins not initializing when a large tool table is configured.
Drivers:
* Simulator: fix for double free bug, ref. issue [#17](https://github.com/grblHAL/Simulator/issues/17) and block reporting not working.
Plugins:
* SD card, macros: fix for lost parameter context when tool change commands that executes macros is run from `G65` macros.
---
<a name="20251020">20251020
Core:

2
grbl.h
View File

@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
#define GRBL_BUILD 20251022
#define GRBL_BUILD 20251023
#define GRBL_URL "https://github.com/grblHAL"

102
grbllib.c
View File

@@ -105,6 +105,27 @@ __attribute__((weak)) void board_ports_init (void)
// NOOP
}
__attribute__((always_inline)) static inline void task_free (core_task_t *task)
{
task->fn = NULL;
task->next = NULL;
if(last_freed == NULL)
last_freed = task;
}
__attribute__((always_inline)) static inline core_task_t *task_run (core_task_t *task)
{
core_task_t *t = task;
foreground_task_ptr fn = task->fn;
void *data = task->data;
task = task->next;
task_free(t);
fn(data);
return task;
}
void dummy_bool_handler (bool arg)
{
// NOOP
@@ -190,6 +211,20 @@ static void onLinestateChanged (serial_linestate_t state)
on_linestate_changed(state);
}
static void print_pos_msg (void *data)
{
hal.stream.write("grblHAL: power on self-test (POS) failed!" ASCII_EOL);
if(on_booted) do {
} while((on_booted = task_run(on_booted)));
}
static void onPosFailure (serial_linestate_t state)
{
if(state.dtr) // delay a bit to let the USB stack come up
task_add_delayed(print_pos_msg, NULL, 50);
}
static bool onProbeToolsetter (tool_data_t *tool, coord_data_t *position, bool at_g59_3, bool on)
{
bool ok = false;
@@ -269,10 +304,6 @@ int grbl_enter (void)
limits_init();
#if NVSDATA_BUFFER_ENABLE
nvs_buffer_alloc(); // Allocate memory block for NVS buffer
#endif
settings_clear();
report_init_fns();
@@ -282,6 +313,10 @@ int grbl_enter (void)
driver.init = driver_init();
#if NVSDATA_BUFFER_ENABLE
nvs_buffer_alloc(); // Allocate memory block for NVS buffer
#endif
#ifdef DEBUGOUT
debug_stream_init();
#endif
@@ -501,27 +536,6 @@ __attribute__((always_inline)) static inline core_task_t *task_alloc (void)
return task;
}
__attribute__((always_inline)) static inline void task_free (core_task_t *task)
{
task->fn = NULL;
task->next = NULL;
if(last_freed == NULL)
last_freed = task;
}
__attribute__((always_inline)) static inline core_task_t *task_run (core_task_t *task)
{
core_task_t *t = task;
foreground_task_ptr fn = task->fn;
void *data = task->data;
task = task->next;
task_free(t);
fn(data);
return task;
}
static void task_execute (sys_state_t state)
{
static uint32_t last_ms = 0;
@@ -752,9 +766,41 @@ ISR_CODE bool ISR_FUNC(task_run_on_startup)(foreground_task_ptr fn, void *data)
// for core use only, called once from protocol.c on cold start
void task_execute_on_startup (void)
{
if(on_booted) do {
if(!sys.driver_started) {
// Clear task queues except startup warnings.
core_task_t *task, *prev = NULL;
if((task = on_booted)) do {
if(!(task->fn == report_warning)) {
if(prev)
prev->next = task->next;
else {
prev = NULL;
on_booted = task->next;
}
task_free(task);
} else
prev = task;
} while((task = prev ? prev->next : on_booted));
while(next_task)
task_delete(next_task->fn, NULL);
while(systick_task)
task_delete_systick(systick_task->fn, NULL);
}
if(on_booted && (sys.driver_started || !hal.stream.state.linestate_event)) do {
} while((on_booted = task_run(on_booted)));
if(!sys.driver_started)
while(true);
if(!sys.driver_started) {
if(hal.stream.state.linestate_event)
hal.stream.on_linestate_changed = onPosFailure;
while(true)
grbl.on_execute_realtime(state_get());
}
}

7
nvs.h
View File

@@ -30,6 +30,13 @@ Minimum 1024 bytes required, more if > 5 axes enabled, space for driver and/or p
#define NVS_SIZE 2048
#endif
#ifndef NVS_SIZE_MAX
/*! \brief Max size in bytes of the NVS buffer in RAM.
Must be >= NVS_SIZE.
*/
#define NVS_SIZE_MAX 4096 // Limit to 4K for now
#endif
//! Number of bytes used for storing CRC values. Do not change this!
#ifndef NVS_CRC_BYTES
#define NVS_CRC_BYTES 2

View File

@@ -37,11 +37,14 @@
#include "crc.h"
#include "nvs.h"
static uint8_t *nvsbuffer = NULL;
settings_dirty_t settings_dirty;
static nvs_io_t physical_nvs;
static bool dirty;
uint32_t nvs_size_max = NVS_SIZE;
settings_dirty_t settings_dirty;
static struct {
uint8_t *addr;
uint32_t size;
} nvsbuffer = { .size = NVS_SIZE };
typedef struct {
uint16_t addr;
@@ -123,17 +126,17 @@ static const emap_t target[] = {
{0, 0, 0} // list termination - do not remove
};
inline static uint8_t ram_get_byte (uint32_t addr)
inline static uint8_t ram_get_byte (uint32_t offset)
{
return nvsbuffer[addr];
return nvsbuffer.addr[offset];
}
inline static void ram_put_byte (uint32_t addr, uint8_t new_value)
inline static void ram_put_byte (uint32_t offset, uint8_t new_value)
{
if(addr == 0)
if(offset == 0)
settings_dirty.version = true;
dirty = dirty || nvsbuffer[addr] != new_value || addr == 0;
nvsbuffer[addr] = new_value;
dirty = dirty || nvsbuffer.addr[offset] != new_value || offset == 0;
nvsbuffer.addr[offset] = new_value;
}
static nvs_transfer_result_t memcpy_to_ram (uint32_t destination, uint8_t *source, uint32_t size, bool with_checksum)
@@ -208,7 +211,7 @@ static nvs_transfer_result_t memcpy_from_ram (uint8_t *destination, uint32_t sou
if(hal.nvs.driver_area.address && source > hal.nvs.driver_area.address + hal.nvs.driver_area.size)
return physical_nvs.memcpy_from_nvs(destination, source, size, with_checksum);
uint16_t checksum = with_checksum ? calc_checksum(&nvsbuffer[source], size) : 0;
uint16_t checksum = with_checksum ? calc_checksum(&nvsbuffer.addr[source], size) : 0;
for(; size > 0; size--)
*(destination++) = ram_get_byte(source++);
@@ -223,29 +226,23 @@ static nvs_transfer_result_t memcpy_from_ram (uint8_t *destination, uint32_t sou
// Try to allocate RAM from heap for buffer/emulation.
bool nvs_buffer_alloc (void)
{
static uint32_t nvs_size = NVS_SIZE;
if(nvsbuffer.addr == NULL) {
if(hal.nvs.size_max > nvs_size) {
nvs_size_max = min(4096, hal.nvs.size_max); // Limit to 4K for now
if(nvsbuffer)
free(nvsbuffer);
if(hal.nvs.size_max > NVS_SIZE)
nvsbuffer.size = min(NVS_SIZE_MAX, hal.nvs.size_max);
if(nvsbuffer.size >= GRBL_NVS_SIZE && (nvsbuffer.addr = malloc(nvsbuffer.size)))
memset(nvsbuffer.addr, 0xFF, nvsbuffer.size);
}
assert(nvs_size_max >= GRBL_NVS_SIZE);
if((nvsbuffer = malloc(nvs_size_max))) {
nvs_size = nvs_size_max;
memset(nvsbuffer, 0xFF, nvs_size_max);
}
return nvsbuffer != NULL;
return nvsbuffer.addr != NULL;
}
void nvs_buffer_free (void)
{
if(nvsbuffer) {
if(nvsbuffer.addr) {
nvs_buffer_sync_physical();
free(nvsbuffer);
free(nvsbuffer.addr);
}
}
//
@@ -255,15 +252,15 @@ bool nvs_buffer_init (void)
{
hal.nvs.size = ((hal.nvs.size - 1) | 0x03) + 1; // Ensure NVS area ends on a word boundary
if(nvsbuffer) {
if(nvsbuffer.addr) {
memcpy(&physical_nvs, &hal.nvs, sizeof(nvs_io_t)); // save pointers to physical storage handler functions
// Copy physical storage content to RAM when available
if(physical_nvs.type == NVS_Flash)
physical_nvs.memcpy_from_flash(nvsbuffer);
physical_nvs.memcpy_from_flash(nvsbuffer.addr);
else if(physical_nvs.type != NVS_None)
physical_nvs.memcpy_from_nvs(nvsbuffer, 0, GRBL_NVS_SIZE + hal.nvs.driver_area.size, false);
physical_nvs.memcpy_from_nvs(nvsbuffer.addr, 0, GRBL_NVS_SIZE + hal.nvs.driver_area.size, false);
// Switch hal to use RAM version of non-volatile storage data
hal.nvs.type = NVS_Emulated;
@@ -279,9 +276,9 @@ bool nvs_buffer_init (void)
if(physical_nvs.type == NVS_None || ram_get_byte(0) != SETTINGS_VERSION) {
settings_restore(settings_all);
if(physical_nvs.type == NVS_Flash)
physical_nvs.memcpy_to_flash(nvsbuffer);
physical_nvs.memcpy_to_flash(nvsbuffer.addr);
else if(physical_nvs.memcpy_to_nvs)
physical_nvs.memcpy_to_nvs(0, nvsbuffer, GRBL_NVS_SIZE + hal.nvs.driver_area.size, false);
physical_nvs.memcpy_to_nvs(0, nvsbuffer.addr, GRBL_NVS_SIZE + hal.nvs.driver_area.size, false);
if(physical_nvs.type != NVS_None)
grbl.report.status_message(Status_SettingReadFail);
}
@@ -291,7 +288,7 @@ bool nvs_buffer_init (void)
// Clear settings dirty flags
memset(&settings_dirty, 0, sizeof(settings_dirty_t));
return nvsbuffer != NULL;
return nvsbuffer.addr != NULL;
}
// Allocate NVS block for driver settings.
@@ -300,22 +297,23 @@ nvs_address_t nvs_alloc (size_t size)
{
static uint8_t *mem_address;
uint8_t *start_address;
nvs_address_t addr = 0;
// Check if already switched to emulation or buffer allocation failed, return NULL if so.
if(hal.nvs.type == NVS_Emulated || nvsbuffer == NULL)
if(hal.nvs.type == NVS_Emulated || (nvsbuffer.addr == NULL && !nvs_buffer_alloc()))
return 0;
if(hal.nvs.driver_area.address == 0) {
hal.nvs.driver_area.address = GRBL_NVS_SIZE;
hal.nvs.driver_area.mem_address = mem_address = nvsbuffer + GRBL_NVS_SIZE;
hal.nvs.driver_area.mem_address = mem_address = nvsbuffer.addr + hal.nvs.driver_area.address;
}
size += NVS_CRC_BYTES; // add room for checksum.
if(hal.nvs.driver_area.size + size < (nvs_size_max - GRBL_NVS_SIZE)) {
mem_address = (uint8_t *)((uintptr_t)(mem_address - 1) | 0x03) + 1; // Align to word boundary
addr = mem_address - nvsbuffer;
mem_address += size;
start_address = (uint8_t *)((uintptr_t)(mem_address - 1) | 0x03) + 1; // Align to word boundary
if(start_address + size < nvsbuffer.addr + nvsbuffer.size) {
addr = start_address - nvsbuffer.addr;
mem_address = start_address + size;
hal.nvs.driver_area.size = mem_address - hal.nvs.driver_area.mem_address;
hal.nvs.size = GRBL_NVS_SIZE + hal.nvs.driver_area.size + 1;
}
@@ -332,13 +330,13 @@ void nvs_buffer_sync_physical (void)
if(physical_nvs.memcpy_to_nvs) {
if(settings_dirty.version)
settings_dirty.version = physical_nvs.memcpy_to_nvs(0, nvsbuffer, 1, false) != NVS_TransferResult_OK;
settings_dirty.version = physical_nvs.memcpy_to_nvs(0, nvsbuffer.addr, 1, false) != NVS_TransferResult_OK;
if(settings_dirty.global_settings)
settings_dirty.global_settings = physical_nvs.memcpy_to_nvs(NVS_ADDR_GLOBAL, (uint8_t *)(nvsbuffer + NVS_ADDR_GLOBAL), sizeof(settings_t) + NVS_CRC_BYTES, false) != NVS_TransferResult_OK;
settings_dirty.global_settings = physical_nvs.memcpy_to_nvs(NVS_ADDR_GLOBAL, (uint8_t *)(nvsbuffer.addr + NVS_ADDR_GLOBAL), sizeof(settings_t) + NVS_CRC_BYTES, false) != NVS_TransferResult_OK;
if(settings_dirty.build_info)
settings_dirty.build_info = physical_nvs.memcpy_to_nvs(NVS_ADDR_BUILD_INFO, (uint8_t *)(nvsbuffer + NVS_ADDR_BUILD_INFO), sizeof(stored_line_t) + NVS_CRC_BYTES, false) != NVS_TransferResult_OK;
settings_dirty.build_info = physical_nvs.memcpy_to_nvs(NVS_ADDR_BUILD_INFO, (uint8_t *)(nvsbuffer.addr + NVS_ADDR_BUILD_INFO), sizeof(stored_line_t) + NVS_CRC_BYTES, false) != NVS_TransferResult_OK;
uint_fast8_t idx = N_STARTUP_LINE, offset;
if(settings_dirty.startup_lines) do {
@@ -346,7 +344,7 @@ void nvs_buffer_sync_physical (void)
if(bit_istrue(settings_dirty.startup_lines, bit(idx))) {
bit_false(settings_dirty.startup_lines, bit(idx));
offset = NVS_ADDR_STARTUP_BLOCK + idx * (sizeof(stored_line_t) + NVS_CRC_BYTES);
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer + offset), sizeof(stored_line_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer.addr + offset), sizeof(stored_line_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
bit_false(settings_dirty.startup_lines, bit(idx));
}
} while(idx);
@@ -355,14 +353,14 @@ void nvs_buffer_sync_physical (void)
if(settings_dirty.coord_data) do {
if(bit_istrue(settings_dirty.coord_data, bit(idx))) {
offset = NVS_ADDR_PARAMETERS + idx * (sizeof(coord_data_t) + NVS_CRC_BYTES);
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer + offset), sizeof(coord_data_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer.addr + offset), sizeof(coord_data_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
bit_false(settings_dirty.coord_data, bit(idx));
}
} while(idx--);
if(settings_dirty.driver_settings) {
if(hal.nvs.driver_area.size > 0)
settings_dirty.driver_settings = physical_nvs.memcpy_to_nvs(hal.nvs.driver_area.address, (uint8_t *)(nvsbuffer + hal.nvs.driver_area.address), hal.nvs.driver_area.size, false) != NVS_TransferResult_OK;
settings_dirty.driver_settings = physical_nvs.memcpy_to_nvs(hal.nvs.driver_area.address, (uint8_t *)(nvsbuffer.addr + hal.nvs.driver_area.address), hal.nvs.driver_area.size, false) != NVS_TransferResult_OK;
else
settings_dirty.driver_settings = false;
}
@@ -373,7 +371,7 @@ void nvs_buffer_sync_physical (void)
idx--;
if(bit_istrue(settings_dirty.tool_data, bit(idx))) {
offset = NVS_ADDR_TOOL_TABLE + idx * (sizeof(tool_data_t) + NVS_CRC_BYTES);
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer + offset), sizeof(tool_data_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer.addr + offset), sizeof(tool_data_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
bit_false(settings_dirty.tool_data, bit(idx));
}
} while(idx);
@@ -390,7 +388,7 @@ void nvs_buffer_sync_physical (void)
} else if(physical_nvs.memcpy_to_flash) {
uint_fast8_t retries = 4;
do {
if(physical_nvs.memcpy_to_flash(nvsbuffer))
if(physical_nvs.memcpy_to_flash(nvsbuffer.addr))
retries = 0;
else if(--retries == 0)
report_message("Settings write failed!", Message_Warning);
@@ -425,7 +423,7 @@ void nvs_memmap (void)
strcpy(buf, "Parameters: ");
strcat(buf, uitoa(NVS_ADDR_PARAMETERS));
strcat(buf, " ");
strcat(buf, uitoa(N_CoordinateSystems * (sizeof(coord_data_t) + NVS_CRC_BYTES)));
strcat(buf, uitoa(NVS_SIZE_PARAMETERS));
strcat(buf, " ");
strcat(buf, uitoa(NVS_ADDR_PARAMETERS + N_CoordinateSystems * (sizeof(coord_data_t) + NVS_CRC_BYTES)));
report_message(buf, Message_Plain);
@@ -433,7 +431,7 @@ void nvs_memmap (void)
strcpy(buf, "Startup block: ");
strcat(buf, uitoa(NVS_ADDR_STARTUP_BLOCK));
strcat(buf, " ");
strcat(buf, uitoa(N_STARTUP_LINE * (sizeof(stored_line_t) + NVS_CRC_BYTES)));
strcat(buf, uitoa(NVS_SIZE_STARTUP_BLOCK));
strcat(buf, " ");
strcat(buf, uitoa(NVS_ADDR_STARTUP_BLOCK + N_STARTUP_LINE * (sizeof(stored_line_t) + NVS_CRC_BYTES)));
report_message(buf, Message_Plain);
@@ -441,7 +439,7 @@ void nvs_memmap (void)
strcpy(buf, "Build info: ");
strcat(buf, uitoa(NVS_ADDR_BUILD_INFO));
strcat(buf, " ");
strcat(buf, uitoa(sizeof(stored_line_t) + NVS_CRC_BYTES));
strcat(buf, uitoa(NVS_SIZE_BUILD_INFO));
strcat(buf, " ");
strcat(buf, uitoa(NVS_ADDR_BUILD_INFO + sizeof(stored_line_t) + NVS_CRC_BYTES));
report_message(buf, Message_Plain);

View File

@@ -288,6 +288,13 @@ plan_block_t *plan_get_current_block (void)
}
// Returns address of the last block added, if available. Called by the grblHAL simulator.
plan_block_t *plan_get_recent_block (void)
{
return block_buffer_head == block_buffer_tail ? NULL : block_buffer_head->prev;
}
inline float plan_get_exec_block_exit_speed_sqr (void)
{
plan_block_t *block = block_buffer_tail->next;

View File

@@ -170,6 +170,9 @@ plan_block_t *plan_get_system_motion_block (void);
// Gets the current block. Returns NULL if buffer empty
plan_block_t *plan_get_current_block (void);
// Gets last added block. Returns NULL if buffer empty
plan_block_t *plan_get_recent_block (void);
// Called by step segment buffer when computing executing block velocity profile.
float plan_get_exec_block_exit_speed_sqr (void);