diff --git a/.fusa-reqs-pending.json b/.fusa-reqs-pending.json index 265dd29..d3a99e2 100644 --- a/.fusa-reqs-pending.json +++ b/.fusa-reqs-pending.json @@ -306,6 +306,213 @@ "asil": "QM", "tc18": "§12.3, TC18.txt L2485-L2488 (Figure 17, \"RC Server lifecycle states\", TC18.txt L2517)", "gap": "Confirmed absent (2026-08-22, cpp-RCP Phase 6 batch 6): this id was a PRE-EXISTING orphan tag in lifecycle.hpp's/test_lifecycle.cpp's own top-of-file `// fusa:req`/`// fusa:test REQ-LIFECYCLE-023` manifest lines, but on investigation the code and tests actually carrying that tag (the four field_writable() TEST_CASEs covering HwGeneric/FunctionalW/FunctionalWStar/ReadOnly) test rcp::lifecycle::field_writable()'s own GENERAL per-FieldKind mechanism -- which matches c-RCP's REQ-LIFECYCLE-018/019/020 far more precisely, and has been re-tagged accordingly in this same batch -- not this id's own, narrower, SPECIFIC claim (that TC18's real register tables EP_GEN_CFG/QUEUE_CFG are mapped to the HwGeneric FieldKind rather than FunctionalWStar). That mapping decision is not lifecycle.hpp's own job: FieldKind is a caller-supplied classification, and the actual 'which live register-map table maps to which FieldKind' decision belongs to rcp/regmap.hpp's EP0 address-routed dispatcher -- which regmap.hpp's own header comment (lines ~1793-1808) explicitly documents as 'DELIBERATELY NOT ported in this batch', naming exactly 'three separate table-specific authorization carve-outs' (i.e. this id's own EP_GEN_CFG/QUEUE_CFG carve-out among them) as deferred, cross-cutting, out-of-scope-for-Phase-4-batch-B work. Grepped regmap.hpp for any ep_generic_cfg/response_queue_cfg call into lifecycle::field_writable(): none exists. The mislabeled `REQ-LIFECYCLE-023` tags have been removed from lifecycle.hpp's/test_lifecycle.cpp's own manifests in this batch (replaced by the correct REQ-LIFECYCLE-018/019/020 ids); regmap.hpp/mock.hpp are both out of this batch's own file-scope (restricted to e2e.hpp/test_e2e.cpp/lifecycle.hpp/test_lifecycle.cpp). Filed here so a future regmap.hpp-scoped batch (the one that ports the EP0 address-routed dispatcher) can wire this specific table-to-FieldKind mapping, add real tags for it, and migrate this entry in." + }, + { + "id": "REQ-CANEP-001", + "title": "rcp_ep_can_frame_format_valid() accepts exactly the six FrameFormat values Table 57 defines", + "text": "rcp_ep_can_frame_format_valid() shall return true for v in 0..5 and false for any value above 5.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.2 Table 56, TC18.txt L5764-5836", + "tc18_master_id": "TC18-13.7.11.3-003", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, CAN (can.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/can.hpp / tests/test_can.cpp. The blocker is that cpp-RCP's pre-existing REQ-CANEP-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-CANEP-001 (\"CAN's ep_type id is 0x0B\"). Genuinely implemented+tested: rcp::can::frame_format_valid(v) (include/rcp/can.hpp) is a real, directly-testable constexpr function returning true for v in 0..5 and false for 6/7 -- tests/test_can.cpp's \"frame_format_valid accepts 0..5 and rejects 6/7\" TEST_CASE exercises it exhaustively over the whole uint8_t domain boundary." + }, + { + "id": "REQ-CANEP-002", + "title": "rcp_ep_can_frame_format_is_xl() identifies the two CAN XL variants", + "text": "rcp_ep_can_frame_format_is_xl() shall return true for RCP_EP_CAN_FRAME_XL_CLASSICAL_PL and RCP_EP_CAN_FRAME_XL_NEW_PL, and false for every other value.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.2 Table 56, TC18.txt L5764-5836", + "tc18_master_id": "TC18-13.7.11.3-003", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, CAN (can.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/can.hpp / tests/test_can.cpp. The blocker is that cpp-RCP's pre-existing REQ-CANEP-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-CANEP-002 (\"CAN identifier range validation\"). Genuinely implemented+tested: rcp::can::frame_format_is_xl(format) (can.hpp) is a real constexpr function returning true only for FrameFormat::XlClassicalPl/XlNewPl -- tests/test_can.cpp's \"frame_format_is_xl is true only for the two CAN XL variants\" TEST_CASE exercises all six defined FrameFormat values." + }, + { + "id": "REQ-CANEP-003", + "title": "rcp_ep_can_frame_format_id_width() reports the correct arbitration-id width per format", + "text": "rcp_ep_can_frame_format_id_width() shall return RCP_EP_CAN_ID_WIDTH_EXTENDED_29 for RCP_EP_CAN_FRAME_CEFF and RCP_EP_CAN_FRAME_FEFF, and RCP_EP_CAN_ID_WIDTH_BASE_11 for every other value including an invalid format.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 (\"Sending remote frames is not supported. In case the CAN ID is 11bits, then it shall be right aligned in the CAN ID field.\"), TC18.txt L5887; §13.7.11.2 Table 56, TC18.txt L5764-5836", + "tc18_master_id": "TC18-13.7.11.3-005", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, CAN (can.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/can.hpp / tests/test_can.cpp. The blocker is that cpp-RCP's pre-existing REQ-CANEP-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-CANEP-003 (\"CAN frame-format payload ceilings, data frames only\"). Genuinely implemented+tested: rcp::can::frame_format_id_width(format) (can.hpp) is a real constexpr function returning IdWidth::Extended29 for Ceff/Feff and IdWidth::Base11 for every other value -- tests/test_can.cpp's \"frame_format_id_width: Extended29 for CEFF/FEFF, Base11 for every other defined format\" TEST_CASE exercises all six values." + }, + { + "id": "REQ-CANEP-004", + "title": "rcp_ep_can_arbitration_id_valid() bounds an identifier against its format's id width", + "text": "rcp_ep_can_arbitration_id_valid() shall return true iff format is rcp_ep_can_frame_format_valid() and id does not exceed 0x7FF (BASE_11 formats) or 0x1FFFFFFF (EXTENDED_29 formats), and false for an invalid format regardless of id.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 (\"In case the CAN ID is 11bits, then it shall be right aligned in the CAN ID field.\"), TC18.txt L5887", + "tc18_master_id": "TC18-13.7.11.3-005", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, CAN (can.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/can.hpp / tests/test_can.cpp. The blocker is that cpp-RCP's pre-existing REQ-CANEP-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-CANEP-004 (\"CAN XL accepted single-AVTPDU payload limitation\"). Genuinely implemented+tested: rcp::can::arbitration_id_valid(format, id) (can.hpp), wrapped by validate_identifier() for the std::error_code-returning surface, bounds id at 0x7FF (Base11) or 0x1FFFFFFF (Extended29) and fails safe to false for an invalid format -- tests/test_can.cpp's \"validate_identifier accepts in-range ids...\"/\"...rejects an out-of-range id...\" TEST_CASEs exercise both width classes and the width-independent-of-format-alone rule." + }, + { + "id": "REQ-CANEP-005", + "title": "rcp_ep_can_frame_format_max_data_len() reports each frame kind's own data ceiling", + "text": "rcp_ep_can_frame_format_max_data_len() shall return 8 for CBFF/CEFF, 64 for FBFF/FEFF, RCP_EP_CAN_XL_MAX_DATA_LEN (2048) for both CAN XL variants, and 0 for an invalid format.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 (\"For CAN XL this can be up to 2054 bytes (2048 + 6, see below)... The 'CAN data' field includes the additional data (RRS, SDT, VCID, AF (in total 6 bytes)- see ISO11898-1) needed for CAN XL frames\"), TC18.txt L5859-5860, L5888-5889", + "tc18_master_id": "TC18-13.7.11.3-002", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, CAN (can.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/can.hpp / tests/test_can.cpp. The blocker is that cpp-RCP's pre-existing REQ-CANEP-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-CANEP-005 (\"CAN per-phase bit-timing register sets\"). Genuinely implemented+tested: rcp::can::frame_format_max_data_len(format) (can.hpp) is a real constexpr function returning kClassicalMaxDataLen (8)/kFdMaxDataLen (64)/kXlMaxDataLen (2048) per format and 0 for an invalid value -- tests/test_can.cpp's \"frame_format_max_data_len: 8/64/2048 per format, 0 for invalid\" TEST_CASE exercises all six values plus an invalid one." + }, + { + "id": "REQ-CANEP-006", + "title": "rcp_ep_can_xl_filter_index_valid() accepts exactly the defined filter-table indices", + "text": "rcp_ep_can_xl_filter_index_valid() shall return true for index in 0..RCP_EP_CAN_XL_MAX_FILTERS-1 and false for index >= RCP_EP_CAN_XL_MAX_FILTERS.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.2 Table 56 (four \"acceptance filter\" entries: acceptance filter 1-4, each 32 bit R/W), TC18.txt L5801-5815", + "tc18_master_id": "TC18-13.7.11.2-002", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, CAN (can.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/can.hpp / tests/test_can.cpp. The blocker is that cpp-RCP's pre-existing REQ-CANEP-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-CANEP-006 (\"CAN-XL-specific acceptance/receive filters\"). Genuinely implemented+tested: rcp::can::xl_filter_index_valid(index) (can.hpp) is a real constexpr function returning true iff index < kMaxXlFilters -- exercised directly by set_xl_filter()'s own boundary check in tests/test_can.cpp's \"set_xl_filter applies only for a valid index and when authorized\" TEST_CASE (index == kMaxXlFilters rejected)." + }, + { + "id": "REQ-CANEP-007", + "title": "rcp_ep_can_functional_cfg_init() zero-initializes every functional-config field", + "text": "rcp_ep_can_functional_cfg_init() shall zero cfg->common's flags, every bit-timing register set, delay_comp_enable/_offset, exec_delay_clk_divider, and every xl_filters[] entry.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, CAN (can.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/can.hpp / tests/test_can.cpp. The blocker is that cpp-RCP's pre-existing REQ-CANEP-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-CANEP-007 (\"CAN has no trigger-signal table; CanErrc is a distinct error category\"). Genuinely implemented+tested: CanFunctionalConfig's own default member initializers (can.hpp) zero every field (flags false, every CanBitTimingPhase field 0, xl_filters entries default-constructed, xl_new_pl_provisioned false) -- the C++ equivalent of a dedicated functional_cfg_init() function, since this module never leaves a CanFunctionalConfig partially constructed. This batch adds a dedicated regression test asserting the zero-initialized state directly (tests/test_can.cpp), matching this module's own equivalent test for ISELED's explicit iseled_functional_cfg_init()." + }, + { + "id": "REQ-CANEP-008", + "title": "rcp_ep_can_functional_cfg_writable() reuses server.h's field-writability authorization", + "text": "rcp_ep_can_functional_cfg_writable() shall return exactly rcp_server_field_writable(state, RCP_LIFECYCLE_FIELD_FUNCTIONAL_W, writer).", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.3.1.2 (\"...all access to the HW_config and EP_generic_config are locked.\"), TC18.txt L2553-2554; §12.3.1.3, TC18.txt L2601-2602 (W* marker convention)", + "tc18_master_id": "TC18-12.3.1.2-001", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, CAN (can.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/can.hpp / tests/test_can.cpp. The blocker is that cpp-RCP's pre-existing REQ-CANEP-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-CANEP-008 (\"CanEndpoint::handle_request applies Table 33 Row 2 evt[2:0] classification before transmitting\"). Genuinely implemented+tested: rcp::can::functional_cfg_writable(state, writer) (can.hpp) is a thin, real wrapper over rcp::lifecycle::field_writable(state, FieldKind::FunctionalW, writer) -- exercised via every setter's own gating test in tests/test_can.cpp (e.g. \"set_arbitration_timing/set_fd_data_timing/set_xl_data_timing store independently, gated by functional_cfg_writable\"), which cover both the denied (HwUnconfigured) and authorized (HwConfigured, via_owning_stream) cases." + }, + { + "id": "REQ-CANEP-009", + "title": "rcp_ep_can_set_arbitration_timing() is gated by functional-config authorization", + "text": "rcp_ep_can_set_arbitration_timing() shall apply timing to cfg->arbitration_timing and return true iff rcp_ep_can_functional_cfg_writable() authorizes the write for state/writer, leaving cfg unchanged and returning false otherwise.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.2 Table 56 (\"CAN bit time register 1 ... Defines bit times for Classical CAN\"), TC18.txt L5795; §12.3.1.3, TC18.txt L2601-2602 (W* marker convention)", + "tc18_master_id": "TC18-13.7.11.2-002", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, CAN (can.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/can.hpp / tests/test_can.cpp. The blocker is that cpp-RCP's pre-existing REQ-CANEP-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-CANEP-009 (\"CanEndpoint::handle_request reports config-write (evt[2:0]==111b) as not yet supported\"). Genuinely implemented+tested: rcp::can::set_arbitration_timing(cfg, timing, state, writer) (can.hpp) applies timing to cfg.timing.arbitration and returns true iff functional_cfg_writable() authorizes the write, leaving cfg unchanged otherwise -- tests/test_can.cpp's \"set_arbitration_timing/set_fd_data_timing/set_xl_data_timing store independently, gated by functional_cfg_writable\" TEST_CASE exercises both the denied and authorized paths and confirms the applied value." + }, + { + "id": "REQ-LINEP-006", + "title": "rcp_ep_lin_trigger_fires() never fires for trigger RCP_EP_LIN_TRIGGER_NONE", + "text": "rcp_ep_lin_trigger_fires() shall return false for trigger RCP_EP_LIN_TRIGGER_NONE regardless of tx_done_event and trailing_time_expired. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533): this entry's own prior text also asserted RCP_EP_LIN_TRIGGER_TX_DONE's own AND-of-both-conditions outcome -- a second, independently-testable switch-arm behavior of the same function, the same per-switch-arm-per-id pattern REQ-PWM-002 through REQ-PWM-009 already establish for evt[2:0] write semantics. That clause is now REQ-LINEP-030's own id, which also absorbs REQ-LINEP-023 (RETIRED -- see that entry): -023's own text, added when issue #201 introduced trailing_time_expired, described the identical TX_DONE AND-of-both-conditions rule this entry's own now-removed TX_DONE clause already restated after its 2026-08-13 correction, both citing the same TC18-13.7.10.1-002 master id. This entry keeps only the NONE case.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.1 (\"The LIN EP issues a trigger when a transmission has been finalized, and the configured trailing time has expired.\"), TC18.txt L5694-5695", + "tc18_master_id": "TC18-13.7.10.1-002", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, LIN (lin.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/lin.hpp / tests/test_lin.cpp. The blocker is that cpp-RCP's pre-existing REQ-LINEP-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-LINEP-006 (\"LinEndpoint::handle_request reports config-write (evt[2:0]==111b) as not yet supported\"). Genuinely implemented+tested: rcp::lin::trigger_fires(LinTrigger::None, tx_done_event, trailing_time_expired) (include/rcp/lin.hpp) is a real function whose switch statement's default/None arm returns false regardless of either boolean argument -- tests/test_lin.cpp's \"trigger_fires: None never fires\" TEST_CASE exercises it directly." + }, + { + "id": "REQ-ISELED-001", + "title": "rcp_ep_iseled_symbol_encode() frames a nibble as a 5-bit even-parity symbol", + "text": "rcp_ep_iseled_symbol_encode() shall return a value in 0..31 whose bit 4 equals the even parity of bits [3:0], with bits [3:0] equal to the input nibble masked to its own low 4 bits.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, ISELED (iseled.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/iseled.hpp / tests/test_iseled.cpp. The blocker is that cpp-RCP's pre-existing REQ-ISELED-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-ISELED-001 (\"ISELED's ep_type id is 0x0C\"). Genuinely implemented+tested: rcp::iseled::symbol_encode(nibble) (include/rcp/iseled.hpp) is a real constexpr function returning a value in 0..31 whose bit 4 is the even parity of bits [3:0] -- tests/test_iseled.cpp's \"symbol_encode/symbol_decode round-trip every nibble\" (all 16 nibbles), \"symbol_encode masks high bits of its input\", and \"symbol_encode gives 0x0 and 0xF distinct parity\" TEST_CASEs exercise it." + }, + { + "id": "REQ-ISELED-002", + "title": "rcp_ep_iseled_symbol_decode() rejects a symbol with a bad parity bit", + "text": "rcp_ep_iseled_symbol_decode() shall return false, leaving *out_nibble unwritten, when the symbol's bit 4 does not equal the even parity of its bits [3:0]. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from a prior single id that also asserted the valid-parity accept-and-decode behavior, now REQ-ISELED-032.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, ISELED (iseled.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/iseled.hpp / tests/test_iseled.cpp. The blocker is that cpp-RCP's pre-existing REQ-ISELED-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-ISELED-002 (\"ISELED Instruction/Address/Data request and Address/Data response shape\"). Genuinely implemented+tested: rcp::iseled::symbol_decode(symbol, out_nibble) (iseled.hpp) returns false, leaving out_nibble untouched, when the symbol's bit 4 does not match the even parity of its low 4 bits -- tests/test_iseled.cpp's \"symbol_decode rejects a corrupted parity bit\" TEST_CASE exercises exactly this reject path." + }, + { + "id": "REQ-ISELED-003", + "title": "rcp_ep_iseled_bitframe_encoded_len() reports two symbols per content octet", + "text": "rcp_ep_iseled_bitframe_encoded_len(data_len, append_crc) shall return 2 * (data_len + (append_crc ? 1 : 0)).", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, ISELED (iseled.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/iseled.hpp / tests/test_iseled.cpp. The blocker is that cpp-RCP's pre-existing REQ-ISELED-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-ISELED-003 (\"ISELED 12-bit address / 12-bit data field-width validation\"). Genuinely implemented+tested: rcp::iseled::bitframe_encoded_len(data_len, append_crc) (iseled.hpp) is a real constexpr function returning 2*(data_len+(append_crc?1:0)) -- tests/test_iseled.cpp's \"bitframe_encoded_len computes 2*(data_len + crc)\" TEST_CASE exercises all four combinations of a zero/nonzero length with/without the CRC flag." + }, + { + "id": "REQ-ISELED-004", + "title": "rcp_ep_iseled_encode_bitframe() produces only valid, round-trippable symbols", + "text": "rcp_ep_iseled_encode_bitframe() shall produce exactly rcp_ep_iseled_bitframe_encoded_len(data_len, append_crc) octets, each holding a value in 0..31 that rcp_ep_iseled_symbol_decode() accepts.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, ISELED (iseled.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/iseled.hpp / tests/test_iseled.cpp. The blocker is that cpp-RCP's pre-existing REQ-ISELED-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-ISELED-004 (\"ISELED TransferComplete and NativeCrcError trigger signals\"). Genuinely implemented+tested: rcp::iseled::encode_bitframe(data, append_crc) (iseled.hpp) produces exactly bitframe_encoded_len(data.size(), append_crc) octets, each a value in 0..31 accepted by symbol_decode() -- tests/test_iseled.cpp's \"encode_bitframe/decode_bitframe round-trip without CRC\" TEST_CASE asserts both the exact length and that every produced octet is <= 0x1F." + }, + { + "id": "REQ-ISELED-005", + "title": "rcp_ep_iseled_encode_bitframe() appends the CRC-8 trailer when append_crc is true", + "text": "rcp_ep_iseled_encode_bitframe() shall, when append_crc is true, frame rcp_ep_iseled_crc8(data, data_len) as one extra trailing content octet after the data octets and before symbol expansion.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, ISELED (iseled.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/iseled.hpp / tests/test_iseled.cpp. The blocker is that cpp-RCP's pre-existing REQ-ISELED-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-ISELED-005 (\"IseledErrc is a distinct error category with a non-empty message\"). Genuinely implemented+tested: rcp::iseled::encode_bitframe(data, /*append_crc=*/true) (iseled.hpp) frames crc8(data) as one extra trailing content octet before the two-symbols-per-octet expansion -- tests/test_iseled.cpp's \"encode_bitframe/decode_bitframe round-trip with CRC\" TEST_CASE asserts the exact framed length ((4+1 trailer)*2) and a successful decode_bitframe() round trip with expect_crc=true." + }, + { + "id": "REQ-ISELED-006", + "title": "rcp_ep_iseled_crc8() is deterministic", + "text": "rcp_ep_iseled_crc8() shall return the same value when called twice with the same input. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from a prior single id that also asserted the zero-length base case (now REQ-ISELED-033) and content-sensitivity (now REQ-ISELED-034).", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, ISELED (iseled.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/iseled.hpp / tests/test_iseled.cpp. The blocker is that cpp-RCP's pre-existing REQ-ISELED-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-ISELED-006 (\"IseledEndpoint::handle_request applies Table 33 Row 2 evt[2:0] classification before transacting\"). Genuinely implemented+tested: rcp::iseled::crc8(data) (iseled.hpp) is a real, directly-testable pure function -- tests/test_iseled.cpp's \"crc8 is deterministic\" TEST_CASE calls it twice with the same input and asserts an identical result." + }, + { + "id": "REQ-ISELED-007", + "title": "rcp_ep_iseled_requires_isp_n() reflects the device-provided-clock mode", + "text": "rcp_ep_iseled_requires_isp_n() shall return true iff use_rcv_clk is true. CORRECTED (2026-08-10, c-RCP-AUDIT-06, issue #256 Group G): this entry previously stated the opposite polarity (false iff use_rcv_clk is true), matching a real inverted-polarity bug in rcp_ep_iseled_requires_isp_n()'s own implementation that this fix corrects. TC18 Table 58 documents iseled_use_rcv_clk itself as \"Use clock provided by ISELED 1st device instead of FreqSync pattern\" -- true selects the device-provided clock, which arrives on ISP_N; ISP_N is unneeded only in the false case (Freq_Sync pattern).", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.2 Table 58, TC18.txt L5950-5952 (\"iseled_use_rcv_clk ... Use clock provided by ISELED 1st device instead of FreqSync pattern\"); §13.7.12.2 (\"For receiving ISELED frames on the ISP_P Pin of the interface either a clock synchronization via the Freq_Sync pattern can be done or data sampling based on the clock provided on the ISP_N pin. If the Freq_Sync pattern is used it is not necessary to connect the ISP_N of the EP to a physical Pin.\"), TC18.txt L5964-5966", + "tc18_master_id": "TC18-13.7.12.2-004", + "gap": "NOT a behavioral gap -- an id-collision traceability gap (2026-08-22, cpp-RCP Phase 6 batch 7, ISELED (iseled.hpp) module). The behavior itself is genuinely implemented and tested in include/rcp/iseled.hpp / tests/test_iseled.cpp. The blocker is that cpp-RCP's pre-existing REQ-ISELED-* catalog range for this low id predates this rewrite's later convention of reusing c-RCP's exact ids: this specific id was already assigned, by file order at the time, to an unrelated pre-rewrite requirement, and this id is already taken in .fusa-reqs.json by a materially different, already-catalogued requirement. Adding a second, conflicting entry under the same id would corrupt the catalog's own id uniqueness and is not attempted here. Filed so a future renumbering-cleanup batch (out of this batch's own scope) can resolve the collision and migrate this entry in. Already taken by: this catalog's own REQ-ISELED-007 (\"IseledEndpoint::handle_request reports config-write (evt[2:0]==111b) as not yet supported\"). Genuinely implemented+tested: rcp::iseled::requires_isp_n(use_rcv_clk) (iseled.hpp) is a real constexpr function returning use_rcv_clk directly -- tests/test_iseled.cpp's \"requires_isp_n is true iff use_rcv_clk is true\" TEST_CASE exercises both polarities." } ] } \ No newline at end of file diff --git a/.fusa-reqs.json b/.fusa-reqs.json index f58662a..55bc72c 100644 --- a/.fusa-reqs.json +++ b/.fusa-reqs.json @@ -5659,6 +5659,1149 @@ "status": "implemented", "tc18": "§12.3, TC18.txt L2492-L2495 (Figure 17, \"RC Server lifecycle states\", TC18.txt L2517; page 51 of OA_TC18_specification_v_0.5.1_RC_5_3624.pdf)", "tc18_master_id": "TC18-12.3-002" + }, + { + "id": "REQ-CANEP-010", + "title": "rcp_ep_can_set_fd_data_timing() is gated by functional-config authorization", + "text": "rcp_ep_can_set_fd_data_timing() shall apply timing to cfg->fd_data_timing and return true iff rcp_ep_can_functional_cfg_writable() authorizes the write for state/writer, leaving cfg unchanged and returning false otherwise.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.2 Table 56 (\"CAN bit time register 2 ... Defines bit times for CAN FD\"), TC18.txt L5796; §12.3.1.3, TC18.txt L2601-2602 (W* marker convention)", + "tc18_master_id": "TC18-13.7.11.2-002" + }, + { + "id": "REQ-CANEP-011", + "title": "rcp_ep_can_set_xl_data_timing() is gated by functional-config authorization", + "text": "rcp_ep_can_set_xl_data_timing() shall apply timing to cfg->xl_data_timing and return true iff rcp_ep_can_functional_cfg_writable() authorizes the write for state/writer, leaving cfg unchanged and returning false otherwise.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.2 Table 56 (\"CAN bit time register 3 ... Defines bit times for CAN XL\"), TC18.txt L5797; §12.3.1.3, TC18.txt L2601-2602 (W* marker convention)", + "tc18_master_id": "TC18-13.7.11.2-002" + }, + { + "id": "REQ-CANEP-012", + "title": "rcp_ep_can_set_delay_compensation() is gated by functional-config authorization", + "text": "rcp_ep_can_set_delay_compensation() shall apply enable/offset to cfg->delay_comp_enable/cfg->delay_comp_offset and return true iff rcp_ep_can_functional_cfg_writable() authorizes the write for state/writer, leaving cfg unchanged and returning false otherwise.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.2 Table 56 (\"TDCC register ... delay compensation control\"), TC18.txt L5798; §12.3.1.3, TC18.txt L2601-2602 (W* marker convention)", + "tc18_master_id": "TC18-13.7.11.2-002" + }, + { + "id": "REQ-CANEP-013", + "title": "rcp_ep_can_set_exec_delay_clk_divider() is gated by functional-config authorization", + "text": "rcp_ep_can_set_exec_delay_clk_divider() shall apply divider to cfg->exec_delay_clk_divider and return true iff rcp_ep_can_functional_cfg_writable() authorizes the write for state/writer, leaving cfg unchanged and returning false otherwise.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.2 Table 56 (\"can_clk_divider ... generate CAN_CLK time reference\"), TC18.txt L5792; §12.3.1.3, TC18.txt L2601-2602 (W* marker convention)", + "tc18_master_id": "TC18-13.7.11.2-002" + }, + { + "id": "REQ-CANEP-014", + "title": "rcp_ep_can_set_xl_filter() validates the filter index and functional-config authorization", + "text": "rcp_ep_can_set_xl_filter() shall apply filter to cfg->xl_filters[index] and return true iff index is rcp_ep_can_xl_filter_index_valid() and rcp_ep_can_functional_cfg_writable() authorizes the write for state/writer, leaving cfg unchanged and returning false otherwise.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.2 Table 56 (four \"acceptance filter\" entries: acceptance filter 1-4), TC18.txt L5801-5815; §12.3.1.3, TC18.txt L2601-2602 (W* marker convention)", + "tc18_master_id": "TC18-13.7.11.2-002" + }, + { + "id": "REQ-CANEP-015", + "title": "rcp_ep_can_strerror() returns a non-null, distinct message for every error code", + "text": "rcp_ep_can_strerror() shall return a non-null, non-empty string for every rcp_ep_can_errc_t value, distinct across all defined values, and a non-null string for an undefined value.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-CANEP-016", + "title": "rcp_ep_can_encode_frame_request() rejects a frame violating frame-format, id, length, or xl_header preconditions", + "text": "rcp_ep_can_encode_frame_request() shall return a zeroed rcp_bytes_t if frame_format is not rcp_ep_can_frame_format_valid(), arbitration_id is not rcp_ep_can_arbitration_id_valid() for frame_format, tx_len exceeds rcp_ep_can_frame_format_max_data_len(frame_format), or xl_header's presence does not match rcp_ep_can_frame_format_is_xl(frame_format). FIXED (v0.109.0): frame_format was previously packed into evt[2:0] instead of the payload's leading quadlet, a wire position TC18 does not assign it (see REQ-CANEP-039's own text). SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533): this entry's own prior text also carried the function's own successful-encoding contract (evt left at 0, frame_format/arbitration_id packed into the leading quadlet, xl-header/data layout appended) under the same id -- two independently-testable outcomes of the same function (reject vs. encode), the same per-outcome granularity REQ-PWM-002..009 and REQ-RMAP-014/-082 already establish. That successful-encoding contract is now REQ-CANEP-033's own separate id; this entry keeps only its own precondition-rejection contract.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-001" + }, + { + "id": "REQ-CANEP-017", + "title": "rcp_ep_can_decode_frame_request() rejects a malformed, misaddressed, wrong-evt, malformed-selector, or bad-arbitration-id frame", + "text": "rcp_ep_can_decode_frame_request() shall return RCP_EP_CAN_ERR_SHORT_FRAME for a frame too short for the ACF_ABB fixed header, its declared payload length, the 4-byte leading quadlet, or the full frame_format-dependent prefix length; RCP_EP_CAN_ERR_BAD_MSG_TYPE if b is not an ACF_ABB message; RCP_EP_CAN_ERR_WRONG_BUS when byte_bus_id != expected_bus_id; RCP_EP_CAN_ERR_WRONG_OP when op is not RCP_ACF_OP_WRITE; RCP_EP_CAN_ERR_BAD_EVT when evt[2:0] is not 0b000 (TC18 §13.5 Table 33's only legal value for a plain request in CAN's endpoint-type row); RCP_EP_CAN_ERR_BAD_FRAME_FORMAT when the leading quadlet's top 3 bits are not rcp_ep_can_frame_format_valid(); and RCP_EP_CAN_ERR_BAD_ARBITRATION_ID when the leading quadlet's low 29 bits are not rcp_ep_can_arbitration_id_valid() for that frame_format.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-001" + }, + { + "id": "REQ-CANEP-018", + "title": "rcp_ep_can_decode_frame_request() round-trips a well-formed frame's fields", + "text": "rcp_ep_can_decode_frame_request() shall, given a frame produced by rcp_ep_can_encode_frame_request(), return RCP_EP_CAN_OK and recover frame_format, arbitration_id, xl_header (iff frame_format is a CAN XL variant), the tx data bytes, and transaction_num unchanged.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-001" + }, + { + "id": "REQ-CANEP-019", + "title": "rcp_ep_can_encode_frame_response() enforces the same preconditions as the request encoder and supports timed/untimed encoding", + "text": "rcp_ep_can_encode_frame_response() shall apply the same frame-format/id/length/xl_header preconditions as rcp_ep_can_encode_frame_request() (substituting rx_data/rx_len), encoding as ACF_ABB when timed is false and as ACF_GBB with message_timestamp set to timestamp when timed is true.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-001" + }, + { + "id": "REQ-CANEP-020", + "title": "rcp_ep_can_decode_frame_response() rejects a malformed, misaddressed, wrong-evt, malformed-selector, or bad-arbitration-id frame across both ACF_ABB and ACF_GBB", + "text": "rcp_ep_can_decode_frame_response() shall return RCP_EP_CAN_ERR_SHORT_FRAME for a frame too short for the applicable fixed header, its declared payload length, the 4-byte leading quadlet, or the full frame_format-dependent prefix length; RCP_EP_CAN_ERR_WRONG_BUS when byte_bus_id != expected_bus_id; RCP_EP_CAN_ERR_BAD_EVT when evt[2:0] is not 0b000 (TC18 §13.5 Table 33's only legal value for a plain response in CAN's endpoint-type row); RCP_EP_CAN_ERR_BAD_FRAME_FORMAT when the leading quadlet's top 3 bits are not rcp_ep_can_frame_format_valid(); and RCP_EP_CAN_ERR_BAD_ARBITRATION_ID when the leading quadlet's low 29 bits are not rcp_ep_can_arbitration_id_valid() for that frame_format -- regardless of whether the message is ACF_ABB or ACF_GBB.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-001" + }, + { + "id": "REQ-CANEP-021", + "title": "rcp_ep_can_decode_frame_response() reports timed/untimed status and timestamp correctly", + "text": "rcp_ep_can_decode_frame_response() shall, given a frame produced by rcp_ep_can_encode_frame_response() with timed false, report out_timed false and out_timestamp 0; given one produced with timed true, report out_timed true and out_timestamp equal to the original timestamp.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-001" + }, + { + "id": "REQ-CANEP-022", + "title": "rcp_ep_can_decode_frame_response() round-trips a well-formed frame's fields", + "text": "rcp_ep_can_decode_frame_response() shall, given a frame produced by rcp_ep_can_encode_frame_response(), return RCP_EP_CAN_OK and recover frame_format, arbitration_id, xl_header (iff frame_format is a CAN XL variant), the rx data bytes, and transaction_num unchanged.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-001" + }, + { + "id": "REQ-CANEP-023", + "title": "rcp_ep_can_frame_response_fragment_count() reports 0 under the same preconditions the unfragmented response encoder fails for", + "text": "rcp_ep_can_frame_response_fragment_count() shall return 0 when frame_format/arbitration_id/xl_header/rx_len fail the same preconditions rcp_ep_can_encode_frame_response() validates. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533): this entry's own prior text also carried the function's own success-path delegation to fragment.h's rcp_fragment_plan_count() under the same id -- two independently-testable outcomes (precondition-failure vs. successful delegation), each already proven by its own distinct test (test_fragment_count_zero_for_bad_preconditions vs. test_fragment_count_one_when_fits_in_one_fragment/test_fragment_count_zero_when_segment_count_exceeds_max_fragment_segments). That delegation contract is now REQ-CANEP-034's own separate id; this entry keeps only its own precondition-failure contract.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 (\"...which requires segmentation, which is supported by the fields 'ms' and 'segment_num'.\"), TC18.txt L5859-5860", + "tc18_master_id": "TC18-13.7.11.3-002" + }, + { + "id": "REQ-CANEP-024", + "title": "rcp_ep_can_encode_frame_response_fragmented() splits a CAN frame response's combined payload into correctly-numbered ACF fragments", + "text": "rcp_ep_can_encode_frame_response_fragmented() shall encode rcp_ep_can_frame_response_fragment_count() ACF frames whose ms/read_size_or_segment_num fields follow fragment.h's rcp_fragment_plan() and whose byte_bus_id/evt/op/transaction_num/timed/timestamp match rcp_ep_can_encode_frame_response()'s own single-frame encoding. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533): this entry's own prior text also carried the degenerate single-fragment special case ('producing exactly one frame identical to that single-frame encoding when the combined payload already fits in one fragment') under the same id -- already proven by its own distinct test (test_fragment_response_unfragmented_matches_single_frame_path), independent from the general multi-fragment tests (test_fragment_worst_case_can_xl_response_round_trip/test_fragment_timed_response_round_trip). That special case is now REQ-CANEP-035's own separate id; this entry keeps only its own general multi-fragment plan-correctness contract.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 (\"...which requires segmentation, which is supported by the fields 'ms' and 'segment_num'.\"), TC18.txt L5859-5860", + "tc18_master_id": "TC18-13.7.11.3-002" + }, + { + "id": "REQ-CANEP-025", + "title": "rcp_ep_can_decode_frame_response_fragment() surfaces a fragment's ms bit, segment_num, and raw ACF payload without stripping this module's own prefix", + "text": "rcp_ep_can_decode_frame_response_fragment() shall, on RCP_EP_CAN_OK, populate out_ms and out_segment_num from the decoded ACF header's ms and read_size_or_segment_num fields and out_payload/out_payload_len as a borrowed view of the full, unstripped ACF payload.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 (\"...which requires segmentation, which is supported by the fields 'ms' and 'segment_num'.\"), TC18.txt L5859-5860; §13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-002" + }, + { + "id": "REQ-CANEP-026", + "title": "rcp_ep_can_decode_frame_response_fragment() validates byte_bus_id, message type, and evt the same way the unfragmented response decoder does", + "text": "rcp_ep_can_decode_frame_response_fragment() shall return the same RCP_EP_CAN_ERR_SHORT_FRAME/_ERR_BAD_MSG_TYPE/_ERR_WRONG_BUS/_ERR_BAD_EVT error codes, under the same conditions, that rcp_ep_can_decode_frame_response() returns them for. It does NOT validate or output frame_format: frame_format lives in the combined payload's own leading quadlet (TC18 §13.7.11.3 Figure 39), not per-fragment header state, so it is only recoverable after reassembly -- see REQ-CANEP-027. FIXED (v0.109.0): this function previously validated and output frame_format derived from evt[2:0], the same non-conformant design corrected across this module.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-001" + }, + { + "id": "REQ-CANEP-027", + "title": "rcp_ep_can_decode_reassembled_frame_response() rejects a too-short, wrong-frame-format, or bad-arbitration-id reassembled buffer", + "text": "rcp_ep_can_decode_reassembled_frame_response() shall, given a reassembled buffer of at least 4 bytes, read frame_format from its leading quadlet's top 3 bits (TC18 §13.7.11.3 Figure 39), returning RCP_EP_CAN_ERR_SHORT_FRAME if the buffer is shorter than 4 bytes or than that format's own full prefix length, RCP_EP_CAN_ERR_BAD_FRAME_FORMAT if it is not rcp_ep_can_frame_format_valid(), or RCP_EP_CAN_ERR_BAD_ARBITRATION_ID if the leading quadlet's low 29 bits are not rcp_ep_can_arbitration_id_valid() for that format. FIXED (v0.109.0): frame_format was previously a caller-supplied input (sourced from a per-fragment evt[2:0], REQ-CANEP-026's now-corrected design); it is now derived from the reassembled payload itself, the only place it is actually carried. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533): this entry's own prior text also carried the function's own successful-populate contract (recovering out_frame_format/out_arbitration_id/out_xl_header/out_rx_data identically to rcp_ep_can_decode_frame_response()) under the same id -- two independently-testable outcomes (reject-taxonomy vs. successful field recovery), the same reject/success split already established for this module's sibling decode functions (REQ-CANEP-017/-018 for the request decoder, REQ-CANEP-020/-022 for the response decoder). That successful-populate contract is now REQ-CANEP-036's own separate id; this entry keeps only its own reject-taxonomy contract. A real coverage gap closed alongside this split: RCP_EP_CAN_ERR_BAD_FRAME_FORMAT had no dedicated test of its own for this function before this batch (test_reassembled_decode_rejects_bad_frame_format, tests/test_ep_can.c, is new).", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-001" + }, + { + "id": "REQ-CANEP-028", + "title": "can_ep_len, can_base_clk and the CAN endpoint's three status registers", + "text": "IMPLEMENTED (issue #201, 2026-08-12): rcp_ep_can_render_registers()/_apply_reconfig() (ep_can.h/ep_can.c) now model the CAN functional-configuration block's own descriptor, clock and status registers (TC18 §13.7.11.2 Table 56 -- RC1's own Table 53, renumbered, issue #341 lineage): can_ep_len (0x0000, 8 bit, R, fixed at RCP_EP_CAN_EP_FUNC_LEN), the reserved octet at 0x0001 (8 bit, R, reads 0x00), can_base_clk (0x0004, 16 bit, R — always renders 0, no real clock source modelled, matching every other endpoint type's own base_clk honesty), can_ep_status (0x0006, 16 bit, R/W, new rcp_ep_can_functional_cfg_t::ep_status field), the 32-bit CAN EP status at 0x001C (R/W, new ::status field) and the 32-bit FIFO status at 0x0020 (R/W, new ::fifo_status field), reachable via the generic §12.7.1 evt[2:0]==111b mechanism. Bus-off, error-passive and FIFO-overflow conditions are now observable and settable through this endpoint. Scoped to end at 0x0024, immediately before REQ-CANEP-029's own already-documented address collision in the acceptance-filter region -- resolving that collision was not required to close this register block, since it lies entirely outside this span. The 0x0008-0x001B span (can_clk_divider, two reserved regions, the three 'CAN bit time register' fields, and TDCC) is deliberately left read-only/rendering 0 for now: an earlier investigation (issue #256 Group I) already found Table 56 gives those 32-bit registers no sub-field bit-layout in the specification text, so converting this module's own rcp_ep_can_bit_timing_t to and from their wire representation is not derivable without inventing an unverified bit-packing scheme -- the same reasoning already deferred mapping them anywhere else in this codebase, and this fix does not force that decision. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533): this entry's own prior text also carried the can_ep_enable&clr (0x0002) CAN_ENABLE_CLR_BIT_CLEAR wire-bit defect fix (issue #470, 2026-08-14) -- a different register, from a different issue, dated two days after this one, sharing only the same two implementing functions. That fix is now REQ-CANEP-037's own separate id; this entry keeps only its own descriptor/clock/status register-block contract.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.2 Table 56, TC18.txt L5764-5836", + "tc18_master_id": "TC18-13.7.11.2-002" + }, + { + "id": "REQ-CANEP-029", + "title": "the CAN endpoint's receive ID filters, which are separate from the CAN XL acceptance filters and valid for all CAN variants", + "text": "NOT IMPLEMENTED: c-RCP conflates two distinct filter tables TC18 §13.7.11.2 keeps separate, AND that table's own printed addresses for one of them are internally inconsistent. Table 53/56 prints acceptance filter 1 at 0x0024, filter 2 at 0x0028, and BOTH filter 3 and filter 4 at 0x002C (a genuine address collision, re-verified against the rendered PDF page image on both the baseline and newest available revision, not an extraction artifact) as 'acceptance filter for CAN XL', immediately followed by receive filters 1..4 at 0x0030..0x003C as 'ID filter for frame reception', the latter explicitly 'valid for all CAN variants' per the §13.7.11.2 bullet list. Which of two readings is intended -- only 3 real acceptance filters with the receive-filter table already correctly addressed, or 4 real acceptance filters requiring every receive-filter address to shift forward by one slot -- is undecidable from the primary source alone; see ep_can.h's own file header for the full analysis. c-RCP implements only one filter table — rcp_ep_can_functional_cfg_t.xl_filters[RCP_EP_CAN_XL_MAX_FILTERS], written by rcp_ep_can_set_xl_filter() (REQ-CANEP-014) — and its own header describes it as the CAN-XL acceptance/ID filter set; no code anywhere serializes either table's byte offsets, so the address ambiguity itself carries no live wire-conformance risk today. Consequence: Classical CAN, CAN FD and CAN FD light traffic cannot be ID-filtered on reception at all through this endpoint; the only filter table available is scoped to CAN XL.", + "standard": "iso26262", + "level": "QM", + "asil": "QM", + "scope": "tc18-gap", + "status": "not-implemented", + "tc18": "§13.7.11.2, TC18.txt L5764-5836 and Table 56 (acceptance filter 3/4 address collision at 0x002C, receive filter 1..4 immediately following), TC18.txt L5813-5815 -- the address-collision defect itself is confirmed unchanged word-for-word in the current RC5 text; disposition (not-implemented, genuinely blocked) is unaffected", + "tc18_master_id": "TC18-13.7.11.2-002" + }, + { + "id": "REQ-CANEP-030", + "title": "rcp_ep_can_set_xl_new_pl_provisioned() records the endpoint's CAN XL physical-layer provisioning choice, authorization-gated", + "text": "PARTIAL (2026-08-13, issue #336): TC18 §13.7.11.2 lists 'usage of new PL (YES|NO) for CAN XL' among the settings the CAN endpoint's functional configuration comprises, i.e. a per-endpoint, runtime-writable choice, alongside bit-rate and filter settings that all do have real register rows in Table 56. This one does not -- the register table goes directly from can_clk_divider (0x0008) to the undecomposed CAN bit-time/TDCC register span (0x000C-0x001B) with no row named for this setting anywhere, and no prose elsewhere in §13.7.11 gives it a bit position either. This is a genuine specification gap, not a local implementation one -- filed as TC18_spec_defects_report.md item 57 (and its own _quadruple_checked.md review copy). New rcp_ep_can_functional_cfg_t::xl_new_pl_provisioned (ep_can.h/ep_can.c) is deliberately an IN-MEMORY-ONLY field with no wire offset, matching that constraint honestly rather than inventing an unverified register bit: rcp_ep_can_set_xl_new_pl_provisioned() shall let a caller record this choice, gated the same way as every other functional-config setter (applying new_pl_provisioned to cfg and returning true iff rcp_ep_can_functional_cfg_writable() authorizes the write for state/writer, leaving cfg unchanged and returning false otherwise). What remains genuinely NOT implemented: a client still cannot read back this setting over the register map the way every other functional-config setting can, since TC18 gives no bit position to expose it at. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533): this entry's own prior text also carried a second function's own contract -- rcp_ep_can_xl_frame_matches_provisioned_pl(), which validates a decoded frame's own XL variant against this stored setting -- under the same id, self-described in that prior text as 'closing this requirement's own ... half'. That validator contract is now REQ-CANEP-038's own separate id; this entry keeps only its own setter/storage contract.", + "standard": "iso26262", + "level": "QM", + "asil": "QM", + "scope": "tc18-gap", + "status": "partial", + "tc18": "§13.7.11.2, TC18.txt L5772", + "tc18_master_id": "TC18-13.7.11.2-001" + }, + { + "id": "REQ-CANEP-031", + "title": "The FrameFormat selector's numeric code assignment (CBFF 0 through XL new-PL 5, with 6 and 7 reserved), carried in the payload's leading quadlet", + "text": "rcp_ep_can_frame_format_t shall assign exactly the FrameFormat codes TC18 §13.7.11.3 Table 57 fixes: RCP_EP_CAN_FRAME_CBFF = 0, RCP_EP_CAN_FRAME_CEFF = 1, RCP_EP_CAN_FRAME_FBFF = 2, RCP_EP_CAN_FRAME_FEFF = 3, RCP_EP_CAN_FRAME_XL_CLASSICAL_PL = 4 (XL over the classical physical layer), and RCP_EP_CAN_FRAME_XL_NEW_PL = 5 (XL over the new physical layer); codes 6 and 7 are reserved, left unassigned to any enumerator. CORRECTED (v0.109.0): an earlier revision of this requirement incorrectly asserted the FrameFormat selector was carried in the ACF byte_message_info evt field's low three bits -- verified against Figure 39 directly, that claim was never true of TC18 and was itself a bug (evt[2:0] instead carries CAN's ordinary TC18 §13.5 Table 33 Row-2 meaning: 000b plain, 111b configuration-write, else UNSUPPORTED_CMD); found and fixed while investigating this repo's Table 33 centralization. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533): this entry's own prior text also carried (a) the selector's wire position ('carried in the top 3 bits of the payload's leading quadlet, sharing that quadlet with a 29-bit arbitration_id'), now REQ-CANEP-039's own separate id, and (b) two near-duplicate clauses removed rather than re-split, since each already belongs to a different id's own tested contract: 'rcp_ep_can_frame_format_valid() shall reject [codes 6/7]' restated REQ-CANEP-001's own contract verbatim, and 'causing the decode functions to return RCP_EP_CAN_ERR_BAD_FRAME_FORMAT' restated a clause each of REQ-CANEP-017/-020/-027's own reject-taxonomies already own and test. This entry keeps only the enum's own numeric code-assignment fact, the one claim no other id captures.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Table 57 + Figure 40, TC18.txt L5842-5872", + "tc18_master_id": "TC18-13.7.11.3-003" + }, + { + "id": "REQ-CANEP-032", + "title": "The CAN endpoint does not support sending remote frames", + "text": "The CAN endpoint shall not support sending remote frames: c-RCP exposes no remote-frame flag, encode function, or decode outcome anywhere in ep_can.h/ep_can.c, so every frame this endpoint encodes or decodes is a data frame. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533): this entry's own prior text also carried an unrelated wire-layout contract -- 'an 11-bit CAN identifier is right-aligned within the CAN ID field' -- under the same id, two facts about two different topics joined only by both being true of this module. That right-alignment contract is now REQ-CANEP-040's own separate id; this entry keeps only its own remote-frame-support contract.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3, TC18.txt L5887" + }, + { + "id": "REQ-CANEP-033", + "title": "rcp_ep_can_encode_frame_request() encodes a valid frame as an ACF_ABB request with frame_format/arbitration_id packed into the leading quadlet", + "text": "rcp_ep_can_encode_frame_request() shall, given preconditions rcp_ep_can_frame_format_valid()/rcp_ep_can_arbitration_id_valid()/rcp_ep_can_frame_format_max_data_len()/rcp_ep_can_frame_format_is_xl() all satisfied, encode an ACF_ABB request with evt left at 0 (TC18 Table 33's plain-request value for CAN's endpoint-type row) and frame_format packed into the payload's leading quadlet's top 3 bits alongside arbitration_id (TC18 §13.7.11.3 Figure 39), followed by this module's own xl-header/data layout. Split 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533) out of REQ-CANEP-016's own prior text, which bundled this successful-encoding outcome with that entry's own precondition-rejection contract under one id -- see REQ-CANEP-016 for that half and its own history.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-001" + }, + { + "id": "REQ-CANEP-034", + "title": "rcp_ep_can_frame_response_fragment_count() delegates to fragment.h's rcp_fragment_plan_count() otherwise", + "text": "rcp_ep_can_frame_response_fragment_count() shall, when frame_format/arbitration_id/xl_header/rx_len pass the same preconditions rcp_ep_can_encode_frame_response() validates, return rcp_fragment_plan_count()'s answer for the combined prefix-then-data payload length and max_fragment_payload. Split 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533) out of REQ-CANEP-023's own prior text, which bundled this successful-delegation outcome with that entry's own precondition-failure contract under one id -- see REQ-CANEP-023 for that half and its own history.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 (\"...which requires segmentation, which is supported by the fields 'ms' and 'segment_num'.\"), TC18.txt L5859-5860", + "tc18_master_id": "TC18-13.7.11.3-002" + }, + { + "id": "REQ-CANEP-035", + "title": "rcp_ep_can_encode_frame_response_fragmented() produces exactly one frame, identical to the unfragmented single-frame encoding, when the combined payload already fits in one fragment", + "text": "rcp_ep_can_encode_frame_response_fragmented() shall produce exactly one frame, byte-for-byte identical to rcp_ep_can_encode_frame_response()'s own single-frame encoding, when the combined prefix-then-data payload already fits within one max_fragment_payload-sized fragment. Split 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533) out of REQ-CANEP-024's own prior text, which bundled this degenerate single-fragment special case with that entry's own general multi-fragment plan-correctness contract under one id -- see REQ-CANEP-024 for that half and its own history.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 (\"...which requires segmentation, which is supported by the fields 'ms' and 'segment_num'.\"), TC18.txt L5859-5860", + "tc18_master_id": "TC18-13.7.11.3-002" + }, + { + "id": "REQ-CANEP-036", + "title": "rcp_ep_can_decode_reassembled_frame_response() recovers frame_format/arbitration_id/xl_header/rx_data from a well-formed reassembled buffer", + "text": "rcp_ep_can_decode_reassembled_frame_response() shall, given a well-formed reassembled buffer (passing the short-frame/frame-format/arbitration-id preconditions REQ-CANEP-027 owns), populate out_frame_format, out_arbitration_id, out_xl_header (only when the format is a CAN XL variant), and out_rx_data/out_rx_len identically to how rcp_ep_can_decode_frame_response() would have for the same original, unfragmented payload, returning RCP_EP_CAN_OK. Split 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533) out of REQ-CANEP-027's own prior text, which bundled this successful-populate outcome with that entry's own reject-taxonomy contract under one id -- see REQ-CANEP-027 for that half and its own history. Prior test coverage for this specific outcome existed only indirectly, folded into REQ-CANEP-024's own multi-fragment round-trip tests (which exercise this function only as the last step of a full fragment/reassemble pipeline); test_reassembled_decode_round_trip_recovers_fields (tests/test_ep_can.c) is a new, focused test proving this function's own contract directly, independent of fragmentation.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Figure 40, TC18.txt L5842-5856", + "tc18_master_id": "TC18-13.7.11.3-001" + }, + { + "id": "REQ-CANEP-037", + "title": "can_ep_enable&clr's ep_clear_req_storage bit is wire bit 4, matching TC18 Table 35 and every sibling endpoint", + "text": "WIRE-BIT DEFECT FIXED (issue #470, 2026-08-14): can_ep_enable&clr's (0x0002) CAN_ENABLE_CLR_BIT_CLEAR constant was defined at bit 1 instead of the bit 4 TC18 Table 35 (EP functional config common entries) fixes for ep_clear_req_storage on EVERY endpoint type -- CAN's own can_ep_enable&clr row explicitly defers to Table 35 for this octet's layout rather than redefining it, and 0x0002.1:3 is reserved (reads 000b) in that table, so bit 1 collided with nothing and the fix to bit 4 introduces no new collision either. Every sibling endpoint (ep_uart.c/ep_lin.c/ep_adc.c/ep_iseled.c/ep_mdio.c) already used bit 4; CAN alone was wrong, self-consistently (render and parse both used the same wrong bit, so this module's own round-trip tests never caught it -- only a byte-literal wire assertion could). Corrected to (1u<<4) in rcp_ep_can_render_registers()/parse_can_registers() (src/ep_can.c); tests/test_tc18_gaps_ep2.c's test_can_ep_enable_clr_clear_bit_is_wire_bit_4() asserts the exact rendered/parsed byte values (0x10, not 0x02) to prevent regression. Split 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533) out of REQ-CANEP-028's own prior text, which bundled this defect fix -- a different register than can_ep_len/can_base_clk/the status registers REQ-CANEP-028 itself describes, from a later, unrelated issue -- under the same id; see REQ-CANEP-028 for its own separate register-block contract.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.2 Table 56, TC18.txt L5764-5836", + "tc18_master_id": "TC18-13.7.11.2-002" + }, + { + "id": "REQ-CANEP-038", + "title": "rcp_ep_can_xl_frame_matches_provisioned_pl() rejects a decoded CAN XL frame whose physical-layer variant contradicts the endpoint's provisioned choice", + "text": "rcp_ep_can_xl_frame_matches_provisioned_pl() shall return true for any non-CAN-XL format (a frame with no physical-layer choice of its own to conflict with); for a CAN XL format, it shall return true iff format matches the physical layer xl_new_pl_provisioned selects (RCP_EP_CAN_FRAME_XL_NEW_PL when true, RCP_EP_CAN_FRAME_XL_CLASSICAL_PL when false), and false otherwise. This closes the 'nothing rejects a frame whose XL variant contradicts the endpoint's actual physical layer' half of REQ-CANEP-030's own prior finding (issue #336): c-RCP's pre-existing per-frame expression of physical-layer selection (RCP_EP_CAN_FRAME_XL_CLASSICAL_PL/_NEW_PL in evt[2:0]) is unchanged and still the wire-level mechanism a real frame carries its own choice in; this function is the endpoint-level constraint that frame is now checked against. Split 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533) out of REQ-CANEP-030's own prior text, which bundled this validator's contract with that entry's own setter/storage contract for xl_new_pl_provisioned under one id -- see REQ-CANEP-030 for that half and its own history.", + "standard": "iso26262", + "level": "QM", + "asil": "QM", + "scope": "tc18-gap", + "status": "implemented", + "tc18": "§13.7.11.2, TC18.txt L5772", + "tc18_master_id": "TC18-13.7.11.2-001" + }, + { + "id": "REQ-CANEP-039", + "title": "The FrameFormat selector is carried in the top 3 bits of the payload's leading quadlet, sharing it with a 29-bit arbitration_id", + "text": "The FrameFormat selector shall be carried in the top 3 bits of the payload's leading quadlet (TC18 §13.7.11.3 Figure 39), sharing that quadlet with a 29-bit arbitration_id -- not the ACF byte_message_info evt field, an earlier, incorrect design this requirement family's own history (see REQ-CANEP-031) already documents and corrected (v0.109.0). Split 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533) out of REQ-CANEP-031's own prior text, which bundled this wire-position contract with that entry's own enum code-assignment fact (and two now-removed near-duplicate clauses already owned by REQ-CANEP-001/-017/-020/-027) under one id -- see REQ-CANEP-031 for that half and its own history.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 Table 57 + Figure 40, TC18.txt L5842-5872", + "tc18_master_id": "TC18-13.7.11.3-003" + }, + { + "id": "REQ-CANEP-040", + "title": "An 11-bit CAN identifier is right-aligned within the CAN ID field", + "text": "An 11-bit CAN identifier shall be right-aligned within the CAN ID field: rcp_ep_can_encode_frame_request()/_encode_frame_response() write arbitration_id via write_prefix()'s big-endian put_u32() into the fixed 4-octet identifier prefix, leaving the unused high bits zero, and rcp_ep_can_arbitration_id_valid() bounds a base-width identifier at 0x7FF so no bit can escape that alignment. Split 2026-08-18 (c-RCP-18-tracker, REQ-CANEP-* atomicity audit, issue #533) out of REQ-CANEP-032's own prior text, which bundled this right-alignment contract with that entry's own remote-frame-support contract under one id -- see REQ-CANEP-032 for that half and its own history.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3, TC18.txt L5887" + }, + { + "id": "REQ-CANEP-041", + "title": "rcp_ep_can_frame_request_fragment_count() reports 0 under the same preconditions the unfragmented request encoder fails for, delegating to fragment.h's rcp_fragment_plan_count() otherwise", + "text": "rcp_ep_can_frame_request_fragment_count() shall return 0 when frame_format/arbitration_id/xl_header/tx_len fail the same preconditions rcp_ep_can_encode_frame_request() validates (mirroring REQ-CANEP-023's own precondition-failure contract for the response side), and shall otherwise return rcp_fragment_plan_count()'s answer for the combined prefix-then-data payload length and max_fragment_payload, capped at RCP_EP_CAN_MAX_FRAGMENT_SEGMENTS (mirroring REQ-CANEP-034's own delegation contract for the response side). Added 2026-08-20 (issue #611): closes ep_can.h's own previously-documented gap that a worst-case CAN XL write request had no fragmented counterpart to rcp_ep_can_encode_frame_response_fragmented(); this id is the request-side sizing companion to rcp_ep_can_encode_frame_request_fragmented() (REQ-CANEP-042), kept as its own id rather than folded together, matching how REQ-CANEP-023/024 (and their own REQ-CANEP-034/035 split) already keep the response side's sizing and encoding contracts separately testable.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 (\"...which requires segmentation, which is supported by the fields 'ms' and 'segment_num'.\"), TC18.txt L5859-5860" + }, + { + "id": "REQ-CANEP-042", + "title": "rcp_ep_can_encode_frame_request_fragmented() splits a CAN frame write request's combined payload into correctly-numbered ACF_ABB fragments", + "text": "rcp_ep_can_encode_frame_request_fragmented() shall encode rcp_ep_can_frame_request_fragment_count() ACF_ABB frames (never ACF_GBB -- a request carries no timestamp, unlike a response) whose ms/read_size_or_segment_num fields follow fragment.h's rcp_fragment_plan() and whose byte_bus_id/evt/op/transaction_num match rcp_ep_can_encode_frame_request()'s own single-frame encoding, producing exactly one frame identical to that single-frame encoding when the combined payload already fits in one fragment. Added 2026-08-20 (issue #611): the missing encode-side convenience function ep_can.h's own file header already flagged as a deliberate follow-up when rcp_ep_can_encode_frame_response_fragmented() shipped -- a worst-case CAN XL (RCP_EP_CAN_XL_MAX_ENCODED_LEN, 2058-octet combined payload) write request cannot be sent in one ACF message (RCP_ACF_ABB_MAX_PAYLOAD, 2036) and, until this fix, had no multi-message alternative. The receiving/reassembly side needed no new production code: srv's own generic frag_reasm[]/rcp_fragment_reassembler_t accumulator (mock.h, REQ-E2E-038/039) already reassembles any endpoint type's fragmented request through rcp_mock_server_dispatch_e2e_fragment(), and rcp_ep_can_decode_reassembled_frame_response() already parses the reassembled combined payload generically (it never inspects op/rsp) -- both reused verbatim for the request direction, not duplicated. TC18 Table 24's rx_stream_max_request_size register (\"Max size of a request in bytes (longer requests will be rejected)\") is enforced by the caller-configured max_total_len already passed to rcp_fragment_reassembler_init()/rcp_mock_server_fragment_reassembler(), which fails closed with RCP_FRAGMENT_REASM_ERR_TOO_LARGE -- this function does not itself re-implement that ceiling, it only produces the fragments a caller-configured reassembler subsequently admits or rejects.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.11.3 (\"...which requires segmentation, which is supported by the fields 'ms' and 'segment_num'.\"), TC18.txt L5859-5860" + }, + { + "id": "REQ-LINEP-007", + "title": "rcp_ep_lin_functional_cfg_init() zero-initializes cfg", + "text": "rcp_ep_lin_functional_cfg_init() shall set every rcp_regmap_ep_functional_cfg_t flag in cfg->common to false, cfg->lin_clk_divider to 0, and cfg->trigger to RCP_EP_LIN_TRIGGER_NONE (0), regardless of cfg's prior contents.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-LINEP-008", + "title": "rcp_ep_lin_functional_cfg_writable() denies every writer while HW_UNCONFIGURED", + "text": "rcp_ep_lin_functional_cfg_writable() shall return false for lifecycle state RCP_LIFECYCLE_HW_UNCONFIGURED regardless of writer.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.3.1.2 (\"...all access to the HW_config and EP_generic_config are locked.\"), TC18.txt L2553-2554", + "tc18_master_id": "TC18-12.3.1.2-001" + }, + { + "id": "REQ-LINEP-009", + "title": "rcp_ep_lin_functional_cfg_writable() requires an authorized writer while HW_CONFIGURED", + "text": "rcp_ep_lin_functional_cfg_writable() shall return true for lifecycle state RCP_LIFECYCLE_HW_CONFIGURED if and only if writer.via_root_client_ep0, writer.via_owning_stream, or writer.via_discovery_stream is true, and false for an unauthorized writer. Corrected 2026-08-10 (c-RCP-AUDIT-06, issue #256 Group F): this entry previously claimed \"regardless of writer\", superseded by REQ-LIFECYCLE-030/036's authorization-gating fix (issue #198) -- the code and this module's own test (test_functional_cfg_writable_hw_configured_requires_authorization_or_discovery_stream, tests/test_ep_lin.c) were already correct; only this entry's own text had gone stale.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.3.1.2 (\"Write access via EP0 to other EPs configuration is only possible for a configured 'root client' of via the discovery stream.\"), TC18.txt L2560", + "tc18_master_id": "TC18-12.3.1.2-004" + }, + { + "id": "REQ-LINEP-010", + "title": "rcp_ep_lin_functional_cfg_writable() requires authorization while RCP_CONFIGURED", + "text": "rcp_ep_lin_functional_cfg_writable() shall return false for lifecycle state RCP_LIFECYCLE_RCP_CONFIGURED when writer has neither via_root_client_ep0 nor via_owning_stream set, and true when either is set.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.3.1.3, TC18.txt L2601-2602 (W* marker convention)", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-LINEP-011", + "title": "rcp_ep_lin_set_clk_divider() rejects an unauthorized write", + "text": "rcp_ep_lin_set_clk_divider() shall leave cfg->lin_clk_divider unchanged and return false when rcp_ep_lin_functional_cfg_writable() denies the write for the given state/writer.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.2 Table 55 (lin_clk_divider, 0x0008, \"generate Lin bit time\"), TC18.txt L5713", + "tc18_master_id": "TC18-13.7.10.2-001" + }, + { + "id": "REQ-LINEP-012", + "title": "rcp_ep_lin_set_clk_divider() applies an authorized write", + "text": "rcp_ep_lin_set_clk_divider() shall set cfg->lin_clk_divider to lin_clk_divider and return true when rcp_ep_lin_functional_cfg_writable() authorizes the write for the given state/writer.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.2 Table 55 (lin_clk_divider register), TC18.txt L5713; writability per §12.3.1.3, TC18.txt L2601-2602 (W* marker convention)", + "tc18_master_id": "TC18-13.7.10.2-001" + }, + { + "id": "REQ-LINEP-013", + "title": "rcp_ep_lin_set_trigger() rejects an unauthorized write", + "text": "rcp_ep_lin_set_trigger() shall leave cfg->trigger unchanged and return false when rcp_ep_lin_functional_cfg_writable() denies the write for the given state/writer. CLARIFIED (2026-08-10, c-RCP-AUDIT-06, issue #256 Group C): the write-authorization behavior is TC18-derived (§12.3.1.3's W* marker convention, delegated via rcp_ep_lin_functional_cfg_writable()); `trigger` itself has no TC18 basis at all -- unlike SPI/PWM_OUT/PWM_IN, TC18 defines no trigger-outputs table for LIN. See ep_lin.h's own file header.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.3.1.3, TC18.txt L2601-2602 (W* marker convention) -- governs the write-authorization behavior; `trigger` itself is this module's own original design with no TC18 counterpart (see ep_lin.h's file header)", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-LINEP-014", + "title": "rcp_ep_lin_set_trigger() applies an authorized write", + "text": "rcp_ep_lin_set_trigger() shall set cfg->trigger to trigger and return true when rcp_ep_lin_functional_cfg_writable() authorizes the write for the given state/writer. CLARIFIED (2026-08-10, c-RCP-AUDIT-06, issue #256 Group C): the write-authorization behavior is TC18-derived (§12.3.1.3's W* marker convention); `trigger` itself has no TC18 basis at all -- see REQ-LINEP-013 and ep_lin.h's own file header.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.3.1.3, TC18.txt L2601-2602 (W* marker convention) -- governs the write-authorization behavior; `trigger` itself is this module's own original design with no TC18 counterpart (see ep_lin.h's file header)", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-LINEP-015", + "title": "rcp_ep_lin_strerror() returns a distinct, non-NULL message per error code", + "text": "rcp_ep_lin_strerror() shall return a non-NULL, non-empty, and distinct string for each of RCP_EP_LIN_OK, RCP_EP_LIN_ERR_SHORT_FRAME, RCP_EP_LIN_ERR_BAD_MSG_TYPE, RCP_EP_LIN_ERR_WRONG_BUS, and RCP_EP_LIN_ERR_WRONG_OP, and a non-NULL string for any other value.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-LINEP-016", + "title": "rcp_ep_lin_encode_command_request() encodes the read direction, evt=0, and carries the raw payload unmodified", + "text": "rcp_ep_lin_encode_command_request() shall encode an ACF_ABB message addressed to byte_bus_id with op RCP_ACF_OP_READ (the reply-expected direction), evt = 0 (Table 33 constrains a plain LIN request's evt[2:0] to 000b; there is no client-selectable comparison mode), transaction_num set to transaction_num, and payload equal to tx_data[0..tx_len) byte for byte. MERGED 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533): REQ-LINEP-026 (RETIRED -- see that entry) duplicated this entry's own \"evt = 0\" fact under a separate id, both citing the same TC18-13.5-001 master id; that entry is now retired, this one remains the canonical home for this function's whole encode contract.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.5 Table 33, TC18.txt L4069-4116", + "tc18_master_id": "TC18-13.5-001" + }, + { + "id": "REQ-LINEP-017", + "title": "rcp_ep_lin_decode_command_request() round-trips a valid command request", + "text": "rcp_ep_lin_decode_command_request() shall, given a frame produced by rcp_ep_lin_encode_command_request() for byte_bus_id == expected_bus_id, return RCP_EP_LIN_OK and recover the raw tx payload and transaction_num unchanged.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.3 Figure 39 (lin request/response format), TC18.txt L5719-5736; \"the LIN endpoint checks each received message against the byte_msg_payload and if a match under the conditions given by evt[2:0] is found a reply is sent if op = 0\" (§13.7.10.1), TC18.txt L5691-5693; evt[2:0] plain-request rule per §13.5 Table 33 (LIN's row-2 group, only evt=111b defined), TC18.txt L4083-4093", + "tc18_master_id": "TC18-13.7.10.3-001" + }, + { + "id": "REQ-LINEP-018", + "title": "rcp_ep_lin_decode_command_request() rejects a frame too short for its ACF_ABB header or payload", + "text": "rcp_ep_lin_decode_command_request() shall return RCP_EP_LIN_ERR_SHORT_FRAME for a frame too short for the ACF_ABB fixed header or its declared payload length. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533): this entry's own prior text bundled four independently-testable rejection outcomes of the same function under one 'shall' clause joined by commas (each already proven by its own separate, pre-existing test function in tests/test_ep_lin.c: test_command_request_rejects_short_frame, test_command_request_rejects_bad_msg_type, test_command_request_rejects_wrong_bus, test_command_request_rejects_wrong_op) -- the exact 2+-independently-testable-behaviors-under-one-id pattern this audit exists to close, missed by the '2+ shall' proxy because the whole rule was phrased as a single sentence. The RCP_EP_LIN_ERR_BAD_MSG_TYPE clause is now REQ-LINEP-031's own id, RCP_EP_LIN_ERR_WRONG_BUS is REQ-LINEP-032's, and RCP_EP_LIN_ERR_WRONG_OP is REQ-LINEP-033's; the RCP_EP_LIN_ERR_BAD_EVT clause had already been split out as REQ-LINEP-027 before this audit. This entry keeps only the RCP_EP_LIN_ERR_SHORT_FRAME case.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.3 Figure 39 (lin request/response format), TC18.txt L5719-5736; general ACF frame-validation basis", + "tc18_master_id": "TC18-13.7.10.3-001" + }, + { + "id": "REQ-LINEP-019", + "title": "rcp_ep_lin_encode_response() encodes an untimed response as ACF_ABB", + "text": "rcp_ep_lin_encode_response() shall encode an ACF_ABB message when timed is false, with op RCP_ACF_OP_READ, byte_bus_id set to byte_bus_id, transaction_num set to transaction_num, and payload equal to rx_data[0..rx_len) byte for byte. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533): this entry's own prior text also asserted the timed=true ACF_GBB encoding shape as a second, materially different (extra message_timestamp/mtv fields, different ACF message type entirely) independently-testable outcome of the same function -- for consistency with rcp_ep_lin_decode_response()'s own sibling untimed/timed split (REQ-LINEP-020/021), which already treats the two shapes as separate ids. That clause is now REQ-LINEP-034's own id. This entry keeps only the untimed (ACF_ABB) case.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.3 Figure 39 (lin request/response format), TC18.txt L5719-5736", + "tc18_master_id": "TC18-13.7.10.3-001" + }, + { + "id": "REQ-LINEP-020", + "title": "rcp_ep_lin_decode_response() round-trips an untimed (ACF_ABB) response", + "text": "rcp_ep_lin_decode_response() shall, given a frame produced by rcp_ep_lin_encode_response() with timed false, return RCP_EP_LIN_OK, recover the rx payload and transaction_num unchanged, report out_timed false, and report out_timestamp 0.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.3 Figure 39 (lin request/response format), TC18.txt L5719-5736; response timed/untimed ACF_GBB/ACF_ABB convention shared with every prior endpoint type", + "tc18_master_id": "TC18-13.7.10.3-001" + }, + { + "id": "REQ-LINEP-021", + "title": "rcp_ep_lin_decode_response() round-trips a timed (ACF_GBB) response", + "text": "rcp_ep_lin_decode_response() shall, given a frame produced by rcp_ep_lin_encode_response() with timed true, return RCP_EP_LIN_OK, recover the rx payload and transaction_num unchanged, report out_timed true, and report out_timestamp equal to the original timestamp.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.3 Figure 39 (lin request/response format), TC18.txt L5719-5736; response timed/untimed ACF_GBB/ACF_ABB convention shared with every prior endpoint type", + "tc18_master_id": "TC18-13.7.10.3-001" + }, + { + "id": "REQ-LINEP-022", + "title": "rcp_ep_lin_decode_response() rejects a frame too short for its applicable ACF header", + "text": "rcp_ep_lin_decode_response() shall return RCP_EP_LIN_ERR_SHORT_FRAME for a frame too short for its applicable ACF fixed header. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533): this entry's own prior text bundled two independently-testable rejection outcomes of the same function under one 'shall' clause joined by 'and' (each already proven by its own separate, pre-existing test function in tests/test_ep_lin.c: test_response_decode_rejects_short_frame, test_response_decode_rejects_wrong_bus). The RCP_EP_LIN_ERR_WRONG_BUS clause is now REQ-LINEP-035's own id. This entry keeps only the RCP_EP_LIN_ERR_SHORT_FRAME case.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.3 Figure 39 (lin request/response format), TC18.txt L5719-5736; general ACF frame-validation basis for the response wire format", + "tc18_master_id": "TC18-13.7.10.3-001" + }, + { + "id": "REQ-LINEP-024", + "title": "lin_ep_len, lin_base_clk, lin_ep_status, and lin_clk_divider (Table 55) are now modeled and wire-reachable", + "text": "FIXED 2026-08-11 (c-RCP-AUDIT-06, issue #256 Group I): c-RCP now models the LIN functional-configuration block's whole register set (TC18 §13.7.10.2 Table 55) via rcp_ep_lin_render_registers()/_apply_reconfig() (REQ-LINEP-028/029): lin_ep_len (0x0000, 8 bit, R), the reserved octet at 0x0001 (8 bit, R), lin_base_clk (0x0004, 16 bit, R — not itself stored, always renders 0, the same 'no real clock source modelled' honesty ep_gpio.h's/ep_i2c.h's own base_clk fields already commit to), lin_ep_status (0x0006, 16 bit, R/W, new field), and lin_clk_divider (0x0008, 8 bit, R/W). This session's own prior text credited the pre-existing rcp_ep_lin_functional_cfg_t.lin_clk_divider (REQ-LINEP-011/012) with already covering the last register — imprecisely: that field is this module's own original, uint32_t, unit-unspecified design (documented as matching ep_spi.h's own non-wire clock_divider shape), not the literal 8-bit wire register. A new, distinct wire_clk_divider (uint8_t) field carries the real register instead, the same 'don't silently redefine an existing public field' caution SPI's own baud_rate_kbps-vs-clock_divider split and UART's own baud_rate_kbps-vs-baud_rate split already established.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.2 Table 55, TC18.txt L5697-5714", + "tc18_master_id": "TC18-13.7.10.2-001" + }, + { + "id": "REQ-LINEP-025", + "title": "rcp_ep_lin_response_matches() delegates to acf.h's shared TC18 §13.5.1 exact-match primitive", + "text": "rcp_ep_lin_response_matches(tx_data, tx_len, rx_data, rx_len) shall return rcp_acf_compound_wait_match(0, tx_data, tx_len, rx_data, rx_len) -- the exact-match mode (evt[2:0] = 000b) of acf.h's shared TC18 §13.5.1 comparison primitive, including its length-capping rule. This is the only comparison a plain LIN command request's evt[2:0] can select, since Table 33 constrains it to 000b (REQ-LINEP-027).", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.1, TC18.txt L5694-5695; §13.5.1, TC18.txt L4134-4142", + "tc18_master_id": "TC18-13.5.1-002" + }, + { + "id": "REQ-LINEP-027", + "title": "rcp_ep_lin_decode_command_request() rejects evt[2:0] != 000b with RCP_EP_LIN_ERR_BAD_EVT", + "text": "rcp_ep_lin_decode_command_request() shall return RCP_EP_LIN_ERR_BAD_EVT when rcp_acf_evt_row2_is_plain(hdr.evt) is false, per TC18 §13.5 Table 33's rule for the {ADC, PWM_IN, I2C, LIN, CAN, UART, ISELED, MDIO} endpoint-type row: evt[2:0] = 000b is the only value a plain (non-configuration) LIN command request may carry. LIN is not called out as an exception anywhere in Table 33, and TC18 §13.7.10.1's own reference to \"the conditions given by evt[2:0]\" describes the same universal §13.5.1 vocabulary Table 33 constrains to 000b here, not a LIN-private multi-mode scheme.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.5 Table 33, TC18.txt L4069-4116; §13.7.10.1, TC18.txt L5694-5695", + "tc18_master_id": "TC18-13.5-001" + }, + { + "id": "REQ-LINEP-028", + "title": "rcp_ep_lin_render_registers() serializes the EP_func register block per Table 55's offsets", + "text": "rcp_ep_lin_render_registers() shall serialize cfg's EP_func registers into out[0..RCP_EP_LIN_EP_FUNC_LEN) exactly as a configuration read of the whole block would report them, per Table 55's offsets. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533): this entry's own prior text bundled two entirely different functions' behavior under one id -- rcp_ep_lin_render_registers()'s own serialization contract (already independently tested by test_render_registers_matches_table_offsets) and rcp_ep_lin_encode_reconfig_request()'s own separate wire-encoding contract (already independently tested by test_reconfig_request_round_trip / test_encode_reconfig_request_rejects_empty_data) -- the same class of violation REQ-AUTH-009 (#519's own seed example) demonstrated: multiple functions' behavior under one id, not one function's one switch/if-arm outcome. rcp_ep_lin_encode_reconfig_request()'s own contract is now REQ-LINEP-036's own id. This entry keeps only rcp_ep_lin_render_registers()'s own serialization contract.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.7.1 Configuration of endpoints + Figure 19 (renumbered from Figure 18 by the RC5 rebaseline), TC18.txt L2844-2860", + "tc18_master_id": "TC18-12.7.1-002" + }, + { + "id": "REQ-LINEP-029", + "title": "rcp_ep_lin_apply_reconfig() rejects a write extending past the EP_func block, leaving cfg unchanged", + "text": "rcp_ep_lin_apply_reconfig() shall return RCP_EP_LIN_RECONFIG_ERR_OUT_OF_RANGE and leave cfg entirely unchanged when start_address + the payload's data length exceeds RCP_EP_LIN_EP_FUNC_LEN, matching §12.7.1's own 'such a payload is to be ignored' rule. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533): this entry's own prior text bundled three of rcp_ep_lin_apply_reconfig()'s own independently-testable outcomes -- this out-of-range rejection (already tested by test_apply_reconfig_rejects_write_past_ep_len), the successful patch-and-adopt behavior including its read-only-register-skip rule (already tested by test_apply_reconfig_writes_clk_divider / test_apply_reconfig_writes_multi_register_span / test_apply_reconfig_ignores_read_only_registers) -- plus a wholly different function, rcp_ep_lin_reconfig_strerror()'s own never-NULL contract (already tested by test_reconfig_strerror_never_null), all under one id. The successful patch/read-only-skip clause is now REQ-LINEP-038's own id; rcp_ep_lin_reconfig_strerror()'s own contract is REQ-LINEP-039's own id. This entry also newly documents (previously untraced in any REQ-LINEP-* text, despite already being implemented and independently tested by test_apply_reconfig_rejects_payload_without_data) the function's third outcome, the too-short-payload rejection, now REQ-LINEP-037's own id. This entry keeps only the out-of-range rejection.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.7.1 Configuration of endpoints, TC18.txt L2844-2860", + "tc18_master_id": "TC18-12.7.1-003" + }, + { + "id": "REQ-LINEP-030", + "title": "rcp_ep_lin_trigger_fires() fires for RCP_EP_LIN_TRIGGER_TX_DONE iff both tx_done_event and trailing_time_expired are true", + "text": "rcp_ep_lin_trigger_fires() shall return true for trigger RCP_EP_LIN_TRIGGER_TX_DONE if and only if both tx_done_event and trailing_time_expired are true, and false otherwise -- TC18 §13.7.10.1's own text: \"The LIN EP issues a trigger when a transmission has been finalized, and the configured trailing time has expired.\" SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533) out of REQ-LINEP-006, which previously bundled this TX_DONE outcome together with RCP_EP_LIN_TRIGGER_NONE's own separate always-false outcome under one id -- two independently-testable switch-arm behaviors of the same function. This id also absorbs REQ-LINEP-023 (RETIRED -- see that entry), whose own text -- added when issue #201 introduced trailing_time_expired as a second, caller-classified boolean input -- described this exact same AND-of-both-conditions rule from a different angle (design-history narrative rather than a formal shall-statement), duplicating what REQ-LINEP-006's own text already restated after its 2026-08-13 correction. Table 55 (§13.7.10.2) defines no dedicated wire register for \"the configured trailing time\" itself -- like this endpoint type's own trigger concept as a whole (no TC18 basis at all, entirely this module's own original design -- see ep_lin.h's own file header), a wire-configurable duration was never something TC18 gives this codebase a register to model; the caller classifying trailing_time_expired is this module's established architecture, not a remaining gap.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.1 (\"The LIN EP issues a trigger when a transmission has been finalized, and the configured trailing time has expired.\"), TC18.txt L5694-5695", + "tc18_master_id": "TC18-13.7.10.1-002" + }, + { + "id": "REQ-LINEP-031", + "title": "rcp_ep_lin_decode_command_request() rejects a non-ACF_ABB message", + "text": "rcp_ep_lin_decode_command_request() shall return RCP_EP_LIN_ERR_BAD_MSG_TYPE when the frame is not an ACF_ABB message. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533) out of REQ-LINEP-018 -- see that entry.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.3 Figure 39 (lin request/response format), TC18.txt L5719-5736; general ACF frame-validation basis", + "tc18_master_id": "TC18-13.7.10.3-001" + }, + { + "id": "REQ-LINEP-032", + "title": "rcp_ep_lin_decode_command_request() rejects a mismatched byte_bus_id", + "text": "rcp_ep_lin_decode_command_request() shall return RCP_EP_LIN_ERR_WRONG_BUS when byte_bus_id != expected_bus_id. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533) out of REQ-LINEP-018 -- see that entry.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.3 Figure 39 (lin request/response format), TC18.txt L5719-5736; general ACF frame-validation basis", + "tc18_master_id": "TC18-13.7.10.3-001" + }, + { + "id": "REQ-LINEP-033", + "title": "rcp_ep_lin_decode_command_request() rejects a write-direction (non-READ) op", + "text": "rcp_ep_lin_decode_command_request() shall return RCP_EP_LIN_ERR_WRONG_OP when op is not RCP_ACF_OP_READ. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533) out of REQ-LINEP-018 -- see that entry.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.3 Figure 39 (lin request/response format), TC18.txt L5719-5736; general ACF frame-validation basis", + "tc18_master_id": "TC18-13.7.10.3-001" + }, + { + "id": "REQ-LINEP-034", + "title": "rcp_ep_lin_encode_response() encodes a timed response as ACF_GBB", + "text": "rcp_ep_lin_encode_response() shall encode an ACF_GBB message with message_timestamp set to timestamp and mtv RCP_ACF_MTV_VALID when timed is true, with op RCP_ACF_OP_READ, byte_bus_id set to byte_bus_id, transaction_num set to transaction_num, and payload equal to rx_data[0..rx_len) byte for byte. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533) out of REQ-LINEP-019 -- see that entry; mirrors REQ-LINEP-021's own timed decode counterpart.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.3 Figure 39 (lin request/response format), TC18.txt L5719-5736", + "tc18_master_id": "TC18-13.7.10.3-001" + }, + { + "id": "REQ-LINEP-035", + "title": "rcp_ep_lin_decode_response() rejects a mismatched byte_bus_id", + "text": "rcp_ep_lin_decode_response() shall return RCP_EP_LIN_ERR_WRONG_BUS when byte_bus_id != expected_bus_id. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533) out of REQ-LINEP-022 -- see that entry.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.10.3 Figure 39 (lin request/response format), TC18.txt L5719-5736; general ACF frame-validation basis for the response wire format", + "tc18_master_id": "TC18-13.7.10.3-001" + }, + { + "id": "REQ-LINEP-036", + "title": "rcp_ep_lin_encode_reconfig_request() encodes an addressed EP_func configuration-write request", + "text": "rcp_ep_lin_encode_reconfig_request() shall encode an ACF_ABB configuration request (op=RCP_ACF_OP_WRITE, evt[2:0] = 111b) addressed to byte_bus_id, with payload equal to start_address (16-bit big-endian) followed by data[0..data_len). SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533) out of REQ-LINEP-028 -- see that entry; matches REQ-LINEP-016's own single-function, single-encode-contract shape for its sibling rcp_ep_lin_encode_command_request().", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.7.1 Configuration of endpoints + Figure 19 (renumbered from Figure 18 by the RC5 rebaseline), TC18.txt L2844-2860", + "tc18_master_id": "TC18-12.7.1-002" + }, + { + "id": "REQ-LINEP-037", + "title": "rcp_ep_lin_apply_reconfig() rejects a payload with no address-and-data, leaving cfg unchanged", + "text": "rcp_ep_lin_apply_reconfig() shall return RCP_EP_LIN_RECONFIG_ERR_SHORT and leave cfg entirely unchanged when payload_len is not at least RCP_EP_LIN_RECONFIG_ADDR_LEN + 1 (no address prefix, or an address prefix with no data octet after it). SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533) out of REQ-LINEP-029 -- see that entry. Already implemented and tested (test_apply_reconfig_rejects_payload_without_data) but untraced by any REQ-LINEP-* requirement text before this split.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.7.1 Configuration of endpoints, TC18.txt L2844-2860", + "tc18_master_id": "TC18-12.7.1-003" + }, + { + "id": "REQ-LINEP-038", + "title": "rcp_ep_lin_apply_reconfig() patches the addressed span at octet granularity, skipping read-only registers", + "text": "rcp_ep_lin_apply_reconfig() shall, for an in-range payload, patch cfg's Table 55 register block at octet granularity over the addressed span and adopt the whole block atomically, silently skipping (leaving unchanged) any octet of the span landing on a read-only register (EP_LEN, the reserved octet, or base_clk) while the rest of the span is still applied. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533) out of REQ-LINEP-029 -- see that entry.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.7.1 Configuration of endpoints, TC18.txt L2844-2860", + "tc18_master_id": "TC18-12.7.1-003" + }, + { + "id": "REQ-LINEP-039", + "title": "rcp_ep_lin_reconfig_strerror() never returns NULL", + "text": "rcp_ep_lin_reconfig_strerror() shall never return NULL, including for an unrecognized rcp_ep_lin_reconfig_errc_t value. SPLIT 2026-08-18 (c-RCP-18-tracker, REQ-LINEP-* atomicity audit, issue #533) out of REQ-LINEP-029, which bundled this wholly different function's own contract in with rcp_ep_lin_apply_reconfig()'s own separate behaviors -- see that entry.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.7.1 Configuration of endpoints, TC18.txt L2844-2860", + "tc18_master_id": "TC18-12.7.1-003" + }, + { + "id": "REQ-ISELED-008", + "title": "rcp_ep_iseled_trigger_fires() never fires for RCP_EP_ISELED_TRIGGER_NONE", + "text": "rcp_ep_iseled_trigger_fires() shall return false for RCP_EP_ISELED_TRIGGER_NONE regardless of tx_complete_event. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from a prior single id that also asserted the RCP_EP_ISELED_TRIGGER_TX_COMPLETE case, now REQ-ISELED-035.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.1 (\"The ISELED endpoint creates a single trigger event, when transmission of a data packet has been completed on the ISELED network.\"), TC18.txt L5913-5914", + "tc18_master_id": "TC18-13.7.12.1-002" + }, + { + "id": "REQ-ISELED-009", + "title": "rcp_ep_iseled_functional_cfg_init() zero-initializes the functional config", + "text": "rcp_ep_iseled_functional_cfg_init() shall set every rcp_regmap_ep_functional_cfg_t flag false, iseled_bit_clk_divider to 0, iseled_use_rcv_clk and iseled_crc_enable to false, and trigger to RCP_EP_ISELED_TRIGGER_NONE.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-ISELED-010", + "title": "rcp_ep_iseled_functional_cfg_writable() wraps server.h's field-writability authorization", + "text": "rcp_ep_iseled_functional_cfg_writable() shall return the result of rcp_server_field_writable() called with kind RCP_LIFECYCLE_FIELD_FUNCTIONAL_W.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.2 Table 58 (iseled_ep_options, 0x0003, R/W*), TC18.txt L5934-5936; W* marker convention per §12.3.1.3, TC18.txt L2601-2602", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-ISELED-011", + "title": "rcp_ep_iseled_set_bit_clk_divider() rejects an unauthorized write", + "text": "rcp_ep_iseled_set_bit_clk_divider() shall leave cfg unchanged and return false when rcp_ep_iseled_functional_cfg_writable() denies the write for the given state/writer. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from a prior single id that also asserted the authorized-write apply behavior, now REQ-ISELED-036 -- matching the REQ-LINEP-011/012 precedent for the identical writable-gated-setter shape.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.2 Table 58 (iseled_clk_divider, 0x0006, 8 bit, R/W, \"generate ISELED_CLK time reference should be nominal 2MHz\"), TC18.txt L5940-5942; writability per §12.3.1.3, TC18.txt L2601-2602", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-ISELED-012", + "title": "rcp_ep_iseled_set_use_rcv_clk() rejects an unauthorized write", + "text": "rcp_ep_iseled_set_use_rcv_clk() shall leave cfg unchanged and return false when rcp_ep_iseled_functional_cfg_writable() denies the write for the given state/writer. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from a prior single id that also asserted the authorized-write apply behavior, now REQ-ISELED-037.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.2 Table 58 (iseled_use_rcv_clk, 0x0007.4, R/W, \"Use clock provided by ISELED 1st device instead of FreqSync pattern\"), TC18.txt L5950-5952; writability per §12.3.1.3, TC18.txt L2601-2602", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-ISELED-013", + "title": "rcp_ep_iseled_set_crc_enable() rejects an unauthorized write", + "text": "rcp_ep_iseled_set_crc_enable() shall leave cfg unchanged and return false when rcp_ep_iseled_functional_cfg_writable() denies the write for the given state/writer. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from a prior single id that also asserted the authorized-write apply behavior, now REQ-ISELED-038.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.1 (\"The optional CRC generation as specified in the ISLED standard can be enabled. If so, the CRC will be generated and attached to the write messages to the ISELED network.\"), TC18.txt L5910-5912; writability per §12.3.1.3, TC18.txt L2601-2602", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-ISELED-014", + "title": "rcp_ep_iseled_set_trigger() rejects an unauthorized write", + "text": "rcp_ep_iseled_set_trigger() shall leave cfg unchanged and return false when rcp_ep_iseled_functional_cfg_writable() denies the write for the given state/writer. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from a prior single id that also asserted the authorized-write apply behavior, now REQ-ISELED-039.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.3.1.3, TC18.txt L2601-2602 (W* marker convention) -- governs the write-authorization behavior; the trigger event itself has its own basis at REQ-ISELED-008 (§13.7.12.1, \"The ISELED endpoint creates a single trigger event, when transmission of a data packet has been completed on the ISELED network.\", TC18.txt L5913-5914)", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-ISELED-015", + "title": "rcp_ep_iseled_strerror() never returns NULL and is distinct per code", + "text": "rcp_ep_iseled_strerror() shall return a non-NULL, non-empty string for every rcp_ep_iseled_errc_t value (including an unknown value), with a distinct string for each defined value.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-ISELED-016", + "title": "rcp_ep_iseled_decode_bitframe() rejects an odd symbol count", + "text": "rcp_ep_iseled_decode_bitframe() shall return RCP_EP_ISELED_ERR_ODD_SYMBOL_COUNT iff symbol_count is odd.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-ISELED-017", + "title": "rcp_ep_iseled_decode_bitframe() rejects an invalid symbol", + "text": "rcp_ep_iseled_decode_bitframe() shall return RCP_EP_ISELED_ERR_BAD_SYMBOL iff any symbol in symbols[0..symbol_count) fails rcp_ep_iseled_symbol_decode().", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-ISELED-018", + "title": "rcp_ep_iseled_decode_bitframe() rejects too few symbols when a CRC trailer is expected", + "text": "rcp_ep_iseled_decode_bitframe() shall return RCP_EP_ISELED_ERR_SHORT_FRAME iff expect_crc is true and symbol_count yields fewer than one reconstructed content octet.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-ISELED-019", + "title": "rcp_ep_iseled_decode_bitframe() rejects a native ISELED CRC-8 mismatch", + "text": "rcp_ep_iseled_decode_bitframe() shall return RCP_EP_ISELED_ERR_CRC_MISMATCH iff expect_crc is true and the trailing reconstructed octet does not equal rcp_ep_iseled_crc8() of the preceding reconstructed octets.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-ISELED-020", + "title": "rcp_ep_iseled_decode_bitframe() round-trips rcp_ep_iseled_encode_bitframe()'s output", + "text": "rcp_ep_iseled_decode_bitframe() shall, for symbols produced by rcp_ep_iseled_encode_bitframe(data, data_len, append_crc) with matching expect_crc, return RCP_EP_ISELED_OK and populate *out_data with exactly data[0..data_len).", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-ISELED-021", + "title": "rcp_ep_iseled_encode_command_request() carries the caller's plain payload verbatim as ACF_OP_WRITE", + "text": "rcp_ep_iseled_encode_command_request() shall encode an ACF_ABB frame addressed to byte_bus_id with op RCP_ACF_OP_WRITE and payload exactly tx_data[0..tx_len), unmodified.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.3 Figure 41 (iseled request format -- Instruction/Address/Data payload, ACF_ABB op=write), TC18.txt L5981-5995", + "tc18_master_id": "TC18-13.7.12.3-001" + }, + { + "id": "REQ-ISELED-022", + "title": "rcp_ep_iseled_decode_command_request() validates and decodes an ACF-level command request", + "text": "rcp_ep_iseled_decode_command_request() shall return RCP_EP_ISELED_ERR_SHORT_FRAME for a frame shorter than the ACF_ABB header or its declared payload, RCP_EP_ISELED_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame, RCP_EP_ISELED_ERR_WRONG_BUS for a mismatched byte_bus_id, RCP_EP_ISELED_ERR_WRONG_OP for an op other than RCP_ACF_OP_WRITE, RCP_EP_ISELED_ERR_BAD_EVT when evt[2:0] is not 0b000 (TC18 §13.5 Table 33's only legal value for a plain request in ISELED's endpoint-type row; every other value shall be rejected with error code UNSUPPORTED_CMD at the dispatch layer), and otherwise RCP_EP_ISELED_OK with the plain payload and transaction number populated.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.5 Table 33, TC18.txt L4069-4116", + "tc18_master_id": "TC18-13.5-001" + }, + { + "id": "REQ-ISELED-023", + "title": "rcp_ep_iseled_encode_response() encodes timed and untimed responses", + "text": "rcp_ep_iseled_encode_response() shall encode an ACF_GBB frame with message_timestamp set to timestamp and mtv RCP_ACF_MTV_VALID when timed is true, and an ACF_ABB frame otherwise, in both cases with payload exactly rx_data[0..rx_len) and op RCP_ACF_OP_READ.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.3 Figure 42 (iseled response format -- Address/Data[11:0]/CRC payload), TC18.txt L6014-6018; response rsp bit per §11.3 Table 17, TC18.txt L2260", + "tc18_master_id": "TC18-13.7.12.3-002" + }, + { + "id": "REQ-ISELED-024", + "title": "rcp_ep_iseled_decode_response() validates and decodes either ACF_ABB or ACF_GBB responses", + "text": "rcp_ep_iseled_decode_response() shall accept either an ACF_ABB or ACF_GBB frame, return RCP_EP_ISELED_ERR_SHORT_FRAME for a frame too short for the applicable header or declared payload, RCP_EP_ISELED_ERR_WRONG_BUS for a mismatched byte_bus_id, and otherwise RCP_EP_ISELED_OK with the plain payload, transaction number, and timed/timestamp populated.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.3 Figure 42 (iseled response format), TC18.txt L6014-6018; timed/untimed ACF_GBB/ACF_ABB response convention shared with every prior endpoint type", + "tc18_master_id": "TC18-13.7.12.3-002" + }, + { + "id": "REQ-ISELED-025", + "title": "ISELED read responses are aggregated into one or more ACF messages up to the requested read_size", + "text": "IMPLEMENTED (2026-08-14): the genuine architectural limit this requirement's own text previously found -- rcp_mock_endpoint_handler_fn (mock.h) produces exactly ONE *out_response per dispatched request, while TC18 13.7.12.1's own response-aggregation rule can genuinely require SEVERAL response frames for one request whenever the read_size-capped response exceeds one fragment's own max_fragment_payload -- is now closed. New rcp_mock_endpoint_multi_response_handler_fn (mock.h): may write UP TO out_cap response frames into a caller-provided array, setting *out_count to how many it actually wrote. New rcp_mock_server_add_endpoint_multi_response() registers this handler kind for a byte_bus_id -- internally calls the existing, unmodified rcp_mock_server_add_endpoint() with a NULL plain handler to reuse its own slot-allocation logic, then patches the slot's own new multi_handler field, rather than duplicating allocation logic. New rcp_mock_server_dispatch_multi_response() is a self-contained dispatch entry point, NOT threaded through dispatch_plain_inner()'s own conditional-request admission machinery: a multi-response handler is, by construction, a synchronous read/report operation (TC18's own rule is about ONE request producing SEVERAL frames, not about deferring when that request runs), so this entry point does lifecycle admission and slot lookup (matching every other dispatch entry point's own DROPPED/REJECTED/ERR_UNKNOWN_BUS outcomes exactly) then calls the slot's own multi-response handler directly -- no queued/pending outcome exists for it. Every response passes through the same suppress_response_per_stream_cfg() discovery-stream suppression rule every other dispatch entry point's own response already does. This is a NEW, additional pair of entry points, not a signature change to rcp_mock_server_add_endpoint()/rcp_mock_server_dispatch() -- neither existing function nor any of their own callers is affected. test_tc18_gaps_ep2.c's own iseled_dispatch_multi_handler()/test_iseled_dispatch_multi_fragment_response_round_trips() demonstrate the genuinely-multi-fragment case end-to-end for the first time: chip_data (8 octets) capped by a deliberately small max_fragment_payload (3 octets) forces 3 real fragments, delivered through a real rcp_mock_server_dispatch_multi_response() call and reassembled back to the exact original data, in order -- not just the fragmentation primitives tested in isolation (test_ep_iseled.c) or the artificially-single-fragment case the prior batch's own iseled_dispatch_handler() was limited to. Mutation-tested (the multi_handler invocation gate, and the registration's own multi_handler assignment), both caught cleanly. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit): the companion frame-count calculation this id previously also asserted (rcp_ep_iseled_response_fragment_count()) is now its own id, REQ-ISELED-040. This id continues to cover rcp_ep_iseled_encode_response_fragmented() and the multi-response mock-dispatch capability described above.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.1, TC18.txt L5910-5912" + }, + { + "id": "REQ-ISELED-026", + "title": "iseled_collect_resp, the multi-response-per-ACF collection enable", + "text": "IMPLEMENTED (FIXED 2026-08-11, c-RCP-AUDIT-06, issue #256 Group I): c-RCP now models iseled_collect_resp (TC18 §13.7.12.2 Table 58, corrected relative address 0x0009 bit 3, 1 bit, R/W -- see REQ-ISELED-029's own note on the table's address-collision editorial defect) as rcp_ep_iseled_functional_cfg_t.collect_resp, reachable via the generic evt[2:0]=111b register-block mechanism (rcp_ep_iseled_apply_reconfig()/_render_registers()) rather than a dedicated set_collect_resp() mutator, matching every other register-block field this codebase models (no individual per-field setters, only the whole-block addressed write). A client can now choose between one ACF message per device response and one message per chain read.", + "standard": "iso26262", + "level": "QM", + "asil": "QM", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.2 Table 58, TC18.txt L5944-5946", + "tc18_master_id": "TC18-13.7.12.2-001" + }, + { + "id": "REQ-ISELED-027", + "title": "iseled_nr_leds is reachable via the generic register-block mechanism", + "text": "IMPLEMENTED (FIXED 2026-08-11, c-RCP-AUDIT-06, issue #256 Group I): c-RCP now models TC18 §13.7.12.2 Table 58's iseled_nr_leds register (0x000A, 16 bit, R/W, rcp_ep_iseled_functional_cfg_t.nr_leds -- corrected relative address, see REQ-ISELED-029's own note on the table's address-collision editorial defect), recording the number of LED devices in the chain and bounding the maximum number of responses to expect from a read, reachable via the generic evt[2:0]=111b register-block mechanism (rcp_ep_iseled_apply_reconfig()/_render_registers()), matching every other register-block field this codebase models. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from a prior single id that also asserted iseled_rcv_timeout's identical reachability, now REQ-ISELED-041. Nothing in this codebase yet consults nr_leds to actually enforce the count bound at runtime -- that remains a distinct, deeper behavior gap (see REQ-ISELED-025).", + "standard": "iso26262", + "level": "QM", + "asil": "QM", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.2 Table 58, TC18.txt L5953-5955", + "tc18_master_id": "TC18-13.7.12.2-001" + }, + { + "id": "REQ-ISELED-029", + "title": "ISELED EP_func register block reachable via evt[2:0]=111b (Table 58, address-collision defect corrected)", + "text": "IMPLEMENTED (FIXED 2026-08-11, c-RCP-AUDIT-06, issue #256 Group I): rcp_ep_iseled_decode_command_request() already correctly rejected evt[2:0]=111b (RCP_EP_ISELED_ERR_BAD_EVT, via acf.h's rcp_acf_evt_row2_is_plain()), but no counterpart implemented that §12.7.1 configuration-write path. TC18 §13.7.12.2 Table 58 has a genuine address-collision editorial defect (visually confirmed, not an extraction artifact, the fourth instance of this class found this audit after ep_pwm.h's/ep_gpio.h's/ep_i2c.h's own): iseled_base_clk (16 bit, R) is printed at relative address 0x0001, one octet after iseled_ep_len, with no reserved octet at 0x0001 the way every other endpoint type's own Table prints one -- colliding with iseled_ep_enable&clr, separately printed at 0x0002. Resolved via the same cross-table structural precedent already established: iseled_base_clk moves to 0x0004-0x0005 (matching the common EP_LEN/reserved/enable&clr/options/base_clk prefix sequence every other endpoint type's Table uses), pushing every field the table lists after it down by three octets in the table's own row order: iseled_ep_status to 0x0006-0x0007, iseled_clk_divider to 0x0008, the bitfield octet (iseled_collect_resp bit 3, iseled_use_rcv_clk bit 4) to 0x0009, iseled_nr_leds to 0x000A-0x000B, iseled_rcv_timeout to 0x000C-0x000D (RCP_EP_ISELED_EP_FUNC_LEN = 0x000E). New rcp_ep_iseled_render_registers()/_apply_reconfig()/_reconfig_strerror()/_encode_reconfig_request() implement the same generic addressed-write mechanism PWM_OUT/GPIO/SPI/I2C/UART/LIN/ADC/PWM_IN already had -- ISELED is now 8 of 11 endpoint types with it. rcp_ep_iseled_functional_cfg_t gains base_clk/ep_status/wire_clk_divider/collect_resp/nr_leds/rcv_timeout; iseled_use_rcv_clk is reused directly for the block's own flags bit (it already names that exact wire bit -- see REQ-ISELED-007); iseled_bit_clk_divider (uint32_t, this module's own outbound-transmission clock choice) is kept deliberately distinct from the new, uint8_t wire_clk_divider carrying the real 0x0008 register, per this audit's established \"don't silently redefine an existing field whose own documented semantics diverge from the real wire register\" rule; iseled_crc_enable (this module's own original, second, independent CRC-8 integrity layer) is deliberately NOT part of this register block -- Table 58 defines no register for it, and it is never rendered onto or parsed from the wire here. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit): rcp_ep_iseled_reconfig_strerror()'s own non-NULL/distinct-per-code contract is now its own id, REQ-ISELED-042, matching the REQ-LINEP-028/029 precedent for the identical register-block-mechanism-vs-its-own-strerror split. This id continues to cover rcp_ep_iseled_render_registers(), rcp_ep_iseled_apply_reconfig(), and rcp_ep_iseled_encode_reconfig_request().", + "standard": "iso26262", + "level": "QM", + "asil": "QM", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.2 Table 58, TC18.txt L5916-5961", + "tc18_master_id": "TC18-13.7.12.2-001" + }, + { + "id": "REQ-ISELED-030", + "title": "rcp_ep_iseled_encode_read_request() carries a plain Instruction/Address payload and read_size as ACF_OP_READ", + "text": "FIXED (2026-08-16, issue #471): rcp_ep_iseled_encode_command_request()/_decode_command_request() modeled only the ACF_OP_WRITE sense of an ISELED command request, with no way to originate or receive the distinct read-eliciting request TC18 §13.7.12.1 describes, and no read_size parameter anywhere in the module -- despite rcp_ep_iseled_encode_response_fragmented() (REQ-ISELED-025) already taking a read_size to cap/fragment a response, with no wire-level way to have actually received that value from an incoming request. rcp_ep_iseled_encode_read_request() shall encode an ACF_ABB frame addressed to byte_bus_id with op RCP_ACF_OP_READ, payload exactly tx_data[0..tx_len) (the plain Instruction/Address content selecting what to read back; no Data octets), and the ACF header's own read_size_or_segment_num field set to read_size (0-4095, RCP_EP_ISELED_MAX_READ_SIZE), returning a zeroed rcp_bytes_t if read_size exceeds that range. The existing write-direction rcp_ep_iseled_encode_command_request()/_decode_command_request() pair is unchanged: it continues to model ACF_OP_WRITE only.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.1 (\"Upon read requests the responses are collected 5/4bit decoded and aggregated into one or multiple ACF [messages] up to the requested read_size\"), TC18.txt L5909-5910; §12.9.1 (\"A response with payload data read from the EP is given, if requested by op=0 (read request)\"), TC18.txt L3604; general ACF byte_message_info rule \"read_size/segment_num -- if op = 0 this is read_size, else segment_num\" (e.g. Table 4), TC18.txt L1235" + }, + { + "id": "REQ-ISELED-031", + "title": "rcp_ep_iseled_decode_read_request() validates and decodes an ACF-level read request", + "text": "FIXED (2026-08-16, issue #471): rcp_ep_iseled_decode_read_request() shall return RCP_EP_ISELED_ERR_SHORT_FRAME for a frame shorter than the ACF_ABB header or its declared payload, RCP_EP_ISELED_ERR_BAD_MSG_TYPE for a non-ACF_ABB frame, RCP_EP_ISELED_ERR_WRONG_BUS for a mismatched byte_bus_id, RCP_EP_ISELED_ERR_WRONG_OP for an op other than RCP_ACF_OP_READ (the mirror image of rcp_ep_iseled_decode_command_request()'s own RCP_ACF_OP_WRITE-only check, REQ-ISELED-022), RCP_EP_ISELED_ERR_BAD_EVT when evt[2:0] is not 0b000, and otherwise RCP_EP_ISELED_OK with the plain Instruction/Address payload, the requested read_size (from the ACF header's own read_size_or_segment_num field), and transaction number all populated. Modeled on ep_i2c.h's rcp_ep_i2c_decode_transfer_request() (a read request may carry addressing payload, unlike ep_uart.h's payload-free read requests).", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.5 Table 33 (evt[2:0]=000b legality), TC18.txt L4069-4116; §13.7.12.3 Figure 41 (iseled request format), TC18.txt L5985-5995", + "tc18_master_id": "TC18-13.7.12.3-001" + }, + { + "id": "REQ-ISELED-032", + "title": "rcp_ep_iseled_symbol_decode() accepts a valid symbol and decodes its nibble", + "text": "rcp_ep_iseled_symbol_decode() shall return true and set *out_nibble to bits [3:0] of the symbol when bit 4 equals the even parity of bits [3:0]. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from REQ-ISELED-002, which now owns only the invalid-parity reject clause.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-ISELED-033", + "title": "rcp_ep_iseled_crc8() returns 0x00 for a zero-length input", + "text": "rcp_ep_iseled_crc8() shall return 0x00 for a zero-length input. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from REQ-ISELED-006.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-ISELED-034", + "title": "rcp_ep_iseled_crc8() is sensitive to its input content", + "text": "rcp_ep_iseled_crc8() shall return a different value for two inputs that differ in content. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from REQ-ISELED-006.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented" + }, + { + "id": "REQ-ISELED-035", + "title": "rcp_ep_iseled_trigger_fires() passes through tx_complete_event for RCP_EP_ISELED_TRIGGER_TX_COMPLETE", + "text": "rcp_ep_iseled_trigger_fires() shall return the value of tx_complete_event for RCP_EP_ISELED_TRIGGER_TX_COMPLETE. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from REQ-ISELED-008.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.1 (\"The ISELED endpoint creates a single trigger event, when transmission of a data packet has been completed on the ISELED network.\"), TC18.txt L5913-5914", + "tc18_master_id": "TC18-13.7.12.1-002" + }, + { + "id": "REQ-ISELED-036", + "title": "rcp_ep_iseled_set_bit_clk_divider() applies an authorized write", + "text": "rcp_ep_iseled_set_bit_clk_divider() shall apply the write and return true when rcp_ep_iseled_functional_cfg_writable() authorizes it for the given state/writer. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from REQ-ISELED-011.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.2 Table 58 (iseled_clk_divider, 0x0006, 8 bit, R/W, \"generate ISELED_CLK time reference should be nominal 2MHz\"), TC18.txt L5940-5942; writability per §12.3.1.3, TC18.txt L2601-2602", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-ISELED-037", + "title": "rcp_ep_iseled_set_use_rcv_clk() applies an authorized write", + "text": "rcp_ep_iseled_set_use_rcv_clk() shall apply the write and return true when rcp_ep_iseled_functional_cfg_writable() authorizes it for the given state/writer. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from REQ-ISELED-012.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.2 Table 58 (iseled_use_rcv_clk, 0x0007.4, R/W, \"Use clock provided by ISELED 1st device instead of FreqSync pattern\"), TC18.txt L5950-5952; writability per §12.3.1.3, TC18.txt L2601-2602", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-ISELED-038", + "title": "rcp_ep_iseled_set_crc_enable() applies an authorized write", + "text": "rcp_ep_iseled_set_crc_enable() shall apply the write and return true when rcp_ep_iseled_functional_cfg_writable() authorizes it for the given state/writer. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from REQ-ISELED-013.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.1 (\"The optional CRC generation as specified in the ISLED standard can be enabled. If so, the CRC will be generated and attached to the write messages to the ISELED network.\"), TC18.txt L5910-5912; writability per §12.3.1.3, TC18.txt L2601-2602", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-ISELED-039", + "title": "rcp_ep_iseled_set_trigger() applies an authorized write", + "text": "rcp_ep_iseled_set_trigger() shall apply the write and return true when rcp_ep_iseled_functional_cfg_writable() authorizes it for the given state/writer. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from REQ-ISELED-014.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.3.1.3, TC18.txt L2601-2602 (W* marker convention) -- governs the write-authorization behavior; the trigger event itself has its own basis at REQ-ISELED-008 (§13.7.12.1, \"The ISELED endpoint creates a single trigger event, when transmission of a data packet has been completed on the ISELED network.\", TC18.txt L5913-5914)", + "tc18_master_id": "TC18-12.3.1.3-005" + }, + { + "id": "REQ-ISELED-040", + "title": "rcp_ep_iseled_response_fragment_count() computes the read_size-capped fragment count", + "text": "rcp_ep_iseled_response_fragment_count(available_len, read_size, max_fragment_payload) shall return the number of response frames rcp_ep_iseled_encode_response_fragmented() will produce for the same arguments, i.e. rcp_fragment_plan_count(min(available_len, read_size), max_fragment_payload). Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from REQ-ISELED-025, which now covers only rcp_ep_iseled_encode_response_fragmented() and the multi-response mock-dispatch capability it enables.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.1, TC18.txt L5910-5912" + }, + { + "id": "REQ-ISELED-041", + "title": "iseled_rcv_timeout is reachable via the generic register-block mechanism", + "text": "IMPLEMENTED (FIXED 2026-08-11, c-RCP-AUDIT-06, issue #256 Group I): c-RCP now models TC18 §13.7.12.2 Table 58's iseled_rcv_timeout register (0x000C, 16 bit, R/W, rcp_ep_iseled_functional_cfg_t.rcv_timeout -- corrected relative address, see REQ-ISELED-029's own note on the table's address-collision editorial defect), giving a receive timeout in ISELED clock tics after which reception ends and the endpoint returns to IDLE, reachable via the generic evt[2:0]=111b register-block mechanism (rcp_ep_iseled_apply_reconfig()/_render_registers()), matching every other register-block field this codebase models. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from REQ-ISELED-027, which now covers only iseled_nr_leds. Nothing in this codebase yet consults rcv_timeout to actually enforce the time bound at runtime -- that remains a distinct, deeper behavior gap (see REQ-ISELED-025).", + "standard": "iso26262", + "level": "QM", + "asil": "QM", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.2 Table 58, TC18.txt L5953-5955", + "tc18_master_id": "TC18-13.7.12.2-001" + }, + { + "id": "REQ-ISELED-042", + "title": "rcp_ep_iseled_reconfig_strerror() never returns NULL and is distinct per code", + "text": "rcp_ep_iseled_reconfig_strerror() shall return a non-NULL, non-empty string for every rcp_ep_iseled_reconfig_errc_t value (including an unknown value), with a distinct string for each defined value. Split 2026-08-18 (issue #533, c-RCP-18 atomicity audit) from REQ-ISELED-029, matching the REQ-LINEP-028/029 precedent for the identical register-block-mechanism-vs-its-own-strerror split.", + "standard": "iso26262", + "level": "QM", + "asil": "QM", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.7.12.2 Table 58, TC18.txt L5916-5961", + "tc18_master_id": "TC18-13.7.12.2-001" } ] } \ No newline at end of file diff --git a/include/rcp/can.hpp b/include/rcp/can.hpp index 781d364..b991a03 100644 --- a/include/rcp/can.hpp +++ b/include/rcp/can.hpp @@ -7,6 +7,39 @@ // fusa:req REQ-CANEP-007 // fusa:req REQ-CANEP-008 // fusa:req REQ-CANEP-009 +// fusa:req REQ-CANEP-010 +// fusa:req REQ-CANEP-011 +// fusa:req REQ-CANEP-012 +// fusa:req REQ-CANEP-013 +// fusa:req REQ-CANEP-014 +// fusa:req REQ-CANEP-015 +// fusa:req REQ-CANEP-016 +// fusa:req REQ-CANEP-017 +// fusa:req REQ-CANEP-018 +// fusa:req REQ-CANEP-019 +// fusa:req REQ-CANEP-020 +// fusa:req REQ-CANEP-021 +// fusa:req REQ-CANEP-022 +// fusa:req REQ-CANEP-023 +// fusa:req REQ-CANEP-024 +// fusa:req REQ-CANEP-025 +// fusa:req REQ-CANEP-026 +// fusa:req REQ-CANEP-027 +// fusa:req REQ-CANEP-028 +// fusa:req REQ-CANEP-029 +// fusa:req REQ-CANEP-030 +// fusa:req REQ-CANEP-031 +// fusa:req REQ-CANEP-032 +// fusa:req REQ-CANEP-033 +// fusa:req REQ-CANEP-034 +// fusa:req REQ-CANEP-035 +// fusa:req REQ-CANEP-036 +// fusa:req REQ-CANEP-037 +// fusa:req REQ-CANEP-038 +// fusa:req REQ-CANEP-039 +// fusa:req REQ-CANEP-040 +// fusa:req REQ-CANEP-041 +// fusa:req REQ-CANEP-042 // CAN controller endpoint (ep_type 0x0B) — the OPEN Alliance TC18 Remote // Control Protocol Specification v0.5.1_RC's Classical/FD/XL frame-format diff --git a/include/rcp/lin.hpp b/include/rcp/lin.hpp index e1acd2a..b1c25f2 100644 --- a/include/rcp/lin.hpp +++ b/include/rcp/lin.hpp @@ -4,6 +4,39 @@ // fusa:req REQ-LINEP-004 // fusa:req REQ-LINEP-005 // fusa:req REQ-LINEP-006 +// fusa:req REQ-LINEP-007 +// fusa:req REQ-LINEP-008 +// fusa:req REQ-LINEP-009 +// fusa:req REQ-LINEP-010 +// fusa:req REQ-LINEP-011 +// fusa:req REQ-LINEP-012 +// fusa:req REQ-LINEP-013 +// fusa:req REQ-LINEP-014 +// fusa:req REQ-LINEP-015 +// fusa:req REQ-LINEP-016 +// fusa:req REQ-LINEP-017 +// fusa:req REQ-LINEP-018 +// fusa:req REQ-LINEP-019 +// fusa:req REQ-LINEP-020 +// fusa:req REQ-LINEP-021 +// fusa:req REQ-LINEP-022 +// fusa:req REQ-LINEP-024 +// fusa:req REQ-LINEP-025 +// fusa:req REQ-LINEP-027 +// fusa:req REQ-LINEP-028 +// fusa:req REQ-LINEP-029 +// fusa:req REQ-LINEP-030 +// fusa:req REQ-LINEP-031 +// fusa:req REQ-LINEP-032 +// fusa:req REQ-LINEP-033 +// fusa:req REQ-LINEP-034 +// fusa:req REQ-LINEP-035 +// fusa:req REQ-LINEP-036 +// fusa:req REQ-LINEP-037 +// fusa:req REQ-LINEP-038 +// fusa:req REQ-LINEP-039 +// REQ-LINEP-023 and REQ-LINEP-026 are retired in c-RCP (near-duplicate ids +// consolidated into REQ-LINEP-030/REQ-LINEP-016 respectively) — not ported. // LIN commander endpoint (ep_type 0x06) — the OPEN Alliance TC18 Remote // Control Protocol Specification v0.5.1_RC's raw-byte-pusher model for LIN: diff --git a/tests/test_can.cpp b/tests/test_can.cpp index c98e5b3..2d06c2f 100644 --- a/tests/test_can.cpp +++ b/tests/test_can.cpp @@ -7,6 +7,39 @@ // fusa:test REQ-CANEP-007 // fusa:test REQ-CANEP-008 // fusa:test REQ-CANEP-009 +// fusa:test REQ-CANEP-010 +// fusa:test REQ-CANEP-011 +// fusa:test REQ-CANEP-012 +// fusa:test REQ-CANEP-013 +// fusa:test REQ-CANEP-014 +// fusa:test REQ-CANEP-015 +// fusa:test REQ-CANEP-016 +// fusa:test REQ-CANEP-017 +// fusa:test REQ-CANEP-018 +// fusa:test REQ-CANEP-019 +// fusa:test REQ-CANEP-020 +// fusa:test REQ-CANEP-021 +// fusa:test REQ-CANEP-022 +// fusa:test REQ-CANEP-023 +// fusa:test REQ-CANEP-024 +// fusa:test REQ-CANEP-025 +// fusa:test REQ-CANEP-026 +// fusa:test REQ-CANEP-027 +// fusa:test REQ-CANEP-028 +// fusa:test REQ-CANEP-029 +// fusa:test REQ-CANEP-030 +// fusa:test REQ-CANEP-031 +// fusa:test REQ-CANEP-032 +// fusa:test REQ-CANEP-033 +// fusa:test REQ-CANEP-034 +// fusa:test REQ-CANEP-035 +// fusa:test REQ-CANEP-036 +// fusa:test REQ-CANEP-037 +// fusa:test REQ-CANEP-038 +// fusa:test REQ-CANEP-039 +// fusa:test REQ-CANEP-040 +// fusa:test REQ-CANEP-041 +// fusa:test REQ-CANEP-042 // Tests for rcp/can.hpp — the CAN controller endpoint type. Ported from // c-RCP's tests/test_ep_can.c (ROADMAP.md "Phase 17", cpp-RCP issue #129, @@ -761,3 +794,305 @@ TEST_CASE("Reassembler correctly reports kErrTooLarge partway through a multi-fr } REQUIRE(saw_too_large); } + +// ── Phase 6 batch 7: closing real test-coverage gaps found while re-deriving +// REQ-CANEP-* from c-RCP (id-collision audit, c-RCP-18-tracker issue #533's +// per-endpoint-type successor). Every function below was already genuinely +// implemented; only the specific branch/edge case exercised here was +// previously untested. + +// ── REQ-CANEP-007: functional-config zero-init (implicit via default member +// initializers — CanFunctionalConfig has no separate free init() function, +// unlike rcp::iseled::iseled_functional_cfg_init(), so this test proves the +// zero-initialized invariant directly instead) ─────────────────────────────── + +TEST_CASE("CanFunctionalConfig default-constructs to an all-zero functional config", + "[can][REQ-CANEP-007]") { + CanFunctionalConfig cfg; + REQUIRE_FALSE(cfg.ep_enable); + REQUIRE_FALSE(cfg.ep_clear_req_storage); + REQUIRE_FALSE(cfg.ep_req_crc_enable); + REQUIRE_FALSE(cfg.ep_response_ts_enable); + REQUIRE_FALSE(cfg.ep_suppress_response); + REQUIRE(cfg.timing.arbitration.prescaler == 0); + REQUIRE(cfg.timing.fd_data.prescaler == 0); + REQUIRE(cfg.timing.xl_data.prescaler == 0); + REQUIRE_FALSE(cfg.delay_comp_enable); + REQUIRE(cfg.delay_comp_offset == 0); + REQUIRE(cfg.exec_delay_clk_divider == 0); + for (const auto& f : cfg.xl_filters) { + REQUIRE(f.id == 0); + REQUIRE(f.mask == 0); + REQUIRE_FALSE(f.enable); + } + REQUIRE(cfg.ep_status == 0); + REQUIRE(cfg.status == 0); + REQUIRE(cfg.fifo_status == 0); + REQUIRE_FALSE(cfg.xl_new_pl_provisioned); +} + +// ── REQ-CANEP-015: strerror-equivalent is non-empty and distinct per code, +// exhaustively over every defined CanErrc value ────────────────────────────── + +TEST_CASE("CanErrc reports a distinct, non-empty message for every defined code, and a non-null " + "message for an undefined one", + "[can][REQ-CANEP-015]") { + const CanErrc codes[] = { + CanErrc::identifier_out_of_range, + CanErrc::payload_exceeds_format_limit, + CanErrc::xl_payload_exceeds_single_avtpdu_bound, + CanErrc::config_write_not_supported, + CanErrc::bad_frame_format, + CanErrc::short_frame, + CanErrc::bad_msg_type, + CanErrc::wrong_bus, + CanErrc::wrong_op, + CanErrc::bad_evt, + CanErrc::bad_arbitration_id, + }; + std::vector seen; + for (auto c : codes) { + auto ec = make_error_code(c); + REQUIRE(ec.category() == can_category()); + REQUIRE_FALSE(ec.message().empty()); + for (const auto& s : seen) REQUIRE(s != ec.message()); + seen.push_back(ec.message()); + } + REQUIRE_FALSE(make_error_code(static_cast(999)).message().empty()); +} + +// ── REQ-CANEP-017: decode_frame_request's full reject taxonomy — wrong_bus/ +// short_frame/wrong_op/bad_evt already covered above; this closes +// bad_msg_type/bad_frame_format/bad_arbitration_id ─────────────────────────── + +TEST_CASE("decode_frame_request reports bad_msg_type for a non-ACF_ABB frame", "[can][REQ-CANEP-017]") { + rcp::acf::AcfMessageInfo hdr; + hdr.byte_bus_id = 7; + hdr.op = true; + auto frame = rcp::acf::encode_acf_gbb(hdr, 0, {0x01, 0x02}); + + FrameFormat out_fmt{}; + uint32_t out_id = 0; + XlHeader out_xl{}; + std::vector out_tx; + uint8_t out_txn = 0; + REQUIRE(decode_frame_request(frame.data(), frame.size(), 7, out_fmt, out_id, out_xl, out_tx, out_txn) == + make_error_code(CanErrc::bad_msg_type)); +} + +TEST_CASE("decode_frame_request reports bad_frame_format for a leading-quadlet value of 6 or 7", + "[can][REQ-CANEP-017]") { + auto frame = encode_frame_request(7, FrameFormat::Cbff, 0x1, std::nullopt, {0xAA}, 1); + REQUIRE(frame.size() >= rcp::acf::kAcfCommonHeaderLen + 1); + // Force the leading quadlet's top 3 bits (payload byte 0's top 3 bits) to + // 110b (6), an unassigned FrameFormat code. + frame[rcp::acf::kAcfCommonHeaderLen] = + static_cast((frame[rcp::acf::kAcfCommonHeaderLen] & 0x1F) | 0xC0); + + FrameFormat out_fmt{}; + uint32_t out_id = 0; + XlHeader out_xl{}; + std::vector out_tx; + uint8_t out_txn = 0; + REQUIRE(decode_frame_request(frame.data(), frame.size(), 7, out_fmt, out_id, out_xl, out_tx, out_txn) == + make_error_code(CanErrc::bad_frame_format)); +} + +TEST_CASE("decode_frame_request reports bad_arbitration_id when the id exceeds its format's own " + "width", + "[can][REQ-CANEP-017]") { + auto frame = encode_frame_request(7, FrameFormat::Cbff, 0x1, std::nullopt, {0xAA}, 1); + // Leading quadlet's byte 1 (payload offset 1) carries id bits [23:16] — + // for a valid Base11 (Cbff) id these are always 0; forcing one high + // makes the id exceed 0x7FF while frame_format (top 3 bits of byte 0) + // stays untouched (still Cbff). + frame[rcp::acf::kAcfCommonHeaderLen + 1] |= 0x01; + + FrameFormat out_fmt{}; + uint32_t out_id = 0; + XlHeader out_xl{}; + std::vector out_tx; + uint8_t out_txn = 0; + REQUIRE(decode_frame_request(frame.data(), frame.size(), 7, out_fmt, out_id, out_xl, out_tx, out_txn) == + make_error_code(CanErrc::bad_arbitration_id)); +} + +// ── REQ-CANEP-020: decode_frame_response's full reject taxonomy (previously +// only its round-trip success paths were tested) ───────────────────────────── + +TEST_CASE("decode_frame_response reports short_frame/wrong_bus/bad_evt/bad_msg_type/" + "bad_frame_format/bad_arbitration_id", + "[can][REQ-CANEP-020]") { + std::vector rx{0x01}; + auto frame = encode_frame_response(7, FrameFormat::Cbff, 0x1, std::nullopt, rx, 1, false, 0); + + FrameFormat out_fmt{}; + uint32_t out_id = 0; + XlHeader out_xl{}; + std::vector out_rx; + bool out_timed = false; + uint64_t out_ts = 0; + uint8_t out_txn = 0; + + std::vector empty; + REQUIRE(decode_frame_response(empty.data(), empty.size(), 7, out_fmt, out_id, out_xl, out_rx, + out_timed, out_ts, out_txn) == make_error_code(CanErrc::short_frame)); + + REQUIRE(decode_frame_response(frame.data(), frame.size(), 8, out_fmt, out_id, out_xl, out_rx, + out_timed, out_ts, out_txn) == make_error_code(CanErrc::wrong_bus)); + + std::vector forced_evt = frame; + forced_evt[4] |= 0x10; // evt[2:0] -> 001b, Reserved + REQUIRE(decode_frame_response(forced_evt.data(), forced_evt.size(), 7, out_fmt, out_id, out_xl, + out_rx, out_timed, out_ts, out_txn) == make_error_code(CanErrc::bad_evt)); + + // bad_msg_type: an acf_msg_type that is neither ACF_ABB (0x0E) nor + // ACF_GBB (0x0D) — peek_msg_type() reports it, decode_frame_response() + // falls into its ABB-decode branch (any non-GBB type takes that path), + // and decode_acf_abb()'s own internal type check rejects it. + std::vector wrong_type(rcp::acf::kAcfCommonHeaderLen, 0x00); + REQUIRE(decode_frame_response(wrong_type.data(), wrong_type.size(), 7, out_fmt, out_id, out_xl, + out_rx, out_timed, out_ts, out_txn) == make_error_code(CanErrc::bad_msg_type)); + + std::vector forced_format = frame; + forced_format[rcp::acf::kAcfCommonHeaderLen] = + static_cast((forced_format[rcp::acf::kAcfCommonHeaderLen] & 0x1F) | 0xE0); // format=7 + REQUIRE(decode_frame_response(forced_format.data(), forced_format.size(), 7, out_fmt, out_id, out_xl, + out_rx, out_timed, out_ts, out_txn) == + make_error_code(CanErrc::bad_frame_format)); + + std::vector forced_id = frame; + forced_id[rcp::acf::kAcfCommonHeaderLen + 1] |= 0x01; // id bit above Base11's 0x7FF ceiling + REQUIRE(decode_frame_response(forced_id.data(), forced_id.size(), 7, out_fmt, out_id, out_xl, out_rx, + out_timed, out_ts, out_txn) == make_error_code(CanErrc::bad_arbitration_id)); +} + +// ── REQ-CANEP-026: decode_frame_response_fragment validates byte_bus_id/ +// message type/evt the same way the unfragmented response decoder does +// (frame_format/arbitration_id are deliberately NOT its job — see +// REQ-CANEP-027) ─────────────────────────────────────────────────────────── + +TEST_CASE("decode_frame_response_fragment reports short_frame/wrong_bus/bad_evt/bad_msg_type", + "[can][REQ-CANEP-026]") { + std::vector rx(300, 0x11); + auto frames = encode_frame_response_fragmented(7, FrameFormat::XlClassicalPl, 0x1, + XlHeader{0, 0, 0}, rx, 1, false, 0, 64); + REQUIRE(frames.size() > 1); + + bool ms; + uint16_t segment_num; + std::vector payload; + bool timed; + uint64_t timestamp; + uint8_t txn; + + std::vector empty; + REQUIRE(decode_frame_response_fragment(empty.data(), empty.size(), 7, ms, segment_num, payload, + timed, timestamp, txn) == make_error_code(CanErrc::short_frame)); + + REQUIRE(decode_frame_response_fragment(frames[0].data(), frames[0].size(), 8, ms, segment_num, + payload, timed, timestamp, txn) == + make_error_code(CanErrc::wrong_bus)); + + std::vector forced_evt = frames[0]; + forced_evt[4] |= 0x10; + REQUIRE(decode_frame_response_fragment(forced_evt.data(), forced_evt.size(), 7, ms, segment_num, + payload, timed, timestamp, txn) == + make_error_code(CanErrc::bad_evt)); + + // bad_msg_type: an acf_msg_type that is neither ACF_ABB nor ACF_GBB — see + // the identical construction in decode_frame_response's own reject- + // taxonomy test above for why this is the only reachable way to trigger + // it (a well-formed ACF_GBB frame always agrees with itself). + std::vector wrong_type(rcp::acf::kAcfCommonHeaderLen, 0x00); + REQUIRE(decode_frame_response_fragment(wrong_type.data(), wrong_type.size(), 7, ms, segment_num, + payload, timed, timestamp, txn) == + make_error_code(CanErrc::bad_msg_type)); +} + +// ── REQ-CANEP-027: decode_reassembled_frame_response's reject taxonomy +// (short_frame/bad_frame_format/bad_arbitration_id — success-path recovery +// is REQ-CANEP-036, already covered by the worst-case round-trip tests) ───── + +TEST_CASE("decode_reassembled_frame_response reports short_frame/bad_frame_format/" + "bad_arbitration_id", + "[can][REQ-CANEP-027]") { + FrameFormat out_fmt{}; + uint32_t out_id = 0; + XlHeader out_xl{}; + std::vector out_data; + + std::vector too_short{0x00, 0x00, 0x00}; + REQUIRE(decode_reassembled_frame_response(too_short.data(), too_short.size(), out_fmt, out_id, out_xl, + out_data) == make_error_code(CanErrc::short_frame)); + + std::vector bad_format{0xE0, 0x00, 0x00, 0x01}; // top 3 bits = 111b (7), unassigned + REQUIRE(decode_reassembled_frame_response(bad_format.data(), bad_format.size(), out_fmt, out_id, + out_xl, out_data) == make_error_code(CanErrc::bad_frame_format)); + + std::vector bad_id{0x00, 0x01, 0x00, 0x00}; // Cbff (format=0) with id bit 16 set (>0x7FF) + REQUIRE(decode_reassembled_frame_response(bad_id.data(), bad_id.size(), out_fmt, out_id, out_xl, + out_data) == make_error_code(CanErrc::bad_arbitration_id)); +} + +// ── REQ-CANEP-032: the CAN endpoint does not support sending remote frames — +// CanDataFrame carries no remote-frame flag and this module's own encode +// functions produce only data frames ───────────────────────────────────────── + +TEST_CASE("CanDataFrame carries no remote-frame concept — every encoded frame is a data frame", + "[can][REQ-CANEP-032]") { + // There is no remote-frame flag on CanDataFrame, no separate + // encode_remote_frame_request() overload, and no decode outcome + // distinguishing a remote frame from a data frame anywhere in this + // module — that absence, not a runtime check, is this requirement's own + // claim. This round trip demonstrates the only frame shape this module + // can produce or consume. + std::vector tx{0xAA}; + auto frame = encode_frame_request(7, FrameFormat::Cbff, 0x1, std::nullopt, tx, 1); + REQUIRE_FALSE(frame.empty()); + FrameFormat out_fmt{}; + uint32_t out_id = 0; + XlHeader out_xl{}; + std::vector out_tx; + uint8_t out_txn = 0; + REQUIRE_FALSE(decode_frame_request(frame.data(), frame.size(), 7, out_fmt, out_id, out_xl, out_tx, out_txn)); + REQUIRE(out_tx == tx); // a plain data payload, never a remote-frame marker +} + +// ── REQ-CANEP-037: can_ep_enable&clr's ep_clear_req_storage bit is wire bit +// 4 (0x10), matching TC18 Table 35 and every sibling endpoint — a dedicated +// byte-literal regression test, since a round-trip test alone cannot catch +// this class of defect (render and parse would agree on a wrong bit too) ──── + +TEST_CASE("render_registers places ep_clear_req_storage at wire bit 4 (0x10), not bit 1", + "[can][REQ-CANEP-037]") { + CanFunctionalConfig cfg; + cfg.ep_clear_req_storage = true; + + std::array out{}; + render_registers(cfg, out); + + REQUIRE(out[kRegEpEnableClr] == 0x10); +} + +// ── REQ-CANEP-040: an 11-bit CAN identifier is right-aligned within the CAN +// ID field — explicit byte-level assertion (a round trip alone cannot rule +// out an unexpected shift that still happens to round-trip) ───────────────── + +TEST_CASE("An 11-bit arbitration id is right-aligned in the leading quadlet's low bits", + "[can][REQ-CANEP-040]") { + auto frame = encode_frame_request(7, FrameFormat::Cbff, 0x7FF, std::nullopt, {}, 1); + REQUIRE(frame.size() >= rcp::acf::kAcfCommonHeaderLen + 4); + // Leading quadlet, big-endian: byte0's top 3 bits are frame_format + // (Cbff = 0); the remaining 29 bits (byte0's low 5 bits, then byte1, + // byte2, byte3) are arbitration_id. For id=0x7FF (11 bits, all set), + // only byte2's low 3 bits and all of byte3 may be nonzero — every bit + // above bit 10 (including all of byte0's low 5 bits and all of byte1) + // must be clear, proving the id is right-aligned rather than shifted. + const uint8_t* p = &frame[rcp::acf::kAcfCommonHeaderLen]; + REQUIRE((p[0] & 0xE0) == 0x00); // format bits (Cbff = 0) + REQUIRE(p[0] == 0x00); // id bits [28:24]: clear + REQUIRE(p[1] == 0x00); // id bits [23:16]: clear + REQUIRE(p[2] == 0x07); // id bits [15:8], only [10:8] set = 0b111 + REQUIRE(p[3] == 0xFF); // id bits [7:0], right-aligned +} diff --git a/tests/test_lin.cpp b/tests/test_lin.cpp index 64b0a75..35b0dd6 100644 --- a/tests/test_lin.cpp +++ b/tests/test_lin.cpp @@ -4,6 +4,37 @@ // fusa:test REQ-LINEP-004 // fusa:test REQ-LINEP-005 // fusa:test REQ-LINEP-006 +// fusa:test REQ-LINEP-007 +// fusa:test REQ-LINEP-008 +// fusa:test REQ-LINEP-009 +// fusa:test REQ-LINEP-010 +// fusa:test REQ-LINEP-011 +// fusa:test REQ-LINEP-012 +// fusa:test REQ-LINEP-013 +// fusa:test REQ-LINEP-014 +// fusa:test REQ-LINEP-015 +// fusa:test REQ-LINEP-016 +// fusa:test REQ-LINEP-017 +// fusa:test REQ-LINEP-018 +// fusa:test REQ-LINEP-019 +// fusa:test REQ-LINEP-020 +// fusa:test REQ-LINEP-021 +// fusa:test REQ-LINEP-022 +// fusa:test REQ-LINEP-024 +// fusa:test REQ-LINEP-025 +// fusa:test REQ-LINEP-027 +// fusa:test REQ-LINEP-028 +// fusa:test REQ-LINEP-029 +// fusa:test REQ-LINEP-030 +// fusa:test REQ-LINEP-031 +// fusa:test REQ-LINEP-032 +// fusa:test REQ-LINEP-033 +// fusa:test REQ-LINEP-034 +// fusa:test REQ-LINEP-035 +// fusa:test REQ-LINEP-036 +// fusa:test REQ-LINEP-037 +// fusa:test REQ-LINEP-038 +// fusa:test REQ-LINEP-039 // Tests for rcp/lin.hpp — the LIN commander endpoint type. Ported from // c-RCP's tests/test_ep_lin.c (ROADMAP.md "Phase 17", cpp-RCP issue #129, @@ -383,3 +414,73 @@ TEST_CASE("encode_reconfig_request builds a WRITE frame with evt[2:0]=111b", "[l TEST_CASE("encode_reconfig_request returns empty for empty data", "[lin][REQ-LINEP-006]") { REQUIRE(encode_reconfig_request(6, 0, {}, 1).empty()); } + +// ── Phase 6 batch 7: closing real test-coverage gaps found while re-deriving +// REQ-LINEP-* from c-RCP (id-collision audit, c-RCP-18-tracker issue #533's +// per-endpoint-type successor). Every function below was already genuinely +// implemented; only the specific branch/edge case exercised here was +// previously untested. + +// ── REQ-LINEP-015: strerror-equivalent is non-empty and distinct per code, +// exhaustively over every defined LinErrc value ────────────────────────────── + +TEST_CASE("LinErrc reports a distinct, non-empty message for every defined code, and a non-null " + "message for an undefined one", + "[lin][REQ-LINEP-015]") { + const LinErrc codes[] = { + LinErrc::no_response, LinErrc::config_write_not_supported, LinErrc::short_frame, + LinErrc::bad_msg_type, LinErrc::wrong_bus, LinErrc::wrong_op, LinErrc::bad_evt, + }; + std::vector seen; + for (auto c : codes) { + auto ec = make_error_code(c); + REQUIRE(ec.category() == lin_category()); + REQUIRE_FALSE(ec.message().empty()); + for (const auto& s : seen) REQUIRE(s != ec.message()); + seen.push_back(ec.message()); + } + REQUIRE_FALSE(make_error_code(static_cast(999)).message().empty()); +} + +// ── REQ-LINEP-027/031: decode_command_request rejects evt[2:0] != 000b and +// a non-ACF_ABB message — previously untested despite being named in the +// pre-existing test's own title ────────────────────────────────────────────── + +TEST_CASE("decode_command_request rejects a nonzero evt[2:0] with bad_evt", "[lin][REQ-LINEP-027]") { + rcp::acf::AcfMessageInfo hdr; + hdr.byte_bus_id = 6; + hdr.op = false; // read direction, matching encode_command_request() + hdr.evt_op = 0x2; + auto frame = rcp::acf::encode_acf_abb(hdr, {0xAA}); + + std::vector out_tx; + uint8_t out_txn = 0; + REQUIRE(decode_command_request(frame.data(), frame.size(), 6, out_tx, out_txn) == + make_error_code(LinErrc::bad_evt)); +} + +TEST_CASE("decode_command_request rejects a non-ACF_ABB frame with bad_msg_type", + "[lin][REQ-LINEP-031]") { + rcp::acf::AcfMessageInfo hdr; + hdr.byte_bus_id = 6; + hdr.op = false; + auto frame = rcp::acf::encode_acf_gbb(hdr, 0, {0xAA}); + + std::vector out_tx; + uint8_t out_txn = 0; + REQUIRE(decode_command_request(frame.data(), frame.size(), 6, out_tx, out_txn) == + make_error_code(LinErrc::bad_msg_type)); +} + +// ── REQ-LINEP-039: reconfig_strerror-equivalent never returns an empty +// message, including for an unrecognized code ──────────────────────────────── + +TEST_CASE("LinReconfigErrc reports a distinct, non-empty message per code", "[lin][REQ-LINEP-039]") { + auto short_ec = make_error_code(LinReconfigErrc::short_payload); + auto range_ec = make_error_code(LinReconfigErrc::out_of_range); + REQUIRE(short_ec.category() == lin_reconfig_category()); + REQUIRE_FALSE(short_ec.message().empty()); + REQUIRE_FALSE(range_ec.message().empty()); + REQUIRE(short_ec.message() != range_ec.message()); + REQUIRE_FALSE(make_error_code(static_cast(999)).message().empty()); +}