/* -*- ld-script -*-
 *
 * Linker script for MAX80 firmware
 */

#include "sys.h"

OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv",
              "elf32-littleriscv")
OUTPUT_ARCH(riscv)
ENTRY(___reset)

MEMORY
{
	SRAM	: org = SRAM_ADDR,  len = SRAM_SIZE
	DRAM	: org = SDRAM_ADDR, len = SDRAM_SIZE
	DRAM2   : org = SDRAM_ADDR+SDRAM_SIZE, len = SDRAM_SIZE
}

SECTIONS
{
	/*
	 * Sections we do not need. This program cannot exit, so
	 * fini/destructors are never needed. Exception handling
	 * is not supported.
	 */
	 /DISCARD/	: {
		*(.note.GNU-stack)
		*(.gnu_debuglink)
		*(.gnu.lto_*)
		*(.discard)
		*(.discard.*)
		*(.dtors.*)
		*(.dtors)
		*(.fini_array)
		*(.fini_array.*)
		*(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*)
		*(.eh_frame) *(.eh_frame.*)
		*(.gcc_except_table .gcc_except_table.*)
		*(.gnu_extab*)
		*(.exception_ranges*)
		*(.text.exit .text.exit.*)
		*crtbegin.o(*)
		*crt0.o(*)
	}

	PROVIDE (__executable_start = 0);

	/*
	 * Make sure the output binary starts at address 0
	 */
	.null 0 : {
		PROVIDE(___NULL = .);
		KEEP (*(SORT_NONE(.null)))
	}

	.init.reset _PC_RESET : ALIGN(4) {
		PROVIDE (___reset = .);
		KEEP (*(SORT_NONE(.init.reset)))
	}

	.init.irq _PC_IRQ : ALIGN(4) {
		PROVIDE (___irq = .);
		KEEP (*(SORT_NONE(.init.irq)))
	}

	/*
	 * Put the short data sections in the zero page.
	 * This means the initialized sections aren't contiguous, but
	 * all memory is intialized during FPGA load anyway.
	 */
	.sdata : ALIGN(4) {
		__SDATA_BEGIN__ = .;
		*(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4)
		*(.srodata.cst2) *(.srodata .srodata.*)
		*(.sdata .sdata.* .gnu.linkonce.s.*)
	}
	.sdata2         : {
		*(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
	}

	. = ALIGN(32);
	__BSS_START__ = .;
	PROVIDE (__bss_start = .);
	.sbss (NOLOAD) : ALIGN(4) {
		*(.dynsbss)
		*(.sbss .sbss.* .gnu.linkonce.sb.*)
		*(.scommon)
	}
	.sbss2 (NOLOAD) : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }

	HIDDEN($assert_zero_page = ASSERT((. <= 2048), "zero page overflow"));

	.bss (NOLOAD) : {
		*(.dynbss)
		*(.bss .bss.* .gnu.linkonce.b.*)
		*(COMMON)
	}
	. = ALIGN(32);
	__BSS_END__ = .;
	__BSS_LEN__ = __BSS_END__ - __BSS_START__;

	. = ALIGN(4);
	__BSS_END__ = .;
	__BSS_LEN__ = __BSS_END__ - __BSS_START__;

	__global_pointer$ = 0;

	PROVIDE(___text = .);

	.init : ALIGN(4) {
		KEEP (*(SORT_NONE(.init)))
	}

	.text.hot : ALIGN(4) {
		*(.text.hot .text.hot.*)
	}

	.text : ALIGN(4) {
		*(.text.startup .text.startup.*)
		*(SORT(.text.sorted.*))
		*(.text .stub .text.* .gnu.linkonce.t.*)
	        *(.text.unlikely .text.*_unlikely .text.unlikely.*)
		/* .gnu.warning sections are handled specially by elf.em.  */
		*(.gnu.warning)
	}

	PROVIDE (__etext = .);
	PROVIDE (_etext = .);
	. = ALIGN(4);
	.rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
	.rodata1        : { *(.rodata1) }

	/* Thread Local Storage sections  */
	.tdata          : {
		PROVIDE_HIDDEN (__tdata_start = .);
		*(.tdata .tdata.* .gnu.linkonce.td.*)
	}
	.tbss       : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
	.preinit_array    : {
		PROVIDE_HIDDEN (__preinit_array_start = .);
		KEEP (*(.preinit_array))
		PROVIDE_HIDDEN (__preinit_array_end = .);
	}
	.init_array    : {
		PROVIDE_HIDDEN (__init_array_start = .);
		KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*)
		SORT_BY_INIT_PRIORITY(.ctors.*)))
		KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
		PROVIDE_HIDDEN (__init_array_end = .);
	}

	/* Are these necessary/supportable? */
	  .jcr          : { KEEP (*(.jcr)) }
	.data.rel.ro	: {
			*(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*)
	}

.data           : {
		__DATA_BEGIN__ = .;
		*(.data .data.* .gnu.linkonce.d.*)
		SORT(CONSTRUCTORS)
	}
	.data1          : { *(.data1) }
	_edata = .;
	_end = .;

	HIDDEN($sram_size_assert = ASSERT(. <= STACK_BOTTOM, "SRAM overflow"));

	.stack STACK_BOTTOM : {
		KEEP (*(.stack))
	}

	/* Sections in SDRAM */
	. = SDRAM_ADDR;
	__dram_start = .;
	__dram_init_start = .;

	.dram.abcrom : AT(SRAM_SIZE) ALIGN(4) {
		__abcrom_start = .;
		KEEP(*(SORT_NONE(.dram.abcrom*)))
		__abcrom_end = .;
		/* Make sure this section is not empty */
		LONG(0xffffffff)
		LONG(0xffffffff)
		LONG(0xffffffff)
		LONG(0xffffffff)
	} >DRAM

	.dram.text : ALIGN(4) {
		*(.dram.text*)
	} >DRAM

	.dram.rodata : ALIGN(4) {
		*(.dram.rodata*)
	} >DRAM

	.dram.data : ALIGN(4) {
		*(.dram.data*)
	} >DRAM

	. = ALIGN(8);
	__dram_init_end = .;
	__dram_init_len = __dram_init_end - __dram_init_start;

	/* Keeps ld from getting confused */
	. = . + SDRAM_SIZE;

	/* Test program image - overlays */
	.dram.test __dram_init_end + SDRAM_SIZE :
		   AT(SRAM_SIZE + __dram_init_len) ALIGN(4) {
		   KEEP(*(.dram.test*))
	} >DRAM2

	/* bss can overlay the test program image */
	. = __dram_init_end;

	__dram_bss_start = .;
	.dram.bss (NOLOAD) : ALIGN(8) {
		*(.dram.bss*)
	} >DRAM
	. = ALIGN(8);
	__dram_bss_end = .;
	__dram_bss_len = __dram_bss_end - __dram_bss_start;

	/* Like BSS except no need to clear on boot */
	.dram.noinit (NOLOAD) : ALIGN(8) {
		*(.dram.noinit*)
	} >DRAM

	/* No need to zero the heap */
	.heap (NOLOAD) : ALIGN(16) {
		*(.heap)
	} >DRAM

	__dram_end = .;
}
