stepcompress: Generate steps in a per-stepper background thread

Create a thread for each stepper and use it for step generation and
step compression.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor
2025-09-03 15:29:24 -04:00
parent 96c3ca160e
commit a89694ac68
8 changed files with 175 additions and 44 deletions

View File

@@ -36,7 +36,7 @@ defs_stepcompress = """
int step_count, interval, add;
};
struct stepcompress *stepcompress_alloc(uint32_t oid);
struct stepcompress *stepcompress_alloc(uint32_t oid, char name[16]);
void stepcompress_fill(struct stepcompress *sc, uint32_t max_error
, int32_t queue_step_msgtag, int32_t set_next_step_dir_msgtag);
void stepcompress_set_invert_sdir(struct stepcompress *sc
@@ -66,10 +66,11 @@ defs_steppersync = """
void steppersync_free(struct steppersync *ss);
void steppersync_set_time(struct steppersync *ss
, double time_offset, double mcu_freq);
int32_t steppersync_generate_steps(struct steppersync *ss
, double gen_steps_time, uint64_t flush_clock);
void steppersync_history_expire(struct steppersync *ss, uint64_t end_clock);
int steppersync_flush(struct steppersync *ss, uint64_t move_clock);
void steppersync_start_gen_steps(struct steppersync *ss
, double gen_steps_time, uint64_t flush_clock);
int32_t steppersync_finalize_gen_steps(struct steppersync *ss
, uint64_t flush_clock);
"""
defs_itersolve = """

View File

@@ -15,6 +15,7 @@
// efficiency - the repetitive integer math is vastly faster in C.
#include <math.h> // sqrt
#include <pthread.h> // pthread_mutex_lock
#include <stddef.h> // offsetof
#include <stdint.h> // uint32_t
#include <stdio.h> // fprintf
@@ -47,8 +48,16 @@ struct stepcompress {
// History tracking
int64_t last_position;
struct list_head history_list;
// Itersolve reference
// Thread for step generation
struct stepper_kinematics *sk;
char name[16];
pthread_t tid;
pthread_mutex_t lock; // protects variables below
pthread_cond_t cond;
int have_work;
double bg_gen_steps_time;
uint64_t bg_flush_clock;
int32_t bg_result;
};
struct step_move {
@@ -244,9 +253,12 @@ check_line(struct stepcompress *sc, struct step_move move)
* Step compress interface
****************************************************************/
static int sc_thread_alloc(struct stepcompress *sc, char name[16]);
static void sc_thread_free(struct stepcompress *sc);
// Allocate a new 'stepcompress' object
struct stepcompress * __visible
stepcompress_alloc(uint32_t oid)
stepcompress_alloc(uint32_t oid, char name[16])
{
struct stepcompress *sc = malloc(sizeof(*sc));
memset(sc, 0, sizeof(*sc));
@@ -254,6 +266,10 @@ stepcompress_alloc(uint32_t oid)
list_init(&sc->history_list);
sc->oid = oid;
sc->sdir = -1;
int ret = sc_thread_alloc(sc, name);
if (ret)
return NULL;
return sc;
}
@@ -299,6 +315,7 @@ stepcompress_free(struct stepcompress *sc)
{
if (!sc)
return;
sc_thread_free(sc);
free(sc->queue);
message_queue_free(&sc->msg_queue);
stepcompress_history_expire(sc, UINT64_MAX);
@@ -666,6 +683,11 @@ stepcompress_extract_old(struct stepcompress *sc, struct pull_history_steps *p
return res;
}
/****************************************************************
* Step generation thread
****************************************************************/
// Store a reference to stepper_kinematics
void __visible
stepcompress_set_stepper_kinematics(struct stepcompress *sc
@@ -682,7 +704,7 @@ stepcompress_get_stepper_kinematics(struct stepcompress *sc)
}
// Generate steps (via itersolve) and flush
int32_t
static int32_t
stepcompress_generate_steps(struct stepcompress *sc, double gen_steps_time
, uint64_t flush_clock)
{
@@ -695,3 +717,96 @@ stepcompress_generate_steps(struct stepcompress *sc, double gen_steps_time
// Flush steps
return stepcompress_flush(sc, flush_clock);
}
// Main background thread for generating steps
static void *
sc_background_thread(void *data)
{
struct stepcompress *sc = data;
set_thread_name(sc->name);
pthread_mutex_lock(&sc->lock);
for (;;) {
if (!sc->have_work) {
pthread_cond_wait(&sc->cond, &sc->lock);
continue;
}
if (sc->have_work < 0)
// Exit request
break;
// Request to generate steps
sc->bg_result = stepcompress_generate_steps(sc, sc->bg_gen_steps_time
, sc->bg_flush_clock);
sc->have_work = 0;
pthread_cond_signal(&sc->cond);
}
pthread_mutex_unlock(&sc->lock);
return NULL;
}
// Signal background thread to start step generation
void
stepcompress_start_gen_steps(struct stepcompress *sc, double gen_steps_time
, uint64_t flush_clock)
{
if (!sc->sk)
return;
pthread_mutex_lock(&sc->lock);
while (sc->have_work)
pthread_cond_wait(&sc->cond, &sc->lock);
sc->bg_gen_steps_time = gen_steps_time;
sc->bg_flush_clock = flush_clock;
sc->have_work = 1;
pthread_mutex_unlock(&sc->lock);
pthread_cond_signal(&sc->cond);
}
// Wait for background thread to complete last step generation request
int32_t
stepcompress_finalize_gen_steps(struct stepcompress *sc)
{
pthread_mutex_lock(&sc->lock);
while (sc->have_work)
pthread_cond_wait(&sc->cond, &sc->lock);
int32_t res = sc->bg_result;
pthread_mutex_unlock(&sc->lock);
return res;
}
// Internal helper to start thread
static int
sc_thread_alloc(struct stepcompress *sc, char name[16])
{
strncpy(sc->name, name, sizeof(sc->name));
sc->name[sizeof(sc->name)-1] = '\0';
int ret = pthread_mutex_init(&sc->lock, NULL);
if (ret)
goto fail;
ret = pthread_cond_init(&sc->cond, NULL);
if (ret)
goto fail;
ret = pthread_create(&sc->tid, NULL, sc_background_thread, sc);
if (ret)
goto fail;
return 0;
fail:
report_errno("sc init", ret);
return -1;
}
// Request background thread to exit
static void
sc_thread_free(struct stepcompress *sc)
{
pthread_mutex_lock(&sc->lock);
while (sc->have_work)
pthread_cond_wait(&sc->cond, &sc->lock);
sc->have_work = -1;
pthread_cond_signal(&sc->cond);
pthread_mutex_unlock(&sc->lock);
int ret = pthread_join(sc->tid, NULL);
if (ret)
report_errno("sc pthread_join", ret);
}

View File

@@ -11,7 +11,7 @@ struct pull_history_steps {
int step_count, interval, add;
};
struct stepcompress *stepcompress_alloc(uint32_t oid);
struct stepcompress *stepcompress_alloc(uint32_t oid, char name[16]);
void stepcompress_fill(struct stepcompress *sc, uint32_t max_error
, int32_t queue_step_msgtag
, int32_t set_next_step_dir_msgtag);
@@ -43,8 +43,8 @@ void stepcompress_set_stepper_kinematics(struct stepcompress *sc
, struct stepper_kinematics *sk);
struct stepper_kinematics *stepcompress_get_stepper_kinematics(
struct stepcompress *sc);
int32_t stepcompress_generate_steps(struct stepcompress *sc
, double gen_steps_time
, uint64_t flush_clock);
void stepcompress_start_gen_steps(struct stepcompress *sc, double gen_steps_time
, uint64_t flush_clock);
int32_t stepcompress_finalize_gen_steps(struct stepcompress *sc);
#endif // stepcompress.h

View File

@@ -76,22 +76,6 @@ steppersync_set_time(struct steppersync *ss, double time_offset
}
}
// Generate steps and flush stepcompress objects
int32_t __visible
steppersync_generate_steps(struct steppersync *ss, double gen_steps_time
, uint64_t flush_clock)
{
int i;
for (i=0; i<ss->sc_num; i++) {
struct stepcompress *sc = ss->sc_list[i];
int32_t ret = stepcompress_generate_steps(sc, gen_steps_time
, flush_clock);
if (ret)
return ret;
}
return 0;
}
// Expire the stepcompress history before the given clock time
void __visible
steppersync_history_expire(struct steppersync *ss, uint64_t end_clock)
@@ -129,7 +113,7 @@ heap_replace(struct steppersync *ss, uint64_t req_clock)
}
// Find and transmit any scheduled steps prior to the given 'move_clock'
int __visible
static void
steppersync_flush(struct steppersync *ss, uint64_t move_clock)
{
// Order commands by the reqclock of each pending command
@@ -172,6 +156,34 @@ steppersync_flush(struct steppersync *ss, uint64_t move_clock)
// Transmit commands
if (!list_empty(&msgs))
serialqueue_send_batch(ss->sq, ss->cq, &msgs);
}
// Start generating steps in stepcompress objects
void __visible
steppersync_start_gen_steps(struct steppersync *ss, double gen_steps_time
, uint64_t flush_clock)
{
int i;
for (i=0; i<ss->sc_num; i++) {
struct stepcompress *sc = ss->sc_list[i];
stepcompress_start_gen_steps(sc, gen_steps_time, flush_clock);
}
}
// Finalize step generation and flush
int32_t __visible
steppersync_finalize_gen_steps(struct steppersync *ss, uint64_t flush_clock)
{
int i;
int32_t res = 0;
for (i=0; i<ss->sc_num; i++) {
struct stepcompress *sc = ss->sc_list[i];
int32_t ret = stepcompress_finalize_gen_steps(sc);
if (ret)
res = ret;
}
if (res)
return res;
steppersync_flush(ss, flush_clock);
return 0;
}

View File

@@ -10,9 +10,10 @@ struct steppersync *steppersync_alloc(
void steppersync_free(struct steppersync *ss);
void steppersync_set_time(struct steppersync *ss, double time_offset
, double mcu_freq);
int32_t steppersync_generate_steps(struct steppersync *ss, double gen_steps_time
, uint64_t flush_clock);
void steppersync_history_expire(struct steppersync *ss, uint64_t end_clock);
int steppersync_flush(struct steppersync *ss, uint64_t move_clock);
void steppersync_start_gen_steps(struct steppersync *ss, double gen_steps_time
, uint64_t flush_clock);
int32_t steppersync_finalize_gen_steps(struct steppersync *ss
, uint64_t flush_clock);
#endif // steppersync.h