Part Number: MSPM0G3519
Other Parts Discussed in Thread: UNIFLASH
Hi,
I came across one observation while development using CSC. As per TI's documentation
what is this delay is about?Part Number: MSPM0G3519
Other Parts Discussed in Thread: UNIFLASH
Hi,
I came across one observation while development using CSC. As per TI's documentation
what is this delay is about?if possible provide some documentation related to this.
Hi TI team,
We are working with your CSC example for our one of requirement
With some of the configuration like bank swap enable etc..
And there we face few issues during boot up of application code with CSC boot code.
Please can you clarify few questions below,
Is the delay before DL_SYSCTL_issueINITDONE() mandatory when bank swap is configured?
If yes,
what is the recommended / bare minimum number of cycles? Is 160 cycles the official minimum.?
If the delay can be shorter or skipped in some cases exactly what does it depend upon?
What hardware mechanism / settling time does the delay actually protect against?
Is there any official timing specification (i.e. cycles/us/ns ) in the TRM, datasheet, errata, or internal design info for this settling requirement?
It would be great help if you can give insights
Thank you.
Hi Mehul, Nisarg,
Sorry I did not see the delay requirement between issue INITDONE and the system reset triggered in my test.
The system reset should immediately occurs after issue INITDONE. If it does not, there may be two possibilities:
1. The CSC Enable and BANK SWAP enable is not configured in NONMAIN.
2. At the time you trigger INITDONE, the INITDONE bit has been set already
You could refer the FAQ section of Cybersecurity Enablers in MSPM0 MCUs (Rev. A) to see whether answer your question.
Best Regards,
Pengfei
Hi Xie.
I have verified configuration in NONMAIN region. got 0xffffffff at address 0x41c0018. it means CSC enabled and BANK swap enabled. So, this is not an issue.
I got initDONE resister already set in first boot. As per my understanding CSC example code work when we keep CSC in debug and then load application image and then run CSC then it found initDONE has not been set. Then CSC do security config and issue initDONE. I think you are also doing same testing, right? Have you tested this process without keeping CSC in debug. I mean load CSC + application code from UNIFLASH.
In our case we are loading Bootloader (integrated CSC) + application from UNIFLASH. UNIFLASH explicitly issue hard reset after programming that set initDONE resister, and as it appears already set our intentionally done initdone never take place.
Hi Mehul,
I tested with Uniflash programming before for a whole CSC + application image, and it works fine from my side.
After you programmed the image by Uniflash, have you tried to take a power cycle, to see whether the program run as expected? I mean the code before INITDONE is executed firstly for security configuration, and then INITDONE issued and a reset is triggered, and next time the application program get executed.
Hi Mehul,
I do think it is related to optimization level since it is only related to one register operation.
Do you see the optimization level have impact on the initdone behavior (trigger reset or not)?
Hi Xie.
Yes i have seen impact on optimization level. The code gets stuck in a HardFault when issuing the initDONE command. I want to know whether the CSC‑referenced code has any strict dependency on specific toolchain versions such as ti‑cgt‑armllvm_4.0.2.LTS or ti‑cgt‑armllvm_4.0.0.LTS.
Sometimes the initDONE command triggers a reset as expected, but at other times the same code ends up in a HardFault. When I lower the optimization level, the same code starts working consistently.
When the HardFault occurs, it is always triggered inside an API call from the bimsupport.a library.
Hi Mehul,
Could you check which code sentence does the hard fault caused by? I assume it is not caused by INITDONE itself, but by the code after INITDONE and reset. There is a brief introduction for how to debug with hard fault.
Besides, you could also try below code for program jump instead, which should be safer for operating SP in runtime.
uint32_t * vector_table_backup;
static void start_app(uint32_t *vector_table)
{
/* The following code resets the SP to the value specified in the
* provided vector table, and then the Reset Handler is invoked.
*
* Per ARM Cortex specification:
*
* ARM Cortex VTOR
*
*
* Offset Vector
*
* 0x00000000 ++++++++++++++++++++++++++
* | Initial SP value |
* 0x00000004 ++++++++++++++++++++++++++
* | Reset |
* 0x00000008 ++++++++++++++++++++++++++
* | NMI |
* ++++++++++++++++++++++++++
* | . |
* | . |
* | . |
*
* */
/* Back up of vector_table to avoid being changed because of SP update */
vector_table_backup = vector_table;
/* Set the Reset Vector to the new vector table (Will be reset to 0x000) */
SCB->VTOR = (uint32_t) vector_table;
/* Reset the SP with the value stored at vector_table[0] */
__asm volatile(
"LDR R3,[%[vectab],#0x0] \n"
"MOV SP, R3 \n" ::[vectab] "r"(vector_table));
/* Jump to the Reset Handler address at vector_table[1] */
((void (*)(void))(*(vector_table_backup + 1)))();
}
Best Regards,
Pengfei
Okay but I am not getting issue while jump from bootloader to application. Code jump logic work fine. I am getting issue when I call DL_SYSCTL_issueINITDONE().
Also as I mentioned before same code work fine if I add some print statements or change optimization level or even adding delay before calling this DL_SYSCTL_issueINITDONE() function.
Hi Pingfei,
I'm Chris and a colleague of Mehul and Nisarg, working on the same product.
I was "lucky" enough to run into a situation in which the following occurs:
Scenario:
2 identical copies of the CSC-based bootloader and 1 copy of our (signed) application flashed to the board using UNIFLASH.
The bootloader runs and starts the application as expected, i.e. no hang when setting INITDONE.
Then I uploaded the same (signed) application to the board using our homegrown and CAN-based file transfer mechanism.
Note that this file transfer and the associated flashing and bank switching mechanism has been proven to work as expected in many other cases.
However:
The bootloader wrote the application to the upper bank, authenticated it, deleted the previously running application from the lower bank and then tried to set INITDONE to boot from it ... and now it hangs and keeps on hanging on consecutive attempts to boot.
To recap:
Also interesting:
I was able to reproduce exactly the same issue with debug (-O0) and release (-Os) builds of the bootloader.
I do agree with your assumption that INITDONE actually does not hang, but something bad happens afterwards.
As such, I went into the debugger and determined that the code sits in an endless loop in the hardfault handler. The LR register in the exception stackframe points to the same code in both the debug and release builds:
Debug Build
LR = 0xa725
Mapfile excerpt:
0000a6e8 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in)
0000a734 0000004c can_sdo.o (.text.sdo_getAbortCode)
Release Build
LR = 0x7171
Mapfile excerpt:
00007134 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in)
00007180 0000004c flash_map_backend.o (.text.flash_area_read)
So, in both cases, the hardfault seem to have occurred 16 byte before the end of copy_in() of libc.a.
We are using ti-cgt-armllvm_4.0.2.LTS.
Hope this helps to identify the root cause of this issue as this and the previous findings clearly indicate that this issue is not caused by our code.
Let me know if you have any questions.
Thx,
Chris.
Pingfei,
I know it sounds weird, but I'm starting to see a pattern:
Working code layouts:
0000712c 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.2.LTS
0000709c 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.4.LTS
0000708c 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.4.LTS
0000708c 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.2.LTS
000070ec 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.2.LTS
Failing code layouts:
00007138 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.2.LTS
00007158 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.2.LTS
000070f8 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.2.LTS
00007104 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.2.LTS
000070a4 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.4.LTS
00007078 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.4.LTS
00007084 0000004c libc.a : cpy_tbl.c.obj (.text.copy_in) - built with ti-cgt-armllvm_4.0.4.LTS
In other words:
INITDONE failures are more likely to appear than not, but only when a switch to the upper flash bank is supposed to occur.
I have not seen any INITDONE failures when remaining in the lower flash bank recently. Such failures do occur too, though.
I've also tried to upgrade the toolchain to the latest 4.x version, but that didn't make any difference.
Hi Xie,
I went through the TI's official document Application Note
Flash Multi Bank Feature in MSPM0 Family and i found code 
initDone API. The original CSC did not require this delay, but is it possible that, for larger or more functionally complex code, this delay might be necessary? Mehul Dabholkar
I've confirmed that inserting a delay does not help! Also not when I make it 100 times larger.
Pengfei Xie
My assumption wrt the location of the offending (?) code described above becomes more and more credible. I probably ran several dozens of builds and it always proved to be true:
Up until now, this behavior is 100% reproducible.
Hi Christian and Mehul,
Sorry for the late response since I'm out of office these two day. And many thinks for the summary on this issue.
It looks there is a stable way to reproduce the issue of INITDONE you see. From your description, this issue looks new from my side, it may related to some internal behavior on libc.a during INITDONE occurs.
I could take the same test by early next week, and I will update to you if I make any progress. Thanks for your patient.
Best Regards,
Pengfei
Hi Mehul and Christian,
I try to reproduce this issue from my side, by making sure the "libc.a : cpy_tbl.c.obj (.text.copy_in)" start address is aligned with 0x4 as shown in below, but did not see the same issue from my side. (I tried both ti-cgt-armllvm_4.0.0.LTS and ti-cgt-armllvm_4.0.4.LTS from my side).

I believe you have some flash operation (such as erase or programming) in your boot program right? With flash operation, the cpy_tbl.c will be linked and it should be applied during boot time (applied in _c_int00 program, it is after Reset_Handler and before main() application), to copy some flash operation APIs from flash memory to SRAM. And I think the hard fault is triggered during this stage, which is after INITDONE has actually takes effect.

Could you share your linker file (.cmd), and share what kind of flash operation you have done and which APIs you are applying in boot program?
And what is your memory mapping for boot program and application program?
To verify the hard fault occurs during _c_ini00 program, you could try to add a loop or trigger to GPIO toggling in Reset_Handler() function, and check when the MCU takes a bankswap and program run from physical bank1, whether you could see the program stuck in loop (connecting debug) or whether you see the GPIO toggling.
Best Regards,
Pengfei
Hi Pengfei:
I believe you have some flash operation (such as erase or programming) in your boot program right?
Yes, we do. When our board has received, flashed, and authenticated a new application image, it will erase the previously installed image from the corresponding bank, switch banks, and then issue INITDONE.
This is where it "hangs" when switching to the upper bank, i.e. after having received an update of the application that was installed initially in the lower bank.
That said, I'm not sure if the same issue would happen when switching to the lower bank because the first "hang" prevents us from going that far. However, I think we can run a test that would do that.
And I think the hard fault is triggered during this stage, which is after INITDONE has actually takes effect.
I agree with this assessment. The question is, though, what could be different compared to running from the lower bank vs the upper bank that could trigger such an issue.
Could you share your linker file (.cmd), and share what kind of flash operation you have done and which APIs you are applying in boot program?
I (or Mehul) will try to do so at a later time. Same for the .map files.
I do not think that the application image has any impact on this issue as only the bootloader code is involved. Agree?
Ciao,
Chris.
That said, I'm not sure if the same issue would happen when switching to the lower bank because the first "hang" prevents us from going that far. However, I think we can run a test that would do that.
Just did that and now INITDONE hangs on first boot, i.e. when trying to switch to the upper bank but without flashing/erasing previously.
In other words:
It seems that this issue occurs only when trying to run from the upper bank.
Could you share your linker file (.cmd), and share what kind of flash operation you have done and which APIs you are applying in boot program?
The .cmd and .map files are attached in init_done_issue-2026-02-10-0.zip.
The APIs we're using in this use case are essentially:
Hi Pingfei,
a minor (?) correction:
Just did that and now INITDONE hangs on first boot, i.e. when trying to switch to the upper bank but without flashing/erasing previously.
This is not true. 2 sectors of the flash are erased and programmed when Lock_writeOut() is invoked.
It should also be noted that CSC_LOCK_STORAGE_ADDR has moved compared to were it is in the CSC sample code. It is located at 0x14400 in our code. The original code has it at 0x4400.
This makes the invocation of DL_SYSCTL_setWriteProtectFirewallAddrRange() with LOCKABLE_FLASH_FIREWALL = 0x00030000 useless, but it should also do no harm.
I also ran a test with everything after DL_SYSCTL_executeFromUpperFlashBank() and before DL_SYSCTL_issueINITDONE() commented out, but the issue is persists.
Ciao,
Chris.
Hi Christian,
Thanks for sharing those information, it helps.
Could you add extra below two tests from your side (sorry for some many tests):
1. When the issue occurs (I mean the MCU is stuck in hard fault handler), try to connect MCU debug by XDS110 (My previous slide shows a step about connect debug with a running MCU), then open top menu: View-Disassembly, and input the address of "libc.a : cpy_tbl.c.obj". Check whether it is all 0xFF. If not, I believe we could also identify which address causing the hard fault from the disassembly code.
2. I think you are using .bin file to load bank1 boot firmware right? Could we try to use the .txt file to load, and just change the @ address of each data part to bank1 base address (for example: @0000 -> @0x40000)
Please let me know your test results. Thanks.
Best Regards,
Pengfei
Hi Pengfei,
I believe we could also identify which address causing the hard fault from the disassembly code
Here's what I got:
LR of the stackframe in the hard fault handler holds 0xa7e5. The disassembly of the function copy_in shows this:
When LR holds 0xa7e5, I think that means that something went bad when the instruction at 0xa7e2 was executed.
r2 seems to be a function pointer. The value of r2 in the stackframe of the hard fault handler is 0xffffffff.
It may have been corrupted when the function was called or already earlier, for instance when loading its value at 0xa7de.
Could we try to use the .txt file to load, and just change the @ address of each data part to bank1 base address (for example: @0000 -> @0x40000)
Sorry, but I don't understand what you mean.
Ciao,
Chris.
Hi Chris,
LR of the stackframe in the hard fault handler holds 0xa7e5.
I set the same test environment from my side for copy_in function, (set the start address of this function as 0xa7a8, and also checked the disassembly code are exactly same for this part), but not find hard fault issue from my side. So I just think maybe this issue is not caused by the copy_in function. Could you follow the guide page 6-9 of below slides to check the hard fault cause? You need to check the memory in SP address, and find the data in the followed sixth address from SP pointer. Please check the slide for details. And then check what is the address represent in disassembly code.
MSPM0 Blocking Issue Debug Flow.pptx
Sorry, but I don't understand what you mean.
What I mean is you could use the ti-txt format as the boot output file loaded in bank1:
1. You cold firstly configure boot project properties to generate ti-txt format output file.
2. If you open the .txt file, you will see the data start from a "@0000", "@41C00000", "@41C00100", and you could remove "@41C00000", "@41C00100" and followed content (this is NONMAIN content and bank0 boot program will include those part). And then add 0x40000 offset for all other address (for example: @0000 -> @40000). And also let me know whether you could see any other address except "@0000", "@41C00000", "@41C00100" in your txt format file.

And just let know in advance, we will have New Year holiday from 2/14 in China, and I will be back after 2/26. So the reply may have some reply after this Friday.
Best Regards,
Pengfei
Hi Pengfei,
given your pending holiday, I try to provide further information as quickly (and possibly incomplete) as I can.
FYI:
We are not using CCS for development. We have been using it to get started, but our general goal is to be independent of vendor-specific IDEs. We are using TI's toolchain (with a GCC-based toolchain as alternative) but otherwise our project is based on a simple Makefile.
If you open the .txt file, you will see the data start from a "@0000", "@41C00000", "@41C00100",
The .txt file does not show any other addresses than @0000 and @14400. That's because we have excluded the unneeded sections .BCRConfig and .BSLConfig already. @14400 is where our LOCK_STORAGE section is located. This section should probably be stripped from the .bin file as well, but it should create no harm. I've attached the complete file for your reference.
Sidenote:
I'm not sure if that's a bug or feature, but TI's tiarmhex utility merely appends unstripped sections regardless of their target address. Other tools like arm-none-eabi-objcopy fill any potential gap in the address space.
As such, our binaries (and with that the bootloader image that gets flashed to the upper bank) have an extra 16 byte (filled with zeros) appended to them. I don't think that's the root cause of our issue, but I will give it a try as soon as I can.
Despite not using TI's IDE, I'm now able to get into a debugger before the hardfault occurs. This shows the following in the routine copy_in (note that the assembly code is different now because I'm running the debug version of the code):

The highlighted instruction is where the hardfault occurs because r2 will be filled with 0xffffffff. r2 is loaded from r7 and r1 where the code is currently stopped.
At this point the registers have the following values:
r7 = 0xfd10 which seems to be valid. According to the map file (complete file attached) this is the __TI_handler_table:
.cinit 0 0000fc00 00000148
0000fc00 0000010d (.cinit..data.load) [load image, compression = lzss]
0000fd0d 00000003 --HOLE-- [fill = 0]
0000fd10 0000000c (__TI_handler_table)
0000fd1c 00000008 (.cinit..TrimTable.load) [load image, compression = zero_init]
0000fd24 00000008 (.cinit..bss.load) [load image, compression = zero_init]
0000fd2c 00000018 (__TI_cinit_table)
0000fd44 00000004 --HOLE-- [fill = 0]
r1 = 0x2dc which seems to be invalid or "out of range". The memory at 0xfd10 looks like this:
Everything up to [r7+r1] (and further) is filled with 0xff as this is beyond the end of the .text section.
The call stack looks like this:

Hope this helps to get to the bottom of this issue.
Hi Pengfei,
As such, our binaries (and with that the bootloader image that gets flashed to the upper bank) have an extra 16 byte (filled with zeros) appended to them. I don't think that's the root cause of our issue, but I will give it a try as soon as I can.
As expected, these extra bytes were not the root cause of the issue.
Ciao,
Chris.
Hi Pengfei,
I sincerely hope that someone else will pick up this thread while you're on holiday.
Correct?
Ciao,
Chris.
Hi Chris,
Thanks for sharing so much details of your debugging information.
From your test, the hard fault is caused by CPU try to jump to an address which is all 0xFF and not a valid instruction.
I personally think it may caused by the binary format file. As you said, the binary file does not have address information, and by default all the separate sectors in binary file will be directly connected without filling data. If we want to set the filling data, we could change .cmd file, to add a "FILL=0xFF" after the FLASH memory definition. This issue just looks like some separated content are connected together in .bin file and causing address access error.
And this is also the reason I want you to use another format with address information (such as txt or hex) as boot output file as a cross test. We could generate a hex or txt firstly (the file will be built in lower bank address because with bankswap, the program runs as it is in lower bank). But when you load the file to bank1 base address by uniflash, you need to change the address in the txt (the address after @) and hex(the address before content sector) to add a 0x40000 offset to make sure them loaded to correct address.
Sorry I will be on vocation from today. So the reply will be delay. You could contact your local TI support (sales) team to get support if it is urgent, our FAE or sales will help you loop correct person during I'm out of offoce. Or you could also raise another e2e ticket for the same topic and there will be our teammates cover during China's New Year holoday. Thanks.
Hi Pengfei,
This issue just looks like some separated content are connected together in .bin file and causing address access error.
Correct. I realized that - depending on the size of the .text segment - there may or may not be a gap between the .text and .rodata segments.
When such a gap is there, it's not filled by TI's tiarmhex tool as I've mentioned earlier:
Sidenote:
I'm not sure if that's a bug or feature, but TI's tiarmhex utility merely appends unstripped sections regardless of their target address. Other tools like arm-none-eabi-objcopy fill any potential gap in the address space.
This then causes the .rodata segment of the bootloader code in the upper bank to be misaligned, resulting in the issue we saw.
In other words:
Using the ti-txt format to produce and then program the copy of the bootloader that goes into the upper bank resolves the issue.
Thx and Happy New Year!