ASELSANMicrokernel
S550 · SOURCE-BOUND GATE EVIDENCE

S550 · R1 ekran: ILI9881 panel DCS init dizisi modeli

tam S550 implementation modülü → Operations --test hedefi ile bağlı tam focused test → ayrı Operations kaydı Bu sayfa yalnız S550 kapısına aittir; komşu kapıların kaynakları bu kabulün içine katılmaz.

S550Focused kod testiOperations id exactsource SHA exacttest target exact

operation: g8l-s550-r1-ili9881-panel-dcs-init-sequence-model

uygulama/model · focused test · Operations · 3 exact excerpt

sequence-bound=true · implementation-bound=true
01 · Yürütme / doğrulama kodu

Kapının gerçek repository sözleşmesi

tam dosyaL1–L936
kernel/src/g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s550_r1_ili9881_panel_dcs_init_sequence_model.rs::S550 r1 ili9881 panel dcs init sequence model implementation
#![allow(unexpected_cfgs)]

//! S550 models an ILI9881C-class panel initialisation program for the
//! Raspberry Pi Touch Display 2 (5" and 7" profiles, both 720x1280 over two
//! MIPI DSI lanes) as a pure host/source model.
//!
//! The model owns three pieces: a table of DCS/generic packets and delays per
//! panel profile, an encoder that turns each packet into raw DSI packet bytes
//! (24-bit header + 6-bit Hamming ECC for every packet, 16-bit reflected
//! CRC-16 with polynomial `0x8408` for long packets), and a fail-closed
//! validator that enforces the ordered phases page select -> register writes
//! -> sleep-out (`0x11`) -> delay >= 120 ms -> display-on (`0x29`), a maximum
//! payload size and a closed set of data types (`0x05`, `0x15`, `0x29`,
//! `0x39`).
//!
//! S550 does NOT claim anything physical: no panel, DSI host, RP1, touch
//! controller, board, UART, SD card or power transition exists for this gate
//! (`S550_PHYSICAL_OBSERVATIONS = 0`, `RUNBOOK_EXECUTED_IN_S550 = false`).
//! The module is not wired into any boot, IRQ, scheduler or driver path; only
//! the focused simulation test drives it.  The S540 and S543 physical verdicts
//! remain immutable RED.  The register subset in the profile tables is a
//! representative model subset, not a vendor-complete init dump.
//!
//! Predecessor: S549 (RP1 DSI host register map / D-PHY timing model).
//! Next gate: S551.

pub const S550_SEQUENCE: usize = 550;
pub const S550_EXPECTED_PREDECESSOR: usize = 549;
pub const S550_R1_STAGE: u8 = 2;
pub const S550_R1_RANGE_FIRST: usize = 536;
pub const S550_R1_RANGE_LAST: usize = 568;
pub const S550_SUPPORTED_PROFILE_RUNTIME_OBSERVATIONS: usize = 0;
pub const S550_PHYSICAL_OBSERVATIONS: usize = 0;
pub const S550_PHYSICAL_OR_DEVICE_OPERATIONS: usize = 0;
pub const S550_SD_WRITES: usize = 0;
pub const S550_UART_OPENS: usize = 0;
pub const S550_POWER_TRANSITIONS: usize = 0;
pub const S550_NEW_IMMUTABLE_RAW_CAPTURES: usize = 0;
pub const S550_S540_PHYSICAL_VERDICT_RETAINED_RED: bool = true;
pub const S550_S543_PHYSICAL_VERDICT_RETAINED_RED: bool = true;
pub const S550_AUTOMATIC_PROMOTION: bool = false;
pub const S550_BOOT_TO_UI_PHYSICALLY_OBSERVED: bool = false;
pub const S550_HARDWARE_PRESENT: bool = false;
pub const S550_R1_ACCEPTANCE_COMPLETE: bool = false;
pub const RUNBOOK_EXECUTED_IN_S550: bool = false;

/// Panel geometry shared by both Touch Display 2 profiles.
pub const S550_PANEL_WIDTH: u16 = 720;
pub const S550_PANEL_HEIGHT: u16 = 1280;
pub const S550_DSI_LANES: u8 = 2;
/// Virtual channel encoded into bits 7:6 of the data identifier byte.
pub const S550_VIRTUAL_CHANNEL: u8 = 0;

/// DSI data types accepted by the model.
pub const S550_DT_DCS_SHORT_WRITE_0: u8 = 0x05;
pub const S550_DT_DCS_SHORT_WRITE_1: u8 = 0x15;
pub const S550_DT_GENERIC_LONG_WRITE: u8 = 0x29;
pub const S550_DT_DCS_LONG_WRITE: u8 = 0x39;

/// DCS commands with ordering significance.
pub const S550_DCS_SLEEP_OUT: u8 = 0x11;
pub const S550_DCS_DISPLAY_ON: u8 = 0x29;
/// ILI9881C page-select prefix: `FF 98 81 <page>`.
pub const S550_PAGE_SELECT_PREFIX: [u8; 3] = [0xFF, 0x98, 0x81];
pub const S550_PAGE_MAX: u8 = 4;

/// Bounded buffers: payload bytes per packet and steps per program.
pub const S550_MAX_PAYLOAD_BYTES: usize = 32;
pub const S550_MAX_ENCODED_BYTES: usize = 4 + S550_MAX_PAYLOAD_BYTES + 2;
pub const S550_MAX_PROGRAM_STEPS: usize = 128;
pub const S550_MIN_SLEEP_OUT_DELAY_MS: u32 = 120;
pub const S550_MAX_SINGLE_DELAY_MS: u32 = 1_000;
pub const S550_MAX_TOTAL_DELAY_MS: u32 = 5_000;

/// CRC-16 used for DSI long packets: `x^16 + x^12 + x^5 + 1`, reflected
/// form `0x8408`, initial value `0xFFFF`, no final XOR, LSB-first.
pub const S550_CRC16_POLY_REFLECTED: u16 = 0x8408;
pub const S550_CRC16_INIT: u16 = 0xFFFF;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS550PanelProfile {
    TouchDisplay2Inch5,
    TouchDisplay2Inch7,
}

impl G8lS550PanelProfile {
    pub const fn id(self) -> u8 {
        match self {
            Self::TouchDisplay2Inch5 => 5,
            Self::TouchDisplay2Inch7 => 7,
        }
    }
}

/// Modelled video timing per profile.  Host table values; not vendor
/// validated and not observed on any panel.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS550PanelTiming {
    pub width: u16,
    pub height: u16,
    pub lanes: u8,
    pub hfront_porch: u16,
    pub hsync: u16,
    pub hback_porch: u16,
    pub vfront_porch: u16,
    pub vsync: u16,
    pub vback_porch: u16,
    pub pixel_clock_khz: u32,
    pub sleep_out_delay_ms: u32,
    pub display_on_delay_ms: u32,
}

impl G8lS550PanelTiming {
    pub const fn htotal(self) -> u32 {
        self.width as u32 + self.hfront_porch as u32 + self.hsync as u32 + self.hback_porch as u32
    }

    pub const fn vtotal(self) -> u32 {
        self.height as u32 + self.vfront_porch as u32 + self.vsync as u32 + self.vback_porch as u32
    }

    /// Frame rate in millihertz, checked; `None` when the timing is empty
    /// or the result does not fit `u32`.
    pub fn refresh_mhz(self) -> Option<u32> {
        let total = (self.htotal() as u64).checked_mul(self.vtotal() as u64)?;
        if total == 0 {
            return None;
        }
        let millihertz = (self.pixel_clock_khz as u64).checked_mul(1_000_000)? / total;
        u32::try_from(millihertz).ok()
    }
}

pub const S550_TIMING_INCH5: G8lS550PanelTiming = G8lS550PanelTiming {
    width: S550_PANEL_WIDTH,
    height: S550_PANEL_HEIGHT,
    lanes: S550_DSI_LANES,
    hfront_porch: 80,
    hsync: 20,
    hback_porch: 80,
    vfront_porch: 12,
    vsync: 4,
    vback_porch: 20,
    pixel_clock_khz: 71_000,
    sleep_out_delay_ms: 120,
    display_on_delay_ms: 20,
};

pub const S550_TIMING_INCH7: G8lS550PanelTiming = G8lS550PanelTiming {
    width: S550_PANEL_WIDTH,
    height: S550_PANEL_HEIGHT,
    lanes: S550_DSI_LANES,
    hfront_porch: 239,
    hsync: 33,
    hback_porch: 50,
    vfront_porch: 20,
    vsync: 2,
    vback_porch: 30,
    pixel_clock_khz: 83_330,
    sleep_out_delay_ms: 120,
    display_on_delay_ms: 20,
};

pub const fn s550_timing_for(profile: G8lS550PanelProfile) -> G8lS550PanelTiming {
    match profile {
        G8lS550PanelProfile::TouchDisplay2Inch5 => S550_TIMING_INCH5,
        G8lS550PanelProfile::TouchDisplay2Inch7 => S550_TIMING_INCH7,
    }
}

/// One init program step: a DSI write packet or a host-side delay.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS550Step<'a> {
    Write { data_type: u8, payload: &'a [u8] },
    DelayMs(u32),
}

const fn page(p: u8) -> G8lS550Step<'static> {
    // Page select is a 4-byte generic long write in the ILI9881C convention.
    match p {
        0 => G8lS550Step::Write {
            data_type: 0x29,
            payload: &[0xFF, 0x98, 0x81, 0x00],
        },
        1 => G8lS550Step::Write {
            data_type: 0x29,
            payload: &[0xFF, 0x98, 0x81, 0x01],
        },
        2 => G8lS550Step::Write {
            data_type: 0x29,
            payload: &[0xFF, 0x98, 0x81, 0x02],
        },
        3 => G8lS550Step::Write {
            data_type: 0x29,
            payload: &[0xFF, 0x98, 0x81, 0x03],
        },
        _ => G8lS550Step::Write {
            data_type: 0x29,
            payload: &[0xFF, 0x98, 0x81, 0x04],
        },
    }
}

/// Representative 7" Touch Display 2 init program (model subset).
pub const S550_INIT_PROGRAM_INCH7: &[G8lS550Step<'static>] = &[
    page(3),
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x01, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x02, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x03, 0x73],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x04, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x05, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x06, 0x0A],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x07, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x08, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x09, 0x01],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x0A, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x0B, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x0C, 0x01],
    },
    page(4),
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x6C, 0x15],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x6E, 0x2A],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x6F, 0x33],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x3A, 0x94],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x8D, 0x14],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x87, 0xBA],
    },
    page(1),
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x22, 0x0A],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x31, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x53, 0x8A],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x55, 0x8A],
    },
    G8lS550Step::Write {
        data_type: 0x39,
        payload: &[0xA0, 0x00, 0x1D, 0x2A, 0x13],
    },
    page(0),
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x36, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x3A, 0x77],
    },
    G8lS550Step::Write {
        data_type: 0x05,
        payload: &[0x11],
    },
    G8lS550Step::DelayMs(120),
    G8lS550Step::Write {
        data_type: 0x05,
        payload: &[0x29],
    },
    G8lS550Step::DelayMs(20),
];

/// Representative 5" Touch Display 2 init program (model subset).
pub const S550_INIT_PROGRAM_INCH5: &[G8lS550Step<'static>] = &[
    page(3),
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x01, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x02, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x03, 0x53],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x04, 0x53],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x05, 0x13],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x06, 0x04],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x07, 0x02],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x08, 0x02],
    },
    page(4),
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x6C, 0x15],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x6E, 0x30],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x6F, 0x37],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x3A, 0xA4],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x8D, 0x1A],
    },
    page(1),
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x22, 0x0A],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x53, 0x72],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x55, 0x77],
    },
    G8lS550Step::Write {
        data_type: 0x39,
        payload: &[0xA0, 0x00, 0x10, 0x1C, 0x0F],
    },
    page(0),
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x36, 0x00],
    },
    G8lS550Step::Write {
        data_type: 0x15,
        payload: &[0x3A, 0x77],
    },
    G8lS550Step::Write {
        data_type: 0x05,
        payload: &[0x11],
    },
    G8lS550Step::DelayMs(120),
    G8lS550Step::Write {
        data_type: 0x05,
        payload: &[0x29],
    },
    G8lS550Step::DelayMs(20),
];

pub const fn s550_canonical_program(
    profile: G8lS550PanelProfile,
) -> &'static [G8lS550Step<'static>] {
    match profile {
        G8lS550PanelProfile::TouchDisplay2Inch5 => S550_INIT_PROGRAM_INCH5,
        G8lS550PanelProfile::TouchDisplay2Inch7 => S550_INIT_PROGRAM_INCH7,
    }
}

/// 6-bit MIPI DSI/CSI-2 header ECC over the 24-bit header
/// (`D0..D7` = data identifier, `D8..D15` = second byte, `D16..D23` = third).
pub const fn s550_header_ecc(data_id: u8, byte1: u8, byte2: u8) -> u8 {
    const ROWS: [u32; 6] = [
        // P0..P5 bit masks over D0..D23 (MIPI DSI Hamming SEC-DED code).
        (1 << 0)
            | (1 << 1)
            | (1 << 2)
            | (1 << 4)
            | (1 << 5)
            | (1 << 7)
            | (1 << 10)
            | (1 << 11)
            | (1 << 13)
            | (1 << 16)
            | (1 << 20)
            | (1 << 21)
            | (1 << 22)
            | (1 << 23),
        (1 << 0)
            | (1 << 1)
            | (1 << 3)
            | (1 << 4)
            | (1 << 6)
            | (1 << 8)
            | (1 << 10)
            | (1 << 12)
            | (1 << 14)
            | (1 << 17)
            | (1 << 20)
            | (1 << 21)
            | (1 << 22)
            | (1 << 23),
        (1 << 0)
            | (1 << 2)
            | (1 << 3)
            | (1 << 5)
            | (1 << 6)
            | (1 << 9)
            | (1 << 11)
            | (1 << 12)
            | (1 << 15)
            | (1 << 18)
            | (1 << 20)
            | (1 << 21)
            | (1 << 22),
        (1 << 1)
            | (1 << 2)
            | (1 << 3)
            | (1 << 7)
            | (1 << 8)
            | (1 << 9)
            | (1 << 13)
            | (1 << 14)
            | (1 << 15)
            | (1 << 19)
            | (1 << 20)
            | (1 << 21)
            | (1 << 23),
        (1 << 4)
            | (1 << 5)
            | (1 << 6)
            | (1 << 7)
            | (1 << 8)
            | (1 << 9)
            | (1 << 16)
            | (1 << 17)
            | (1 << 18)
            | (1 << 19)
            | (1 << 20)
            | (1 << 22)
            | (1 << 23),
        (1 << 10)
            | (1 << 11)
            | (1 << 12)
            | (1 << 13)
            | (1 << 14)
            | (1 << 15)
            | (1 << 16)
            | (1 << 17)
            | (1 << 18)
            | (1 << 19)
            | (1 << 21)
            | (1 << 22)
            | (1 << 23),
    ];
    let header = data_id as u32 | ((byte1 as u32) << 8) | ((byte2 as u32) << 16);
    let mut ecc = 0u8;
    let mut i = 0;
    while i < 6 {
        let parity = (header & ROWS[i]).count_ones() & 1;
        ecc |= (parity as u8) << i;
        i += 1;
    }
    ecc
}

/// Reflected CRC-16 (`0x8408`, init `0xFFFF`) over a long-packet payload.
pub fn s550_payload_crc16(payload: &[u8]) -> u16 {
    let mut crc = S550_CRC16_INIT;
    for &byte in payload {
        crc ^= byte as u16;
        for _ in 0..8 {
            crc = if crc & 1 != 0 {
                (crc >> 1) ^ S550_CRC16_POLY_REFLECTED
            } else {
                crc >> 1
            };
        }
    }
    crc
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS550EncodedPacket {
    pub bytes: [u8; S550_MAX_ENCODED_BYTES],
    pub len: usize,
    pub ecc: u8,
    pub crc: Option<u16>,
}

impl G8lS550EncodedPacket {
    pub fn as_bytes(&self) -> &[u8] {
        &self.bytes[..self.len]
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS550PanelInitReceipt {
    pub sequence: usize,
    pub predecessor_sequence: usize,
    pub r1_stage: u8,
    pub profile: G8lS550PanelProfile,
    pub width: u16,
    pub height: u16,
    pub lanes: u8,
    pub step_count: usize,
    pub packet_count: usize,
    pub short_packet_count: usize,
    pub long_packet_count: usize,
    pub page_select_count: usize,
    pub register_write_count: usize,
    pub total_delay_ms: u32,
    pub sleep_out_delay_ms: u32,
    pub encoded_bytes: usize,
    pub sleep_out_ecc: u8,
    pub display_on_ecc: u8,
    pub program_digest: u64,
    pub hardware_present: bool,
    pub s540_physical_verdict_retained_red: bool,
    pub s543_physical_verdict_retained_red: bool,
    pub automatic_promotion: bool,
    pub supported_profile_runtime_observations: usize,
    pub physical_observations: usize,
    pub runbook_executed: bool,
}

#[derive(Debug)]
pub struct G8lS550PanelInitState {
    receipt: Option<G8lS550PanelInitReceipt>,
}

impl G8lS550PanelInitState {
    pub const fn new() -> Self {
        Self { receipt: None }
    }

    pub const fn receipt(&self) -> Option<G8lS550PanelInitReceipt> {
        self.receipt
    }
}

impl Default for G8lS550PanelInitState {
    fn default() -> Self {
        Self::new()
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS550PanelInitOutcome {
    Published(G8lS550PanelInitReceipt),
    Retained(G8lS550PanelInitReceipt),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS550PanelInitError {
    WrongPredecessor,
    EmptyProgram,
    ProgramTooLong,
    UnknownDataType,
    PayloadTooLong,
    ShortPacketLengthMismatch,
    EmptyLongPayload,
    MalformedPageSelect,
    PageOutOfRange,
    FirstStepNotPageSelect,
    SleepOutOutsidePageZero,
    DisplayOnBeforeSleepOut,
    InsufficientSleepOutDelay,
    DuplicateSleepOut,
    PacketAfterDisplayOn,
    DelayTooLong,
    DelayOverflow,
    IncompleteProgram,
    RegisterWriteAfterSleepOut,
    PublishedStateDrift,
}

impl G8lS550PanelInitError {
    pub const fn diagnostic_code(self) -> u64 {
        match self {
            Self::WrongPredecessor => 1,
            Self::EmptyProgram => 2,
            Self::ProgramTooLong => 3,
            Self::UnknownDataType => 4,
            Self::PayloadTooLong => 5,
            Self::ShortPacketLengthMismatch => 6,
            Self::EmptyLongPayload => 7,
            Self::MalformedPageSelect => 8,
            Self::PageOutOfRange => 9,
            Self::FirstStepNotPageSelect => 10,
            Self::SleepOutOutsidePageZero => 11,
            Self::DisplayOnBeforeSleepOut => 12,
            Self::InsufficientSleepOutDelay => 13,
            Self::DuplicateSleepOut => 14,
            Self::PacketAfterDisplayOn => 15,
            Self::DelayTooLong => 16,
            Self::DelayOverflow => 17,
            Self::IncompleteProgram => 18,
            Self::RegisterWriteAfterSleepOut => 19,
            Self::PublishedStateDrift => 20,
        }
    }
}

pub const fn s550_data_type_is_long(data_type: u8) -> Option<bool> {
    match data_type {
        S550_DT_DCS_SHORT_WRITE_0 | S550_DT_DCS_SHORT_WRITE_1 => Some(false),
        S550_DT_GENERIC_LONG_WRITE | S550_DT_DCS_LONG_WRITE => Some(true),
        _ => None,
    }
}

/// Encodes one write packet into raw DSI bytes.  Short packets are
/// `[DI, D0, D1, ECC]`; long packets are
/// `[DI, WC.lo, WC.hi, ECC, payload..., CRC.lo, CRC.hi]`.
pub fn s550_encode_packet(
    data_type: u8,
    payload: &[u8],
) -> Result<G8lS550EncodedPacket, G8lS550PanelInitError> {
    let long = s550_data_type_is_long(data_type).ok_or(G8lS550PanelInitError::UnknownDataType)?;
    if payload.len() > S550_MAX_PAYLOAD_BYTES {
        return Err(G8lS550PanelInitError::PayloadTooLong);
    }
    let data_id = (S550_VIRTUAL_CHANNEL << 6) | (data_type & 0x3F);
    let mut packet = G8lS550EncodedPacket {
        bytes: [0; S550_MAX_ENCODED_BYTES],
        len: 0,
        ecc: 0,
        crc: None,
    };
    if !long {
        let expected = if data_type == S550_DT_DCS_SHORT_WRITE_0 {
            1
        } else {
            2
        };
        if payload.len() != expected {
            return Err(G8lS550PanelInitError::ShortPacketLengthMismatch);
        }
        let d1 = if payload.len() == 2 { payload[1] } else { 0 };
        let ecc = s550_header_ecc(data_id, payload[0], d1);
        packet.bytes[0] = data_id;
        packet.bytes[1] = payload[0];
        packet.bytes[2] = d1;
        packet.bytes[3] = ecc;
        packet.len = 4;
        packet.ecc = ecc;
        return Ok(packet);
    }
    if payload.is_empty() {
        return Err(G8lS550PanelInitError::EmptyLongPayload);
    }
    let word_count = payload.len() as u16;
    let ecc = s550_header_ecc(data_id, word_count as u8, (word_count >> 8) as u8);
    let crc = s550_payload_crc16(payload);
    packet.bytes[0] = data_id;
    packet.bytes[1] = word_count as u8;
    packet.bytes[2] = (word_count >> 8) as u8;
    packet.bytes[3] = ecc;
    packet.bytes[4..4 + payload.len()].copy_from_slice(payload);
    packet.bytes[4 + payload.len()] = crc as u8;
    packet.bytes[5 + payload.len()] = (crc >> 8) as u8;
    packet.len = 6 + payload.len();
    packet.ecc = ecc;
    packet.crc = Some(crc);
    Ok(packet)
}

/// Returns `Some(page)` when the packet is an ILI9881C page select.
pub fn s550_page_select(
    data_type: u8,
    payload: &[u8],
) -> Result<Option<u8>, G8lS550PanelInitError> {
    if payload.first() != Some(&S550_PAGE_SELECT_PREFIX[0]) {
        return Ok(None);
    }
    if data_type != S550_DT_GENERIC_LONG_WRITE
        || payload.len() != 4
        || payload[1] != S550_PAGE_SELECT_PREFIX[1]
        || payload[2] != S550_PAGE_SELECT_PREFIX[2]
    {
        return Err(G8lS550PanelInitError::MalformedPageSelect);
    }
    if payload[3] > S550_PAGE_MAX {
        return Err(G8lS550PanelInitError::PageOutOfRange);
    }
    Ok(Some(payload[3]))
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Phase {
    AwaitingPageSelect,
    RegisterWrites,
    SleepOutDelay,
    Complete,
}

const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;

fn digest_bytes(mut digest: u64, bytes: &[u8]) -> u64 {
    for &byte in bytes {
        digest ^= byte as u64;
        digest = digest.wrapping_mul(FNV_PRIME);
    }
    digest
}

/// Validates the ordered phases of an init program and encodes every packet.
/// Every violation fails closed with a distinct error.
pub fn s550_validate_program(
    profile: G8lS550PanelProfile,
    program: &[G8lS550Step<'_>],
) -> Result<G8lS550PanelInitReceipt, G8lS550PanelInitError> {
    if program.is_empty() {
        return Err(G8lS550PanelInitError::EmptyProgram);
    }
    if program.len() > S550_MAX_PROGRAM_STEPS {
        return Err(G8lS550PanelInitError::ProgramTooLong);
    }
    let timing = s550_timing_for(profile);
    let mut phase = Phase::AwaitingPageSelect;
    let mut current_page: Option<u8> = None;
    let mut packet_count = 0usize;
    let mut short_packet_count = 0usize;
    let mut long_packet_count = 0usize;
    let mut page_select_count = 0usize;
    let mut register_write_count = 0usize;
    let mut total_delay_ms = 0u32;
    let mut sleep_out_delay_ms = 0u32;
    let mut encoded_bytes = 0usize;
    let mut sleep_out_ecc = 0u8;
    let mut display_on_ecc = 0u8;
    let mut digest = digest_bytes(FNV_OFFSET, &[profile.id(), timing.lanes]);

    for step in program {
        match *step {
            G8lS550Step::DelayMs(ms) => {
                if ms > S550_MAX_SINGLE_DELAY_MS {
                    return Err(G8lS550PanelInitError::DelayTooLong);
                }
                total_delay_ms = total_delay_ms
                    .checked_add(ms)
                    .filter(|&total| total <= S550_MAX_TOTAL_DELAY_MS)
                    .ok_or(G8lS550PanelInitError::DelayOverflow)?;
                if phase == Phase::SleepOutDelay {
                    sleep_out_delay_ms = sleep_out_delay_ms
                        .checked_add(ms)
                        .ok_or(G8lS550PanelInitError::DelayOverflow)?;
                }
                digest = digest_bytes(
                    digest,
                    &[
                        0xD0,
                        ms as u8,
                        (ms >> 8) as u8,
                        (ms >> 16) as u8,
                        (ms >> 24) as u8,
                    ],
                );
            }
            G8lS550Step::Write { data_type, payload } => {
                if phase == Phase::Complete {
                    return Err(G8lS550PanelInitError::PacketAfterDisplayOn);
                }
                let encoded = s550_encode_packet(data_type, payload)?;
                let page = s550_page_select(data_type, payload)?;
                let long = s550_data_type_is_long(data_type) == Some(true);
                let dcs = if data_type == S550_DT_DCS_SHORT_WRITE_0 {
                    Some(payload[0])
                } else {
                    None
                };
                match phase {
                    Phase::AwaitingPageSelect => {
                        if page.is_none() {
                            return Err(G8lS550PanelInitError::FirstStepNotPageSelect);
                        }
                        phase = Phase::RegisterWrites;
                    }
                    Phase::RegisterWrites => {}
                    Phase::SleepOutDelay => {
                        if dcs == Some(S550_DCS_SLEEP_OUT) {
                            return Err(G8lS550PanelInitError::DuplicateSleepOut);
                        }
                        if dcs != Some(S550_DCS_DISPLAY_ON) {
                            return Err(G8lS550PanelInitError::RegisterWriteAfterSleepOut);
                        }
                    }
                    Phase::Complete => unreachable!(),
                }
                if let Some(p) = page {
                    current_page = Some(p);
                    page_select_count += 1;
                } else if dcs == Some(S550_DCS_SLEEP_OUT) {
                    if current_page != Some(0) {
                        return Err(G8lS550PanelInitError::SleepOutOutsidePageZero);
                    }
                    phase = Phase::SleepOutDelay;
                    sleep_out_ecc = encoded.ecc;
                } else if dcs == Some(S550_DCS_DISPLAY_ON) {
                    if phase != Phase::SleepOutDelay {
                        return Err(G8lS550PanelInitError::DisplayOnBeforeSleepOut);
                    }
                    if sleep_out_delay_ms < S550_MIN_SLEEP_OUT_DELAY_MS
                        || sleep_out_delay_ms < timing.sleep_out_delay_ms
                    {
                        return Err(G8lS550PanelInitError::InsufficientSleepOutDelay);
                    }
                    phase = Phase::Complete;
                    display_on_ecc = encoded.ecc;
                } else {
                    register_write_count += 1;
                }
                packet_count += 1;
                if long {
                    long_packet_count += 1;
                } else {
                    short_packet_count += 1;
                }
                encoded_bytes += encoded.len;
                digest = digest_bytes(digest, encoded.as_bytes());
            }
        }
    }
    if phase != Phase::Complete {
        return Err(G8lS550PanelInitError::IncompleteProgram);
    }
    Ok(G8lS550PanelInitReceipt {
        sequence: S550_SEQUENCE,
        predecessor_sequence: S550_EXPECTED_PREDECESSOR,
        r1_stage: S550_R1_STAGE,
        profile,
        width: timing.width,
        height: timing.height,
        lanes: timing.lanes,
        step_count: program.len(),
        packet_count,
        short_packet_count,
        long_packet_count,
        page_select_count,
        register_write_count,
        total_delay_ms,
        sleep_out_delay_ms,
        encoded_bytes,
        sleep_out_ecc,
        display_on_ecc,
        program_digest: digest,
        hardware_present: S550_HARDWARE_PRESENT,
        s540_physical_verdict_retained_red: S550_S540_PHYSICAL_VERDICT_RETAINED_RED,
        s543_physical_verdict_retained_red: S550_S543_PHYSICAL_VERDICT_RETAINED_RED,
        automatic_promotion: S550_AUTOMATIC_PROMOTION,
        supported_profile_runtime_observations: S550_SUPPORTED_PROFILE_RUNTIME_OBSERVATIONS,
        physical_observations: S550_PHYSICAL_OBSERVATIONS,
        runbook_executed: RUNBOOK_EXECUTED_IN_S550,
    })
}

/// Fail-closed publication of a validated init program.  An exact replay
/// returns `Retained` with the same receipt; any divergence after publication
/// fails with `PublishedStateDrift`.
pub fn service_s550_model_panel_init_program(
    state: &mut G8lS550PanelInitState,
    predecessor_sequence: usize,
    profile: G8lS550PanelProfile,
    program: &[G8lS550Step<'_>],
) -> Result<G8lS550PanelInitOutcome, G8lS550PanelInitError> {
    if predecessor_sequence != S550_EXPECTED_PREDECESSOR {
        return Err(G8lS550PanelInitError::WrongPredecessor);
    }
    let receipt = s550_validate_program(profile, program)?;
    if let Some(published) = state.receipt {
        if published != receipt {
            return Err(G8lS550PanelInitError::PublishedStateDrift);
        }
        return Ok(G8lS550PanelInitOutcome::Retained(published));
    }
    state.receipt = Some(receipt);
    Ok(G8lS550PanelInitOutcome::Published(receipt))
}
snippet sha256: cdc545f16057file sha256: cdc545f16057
02 · Doğrulayan test kodu

Operations komutuna bağlı focused test

tam dosyaL1–L645
simulation/tests/g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s550_r1_ili9881_panel_dcs_init_sequence_model.rs::S550 r1 ili9881 panel dcs init sequence model focused tests
use aselsan_microkernel_simulation::g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s550_r1_ili9881_panel_dcs_init_sequence_model::*;
use std::collections::BTreeSet;

const SOURCE: &str = include_str!(
    "../../kernel/src/g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s550_r1_ili9881_panel_dcs_init_sequence_model.rs"
);
const MAIN: &str = include_str!("../../kernel/src/main.rs");
const SIMULATION_LIB: &str = include_str!("../src/lib.rs");

const PAGE0: G8lS550Step<'static> = G8lS550Step::Write {
    data_type: 0x29,
    payload: &[0xFF, 0x98, 0x81, 0x00],
};
const PAGE3: G8lS550Step<'static> = G8lS550Step::Write {
    data_type: 0x29,
    payload: &[0xFF, 0x98, 0x81, 0x03],
};
const REG: G8lS550Step<'static> = G8lS550Step::Write {
    data_type: 0x15,
    payload: &[0x36, 0x00],
};
const SLEEP_OUT: G8lS550Step<'static> = G8lS550Step::Write {
    data_type: 0x05,
    payload: &[0x11],
};
const DISPLAY_ON: G8lS550Step<'static> = G8lS550Step::Write {
    data_type: 0x05,
    payload: &[0x29],
};

fn minimal_program() -> Vec<G8lS550Step<'static>> {
    vec![
        PAGE3,
        REG,
        PAGE0,
        SLEEP_OUT,
        G8lS550Step::DelayMs(120),
        DISPLAY_ON,
    ]
}

fn publish(
    state: &mut G8lS550PanelInitState,
    profile: G8lS550PanelProfile,
    program: &[G8lS550Step<'_>],
) -> Result<G8lS550PanelInitOutcome, G8lS550PanelInitError> {
    service_s550_model_panel_init_program(state, S550_EXPECTED_PREDECESSOR, profile, program)
}

fn validate(program: &[G8lS550Step<'_>]) -> Result<G8lS550PanelInitReceipt, G8lS550PanelInitError> {
    s550_validate_program(G8lS550PanelProfile::TouchDisplay2Inch7, program)
}

#[test]
fn sequence_scope_and_nonpromotion_are_exact() {
    assert_eq!(S550_SEQUENCE, 550);
    assert_eq!(S550_EXPECTED_PREDECESSOR, 549);
    assert_eq!(S550_R1_STAGE, 2);
    assert_eq!(S550_R1_RANGE_FIRST, 536);
    assert_eq!(S550_R1_RANGE_LAST, 568);
    assert_eq!(S550_SUPPORTED_PROFILE_RUNTIME_OBSERVATIONS, 0);
    assert_eq!(S550_PHYSICAL_OBSERVATIONS, 0);
    assert_eq!(S550_PHYSICAL_OR_DEVICE_OPERATIONS, 0);
    assert_eq!(S550_SD_WRITES, 0);
    assert_eq!(S550_UART_OPENS, 0);
    assert_eq!(S550_POWER_TRANSITIONS, 0);
    assert_eq!(S550_NEW_IMMUTABLE_RAW_CAPTURES, 0);
    assert!(S550_S540_PHYSICAL_VERDICT_RETAINED_RED);
    assert!(S550_S543_PHYSICAL_VERDICT_RETAINED_RED);
    assert!(!S550_AUTOMATIC_PROMOTION);
    assert!(!S550_BOOT_TO_UI_PHYSICALLY_OBSERVED);
    assert!(!S550_HARDWARE_PRESENT);
    assert!(!S550_R1_ACCEPTANCE_COMPLETE);
    assert!(!RUNBOOK_EXECUTED_IN_S550);
}

#[test]
fn module_is_registered_in_kernel_and_simulation() {
    let module = "g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s550_r1_ili9881_panel_dcs_init_sequence_model";
    // Line-anchored match (equivalent to the regexes `^mod <module>;$` and
    // `^pub mod <module>;$`).
    assert!(MAIN.lines().any(|line| line == format!("mod {module};")));
    assert!(SIMULATION_LIB
        .lines()
        .any(|line| line == format!("pub mod {module};")));
    assert!(SIMULATION_LIB.contains(&format!("#[path = \"../../kernel/src/{module}.rs\"]")));
}

#[test]
fn source_has_no_device_execution_or_uart_emission_surface() {
    for forbidden in [
        "unsafe",
        "asm!",
        "write_volatile",
        "crate::uart",
        "crate::arch",
        "#[no_mangle]",
        "spin::",
        "std::",
        "/dev/",
        "kprintln!",
    ] {
        assert!(!SOURCE.contains(forbidden), "forbidden token: {forbidden}");
    }
    assert!(SOURCE.contains("does NOT claim anything physical"));
    assert!(SOURCE.contains("not wired into any boot, IRQ, scheduler or driver path"));
}

#[test]
fn diagnostic_codes_are_nonzero_and_unique() {
    let errors = [
        G8lS550PanelInitError::WrongPredecessor,
        G8lS550PanelInitError::EmptyProgram,
        G8lS550PanelInitError::ProgramTooLong,
        G8lS550PanelInitError::UnknownDataType,
        G8lS550PanelInitError::PayloadTooLong,
        G8lS550PanelInitError::ShortPacketLengthMismatch,
        G8lS550PanelInitError::EmptyLongPayload,
        G8lS550PanelInitError::MalformedPageSelect,
        G8lS550PanelInitError::PageOutOfRange,
        G8lS550PanelInitError::FirstStepNotPageSelect,
        G8lS550PanelInitError::SleepOutOutsidePageZero,
        G8lS550PanelInitError::DisplayOnBeforeSleepOut,
        G8lS550PanelInitError::InsufficientSleepOutDelay,
        G8lS550PanelInitError::DuplicateSleepOut,
        G8lS550PanelInitError::PacketAfterDisplayOn,
        G8lS550PanelInitError::DelayTooLong,
        G8lS550PanelInitError::DelayOverflow,
        G8lS550PanelInitError::IncompleteProgram,
        G8lS550PanelInitError::RegisterWriteAfterSleepOut,
        G8lS550PanelInitError::PublishedStateDrift,
    ];
    let codes: BTreeSet<_> = errors
        .into_iter()
        .map(G8lS550PanelInitError::diagnostic_code)
        .collect();
    assert_eq!(codes.len(), errors.len());
    assert_eq!(codes.len(), 20);
    assert!(!codes.contains(&0));
}

#[test]
fn exact_replay_retains_the_same_receipt() {
    let mut state = G8lS550PanelInitState::new();
    let profile = G8lS550PanelProfile::TouchDisplay2Inch7;
    let program = s550_canonical_program(profile);
    let G8lS550PanelInitOutcome::Published(receipt) =
        publish(&mut state, profile, program).unwrap()
    else {
        panic!("first S550 publication missing")
    };
    assert_eq!(state.receipt(), Some(receipt));
    assert_eq!(
        publish(&mut state, profile, program),
        Ok(G8lS550PanelInitOutcome::Retained(receipt))
    );
    assert_eq!(
        publish(&mut state, profile, program),
        Ok(G8lS550PanelInitOutcome::Retained(receipt))
    );
}

#[test]
fn divergent_input_after_publication_fails_closed() {
    let mut state = G8lS550PanelInitState::new();
    let program = s550_canonical_program(G8lS550PanelProfile::TouchDisplay2Inch7);
    let G8lS550PanelInitOutcome::Published(receipt) =
        publish(&mut state, G8lS550PanelProfile::TouchDisplay2Inch7, program).unwrap()
    else {
        panic!("first S550 publication missing")
    };
    // Other profile with a valid program: drift.
    assert_eq!(
        publish(
            &mut state,
            G8lS550PanelProfile::TouchDisplay2Inch5,
            s550_canonical_program(G8lS550PanelProfile::TouchDisplay2Inch5),
        ),
        Err(G8lS550PanelInitError::PublishedStateDrift)
    );
    // Same profile, one register value changed: drift.
    let mut mutated = program.to_vec();
    mutated[1] = G8lS550Step::Write { data_type: 0x15, payload: &[0x01, 0x01] };
    assert_eq!(
        publish(&mut state, G8lS550PanelProfile::TouchDisplay2Inch7, &mutated),
        Err(G8lS550PanelInitError::PublishedStateDrift)
    );
    // Same profile, longer post sleep-out delay: drift.
    let mut longer = program.to_vec();
    longer[program.len() - 3] = G8lS550Step::DelayMs(150);
    assert_eq!(
        publish(&mut state, G8lS550PanelProfile::TouchDisplay2Inch7, &longer),
        Err(G8lS550PanelInitError::PublishedStateDrift)
    );
    // Invalid programs still fail with their own error, not drift.
    assert_eq!(
        publish(&mut state, G8lS550PanelProfile::TouchDisplay2Inch7, &[]),
        Err(G8lS550PanelInitError::EmptyProgram)
    );
    assert_eq!(state.receipt(), Some(receipt));
}

#[test]
fn wrong_predecessor_is_rejected_before_validation() {
    let mut state = G8lS550PanelInitState::new();
    let program = s550_canonical_program(G8lS550PanelProfile::TouchDisplay2Inch7);
    for predecessor in [0, 548, 550, 551] {
        assert_eq!(
            service_s550_model_panel_init_program(
                &mut state,
                predecessor,
                G8lS550PanelProfile::TouchDisplay2Inch7,
                program,
            ),
            Err(G8lS550PanelInitError::WrongPredecessor)
        );
    }
    assert_eq!(
        service_s550_model_panel_init_program(
            &mut state,
            548,
            G8lS550PanelProfile::TouchDisplay2Inch7,
            &[],
        ),
        Err(G8lS550PanelInitError::WrongPredecessor)
    );
    assert_eq!(state.receipt(), None);
}

#[test]
fn header_ecc_matches_pinned_vectors() {
    // (DI, D0, D1) -> ECC for the MIPI DSI 24-bit header Hamming code.
    for ((di, d0, d1), ecc) in [
        ((0x00, 0x00, 0x00), 0x00),
        ((0x05, 0x11, 0x00), 0x36), // DCS sleep-out short packet
        ((0x05, 0x29, 0x00), 0x1C), // DCS display-on short packet
        ((0x05, 0x10, 0x00), 0x2C), // DCS sleep-in
        ((0x15, 0x36, 0x00), 0x29), // MADCTL = 0
        ((0x15, 0x53, 0x24), 0x08),
        ((0x29, 0x04, 0x00), 0x3F), // generic long, WC = 4 (page select)
        ((0x29, 0x03, 0x00), 0x1A),
        ((0x39, 0x05, 0x00), 0x36), // DCS long, WC = 5
        ((0xFF, 0xFF, 0xFF), 0x3C),
    ] {
        assert_eq!(s550_header_ecc(di, d0, d1), ecc, "ecc({di:#04x},{d0:#04x},{d1:#04x})");
        assert!(s550_header_ecc(di, d0, d1) < 0x40);
    }
}

#[test]
fn header_ecc_is_a_single_error_correcting_hamming_code() {
    // Linear code: the syndrome of a single flipped header bit is the XOR of
    // the two ECC values.  All 24 syndromes must be distinct, nonzero and of
    // odd weight >= 3 so that single-bit errors are correctable and
    // distinguishable from a flipped parity bit.
    let base = (0x39u8, 0x21u8, 0x84u8);
    let ecc0 = s550_header_ecc(base.0, base.1, base.2);
    let mut syndromes = BTreeSet::new();
    for bit in 0..24u32 {
        let flipped = ((base.0 as u32) | ((base.1 as u32) << 8) | ((base.2 as u32) << 16)) ^ (1 << bit);
        let ecc = s550_header_ecc(flipped as u8, (flipped >> 8) as u8, (flipped >> 16) as u8);
        let syndrome = ecc ^ ecc0;
        assert!(syndrome != 0);
        assert!(syndrome.count_ones() >= 3 && syndrome.count_ones() % 2 == 1);
        syndromes.insert(syndrome);
    }
    assert_eq!(syndromes.len(), 24);
}

#[test]
fn payload_crc16_matches_pinned_vectors() {
    assert_eq!(S550_CRC16_POLY_REFLECTED, 0x8408);
    assert_eq!(S550_CRC16_INIT, 0xFFFF);
    assert_eq!(s550_payload_crc16(&[]), 0xFFFF);
    // MIPI CSI-2/DSI specification checksum example (24-byte payload).
    let spec_example: [u8; 24] = [
        0xFF, 0x00, 0x00, 0x00, 0x1E, 0xF0, 0x1E, 0xC7, 0x4F, 0x82, 0x78, 0xC5, 0x82, 0xE0,
        0x8C, 0x70, 0xD2, 0x3C, 0x78, 0xE9, 0xFF, 0x00, 0x00, 0x01,
    ];
    assert_eq!(s550_payload_crc16(&spec_example), 0xE569);
    assert_eq!(s550_payload_crc16(&[0xFF, 0x98, 0x81, 0x03]), 0x2EC7);
    assert_eq!(s550_payload_crc16(&[0xFF, 0x98, 0x81, 0x00]), 0x1C5C);
    assert_eq!(s550_payload_crc16(&[0x11]), 0x0E8F);
    assert_eq!(s550_payload_crc16(&[0x29, 0x05, 0x01]), 0xC927);
    // Single-byte change changes the CRC.
    assert_ne!(
        s550_payload_crc16(&[0xFF, 0x98, 0x81, 0x03]),
        s550_payload_crc16(&[0xFF, 0x98, 0x81, 0x04])
    );
}

#[test]
fn short_packets_encode_to_four_bytes_with_ecc() {
    let sleep_out = s550_encode_packet(0x05, &[0x11]).unwrap();
    assert_eq!(sleep_out.as_bytes(), &[0x05, 0x11, 0x00, 0x36]);
    assert_eq!(sleep_out.ecc, 0x36);
    assert_eq!(sleep_out.crc, None);
    let display_on = s550_encode_packet(0x05, &[0x29]).unwrap();
    assert_eq!(display_on.as_bytes(), &[0x05, 0x29, 0x00, 0x1C]);
    let madctl = s550_encode_packet(0x15, &[0x36, 0x00]).unwrap();
    assert_eq!(madctl.as_bytes(), &[0x15, 0x36, 0x00, 0x29]);
    let two = s550_encode_packet(0x15, &[0x53, 0x24]).unwrap();
    assert_eq!(two.as_bytes(), &[0x15, 0x53, 0x24, 0x08]);
    assert_eq!(two.len, 4);
}

#[test]
fn long_packets_encode_word_count_ecc_payload_and_crc() {
    let page3 = s550_encode_packet(0x29, &[0xFF, 0x98, 0x81, 0x03]).unwrap();
    assert_eq!(
        page3.as_bytes(),
        &[0x29, 0x04, 0x00, 0x3F, 0xFF, 0x98, 0x81, 0x03, 0xC7, 0x2E]
    );
    assert_eq!(page3.ecc, 0x3F);
    assert_eq!(page3.crc, Some(0x2EC7));
    assert_eq!(page3.len, 10);
    let dcs_long = s550_encode_packet(0x39, &[0xA0, 0x00, 0x1D, 0x2A, 0x13]).unwrap();
    assert_eq!(dcs_long.len, 11);
    assert_eq!(&dcs_long.as_bytes()[..4], &[0x39, 0x05, 0x00, 0x36]);
    assert_eq!(&dcs_long.as_bytes()[4..9], &[0xA0, 0x00, 0x1D, 0x2A, 0x13]);
    let crc = s550_payload_crc16(&[0xA0, 0x00, 0x1D, 0x2A, 0x13]);
    assert_eq!(&dcs_long.as_bytes()[9..], &[crc as u8, (crc >> 8) as u8]);
    // Maximum payload is encodable and fills the bounded buffer exactly.
    let max = [0x5Au8; S550_MAX_PAYLOAD_BYTES];
    let encoded = s550_encode_packet(0x39, &max).unwrap();
    assert_eq!(encoded.len, S550_MAX_ENCODED_BYTES);
    assert_eq!(encoded.bytes[1], S550_MAX_PAYLOAD_BYTES as u8);
    assert_eq!(encoded.bytes[2], 0);
}

#[test]
fn unknown_data_types_and_oversized_payloads_are_rejected() {
    for data_type in [0x00, 0x01, 0x03, 0x06, 0x13, 0x14, 0x23, 0x28, 0x32, 0x3E, 0x49, 0x7F, 0xFF] {
        assert_eq!(
            s550_encode_packet(data_type, &[0x00, 0x00]),
            Err(G8lS550PanelInitError::UnknownDataType),
            "data type {data_type:#04x}"
        );
        assert_eq!(s550_data_type_is_long(data_type), None);
    }
    let oversized = [0u8; S550_MAX_PAYLOAD_BYTES + 1];
    assert_eq!(
        s550_encode_packet(0x39, &oversized),
        Err(G8lS550PanelInitError::PayloadTooLong)
    );
    assert_eq!(
        s550_encode_packet(0x29, &oversized),
        Err(G8lS550PanelInitError::PayloadTooLong)
    );
    // Unknown data types are also rejected inside a program.
    let program = [PAGE3, G8lS550Step::Write { data_type: 0x23, payload: &[0x01, 0x02] }];
    assert_eq!(validate(&program), Err(G8lS550PanelInitError::UnknownDataType));
}

#[test]
fn short_packet_length_mismatch_and_empty_long_payload_fail() {
    assert_eq!(
        s550_encode_packet(0x05, &[]),
        Err(G8lS550PanelInitError::ShortPacketLengthMismatch)
    );
    assert_eq!(
        s550_encode_packet(0x05, &[0x11, 0x00]),
        Err(G8lS550PanelInitError::ShortPacketLengthMismatch)
    );
    assert_eq!(
        s550_encode_packet(0x15, &[0x36]),
        Err(G8lS550PanelInitError::ShortPacketLengthMismatch)
    );
    assert_eq!(
        s550_encode_packet(0x15, &[0x36, 0x00, 0x01]),
        Err(G8lS550PanelInitError::ShortPacketLengthMismatch)
    );
    assert_eq!(
        s550_encode_packet(0x29, &[]),
        Err(G8lS550PanelInitError::EmptyLongPayload)
    );
    assert_eq!(
        s550_encode_packet(0x39, &[]),
        Err(G8lS550PanelInitError::EmptyLongPayload)
    );
}

#[test]
fn canonical_profiles_validate_and_publish_distinct_receipts() {
    let mut receipts = Vec::new();
    for profile in [
        G8lS550PanelProfile::TouchDisplay2Inch5,
        G8lS550PanelProfile::TouchDisplay2Inch7,
    ] {
        let program = s550_canonical_program(profile);
        let mut state = G8lS550PanelInitState::new();
        let G8lS550PanelInitOutcome::Published(receipt) =
            publish(&mut state, profile, program).unwrap()
        else {
            panic!("canonical program must publish")
        };
        assert_eq!(receipt.sequence, 550);
        assert_eq!(receipt.predecessor_sequence, 549);
        assert_eq!(receipt.r1_stage, 2);
        assert_eq!(receipt.profile, profile);
        assert_eq!((receipt.width, receipt.height, receipt.lanes), (720, 1280, 2));
        assert_eq!(receipt.step_count, program.len());
        assert_eq!(receipt.packet_count, program.len() - 2);
        assert_eq!(
            receipt.short_packet_count + receipt.long_packet_count,
            receipt.packet_count
        );
        assert_eq!(receipt.page_select_count, 4);
        assert_eq!(
            receipt.register_write_count,
            receipt.packet_count - receipt.page_select_count - 2
        );
        assert_eq!(receipt.total_delay_ms, 140);
        assert_eq!(receipt.sleep_out_delay_ms, 120);
        assert_eq!(receipt.sleep_out_ecc, 0x36);
        assert_eq!(receipt.display_on_ecc, 0x1C);
        // 4 page selects (10 B) + 1 DCS long (11 B) + short packets (4 B).
        assert_eq!(
            receipt.encoded_bytes,
            4 * 10 + 11 + 4 * receipt.short_packet_count
        );
        assert!(!receipt.hardware_present);
        assert!(receipt.s540_physical_verdict_retained_red);
        assert!(receipt.s543_physical_verdict_retained_red);
        assert!(!receipt.automatic_promotion);
        assert_eq!(receipt.supported_profile_runtime_observations, 0);
        assert_eq!(receipt.physical_observations, 0);
        assert!(!receipt.runbook_executed);
        receipts.push(receipt);
    }
    assert_ne!(receipts[0], receipts[1]);
    assert_ne!(receipts[0].program_digest, receipts[1].program_digest);
    assert_eq!(receipts[0].step_count, 27);
    assert_eq!(receipts[1].step_count, 33);
}

#[test]
fn phase_ordering_violations_fail_closed() {
    assert_eq!(validate(&[]), Err(G8lS550PanelInitError::EmptyProgram));
    assert_eq!(
        validate(&[REG, PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(120), DISPLAY_ON]),
        Err(G8lS550PanelInitError::FirstStepNotPageSelect)
    );
    assert_eq!(
        validate(&[G8lS550Step::DelayMs(10), PAGE0]),
        Err(G8lS550PanelInitError::IncompleteProgram)
    );
    assert_eq!(
        validate(&[PAGE0, REG, DISPLAY_ON]),
        Err(G8lS550PanelInitError::DisplayOnBeforeSleepOut)
    );
    assert_eq!(
        validate(&[PAGE3, REG, SLEEP_OUT, G8lS550Step::DelayMs(120), DISPLAY_ON]),
        Err(G8lS550PanelInitError::SleepOutOutsidePageZero)
    );
    assert_eq!(
        validate(&[PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(120), REG, DISPLAY_ON]),
        Err(G8lS550PanelInitError::RegisterWriteAfterSleepOut)
    );
    assert_eq!(
        validate(&[PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(120), PAGE3, DISPLAY_ON]),
        Err(G8lS550PanelInitError::RegisterWriteAfterSleepOut)
    );
    assert_eq!(
        validate(&[PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(120), SLEEP_OUT, DISPLAY_ON]),
        Err(G8lS550PanelInitError::DuplicateSleepOut)
    );
    assert_eq!(
        validate(&[PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(120), DISPLAY_ON, REG]),
        Err(G8lS550PanelInitError::PacketAfterDisplayOn)
    );
    assert_eq!(
        validate(&[PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(120), DISPLAY_ON, DISPLAY_ON]),
        Err(G8lS550PanelInitError::PacketAfterDisplayOn)
    );
    assert_eq!(
        validate(&[PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(120)]),
        Err(G8lS550PanelInitError::IncompleteProgram)
    );
    // A trailing delay after display-on is allowed; a trailing packet is not.
    assert!(validate(&[PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(120), DISPLAY_ON, G8lS550Step::DelayMs(20)]).is_ok());
    assert!(validate(&minimal_program()).is_ok());
}

#[test]
fn sleep_out_delay_below_120ms_is_rejected_and_accumulates() {
    for delay in [0, 1, 50, 100, 119] {
        assert_eq!(
            validate(&[PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(delay), DISPLAY_ON]),
            Err(G8lS550PanelInitError::InsufficientSleepOutDelay),
            "delay {delay}"
        );
    }
    assert_eq!(
        validate(&[PAGE0, SLEEP_OUT, DISPLAY_ON]),
        Err(G8lS550PanelInitError::InsufficientSleepOutDelay)
    );
    let split = validate(&[
        PAGE0,
        SLEEP_OUT,
        G8lS550Step::DelayMs(60),
        G8lS550Step::DelayMs(60),
        DISPLAY_ON,
    ])
    .unwrap();
    assert_eq!(split.sleep_out_delay_ms, 120);
    assert_eq!(split.total_delay_ms, 120);
    // Delay before sleep-out does not count towards the sleep-out wait.
    assert_eq!(
        validate(&[
            PAGE0,
            G8lS550Step::DelayMs(200),
            SLEEP_OUT,
            G8lS550Step::DelayMs(100),
            DISPLAY_ON,
        ]),
        Err(G8lS550PanelInitError::InsufficientSleepOutDelay)
    );
    let exact = validate(&[PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(120), DISPLAY_ON]).unwrap();
    assert_eq!(exact.sleep_out_delay_ms, S550_MIN_SLEEP_OUT_DELAY_MS);
}

#[test]
fn delay_bounds_and_overflow_fail_closed() {
    assert_eq!(
        validate(&[PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(1_001), DISPLAY_ON]),
        Err(G8lS550PanelInitError::DelayTooLong)
    );
    assert_eq!(
        validate(&[PAGE0, SLEEP_OUT, G8lS550Step::DelayMs(u32::MAX), DISPLAY_ON]),
        Err(G8lS550PanelInitError::DelayTooLong)
    );
    let mut program = vec![PAGE0];
    for _ in 0..5 {
        program.push(G8lS550Step::DelayMs(1_000));
    }
    program.extend([SLEEP_OUT, G8lS550Step::DelayMs(120), DISPLAY_ON]);
    assert_eq!(validate(&program), Err(G8lS550PanelInitError::DelayOverflow));
    let mut ok = vec![PAGE0];
    for _ in 0..4 {
        ok.push(G8lS550Step::DelayMs(1_000));
    }
    ok.extend([SLEEP_OUT, G8lS550Step::DelayMs(1_000), DISPLAY_ON]);
    assert_eq!(validate(&ok).unwrap().total_delay_ms, S550_MAX_TOTAL_DELAY_MS);
    // Step-count bound.
    let mut too_long = vec![PAGE0; S550_MAX_PROGRAM_STEPS + 1];
    too_long.push(SLEEP_OUT);
    assert_eq!(validate(&too_long), Err(G8lS550PanelInitError::ProgramTooLong));
    let mut at_bound = vec![PAGE0; S550_MAX_PROGRAM_STEPS - 3];
    at_bound.extend([SLEEP_OUT, G8lS550Step::DelayMs(120), DISPLAY_ON]);
    assert_eq!(at_bound.len(), S550_MAX_PROGRAM_STEPS);
    assert_eq!(
        validate(&at_bound).unwrap().page_select_count,
        S550_MAX_PROGRAM_STEPS - 3
    );
}

#[test]
fn page_select_malformed_and_out_of_range_fail() {
    assert_eq!(s550_page_select(0x29, &[0xFF, 0x98, 0x81, 0x03]), Ok(Some(3)));
    assert_eq!(s550_page_select(0x15, &[0x36, 0x00]), Ok(None));
    assert_eq!(
        s550_page_select(0x39, &[0xFF, 0x98, 0x81, 0x03]),
        Err(G8lS550PanelInitError::MalformedPageSelect)
    );
    assert_eq!(
        s550_page_select(0x29, &[0xFF, 0x98, 0x81]),
        Err(G8lS550PanelInitError::MalformedPageSelect)
    );
    assert_eq!(
        s550_page_select(0x29, &[0xFF, 0x98, 0x80, 0x03]),
        Err(G8lS550PanelInitError::MalformedPageSelect)
    );
    assert_eq!(
        s550_page_select(0x29, &[0xFF, 0x99, 0x81, 0x03]),
        Err(G8lS550PanelInitError::MalformedPageSelect)
    );
    assert_eq!(
        s550_page_select(0x29, &[0xFF, 0x98, 0x81, 0x05]),
        Err(G8lS550PanelInitError::PageOutOfRange)
    );
    assert_eq!(
        s550_page_select(0x29, &[0xFF, 0x98, 0x81, 0xFF]),
        Err(G8lS550PanelInitError::PageOutOfRange)
    );
    let bad_page = [G8lS550Step::Write { data_type: 0x29, payload: &[0xFF, 0x98, 0x81, 0x09] }];
    assert_eq!(validate(&bad_page), Err(G8lS550PanelInitError::PageOutOfRange));
    let short_ff = [G8lS550Step::Write { data_type: 0x15, payload: &[0xFF, 0x03] }];
    assert_eq!(validate(&short_ff), Err(G8lS550PanelInitError::MalformedPageSelect));
}

#[test]
fn timing_tables_are_720x1280_two_lane_and_refresh_is_checked() {
    for profile in [
        G8lS550PanelProfile::TouchDisplay2Inch5,
        G8lS550PanelProfile::TouchDisplay2Inch7,
    ] {
        let timing = s550_timing_for(profile);
        assert_eq!((timing.width, timing.height, timing.lanes), (720, 1280, 2));
        assert_eq!(timing.sleep_out_delay_ms, 120);
        assert_eq!(timing.display_on_delay_ms, 20);
        let refresh = timing.refresh_mhz().unwrap();
        assert!((50_000..=70_000).contains(&refresh), "refresh {refresh} mHz");
    }
    assert_eq!(S550_TIMING_INCH7.htotal(), 1042);
    assert_eq!(S550_TIMING_INCH7.vtotal(), 1332);
    assert_eq!(S550_TIMING_INCH5.htotal(), 900);
    assert_eq!(S550_TIMING_INCH5.vtotal(), 1316);
    assert_ne!(S550_TIMING_INCH5, S550_TIMING_INCH7);
    let overflow = G8lS550PanelTiming {
        width: 1,
        height: 1,
        hfront_porch: 0,
        hsync: 0,
        hback_porch: 0,
        vfront_porch: 0,
        vsync: 0,
        vback_porch: 0,
        pixel_clock_khz: u32::MAX,
        ..S550_TIMING_INCH7
    };
    assert_eq!(overflow.refresh_mhz(), None);
    let zero = G8lS550PanelTiming {
        width: 0,
        hfront_porch: 0,
        hsync: 0,
        hback_porch: 0,
        ..S550_TIMING_INCH7
    };
    assert_eq!(zero.refresh_mhz(), None);
}

#[test]
fn source_only_gate_keeps_runtime_physical_and_r1_claims_zero() {
    assert!(SOURCE.contains("S550_SUPPORTED_PROFILE_RUNTIME_OBSERVATIONS: usize = 0"));
    assert!(SOURCE.contains("S550_PHYSICAL_OBSERVATIONS: usize = 0"));
    assert!(SOURCE.contains("S550_PHYSICAL_OR_DEVICE_OPERATIONS: usize = 0"));
    assert!(SOURCE.contains("S550_HARDWARE_PRESENT: bool = false"));
    assert!(SOURCE.contains("S550_BOOT_TO_UI_PHYSICALLY_OBSERVED: bool = false"));
    assert!(SOURCE.contains("S550_R1_ACCEPTANCE_COMPLETE: bool = false"));
    assert!(SOURCE.contains("RUNBOOK_EXECUTED_IN_S550: bool = false"));
    assert!(SOURCE.contains("S550_S540_PHYSICAL_VERDICT_RETAINED_RED: bool = true"));
    assert!(SOURCE.contains("S550_S543_PHYSICAL_VERDICT_RETAINED_RED: bool = true"));
}
snippet sha256: 8a49e5bdc6e1file sha256: 8a49e5bdc6e1
03 · Kapı kimlik kaydı

Operations sıra, kimlik ve başlık bağı

tam Operations kaydıL2902–L2961
website/src/lib/operations.ts::g8l-s550-r1-ili9881-panel-dcs-init-sequence-model
  {
    id: "g8l-s550-r1-ili9881-panel-dcs-init-sequence-model",
    date: "2026-08-30",
    sequence: 550,
    status: "passed",
    umbrella_status: "partial",
    title: "S550 · R1 ekran: ILI9881 panel DCS init dizisi modeli",
    summary:
      "S550 kaynak/host model kapısı PASS'tir: Raspberry Pi Touch Display 2'nin 5 inç ve 7 inç ILI9881C profilleri için (ikisi de 720x1280, 2 lane, ayrı timing ve init tabloları) DCS/generic paket ve gecikme adımlarından oluşan init programı, 24-bit başlık üzerinde 6-bit MIPI DSI Hamming ECC ve long paketler için reflected CRC-16 (0x8408, init 0xFFFF) üreten ham DSI byte encoder'ı ve page select → register write → sleep-out (0x11) → ≥120 ms → display-on (0x29) faz sırasını, 32 B payload sınırını ve 0x05/0x15/0x29/0x39 dışındaki data type'ları fail-closed reddeden validator modellenmiştir. Focused 21/21 PASS'tir; ECC/CRC değerleri bilinen vektörlerle pinlenmiştir. Hiçbir panel, DSI host, dokunmatik, board, UART, SD veya güç işlemi yoktur; S540 ve S543 fiziksel RED immutable kalır, physical observation=0, RUNBOOK_EXECUTED_IN_S550=NO, Boot-to-UI=false ve R1 acceptance=false'dur. S551 R1 2. aşamanın bir sonraki host-only ekran/dokunma/UI model kapısıdır.",
    evidence: [
      "S550, S549'dan ayrı kernel model modülü, 21-test focused binary, proof, status bloğu, Operations kaydı ve complete Code kartına sahiptir; modül kernel main.rs ve simulation lib.rs'te kayıtlıdır fakat hiçbir boot, IRQ, scheduler veya sürücü yoluna bağlanmamıştır.",
      "Dar S550 source/host model status=PASS; R1 umbrella=PARTIAL, S540 ve S543 physical gate status=RED olarak ayrı tutulur.",
      "Model iki panel profili taşır: TouchDisplay2Inch5 (hfp/hs/hbp 80/20/80, vfp/vs/vbp 12/4/20, 71000 kHz, htotal 900, vtotal 1316) ve TouchDisplay2Inch7 (239/33/50, 20/2/30, 83330 kHz, htotal 1042, vtotal 1332); ikisi de 720x1280 ve 2 lane'dir. Timing değerleri host tablosudur, vendor doğrulaması veya panel gözlemi yoktur.",
      "Kabul edilen DSI data type kümesi kapalıdır: 0x05 DCS short (0 parametre), 0x15 DCS short (1 parametre), 0x29 generic long ve 0x39 DCS long; diğer tüm data type'lar UnknownDataType ile reddedilir.",
      "Encoder short paketleri [DI, D0, D1, ECC], long paketleri [DI, WC.lo, WC.hi, ECC, payload, CRC.lo, CRC.hi] olarak üretir; payload sınırı 32 B, kodlanmış paket sınırı 38 B'dir.",
      "6-bit ECC MIPI DSI Hamming kodudur; pinlenen vektörler ECC(05 11 00)=0x36, ECC(05 29 00)=0x1C, ECC(29 04 00)=0x3F, ECC(15 36 00)=0x29, ECC(39 05 00)=0x36, ECC(FF FF FF)=0x3C'dir ve 24 tek-bit sendromunun ayrık, sıfırdan farklı ve tek ağırlıklı (≥3) olduğu test edilir.",
      "CRC-16 reflected polinom 0x8408, init 0xFFFF, final XOR yoktur; boş payload 0xFFFF, MIPI spesifikasyonu 24-byte checksum örneği 0xE569, FF 98 81 03 için 0x2EC7 ve FF 98 81 00 için 0x1C5C pinlenmiştir; page-3 select 29 04 00 3F FF 98 81 03 C7 2E olarak kodlanır.",
      "Validator faz sırasını zorlar: ilk adım FF 98 81 <page> page select (page 0..=4), register write'lar, page 0 üzerinde sleep-out 0x11, birikimli ≥120 ms gecikme, display-on 0x29 ve yalnız kuyruk gecikmesi; sleep-out sonrası register write, çift sleep-out, display-on sonrası paket ve eksik program reddedilir.",
      "Sınırlar checked aritmetikle korunur: program ≤128 adım, tek gecikme ≤1000 ms, toplam gecikme ≤5000 ms; aşımlar DelayTooLong/DelayOverflow/ProgramTooLong ile fail-closed döner.",
      "20 hata kodu sıfırdan farklı ve benzersizdir; yanlış predecessor (≠549) doğrulamadan önce reddedilir.",
      "Canonical 5 inç programı 27 adım / 25 paket, 7 inç programı 33 adım / 31 paket üretir; ikisi de 4 page select, 120 ms sleep-out beklemesi ve 140 ms toplam gecikme taşır ve farklı FNV-1a program digest'i verir.",
      "service_s550_model_panel_init_program exact replay'de aynı receipt ile Retained döner; publish sonrası farklı profil, register değeri veya gecikme PublishedStateDrift ile reddedilir.",
      "Focused target 1 grup / 21 passed / 0 failed / 0 ignored / 0 filtered verdi.",
      "Implementation 29042 B / cdc545f16057b821e86b6df183a6773a6052e6ba2dd6d5b002675f5bfc938169; focused test 24916 B / 8a49e5bdc6e190c2aa75ce475858783a933e11b2fbf721e722702cc6881b0692 SHA-256'dır.",
      "Proof 5688 B'dir.",
      "Register tabloları temsili model alt kümesidir; vendor'un tam init dökümü değildir ve hiçbir panel üzerinde doğrulanmamıştır.",
      "S550 sırasında panel, DSI host, RP1, dokunmatik, board, SD write/read-back/eject, UART open/capture, power transition, physical retry veya yeni immutable raw üretimi yapılmadı; S540 ve S543 raw/verdict byte-exact RED korunur, automatic promotion=false ve S546 kararı varsayılmaz.",
      "RUNBOOK_EXECUTED_IN_S550=NO; supported-profile runtime observations=0, physical observations=0, hardware present=false, production callsite wired=false, Boot-to-UI physically observed=false ve R1 acceptance=false'dur.",
      "S551 R1 2. aşamada bir sonraki host-only ekran/dokunma/UI model kapısıdır; panel, DSI, SD, UART, güç veya fiziksel koşu yetkisi değildir.",
    ],
    commands: [
      "CARGO_INCREMENTAL=0 cargo test -p aselsan_microkernel_simulation --test g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s550_r1_ili9881_panel_dcs_init_sequence_model -- --test-threads=1",
    ],
    terminalSessions: [
      {
        id: "s550-focused",
        title: "S550 ILI9881 panel DCS init dizisi modeli focused acceptance",
        commandLines: [
          "CARGO_INCREMENTAL=0 cargo test -p aselsan_microkernel_simulation --test g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s550_r1_ili9881_panel_dcs_init_sequence_model -- --test-threads=1",
        ],
        outputLines: [
          "test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s",
          "S550 focused=1 group / 21 passed / 0 failed",
          "hardware=none physical=0 runbook=NO",
        ],
        exitCode: 0,
        outputMode: "complete",
      },
    ],
    terminalSessionsNote:
      "S550 kaynak/host model PASS'tir; supported-profile runtime veya fiziksel PASS değildir. S540 ve S543 RED raw ve kararları değişmez.",
    limitations: [
      "S550 yalnız host üzerinde derlenen ve focused testle sürülen bir modeldir; hiçbir donanım/panel/modem/board gözlemi yoktur.",
      "Timing tabloları ve register alt kümesi vendor doğrulaması taşımaz; gerçek ILI9881C init dökümüyle byte-exact eşleşme iddiası yoktur.",
      "Modül hiçbir boot, IRQ, scheduler veya sürücü yoluna bağlanmamıştır; production callsite yoktur.",
      "S540 ve S543 fiziksel RED immutable kalır; S546 üçüncü fiziksel koşunun kararı bu kapıda varsayılmaz ve otomatik yükseltme yoktur.",
      "BOOT_TO_UI_READY gerçek UART'ta görülmedi; Boot-to-UI ve R1 acceptance false kalır.",
      "S551 R1 2. aşamanın bir sonraki host-only model kapısıdır; yeni SD/UART/power/panel koşusu ayrı kapı, fresh target revalidation, açık operatör yetkisi ve yeni immutable raw ister.",
    ],
  },
snippet sha256: 2a90e5f04bdcfile sha256: 9726dbf00f84
Focused test komutu
CARGO_INCREMENTAL=0 cargo test -p aselsan_microkernel_simulation --test g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s550_r1_ili9881_panel_dcs_init_sequence_model -- --test-threads=1
proof: docs/M8.1-RPi5-G8l-S550-R1-ILI9881-Panel-DCS-Init-Sequence-Model-Proof.md
Registry schema v5 · generator website/scripts/generate-code-gates.mjs · Tam SHA-256: 3050638b71a684d8f8f947a8a6faa237a17fa8db5dc0db04fb207b668b462af9