2024-07-25 05:41 PM - edited 2024-07-25 05:47 PM
I want to download and use firmware (FW) to DDR3. Since DDR is initially disabled, the firmware to activate DDR and the main FW project are separate, and the download procedure is as follows:
For reference, the MPU is the STM32MP131FAE7, and the DDR is the K4B4G1646E.
I have successfully enabled DDR and modified the linker script of the main FW to download FW to DDR (base address: 0xC0000000). However, there is an issue. Among various possible problems, the most challenging one to solve right now is that the uint8_t variables are not changing as expected.
Even when assigning values to uint8_t variables like:
uint8_t val1 = 10; uint8_t val2 = 20; uint8_t val3 = 30; uint8_t val4 = 40;
they do not change properly. There are no such issues with uint16_t or uint32_t variables.
Here is a portion of my DDR activation FW code. I wrote it with reference to examples from the STM32MP135C-DK board, and there are almost no differences.
/*Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C2_Init(); MX_I2C3_Init(); MX_I2C1_Init(); /* USER CODE BEGIN 2 */ pmicInit(); pmic_InitRegulators(); /* USER CODE BEGIN 2 */ /*##-1- Enable MCE ####################*/ __HAL_RCC_MCE_CLK_ENABLE(); /*##-2- TZC configuration ####################*/ __HAL_RCC_TZC_CLK_ENABLE(); /* Configure TZC to allow DDR Region0 R/W non-secure for all IDs */ TZC->GATE_KEEPER = 0; TZC->REG_ID_ACCESSO = 0xFFFFFFFF; // Allow DDR Region0 R/W non-secure for all IDs TZC->REG_ATTRIBUTESO = 0xC0000001; TZC->GATE_KEEPER |= 1; // Enable the access in secure Mode // filter 0 request close /*##-3- Enable ETZPC & BACKUP SRAM for security ####################*/ __HAL_RCC_ETZPC_CLK_ENABLE(); __HAL_RCC_BKPSRAM_CLK_ENABLE(); /*##-4- Unlock debugger ####################*/ BSEC->BSEC_DENABLE = 0x47f; /*##-5- Init DDR ####################*/ hddr.wakeup_from_standby = false; hddr.self_refresh = false; hddr.zdata = 0; hddr.clear_bkp = false; if (HAL_DDR_Init(&hddr) != HAL_OK) { Error_Handler(); } /*##-6- Check DDR Write/Read ####################*/ *p = DDR_PATTERN; if (*p != DDR_PATTERN) { Error_Handler(); } /* Write and read operations */ WRITE_REG(*(volatile uint8_t *)0xC0000004, 0xAA); WRITE_REG(*(volatile uint8_t *)0xC0000005, 0xBB); WRITE_REG(*(volatile uint8_t *)0xC0000006, 0xCC); WRITE_REG(*(volatile uint8_t *)0xC0000007, 0xDD); uint8_t ret1 = READ_REG(*(volatile uint8_t *)0xC0000004); uint8_t ret2 = READ_REG(*(volatile uint8_t *)0xC0000005); uint8_t ret3 = READ_REG(*(volatile uint8_t *)0xC0000006); uint8_t ret4 = READ_REG(*(volatile uint8_t *)0xC0000007);
The code below works correctly in the DDR activation FW, but in the main FW running on DDR, it is not able to read the values correctly:
WRITE_REG(*(volatile uint8_t *)0xC0000004, 0xAA); WRITE_REG(*(volatile uint8_t *)0xC0000005, 0xBB); WRITE_REG(*(volatile uint8_t *)0xC0000006, 0xCC); WRITE_REG(*(volatile uint8_t *)0xC0000007, 0xDD); uint8_t ret1 = READ_REG(*(volatile uint8_t *)0xC0000004); uint8_t ret2 = READ_REG(*(volatile uint8_t *)0xC0000005); uint8_t ret3 = READ_REG(*(volatile uint8_t *)0xC0000006); uint8_t ret4 = READ_REG(*(volatile uint8_t *)0xC0000007);
I cannot identify the cause of the issue and need assistance.
Thank you for your help
Solved! Go to Solution.
2024-08-01 12:06 AM
Hi @Stnoobs
on DDR memories, you have some freedom to swap bit within a byte and swapping bytes. This is to ease PCB routing to achieve better signal integrity (e.g. avoid some vias).
This is described in various documents linked above.
On our board example, we do both. This explain why you see DQS0/DQM0 together with DQ0-7, which is the lower byte of STM32MP13 connected the the DDR3L upper byte.
As general, a memory does not care about bit or byte number, you simply read a bit value where you have written it. There is some exceptions when there is 'registers' using DQ inside the memory (e.g. LPDDRx)
As spotted in my first reply, your schematics confirm there is a mistake around DQM/DQS. Your DM0/1 are swapped (i.e. if DQ0-7 and DQS0 are connected to lower byte of the DDR, then DQM0 must be also going to lower byte).
This explain your issue around byte write (as the byte 'mask' does not mask the right byte on the memory bus).
There is no solution than reworking your PCB.
Regards.
2024-07-25 11:52 PM
Hi,
Maybe look at some PCB HW mixup on DDR_DQM/DQS lines Vs DQ.
Regards.
2024-07-26 01:23 AM
If there were a hardware connection issue with the DDR, shouldn't the DDR Init FW's DDR Read/Write test routine fail?
I have confirmed that all Read/Write operations in the DDR are functioning correctly within the DDR Init FW, and subsequently, I have downloaded the Main FW to the DDR
2024-07-26 01:33 AM
Hi,
I think DDR tests are done using only 32-bits access and/or with cache enabled (as goal is to check timings, not wiring), which might mean only 32-bits access are done to the DDR (i.e. no byte mask differentiation).
We have already seen this kind of mistake on some other customers with similar behavior.
A quick check on your schematics should confirm or not this hypothesis.
Regards.
2024-07-26 02:17 AM
I've checked the hardware connections multiple times and found no issues. I have also reviewed both the DDR RAM and MPU datasheet.
Does a 32-bit access mean code like the following?
/******************************************************************************* * This function tests the DDR data bus wiring. * This is inspired from the Data Bus Test algorithm written by Michael Barr * in "Programming Embedded Systems in C and C++" book. * resources.oreilly.com/examples/9781565923546/blob/master/Chapter6/ * File: memtest.c - This source code belongs to Public Domain. * Returns 0 if success, and address value else. *******************************************************************************/ static uint32_t ddr_test_data_bus(void) { uint32_t i; uint32_t pattern; for (i = 0U; i < 32U; i++) { pattern = (uint32_t)1 << i; WRITE_REG(*(volatile uint32_t *)DDR_BASE_ADDR, pattern); if (READ_REG(*(volatile uint32_t *)DDR_BASE_ADDR) != pattern) { return (uint32_t)DDR_BASE_ADDR; } } return 0U; } /******************************************************************************* * This function tests the DDR address bus wiring. * This is inspired from the Data Bus Test algorithm written by Michael Barr * in "Programming Embedded Systems in C and C++" book. * resources.oreilly.com/examples/9781565923546/blob/master/Chapter6/ * File: memtest.c - This source code belongs to Public Domain. * Returns 0 if success, and address value else. *******************************************************************************/ static uint32_t ddr_test_addr_bus(void) { uint32_t addressmask = (static_ddr_config.info.size - 1U); uint32_t offset; uint32_t testoffset = 0; /* Write the default pattern at each of the power-of-two offsets. */ for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; offset <<= 1) { WRITE_REG(*(volatile uint32_t *)(DDR_BASE_ADDR + (uint32_t)offset), DDR_PATTERN); } /* Check for address bits stuck high. */ WRITE_REG(*(volatile uint32_t *)(DDR_BASE_ADDR + (uint32_t)testoffset), DDR_ANTIPATTERN); for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; offset <<= 1) { if (READ_REG(*(volatile uint32_t *)(DDR_BASE_ADDR + (uint32_t)offset)) != DDR_PATTERN) { return (uint32_t)(DDR_BASE_ADDR + offset); } } WRITE_REG(*(volatile uint32_t *)(DDR_BASE_ADDR + (uint32_t)testoffset), DDR_PATTERN); /* Check for address bits stuck low or shorted. */ for (testoffset = sizeof(uint32_t); (testoffset & addressmask) != 0U; testoffset <<= 1) { WRITE_REG(*(volatile uint32_t *)(DDR_BASE_ADDR + (uint32_t)testoffset), DDR_ANTIPATTERN); if (READ_REG(*(volatile uint32_t *)DDR_BASE_ADDR) != DDR_PATTERN) { return DDR_BASE_ADDR; } for (offset = sizeof(uint32_t); (offset & addressmask) != 0U; offset <<= 1) { if ((READ_REG(*(volatile uint32_t *)(DDR_BASE_ADDR + (uint32_t)offset)) != DDR_PATTERN) && (offset != testoffset)) { return (uint32_t)(DDR_BASE_ADDR + offset); } } WRITE_REG(*(volatile uint32_t *)(DDR_BASE_ADDR + (uint32_t)testoffset), DDR_PATTERN); } return 0U; }
I understand that because these functions are reading and writing in 32-bit, they can't check 8-bit operations.
What does it mean that there is no bit mask differentiation?
Could the RAM timing settings be related? For the K4B4G1646E RAM, DDR3-2133 (14-14-14), DDR3-1866 (13-13-13), DDR3-1600 (11-11-11) are recommended, but it seems that the STM32MP131FAE7 does not support these speeds. Therefore, I am using the slower DDR3-1066 (8-8-8) speed.
Additionally, please check why the CubeMX DDR-related code is not automatically generated in the Baremetal environment.
2024-07-26 02:37 AM
Hi,
I guess the issue might not be linked to DDR timing settings. Could you elaborate more on the
Does this mean the 8-bit write is ok in some situations ?
When it fail, what is the read value ? 0 ? value from another write ? Maybe worth to fill a larger table (e.g. 256 Bytes) and read it back. to see if result could bring some clues.
Did you have cache enabled on this test area ?
Regards.
2024-07-26 02:52 AM - edited 2024-07-26 02:56 AM
The read/write verification across the entire DDR region was performed by reading and writing 32 bits at a time.
static uint32_t ddr_test_data_bus(void) { uint32_t i; uint32_t pattern; for (i = 0U; i < 32U; i++) { pattern = (uint32_t)1 << i; WRITE_REG(*(volatile uint32_t *)DDR_BASE_ADDR, pattern); if (READ_REG(*(volatile uint32_t *)DDR_BASE_ADDR) != pattern) { return (uint32_t)DDR_BASE_ADDR; } } return 0U; }
I modified the above function to perform read and write operations across the entire memory region.
Additionally, before downloading the Main FW to the DDR, the following code was executed in the DDR Init function:
/* Write and read operations */ WRITE_REG(*(volatile uint8_t *)0xC0000004, 0xAA); WRITE_REG(*(volatile uint8_t *)0xC0000005, 0xBB); WRITE_REG(*(volatile uint8_t *)0xC0000006, 0xCC); WRITE_REG(*(volatile uint8_t *)0xC0000007, 0xDD); uint8_t ret1 = READ_REG(*(volatile uint8_t *)0xC0000004); uint8_t ret2 = READ_REG(*(volatile uint8_t *)0xC0000005); uint8_t ret3 = READ_REG(*(volatile uint8_t *)0xC0000006); uint8_t ret4 = READ_REG(*(volatile uint8_t *)0xC0000007);
After executing this code, it was confirmed that ret1 to ret4 each read and stored 0xAA, 0xBB, 0xCC, and 0xDD, respectively.
The reason for the inquiry about RAM timing is that whenever the RAM timing was changed, there were cases where the above code did not execute correctly.
Additionally, after loading the main FW into the DDR, the memory data checked in debug mode and the data recorded in the created bin file had reversed values, which also needs to be verified whether this is correct.
Should the cache be enabled or disabled? I am not sure where to configure this.
2024-07-26 03:00 AM
Hi,
regarding DDR, did you read following application notes ?
AN5168 How to configure DDR on STM32MP1 MPUs
2024-07-30 12:04 AM
Yes, I have reviewed the entire document and I believe there are no issues.
However, on the DDR datasheet, pin M7 is marked as NC (No Connection), but on our PCB, M7 is connected to A15 (Address 15). Could this pose a problem?
2024-07-30 12:32 AM
Hi,
I don't think this is the issue as A15 is available on JEDEC to support larger density.
If behavior depend on timing setup, there is likely something weird around the memory (routing, supplies, voltage references, etc..).
We have seen DDR3L quite robust on customer design, so the issue is probably easy to detect.
Maybe share schematics here.
Regards.