Hello NXP Community,
I am currently working on a project using the S32K396 (RTD 3.0.0, AUTOSAR 4.7) and I am facing a persistent Asynchronous Bus Fault (HardFault) during the peripheral initialization phase.
Objective:
My goal is to configure PTD1 as an analog input (ADC0_AN1) to read a sensor and transmit the converted data via LPUART2 (using PTH8 for TX). I am working in low-level because is something that I think is easy in low-level.
Current Implementation Details:
I have implemented a Hardware_Init() function that follows the MC_ME sequence for clock distribution across different partitions:
Partition 0 Initialization: I am enabling ADC0 (REQ40) by setting bit 8 in MC_ME_PRTN0_COFB1_CLKEN.
Partition 1 Initialization: I am enabling FXOSC (REQ53, bit 21 in COFB1) and LPUART2 (REQ76, bit 12 in COFB2).
Clock Commitment: For both partitions, I am using the standard PCONF/PUPD sequence followed by writing the keys (0x5AF05AF0 and 0xA50FA50F) to the MC_ME_CTL_KEY register.
Peripheral Config: * LPUART2: Using base address 0x40330000 as per the memory map.
ADC0: Attempting to clear the PWDN bit (bit 0) in ADC0_MCR.
The Problem:
Even though I am following the commit sequence and waiting for the PUPD bits to clear, the system triggers an asynchronous Bus Fault. This usually happens when the code attempts to access a peripheral register (like ADC0_MCR or LPUART_CTRL) while the module is still clock-gated or if there's a memory access violation.
I have verified the following:
The base address for LPUART2 is 0x4033_0000h.
The ADC0 clock enable is indeed in Partition 0, COFB1, Bit 8.
The LPUART2 clock enable is in Partition 1, COFB2, Bit 12.
Code Snippet:
#include <stdint.h>
#include <stdio.h>
#define REG_WRITE32(addr, val) (*(volatile uint32_t *)(addr) = (val))
#define REG_READ32(addr) (*(volatile uint32_t *)(addr))
/* --- SIUL2 (Pins) --- */
#define SIUL2_MSCR97 0x402903C4 /* PTD1 - ADC0_AN1 */
#define SIUL2_MSCR232 0x402905E0 /* PTH8 - LPUART0_TX */
/* --- MC_ME (Clocks) --- */
#define MC_ME_BASE 0x402DC000
#define MC_ME_CTL_KEY (MC_ME_BASE + 0x00)
#define MC_ME_PRTN1_PCONF (MC_ME_BASE + 0x300)
#define MC_ME_PRTN1_PUPD (MC_ME_BASE + 0x304)
#define MC_ME_PRTN0_PCONF (MC_ME_BASE + 0x100)
#define MC_ME_PRTN0_PUPD (MC_ME_BASE + 0x104)
#define MC_ME_PRTN1_STAT (MC_ME_BASE + 0x308)
#define MC_ME_PRTN1_COFB2_CLKEN (MC_ME_BASE + 0x338) // LPUART_2 - REQ76, bit 12
#define MC_ME_PRTN0_COFB1_CLKEN (MC_ME_BASE + 0x134) // ADC_0 - REQ 40, bit 8
#define MC_ME_PRTN1_COFB1_CLKEN (MC_ME_BASE + 0x334) // FXOSC - REQ 53, bit 21
/* --- ADC0 --- */
#define ADC0_BASE 0x400A0000
#define ADC0_MCR (ADC0_BASE + 0x00)
#define ADC0_MSR (ADC0_BASE + 0x04)
#define ADC0_NCMR0 (ADC0_BASE + 0xA4)
#define ADC0_PCDR1 (ADC0_BASE + 0x104)
/* --- LPUART_2 --- */
#define LPUART_2_BASE 0x40330000
#define LPUART_2_BAUD (LPUART_2_BASE + 0x10)
#define LPUART_2_STAT (LPUART_2_BASE + 0x14)
#define LPUART_2_CTRL (LPUART_2_BASE + 0x18)
#define LPUART_2_DATA (LPUART_2_BASE + 0x1C)
#define MC_ME_KEY 0x5AF05AF0
#define MC_ME_INVKEY 0xA50FA50F
void Hardware_Init(void) {
/* Habilitar Relojes en Partición 1 (ADC_0 - REQ 40, bit */
uint32_t clk_ADC = REG_READ32(MC_ME_PRTN0_COFB1_CLKEN);
REG_WRITE32(MC_ME_PRTN0_COFB1_CLKEN, clk_ADC | (1 << 8));
/* Commit clock changes */
REG_WRITE32(MC_ME_PRTN0_PCONF, 0x01);
REG_WRITE32(MC_ME_PRTN0_PUPD, 0x01);
REG_WRITE32(MC_ME_CTL_KEY, MC_ME_KEY);
REG_WRITE32(MC_ME_CTL_KEY, MC_ME_INVKEY);
while(REG_READ32(MC_ME_PRTN0_PUPD) & 0x01);
/* Habilitar Relojes en Partición 1 (FXOSC - REQ 53, bit 21) */
uint32_t clk_FXOSC = REG_READ32(MC_ME_PRTN1_COFB1_CLKEN);
REG_WRITE32(MC_ME_PRTN1_COFB1_CLKEN, clk_FXOSC | (1 << 21));
/* Habilitar Relojes en Partición 1 (LPUART_2 bit 21 - REQ53) */
uint32_t clk_UART = REG_READ32(MC_ME_PRTN1_COFB2_CLKEN);
REG_WRITE32(MC_ME_PRTN1_COFB2_CLKEN, clk_UART | (1 << 12));
/* Commit clock changes */
REG_WRITE32(MC_ME_PRTN1_PCONF, 0x01);
REG_WRITE32(MC_ME_PRTN1_PUPD, 0x01);
REG_WRITE32(MC_ME_CTL_KEY, MC_ME_KEY);
REG_WRITE32(MC_ME_CTL_KEY, MC_ME_INVKEY);
while(REG_READ32(MC_ME_PRTN1_PUPD) & 0x01);
/* 2. Configurar Pines */
REG_WRITE32(SIUL2_MSCR97, 0x200000); /* PTD1 como Analógico */
REG_WRITE32(SIUL2_MSCR232, 0x00210001); /* LPUART2 TX */
/* 3. Configurar LPUART2 */
REG_WRITE32(LPUART_2_BAUD, 26); /* 115200 Baud */
REG_WRITE32(LPUART_2_CTRL, 0x00080000); /* TX Enable */
/* 4. Inicializar ADC0 (Solo si el reloj ya es estable) */
/* Salir de Power Down: PWDN = 0 */
uint32_t mcr = REG_READ32(ADC0_MCR);
REG_WRITE32(ADC0_MCR, mcr & ~(1 << 0));
for(volatile int i=0; i<1000; i++);
}
void UART_SendChar(char c) {
while(!(REG_READ32(LPUART_2_STAT) & (1 << 23))); //Transmit Data Register Empty Flag, bit 23
REG_WRITE32(LPUART_2_DATA, (uint32_t)c);
}
void UART_SendString(char* str) {
while(*str) UART_SendChar(*str++);
}
void UART_SendInt(uint32_t val) {
if (val == 0) { UART_SendChar('0'); return; }
char buffer[10];
int i = 0;
while (val > 0) {
buffer[i++] = (val % 10) + '0';
val /= 10;
}
while (i > 0) UART_SendChar(buffer[--i]);
}
int main(void) {
Hardware_Init();
while(1) {
/* Configurar canal y disparar conversión */
REG_WRITE32(ADC0_NCMR0, (1 << 1)); /* Seleccionar Canal 1 */
REG_WRITE32(ADC0_MCR, REG_READ32(ADC0_MCR) | (1 << 24)); /* NSTART = 1 */
/* Esperar fin de conversión (NEND en MSR) */
while(!(REG_READ32(ADC0_MSR) & (1 << 1)));
/* LEER DATOS: Usando el PCDR1 (Precision Data Register) */
uint32_t val_pcdr = REG_READ32(ADC0_PCDR1);
/* Verificar si el dato es válido (Bit 31) */
if (val_pcdr & (1 << 31)) {
uint32_t sensor = val_pcdr & 0x7FFF; /* Máscara para 15 bits de datos */
UART_SendString("Sensor Value: ");
UART_SendInt(sensor);
UART_SendString("\r\n");
}
/* Limpiar bandera NEND escribiendo un 1 */
REG_WRITE32(ADC0_MSR, (1 << 1));
for(volatile uint32_t i = 0; i < 2000000; i++);
}
}
Question:
Is there a specific dependency between Partition 0 and Partition 1 that I am missing? Or is there a specific delay required beyond the PUPD polling before the peripheral registers become accessible on the IPS bus?
Any insights on why this register access would trigger a Bus Fault on the S32K3xx would be greatly appreciated.
Best regards,
Jesus A