rcbench
Servo and ESC test bench: panel, IOmcu and shared C
Loading...
Searching...
No Matches
kst_plan.c File Reference

Search for the order of the register writes that changes an image. More...

#include "kst_plan.h"
#include <string.h>

Macros

#define GROUPS   2u
#define UNITS_CHOOSE   KST_PLAN_PIECE_UNITS
 Units of the work that is no image check, by its cost against one.
#define UNITS_SETTLE   4u

Enumerations

enum  {
  PH_IDLE = 0 , PH_GROUP , PH_SEARCH , PH_FIELD ,
  PH_OPEN , PH_CHOOSE , PH_SETTLE , PH_DONE
}
 What a job does next. More...

Functions

static bool bit_of (const uint8_t *set, unsigned i)
static void bit_set (uint8_t *set, unsigned i)
 Set one bit of a bit set.
static void bit_clear (uint8_t *set, unsigned i)
 Clear one bit of a bit set.
static const uint8_t * regs_of (const kst_plan_job_t *j)
 The register list of the group the job works on.
static unsigned count_of (const kst_plan_job_t *j)
 Number of registers in the group the job is working on.
static void emit (kst_plan_job_t *j, uint8_t reg, uint8_t value)
 Append a write to the plan and apply it to the job's image.
static bool valid (kst_plan_job_t *j)
 Check the job's image against the hard rules it may not break.
static bool sound (kst_plan_job_t *j, unsigned todo)
 Whether the image with the registers of todo still to write is valid; j->img is that image.
static void forget (kst_plan_job_t *j, unsigned lo)
 Forget what is known of every subset that holds register index lo: its images change with the value of that register.
static uint16_t raw_of (const kst_image_t *img, kst_field_id_t id)
 A field's raw value, as kst_field_get() gives it, read from the table row in place: the search reads two fields for every image it scores.
static int32_t authority (const kst_plan_job_t *j)
 Lower is better: less travel between the end points, or less speed.
static unsigned pick (kst_plan_job_t *j)
 The register of j->todo, not tried at this depth, whose write leaves a valid image of the least authority; of two alike the first.
static void take_back (kst_plan_job_t *j)
 Take back the write at the depth below.
static void search_begin (kst_plan_job_t *j)
 Start the depth-first search for an order of the group's changed registers.
static void search_found (kst_plan_job_t *j)
 The search found an order: its writes go into the plan, after the extra write when the search ran from a detour value.
static void search_level (kst_plan_job_t *j)
 One level of a depth-first search for an order of the registers in j->todo, bit i for the group's register i, in which every image is valid.
static uint16_t excursion (uint16_t a, uint16_t b)
 Absolute difference of two 16-bit values.
static uint16_t low_part (const kst_field_t *f, unsigned x)
 The part of a field's value that its low register holds when the register holds x.
static unsigned index_of (const kst_plan_job_t *j, uint8_t reg)
 Index of a register in the group the job is working on.
static void finish (kst_plan_job_t *j, kst_plan_result_t result)
 End the job with a result.
static void emit_if_changed (kst_plan_job_t *j, uint8_t reg)
 Write a register to its target value when the job's image differs from it.
static void emit_duty (kst_plan_job_t *j)
 Write Max.
static void settle_begin (kst_plan_job_t *j)
 Start the settled-mark pass from the start image.
static void group_failed (kst_plan_job_t *j)
 No order and no detour for a group.
static void field_next (kst_plan_job_t *j)
 The next field, from j->field on, that lies in two of the group's registers which both change: its low byte can take one extra write to a third value before the search runs again.
static void open_one (kst_plan_job_t *j)
 One value of the detour byte: it is a candidate when the image with it is valid and it is neither the old nor the new value.
static void choose (kst_plan_job_t *j)
 The untried detour value with the smallest excursion of the field from its old value before, and from its new value after, the high part changes; of two alike the lower value.
static bool in_group (uint8_t reg)
 Check whether a register is one of the two searched groups.
static bool pair_is (const kst_image_t *img, const kst_image_t *ref)
 Check whether registers 0x02 and 0x01 of two images agree.
static bool is_settled (const kst_plan_t *plan, const kst_image_t *img)
 Check that every two-register field of an image holds its start or its target value.
static void settle_one (kst_plan_job_t *j)
 The settled mark of the next write, or the end of the job after the last. j->img follows the plan from its start image.
static void group_begin (kst_plan_job_t *j)
 Take up the next group: the registers of it that change, and nothing known of their subsets.
static void advance (kst_plan_job_t *j)
 Run one step of the job, selected by its phase.
kst_plan_result_t kst_plan_step (kst_plan_job_t *job, uint32_t budget)
 Advance a job by budget units.
uint32_t kst_plan_units (const kst_plan_job_t *job)
 Units the job has taken so far.
static void begin (kst_plan_t *out, const kst_image_t *start, const kst_image_t *target, kst_plan_kind_t kind)
 start and target may lie in out, as a plan's own start or target do: both are read before the plan is cleared.
static kst_plan_result_t ended (kst_plan_job_t *job, kst_plan_result_t result)
 A final result at a begin call.
static kst_plan_result_t job_begin (kst_plan_job_t *job, kst_plan_t *out, bool may_be_unchecked, uint32_t cap)
 Set up a planning job for a plan from its start and target images.
kst_plan_result_t kst_plan_edit_begin (kst_plan_job_t *job, const kst_image_t *start, const kst_image_t *target, kst_field_set_t unlocked, kst_plan_t *out, kst_rules_t *violated, uint32_t cap)
 Check an edit from start to target and prepare job to plan it into out.
kst_plan_result_t kst_plan_restore_begin (kst_plan_job_t *job, const kst_image_t *current, const kst_image_t *backup, kst_plan_t *out, uint32_t cap)
 The same for kst_plan_restore(): KST_PLAN_BUSY, or KST_PLAN_ERR_ARG or KST_PLAN_ERR_R00 at once.
static kst_plan_result_t run (kst_plan_job_t *job, kst_plan_result_t r)
 Run a job to its end in one call.
kst_plan_result_t kst_plan_edit (const kst_image_t *start, const kst_image_t *target, kst_field_set_t unlocked, kst_plan_t *out, kst_rules_t *violated)
 Plan an edit from start to target.
kst_plan_result_t kst_plan_restore (const kst_image_t *current, const kst_image_t *backup, kst_plan_t *out)
 Plan the way from current back to backup.
kst_plan_result_t kst_plan_release_pairing (const kst_image_t *current, kst_plan_t *out)
 Plan the write of 0x00 to register 0x1D.
bool kst_plan_image_after (const kst_plan_t *plan, unsigned steps, kst_image_t *out)
 The image after the first steps writes of a plan.

Variables

static const uint8_t k_position [] = { 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D }
 The registers whose hard rules read each other.
static const uint8_t k_speed [] = { 0x04, 0x1B, 0x1C }

Detailed Description

Search for the order of the register writes that changes an image.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ UNITS_SETTLE

#define UNITS_SETTLE   4u

the settled mark of one write

Enumeration Type Documentation

◆ anonymous enum

anonymous enum

What a job does next.

One call of advance() does one of these once.

Function Documentation

◆ authority()

int32_t authority ( const kst_plan_job_t * j)
static

Lower is better: less travel between the end points, or less speed.

With spd_sel off the servo runs at its own speed, taken as the highest.

◆ begin()

void begin ( kst_plan_t * out,
const kst_image_t * start,
const kst_image_t * target,
kst_plan_kind_t kind )
static

start and target may lie in out, as a plan's own start or target do: both are read before the plan is cleared.

The caller reads them from the plan afterwards.

◆ choose()

void choose ( kst_plan_job_t * j)
static

The untried detour value with the smallest excursion of the field from its old value before, and from its new value after, the high part changes; of two alike the lower value.

The search then runs from it. The excursion is computed again for every pass, from the field's layout: the open set is 32 bytes, a table of the 256 excursions is 512.

◆ emit_duty()

void emit_duty ( kst_plan_job_t * j)
static

Write Max.

Duty and its copy to their target values when they differ.

◆ ended()

kst_plan_result_t ended ( kst_plan_job_t * job,
kst_plan_result_t result )
static

A final result at a begin call.

The job, when there is one, holds it.

◆ finish()

void finish ( kst_plan_job_t * j,
kst_plan_result_t result )
static

End the job with a result.

A result other than KST_PLAN_OK leaves no writes in the plan.

◆ forget()

void forget ( kst_plan_job_t * j,
unsigned lo )
static

Forget what is known of every subset that holds register index lo: its images change with the value of that register.

The subsets without it keep their verdicts, which hold for every detour value.

◆ group_begin()

void group_begin ( kst_plan_job_t * j)
static

Take up the next group: the registers of it that change, and nothing known of their subsets.

After the last group the duty pair and every other register follow, which need no search.

◆ group_failed()

void group_failed ( kst_plan_job_t * j)
static

No order and no detour for a group.

An edit ends here; a restore takes ascending address order and is marked unchecked.

◆ index_of()

unsigned index_of ( const kst_plan_job_t * j,
uint8_t reg )
static

Index of a register in the group the job is working on.

The group's count when the register is not in it.

◆ kst_plan_edit()

kst_plan_result_t kst_plan_edit ( const kst_image_t * start,
const kst_image_t * target,
kst_field_set_t unlocked,
kst_plan_t * out,
kst_rules_t * violated )

Plan an edit from start to target.

The hard rules of kst_limits_edit(start, target, unlocked) are checked first; violated, when not NULL, receives the broken ones. Soft rules do not refuse a plan: the caller shows them and asks.

out holds a plan with 0 writes when the result is not KST_PLAN_OK.

Parameters
startImage the servo holds.
targetImage to reach.
unlockedFields whose KST_ACCESS_LOCKED is lifted.
outReceives the plan.
violatedReceives the hard rules broken; may be NULL.
Returns
KST_PLAN_OK, or the reason no plan was made.

◆ kst_plan_edit_begin()

kst_plan_result_t kst_plan_edit_begin ( kst_plan_job_t * job,
const kst_image_t * start,
const kst_image_t * target,
kst_field_set_t unlocked,
kst_plan_t * out,
kst_rules_t * violated,
uint32_t cap )

Check an edit from start to target and prepare job to plan it into out.

KST_PLAN_BUSY: the job is ready for kst_plan_step(). Every other result is final and is what kst_plan_edit() returns for the same request before it searches: KST_PLAN_ERR_ARG, _R00, _PAIRING, _RULES. The call checks at most 4 images; its cost does not grow with the request.

start and target are copied before out is cleared: either may lie in out. The same holds for kst_plan_restore_begin(), kst_plan_edit(), kst_plan_restore() and kst_plan_release_pairing().

cap is the most units the job may take, or KST_PLAN_NO_CAP.

Parameters
jobJob to prepare.
startImage the servo holds.
targetImage to reach.
unlockedFields whose KST_ACCESS_LOCKED is lifted.
outReceives the plan.
violatedReceives the hard rules broken; may be NULL.
capMost units the job may take, or KST_PLAN_NO_CAP.
Returns
KST_PLAN_BUSY, or the final result of the request.

◆ kst_plan_image_after()

bool kst_plan_image_after ( const kst_plan_t * plan,
unsigned steps,
kst_image_t * out )

The image after the first steps writes of a plan.

False for more steps than the plan has.

Parameters
planPlan to apply.
stepsNumber of writes to apply.
outReceives the image.
Returns
true when steps does not exceed the plan length.

◆ kst_plan_release_pairing()

kst_plan_result_t kst_plan_release_pairing ( const kst_image_t * current,
kst_plan_t * out )

Plan the write of 0x00 to register 0x1D.

0x00 is the only value the vendor's card does not refuse in any tested case. The effect inside the servo is not known. A card identifier is never written.

Parameters
currentImage the servo holds.
outReceives the plan.
Returns
KST_PLAN_OK, or the reason no plan was made.

◆ kst_plan_restore()

kst_plan_result_t kst_plan_restore ( const kst_image_t * current,
const kst_image_t * backup,
kst_plan_t * out )

Plan the way from current back to backup.

Access classes and limits do not apply: every value is one this servo held. Register 0x1D is written back like any other. A difference in register 0x00 is refused.

Parameters
currentImage the servo holds.
backupImage to return to.
outReceives the plan.
Returns
KST_PLAN_OK, or the reason no plan was made.

◆ kst_plan_restore_begin()

kst_plan_result_t kst_plan_restore_begin ( kst_plan_job_t * job,
const kst_image_t * current,
const kst_image_t * backup,
kst_plan_t * out,
uint32_t cap )

The same for kst_plan_restore(): KST_PLAN_BUSY, or KST_PLAN_ERR_ARG or KST_PLAN_ERR_R00 at once.

Parameters
jobJob to prepare.
currentImage the servo holds.
backupImage to return to.
outReceives the plan.
capMost units the job may take, or KST_PLAN_NO_CAP.
Returns
KST_PLAN_BUSY, or the final result of the request.

◆ kst_plan_step()

kst_plan_result_t kst_plan_step ( kst_plan_job_t * job,
uint32_t budget )

Advance a job by budget units.

The call ends after the piece of work that brings its count to budget or above; a piece is at most KST_PLAN_PIECE_UNITS units. budget 0 does nothing.

KST_PLAN_BUSY while work is left. Then the job's result, and the same result on every later call: KST_PLAN_OK, KST_PLAN_ERR_NO_PATH, or KST_PLAN_ERR_TOO_COMPLEX when the job needs more units than its cap. A job that needs as many units as its cap ends with its verdict. The plan holds 0 writes after every result but KST_PLAN_OK.

A job whose begin call returned a final result gives that result. KST_PLAN_ERR_ARG for NULL and for a job of zeroed memory.

Parameters
jobJob to advance.
budgetUnits to take.
Returns
KST_PLAN_BUSY while work is left, then the job result.

◆ kst_plan_units()

uint32_t kst_plan_units ( const kst_plan_job_t * job)

Units the job has taken so far.

Parameters
jobJob to read.
Returns
Units taken so far.

◆ pick()

unsigned pick ( kst_plan_job_t * j)
static

The register of j->todo, not tried at this depth, whose write leaves a valid image of the least authority; of two alike the first.

The group's count when there is none. A register whose write leaves an invalid image, or a set of registers known to have no order, is marked tried.

◆ regs_of()

const uint8_t * regs_of ( const kst_plan_job_t * j)
static

The register list of the group the job works on.

Parameters
jJob whose group is read.
Returns
k_position for group 0, else k_speed.

◆ search_level()

void search_level ( kst_plan_job_t * j)
static

One level of a depth-first search for an order of the registers in j->todo, bit i for the group's register i, in which every image is valid.

At most 7 levels deep; the levels are the entries of order, tried and held. No image is copied and no function calls itself: a write is tried in j->img and taken back.

◆ sound()

bool sound ( kst_plan_job_t * j,
unsigned todo )
static

Whether the image with the registers of todo still to write is valid; j->img is that image.

The image is a function of todo and, while the detour byte is in todo, of the detour value, so the verdict is kept per subset and an image is checked once.

◆ take_back()

void take_back ( kst_plan_job_t * j)
static

Take back the write at the depth below.

Its bit returns to the todo set.

◆ valid()

bool valid ( kst_plan_job_t * j)
static

Check the job's image against the hard rules it may not break.

Costs one unit.

Variable Documentation

◆ k_position

const uint8_t k_position[] = { 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D }
static

The registers whose hard rules read each other.

Every other register has rules that read that register alone.