diff --git a/changelog.md b/changelog.md index ff6c4e6..9c5a5a8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,24 @@ ## grblHAL changelog +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. + +--- + 20251020 Core: diff --git a/grbl.h b/grbl.h index 0921f1f..26ee66c 100644 --- a/grbl.h +++ b/grbl.h @@ -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" diff --git a/grbllib.c b/grbllib.c index 9f81fab..76cfb1f 100644 --- a/grbllib.c +++ b/grbllib.c @@ -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()); + } } diff --git a/nvs.h b/nvs.h index 4bd9b7d..e89b05c 100644 --- a/nvs.h +++ b/nvs.h @@ -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 diff --git a/nvs_buffer.c b/nvs_buffer.c index 42a469d..0ea5bff 100644 --- a/nvs_buffer.c +++ b/nvs_buffer.c @@ -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); diff --git a/planner.c b/planner.c index acc1fd6..2fc5a18 100644 --- a/planner.c +++ b/planner.c @@ -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; diff --git a/planner.h b/planner.h index eb1f7cd..1e2f5ef 100644 --- a/planner.h +++ b/planner.h @@ -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);