#include "sim_hal.h" #include #include #include typedef struct { uint32_t used; char name[SIM_HAL_NAME_CAPACITY]; sim_hal_value value; } pin_entry; typedef struct { uint32_t used; char name[SIM_HAL_NAME_CAPACITY]; sim_hal_value value; } param_entry; typedef struct { uint32_t used; char name[SIM_HAL_NAME_CAPACITY]; uint32_t period; } thread_entry; typedef struct { uint32_t used; char name[SIM_HAL_NAME_CAPACITY]; uint32_t thread; uint32_t calls; uint32_t fail; uint32_t component_index; uint32_t component_kind; } function_entry; typedef struct { uint32_t used; char instance[SIM_HAL_NAME_CAPACITY]; char type[SIM_HAL_NAME_CAPACITY]; uint32_t kind; uint32_t function_index; double memory[4]; uint32_t flags[4]; } component_entry; typedef struct { uint32_t used; uint32_t source; uint32_t target; } link_entry; enum { SIM_HAL_COMPONENT_NONE = 0, SIM_HAL_COMPONENT_COREXY = 1, SIM_HAL_COMPONENT_CONSTANT, SIM_HAL_COMPONENT_AND2, SIM_HAL_COMPONENT_OR2, SIM_HAL_COMPONENT_XOR2, SIM_HAL_COMPONENT_NOT, SIM_HAL_COMPONENT_ABS, SIM_HAL_COMPONENT_ABS_S32, SIM_HAL_COMPONENT_ABS_S64, SIM_HAL_COMPONENT_LOWPASS, SIM_HAL_COMPONENT_DDT, SIM_HAL_COMPONENT_INTEG, SIM_HAL_COMPONENT_MUX2, SIM_HAL_COMPONENT_SUM2, SIM_HAL_COMPONENT_MULT2, SIM_HAL_COMPONENT_DIV2, SIM_HAL_COMPONENT_HYPOT, SIM_HAL_COMPONENT_NEAR, SIM_HAL_COMPONENT_COMP, SIM_HAL_COMPONENT_INVERT, SIM_HAL_COMPONENT_EDGE, SIM_HAL_COMPONENT_TOGGLE, SIM_HAL_COMPONENT_ESTOP_LATCH }; typedef struct { uint32_t tick; pin_entry pins[SIM_HAL_PIN_CAPACITY]; param_entry params[SIM_HAL_PARAM_CAPACITY]; thread_entry threads[SIM_HAL_THREAD_CAPACITY]; function_entry functions[SIM_HAL_FUNCTION_CAPACITY]; component_entry components[SIM_HAL_COMPONENT_CAPACITY]; link_entry links[256U]; } hal_state; static hal_state state; static int valid_name(const char *name) { return name != NULL && name[0] != '\0' && strlen(name) < SIM_HAL_NAME_CAPACITY; } static int find_pin(const char *name) { uint32_t index; for (index = 0; index < SIM_HAL_PIN_CAPACITY; ++index) { if (state.pins[index].used && strcmp(state.pins[index].name, name) == 0) return (int)index; } return -1; } static int find_param(const char *name) { uint32_t index; for (index = 0; index < SIM_HAL_PARAM_CAPACITY; ++index) { if (state.params[index].used && strcmp(state.params[index].name, name) == 0) return (int)index; } return -1; } static int find_thread(const char *name) { uint32_t index; for (index = 0; index < SIM_HAL_THREAD_CAPACITY; ++index) { if (state.threads[index].used && strcmp(state.threads[index].name, name) == 0) return (int)index; } return -1; } static int find_function(const char *name) { uint32_t index; for (index = 0; index < SIM_HAL_FUNCTION_CAPACITY; ++index) { if (state.functions[index].used && strcmp(state.functions[index].name, name) == 0) return (int)index; } return -1; } static int find_component(const char *instance) { uint32_t index; for (index = 0; index < SIM_HAL_COMPONENT_CAPACITY; ++index) { if (state.components[index].used && strcmp(state.components[index].instance, instance) == 0) return (int)index; } return -1; } static int finite_value(sim_hal_value value) { return value.type != SIM_HAL_FLOAT || isfinite((double)value.value.real); } static int type_from_name(const char *name, uint32_t *type) { if (name == NULL || type == NULL) return 0; if (strcmp(name, "bit") == 0) *type = SIM_HAL_BIT; else if (strcmp(name, "s32") == 0 || strcmp(name, "signed") == 0) *type = SIM_HAL_S32; else if (strcmp(name, "u32") == 0 || strcmp(name, "unsigned") == 0) *type = SIM_HAL_U32; else if (strcmp(name, "float") == 0) *type = SIM_HAL_FLOAT; else return 0; return 1; } void sim_hal_reset(void) { memset(&state, 0, sizeof(state)); } int32_t sim_hal_register_pin(const char *name, uint32_t type) { uint32_t index; if (!valid_name(name) || type < SIM_HAL_BIT || type > SIM_HAL_FLOAT) return SIM_HAL_ERR_ARGUMENT; if (find_pin(name) >= 0) return SIM_HAL_ERR_DUPLICATE; for (index = 0; index < SIM_HAL_PIN_CAPACITY; ++index) { if (!state.pins[index].used) { state.pins[index].used = 1; strcpy(state.pins[index].name, name); state.pins[index].value.type = type; return SIM_HAL_OK; } } return SIM_HAL_ERR_FULL; } int32_t sim_hal_set_pin(const char *name, sim_hal_value value) { int index = find_pin(name); if (index < 0) return SIM_HAL_ERR_NOT_FOUND; if (value.type != state.pins[index].value.type) return SIM_HAL_ERR_TYPE; if (value.type == SIM_HAL_BIT && value.value.bit > 1U) return SIM_HAL_ERR_RANGE; if (!finite_value(value)) return SIM_HAL_ERR_RANGE; state.pins[index].value = value; return SIM_HAL_OK; } int32_t sim_hal_set_pin_scalar(const char *name, uint32_t type, double value) { sim_hal_value scalar = {0}; scalar.type = type; if (type == SIM_HAL_BIT) scalar.value.bit = value != 0.0; else if (type == SIM_HAL_S32) scalar.value.s32 = (int32_t)value; else if (type == SIM_HAL_U32) scalar.value.u32 = (uint32_t)value; else if (type == SIM_HAL_FLOAT) scalar.value.real = (float)value; else return SIM_HAL_ERR_TYPE; return sim_hal_set_pin(name, scalar); } int32_t sim_hal_get_pin(const char *name, sim_hal_value *out) { int index = find_pin(name); if (index < 0 || out == NULL) return index < 0 ? SIM_HAL_ERR_NOT_FOUND : SIM_HAL_ERR_ARGUMENT; *out = state.pins[index].value; return SIM_HAL_OK; } int32_t sim_hal_set_param(const char *name, sim_hal_value value) { int index = find_param(name); if (index < 0) return SIM_HAL_ERR_NOT_FOUND; if (value.type != state.params[index].value.type) return SIM_HAL_ERR_TYPE; if (value.type == SIM_HAL_BIT && value.value.bit > 1U) return SIM_HAL_ERR_RANGE; if (!finite_value(value)) return SIM_HAL_ERR_RANGE; state.params[index].value = value; return SIM_HAL_OK; } int32_t sim_hal_set_param_scalar(const char *name, uint32_t type, double value) { sim_hal_value scalar = {0}; scalar.type = type; if (type == SIM_HAL_BIT) scalar.value.bit = value != 0.0; else if (type == SIM_HAL_S32) scalar.value.s32 = (int32_t)value; else if (type == SIM_HAL_U32) scalar.value.u32 = (uint32_t)value; else if (type == SIM_HAL_FLOAT) scalar.value.real = (float)value; else return SIM_HAL_ERR_TYPE; return sim_hal_set_param(name, scalar); } int32_t sim_hal_get_param(const char *name, sim_hal_value *out) { int index = find_param(name); if (index < 0 || out == NULL) return index < 0 ? SIM_HAL_ERR_NOT_FOUND : SIM_HAL_ERR_ARGUMENT; *out = state.params[index].value; return SIM_HAL_OK; } int32_t sim_hal_connect(const char *source, const char *target) { int source_index = find_pin(source); int target_index = find_pin(target); uint32_t link_index; if (source_index < 0 || target_index < 0) return SIM_HAL_ERR_NOT_FOUND; if (state.pins[source_index].value.type != state.pins[target_index].value.type) return SIM_HAL_ERR_CONNECTION; for (link_index = 0; link_index < 256U; ++link_index) { if (state.links[link_index].used && state.links[link_index].source == (uint32_t)source_index && state.links[link_index].target == (uint32_t)target_index) return SIM_HAL_ERR_DUPLICATE; } for (link_index = 0; link_index < 256U; ++link_index) { if (!state.links[link_index].used) { state.links[link_index].used = 1U; state.links[link_index].source = (uint32_t)source_index; state.links[link_index].target = (uint32_t)target_index; break; } } if (link_index == 256U) return SIM_HAL_ERR_FULL; state.pins[target_index].value = state.pins[source_index].value; return SIM_HAL_OK; } int32_t sim_hal_register_thread(const char *name, uint32_t period_ticks) { uint32_t index; if (!valid_name(name) || period_ticks == 0U || period_ticks > 1000000U) return SIM_HAL_ERR_RANGE; if (find_thread(name) >= 0) return SIM_HAL_ERR_DUPLICATE; for (index = 0; index < SIM_HAL_THREAD_CAPACITY; ++index) { if (!state.threads[index].used) { state.threads[index].used = 1; strcpy(state.threads[index].name, name); state.threads[index].period = period_ticks; return SIM_HAL_OK; } } return SIM_HAL_ERR_FULL; } int32_t sim_hal_register_function(const char *thread, const char *name) { uint32_t index; int thread_index = find_thread(thread); if (thread_index < 0) return SIM_HAL_ERR_NOT_FOUND; if (!valid_name(name)) return SIM_HAL_ERR_ARGUMENT; if (find_function(name) >= 0) return SIM_HAL_ERR_DUPLICATE; for (index = 0; index < SIM_HAL_FUNCTION_CAPACITY; ++index) { if (!state.functions[index].used) { state.functions[index].used = 1; strcpy(state.functions[index].name, name); state.functions[index].thread = (uint32_t)thread_index; return SIM_HAL_OK; } } return SIM_HAL_ERR_FULL; } typedef struct { const char *name; uint32_t type; } component_pin_spec; typedef struct { const char *name; uint32_t type; sim_hal_value initial; } component_param_spec; typedef struct { uint32_t kind; const component_pin_spec *pins; uint32_t pin_count; const component_param_spec *params; uint32_t param_count; } component_spec; static const component_pin_spec pins_corexy[] = { {"j0-motor-pos-cmd", SIM_HAL_FLOAT}, {"j1-motor-pos-cmd", SIM_HAL_FLOAT}, {"alpha-cmd", SIM_HAL_FLOAT}, {"beta-cmd", SIM_HAL_FLOAT}, {"alpha-fb", SIM_HAL_FLOAT}, {"beta-fb", SIM_HAL_FLOAT}, {"j0-motor-pos-fb", SIM_HAL_FLOAT}, {"j1-motor-pos-fb", SIM_HAL_FLOAT} }; static const component_pin_spec pins_bit2[] = { {"in0", SIM_HAL_BIT}, {"in1", SIM_HAL_BIT}, {"out", SIM_HAL_BIT} }; static const component_pin_spec pins_bit1[] = {{"in", SIM_HAL_BIT}, {"out", SIM_HAL_BIT}}; static const component_pin_spec pins_abs[] = { {"in", SIM_HAL_FLOAT}, {"out", SIM_HAL_FLOAT}, {"sign", SIM_HAL_BIT}, {"is-positive", SIM_HAL_BIT}, {"is-negative", SIM_HAL_BIT} }; static const component_pin_spec pins_abs_s32[] = { {"in", SIM_HAL_S32}, {"out", SIM_HAL_S32}, {"sign", SIM_HAL_BIT}, {"is-positive", SIM_HAL_BIT}, {"is-negative", SIM_HAL_BIT} }; static const component_pin_spec pins_float2[] = { {"in0", SIM_HAL_FLOAT}, {"in1", SIM_HAL_FLOAT}, {"out", SIM_HAL_FLOAT} }; static const component_pin_spec pins_lowpass[] = { {"in", SIM_HAL_FLOAT}, {"out", SIM_HAL_FLOAT}, {"load", SIM_HAL_BIT} }; static const component_pin_spec pins_ddt[] = {{"in", SIM_HAL_FLOAT}, {"out", SIM_HAL_FLOAT}}; static const component_pin_spec pins_integ[] = { {"in", SIM_HAL_FLOAT}, {"gain", SIM_HAL_FLOAT}, {"out", SIM_HAL_FLOAT}, {"reset", SIM_HAL_BIT}, {"max", SIM_HAL_FLOAT}, {"min", SIM_HAL_FLOAT} }; static const component_pin_spec pins_mux2[] = { {"sel", SIM_HAL_BIT}, {"out", SIM_HAL_FLOAT}, {"in1", SIM_HAL_FLOAT}, {"in0", SIM_HAL_FLOAT} }; static const component_pin_spec pins_sum2[] = { {"in0", SIM_HAL_FLOAT}, {"in1", SIM_HAL_FLOAT}, {"out", SIM_HAL_FLOAT} }; static const component_pin_spec pins_hypot[] = { {"in0", SIM_HAL_FLOAT}, {"in1", SIM_HAL_FLOAT}, {"in2", SIM_HAL_FLOAT}, {"out", SIM_HAL_FLOAT} }; static const component_pin_spec pins_near[] = { {"in1", SIM_HAL_FLOAT}, {"in2", SIM_HAL_FLOAT}, {"out", SIM_HAL_BIT} }; static const component_pin_spec pins_comp[] = { {"in0", SIM_HAL_FLOAT}, {"in1", SIM_HAL_FLOAT}, {"out", SIM_HAL_BIT}, {"equal", SIM_HAL_BIT} }; static const component_pin_spec pins_edge[] = { {"in", SIM_HAL_BIT}, {"out", SIM_HAL_BIT}, {"out-invert", SIM_HAL_BIT} }; static const component_pin_spec pins_estop[] = { {"ok-in", SIM_HAL_BIT}, {"fault-in", SIM_HAL_BIT}, {"reset", SIM_HAL_BIT}, {"ok-out", SIM_HAL_BIT}, {"fault-out", SIM_HAL_BIT}, {"watchdog", SIM_HAL_BIT} }; static const component_pin_spec pins_constant[] = {{"out", SIM_HAL_FLOAT}}; static const component_pin_spec pins_invert[] = {{"in", SIM_HAL_FLOAT}, {"out", SIM_HAL_FLOAT}}; static const component_pin_spec pins_toggle[] = {{"in", SIM_HAL_BIT}, {"out", SIM_HAL_BIT}}; static const component_param_spec param_constant[] = { {"value", SIM_HAL_FLOAT, {SIM_HAL_FLOAT, {.real = 0.0f}}} }; static const component_param_spec param_lowpass[] = { {"gain", SIM_HAL_FLOAT, {SIM_HAL_FLOAT, {.real = 0.0f}}} }; static const component_param_spec param_sum2[] = { {"gain0", SIM_HAL_FLOAT, {SIM_HAL_FLOAT, {.real = 1.0f}}}, {"gain1", SIM_HAL_FLOAT, {SIM_HAL_FLOAT, {.real = 1.0f}}}, {"offset", SIM_HAL_FLOAT, {SIM_HAL_FLOAT, {.real = 0.0f}}} }; static const component_param_spec param_div2[] = { {"deadband", SIM_HAL_FLOAT, {SIM_HAL_FLOAT, {.real = 0.0f}}} }; static const component_param_spec param_near[] = { {"scale", SIM_HAL_FLOAT, {SIM_HAL_FLOAT, {.real = 1.0f}}}, {"difference", SIM_HAL_FLOAT, {SIM_HAL_FLOAT, {.real = 0.0f}}} }; static const component_param_spec param_comp[] = { {"hyst", SIM_HAL_FLOAT, {SIM_HAL_FLOAT, {.real = 0.0f}}} }; static const component_param_spec param_invert[] = { {"deadband", SIM_HAL_FLOAT, {SIM_HAL_FLOAT, {.real = 0.0f}}} }; static const component_param_spec param_edge[] = { {"both", SIM_HAL_BIT, {SIM_HAL_BIT, {.bit = 0U}}}, {"in-edge", SIM_HAL_BIT, {SIM_HAL_BIT, {.bit = 1U}}}, {"out-width-ns", SIM_HAL_S32, {SIM_HAL_S32, {.s32 = 0}}}, {"time-left-ns", SIM_HAL_S32, {SIM_HAL_S32, {.s32 = 0}}}, {"last-in", SIM_HAL_BIT, {SIM_HAL_BIT, {.bit = 0U}}} }; static const component_param_spec param_toggle[] = { {"debounce", SIM_HAL_U32, {SIM_HAL_U32, {.u32 = 2U}}} }; static int component_spec_for(const char *type, component_spec *out) { if (type == NULL || out == NULL) return 0; memset(out, 0, sizeof(*out)); #define SPEC(kind_value, pin_array) do { out->kind = (kind_value); out->pins = (pin_array); out->pin_count = (uint32_t)(sizeof(pin_array) / sizeof((pin_array)[0])); return 1; } while (0) if (strcmp(type, "corexy_by_hal") == 0) SPEC(SIM_HAL_COMPONENT_COREXY, pins_corexy); if (strcmp(type, "constant") == 0) { out->kind = SIM_HAL_COMPONENT_CONSTANT; out->pins = pins_constant; out->pin_count = 1U; out->params = param_constant; out->param_count = 1U; return 1; } if (strcmp(type, "and2") == 0) SPEC(SIM_HAL_COMPONENT_AND2, pins_bit2); if (strcmp(type, "or2") == 0) SPEC(SIM_HAL_COMPONENT_OR2, pins_bit2); if (strcmp(type, "xor2") == 0) SPEC(SIM_HAL_COMPONENT_XOR2, pins_bit2); if (strcmp(type, "not") == 0 || strcmp(type, "invert_bit") == 0) SPEC(SIM_HAL_COMPONENT_NOT, pins_bit1); if (strcmp(type, "abs") == 0) SPEC(SIM_HAL_COMPONENT_ABS, pins_abs); if (strcmp(type, "abs_s32") == 0) SPEC(SIM_HAL_COMPONENT_ABS_S32, pins_abs_s32); if (strcmp(type, "lowpass") == 0) { out->kind = SIM_HAL_COMPONENT_LOWPASS; out->pins = pins_lowpass; out->pin_count = 3U; out->params = param_lowpass; out->param_count = 1U; return 1; } if (strcmp(type, "ddt") == 0) SPEC(SIM_HAL_COMPONENT_DDT, pins_ddt); if (strcmp(type, "integ") == 0) SPEC(SIM_HAL_COMPONENT_INTEG, pins_integ); if (strcmp(type, "mux2") == 0) SPEC(SIM_HAL_COMPONENT_MUX2, pins_mux2); if (strcmp(type, "sum2") == 0) { out->kind = SIM_HAL_COMPONENT_SUM2; out->pins = pins_sum2; out->pin_count = 3U; out->params = param_sum2; out->param_count = 3U; return 1; } if (strcmp(type, "mult2") == 0) SPEC(SIM_HAL_COMPONENT_MULT2, pins_float2); if (strcmp(type, "div2") == 0) { out->kind = SIM_HAL_COMPONENT_DIV2; out->pins = pins_float2; out->pin_count = 3U; out->params = param_div2; out->param_count = 1U; return 1; } if (strcmp(type, "hypot") == 0) SPEC(SIM_HAL_COMPONENT_HYPOT, pins_hypot); if (strcmp(type, "near") == 0) { out->kind = SIM_HAL_COMPONENT_NEAR; out->pins = pins_near; out->pin_count = 3U; out->params = param_near; out->param_count = 2U; return 1; } if (strcmp(type, "comp") == 0) { out->kind = SIM_HAL_COMPONENT_COMP; out->pins = pins_comp; out->pin_count = 4U; out->params = param_comp; out->param_count = 1U; return 1; } if (strcmp(type, "invert") == 0) { out->kind = SIM_HAL_COMPONENT_INVERT; out->pins = pins_invert; out->pin_count = 2U; out->params = param_invert; out->param_count = 1U; return 1; } if (strcmp(type, "edge") == 0) { out->kind = SIM_HAL_COMPONENT_EDGE; out->pins = pins_edge; out->pin_count = 3U; out->params = param_edge; out->param_count = 5U; return 1; } if (strcmp(type, "toggle") == 0) { out->kind = SIM_HAL_COMPONENT_TOGGLE; out->pins = pins_toggle; out->pin_count = 2U; out->params = param_toggle; out->param_count = 1U; return 1; } if (strcmp(type, "estop_latch") == 0) SPEC(SIM_HAL_COMPONENT_ESTOP_LATCH, pins_estop); #undef SPEC return 0; } static int32_t qualified_name(const char *instance, const char *suffix, char *out) { size_t left; size_t right; if (!valid_name(instance) || suffix == NULL || out == NULL) return SIM_HAL_ERR_ARGUMENT; left = strlen(instance); right = strlen(suffix); if (left + 1U + right >= SIM_HAL_NAME_CAPACITY) return SIM_HAL_ERR_RANGE; memcpy(out, instance, left); out[left] = '.'; memcpy(out + left + 1U, suffix, right + 1U); return SIM_HAL_OK; } static int32_t register_param_internal(const char *name, sim_hal_value value) { uint32_t index; if (!valid_name(name) || !finite_value(value)) return SIM_HAL_ERR_ARGUMENT; if (find_param(name) >= 0) return SIM_HAL_ERR_DUPLICATE; for (index = 0; index < SIM_HAL_PARAM_CAPACITY; ++index) { if (!state.params[index].used) { state.params[index].used = 1U; strcpy(state.params[index].name, name); state.params[index].value = value; return SIM_HAL_OK; } } return SIM_HAL_ERR_FULL; } static void component_initialize_defaults(const component_entry *component); int32_t sim_hal_register_component(const char *instance, const char *type, const char *thread) { component_spec spec; hal_state before; char qualified[SIM_HAL_NAME_CAPACITY]; uint32_t index; int component_index; int function_index; int32_t result; if (!valid_name(instance) || !valid_name(type) || !valid_name(thread)) return SIM_HAL_ERR_ARGUMENT; if (!component_spec_for(type, &spec)) return SIM_HAL_ERR_REAL_EXCLUDED; if (find_component(instance) >= 0 || find_function(instance) >= 0) return SIM_HAL_ERR_DUPLICATE; if (find_thread(thread) < 0) return SIM_HAL_ERR_NOT_FOUND; memcpy(&before, &state, sizeof(state)); component_index = -1; for (index = 0; index < SIM_HAL_COMPONENT_CAPACITY; ++index) { if (!state.components[index].used) { component_index = (int)index; break; } } if (component_index < 0) return SIM_HAL_ERR_FULL; state.components[component_index].used = 1U; strcpy(state.components[component_index].instance, instance); strcpy(state.components[component_index].type, type); state.components[component_index].kind = spec.kind; state.components[component_index].function_index = 0U; for (index = 0; index < spec.pin_count; ++index) { result = qualified_name(instance, spec.pins[index].name, qualified); if (result != SIM_HAL_OK || (result = sim_hal_register_pin(qualified, spec.pins[index].type)) != SIM_HAL_OK) { memcpy(&state, &before, sizeof(state)); return result; } } for (index = 0; index < spec.param_count; ++index) { result = qualified_name(instance, spec.params[index].name, qualified); if (result != SIM_HAL_OK || (result = register_param_internal(qualified, spec.params[index].initial)) != SIM_HAL_OK) { memcpy(&state, &before, sizeof(state)); return result; } } result = sim_hal_register_function(thread, instance); if (result != SIM_HAL_OK) { memcpy(&state, &before, sizeof(state)); return result; } function_index = find_function(instance); state.components[component_index].function_index = (uint32_t)function_index; state.functions[function_index].component_index = (uint32_t)component_index; state.functions[function_index].component_kind = spec.kind; component_initialize_defaults(&state.components[component_index]); return SIM_HAL_OK; } static int32_t corexy_pin_names(const char *component, char names[8][SIM_HAL_NAME_CAPACITY]) { static const char *suffixes[8] = { ".j0-motor-pos-cmd", ".j1-motor-pos-cmd", ".alpha-cmd", ".beta-cmd", ".alpha-fb", ".beta-fb", ".j0-motor-pos-fb", ".j1-motor-pos-fb", }; uint32_t index; if (!valid_name(component)) return SIM_HAL_ERR_ARGUMENT; for (index = 0; index < 8; ++index) { size_t prefix = strlen(component); size_t suffix = strlen(suffixes[index]); if (prefix + suffix >= SIM_HAL_NAME_CAPACITY) return SIM_HAL_ERR_RANGE; memcpy(names[index], component, prefix); memcpy(names[index] + prefix, suffixes[index], suffix + 1); } return SIM_HAL_OK; } int32_t sim_hal_register_corexy_by_hal(const char *component, const char *thread) { char names[8][SIM_HAL_NAME_CAPACITY]; uint32_t index; int32_t result; if (component == NULL || thread == NULL) return SIM_HAL_ERR_ARGUMENT; result = corexy_pin_names(component, names); if (result != SIM_HAL_OK) return result; for (index = 0; index < 8; ++index) { if (find_pin(names[index]) >= 0) return SIM_HAL_ERR_DUPLICATE; } if (find_function(component) >= 0) return SIM_HAL_ERR_DUPLICATE; for (index = 0; index < 8; ++index) { result = sim_hal_register_pin(names[index], SIM_HAL_FLOAT); if (result != SIM_HAL_OK) return result; } result = sim_hal_register_function(thread, component); if (result != SIM_HAL_OK) return result; state.functions[find_function(component)].component_index = UINT32_MAX; state.functions[find_function(component)].component_kind = SIM_HAL_COMPONENT_COREXY; return SIM_HAL_OK; } static int32_t float_pin(const char *name, float *value) { sim_hal_value pin = {0}; int32_t result = sim_hal_get_pin(name, &pin); if (result != SIM_HAL_OK) return result; if (pin.type != SIM_HAL_FLOAT) return SIM_HAL_ERR_TYPE; *value = pin.value.real; return SIM_HAL_OK; } static int32_t set_float_pin(const char *name, float value) { sim_hal_value pin = {SIM_HAL_FLOAT, {.real = value}}; return sim_hal_set_pin(name, pin); } int32_t sim_hal_corexy_transform(const char *component) { char names[8][SIM_HAL_NAME_CAPACITY]; float j0_cmd, j1_cmd, alpha_fb, beta_fb; int32_t result = corexy_pin_names(component, names); if (result != SIM_HAL_OK) return result; result = float_pin(names[0], &j0_cmd); if (result != SIM_HAL_OK) return result; result = float_pin(names[1], &j1_cmd); if (result != SIM_HAL_OK) return result; result = float_pin(names[4], &alpha_fb); if (result != SIM_HAL_OK) return result; result = float_pin(names[5], &beta_fb); if (result != SIM_HAL_OK) return result; result = set_float_pin(names[2], 0.5f * (j0_cmd + j1_cmd)); if (result != SIM_HAL_OK) return result; result = set_float_pin(names[3], 0.5f * (j0_cmd - j1_cmd)); if (result != SIM_HAL_OK) return result; result = set_float_pin(names[6], alpha_fb + beta_fb); if (result != SIM_HAL_OK) return result; return set_float_pin(names[7], alpha_fb - beta_fb); } static int32_t component_pin_name(const component_entry *component, const char *suffix, char out[SIM_HAL_NAME_CAPACITY]) { return qualified_name(component->instance, suffix, out); } static int32_t component_float_pin(const component_entry *component, const char *suffix, float *value) { char name[SIM_HAL_NAME_CAPACITY]; sim_hal_value pin = {0}; int32_t result = component_pin_name(component, suffix, name); if (result != SIM_HAL_OK) return result; result = sim_hal_get_pin(name, &pin); if (result != SIM_HAL_OK) return result; if (pin.type != SIM_HAL_FLOAT || !finite_value(pin)) return SIM_HAL_ERR_TYPE; *value = pin.value.real; return SIM_HAL_OK; } static int32_t component_bit_pin(const component_entry *component, const char *suffix, uint32_t *value) { char name[SIM_HAL_NAME_CAPACITY]; sim_hal_value pin = {0}; int32_t result = component_pin_name(component, suffix, name); if (result != SIM_HAL_OK) return result; result = sim_hal_get_pin(name, &pin); if (result != SIM_HAL_OK) return result; if (pin.type != SIM_HAL_BIT) return SIM_HAL_ERR_TYPE; *value = pin.value.bit; return SIM_HAL_OK; } static int32_t component_s32_pin(const component_entry *component, const char *suffix, int32_t *value) { char name[SIM_HAL_NAME_CAPACITY]; sim_hal_value pin = {0}; int32_t result = component_pin_name(component, suffix, name); if (result != SIM_HAL_OK) return result; result = sim_hal_get_pin(name, &pin); if (result != SIM_HAL_OK) return result; if (pin.type != SIM_HAL_S32) return SIM_HAL_ERR_TYPE; *value = pin.value.s32; return SIM_HAL_OK; } static int32_t component_write_float(const component_entry *component, const char *suffix, float value) { char name[SIM_HAL_NAME_CAPACITY]; int32_t result = component_pin_name(component, suffix, name); if (result != SIM_HAL_OK) return result; return sim_hal_set_pin(name, (sim_hal_value){SIM_HAL_FLOAT, {.real = value}}); } static int32_t component_write_bit(const component_entry *component, const char *suffix, uint32_t value) { char name[SIM_HAL_NAME_CAPACITY]; int32_t result = component_pin_name(component, suffix, name); if (result != SIM_HAL_OK) return result; return sim_hal_set_pin(name, (sim_hal_value){SIM_HAL_BIT, {.bit = value ? 1U : 0U}}); } static int32_t component_write_s32(const component_entry *component, const char *suffix, int32_t value) { char name[SIM_HAL_NAME_CAPACITY]; int32_t result = component_pin_name(component, suffix, name); if (result != SIM_HAL_OK) return result; return sim_hal_set_pin(name, (sim_hal_value){SIM_HAL_S32, {.s32 = value}}); } static int32_t component_float_param(const component_entry *component, const char *suffix, float *value) { char name[SIM_HAL_NAME_CAPACITY]; sim_hal_value param = {0}; int32_t result = component_pin_name(component, suffix, name); if (result != SIM_HAL_OK) return result; result = sim_hal_get_param(name, ¶m); if (result != SIM_HAL_OK) return result; if (param.type != SIM_HAL_FLOAT || !finite_value(param)) return SIM_HAL_ERR_TYPE; *value = param.value.real; return SIM_HAL_OK; } static int32_t component_bit_param(const component_entry *component, const char *suffix, uint32_t *value) { char name[SIM_HAL_NAME_CAPACITY]; sim_hal_value param = {0}; int32_t result = component_pin_name(component, suffix, name); if (result != SIM_HAL_OK) return result; result = sim_hal_get_param(name, ¶m); if (result != SIM_HAL_OK) return result; if (param.type != SIM_HAL_BIT) return SIM_HAL_ERR_TYPE; *value = param.value.bit; return SIM_HAL_OK; } static int32_t component_s32_param(const component_entry *component, const char *suffix, int32_t *value) { char name[SIM_HAL_NAME_CAPACITY]; sim_hal_value param = {0}; int32_t result = component_pin_name(component, suffix, name); if (result != SIM_HAL_OK) return result; result = sim_hal_get_param(name, ¶m); if (result != SIM_HAL_OK) return result; if (param.type != SIM_HAL_S32) return SIM_HAL_ERR_TYPE; *value = param.value.s32; return SIM_HAL_OK; } static int32_t component_write_bit_param(const component_entry *component, const char *suffix, uint32_t value) { char name[SIM_HAL_NAME_CAPACITY]; int32_t result = component_pin_name(component, suffix, name); if (result != SIM_HAL_OK) return result; return sim_hal_set_param(name, (sim_hal_value){SIM_HAL_BIT, {.bit = value ? 1U : 0U}}); } static void component_initialize_defaults(const component_entry *component) { if (component->kind == SIM_HAL_COMPONENT_INTEG) { (void)component_write_float(component, "gain", 1.0f); (void)component_write_float(component, "max", 1.0e20f); (void)component_write_float(component, "min", -1.0e20f); } else if (component->kind == SIM_HAL_COMPONENT_ESTOP_LATCH) { (void)component_write_bit(component, "ok-in", 1U); (void)component_write_bit(component, "fault-in", 0U); (void)component_write_bit(component, "ok-out", 0U); (void)component_write_bit(component, "fault-out", 1U); } } static int32_t component_step(component_entry *component) { float a, b, c, value; uint32_t bit, other; int32_t integer; int32_t result; if (component->kind == SIM_HAL_COMPONENT_COREXY) return sim_hal_corexy_transform(component->instance); switch (component->kind) { case SIM_HAL_COMPONENT_CONSTANT: result = component_float_param(component, "value", &value); if (result != SIM_HAL_OK) return result; return component_write_float(component, "out", value); case SIM_HAL_COMPONENT_AND2: case SIM_HAL_COMPONENT_OR2: case SIM_HAL_COMPONENT_XOR2: result = component_bit_pin(component, "in0", &bit); if (result != SIM_HAL_OK) return result; result = component_bit_pin(component, "in1", &other); if (result != SIM_HAL_OK) return result; value = (float)(component->kind == SIM_HAL_COMPONENT_AND2 ? (bit && other) : component->kind == SIM_HAL_COMPONENT_OR2 ? (bit || other) : (bit != other)); return component_write_bit(component, "out", value != 0.0f); case SIM_HAL_COMPONENT_NOT: result = component_bit_pin(component, "in", &bit); if (result != SIM_HAL_OK) return result; return component_write_bit(component, "out", !bit); case SIM_HAL_COMPONENT_ABS: result = component_float_pin(component, "in", &a); if (result != SIM_HAL_OK) return result; result = component_write_float(component, "out", fabsf(a)); if (result != SIM_HAL_OK) return result; result = component_write_bit(component, "sign", a < 0.0f); if (result != SIM_HAL_OK) return result; result = component_write_bit(component, "is-positive", a > 0.000001f); if (result != SIM_HAL_OK) return result; return component_write_bit(component, "is-negative", a < -0.000001f); case SIM_HAL_COMPONENT_ABS_S32: case SIM_HAL_COMPONENT_ABS_S64: result = component_s32_pin(component, "in", &integer); if (result != SIM_HAL_OK) return result; if (integer == INT32_MIN) return SIM_HAL_ERR_RANGE; result = component_write_s32(component, "out", integer < 0 ? -integer : integer); if (result != SIM_HAL_OK) return result; result = component_write_bit(component, "sign", integer < 0); if (result != SIM_HAL_OK) return result; result = component_write_bit(component, "is-positive", integer > 0); if (result != SIM_HAL_OK) return result; return component_write_bit(component, "is-negative", integer < 0); case SIM_HAL_COMPONENT_LOWPASS: result = component_float_pin(component, "in", &a); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, "out", &b); if (result != SIM_HAL_OK) return result; result = component_bit_pin(component, "load", &bit); if (result != SIM_HAL_OK) return result; result = component_float_param(component, "gain", &c); if (result != SIM_HAL_OK) return result; return component_write_float(component, "out", bit ? a : b + (a - b) * c); case SIM_HAL_COMPONENT_DDT: result = component_float_pin(component, "in", &a); if (result != SIM_HAL_OK) return result; result = component_write_float(component, "out", (float)(a - component->memory[0])); component->memory[0] = a; return result; case SIM_HAL_COMPONENT_INTEG: result = component_float_pin(component, "in", &a); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, "gain", &b); if (result != SIM_HAL_OK) return result; result = component_bit_pin(component, "reset", &bit); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, "max", &c); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, "min", &value); if (result != SIM_HAL_OK) return result; if (bit) component->memory[0] = 0.0; else component->memory[0] += (double)a * (double)b; if (component->memory[0] > c) component->memory[0] = c; if (component->memory[0] < value) component->memory[0] = value; return component_write_float(component, "out", (float)component->memory[0]); case SIM_HAL_COMPONENT_MUX2: result = component_bit_pin(component, "sel", &bit); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, bit ? "in1" : "in0", &a); if (result != SIM_HAL_OK) return result; return component_write_float(component, "out", a); case SIM_HAL_COMPONENT_SUM2: result = component_float_pin(component, "in0", &a); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, "in1", &b); if (result != SIM_HAL_OK) return result; result = component_float_param(component, "gain0", &c); if (result != SIM_HAL_OK) return result; result = component_float_param(component, "gain1", &value); if (result != SIM_HAL_OK) return result; { float offset; result = component_float_param(component, "offset", &offset); if (result != SIM_HAL_OK) return result; return component_write_float(component, "out", a * c + b * value + offset); } case SIM_HAL_COMPONENT_MULT2: result = component_float_pin(component, "in0", &a); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, "in1", &b); if (result != SIM_HAL_OK) return result; return component_write_float(component, "out", a * b); case SIM_HAL_COMPONENT_DIV2: result = component_float_pin(component, "in0", &a); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, "in1", &b); if (result != SIM_HAL_OK) return result; result = component_float_param(component, "deadband", &c); if (result != SIM_HAL_OK) return result; if (c < 1e-12f) c = 1e-12f; if (fabsf(b) < c) b = b < 0.0f ? -c : c; return component_write_float(component, "out", a / b); case SIM_HAL_COMPONENT_HYPOT: result = component_float_pin(component, "in0", &a); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, "in1", &b); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, "in2", &c); if (result != SIM_HAL_OK) return result; return component_write_float(component, "out", sqrtf(a * a + b * b + c * c)); case SIM_HAL_COMPONENT_NEAR: result = component_float_pin(component, "in1", &a); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, "in2", &b); if (result != SIM_HAL_OK) return result; result = component_float_param(component, "scale", &c); if (result != SIM_HAL_OK) return result; result = component_float_param(component, "difference", &value); if (result != SIM_HAL_OK) return result; if (a < 0.0f) { a = -a; b = -b; } return component_write_bit(component, "out", ((c > 1.0f && a / c <= b && b <= a * c) || fabsf(a - b) <= value)); case SIM_HAL_COMPONENT_COMP: result = component_float_pin(component, "in0", &a); if (result != SIM_HAL_OK) return result; result = component_float_pin(component, "in1", &b); if (result != SIM_HAL_OK) return result; result = component_float_param(component, "hyst", &c); if (result != SIM_HAL_OK) return result; if (b - a < -0.5f * c) { result = component_write_bit(component, "out", 0U); if (result != SIM_HAL_OK) return result; return component_write_bit(component, "equal", 0U); } if (b - a > 0.5f * c) { result = component_write_bit(component, "out", 1U); if (result != SIM_HAL_OK) return result; return component_write_bit(component, "equal", 0U); } return component_write_bit(component, "equal", 1U); case SIM_HAL_COMPONENT_INVERT: result = component_float_pin(component, "in", &a); if (result != SIM_HAL_OK) return result; result = component_float_param(component, "deadband", &c); if (result != SIM_HAL_OK) return result; if (c < 1e-12f) c = 1e-12f; if (fabsf(a) < c) a = a < 0.0f ? -c : c; return component_write_float(component, "out", 1.0f / a); case SIM_HAL_COMPONENT_EDGE: result = component_bit_pin(component, "in", &bit); if (result != SIM_HAL_OK) return result; result = component_bit_param(component, "last-in", &other); if (result != SIM_HAL_OK) return result; if (component->flags[0] == 0U) { component->flags[0] = 1U; component->memory[0] = bit; return component_write_bit(component, "out-invert", 1U); } { uint32_t both, in_edge; result = component_bit_param(component, "both", &both); if (result != SIM_HAL_OK) return result; result = component_bit_param(component, "in-edge", &in_edge); if (result != SIM_HAL_OK) return result; uint32_t rise = bit && !other, fall = !bit && other; uint32_t hit = both ? (rise || fall) : (in_edge ? fall : rise); result = component_write_bit(component, "out", hit); if (result != SIM_HAL_OK) return result; result = component_write_bit(component, "out-invert", !hit); if (result != SIM_HAL_OK) return result; result = component_write_bit_param(component, "last-in", bit); component->memory[0] = bit; return result; } case SIM_HAL_COMPONENT_TOGGLE: result = component_bit_pin(component, "in", &bit); if (result != SIM_HAL_OK) return result; result = component_bit_pin(component, "out", &other); if (result != SIM_HAL_OK) return result; { sim_hal_value param = {0}; char name[SIM_HAL_NAME_CAPACITY]; component_pin_name(component, "debounce", name); result = sim_hal_get_param(name, ¶m); if (result != SIM_HAL_OK) return result; uint32_t debounce = param.value.u32 < 1U || param.value.u32 > 10000U ? 2U : param.value.u32; if (bit) { if (component->flags[0] < debounce) component->flags[0] += 1U; if (component->flags[0] >= debounce && component->flags[1] == 0U) { other = !other; component->flags[1] = 1U; } } else { component->flags[0] = 0U; component->flags[1] = 0U; } return component_write_bit(component, "out", other); } case SIM_HAL_COMPONENT_ESTOP_LATCH: result = component_bit_pin(component, "ok-in", &bit); if (result != SIM_HAL_OK) return result; result = component_bit_pin(component, "fault-in", &other); if (result != SIM_HAL_OK) return result; { uint32_t reset; result = component_bit_pin(component, "reset", &reset); if (result != SIM_HAL_OK) return result; if (!bit || other) { component->flags[0] = 0U; } else if (reset && component->flags[1] == 0U) component->flags[0] = 1U; component->flags[1] = reset; result = component_write_bit(component, "ok-out", component->flags[0]); if (result != SIM_HAL_OK) return result; result = component_write_bit(component, "fault-out", !component->flags[0]); if (result != SIM_HAL_OK) return result; if (component->flags[0]) component->flags[2] ^= 1U; return component_write_bit(component, "watchdog", component->flags[2]); } default: return SIM_HAL_ERR_REAL_EXCLUDED; } } int32_t sim_hal_set_function_failure(const char *name, uint32_t fail) { int index = find_function(name); if (index < 0) return SIM_HAL_ERR_NOT_FOUND; state.functions[index].fail = fail ? 1U : 0U; return SIM_HAL_OK; } int32_t sim_hal_step(uint32_t ticks) { hal_state before; uint64_t target; uint64_t tick; uint32_t function_index; if ((uint64_t)state.tick + (uint64_t)ticks > UINT32_MAX) return SIM_HAL_ERR_RANGE; memcpy(&before, &state, sizeof(state)); target = (uint64_t)state.tick + (uint64_t)ticks; for (tick = (uint64_t)state.tick + 1U; tick <= target; ++tick) { for (function_index = 0; function_index < 256U; ++function_index) { if (state.links[function_index].used) { uint32_t source = state.links[function_index].source; uint32_t target_pin = state.links[function_index].target; state.pins[target_pin].value = state.pins[source].value; } } for (function_index = 0; function_index < SIM_HAL_FUNCTION_CAPACITY; ++function_index) { function_entry *function = &state.functions[function_index]; if (!function->used || tick % state.threads[function->thread].period != 0U) continue; if (function->fail) { memcpy(&state, &before, sizeof(state)); return SIM_HAL_ERR_FUNCTION; } if (function->component_kind != SIM_HAL_COMPONENT_NONE) { int32_t result; if (function->component_kind == SIM_HAL_COMPONENT_COREXY && function->component_index == UINT32_MAX) result = sim_hal_corexy_transform(function->name); else if (function->component_index >= SIM_HAL_COMPONENT_CAPACITY || !state.components[function->component_index].used) result = SIM_HAL_ERR_NOT_FOUND; else result = component_step(&state.components[function->component_index]); if (result != SIM_HAL_OK) { memcpy(&state, &before, sizeof(state)); return SIM_HAL_ERR_FUNCTION; } } function->calls += 1U; } } state.tick = (uint32_t)target; return SIM_HAL_OK; } uint32_t sim_hal_tick(void) { return state.tick; } uint32_t sim_hal_function_calls(const char *name) { int index = find_function(name); return index < 0 ? 0U : state.functions[index].calls; } uint32_t sim_hal_pin_count(void) { uint32_t i, count = 0; for (i = 0; i < SIM_HAL_PIN_CAPACITY; ++i) count += state.pins[i].used; return count; } uint32_t sim_hal_thread_count(void) { uint32_t i, count = 0; for (i = 0; i < SIM_HAL_THREAD_CAPACITY; ++i) count += state.threads[i].used; return count; } uint32_t sim_hal_function_count(void) { uint32_t i, count = 0; for (i = 0; i < SIM_HAL_FUNCTION_CAPACITY; ++i) count += state.functions[i].used; return count; } uint32_t sim_hal_component_count(void) { uint32_t i, count = 0; for (i = 0; i < SIM_HAL_COMPONENT_CAPACITY; ++i) count += state.components[i].used; return count; } uint32_t sim_hal_param_count(void) { uint32_t i, count = 0; for (i = 0; i < SIM_HAL_PARAM_CAPACITY; ++i) count += state.params[i].used; return count; }