cancel
Showing results for 
Search instead for 
Did you mean: 

External Loader Debug Printing

DWeie
Associate III

Hi all,

I'm developing a custom external loader to flash a QUADSPI memory as part of a TouchGFX project. I'm using an STM32F7 processor and developing using the STM32CubeIDE.

I’m struggling with a specific issue that I’m hoping I’ll be able to get help with. Since I’m unable to observe the state of the processor while the external loader is being called from the ST-LINK Utility I’m planning on redirecting stdio to an unused USART to get run time information.

When running in debug mode in the ST32CubeIDE this is working fine, but as soon as I switch to using the .stldr (generated from the same source) through the ST-LINK Utility the functionality goes away. A rundown of what I have done on this so far is below:

I’ve proven that the Init() function is being called and is running by toggling some other GPIO and observing them.

I’ve globally disabled interrupts in Debug mode to verify that interrupts are not needed for functionality of the UART in blocking mode.

I’ve tested removing the stdio redirect from consideration by writing directly to the UART instance’s data register.

I’ve made sure to call SystemInit() before other initialization code. Normally the assembly startup calls this function, but this (along with data initialization) would be skipped when Init() is called directly by the ST-LINK application.

I have not implemented the data (.bss and zero) initialization that would normally occur in the startup code because, if I understand correctly, the ST-LINK should be doing this based on the provided .stldr file. If it is not then I am unsure where to initialize the data from since there is no reference in flash memory as there would be for normal operation.

Despite this, I am getting no print statements. I'm wondering if anyone with more experience in this might have some suggestions for me to try since I'm not able to think of a next step beyond what I've already attempted.

Thank you,

Don

25 REPLIES 25

I wouldn't assume the ST-LINK drivers initialize memory for you, both Keil and IAR external loader examples explicitly memset() stuff they use to zero.

My preference in Keil is to call the scatter loader from Init() to get the job done transparently, GNU obviously will be more of a hack.

USART should work. So issues there are Pin configurations and AF settings, clocks, the USART library with a clear grasp of the system/bus clocks to set the USART->BRR correctly. Would have Init() output a whole load of 'U' chars and scope those for appropriate bit timing.

Make standalone OutputString() type functions talking to USART ISR/TDR directly.

Consider not programming the RCC/PLL, and just run from the 16 MHz HSI, see if that works. ie comment out SystemClock_Config() call.

Watch for things dependent on Ticks. Watch for while(1) loops of death like ErrorHandler(), flag to a RED LED if you have one.

It is technically possible to get HardFault working, helpful at times.

Do not implement a Read() function, the ST-LINK does this reading directly from memory itself, so make sure the memory is mapped.

Mask memory address passed to QSPI chips via commands.

What memory part(s) are you using? What pins? What USART/Pins? What HSE source?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Thanks for the input Clive, I'll get to work trying some of these suggestions right away. I'll clarify in a couple of spots and answer the questions that I can already.

I have attempted writing to the TDR directly while observing the output with an oscilloscope. I was thinking along the same lines as you regarding in incorrect baud rate. In this test I did not see any edges at all which tells me that the error is something else. I'll dive into the HAL library and see if there's a path that the uart init can take where it'll skip initialization because it thinks the BR is impossible.

The memory part is an N25Q128A13ESF40F from Micron. The QUADSPI pinout is    

/**QUADSPI GPIO Configuration   

   PE2    ------> QUADSPI_BK1_IO2

   PF6    ------> QUADSPI_BK1_IO3

   PF8    ------> QUADSPI_BK1_IO0

   PF9    ------> QUADSPI_BK1_IO1

   PB2    ------> QUADSPI_CLK

   PB6    ------> QUADSPI_BK1_NCS

   */

I am using the USART1 on PA9 (TX) and PA10(RX is inited but the pin is unconnected).

HSE is sourced from a 16MHz oscillator. I'll try using HSI per your suggestion. I think that'll be a great way to remove some variables. I will also memset my variables to remove that potential for error.

Thank you,

Don

Clive,

With some effort I've been able to get the UART working. Per your suggestion I removed the call to SystemClock_Config(). I forced the UART1 to reference the HSI, and then I manually set the contents of the BRR register since making a call to the HAL to do this inexplicably broke functionality. I also memset the UART handle to 0 before using it. I've implemented a pared down uart write function that takes a buffer, a length, and uses a polling loop on the TXE flag to write the contents onto the UART Tx.

I'm not out of the woods yet however. It seems that when passing a constant string to this function the behavior will vary from compilation to compilation and I'm quite stuck. As an example of this, if I call

UART_PrintChars("\r\nUART1 inited", 14);

My output will be (in one case):

%s[file ed

This is the beginning of a formatting string that is not currently used by my code but is still present in memory as a constant. If I look at the .bin produced by the obj-copy being run on the .elf output I can see that the characters being printed are located near the characters that should be printed. It seems to be a linking error to me so I've been reading into the documentation for GCC. Is this anything you've seen while developing an external loader?

I would move past this but since it seems that my code isn't accessing the values it thinks it should be I don't think the Init(), Read(), Write() etc. functions will work outside the testing environment when they are called by the tool.

Best,

Don

The .ELF loader in the ST tools probably isn't geared to very complex files. It typically expects a 0x20000004 base, with no relocation information. It should be a singular linear load region, with the StorageInfo being in a secondary region, which I assume isn't loaded.

You can review the linker script, the .MAP file, and perhaps dump/disassemble the file via objcopy

With Keil there is FromELF, and I'd suspect IAR has something similar.

You could compare/contrast these with the other .STLDR files

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
 
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
DWeie
Associate III

This works like a charm Clive, thank you for the help. If you have a chance would you mind walking me through what your process was? I'll need to recreate this so that I can support it for our client in case they have other requirments or would like to build from source etc. It sounds like you prefer to use Keil for your development. With such a small output size can this be done using a code-size limited version of the IDE?

This was created with some slight of hand on my part, I Frankenstein'd the existing ST loader to fit your configuration. There is a bunch of dead code in that loader, so it could arguably be shrunk further.

Yes, technically something this size could be built/rendered via the Keil evaluation tool, I'd posture that you could probably tell the compiler you're building for a CM0 part and the code would get a tad more verbose, but executable.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
DWeie
Associate III

Pretty clever! I'll see what I can do about getting this going in the free Keil evaluation tool. Thanks again.

Don

DWeie
Associate III

Clive,

I hope that my questions are not a nuisance. I haven't reverse engineered like this before so It's taking me some time to understand how you were able to edit the provided .elf file to match my pinout. I'm able to run the fromelf command to see the layout of the .elf and I'm able to see the location of Init() at 0x200012b5. If I use the GCC objdump to generate assembly I can look into the resulting file at the address and see assembly code there. In working a new pinout into the file, did you edit the assembly produced by objdump then recompile the resulting assembly? If so, how did you set the compiler up to properly generate the new .elf without it trying to take the header information etc. and turning it into bad code?