I am looking into using the NXP LPC4330 microcontroller in a future design and one of the features we need is the ability to update flash in the field using a bootloader. In the past I have done this using the NXP IAP commands, but since the 4330 uses SPIFI flash, IAP is not available.
From what I have gathered, it seems that my bootloader application will need to be executing from RAM and then I can write to flash using the SPIFI library from NXP. My question is this:
Using IAR Embedded Workbench, how can I create a program that will start from flash and then start running via RAM so I can write to the SPIFI chip? I have looked at the example below, but it is unclear how to port it over to the LPC4330. I believe I have the code copied into RAM in the startup code, but not sure how to copy the interrupt vector table to RAM or how to start executing from RAM.
https://www.iar.com/support/tech-notes/general/execute-in-ram-after-copying-from-flashrom-v5.20-and-later/
what I often do is write the main program and link it for ram only, make a complete binary. Then I write a thin shell program for flash whose entire job is to copy and jump. usually a few lines of assembly plus the contents of the binary file for the main program (I tend to write an adhoc host util to turn the main program into .word or an array of words if using C to link in with the flash program.
I have no use for IDEs so cant help you with your specific tools, I assume they have command line tools that the ide calls and you can use those and a makefile.
I figured out how this is done. Using the link in the question to get a good idea of the general process.
This is what the startup file should look like. There is an interrupt vector table in flash, with all interrupt vectors pointing to a dummy handler. Then later on there is an interrupt vector table stored in RAM which is used.
;Interrupt vector table in flash, will never be used since VTOR
;(interrupt vector table)will be mapped to RAM on startup
MODULE ?cstartup
;; Forward declaration of sections.
SECTION CSTACK:DATA:NOROOT(3)
SECTION .intvec:CODE:NOROOT(2)
EXTERN __iar_program_start
EXTERN SystemInit
PUBLIC __vector_table_RAM
PUBLIC __vector_table_0x1c
PUBLIC __Vectors
PUBLIC __Vectors_End
PUBLIC __Vectors_Size
DATA
__vector_table
DCD sfe(CSTACK)
DCD Reset_Handler
DCD NMI_Handler
DCD HardFault_Handler
DCD MemManage_Handler
DCD BusFault_Handler
DCD UsageFault_Handler
__vector_table_0x1c
DCD 0
DCD 0
DCD 0
DCD 0
DCD SVC_Handler
DCD DebugMon_Handler
DCD 0
DCD PendSV_Handler
DCD SysTick_Handler
; External Interrupts
DCD Dummy_Handler_ROM ; 16 D/A Converter
DCD Dummy_Handler_ROM ; 17 CortexM0 (LPC43XX ONLY)
DCD Dummy_Handler_ROM ; 18 General Purpose DMA
DCD 0 ; 19 Reserved
DCD Dummy_Handler_ROM ; 20 ORed flash bank A, flash bank B, EEPROM interrupts
DCD Dummy_Handler_ROM ; 21 Ethernet
DCD Dummy_Handler_ROM ; 22 SD/MMC
DCD Dummy_Handler_ROM ; 23 LCD
DCD Dummy_Handler_ROM ; 24 USB0
DCD Dummy_Handler_ROM ; 25 USB1
DCD Dummy_Handler_ROM ; 26 State Configurable Timer
DCD Dummy_Handler_ROM ; 27 Repetitive Interrupt Timer
DCD Dummy_Handler_ROM ; 28 Timer0
DCD Dummy_Handler_ROM ; 29 Timer1
DCD Dummy_Handler_ROM ; 30 Timer2
DCD Dummy_Handler_ROM ; 31 Timer3
DCD Dummy_Handler_ROM ; 32 Motor Control PWM
DCD Dummy_Handler_ROM ; 33 A/D Converter 0
DCD Dummy_Handler_ROM ; 34 I2C0
DCD Dummy_Handler_ROM ; 35 I2C1
DCD Dummy_Handler_ROM ; 36 SPI (LPC43XX ONLY)
DCD Dummy_Handler_ROM ; 37 A/D Converter 1
DCD Dummy_Handler_ROM ; 38 SSP0
DCD Dummy_Handler_ROM ; 39 SSP1
DCD Dummy_Handler_ROM ; 40 UART0
DCD Dummy_Handler_ROM ; 41 UART1
DCD Dummy_Handler_ROM ; 42 UART2
DCD Dummy_Handler_ROM ; 43 UART3
DCD Dummy_Handler_ROM ; 44 I2S0
DCD Dummy_Handler_ROM ; 45 I2S1
DCD Dummy_Handler_ROM ; 46 SPI Flash Interface
DCD Dummy_Handler_ROM ; 47 SGPIO (LPC43XX ONLY)
DCD Dummy_Handler_ROM ; 48 GPIO0
DCD Dummy_Handler_ROM ; 49 GPIO1
DCD Dummy_Handler_ROM ; 50 GPIO2
DCD Dummy_Handler_ROM ; 51 GPIO3
DCD Dummy_Handler_ROM ; 52 GPIO4
DCD Dummy_Handler_ROM ; 53 GPIO5
DCD Dummy_Handler_ROM ; 54 GPIO6
DCD Dummy_Handler_ROM ; 55 GPIO7
DCD Dummy_Handler_ROM ; 56 GINT0
DCD Dummy_Handler_ROM ; 57 GINT1
DCD Dummy_Handler_ROM ; 58 Event Router
DCD Dummy_Handler_ROM ; 59 C_CAN1
DCD 0
DCD Dummy_Handler_ROM ; 61 ADCHS combined interrupt
DCD Dummy_Handler_ROM ; 62 ATIMER
DCD Dummy_Handler_ROM ; 63 RTC
DCD 0
DCD Dummy_Handler_ROM ; 65 WDT
DCD Dummy_Handler_ROM ; 66 M0SUB TXEVT
DCD Dummy_Handler_ROM ; 67 C_CAN0
DCD Dummy_Handler_ROM ; 68 QEI
;Interrupt vector table which will be placed in RAM
SECTION .vectors_RAM:CODE:ROOT(2)
EXTERN __iar_program_start
EXTERN SystemInit
PUBLIC __vector_table
PUBLIC __vector_table_0x1c
PUBLIC __Vectors
PUBLIC __Vectors_End
PUBLIC __Vectors_Size
DATA
__vector_table_RAM
DCD sfe(CSTACK)
DCD Reset_Handler
DCD NMI_Handler
DCD HardFault_Handler
DCD MemManage_Handler
DCD BusFault_Handler
DCD UsageFault_Handler
DCD 0
DCD 0
DCD 0
DCD 0
DCD SVC_Handler
DCD DebugMon_Handler
DCD 0
DCD PendSV_Handler
DCD SysTick_Handler
; External Interrupts
DCD DAC_IRQHandler ; 16 D/A Converter
DCD M0APP_IRQHandler ; 17 CortexM0 (LPC43XX ONLY)
DCD DMA_IRQHandler ; 18 General Purpose DMA
DCD 0 ; 19 Reserved
DCD FLASH_EEPROM_IRQHandler ; 20 ORed flash bank A, flash bank B, EEPROM interrupts
DCD ETH_IRQHandler ; 21 Ethernet
DCD SDIO_IRQHandler ; 22 SD/MMC
DCD LCD_IRQHandler ; 23 LCD
DCD USB0_IRQHandler ; 24 USB0
DCD USB1_IRQHandler ; 25 USB1
DCD SCT_IRQHandler ; 26 State Configurable Timer
DCD RIT_IRQHandler ; 27 Repetitive Interrupt Timer
DCD TIMER0_IRQHandler ; 28 Timer0
DCD TIMER1_IRQHandler ; 29 Timer1
DCD TIMER2_IRQHandler ; 30 Timer2
DCD TIMER3_IRQHandler ; 31 Timer3
DCD MCPWM_IRQHandler ; 32 Motor Control PWM
DCD ADC0_IRQHandler ; 33 A/D Converter 0
DCD I2C0_IRQHandler ; 34 I2C0
DCD I2C1_IRQHandler ; 35 I2C1
DCD SPI_IRQHandler ; 36 SPI (LPC43XX ONLY)
DCD ADC1_IRQHandler ; 37 A/D Converter 1
DCD SSP0_IRQHandler ; 38 SSP0
DCD SSP1_IRQHandler ; 39 SSP1
DCD UART0_IRQHandler ; 40 UART0
DCD UART1_IRQHandler ; 41 UART1
DCD UART2_IRQHandler ; 42 UART2
DCD UART3_IRQHandler ; 43 UART3
DCD I2S0_IRQHandler ; 44 I2S0
DCD I2S1_IRQHandler ; 45 I2S1
DCD SPIFI_IRQHandler ; 46 SPI Flash Interface
DCD SGPIO_IRQHandler ; 47 SGPIO (LPC43XX ONLY)
DCD GPIO0_IRQHandler ; 48 GPIO0
DCD GPIO1_IRQHandler ; 49 GPIO1
DCD GPIO2_IRQHandler ; 50 GPIO2
DCD GPIO3_IRQHandler ; 51 GPIO3
DCD GPIO4_IRQHandler ; 52 GPIO4
DCD GPIO5_IRQHandler ; 53 GPIO5
DCD GPIO6_IRQHandler ; 54 GPIO6
DCD GPIO7_IRQHandler ; 55 GPIO7
DCD GINT0_IRQHandler ; 56 GINT0
DCD GINT1_IRQHandler ; 57 GINT1
DCD EVRT_IRQHandler ; 58 Event Router
DCD CAN1_IRQHandler ; 59 C_CAN1
DCD 0
DCD ADCHS_IRQHandler ; 61 ADCHS combined interrupt
DCD ATIMER_IRQHandler ; 62 ATIMER
DCD RTC_IRQHandler ; 63 RTC
DCD 0
DCD WDT_IRQHandler ; 65 WDT
DCD M0SUB_IRQHandler ; 66 M0SUB TXEVT
DCD CAN0_IRQHandler ; 67 C_CAN0
DCD QEI_IRQHandler ; 68 QEI
__Vectors_End
__Vectors EQU __vector_table
__Vectors_Size EQU __Vectors_End - __Vectors
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Default interrupt handlers.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
THUMB
PUBWEAK Reset_Handler
SECTION .text:CODE:REORDER:NOROOT(2)
Reset_Handler
LDR R0, =SystemInit
BLX R0
LDR R0, =__iar_program_start
BX R0
PUBWEAK NMI_Handler
PUBWEAK HardFault_Handler
PUBWEAK MemManage_Handler
PUBWEAK BusFault_Handler
PUBWEAK UsageFault_Handler
PUBWEAK SVC_Handler
PUBWEAK DebugMon_Handler
PUBWEAK PendSV_Handler
PUBWEAK SysTick_Handler
PUBWEAK DAC_IRQHandler
PUBWEAK M0APP_IRQHandler
PUBWEAK DMA_IRQHandler
PUBWEAK FLASH_EEPROM_IRQHandler
PUBWEAK ETH_IRQHandler
PUBWEAK SDIO_IRQHandler
PUBWEAK LCD_IRQHandler
PUBWEAK USB0_IRQHandler
PUBWEAK USB1_IRQHandler
PUBWEAK SCT_IRQHandler
PUBWEAK RIT_IRQHandler
PUBWEAK TIMER0_IRQHandler
PUBWEAK TIMER1_IRQHandler
PUBWEAK TIMER2_IRQHandler
PUBWEAK TIMER3_IRQHandler
PUBWEAK MCPWM_IRQHandler
PUBWEAK ADC0_IRQHandler
PUBWEAK I2C0_IRQHandler
PUBWEAK I2C1_IRQHandler
PUBWEAK SPI_IRQHandler
PUBWEAK ADC1_IRQHandler
PUBWEAK SSP0_IRQHandler
PUBWEAK SSP1_IRQHandler
PUBWEAK UART0_IRQHandler
PUBWEAK UART1_IRQHandler
PUBWEAK UART2_IRQHandler
PUBWEAK UART3_IRQHandler
PUBWEAK I2S0_IRQHandler
PUBWEAK I2S1_IRQHandler
PUBWEAK SPIFI_IRQHandler
PUBWEAK SGPIO_IRQHandler
PUBWEAK GPIO0_IRQHandler
PUBWEAK GPIO1_IRQHandler
PUBWEAK GPIO2_IRQHandler
PUBWEAK GPIO3_IRQHandler
PUBWEAK GPIO4_IRQHandler
PUBWEAK GPIO5_IRQHandler
PUBWEAK GPIO6_IRQHandler
PUBWEAK GPIO7_IRQHandler
PUBWEAK GINT0_IRQHandler
PUBWEAK GINT1_IRQHandler
PUBWEAK EVRT_IRQHandler
PUBWEAK CAN1_IRQHandler
PUBWEAK ADCHS_IRQHandler
PUBWEAK ATIMER_IRQHandler
PUBWEAK RTC_IRQHandler
PUBWEAK WDT_IRQHandler
PUBWEAK M0SUB_IRQHandler
PUBWEAK CAN0_IRQHandler
PUBWEAK QEI_IRQHandler
SECTION .text:CODE:REORDER:NOROOT(1)
NMI_Handler
B NMI_Handler
SVC_Handler
B SVC_Handler
DebugMon_Handler
B DebugMon_Handler
PendSV_Handler
B PendSV_Handler
SysTick_Handler
B SysTick_Handler
HardFault_Handler
B HardFault_Handler
MemManage_Handler
B MemManage_Handler
BusFault_Handler
B BusFault_Handler
UsageFault_Handler
DAC_IRQHandler
M0APP_IRQHandler
DMA_IRQHandler
FLASH_EEPROM_IRQHandler
ETH_IRQHandler
SDIO_IRQHandler
LCD_IRQHandler
USB0_IRQHandler
USB1_IRQHandler
SCT_IRQHandler
RIT_IRQHandler
TIMER0_IRQHandler
TIMER1_IRQHandler
TIMER2_IRQHandler
TIMER3_IRQHandler
MCPWM_IRQHandler
ADC0_IRQHandler
I2C0_IRQHandler
I2C1_IRQHandler
SPI_IRQHandler
ADC1_IRQHandler
SSP0_IRQHandler
SSP1_IRQHandler
UART0_IRQHandler
UART1_IRQHandler
UART2_IRQHandler
UART3_IRQHandler
I2S0_IRQHandler
I2S1_IRQHandler
SPIFI_IRQHandler
SGPIO_IRQHandler
GPIO0_IRQHandler
GPIO1_IRQHandler
GPIO2_IRQHandler
GPIO3_IRQHandler
GPIO4_IRQHandler
GPIO5_IRQHandler
GPIO6_IRQHandler
GPIO7_IRQHandler
GINT0_IRQHandler
GINT1_IRQHandler
EVRT_IRQHandler
CAN1_IRQHandler
ADCHS_IRQHandler
ATIMER_IRQHandler
RTC_IRQHandler
WDT_IRQHandler
CAN0_IRQHandler
M0SUB_IRQHandler
QEI_IRQHandler
Default_IRQHandler
B Default_IRQHandler
/* CRP Section - not needed for flashless devices */
;;; SECTION .crp:CODE:ROOT(2)
;;; DATA
/* Code Read Protection
NO_ISP 0x4E697370 - Prevents sampling of pin PIO0_1 for entering ISP mode
CRP1 0x12345678 - Write to RAM command cannot access RAM below 0x10000300.
- Copy RAM to flash command can not write to Sector 0.
- Erase command can erase Sector 0 only when all sectors
are selected for erase.
- Compare command is disabled.
- Read Memory command is disabled.
CRP2 0x87654321 - Read Memory is disabled.
- Write to RAM is disabled.
- "Go" command is disabled.
- Copy RAM to flash is disabled.
- Compare is disabled.
CRP3 0x43218765 - Access to chip via the SWD pins is disabled. ISP entry
by pulling PIO0_1 LOW is disabled if a valid user code is
present in flash sector 0.
Caution: If CRP3 is selected, no future factory testing can be
performed on the device.
*/
;;; DCD 0xFFFFFFFF
;;;
; --------------------
; Dummy handler placed in ROM
Dummy_Handler_ROM
b Dummy_Handler_ROM
END
Then the linker file (.icf) needs to be edited to put all of the readonly items in ram (readwrite section) as shown below. The important line is "initialize by copy"
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
/*-Specials-*/
define symbol __ICFEDIT_intvec_start__ = 0x14000000;
/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x14000140;
define symbol __ICFEDIT_region_ROM_end__ = 0x140FFFFF;
define symbol __ICFEDIT_region_RAM_start__ = 0x10000140;
define symbol __ICFEDIT_region_RAM_end__ = 0x10007FFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__ = 0x800;
define symbol __ICFEDIT_size_heap__ = 0x200;
/**** End of ICF editor section. ###ICF###*/
define symbol RAM_vectors_start = 0x10000000;
define memory mem with size = 4G;
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
initialize by copy { readonly, readwrite };
do not initialize { section .noinit };
place at address mem:__ICFEDIT_intvec_start__ { section .intvec };
place at address mem:RAM_vectors_start { section .vectors_RAM };
place in ROM_region { readonly };
place in RAM_region { readwrite,
block CSTACK, block HEAP };
After that, the only thing left to do is remap the interrupt vector table to RAM from ROM. This should be the first thing your application does, before any interrupts are enabled. The code should look like this:
VTOR = (unsigned int) 0x10000000;
Related
This is code for my wdt_handler.s
.file "_wdt_handler.c"
.text
.balign 2
.global WDT
.section __interrupt_vector_11,"ax",#progbits
.word WDT
.text
.extern redrawScreen
.extern wdt_c_handler
WDT:
; start of function
; attributes: interrupt
; framesize_regs: 24
; framesize_locals: 0
; framesize_outgoing: 0
; framesize: 24
; elim ap -> fp 26
; elim fp -> sp 0
; saved regs: R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15
; start of prologue
PUSH R15
PUSH R14
PUSH R13
PUSH R12
PUSH R11
PUSH R10
PUSH R9
PUSH R8
PUSH R7
PUSH R6
PUSH R5
PUSH R4
; end of prologue
CALL #wdt_c_handler
; start of epilogue
POP R4
POP R5
POP R6
POP R7
POP R8
POP R9
POP R10
POP R11
POP R12
POP R13
POP R14
POP R15
cmp #0, &redrawScreen
jz ball_no_move
and #0xffef, 0(r1) ; clear CPU off in saved SR
ball_no_move:
RETI
.size WDT, .-WDT
.local count
.comm count,1,1
.ident "GCC: (GNU) 4.9.1 20140707 (prerelease (msp430-14r1-364)) (GNUPro 14r1) (Based on: GCC 4.8 GDB 7.7 Binutils 2.24 Newlib 2.1)"
This is code for my main.c
void main()
{
P1DIR |= GREEN_LED; /**< Green led on when CPU on */
P1OUT |= GREEN_LED;
configureClocks();
lcd_init();
shapeInit();
p2sw_init(15);
buzzer_init();
shapeInit();
clearScreen(COLOR_WHITE);
enableWDTInterrupts(); /**< enable periodic interrupt */
or_sr(0x8); /**< GIE (enable interrupts) */
for (;;)
{
while (!redrawScreen)
{ /**< Pause CPU if screen doesn't need updating */
P1OUT &= ~GREEN_LED; /**< Green led off witHo CPU */
or_sr(0x10); /**< CPU OFF */
}
P1OUT |= GREEN_LED; /**< Green led on when CPU on */
redrawScreen = 0;
drawString8x12(0, 70, "Welcome to Lab", COLOR_RED);
drawString8x12(65, 75, "3", COLOR_RED);
randomDraw(50, 20, COLOR_BLACK);
drawRand(100, 150, COLOR_BLACK);
}
}
void wdt_c_handler()
{
u_int switches = p2sw_read();
static short count = 0;
P1OUT |= GREEN_LED; /**< Green LED on when cpu on */
count++;
if (count == 15)
{
stateAdvance_(); // assembly function not related to cpu
count = 0;
}
P1OUT &= ~GREEN_LED; /**< Green LED off when cpu off */
}
I understand in the wdt_handler.s we do a comparison for redraw screen if they're both zero then what line is executed next? what is the and #0xffef, 0(r1) doing?
Im struggling to understand how the cpu wakes up and sleeps, I know this is very vague but anything helps.
Ok so I am trying to ditch energia texas instruments arduino style ide and I have used IAR for coding a Tiva C series development board where I was able to use pointers to memory locations to perform specific things like toggling led for example. I have had a hard time doing the same on a dev board running a MSP430FR5994 mcu, I know the memory address of the green led pin to be PORT 1 PIN 1 OR P1.1 on the board. I also have included the msp430.h header file for an api to the board from my ide. What I don't understand is why when in debug my code is changing the value of the correct registers to the correct numbers but it is not altering the board. I have also verified that it is connected to the board as it will not proceed to debug with it unplugged. My direct questions are this: 1 I should be able to alter memory locations with no headerfiles or any special api's as long as I know the specific addresses correct? 2 I did not see anything about clock gating in the data sheet and in debug I can see those registers changing values so is there something other than setting the pin direction and value that I need to do?( the default pin function is generic gpio I checked so I left that register alone. Any ideas or pointing out obvious errors in my approach would be very helpful thanks. In the code below I used the header file names as I could not get the direct pointers to work. Also I was confused by the data sheet as the base address for port 1 was written as 0200H which is 5 hex numbers when I was expecting 4 since the chip is 16bit system? I assumed with the offsets it meant 0x202H etc am I incorrect in this assumption?
registers during debugging image
ti.com/lit/ds/symlink/msp430fr5994.pdf (datasheet port 1 mem locations page 130)
#include <msp430.h>
/**
* main.c
*/
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
while(1){
int i ;
int j ;
P1DIR = 2;
//*((unsigned int *)0x204Hu) = 2;
P1OUT = 2;
//*((unsigned int *)0x202Hu)= 2;
for( i = 0; i< 2 ; i++){}
P1OUT = 0;
//*((unsigned int *)0x202Hu)= 0;
for (j = 0 ; j< 2; j++){}
}
return 0;
}
See section 12.3.1 of the MSP430FR59xx User's Guide.
After a BOR reset, all port pins are high-impedance with Schmitt
triggers and their module functions disabled to prevent any cross
currents. The application must initialize all port pins including
unused ones (Section 12.3.2) as input high impedance, input with
pulldown, input with pullup, output high, or output low according to
the application needs by configuring PxDIR, PxREN, PxOUT, and PxIES
accordingly. This initialization takes effect as soon as the LOCKLPM5
bit in the PM5CTL register (described in the PMM chapter) is cleared;
until then, the I/Os remain in their high-impedance state with Schmitt
trigger inputs disabled.
And here is the example blinky code provided in the MSP430FR599x Code Examples available to download from here.
#include <msp430.h>
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop WDT
// Configure GPIO
P1OUT &= ~BIT0; // Clear P1.0 output latch for a defined power-on state
P1DIR |= BIT0; // Set P1.0 to output direction
PM5CTL0 &= ~LOCKLPM5; // Disable the GPIO power-on default high-impedance mode
// to activate previously configured port settings
while(1)
{
P1OUT ^= BIT0; // Toggle LED
__delay_cycles(100000);
}
}
You probably need to add that PM5CTL0 &= ~LOCKLPM5; line to your code.
And then single-step through your code in the debugger to observe the LED. Because if you let your code run at full speed the delay loops are way too short to observe the LED flash with your eye.
This is derived from one of my examples; I don't have a card handy to test it on, but it should just work or be close.
startup.s
.word hang /* 0xFFE0 */
.word hang /* 0xFFE2 */
.word hang /* 0xFFE4 */
.word hang /* 0xFFE6 */
.word hang /* 0xFFE8 */
.word hang /* 0xFFEA */
.word hang /* 0xFFEC */
.word hang /* 0xFFEE */
.word hang /* 0xFFF0 */
.word hang /* 0xFFF2 */
.word hang /* 0xFFF4 */
.word hang /* 0xFFF6 */
.word hang /* 0xFFF8 */
.word hang /* 0xFFFA */
.word hang /* 0xFFFC */
.word reset /* 0xFFFE */
reset.s
.global reset
reset:
mov #0x03FF,r1
call #notmain
jmp hang
.global hang
hang:
jmp hang
.globl dummy
dummy:
ret
so.c
void dummy ( unsigned short );
#define WDTCTL (*((volatile unsigned short *)0x015C))
#define P1OUT (*((volatile unsigned short *)0x0202))
#define P1DIR (*((volatile unsigned short *)0x0204))
void notmain ( void )
{
unsigned short ra;
WDTCTL = 0x5A80;
P1DIR|=0x02;
while(1)
{
P1OUT |= 0x0002;
for(ra=0;ra<10000;ra++) dummy(ra);
P1OUT &= 0xFFFD;
for(ra=0;ra<10000;ra++) dummy(ra);
}
}
memmap
MEMORY
{
rom : ORIGIN = 0xC000, LENGTH = 0xFFE0-0xC000
ram : ORIGIN = 0x1C00, LENGTH = 0x2C00-0x1C00
vect : ORIGIN = 0xFFE0, LENGTH = 0x20
}
SECTIONS
{
VECTORS : { startup.o } > vect
.text : { *(.text*) } > rom
.bss : { *(.bss*) } > ram
.data : { *(.data*) } > ram
}
build
msp430-gcc -Wall -O2 -c so.c -o so.o
msp430-ld -T memmap reset.o so.o startup.o -o so.elf
msp430-objdump -D so.elf > so.list
msp430-objcopy -O ihex so.elf out.hex
examine output
so.elf: file format elf32-msp430
Disassembly of section VECTORS:
0000ffe0 <VECTORS>:
ffe0: 0a c0 bic r0, r10
ffe2: 0a c0 bic r0, r10
ffe4: 0a c0 bic r0, r10
ffe6: 0a c0 bic r0, r10
ffe8: 0a c0 bic r0, r10
ffea: 0a c0 bic r0, r10
ffec: 0a c0 bic r0, r10
ffee: 0a c0 bic r0, r10
fff0: 0a c0 bic r0, r10
fff2: 0a c0 bic r0, r10
fff4: 0a c0 bic r0, r10
fff6: 0a c0 bic r0, r10
fff8: 0a c0 bic r0, r10
fffa: 0a c0 bic r0, r10
fffc: 0a c0 bic r0, r10
fffe: 00 c0 bic r0, r0
Disassembly of section .text:
0000c000 <reset>:
c000: 31 40 ff 03 mov #1023, r1 ;#0x03ff
c004: b0 12 0e c0 call #0xc00e
c008: 00 3c jmp $+2 ;abs 0xc00a
0000c00a <hang>:
c00a: ff 3f jmp $+0 ;abs 0xc00a
0000c00c <dummy>:
c00c: 30 41 ret
0000c00e <notmain>:
c00e: 0b 12 push r11
c010: b2 40 80 5a mov #23168, &0x015c ;#0x5a80
c014: 5c 01
c016: a2 d3 04 02 bis #2, &0x0204 ;r3 As==10
c01a: a2 d3 02 02 bis #2, &0x0202 ;r3 As==10
c01e: 0b 43 clr r11
c020: 0f 4b mov r11, r15
c022: b0 12 0c c0 call #0xc00c
c026: 1b 53 inc r11
c028: 3b 90 10 27 cmp #10000, r11 ;#0x2710
c02c: f9 23 jnz $-12 ;abs 0xc020
c02e: b2 f0 fd ff and #-3, &0x0202 ;#0xfffd
c032: 02 02
c034: 0b 43 clr r11
c036: 0f 4b mov r11, r15
c038: b0 12 0c c0 call #0xc00c
c03c: 1b 53 inc r11
c03e: 3b 90 10 27 cmp #10000, r11 ;#0x2710
c042: f9 23 jnz $-12 ;abs 0xc036
c044: ea 3f jmp $-42 ;abs 0xc01a
looks fine the vector table is there and points to the right place, etc.
10,000 might not be enough to see the led blink.
From the datasheet it appears that 0x202 is P1OUT and 0x204 is P1DIR
And you have to get it programmed into the board. I use mspdebug for the boards I have but that program may have stopped working on the eval boards from TI a while ago. And mspdebug supported the Intel hex format. So use objcopy for other formats.
If you were not wanting to use gnu tools then you still have to deal with the vector table and the bootstrap in front of the C code if you are trying to get away from someone's sandbox and do your own thing.
You are on the right path it may be as simple as your delay loops are way way too small and or getting optimized out since they are dead code as written.
If you rely on .bss or .data being initialized then you have more work to do in the linker script and bootstrap. I don't, so don't have that problem, actually wondering why .data was in this linker script...
I've matched addresses to the datasheet for your part, increasing the odds of success. If you use an external function and pass the loop variable to that function (the external can be C or asm, doesn't matter) then the optimizer won't remove it as dead code. That or add volatile on the loop variable and check the disassembly to see that it wasn't removed.
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.
The following has a priority higher than reset_handler.
Why do such a thing?
I know that datasheet of any ARM-based MCU forces that order by hardware, but why it is the highest priority?
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
I have a simple code that I am trying to compile with lm32-rtems4.11-gcc.
I have the code, the compile command and the lst below. When I compile I see a bunch of code added on the top instead of the startup code that I want in there. The code I want the processor to start with after reset is at location 3f4 instead of 0. What I wanted help on is to figure out how the rest of the code got in and find a way to remove it or move all that code to addresses after my code. I appreciate the help.
Thanks
The code:
//FILE: crt.S
.globl _start
.text
_start:
xor r0, r0, r0
mvhi sp, hi(_fstack)
ori sp, sp, lo(_fstack)
mv fp,r0
mvhi r1, hi(_fbss)
ori r1, r1, lo(_fbss)
mvhi r2, hi(_ebss)
ori r2, r2, lo(_ebss)
1:
bge r1, r2, 2f
sw (r1+0), r0
addi r1, r1, 4
bi 1b
2:
calli main
mvhi r1, 0xdead
ori r2, r0, 0xbeef
sw (r1+0), r2
//FILE: hello_world.c
void putc(char c)
{
char *tx = (char*)0xff000000;
*tx = c;
}
void puts(char *s)
{
while (*s) putc(*s++);
}
void main(void)
{
puts("Hello World\n");
}
//FILE: linker.ld
OUTPUT_FORMAT("elf32-lm32")
ENTRY(_start)
__DYNAMIC = 0;
MEMORY {
pmem : ORIGIN = 0x00000000, LENGTH = 0x8000
dmem : ORIGIN = 0x00008000, LENGTH = 0x8000
}
SECTIONS
{
.text :
{
_ftext = .;
*(.text .stub .text.* .gnu.linkonce.t.*)
_etext = .;
} > pmem
.rodata :
{
. = ALIGN(4);
_frodata = .;
*(.rodata .rodata.* .gnu.linkonce.r.*)
*(.rodata1)
_erodata = .;
} > dmem
.data :
{
. = ALIGN(4);
_fdata = .;
*(.data .data.* .gnu.linkonce.d.*)
*(.data1)
_gp = ALIGN(16);
*(.sdata .sdata.* .gnu.linkonce.s.*)
_edata = .;
} > dmem
.bss :
{
. = ALIGN(4);
_fbss = .;
*(.dynsbss)
*(.sbss .sbss.* .gnu.linkonce.sb.*)
*(.scommon)
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
_end = .;
} > dmem
}
The compile command
lm32-rtems4.11-gcc -Tlinker.ld -fno-builtin -o hello_world.elf crt.S hello_world.c
lm32-rtems4.11-objdump -DS hello_world.lst hello_world.elf
The lst file
00000000 <rtems_provides_crt0>:
#include <signal.h> /* sigset_t */
#include <time.h> /* struct timespec */
#include <unistd.h> /* isatty */
void rtems_provides_crt0( void ) {} /* dummy symbol so file always has one */
0: c3 a0 00 00 ret
00000004 <rtems_stub_malloc>:
#define RTEMS_STUB(ret, func, body) \
ret rtems_stub_##func body; \
ret func body
/* RTEMS provides some of its own routines including a Malloc family */
RTEMS_STUB(void *,malloc(size_t s), { return 0; })
4: 34 01 00 00 mvi r1,0
8: c3 a0 00 00 ret
0000000c <malloc>:
c: 34 01 00 00 mvi r1,0
10: c3 a0 00 00 ret
.
.
.
//omitting other such unrelated code that was inserted into the code and going to the
//code at 3f4 that is the code I wanted at 0
000003f0 <__assert_func>:
3f0: c3 a0 00 00 ret
000003f4 <_start>:
3f4: 98 00 00 00 xor r0,r0,r0
3f8: 78 1c 00 00 mvhi sp,0x0
3fc: 3b 9c ff fc ori sp,sp,0xfffc
400: b8 00 d8 00 mv fp,r0
404: 78 01 00 00 mvhi r1,0x0
408: 38 21 84 48 ori r1,r1,0x8448
40c: 78 02 00 00 mvhi r2,0x0
410: 38 42 84 48 ori r2,r2,0x8448
414: 4c 22 00 04 bge r1,r2,424 <_start+0x30>
418: 58 20 00 00 sw (r1+0),r0
41c: 34 21 00 04 addi r1,r1,4
420: e3 ff ff fd bi 414 <_start+0x20>
424: f8 00 00 28 calli 4c4 <main>
428: 78 01 de ad mvhi r1,0xdead
42c: 38 02 be ef mvu r2,0xbeef
430: 58 22 00 00 sw (r1+0),r2
.
.
.
As far as the .elf object you have generated is concerned, execution starts from 0x3f4, not from location 0. That's a result of your linker map specifying the entry point as the _start symbol. Whatever parses the .elf object should jump to that location when transferring execution to the program.
Now, perhaps an .elf object is not what you want to end up with - if the result isn't to be loaded by something which knows how to parse an .elf object, then you may need some other format, such as a flat binary image.
It's quite common when using a gcc elf toolchain with a small embedded chip to turn the .elf object into a flat binary using a command along the lines of
toolchain-prefix-objcopy -O binary something.elf something.bin
It's also possible you may need to create some sort of stub to jump to the _start label, and adjust your linker map to make sure that is the first thing in the image.
More generally though, you can probably find a working example for this toolchain and either this processor or a comparable one. Setting up embedded build systems from scratch is a bit tricky, so don't do it the hard way if there's any chance of finding an example to follow.
So I could not figure out why the compiler does not move the .start label to 0 when the linker.ld clearly tells it to do so. But I did figure a work around.
I created a section name for the startup code as shown in BOLD below. I then created a section in memory starting at 0 which I reserved only for this start up code. That seemed to do the trick. I ran the code and got a hello world :) . All the changes I made are in BOLD and also commented //Change 1 //Change 2 and //Change 3.
//FILE: crt.S
.section .init// Change 1
.globl _start
.text
_start:
xor r0, r0, r0
mvhi sp, hi(_fstack)
ori sp, sp, lo(_fstack)
mv fp,r0
mvhi r1, hi(_fbss)
ori r1, r1, lo(_fbss)
.
.
//linker.ld
OUTPUT_FORMAT("elf32-lm32")
ENTRY(_start)
__DYNAMIC = 0;
MEMORY {
init : ORIGIN = 0x00000000, LENGTH = 0x40 //Change 2
pmem : ORIGIN = 0x00000040, LENGTH = 0x8000
dmem : ORIGIN = 0x00008000, LENGTH = 0x8000
}
SECTIONS
{
.init : {*(.init)}>init //Change 3
.text :
{
_ftext = .;
*(.text .stub .text.* .gnu.linkonce.t.*)
_etext = .;
} > pmem