We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hi
I have a requirement to have a header struct containing CRC16, minor,major versions and an ident as a total of 4 x uint16_t at the start of my firmware file. This will be in each of 3 firmwares HEX files that live at different addresses within the flash starting 0x08000000.
Obviously, my bootloader will be at 0x08000000 (start of flash), but the initial 8 bytes will be the struct above. Does anyone know if this is achievable??
Micro is Cortex M3 with 1MB flash (STM32F103ZG) with RTX RTOS
My code to add my struct at 0x08000000 is thus:
//////////////////////////////////////////////
#pragma pack(1) typedef struct { uint16_t FileCRC16; uint16_t FwType; uint16_t MajorRev; uint16_t MinorRev; } str_Firmware_Header_Info_t,*pstr_Firmware_Header_Info_t;
//Enum of FirmwareTypes typedef enum { BL = 0, //BootLoader FC = 1, //FunctionalCode HI = 2, //HumanInterface BLFC = 3, BLFCHI = 4, FCHI = 5, INVALID = 6, //No Start line, or bad addresses etc UNKNOWN = 7, } FirmwareTypes;
#define BOOTLOADER_BASE_ADDRESS 0x08000000 #define CRC16_PLACEHOLDER 0x0000 #define BOOTLOADER_MajorRev 0 #define BOOTLOADER_MinorRev 1
const str_Firmware_Header_Info_t FIRMWARE_HEADER __attribute__ ((at(BOOTLOADER_BASE_ADDRESS))) = {CRC16_PLACEHOLDER,BL,BOOTLOADER_MajorRev,BOOTLOADER_MinorRev};
///////////////////////////////
In the project setting, 'Target' tab, I have IROM1 defined as Start:0x08000000 with Size:0x20000
building the above leads to a map file extract below:
/////////////////////////////////////////////
FIRMWARE_HEADER 0x08000000 Data 8 main.o(.ARM.__AT_0x08000000) __Vectors 0x08000008 Data 4 startup_stm32f10x_hd.o(RESET) __Vectors_End 0x08000138 Data 0 startup_stm32f10x_hd.o(RESET) __main 0x08000139 Thumb Code 0 entry.o(.ARM.Collect$$$$00000000) _main_stk 0x08000139 Thumb Code 0 entry2.o(.ARM.Collect$$$$00000001) _main_scatterload 0x0800013d Thumb Code 0 entry5.o(.ARM.Collect$$$$00000004) __main_after_scatterload 0x08000141 Thumb Code 0 entry5.o(.ARM.Collect$$$$00000004) _main_clock 0x08000141 Thumb Code 0 entry7b.o(.ARM.Collect$$$$00000008) _main_init 0x08000141 Thumb Code 0 entry8.o(.ARM.Collect$$$$00000009) BOOT_jump 0x0800014d Thumb Code 20 bootloader.o(.emb_text)
///////////////////////////////////////////////
Using the above, if I stick a breakpoint at the start of main(), I can step through the initial lines, but then it gets to my 'system startup task':
os_sys_init_user( System_Startup_Task, PRIORITY_ZERO, Startup_System_Task_Stack, sizeof(Startup_System_Task_Stack) );
The 'system startup task' never actually starts and the code falls unto the while(1); that sits below it.
Now, if I remove the line that creates the FIRMWARE_HEADER:
then everything works. The map file becomes:
__Vectors 0x08000000 Data 4 startup_stm32f10x_hd.o(RESET) __Vectors_End 0x08000130 Data 0 startup_stm32f10x_hd.o(RESET) __main 0x08000131 Thumb Code 0 entry.o(.ARM.Collect$$$$00000000) _main_stk 0x08000131 Thumb Code 0 entry2.o(.ARM.Collect$$$$00000001) _main_scatterload 0x08000135 Thumb Code 0 entry5.o(.ARM.Collect$$$$00000004) __main_after_scatterload 0x08000139 Thumb Code 0 entry5.o(.ARM.Collect$$$$00000004) _main_clock 0x08000139 Thumb Code 0 entry7b.o(.ARM.Collect$$$$00000008) _main_init 0x08000139 Thumb Code 0 entry8.o(.ARM.Collect$$$$00000009) BOOT_jump 0x08000145 Thumb Code 20 bootloader.o(.emb_text)
////////////////////////////////////////////
So... vectors are offset by 8 bytes (the size of my struct) in the 1st map, and placed at 0x08000000 in the second map file.
Am I barking up the wrong tree here, is there any way to have the struct at the start of my hex file?
Kind Regards
Nigel
If the boot loader "eats" intel hex, and is well written, then you could let your first lines of the hex file actually contains bytes belonging to the end of the memory area.
I normally let the boot loader process a custom binary file. The binary file have a file header that the boot loader processes and strips away before the boot loader takes care of the actual content. The boot loader may then make use of that header information and possibly write the version information last in the flash region even if the binary download file just contained continuous data for the first 50% of the download area.
Hmm. I think I will just stick it at the end. My reason for trying to find a work-around is that it was specced to go at the start, and the PC software that processes the hex files, and manually creates a UI hex file its self was coded assuming the CRC was at the start and was already tested. You live and learn!!
On another note, any idea why, if I place my struct at the end of the memory allocated to the bootloader, the area between the end of the bootloader code and the actual struct is getting padded with zero's??
Baseaddr: 0x08000000, length: 0x00020000, so my struct goes at: baseaddr+length-sizeof(struct), i.e 0x0801FFF8 through 0x0801FFFF
I do NOT have '--pad 0' defined in my project settings. I would thus have expected the following in my hex file:
EXT_LINEAR_ADDR Record (base of firmware 0x08000000) DATA Records x n (less than 64K worth) EXT_LINEAR_ADDR Record (0x08010000) (for csum struct) DATA Record 08FFF800xxxxxxxxxxxxxxCC (for csum struct) END Record
Instead, I get zero padding for the area of memory between the end of the firmware and the checksum location.
How to I get Keil to throw out a hex file without all the zero padding?
Just to update this... The zero-padding in the output hex file where there is a gap between the end of the actual firmware code and a struct/etc that has been manually fixed at an address using, for instance:
#define CRC16_PLACEHOLDER 0x0000 #define FUNCTIONALCODE_MajorRev 0 #define FUNCTIONALCODE_MinorRev 1
const str_BLFCSP_Firmware_Ident_Info_t FIRMWARE_HEADER __attribute__ ((at(FC_BASE_ADDRESS+FC_LENGTH-sizeof(str_BLFCSP_Firmware_Ident_Info_t)))) = {0,0,0,0,FC,FUNCTIONALCODE_MajorRev,FUNCTIONALCODE_MinorRev,CRC16_PLACEHOLDER};
Would appear to be a 'feature' of Keil. To force the output HEX file to NOT include said zero-padding, one needs to define IROM1 and IROM2
IROM1: 0x8010000 0xDFFF0 IROM2: 0x80EFFF0 0x10
Then, one put the struct/etc that is to be located at a specific location IN ITS OWN c file 'firmware_itent.c'. You can then right click the 'firmware_ident.c' in the project tree and change the 'code/const' settings to 'IROM2'.
The output hex file then contains:
:020000040801F1 :10000000586A0120B1190308B9190308BB1903087C :10001000BD190308BF190308C11903080000000037 :10002000000000000000000000000000A90101081D :10003000C519030800000000390201088502010803 :10004000CB190308CB190308CB190308CB190308F4 :10005000CB190308CB190308CB190308CB190308E4 :10006000CB190308CB190308CB1903089B6A0108B5 :10007000CD6A0108CB190308B76A0108CB19030838 //Lots more hex data lines, but no Zero-padding :02000004080EE4 //Ext lin addr record pointing at upper 4 bytes for location of firmware_ident :10FFF00000000000000000000200000001000000FE //Actual firmware ident data :0400000508010131BC //Record 5 - location of main() :00000001FF //end record
the PC software ... was coded assuming the CRC was at the start and was already tested
You can still use the first 4 bytes of the on-chip flash memory for your purposes. It's the initial value of the stack pointer, and it can be overridden in the startup code. The next 4 bytes are critical for normal operation since they are the entry point of your firmware. By the way, some entries in the exception vector table of the STM32F1xx are not used by the processor, so you can place arbitrary data there.
Its all sorted now - moved my struct to the end. I wanted version FW type and CRC in the same struct. There's three distinct parts to the firmware, I wanted the version structs in the same place for all.
Don't suppose you have any idea why my jump from my bootloader into my main firmware is not working...??!
Followed the docs, PC gets loaded with the right address, but it aint playing ball. I am thinking that its related to the vector table...
From my bootloader (0x08000000) I call JumpToAddress(0x08010000); to run the main code that starts from 0x08010000
//tried various different takes on this method... static __asm void Boot_Jump(uint32_t firmwareStartAddress) { LDR SP, [R0] ;Load new stack pointer address LDR PC, [R0, #4] ;Load new program counter address }
void JumpToAddress(uint32_t dwAddress) { /* Reset registers */ /* Clear all interrupts set. */
// __disable_irq();
///NVIC->ICER[0] = 0xFFFFFFFF; //NVIC->ICER[1] = 0xFFFFFFFF;
NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x010000);
/* Set new vector table */ //SCB->VTOR = (uint32_t) dwAddress;
Boot_Jump(dwAddress); }
Any thoughts??
Thanks for that, I have tried most of the things mentioned there anyway, I think I need to create a simple 'main' firmware for the bootloader to call that just toggles a port pin - see if I am actually getting into the intended code correctly.
I tried the +1 for 'thumb' mode as used on the M3, also to no avail.
Fresh head this morning so i'll take another look