├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── nim.cfg ├── setup.sh ├── src ├── main.nim ├── panicoverride.nim ├── rcc_pll.nim └── stm32f3 │ ├── flash.nim │ ├── memmap.nim │ ├── ptr_macros.nim │ └── rcc.nim └── stm32f303vct6.ld /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | nimcache/ 3 | *.elf 4 | *.bin 5 | *.swp 6 | cscope.out 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libopencm3"] 2 | path = libopencm3 3 | url = git://github.com/libopencm3/libopencm3 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | NIMFILE=src/main.nim 3 | 4 | NIMDEPS:= 5 | 6 | NIMDEPS += \ 7 | src/panicoverride.nim \ 8 | src/rcc_pll.nim \ 9 | src/stm32f3/rcc.nim \ 10 | src/stm32f3/memmap.nim \ 11 | src/stm32f3/ptr_macros.nim 12 | 13 | PROJECT=nim_stm32 14 | 15 | CFLAGS:= 16 | CLIBS:= 17 | CLIBDIRS:= 18 | 19 | # 20 | # Do not modify below this line. 21 | # 22 | 23 | OCM3_DIR := ./libopencm3 24 | OCM3_LIB := opencm3_stm32f3 25 | 26 | CLIBDIRS += -L$(OCM3_DIR)/lib 27 | CLIBS += -l$(OCM3_LIB) 28 | LD_SCRIPT := stm32f303vct6.ld 29 | 30 | 31 | CFLAGS += -Wl,-gc-sections,-T,$(LD_SCRIPT) 32 | CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 33 | 34 | .PHONY: clean all 35 | 36 | all: $(PROJECT).bin 37 | 38 | clean: 39 | rm -rf nimcache 40 | rm -f $(PROJECT).bin $(PROJECT).elf 41 | 42 | $(PROJECT).elf: $(NIMFILE) $(NIMDEPS) $(OCM3_DIR)/lib/lib$(OCM3_LIB).a nim.cfg 43 | nim c --nimcache=$(CURDIR)/nimcache $(NIMFILE) 44 | arm-none-eabi-gcc $(CFLAGS) -o $@ nimcache/*.o $(CLIBDIRS) $(CLIBS) 45 | 46 | $(OCM3_DIR)/lib/lib$(OCM3_LIB).a: 47 | $(MAKE) -C $(OCM3_DIR) 48 | 49 | %.bin: %.elf 50 | arm-none-eabi-objcopy $< -O binary $@ 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nim_stm32f3 2 | 3 | ## Description 4 | 5 | Small embedded Nim program running on an STM32F3. 6 | 7 | ## Building 8 | 9 | The Makefile requires the `arm-none-eabi-gcc` toolchain to be installed. To build: 10 | 11 | ``` 12 | git clone git://github.com/mwbrown/nim_stm32f3.git nim_stm32f3 13 | cd nim_stm32f3 14 | ./setup.sh 15 | make 16 | ``` 17 | 18 | ## Debugging 19 | 20 | TODO 21 | -------------------------------------------------------------------------------- /nim.cfg: -------------------------------------------------------------------------------- 1 | 2 | arm.standalone.gcc.exe="arm-none-eabi-gcc" 3 | arm.standalone.gcc.linkerexe="arm-none-eabi-gcc" 4 | 5 | --passC:"-O3 -g -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fno-common -ffunction-sections -fdata-sections -std=c11" 6 | --noMain 7 | --noLinking 8 | 9 | --debuginfo 10 | --linedir:on 11 | 12 | --os:standalone 13 | --cpu:arm 14 | --deadCodeElim:on 15 | --parallelBuild:"1" 16 | --nimcache="../" 17 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Checking out libopencm3..." 4 | git submodule update --init 5 | 6 | echo "Building libopencm3..." 7 | make --no-print-directory -C libopencm3 clean all 8 | 9 | -------------------------------------------------------------------------------- /src/main.nim: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # main.nim 4 | # 5 | # Entry point for system image. 6 | # 7 | 8 | import rcc_pll 9 | 10 | var SysTickCount {.volatile.} : cint = 0 11 | 12 | # TODO determine if Nimrod is interrupt-safe 13 | proc sys_tick_handler {.exportc.} = 14 | SysTickCount += 1 15 | 16 | # Import some systick helper functions from libopencm3. 17 | proc systick_set_reload(ticks: cint) {.importc.} 18 | proc systick_set_clocksource(enable: cint) {.importc.} 19 | proc systick_interrupt_enable() {.importc.} 20 | proc systick_counter_enable() {.importc.} 21 | 22 | # The libopencm3 library looks for a main() symbol to link against, and will 23 | # call this function after performing system initialization. 24 | proc main() : cint {.exportc.} = 25 | var myVolatileVar {.volatile.} : cint 26 | 27 | # TODO implement SysTick_Config with checking 28 | 29 | # Initialize the system clock to the PLL (72MHz). 30 | discard rcc_pll_init() 31 | 32 | systick_set_reload(8000000 * 2) 33 | systick_set_clocksource(1) 34 | systick_interrupt_enable() 35 | systick_counter_enable() 36 | 37 | # TODO add a blinking LED via RCC and GPIO functions 38 | 39 | while true: 40 | for x in 1..10: 41 | myVolatileVar = (cint)x 42 | 43 | return cint(0) 44 | -------------------------------------------------------------------------------- /src/panicoverride.nim: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # panicoverride.nim 4 | # 5 | # Provides required functions for Nimrod in standalone mode. 6 | # 7 | 8 | proc rawoutput(s: string) = 9 | discard 10 | 11 | proc panic(s: string) = 12 | rawoutput(s) 13 | 14 | while true: 15 | discard -------------------------------------------------------------------------------- /src/rcc_pll.nim: -------------------------------------------------------------------------------- 1 | 2 | import stm32f3.rcc 3 | import stm32f3.ptr_macros 4 | 5 | const HSE_STARTUP_TIMEOUT = 0x500 6 | 7 | proc rcc_pll_init*() : bool = 8 | 9 | # Enable the HSE register. 10 | volatileSetBits(RCC_CR, RCC_CR_HSEON) 11 | 12 | var startup_ctr = HSE_STARTUP_TIMEOUT 13 | while startup_ctr > 0: 14 | if volatileTestBits(RCC_CR, RCC_CR_HSERDY): 15 | break 16 | startup_ctr -= 1 17 | 18 | # Determine if the HSE timed out becoming ready. 19 | if startup_ctr == 0: 20 | return false 21 | 22 | # Enable prefetch buffer and set Flash latency. 23 | #volatileSetBits(FLASH_ACR, FLASH_ACR_PRFTBE or FLASH_ACR_LATENCY_1) 24 | 25 | # Set HCLK, PCLK2, PCLK1 26 | volatileSetBits(RCC_CFGR, RCC_CFGR_HPRE_DIV1 or RCC_CFGR_PPRE2_DIV1 or RCC_CFGR_PPRE1_DIV2) 27 | 28 | # Set PLL Configuration 29 | #volatileClrBits(RCC_CFGR, RCC_CFGR_PLLSRC or RCC_CFGR_PLLXTPRE or RCC_CFGR_PLLMULL) 30 | #volatileSetBits(RCC_CFGR, RCC_CFGR_PLLSRC_PREDIV1 or RCC_CFGR_PLLXTPRE_PREDIV1 or RCC_CFGR_PLLMULL9) 31 | 32 | # Enable PLL. 33 | volatileSetBits(RCC_CR, RCC_CR_PLLON) 34 | 35 | # Wait until PLL is ready. 36 | while not volatileTestBits(RCC_CR, RCC_CR_PLLRDY): 37 | discard 38 | 39 | # Use the PLL as the system clock source. 40 | #volatileClrBits(RCC_CFGR, RCC_CFGR_SW) 41 | #volatileSetBits(RCC_CFGR, RCC_CFGR_SW_PLL) 42 | 43 | # Wait until the PLL is the active clock. 44 | #while (volatileLoad(RCC_CFGR) and RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL: 45 | # discard 46 | 47 | return true 48 | -------------------------------------------------------------------------------- /src/stm32f3/flash.nim: -------------------------------------------------------------------------------- 1 | 2 | import memmap 3 | 4 | const FLASH_ACR = cast[ptr cuint](FLASH_MEM_INTERFACE_BASE + 0x00) 5 | const FLASH_KEYR = cast[ptr cuint](FLASH_MEM_INTERFACE_BASE + 0x04) 6 | const FLASH_OPTKEYR = cast[ptr cuint](FLASH_MEM_INTERFACE_BASE + 0x08) 7 | const FLASH_SR = cast[ptr cuint](FLASH_MEM_INTERFACE_BASE + 0x0C) 8 | const FLASH_CR = cast[ptr cuint](FLASH_MEM_INTERFACE_BASE + 0x10) 9 | const FLASH_AR = cast[ptr cuint](FLASH_MEM_INTERFACE_BASE + 0x14) 10 | const FLASH_OBR = cast[ptr cuint](FLASH_MEM_INTERFACE_BASE + 0x1C) 11 | const FLASH_WRPR = cast[ptr cuint](FLASH_MEM_INTERFACE_BASE + 0x20) 12 | 13 | #define FLASH_ACR_LATENCY_0WS 0x00 14 | #define FLASH_ACR_LATENCY_1WS 0x01 15 | #define FLASH_ACR_LATENCY_2WS 0x02 16 | #define FLASH_ACR_LATENCY_3WS 0x03 17 | #define FLASH_ACR_LATENCY_4WS 0x04 18 | #define FLASH_ACR_LATENCY_5WS 0x05 19 | #define FLASH_ACR_LATENCY_6WS 0x06 20 | #define FLASH_ACR_LATENCY_7WS 0x07 21 | 22 | #define FLASH_KEYR_KEY1 ((uint32_t)0x45670123) 23 | #define FLASH_KEYR_KEY2 ((uint32_t)0xcdef89ab) 24 | 25 | 26 | #define FLASH_ACR_PRFTBS (1 << 5) 27 | #define FLASH_ACR_PRFTBE (1 << 4) 28 | #define FLASH_ACR_HLFCYA (1 << 3) 29 | 30 | #define FLASH_SR_BSY (1 << 0) 31 | #define FLASH_SR_ERLYBSY (1 << 1) 32 | #define FLASH_SR_PGPERR (1 << 2) 33 | #define FLASH_SR_WRPRTERR (1 << 4) 34 | #define FLASH_SR_EOP (1 << 5) 35 | 36 | #define FLASH_CR_OBL_LAUNCH (1 << 13) 37 | #define FLASH_CR_EOPIE (1 << 12) 38 | #define FLASH_CR_ERRIE (1 << 10) 39 | #define FLASH_CR_OPTWRE (1 << 9) 40 | #define FLASH_CR_LOCK (1 << 7) 41 | #define FLASH_CR_STRT (1 << 6) 42 | #define FLASH_CR_OPTER (1 << 5) 43 | #define FLASH_CR_OPTPG (1 << 4) 44 | #define FLASH_CR_MER (1 << 2) 45 | #define FLASH_CR_PER (1 << 1) 46 | #define FLASH_CR_PG (1 << 0) 47 | -------------------------------------------------------------------------------- /src/stm32f3/memmap.nim: -------------------------------------------------------------------------------- 1 | 2 | const PERIPH_BASE* = 0x40000000 3 | const PERIPH_BASE_APB1* = PERIPH_BASE + 0x00000 4 | const PERIPH_BASE_APB2* = PERIPH_BASE + 0x10000 5 | const PERIPH_BASE_AHB1* = PERIPH_BASE + 0x20000 6 | const PERIPH_BASE_AHB2* = 0x48000000 7 | const PERIPH_BASE_AHB3* = 0x50000000 8 | 9 | const RCC_BASE* = PERIPH_BASE_AHB1 + 0x1000 10 | -------------------------------------------------------------------------------- /src/stm32f3/ptr_macros.nim: -------------------------------------------------------------------------------- 1 | 2 | # TODO: Make these inline 3 | 4 | proc volatileLoad*[T](address: ptr T) : T {.inline.} = 5 | 6 | when sizeof(T) == 1: 7 | {.emit: "`result` = *((volatile NU8 *)(`address`));" .} 8 | elif sizeof(T) == 2: 9 | {.emit: "`result` = *((volatile NU16 *)(`address`));" .} 10 | elif sizeof(T) == 4: 11 | {.emit: "`result` = *((volatile NU32 *)(`address`));" .} 12 | else: 13 | {.error: "Unhandled size" .} 14 | 15 | proc volatileStore*[T](address: ptr T, value: T) {.inline.} = 16 | 17 | when sizeof(T) == 1: 18 | {.emit: "*((volatile NU8 *)(`address`)) = `value`;" .} 19 | elif sizeof(T) == 2: 20 | {.emit: "*((volatile NU16 *)(`address`)) = `value`;" .} 21 | elif sizeof(T) == 4: 22 | {.emit: "*((volatile NU32 *)(`address`)) = `value`;" .} 23 | else: 24 | {.error: "Unhandled size" .} 25 | 26 | proc volatileSetBits*[T](address: ptr T, bits: T) {.inline.} = 27 | var currValue = volatileLoad(address) 28 | volatileStore(address, currValue or bits) 29 | 30 | proc volatileClrBits*[T](address: ptr T, bits: T) {.inline.} = 31 | var currValue = volatileLoad(address) 32 | volatileStore(address, currValue and (not bits)) 33 | 34 | proc volatileTestBits*[T](address: ptr T, bits: T) : bool {.inline.} = 35 | var currValue = volatileLoad(address) 36 | return (currValue and bits) == bits 37 | -------------------------------------------------------------------------------- /src/stm32f3/rcc.nim: -------------------------------------------------------------------------------- 1 | 2 | import memmap 3 | 4 | # 5 | # RCC Registers 6 | # 7 | 8 | const RCC_CR* = cast[ptr cuint](RCC_BASE + 0x00) 9 | const RCC_CFGR* = cast[ptr cuint](RCC_BASE + 0x04) 10 | const RCC_CIR* = cast[ptr cuint](RCC_BASE + 0x08) 11 | const RCC_APB2RSTR* = cast[ptr cuint](RCC_BASE + 0x0C) 12 | const RCC_APB1RSTR* = cast[ptr cuint](RCC_BASE + 0x10) 13 | const RCC_AHBENR* = cast[ptr cuint](RCC_BASE + 0x14) 14 | const RCC_APB2ENR* = cast[ptr cuint](RCC_BASE + 0x18) 15 | const RCC_APB1ENR* = cast[ptr cuint](RCC_BASE + 0x1C) 16 | const RCC_BDCR* = cast[ptr cuint](RCC_BASE + 0x20) 17 | const RCC_CSR* = cast[ptr cuint](RCC_BASE + 0x24) 18 | const RCC_AHBRSTR* = cast[ptr cuint](RCC_BASE + 0x28) 19 | const RCC_CFGR2* = cast[ptr cuint](RCC_BASE + 0x2C) 20 | const RCC_CFGR3* = cast[ptr cuint](RCC_BASE + 0x30) 21 | 22 | # 23 | # RCC_CR Values 24 | # 25 | 26 | const RCC_CR_PLLRDY* = (1 shl 25) 27 | const RCC_CR_PLLON* = (1 shl 24) 28 | const RCC_CR_CSSON* = (1 shl 19) 29 | const RCC_CR_HSEBYP* = (1 shl 18) 30 | const RCC_CR_HSERDY* = (1 shl 17) 31 | const RCC_CR_HSEON* = (1 shl 16) 32 | #/* HSICAL: [15:8] */ 33 | #/* HSITRIM: [7:3] */ 34 | const RCC_CR_HSIRDY* : cuint = (1 shl 1) 35 | const RCC_CR_HSION* = (1 shl 0) 36 | 37 | # 38 | # RCC_CFGR 39 | # 40 | 41 | const RCC_CFGR_MCOF* = (1 shl 28) 42 | const RCC_CFGR_I2SSRC* = (1 shl 23) 43 | const RCC_CFGR_USBPRES* = (1 shl 22) 44 | const RCC_CFGR_PLLXTPRE* = (1 shl 17) 45 | const RCC_CFGR_PLLSRC* = (1 shl 16) 46 | 47 | #/* MCO: Microcontroller clock output */ 48 | #define RCC_CFGR_MCO_SHIFT 24 49 | #define RCC_CFGR_MCO_MASK 0x7 50 | #define RCC_CFGR_MCO_DISABLED 0x0 51 | #/*Reserve RCC_CFGR_MCO 0x1*/ 52 | #define RCC_CFGR_MCO_LSI 0x2 53 | #define RCC_CFGR_MCO_LSE 0x3 54 | #define RCC_CFGR_MCO_SYSCLK 0x4 55 | #define RCC_CFGR_MCO_HSI 0x5 56 | #define RCC_CFGR_MCO_HSE 0x6 57 | #define RCC_CFGR_MCO_PLL 0x7 58 | 59 | #/* PLLSRC: PLL source values */ 60 | const RCC_CFGR_PLLSRC_HSI_DIV2* = 0 61 | const RCC_CFGR_PLLSRC_HSE_PREDIV1* = 1 62 | 63 | #/* PLLMUL: PLL multiplication factor */ 64 | #define RCC_CFGR_PLLMUL_SHIFT 18 65 | #define RCC_CFGR_PLLMUL_MASK 0xF 66 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X2 0x0 67 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X3 0x1 68 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X4 0x2 69 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X5 0x3 70 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X6 0x4 71 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X7 0x5 72 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X8 0x6 73 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X9 0x7 74 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X10 0x8 75 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X11 0x9 76 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X12 0xA 77 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X13 0xB 78 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X14 0xC 79 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X15 0xD 80 | #define RCC_CFGR_PLLMUL_PLL_IN_CLK_X16 0xE 81 | 82 | #/* PPRE2: APB high-speed prescaler (APB2) */ 83 | #define RCC_CFGR_PPRE2_SHIFT 11 84 | #define RCC_CFGR_PPRE2_MASK 0x7 85 | #/* 0XX: HCLK not divided */ 86 | const RCC_CFGR_PPRE2_DIV_1* = 0x0 87 | const RCC_CFGR_PPRE2_DIV_2* = 0x4 88 | const RCC_CFGR_PPRE2_DIV_4* = 0x5 89 | const RCC_CFGR_PPRE2_DIV_8* = 0x6 90 | const RCC_CFGR_PPRE2_DIV_16* = 0x7 91 | 92 | #/* PPRE1:APB Low-speed prescaler (APB1) */ 93 | #define RCC_CFGR_PPRE1_SHIFT 8 94 | #define RCC_CFGR_PPRE1_MASK 0x7 95 | #/* 0XX: HCLK not divided */ 96 | const RCC_CFGR_PPRE1_DIV_1* = 0x0 97 | const RCC_CFGR_PPRE1_DIV_2* = 0x4 98 | const RCC_CFGR_PPRE1_DIV_4* = 0x5 99 | const RCC_CFGR_PPRE1_DIV_8* = 0x6 100 | const RCC_CFGR_PPRE1_DIV_16* = 0x7 101 | 102 | #/* HPRE: HLCK prescaler */ 103 | #define RCC_CFGR_HPRE_SHIFT 4 104 | #define RCC_CFGR_HPRE_MASK 0xf 105 | #/* 0XXX: SYSCLK not divided */ 106 | const RCC_CFGR_HPRE_DIV_1* = 0x0 107 | const RCC_CFGR_HPRE_DIV_2* = 0x8 108 | const RCC_CFGR_HPRE_DIV_4* = 0x9 109 | const RCC_CFGR_HPRE_DIV_8* = 0xA 110 | const RCC_CFGR_HPRE_DIV_16* = 0xB 111 | const RCC_CFGR_HPRE_DIV_64* = 0xC 112 | const RCC_CFGR_HPRE_DIV_128* = 0xD 113 | const RCC_CFGR_HPRE_DIV_256* = 0xE 114 | const RCC_CFGR_HPRE_DIV_512* = 0xF 115 | 116 | #/* SWS: System clock switch status */ 117 | #define RCC_CFGR_SWS_SHIFT 2 118 | #define RCC_CFGR_SWS_HSI 0x0 119 | #define RCC_CFGR_SWS_HSE 0x1 120 | #define RCC_CFGR_SWS_PLL 0x2 121 | 122 | #/* SW: System clock switch */ 123 | #define RCC_CFGR_SW_SHIFT 0 124 | #define RCC_CFGR_SW_HSI 0x0 125 | #define RCC_CFGR_SW_HSE 0x1 126 | #define RCC_CFGR_SW_PLL 0x2 127 | -------------------------------------------------------------------------------- /stm32f303vct6.ld: -------------------------------------------------------------------------------- 1 | /* Linker script for STM32F3. Generated using libopencm3. */ 2 | 3 | EXTERN(vector_table) 4 | ENTRY(reset_handler) 5 | 6 | /* STM32F303 Memory Map Definition */ 7 | 8 | MEMORY 9 | { 10 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 40K 11 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 256K 12 | ccm (rwx) : ORIGIN = 0x10000000, LENGTH = 8K 13 | } 14 | 15 | SECTIONS 16 | { 17 | .text : { 18 | *(.vectors) 19 | *(.text*) 20 | . = ALIGN(4); 21 | *(.rodata*) 22 | . = ALIGN(4); 23 | } >rom 24 | 25 | .preinit_array : { 26 | . = ALIGN(4); 27 | __preinit_array_start = .; 28 | KEEP (*(.preinit_array)) 29 | __preinit_array_end = .; 30 | } >rom 31 | 32 | .init_array : { 33 | . = ALIGN(4); 34 | __init_array_start = .; 35 | KEEP (*(SORT(.init_array.*))) 36 | KEEP (*(.init_array)) 37 | __init_array_end = .; 38 | } >rom 39 | 40 | .fini_array : { 41 | . = ALIGN(4); 42 | __fini_array_start = .; 43 | KEEP (*(.fini_array)) 44 | KEEP (*(SORT(.fini_array.*))) 45 | __fini_array_end = .; 46 | } >rom 47 | 48 | .ARM.extab : { 49 | *(.ARM.extab*) 50 | } >rom 51 | 52 | .ARM.exidx : { 53 | __exidx_start = .; 54 | *(.ARM.exidx*) 55 | __exidx_end = .; 56 | } >rom 57 | 58 | . = ALIGN(4); 59 | _etext = .; 60 | 61 | .data : { 62 | _data = .; 63 | *(.data*) 64 | . = ALIGN(4); 65 | _edata = .; 66 | } >ram AT >rom 67 | 68 | _data_loadaddr = LOADADDR(.data); 69 | 70 | .bss : { 71 | *(.bss*) 72 | *(COMMON) 73 | . = ALIGN(4); 74 | _ebss = .; 75 | } >ram 76 | 77 | .ccm : { 78 | *(.ccmram*) 79 | . = ALIGN(4); 80 | } >ccm 81 | 82 | /* Discard the exception handling frame (Needed for C++ only) */ 83 | 84 | /*/DISCARD/ : { *(.eh_frame) }*/ 85 | . = ALIGN(4); 86 | end = .; 87 | } 88 | 89 | PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); 90 | --------------------------------------------------------------------------------