From bf0254f854e2b48088bec2f5d97e2c9455378366 Mon Sep 17 00:00:00 2001 From: LucianoDev Date: Wed, 6 May 2026 18:39:37 +0200 Subject: [PATCH] almera done, dynamic tested ckp reading, pid control, open loop, and phiad sweep with minimal error, testing in car required --- Core/Advance_Control/FBKW.c | 2 +- Core/Advance_Control/cal_tables_rom.c | 207 +++++---- Core/Advance_Control/cal_tables_rom.h | 4 +- Core/Advance_Control/ckp_acquisition.h | 50 +-- Core/Advance_Control/pwm.c | 577 ++++++++++++++----------- Core/Advance_Control/pwm.h | 445 ++++++++++++------- Core/CAN_Libs/can_db.c | 2 +- Core/Inc/id.h | 19 +- Core/Phi/phi.c | 284 ++++++------ Core/Phi/phi.h | 52 ++- Core/Phi/phi_cal_tables.c | 248 ++++++----- Core/Src/fuel_map.c | 2 +- 12 files changed, 1034 insertions(+), 858 deletions(-) diff --git a/Core/Advance_Control/FBKW.c b/Core/Advance_Control/FBKW.c index d04bd35..b9613ef 100644 --- a/Core/Advance_Control/FBKW.c +++ b/Core/Advance_Control/FBKW.c @@ -157,7 +157,7 @@ void FBKW_service(void) { fbkw_rt.system_flags_110 &= ~0x20u; // clear ONLY bit 5 (OL gate). Leave other bits alone. #if defined(T06211) - fbkw_rt.pi_open_loop_flag = 0x00; //0x2000 + //fbkw_rt.pi_open_loop_flag = 0x00; //0x2000 #endif } } diff --git a/Core/Advance_Control/cal_tables_rom.c b/Core/Advance_Control/cal_tables_rom.c index 4180403..30f9997 100644 --- a/Core/Advance_Control/cal_tables_rom.c +++ b/Core/Advance_Control/cal_tables_rom.c @@ -1,58 +1,47 @@ /** - * @file cal_tables_rom.c (families/T06215/compact_src) - * @brief ROM-decoded T06215 calibration. + * @file cal_tables_rom.c (variant/t06211/compact_src) + * @brief ROM-decoded t06211 calibration. * - * Source ROM: 424026.bin (Bosch P/N 167002X9001494060200) - * Calibration base: RWA4 = 0x9BD8 - * Flash anchor: 0x9618 - * Generated: 2026-04-29 (hand-extracted via Ghidra MCP + ROM readback) + * AUTO-GENERATED by tools/extract_calibration.py + * Source ROM: rom_eeprom_dump_0000-9FFF_504012.bin + * Calibration base (RWA4): 0x9BD8 + * Flash anchor: 0x7E18 + * Generated: 2026-05-05 10:31:48 * - * Field name → ROM-address binding lives in pwm_addr_map.h. Boot-derived - * values that previously lived as hard-coded literals in pwm.c's - * runtime_reset are now in `init_*` fields here so each variant carries - * its own values without code edits. - * - * Cal-offset deltas vs t06211: - * - PI body thresholds (0x10E, 0x110, 0x116) — same offsets. - * - target/pi clamps (-512) live at cal+0x128 / cal+0x12A in T06215 - * (vs t06211's cal+0x120 / cal+0x122). - * - pwm_y_table_ptr at cal+0x15C; shape_y_table_ptr at cal+0x166. - * - PWM RPM-window block 0xEE..0x104 — UNCHANGED offsets. - * - setpoint_offset = cal+0x4c - cal+0x4e = 3499 - 4147 = -648. - * - s_recovery RPM gate at cal+0x126 (t06211 used cal+0x11E). + * DO NOT EDIT — regenerate with: + * python tools/extract_calibration.py --variant t06211 */ #include "pwm.h" /* ── Submap x/y arrays ──────────────────────────────────────────────── */ -static const int16_t setpoint_x[6] = { 8389, 5872, 3775, 2726, 1426, 0 }; -static const int16_t setpoint_y[6] = { 1707, 1707, 1195, 768, 427, 427 }; +static const int16_t setpoint_x[6] = { 8389, 5872, 4614, 2726, 1426, 0 }; +static const int16_t setpoint_y[6] = { 1707, 1707, 1707, 939, 427, 427 }; -static const int16_t pwm_A_x[9] = { 25166, 18455, 13841, 8389, 5872, 3775, 2726, 1426, 0 }; -static const int16_t pwm_A_y[9] = { 1707, 1365, 1195, 768, 427, 85, 0, -171, -469 }; +static const int16_t pwm_A_x[9] = { 25166, 18455, 13841, 8389, 5872, 4614, 2726, 1426, 0 }; +static const int16_t pwm_A_y[9] = { 1707, 1365, 1195, 768, 427, 85, 0, -171, -469 }; static const int16_t pwm_B_x[10] = { 1707, 1365, 1195, 768, 427, 85, 0, -171, -469, -512 }; -static const int16_t pwm_B_y[10] = { 819, 737, 492, 0, 41, 49, 82, 102, 205, 205 }; +static const int16_t pwm_B_y[10] = { 819, 737, 492, 0, 41, 49, 82, 102, 205, 205 }; static const int16_t shape_x[4] = { 819, 737, 492, 0 }; -static const int16_t shape_y[4] = { 41, 49, 82, 102 }; +static const int16_t shape_y[4] = { 41, 49, 82, 102 }; -/* ── Y-tables ────────────────────────────────────────────────────────── */ +/* ── Y-tables (dereferenced from pwm_y_table_ptr / shape_y_table_ptr) ── */ static const int16_t shape_y_table_rom[4] = { 41, 49, 82, 102 }; -/* PWM bilinear Y-table at flash 0x9E30 — 10 rows × 9 cols (90 entries). */ static const int16_t pwm_y_table_rom[90] = { - /* row 0 */ 205, 205, 205, 205, 205, 205, 205, 205, 0, - /* row 1 */ 1568, 1433, 1355, 1065, 799, 205, 205, 205, 0, - /* row 2 */ 1716, 1679, 1556, 1290, 983, 205, 205, 205, 0, - /* row 3 */ 2219, 2129, 2052, 1925, 1679, 880, 205, 205, 0, - /* row 4 */ 2744, 2580, 2518, 2477, 2355, 1740, 1310, 205, 0, - /* row 5 */ 3210, 3071, 3030, 3194, 3194, 3018, 2867, 737, 205, - /* row 6 */ 3702, 3493, 3403, 3583, 3583, 3493, 3071, 1192, 483, - /* row 7 */ 3890, 3890, 3890, 3890, 3890, 3890, 3890, 2076, 1229, + /* row 0 */ 205, 205, 205, 205, 205, 205, 0, 0, 0, + /* row 1 */ 1638, 1392, 1229, 962, 778, 512, 0, 0, 0, + /* row 2 */ 1843, 1577, 1433, 1208, 1003, 758, 0, 0, 0, + /* row 3 */ 2457, 2109, 1986, 1761, 1577, 1229, 287, 0, 0, + /* row 4 */ 2867, 2744, 2641, 2457, 2314, 2129, 1269, 205, 205, + /* row 5 */ 3686, 3481, 3460, 3378, 3276, 3174, 2907, 1024, 737, + /* row 6 */ 3890, 3870, 3849, 3829, 3808, 3788, 3235, 1310, 1024, + /* row 7 */ 3890, 3890, 3890, 3890, 3890, 3890, 3890, 2076, 1638, /* row 8 */ 3890, 3890, 3890, 3890, 3890, 3890, 3890, 3890, 3481, - /* row 9 */ 3890, 3890, 3890, 3890, 3890, 3890, 3890, 3890, 4095, + /* row 9 */ 3890, 3890, 3890, 3890, 3890, 3890, 3890, 3890, 3890, }; /* ── Descriptors ────────────────────────────────────────────────────── */ @@ -80,96 +69,96 @@ const int16_t *pwm_submap_y_of(uint16_t idx) { switch (idx) { case PWM_SUBMAP_SETPOINT_INTERP: return setpoint_y; - case PWM_SUBMAP_PWM_A: return pwm_A_y; - case PWM_SUBMAP_PWM_B: return pwm_B_y; - case PWM_SUBMAP_SHAPE_EVAL: return shape_y; - default: return NULL; + case PWM_SUBMAP_PWM_A: return pwm_A_y; + case PWM_SUBMAP_PWM_B: return pwm_B_y; + case PWM_SUBMAP_SHAPE_EVAL: return shape_y; + default: return NULL; } } /* ── Scalars ────────────────────────────────────────────────────────── */ const pwm_calibration_t pwm_cal_rom = { - /* PI controller error-band thresholds */ - .large_pos_error_thresh = 128, /* cal+0x10E */ - .large_neg_error_thresh = (int16_t)0xFF00, /* cal+0x110 = -256 */ - .pi_low_clamp = (int16_t)0xFE00, /* cal+0x128 = -512 */ - .pi_high_clamp = 1707, /* fallback; runtime uses pi_high_clamp_ceiling */ + .large_pos_error_thresh = 128, /* cal+0x10E */ + .large_neg_error_thresh = (int16_t)0xFF00, /* cal+0x110 */ + .pi_low_clamp = (int16_t)0xFE00, /* cal+0x120 */ + .pi_high_clamp = 1707, /* cal+0x124 */ - /* CAN-decoded setpoint cal constants */ - .b_fb_kw_upper_bound = 7680, /* cal+0x004 */ - .b_fb_kw_lower_bound = (int16_t)0xFD00, /* cal+0x006 */ - .setpoint_offset = -648, /* cal+0x4c − cal+0x4e */ - .target_5e_min_clamp = (int16_t)0xFE00, /* cal+0x12A */ - .can_aux_12e_max = 1451, /* cal+0x002 */ + /* CAN-decoded setpoint (FUN_64c3) cal constants */ + .b_fb_kw_upper_bound = 7680, /* cal+0x004 */ + .b_fb_kw_lower_bound = (int16_t)0xFD00, /* cal+0x006 */ + /* setpoint_offset = cal+0x4c - cal+0x4e = 3499 - 4156 = -657 */ + .setpoint_offset = (int16_t)0xFD6F, + .target_5e_min_clamp = (int16_t)0xFE00, /* cal+0x122 */ + .can_aux_12e_max = 1451, /* cal+0x002 */ - /* CKP-zero acquisition — values re-extracted from this ROM - * (RWA4=0x9BD8); cal slot offsets differ from T06235 (T06215 has - * cal+0x4E/0x50/0xA0 vs T06235 cal+0x50/0x52/0xA2). */ - .ckp_zero_anchor = 4147, /* cal+0x04E @ 0x9C26 = 0x1033 */ - .can_dckp_offset_bias = -427, /* cal+0x050 @ 0x9C28 = 0xFE55 */ - .ckp_modulus = 7680, /* cal+0x0A0 @ 0x9C78 = 0x1E00 (90°) */ + /* CKP-zero acquisition (FUN_6b67 chain) — values re-extracted from + * rom_eeprom_dump_0000-9FFF_504012.bin (RWA4=0x9BD8). */ + .ckp_zero_anchor = 4156, /* cal+0x04E @ 0x9C26 = 0x103C */ + .can_dckp_offset_bias = -427, /* cal+0x050 @ 0x9C28 = 0xFE55 */ + .ckp_modulus = 7680, /* cal+0x0A0 @ 0x9C78 = 0x1E00 (90°) */ - /* CKP process-tooth derivation (FUN_7293 — feeds get_ckp_process_tooth()). - * Read direct from 424026.bin at RWA4=0x9BD8 + offset; default-family - * analogs are at cal+0x120/0x0A3/0x0A4. */ - .ckp_advance_per_tick = 1707, /* cal+0x12C @ 0x9D04 = 0x06AB (= 20° per tick; - * t06211 cal+0x124, T06235 cal+0x140, default cal+0x120 = 1536 = 18°) */ - .ckp_seg_wrap_threshold = 29, /* cal+0x09D @ 0x9C75 (default cal+0x0A3) */ - .ckp_teeth_per_seg = 26, /* cal+0x09E @ 0x9C76 (default cal+0x0A4; same value across all variants) */ + /* CKP process-tooth derivation (FUN_6d4a @ 0x6d4a). cal+0x124 is + * aliased with pi_high_clamp — same value (1707) used by both. */ + .ckp_advance_per_tick = 1707, /* cal+0x124 @ 0x9CFC = 0x06AB (= 20° per tick; + * matches T06215 cal+0x12C semantically). */ + .ckp_seg_wrap_threshold = 29, /* cal+0x09D @ 0x9C75 (byte) */ + .ckp_teeth_per_seg = 26, /* cal+0x09E @ 0x9C76 (byte; same value across all variants) */ - /* Recovery / sustained-error machinery */ - .pi_state_c2_reload = 100, /* cal+0x114 (ROM 0x9CEC = 0x0064) */ - .inj_qty_thresh = 96, /* cal+0x116 (ROM 0x9CEE = 0x0060) */ - .pi_sat_count_threshold = 800, /* cal+0x112 (ROM 0x9CEA = 0x0320) */ - .rpm_threshold_recovery = 2936, /* cal+0x126 (ROM 0x9CFE = 0x0B78) — was wrong (256, t06211 holdover) */ - .pi_cl_rpm_floor = 420, /* flash[0x605C] = 0x01A4 */ + .error_thresh_114 = 100, /* cal+0x114 */ + .pi_thresh_116 = 96, /* cal+0x116 */ + .pi_sat_count_threshold = 800, /* cal+0x112 = 0x0320 */ + .rpm_threshold_11E = 2936, /* cal+0x11E */ + .pi_cl_rpm_floor = 420, /* RAM[0x605c] = 0x01A4 (flash mirror) */ - /* PI runtime-reset values (ROM-extracted from t06215 cal block at - * RWA4=0x9BD8 + offset). Boot multipliers DAT_0410..0416 are - * uninitialized RAM (no writers anywhere in ROM), so they default - * to 0 — the boot equation `(trim_byte << N) + cal_value` resolves - * to cal_value. Earlier defaults of ±853 / 480 / etc. were t06211 - * holdovers from the original fork; corrected 2026-04-29 against - * the actual t06215 ROM bytes. */ - .init_p_shape_bound_pos = +107, /* cal+0x10A (ROM 0x9CE2 = 0x006B) → DAT_0450 */ - .init_p_shape_bound_neg = -107, /* cal+0x10C (ROM 0x9CE4 = 0xFF95) → DAT_0452 */ - .init_p_gain_normal = +336, /* cal+0x118 (ROM 0x9CF0 = 0x0150) → DAT_0454 */ - .init_p_slope_large_pos = +1792, /* cal+0x11A (ROM 0x9CF2 = 0x0700) → DAT_027e */ - .init_p_slope_large_neg = +512, /* cal+0x11C (ROM 0x9CF4 = 0x0200) → DAT_0280 */ - .init_integ_step_normal = +256, /* cal+0x11E (ROM 0x9CF6 = 0x0100) → DAT_0456 */ - .init_integ_step_large_pos = +512, /* cal+0x120 (ROM 0x9CF8 = 0x0200) → DAT_0282 */ - .init_integ_step_large_neg = +256, /* cal+0x122 (ROM 0x9CFA = 0x0100) → DAT_0284 */ - .init_open_loop_p_gain = +6, /* cal+0x124 (ROM 0x9CFC = 0x06, byte, clamped to 15) → DAT_033e */ + .pwm_detail_x0 = (int16_t)0x9C40, /* cal+0x0EE */ + .pwm_detail_x1 = (int16_t)0x8235, /* cal+0x0F0 */ + .pwm_cached_ptr_0F2 = 5662, /* cal+0x0F2 (legacy scalar) */ + .pwm_cached_ptr_102 = 168, /* cal+0x102 (legacy scalar) */ - /* CL correction normalizers — boot-cached at DAT_0340/DAT_0342 in - * the ROM (NOT 0x0332/0x0334 — that was a t06211 holdover). Source - * cal offsets are 0x108 (pos) and 0x106 (neg); both equal 350 in - * this ROM. */ - .init_pos_error_normalizer = 350, /* cal+0x108 (ROM 0x9CE0 = 0x015E) → DAT_0340 */ - .init_neg_error_normalizer = 350, /* cal+0x106 (ROM 0x9CDE = 0x015E) → DAT_0342 */ - - /* PWM stage scalars */ - .pwm_detail_x0 = (int16_t)0x9C40, - .pwm_detail_x1 = (int16_t)0x8235, - .pwm_cached_ptr_0F2 = 5662, - .pwm_cached_ptr_102 = 168, - .pwm_const_104 = 354, - - .pwm_rpm_windows = { 5662, 6837, 8808, 10486, 11954, 13422, 18036, 19713 }, + /* RPM-window matching: inline 8-int16 band array at cal+0x0F2..0x100 + * (4 (lo,hi) pairs) plus halfwidth at cal+0x102. Drives the three- + * phase pwm_period slew in FUN_5314 (open-questions §5 closeout). */ + .pwm_rpm_windows = { 5662, 6795, 8808, 10486, 11954, 13422, 18036, 19713 }, .pwm_window_halfwidth = 168, - .pwm_slew_step = 354, - .pwm_y_table = pwm_y_table_rom, - .shape_y_table = shape_y_table_rom, + /* pwm_const_104 / pwm_slew_step are the same value (cal+0x104 = 354); + * pwm_const_104 retained for backward compat, pwm_slew_step is the + * semantic name used by the RPM-window matcher (per-cycle pwm_period + * slew magnitude, applied at Phase 1 / Phase 3). Cached to + * RAM[0x02ec] at FUN_5314:0x53b9. */ + .pwm_const_104 = 354, /* cal+0x104 (legacy alias) */ + .pwm_slew_step = 354, /* cal+0x104 (semantic) */ - .closed_loop_gain_const = 10, + .pwm_y_table = pwm_y_table_rom, /* cal+0x154 @ 0x9E28 */ + .shape_y_table = shape_y_table_rom, /* cal+0x15E @ 0x9E20 */ - .pwm_period_min = 33333, - .pwm_period_max = 40000, + /* CL-correction gain — cached at absolute ROM[0x6056], not cal-relative. */ + .closed_loop_gain_const = 10, /* ROM[0x6056] */ - .pwm_min = 0x00CD, - .pwm_max = 0x0F32, + /* PI runtime-reset values — boot-derived in ROM (FUN_76aa @ 0x76aa). + * Trim bytes at RAM[0x0410-0x0416] have no writers so each cal base + * passes through verbatim. Field shape mirrors T06215's + * pwm_calibration_t for cross-family consistency. */ + .init_p_shape_bound_pos = 853, /* cal+0x10A */ + .init_p_shape_bound_neg = (int16_t)0xFCAB, /* cal+0x10C */ + .init_p_gain_normal = 480, /* cal+0x118 */ + .init_integ_step_normal = 256, /* cal+0x11A */ + .init_open_loop_p_gain = 6, /* cal+0x11C low byte (clamped <=15) */ + .init_pos_error_normalizer = 350, /* cal+0x108 */ + .init_neg_error_normalizer = 350, /* cal+0x106 */ + + /* PWM period endpoints (alias of pwm_detail_x0/x1) as unsigned. */ + .pwm_period_min = 33333, /* cal+0x0F0 */ + .pwm_period_max = 40000, /* cal+0x0EE */ + + /* Duty clamp bounds — RAM[0x6058]/RAM[0x605a] flash mirrors with the + * same defaults as the default family's pwm_min/pwm_max. */ + .pwm_min = 0x00CD, /* RAM[0x6058] = 205 */ + .pwm_max = 0x0F32, /* RAM[0x605a] = 3890 */ }; +/* Family-1 API parity placeholder — t06211 keeps Y-tables inside + * pwm_cal_rom, so pwm_flash_rom has no data. Callers pass &pwm_flash_rom + * to pwm_init() purely for signature compatibility. */ const pwm_flash_t pwm_flash_rom = { 0 }; diff --git a/Core/Advance_Control/cal_tables_rom.h b/Core/Advance_Control/cal_tables_rom.h index 5db2e4d..54682cc 100644 --- a/Core/Advance_Control/cal_tables_rom.h +++ b/Core/Advance_Control/cal_tables_rom.h @@ -1,6 +1,6 @@ /** - * @file cal_tables_rom.h (families/T06215/compact_src) - * @brief Extern decls for the ROM-decoded T06215 calibration. + * @file cal_tables_rom.h (families/t06211/compact_src) + * @brief Extern decls for the ROM-decoded t06211 calibration. * All declarations live in pwm.h; this file just re-exports the enum * and array symbols for the compact_src/pwm.c translation unit. */ diff --git a/Core/Advance_Control/ckp_acquisition.h b/Core/Advance_Control/ckp_acquisition.h index cb07d18..c5c6808 100644 --- a/Core/Advance_Control/ckp_acquisition.h +++ b/Core/Advance_Control/ckp_acquisition.h @@ -1,54 +1,44 @@ /* - * ckp_acquisition.h (variant/T06215/compact_src) + * ckp_acquisition.h (variant/t06211/compact_src) * - * CKP-zero acquisition chain for T06215. Mirrors ROM - * FUN_624a-class Stage-1 caller @ 0x6330 (LCALL FUN_70d8) → FUN_70d8 @ 0x70d8. - * - * Body copied verbatim from variant/T06235 per the pillar-port copy gate - * (docs/pillar-functions.md). Only cal-field bindings change. - * - * See docs/algorithm-ckp-zero-acquisition.md for the data-flow summary. + * CKP-zero acquisition chain for t06211. Mirrors ROM Stage-1 caller @ + * 0x65c7 (LCALL FUN_6b67) → FUN_6b67 @ 0x6b67. Body verbatim from T06235; + * cal-field bindings differ — see pwm_addr_map / cal_offsets.py. */ -#ifndef PWM_T06215_CKP_ACQUISITION_H -#define PWM_T06215_CKP_ACQUISITION_H +#ifndef PWM_T06211_CKP_ACQUISITION_H +#define PWM_T06211_CKP_ACQUISITION_H #include -/* Inputs */ -extern int16_t B_CKP_OFFSET; /* RW134 / DAT_0134 — CAN raw input */ -extern uint8_t commitCKP_offset; /* unused for now (commit gate hook) */ +extern int16_t B_CKP_OFFSET; +extern uint8_t commitCKP_offset; -/* Internal RAM trims (DAT_0430 word, DAT_0404 signed byte). No runtime - * writer was found in this ROM/car; sit at 0 unless a future step wires - * them up. Exposed for diagnostic poking. */ extern int16_t CKP_RAM_TRIM_0430; extern int8_t CKP_RAM_TRIM_0404; -/* Outputs */ -extern int16_t dCKP_OFFSET; /* DAT_0434 — output of Stage 1 */ -extern int16_t CKP_ZERO_OFFSET; /* DAT_0152 — output of Stage 2 (FUN_70d8) */ +extern int16_t dCKP_OFFSET; /* DAT_0434 */ +extern int16_t CKP_ZERO_OFFSET; /* DAT_0152 */ int16_t get_ckp_zero(void); /* Get the next process-tooth index — mirrors the byte stored to R90 - * in ROM FUN_7293 @ 0x7293 (analog of default-family FUN_87ea @ 0x87ea). - * - * The ROM routine advances the CKP zero-crossing accumulator by a fixed - * per-tick angular increment, then renormalizes the high byte (segment- - * tooth view) into the valid tooth range with two wrap branches: + * in ROM FUN_6d4a @ 0x6d4a (analog of T06215 FUN_7293 / default + * FUN_87ea). Same body shape as T06215; the only difference is the + * cal slot for `ckp_advance_per_tick` (cal+0x124 in t06211 vs cal+0x12C + * in T06215). cal+0x124 is **aliased** with pi_high_clamp — same flash + * word, value 1707, used both as the PI Block-4 ceiling and the + * per-tick CKP angular advance. * * advanced = CKP_ZERO_OFFSET + ckp_advance_per_tick (16-bit ADD) * tooth = (advanced >> 8) + 1 (byte INC of high) - * if (tooth > ckp_seg_wrap_threshold) (T06215: 29) + * if (tooth > ckp_seg_wrap_threshold) (t06211: 29) * tooth -= (ckp_modulus >> 8) (= 30 — angular wrap) - * else if (tooth > ckp_teeth_per_seg) (T06215: 26) + * else if (tooth > ckp_teeth_per_seg) (t06211: 26) * tooth = 0 (hard reset) * return tooth; * - * No side-effects on CKP_ZERO_OFFSET or dCKP_OFFSET — the ROM SUB on - * RW1C is local to the function frame; only the byte (R90) escapes via - * the store at 0x72d1. + * No side-effects on CKP_ZERO_OFFSET or dCKP_OFFSET. */ uint8_t get_ckp_process_tooth(void); -#endif /* PWM_T06215_CKP_ACQUISITION_H */ +#endif /* PWM_T06211_CKP_ACQUISITION_H */ diff --git a/Core/Advance_Control/pwm.c b/Core/Advance_Control/pwm.c index 24bf85b..03c6964 100644 --- a/Core/Advance_Control/pwm.c +++ b/Core/Advance_Control/pwm.c @@ -1,24 +1,22 @@ /** - * @file pwm.c (families/T06215/compact_src) - * @brief Compact single-file implementation of the T06215 PWM control - * pipeline. + * @file pwm.c (families/t06211/compact_src) + * @brief Compact single-file implementation of the t06211 PWM control + * pipeline. Merges the 10 per-module files from + * families/t06211/src/ into one translation unit. * - * Pipeline (pwm_service @ 0x7780): - * 0. setpoint interp (FUN_7051) — writes pi_high_clamp_ceiling (DAT_0344) - * 1. supervisor (s_supervisor) — reset + counter + error + clamp - * 2. publish_cl (s_publish_cl) — calls cl_correction, publishes est_angle - * 3. pi_update (s_pi_update @ 0x542f) — open/closed-loop PI body - * 4. pwm_output (s_pwm_output) — eval+eval+combine+saturate+HW shadow + * Pipeline (t06211 FUN_77b3 @ 0x77b3): + * 1. setpoint (FUN_7168) — single-submap RPM-indexed interp + * 2. supervisor (FUN_7beb) — reset + counter + error + clamp + * 3. publish_cl (FUN_7cd8) — calls cl_correction, publishes est_angle + * 4. pi_controller (FUN_67c4) — open/closed-loop branch + * 5. pwm_output (FUN_5314) — eval+eval+combine+saturate+HW shadow * - * The PI block (s_pi_update + s_recovery) was re-translated 1:1 from the - * t06215 disasm at 0x542f / 0x53a2 on 2026-04-29. Earlier versions of this - * file inherited a t06211-shaped body (separate s_pi_compensation / - * s_pi_integrator_step / pi_flag_338 / pi_flag_c7) whose cited addresses - * (0x67c4, 0x66ad, 0x672b, 0x7c85) do not exist in this binary. + * Per-block address citations follow the families/t06211/src/ per-module + * ports verbatim; see those files for expanded commentary. */ #include "pwm.h" -/* Forward decls. */ +/* Forward decls for file-static helpers. */ static void s_eval_submap(const pwm_submap_descr_t *d, pwm_interp_slot_t *slot); static int16_t s_combine(const int16_t *y_base, @@ -37,8 +35,6 @@ static void s_pi_update (pwm_runtime_t *rt, const pwm_calibration_t *cal); static void s_pwm_output (pwm_runtime_t *rt, const pwm_calibration_t *cal); -static void s_recovery (pwm_runtime_t *rt, - const pwm_calibration_t *cal); /* ═════════════════════════════════════════════════════════════════════ * Init / binding @@ -47,21 +43,24 @@ static void s_recovery (pwm_runtime_t *rt, static void apply_cal(pwm_runtime_t *rt, const pwm_calibration_t *cal) { - /* All boot-derived constants come from cal — no literals here. */ - rt->setpoint_offset = cal->setpoint_offset; + /* All boot-derived constants come from cal — no literals here. + * Mirrors FUN_76aa @ 0x76aa, which copies cal+0x108/0x106 into + * RAM[0x0332]/[0x0334] (normalizers), cal+0x10A/0x10C into + * RAM[0x0450]/[0x0452] (P-shape bounds; trim bytes at RAM[0x414]/ + * [0x416] have no writers in the ROM so the bases pass through), + * cal+0x118/0x11A into RAM[0x0454]/[0x0456] (P-gain + integ step; + * same trim-byte note for RAM[0x0410]/[0x0412]), and cal+0x11C + * (byte, clamped to 15) into RAM[0x0330]. */ + rt->setpoint_offset = cal->setpoint_offset; - rt->p_shape_bound_pos = cal->init_p_shape_bound_pos; - rt->p_shape_bound_neg = cal->init_p_shape_bound_neg; - rt->p_gain_normal = cal->init_p_gain_normal; - rt->integ_step_normal = cal->init_integ_step_normal; - rt->p_slope_large_pos = cal->init_p_slope_large_pos; - rt->p_slope_large_neg = cal->init_p_slope_large_neg; - rt->integ_step_large_pos = cal->init_integ_step_large_pos; - rt->integ_step_large_neg = cal->init_integ_step_large_neg; - rt->open_loop_p_gain = cal->init_open_loop_p_gain; + rt->p_shape_bound_pos = cal->init_p_shape_bound_pos; + rt->p_shape_bound_neg = cal->init_p_shape_bound_neg; + rt->p_gain_normal = cal->init_p_gain_normal; + rt->integ_step_normal = cal->init_integ_step_normal; + rt->open_loop_p_gain = cal->init_open_loop_p_gain; - rt->pos_error_normalizer = cal->init_pos_error_normalizer; - rt->neg_error_normalizer = cal->init_neg_error_normalizer; + rt->pos_error_normalizer = cal->init_pos_error_normalizer; + rt->neg_error_normalizer = cal->init_neg_error_normalizer; } static void runtime_reset(pwm_runtime_t *rt) @@ -74,7 +73,7 @@ void pwm_init(pwm_runtime_t *rt, const pwm_flash_t *flash, const pwm_input_getters_t *getters) { - (void)flash; + (void)flash; /* family-1 API parity; t06211 keeps Y-tables in cal */ runtime_reset(rt); rt->bound_cal = cal; rt->bound_getters = getters; @@ -88,18 +87,22 @@ static void read_inputs(pwm_runtime_t *rt) void *ctx = g->ctx; rt->inputs.ckp_in = g->ckp_in (ctx); rt->inputs.rpm = g->rpm (ctx); - rt->inputs.angle_dec_cmd = g->angle_dec_cmd (ctx); + rt->inputs.angle_dec_cmd = g->angle_dec_cmd (ctx); /* accepted; unused */ rt->inputs.inj_qty_demand = g->inj_qty_demand(ctx); rt->inputs.b_fb_kw = g->b_fb_kw (ctx); - rt->inputs.cl_gate_input = g->cl_gate_input (ctx); + rt->inputs.cl_gate_input = g->cl_gate_input (ctx); rt->inputs.supply_voltage = g->supply_voltage(ctx); - rt->inputs.temperature = g->temperature (ctx); + rt->inputs.temperature = g->temperature (ctx); /* accepted; unused */ + /* The b_fb_kw getter result drives the CAN-decoded setpoint chain + * (FUN_64c3). Mirrors the real ROM where RAM[0x12c] is written by + * the CAN parser before FUN_64c3 runs. */ rt->can_raw_b_fb_kw = rt->inputs.b_fb_kw; } /* ═════════════════════════════════════════════════════════════════════ - * Interpolation (FUN_7168, fingerprint #3) + * Interpolation — FUN_7168 (0x7168-0x71d7), fingerprint #3 + * Raw helper; also drives the setpoint stage via descriptor. * ═════════════════════════════════════════════════════════════════════ */ int16_t pwm_interp_lookup(const int16_t *x, const int16_t *y, @@ -118,11 +121,19 @@ int16_t pwm_interp_lookup(const int16_t *x, const int16_t *y, int32_t prod = MUL_S16(num, dy); int16_t quot = (int16_t)(prod / (int32_t)dx); + /* Disasm 0x71c5: ADD RW1C, -0x2[RW20]. After the disasm's pointer + * advance to the y[] half (0x71b2 ADD RW20, RW1E), RW20 points one + * past the breakpoint where the search settled, so -0x2[RW20] is + * y at that breakpoint — which is y[k] in C's k-naming (k = first + * index where in >= x[k] given descending x). Earlier this returned + * y[k-1], producing wrong-polarity setpoints (e.g. pi_high_clamp_ceiling + * = 1962 instead of 1195 at rpm=3355). */ return (int16_t)(quot + y[k]); } /* ═════════════════════════════════════════════════════════════════════ - * Submap eval / bilinear combine / refine + * Submap eval / bilinear combine / refine — t06211 FUN_6fb8 / FUN_7035 / + * FUN_7014. Scratch layout matches the family-1 ROM convention. * ═════════════════════════════════════════════════════════════════════ */ static void s_eval_submap(const pwm_submap_descr_t *d, @@ -144,6 +155,7 @@ static void s_eval_submap(const pwm_submap_descr_t *d, } if (k == 1u && input >= d->x[0]) { + /* Upper-clamp sentinel [_, 2, 2, 2] */ slot->x_interval = 2; slot->x_offset = 2; slot->y_byte_off = 2; @@ -199,6 +211,7 @@ static int16_t s_refine(const int16_t *y_base, return (int16_t)(y_here + (int32_t)(diff / (int32_t)den)); } +/* Descriptor-driven wrapper used only by the setpoint stage. */ static int16_t s_interp_descr(const pwm_submap_descr_t *d, const int16_t *y_array) { @@ -207,8 +220,7 @@ static int16_t s_interp_descr(const pwm_submap_descr_t *d, } /* ═════════════════════════════════════════════════════════════════════ - * Stage 0 — Setpoint interp (pwm_service:0x7780 → FUN_7051(cal+0x142)) - * Writes pi_high_clamp_ceiling (DAT_0344 in ROM). + * Stage 1 — Setpoint (FUN_77b3:77b3-77c7 + FUN_7168) * ═════════════════════════════════════════════════════════════════════ */ static void s_setpoint(pwm_runtime_t *rt) @@ -220,20 +232,29 @@ static void s_setpoint(pwm_runtime_t *rt) } /* ═════════════════════════════════════════════════════════════════════ - * Stage 0b — CAN-decoded setpoint (FUN_64c3 family) + * Stage 1b — CAN-decoded setpoint (FUN_64c3 @ 0x64c3-0x650a) + * Real ROM call site: FUN_6192:0x61cc (CAN parser dispatcher). + * For the C model, invoked from pwm_service after read_inputs so the + * harness's per-cycle b_fb_kw value flows into target before PI runs. + * Skip the RE7 gate and error path (sim trusts caller). * ═════════════════════════════════════════════════════════════════════ */ static void s_setpoint_can_decode(pwm_runtime_t *rt, const pwm_calibration_t *cal) { int16_t raw = rt->can_raw_b_fb_kw; + /* [0x64ca-0x64d6] Bounds check */ if (raw > cal->b_fb_kw_upper_bound) return; if (raw < cal->b_fb_kw_lower_bound) return; + /* [0x64dd] half = raw >> 1 (SHRA — sign-extending) */ int16_t half = shra16(raw, 1); rt->can_half_12a = half; + /* [0x64e5-0x64ea] result = half + RAM[0x150] + RW42 */ int16_t result = (int16_t)(half + rt->setpoint_offset + rt->rw42_state); + + /* [0x64ed-0x64f9] Lower clamp */ if (result < cal->target_5e_min_clamp) { result = cal->target_5e_min_clamp; } @@ -241,57 +262,47 @@ static void s_setpoint_can_decode(pwm_runtime_t *rt, } /* ═════════════════════════════════════════════════════════════════════ - * Stage 1 — Supervisor (s_supervisor @ 0x7d26) - * - * 1:1 re-translation 2026-04-29. Earlier port body was inherited from - * t06211 and used `compensation_angle` (no t06215 producer) and a - * `b_fb_kw_baseline` reset stash (no t06215 binding). The actual t06215 - * supervisor adds the **PI P-term** (DAT_0276) and stashes the - * integrator high word into DAT_02ec on reset (a dead store, but kept - * here for parity). - * - * Disasm: - * 7d26 LDBZE pi_shape_flag-adjacent (R2E = reset_flag) - * 7d2b CMP #1 ; JNE LAB_7d4c - * ; --- reset path --- - * 7d31 reset_flag = 0 - * 7d36 cl_enable_counter (DAT_02ee) = 0 - * 7d3b supervisor_state (RW17E) = 0 - * 7d40-7d45 DAT_02ec = pi_integ_hi (DAT_028a snapshot — dead store) - * 7d4a SJMP LAB_7d5b - * ; --- non-reset path --- - * LAB_7d4c LD RW1C, cl_enable_counter; RW1E = RW1C+1; ST cl_enable_counter - * ; --- common publish --- - * LAB_7d5b SUB RW1C, RW5E, ckp_in - * ADD RW1C, pi_p_term (DAT_0276) - * ST angle_error_raw (DAT_02f0), RW1C - * CMP RW1C, pi_high_clamp_ceiling (DAT_0344) - * JLE exit - * angle_error_raw = pi_high_clamp_ceiling + * Stage 2 — Supervisor (FUN_7beb @ 0x7beb-0x7c41) * ═════════════════════════════════════════════════════════════════════ */ static void s_supervisor(pwm_runtime_t *rt) { + /* Reset-flag branch (0x7beb-0x7c0c). Disasm: + * 7bf6 reset_flag = 0 + * 7bfb cl_enable_counter (DAT_033c) = 0 + * 7c00 supervisor_state (RW17E) = 0 + * 7c05 LD RW1C, DAT_02b4 ; pi_integ_hi + * 7c0a ST RW1C, DAT_033a ; snapshot pi_integ_hi + * The DAT_033a store is a dead-store (no readers anywhere in the + * ROM); kept here for parity. The earlier port assigned + * rt->inputs.b_fb_kw to this slot — that was inherited from the + * family-1 idiom and is wrong for this variant. */ if (rt->reset_flag == 1u) { - rt->reset_flag = 0u; - rt->cl_enable_counter = 0u; - rt->supervisor_state = 0; - rt->pi_integ_hi_snapshot = rt->pi_integ_hi; /* dead-store parity */ + rt->reset_flag = 0u; + rt->cl_enable_counter = 0u; + rt->supervisor_state = 0; + rt->pi_integ_hi_snapshot = rt->pi_integ_hi; /* dead-store parity */ } else { + /* Counter tick (0x7c11-0x7c1b) */ rt->cl_enable_counter = (uint16_t)(rt->cl_enable_counter + 1u); } + /* Error compute (0x7c20-0x7c2b) + * Disasm: SUB RW1C, RW5E, DAT_02f8 — primary setpoint is target_5e. */ int16_t error = (int16_t)(rt->target_5e - rt->inputs.ckp_in); error = (int16_t)(error + rt->pi_p_term); rt->angle_error_raw = error; + /* Ceiling clamp (0x7c30-0x7c41) + * Disasm: CMP RW1C, DAT_0336 — clamp uses RPM-derived ceiling. */ if (error > rt->pi_high_clamp_ceiling) { rt->angle_error_raw = rt->pi_high_clamp_ceiling; } } /* ═════════════════════════════════════════════════════════════════════ - * Stage 2a — Closed-loop correction + * Stage 3a — Closed-loop correction (FUN_5f1f @ 0x5f1f-0x5f66) + * Fingerprint #10 bit-for-bit match to family-1 algorithm. * ═════════════════════════════════════════════════════════════════════ */ static void s_cl_correct(pwm_runtime_t *rt, @@ -299,6 +310,7 @@ static void s_cl_correct(pwm_runtime_t *rt, { int16_t correction; + /* Gate on low byte of cl_enable_counter (0x5f1f-0x5f24). */ if ((rt->cl_enable_counter & 0xFFu) == 0u) { correction = 0; } else { @@ -319,7 +331,7 @@ static void s_cl_correct(pwm_runtime_t *rt, } /* ═════════════════════════════════════════════════════════════════════ - * Stage 2 — Publish CL + * Stage 3 — Publish CL (FUN_7cd8 @ 0x7cd8-0x7cea) * ═════════════════════════════════════════════════════════════════════ */ static void s_publish_cl(pwm_runtime_t *rt, @@ -330,119 +342,202 @@ static void s_publish_cl(pwm_runtime_t *rt, } /* ═════════════════════════════════════════════════════════════════════ - * Stage 3 — PI controller - * - * 1:1 re-translation from t06215 disasm s_pi_update @ 0x542f and - * s_recovery @ 0x53a2 (live Ghidra session, 2026-04-29). Cross-reference: - * the trailing byte rotate at LAB_56e8 (56e8–5708) saves bits 4,5 of the - * current cycle into bits 6,7 for next cycle's s_recovery sustained-band - * detector. + * Stage 4 — PI controller (FUN_67c4 @ 0x67c4 + FUN_66a8 + FUN_672b) * ═════════════════════════════════════════════════════════════════════ */ -/* s_recovery @ 0x53a2. - * - * Disasm: - * 53a2 LDBZE RW1C, pi_shape_flag ; zero-extended into 16-bit - * 53a7 SHL RW1C, #2 ; <<2 (in 16-bit reg) - * 53aa LDBZE RW1E, pi_shape_flag - * 53af AND RW1C, RW1E ; (flag<<2) & flag - * 53b2 CMP RW1C, #0x30 - * 53b6 JNH LAB_5421 ; <= 0x30 → "no sustained" path - * ; --- Sustained large-error detected (this+previous cycle bit4 or bit5) --- - * 53b8 LD RW1C, pi_state_118 - * 53bd CMP RW1C, RAM[0x027a] ; RAM[0x027a] = cal+0x112 boot-cached - * 53c2 JNC LAB_53ea ; counter < threshold → gated-increment - * ; --- Latch path: counter saturated --- - * 53c4 system_flags_110 |= 1 - * 53d1 pi_state_118 = 0 - * 53e1 pi_state_c2 = cal[0x114] ; reload cooldown - * 53e9 RET - * LAB_53ea: ; counter still < threshold - * 53f5 CMP rpm, cal[0x126] - * 53f8 JLE LAB_542e (RET) ; rpm <= threshold → no increment - * 53fa-5407 if (system_flags_110 & 0x30) RET - * 540a if pi_state_c2 != 0 RET - * 5411 pi_state_118 += 1 - * 5420 RET - * LAB_5421: ; no sustained large-error - * 5421 if (system_flags_110 & 1) RET ; latched → keep counter - * 5429 pi_state_118 = 0 - * 542e RET +/* FUN_66a8 — saturation-latch recovery handler. Role-equivalent to the + * default family's `s_recovery` / `fast_recovery`; same name across + * variants for cross-family alignment. Disasm/source mapping (audit + * table — keep the cooldown gate and gated-increment branches in this + * exact order): + * 66ad-66b2 CMPB pi_shape_flag, pi_flag_c6 ; JNE LAB_671d + * 66b9-66be CMP pi_state_118, [0x02c0]=cal+0x112 ; JNC LAB_66e6 + * JNC ⇒ counter < threshold ⇒ gated-increment + * fall-through ⇒ counter ≥ threshold ⇒ latch+reset + * 66c0-66e5 latch bit0 ; counter = 0 ; pi_state_c2 ← cal+0x114 + * 66e6-66f4 CMP rpm, [cal+0x11E] ; JLE exit + * 66f6-6705 JBS bit4 / JBS bit5 — exit + * 6706-670b CMP ZR, pi_state_c2 ; JNE exit + * 670d-6717 pi_state_118 += 1 + * 671d-6725 JBS bit0 exit ; pi_state_118 = 0 */ static void s_recovery(pwm_runtime_t *rt, const pwm_calibration_t *cal) { - uint16_t flag_u16 = (uint16_t)rt->pi_shape_flag; - uint16_t fold = (uint16_t)((flag_u16 << 2) & flag_u16); - - if (fold > 0x30u) { - /* Sustained large-error detected. */ - if ((uint16_t)rt->pi_state_118 < (uint16_t)cal->pi_sat_count_threshold) { - /* Counter still below threshold — gated increment. */ - if ((int16_t)rt->inputs.rpm <= cal->rpm_threshold_recovery) return; - if ((rt->system_flags_110 & 0x10u) != 0u) return; - if ((rt->system_flags_110 & 0x20u) != 0u) return; - if (rt->pi_state_c2 != 0) return; - rt->pi_state_118 = (int16_t)(rt->pi_state_118 + 1); - } else { - /* Saturated — latch + reset. */ - rt->system_flags_110 = (uint8_t)(rt->system_flags_110 | 0x01u); - rt->pi_state_118 = 0; - rt->pi_state_c2 = cal->pi_state_c2_reload; - } - } else { - /* No sustained large-error this cycle. */ + if (rt->pi_shape_flag != rt->pi_flag_c6) { if ((rt->system_flags_110 & 0x01u) == 0u) { rt->pi_state_118 = 0; } + return; } + /* Counter ≥ threshold → latch+reset. Threshold is boot-cached at + * RAM[0x02c0] from cal+0x112 by FUN_6b7b:0x6bd4. */ + if ((uint16_t)rt->pi_state_118 >= (uint16_t)cal->pi_sat_count_threshold) { + rt->system_flags_110 = (uint8_t)(rt->system_flags_110 | 0x01u); + rt->pi_state_118 = 0; + rt->pi_state_c2 = cal->error_thresh_114; + return; + } + /* Gated increment: rpm > rpm_threshold_11E required. */ + if ((int16_t)rt->inputs.rpm <= cal->rpm_threshold_11E) return; + /* Bits 4 or 5 of system_flags abort. */ + if ((rt->system_flags_110 & 0x30u) != 0u) return; + /* c2 cooldown gate — counter advances only after c2 has decayed to 0. */ + if (rt->pi_state_c2 != 0) return; + rt->pi_state_118 = (int16_t)(rt->pi_state_118 + 1); +} + +/* FUN_7c85 @ 0x7c85-0x7cbf — PI integrator step. Anti-windup gates + * the update direction against pi_flag_c7 (0=in-range, 1=clamped low, + * 2=clamped high). Updates the {pi_integ_hi:pi_integ_lo} 32-bit pair. + * Disasm: + * 7c85 LD RW1C, err + * 7c8a MUL RL1C, [0x0456] ; signed 32-bit, RL1C = err*integ_step_normal + * 7c90 SHLL RL1C, #0x4 ; RL1C <<= 4 + * 7c93 JGE LAB_7ca1 ; if signed result >= 0 + * 7c95-7c9d if pi_flag_c7==1 RET ; (clamped-low → don't push more negative) + * 7c9f SJMP LAB_7cab ; else update + * LAB_7ca1: if pi_flag_c7==2 RET ; (clamped-high → don't push more positive) + * LAB_7cab: integrator += RL1C ; ADD lo, ADDC hi + */ +static void s_pi_integrator_step(pwm_runtime_t *rt) +{ + int32_t prod = MUL_S16(rt->angle_error_pi, rt->integ_step_normal); + int32_t step = (int32_t)((uint32_t)prod << 4); + + if (step >= 0) { + if (rt->pi_flag_c7 == 0x02) return; /* anti-windup: clamped-high */ + } else { + if (rt->pi_flag_c7 == 0x01) return; /* anti-windup: clamped-low */ + } + + /* Combine {hi:lo} as int32 -> add step -> split back. */ + int32_t accum = ((int32_t)rt->pi_integ_hi << 16) + | (uint16_t)rt->pi_integ_lo; + accum += step; + rt->pi_integ_lo = (int16_t)(accum & 0xFFFF); + rt->pi_integ_hi = (int16_t)((accum >> 16) & 0xFFFF); +} + +/* FUN_672b @ 0x672b-0x67c3 — PI compensation + final clamp. + * Two arms decided by (pi_flag_338==1 || pi_open_loop_flag==0): + * "fresh" → reset pi_integ_hi from a new (err*open_loop_p_gain>>4)+ckp + * "settled" → step the integrator via FUN_7c85 + * Then compute pi_p_term = (p_gain_normal * err) >> 8 and + * active_request = pi_p_term + pi_integ_hi, clamped to + * [pi_low_clamp, pi_high_clamp_ceiling]. + */ +static void s_pi_compensation(pwm_runtime_t *rt, + const pwm_calibration_t *cal) +{ + int16_t error = rt->angle_error_pi; + + /* [0x672b-0x673a] Branch select. */ + bool fresh = (rt->pi_flag_338 == 0x01) || (rt->pi_open_loop_flag == 0u); + + if (fresh) { + /* [LAB_673c 0x673c-0x6762] Reset pi_integ_hi. Disasm: + * STB ZRlo, DAT_0338 ; pi_flag_338 = 0 + * LD RW1C, err + * EXT RL1C ; sign-extend to 32-bit + * MUL RL1C, [0x0330] ; signed: RL1C = err32 * open_loop_p_gain + * SHRA RW1C, #0x4 ; LOW WORD shifted (high word discarded) + * ADD RW1C, RW1C, ckp_in + * ST RW1C, DAT_02b4 ; pi_integ_hi = result + */ + rt->pi_flag_338 = 0; + int32_t prod = MUL_S16(error, rt->open_loop_p_gain); + int16_t scaled = shra16((int16_t)(prod & 0xFFFF), 4); + rt->pi_integ_hi = (int16_t)(scaled + rt->inputs.ckp_in); + } else { + /* [LAB_6764 0x6764] LCALL FUN_7c85 — integrator step. */ + s_pi_integrator_step(rt); + } + + /* [LAB_6767 0x6767-0x6779] pi_p_term = (RL1C = p_gain_normal*err) >> 8. + * Disasm: + * LD RW1C, [0x0454] ; p_gain_normal (cal+0x118 default +480) + * MUL RL1C, err ; RL1C = p_gain_normal * err (signed) + * SHRAL RL1C, #0x8 ; arithmetic shift right 8 (32-bit) + * ST RW1C, DAT_02b8 ; pi_p_term = lo16 + */ + int32_t comp32 = MUL_S16(rt->p_gain_normal, error); + int16_t comp = (int16_t)(shra32(comp32, 8) & 0xFFFF); + rt->pi_p_term = comp; + + /* [0x677a-0x677f] active_request = pi_p_term + pi_integ_hi */ + int16_t combined = (int16_t)(comp + rt->pi_integ_hi); + rt->active_request = combined; + + /* [0x6782-0x67a8] Lower clamp: if active_request < pi_low_clamp, + * pin to pi_low_clamp and set pi_flag_c7=1. */ + if (rt->active_request < cal->pi_low_clamp) { + rt->active_request = cal->pi_low_clamp; + rt->pi_flag_c7 = 0x01; + return; + } + + /* [LAB_67a9 0x67a9-0x67bd] Upper clamp: if active_request > + * pi_high_clamp_ceiling, pin to ceiling and set pi_flag_c7=2. + * Disasm 0x67a9-0x67b0: CMP RW46, DAT_0336 — upper clamp uses + * the RPM-derived ceiling, not the CAN-decoded primary setpoint. */ + if (rt->active_request > rt->pi_high_clamp_ceiling) { + rt->active_request = rt->pi_high_clamp_ceiling; + rt->pi_flag_c7 = 0x02; + return; + } + + /* [LAB_67be 0x67be-0x67c3] In-range: pi_flag_c7 = 0. */ + rt->pi_flag_c7 = 0; } -/* s_pi_update @ 0x542f. See doc-comment at top of "Stage 3". */ static void s_pi_update(pwm_runtime_t *rt, const pwm_calibration_t *cal) { - /* ── Phase A — Open-loop gate [0x542f-0x5489] ── - * Disasm: bit-test R110.5; CMP RW130 < 0; CMP rpm < flash[0x605c]. */ + /* PI CL-gate threshold = cached ROM[0x605c]. Disasm site: + * FUN_67c4:67d9 `CMP RW1C, DAT_605c` (literal 0x01A4 = 420). In the + * real ECU, cl_gate_input is almost always negative (CAN inactive + * sentinel) which forces OL at line 67d4 before this RPM gate is + * reached. Lifted to cal->pi_cl_rpm_floor. */ + int16_t rpm_floor = cal->pi_cl_rpm_floor; bool open_loop = false; - if ((rt->system_flags_110 & 0x20u) != 0u) open_loop = true; - else if (rt->inputs.cl_gate_input < 0) open_loop = true; - else if ((int16_t)rt->inputs.rpm < cal->pi_cl_rpm_floor) open_loop = true; + if ((rt->system_flags_110 & 0x20u) != 0u) open_loop = true; + else if (rt->inputs.cl_gate_input < 0) open_loop = true; + else if ((int16_t)rt->inputs.rpm < rpm_floor) open_loop = true; if (open_loop) { - /* 0x544b-0x5453: pi_shape_flag |= 0x01 */ - rt->pi_shape_flag = (uint8_t)(rt->pi_shape_flag | 0x01u); - /* 0x5458: DAT_0274 = RW5E */ - rt->pi_preclamp_out = rt->target_5e; - /* 0x5468-0x5482: if (RW5E < cal[0x128]) DAT_0274 = cal[0x128] */ - if (rt->target_5e < cal->pi_low_clamp) { - rt->pi_preclamp_out = cal->pi_low_clamp; + rt->pi_open_loop_flag = 0; + /* Disasm 0x67e5: LD RW46, RW5E — open-loop assigns the CAN-decoded + * primary setpoint, NOT the RPM-derived ceiling. */ + rt->active_request = rt->target_5e; + /* cal+0x120 is a LOWER bound here (disasm: JLT clamps when + * active_request < limit). Same constant also serves as the + * Block-4 low clamp, hence `pi_low_clamp`. See + * families/t06211/src/pi_controller_t06211.c for the full-disasm + * annotation. */ + if (rt->active_request < cal->pi_low_clamp) { + rt->active_request = cal->pi_low_clamp; } - goto trailing_rotate; + goto final_flag; } - /* ── Phase B — Error-band classify [0x5489-0x554e] ── - * Disasm 0x5489: SUB RW1C, RW5E, DAT_02cc → DAT_0278. */ - int16_t err = (int16_t)(rt->target_5e - rt->estimated_angle); - rt->angle_error_pi = err; + /* Disasm 0x680a: SUB RW1C, RW5E, DAT_02cc — CL error uses target. */ + rt->angle_error_pi = (int16_t)(rt->target_5e - rt->estimated_angle); + int16_t err = rt->angle_error_pi; if (err > cal->large_pos_error_thresh) { - /* 0x54a4-0x54af: flag = (flag | 0x10) & 0xDF */ - rt->pi_shape_flag = (uint8_t)((rt->pi_shape_flag | 0x10u) & 0xDFu); + rt->pi_shape_flag = 0; s_recovery(rt, cal); } else if (err < cal->large_neg_error_thresh) { - /* 0x54cd-0x54d8: flag = (flag | 0x20) & 0xEF */ - rt->pi_shape_flag = (uint8_t)((rt->pi_shape_flag | 0x20u) & 0xEFu); - /* 0x54dd-0x54f9: JH check is unsigned; matches CMP RW44, cal[0x116]. */ - if ((uint16_t)rt->inputs.inj_qty_demand > (uint16_t)cal->inj_qty_thresh) { - s_recovery(rt, cal); - } else { + rt->pi_shape_flag = 0x01; + if (rt->inputs.inj_qty_demand <= cal->pi_thresh_116) { rt->pi_state_118 = 0; + } else { + s_recovery(rt, cal); } } else { - /* 0x54fb-0x554e: flag &= 0xCF; decrement c2 (clear latch when 0); decrement 118 */ - rt->pi_shape_flag = (uint8_t)(rt->pi_shape_flag & 0xCFu); - if ((uint16_t)rt->pi_state_c2 != 0u) { + rt->pi_shape_flag = 0x20; + if (rt->pi_state_c2 > 0) { rt->pi_state_c2 = (int16_t)(rt->pi_state_c2 - 1); } if (rt->pi_state_c2 == 0) { @@ -453,125 +548,73 @@ static void s_pi_update(pwm_runtime_t *rt, } } - /* ── Phase C — P-shape into pi_p_term / pi_p_gain_active [0x554e-0x560c] ── */ - int32_t pterm32; /* 32-bit accumulator for the SHRAL (>>8) */ + /* ── Block 4: error-window decision [0x68b7-0x68ef] ── + * Strict > / < per disasm JLE 68c9 / JGE 68ed. P-shape bounds at + * DAT_0450 / DAT_0452 are set once at boot by FUN_76aa — see + * docs/open-questions.md §2 closeout. */ if (err > rt->p_shape_bound_pos) { - /* 0x555a-0x558f: large-positive arm. */ - rt->pi_shape_flag = (uint8_t)(rt->pi_shape_flag | 0x02u); - rt->pi_p_gain_active = rt->integ_step_large_pos; /* DAT_0286 = DAT_0282 */ - int32_t slope_part = MUL_S16((int16_t)(err - rt->p_shape_bound_pos), - rt->p_slope_large_pos); - int32_t anchor_part = MUL_S16(rt->p_gain_normal, rt->p_shape_bound_pos); - pterm32 = slope_part + anchor_part; - rt->pi_p_term = (int16_t)(shra32(pterm32, 8) & 0xFFFF); + rt->active_request = cal->pi_high_clamp; + rt->pi_flag_338 = 0x01; } else if (err < rt->p_shape_bound_neg) { - /* 0x559d-0x55cc: large-negative arm. */ - rt->pi_shape_flag = (uint8_t)(rt->pi_shape_flag | 0x02u); - rt->pi_p_gain_active = rt->integ_step_large_neg; /* DAT_0286 = DAT_0284 */ - int32_t slope_part = MUL_S16((int16_t)(err - rt->p_shape_bound_neg), - rt->p_slope_large_neg); - int32_t anchor_part = MUL_S16(rt->p_gain_normal, rt->p_shape_bound_neg); - pterm32 = slope_part + anchor_part; - rt->pi_p_term = (int16_t)(shra32(pterm32, 8) & 0xFFFF); + rt->active_request = cal->pi_low_clamp; + rt->pi_flag_338 = 0x01; } else { - /* 0x55e2-0x5607: normal-range arm. */ - rt->pi_shape_flag = (uint8_t)(rt->pi_shape_flag & 0xFDu); - rt->pi_p_gain_active = rt->integ_step_normal; /* DAT_0286 = DAT_0456 */ - int32_t prod = MUL_S16(rt->p_gain_normal, err); - rt->pi_p_term = (int16_t)(shra32(prod, 8) & 0xFFFF); + s_pi_compensation(rt, cal); } - /* ── Phase D — Integrator gate [0x560c-0x5679] ── - * Disasm 0x560c-0x5611: if pi_shape_flag.bit0 set (open-loop, but we - * returned earlier in that case) → take fast-path that overrides - * pi_integ_hi from open_loop_p_gain. We never enter here in the - * closed-loop path, but preserve the structure. - * 0x563b onward: closed-loop integrator step with anti-windup. */ - if ((rt->pi_shape_flag & 0x01u) != 0u) { - /* Open-loop fast-path (defensive — the open-loop early-return - * above means this branch is unreachable from the closed-loop - * path; preserved here in case a future cycle enters with bit 0 - * latched from elsewhere). */ - int32_t prod = MUL_S16(rt->open_loop_p_gain, err); - int16_t shifted = shra16((int16_t)(prod & 0xFFFF), 4); - rt->pi_integ_hi = (int16_t)(shifted + rt->inputs.ckp_in); - rt->pi_shape_flag = (uint8_t)(rt->pi_shape_flag & 0xFEu); - } else { - /* 0x563b-0x5654: anti-windup gate via previous-cycle clamp bits. */ - bool gate_skip; - if (err < 0) { - gate_skip = (rt->pi_shape_flag & 0x04u) != 0u; /* bit 2: clamped low */ - } else { - gate_skip = (rt->pi_shape_flag & 0x08u) != 0u; /* bit 3: clamped high */ - } - if (!gate_skip) { - /* 0x5657-0x5674: 32-bit signed integrator step. - * step = (err * pi_p_gain_active) << 4 - * {hi:lo} += step - * MCS-96 SHLL is logical, so cast through unsigned. */ - int32_t prod = MUL_S16(err, rt->pi_p_gain_active); - int32_t step = (int32_t)((uint32_t)prod << 4); - int32_t accum = ((int32_t)rt->pi_integ_hi << 16) - | (uint16_t)rt->pi_integ_lo; - accum += step; - rt->pi_integ_lo = (int16_t)(accum & 0xFFFF); - rt->pi_integ_hi = (int16_t)((accum >> 16) & 0xFFFF); - } - } - - /* ── Phase E — Combine + clamp [0x5679-0x56e3] ── */ - rt->pi_preclamp_out = (int16_t)(rt->pi_p_term + rt->pi_integ_hi); - - if (rt->pi_preclamp_out < cal->pi_low_clamp) { - /* 0x5696-0x56b6: clamp low; flag = (flag & 0xF7) | 4 */ - rt->pi_preclamp_out = cal->pi_low_clamp; - rt->pi_shape_flag = (uint8_t)((rt->pi_shape_flag & 0xF7u) | 0x04u); - } else if (rt->pi_preclamp_out > rt->pi_high_clamp_ceiling) { - /* 0x56c2-0x56d9: clamp high; flag = (flag & 0xFB) | 8 */ - rt->pi_preclamp_out = rt->pi_high_clamp_ceiling; - rt->pi_shape_flag = (uint8_t)((rt->pi_shape_flag & 0xFBu) | 0x08u); - } else { - /* 0x56db-0x56e0: in-range — clear bits 2,3 */ - rt->pi_shape_flag = (uint8_t)(rt->pi_shape_flag & 0xF3u); - } - -trailing_rotate: - /* ── Trailing rotate [LAB_56e8 0x56e8-0x5708] ── - * Disasm 0x56e8: ST DAT_0274, RW46 ; publish active_request - * 0x56ed-0x5703: pi_shape_flag = (flag & 0x3F) | ((flag<<2) & 0xC0) - * Saves this cycle's bits 4,5 into bits 6,7 for the next cycle's - * s_recovery sustained-band detector. - */ - rt->active_request = rt->pi_preclamp_out; - { - uint8_t f = rt->pi_shape_flag; - rt->pi_shape_flag = (uint8_t)((f & 0x3Fu) | (uint8_t)((f << 2) & 0xC0u)); - } +final_flag: + rt->pi_open_loop_flag = 0x01; + rt->pi_flag_c6 = rt->pi_shape_flag; } /* ═════════════════════════════════════════════════════════════════════ - * Stage 4 — PWM output (FUN_5314 @ 0x5314-0x565f) + * Stage 5 — PWM output (FUN_5314 @ 0x5314-0x565f) * ═════════════════════════════════════════════════════════════════════ */ static void s_pwm_output(pwm_runtime_t *rt, const pwm_calibration_t *cal) { + /* A. eval pwm_A */ s_eval_submap(&pwm_submap_descrs[PWM_SUBMAP_PWM_A], &rt->pwm_slot_a); + + /* B. eval pwm_B */ s_eval_submap(&pwm_submap_descrs[PWM_SUBMAP_PWM_B], &rt->pwm_slot_b); + + /* C. combine bilinear */ int16_t duty = s_combine(cal->pwm_y_table, &rt->pwm_slot_a, &rt->pwm_slot_b); rt->pwm_duty = (uint16_t)duty; + /* D. Duty-range classification [0x5361-0x538b] — based on pre-shape duty. */ if (rt->pwm_duty < 0x29u) rt->pwm_duty_range_flag = 1u; else if (rt->pwm_duty > 0xFD7u) rt->pwm_duty_range_flag = 2u; else rt->pwm_duty_range_flag = 0u; + /* E1. RPM-window three-phase matcher [0x53be-0x552a]. + * + * Phase 1 (strict-band): rpm strictly inside any of 4 bands at + * cal+0xF2 → slew_increment = +pwm_slew_step. + * Phase 2 (hysteresis margin): rpm in any band's halfwidth-extended + * region → HOLD (preserve previous slew_increment). + * Phase 3 (deep-out): rpm clear of every extended band AND + * pwm_period < pwm_period_max → slew_increment = -pwm_slew_step. + * + * Slew applied as `pwm_period -= slew_increment` (subtraction; + * positive increment shrinks period → faster PWM). + * pwm_slew_increment lives in rt and persists across cycles so the + * HOLD path keeps the previous direction. + * + * Disasm note: high-edge tests use JC (db) at 0x5463/0x5497 for + * bands 0/1 and JNC (d3) at 0x54cb/0x54fd for bands 2/3 — these + * encodings produce equivalent control flow (HOLD on rpm < hi+hw, + * advance on rpm >= hi+hw). See open-questions.md §5 closeout. */ uint16_t *pwm_period = (uint16_t *)&rt->pwm_shape_state[0]; const int16_t rpm_s = (int16_t)rt->inputs.rpm; const int16_t halfwidth = cal->pwm_window_halfwidth; const int16_t step_mag = cal->pwm_slew_step; + /* Phase 1 — strict-band test [0x53be-0x542d] */ int slew_set = 0; for (int bi = 0; bi < 4; bi++) { int16_t lo = cal->pwm_rpm_windows[bi * 2]; @@ -584,31 +627,38 @@ static void s_pwm_output(pwm_runtime_t *rt, } if (!slew_set) { + /* Phase 2 — fine hysteresis margin [0x5434-0x54fe] */ int phase3 = 1; for (int bi = 0; bi < 4; bi++) { int16_t lo = cal->pwm_rpm_windows[bi * 2]; int16_t hi = cal->pwm_rpm_windows[bi * 2 + 1]; if (rpm_s <= (int16_t)(lo - halfwidth)) { - continue; + continue; /* below extended low: try next band */ } if (rpm_s < (int16_t)(hi + halfwidth)) { - phase3 = 0; + phase3 = 0; /* HOLD */ break; } + /* rpm_s >= hi + halfwidth: advance to next band */ } if (phase3 && *pwm_period < cal->pwm_period_max) { + /* Phase 3 — deep-out negative slew [0x54ff-0x551b] */ rt->pwm_slew_increment = (int16_t)(-step_mag); } + /* else: HOLD (no write) */ } + /* Slew application [0x5520-0x552a] */ int32_t period_next = (int32_t)*pwm_period - (int32_t)rt->pwm_slew_increment; if (period_next < 0) period_next = 0; if (period_next > 0xFFFF) period_next = 0xFFFF; *pwm_period = (uint16_t)period_next; + /* Final clamp [0x552f-0x5575] */ if (*pwm_period < cal->pwm_period_min) *pwm_period = cal->pwm_period_min; if (*pwm_period > cal->pwm_period_max) *pwm_period = cal->pwm_period_max; + /* E2. Shape detail [0x557a-0x5608] */ pwm_interp_slot_t shape_slot; s_eval_submap(&pwm_submap_descrs[PWM_SUBMAP_SHAPE_EVAL], &shape_slot); @@ -617,6 +667,11 @@ static void s_pwm_output(pwm_runtime_t *rt, if (shape_height > 0x199) shape_height = 0x199; rt->pwm_shape_state[5] = shape_height; + /* E3. Shape composition additive [0x55cb-0x560f]. ROM formula: + * slope = (period_max - period_min) >> 8 + * numerator = ((period_max - pwm_period) >> 8) * shape_height + * pwm_duty += numerator / slope + * Shifts happen before multiply (MCS-96 16-bit intermediate). */ int16_t slope = (int16_t)(((int32_t)cal->pwm_period_max - (int32_t)cal->pwm_period_min) >> 8); int16_t pmx_delta = (int16_t)(((int32_t)cal->pwm_period_max @@ -626,10 +681,14 @@ static void s_pwm_output(pwm_runtime_t *rt, : 0; int32_t duty_new = (int32_t)rt->pwm_duty + shape_add; + /* F. Duty bounds clamp [0x5614-0x5636] — ROM reads RAM[0x6058]/[0x605a] + * (producers open-questions §3). Lifted to cal->pwm_min/cal->pwm_max + * (defaults 205/3890, same as the default family). */ if (duty_new < (int32_t)cal->pwm_min) duty_new = (int32_t)cal->pwm_min; if (duty_new > (int32_t)cal->pwm_max) duty_new = (int32_t)cal->pwm_max; rt->pwm_duty = (uint16_t)duty_new; + /* G. HW shadow writes [0x563b-0x565f] using the slewed pwm_period. */ uint32_t on_product = (uint32_t)(*pwm_period) * (uint32_t)rt->pwm_duty; rt->pwm_on_time = (uint16_t)(on_product / 0xFFFu); rt->pwm_off_time = (uint16_t)(*pwm_period - rt->pwm_on_time); @@ -637,7 +696,10 @@ static void s_pwm_output(pwm_runtime_t *rt, } /* ═════════════════════════════════════════════════════════════════════ - * Bypass-PI LUT helper + * Bypass-PI LUT helper: query the ROM Y-table directly with (rpm, fbkw) + * as the two axes, applying the same eval+combine+clamp the PWM stage + * does. Useful for plotting the static (rpm, fbkw) → duty surface + * without the PI controller in the loop. * ═════════════════════════════════════════════════════════════════════ */ uint16_t pwm_lut_duty(const pwm_calibration_t *cal, @@ -658,7 +720,8 @@ uint16_t pwm_lut_duty(const pwm_calibration_t *cal, } /* ═════════════════════════════════════════════════════════════════════ - * Public entry — pwm_service (mirrors pwm_service @ 0x7780) + * Public entry — pwm_service + * Mirrors FUN_77b3 (0x77b3-0x77d8) — 5-call linear dispatcher. * ═════════════════════════════════════════════════════════════════════ */ void pwm_service(pwm_runtime_t *rt) @@ -667,10 +730,10 @@ void pwm_service(pwm_runtime_t *rt) const pwm_calibration_t *cal = rt->bound_cal; - s_setpoint(rt); /* writes pi_high_clamp_ceiling */ - s_setpoint_can_decode(rt, cal); - s_supervisor(rt); - s_publish_cl(rt, cal); - s_pi_update(rt, cal); - s_pwm_output(rt, cal); + s_setpoint(rt); /* [0x77b3-0x77c7] pi_high_clamp_ceiling = setpoint_interp(rpm) */ + s_setpoint_can_decode(rt, cal); /* CAN-decoded target (FUN_64c3 in ROM) */ + s_supervisor(rt); /* [0x77cc] */ + s_publish_cl(rt, cal);/* [0x77cf] */ + s_pi_update(rt, cal); /* [0x77d2] */ + s_pwm_output(rt, cal);/* [0x77d5] */ } diff --git a/Core/Advance_Control/pwm.h b/Core/Advance_Control/pwm.h index e1e0e9a..5aef383 100644 --- a/Core/Advance_Control/pwm.h +++ b/Core/Advance_Control/pwm.h @@ -1,20 +1,21 @@ /** - * @file pwm.h (families/T06215/compact_src) - * @brief Compact single-header API for the T06215 PWM controller. + * @file pwm.h (families/t06211/compact_src) + * @brief Compact single-header API for the t06211 PWM controller. * - * Variant note: this header was originally forked from t06211 and carried - * runtime/cal field names that embedded RAM addresses (e.g. `pi_b4_state`, - * `target_336`). Those addresses were t06211-correct but t06215-wrong. - * Field names here are now purely semantic; the address binding for any - * field lives in `pwm_addr_map.h` (documentation-only; not included by - * pwm.c). + * Mirrors the public shape of the default family's `compact_src/pwm.h` + * but tailored to t06211's 5-stage pipeline (no standalone max_cl_error + * lookup, single-submap setpoint, factor+offset target compute). * - * Public API: + * Public API is just two functions: * pwm_init() — one-time setup - * pwm_service() — per-cycle update (5-stage pipeline) + * pwm_service() — per-cycle update (pulls inputs via getters, + * runs the 5-stage pipeline, writes rt outputs) * - * External inputs arrive through a getter vtable; callbacks return one - * signal in native PWM units. + * External inputs arrive through a getter vtable; each callback returns + * one signal in native PWM units. + * + * Every arithmetic choice mirrors the MCS-96 assembly; see + * families/t06211/src/ for per-stage commentary. */ #ifndef PWM_H #define PWM_H @@ -43,37 +44,49 @@ static inline int32_t shra32(int32_t v, unsigned n) { /* ══════════════════════════════════════════════════════════════════════ * 1D INTERPOLATION SLOT + * Layout matches family-1's pwm_interp_slot_t and the family-1 ROM + * convention [count*2, range, offset, seg_bytes] — t06211's FUN_6fb8 + * fingerprints identically, so the same layout applies. * ══════════════════════════════════════════════════════════════════════ */ typedef struct pwm_interp_slot { - int16_t row_stride; - int16_t x_interval; - int16_t x_offset; - int16_t y_byte_off; + int16_t row_stride; /* count * 2 (byte stride for Y-table rows) */ + int16_t x_interval; /* x[k-1] - x[k] */ + int16_t x_offset; /* input - x[k] */ + int16_t y_byte_off; /* k * 2 */ } pwm_interp_slot_t; /* ══════════════════════════════════════════════════════════════════════ * EXTERNAL INPUTS + * Populated each cycle by pwm_service via the getter vtable. + * t06211-specific set: no temperature, no angle_dec_cmd in the setpoint + * (the family-2 setpoint is single-submap RPM-indexed). * ══════════════════════════════════════════════════════════════════════ */ typedef struct pwm_inputs { - int16_t ckp_in; /* sensed position */ - uint16_t rpm; /* engine RPM */ - int16_t angle_dec_cmd; /* accepted for cross-family API parity; unused here */ - int16_t inj_qty_demand; /* CAN: injection-quantity demand */ - int16_t b_fb_kw; /* CAN: plunger feedback baseline (drives target) */ - int16_t cl_gate_input; /* CAN: open/closed-loop discriminant (was state_130) */ - uint16_t supply_voltage; /* shape_eval submap input */ - int16_t temperature; /* accepted for cross-family API parity; unused here */ + int16_t ckp_in; /* 0x02f8 — sensed position */ + uint16_t rpm; /* 0x0040 — engine RPM */ + int16_t angle_dec_cmd; /* 0x0042 — accepted for family-1 API parity; unused here */ + int16_t inj_qty_demand; /* 0x0044 — CAN: inj quantity demand (PI large-neg gate) */ + int16_t b_fb_kw; /* CAN: plunger feedback baseline. Gets copied into + can_raw_b_fb_kw and decoded by s_setpoint_can_decode + (port of FUN_64c3) to produce target. */ + int16_t cl_gate_input; /* 0x0130 — CAN: open/closed-loop discriminant */ + uint16_t supply_voltage; /* 0x0142 — shape_eval submap input */ + int16_t temperature; /* 0x0146 — accepted for family-1 API parity; unused here */ } pwm_inputs_t; +/** Getter vtable. 8-callback layout matches family-1's pwm_input_getters_t + * so a single FBKW.c can drive either family's pwm.c without changes. + * t06211 does not consume angle_dec_cmd or temperature; those getters are + * called and stored into rt->inputs but the pipeline ignores them. */ typedef struct pwm_input_getters { int16_t (*ckp_in) (void *ctx); uint16_t (*rpm) (void *ctx); - int16_t (*angle_dec_cmd) (void *ctx); + int16_t (*angle_dec_cmd) (void *ctx); /* accepted; unused by t06211 */ int16_t (*inj_qty_demand)(void *ctx); int16_t (*b_fb_kw) (void *ctx); - int16_t (*cl_gate_input) (void *ctx); + int16_t (*cl_gate_input) (void *ctx); uint16_t (*supply_voltage)(void *ctx); - int16_t (*temperature) (void *ctx); + int16_t (*temperature) (void *ctx); /* accepted; unused by t06211 */ void *ctx; } pwm_input_getters_t; @@ -82,95 +95,135 @@ typedef struct pwm_submap_descr pwm_submap_descr_t; /* ══════════════════════════════════════════════════════════════════════ * RUNTIME STATE - * Field names are purely semantic. ROM-address bindings live in - * pwm_addr_map.h (documentation-only). + * RAM-address comments reference the t06211 MCS-96 map (see + * families/t06211/docs/variable-glossary.md). * ══════════════════════════════════════════════════════════════════════ */ typedef struct pwm_runtime { /* External inputs — refreshed each cycle from getters */ pwm_inputs_t inputs; - /* Async flags. */ - uint8_t reset_flag; - uint8_t system_flags_110; /* bit 5 = OL force; bit 0 = recovery latch */ + /* Async flags. + * reset_flag — set on reset edge; supervisor clears. + * system_flags_110 — bit 5 forces open-loop; bit 0 is fast-recovery latch. + */ + uint8_t reset_flag; /* 0x002e */ + uint8_t system_flags_110; /* 0x0110 */ - /* ── Setpoint architecture ── */ - /* target_5e: PRIMARY CAN-decoded setpoint (RW5E in t06215). */ - int16_t target_5e; - /* pi_high_clamp_ceiling: SECONDARY RPM-derived ceiling — written each - * cycle by pwm_service from the setpoint submap interp; consumed as - * the PI integrator output upper-clamp inside s_pi_update and the - * supervisor error ceiling. (DAT_0344 in t06215; was misnamed - * target_336 in the original fork from t06211.) */ + /* ── Dual-target setpoint architecture ── */ + /* target (RW5E = RAM[0x005e]): PRIMARY CAN-driven setpoint + * computed by FUN_64c3 (called from CAN parser FUN_6192). Drives + * the PI controller in both open-loop (FUN_67c4:67e5) and + * closed-loop (FUN_67c4:680a) paths, and supervisor error compute + * (FUN_7beb:7c20). Formula: raw/2 + setpoint_offset + rw42_state, + * clamped >= cal->target_5e_min_clamp. */ + int16_t target_5e; /* RW5E — primary CAN-decoded setpoint */ + /* pi_high_clamp_ceiling (DAT_0336): SECONDARY RPM-derived ceiling + * computed by FUN_7168(setpoint_descr) + FUN_77b3:77c7. Used ONLY as + * upper-clamp in FUN_672b PI compensation (0x67a9/0x67b0) and + * FUN_7beb supervisor (0x7c30/0x7c37). Does not drive control + * directly. (Was misnamed `target_336` across earlier ports — the + * address-suffixed style hid that this is a PI clamp, not a target.) */ int16_t pi_high_clamp_ceiling; - - /* CAN-staging buffers — written before pwm_service so s_setpoint_can_decode - * can transform them into target_5e. */ - int16_t can_raw_b_fb_kw; - int16_t can_aux_12e; - int16_t can_half_12a; - int16_t setpoint_offset; /* runtime copy of cal->setpoint_offset */ - int16_t rw42_state; - int16_t angle_error_raw; /* DAT_02f0 — supervisor's published error */ - int16_t pi_integ_hi_snapshot; /* DAT_02ec — supervisor reset stash; dead store but kept for parity */ - uint16_t cl_enable_counter; /* DAT_02ee */ + /* CAN-staging buffers — written externally before pwm_service so + * s_setpoint_can_decode can transform them into target. */ + int16_t can_raw_b_fb_kw; /* 0x012c — raw b_fb_kw from CAN */ + int16_t can_aux_12e; /* 0x012e — secondary CAN field (drives rw42_state) */ + int16_t can_half_12a; /* 0x012a — cached raw>>1 (debug visibility) */ + /* setpoint_offset (RAM[0x0150]) is copied at runtime_init from + * cal.setpoint_offset (mirrors family-1's pattern at + * compact_src/pwm.c:66). The REAL ROM derives it at boot via + * FUN_6b7b @ 0x6bb5: RAM[0x150] = cal+0x4c - RW1E. Because RW1E's + * source at the call site (0x6c46) is in a Ghidra-unrecognised code + * region, we expose the field directly through cal so the user can + * set it to the match-fitted value without needing to simulate the + * full FUN_6b7b boot path. */ + int16_t setpoint_offset; /* runtime copy of cal->setpoint_offset (DAT_0150 in ROM) */ + int16_t rw42_state; /* RW42 register — = can_aux_12e when valid */ + /* DAT_033a — supervisor reset stash: snapshots pi_integ_hi at the + * reset edge (FUN_7beb:7c05-7c0a). Dead store (no readers anywhere + * in the ROM); kept here for parity with the disasm. NOT a baseline + * of b_fb_kw — that name was inherited from the family-1 idiom and + * is incorrect for this variant. */ + int16_t pi_integ_hi_snapshot; + /* PI P-term — produced by FUN_672b @ 0x6775 as `(p_gain_normal * err) >> 8` + * and consumed by the supervisor at FUN_7beb:0x7c26 as the additive + * shaping term on the error. (DAT_02b8 in ROM; was named + * `compensation_angle` in earlier ports.) */ + int16_t pi_p_term; + int16_t angle_error_raw; /* DAT_033e — supervisor output (raw + ceiling-clamped) */ + uint16_t cl_enable_counter; /* DAT_033c */ /* ── CL correction ── */ - int16_t cl_correction_raw; - int16_t angle_offset; - int16_t supervisor_state; - int16_t pos_error_normalizer; /* runtime copy of cal->pos_error_normalizer */ - int16_t neg_error_normalizer; /* runtime copy of cal->neg_error_normalizer */ + int16_t cl_correction_raw; /* DAT_0176 */ + int16_t angle_offset; /* DAT_017c */ + int16_t supervisor_state; /* RW17E — CL accumulator */ + int16_t pos_error_normalizer; /* runtime copy of cal->init_pos_error_normalizer (DAT_0332 in ROM) */ + int16_t neg_error_normalizer; /* runtime copy of cal->init_neg_error_normalizer (DAT_0334 in ROM) */ /* ── Publish + PI ── */ - int16_t estimated_angle; - int16_t angle_error_pi; /* target_5e − estimated_angle (DAT_0278) */ - int16_t active_request; /* PI output = pre-clamp output (RW46 + DAT_0274) */ + int16_t estimated_angle; /* DAT_02cc */ + int16_t angle_error_pi; /* DAT_02be — target_5e − estimated_angle */ + int16_t active_request; /* RW46 — published PI output / PWM feed-forward */ + uint8_t pi_open_loop_flag; /* DAT_02c4 */ + uint8_t pi_shape_flag; /* DAT_02c5 */ + uint8_t pi_flag_c6; /* DAT_02c6 — previous-cycle pi_shape_flag latch */ + uint8_t pi_flag_338; /* DAT_0338 — Block-4 cal-pinned latch */ - /* PI flag byte (DAT_028c). Bit layout (s_pi_update @ 0x542f): - * bit 0 = open-loop indicator (consumed by integrator gate) - * bit 1 = P-shape large-error arm taken - * bit 2 = output clamped to pi_low_clamp this cycle - * bit 3 = output clamped to pi_high_clamp_ceiling this cycle - * bit 4 = error > large_pos_error_thresh this cycle - * bit 5 = error < large_neg_error_thresh this cycle - * bits 6,7 = previous cycle's bits 4,5 (rotated by trailing op) */ - uint8_t pi_shape_flag; + /* P-shape segment bounds (boot-init from cal+0x10A/0x10C). PI Block-4 + * error-window bounds (independent int16s, NOT a 32-bit integrator). + * Trim bytes at RAM[0x0414]/[0x0416] have no writers in the ROM, so + * bounds resolve to the cal bases verbatim. See open-questions §2. */ + int16_t p_shape_bound_pos; /* DAT_0450 */ + int16_t p_shape_bound_neg; /* DAT_0452 */ - /* PI working state used by s_pi_update */ - int16_t pi_p_term; /* DAT_0276 — output of P-shape segment */ - int16_t pi_p_gain_active; /* DAT_0286 — selected gain for integrator step */ - int16_t pi_preclamp_out; /* DAT_0274 — pre-publish active_request */ + int16_t pi_state_118; /* DAT_0118 — recovery counter */ + int16_t pi_state_c2; /* DAT_02c2 — cooldown counter */ - /* PI integrator pair {hi:lo} = {DAT_028a:DAT_0288} */ - int16_t pi_integ_lo; - int16_t pi_integ_hi; + /* PI integrator pair {hi:lo} at {DAT_02b4 : DAT_02b6}. Disasm + * `disasm_nav rw 0x02b4` shows only internal writers (FUN_672b, + * FUN_76aa, FUN_7c85), confirming this is a PI integrator state, + * not an external/CAN input. (Was misnamed `b_fb_kw` in the original + * glossary; renamed via the address-suffixed `pi_b4_state`/`pi_b6_state` + * intermediate; final semantic name aligns with the cross-family pair.) */ + int16_t pi_integ_hi; /* DAT_02b4 — integrator high word */ + int16_t pi_integ_lo; /* DAT_02b6 — integrator low word */ - /* P-shape segment bounds (boot-init from cal+0x10A/0x10C). */ - int16_t p_shape_bound_pos; - int16_t p_shape_bound_neg; + /* PI gains — boot-set by FUN_76aa from cal scalars (with optional + * trim bytes at RAM[0x0410]/[0x0412] which have no writers anywhere + * in the ROM, so the cal bases pass through unchanged). */ + int16_t p_gain_normal; /* DAT_0454 — boot from cal+0x118; multiplier in pi_p_term = (gain*err)>>8 */ + int16_t integ_step_normal; /* DAT_0456 — boot from cal+0x11A; multiplier in FUN_7c85 integrator step */ + int16_t open_loop_p_gain; /* DAT_0330 — boot from cal+0x11C byte (clamped ≤15); used in `pi_integ_hi = (gain*err)>>4 + ckp` reset branch */ - /* P-shape gains and integrator steps (boot-init from cal). */ - int16_t p_gain_normal; /* DAT_0454 — normal-range P-term multiplier */ - int16_t integ_step_normal; /* DAT_0456 — normal-range integrator multiplier */ - int16_t p_slope_large_pos; /* DAT_027e */ - int16_t p_slope_large_neg; /* DAT_0280 */ - int16_t integ_step_large_pos; /* DAT_0282 */ - int16_t integ_step_large_neg; /* DAT_0284 */ - int16_t open_loop_p_gain; /* DAT_033e (byte, boot-clamped to 15) */ - - int16_t pi_state_118; /* recovery counter */ - int16_t pi_state_c2; /* cooldown counter */ + /* Anti-windup flag set by FUN_672b clamps; consulted by FUN_7c85 + * to gate integration direction. Values: 0 (in-range), 1 (clamped + * low), 2 (clamped high). */ + uint8_t pi_flag_c7; /* DAT_02c7 */ /* ── PWM output ── */ - uint16_t pwm_duty; - uint16_t pwm_on_time; - uint16_t pwm_off_time; + uint16_t pwm_duty; /* 0x02d2 */ + uint16_t pwm_on_time; /* 0x02ce */ + uint16_t pwm_off_time; /* 0x02d0 */ + /* pwm_period — total period in Timer2 ticks = on_time + off_time. + * Mirrored from t06211 RAM[0x02e4] (computed each cycle by the PWM + * output stage). Exposed for family-1 FBKW.c API parity + * (UpdatePWM(&htim4, ch, pwm_on_time, pwm_period)). */ uint16_t pwm_period; - uint8_t pwm_duty_range_flag; - pwm_interp_slot_t pwm_slot_a; - pwm_interp_slot_t pwm_slot_b; - int16_t pwm_shape_state[6]; - int16_t pwm_slew_increment; + uint8_t pwm_duty_range_flag; /* 0x00d1 */ + pwm_interp_slot_t pwm_slot_a; /* 0x02d4 */ + pwm_interp_slot_t pwm_slot_b; /* 0x02dc */ + /* pwm_shape_state[6] mirrors RAM[0x02e4..0x02ee]: + * [0] pwm_period working value (RAM 0x02e4) + * [1] (unused — was slew_increment, broken out as field below) + * [2] (RAM 0x02e8 — ROM band-array ptr, unused in C) + * [3] (RAM 0x02ea — ROM halfwidth ptr, unused in C) + * [4] (RAM 0x02ec — slew step magnitude, unused; read from cal) + * [5] shape_height (RAM 0x02ee, E2 output) */ + int16_t pwm_shape_state[6]; /* 0x02e4-0x02ee */ + /* Persistent slew_increment (RAM[0x02e6]). Carried across cycles so + * the hysteresis-margin HOLD path can preserve previous direction. + * Subtracted from pwm_period each cycle. See open-questions §5. */ + int16_t pwm_slew_increment; /* 0x02e6 */ /* ── Bindings (set once by pwm_init) ── */ const pwm_calibration_t *bound_cal; @@ -179,96 +232,122 @@ typedef struct pwm_runtime { /* ── Calibration (decoded ROM values) ───────────────────────────────── */ struct pwm_calibration { - /* PI controller error-band thresholds */ + /* Scalars (from families/t06211/cal_offsets.py FLASH_OFFSETS) */ int16_t large_pos_error_thresh; /* cal+0x10E */ int16_t large_neg_error_thresh; /* cal+0x110 */ - int16_t pi_low_clamp; /* cal+0x128 — output low clamp + open-loop lower bound */ - int16_t pi_high_clamp; /* fallback upper clamp; runtime uses pi_high_clamp_ceiling */ + int16_t pi_low_clamp; /* cal+0x120 = -512 — Block-4 low clamp + open-loop lower bound */ + int16_t pi_high_clamp; /* cal+0x124 = +1707 — Block-4 high clamp */ - /* CAN-decoded setpoint (FUN_64c3 family) cal constants */ - int16_t b_fb_kw_upper_bound; /* cal+0x004 */ - int16_t b_fb_kw_lower_bound; /* cal+0x006 */ - int16_t setpoint_offset; /* = cal+0x4c − cal+0x4e */ - int16_t target_5e_min_clamp; /* cal+0x12A */ - int16_t can_aux_12e_max; /* cal+0x002 */ + /* CAN-decoded setpoint (FUN_64c3) cal constants */ + int16_t b_fb_kw_upper_bound; /* cal+0x004 = +7680 — raw b_fb_kw upper sanity bound */ + int16_t b_fb_kw_lower_bound; /* cal+0x006 = -768 — raw b_fb_kw lower sanity bound */ - /* CKP-zero acquisition (FUN_70d8 chain) cal constants — see - * docs/algorithm-ckp-zero-acquisition.md. T06215 cal slot offsets - * differ from T06235 (T06235 has +0x02 lower-region shift). */ - int16_t ckp_zero_anchor; /* cal+0x04E — FUN_70d8 additive anchor */ + /* setpoint_offset — static bias added after halving raw b_fb_kw. + * Family-1 analog: CAL+0x0052 - CAL+0x0054 (compact_src/pwm.h:210). + * For t06211, FUN_6b7b computes the equivalent as cal+0x4c - cal+0x4e + * (= 3499 - 4156 = -657) at boot and stores at RAM[0x150]. + * Copied into runtime.setpoint_offset by runtime_reset. */ + int16_t setpoint_offset; /* = cal+0x4c - cal+0x4e */ + int16_t target_5e_min_clamp; /* cal+0x122 = -512 — RW5E lower clamp */ + int16_t can_aux_12e_max; /* cal+0x002 — upper bound for RAM[0x12e] (FUN_649e) */ + + /* CKP-zero acquisition (FUN_6b67 chain — caller @ 0x65c7). + * Slot offsets verified at FUN_6b67:0x6b71/0x6b81/0x6b93 + Stage-1 + * caller @ 0x654f/0x656d. */ + int16_t ckp_zero_anchor; /* cal+0x04E — FUN_6b67 additive anchor */ int16_t can_dckp_offset_bias; /* cal+0x050 — Stage-1/Stage-2 bias */ - int16_t ckp_modulus; /* cal+0x0A0 — FUN_70d8 modulo wrap (also reused - * by the FUN_7293 process-tooth derivation + int16_t ckp_modulus; /* cal+0x0A0 — FUN_6b67 modulo wrap (also + * reused by FUN_6d4a process-tooth * below; high byte = ckp_modulus>>8 = 30). */ - /* CKP process-tooth derivation (FUN_7293 @ 0x7293 — analog of default- - * family FUN_87ea @ 0x87ea). Computes the byte stored to R90: a - * segment-counter view of (CKP_ZERO_OFFSET + advance) with wrap rules. - * Consumed by get_ckp_process_tooth() in ckp_acquisition.c. */ - int16_t ckp_advance_per_tick; /* cal+0x12C — angular advance added to - * word[0x152] before tooth derivation - * (T06215: 1707 = 20° at 85.33/°). */ - int16_t ckp_seg_wrap_threshold; /* cal+0x09D — tooth-counter test: - * if tooth > threshold → tooth -= 30 - * (T06215: 29). */ - int16_t ckp_teeth_per_seg; /* cal+0x09E — per-segment clamp: - * if tooth > teeth_per_seg → tooth = 0 - * (T06215: 26 teeth / 90° segment). */ + /* CKP process-tooth derivation (FUN_6d4a @ 0x6d4a — analog of T06215 + * FUN_7293 / default FUN_87ea). Same body shape; cal slot for the + * per-tick advance is at cal+0x124 (NOT cal+0x12C as in T06215), + * verified as the sole `TABLE[RWA4]` reader at FUN_6d4a:0x6d6a. + * Consumed by get_ckp_process_tooth() in ckp_acquisition.c. + * + * Note: cal+0x124 is **aliased** with `pi_high_clamp` (FUN_67c4 + * reads it via the indirect cal-address idiom at 0x68c3/0x68d9). + * The value 1707 happens to serve both roles in this ROM. */ + int16_t ckp_advance_per_tick; /* cal+0x124 — angular advance per tick + * (t06211: 1707 = same value as + * pi_high_clamp; alias by coincidence). */ + int16_t ckp_seg_wrap_threshold; /* cal+0x09D — tooth-counter test + * (t06211: 29; if tooth > threshold + * → tooth -= 30). */ + int16_t ckp_teeth_per_seg; /* cal+0x09E — per-segment clamp + * (t06211: 26 teeth / 90° segment; + * if tooth > teeth_per_seg → 0). */ + int16_t error_thresh_114; /* cal+0x114 */ + int16_t pi_thresh_116; /* cal+0x116 */ + /* pi_state_118 saturation threshold — when the recovery counter + * reaches this, FUN_66a8 latches bit0 of system_flags_110, zeroes + * the counter, and reloads pi_state_c2 from error_thresh_114. + * Boot-cached at RAM[0x02c0] by FUN_6b7b:0x6bd4. ROM literal 0x0320 = 800. */ + int16_t pi_sat_count_threshold; /* cal+0x112 */ + int16_t rpm_threshold_11E; /* cal+0x11E — RPM gate inside FUN_66a8 */ + /* Closed-loop entry RPM floor (FUN_67c4:0x67d9). Sourced from + * RAM[0x605c] (flash mirror; ROM literal 0x01A4 = 420). */ + int16_t pi_cl_rpm_floor; - /* Recovery / sustained-error machinery */ - int16_t pi_state_c2_reload; /* cal+0x114 — reload value for pi_state_c2 on latch */ - int16_t inj_qty_thresh; /* cal+0x116 — inj-qty threshold for recovery vs reset */ - int16_t pi_sat_count_threshold; /* cal+0x112 — recovery counter latch threshold */ - int16_t rpm_threshold_recovery; /* cal+0x126 — s_recovery RPM gate (was wrongly cal+0x11E in t06211 idiom) */ - int16_t pi_cl_rpm_floor; /* flash[0x605C] — s_pi_update OL gate threshold */ + int16_t pwm_detail_x0; /* cal+0x0EE */ + int16_t pwm_detail_x1; /* cal+0x0F0 */ + int16_t pwm_cached_ptr_0F2; /* cal+0x0F2 — first RPM-window breakpoint (legacy scalar) */ + int16_t pwm_cached_ptr_102; /* cal+0x102 — window halfwidth (legacy scalar) */ + int16_t pwm_const_104; /* cal+0x104 */ - /* PI runtime-reset values (boot-derived in ROM; cal-resident here so - * each variant carries its own values without code edits). */ + /* RPM-window matching for pwm_period slew (ROM 0x538b-0x5575). + * Eight breakpoints at cal+0xF2 define four bands (lo,hi); the + * cal+0x102 halfwidth adds hysteresis. RPM INSIDE any band drives + * pwm_period toward pwm_period_min (→ non-zero shape contribution + * adds ~49 duty ticks). RPM OUTSIDE all bands drives pwm_period + * toward pwm_period_max (→ zero contribution; Y-table floor). */ + int16_t pwm_rpm_windows[8]; /* cal+0xF2 — 4 (lo,hi) pairs */ + int16_t pwm_window_halfwidth; /* cal+0x102 — hysteresis halfwidth */ + /* Per-cycle slew step magnitude for pwm_period. Sourced from + * cal+0x104 (= 354); cached to RAM[0x02ec] at FUN_5314:0x53b9 and + * read at the Phase 1 and Phase 3 sites. Aliases pwm_const_104 — + * same offset, semantic name. */ + int16_t pwm_slew_step; /* cal+0x104 — slew magnitude */ + + const int16_t *pwm_y_table; /* cal+0x154 (indirect) */ + const int16_t *shape_y_table; /* cal+0x15E (indirect) */ + + int16_t closed_loop_gain_const; /* cached at ROM 0x6056 = 0x000A */ + + /* PI runtime-reset values — boot-derived in ROM (FUN_76aa @ 0x76aa); + * cal-resident here so each variant carries its own values without + * code edits. Trim bytes at RAM[0x0410-0x0416] have no writers in the + * ROM (EEPROM trim slots, default 0), so each cal base is used + * verbatim — see open-questions §2 closeout. Field shape mirrors + * T06215's pwm_calibration_t for cross-family consistency. */ int16_t init_p_shape_bound_pos; /* cal+0x10A — copied to rt->p_shape_bound_pos */ int16_t init_p_shape_bound_neg; /* cal+0x10C — copied to rt->p_shape_bound_neg */ int16_t init_p_gain_normal; /* cal+0x118 — copied to rt->p_gain_normal */ - int16_t init_integ_step_normal; /* cal+0x11E — copied to rt->integ_step_normal */ - int16_t init_p_slope_large_pos; /* cal+0x11A — copied to rt->p_slope_large_pos */ - int16_t init_p_slope_large_neg; /* cal+0x11C — copied to rt->p_slope_large_neg */ - int16_t init_integ_step_large_pos;/* cal+0x120 — copied to rt->integ_step_large_pos */ - int16_t init_integ_step_large_neg;/* cal+0x122 — copied to rt->integ_step_large_neg */ - int16_t init_open_loop_p_gain; /* cal+0x124 byte, clamped to 15 — copied to rt->open_loop_p_gain */ + int16_t init_integ_step_normal; /* cal+0x11A — copied to rt->integ_step_normal */ + int16_t init_open_loop_p_gain; /* cal+0x11C byte clamped to 15 — copied to rt->open_loop_p_gain */ + int16_t init_pos_error_normalizer;/* cal+0x108 — copied to rt->pos_error_normalizer */ + int16_t init_neg_error_normalizer;/* cal+0x106 — copied to rt->neg_error_normalizer */ - /* CL correction normalizers (RAM in ROM, cal-resident here for parity). */ - int16_t init_pos_error_normalizer; - int16_t init_neg_error_normalizer; + uint16_t pwm_period_min; /* hypothesised; see cal_tables_rom.c */ + uint16_t pwm_period_max; /* hypothesised */ - /* PWM stage scalars */ - int16_t pwm_detail_x0; - int16_t pwm_detail_x1; - int16_t pwm_cached_ptr_0F2; - int16_t pwm_cached_ptr_102; - int16_t pwm_const_104; - - int16_t pwm_rpm_windows[8]; - int16_t pwm_window_halfwidth; - int16_t pwm_slew_step; - - const int16_t *pwm_y_table; - const int16_t *shape_y_table; - - int16_t closed_loop_gain_const; - - uint16_t pwm_period_min; - uint16_t pwm_period_max; - uint16_t pwm_min; - uint16_t pwm_max; + /* PWM duty clamp bounds — RAM[0x6058]/RAM[0x605a] flash mirrors. + * Defaults 0x00CD/0x0F32 match the default family's pwm_min/pwm_max. */ + uint16_t pwm_min; /* RAM[0x6058] cache */ + uint16_t pwm_max; /* RAM[0x605a] cache */ }; /* ── Submap descriptor ──────────────────────────────────────────────── */ struct pwm_submap_descr { - uint16_t flags; - const int16_t *input_ptr; - uint16_t count; - const int16_t *x; + uint16_t flags; /* +0 */ + const int16_t *input_ptr; /* +2 (bound at runtime) */ + uint16_t count; /* +4 */ + const int16_t *x; /* +6 */ uint16_t input_addr; }; +/** Bind descriptor input_ptr fields to rt inputs. */ static inline void pwm_bind_submap_inputs( pwm_runtime_t *rt, pwm_submap_descr_t *descrs, @@ -289,8 +368,15 @@ static inline void pwm_bind_submap_inputs( * PUBLIC API * ══════════════════════════════════════════════════════════════════════ */ +/** pwm_flash_t — placeholder for family-1 API parity. Family-1 has Y-tables + * in a separate `pwm_flash_t` struct; t06211 keeps them inside + * pwm_calibration_t so this struct is empty. Kept so FBKW.c / callers + * can pass &pwm_flash_rom without the call failing. */ typedef struct pwm_flash { char _unused; } pwm_flash_t; +/** 4-argument init matching family-1's pwm_init signature. The `flash` + * pointer is accepted (must be non-NULL — pass &pwm_flash_rom) and + * ignored by the t06211 pipeline. */ void pwm_init(pwm_runtime_t *rt, const pwm_calibration_t *cal, const pwm_flash_t *flash, @@ -298,17 +384,44 @@ void pwm_init(pwm_runtime_t *rt, void pwm_service(pwm_runtime_t *rt); +/** @brief CKP-edge reset hook — call from the CKP interrupt handler. + * + * Sets rt->reset_flag = 1 so the next pwm_service() call zeroes the + * closed-loop accumulator (supervisor_state) and the enable counter + * (cl_enable_counter). Byte store is atomic on all mainstream targets; + * no locking required as long as it is not called concurrently with + * pwm_service(). + * + * Pattern parity with the default family. See + * docs/re-guide-ckp-reset-pattern.md for the full rationale. + * + * **Variant note for t06211:** the reset_flag producer is **absent from + * the ROM** (no STB #1 site writing to 0x002e). The supervisor at 0x7beb + * still consumes reset_flag == 1 and clears it, but nothing inside the + * ROM ever sets it. Consequence: the accumulator walk-off hazard is + * structurally identical to family-1, and the application layer must + * drive this hook itself at engine-rev rate (typically 4 pulses per + * revolution on VP44, ~12–15 scheduler cycles apart at 1200 rpm with a + * 1 kHz scheduler). Without it, supervisor_state integrates forever. + */ static inline void pwm_ckp_isr(pwm_runtime_t *rt) { rt->reset_flag = 1; } +/** Utility: 1D descending-X piecewise-linear lookup. */ int16_t pwm_interp_lookup(const int16_t *x, const int16_t *y, uint16_t n, int16_t in); +/** Utility: bypass-PI bilinear LUT — eval(pwm_A, rpm), eval(pwm_B, fbkw), + * combine over Y-table, then apply [205, 3890] clamp. Use this to query + * the ROM Y-table directly as a (rpm, fbkw) lookup, skipping the PI + * controller entirely. Returns the clamped duty. */ uint16_t pwm_lut_duty(const pwm_calibration_t *cal, uint16_t rpm, int16_t fbkw); +/* ── ROM-decoded cal + flash placeholder (defined in cal_tables_rom.c) ── */ extern const pwm_calibration_t pwm_cal_rom; extern const pwm_flash_t pwm_flash_rom; +/** Descriptor array indices. */ enum pwm_submap_id { PWM_SUBMAP_SETPOINT_INTERP = 0, PWM_SUBMAP_PWM_A = 1, diff --git a/Core/CAN_Libs/can_db.c b/Core/CAN_Libs/can_db.c index 37c4cdd..ad465b9 100644 --- a/Core/CAN_Libs/can_db.c +++ b/Core/CAN_Libs/can_db.c @@ -338,7 +338,7 @@ static CanSymbolDef SYM_ID_SEND4[] = { #endif static CanSymbolDef SYM_ID_SEND3[] = { -#if defined(T06235) || defined(T06215) +#if defined(T06235) || defined(T06215) || defined(T06211) { "CKP_OFFSET", 0, 16, CAN_ENDIAN_INTEL, CAN_SYM_UX, 0,0, &B_CKP_OFFSET, NULL, CAN_STORE_S16}, { "commit", 16, 1, CAN_ENDIAN_INTEL, CAN_SYM_UX, 0,0, &commitCKP_offset, NULL, CAN_STORE_U8}, diff --git a/Core/Inc/id.h b/Core/Inc/id.h index c9cf6ff..54df1b3 100644 --- a/Core/Inc/id.h +++ b/Core/Inc/id.h @@ -12,13 +12,13 @@ /* DEBUG PARAMETERS*/ //#define T06301 //ford 004 -> 002 004 006 || 504 -> 010 018 -#define T06215 //bmw rover 004 -> 005 014 015 016 017 || 504 -> 005 007 017 || 006 -> 001 002 003 004 007 008 +//#define T06215 //bmw rover 004 -> 005 014 015 016 017 || 504 -> 005 007 017 || 006 -> 001 002 003 004 007 008 //#define T15021 //audi 506 -> 030 033 //#define T31804 //audi 506 -> 037 038 //#define T06209 //eq: T06216 -//#define T06211 +#define T06211 -#define _424026 +#define _504012 /* FORD */ #define FORD_SYNC_PULSE_OUT 0 @@ -29,14 +29,15 @@ #define CYLINDERS 4 /* TIMING COMPENSATIONS */ -#define PHI1 41.0f +#define PHI1 41.004 #define TEIN_NOMINAL 1556 //(phiad - injangle with fault tein)*totime = teinnom #define TEIN_FAULT 950 -//#define FBKW_FEEDBACK_ZERO 45.2 -#define FBKW_FEEDBACK_ZERO 53.613 +/* ALL FBKW */ + +#define FBKW_FEEDBACK_ZERO 53.719 #define FBKW_FEEDBACK_MIN -5.367 #define FBKW_FEEDBACK_MAX 21.27 @@ -48,12 +49,6 @@ #define CAN_RPM_SEND_ASYNC 250 #define CAN_EMPF2_INSTANT 0 -/* ALL FUELMAP */ -#define FM_N_RPM 6 -#define FM_N_ME 11 -#define FM_N_T 5 - - /* PEAK AND HOLD */ #define PH_PEAK_DEF 600 diff --git a/Core/Phi/phi.c b/Core/Phi/phi.c index 76b576a..085f74e 100644 --- a/Core/Phi/phi.c +++ b/Core/Phi/phi.c @@ -1,17 +1,14 @@ /** * @file phi.c - * @brief T06215 compact-port — monolithic single-translation-unit + * @brief T06211 compact-port — monolithic single-translation-unit * implementation of the injection-angle producer chain. * * All seven producer stages and the shared submap helpers are inlined * directly into this file as `static` functions (1:1 translations of - * the verbose modules under variants/T06215/src/, brought along + * the verbose modules under variants/T06211/src/, brought along * verbatim so per-block disassembly-address comments remain - * authoritative). The producer itself (s_final_injection_angle @ - * 0x754D) is byte-equivalent to T06211's FUN_7453 — see - * variants/T06215/README.md — so the inlined bodies and assembly - * address ranges are shared with T06211. The call order in - * `phi_service` matches T06211's fused-scheduler walk + * authoritative). The call order in `phi_service` matches the + * disassembly walk through the fused scheduler at FUN_698C * (variants/T06211/docs/open-questions.md §4 / §5). * * Pipeline (Stages 2–7 in `phi_service`): @@ -25,7 +22,7 @@ * * Every MCS-96 arithmetic choice (MUL vs MULU, SHRA vs SHR logical, JGE * vs JC) mirrors the disassembly — see the per-block comments with - * disassembly address ranges, and variants/T06215/src/ for the + * disassembly address ranges, and variants/T06211/src/ for the * long-form function-level commentary these stages were ported from. */ #include "phi.h" @@ -40,7 +37,7 @@ * combine_three_submaps_to_word, refine_submap_result), plus two * file-static utilities (bytes_to_words, bilinear_at_plane). * - * Brought verbatim from variants/T06215/src/submap_eval.c with + * Brought verbatim from variants/T06211/src/submap_eval.c with * external linkage demoted to `static`. * ══════════════════════════════════════════════════════════════════════ */ @@ -232,7 +229,7 @@ static int16_t refine_submap_result(const int16_t *data_table, /* ══════════════════════════════════════════════════════════════════════ * Producer stages — 1:1 ports of the seven verbose modules under - * variants/T06215/src/, brought along verbatim with external linkage + * variants/T06211/src/, brought along verbatim with external linkage * demoted to `static`. Each docstring identifies the originating ROM * function (FUN_xxxx @ 0xyyyy). * ══════════════════════════════════════════════════════════════════════ */ @@ -439,8 +436,8 @@ static void compute_angle_accumulator_3d(runtime_state_t *rt, const calibration_ /* 0x7288–0x728a: LD RW52, RW1C — write angle_accumulator. */ rt->angle_accumulator = result; - /* 0x728b–0x72af: Alternate_phiad_calc embedded gate (FUN_5df3 @ - * 0x5e50–0x5e62 in T06215, analog of T06031's FUN_5dd0 tail). + /* 0x728b–0x72af: Alternate_phiad_calc embedded gate (FUN_5dd0 tail in + * T06031, FUN_5df3 in T06215; T06211 mirrors the same structure). * * Below the hysteresis threshold (gate_0220 == 0) the live ROM both * recomputes the accel_comp_gain and force-flags REC.1 so the next @@ -455,7 +452,13 @@ static void compute_angle_accumulator_3d(runtime_state_t *rt, const calibration_ * any prior value). This is the PTS-event fallback path — at high * RPM the per-cylinder hook will have already set REC.0, so this * gate skips and the producer fires from the per-cylinder side - * exclusively. */ + * exclusively. + * + * Note: in T06211, this function only runs from the orchestrator's + * else-branch (inj_qty_demand != 0), which matches the live ROM + * behavior where Alternate_phiad_calc lives inside the kick chain + * skipped on demand=0. The per-cylinder hook fires the producer + * independently of demand. */ if (rt->gate_0220 == 0u) { rt->rec = (uint8_t)(rt->rec | 0x02u); } @@ -471,8 +474,8 @@ static void compute_angle_accumulator_3d(runtime_state_t *rt, const calibration_ * Sole producer of `rt->rw9e` — the high word of the signed 32-bit * IIR-filter accumulator `{rt->rw9e:rt->rw9c}`. Low-passes the * rpm-derived term `rt->rw9a` (produced by compute_temp_comp_factor at - * 0x6AF9). `rt->rw9e` is consumed by the same compute_temp_comp_factor - * at 0x6AFC. + * 0x5DBD). `rt->rw9e` is consumed by the same compute_temp_comp_factor + * at 0x5DC0. * * Cadence: in the ROM the IIR step runs from Timer_1khz (1 kHz) gated * by an R94 prescaler that reloads from DAT_6061 = 0x64 = 100, so the @@ -606,20 +609,17 @@ void phi_tick_1khz(phi_state_t *state, const phi_cal_t *cal) } /* - * 1:1 translation of orphan calc_temp_comp_factor @ 0x6AE7–0x6B29 - * (T06215 variant; algorithmically byte-equivalent to T06211 FUN_5DAB - * @ 0x5DAB). + * 1:1 translation of orphan FUN_5DAB @ 0x5DAB–0x5DED (T06211 variant). * - * Per-tick producer of `rt->temp_comp_factor` (the runtime mirror of - * *(0x02FC) in T06215; T06211 uses *(0x02F4)). Reached on-tick by - * `LCALL calc_temp_comp_factor` at 0x7B14 inside the per-cylinder gate - * that precedes the orphan scheduler at FUN_7b1c. + * Per-tick producer of `rt->temp_comp_factor` (= the runtime mirror of + * *(0x02F4)). Reached on-tick by `LCALL 0x5DAB` at 0x79D9 inside the + * per-cylinder gate that precedes the orphan scheduler at 0x79E1. * * Both MULs in this function carry the FE prefix → signed 16×16→32. - * The final store at 0x6B1E is `ST RW1C, temp_comp` — the LOW word of + * The final store at 0x5DE2 is `ST RW1C, *(0x02F4)` — the LOW word of * the second product, NOT the high word. * - * The R0CB-gated `ADD RW20, RWCE` branch at 0x6B00–0x6B07 is + * The R0CB-gated `ADD RW20, RWCE` branch at 0x5DC4–0x5DCB is * INTENTIONALLY DROPPED — both register-bank slots are observed zero in * nominal operation, so the branch is dead. Restore it if a future * live-ECU dump shows R0CB transitioning out of zero. @@ -628,98 +628,95 @@ void phi_tick_1khz(phi_state_t *state, const phi_cal_t *cal) */ static void compute_temp_comp_factor(runtime_state_t *rt, const calibration_t *cal) { - /* 0x6AE7–0x6AEC: prologue (PUSH RW1C, RW1E, RW20) — handled by C locals. */ + /* 0x5DAB–0x5DB0: prologue (PUSH RW1C, RW1E, RW20) — handled by C locals. */ - /* 0x6AED–0x6AEF: LD RW1C, RW40 — rw1c = rpm. */ + /* 0x5DB1–0x5DB3: LD RW1C, RW40 — rw1c = rpm. */ int16_t rpm_signed = (int16_t)rt->rpm; - /* 0x6AF0–0x6AF5: MUL RL1C, RW1C, temp_comp_dynamic — signed (FE prefix); - * cal_temp_comp_switch_dynamic mirrors temp_comp_dynamic. - * 0x6AF6–0x6AF8: SHRAL RL1C, #2 — arithmetic right shift on the + /* 0x5DB4–0x5DB9: MUL RL1C, RW1C, *(0x02F6) — signed (FE prefix); + * cal_temp_comp_switch_dynamic mirrors *(0x02F6). + * 0x5DBA–0x5DBC: SHRAL RL1C, #2 — arithmetic right shift on the * 32-bit signed product. */ int32_t rl1c = (int32_t)rpm_signed * (int32_t)cal->cal_temp_comp_switch_dynamic; rl1c = rl1c >> 2; /* arithmetic */ int16_t rw1e = (int16_t)((rl1c >> 16) & 0xFFFF); /* high word */ - /* 0x6AF9–0x6AFB: LD RW9A, RW1E — publish high word as state. - * Consumed by other orphans outside the RW48 chain. */ + /* 0x5DBD–0x5DBF: LD RW9A, RW1E — publish high word as state. + * Consumed by other orphans (FUN_5E12) outside the RW48 chain. */ rt->rw9a = rw1e; - /* 0x6AFC–0x6AFF: SUB RW20, RW9E, RW1E (3-op) — rw20 = RW9E − rw1e. */ + /* 0x5DC0–0x5DC3: SUB RW20, RW9E, RW1E (3-op) — rw20 = RW9E − rw1e. */ int16_t rw20 = (int16_t)(rt->rw9e - rw1e); - /* 0x6B00–0x6B07: R0CB-gated `ADD RW20, RWCE` — DROPPED (see docstring). */ + /* 0x5DC4–0x5DCB: R0CB-gated `ADD RW20, RWCE` — DROPPED (see docstring). */ - /* 0x6B08–0x6B0C: ADD RW20, *(0x0146) — fold in temperature. */ + /* 0x5DCC–0x5DD0: ADD RW20, *(0x0146) — fold in temperature. */ rw20 = (int16_t)(rw20 + rt->temperature); - /* 0x6B0D–0x6B11: SUB RW20, [RWA4+0x007E] — temperature reference. */ + /* 0x5DD1–0x5DD5: SUB RW20, [RWA4+0x007E] — temperature reference. */ rw20 = (int16_t)(rw20 - cal->cal_7e); - /* 0x6B12–0x6B16: SUB RW20, *(0x0402) — sign-extended boot byte - * (cal_byte_402 = 0xFFF2 = -14 in this T06215 image). */ + /* 0x5DD6–0x5DDA: SUB RW20, *(0x0402) — sign-extended boot byte + * (cal_byte_402 = 0xFFF8 = -8 in this image). */ rw20 = (int16_t)(rw20 - cal->cal_byte_402); - /* 0x6B17–0x6B1D: MUL RL1C, RW20, temp_comp_complete — signed (FE prefix); - * cal_temp_comp_switch_complete mirrors temp_comp_complete. + /* 0x5DDB–0x5DE1: MUL RL1C, RW20, *(0x02F2) — signed (FE prefix); + * cal_temp_comp_switch_complete mirrors *(0x02F2). * With switch == 1, rl1c == rw20 (sign-extended). */ int32_t prod = (int32_t)rw20 * (int32_t)cal->cal_temp_comp_switch_complete; - /* 0x6B1E–0x6B22: ST RW1C, temp_comp — LOW word of RL1C. + /* 0x5DE2–0x5DE6: ST RW1C, *(0x02F4) — LOW word of RL1C. * NOTE: stores the LOW 16 bits of the signed 32-bit product, not * the high word. Anything that overflows int16 wraps. */ rt->temp_comp_factor = (int16_t)((uint32_t)prod & 0xFFFFu); - /* 0x6B23–0x6B29: epilogue. */ + /* 0x5DE7–0x5DED: epilogue. */ } /* - * 1:1 translation of orphan compute_angle_kick_2d @ 0x6A94–0x6AE6 - * (T06215 variant; algorithmically byte-equivalent to T06211 FUN_5D58 - * @ 0x5D58). + * 1:1 translation of orphan FUN_5D58 @ 0x5D58–0x5DAA (T06211 variant). * * Per-tick producer of `rt->angle_kick_2d` (RW3E). Reached by - * `LCALL compute_angle_kick_2d` at 0x7B76 in the orphan scheduler - * FUN_7b1c, immediately after the angle-accumulator-3D analog and - * immediately before `RW52 += RW3E` at 0x7B79. Reuses scratch_rpm / - * scratch_demand already populated by compute_angle_accumulator_3d - * earlier in the same tick. + * `LCALL 0x5D58` at 0x7A3B in the orphan scheduler, immediately after + * `compute_angle_accumulator_3d` (FUN_722E) and immediately before + * `RW52 += RW3E` at 0x7A3E. Reuses scratch_rpm / scratch_demand + * already populated by compute_angle_accumulator_3d earlier in the + * same tick. * * Both MULs are SIGNED (FE prefix). SHRAL is arithmetic; SHLL is * logical. */ static void compute_angle_kick_2d(runtime_state_t *rt, const calibration_t *cal) { - /* 0x6A94–0x6AA3: prologue (PUSH RW1C/.../RW2A) — handled by C locals. - * 0x6AA4–0x6AAA: RW2A = RWC6 + 0x32 — points at the kick table cal slot. */ + /* 0x5D58–0x5D67: prologue (PUSH RW1C/.../RW2A) — handled by C locals. + * 0x5D68–0x5D6E: RW2A = RWC6 + 0x32 — points at the kick table cal slot. */ - /* 0x6AAB–0x6AB6: PUSH #0x194 / #0x184 / [RW2A] / LCALL FUN_6f1e - * (T06215's combine_two_submaps_to_word analog). + /* 0x5D6F–0x5D79: PUSH #0x194 / #0x184 / [RW2A] / LCALL FUN_7035. * 2-D bilinear combine on (rpm, demand) over data_table_2d_kick. - * 0x6AB6: ST RW1C, RW3E — RW3E = bilinear result. */ + * 0x5D7A: ST RW1C, RW3E — RW3E = bilinear result. */ int16_t bilinear = combine_two_submaps_to_word(cal->data_table_2d_kick, &rt->scratch_rpm, &rt->scratch_demand); rt->angle_kick_2d = bilinear; - /* 0x6ABD–0x6AC2: MUL RL1C, RW42, [RWC6+0x34] — signed (FE prefix); - * 0x6AC3–0x6AC5: SHRAL RL1C, #0x8 (arithmetic). - * 0x6AC6–0x6AC8: ADD RW3E, RW1C — fold demand-weighted offset in. */ + /* 0x5D81–0x5D89: MUL RL1C, RW42, [RWC6+0x34] — signed (FE prefix); + * SHRAL RL1C, #0x8 (arithmetic). + * 0x5D8A: ADD RW3E, RW1C — fold demand-weighted offset in. */ int32_t dem_prod = (int32_t)rt->angle_dec_cmd * (int32_t)cal->cal_rwc6_34; int32_t dem_shifted = dem_prod >> 8; /* arithmetic */ int16_t dem_low = (int16_t)(dem_shifted & 0xFFFF); rt->angle_kick_2d = (int16_t)(rt->angle_kick_2d + dem_low); - /* 0x6AC9–0x6ACF: MUL RL1C, RW3E, temp_comp — signed (FE prefix); - * 0x6AD0–0x6AD2: SHLL RL1C, #0x2 (LOGICAL); take HIGH word. - * 0x6AD3–0x6AD5: ST RW1E, RW3E — RW3E = high_word((RW3E × temp_comp_factor) << 2). */ + /* 0x5D8D–0x5D97: MUL RL1C, RW3E, *(0x02F4) — signed (FE prefix); + * SHLL RL1C, #0x2 (LOGICAL); take HIGH word. + * 0x5D97: ST RW1E, RW3E — RW3E = high_word((RW3E × temp_comp_factor) << 2). */ int32_t scale_prod = (int32_t)rt->angle_kick_2d * (int32_t)rt->temp_comp_factor; uint32_t scale_shifted = ((uint32_t)scale_prod) << 2; /* logical */ rt->angle_kick_2d = (int16_t)(uint16_t)(scale_shifted >> 16); - /* 0x6AD6–0x6AE6: epilogue. */ + /* 0x5D9A–0x5DAA: epilogue. */ } /* @@ -778,36 +775,35 @@ static void compute_accel_comp_offset(runtime_state_t *rt, const calibration_t * } /* - * 1:1 translation of FUN_5f33 @ 0x5f33–0x5f4d (T06215 ROM analog of - * T06031's FUN_736e). The "Try_calc_accel_offset" wrapper that conditionally - * calls compute_accel_comp_offset based on REC.0 / REC.1 / gate_0220. + * 1:1 translation of FUN_736e @ 0x736e–0x7388 (T06211 — Try_calc_accel_offset + * analog; byte-equivalent in shape to T06215's FUN_5f33 and T06031's + * FUN_736e — same address in T06211 / T06031, different in T06215). The + * verbose tree's compute_accel_comp_offset_gated translates the same + * function. Triggered from the live-ROM tooth dispatcher at the per-cylinder + * cal_byte_56 (=13) tooth event, immediately after Calculate_mid_rpm + * updates RW138. Wired into the public API as phi_per_cylinder_event. * - * Triggered from the live-ROM Tooth_scheduler at 0x7abe, on the per-cylinder - * tooth event where R88 == cal_byte_56 — i.e. once per cylinder cycle, - * immediately after the rpm_baseline producer (Calculate_mid_rpm @ 0x51e1) - * updates *(0x0138). Wired into the public API as phi_per_cylinder_event. - * - * 0x5f37: ORB REC, #0x4 ; REC.2 := 1 (wrapper visited flag) - * 0x5f3a: JBS REC, 0x0, exit ; REC.0 set → fresh value pending, skip - * 0x5f3d: JBS REC, 0x1, compute ; REC.1 set → forced compute path - * 0x5f40: CMPB ZRlo, *(0x0220) ; gate_0220 (is_rpm_above_1000_hyst.) - * 0x5f45: JE exit ; gate_0220 == 0 → skip - * 0x5f47: SCALL FUN_5ef2 ; compute_accel_comp_offset - * 0x5f49: epilogue + * 0x7372: ORB REC, #0x4 ; REC.2 := 1 (wrapper visited flag) + * 0x7375: JBS REC, 0x0, exit ; REC.0 set → fresh value pending, skip + * 0x7378: JBS REC, 0x1, compute ; REC.1 set → forced compute path + * 0x737b: CMPB ZRlo, *(0x0220) ; gate_0220 (is_rpm_above_1000_hyst.) + * 0x7380: JE exit ; gate_0220 == 0 → skip + * 0x7382: SCALL FUN_732d ; compute_accel_comp_offset + * 0x7384: epilogue */ static void compute_accel_comp_offset_gated(runtime_state_t *rt, const calibration_t *cal) { - /* 0x5f37: REC |= 0x04 — set unconditionally, even on skip paths. */ + /* 0x7372: REC |= 0x04 — set unconditionally, even on skip paths. */ rt->rec = (uint8_t)(rt->rec | 0x04u); - /* 0x5f3a: REC.0 set → consumer (FUN_7453) hasn't drained the previous + /* 0x7375: REC.0 set → consumer (FUN_7453) hasn't drained the previous * RW3C yet; skip recomputation to avoid losing the pending value. */ if ((rt->rec & 0x01u) != 0u) { return; } - /* 0x5f3d–0x5f47: REC.1 forces the compute path; otherwise gate on + /* 0x7378–0x7381: REC.1 forces the compute path; otherwise gate on * the live-byte hysteresis at *(0x0220). */ if (((rt->rec & 0x02u) != 0u) || (rt->gate_0220 != 0u)) { compute_accel_comp_offset(rt, cal); @@ -1076,7 +1072,7 @@ static void compute_target_injection_angle(runtime_state_t *rt, const calibratio /* 0x74a2–0x74a7: ADD RW5A, RW1C, 0x14e (3-op, TABLE[ZR]) * target_eoi = target_inj_angle + phi1, where phi1 is the precomputed * cell at *(0x014e). The C model recomputes phi1 = phi0 + dphi inline - * (see variants/T06215/docs/open-questions.md §6). */ + * (see variants/T06211/docs/open-questions.md §6). */ { int16_t phi1 = (int16_t)(cal->phi0 + rt->dphi); rt->target_eoi = (int16_t)(rw1c + phi1); @@ -1100,7 +1096,7 @@ void phi_init(phi_state_t *state, const phi_cal_t *cal, * input_var field is runtime-bound by design (the C model treats the * cal as a writable buffer, not flash-resident, since extract_*.py * emits a non-const initializer for this very reason). */ - phi_t06215_bind_inputs(&state->rt, (phi_cal_t *)cal); + phi_t06211_bind_inputs(&state->rt, (phi_cal_t *)cal); /* Pull boot-only getters — Phase-1 / Phase-2 thresholds for the * FUN_62a2 state machine. */ @@ -1147,15 +1143,17 @@ void phi_service(phi_state_t *state, const phi_cal_t *cal, phi_outputs_t *out) * tooth-13-event-driven, not main-loop-driven. The pull happens at * the two consumer-side entry points instead: * - phi_per_cylinder_event (the per-cylinder hook) - * - just before compute_angle_accumulator_3d in Stage 3b (the - * PTS-event fallback path's embedded Alternate_phiad_calc gate). + * - just before compute_angle_accumulator_3d in Stage 3b's + * else-branch (the PTS-event fallback path's embedded + * Alternate_phiad_calc gate; on demand=0 the orchestrator's + * alt-path skips this branch, matching the live-ROM behavior). * See the comment at each pull site. */ rt->rwc2 = g->get_rwc2(); rt->reset_gate_0226 = g->get_reset_gate_0226(); rt->dphi = g->get_dphi(); - /* rt->rw9e is produced internally by compute_temp_phi_comp on its - * 10 Hz cadence; it is not pulled from a getter. The host drives the - * 1 kHz hook compute_temp_phi_comp_tick_1khz from a separate timer. */ + /* rt->rw9e is produced internally by phi_tick_1khz on its 10 Hz + * cadence; it is not pulled from a getter. The host drives the 1 kHz + * hook from a separate timer. */ /* Per-tick constants — hard-wire chain inputs that the live ECU drives * but the simulation pins to a single value: @@ -1185,46 +1183,51 @@ void phi_service(phi_state_t *state, const phi_cal_t *cal, phi_outputs_t *out) * unconditionally so the gain is fresh each tick. */ compute_accel_comp_gain(rt, cal); - /* ── Stage 3b: compute_angle_accumulator_3d (FUN_722e @ 0x722e) ──── - * Three RWC6-relative submap evals (RPM, demand, angle_dec_cmd) through - * FUN_7092 (3-D trilinear = combine_three_submaps_to_word) → writes - * rt->angle_accumulator (RW52) and rt->scratch_0158 debug mirror. - * Also populates rt->scratch_rpm and rt->scratch_demand which the - * angle-kick stage (3d) reuses without re-evaluating. + /* ── Stages 3b–3e: angle accumulator + temperature-kick fold ────── + * (Aligned with T06215/T06235: no orchestration-level demand-zero + * guard — the demand=0 alt path is handled inside + * compute_target_injection_angle at Stage 7.) * - * The Alternate_phiad_calc embedded gate at the tail of this function - * may fire compute_accel_comp_offset (the "PTS-event fallback path"). - * Pull rpm_baseline FRESH right before this gate so the producer reads - * the latest tooth-13 snapshot rather than whatever stale value Stage - * 1 left behind — phi_service's main-loop cadence is not synchronised - * with the per-cylinder tooth-13 event, so a Stage-1 pull may capture - * the previous cycle's baseline and invert RW3C polarity. Mirrors the - * pull at the top of phi_per_cylinder_event so both producer entry - * points see the same snapshot ordering. */ + * ── Stage 3b: compute_angle_accumulator_3d (FUN_722e @ 0x722e) + * Three RWC6-relative submap evals (RPM, demand, angle_dec_cmd) + * through FUN_7092 (3-D trilinear) → writes rt->angle_accumulator + * (RW52) and rt->scratch_0158 debug mirror. Also populates + * rt->scratch_rpm and rt->scratch_demand which the angle-kick + * stage (3d) reuses without re-evaluating. + * + * The Alternate_phiad_calc embedded gate at the tail of this + * function may fire compute_accel_comp_offset (the "PTS-event + * fallback path"). Pull rpm_baseline FRESH right before this + * gate so the producer reads the latest tooth-13 snapshot rather + * than whatever stale value Stage 1 left behind — phi_service's + * main-loop cadence is not synchronised with the per-cylinder + * tooth-13 event. Mirrors the pull at the top of + * phi_per_cylinder_event so both producer entry points see the + * same snapshot ordering. */ rt->rpm_baseline = g->get_rpm_baseline(); compute_angle_accumulator_3d(rt, cal); - /* ── Stage 3c: compute_temp_comp_factor (orphan calc_temp_comp_factor @ 0x6AE7) ─ - * Per-tick rebuild of *(0x02FC) in T06215 (T06211: *(0x02F4)) from rpm, - * temperature, the two boot switches (cal_temp_comp_switch_complete / - * _dynamic), and the external rw9e state input. Mirrors the orphan call - * at 0x7B14 that fires before the angle scheduler at FUN_7b1c. */ + /* ── Stage 3c: compute_temp_comp_factor (orphan FUN_5DAB @ 0x5DAB) + * Per-tick rebuild of *(0x02F4) from rpm, temperature, the two + * boot switches (cal_temp_comp_switch_complete / _dynamic), and + * the external rw9e state input. Mirrors the orphan call at + * 0x79D9 that fires before the angle scheduler at 0x79E1. */ compute_temp_comp_factor(rt, cal); - /* ── Stage 3d: compute_angle_kick_2d (orphan @ 0x6A94) ───────────── + /* ── Stage 3d: compute_angle_kick_2d (orphan FUN_5D58 @ 0x5D58) * 2-D bilinear kick over the table at *(RWC6+0x32), reusing the * scratches populated by Stage 3b, plus a demand-weighted offset * `(angle_dec_cmd × cal_rwc6_34) >> 8`, post-scaled by - * temp_comp_factor (Stage 3c output). Mirrors `LCALL compute_angle_kick_2d` - * at 0x7B76. Writes rt->angle_kick_2d (RW3E). */ + * temp_comp_factor (Stage 3c output). Mirrors `LCALL 0x5D58` at + * 0x7A3B. Writes rt->angle_kick_2d (RW3E). */ compute_angle_kick_2d(rt, cal); - /* ── Stage 3e: fold the kick into the accumulator + saturate ────── - * Mirrors `ADD RW52, RW3E` at 0x7B79 followed by the `JBC R53.7 / - * CLR RW52` saturate at 0x7B7C–0x7B7F. This happens BEFORE - * compute_target_injection_angle (FUN_7453 analog @ 0x754D); - * FUN_7453's own REC.0 fold of accel_comp_offset (RW3C) and - * *(0x0200) drain run later inside Stage 7. */ + /* ── Stage 3e: fold the kick into the accumulator + saturate ── + * Mirrors `ADD RW52, RW3E` at 0x7A3E followed by the `JBC R53.7 + * / CLR RW52` saturate at 0x7A41–0x7A44. This happens BEFORE + * compute_target_injection_angle (FUN_7453); FUN_7453's own + * REC.0 fold of accel_comp_offset (RW3C) and *(0x0200) drain + * run later inside Stage 7. */ rt->angle_accumulator = (int16_t)(rt->angle_accumulator + rt->angle_kick_2d); if (rt->angle_accumulator < 0) { rt->angle_accumulator = 0; @@ -1234,18 +1237,21 @@ void phi_service(phi_state_t *state, const phi_cal_t *cal, phi_outputs_t *out) * The producer is now driven by the live-ROM cadence: * * 1. Per-cylinder path — host calls phi_per_cylinder_event() at the - * cal_byte_56 tooth (mirrors Tooth_scheduler @ 0x7abe). This - * sets REC.2 and may run compute_accel_comp_offset depending on - * gate_0220 / REC.1 state. + * cal_byte_56 (=13) tooth (mirrors Tooth_scheduler @ 0x7abe in + * T06215; T06211 same tooth value). This sets REC.2 and may run + * compute_accel_comp_offset depending on gate_0220 / REC.1 state. * * 2. PTS-event fallback path — the Alternate_phiad_calc embedded * gate at the tail of compute_angle_accumulator_3d (Stage 3b) * runs compute_accel_comp_offset when REC.2 is set AND REC.0 - * is clear. + * is clear. In T06211 this lives inside the demand!=0 else-branch, + * so on demand=0 the fallback is skipped (matching the live-ROM + * alt-path); the per-cylinder hook still fires the producer + * independently of demand. * * Calling the bare producer here would overwrite RW3C every tick - * regardless of cadence, which is the pre-2026-05-06 divergence the - * live-ECU comparison surfaced. */ + * regardless of cadence, which is the divergence the live-ECU + * comparison surfaced. */ /* ── Stage 5: compute_gate_0220 (orphan @ 0x77ff) ─────────────── * Updates rt->gate_0220 from current rpm with hysteresis bands at @@ -1298,42 +1304,38 @@ size_t phi_state_size(void) { return sizeof(phi_state_t); } size_t phi_cal_size(void) { return sizeof(phi_cal_t); } /* - * phi_per_cylinder_event — public per-cylinder hook. + * phi_per_cylinder_event — public per-cylinder hook for T06211. * - * Models the live-ROM `Tooth_scheduler` dispatch at 0x7abe: + * Models the live-ROM tooth dispatcher path that fires once per cylinder + * at the cal_byte_56 = 13 tooth event: * - * if (R88 == cal_byte_56) { // 0x7aa9 JE LAB_7abb - * Calculate_mid_rpm(); // 0x7abb LCALL FUN_51e1 - * Try_calc_accel_offset(); // 0x7abe LCALL FUN_5f33 + * if (R88 == cal_byte_56) { + * Calculate_mid_rpm(); // updates rpm_baseline (RW138) + * compute_accel_comp_offset_gated(); // FUN_736e @ 0x736e * } * - * The host is responsible for the `Calculate_mid_rpm` analog: write the - * fresh rpm_baseline (RW138) into `state->rt.rpm_baseline` (or via the - * getter) BEFORE calling this hook. This routine then runs the - * Try_calc_accel_offset wrapper (compute_accel_comp_offset_gated), which - * sets REC.2 and conditionally calls compute_accel_comp_offset. + * Pulls rpm_baseline from the getter at hook entry — the host computes + * the fresh tooth-13 snapshot just before invoking this hook, but + * phi_service's Stage 1 used to capture rpm_baseline at main-loop cadence + * (independent of tooth-13 events), leaving rt->rpm_baseline stale at + * this moment. The fresh pull here guarantees the producer sees the + * just-computed snapshot. The mirror pull just before Stage 3b in + * phi_service does the same for the PTS-event fallback path. * * Cadence (per the live-ECU side-by-side traces): * - High RPM (gate_0220 == 1): hook fires the producer; REC.0 is set; * phi_service's Stage 3b fallback gate observes REC.0 and skips. * - Low RPM (gate_0220 == 0): hook only sets REC.2; phi_service's * Stage 3b runs the producer because REC.1 was set there and REC.0 - * is still clear. - * - In both cases compute_target_injection_angle (Stage 7) clears the + * is still clear (provided demand != 0; on demand=0 only the hook + * can fire the producer). + * - In all cases compute_target_injection_angle (Stage 7) clears the * entire REC byte after consuming RW3C, so REC reads back as 0 * between events — matching the live observation that *(0x00ec) * stays at zero on the bus probe. * * Reentrant per phi_state_t. Safe to invoke from a tooth-edge ISR * provided the host serialises against phi_service. - * - * Pulls rpm_baseline from the getter at hook entry — the host computes - * the fresh tooth-13 snapshot just before invoking this hook, but - * phi_service's Stage 1 pull captures rpm_baseline at main-loop cadence - * (independent of tooth-13 events), leaving rt->rpm_baseline stale at - * this moment. The fresh pull here guarantees the producer sees the - * just-computed snapshot. The mirror pull just before Stage 3b in - * phi_service does the same for the PTS-event fallback path. */ void phi_per_cylinder_event(phi_state_t *state, const phi_cal_t *cal) { diff --git a/Core/Phi/phi.h b/Core/Phi/phi.h index 513710c..d7abb32 100644 --- a/Core/Phi/phi.h +++ b/Core/Phi/phi.h @@ -1,16 +1,16 @@ /** * @file phi.h - * @brief T06215 injection-angle algorithm — compact public interface. + * @brief T06211 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 + * AUTO-GENERATED by tools/extract_t06211_cal.py + * Source ROM: rom_eeprom_dump_0000-9FFF_504012.bin + * Bases: RWA4 = 0x9BD8, RWC6 = 0x7D0E * * DO NOT EDIT -- regenerate with: - * python tools/extract_t06215_cal.py --target compact + * python tools/extract_t06211_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 + * verbose tree under variants/T06211/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. @@ -19,7 +19,7 @@ * * phi_input_getters_t getters = { .get_rpm = ..., ... }; * phi_state_t state; - * phi_cal_t cal = phi_t06215_cal; // copy from auto-extracted master + * phi_cal_t cal = phi_t06211_cal; // copy from auto-extracted master * phi_outputs_t out; * * phi_init(&state, &cal, &getters); // boot-once @@ -46,14 +46,14 @@ * the host must drive it once per 1 ms from its own millisecond timer. * See variants/T06211/docs/open-questions.md §9. */ -#ifndef PHI_T06215_H -#define PHI_T06215_H +#ifndef PHI_T06211_H +#define PHI_T06211_H #include #include /* ─── Internal runtime / calibration types ─────────────────────────── - * Inlined verbatim from variants/T06215/src/injection_angle_state.h. + * Inlined verbatim from variants/T06211/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. */ @@ -220,7 +220,15 @@ typedef struct { int16_t (*get_angle_dec_cmd)(void); /* RW42 */ uint16_t (*get_rpm)(void); /* RW40 */ int16_t (*get_temperature)(void); /* *(0x0146) */ - int16_t (*get_rpm_baseline)(void); /* *(0x0138) */ + int16_t (*get_rpm_baseline)(void); /* *(0x0138) — tooth-13 snapshot. + * Called by phi_per_cylinder_event AND by phi_service + * just before Stage 3b (NOT from Stage 1's bulk pull). + * Must return the rpm sampled at the most recent + * cal_byte_56=13 tooth event — a point sample, not + * a moving average. Doing the unit conversion (e.g. + * MT_offset_RPM × scale) inside the getter body is + * the right place; the algorithm reads it as an + * already-converted int16 Q-unit value. */ uint16_t (*get_rwc2)(void); /* RWC2 */ uint8_t (*get_reset_gate_0226)(void); /* *(0x0226) */ int16_t (*get_dphi)(void); /* *(0x014c) */ @@ -256,18 +264,22 @@ void phi_service(phi_state_t *state, void phi_tick_1khz(phi_state_t *state, const phi_cal_t *cal); /** Per-cylinder hook. Host invokes once per cylinder cycle, on the tooth - * edge where `current_tooth == cal->cal_byte_56`, AFTER the host has - * written the fresh rpm_baseline (RW138) into the runtime state. + * edge where `current_tooth == cal->cal_byte_56` (= 13 in this ROM), + * AFTER the host has computed the fresh rpm_baseline (RW138). * - * Mirrors the live-ROM Tooth_scheduler dispatch at 0x7abe (T06215): + * Mirrors the live-ROM Tooth_scheduler dispatch (T06211 mirrors T06215's + * 0x7abe path): * * if (R88 == cal_byte_56) { - * Calculate_mid_rpm(); // host responsibility (writes rpm_baseline) + * Calculate_mid_rpm(); // host responsibility — host's + * // get_rpm_baseline() must return + * // this fresh value when called. * Try_calc_accel_offset(); // this function * } * - * Internally calls the FUN_5f33 analog (compute_accel_comp_offset_gated) - * which sets REC.2 unconditionally and then conditionally fires + * Internally calls compute_accel_comp_offset_gated (FUN_5f33 analog), + * which pulls rpm_baseline fresh from the host getter at hook entry, + * then sets REC.2 unconditionally and conditionally fires * compute_accel_comp_offset based on REC.0 / REC.1 / gate_0220: * - REC.0 set → skip (fresh value already pending) * - REC.1 set OR gate_0220 != 0 → run producer (writes RW3C, sets REC.0) @@ -286,8 +298,8 @@ size_t phi_cal_size(void); /* ─── Auto-generated cal export (defined in phi_cal_tables.c) ──────── */ -extern calibration_t phi_t06215_cal; +extern calibration_t phi_t06211_cal; -void phi_t06215_bind_inputs(runtime_state_t *rt, calibration_t *cal); +void phi_t06211_bind_inputs(runtime_state_t *rt, calibration_t *cal); -#endif /* PHI_T06215_H */ +#endif /* PHI_T06211_H */ diff --git a/Core/Phi/phi_cal_tables.c b/Core/Phi/phi_cal_tables.c index 84209e6..af940c4 100644 --- a/Core/Phi/phi_cal_tables.c +++ b/Core/Phi/phi_cal_tables.c @@ -1,38 +1,38 @@ /** * @file phi_cal_tables.c - * @brief T06215 compact-port calibration data (auto-generated). + * @brief T06211 compact-port calibration data (auto-generated). * - * AUTO-GENERATED by tools/extract_t06215_cal.py - * Source ROM: rom_eeprom_dump_0000-9FFF_424026.bin - * Bases: RWA4 = 0x9BD8, RWC6 = 0x7E56 + * AUTO-GENERATED by tools/extract_t06211_cal.py + * Source ROM: rom_eeprom_dump_0000-9FFF_504012.bin + * Bases: RWA4 = 0x9BD8, RWC6 = 0x7D0E * * DO NOT EDIT -- regenerate with: - * python tools/extract_t06215_cal.py --target compact + * python tools/extract_t06211_cal.py --target compact */ #include #include "phi.h" /* ====================================================================== - * Accel axes + data tables (file-local; expose only phi_t06215_cal). + * Accel axes + data tables (file-local; expose only phi_t06211_cal). * Angle accumulator axes + 3-D table (FUN_722e producer chain). * ====================================================================== */ -/* Accel RPM axis -- 7 words @ 0x9D40. */ +/* Accel RPM axis -- 7 words @ 0x9D38. */ static const int16_t phi_accel_axis_rpm[7] = { (int16_t)0x4B5E, (int16_t)0x3127, (int16_t)0x16F0, (int16_t)0x1206, (int16_t)0x09D5, (int16_t)0x068E, (int16_t)0x0000 }; -/* Accel inj_qty_demand axis -- 6 words @ 0x9D4E. */ +/* Accel inj_qty_demand axis -- 6 words @ 0x9D46. */ static const int16_t phi_accel_axis_demand[6] = { - (int16_t)0x08C0, (int16_t)0x0640, (int16_t)0x0460, (int16_t)0x0280, (int16_t)0x01E0, (int16_t)0x0000 + (int16_t)0x08C0, (int16_t)0x0640, (int16_t)0x0320, (int16_t)0x0280, (int16_t)0x01E0, (int16_t)0x0000 }; -/* Accel temperature axis -- 5 words @ 0x9D5A. */ +/* Accel temperature axis -- 5 words @ 0x9D52. */ static const int16_t phi_accel_axis_temp[5] = { (int16_t)0x12F0, (int16_t)0x1250, (int16_t)0x1110, (int16_t)0x0FD0, (int16_t)0x0000 }; -/* Accel 2-D combine (RPM x demand) -- 42 words @ 0x9D64. */ +/* Accel 2-D combine (RPM x demand) -- 42 words @ 0x9D5C. */ static const int16_t phi_accel_combine_table[42] = { (int16_t)0x0000, (int16_t)0x020A, (int16_t)0x052C, (int16_t)0x082A, (int16_t)0x082A, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x020A, (int16_t)0x052C, (int16_t)0x082A, (int16_t)0x082A, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x020A, (int16_t)0x052C, (int16_t)0x082A, (int16_t)0x082A, (int16_t)0x0000, @@ -41,145 +41,157 @@ static const int16_t phi_accel_combine_table[42] = { (int16_t)0x0000, (int16_t)0x0000 }; -/* Accel 1-D refine (temperature) -- 5 words @ 0x9DB8. */ +/* Accel 1-D refine (temperature) -- 5 words @ 0x9DB0. */ static const int16_t phi_accel_refine_table[5] = { (int16_t)0x00FF, (int16_t)0x00FF, (int16_t)0x00FF, (int16_t)0x00FF, (int16_t)0x00FF }; -/* Angle RPM axis -- 13 words @ 0x7E8C. */ -static const int16_t phi_angle_axis_rpm[13] = { - (int16_t)0x4B5E, (int16_t)0x4674, (int16_t)0x4189, (int16_t)0x3AFB, (int16_t)0x346E, (int16_t)0x2DE0, (int16_t)0x240B, (int16_t)0x1A37, (int16_t)0x13A9, (int16_t)0x0D1B, - (int16_t)0x068E, (int16_t)0x0347, (int16_t)0x0000 +/* Angle RPM axis -- 15 words @ 0x7D44. */ +static const int16_t phi_angle_axis_rpm[15] = { + (int16_t)0x51EC, (int16_t)0x4D01, (int16_t)0x4817, (int16_t)0x4189, (int16_t)0x3AFB, (int16_t)0x346E, (int16_t)0x2DE0, (int16_t)0x2752, (int16_t)0x20C5, (int16_t)0x1A37, + (int16_t)0x13A9, (int16_t)0x0D1B, (int16_t)0x068E, (int16_t)0x0347, (int16_t)0x0000 }; -/* Angle inj_qty_demand axis -- 15 words @ 0x7EA6. */ +/* Angle inj_qty_demand axis -- 15 words @ 0x7D62. */ static const int16_t phi_angle_axis_demand[15] = { - (int16_t)0x0F90, (int16_t)0x0C73, (int16_t)0x0A60, (int16_t)0x0956, (int16_t)0x08D2, (int16_t)0x07C8, (int16_t)0x063A, (int16_t)0x04AB, (int16_t)0x031D, (int16_t)0x0213, + (int16_t)0x0C73, (int16_t)0x0A60, (int16_t)0x08D2, (int16_t)0x07C8, (int16_t)0x06BE, (int16_t)0x05B5, (int16_t)0x04AB, (int16_t)0x03A2, (int16_t)0x0298, (int16_t)0x018E, (int16_t)0x010A, (int16_t)0x0085, (int16_t)0x0035, (int16_t)0x000D, (int16_t)0x0000 }; -/* Angle angle_dec_cmd axis -- 3 words @ 0x7EC4. */ +/* Angle angle_dec_cmd axis -- 3 words @ 0x7D80. */ static const int16_t phi_angle_axis_dec_cmd[3] = { (int16_t)0x0300, (int16_t)0x01C4, (int16_t)0x0000 }; -/* 3-D angle combine (RPM x demand x angle_dec_cmd) -- 585 words @ 0x7ECA. */ -static const int16_t phi_angle_3d_table[585] = { - (int16_t)0x0508, (int16_t)0x07FB, (int16_t)0x0A1A, (int16_t)0x097B, (int16_t)0x08AD, (int16_t)0x07A5, (int16_t)0x0654, (int16_t)0x04FF, (int16_t)0x046F, (int16_t)0x03A0, - (int16_t)0x034F, (int16_t)0x02A7, (int16_t)0x02A7, (int16_t)0x0508, (int16_t)0x07FB, (int16_t)0x0A1A, (int16_t)0x097B, (int16_t)0x08AD, (int16_t)0x07A5, (int16_t)0x0654, - (int16_t)0x04FF, (int16_t)0x046F, (int16_t)0x03A0, (int16_t)0x034F, (int16_t)0x02A7, (int16_t)0x02A7, (int16_t)0x0508, (int16_t)0x07FB, (int16_t)0x0A1A, (int16_t)0x097B, - (int16_t)0x08AD, (int16_t)0x07A5, (int16_t)0x0654, (int16_t)0x04FF, (int16_t)0x046F, (int16_t)0x03A0, (int16_t)0x02F4, (int16_t)0x0238, (int16_t)0x0238, (int16_t)0x0508, - (int16_t)0x07FB, (int16_t)0x0A1A, (int16_t)0x097B, (int16_t)0x08AD, (int16_t)0x07A5, (int16_t)0x0654, (int16_t)0x04FF, (int16_t)0x042A, (int16_t)0x0367, (int16_t)0x02BB, - (int16_t)0x020C, (int16_t)0x020C, (int16_t)0x0508, (int16_t)0x07FB, (int16_t)0x0A1A, (int16_t)0x0956, (int16_t)0x086A, (int16_t)0x0772, (int16_t)0x0620, (int16_t)0x04D4, - (int16_t)0x0407, (int16_t)0x034F, (int16_t)0x02A4, (int16_t)0x01FA, (int16_t)0x01FA, (int16_t)0x0506, (int16_t)0x07F9, (int16_t)0x0974, (int16_t)0x0892, (int16_t)0x07C3, - (int16_t)0x06F0, (int16_t)0x05BA, (int16_t)0x0490, (int16_t)0x03C7, (int16_t)0x031C, (int16_t)0x0271, (int16_t)0x01D4, (int16_t)0x01D4, (int16_t)0x0504, (int16_t)0x07F7, - (int16_t)0x0838, (int16_t)0x0787, (int16_t)0x06D2, (int16_t)0x0628, (int16_t)0x050A, (int16_t)0x0413, (int16_t)0x036D, (int16_t)0x02DA, (int16_t)0x0229, (int16_t)0x019C, - (int16_t)0x019C, (int16_t)0x0501, (int16_t)0x07F4, (int16_t)0x0766, (int16_t)0x06C4, (int16_t)0x0626, (int16_t)0x0576, (int16_t)0x0487, (int16_t)0x039A, (int16_t)0x030F, - (int16_t)0x0291, (int16_t)0x01DE, (int16_t)0x0167, (int16_t)0x0167, (int16_t)0x04FF, (int16_t)0x06F4, (int16_t)0x068B, (int16_t)0x0600, (int16_t)0x057C, (int16_t)0x04EE, - (int16_t)0x03F4, (int16_t)0x031F, (int16_t)0x02A0, (int16_t)0x0231, (int16_t)0x018C, (int16_t)0x0140, (int16_t)0x0140, (int16_t)0x04FD, (int16_t)0x0661, (int16_t)0x05F9, - (int16_t)0x0582, (int16_t)0x04F9, (int16_t)0x0480, (int16_t)0x03AA, (int16_t)0x02D0, (int16_t)0x0261, (int16_t)0x01E6, (int16_t)0x0164, (int16_t)0x0121, (int16_t)0x0121, - (int16_t)0x04FD, (int16_t)0x05FD, (int16_t)0x059B, (int16_t)0x0524, (int16_t)0x04A7, (int16_t)0x041E, (int16_t)0x0363, (int16_t)0x029E, (int16_t)0x020E, (int16_t)0x0190, - (int16_t)0x0136, (int16_t)0x0103, (int16_t)0x0103, (int16_t)0x04FD, (int16_t)0x0593, (int16_t)0x0530, (int16_t)0x04C0, (int16_t)0x045A, (int16_t)0x03EF, (int16_t)0x0325, - (int16_t)0x0260, (int16_t)0x01E7, (int16_t)0x0158, (int16_t)0x011B, (int16_t)0x00AB, (int16_t)0x00AB, (int16_t)0x04FD, (int16_t)0x0523, (int16_t)0x04CB, (int16_t)0x0461, - (int16_t)0x03F5, (int16_t)0x038F, (int16_t)0x02EB, (int16_t)0x022F, (int16_t)0x01B1, (int16_t)0x0138, (int16_t)0x00BB, (int16_t)0x009B, (int16_t)0x009B, (int16_t)0x04FD, - (int16_t)0x04E5, (int16_t)0x0492, (int16_t)0x041D, (int16_t)0x03B0, (int16_t)0x034F, (int16_t)0x029C, (int16_t)0x01F1, (int16_t)0x0178, (int16_t)0x011B, (int16_t)0x00AE, - (int16_t)0x008B, (int16_t)0x008B, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, - (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0648, (int16_t)0x08FE, (int16_t)0x0B09, (int16_t)0x0A71, (int16_t)0x0972, - (int16_t)0x0889, (int16_t)0x0729, (int16_t)0x05D6, (int16_t)0x0552, (int16_t)0x046C, (int16_t)0x0420, (int16_t)0x035C, (int16_t)0x035C, (int16_t)0x0648, (int16_t)0x08FE, - (int16_t)0x0B09, (int16_t)0x0A71, (int16_t)0x0972, (int16_t)0x0889, (int16_t)0x0729, (int16_t)0x05D6, (int16_t)0x0552, (int16_t)0x046C, (int16_t)0x0420, (int16_t)0x035C, - (int16_t)0x035C, (int16_t)0x0648, (int16_t)0x08FE, (int16_t)0x0B09, (int16_t)0x0A71, (int16_t)0x0972, (int16_t)0x0889, (int16_t)0x0729, (int16_t)0x05D6, (int16_t)0x0552, - (int16_t)0x046C, (int16_t)0x03C1, (int16_t)0x0314, (int16_t)0x0314, (int16_t)0x0648, (int16_t)0x08FE, (int16_t)0x0B09, (int16_t)0x0A71, (int16_t)0x0972, (int16_t)0x0889, - (int16_t)0x0729, (int16_t)0x05D6, (int16_t)0x050F, (int16_t)0x043B, (int16_t)0x039A, (int16_t)0x02EE, (int16_t)0x02EE, (int16_t)0x0648, (int16_t)0x08FE, (int16_t)0x0B09, - (int16_t)0x0A19, (int16_t)0x092F, (int16_t)0x084F, (int16_t)0x06F8, (int16_t)0x05AF, (int16_t)0x04ED, (int16_t)0x0425, (int16_t)0x0384, (int16_t)0x02D9, (int16_t)0x02D9, - (int16_t)0x0646, (int16_t)0x08FC, (int16_t)0x0A4D, (int16_t)0x0975, (int16_t)0x089C, (int16_t)0x07D0, (int16_t)0x0697, (int16_t)0x0564, (int16_t)0x04B1, (int16_t)0x03F8, - (int16_t)0x0351, (int16_t)0x02AF, (int16_t)0x02AF, (int16_t)0x0644, (int16_t)0x08FA, (int16_t)0x0939, (int16_t)0x086F, (int16_t)0x07B7, (int16_t)0x0711, (int16_t)0x0602, - (int16_t)0x04FA, (int16_t)0x0458, (int16_t)0x03B0, (int16_t)0x0305, (int16_t)0x0280, (int16_t)0x0280, (int16_t)0x0641, (int16_t)0x08F7, (int16_t)0x0867, (int16_t)0x07B6, - (int16_t)0x0707, (int16_t)0x066B, (int16_t)0x0570, (int16_t)0x0488, (int16_t)0x03FA, (int16_t)0x035E, (int16_t)0x02B1, (int16_t)0x0251, (int16_t)0x0251, (int16_t)0x063F, - (int16_t)0x07FB, (int16_t)0x077B, (int16_t)0x06E4, (int16_t)0x063D, (int16_t)0x05CB, (int16_t)0x04E8, (int16_t)0x040E, (int16_t)0x03A1, (int16_t)0x031B, (int16_t)0x0273, - (int16_t)0x021C, (int16_t)0x021C, (int16_t)0x063D, (int16_t)0x0761, (int16_t)0x06E7, (int16_t)0x064D, (int16_t)0x05BF, (int16_t)0x0552, (int16_t)0x048A, (int16_t)0x03BE, - (int16_t)0x0354, (int16_t)0x02D6, (int16_t)0x0238, (int16_t)0x01F5, (int16_t)0x01F5, (int16_t)0x063D, (int16_t)0x06C1, (int16_t)0x0666, (int16_t)0x05DA, (int16_t)0x0574, - (int16_t)0x04FF, (int16_t)0x0421, (int16_t)0x0361, (int16_t)0x02EA, (int16_t)0x0274, (int16_t)0x0201, (int16_t)0x01D2, (int16_t)0x01D2, (int16_t)0x063D, (int16_t)0x0663, - (int16_t)0x05F9, (int16_t)0x0586, (int16_t)0x0527, (int16_t)0x04B7, (int16_t)0x03E0, (int16_t)0x0316, (int16_t)0x02A2, (int16_t)0x0239, (int16_t)0x01A0, (int16_t)0x016B, - (int16_t)0x016B, (int16_t)0x063D, (int16_t)0x063D, (int16_t)0x05CF, (int16_t)0x0553, (int16_t)0x04FB, (int16_t)0x048E, (int16_t)0x03C6, (int16_t)0x02E9, (int16_t)0x0275, - (int16_t)0x01FB, (int16_t)0x0166, (int16_t)0x012F, (int16_t)0x012F, (int16_t)0x063D, (int16_t)0x0619, (int16_t)0x05B8, (int16_t)0x052C, (int16_t)0x04D3, (int16_t)0x0466, - (int16_t)0x03A0, (int16_t)0x02BC, (int16_t)0x0249, (int16_t)0x01C4, (int16_t)0x013B, (int16_t)0x0104, (int16_t)0x0104, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, +/* 3-D angle combine (RPM x demand x angle_dec_cmd) -- 675 words @ 0x7D86. */ +static const int16_t phi_angle_3d_table[675] = { + (int16_t)0x0A88, (int16_t)0x0A45, (int16_t)0x09CC, (int16_t)0x0905, (int16_t)0x0943, (int16_t)0x0881, (int16_t)0x079F, (int16_t)0x074F, (int16_t)0x06C2, (int16_t)0x0665, + (int16_t)0x0532, (int16_t)0x04C3, (int16_t)0x03E6, (int16_t)0x036F, (int16_t)0x036F, (int16_t)0x0A88, (int16_t)0x0A45, (int16_t)0x09CC, (int16_t)0x0905, (int16_t)0x0943, + (int16_t)0x0881, (int16_t)0x079F, (int16_t)0x074F, (int16_t)0x06C2, (int16_t)0x0665, (int16_t)0x0532, (int16_t)0x043A, (int16_t)0x0365, (int16_t)0x02E1, (int16_t)0x02E1, + (int16_t)0x0A88, (int16_t)0x0A45, (int16_t)0x09CC, (int16_t)0x0905, (int16_t)0x0943, (int16_t)0x0881, (int16_t)0x079F, (int16_t)0x074F, (int16_t)0x06C2, (int16_t)0x05BE, + (int16_t)0x04B2, (int16_t)0x03D5, (int16_t)0x0309, (int16_t)0x028C, (int16_t)0x028C, (int16_t)0x0A88, (int16_t)0x0A45, (int16_t)0x09CC, (int16_t)0x0905, (int16_t)0x0943, + (int16_t)0x0881, (int16_t)0x079F, (int16_t)0x074F, (int16_t)0x064D, (int16_t)0x0558, (int16_t)0x045E, (int16_t)0x0392, (int16_t)0x02C9, (int16_t)0x0247, (int16_t)0x0247, + (int16_t)0x0A88, (int16_t)0x0A45, (int16_t)0x09CC, (int16_t)0x0905, (int16_t)0x0943, (int16_t)0x0881, (int16_t)0x079F, (int16_t)0x06AD, (int16_t)0x05CA, (int16_t)0x04EB, + (int16_t)0x040A, (int16_t)0x0351, (int16_t)0x029A, (int16_t)0x0214, (int16_t)0x0214, (int16_t)0x0A88, (int16_t)0x0A45, (int16_t)0x09CC, (int16_t)0x0905, (int16_t)0x083F, + (int16_t)0x0786, (int16_t)0x06D0, (int16_t)0x0600, (int16_t)0x053E, (int16_t)0x0485, (int16_t)0x03C1, (int16_t)0x0312, (int16_t)0x0250, (int16_t)0x01CF, (int16_t)0x01CF, + (int16_t)0x0990, (int16_t)0x0957, (int16_t)0x08DD, (int16_t)0x081E, (int16_t)0x075F, (int16_t)0x06C3, (int16_t)0x0624, (int16_t)0x056A, (int16_t)0x04AC, (int16_t)0x0413, + (int16_t)0x036E, (int16_t)0x02D8, (int16_t)0x020F, (int16_t)0x01AE, (int16_t)0x01AE, (int16_t)0x08DD, (int16_t)0x0866, (int16_t)0x07FD, (int16_t)0x075C, (int16_t)0x06C7, + (int16_t)0x0637, (int16_t)0x058C, (int16_t)0x04EB, (int16_t)0x043B, (int16_t)0x03A3, (int16_t)0x0314, (int16_t)0x029B, (int16_t)0x01D0, (int16_t)0x018C, (int16_t)0x018C, + (int16_t)0x0832, (int16_t)0x07A9, (int16_t)0x073F, (int16_t)0x06A6, (int16_t)0x0614, (int16_t)0x0579, (int16_t)0x0508, (int16_t)0x0477, (int16_t)0x03C9, (int16_t)0x0348, + (int16_t)0x02BF, (int16_t)0x024F, (int16_t)0x01A6, (int16_t)0x0165, (int16_t)0x0165, (int16_t)0x0798, (int16_t)0x0738, (int16_t)0x06CB, (int16_t)0x0643, (int16_t)0x05BB, + (int16_t)0x053B, (int16_t)0x04A5, (int16_t)0x042E, (int16_t)0x0389, (int16_t)0x0313, (int16_t)0x027C, (int16_t)0x0205, (int16_t)0x0175, (int16_t)0x014C, (int16_t)0x014C, + (int16_t)0x0721, (int16_t)0x06BA, (int16_t)0x0654, (int16_t)0x05DC, (int16_t)0x055F, (int16_t)0x04E0, (int16_t)0x0460, (int16_t)0x03C7, (int16_t)0x0346, (int16_t)0x02C8, + (int16_t)0x0242, (int16_t)0x01C9, (int16_t)0x013C, (int16_t)0x0125, (int16_t)0x0125, (int16_t)0x067F, (int16_t)0x061F, (int16_t)0x05C3, (int16_t)0x054B, (int16_t)0x04E7, + (int16_t)0x047A, (int16_t)0x0408, (int16_t)0x038D, (int16_t)0x0312, (int16_t)0x029D, (int16_t)0x0213, (int16_t)0x0192, (int16_t)0x0117, (int16_t)0x00FF, (int16_t)0x00FF, + (int16_t)0x064B, (int16_t)0x05E5, (int16_t)0x0590, (int16_t)0x0510, (int16_t)0x04AF, (int16_t)0x0436, (int16_t)0x03C4, (int16_t)0x0348, (int16_t)0x02DF, (int16_t)0x027F, + (int16_t)0x01F5, (int16_t)0x017B, (int16_t)0x0108, (int16_t)0x00E1, (int16_t)0x00E1, (int16_t)0x0618, (int16_t)0x05BA, (int16_t)0x0565, (int16_t)0x04EE, (int16_t)0x0472, + (int16_t)0x040B, (int16_t)0x0388, (int16_t)0x031E, (int16_t)0x02B4, (int16_t)0x025B, (int16_t)0x01E0, (int16_t)0x0166, (int16_t)0x00F7, (int16_t)0x00D0, (int16_t)0x00D0, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, - (int16_t)0x079D, (int16_t)0x0AB3, (int16_t)0x0CC8, (int16_t)0x0C36, (int16_t)0x0B3C, (int16_t)0x0A48, (int16_t)0x09F1, (int16_t)0x0896, (int16_t)0x0796, (int16_t)0x068B, - (int16_t)0x066B, (int16_t)0x067C, (int16_t)0x067C, (int16_t)0x079D, (int16_t)0x0AB3, (int16_t)0x0CC8, (int16_t)0x0C36, (int16_t)0x0B3C, (int16_t)0x0A48, (int16_t)0x09F1, - (int16_t)0x0896, (int16_t)0x0796, (int16_t)0x068B, (int16_t)0x05E8, (int16_t)0x0526, (int16_t)0x0526, (int16_t)0x079D, (int16_t)0x0AB3, (int16_t)0x0CC8, (int16_t)0x0C36, - (int16_t)0x0B3C, (int16_t)0x0A48, (int16_t)0x093E, (int16_t)0x07F0, (int16_t)0x0713, (int16_t)0x062F, (int16_t)0x0586, (int16_t)0x04D3, (int16_t)0x04D3, (int16_t)0x079D, - (int16_t)0x0AB3, (int16_t)0x0CC8, (int16_t)0x0C36, (int16_t)0x0B3C, (int16_t)0x0A48, (int16_t)0x08E5, (int16_t)0x079A, (int16_t)0x06D0, (int16_t)0x0601, (int16_t)0x055B, - (int16_t)0x04B2, (int16_t)0x04B2, (int16_t)0x079D, (int16_t)0x0AB3, (int16_t)0x0CC8, (int16_t)0x0BE0, (int16_t)0x0AF3, (int16_t)0x0A0E, (int16_t)0x08B8, (int16_t)0x0770, - (int16_t)0x06B0, (int16_t)0x05E9, (int16_t)0x0549, (int16_t)0x0499, (int16_t)0x0499, (int16_t)0x079B, (int16_t)0x0AB1, (int16_t)0x0C12, (int16_t)0x0B37, (int16_t)0x0A62, - (int16_t)0x0991, (int16_t)0x0858, (int16_t)0x0728, (int16_t)0x0676, (int16_t)0x05BB, (int16_t)0x0514, (int16_t)0x0476, (int16_t)0x0476, (int16_t)0x0799, (int16_t)0x0AAF, - (int16_t)0x0AFB, (int16_t)0x0A4B, (int16_t)0x0997, (int16_t)0x08E3, (int16_t)0x07C8, (int16_t)0x06BA, (int16_t)0x061E, (int16_t)0x0575, (int16_t)0x04C4, (int16_t)0x0441, - (int16_t)0x0441, (int16_t)0x0796, (int16_t)0x0AAC, (int16_t)0x0A25, (int16_t)0x0977, (int16_t)0x08CF, (int16_t)0x0826, (int16_t)0x072F, (int16_t)0x0647, (int16_t)0x05BE, - (int16_t)0x0522, (int16_t)0x0474, (int16_t)0x0412, (int16_t)0x0412, (int16_t)0x0794, (int16_t)0x09C2, (int16_t)0x0944, (int16_t)0x089A, (int16_t)0x0818, (int16_t)0x078D, - (int16_t)0x06AF, (int16_t)0x05EE, (int16_t)0x0566, (int16_t)0x04E2, (int16_t)0x0434, (int16_t)0x03DA, (int16_t)0x03DA, (int16_t)0x0792, (int16_t)0x08D7, (int16_t)0x0876, - (int16_t)0x07EC, (int16_t)0x076D, (int16_t)0x06FF, (int16_t)0x064A, (int16_t)0x0586, (int16_t)0x0523, (int16_t)0x04A0, (int16_t)0x03FD, (int16_t)0x03B6, (int16_t)0x03B6, - (int16_t)0x0792, (int16_t)0x083A, (int16_t)0x07DB, (int16_t)0x076F, (int16_t)0x070F, (int16_t)0x069B, (int16_t)0x05EA, (int16_t)0x0523, (int16_t)0x04AB, (int16_t)0x043D, - (int16_t)0x03C6, (int16_t)0x0394, (int16_t)0x0394, (int16_t)0x0792, (int16_t)0x07D1, (int16_t)0x077A, (int16_t)0x0712, (int16_t)0x06B5, (int16_t)0x0654, (int16_t)0x0594, - (int16_t)0x04D9, (int16_t)0x0468, (int16_t)0x03FD, (int16_t)0x035F, (int16_t)0x0304, (int16_t)0x0304, (int16_t)0x0792, (int16_t)0x07AB, (int16_t)0x074E, (int16_t)0x06CF, - (int16_t)0x067A, (int16_t)0x061B, (int16_t)0x0568, (int16_t)0x04B1, (int16_t)0x0444, (int16_t)0x03BB, (int16_t)0x0332, (int16_t)0x02D1, (int16_t)0x02D1, (int16_t)0x0792, - (int16_t)0x0773, (int16_t)0x0710, (int16_t)0x069A, (int16_t)0x0640, (int16_t)0x05CE, (int16_t)0x051F, (int16_t)0x0479, (int16_t)0x0418, (int16_t)0x0390, (int16_t)0x0321, - (int16_t)0x02C0, (int16_t)0x02C0, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, - (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000 + (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0003, (int16_t)0x0003, (int16_t)0x0B84, (int16_t)0x0B26, (int16_t)0x0BDB, (int16_t)0x0AE0, (int16_t)0x0AAE, + (int16_t)0x09F4, (int16_t)0x094A, (int16_t)0x0890, (int16_t)0x0805, (int16_t)0x07A5, (int16_t)0x0655, (int16_t)0x0545, (int16_t)0x048F, (int16_t)0x0436, (int16_t)0x0436, + (int16_t)0x0B84, (int16_t)0x0B26, (int16_t)0x0BDB, (int16_t)0x0AE0, (int16_t)0x0AAE, (int16_t)0x09F4, (int16_t)0x094A, (int16_t)0x0890, (int16_t)0x0805, (int16_t)0x06E2, + (int16_t)0x05C9, (int16_t)0x04CF, (int16_t)0x0417, (int16_t)0x03C7, (int16_t)0x03C7, (int16_t)0x0B84, (int16_t)0x0B26, (int16_t)0x0BDB, (int16_t)0x0AE0, (int16_t)0x0AAE, + (int16_t)0x09F4, (int16_t)0x094A, (int16_t)0x0890, (int16_t)0x077E, (int16_t)0x066B, (int16_t)0x0566, (int16_t)0x0480, (int16_t)0x03C4, (int16_t)0x0369, (int16_t)0x0369, + (int16_t)0x0B84, (int16_t)0x0B26, (int16_t)0x0BDB, (int16_t)0x0AE0, (int16_t)0x0AAE, (int16_t)0x09F4, (int16_t)0x08E8, (int16_t)0x080B, (int16_t)0x070C, (int16_t)0x060B, + (int16_t)0x051B, (int16_t)0x0440, (int16_t)0x0387, (int16_t)0x0325, (int16_t)0x0325, (int16_t)0x0B84, (int16_t)0x0B26, (int16_t)0x0BDB, (int16_t)0x0AE0, (int16_t)0x09F4, + (int16_t)0x0936, (int16_t)0x0843, (int16_t)0x076C, (int16_t)0x068E, (int16_t)0x05A0, (int16_t)0x04C9, (int16_t)0x0403, (int16_t)0x0350, (int16_t)0x02D8, (int16_t)0x02D8, + (int16_t)0x0B84, (int16_t)0x0B26, (int16_t)0x0AB2, (int16_t)0x09D7, (int16_t)0x0912, (int16_t)0x0851, (int16_t)0x0787, (int16_t)0x06BE, (int16_t)0x060A, (int16_t)0x053C, + (int16_t)0x0476, (int16_t)0x03BD, (int16_t)0x0316, (int16_t)0x0297, (int16_t)0x0297, (int16_t)0x0ADD, (int16_t)0x0A4E, (int16_t)0x09C4, (int16_t)0x0909, (int16_t)0x0853, + (int16_t)0x07A0, (int16_t)0x06DD, (int16_t)0x062B, (int16_t)0x0581, (int16_t)0x04DC, (int16_t)0x042A, (int16_t)0x038B, (int16_t)0x02E5, (int16_t)0x0269, (int16_t)0x0269, + (int16_t)0x09B2, (int16_t)0x0937, (int16_t)0x08BF, (int16_t)0x082F, (int16_t)0x0795, (int16_t)0x0702, (int16_t)0x0669, (int16_t)0x05B4, (int16_t)0x0509, (int16_t)0x0477, + (int16_t)0x03DA, (int16_t)0x0355, (int16_t)0x02A9, (int16_t)0x0246, (int16_t)0x0246, (int16_t)0x0929, (int16_t)0x08B8, (int16_t)0x0836, (int16_t)0x078A, (int16_t)0x06FC, + (int16_t)0x0670, (int16_t)0x05C4, (int16_t)0x053B, (int16_t)0x04A7, (int16_t)0x0418, (int16_t)0x038D, (int16_t)0x031D, (int16_t)0x026F, (int16_t)0x021D, (int16_t)0x021D, + (int16_t)0x0832, (int16_t)0x07BB, (int16_t)0x0754, (int16_t)0x06CB, (int16_t)0x0643, (int16_t)0x05DD, (int16_t)0x0549, (int16_t)0x04C3, (int16_t)0x0430, (int16_t)0x03AD, + (int16_t)0x0334, (int16_t)0x02C0, (int16_t)0x0234, (int16_t)0x01F5, (int16_t)0x01F5, (int16_t)0x07FF, (int16_t)0x077F, (int16_t)0x0725, (int16_t)0x0698, (int16_t)0x0611, + (int16_t)0x0594, (int16_t)0x0519, (int16_t)0x0491, (int16_t)0x040C, (int16_t)0x0392, (int16_t)0x030D, (int16_t)0x028D, (int16_t)0x0218, (int16_t)0x01EB, (int16_t)0x01EB, + (int16_t)0x07C3, (int16_t)0x0733, (int16_t)0x06CB, (int16_t)0x066B, (int16_t)0x05F0, (int16_t)0x056A, (int16_t)0x04F7, (int16_t)0x046B, (int16_t)0x03C6, (int16_t)0x0346, + (int16_t)0x02C6, (int16_t)0x024E, (int16_t)0x01FB, (int16_t)0x01DD, (int16_t)0x01DD, (int16_t)0x0790, (int16_t)0x070E, (int16_t)0x0698, (int16_t)0x061E, (int16_t)0x05B4, + (int16_t)0x052D, (int16_t)0x04B4, (int16_t)0x0440, (int16_t)0x03AE, (int16_t)0x0322, (int16_t)0x02AC, (int16_t)0x0230, (int16_t)0x01DD, (int16_t)0x01CD, (int16_t)0x01CD, + (int16_t)0x0754, (int16_t)0x06CB, (int16_t)0x066E, (int16_t)0x05E5, (int16_t)0x057B, (int16_t)0x04FB, (int16_t)0x0488, (int16_t)0x040D, (int16_t)0x0381, (int16_t)0x030B, + (int16_t)0x027E, (int16_t)0x020C, (int16_t)0x01BB, (int16_t)0x01B6, (int16_t)0x01B6, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, + (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0003, (int16_t)0x0003, + (int16_t)0x0CA6, (int16_t)0x0CA6, (int16_t)0x0D83, (int16_t)0x0C7C, (int16_t)0x0B98, (int16_t)0x0B71, (int16_t)0x0A7A, (int16_t)0x0A0E, (int16_t)0x0913, (int16_t)0x080E, + (int16_t)0x0724, (int16_t)0x0636, (int16_t)0x0667, (int16_t)0x059D, (int16_t)0x059D, (int16_t)0x0CA6, (int16_t)0x0CA6, (int16_t)0x0D83, (int16_t)0x0C7C, (int16_t)0x0B98, + (int16_t)0x0B71, (int16_t)0x0A7A, (int16_t)0x0A0E, (int16_t)0x0913, (int16_t)0x080E, (int16_t)0x0724, (int16_t)0x0636, (int16_t)0x05E6, (int16_t)0x0527, (int16_t)0x0527, + (int16_t)0x0CA6, (int16_t)0x0CA6, (int16_t)0x0D83, (int16_t)0x0C7C, (int16_t)0x0B98, (int16_t)0x0B71, (int16_t)0x0A7A, (int16_t)0x0A0E, (int16_t)0x0913, (int16_t)0x080E, + (int16_t)0x0724, (int16_t)0x0636, (int16_t)0x0598, (int16_t)0x04DC, (int16_t)0x04DC, (int16_t)0x0CA6, (int16_t)0x0CA6, (int16_t)0x0D83, (int16_t)0x0C7C, (int16_t)0x0B98, + (int16_t)0x0B71, (int16_t)0x0A7A, (int16_t)0x0987, (int16_t)0x089F, (int16_t)0x07AC, (int16_t)0x06D6, (int16_t)0x05FD, (int16_t)0x0556, (int16_t)0x04AE, (int16_t)0x04AE, + (int16_t)0x0CA6, (int16_t)0x0CA6, (int16_t)0x0D83, (int16_t)0x0C7C, (int16_t)0x0B98, (int16_t)0x0ABC, (int16_t)0x09E0, (int16_t)0x0905, (int16_t)0x082C, (int16_t)0x0751, + (int16_t)0x068D, (int16_t)0x05C3, (int16_t)0x051B, (int16_t)0x048A, (int16_t)0x048A, (int16_t)0x0CA6, (int16_t)0x0CA6, (int16_t)0x0C36, (int16_t)0x0B72, (int16_t)0x0AAE, + (int16_t)0x09F6, (int16_t)0x093A, (int16_t)0x0879, (int16_t)0x07B5, (int16_t)0x06F1, (int16_t)0x063F, (int16_t)0x0589, (int16_t)0x04DE, (int16_t)0x045B, (int16_t)0x045B, + (int16_t)0x0C62, (int16_t)0x0BDD, (int16_t)0x0B57, (int16_t)0x0A9C, (int16_t)0x09EE, (int16_t)0x0943, (int16_t)0x0893, (int16_t)0x07EB, (int16_t)0x0741, (int16_t)0x0691, + (int16_t)0x05F6, (int16_t)0x0554, (int16_t)0x04A9, (int16_t)0x042C, (int16_t)0x042C, (int16_t)0x0B8C, (int16_t)0x0B19, (int16_t)0x0AA6, (int16_t)0x0A01, (int16_t)0x0953, + (int16_t)0x08AA, (int16_t)0x0806, (int16_t)0x0769, (int16_t)0x06D0, (int16_t)0x0635, (int16_t)0x05AB, (int16_t)0x0522, (int16_t)0x046D, (int16_t)0x03FF, (int16_t)0x03FF, + (int16_t)0x0B04, (int16_t)0x0A74, (int16_t)0x09E5, (int16_t)0x0959, (int16_t)0x08BC, (int16_t)0x081F, (int16_t)0x0780, (int16_t)0x06EE, (int16_t)0x065E, (int16_t)0x05C9, + (int16_t)0x056C, (int16_t)0x04E6, (int16_t)0x042D, (int16_t)0x03D2, (int16_t)0x03D2, (int16_t)0x09CD, (int16_t)0x0966, (int16_t)0x08FF, (int16_t)0x084B, (int16_t)0x07E2, + (int16_t)0x0775, (int16_t)0x06F2, (int16_t)0x066E, (int16_t)0x05F3, (int16_t)0x0579, (int16_t)0x050D, (int16_t)0x0481, (int16_t)0x03E8, (int16_t)0x03A4, (int16_t)0x03A4, + (int16_t)0x096E, (int16_t)0x08F9, (int16_t)0x0883, (int16_t)0x07F8, (int16_t)0x0785, (int16_t)0x0726, (int16_t)0x06BF, (int16_t)0x0642, (int16_t)0x05C8, (int16_t)0x0548, + (int16_t)0x04C9, (int16_t)0x0449, (int16_t)0x03C8, (int16_t)0x0394, (int16_t)0x0394, (int16_t)0x0921, (int16_t)0x08B2, (int16_t)0x0843, (int16_t)0x07A2, (int16_t)0x074E, + (int16_t)0x06C9, (int16_t)0x065E, (int16_t)0x05EC, (int16_t)0x056A, (int16_t)0x04F4, (int16_t)0x047A, (int16_t)0x0407, (int16_t)0x03A9, (int16_t)0x035B, (int16_t)0x035B, + (int16_t)0x0880, (int16_t)0x083A, (int16_t)0x07F4, (int16_t)0x077B, (int16_t)0x06F3, (int16_t)0x0699, (int16_t)0x060B, (int16_t)0x05B0, (int16_t)0x052C, (int16_t)0x04BF, + (int16_t)0x044C, (int16_t)0x03DB, (int16_t)0x0392, (int16_t)0x0338, (int16_t)0x0338, (int16_t)0x07FF, (int16_t)0x07CA, (int16_t)0x0794, (int16_t)0x074F, (int16_t)0x06D8, + (int16_t)0x067C, (int16_t)0x05E7, (int16_t)0x0587, (int16_t)0x0509, (int16_t)0x04A2, (int16_t)0x0437, (int16_t)0x03C6, (int16_t)0x0354, (int16_t)0x0327, (int16_t)0x0327, + (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, + (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0000, (int16_t)0x0003, (int16_t)0x0003 }; -/* 2-D angle-kick combine (RPM x demand) -- consumed by orphan compute_angle_kick_2d @ 0x6AC9 (T06211 analog FUN_5D58 @ 0x5D8D) -- 195 words @ 0x835C. */ -static const int16_t phi_angle_2d_kick_table[195] = { - (int16_t)0x0C16, (int16_t)0x0C16, (int16_t)0x0C16, (int16_t)0x0C16, (int16_t)0x0B8E, (int16_t)0x0C42, (int16_t)0x0957, (int16_t)0x066D, (int16_t)0x074F, (int16_t)0x0831, - (int16_t)0x05F6, (int16_t)0x05F6, (int16_t)0x05F6, (int16_t)0x0C16, (int16_t)0x0C16, (int16_t)0x0C16, (int16_t)0x0C16, (int16_t)0x0B8E, (int16_t)0x0C42, (int16_t)0x0957, - (int16_t)0x066D, (int16_t)0x074F, (int16_t)0x0831, (int16_t)0x05F6, (int16_t)0x05F6, (int16_t)0x05F6, (int16_t)0x0C16, (int16_t)0x0C16, (int16_t)0x0C16, (int16_t)0x0C16, - (int16_t)0x0B8E, (int16_t)0x0C42, (int16_t)0x0957, (int16_t)0x066D, (int16_t)0x074F, (int16_t)0x0831, (int16_t)0x05F6, (int16_t)0x05F6, (int16_t)0x05F6, (int16_t)0x0C16, - (int16_t)0x0C16, (int16_t)0x0C16, (int16_t)0x0C16, (int16_t)0x0B8E, (int16_t)0x0C42, (int16_t)0x0957, (int16_t)0x066D, (int16_t)0x074F, (int16_t)0x0831, (int16_t)0x05F6, - (int16_t)0x05F6, (int16_t)0x05F6, (int16_t)0x0A33, (int16_t)0x0A33, (int16_t)0x0A33, (int16_t)0x0A33, (int16_t)0x09A2, (int16_t)0x0A10, (int16_t)0x07C0, (int16_t)0x0570, - (int16_t)0x0682, (int16_t)0x0795, (int16_t)0x054C, (int16_t)0x054C, (int16_t)0x054C, (int16_t)0x075F, (int16_t)0x075F, (int16_t)0x075F, (int16_t)0x075F, (int16_t)0x06C0, - (int16_t)0x06C6, (int16_t)0x055D, (int16_t)0x03F4, (int16_t)0x054F, (int16_t)0x06AB, (int16_t)0x044D, (int16_t)0x044D, (int16_t)0x044D, (int16_t)0x0481, (int16_t)0x0481, - (int16_t)0x0481, (int16_t)0x0481, (int16_t)0x0542, (int16_t)0x04A0, (int16_t)0x03A9, (int16_t)0x02B3, (int16_t)0x0372, (int16_t)0x0431, (int16_t)0x04AE, (int16_t)0x04AE, - (int16_t)0x04AE, (int16_t)0x01A3, (int16_t)0x01A3, (int16_t)0x01A3, (int16_t)0x01A3, (int16_t)0x03C4, (int16_t)0x027A, (int16_t)0x01F6, (int16_t)0x0172, (int16_t)0x0194, - (int16_t)0x01B7, (int16_t)0x0510, (int16_t)0x0510, (int16_t)0x0510, (int16_t)0x016B, (int16_t)0x016B, (int16_t)0x016B, (int16_t)0x016B, (int16_t)0x02CB, (int16_t)0x013D, - (int16_t)0x0141, (int16_t)0x0146, (int16_t)0x01A2, (int16_t)0x01FF, (int16_t)0x0421, (int16_t)0x0421, (int16_t)0x0421, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x0133, - (int16_t)0x0133, (int16_t)0x01D3, (int16_t)0x0000, (int16_t)0x008D, (int16_t)0x011A, (int16_t)0x01B0, (int16_t)0x0247, (int16_t)0x0333, (int16_t)0x0333, (int16_t)0x0333, - (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x01D3, (int16_t)0x0000, (int16_t)0x008D, (int16_t)0x011A, (int16_t)0x01B0, (int16_t)0x0247, - (int16_t)0x0333, (int16_t)0x0333, (int16_t)0x0333, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x01D3, (int16_t)0x0000, (int16_t)0x008D, - (int16_t)0x011A, (int16_t)0x01B0, (int16_t)0x0247, (int16_t)0x0333, (int16_t)0x0333, (int16_t)0x0333, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x0133, - (int16_t)0x01D3, (int16_t)0x0000, (int16_t)0x008D, (int16_t)0x011A, (int16_t)0x01B0, (int16_t)0x0247, (int16_t)0x0333, (int16_t)0x0333, (int16_t)0x0333, (int16_t)0x0133, - (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x01D3, (int16_t)0x0000, (int16_t)0x008D, (int16_t)0x011A, (int16_t)0x01B0, (int16_t)0x0247, (int16_t)0x0333, - (int16_t)0x0333, (int16_t)0x0333, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x0133, (int16_t)0x01D3, (int16_t)0x0000, (int16_t)0x008D, (int16_t)0x011A, - (int16_t)0x01B0, (int16_t)0x0247, (int16_t)0x0333, (int16_t)0x0333, (int16_t)0x0333 +/* 2-D angle-kick combine (RPM x demand) -- consumed by orphan FUN_5D58 @ 0x5D8D -- 225 words @ 0x82CC. */ +static const int16_t phi_angle_2d_kick_table[225] = { + (int16_t)0x0B0D, (int16_t)0x0B0D, (int16_t)0x0CAD, (int16_t)0x0D73, (int16_t)0x0BFB, (int16_t)0x0B63, (int16_t)0x0A4B, (int16_t)0x08F7, (int16_t)0x085F, (int16_t)0x0720, + (int16_t)0x0838, (int16_t)0x0CEE, (int16_t)0x0616, (int16_t)0x05B2, (int16_t)0x05B2, (int16_t)0x0B0D, (int16_t)0x0B0D, (int16_t)0x0CAD, (int16_t)0x0D73, (int16_t)0x0BFB, + (int16_t)0x0B63, (int16_t)0x0A4B, (int16_t)0x08F7, (int16_t)0x085F, (int16_t)0x0720, (int16_t)0x0838, (int16_t)0x0CEE, (int16_t)0x0616, (int16_t)0x05B2, (int16_t)0x05B2, + (int16_t)0x0B0D, (int16_t)0x0B0D, (int16_t)0x0CAD, (int16_t)0x0D73, (int16_t)0x0BFB, (int16_t)0x0B63, (int16_t)0x0A4B, (int16_t)0x08F7, (int16_t)0x085F, (int16_t)0x0720, + (int16_t)0x0838, (int16_t)0x0CEE, (int16_t)0x0616, (int16_t)0x05B2, (int16_t)0x05B2, (int16_t)0x0B0D, (int16_t)0x0B0D, (int16_t)0x0CAD, (int16_t)0x0D73, (int16_t)0x0BFB, + (int16_t)0x0B63, (int16_t)0x0A4B, (int16_t)0x08F7, (int16_t)0x085F, (int16_t)0x0720, (int16_t)0x0838, (int16_t)0x0CEE, (int16_t)0x0616, (int16_t)0x05B2, (int16_t)0x05B2, + (int16_t)0x0B0D, (int16_t)0x0B0D, (int16_t)0x0CAD, (int16_t)0x0D73, (int16_t)0x0BFB, (int16_t)0x0B63, (int16_t)0x0A4B, (int16_t)0x08F7, (int16_t)0x085F, (int16_t)0x0720, + (int16_t)0x0838, (int16_t)0x0CEE, (int16_t)0x0616, (int16_t)0x05B2, (int16_t)0x05B2, (int16_t)0x0B0D, (int16_t)0x0B0D, (int16_t)0x0CAD, (int16_t)0x0B30, (int16_t)0x0AA5, + (int16_t)0x08BC, (int16_t)0x07E7, (int16_t)0x07E9, (int16_t)0x06A6, (int16_t)0x0613, (int16_t)0x0831, (int16_t)0x0CF2, (int16_t)0x04EB, (int16_t)0x05FE, (int16_t)0x05FE, + (int16_t)0x0686, (int16_t)0x0686, (int16_t)0x0632, (int16_t)0x0886, (int16_t)0x06A4, (int16_t)0x0661, (int16_t)0x05C9, (int16_t)0x04A3, (int16_t)0x0526, (int16_t)0x0525, + (int16_t)0x06B5, (int16_t)0x09CF, (int16_t)0x039E, (int16_t)0x04BA, (int16_t)0x04BA, (int16_t)0x0660, (int16_t)0x0660, (int16_t)0x05D3, (int16_t)0x067D, (int16_t)0x0572, + (int16_t)0x04B2, (int16_t)0x0495, (int16_t)0x032B, (int16_t)0x044F, (int16_t)0x0403, (int16_t)0x0579, (int16_t)0x06D7, (int16_t)0x0443, (int16_t)0x0431, (int16_t)0x0431, + (int16_t)0x05C5, (int16_t)0x05C5, (int16_t)0x0388, (int16_t)0x045D, (int16_t)0x048D, (int16_t)0x0427, (int16_t)0x0306, (int16_t)0x029E, (int16_t)0x0230, (int16_t)0x02D8, + (int16_t)0x055D, (int16_t)0x0397, (int16_t)0x031F, (int16_t)0x03BC, (int16_t)0x03BC, (int16_t)0x0235, (int16_t)0x0235, (int16_t)0x0423, (int16_t)0x0463, (int16_t)0x01B4, + (int16_t)0x0236, (int16_t)0x0371, (int16_t)0x01A1, (int16_t)0x0215, (int16_t)0x0274, (int16_t)0x025F, (int16_t)0x0255, (int16_t)0x0286, (int16_t)0x028E, (int16_t)0x028E, + (int16_t)0x0235, (int16_t)0x0235, (int16_t)0x0154, (int16_t)0x005D, (int16_t)0x02B8, (int16_t)0x004B, (int16_t)0x01A0, (int16_t)0x0134, (int16_t)0x0133, (int16_t)0x015B, + (int16_t)0x0152, (int16_t)0x022F, (int16_t)0x0202, (int16_t)0x0224, (int16_t)0x0224, (int16_t)0x0235, (int16_t)0x0235, (int16_t)0x0154, (int16_t)0x005D, (int16_t)0x02B8, + (int16_t)0x004B, (int16_t)0x01A0, (int16_t)0x0134, (int16_t)0x0133, (int16_t)0x015B, (int16_t)0x0152, (int16_t)0x022F, (int16_t)0x0202, (int16_t)0x0224, (int16_t)0x0224, + (int16_t)0x0235, (int16_t)0x0235, (int16_t)0x0154, (int16_t)0x005D, (int16_t)0x02B8, (int16_t)0x004B, (int16_t)0x01A0, (int16_t)0x0134, (int16_t)0x0133, (int16_t)0x015B, + (int16_t)0x0152, (int16_t)0x022F, (int16_t)0x0202, (int16_t)0x0224, (int16_t)0x0224, (int16_t)0x0235, (int16_t)0x0235, (int16_t)0x0154, (int16_t)0x005D, (int16_t)0x02B8, + (int16_t)0x004B, (int16_t)0x01A0, (int16_t)0x0134, (int16_t)0x0133, (int16_t)0x015B, (int16_t)0x0152, (int16_t)0x022F, (int16_t)0x0202, (int16_t)0x0224, (int16_t)0x0224, + (int16_t)0x0235, (int16_t)0x0235, (int16_t)0x0154, (int16_t)0x005D, (int16_t)0x02B8, (int16_t)0x004B, (int16_t)0x01A0, (int16_t)0x0134, (int16_t)0x0133, (int16_t)0x015B, + (int16_t)0x0152, (int16_t)0x022F, (int16_t)0x0202, (int16_t)0x0224, (int16_t)0x0224 }; /* ====================================================================== * Master calibration literal. * ====================================================================== */ -calibration_t phi_t06215_cal = { +calibration_t phi_t06211_cal = { /* RWA4-relative scalars (base 0x9BD8) */ - .tein_nominal = (int16_t)0x0C28, /* CAL+0x1E @ 0x9BF6 -- tein_nominal — nominal TE_IN base added to tein_valve_fault_guard / tein_overtemp_guard (T06211 FUN_7453 0x747b; T06215 FUN_74EA -- see README) */ + .tein_nominal = (int16_t)0x0C28, /* CAL+0x1E @ 0x9BF6 -- tein_nominal — nominal TE_IN base added to tein_valve_fault_guard / tein_overtemp_guard (FUN_7453 0x747b) */ .cal_48 = (int16_t)0x04B0, /* CAL+0x48 @ 0x9C20 -- RW7A latch value when FUN_62a2 fires (0x62d8) */ - .phi0 = (int16_t)0x0DAB, /* CAL+0x4C @ 0x9C24 -- phi0: base/initial angle. ROM addend baked into phi1 (= *(0x014e)) by FUN_6aaf (0x6ad2); T06211 FUN_7453 reads phi0 + dphi at 0x74a2 */ - .cal_byte_56 = 0x0Du, /* CAL+0x56 @ 0x9C2E -- tooth phase comparand for the ROM's rpm_baseline producer (FUN_51e1 @ 0x51e1; T06211 analog @ 0x5153). The C port treats rpm_baseline as an external input — this byte is published so the host knows which tooth phase to gate its capture on. */ + .phi0 = (int16_t)0x0DAB, /* CAL+0x4C @ 0x9C24 -- phi0: base/initial angle. ROM addend baked into phi1 (= *(0x014e)) by FUN_6aaf (0x6ad2); FUN_7453 reads phi0 + dphi at 0x74a2 */ + .cal_byte_56 = 0x0Du, /* CAL+0x56 @ 0x9C2E -- tooth phase comparand for rpm_baseline producer (T06211 producer @ 0x5153 from dispatcher 0x7969) */ .cal_byte_9c = 0x03u, /* CAL+0x9C @ 0x9C74 -- FUN_62a2 counter increment (0x62c6) */ - .cal_54 = (int16_t)0x0B2B, /* CAL+0x54 @ 0x9C2C -- UPPER clamp on target_inj_angle (T06211 FUN_7453 0x7493) */ + .cal_54 = (int16_t)0x0B2B, /* CAL+0x54 @ 0x9C2C -- UPPER clamp on target_inj_angle (FUN_7453 0x7493) */ .cal_74 = (int16_t)0x20C5, /* CAL+0x74 @ 0x9C4C -- gate_0220 lower RPM threshold */ .cal_76 = (int16_t)0x01A3, /* CAL+0x76 @ 0x9C4E -- gate_0220 hysteresis width (upper = cal_74 + cal_76) */ .cal_78 = (int16_t)0x007F, /* CAL+0x78 @ 0x9C50 -- accel_comp_offset upper clamp (FUN_732d 0x7340) */ .cal_7a = (int16_t)0xFFCF, /* CAL+0x7A @ 0x9C52 -- accel_comp_offset lower clamp (FUN_732d 0x734e) */ - .cal_7e = (int16_t)0x13AA, /* CAL+0x7E @ 0x9C56 -- temperature reference subtrahend in compute_temp_comp_factor (calc_temp_comp_factor @ 0x6B0D; T06211 analog FUN_5DAB 0x5DD1) */ - .cal_temp_comp_switch_dynamic = (int16_t)0x1702, /* CAL+0x80 @ 0x9C58 -- boot value of temp_comp_dynamic = TK_AT_W switch (compute_temp_comp_factor 0x6AF0; T06211 analog 0x5DB4) */ - .cal_82 = (int16_t)0x026F, /* CAL+0x82 @ 0x9C5A -- IIR input gain b for compute_temp_phi_comp (FUN_6b4e MULU at 0x6B66; Q16 unsigned) */ - .cal_84 = (int16_t)0xFD91, /* CAL+0x84 @ 0x9C5C -- IIR pole coefficient a for compute_temp_phi_comp (FUN_6b4e MULU at 0x6B4E and 0x6B57; Q16 unsigned) */ - .cal_temp_comp_switch_complete = (int16_t)0x0001, /* CAL+0x86 @ 0x9C5E -- boot value of temp_comp_complete = F_TK_TE_W switch (compute_temp_comp_factor 0x6B17; T06211 analog 0x5DDB) */ + .cal_7e = (int16_t)0x13C0, /* CAL+0x7E @ 0x9C56 -- temperature reference subtrahend in compute_temp_comp_factor (FUN_5DAB 0x5DD1) */ + .cal_temp_comp_switch_dynamic = (int16_t)0x1702, /* CAL+0x80 @ 0x9C58 -- boot value of *(0x02F6) = TK_AT_W switch (compute_temp_comp_factor 0x5DB4) */ + .cal_82 = (int16_t)0x026F, /* CAL+0x82 @ 0x9C5A -- IIR input gain b for phi_tick_1khz (FUN_6b4e MULU at 0x6B66; Q16 unsigned) */ + .cal_84 = (int16_t)0xFD91, /* CAL+0x84 @ 0x9C5C -- IIR pole coefficient a for phi_tick_1khz (FUN_6b4e MULU at 0x6B4E and 0x6B57; Q16 unsigned) */ + .cal_temp_comp_switch_complete = (int16_t)0x0001, /* CAL+0x86 @ 0x9C5E -- boot value of *(0x02F2) = F_TK_TE_W switch (compute_temp_comp_factor 0x5DDB) */ .cal_92 = (int16_t)0x1750, /* CAL+0x92 @ 0x9C6A -- temperature offset subtracted in FUN_5ca1 (0x5cf7) */ .cal_94 = (int16_t)0x0008, /* CAL+0x94 @ 0x9C6C -- FUN_5ca1 multiplier (0x5d05) */ .cal_96 = (int16_t)0x04B0, /* CAL+0x96 @ 0x9C6E -- FUN_5ca1 RW1C upper clamp (0x5d0b, unsigned) */ .cal_98 = (int16_t)0x1D7E, /* CAL+0x98 @ 0x9C70 -- tein_overtemp_guard lower RPM (FUN_5ca1 0x5d1a, unsigned) */ .cal_9a = (int16_t)0x3127, /* CAL+0x9A @ 0x9C72 -- tein_overtemp_guard upper RPM (FUN_5ca1 0x5d21, unsigned) */ - /* RWC6-relative scalars (base 0x7E56) */ - .cal_rwc6_34 = (int16_t)0x0831, /* RWC6+0x34 @ 0x7E8A -- demand-weight multiplier in compute_angle_kick_2d (orphan @ 0x6ABD; T06211 analog FUN_5D58 @ 0x5D81) */ + /* RWC6-relative scalars (base 0x7D0E) */ + .cal_rwc6_34 = (int16_t)0x0831, /* RWC6+0x34 @ 0x7D42 -- demand-weight multiplier in compute_angle_kick_2d (orphan FUN_5D58 @ 0x5D81) */ - /* Accel descriptors (input_var bound at runtime by phi_t06215_bind_inputs). */ + /* Accel descriptors (input_var bound at runtime by phi_t06211_bind_inputs). */ .desc_accel_rpm = { .runtime_slot = 0, .input_var = NULL, @@ -204,14 +216,14 @@ calibration_t phi_t06215_cal = { .accel_refine_table = phi_accel_refine_table, /* Absolute-address ROM constants. */ - .dat_604c = (int16_t)0x0444, /* *(0x604C) -- FUN_62a2 RWC2 timing threshold (T06211 = 0x0444; verify in T06215) */ - .cal_byte_402 = (int16_t)0xFFF2, /* *(0x0402) -- FUN_6ba3 sign-extends byte 0x0402 (compute_temp_comp_factor 0x6B12; T06211 analog 0x5DD6) */ + .dat_604c = (int16_t)0x0444, /* *(0x604C) -- FUN_62a2 RWC2 timing threshold (= 0x0444) */ + .cal_byte_402 = (int16_t)0xFFF8, /* *(0x0402) -- FUN_5e67 sign-extends byte 0x0402 (compute_temp_comp_factor 0x5DD6) */ /* Angle accumulator descriptors (FUN_722e, RWC6-relative; input_var bound at runtime). */ .desc_rpm = { .runtime_slot = 0, .input_var = NULL, - .stride_items = 13, + .stride_items = 15, .axis = phi_angle_axis_rpm, }, .desc_demand = { @@ -236,7 +248,7 @@ calibration_t phi_t06215_cal = { * Runtime input binder. * ====================================================================== */ -void phi_t06215_bind_inputs(runtime_state_t *rt, calibration_t *cal) +void phi_t06211_bind_inputs(runtime_state_t *rt, calibration_t *cal) { cal->desc_accel_rpm.input_var = (int16_t *)&rt->rpm; cal->desc_accel_demand.input_var = &rt->inj_qty_demand; diff --git a/Core/Src/fuel_map.c b/Core/Src/fuel_map.c index c684043..71c8070 100644 --- a/Core/Src/fuel_map.c +++ b/Core/Src/fuel_map.c @@ -94,7 +94,7 @@ void init_FuelMap(float *PHIAD) { s_phi_getters.get_scratch_0103 = get_scratch_0103; s_phi_getters.get_scratch_0108 = get_scratch_0108; - s_phi_cal = phi_t06215_cal; + s_phi_cal = phi_t06211_cal; phi_init(&s_phi_state, &s_phi_cal, &s_phi_getters); /* Pre-latch to skip the cranking-phase ramp (mirrors