Software Detailed Design

Document ID Rev Date Classification
SWE.3-001 1.0 2026-03-21 Confidential

Revision History

Rev Date Author Reviewer Description
1.0 2026-03-21 An Dao -- Initial release

1. Purpose

This document provides the detailed design for safety-critical software modules of the foxBMS 2 POSIX port. It satisfies ASPICE SWE.3 and ISO 26262 Part 6 Clause 8 (software unit design and implementation). The document focuses on the DIAG module configuration, the SOA module check functions, and the BMS state machine transitions.

2. Scope

Detailed design of three modules: - DIAG (Diagnostic Handler) -- 85-entry configuration table - SOA (Safe Operating Area) -- threshold check functions - BMS (Battery Management State Machine) -- state transitions

3. References

ID Title
[SWE.1-001] Software Requirements Specification
[SWE.2-001] Software Architecture Description
[ISO-SSR-001] Software Safety Requirements

4. DIAG Module Detailed Design

4.1 Overview

The DIAG module maintains a configuration table (diag_cfg) with 85 entries. Each entry defines a diagnostic identifier, its threshold counter limit, severity level, evaluation delay, and callback function. The DIAG_Handler() function is the sole entry point for reporting diagnostic events.

4.2 DIAG Handler Algorithm

DIAG_Handler(diagId, event, impact, data):
    entry = diag_cfg[diagId]

    if event == DIAG_EVENT_OK:
        if entry.counter > 0:
            entry.counter -= 1
        return STD_OK

    // event == DIAG_EVENT_NOT_OK
    now = OS_GetTickCount()
    if (now - entry.lastEvaluation) < entry.delay:
        return STD_OK  // debounce: too soon to re-evaluate

    entry.lastEvaluation = now
    entry.counter += 1

    if entry.counter >= entry.threshold:
        entry.fatalFlag = true
        if entry.callback != NULL:
            entry.callback(diagId, event, impact, data)

    return STD_NOT_OK

4.3 DIAG Configuration Table -- FATAL Entries

The following table lists all 29 diagnostic IDs configured with FATAL severity. These are the entries that, when their threshold counter is reached, cause the BMS to transition to ERROR state and open contactors.

Traces to: SW-REQ-030

, SW-REQ-031, SW-REQ-035
↓ Traces Down
UT-040

# DIAG ID Threshold Severity Delay (ms) Traces To
1 CELL_VOLTAGE_OVERVOLTAGE_MSL 50 FATAL 200 SW-REQ-001
2 CELL_VOLTAGE_UNDERVOLTAGE_MSL 50 FATAL 200 SW-REQ-002
↑ Traces Up
SYS-REQ-021TSR-001
3 TEMP_OVERTEMPERATURE_CHARGE_MSL 500 FATAL 1000 SW-REQ-022
4 TEMP_OVERTEMPERATURE_DISCHARGE_MSL 500 FATAL 1000 SW-REQ-020
5 TEMP_UNDERTEMPERATURE_CHARGE_MSL 500 FATAL 1000 SW-REQ-023
↑ Traces Up
SYS-REQ-043TSR-003
6 TEMP_UNDERTEMPERATURE_DISCHARGE_MSL 500 FATAL 1000 SW-REQ-021
7 OVERCURRENT_CHARGE_CELL_MSL 10 FATAL 100 SW-REQ-011
↑ Traces Up
SYS-REQ-031TSR-002
8 OVERCURRENT_DISCHARGE_CELL_MSL 10 FATAL 100 SW-REQ-010
9 STRING_OVERCURRENT_CHARGE_MSL 10 FATAL 100 SW-REQ-012
↑ Traces Up
SYS-REQ-013
↓ Traces Down
SSR-003UT-020
10 STRING_OVERCURRENT_DISCHARGE_MSL 10 FATAL 100 SW-REQ-012
↑ Traces Up
SYS-REQ-013
↓ Traces Down
SSR-003UT-020
11 PACK_OVERCURRENT_DISCHARGE_MSL 10 FATAL 100 SW-REQ-013
↑ Traces Up
SYS-REQ-014
↓ Traces Down
SSR-003UT-020
12 PACK_OVERCURRENT_CHARGE_MSL 10 FATAL 100 SW-REQ-013
↑ Traces Up
SYS-REQ-014
↓ Traces Down
SSR-003UT-020
13 CURRENT_ON_OPEN_STRING 10 FATAL 100 SW-REQ-014
↑ Traces Up
SYS-REQ-013
↓ Traces Down
IT-039SSR-010UT-020
14 DEEP_DISCHARGE_DETECTED 1 FATAL 100 SW-REQ-005
↑ Traces Up
SYS-REQ-022
15 PLAUSIBILITY_PACK_VOLTAGE 10 FATAL 100 SW-REQ-060
16 INTERLOCK_FEEDBACK 10 FATAL 100 SW-REQ-030
17 AFE_SPI 5 FATAL 100 SW-REQ-030
18 AFE_COMMUNICATION_INTEGRITY 5 FATAL 100 SW-REQ-030
19 AFE_MUX 5 FATAL 100 SW-REQ-030
20 AFE_CONFIG 1 FATAL 100 SW-REQ-030
21 CAN_TIMING 100 FATAL 200 SW-REQ-110
↓ Traces Down
SSR-040
22 CURRENT_SENSOR_RESPONDING 100 FATAL 200 SW-REQ-111
↓ Traces Down
SSR-041
23 SBC_RSTB_ERROR 1 FATAL 100 SW-REQ-030
24 STRING_MINUS_CONTACTOR_FEEDBACK 20 FATAL 100 SW-REQ-044
25 STRING_PLUS_CONTACTOR_FEEDBACK 20 FATAL 100 SW-REQ-044
26 PRECHARGE_CONTACTOR_FEEDBACK 20 FATAL 100 SW-REQ-044
27 SYSTEM_MONITORING 1 FATAL 0 SW-REQ-030
28 FLASHCHECKSUM 1 FATAL 0 SW-REQ-030
29 ALERT_MODE 1 FATAL 0 SW-REQ-030

4.4 POSIX Port DIAG Adjustments

Of the 85 total DIAG IDs: - 61 retained with original FATAL/non-FATAL severity (including all 29 FATAL entries listed above that are software-checkable) - 24 suppressed (hardware-dependent entries whose absence would cause false FATAL triggers)

Suppressed categories include hardware-specific watchdog, power supply monitoring, and physical pin-level diagnostics that have no meaningful analog in the POSIX environment.

4.5 Threshold Counter Design Rationale

DIAG Category Threshold Delay Rationale
Cell voltage (OV/UV) 50 200 ms High threshold + delay allows transient ADC noise rejection
Temperature 500 1000 ms Thermal events are slow; high threshold prevents nuisance trips
Overcurrent 10 100 ms Current events are fast and dangerous; low threshold for quick response
AFE communication 5 100 ms Communication failures need quick detection but some retry tolerance
CAN/sensor timing 100 200 ms High threshold accommodates bus load variations
Contactor feedback 20 100 ms Moderate threshold for electromechanical bounce rejection
Critical system (monitoring, flash, alert) 1 0 Immediate response required; no debounce

5. SOA Module Detailed Design

5.1 Overview

The SOA module implements safe operating area checks for cell voltage, current, and temperature. It is called from the 100ms task and reads measurement data from the database, comparing each value against the three-tier threshold structure (MOL, RSL, MSL).

5.2 SOA Check Functions

5.2.1 SOA_CheckVoltages

SOA_CheckVoltages(pCellVoltages):
    for each cell i in [0..17]:
        v = pCellVoltages->cellVoltage_mV[i]

        // Overvoltage checks (ascending severity)
        if v > 2720:
            DIAG_Handler(CELL_VOLTAGE_OVERVOLTAGE_MOL, NOT_OK, ...)
        if v > 2750:
            DIAG_Handler(CELL_VOLTAGE_OVERVOLTAGE_RSL, NOT_OK, ...)
        if v > 2800:
            DIAG_Handler(CELL_VOLTAGE_OVERVOLTAGE_MSL, NOT_OK, ...)
        else:
            DIAG_Handler(CELL_VOLTAGE_OVERVOLTAGE_MSL, OK, ...)

        // Undervoltage checks (descending severity)
        if v < 1580:
            DIAG_Handler(CELL_VOLTAGE_UNDERVOLTAGE_MOL, NOT_OK, ...)
        if v < 1550:
            DIAG_Handler(CELL_VOLTAGE_UNDERVOLTAGE_RSL, NOT_OK, ...)
        if v < 1500:
            DIAG_Handler(CELL_VOLTAGE_UNDERVOLTAGE_MSL, NOT_OK, ...)
            DIAG_Handler(DEEP_DISCHARGE_DETECTED, NOT_OK, ...)
        else:
            DIAG_Handler(CELL_VOLTAGE_UNDERVOLTAGE_MSL, OK, ...)

Traces to: SW-REQ-001

through SW-REQ-005
↑ Traces Up
SYS-REQ-022

5.2.2 SOA_CheckCurrents

SOA_CheckCurrents(pCurrentData):
    // Cell-level discharge
    if abs(pCurrentData->current_mA) > 170000:
        DIAG_Handler(OVERCURRENT_DISCHARGE_CELL_MOL, NOT_OK, ...)
    if abs(pCurrentData->current_mA) > 175000:
        DIAG_Handler(OVERCURRENT_DISCHARGE_CELL_RSL, NOT_OK, ...)
    if abs(pCurrentData->current_mA) > 180000:
        DIAG_Handler(OVERCURRENT_DISCHARGE_CELL_MSL, NOT_OK, ...)
    else:
        DIAG_Handler(OVERCURRENT_DISCHARGE_CELL_MSL, OK, ...)

    // String-level checks
    if stringCurrent > stringOvercurrentThreshold:
        DIAG_Handler(STRING_OVERCURRENT_DISCHARGE_MSL, NOT_OK, ...)
    if stringCurrent > stringOvercurrentChargeThreshold:
        DIAG_Handler(STRING_OVERCURRENT_CHARGE_MSL, NOT_OK, ...)

    // Pack-level checks
    if packCurrent > packOvercurrentThreshold:
        DIAG_Handler(PACK_OVERCURRENT_DISCHARGE_MSL, NOT_OK, ...)
    if packCurrent > packOvercurrentChargeThreshold:
        DIAG_Handler(PACK_OVERCURRENT_CHARGE_MSL, NOT_OK, ...)

    // Open string current detection
    if contactorsOpen AND abs(current) > restCurrentThreshold:
        DIAG_Handler(CURRENT_ON_OPEN_STRING, NOT_OK, ...)

Traces to: SW-REQ-010

through SW-REQ-014
↑ Traces Up
SYS-REQ-013
↓ Traces Down
IT-039SSR-010UT-020

5.2.3 SOA_CheckTemperatures

SOA_CheckTemperatures(pTempData, chargingState):
    for each sensor i in [0..7]:
        t = pTempData->cellTemperature_degC[i]

        if chargingState == DISCHARGING:
            // Overtemperature discharge
            if t > 45: DIAG_Handler(TEMP_OVERTEMPERATURE_DISCHARGE_MOL, NOT_OK, ...)
            if t > 50: DIAG_Handler(TEMP_OVERTEMPERATURE_DISCHARGE_RSL, NOT_OK, ...)
            if t > 55: DIAG_Handler(TEMP_OVERTEMPERATURE_DISCHARGE_MSL, NOT_OK, ...)
            else:      DIAG_Handler(TEMP_OVERTEMPERATURE_DISCHARGE_MSL, OK, ...)

            // Undertemperature discharge
            if t < -10: DIAG_Handler(TEMP_UNDERTEMPERATURE_DISCHARGE_MOL, NOT_OK, ...)
            if t < -15: DIAG_Handler(TEMP_UNDERTEMPERATURE_DISCHARGE_RSL, NOT_OK, ...)
            if t < -20: DIAG_Handler(TEMP_UNDERTEMPERATURE_DISCHARGE_MSL, NOT_OK, ...)
            else:       DIAG_Handler(TEMP_UNDERTEMPERATURE_DISCHARGE_MSL, OK, ...)

        if chargingState == CHARGING:
            // Overtemperature charge
            if t > 35: DIAG_Handler(TEMP_OVERTEMPERATURE_CHARGE_MOL, NOT_OK, ...)
            if t > 40: DIAG_Handler(TEMP_OVERTEMPERATURE_CHARGE_RSL, NOT_OK, ...)
            if t > 45: DIAG_Handler(TEMP_OVERTEMPERATURE_CHARGE_MSL, NOT_OK, ...)
            else:      DIAG_Handler(TEMP_OVERTEMPERATURE_CHARGE_MSL, OK, ...)

            // Undertemperature charge
            if t < -10: DIAG_Handler(TEMP_UNDERTEMPERATURE_CHARGE_MOL, NOT_OK, ...)
            if t < -15: DIAG_Handler(TEMP_UNDERTEMPERATURE_CHARGE_RSL, NOT_OK, ...)
            if t < -20: DIAG_Handler(TEMP_UNDERTEMPERATURE_CHARGE_MSL, NOT_OK, ...)
            else:       DIAG_Handler(TEMP_UNDERTEMPERATURE_CHARGE_MSL, OK, ...)

Traces to: SW-REQ-020

through SW-REQ-023
↑ Traces Up
SYS-REQ-043TSR-003

6. BMS State Machine Detailed Design

6.1 State Diagram

                    +-------------------+
                    |    STANDBY (5)    |<---------+
                    +--------+----------+          |
                             |                     |
                    CAN 0x210 request               |
                    (NORMAL requested)              |
                             |                     |
                    +--------v----------+          |
                    |  PRECHARGE (6)    |          |
                    +--------+----------+          |
                             |                     |
                    Precharge complete              | Fault cleared
                    (voltage in tolerance)          | AND
                             |                     | STANDBY requested
                    +--------v----------+          |
                    |   NORMAL (7)      |          |
                    +--------+----------+          |
                             |                     |
                    Any FATAL diag                  |
                             |                     |
                    +--------v----------+          |
                    |   ERROR (9)       +----------+
                    | Open contactors   |
                    +-------------------+

6.2 State Transition Table

Current State Event Guard Condition Next State Action Traces To
STANDBY State request via CAN 0x210 Request == NORMAL PRECHARGE Begin precharge sequence SW-REQ-041
STANDBY FATAL diag -- ERROR Open all contactors SW-REQ-043
PRECHARGE Precharge complete Voltage within tolerance NORMAL Close string+ contactor SW-REQ-042
PRECHARGE Precharge timeout Timeout expired ERROR Open all contactors SW-REQ-043
PRECHARGE FATAL diag -- ERROR Open all contactors SW-REQ-043
NORMAL FATAL diag -- ERROR Open all contactors SW-REQ-043
NORMAL State request via CAN 0x210 Request == STANDBY STANDBY Open all contactors SW-REQ-041
ERROR Fault cleared + STANDBY request Both conditions met STANDBY Reset error flags SW-REQ-045

6.3 BMS_Trigger Implementation

BMS_Trigger():
    // Check for FATAL diagnostic -- highest priority
    if DIAG_IsAnyFatalErrorSet():
        if bmsState != ERROR:
            bmsState = ERROR
            CONT_SetContactorState(STRING_PLUS, OPEN)
            CONT_SetContactorState(STRING_MINUS, OPEN)
            CONT_SetContactorState(PRECHARGE, OPEN)
        return

    switch (bmsState):
        case STANDBY:
            request = DATA_Read(STATE_REQUEST)
            if request == NORMAL_REQUESTED:
                bmsState = PRECHARGE
                CONT_SetContactorState(PRECHARGE, CLOSE)
                CONT_SetContactorState(STRING_MINUS, CLOSE)
                prechargeTimer = OS_GetTickCount()

        case PRECHARGE:
            if prechargeVoltageInTolerance():
                CONT_SetContactorState(STRING_PLUS, CLOSE)
                CONT_SetContactorState(PRECHARGE, OPEN)
                bmsState = NORMAL
            elif prechargeTimedOut():
                bmsState = ERROR
                openAllContactors()

        case NORMAL:
            request = DATA_Read(STATE_REQUEST)
            if request == STANDBY_REQUESTED:
                openAllContactors()
                bmsState = STANDBY

        case ERROR:
            if NOT DIAG_IsAnyFatalErrorSet():
                request = DATA_Read(STATE_REQUEST)
                if request == STANDBY_REQUESTED:
                    bmsState = STANDBY

Traces to: SW-REQ-040

through SW-REQ-045

6.4 Contactor Sequence Timing

Phase Action Duration Constraint
Precharge start Close precharge contactor, close string- Immediate --
Precharge monitor Wait for pack voltage to reach string voltage Configurable timeout Max 2 seconds
Precharge complete Close string+, open precharge Immediate Current < 3500 mA (contactor break limit)
Normal disconnect Open string+, open string-, open precharge Sequenced 10 ms apart Break current < 3500 mA
Emergency disconnect Open all simultaneously Immediate Safety priority overrides break current limit

7. Data Structures

7.1 DIAG Configuration Entry

typedef struct {
    DIAG_ID_e           id;             /* Unique diagnostic identifier */
    uint16_t            threshold;      /* Counter limit before FATAL */
    DIAG_SEVERITY_e     severity;       /* FATAL or non-FATAL */
    uint32_t            delay_ms;       /* Minimum re-evaluation interval */
    DIAG_CALLBACK_f     callback;       /* Optional callback on threshold breach */
    /* Runtime state (not configured, initialized to zero) */
    uint16_t            counter;        /* Current fault count */
    uint32_t            lastEvaluation; /* Tick of last evaluation */
    bool                fatalFlag;      /* True when counter >= threshold */
} DIAG_CFG_s;

7.2 BMS State

typedef enum {
    BMS_STANDBY   = 5,
    BMS_PRECHARGE = 6,
    BMS_NORMAL    = 7,
    BMS_ERROR     = 9,
} BMS_STATE_e;

7.3 SYS State

typedef enum {
    SYS_UNINITIALIZED  = 0,
    SYS_INITIALIZATION = 1,
    SYS_INITIALIZED    = 2,
    SYS_IDLE           = 3,
    SYS_RUNNING        = 5,
} SYS_STATE_e;

End of Document