January 2, 2022

Orange Pi 4 (Rockchip 3399) add a printf function

You will find the sources for this "printf" project on my Github page:

If you hadn't noticed, each of my demo projects builds on the previous one. I typically copy all the files to a new directory, then get busy. In this case I copy the blink demo as a starting point.

What I do here is to add a printf function, placing it in the file prf.c This begins something new, namely separating things into multiple C source files. I also split the uart driver out of the main C file and place it into uart.c I steal the prf.c from a previous project (the EBAZ4205 Bitcoin board). I stole it for that project from my STM32F411 project, and who knows where it came before that. That project was a 32 bit ARM board, but it is basically portable code and only one tweak is required to avoid compiler warnings.

This is only a few minutes work and now I have a simple printf available. I still just have a blink demo, so I am thinking of some more interesting things to do now that I have a printf function available.

Where is the stack?

We have done nothing special to establish the location of the stack, so we are simply inheriting whatever stack U-Boot has set up. It would be interesting to know where that is. It would be interesting to know where U-Boot is, but that is another problem altogether.

In the past I have always added a bit of assembly language code to return the value of the SP register from a function. On some reflection, I realize that this is unnecessary. Simply taking the address of a local variable in an arbitrary C function will provide the desired information. So I write some code like this:

show_stack ( int recursion )
        int dummy;

        show_reg ( "Stack: ", &dummy );
        if ( recursion )
            show_stack ( 0 );
Running it I see the following:
Stack:  F5F03B7C 00000000
Stack:  F5F03B4C 00000000
So the stack is at 0xf5f0xxxx, perhaps it was started at 0xf5f04000. Hardware registers start at 0xf8000000, so this is at the end of available DDR ram (which we have 4G of).

Clear the BSS

This is one of those things that you take for granted when writing C programs that run under a unix/linux operating system. You never see the startup code before main() runs or care much about it. And you never see the ".lds" linker script. When you do embedded programming, you have to take care of what happens in these things yourself. The lds script gathers all the BSS segments for the files it links together and defines a pair of symbols to indicate the addresses where BSS starts and ends. Code in the assembly language startup file can reference these and then clear the area to zero as expected. I add the following lines to start.S to do this:
        str             xzr, [x1], #8
        cmp             x1, x2
        bls             clear_bss_loop  // if x1 <= x2 goto clear_bss_loop

Set our own stack

This gets into more tricky issues than you might think. The bottom line ends up being a line of code like this:
	msr             SP_EL2, x0
The tricky issue is knowing what EL we are running at, since each EL has its own stack. I am pretty sure we are running at EL3, but when I worked with the Fire3, it was EL2 as shown above. There are ways to find out, but for now I will just be lazy and continue to use the U-Boot stack.
Have any comments? Questions? Drop me a line!

Tom's electronics pages / tom@mmto.org