284 lines
17 KiB
C
284 lines
17 KiB
C
/**
|
||
* @file phi.h
|
||
* @brief T06215 injection-angle algorithm — compact public interface.
|
||
*
|
||
* AUTO-GENERATED by tools/extract_t06215_cal.py
|
||
* Source ROM: rom_eeprom_dump_0000-9FFF_424026.bin
|
||
* Bases: RWA4 = 0x9BD8, RWC6 = 0x7E56
|
||
*
|
||
* DO NOT EDIT -- regenerate with:
|
||
* python tools/extract_t06215_cal.py --target compact
|
||
*
|
||
* Companion to phi.c (single-translation-unit port of the per-function
|
||
* verbose tree under variants/T06215/src/) and the auto-generated
|
||
* phi_cal_tables.c. Callers see only this header; embedded
|
||
* `runtime_state_t` / `calibration_t` fields are IMPLEMENTATION DETAIL
|
||
* — touch only the outputs struct and the getter vtable.
|
||
*
|
||
* Lifecycle:
|
||
*
|
||
* phi_input_getters_t getters = { .get_rpm = ..., ... };
|
||
* phi_state_t state;
|
||
* phi_cal_t cal = phi_t06215_cal; // copy from auto-extracted master
|
||
* phi_outputs_t out;
|
||
*
|
||
* phi_init(&state, &cal, &getters); // boot-once
|
||
* for each tick:
|
||
* phi_service(&state, &cal, &out);
|
||
* use(out.target_inj_angle, ...);
|
||
*
|
||
* The producer chain runs in this fixed order each tick (see
|
||
* variants/T06211/docs/open-questions.md §4 / §5 — fused scheduler
|
||
* `FUN_698C` enclosing `0x6a3f LCALL FUN_7453` is authoritative):
|
||
*
|
||
* 1. CAN / external inputs pulled via the getter vtable.
|
||
* 2. compute_tein_overtemp_guard (FUN_5ca1) -> temperature, tein_overtemp_guard
|
||
* 3. compute_accel_comp_gain (FUN_72b0) -> accel_comp_gain @ *(0x0164)
|
||
* 4. compute_accel_comp_offset (FUN_732d) -> RW3C, REC.0
|
||
* 5. compute_gate_0220 (orphan @ 0x77ff) -> gate_0220 hysteresis
|
||
* 6. compute_tein_valve_fault_guard (FUN_62a2) -> RW7A
|
||
* 7. compute_target_injection_angle (FUN_7453) -> RW48 / RW5A
|
||
*
|
||
* The orphan temperature-kick chain (compute_temp_comp_factor → orphan
|
||
* FUN_5D58 angle_kick + `RW52 += RW3E` fold-in @ 0x7A3E) is ported in
|
||
* variants/T06215/src/ and wired into this compact phi_service.
|
||
* RW9E (the IIR state high word consumed by compute_temp_comp_factor)
|
||
* is produced internally by compute_temp_phi_comp — see
|
||
* compute_temp_phi_comp.h for the 1 kHz hook the host must drive.
|
||
* See variants/T06211/docs/open-questions.md §9.
|
||
*/
|
||
#ifndef PHI_T06215_H
|
||
#define PHI_T06215_H
|
||
|
||
#include <stdint.h>
|
||
#include <stddef.h>
|
||
|
||
/* ─── Internal runtime / calibration types ───────────────────────────
|
||
* Inlined verbatim from variants/T06215/src/injection_angle_state.h.
|
||
* Treat all fields as IMPLEMENTATION DETAIL — see the lifecycle
|
||
* comment above; the supported surface is `phi_outputs_t` and the
|
||
* getter vtable. */
|
||
|
||
typedef struct {
|
||
int16_t stride_bytes; /* +0: 2 * stride_items */
|
||
int16_t delta; /* +2: axis[idx-1] - axis[idx] */
|
||
int16_t fraction; /* +4: input_var - axis[idx] */
|
||
int16_t index_bytes; /* +6: byte offset into the axis array */
|
||
} submap_scratch_t;
|
||
|
||
typedef struct {
|
||
int16_t runtime_slot; /* +0: written by eval_submap_to_scratch */
|
||
int16_t *input_var; /* +2: pointer to the RW input variable */
|
||
int16_t stride_items; /* +4: breakpoints in the axis */
|
||
const int16_t *axis; /* +6: axis breakpoint table (descending) */
|
||
} submap_descriptor_t;
|
||
|
||
typedef struct {
|
||
/* ---- Per-tick inputs (produced elsewhere / by earlier producers) ---- */
|
||
uint16_t rpm; /* RW40 @ 0x0040 — engine RPM (external, HSI.0 ISR) */
|
||
int16_t angle_dec_cmd; /* RW42 @ 0x0042 — angle-dec command (external input) */
|
||
int16_t inj_qty_demand; /* RW44 @ 0x0044 — injection-quantity command. EXTERNAL CAN input. */
|
||
int16_t accel_comp_offset; /* RW3C @ 0x003C — internally produced by compute_accel_comp_offset (FUN_732d). */
|
||
int16_t tein_valve_fault_guard; /* RW7A @ 0x007A — internally produced by compute_tein_valve_fault_guard (FUN_62a2). */
|
||
|
||
/* ---- Reset/run flag byte (REC) ---- */
|
||
uint8_t rec; /* REC — bit 0 tested by FUN_7453 at 0x745c. */
|
||
|
||
/* ---- Persistent accumulator ---- */
|
||
int16_t angle_accumulator; /* RW52 @ 0x0052 — class-B state, updated in-place by FUN_7453. */
|
||
|
||
/* ---- Per-tick angle-kick fold-in (orphan FUN_5D58 / 0x7A3E) ---- */
|
||
int16_t angle_kick_2d; /* RW3E @ 0x003E — produced by compute_angle_kick_2d (port of orphan
|
||
* FUN_5D58 @ 0x5D58). Folded into angle_accumulator at 0x7A3E
|
||
* before FUN_7453. */
|
||
|
||
/* ---- Outputs of the chain ---- */
|
||
int16_t target_inj_angle; /* RW48 @ 0x0048 — SOLE writer is FUN_7453 @ 0x749f */
|
||
int16_t target_eoi; /* RW5A @ 0x005A — written @ 0x74a2 as target_inj_angle + phi1. */
|
||
|
||
/* ---- Absolute-address RAM touched by FUN_7453 ---- */
|
||
int16_t scratch_0200; /* *(0x0200) — event accumulator. */
|
||
int16_t tein_overtemp_guard; /* *(0x011a) — seed addend, produced by FUN_5ca1 @ 0x5d3e. */
|
||
int16_t dphi; /* *(0x014c) — angle delta. */
|
||
|
||
/* ---- Inputs to FUN_732d (compute_accel_comp_offset) ---- */
|
||
int16_t rpm_baseline; /* *(0x0138) — RPM baseline produced internally by tooth_isr
|
||
* (FUN_51e1 @ 0x51e1). No longer an external input. */
|
||
|
||
/* ---- Tooth-ISR pipeline state (FUN_51e1 @ 0x51e1 + FUN_5210 @ 0x5210) ---- */
|
||
uint16_t mt_rpm_capture_prev; /* *(0x02A8) — RW64 from previous phase-13 hit */
|
||
uint16_t mt_rpm_index_prev; /* *(0x02AA) — RW7E (capture index) at previous hit */
|
||
uint8_t mt_rpm_seeded; /* 0 = first phase-13 after reset; skip divide and seed only */
|
||
|
||
int16_t accel_comp_gain; /* *(0x0164) — Δ-rpm × gain multiplier. */
|
||
uint8_t reset_gate_0226; /* *(0x0226) — accel_comp_offset enable gate. */
|
||
uint8_t gate_0220; /* *(0x0220) — RPM-hysteresis gate. */
|
||
|
||
/* ---- Inputs/state for FUN_5ca1 (compute_tein_overtemp_guard) ---- */
|
||
int16_t temperature; /* *(0x0146) — engine temperature (external input boundary). */
|
||
|
||
/* ---- Per-tick temperature-compensation factor (orphan FUN_5DAB / 0x5DE2) ----
|
||
* The R0CB-gated `ADD RW20, RWCE` branch at 0x5DC4–0x5DCB is
|
||
* intentionally not modeled — both slots are observed zero. */
|
||
int16_t temp_comp_factor; /* *(0x02F4) — produced by compute_temp_comp_factor (port of orphan
|
||
* FUN_5DAB @ 0x5DAB). Consumed by compute_angle_kick_2d at 0x5D8D. */
|
||
int16_t rw9a; /* RW9A @ 0x009A — published by compute_temp_comp_factor at 0x5DBD. */
|
||
int16_t rw9c; /* RW9C @ 0x009C — low word of the {rw9e:rw9c} signed IIR-filter
|
||
* state pair updated by compute_temp_phi_comp (port of FUN_6b4e
|
||
* @ 0x6b4e). Boot-zero (FUN_6ba3 @ 0x6bbf clears it). */
|
||
int16_t rw9e; /* RW9E @ 0x009E — high word of the {rw9e:rw9c} signed IIR-filter
|
||
* state pair, sole writer is phi_tick_1khz at the 10 Hz
|
||
* derived rate (port of FUN_6b2a @ 0x6b2a). Consumed by
|
||
* compute_temp_comp_factor at 0x6AFC. */
|
||
uint8_t temp_phi_comp_r94; /* R94 byte register slot driven by Timer_1khz at 0x79e2 / 0x7a26.
|
||
* Prescaler that gates the IIR step inside phi_tick_1khz: counts
|
||
* down on every 1 ms call, IIR fires when it reaches 0 and reloads
|
||
* to DAT_6061 = 0x64 = 100 → effective 10 Hz cadence. Seeded to
|
||
* 100 by phi_init. */
|
||
|
||
/* ---- 3-D angle-accumulator submap scratches (FUN_722e @ 0x722e) ---- */
|
||
submap_scratch_t scratch_rpm; /* *(0x0184) — RPM axis scratch. */
|
||
submap_scratch_t scratch_demand; /* *(0x0194) — inj_qty_demand axis scratch. */
|
||
submap_scratch_t scratch_dec_cmd; /* *(0x018c) — angle_dec_cmd axis scratch. */
|
||
int16_t scratch_0158; /* *(0x0158) — debug mirror of angle_accumulator result. */
|
||
|
||
/* ---- State for FUN_62a2 (compute_tein_valve_fault_guard, RW7A producer) ---- */
|
||
uint16_t rwc2; /* RWC2 — timing-baseline scratch compared against dat_604c. */
|
||
uint8_t rf5_gate; /* RF5 byte — peripheral input gate. */
|
||
uint8_t scratch_0221; /* *(0x0221) — FUN_62a2 early-out gate (baked to 1 in phi_init). */
|
||
uint8_t scratch_010e; /* *(0x010e) — Phase-1 counter byte. */
|
||
uint8_t scratch_010f; /* *(0x010f) — Phase-2 state byte. */
|
||
uint8_t scratch_0103; /* *(0x0103) — Phase-1 counter firing threshold. */
|
||
uint8_t scratch_0108; /* *(0x0108) — Phase-2 init/re-load value. */
|
||
} runtime_state_t;
|
||
|
||
typedef struct {
|
||
/* ---- RWA4-relative scalar calibration ---- */
|
||
int16_t tein_nominal; /* CAL[0x1e] — seed base added to tein_valve_fault_guard at 0x747b. */
|
||
int16_t cal_48; /* CAL[0x48] — value loaded into RW7A when FUN_62a2 fires (= 0x04B0). */
|
||
int16_t phi0; /* CAL[0x4c] — base/initial angle. */
|
||
uint8_t cal_byte_56; /* CAL_byte[0x56] — tooth phase comparand for rpm_baseline producer (= 0x0D = 13). */
|
||
uint8_t cal_byte_9c; /* CAL_byte[0x9c] — counter increment in FUN_62a2 (= 0x03). */
|
||
int16_t cal_54; /* CAL[0x54] — UPPER clamp on target_inj_angle at 0x7493. */
|
||
int16_t cal_74; /* CAL[0x74] — LOWER RPM threshold for gate_0220 hysteresis. */
|
||
int16_t cal_76; /* CAL[0x76] — gate_0220 hysteresis width. */
|
||
int16_t cal_78; /* CAL[0x78] — UPPER clamp on accel_comp_offset. */
|
||
int16_t cal_7a; /* CAL[0x7a] — LOWER clamp on accel_comp_offset. */
|
||
int16_t cal_7e; /* CAL[0x7e] — temperature reference subtrahend in compute_temp_comp_factor (FUN_5DAB 0x5DD1). */
|
||
int16_t cal_temp_comp_switch_dynamic; /* CAL[0x80] — boot value of *(0x02F6) (German label TK_AT_W). */
|
||
int16_t cal_82; /* CAL[0x82] — IIR input-gain `b` (Q16 unsigned) used by compute_temp_phi_comp
|
||
* at the FUN_6b4e MULU @ 0x6B66. Read via (uint16_t) cast. */
|
||
int16_t cal_84; /* CAL[0x84] — IIR pole `a` (Q16 unsigned) used by compute_temp_phi_comp
|
||
* at the FUN_6b4e MULUs @ 0x6B4E / 0x6B57. Read via (uint16_t) cast.
|
||
* On this ROM cal_82 + (uint16_t)cal_84 == 0x10000 (unity DC). */
|
||
int16_t cal_temp_comp_switch_complete; /* CAL[0x86] — boot value of *(0x02F2) (German label F_TK_TE_W). */
|
||
int16_t cal_92; /* CAL[0x92] — temperature offset subtracted in FUN_5ca1. */
|
||
int16_t cal_94; /* CAL[0x94] — multiplier in FUN_5ca1. */
|
||
int16_t cal_96; /* CAL[0x96] — UPPER clamp on RW1C in FUN_5ca1 (unsigned). */
|
||
int16_t cal_98; /* CAL[0x98] — LOWER rpm threshold for tein_overtemp_guard. */
|
||
int16_t cal_9a; /* CAL[0x9a] — UPPER rpm threshold for tein_overtemp_guard. */
|
||
|
||
/* ---- accel_comp_gain (FUN_72b0) submaps & tables (RWA4-relative) ---- */
|
||
submap_descriptor_t desc_accel_rpm; /* CAL+0x58 — RPM axis */
|
||
submap_descriptor_t desc_accel_demand; /* CAL+0x60 — inj_qty_demand axis */
|
||
submap_descriptor_t desc_accel_temp; /* CAL+0x68 — temperature axis */
|
||
const int16_t *accel_combine_table; /* CAL+0x70 — 2D RPM×demand combine table */
|
||
const int16_t *accel_refine_table; /* CAL+0x72 — 1D temperature refine table */
|
||
|
||
/* ---- ROM constants ---- */
|
||
int16_t dat_604c; /* *(0x604c) = 0x0444 — RWC2 timing threshold in FUN_62a2. */
|
||
int16_t cal_byte_402; /* *(0x0402) — boot sign-extended byte. Subtracted in FUN_5DAB at 0x5DD6. */
|
||
|
||
/* ---- RWC6-relative: 3-D angle-accumulator submap (FUN_722e) ---- */
|
||
submap_descriptor_t desc_rpm; /* RWC6+0x00 — RPM axis */
|
||
submap_descriptor_t desc_demand; /* RWC6+0x08 — inj_qty_demand axis */
|
||
submap_descriptor_t desc_dec_cmd; /* RWC6+0x10 — angle_dec_cmd axis */
|
||
const int16_t *data_table_3d; /* word @ RWC6+0x1a — 3-D table data */
|
||
|
||
/* ---- RWC6-relative: 2-D angle-kick submap + scalar (FUN_5D58) ---- */
|
||
const int16_t *data_table_2d_kick; /* word @ RWC6+0x32 — 2-D kick table (RPM × demand). */
|
||
int16_t cal_rwc6_34; /* CAL[RWC6+0x34] — demand-weight multiplier in FUN_5D58. */
|
||
} calibration_t;
|
||
|
||
/* ─── Outputs surfaced after every phi_service ───────────────────── */
|
||
typedef struct {
|
||
int16_t target_inj_angle; /* RW48 — primary output (FUN_7453 @ 0x749f) */
|
||
int16_t target_eoi; /* RW5A — target_inj_angle + phi1; phi1 = phi0 + dphi */
|
||
int16_t angle_accumulator; /* RW52 — class-B accumulator state */
|
||
int16_t accel_comp_offset; /* RW3C — FUN_732d output */
|
||
int16_t accel_comp_gain; /* *(0x0164) — FUN_72b0 output */
|
||
int16_t tein_valve_fault_guard; /* RW7A — FUN_62a2 output */
|
||
int16_t tein_overtemp_guard; /* *(0x011a) — FUN_5ca1 seed addend */
|
||
int16_t temperature; /* *(0x0146) — FUN_5ca1 filtered temperature */
|
||
int16_t temp_comp_factor; /* *(0x02F4) — compute_temp_comp_factor (FUN_5DAB) output */
|
||
int16_t angle_kick_2d; /* RW3E — compute_angle_kick_2d (FUN_5D58) output (folded into angle_accumulator) */
|
||
uint8_t gate_0220; /* *(0x0220) — RPM hysteresis gate */
|
||
uint8_t flags; /* bit 0: target_inj_angle invalid (sign bit set);
|
||
* bit 1: REC.0 was set entering FUN_7453;
|
||
* bit 2: gate_0220 active */
|
||
} phi_outputs_t;
|
||
|
||
/* ─── Getter vtable ────────────────────────────────────────────────── */
|
||
typedef struct {
|
||
int16_t (*get_inj_qty_demand)(void); /* RW44 */
|
||
int16_t (*get_angle_dec_cmd)(void); /* RW42 */
|
||
uint16_t (*get_rpm)(void); /* RW40 */
|
||
int16_t (*get_temperature)(void); /* *(0x0146) */
|
||
uint16_t (*get_rwc2)(void); /* RWC2 */
|
||
uint8_t (*get_reset_gate_0226)(void); /* *(0x0226) */
|
||
int16_t (*get_dphi)(void); /* *(0x014c) */
|
||
uint8_t (*get_scratch_0103)(void); /* *(0x0103) — boot-only */
|
||
uint8_t (*get_scratch_0108)(void); /* *(0x0108) — boot-only */
|
||
} phi_input_getters_t;
|
||
|
||
/* ─── Lifecycle types ────────────────────────────────────────────────── */
|
||
typedef struct {
|
||
runtime_state_t rt;
|
||
const phi_input_getters_t *getters;
|
||
} phi_state_t;
|
||
|
||
typedef calibration_t phi_cal_t;
|
||
|
||
/* ─── Public API ───────────────────────────────────────────────────── */
|
||
|
||
void phi_init(phi_state_t *state,
|
||
const phi_cal_t *cal,
|
||
const phi_input_getters_t *getters);
|
||
|
||
void phi_service(phi_state_t *state,
|
||
const phi_cal_t *cal,
|
||
phi_outputs_t *out);
|
||
|
||
/** 1 kHz hook. Host invokes once every 1 ms from its millisecond timer.
|
||
* Internally decrements `state->rt.temp_phi_comp_r94`; on R94 == 0 runs
|
||
* the IIR step (port of FUN_6b2a + FUN_6b4e @ 0x6b2a / 0x6b4e) and
|
||
* reloads R94 to 100 (DAT_6061). The IIR updates `{rt->rw9e, rt->rw9c}`
|
||
* in place using `rt->rw9a` as the input and `cal->cal_82` / `cal->cal_84`
|
||
* as coefficients. Reentrant per phi_state_t — the prescaler lives in
|
||
* runtime_state_t. */
|
||
void phi_tick_1khz(phi_state_t *state, const phi_cal_t *cal);
|
||
|
||
/** Per-accepted-tooth ISR entry point. Updates rt->rpm_baseline when
|
||
* current_tooth == cal->cal_byte_56 (=13). No-op for other phases.
|
||
* The caller invokes this once per accepted tooth event at HSI.0 rate,
|
||
* separate from the phi_service scheduler cadence. */
|
||
void phi_tooth_isr(phi_state_t *state,
|
||
const phi_cal_t *cal,
|
||
uint8_t current_tooth,
|
||
uint16_t current_capture);
|
||
|
||
/** Clear the tooth-ISR pipeline (mirrors reset_toothed_wheel @ 0x5301).
|
||
* Next phi_tooth_isr call will seed without dividing. */
|
||
void phi_reset_tooth_state(phi_state_t *state);
|
||
|
||
size_t phi_state_size(void);
|
||
size_t phi_cal_size(void);
|
||
|
||
/* ─── Auto-generated cal export (defined in phi_cal_tables.c) ──────── */
|
||
|
||
extern calibration_t phi_t06215_cal;
|
||
|
||
void phi_t06215_bind_inputs(runtime_state_t *rt, calibration_t *cal);
|
||
|
||
#endif /* PHI_T06215_H */
|