I am currently trying to use a bootloader application created using MCUXPresso that requires that my application start address is located at 0x80000. According to the following documentation:
However, the .bin I generate is created with Kinetis Design Studio (an earlier version of MCUXpresso) and does not have the option to modify the vector table in such an easy way as in MCUXPresso. What I've been trying is modifying the linker file manually doing the following:
ENTRY(Reset_Handler)
/* Original Memory Map */
MEMORY
{
m_interrupts (RX) : ORIGIN = 0x00000000, LENGTH = 0x00000400
m_flash_config (RX) : ORIGIN = 0x00000400, LENGTH = 0x00000010
m_text (RX) : ORIGIN = 0x00000410, LENGTH = 0x001FFBF0
m_data (RW) : ORIGIN = 0x1FFF0000, LENGTH = 0x00030000
m_data_2 (RW) : ORIGIN = 0x20000000, LENGTH = 0x00030000
}
/* Modified Memory Map */
MEMORY
{
m_interrupts (RX) : ORIGIN = 0x00080000, LENGTH = 0x00000400
m_flash_config (RX) : ORIGIN = 0x00080400, LENGTH = 0x00000010
m_text (RX) : ORIGIN = 0x00080410, LENGTH = 0x001FFBF0
m_data (RW) : ORIGIN = 0x1FFF0000, LENGTH = 0x00030000
m_data_2 (RW) : ORIGIN = 0x20000000, LENGTH = 0x00030000
}
/* rest of linker file */
/* Define output sections */
SECTIONS
{
/* The startup code goes first into internal flash */
.interrupts :
{
__VECTOR_TABLE = .;
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} > m_interrupts
.flash_config :
{
. = ALIGN(4);
KEEP(*(.FlashConfig)) /* Flash Configuration Field (FCF) */
. = ALIGN(4);
} > m_flash_config
/* The program code and other data goes into internal flash */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
} > m_text
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > m_text
.ARM :
{
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > m_text
.ctors :
{
__CTOR_LIST__ = .;
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
from the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
__CTOR_END__ = .;
} > m_text
.dtors :
{
__DTOR_LIST__ = .;
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
__DTOR_END__ = .;
} > m_text
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} > m_text
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} > m_text
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} > m_text
__etext = .; /* define a global symbol at end of code */
__DATA_ROM = .; /* Symbol is used by startup for data initialization */
.interrupts_ram :
{
. = ALIGN(4);
__VECTOR_RAM__ = .;
__interrupts_ram_start__ = .; /* Create a global symbol at data start */
*(.m_interrupts_ram) /* This is a user defined section */
. += M_VECTOR_RAM_SIZE;
. = ALIGN(4);
__interrupts_ram_end__ = .; /* Define a global symbol at data end */
} > m_data
__VECTOR_RAM = DEFINED(__ram_vector_table__) ? __VECTOR_RAM__ : ORIGIN(m_interrupts);
__RAM_VECTOR_TABLE_SIZE_BYTES = DEFINED(__ram_vector_table__) ? (__interrupts_ram_end__ - __interrupts_ram_start__) : 0x0;
.data : AT(__DATA_ROM)
{
. = ALIGN(4);
__DATA_RAM = .;
__data_start__ = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
KEEP(*(.jcr*))
. = ALIGN(4);
__data_end__ = .; /* define a global symbol at data end */
} > m_data
__DATA_END = __DATA_ROM + (__data_end__ - __data_start__);
text_end = ORIGIN(m_text) + LENGTH(m_text);
ASSERT(__DATA_END <= text_end, "region m_text overflowed with text and data")
USB_RAM_GAP = DEFINED(__usb_ram_size__) ? __usb_ram_size__ : 0x800;
/* Uninitialized data section */
.bss :
{
/* This is used by the startup in order to initialize the .bss section */
. = ALIGN(4);
__START_BSS = .;
__bss_start__ = .;
*(.bss)
*(.bss*)
. = ALIGN(512);
USB_RAM_START = .;
. += USB_RAM_GAP;
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
__END_BSS = .;
} > m_data
.heap :
{
. = ALIGN(8);
__end__ = .;
PROVIDE(end = .);
__HeapBase = .;
. += HEAP_SIZE;
__HeapLimit = .;
__heap_limit = .; /* Add for _sbrk */
} > m_data_2
.stack :
{
. = ALIGN(8);
. += STACK_SIZE;
} > m_data_2
m_usb_bdt USB_RAM_START (NOLOAD) :
{
*(m_usb_bdt)
USB_RAM_BDT_END = .;
}
m_usb_global USB_RAM_BDT_END (NOLOAD) :
{
*(m_usb_global)
}
/* Initializes stack on the end of block */
__StackTop = ORIGIN(m_data_2) + LENGTH(m_data_2);
__StackLimit = __StackTop - STACK_SIZE;
PROVIDE(__stack = __StackTop);
.ARM.attributes 0 : { *(.ARM.attributes) }
ASSERT(__StackLimit >= __HeapLimit, "region m_data_2 overflowed with stack and heap")
}
With this implementation, the bootloader does not load the application and it restarts. I assume the error comes from an erroneous memory map in my .bin.
The memory map should comply with the following:
The startup_XXX.s file im currently using:
.syntax unified
.arch armv7-m
.section .isr_vector, "a"
.align 2
.globl __isr_vector
__isr_vector:
.long __StackTop /* Top of Stack */
.long Reset_Handler /* Reset Handler */
.long NMI_Handler /* NMI Handler*/
.long HardFault_Handler /* Hard Fault Handler*/
.long MemManage_Handler /* MPU Fault Handler*/
.long BusFault_Handler /* Bus Fault Handler*/
.long UsageFault_Handler /* Usage Fault Handler*/
.long 0 /* Reserved*/
.long 0 /* Reserved*/
.long 0 /* Reserved*/
.long 0 /* Reserved*/
.long SVC_Handler /* SVCall Handler*/
.long DebugMon_Handler /* Debug Monitor Handler*/
.long 0 /* Reserved*/
.long PendSV_Handler /* PendSV Handler*/
.long SysTick_Handler /* SysTick Handler*/
/* External Interrupts*/
.long DMA0_DMA16_IRQHandler /* DMA Channel 0, 16 Transfer Complete*/
.long DMA1_DMA17_IRQHandler /* DMA Channel 1, 17 Transfer Complete*/
.long DMA2_DMA18_IRQHandler /* DMA Channel 2, 18 Transfer Complete*/
.long DMA3_DMA19_IRQHandler /* DMA Channel 3, 19 Transfer Complete*/
.long DMA4_DMA20_IRQHandler /* DMA Channel 4, 20 Transfer Complete*/
.long DMA5_DMA21_IRQHandler /* DMA Channel 5, 21 Transfer Complete*/
.long DMA6_DMA22_IRQHandler /* DMA Channel 6, 22 Transfer Complete*/
.long DMA7_DMA23_IRQHandler /* DMA Channel 7, 23 Transfer Complete*/
.long DMA8_DMA24_IRQHandler /* DMA Channel 8, 24 Transfer Complete*/
.long DMA9_DMA25_IRQHandler /* DMA Channel 9, 25 Transfer Complete*/
.long DMA10_DMA26_IRQHandler /* DMA Channel 10, 26 Transfer Complete*/
.long DMA11_DMA27_IRQHandler /* DMA Channel 11, 27 Transfer Complete*/
.long DMA12_DMA28_IRQHandler /* DMA Channel 12, 28 Transfer Complete*/
.long DMA13_DMA29_IRQHandler /* DMA Channel 13, 29 Transfer Complete*/
.long DMA14_DMA30_IRQHandler /* DMA Channel 14, 30 Transfer Complete*/
.long DMA15_DMA31_IRQHandler /* DMA Channel 15, 31 Transfer Complete*/
.long DMA_Error_IRQHandler /* DMA Error Interrupt*/
.long MCM_IRQHandler /* Normal Interrupt*/
.long FTFE_IRQHandler /* FTFE Command complete interrupt*/
.long Read_Collision_IRQHandler /* Read Collision Interrupt*/
.long LVD_LVW_IRQHandler /* Low Voltage Detect, Low Voltage Warning*/
.long LLWU_IRQHandler /* Low Leakage Wakeup Unit*/
.long WDOG_EWM_IRQHandler /* WDOG Interrupt*/
.long RNG_IRQHandler /* RNG Interrupt*/
.long I2C0_IRQHandler /* I2C0 interrupt*/
.long I2C1_IRQHandler /* I2C1 interrupt*/
.long SPI0_IRQHandler /* SPI0 Interrupt*/
.long SPI1_IRQHandler /* SPI1 Interrupt*/
.long I2S0_Tx_IRQHandler /* I2S0 transmit interrupt*/
.long I2S0_Rx_IRQHandler /* I2S0 receive interrupt*/
.long Reserved46_IRQHandler /* Reserved interrupt 46*/
.long UART0_RX_TX_IRQHandler /* UART0 Receive/Transmit interrupt*/
.long UART0_ERR_IRQHandler /* UART0 Error interrupt*/
.long UART1_RX_TX_IRQHandler /* UART1 Receive/Transmit interrupt*/
.long UART1_ERR_IRQHandler /* UART1 Error interrupt*/
.long UART2_RX_TX_IRQHandler /* UART2 Receive/Transmit interrupt*/
.long UART2_ERR_IRQHandler /* UART2 Error interrupt*/
.long UART3_RX_TX_IRQHandler /* UART3 Receive/Transmit interrupt*/
.long UART3_ERR_IRQHandler /* UART3 Error interrupt*/
.long ADC0_IRQHandler /* ADC0 interrupt*/
.long CMP0_IRQHandler /* CMP0 interrupt*/
.long CMP1_IRQHandler /* CMP1 interrupt*/
.long FTM0_IRQHandler /* FTM0 fault, overflow and channels interrupt*/
.long FTM1_IRQHandler /* FTM1 fault, overflow and channels interrupt*/
.long FTM2_IRQHandler /* FTM2 fault, overflow and channels interrupt*/
.long CMT_IRQHandler /* CMT interrupt*/
.long RTC_IRQHandler /* RTC interrupt*/
.long RTC_Seconds_IRQHandler /* RTC seconds interrupt*/
.long PIT0_IRQHandler /* PIT timer channel 0 interrupt*/
.long PIT1_IRQHandler /* PIT timer channel 1 interrupt*/
.long PIT2_IRQHandler /* PIT timer channel 2 interrupt*/
.long PIT3_IRQHandler /* PIT timer channel 3 interrupt*/
.long PDB0_IRQHandler /* PDB0 Interrupt*/
.long USB0_IRQHandler /* USB0 interrupt*/
.long USBDCD_IRQHandler /* USBDCD Interrupt*/
.long Reserved71_IRQHandler /* Reserved interrupt 71*/
.long DAC0_IRQHandler /* DAC0 interrupt*/
.long MCG_IRQHandler /* MCG Interrupt*/
.long LPTMR0_IRQHandler /* LPTimer interrupt*/
.long PORTA_IRQHandler /* Port A interrupt*/
.long PORTB_IRQHandler /* Port B interrupt*/
.long PORTC_IRQHandler /* Port C interrupt*/
.long PORTD_IRQHandler /* Port D interrupt*/
.long PORTE_IRQHandler /* Port E interrupt*/
.long SWI_IRQHandler /* Software interrupt*/
.long SPI2_IRQHandler /* SPI2 Interrupt*/
.long UART4_RX_TX_IRQHandler /* UART4 Receive/Transmit interrupt*/
.long UART4_ERR_IRQHandler /* UART4 Error interrupt*/
.long Reserved84_IRQHandler /* Reserved interrupt 84*/
.long Reserved85_IRQHandler /* Reserved interrupt 85*/
.long CMP2_IRQHandler /* CMP2 interrupt*/
.long FTM3_IRQHandler /* FTM3 fault, overflow and channels interrupt*/
.long DAC1_IRQHandler /* DAC1 interrupt*/
.long ADC1_IRQHandler /* ADC1 interrupt*/
.long I2C2_IRQHandler /* I2C2 interrupt*/
.long CAN0_ORed_Message_buffer_IRQHandler /* CAN0 OR'd message buffers interrupt*/
.long CAN0_Bus_Off_IRQHandler /* CAN0 bus off interrupt*/
.long CAN0_Error_IRQHandler /* CAN0 error interrupt*/
.long CAN0_Tx_Warning_IRQHandler /* CAN0 Tx warning interrupt*/
.long CAN0_Rx_Warning_IRQHandler /* CAN0 Rx warning interrupt*/
.long CAN0_Wake_Up_IRQHandler /* CAN0 wake up interrupt*/
.long SDHC_IRQHandler /* SDHC interrupt*/
.long ENET_1588_Timer_IRQHandler /* Ethernet MAC IEEE 1588 Timer Interrupt*/
.long ENET_Transmit_IRQHandler /* Ethernet MAC Transmit Interrupt*/
.long ENET_Receive_IRQHandler /* Ethernet MAC Receive Interrupt*/
.long ENET_Error_IRQHandler /* Ethernet MAC Error and miscelaneous Interrupt*/
.long LPUART0_IRQHandler /* LPUART0 status/error interrupt*/
.long TSI0_IRQHandler /* TSI0 interrupt*/
.long TPM1_IRQHandler /* TPM1 fault, overflow and channels interrupt*/
.long TPM2_IRQHandler /* TPM2 fault, overflow and channels interrupt*/
.long USBHSDCD_IRQHandler /* USBHSDCD, USBHS Phy Interrupt*/
.long I2C3_IRQHandler /* I2C3 interrupt*/
.long CMP3_IRQHandler /* CMP3 interrupt*/
.long USBHS_IRQHandler /* USB high speed OTG interrupt*/
.long CAN1_ORed_Message_buffer_IRQHandler /* CAN1 OR'd message buffers interrupt*/
.long CAN1_Bus_Off_IRQHandler /* CAN1 bus off interrupt*/
.long CAN1_Error_IRQHandler /* CAN1 error interrupt*/
.long CAN1_Tx_Warning_IRQHandler /* CAN1 Tx warning interrupt*/
.long CAN1_Rx_Warning_IRQHandler /* CAN1 Rx warning interrupt*/
.long CAN1_Wake_Up_IRQHandler /* CAN1 wake up interrupt*/
.long DefaultISR /* 116*/
.long DefaultISR /* 117*/
.long DefaultISR /* 118*/
.long DefaultISR /* 119*/
.long DefaultISR /* 120*/
.long DefaultISR /* 121*/
.long DefaultISR /* 122*/
.long DefaultISR /* 123*/
.long DefaultISR /* 124*/
.long DefaultISR /* 125*/
(...)
.long DefaultISR /* 245*/
.long DefaultISR /* 246*/
.long DefaultISR /* 247*/
.long DefaultISR /* 248*/
.long DefaultISR /* 249*/
.long DefaultISR /* 250*/
.long DefaultISR /* 251*/
.long DefaultISR /* 252*/
.long DefaultISR /* 253*/
.long DefaultISR /* 254*/
.long 0xFFFFFFFF /* Reserved for user TRIM value*/
.size __isr_vector, . - __isr_vector
/* Flash Configuration */
.section .FlashConfig, "a"
.long 0xFFFFFFFF
.long 0xFFFFFFFF
.long 0xFFFFFFFF
.long 0xFFFFFFFE
.text
.thumb
/* Reset Handler */
.thumb_func
.align 2
.globl Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
cpsid i /* Mask interrupts */
.equ VTOR, 0xE000ED08
ldr r0, =VTOR
ldr r1, =__isr_vector
str r1, [r0]
ldr r2, [r1]
msr msp, r2
#ifndef __NO_SYSTEM_INIT
ldr r0,=SystemInit
blx r0
#endif
/* Loop to copy data from read only memory to RAM. The ranges
* of copy from/to are specified by following symbols evaluated in
* linker script.
* __etext: End of code section, i.e., begin of data sections to copy from.
* __data_start__/__data_end__: RAM address range that data should be
* copied to. Both must be aligned to 4 bytes boundary. */
ldr r1, =__etext
ldr r2, =__data_start__
ldr r3, =__data_end__
#if 1
/* Here are two copies of loop implemenations. First one favors code size
* and the second one favors performance. Default uses the first one.
* Change to "#if 0" to use the second one */
.LC0:
cmp r2, r3
ittt lt
ldrlt r0, [r1], #4
strlt r0, [r2], #4
blt .LC0
#else
subs r3, r2
ble .LC1
.LC0:
subs r3, #4
ldr r0, [r1, r3]
str r0, [r2, r3]
bgt .LC0
.LC1:
#endif
#ifdef __STARTUP_CLEAR_BSS
/* This part of work usually is done in C library startup code. Otherwise,
* define this macro to enable it in this startup.
*
* Loop to zero out BSS section, which uses following symbols
* in linker script:
* __bss_start__: start of BSS section. Must align to 4
* __bss_end__: end of BSS section. Must align to 4
*/
ldr r1, =__bss_start__
ldr r2, =__bss_end__
movs r0, 0
.LC2:
cmp r1, r2
itt lt
strlt r0, [r1], #4
blt .LC2
#endif /* __STARTUP_CLEAR_BSS */
cpsie i /* Unmask interrupts */
#ifndef __START
#define __START _start
#endif
#ifndef __ATOLLIC__
ldr r0,=__START
blx r0
#else
ldr r0,=__libc_init_array
blx r0
ldr r0,=main
bx r0
#endif
.pool
[Could not paste the whole file]
My attempt to solve the issue comes from this tutorial.
You should place the startup function in the position 0x80000, so the bootloader can execute it correctly.
Another problem are the interrupts. Since you have a bootloader, and I guess it is running the entire program life, you should not replace its interrupt vector. The bootloader probably have some functions to set the interrupts, so you should use those, instead of relocating the ISR vector.
Placing the startup function at a known address:
Since you are using the MCUXpresso and the KDS, I supose you are using the toolchain provided by NXP, based in GCC.
If so, you will need to use sections in order to set a function at a defined address. In the SDK, the startup function is located in the file startup_XXX.S, and in my file (I don't know if they use always the same naming) it is called Reset_Handler.
You can find it also from your ISR vector, being it the second entry (the reset entry).
In my case, it is defined as follows:
.section .reset_handler_section, "a" //EDIT 3: This is the line added
.thumb_func
.align 2
.globl Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
//Actual reset code follows
You should have something similar in your ASM code.
Now, once you know which is your startup function, you should place it at 0x80000.
This is done in your linker file, in the SECTIONS section.
But first, your new memory map should only include the memory which you are allowed to modify, this is the section called "Application Area" in the image you attached.
So, the memory map for our application should be:
MEMORY
{
m_text (RX) : ORIGIN = 0x00080000, LENGTH = 0x00080000
m_data (RWX) : ORIGIN = 0x20000000, LENGTH = 0x00030000
}
WARNING: You should know where your data (m_data) can start in RAM, since you don't want to override the bootloader data. You didn't show it in your image, so I just picked an ORIGIN in RAM, but you should check this.
Note also that there are no interrupts nor flash_config sections. I presume that the bootloader already have those, so you don't need to add them again.
Once you have defined your memory map, you can add all your program to it:
SECTIONS
{
/* The startup code*/
.startup_text :
{
. = ALIGN(4);
KEEP(*(.reset_handler_section)) /* Startup data */ /*EDIT 3: This is the modification*/
KEEP(*(.isr_vector)) /* EDIT 6: Startup code. It is needed in order to avoid modifying source files. It is not used, since the reset vector is the defined in the Bootloader build */
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
KEEP (*(.init)) /*EDIT 2: The init section. If there are more
* sections like this, just keep adding them here.
*/
} > m_text
/*EDIT 5. Added entire section*/
.ARM :
{
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > m_text
__DATA_ROM = .; /* Symbol is used by startup for data initialization */ /*EDIT 7: This symbol must be placed at the end of the text sections, so the data can follow all the code in ROM*/
__etext = .; /* define a global symbol at end of code */ /*EDIT 4*/
/*The application variables and other data in RAM*/
.data : AT(__DATA_ROM)
{
. = ALIGN(4);
__DATA_RAM = .;
__data_start__ = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
KEEP(*(.jcr*))
KEEP(*(.ramSection))
. = ALIGN(4);
__data_end__ = .; /* define a global symbol at data end */
} > m_data
/* Uninitialized data section */
.bss :
{
/* This is used by the startup in order to initialize the .bss section */
. = ALIGN(4);
__START_BSS = .;
__bss_start__ = .;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
__END_BSS = .;
} > m_data
}
As a little explanation, we are telling the linker to place all inside the section ".startup_text" (this can be any name) into the m_text memory, always in order. So, in the first address of m_text (this is, 0x80000) it will place the Reset_Handler. After it, it will place all the other functions (.text) and the constant data (rodata).
We also define the symbol __DATA_ROM to be in the last address of the section.
After all the constant data, we also append the initialised data. This data has constant values that will go in ROM, but the linker should also reserve memory for them in RAM, in order to be able to modify them. This is what is done in the data section.
Edit 1: For the linker to know where to start your program, thus being able to look what code will be needed, you have to tell it which one is the startup point of your program, since the linker does not understands about chip specific hardware (like reset vector entry). This is done by adding this to the beginning of the linker file, before the MEMORY section:
/* Entry Point */
ENTRY(Reset_Handler)
STACK_SIZE = 0x0400;
M_VECTOR_RAM_SIZE = 0x0400;
I don't know if the size's definitions are really required, but just in case I put them also here.
Adding to the solution:
The modification on the linker file in my question was indeed correct.
MEMORY
{
m_interrupts (RX) : ORIGIN = 0x00080000, LENGTH = 0x00000400
m_flash_config (RX) : ORIGIN = 0x00080400, LENGTH = 0x00000010
m_text (RX) : ORIGIN = 0x00080410, LENGTH = 0x001FFBF0
m_data (RW) : ORIGIN = 0x1FFF0000, LENGTH = 0x00030000
m_data_2 (RW) : ORIGIN = 0x20000000, LENGTH = 0x00030000
}
The problem of not loading the program came from the fact that it runs a real time operating system. Therefore I later realized that the solution to my problem was not on the linker file but in restarting the SysTick clock which seemed to affect the RTOS and therefore it did not start the loaded application.
If you are using a real time operating system, make sure to reset the system clock before loading the application from the bootloader. This seemed to solve my issues.
However, the proposed solution to the linker file does work but the problem came from a different source.
Related
I'm trying to write a bare metal blink program for a Nucleo-64 Stm32F401re board using C.
However while starting debugging for errors (it didn't blink yet) I found an odd adress for which I found no explanation. This is the output of the relevant part of the disassembly:
blink.elf: file format elf32-littlearm
Disassembly of section .text:
08000000 <isr_vector_table>:
8000000: 20018000 andcs r8, r1, r0
8000004: 08000009 stmdaeq r0, {r0, r3}
08000008 <Reset_Handler>:
8000008: b480 push {r7}
800000a: af00 add r7, sp, #0
800000c: bf00 nop
800000e: 46bd mov sp, r7
8000010: bc80 pop {r7}
8000012: 4770 bx lr
Disassembly of section .ARM.attributes:
00000000 <.ARM.attributes>:
0: 00002d41 andeq r2, r0, r1, asr #26
4: 61656100 cmnvs r5, r0, lsl #2
8: 01006962 tsteq r0, r2, ror #18
c: 00000023 andeq r0, r0, r3, lsr #32
10: 2d453705 stclcs 7, cr3, [r5, #-20] ; 0xffffffec
14: 0d06004d stceq 0, cr0, [r6, #-308] ; 0xfffffecc
18: 02094d07 andeq r4, r9, #448 ; 0x1c0
1c: 01140412 tsteq r4, r2, lsl r4
20: 03170115 tsteq r7, #1073741829 ; 0x40000005
24: 01190118 tsteq r9, r8, lsl r1
28: 061e011a ; <UNDEFINED> instruction: 0x061e011a
2c: Address 0x0000002c is out of bounds.
The Reset_Handler function itself is on the right adress but by using its name as pointer in the code it points one adress further! Here is the corresponding code:
extern int _stack_top; // bigger Memory Adress
void Reset_Handler (void);
__attribute__((section(".isr_vector"))) int* isr_vector_table[] = {
(int*)&_stack_top,
(int*)Reset_Handler
};
void Reset_Handler (void) {
}
And the Linker script I used which is basically the same used in most tutorials.
OUTPUT_ARCH(arm)
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
ENTRY(Reset_Handler)
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 96K
}
_stack_top = ORIGIN(SRAM)+LENGTH(SRAM);
SECTIONS
{
.text :
{
. = ALIGN(4);
*(.isr_vector)
*(.text*)
*(.glue_7)
*(.glue_7t)
*(.eh_frame)
KEEP(*(.init))
KEEP(*(.fini))
. = ALIGN(4);
_etext = .;
} > FLASH
.rodata :
{
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} >FLASH
.ARM :
{
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(.fini_array*))
KEEP (*(SORT(.fini_array.*)))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
. = ALIGN(4);
_sidata = LOADADDR(.data);
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data*)
. = ALIGN(4);
_edata = .;
} > SRAM AT > FLASH
.bss :
{
. = ALIGN(4);
_sbss = .;
__bss_start__ = _sbss;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
__bss_end__ = _ebss;
} > SRAM
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
So why the adress stored in the isr_vector_table is 08000009 and not 08000008?
The only way I so far could change it to the right value was through hardcoding the value or defining a extra section for the Reset_Handler so I could use the adress as another extern value like the _stack_top.
Here are the commands I used for compilation as I don't know if they are necessary to find an answer:
cd C:/bare_metal
arm-none-eabi-gcc.exe -g main.c -o blink.elf -Wall -T STM32F4.ld -mcpu=cortex-m4 -mthumb --specs=nosys.specs -nostdlib -O0
arm-none-eabi-objdump.exe -D blink.elf
From the Programming Manual PM0214 of STM32F4:
Vector table
The vector table contains the reset value of the stack
pointer, and the start addresses, also called exception vectors, for
all exception handlers. Figure 11 on page 39 shows the order of the
exception vectors in the vector table. The least-significant bit of
each vector must be 1, indicating that the exception handler is Thumb
code.
So, the LSb = 1 indicates that the instruction pointed by that vector is a Thumb instruction. Cortex-M cores support only Thumb instruction set. The compiler knows that, and makes LSb = 1 automatically. If you somehow manage to make it 0, it won't work.
I work on the project where I copy some functions to the RAM from FLASH and call them. Everything is OK except one small problem I have - if I call function directly the compiler adds the veneer call instead (which calls the funtion in the RAM correctly).
IF I call it via the pointer all is OK. The debugger shows that resolved address of the function is correct.
#define RAMFCALL(func, ...) {unsigned (* volatile fptr)() = (unsigned (* volatile)())func; fptr(__VA_ARGS__);}
RAMFCALL(FLASH_EraseSector, 0, 0);
FLASH_EraseSector(0,0);
and the corresponding calls:
311 RAMFCALL(FLASH_EraseSector, 0, 0);
0801738e: ldr r3, [pc, #88] ; (0x80173e8 <flashSTMInit+140>)
08017390: str r3, [sp, #12]
08017392: ldr r3, [sp, #12]
08017394: movs r1, #0
08017396: mov r0, r1
08017398: blx r3
312 FLASH_EraseSector(0,0);
0801739a: movs r1, #0
0801739c: mov r0, r1
0801739e: bl 0x801e9f0 <__FLASH_EraseSector_veneer>
Debugger shows the correct addresses.
and the corresponding part of the linker script
OVERLAY : NOCROSSREFS
{
.RAM_functions
{
. = ALIGN(512);
RAM_functions_load = LOADADDR(.RAM_functions);
PROVIDE(RAM_VectorTable_start = .);
KEEP(*(.RAM_VectorTable))
KEEP(*(.RAM_VectorTable*))
PROVIDE(RAM_VectorTable_end = .);
. = ALIGN(4);
RAM_functions_start = .;
KEEP(*(.RAM_functions))
KEEP(*(.RAM_functions*))
RAM_functions_end = .;
. = ALIGN(4);
RAM_functionsDATA_start = .;
KEEP(*(.RAM_functionsDATA))
KEEP(*(.RAM_functionsDATA*))
RAM_functionsDATA_end = .;
. = ALIGN(4);
RAM_functionsBUFFER_start = .;
}
/* used by the startup to initialize data */
/* Initialized data sections goes into RAM, load LMA copy after code */
.data
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
}
}>RAM AT> FLASH
And again the question: how to remove the veneer call
I will answer myself as I have found the reason :)
The bl instruction is += 32MB relative to PC. I was calling the function in the RAM from FLASH and the actual distance was much longer than 32MB. So the linker had to place the veneer function call.
Veneers could be eliminated by giving the -mlong-calls argument to the compiler. Each call site becomes a bit longer loosing some performance, however it might still be better than loosing performance in the veneers.
Individual functions can also be marked to be called through registers by applying the long_call attribute ( ARM assumed based on the assembly, decribed at https://gcc.gnu.org/onlinedocs/gcc/ARM-Function-Attributes.html#ARM-Function-Attributes )
I have a very small bootloader sitting in front of the main firmware running on a custom-designed board based around the STM32F405VGT chip. It has a fairly minimally modified startup.s and linker files for both applications. The primary application runs fine when loaded into the root of the FLASH memory, but does not launch from the bootloader.
When stepping through the code, as soon as it tries to launch the app, the program ends up in the WWDG_IRQHandler, which is aliased to the Default_Handler and just sits and spins in the infinite loop (WWDG is disabled for the bootloader).
Bootloader Code:
uint32_t addr = 0x08010000;
/* Get the application stack pointer (First entry in the application vector table) */
uint32_t appStack = (uint32_t) *((__IO uint32_t*) addr);
/* Get the application entry point (Second entry in the application vector table) */
ApplicationEntryPoint entryPoint = (ApplicationEntryPoint)*((__IO uint32_t*)(addr + sizeof(uint32_t)));
/* would expect the value of entryPoint to be 0x802bc9c based on the values in the .map file as well as the actual values downloaded from the image using openocd. Instead, it comes back as 0x802bc9d, not sure if this is related to THUMB code */
/* Reconfigure vector table offset register to match the application location */
SCB->VTOR = addr;
/* Set the application stack pointer */
__set_MSP(appStack);
/* Start the application */
entryPoint();
Here is the .ld file for the application:
/* Include memory map */
/* Uncomment this section to use the real memory map */
MEMORY
{
BOOTLOADER (rx) : ORIGIN = 0x08000000, LENGTH = 32K
USER_PROPS (rw) : ORIGIN = 0x08008000, LENGTH = 16K
SYS_PROPS (r) : ORIGIN = 0x0800C000, LENGTH = 16K
APP_CODE (rx) : ORIGIN = 0x08010000, LENGTH = 448K
SWAP (rx) : ORIGIN = 0x08070000, LENGTH = 384K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
BOOT_RAM (xrw) : ORIGIN = 0x2001E000, LENGTH = 8K
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
}
/* Uncomment this section to load directly into root memory */
/*
MEMORY
{
APP_CODE (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
}
*/
/* Highest address of the user mode stack */
_estack = 0x20020000; /* end of 128K RAM */
/* Entry Point */
ENTRY(Reset_Handler)
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x000; /* required amount of heap (none) */
_Min_Stack_Size = 0x400; /* required amount of stack */
SECTIONS
{
/* The startup code goes first into EEPROM */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >APP_CODE
/* The program code and other data goes into EEPROM */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >APP_CODE
/* Constant data goes into EEPROM */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >APP_CODE
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >APP_CODE
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >APP_CODE
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} >APP_CODE
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> APP_CODE
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
The bootloader .ld is identical, except all references to APP_CODE are replaced with BOOTLOADER
Here is the startup.s file for the bootloader. The startup.s file for the application is identical, except Boot_Reset_Handler is called Reset_Handler instead :
.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb
.global g_pfnVectors
.global Default_Handler
/* start address for the initialization values of the .data section.
defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */
/**
* #brief This is the code that gets called when the processor first
* starts execution following a reset event. Only the absolutely
* necessary set is performed, after which the application
* supplied main() routine is called.
* #param None
* #retval : None
*/
.section .text.Boot_Reset_Handler
.weak Boot_Reset_Handler
.type Boot_Reset_Handler, %function
Boot_Reset_Handler:
ldr sp, =_estack /* set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Call the clock system intitialization function.*/
bl SystemInit
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
bl main
bx lr
.size Boot_Reset_Handler, .-Boot_Reset_Handler
/**
* #brief This is the code that gets called when the processor receives an
* unexpected interrupt. This simply enters an infinite loop, preserving
* the system state for examination by a debugger.
* #param None
* #retval None
*/
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:
.word _estack
.word Boot_Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
...
I want to point out that this is not a duplicate of Bootloader for Cortex M4 - Jump to loaded Application although the problem seems similar, the author of that post did not adequately explain how the problem was resolved.
Everything is built using standard gcc tools for embedded development.
I have used the following approach on various STM32 Cortex-M3 and M4 parts:
Given the following in-line assembly function:
__asm void boot_jump( uint32_t address )
{
LDR SP, [R0] ;Load new stack pointer address
LDR PC, [R0, #4] ;Load new program counter address
}
The bootloader switches to the application image thus:
// Switch off core clock before switching vector table
SysTick->CTRL = 0 ;
// Switch off any other enabled interrupts too
...
// Switch vector table
SCB->VTOR = APPLICATION_START_ADDR ;
//Jump to start address
boot_jump( APPLICATION_START_ADDR ) ;
Where APPLICATION_START_ADDR is the base address of the application area (addr in your example); this address is the start of the application's vector table, which starts with the initial stack pointer and reset vector, the boot_jump() function loads these into the SP and PC registers to start the application as if it had been started at reset. The application's reset vector contains the application's execution start address.
The obvious difference between this and your solution is the disabling of any interrupt generators before switching the vector table. You may of course not be using any interrupts in the bootloader.
I am currently trying to learn the ways of C and am using GCC to translate C to ARM instructions. To do so, I am attempting to draw a simple shape to the window, but am having a hard time working out how to do it without using a graphics package like . Here is what I have managed to work out so far:
My make file is pretty self-explanatory I would say.
CC=/cygdrive/c/Users/Ribbyon/Desktop/gcc-arm-none-eabi-5_3-2016q1-20160330-win32/bin/arm-none-eabi-gcc
LD=/cygdrive/c/Users/Ribbyon/Desktop/gcc-arm-none-eabi-5_3-2016q1-20160330-win32/bin/arm-none-eabi-ld.exe
AS=$(CC)
OBJCOPY=/cygdrive/c/Users/Ribbyon/Desktop/gcc-arm-none-eabi-5_3-2016q1-20160330-win32/bin/arm-none-eabi-objcopy.exe
QEMU=/cygdrive/c/Users/Ribbyon/Desktop/qemu/qemu-system-arm.exe
#these should be cross-platform...
CC+= -Wall -c -mcpu=arm926ej-s -marm -Werror
LD+=-Map kernelmap.txt -T linkerscript.txt
AS+= -c -x assembler-with-cpp -mcpu=arm926ej-s
QEMUARGS=-machine integratorcp -kernel kernel.bin -serial stdio
DISPLAY?=:0
export DISPLAY
SDL_STDIO_REDIRECT=no
export SDL_STDIO_REDIRECT
all:
$(AS) kernelasm.s
$(CC) kernelc.c
$(CC) console.c
$(LD) -o kernel.tmp kernelasm.o kernelc.o console.o
$(OBJCOPY) -Obinary kernel.tmp kernel.bin
$(QEMU) $(QEMUARGS) kernel.bin
clean:
-/bin/rm *.o *.exe *.bin *.img *.tmp
Linkerscript to put it together:
ENTRY (_start)
SECTIONS {
/* The kernel will be loaded at this address in RAM.
The dot (.) means "the current location" */
. = 0x10000 ;
.text : {
/* stext = start of text (read-only) section */
stext = .;
/* .text = program code. rodata and rdata = read-only data */
*(.text)
*(.rodata)
*(.rdata)
*(.rdata$zzz)
/* etext = end of text section */
etext = .;
/* pad to a 4K boundary */
. = ALIGN( ABSOLUTE(.) , 0x1000 );
/* start of data (writable) section */
_sdata = .;
sdata = .;
*(.data)
_edata = .;
edata = .;
/* end of data section */
/* bss: Block Started by Symbol: Uninitialized data */
_sbss = . ;
sbss = . ;
*(COMMON)
*(.bss)
_ebss = . ;
ebss = . ;
}
/DISCARD/ : {
*(.eh_frame)
*(.comment)
}
}
Kernel Map (for completeness):
Discarded input sections
.comment 0x0000000000000000 0x33 kernelc.o
.comment 0x0000000000000000 0x33 console.o
Memory Configuration
Name Origin Length Attributes
*default* 0x0000000000000000 0xffffffffffffffff
Linker script and memory map
0x0000000000010000 . = 0x10000
.text 0x0000000000010000 0x2000
0x0000000000010000 stext = .
*(.text)
.text 0x0000000000010000 0x10 kernelasm.o
.text 0x0000000000010010 0x14 kernelc.o
0x0000000000010010 kmain
.text 0x0000000000010024 0x0 console.o
*(.rodata)
*(.rdata)
*(.rdata$zzz)
0x0000000000010024 etext = .
0x0000000000011000 . = ALIGN (ABSOLUTE (.), 0x1000)
*fill* 0x0000000000010024 0xfdc
0x0000000000011000 _sdata = .
0x0000000000011000 sdata = .
*(.data)
.data 0x0000000000011000 0x1000 kernelasm.o
0x0000000000012000 stack
.data 0x0000000000012000 0x0 kernelc.o
.data 0x0000000000012000 0x0 console.o
0x0000000000012000 _edata = .
0x0000000000012000 edata = .
0x0000000000012000 _sbss = .
0x0000000000012000 sbss = .
*(COMMON)
*(.bss)
.bss 0x0000000000012000 0x0 kernelasm.o
.bss 0x0000000000012000 0x0 kernelc.o
.bss 0x0000000000012000 0x0 console.o
0x0000000000012000 _ebss = .
0x0000000000012000 ebss = .
.glue_7 0x0000000000012000 0x0
.glue_7 0x0000000000012000 0x0 linker stubs
.glue_7t 0x0000000000012000 0x0
.glue_7t 0x0000000000012000 0x0 linker stubs
.vfp11_veneer 0x0000000000012000 0x0
.vfp11_veneer 0x0000000000012000 0x0 linker stubs
.v4_bx 0x0000000000012000 0x0
.v4_bx 0x0000000000012000 0x0 linker stubs
.iplt 0x0000000000012000 0x0
.iplt 0x0000000000012000 0x0 kernelasm.o
.igot.plt 0x0000000000012000 0x0
.igot.plt 0x0000000000012000 0x0 kernelasm.o
.rel.dyn 0x0000000000012000 0x0
.rel.iplt 0x0000000000012000 0x0 kernelasm.o
/DISCARD/
*(.eh_frame)
*(.comment)
LOAD kernelasm.o
LOAD kernelc.o
LOAD console.o
OUTPUT(kernel.tmp elf32-littlearm)
.ARM.attributes
0x0000000000000000 0x32
.ARM.attributes
0x0000000000000000 0x24 kernelasm.o
.ARM.attributes
0x0000000000000024 0x36 kernelc.o
.ARM.attributes
0x000000000000005a 0x36 console.o
.note.GNU-stack
0x0000000000000000 0x0
.note.GNU-stack
0x0000000000000000 0x0 kernelc.o
.note.GNU-stack
0x0000000000000000 0x0 console.o
Kernelasm:
ldr sp,=stack
b kmain
forever:
b forever
.section .data
.global stack
.rept 1024
.word 0
.end
stack:
Now, I know in C, I could do something like:
#include<graphics.h>
#include<conio.h>
main()
{
int gd = DETECT, gm;
initgraph(&gd, &gm, "C:\\TC\\BGI");
setcolor(BLUE);
rectangle(50,50,100,100);
getch();
closegraph();
return 0;
}
But since I am working with ARM, it complicates matters. As such, I am thinking I need a kernel.
The actual file that does the work:
#define blue COLOR16(0,0,255)
void console_init(){
}
void setpixel(x, y, blue){
}
The kernel I am using it in:
#include "console.h"
void kmain(){
console_init();
//draw using setpixel
while(1){
}
}
I wrote up the skeleton of it, but I am not sure where to go from here. I believe I need to isolate blue from the RBG band and display it, but I am having a hard time figuring out how to go about utilizing the logic I used in the C example for the ARM.
I think I might need to define width and height of the screen for the placement of the rectangle:
#define WIDTH 800
#define HEIGHT 600
As well as a framebuffer to do the actual communication of where to put the rectangle on the screen:
#define framebuffer ((volatile unsigned short*) (((0x07ffffff - WIDTH*HEIGHT*2))&~0xf))
And a way to assign the blue to the screen:
framebuffer[ HEIGHT/2 * WIDTH + WIDTH/2 ] = #0000FF
Could I use something like a script to grab the colors?
((b >> 3) | (r << 8) | (g << 3))
Any insight would be very helpful in getting the hang of this.
I wrote a motor controller and I tested on a respberry pi using Arch Arm Linux distro, to calculate the control signal took ~0.4ms, so I thought I can make better if I'm using real time OS, so I started with ChibiOS, but there the runtime was ~2.5ms, first I used Crossfire cross compiler than I switch to linaro, with the linaro the runtime was a bit worse ~2.7ms. What can be the problem? Is there possible that I'm not initializing the HW in an optimal way?
/*
* Stack pointers initialization.
*/
ldr r0, =__ram_end__
/* Undefined */
msr CPSR_c, #MODE_UND | I_BIT | F_BIT
mov sp, r0
ldr r1, =__und_stack_size__
sub r0, r0, r1
/* Abort */
msr CPSR_c, #MODE_ABT | I_BIT | F_BIT
mov sp, r0
ldr r1, =__abt_stack_size__
sub r0, r0, r1
/* FIQ */
msr CPSR_c, #MODE_FIQ | I_BIT | F_BIT
mov sp, r0
ldr r1, =__fiq_stack_size__
sub r0, r0, r1
/* IRQ */
msr CPSR_c, #MODE_IRQ | I_BIT | F_BIT
mov sp, r0
ldr r1, =__irq_stack_size__
sub r0, r0, r1
/* Supervisor */
msr CPSR_c, #MODE_SVC | I_BIT | F_BIT
mov sp, r0
ldr r1, =__svc_stack_size__
sub r0, r0, r1
/* System */
msr CPSR_c, #MODE_SYS | I_BIT | F_BIT
mov sp, r0
mov r0,#0x8000
mov r1,#0x0000
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
;# enable fpu
mrc p15, 0, r0, c1, c0, 2
orr r0,r0,#0x300000 ;# single precision
orr r0,r0,#0xC00000 ;# double precision
mcr p15, 0, r0, c1, c0, 2
mov r0,#0x40000000
fmxr fpexc,r0
mov r0, #0
ldr r1, =_bss_start
ldr r2, =_bss_end
And the memory setup:
__und_stack_size__ = 0x0004;
__abt_stack_size__ = 0x0004;
__fiq_stack_size__ = 0x0010;
__irq_stack_size__ = 0x0080;
__svc_stack_size__ = 0x0004;
__sys_stack_size__ = 0x0400;
__stacks_total_size__ = __und_stack_size__ + __abt_stack_size__ + __fiq_stack_size__ + __irq_stack_size__ + __svc_stack_size__ + __sys_stack_size__;
MEMORY
{
ram : org = 0x8000, len = 0x06000000 - 0x20
}
__ram_start__ = ORIGIN(ram);
__ram_size__ = LENGTH(ram);
__ram_end__ = __ram_start__ + __ram_size__;
SECTIONS
{
. = 0;
.text : ALIGN(16) SUBALIGN(16)
{
_text = .;
KEEP(*(vectors))
*(.text)
*(.text.*)
*(.rodata)
*(.rodata.*)
*(.glue_7t)
*(.glue_7)
*(.gcc*)
*(.ctors)
*(.dtors)
} > ram
.ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > ram
__exidx_start = .;
.ARM.exidx : {*(.ARM.exidx* .gnu.linkonce.armexidx.*)} > ram
__exidx_end = .;
.eh_frame_hdr : {*(.eh_frame_hdr)}
.eh_frame : ONLY_IF_RO {*(.eh_frame)}
. = ALIGN(4);
_etext = .;
_textdata = _etext;
.data :
{
_data = .;
*(.data)
. = ALIGN(4);
*(.data.*)
. = ALIGN(4);
*(.ramtext)
. = ALIGN(4);
_edata = .;
} > ram
.bss :
{
_bss_start = .;
*(.bss)
. = ALIGN(4);
*(.bss.*)
. = ALIGN(4);
*(COMMON)
. = ALIGN(4);
_bss_end = .;
} > ram
}
PROVIDE(end = .);
_end = .;
__heap_base__ = _end;
__heap_end__ = __ram_end__ - __stacks_total_size__;
__main_thread_stack_base__ = __ram_end__ - __stacks_total_size__;
Where do I make the mistake(s)?
A long time ago (yes, that means somewhen in the previous millenium), I used the old PC Speaker pcsp device driver (a little more current patch here) to control stepper motors via a relay attached to the data lines of the parallel port.
Note that's not the same driver as the current pcspkr driver (which only writes to the actual speaker, not to the parallel port); the parallel-output-capable parts of pcsp were never ported to the 2.6 audio architecture.
The trick there is that the driver can register a (high-priority, if needed) interrupt routine that does the actual device register / IO port writes to change the line state. As a result, you simply ioctl() the sample rate to the driver, and then just asynchronously write "ramps" (of data signals to step up/down to/from a certain speed or to perform a number of steps) created in-memory - the driver will then spool them for you, without the need for additional timing-/scheduling-sensitive code.
In the end you got an 8bit digital signal on the parallel port data pins, with timing precision as high as your timer interrupt allows.
There were sufficient lines to drive a stepper; if you wanted to make it turn a given number of steps, you had to:
create a "ramp up" to speed it up from still to fastest
create a "rect wave" to keep it turning
create a "ramp down" to slow it down to still again
If the number of steps was small, write the whole thing in one go, other wise, write the ramp-up, then write as many of the rect-wave blocks as needed, then the ramp down. Although you'd program possibly thousands of steps in one go, you'd only write three blocks of mem a few kB each, and the driver's interrupt handler does the rest.
It sounded rather funny if you attached a resistor-array DAC convertor ;-)
The approach can be generalized to the RaspPI; from the interrupt routine, simply write a GPIO control register (on ARM, device regs are always memory mapped, so it's simply a memory access).
Decoupling the "ramp" / "control signal" generation from the timing-sensitive state change (the "control signal application", in effect) and delegating the latter to the interrupt part of a device driver allows to do such tasks with "normal" Linux.
Your timing precision, again, is limited by rate and jitter of your timer interrupt. The RaspPI is capable of running higher timer interrupt rates than an i386 was. I'm pretty sure 1ms isn't a challenge with this approach (it wasn't in 1995). The methodology depends, as said, on the ability to precreate the signal.