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".
(*)
> 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".
So the argument here is clarity.
I am a bit surprised to be asked giving reasons for "C vs assembly" argument, to be honest.
I believe this is a more accepted behaviour in Eastern Europe than in the West.
(*)
> 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".
Please note that the "stupid" remark was given to the VENDOR's startup code that they ship with their IDEs and CMSIS packages.
I understand that some assembly would be required. But definitely not the whole startup file should be in assembly. Just few snippets, like setting specific registers, and that's all. Initialising data, BSS, vector table - can be perfectly done in C, and it will be much more readable and understood code.
So the argument here is clarity. If it can be written in C, in a much more clear way than in assembly, then why shouldn't it? Is there a reason? What's the reason?
I am a bit surprised to be asked giving reasons for "C vs assembly" argument, to be honest.
QuoteI believe this is a more accepted behaviour in Eastern Europe than in the West.
Do you mean being polite is more desirable, or the opposite? There is a bunch of us ex Soviet Bloc guys in this thread
IME, the level of politeness depends strongly on where in the Soviet Bloc one is from.
In W Europe, the level of politeness depends strongly on the country, as any forum admin will tell you
OK but putting executable code into holes in the vector table must rank alongside self-modifying code, like modifying the offset in
ld a, (ix+23)
I suppose it safeguards your employment
Russian is known to be the opposite of the native british in terms of that "politeness".
There is a LOT to know to output Hello World on an embedded system!
QuoteThere is a LOT to know to output Hello World on an embedded system!
It was more of a statement meaning C is taught or someone has a desire to learn. The books all have you learn Hello World (as with other languages too), math functions, etc... but I've never seen a basic/simple explanation how to go from that to implementing that knowledge into a micro.
QuoteThere is a LOT to know to output Hello World on an embedded system!
It was more of a statement meaning C is taught or someone has a desire to learn. The books all have you learn Hello World (as with other languages too), math functions, etc... but I've never seen a basic/simple explanation how to go from that to implementing that knowledge into a micro.
"Hello World" is only basic if someone hands you a working "printf", which itself consists of thousands of individual instructions.
(and recent confusion was whether I want PICs of PICaxes).
I though that 101 is clear in this regard, and also it was aimed at answering exactly the questions @bostonman threw up above. Apparently, I failed miserably. Sorry.
and run the .exe
and run the .exe
And there is the problem