ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_cnecc.c
Go to the documentation of this file.
1
61
62#include "ra8_cnecc.h"
63
64#include <stdint.h>
65
66#include "ra8_attributes.h"
67#include "ra8_check.h"
68#include "ra8_cnecc_regs.h"
69#include "ra8_elc_regs.h"
70#include "ra8_err.h"
71#include "ra8_isr.h"
72#include "ra8_log.h"
73#include "ra8_mstp.h"
74
80static const char* s_tag = "CNECC";
81
95
109
116
121static void* s_cnecc_ctx;
122
128
134
140
153
159
165
171
176typedef enum : uint16_t {
179
203{
204 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register", p 2868
205 * Step 2: program IRQ enables + correction permission. The
206 * ``EC1ECP`` bit is "correction NOT executed" when SET, so the
207 * caller's "correct_1bit = true" maps to bit cleared. */
208 uint32_t ctl = 0U;
209 if (cfg->irq_1bit) {
211 }
212 if (cfg->irq_2bit) {
214 }
215 if (!cfg->correct_1bit) {
217 }
218
219 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register" EMCA notes,
220 * p 2870: writes to ``ECERVF`` are ignored unless ``EMCA[1:0]``
221 * is ``01b`` in the same write. Combine the unlock pattern with
222 * the ECERVF bit (or omit it for "configured but not running"). */
224 if (cfg->enable) {
226 }
227 return ctl;
228}
229
256{
257 /* HUM Ch 11.2.8 "MSTPCRC : Module Stop Control Register C", p 447 */
258 const ra8_err_t mst_err = ra8_mstp_enable(s_cnecc_mstp_table[instance]);
259 RA8_RETURN_ON_ERROR(mst_err, s_tag, "cnecc_init: mstp enable");
260
261 volatile r_cnecc_regs_t* reg = ra8_cnecc(instance);
262 if (reg == nullptr) { /* GCOVR_EXCL_BR_LINE -- bounded by caller. */
263 return k_ra8_err_hw_init_failed; /* GCOVR_EXCL_LINE -- MSTP HW readback */
264 }
265
266 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register", p 2868
267 * Step 1: clear any latched 1-bit / 2-bit / overflow / address
268 * flags before turning judgment on, so we start from a clean
269 * slate (HUM 42.2.1 p 2870 ECER1C/ECER2C clearing notes). */
271
272 const uint32_t ctl = internal_cnecc_ctl_value(cfg);
273
274 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register", p 2868 */
275 reg->EC710CTL = ctl;
276
277 /* Always leave fault-injection mode disabled at init -- the test
278 * register defaults to 0 anyway, but a previous test run could
279 * have left ECTMCE set (HUM Ch 42.2.2 "EC710TMC : ECC Test Mode
280 * Control Register", p 2871). */
282
283 s_cnecc_one_bit_count[instance] = 0U;
284 s_cnecc_two_bit_count[instance] = 0U;
285 s_cnecc_overflow_count[instance] = 0U;
286 if (s_cnecc_bbr_mirror[instance] != nullptr) {
287 s_cnecc_bbr_mirror[instance]->one_bit_count = 0U;
288 s_cnecc_bbr_mirror[instance]->two_bit_count = 0U;
289 s_cnecc_bbr_mirror[instance]->overflow_count = 0U;
290 }
291 return k_ra8_ok;
292}
293
320static void internal_ctl_rmw(volatile r_cnecc_regs_t* reg, uint32_t new_bits, uint32_t mask)
321{
322 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register", p 2868 */
323 uint32_t live = reg->EC710CTL;
324 /* Drop status / W1C bits (we never want to accidentally clear them
325 * on a config rewrite -- callers go through ra8_cnecc_clear_status
326 * for that). */
328 /* Drop the W1C clear bits -- they read 0 but writing 1 latches a
329 * clear; we want this RMW to be neutral on the status flags. */
331 /* Replace just the requested bits. */
332 live = ((live & ~mask) | (new_bits & mask));
333 /* Always re-assert the EMCA = 01b unlock pattern (HUM 42.2.1 p 2870
334 * EMCA notes). */
336 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register", p 2868 */
337 reg->EC710CTL = live;
338}
339
341{
342 RA8_CHECK_NULL_PTR(cfg, s_tag, "cfg must not be nullptr");
343
344 for (uint8_t i = 0U; i < (uint8_t)k_ra8_cnecc_instance_count; ++i) {
345 const ra8_err_t err = internal_apply_instance(i, &cfg->instances[i]);
346 RA8_RETURN_ON_ERROR(err, s_tag, "cnecc_init apply");
347 }
348 s_cnecc_cached_cfg = *cfg;
349 s_cnecc_initialized = true;
350 ra8_log_info(s_tag, "cnecc_init");
351 return k_ra8_ok;
352}
353
354[[nodiscard]] ra8_err_t ra8_cnecc_deinit(void)
355{
356 /* If the ISR was attached, drop those slots first so they don't
357 * fire after the MSTP gate closes. */
359 (void)ra8_cnecc_detach_isr();
360 }
361
362 for (uint8_t i = 0U; i < (uint8_t)k_ra8_cnecc_instance_count; ++i) {
363 volatile r_cnecc_regs_t* reg = ra8_cnecc(i);
364 if (reg != nullptr) {
365 /* HUM Ch 42.2.2 "EC710TMC : ECC Test Mode Control Register", p 2871
366 * Always leave test mode off when tearing down. */
368 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register" EMCA notes,
369 * p 2870: clearing ``ECERVF`` also requires the EMCA = 01b
370 * unlock pattern in the same write. */
372 }
373 /* HUM Ch 11.2.8 "MSTPCRC : Module Stop Control Register C", p 447 */
375 s_cnecc_one_bit_count[i] = 0U;
376 s_cnecc_two_bit_count[i] = 0U;
378 }
379 s_cnecc_initialized = false;
380 return k_ra8_ok;
381}
382
383[[nodiscard]] ra8_err_t ra8_cnecc_enable_instance(uint8_t instance)
384{
385 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
387 }
388 volatile r_cnecc_regs_t* reg = ra8_cnecc(instance);
389 RA8_CHECK_NULL_PTR(reg, s_tag, "instance mapping failed");
390 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register", p 2870 */
392 return k_ra8_ok;
393}
394
395[[nodiscard]] ra8_err_t ra8_cnecc_disable_instance(uint8_t instance)
396{
397 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
399 }
400 volatile r_cnecc_regs_t* reg = ra8_cnecc(instance);
401 RA8_CHECK_NULL_PTR(reg, s_tag, "instance mapping failed");
402 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register", p 2870 */
404 return k_ra8_ok;
405}
406
408{
409 for (uint8_t i = 0U; i < (uint8_t)k_ra8_cnecc_instance_count; ++i) {
410 volatile r_cnecc_regs_t* reg = ra8_cnecc(i);
411 if (reg == nullptr) {
412 continue; /* GCOVR_EXCL_LINE -- bounded loop, ra8_cnecc never NULL here. */
413 }
414 /* HUM Ch 42.5.1 "Enter Software Standby Mode", p 2876
415 * Step 1: clear ECC error flags + address. */
417 /* Step 2: clear ECERVF (with EMCA unlock). */
419 }
420 return k_ra8_ok;
421}
422
424{
425 if (!s_cnecc_initialized) {
427 }
428 for (uint8_t i = 0U; i < (uint8_t)k_ra8_cnecc_instance_count; ++i) {
429 /* HUM Ch 42.5.2 "Return from Software Standby Mode", p 2876
430 * Step 1 + 2: replay cached cfg and re-enable judgment. */
431 const ra8_err_t err = internal_apply_instance(i, &s_cnecc_cached_cfg.instances[i]);
432 /* GCOVR_EXCL_BR_START -- internal_apply_instance() error edge on the standby replay path */
433 RA8_RETURN_ON_ERROR(err, s_tag, "cnecc_exit_standby apply");
434 /* GCOVR_EXCL_BR_STOP */
435 }
436 return k_ra8_ok;
437}
438
439[[nodiscard]] ra8_err_t ra8_cnecc_set_irq_enables(uint8_t instance, bool irq_1bit, bool irq_2bit)
440{
441 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
443 }
444 volatile r_cnecc_regs_t* reg = ra8_cnecc(instance);
445 RA8_CHECK_NULL_PTR(reg, s_tag, "instance mapping failed");
446 uint32_t set_bits = 0U;
447 if (irq_1bit) {
448 set_bits |= k_ra8_cnecc_mask_ec1edic;
449 }
450 if (irq_2bit) {
451 set_bits |= k_ra8_cnecc_mask_ec2edic;
452 }
453 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register", p 2870 */
455 /* Refresh cached cfg so a later exit_standby preserves the change. */
456 s_cnecc_cached_cfg.instances[instance].irq_1bit = irq_1bit;
457 s_cnecc_cached_cfg.instances[instance].irq_2bit = irq_2bit;
458 return k_ra8_ok;
459}
460
461[[nodiscard]] ra8_err_t ra8_cnecc_set_correction_permission(uint8_t instance, bool correct_1bit)
462{
463 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
465 }
466 volatile r_cnecc_regs_t* reg = ra8_cnecc(instance);
467 RA8_CHECK_NULL_PTR(reg, s_tag, "instance mapping failed");
468 /* EC1ECP semantics are inverted: bit set means "correction NOT
469 * executed" per HUM Ch 42.2.1 "EC710CTL : ECC Control Register"
470 * EC1ECP, p 2870. */
471 uint32_t set_bits = k_ra8_cnecc_mask_ec1ecp;
472 if (correct_1bit) {
473 set_bits = 0U;
474 }
476 s_cnecc_cached_cfg.instances[instance].correct_1bit = correct_1bit;
477 return k_ra8_ok;
478}
479
480[[nodiscard]] ra8_err_t ra8_cnecc_get_status(uint8_t instance, ra8_cnecc_status_t* out)
481{
482 RA8_CHECK_NULL_PTR(out, s_tag, "out must not be nullptr");
483 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
485 }
486 volatile r_cnecc_regs_t* reg = ra8_cnecc(instance);
487 RA8_CHECK_NULL_PTR(reg, s_tag, "instance mapping failed");
488
489 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register", p 2868 */
490 const uint32_t ctl = reg->EC710CTL;
491 /* HUM Ch 42.2.2 "EC710TMC : ECC Test Mode Control Register", p 2871 */
492 const uint16_t tmc = reg->EC710TMC;
493 /* HUM Ch 42.2.4 "EC710EAD0 : ECC Error Address Register", p 2872 */
494 const uint32_t ead = reg->EC710EAD0;
495
496 out->raw_ctl = ctl;
497 out->raw_tmc = tmc;
498 out->reserved0 = 0U;
499 out->one_bit_count = s_cnecc_one_bit_count[instance];
500 out->two_bit_count = s_cnecc_two_bit_count[instance];
501 out->overflow_count = s_cnecc_overflow_count[instance];
502 out->last_addr = (uint16_t)(ead & k_ra8_cnecc_mask_ecead);
503 out->err_present = ((ctl & k_ra8_cnecc_mask_ecemf) != 0U);
504 out->err_1bit = ((ctl & k_ra8_cnecc_mask_ecer1f) != 0U);
505 out->err_2bit = ((ctl & k_ra8_cnecc_mask_ecer2f) != 0U);
506 out->overflow = ((ctl & k_ra8_cnecc_mask_ecovff) != 0U);
507 out->addr_is_1bit = ((ctl & k_ra8_cnecc_mask_ecsedf0) != 0U);
508 out->addr_is_2bit = ((ctl & k_ra8_cnecc_mask_ecdedf0) != 0U);
509 out->judgment_active = ((ctl & k_ra8_cnecc_mask_ecervf) != 0U);
510 out->correct_enabled = ((ctl & k_ra8_cnecc_mask_ec1ecp) == 0U);
511 out->irq1_enabled = ((ctl & k_ra8_cnecc_mask_ec1edic) != 0U);
512 out->irq2_enabled = ((ctl & k_ra8_cnecc_mask_ec2edic) != 0U);
513 out->test_mode = ((tmc & k_ra8_cnecc_mask_ectmce) != 0U);
514 out->reserved1 = false;
515 return k_ra8_ok;
516}
517
518[[nodiscard]] ra8_err_t ra8_cnecc_get_counters(uint8_t instance, ra8_cnecc_counters_t* out)
519{
520 RA8_CHECK_NULL_PTR(out, s_tag, "out must not be nullptr");
521 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
523 }
524 out->one_bit_count = s_cnecc_one_bit_count[instance];
525 out->two_bit_count = s_cnecc_two_bit_count[instance];
526 out->overflow_count = s_cnecc_overflow_count[instance];
527 return k_ra8_ok;
528}
529
530[[nodiscard]] ra8_err_t ra8_cnecc_reset_counters(uint8_t instance)
531{
532 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
534 }
535 s_cnecc_one_bit_count[instance] = 0U;
536 s_cnecc_two_bit_count[instance] = 0U;
537 s_cnecc_overflow_count[instance] = 0U;
538 if (s_cnecc_bbr_mirror[instance] != nullptr) {
539 s_cnecc_bbr_mirror[instance]->one_bit_count = 0U;
540 s_cnecc_bbr_mirror[instance]->two_bit_count = 0U;
541 s_cnecc_bbr_mirror[instance]->overflow_count = 0U;
542 }
543 return k_ra8_ok;
544}
545
546[[nodiscard]] ra8_err_t ra8_cnecc_set_counter_mirror(uint8_t instance, ra8_cnecc_counters_t* mirror)
547{
548 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
550 }
551 s_cnecc_bbr_mirror[instance] = mirror;
552 if (mirror != nullptr) {
553 /* Seed the mirror with the current driver-local values so the
554 * caller does not see a transient zero on first attach. */
555 mirror->one_bit_count = s_cnecc_one_bit_count[instance];
556 mirror->two_bit_count = s_cnecc_two_bit_count[instance];
557 mirror->overflow_count = s_cnecc_overflow_count[instance];
558 }
559 return k_ra8_ok;
560}
561
562[[nodiscard]] ra8_err_t ra8_cnecc_clear_status(uint8_t instance)
563{
564 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
566 }
567 volatile r_cnecc_regs_t* reg = ra8_cnecc(instance);
568 RA8_CHECK_NULL_PTR(reg, s_tag, "instance mapping failed");
569
570 /* HUM Ch 42.2.1 "EC710CTL : ECC Control Register" ECER1C/ECER2C,
571 * p 2870: write-1 to both bits clears ECER1F, ECER2F, ECOVFF,
572 * ECSEDF0 and ECDEDF0 in one shot, and resets EC710EAD0
573 * (HUM 42.2.4 p 2873 "reset by clearing the status flag"). */
575 return k_ra8_ok;
576}
577
578[[nodiscard]] ra8_err_t ra8_cnecc_inject_fault(uint8_t instance, const ra8_cnecc_inject_t* req)
579{
580 RA8_CHECK_NULL_PTR(req, s_tag, "req must not be nullptr");
581 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
583 }
584 volatile r_cnecc_regs_t* reg = ra8_cnecc(instance);
585 RA8_CHECK_NULL_PTR(reg, s_tag, "instance mapping failed");
586
587 /* HUM Ch 42.3.2 "ECC Decoder Testing", figure 42.2 p 2875.
588 * The procedure literal values come straight from the figure --
589 * 0x8000 / 0x8080 / 0x8082 -- and re-assert the ETMA = 10b unlock
590 * pattern on every write. */
591
592 /* Step 1: disable test mode (also clears ECDCS per HUM Ch 42.2.2
593 * "EC710TMC : ECC Test Mode Control Register" ECDCS notes, p 2872). */
595
596 /* Step 2: write the substitute value into EC710TED. The caller is
597 * responsible for picking a value that differs by 1 or 2 bits
598 * from the genuine MBRAM word at the target address; the
599 * one_bit_flip flag is recorded in the descriptor for traceability
600 * but does not change the register write itself. */
601 /* HUM Ch 42.2.3 "EC710TED : ECC Test Substitute Data Register", p 2872 */
602 reg->EC710TED = req->substitute;
603 (void)req->one_bit_flip; /* Tracked for caller bookkeeping. */
604
605 /* Step 3: enable test mode (ETMA unlock + ECTMCE). */
607
608 /* Step 4: select EC710TED as decoder input (sets ECDCS in addition
609 * to ECTMCE). After the next MBRAM read the decoder will see the
610 * substitute value and either ECER1F or ECER2F will latch
611 * depending on the Hamming distance the caller chose. */
613 return k_ra8_ok;
614}
615
616[[nodiscard]] ra8_err_t ra8_cnecc_test_mode_disable(uint8_t instance)
617{
618 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
620 }
621 volatile r_cnecc_regs_t* reg = ra8_cnecc(instance);
622 RA8_CHECK_NULL_PTR(reg, s_tag, "instance mapping failed");
623 /* HUM Ch 42.2.2 "EC710TMC : ECC Test Mode Control Register", p 2871 */
625 return k_ra8_ok;
626}
627
628[[nodiscard]] ra8_err_t ra8_cnecc_test_mode_active(uint8_t instance, bool* out)
629{
630 RA8_CHECK_NULL_PTR(out, s_tag, "out must not be nullptr");
631 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
633 }
634 volatile r_cnecc_regs_t* reg = ra8_cnecc(instance);
635 RA8_CHECK_NULL_PTR(reg, s_tag, "instance mapping failed");
636 /* HUM Ch 42.2.2 "EC710TMC : ECC Test Mode Control Register", p 2872 */
637 *out = ((reg->EC710TMC & k_ra8_cnecc_mask_ectmce) != 0U);
638 return k_ra8_ok;
639}
640
642{
643 RA8_CHECK_NULL_PTR(fn, s_tag, "fn must not be nullptr");
644 s_cnecc_fn = fn;
645 s_cnecc_ctx = ctx;
646 return k_ra8_ok;
647}
648
649[[nodiscard]] ra8_err_t ra8_cnecc_attach_isr(uint8_t priority)
650{
651 if (priority > k_ra8_isr_prio_max) {
653 }
654 for (uint8_t i = 0U; i < (uint8_t)k_ra8_cnecc_instance_count; ++i) {
655 /* HUM Ch 42.4 "Interrupts", p 2875 */
656 void* const instance_ctx = (void*)(uintptr_t)i;
659 instance_ctx,
660 priority,
661 nullptr);
662 if (err != k_ra8_ok) {
663 /* Roll back any earlier slot we opened. */
664 for (uint8_t j = 0U; j < i; ++j) {
666 }
667 return err;
668 }
669 }
671 return k_ra8_ok;
672}
673
675{
676 for (uint8_t i = 0U; i < (uint8_t)k_ra8_cnecc_instance_count; ++i) {
677 /* HUM Ch 42.4 "Interrupts", p 2875 */
679 }
680 s_cnecc_isr_attached = false;
681 return k_ra8_ok;
682}
683
686{
687 const uint8_t instance = (uint8_t)((uintptr_t)ctx & (uintptr_t)k_ra8_cnecc_isr_ctx_inst_mask);
688 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
689 return; /* GCOVR_EXCL_LINE -- attach packs a valid index. */
690 }
691 volatile r_cnecc_regs_t* reg = ra8_cnecc(instance);
692 if (reg == nullptr) { /* GCOVR_EXCL_BR_LINE -- ISR unit reg fixed non-null */
693 return; /* GCOVR_EXCL_LINE -- ISR unit reg fixed non-null */
694 }
695
696 /* HUM Ch 42.3.1 figure 42.1 p 2874:
697 * - Confirm ECC error flags (ECER1F / ECER2F / ECOVFF).
698 * - Confirm ECC error address flags (ECSEDF0 / ECDEDF0).
699 * - Confirm ECC error address (EC710EAD0).
700 * - Clear ECC error flag (ECER1C = 1, ECER2C = 1). */
701 const uint32_t ctl = reg->EC710CTL;
702 const uint32_t ead = reg->EC710EAD0;
703 const uint16_t addr = (uint16_t)(ead & k_ra8_cnecc_mask_ecead);
704
705 const bool one_bit = (ctl & k_ra8_cnecc_mask_ecer1f) != 0U;
706 const bool two_bit = (ctl & k_ra8_cnecc_mask_ecer2f) != 0U;
707 const bool overflow = (ctl & k_ra8_cnecc_mask_ecovff) != 0U;
708
709 /* Dispatch each kind separately so the host callback sees one
710 * event per latched bit. 2-bit faults take priority for the
711 * "is_2bit" classification when both happen to be set in the
712 * same window. */
713 if (two_bit) {
714 ra8_cnecc_dispatch(instance, true, addr);
715 }
716 if (one_bit) {
717 ra8_cnecc_dispatch(instance, false, addr);
718 }
719 if (overflow) {
721 }
722
723 /* HUM Ch 42.3.1 "ECC Function Setting", p 2874 */
725}
726
728void ra8_cnecc_dispatch(uint8_t instance, bool is_2bit, uint16_t err_addr)
729{
730 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
731 return;
732 }
733 if (is_2bit) {
734 ++s_cnecc_two_bit_count[instance];
735 if (s_cnecc_bbr_mirror[instance] != nullptr) {
736 ++s_cnecc_bbr_mirror[instance]->two_bit_count;
737 }
738 } else {
739 ++s_cnecc_one_bit_count[instance];
740 if (s_cnecc_bbr_mirror[instance] != nullptr) {
741 ++s_cnecc_bbr_mirror[instance]->one_bit_count;
742 }
743 }
745 void* const ctx = s_cnecc_ctx;
746 if (fn != nullptr) {
747 fn(ctx, instance, is_2bit, err_addr);
748 }
749}
750
752void ra8_cnecc_dispatch_overflow(uint8_t instance)
753{
754 if ((uint16_t)instance >= k_ra8_cnecc_instance_count) {
755 return;
756 }
757 ++s_cnecc_overflow_count[instance];
758 if (s_cnecc_bbr_mirror[instance] != nullptr) {
759 ++s_cnecc_bbr_mirror[instance]->overflow_count;
760 }
761}
762
763/* =============================================================================
764 * Code-flash ECC compute / verify (software fallback)
765 *
766 * The CNECC hardware (HUM Ch 42) only describes runtime read-side ECC for
767 * the CANFD MBRAM. The bootloader's anti-rollback and code-flash auditing
768 * paths still want a deterministic ECC tag for arbitrary memory regions
769 * (e.g. the active firmware slot in MRAM). We expose a small CRC32-style
770 * accumulator so callers can keep using the CNECC vocabulary without
771 * minting a separate driver.
772 * =============================================================================
773 */
774
779typedef enum : uint32_t {
780 k_ra8_cnecc_crc_seed = 0xFFFFFFFFUL,
781 k_ra8_cnecc_crc_xorout = 0xFFFFFFFFUL,
782 k_ra8_cnecc_crc_poly = 0xEDB88320UL,
784
794
814static uint32_t internal_crc32(const uint8_t* data, uint32_t bytes)
815{
816 uint32_t crc = (uint32_t)k_ra8_cnecc_crc_seed;
817 for (uint32_t i = 0U; i < bytes; ++i) {
818 crc ^= (uint32_t)data[i];
819 for (uint8_t b = 0U; b < (uint8_t)k_ra8_cnecc_compute_byte_bits; ++b) {
820 const uint32_t mask = (crc & 1UL) != 0UL ? (uint32_t)k_ra8_cnecc_crc_poly : 0UL;
821 crc = (crc >> 1U) ^ mask;
822 }
823 }
824 return crc ^ (uint32_t)k_ra8_cnecc_crc_xorout;
825}
826
827[[nodiscard]] ra8_err_t ra8_cnecc_open(void)
828{
829 /* Default config: every instance enabled with correction + IRQs on.
830 * Mirrors the typical bring-up pattern in apps that don't care about
831 * per-instance tuning. HUM Ch 42.3.1 figure 42.1 p 2874 procedure. */
832 const ra8_cnecc_config_t cfg = {
833 .instances =
834 {
835 {.correct_1bit = true, .irq_1bit = true, .irq_2bit = true, .enable = true},
836 {.correct_1bit = true, .irq_1bit = true, .irq_2bit = true, .enable = true},
837 },
838 };
839 return ra8_cnecc_init(&cfg);
840}
841
842[[nodiscard]] ra8_err_t ra8_cnecc_compute(uint32_t addr, uint32_t len, uint32_t* out_ecc)
843{
844 if (out_ecc == nullptr) {
845 return k_ra8_err_null_ptr;
846 }
847 if (addr == 0U) {
848 return k_ra8_err_null_ptr;
849 }
850 if ((addr % (uint32_t)k_ra8_cnecc_compute_align) != 0U) {
852 }
853 if (len < (uint32_t)k_ra8_cnecc_compute_align) {
855 }
856 /* Round down to the nearest 4-byte boundary so the computation is
857 * deterministic regardless of trailing bytes. */
858 const uint32_t aligned_len = len & ~((uint32_t)k_ra8_cnecc_compute_align - 1U);
859 *out_ecc = internal_crc32((const uint8_t*)(uintptr_t)addr, aligned_len);
860 ra8_log_info_val(s_tag, "compute ecc", *out_ecc);
861 return k_ra8_ok;
862}
863
864[[nodiscard]] ra8_err_t ra8_cnecc_verify(uint32_t addr, uint32_t len, uint32_t expected_ecc)
865{
866 uint32_t got = 0U;
867 const ra8_err_t err = ra8_cnecc_compute(addr, len, &got);
868 RA8_RETURN_ON_ERROR(err, s_tag, "verify: compute failed");
869 if (got != expected_ecc) {
870 ra8_log_error_val(s_tag, "verify mismatch", got);
872 }
873 return k_ra8_ok;
874}
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_ISR_SAFE
The function is callable from interrupt context.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_RETURN_ON_ERROR(err, tag, message)
Early return on error, propagating the code upward.
Definition ra8_check.h:184
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
ra8_err_t ra8_cnecc_attach_handler(ra8_cnecc_error_fn_t fn, void *ctx)
Attach a callback for CNECC fault events.
Definition ra8_cnecc.c:641
static const ra8_mstp_t s_cnecc_mstp_table[k_ra8_cnecc_instance_count]
Per-instance MSTP id used to ungate the parent CANFD block.
Definition ra8_cnecc.c:91
ra8_err_t ra8_cnecc_set_counter_mirror(uint8_t instance, ra8_cnecc_counters_t *mirror)
Attach a BBR-mirrored counter triple for one instance.
Definition ra8_cnecc.c:546
ra8_err_t ra8_cnecc_disable_instance(uint8_t instance)
Disable error judgment for one instance only.
Definition ra8_cnecc.c:395
void ra8_cnecc_dispatch_overflow(uint8_t instance)
Force-bump the overflow counter (used by the ISR trampoline).
Definition ra8_cnecc.c:752
ra8_err_t ra8_cnecc_enter_standby(void)
Software-standby preparation (HUM 42.5.1 p 2876).
Definition ra8_cnecc.c:407
static ra8_cnecc_error_fn_t s_cnecc_fn
Currently registered fault callback.
Definition ra8_cnecc.c:115
ra8_err_t ra8_cnecc_open(void)
One-shot CNECC bring-up using the driver's default config.
Definition ra8_cnecc.c:827
ra8_err_t ra8_cnecc_detach_isr(void)
Tear down the ICU vector wiring established by ra8_cnecc_attach_isr.
Definition ra8_cnecc.c:674
static bool s_cnecc_isr_attached
True when ra8_cnecc_attach_isr has wired the ICU vectors.
Definition ra8_cnecc.c:170
ra8_err_t ra8_cnecc_get_counters(uint8_t instance, ra8_cnecc_counters_t *out)
Read the cumulative fault counters for one instance.
Definition ra8_cnecc.c:518
ra8_err_t ra8_cnecc_inject_fault(uint8_t instance, const ra8_cnecc_inject_t *req)
Run the HUM Ch 42.3.2 fault-injection sequence for one instance.
Definition ra8_cnecc.c:578
ra8_err_t ra8_cnecc_enable_instance(uint8_t instance)
Enable error judgment for one instance only.
Definition ra8_cnecc.c:383
ra8_err_t ra8_cnecc_test_mode_disable(uint8_t instance)
Force EC710TMC = 0x8000 to leave fault-injection mode.
Definition ra8_cnecc.c:616
ra8_err_t ra8_cnecc_init(const ra8_cnecc_config_t *cfg)
Initialise the CNECC driver and configure both instances.
Definition ra8_cnecc.c:340
static uint32_t s_cnecc_overflow_count[k_ra8_cnecc_instance_count]
Cumulative ECOVFF counter, per instance.
Definition ra8_cnecc.c:139
static uint32_t s_cnecc_one_bit_count[k_ra8_cnecc_instance_count]
Cumulative 1-bit fault counter, per instance.
Definition ra8_cnecc.c:127
ra8_err_t ra8_cnecc_set_correction_permission(uint8_t instance, bool correct_1bit)
Update the 1-bit correction permission for one instance.
Definition ra8_cnecc.c:461
static const ra8_elc_event_t s_cnecc_event_table[k_ra8_cnecc_instance_count]
Per-instance ICU event for the shared CANn_MRAM_ERI vector.
Definition ra8_cnecc.c:105
ra8_err_t ra8_cnecc_clear_status(uint8_t instance)
Clear the latched ECC fault state for one instance.
Definition ra8_cnecc.c:562
ra8_err_t ra8_cnecc_set_irq_enables(uint8_t instance, bool irq_1bit, bool irq_2bit)
Update the IRQ enable bits for one instance without touching any other CTL field.
Definition ra8_cnecc.c:439
ra8_cnecc_compute_align_t
Alignment / size constants for the compute path.
Definition ra8_cnecc.c:789
@ k_ra8_cnecc_compute_byte_mask
Byte mask.
Definition ra8_cnecc.c:792
@ k_ra8_cnecc_compute_byte_bits
Bits per byte.
Definition ra8_cnecc.c:791
@ k_ra8_cnecc_compute_align
4-byte alignment for addr / len.
Definition ra8_cnecc.c:790
ra8_cnecc_compute_const_t
Magic numbers used by ra8_cnecc_compute promoted to typed enums.
Definition ra8_cnecc.c:779
@ k_ra8_cnecc_crc_poly
Reflected CRC32 polynomial.
Definition ra8_cnecc.c:782
@ k_ra8_cnecc_crc_seed
Initial CRC32 register value.
Definition ra8_cnecc.c:780
@ k_ra8_cnecc_crc_xorout
Final XOR mask.
Definition ra8_cnecc.c:781
void ra8_cnecc_dispatch(uint8_t instance, bool is_2bit, uint16_t err_addr)
Dispatch a CNECC fault event to the installed handler.
Definition ra8_cnecc.c:728
ra8_cnecc_isr_const_t
Numeric constants for ISR ctx packing.
Definition ra8_cnecc.c:176
@ k_ra8_cnecc_isr_ctx_inst_mask
Low byte of ctx -> instance.
Definition ra8_cnecc.c:177
ra8_err_t ra8_cnecc_reset_counters(uint8_t instance)
Zero the cumulative fault counters for one instance.
Definition ra8_cnecc.c:530
static uint32_t s_cnecc_two_bit_count[k_ra8_cnecc_instance_count]
Cumulative 2-bit fault counter, per instance.
Definition ra8_cnecc.c:133
static ra8_cnecc_config_t s_cnecc_cached_cfg
Last-applied configuration – replayed by exit_standby.
Definition ra8_cnecc.c:158
ra8_err_t ra8_cnecc_test_mode_active(uint8_t instance, bool *out)
Query whether fault injection is currently armed.
Definition ra8_cnecc.c:628
static ra8_err_t internal_apply_instance(uint8_t instance, const ra8_cnecc_instance_cfg_t *cfg)
Apply one instance's configuration.
Definition ra8_cnecc.c:255
static bool s_cnecc_initialized
True between successful init and matching deinit.
Definition ra8_cnecc.c:164
void ra8_cnecc_isr_handler(void *ctx)
Generic ICU trampoline for both CANn_MRAM_ERI vectors.
Definition ra8_cnecc.c:685
static ra8_cnecc_counters_t * s_cnecc_bbr_mirror[k_ra8_cnecc_instance_count]
Optional BBR-mirrored counter pointer, per instance.
Definition ra8_cnecc.c:152
ra8_err_t ra8_cnecc_verify(uint32_t addr, uint32_t len, uint32_t expected_ecc)
Verify a region's ECC tag matches an expected value.
Definition ra8_cnecc.c:864
static void internal_ctl_rmw(volatile r_cnecc_regs_t *reg, uint32_t new_bits, uint32_t mask)
Read-modify-write helper for EC710CTL that preserves the unlock pattern and clears RO/clear-on-write ...
Definition ra8_cnecc.c:320
ra8_err_t ra8_cnecc_deinit(void)
Tear down both CNECC instances and gate the CANFD MSTP bits.
Definition ra8_cnecc.c:354
ra8_err_t ra8_cnecc_attach_isr(uint8_t priority)
Wire the CANn_MRAM_ERI ICU vectors to the driver dispatcher.
Definition ra8_cnecc.c:649
ra8_err_t ra8_cnecc_exit_standby(void)
Software-standby exit (HUM 42.5.2 p 2876).
Definition ra8_cnecc.c:423
static void * s_cnecc_ctx
Opaque context passed to s_cnecc_fn.
Definition ra8_cnecc.c:121
ra8_err_t ra8_cnecc_get_status(uint8_t instance, ra8_cnecc_status_t *out)
Snapshot the current ECC state of one CNECC instance.
Definition ra8_cnecc.c:480
ra8_err_t ra8_cnecc_compute(uint32_t addr, uint32_t len, uint32_t *out_ecc)
Compute a 32-bit ECC code over a (addr, len) memory region.
Definition ra8_cnecc.c:842
static uint32_t internal_cnecc_ctl_value(const ra8_cnecc_instance_cfg_t *cfg)
Compute the EC710CTL value one instance's configuration asks for.
Definition ra8_cnecc.c:202
static uint32_t internal_crc32(const uint8_t *data, uint32_t bytes)
Reflected CRC32 (poly 0xEDB88320) over a byte buffer.
Definition ra8_cnecc.c:814
CANFD ECC (CNECC) HAL driver public API.
void(* ra8_cnecc_error_fn_t)(void *ctx, uint8_t instance, bool is_2bit, uint16_t err_addr)
CANFD ECC fault callback signature.
Definition ra8_cnecc.h:183
void ra8_cnecc_isr_handler(void *ctx)
Generic ICU trampoline for both CANn_MRAM_ERI vectors.
Definition ra8_cnecc.c:685
CANFD ECC (CNECC) register layout for the RA8D2.
@ k_ra8_cnecc_instance_count
ECCMB0 + ECCMB1.
static volatile r_cnecc_regs_t * ra8_cnecc(uint8_t instance)
Get pointer to CNECC instance instance.
@ k_ra8_cnecc_mask_ecead
ECEAD[9:0].
@ k_ra8_cnecc_mask_ectmce
ECTMCE test-mode enable.
@ k_ra8_cnecc_mask_test_subst
Figure 42.2 "Use ECEDB".
@ k_ra8_cnecc_mask_test_enable
Figure 42.2 "Enable test".
@ k_ra8_cnecc_mask_test_disable
Figure 42.2 "Disable test".
@ k_ra8_cnecc_mask_ecovff
ECOVFF overflow.
@ k_ra8_cnecc_mask_ecemf
ECEMF.
@ k_ra8_cnecc_mask_ecer1f
ECER1F.
@ k_ra8_cnecc_mask_ec1ecp
EC1ECP.
@ k_ra8_cnecc_mask_ecer1c
ECER1C (write-1-clear).
@ k_ra8_cnecc_mask_ctl_writable
Bits we may legally write (excludes RO + reserved).
@ k_ra8_cnecc_mask_ecer2c
ECER2C (write-1-clear).
@ k_ra8_cnecc_mask_emca_unlock
EMCA = 01b -> unlock ECERVF.
@ k_ra8_cnecc_mask_clear_all
ECER1C | ECER2C bundle.
@ k_ra8_cnecc_mask_ec2edic
EC2EDIC.
@ k_ra8_cnecc_mask_emca
EMCA[1:0] @ [15:14].
@ k_ra8_cnecc_mask_ecsedf0
1-bit address captured.
@ k_ra8_cnecc_mask_ecervf
ECERVF.
@ k_ra8_cnecc_mask_ecer2f
ECER2F.
@ k_ra8_cnecc_mask_ec1edic
EC1EDIC.
@ k_ra8_cnecc_mask_ecdedf0
2-bit address captured.
@ k_ra8_cnecc_mask_irq_all
EC1EDIC | EC2EDIC.
Event Link Controller (ELC) register layout for the Renesas RA8D2.
ra8_elc_event_t
Partial list of ELC events (populate as drivers need them).
@ k_ra8_elc_event_can1_mram_eri
CANFD1 MRAM ECC error (HUM Ch 19 Table 19.3).
@ k_ra8_elc_event_can0_mram_eri
CANFD0 MRAM ECC error (HUM Ch 19 Table 19.3).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_hw_init_failed
Hardware peripheral failed to initialise.
Definition ra8_err.h:290
@ k_ra8_err_crc_mismatch
CRC mismatch detected on received data.
Definition ra8_err.h:423
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_not_initialized
Module not initialized – _init() not yet called successfully.
Definition ra8_err.h:235
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
NVIC + ICU IELSR allocator.
@ k_ra8_isr_prio_max
Lowest priority.
Definition ra8_isr.h:106
ra8_err_t ra8_isr_register(ra8_elc_event_t event, ra8_isr_handler_t handler, void *ctx, uint8_t priority, uint16_t *out_slot)
Allocate an IELSR slot for an ELC event + handler.
Definition ra8_isr.c:296
ra8_err_t ra8_isr_unregister(ra8_elc_event_t event)
Release a previously-allocated IELSR slot.
Definition ra8_isr.c:335
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_info_val(tag, message, value)
RA8 log info val.
Definition ra8_log.h:366
#define ra8_log_error_val(tag, message, value)
RA8 log error val.
Definition ra8_log.h:337
#define ra8_log_info(tag, message)
RA8 log info.
Definition ra8_log.h:364
Ref-counted Module Stop Control wrapper for the RA8D2.
ra8_err_t ra8_mstp_enable(ra8_mstp_t id)
Reference-counted "ungate this peripheral" request.
Definition ra8_mstp.c:343
ra8_err_t ra8_mstp_disable(ra8_mstp_t id)
Reference-counted "gate this peripheral" request.
Definition ra8_mstp.c:382
ra8_mstp_t
Packed (reg << 8) | bit module-stop identifier.
@ k_ra8_mstp_canfd1
MSTPC26 CANFD1.
@ k_ra8_mstp_canfd0
MSTPC27 CANFD0.
Per-instance CNECC register window (size = 0x14 bytes).
volatile uint32_t EC710TED
+0x0C Test substitute data.
volatile uint32_t EC710EAD0
+0x10 Captured error address (read-only).
volatile uint32_t EC710CTL
+0x00 ECC control / status / IRQ enables.
volatile uint16_t EC710TMC
+0x04 Test-mode control.
Top-level driver configuration.
Definition ra8_cnecc.h:110
ra8_cnecc_instance_cfg_t instances[k_ra8_cnecc_instance_count]
Per-instance settings.
Definition ra8_cnecc.h:111
Cumulative per-instance fault counters.
Definition ra8_cnecc.h:166
uint32_t one_bit_count
Lifetime 1-bit fault count for this instance.
Definition ra8_cnecc.h:167
uint32_t two_bit_count
Lifetime 2-bit fault count for this instance.
Definition ra8_cnecc.h:168
uint32_t overflow_count
Lifetime ECOVFF count for this instance.
Definition ra8_cnecc.h:169
Fault-injection request descriptor.
Definition ra8_cnecc.h:595
uint32_t substitute
32-bit value stored in EC710TED.
Definition ra8_cnecc.h:596
bool one_bit_flip
true => 1-bit error variant; false => 2-bit.
Definition ra8_cnecc.h:597
Per-instance ECC configuration descriptor.
Definition ra8_cnecc.h:93
bool enable
true => leave ECERVF set after init.
Definition ra8_cnecc.h:97
bool irq_2bit
true => raise IRQ on every 2-bit fault.
Definition ra8_cnecc.h:96
bool correct_1bit
true => HW corrects 1-bit faults (clears EC1ECP).
Definition ra8_cnecc.h:94
bool irq_1bit
true => raise IRQ on every 1-bit fault.
Definition ra8_cnecc.h:95
Snapshot of one CNECC instance, returned by ra8_cnecc_get_status.
Definition ra8_cnecc.h:133
uint16_t reserved0
Padding for alignment.
Definition ra8_cnecc.h:136
bool overflow
Live ECOVFF: address-capture overflow.
Definition ra8_cnecc.h:144
bool err_present
Live ECEMF: present-cycle error.
Definition ra8_cnecc.h:141
uint32_t two_bit_count
Cumulative 2-bit fault count.
Definition ra8_cnecc.h:138
bool irq2_enabled
Live EC2EDIC.
Definition ra8_cnecc.h:150
uint32_t raw_ctl
Live EC710CTL value.
Definition ra8_cnecc.h:134
bool addr_is_2bit
true => last_addr was a 2-bit fault.
Definition ra8_cnecc.h:146
bool judgment_active
Live ECERVF: judgment currently enabled.
Definition ra8_cnecc.h:147
uint16_t raw_tmc
Live EC710TMC value.
Definition ra8_cnecc.h:135
bool irq1_enabled
Live EC1EDIC.
Definition ra8_cnecc.h:149
uint16_t last_addr
Last captured ECEAD[9:0] RAM offset.
Definition ra8_cnecc.h:140
bool err_1bit
Live ECER1F: 1-bit fault latched.
Definition ra8_cnecc.h:142
uint32_t one_bit_count
Cumulative 1-bit fault count.
Definition ra8_cnecc.h:137
bool addr_is_1bit
true => last_addr was a 1-bit fault.
Definition ra8_cnecc.h:145
uint32_t overflow_count
Cumulative ECOVFF count.
Definition ra8_cnecc.h:139
bool test_mode
Live ECTMCE: fault-injection currently armed.
Definition ra8_cnecc.h:151
bool err_2bit
Live ECER2F: 2-bit fault latched.
Definition ra8_cnecc.h:143
bool reserved1
Padding.
Definition ra8_cnecc.h:152
bool correct_enabled
true => 1-bit correction enabled (EC1ECP=0).
Definition ra8_cnecc.h:148