Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ports/cortex_r52/gnu/example_build/s32z280_evb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ add_executable(s32z280_boot.elf EXCLUDE_FROM_ALL
${EVB_DIR}/gicv3.c
${EVB_DIR}/irq_dispatch.c
${EVB_DIR}/cache.c
${EVB_DIR}/tcm.c
)

target_include_directories(s32z280_boot.elf PRIVATE ${EVB_DIR})
Expand Down Expand Up @@ -67,6 +68,7 @@ add_executable(s32z280_demo.elf EXCLUDE_FROM_ALL
${EVB_DIR}/irq_dispatch.c
${EVB_DIR}/gic_probe.c
${EVB_DIR}/cache.c
${EVB_DIR}/tcm.c
${EVB_DIR}/demo_s32z280.c
)

Expand Down Expand Up @@ -106,6 +108,7 @@ if(TX_R52_ENABLE_VFP)
${EVB_DIR}/irq_dispatch.c
${EVB_DIR}/gic_probe.c
${EVB_DIR}/cache.c
${EVB_DIR}/tcm.c
${EVB_DIR}/demo_vfp_s32z280.c
)

Expand Down Expand Up @@ -143,6 +146,7 @@ if(TX_R52_ENABLE_IRQ_NESTING)
${EVB_DIR}/irq_dispatch.c
${EVB_DIR}/gic_probe.c
${EVB_DIR}/cache.c
${EVB_DIR}/tcm.c
${EVB_DIR}/demo_nesting_s32z280.c
)

Expand Down Expand Up @@ -180,6 +184,7 @@ if(TX_R52_ENABLE_FIQ_NESTING)
${EVB_DIR}/irq_dispatch.c
${EVB_DIR}/gic_probe.c
${EVB_DIR}/cache.c
${EVB_DIR}/tcm.c
${EVB_DIR}/demo_fiq_s32z280.c
)

Expand Down
52 changes: 52 additions & 0 deletions ports/cortex_r52/gnu/example_build/s32z280_evb/bsp_boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#include "mpu.h"
#include "gicv3.h"
#include "cache.h"
#include "tcm.h"

/* Captured at EL2 by entry.S, before the drop to EL1. */

Expand Down Expand Up @@ -346,6 +347,57 @@ void bsp_main(void)
linflexd_puts("P1 RTU0.GPR CFG_CNTDV via LLPP\n");
report("CFG_CNTDV", *(volatile unsigned int *)S32Z_RTU0_GPR_CFG_CNTDV);

/* --- TCM: what the hardware reports about itself ---------------------
Reads only. platform.h has recorded since bring-up that the TCM windows
fault at reset, and the Cortex-R52 TRM says why: every bit of the region
registers resets to zero apart from SIZE and WAITSTATES, so the banks sit
at base 0 with both enables clear. This confirms that on the part rather
than trusting it, and cross-checks the reported sizes against the S32Z2
reference manual, which documents TCMA 64KB, TCMB 16KB and TCMC 16KB.

Nothing is written here. Enabling TCM also requires initialising the ECC
bits before any read (TRM 6.2.2), with ATCM needing 64-bit aligned stores
where BTCM and CTCM accept 32-bit, so the enable belongs in its own step
once these values are known. */

linflexd_puts("T1 TCM configuration as reported by the core\n");
{
static const char *const bank_name[3] = { "ATCM", "BTCM", "CTCM" };
static const unsigned long expect_bytes[3] = { 0x10000UL, 0x4000UL, 0x4000UL };
unsigned int i;

for (i = 0U; i < 3U; i++)
{
tcm_bank_t bank;

tcm_read_bank(i, &bank);

linflexd_puts(bank_name[i]);
linflexd_puts(" raw=0x");
linflexd_put_hex32((unsigned int) bank.raw);
linflexd_puts(" base=0x");
linflexd_put_hex32((unsigned int) bank.base);
linflexd_puts(" size=0x");
linflexd_put_hex32((unsigned int) bank.size_bytes);
linflexd_puts(" ws=");
linflexd_putc((char) ('0' + (char) bank.wait_states));
linflexd_puts(" en2=");
linflexd_putc((char) ('0' + (char) bank.enable_el2));
linflexd_puts(" en10=");
linflexd_putc((char) ('0' + (char) bank.enable_el10));
linflexd_puts((bank.size_bytes == expect_bytes[i])
? " size matches the RM\n"
: " SIZE DISAGREES WITH THE RM\n");
}

report("MEMPROTCTLR", (unsigned int) tcm_read_memprotctlr());
linflexd_puts("ECC implemented = ");
linflexd_putc((char) ('0' + (char) tcm_ecc_implemented()));
linflexd_puts(", enabled = ");
linflexd_putc((char) ('0' + (char) tcm_ecc_enabled()));
linflexd_putc('\n');
}

/* --- MPU, then the GIC ------------------------------------------------
The GIC needs its region mapped Device nGnRnE (Cortex-R52 TRM), which
needs the MPU on. Markers around the enable: if the console survives it,
Expand Down
26 changes: 21 additions & 5 deletions ports/cortex_r52/gnu/example_build/s32z280_evb/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,27 @@
/* self-booting image loaded from flash would still need the data */
/* alias, since the core's own data side may not write through AXI-F. */
/* */
/* The TCMs (0x30000000, 0x30100000, 0x30200000) are NOT accessible */
/* at reset -- reads fault -- because nothing has programmed the TCM */
/* region registers yet. They are therefore unusable for early boot */
/* and are deliberately absent from this map. The DDR window at */
/* 0x7A000000 is likewise dark until DDR is initialised. */
/* The TCMs need care, and the earlier note here was wrong about why. */
/* Reads of 0x30000000, 0x30100000 and 0x30200000 do fault, but not */
/* because the TCMs are disabled. Reading the region registers on */
/* this part gives: */
/* */
/* ATCM 0x0000011F 64KB, 1 wait state, ENABLED at EL2 and EL1/0 */
/* BTCM 0x00000014 16KB, 0 wait states, disabled */
/* CTCM 0x00000114 16KB, 1 wait state, disabled */
/* */
/* Every BASEADDRESS field is zero, so ATCM is live as 64KB at address */
/* 0x00000000 -- not at 0x30000000. The faulting reads were of an */
/* address the TCM is not at. ATCM's enables reset set because */
/* CFGTCMBOOTx is tied high on this part (Cortex-R52 TRM r1p3 3.3.94). */
/* The sizes and wait states match the S32Z2 reference manual exactly. */
/* */
/* ECC is implemented and enabled: IMP_MEMPROTCTLR reads 0x00000011, */
/* so RAMPROTIMP and RAMPROTEN are both set. TRM 6.2.2 therefore */
/* applies -- a TCM location must be written before it is read, and */
/* ATCM needs 64-bit aligned stores where BTCM and CTCM accept 32-bit. */
/* */
/* The DDR window at 0x7A000000 is dark until DDR is initialised. */
/* */
/**************************************************************************/

Expand Down
21 changes: 16 additions & 5 deletions ports/cortex_r52/gnu/example_build/s32z280_evb/readme_s32z280.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,22 @@ model and not an R52 at all.
Data, bss and the per-mode stacks live in RTU-local data SRAM at
0x31780000 (256 KB); 0x31800000 gives 2 MB if an image outgrows it.

The TCMs at 0x30000000/0x30100000/0x30200000 are NOT accessible at reset --
reads fault -- until their region registers are programmed, so nothing
needed for early boot can live there. The DDR window at 0x7A000000 is
likewise dark until DDR is initialised. Both are absent from link.lds on
purpose.
Reads of 0x30000000/0x30100000/0x30200000 do fault, but the earlier note
here had the reason wrong. Reading the region registers on this part shows
ATCM already ENABLED at both exception levels, 64 KB with one wait state,
and every BASEADDRESS field zero -- so ATCM is live at address 0x00000000,
not at 0x30000000. The faulting reads were of an address the TCM is not
at. ATCM's enables reset set because CFGTCMBOOTx is tied high here. BTCM
(16 KB, 0 wait states) and CTCM (16 KB, 1 wait state) really are disabled.
Sizes and wait states agree with the S32Z2 reference manual exactly.

ECC is implemented and enabled -- IMP_MEMPROTCTLR reads 0x11 -- so per
Cortex-R52 TRM 6.2.2 a TCM location must be written before it is read, and
ATCM needs 64-bit aligned stores where BTCM and CTCM accept 32-bit. Using
TCM therefore takes more than setting an enable bit.

The DDR window at 0x7A000000 is dark until DDR is initialised. Both TCM
and DDR are absent from link.lds for now.


5. What this image reports
Expand Down
154 changes: 154 additions & 0 deletions ports/cortex_r52/gnu/example_build/s32z280_evb/tcm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/***************************************************************************
* Copyright (c) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/

/**************************************************************************/
/* */
/* BOARD SUPPORT RELEASE */
/* */
/* tcm.c Cortex-R52/GNU */
/* 6.5.2 */
/* AUTHOR */
/* */
/* Frederic Desbiens, Eclipse Foundation */
/* */
/* DESCRIPTION */
/* */
/* Reads what the TCM hardware reports about itself. See tcm.h for */
/* the register layouts and where they come from; nothing here is */
/* inferred from a neighbouring register's shape. */
/* */
/* Reading only. Programming a base address and setting the enables */
/* comes after these values have been seen and agree with both the */
/* Cortex-R52 TRM and the S32Z2 reference manual, because a disagreement*/
/* would mean one of the two documents does not describe this part and */
/* guessing which would be the whole PRBAR mistake again. */
/* */
/* MISRA C:2012 deviations (justified) */
/* */
/* Dir 4.3 -- assembly encapsulated one operation per function. */
/* */
/**************************************************************************/

#include "tcm.h"

#define TCM_SIZE_SHIFT 2U
#define TCM_SIZE_MASK 0x1FUL
#define TCM_WAITSTATES_SHIFT 8U
#define TCM_ENABLE_EL2 (1UL << 1)
#define TCM_ENABLE_EL10 (1UL << 0)
#define TCM_BASE_SHIFT 13U /* TRM table 3-136: [31:13] */

#define MEMPROT_RAMPROTIMP (1UL << 4)
#define MEMPROT_RAMPROTEN (1UL << 0)


static unsigned long read_atcmregionr(void)
{
unsigned long value;
__asm volatile ("mrc p15, 0, %0, c9, c1, 0" : "=r" (value));
return value;
}

static unsigned long read_btcmregionr(void)
{
unsigned long value;
__asm volatile ("mrc p15, 0, %0, c9, c1, 1" : "=r" (value));
return value;
}

static unsigned long read_ctcmregionr(void)
{
unsigned long value;
__asm volatile ("mrc p15, 0, %0, c9, c1, 2" : "=r" (value));
return value;
}


/* SIZE is an encoding, not a shift count: 4 means 8KB and each step doubles,
so 8KB << (field - 4). Anything outside the documented range yields 0
rather than a plausible-looking wrong number. */

static unsigned long decode_size(unsigned int field)
{
unsigned long bytes = 0UL;

if ((field >= 4U) && (field <= 11U))
{
bytes = 0x2000UL << (field - 4U);
}

return bytes;
}


void tcm_read_bank(unsigned int index, tcm_bank_t *bank_ptr)
{
unsigned long raw;

if (bank_ptr == 0)
{
return;
}

switch (index)
{
case 0U: raw = read_atcmregionr(); break;
case 1U: raw = read_btcmregionr(); break;
case 2U: raw = read_ctcmregionr(); break;
default: raw = 0UL; break;
}

bank_ptr->raw = raw;
bank_ptr->base = raw & ~((1UL << TCM_BASE_SHIFT) - 1UL);
bank_ptr->size_field = (unsigned int) ((raw >> TCM_SIZE_SHIFT) & TCM_SIZE_MASK);
bank_ptr->size_bytes = decode_size(bank_ptr->size_field);
bank_ptr->wait_states = (unsigned int) ((raw >> TCM_WAITSTATES_SHIFT) & 1UL);
bank_ptr->enable_el2 = ((raw & TCM_ENABLE_EL2) != 0UL) ? 1U : 0U;
bank_ptr->enable_el10 = ((raw & TCM_ENABLE_EL10) != 0UL) ? 1U : 0U;
}


unsigned long tcm_read_memprotctlr(void)
{
unsigned long value;

/* Note opc1 is 1 here, not 0 as for the region registers. */

__asm volatile ("mrc p15, 1, %0, c9, c1, 2" : "=r" (value));
return value;
}


unsigned int tcm_ecc_implemented(void)
{
return ((tcm_read_memprotctlr() & MEMPROT_RAMPROTIMP) != 0UL) ? 1U : 0U;
}


unsigned int tcm_ecc_enabled(void)
{
unsigned long ctlr = tcm_read_memprotctlr();

/* RAMPROTEN is ignored when RAMPROTIMP is 0: the TRM says the design
behaves as if it were 0, so report it that way rather than echoing a
bit that has no effect. */

if ((ctlr & MEMPROT_RAMPROTIMP) == 0UL)
{
return 0U;
}

return ((ctlr & MEMPROT_RAMPROTEN) != 0UL) ? 1U : 0U;
}
Loading
Loading