Jelajahi Sumber

fw: add interrupt support; use a linker script

Add proper interrupt support.
Use a linker script to get more control over the layout of the binary.
Don't build diskio.c from fatfs; it is a sample file which does not
apply to us (all those functions are in sdcard.c).
H. Peter Anvin 3 tahun lalu
induk
melakukan
2b261e54f6
13 mengubah file dengan 3541 tambahan dan 3299 penghapusan
  1. 13 6
      fw/Makefile
  2. 3227 3227
      fw/boot.mif
  3. 12 0
      fw/dummy.c
  4. 0 0
      fw/fatfs/orig/diskio.c
  5. 10 0
      fw/fw.h
  6. 17 52
      fw/head.S
  7. 1 6
      fw/hello.c
  8. 64 0
      fw/irq.c
  9. 147 0
      fw/max80.ld
  10. 20 3
      fw/picorv32.h
  11. 19 2
      fw/sys.h
  12. 3 2
      riscv-opts.mk
  13. 8 1
      tools/Makefile

+ 13 - 6
fw/Makefile

@@ -11,13 +11,14 @@ include ../riscv-opts.mk
 CPPFLAGS  = $(INCLUDE) $(riscv_flags)
 CFLAGS    = $(CPPFLAGS)
 SFLAGS    = $(CPPFLAGS) -D__ASSEMBLY__
+LDSCRIPT  = max80.ild
 LDFLAGS   = $(CFLAGS) \
 	    -Wl,--gc-sections \
-	    -Wl,--section-start=.init=0 \
+	    -Wl,-T,$(LDSCRIPT) \
 	    -Wl,-z,common-page-size=16 \
 	    -Wl,-z,max-page-size=16
 
-gendeps   = -MD -MF .$(@F).d
+gendeps   = -MD -MF .$(@F).d -MT $@
 
 # Delete output files on error
 .DELETE_ON_ERROR:
@@ -32,7 +33,7 @@ boot_depth  := 8192
 boot_width  := 32
 boot_stride := 1
 
-boot.elf: head.o die.o sbrk.o hello.o console.o sdcard.o fatfs.a
+boot.elf: head.o die.o dummy.o irq.o sbrk.o hello.o console.o sdcard.o fatfs.a
 
 FATFS_C = $(wildcard fatfs/source/*.c)
 FATFS_O = $(FATFS_C:.c=.o)
@@ -52,8 +53,8 @@ fatfs.a: $(FATFS_O)
 %.bin: %.elf
 	$(OBJCOPY) -O binary $< $@
 
-%.elf:
-	$(CC) $(LDFLAGS) -o $@ $^
+%.elf: $(LDSCRIPT)
+	$(CC) $(LDFLAGS) -o $@ $(filter-out $(LDSCRIPT),$^)
 
 %.o: %.c
 	$(CC) $(CFLAGS) $(CFLAGS_$<) $(gendeps) -c -o $@ $<
@@ -70,6 +71,12 @@ fatfs.a: $(FATFS_O)
 %.s: %.S
 	$(CC) $(SFLAGS) $(SFLAGS_$<) $(gendeps) -E -o $@ $<
 
+%.ild: %.ld
+	$(CC) $(CFLAGS) $(CFLAGS_$<) $(gendeps) \
+		-x assembler-with-cpp \
+		-fdollars-in-identifiers \
+		-C -P -E $< | $(PERL) -pe 's:^(#.*)$$:/* $$1 */:' > $@
+
 testdata.bin: testdata.pl
 	$(PERL) $< > $@
 
@@ -77,7 +84,7 @@ testdata.hex: testdata.bin
 	$(OBJCOPY) -I binary -O ihex $< $@
 
 clean:
-	rm -f *.o *.i *.s *.elf *.bin .*.d
+	rm -f *.o *.i *.s *.elf *.bin .*.d *.ild
 
 spotless: clean
 	rm -f *.mem *.hex *.mif

File diff ditekan karena terlalu besar
+ 3227 - 3227
fw/boot.mif


+ 12 - 0
fw/dummy.c

@@ -0,0 +1,12 @@
+/*
+ * Dummy replacements for some newlib functions which we neither need
+ * nor want.
+ */
+
+void __dummy(void)
+{
+}
+
+#pragma GCC diagnostic ignored "-Wattribute-alias"
+void __do_global_dtors_aux(void) __attribute__((alias("__dummy")));
+void frame_dummy(void) __attribute__((alias("__dummy")));

+ 0 - 0
fw/fatfs/source/diskio.c → fw/fatfs/orig/diskio.c


+ 10 - 0
fw/fw.h

@@ -1,17 +1,27 @@
 #ifndef FW_H
 #define FW_H
 
+#include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <stdbool.h>
 
 #define likely(x)	__builtin_expect(!!(x), 1)
 #define unlikely(x)	__builtin_expect(!!(x), 0)
 
 extern void __attribute__((noreturn)) _die(void);
+extern void __attribute__((noreturn)) exit(int);
+extern void __attribute__((noreturn)) _exit(int);
 
 extern const uint8_t _end[];
 extern void *_sbrk(size_t);
 
 extern int disk_init(void);
 
+#define IRQ_VECTORS	32
+
+typedef bool (*irq_handler_t)(unsigned int vector);
+
+extern irq_handler_t register_irq(unsigned int vector, irq_handler_t handler);
+
 #endif /* FW_H */

+ 17 - 52
fw/head.S

@@ -2,20 +2,21 @@
 #include "picorv32.h"
 
 	// The linker ensures that section .init is first
-	.section ".init","ax"
-	.org 0
+	.section ".init.reset","ax"
 	.globl _reset
 _reset:
 	rdtime t0		// Record timer at reset
-	li sp,SRAM_SIZE
-	la t1,time_zero
-	sw t0,(t1)
+	li sp,STACK_TOP		// Cheaper than using la sp,___stack_top
+	.option push
+	.option norelax		// Can't make gp references to set up gp...
+	la gp, __global_pointer$
+	.option pop
+	sw t0, time_zero, t1
 	j _start
 	.type _reset, @function
 	.size _reset, . - _reset
 
-	.pushsection ".rodata","a"
-	// Not really readonly, but close enough
+	.pushsection ".sdata","a"
 	.balign 4
 	.globl time_zero
 time_zero:
@@ -23,50 +24,14 @@ time_zero:
 	.type time_zero, @object
 	.size time_zero, . - time_zero
 	.popsection
-	
-	.org 0x20
-	.globl __irq
-__irq:
-	add sp,sp,-64
-	sw ra,0(sp)
-	sw t0,4(sp)
-	sw t1,8(sp)
-	sw t2,12(sp)
-	sw t3,16(sp)
-	sw t4,20(sp)
-	sw t5,24(sp)
-	sw t6,28(sp)
-	sw a0,32(sp)
-	sw a1,36(sp)
-	sw a2,40(sp)
-	sw a3,44(sp)
-	sw a4,48(sp)
-	sw a5,52(sp)
-	sw a6,56(sp)
-	sw a7,60(sp)
 
-	getq a0,q1
-
-	jal _irq
-
-	lw ra,0(sp)
-	lw t0,4(sp)
-	lw t1,8(sp)
-	lw t2,12(sp)
-	lw t3,16(sp)
-	lw t4,20(sp)
-	lw t5,24(sp)
-	lw t6,28(sp)
-	lw a0,32(sp)
-	lw a1,36(sp)
-	lw a2,40(sp)
-	lw a3,44(sp)
-	lw a4,48(sp)
-	lw a5,52(sp)
-	lw a6,56(sp)
-	lw a7,60(sp)
+	.section ".stack","aw",@nobits
+	.balign 4
+	.globl ___stack_bottom
+___stack:
+	.space STACK_SIZE
+	.type ___stack, @object
+	.size ___stack, STACK_SIZE
 
-	add sp,sp,64
-	retirq
-	.type __irq, @function
-	.size __irq, . - __irq
+	.globl ___stack_top
+___stack_top:

+ 1 - 6
fw/hello.c

@@ -1,13 +1,8 @@
 #include "fw.h"
 #include "io.h"
+#include "sys.h"
 #include "console.h"
 
-#define SDRAM_ADDR	0x40000000
-#define SDRAM_ADDR_BITS	25
-#define SDRAM_SIZE	(1U << SDRAM_ADDR_BITS)
-#define SDRAM_MASK	(SDRAM_SIZE - 1)
-#define SDRAM_END	(SDRAM_ADDR + SDRAM_SIZE)
-
 static unsigned int errors = 0;
 static unsigned int rate_limit;
 static char err_char;

+ 64 - 0
fw/irq.c

@@ -0,0 +1,64 @@
+#include "picorv32.h"
+#include "fw.h"
+
+static irq_handler_t irq_handlers[IRQ_VECTORS];
+
+/* Main IRQ dispatch; the .init.irq section puts it at the IRQ vector */
+void __attribute__((interrupt,section(".init.irq"))) _irq(void)
+{
+    unsigned int mask = p_getq(1);
+    unsigned int nirq = 0;
+
+    while (mask) {
+	bool handled;
+	irq_handler_t handler;
+
+	if (!(uint16_t)mask) {
+	    mask >>= 16;
+	    nirq += 16;
+	}
+	if (!(uint8_t)mask) {
+	    mask >>= 8;
+	    nirq += 8;
+	}
+	if (!(mask & 15)) {
+	    mask >>= 4;
+	    nirq += 4;
+	}
+	if (!(mask & 3)) {
+	    mask >>= 2;
+	    nirq += 2;
+	}
+	if (!(mask & 1)) {
+	    mask >>= 1;
+	    nirq += 1;
+	}
+
+	/* Now mask[0] is known to be 1 and nirq contains an active irq */
+	handled = false;
+	handler = irq_handlers[nirq];
+
+	if (likely(handler))
+	    handled = handler(nirq);
+
+	if (unlikely(!handled)) {
+	    uint32_t thisirq = (uint32_t)1 << nirq;
+
+	    /* Mask this IRQ but don't touch others */
+	    p_maskirq(thisirq, ~thisirq);
+	}
+    }
+}
+
+irq_handler_t register_irq(unsigned int vector, irq_handler_t handler)
+{
+    irq_handler_t old_handler;
+
+    if (vector >= IRQ_VECTORS)
+	return NULL;		/* Invalid vector */
+
+    old_handler = irq_handlers[vector];
+    irq_handlers[vector] = handler;
+
+    return old_handler;
+}

+ 147 - 0
fw/max80.ld

@@ -0,0 +1,147 @@
+/* -*- ld-script -*-
+ *
+ * Linker script for MAX80 firmware
+ */
+
+#include "sys.h"
+
+OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv",
+              "elf32-littleriscv")
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+MEMORY
+{
+	SRAM	: org = SRAM_ADDR,  len = SRAM_SIZE
+	DRAM	: org = SDRAM_ADDR, 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*)
+		*crtbegin.o(*)
+	}
+
+	PROVIDE (__executable_start = _PC_RESET);
+
+	.init.reset _PC_RESET : ALIGN(4) {
+		PROVIDE (___reset = .);
+		KEEP (*(SORT_NONE(.init.reset)))
+	} >SRAM
+
+	.init.irq _PC_IRQ : ALIGN(4) {
+		PROVIDE (___irq = .);
+		KEEP (*(SORT_NONE(.init.irq)))
+	}
+
+	.text : ALIGN(4) {
+		KEEP (*(SORT_NONE(.init)))
+	        *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+		*(.text.exit .text.exit.*)
+ 		*(.text.startup .text.startup.*)
+  		*(.text.hot .text.hot.*)
+  		*(SORT(.text.sorted.*))
+  		*(.text .stub .text.* .gnu.linkonce.t.*)
+  		/* .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) }
+ 	.sdata2         : {
+		*(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+ 	}
+	.sbss2          : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+
+	/* 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) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+	.sdata          :  {
+ 		__SDATA_BEGIN__ = .;
+ 		*(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4)
+		*(.srodata.cst2) *(.srodata .srodata.*)
+ 		*(.sdata .sdata.* .gnu.linkonce.s.*)
+ 	}
+ 	_edata = .;
+	. = ALIGN(16);
+ 	__bss_start = .;
+ 	.sbss           : {
+		*(.dynsbss)
+    		*(.sbss .sbss.* .gnu.linkonce.sb.*)
+    		*(.scommon)
+  	}
+	.bss            : {
+		*(.dynbss)
+ 		*(.bss .bss.* .gnu.linkonce.b.*)
+  		*(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.
+      FIXME: Why do we need it? When there is no .bss section, we do not
+      pad the .data section.  */
+ 		. = ALIGN(16);
+	}
+ 	. = SEGMENT_START("ldata-segment", .);
+ 	. = ALIGN(16);
+	__BSS_END__ = .;
+
+	.stack STACK_BOTTOM : {
+		KEEP (*(.stack))
+	}
+ 	__global_pointer$ = MIN(__SDATA_BEGIN__ + 0x800,
+                            MAX(__DATA_BEGIN__ + 0x800, __BSS_END__ - 0x800));
+ 	_end = .;
+}

+ 20 - 3
fw/picorv32.h

@@ -23,14 +23,31 @@ static inline void p_retirq(void)
     __builtin_unreachable();
 }
 
-static inline unsigned int p_maskirq(unsigned int newmask)
+/*
+ * hpa: the keepmask is a local addition.
+ *
+ * oldmask  = irq_mask;
+ * irq_mask = ((irq_mask & ~keepmask) ^ newmask) | MASKED
+ *
+ * ... where MASKED represents IRQs permanently masked
+ * in the hardware.
+ */
+static inline unsigned int
+p_maskirq(unsigned int newmask, unsigned int keepmask)
 {
     unsigned int oldmask;
-    asm volatile(".insn r 0x0b, 0, 3, %0, %1, zero"
-		 : "=r" (oldmask) : "r" (newmask));
+    asm volatile(".insn r 0x0b, 0, 3, %0, %1, %2"
+		 : "=r" (oldmask)
+		 : "r" (newmask), "r" (keepmask));
     return oldmask;
 }
 
+/* Read the current IRQ mask without changing it */
+static inline unsigned int p_irqmask(void)
+{
+    return p_maskirq(0, ~0);
+}
+
 static inline unsigned int p_waitirq(void)
 {
     unsigned int pending_mask;

+ 19 - 2
fw/sys.h

@@ -1,7 +1,24 @@
 #ifndef SYS_H
 #define SYS_H
 
-#define SRAM_SIZE	32768	/* Size of builtin SRAM */
-#define STACK_SIZE	2048	/* Minimum stack size */
+#define SRAM_ADDR	0
+#define SRAM_ADDR_BITS	15
+#define SRAM_SIZE      (0x1 << SRAM_ADDR_BITS)
+#define SRAM_MASK      (SRAM_SIZE - 1)
+#define SRAM_END       (SRAM_ADDR + SRAM_SIZE)
+
+#define STACK_SIZE	2048		/* Minimum stack size */
+#define STACK_TOP	SRAM_END	/* Initial stack pointer */
+#define STACK_BOTTOM	(STACK_TOP - STACK_SIZE)
+
+#define SDRAM_ADDR      0x40000000
+#define SDRAM_ADDR_BITS 25
+#define SDRAM_SIZE      (0x1 << SDRAM_ADDR_BITS)
+#define SDRAM_MASK      (SDRAM_SIZE - 1)
+#define SDRAM_END       (SDRAM_ADDR + SDRAM_SIZE)
+
+/* This need to match the corresponding Verilog constants */
+#define _PC_RESET	0
+#define _PC_IRQ		0x20
 
 #endif /* SYS_H */

+ 3 - 2
riscv-opts.mk

@@ -1,8 +1,9 @@
 # Extra flags during tools build
-riscv_target_flags =	-fvisibility=hidden -fno-pic \
+riscv_target_flags =	-fvisibility=hidden \
+			-fno-pic -Wa,-fno-pic -fno-PIE \
 			-frename-registers -fshort-enums -fshort-wchar \
 			-ffunction-sections -fdata-sections \
-			-mshorten-memrefs -mdiv -mstrict-align
+			-mshorten-memrefs -mstrict-align -fno-exceptions
 
 # Additional flags during application build
 riscv_flags = $(riscv_target_flags) \

+ 8 - 1
tools/Makefile

@@ -29,7 +29,14 @@ strip_flags  = $(if $(findstring -,$(word 1,$(2))),$(2),$(call _strip_flags,$(1)
 
 RMAKE = +$(MAKE) MAKEFLAGS='$(call strip_flags,r R,$(MAKEFLAGS))'
 
-riscv_configargs := target_configargs='--enable-lite-exit --disable-newlib-register-fini'
+riscv_target_configargs := --enable-lite-exit \
+	--disable-newlib-register-fini --disable-newlib-mb \
+	--disable-newlib-reent-check-verify --disable-newlib-wide-orient \
+	--disable-newlib-multithread --disable-newlib-supplied-syscalls \
+	--enable-newlib-nano-malloc --enable-newlib-io-c99-formats \
+	--disable-newlib-fvwrite-in-streamio --disable-shared
+
+riscv_configargs := target_configargs='$(riscv_target_configargs)'
 
 all: gnu
 

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini