I know this might not be the best place for this question but I tried the Microchip forum and didn't haven't gotten a response yet. I am working trying to get an HID bootloader project working on a prototype board that I build using a PIC24FJ64GB002. I modified the example HID Bootloader project to work with my board and I modified the example HID Mouse project to work with my board as well. When I program my device with the bootloader code it runs fine and the Microchip Bootloader Windows Program finds the device and displays "Device attached.". But when I try to load the hex file of the Mouse program onto my device it says it completes successfully but the mouse program never runs. I am not sure if I am using the correct linker scripts. Has anyone done this and know what linker scripts I should be using for the bootloader project and the loadable project?
I was able to get a breadboarded PIC24FJ64GB002 working with the Microchip HID bootloader and the Microchip HID mouse app.
The key things to do are use the the correct linker script for the bootloader and the app.
Bootloader Linker changes:
MEMORY
{
...
program (xr) : ORIGIN = 0x400, LENGTH = 0x1000
app_ivt : ORIGIN = 0x1400, LENGTH = 0xC0
...
}
__CODE_BASE = 0x400;
App linker changes:
MEMORY
{
...
app_ivt : ORIGIN = 0x1400, LENGTH = 0xC0
program (xr) : ORIGIN = 0x14C0, LENGTH = 0x96E8
...
}
__CODE_BASE = 0x200;
After you load the application via the bootloader, you must reset the device.
The following code at the beginning of main() in the bootloader is what causes the bootloader to jump to the application.
mInitSwitch2();
if((sw2==1) && ((RCON & 0x83) != 0))
{
__asm__("goto 0x1400");
}
Related
I have a STM32 Nucleo-64 development board with STM32L476RG MCU on it.. I have made a small PCB that, among other things, brings the USB interface to a connector.
I have followed the steps shown here: https://www.youtube.com/watch?v=rLnQ3W8gmjY
When I run the code in debug mode (in STM32CubeIDE v1.10.1) the USB never appears on my computer. I have also probed the USB data +/- signals and see no activity.
When I step through the code, I can see that something is failing immediately in:
MX_USB_DEVICE_Init()
USBD_Init()
The code the that fails is:
USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev,
USBD_DescriptorsTypeDef *pdesc, uint8_t id)
{
USBD_StatusTypeDef ret;
/* Check whether the USB Host handle is valid */
if (pdev == NULL)
{
#if (USBD_DEBUG_LEVEL > 1U)
USBD_ErrLog("Invalid Device handle");
#endif
return USBD_FAIL;
}
The exact line above that fails is if (pdev == NULL)
One thing to note is that the CubeMX tool did NOT add a handler declaration:
USBD_HandleTypeDef hUsbDeviceFS;
...despite me configuring up the USB interface... I added my own line in main.c:
extern USBD_HandleTypeDef hUsbDeviceFS;
..thinking it may be declared elsewhere??
Can someone please help me figure out whats going on?
Thanks!
Followed a video example online, but its not working as expected. I was expecting to see the dev board appear as a USB device on my computer and spit out some text.
I am currently running into problems. So, I am making my own OS, and currently have no problems except for one.
I don't know if it's with my linker script or what, but for some reason, when I attempt to deinit a region of physical memory, QEMU simply doesn't show anything on the screen.
But, as soon as I comment the function out, it works just fine. I tried packing the binary file full of random junk as well, OS still works fine, everything displays in QEMU. I tried doing hefty function calling for things that the OS doesn't even support yet, still runs. But, for some reason, when I call my function to de-init a region of used physical memory, the whole thing just crashes and doesn't work.
I am only including the linker script below, I will link my github page to the OS project for anyone to skim through it and possible help out. This is seriously super annoying.
SECTIONS {
.text 0x1F00 :
{
*(kernel_entry);
*(.text*);
}
.idt BLOCK(.) : ALIGN(.)
{
_idt = .;
. = . * 0x100;
}
.rodata :
{
*(.rodata*);
}
.bss :
{
*(.bss*);
}
end = .;
}
I did roll my own Bootloader as well. The github layout goes as so:
All bootloader content is in folder Bootloader, all kernel content(including linker script) is in folder Kernel.
GitHub page
I use the STM32L073RZ (Nucleo 64 board).
I would like to jump into the system memory in application programming (IAP).
My code works on the revision B of the STM32L073 microcontroller but fails on the latest revision, rev Z.
I read the errata sheet, no details are given, just a limitation fixed on the dual boot mechanism into system memory according to the BFB2 bit.
Is the system memory no longer supports an IAP jumping to execute its code (to flash firmwares through USB or UART without using the BOOT0 pin) ?
The function is the first line of my main program, it tests if the code has to jump to the booloader:
void jumpBootLoader(void)
{
/* to do jump? */
if ( *((unsigned long *)0x20003FF0) == 0xDEADBEEF )
{
/* erase the label */
*((unsigned long *)0x20003FF0) = 0xCAFEFEED;
/* set stack pointer to the bootloader start address */
__set_MSP(*((uint32_t*)(0x1FF00000)));
/* system memory mapped at 0x00000000 */
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
/* jump to #bootloader + 4 */
((void (*)(void))(*((uint32_t*)(0x1FF00004))))();
}
}
I call these two lines as soon as the BP1 button is pressed to trig the jump operation after resetting the µC:
*((unsigned long *)0x20003FF0) = 0xDEADBEEF;
NVIC_SystemReset();
I use the HSI 16Mhz clock source.
The solution is to jump twice to the system memory.
First Jump to bootloader startup to initialize Data in RAM until the Program counter will returned to Flash by the Dualbank management.
Second Jump: Jump to the Dualbank bypassed address
How to use: User has first to initialize a variable “ Data_Address” (must be an offset Flash sector aligned address) in Flash to distinguish between first/second Jump.
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = Data_Address;
EraseInitStruct.NbPages = 1;
First_jump = *(__IO uint32_t *)(Data_Address);
if (First_jump == 0) {
HAL_FLASH_Unlock();
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Data_Address, 0xAAAAAAAA);
HAL_FLASH_Lock();
/* Reinitialize the Stack pointer and jump to application address */
JumpAddress = *(__IO uint32_t *)(0x1FF00004);
}
if (First_jump != 0) {
HAL_FLASH_Unlock();
HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
HAL_FLASH_Lock();
/* Reinitialize the Stack pointer and jump to application address */
JumpAddress = (0x1FF00369);
}
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t *)(0x1FF00000));
Jump_To_Application();
First important thing: you use 0x1FF0 0000 as the addres where SP is stored, this is correct. Then you use 0x1 FF00 0004 as the address from which you load the function pointer. This is not correct - one zero too many.
Note that using __set_MSP() is generally not such a good idea if you also use MSP as your stack pointer (which you most likely are). The recent definition of this function, which marks "sp" as clobbered register, causes your change to be reverted almost immediately. Incidentally today I was doing exactly the same thing you are doing and I've found that problem. In your assembly listing you'll see that SP is saved into some other register before the msr msp, ... instruction and restored right after that.
Finally I wrote that manually (STM32F4, so different addresses):
constexpr uint32_t systemMemoryBase {0x1fff0000};
asm volatile
(
" msr msp, %[sp] \n"
" bx %[pc] \n"
:: [sp] "r" (*reinterpret_cast<const uint32_t*>(systemMemoryBase)),
[pc] "r" (*reinterpret_cast<const uint32_t*>(systemMemoryBase + 4))
);
BTW - you don't need to set memory remap for the bootloader to work.
Thanks for your help. I have my answer !
The v4.0 bootloader (initial version) does not implement the dual bank mechanism but this feature is supported by v4.1.
Software can jump to bootloader but it will execute the dual boot mechanism.
So the bootloader goes back to bank1 (or bank2 if a code is "valid").
Today it is not possible to bypass the dual bank mechanism to execute the bootloader with my configuration:
The boot0 pin is reset and the protection level is 0 (see "Table 11. Boot pin and BFB2 bit configuration" in the reference manual).
Where is your program counter when you call __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH()?
Remapping a memory region while you're executing out of that same region will end poorly! You may need to relocate this code into SRAM, or execute this code with PC set to the fixed FLASH memory mapping (0x0800xxxx).
STM32 microcontrollers are capable to boot from different sources such as:
User-Flash
System-memory
Embedded-SRAM.
On the firmware side, does "boot from user Flash" means executing a custom bootloader?
No.
The "Boot from User Flash" mode means that the application code that will be run after reset is located in user flash memory. The user flash memory in that mode is aliased to start at address 0x00000000 in boot memory space. Upon reset, the top-of-stack value is fetched from address 0x00000000, and code then begins execution at address 0x00000004.
In contrast, the "Boot from System Memory" mode simply means that the system memory (not the user flash) is now aliased to start at address 0x00000000. The application code in this case must have already been loaded into system memory.
The "Boot from Embedded SRAM" mode does not alias the SRAM address. When this mode is selected, the device expects the vector table to have been relocated using the NVIC exception table and offset register, and execution begins at the start of embedded SRAM. The application code in this case must have already been loaded into embedded SRAM.
For more details, refer to the Reference Manual for the specific family of STM32 devices you are using, in the section titled "Boot Configuration".
It depends.
The option "Boot from user Flash" will run whatever is in the user flash. This code could be anything you want. It could be:
a custom bootloader or
it could be application code.
The distinction is a logical one based on what you write your code to do. i.e. it is even possible to have an app that rewrites part or all of itself (what you call it is up to you).
If you do use a custom bootloader then you will normally have two projects.
The custom bootloader will have its own projects with its own generated binary image loaded into the start of flash.
The application code also having its own project and generated .bin file.
The application is loaded into a specific address that the bootloader knows about when it was built (in some free statically allocated memory block - normally on page boundaries to allow for reflashing while keeping the bootloader unchanged). These addresses can be configured in linker files.
Custom bootloaders are a great way of allowing features like:
A custom coms stack, to reflash your application via some other coms protocol.
Dual redundancy of the application code.
Note the STM32 micros "System Memory" already contains ST's own bootloader which supports a range of serial links to reflash the micro without needing a fully custom bootloader.
ST's documentation is stupid. They mention this, but they don't also tell that you need to only configure BOOT0 & BOOT1 pins in order to select the boot source.
So these two pins binary speaking give you three options that are:
(A) BOOT0 = 0, BOOT1 = whatever,
(B) BOOT0 = 1, BOOT1 = 0 and
(C) BOOT0 = 1, BOOT1 = 1.
Option A boots in FLASH, option B boots in bootloader and option C boots in SRAM.
So how to use this. First choose configuration B to start communicating with the bootloader to whom you instruct (in bootloader's own commands) to erase FLASH and upload your program inside FLASH. Then you change the configuration to A and reboot using MCU's RESET pin.
Note:
I perfer the NXP's documentation which has everything black on white.
As a developper I value my time and good documentation is a way to go.
Ditch ST and go for NXP if documentation is what you value. Here is NXP's sample documentation:
https://www.nxp.com/docs/en/user-guide/UM10562.pdf
I am really sorry ST, but you have to do better than what you are currently doing...
STM MCUs are a bit stupid in this regard. You need to configure the Option bytes in Flash so that your MCU will always run code from the user Flash after reset.
Run this code at the beginning of your main:
if ((FLASH->OPTR & FLASH_OPTR_nSWBOOT0_Msk) != 0x0 || ((FLASH->OPTR & FLASH_OPTR_nBOOT0_Msk) == 0x0))
{
while ((FLASH->SR & FLASH_SR_BSY_Msk) != 0x0) { ; }
FLASH->KEYR = 0x45670123;
while ((FLASH->SR & FLASH_SR_BSY_Msk) != 0x0) { ; }
FLASH->KEYR = 0xCDEF89AB;
while ((FLASH->SR & FLASH_SR_BSY_Msk) != 0x0) { ; }
FLASH->OPTKEYR = 0x08192A3B;
while ((FLASH->SR & FLASH_SR_BSY_Msk) != 0x0) { ; }
FLASH->OPTKEYR = 0x4C5D6E7F;
while ((FLASH->SR & FLASH_SR_BSY_Msk) != 0x0) { ; }
FLASH->OPTR = (FLASH->OPTR & ~(FLASH_OPTR_nSWBOOT0_Msk)) | FLASH_OPTR_nBOOT0_Msk;
while ((FLASH->SR & FLASH_SR_BSY_Msk) != 0x0) { ; }
FLASH->CR = FLASH->CR | FLASH_CR_OPTSTRT;
while ((FLASH->SR & FLASH_SR_BSY_Msk) != 0x0) { ; }
}
Considering a microcontroller(in my case STM32L4 series) once the new firmware image is written into the flash, how the new start address of the application can be intimated to the bootloader?
Normally the bootloader should know the sector where the application has been written. If this is not case then there are two options:
1. When you write the firmware image to a particular sector - record in flash in a known sector the firmware sector i.e in a known sector write a struct bootloaderInfo that records the sector address of the firmware and any other info that is useful for the bootloader e.g a CRC for the firmware image, a firmware version etc. The bootloader can read this struct (its always in a known sector) and from that know where the sector where the firmware is to run.
2. The bootloader could scan flash sectors looking for the firmware image. This is not really recommended but would work. A sector with a valid firmware image will have a. a valid stack pointer and b. a valid reset vector. They could be tested with the following macros:
#define STACK_POINTER_RANGE 0x2FFE0000
#define IS_VALID_STACK_POINTER(address) (((*(volatile uint32_t*)address) & STACK_POINTER_RANGE) == SRAM_BASE)
#define IS_VALID_RESET_VECTOR(sectorAddress) \
(((*(uint32_t*)(sectorAddress+4)) & 0x8FF0000 ) == sectorAddress)
Once the application address in known the bootloader can jump to it in the normal way:
uint32_t JumpAddress = *(__IO uint32_t*)(sector + 4);
pFunction JumpToApplication = (pFunction)JumpAddress;
// Initialize user application's Stack Pointer:
__set_MSP(*(__IO uint32_t*)sector);
JumpToApplication();