Good to see someone promote bare metal programming instead of using the manufacturers pile of garbage like STM HAL (Hardware Abstraction Layers) code. I, for sure am no fan of those
Sorry for the errors in grammar. I don't think I can improve in that too much.
@tellurium, I see your point, and I beg to disagree. IMO the main thrill in mcu programming is the physical aspect ("see the LED blinking"). Remote debugging also adds "magic", which I propose to minimize at this stage. Don't get me wrong; I understand that there are merits in remote debugging.
extern char _loader_ram_start;
extern char _loader_ram_end;
extern char _loader_flash_start;
// Copy loader code and its init data to RAM.
memcpy(&_loader_ram_start, &_loader_flash_start, &_loader_ram_end - &_loader_ram_start);
// Set SP to top of CCM. This is not where the general stack is normally but it doesn't matter because
// the loader always reboots at the end. This assignment can't be done inside loader because it trashes
// the stack frame and any local variables which are allocated *at* the call.
// NOTE local variables cannot be used after the SP load.
asm volatile ("ldr sp, = 0x10010000 \n");
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH_APP
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH_APP
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(.fini_array*))
KEEP (*(SORT(.fini_array.*)))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH_APP
/*
* Copied here from core_cm4.h.
\brief ITM Send Character
\details Transmits a character via the ITM channel 0, and
\li Just returns when no debugger is connected that has booked the output.
\li Is blocking when a debugger is connected, but the previous character sent has not been transmitted.
\param [in] ch Character to transmit.
*/
static void ITM_SendChar_2 (uint32_t ch)
{
if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */
((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */
{
while (ITM->PORT[0U].u32 == 0UL)
{
__NOP();
}
ITM->PORT[0U].u8 = (uint8_t)ch;
}
return;
}
Also, what I don't quite get is the urge to set SP. I see that regularly. But, it's set by hardware from the first word in the vector table, after all.
Being able to put pointers to standard C API functions as interrupt vector is most unusual
Startup code is to prepare everything needed to run C, so how could startup be in C if it haven't had things prepared?
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
/* SP for all of boot block
/* If we have the extra 64k (32F437) then SP is adjusted later */
ldr sp, =_estack
/* Copy the boot block initialised data from FLASH to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_si_boot_data
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_s_boot_data
ldr r3, =_e_boot_data
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
/* Zero fill the boot block bss segment. */
ldr r2, =_s_boot_bss
b LoopFillZerobss
FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _e_boot_bss
cmp r2, r3
bcc FillZerobss
/* Initialise CCM RAM - fills the whole CCM */
ldr r2, = 0x10000000 /* was _sccmram */
b LoopFillZeroCcm
FillZeroCcm:
movs r3, 0xaaaaaaaa /* this fill intentionally differs from the a5a5a5a5 fill used by FreeRTOS for its stacks */
str r3, [r2]
adds r2, r2, #4
LoopFillZeroCcm:
ldr r3, = 0x10010000 /* was _eccmram */
cmp r2, r3
bcc FillZeroCcm
/* Initialise the 8k stack at the top of the 128k RAM. This is 32F417 only */
/* For the 32F437, extra 64k, the fill is done later */
ldr r2, = 0x2001e000 /* = _estack - _Stack_Size */
b LoopFillStack
FillStack:
movs r3, 0x73737373 /* fill with 's' */
str r3, [r2]
adds r2, r2, #4
LoopFillStack:
ldr r3, = _estack /* = 0x20020000 */
cmp r2, r3
bcc FillStack
/* Call the application's entry point - in this case the "main()" in the boot loader */
bl main
bx lr
.size Reset_Handler, .-Reset_Handler
Is there any relevant mcu *application* where code portability is of any concern whatsoever?
Also, what I don't quite get is the urge to set SP. I see that regularly. But, it's set by hardware from the first word in the vector table, after all.
On a VERY limited set of hardware: Cortex-M. It's not the case on [...]
Being able to put pointers to standard C API functions as interrupt vector is most unusual, for example.
Are you referring to the way ISRs can just be written in C, with no special code needed, and implemented via the magic numbers on the stack telling the CPU that it has just exited an ISR so no "RETI" is needed?
That costs both hardware (gates) and execution time for interrupt functions that don't actually need that many registers.
But it makes training of new bare-metal programmers a little easier.
> startup_stm32f407xx.s - it is odd why this is in asm
Chicken/egg. Startup code is to prepare everything needed to run C, so how could startup be in C if it haven't had things prepared?
It is a common misconception that a startup code is required to run C.
It is perfectly possible to have an ARM code with zero assembly.
In fact I don't really understand why all these vendors keep writing their startup code in assembly, IMO it is just stupid.
The startup code can be easily written in C.
For example, look at Alex Taradov's https://github.com/ataradov/mcu-starter-projects
Good to see someone promote bare metal programming instead of using the manufacturers pile of garbage like STM HAL (Hardware Abstraction Layers) code.
src = &_etext;
dst = &_data;
while (dst < &_edata)
*dst++ = *src++;
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
(*)
> In fact I don't really understand why all these vendors keep writing their startup code in assembly, IMO it is just stupid.
Can you please explain, why do you think it's stupid? Whatever I wrote above, I am genuinely interested in what's the reasoning behind this, except "it's possible".