summaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/README2
-rw-r--r--firmware/custom_ops.S102
-rw-r--r--firmware/firmware.h43
-rw-r--r--firmware/hello.c14
-rw-r--r--firmware/irq.c140
-rw-r--r--firmware/makehex.py27
-rw-r--r--firmware/multest.c151
-rw-r--r--firmware/print.c41
-rw-r--r--firmware/riscv.ld200
-rw-r--r--firmware/riscv.ld.orig236
-rw-r--r--firmware/sections.lds25
-rw-r--r--firmware/sieve.c84
-rw-r--r--firmware/start.S537
-rw-r--r--firmware/stats.c42
14 files changed, 1644 insertions, 0 deletions
diff --git a/firmware/README b/firmware/README
new file mode 100644
index 0000000..2ade487
--- /dev/null
+++ b/firmware/README
@@ -0,0 +1,2 @@
+A simple test firmware. This code is in the public domain. Simply copy whatever
+you can use.
diff --git a/firmware/custom_ops.S b/firmware/custom_ops.S
new file mode 100644
index 0000000..71889b9
--- /dev/null
+++ b/firmware/custom_ops.S
@@ -0,0 +1,102 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+
+#define regnum_q0 0
+#define regnum_q1 1
+#define regnum_q2 2
+#define regnum_q3 3
+
+#define regnum_x0 0
+#define regnum_x1 1
+#define regnum_x2 2
+#define regnum_x3 3
+#define regnum_x4 4
+#define regnum_x5 5
+#define regnum_x6 6
+#define regnum_x7 7
+#define regnum_x8 8
+#define regnum_x9 9
+#define regnum_x10 10
+#define regnum_x11 11
+#define regnum_x12 12
+#define regnum_x13 13
+#define regnum_x14 14
+#define regnum_x15 15
+#define regnum_x16 16
+#define regnum_x17 17
+#define regnum_x18 18
+#define regnum_x19 19
+#define regnum_x20 20
+#define regnum_x21 21
+#define regnum_x22 22
+#define regnum_x23 23
+#define regnum_x24 24
+#define regnum_x25 25
+#define regnum_x26 26
+#define regnum_x27 27
+#define regnum_x28 28
+#define regnum_x29 29
+#define regnum_x30 30
+#define regnum_x31 31
+
+#define regnum_zero 0
+#define regnum_ra 1
+#define regnum_sp 2
+#define regnum_gp 3
+#define regnum_tp 4
+#define regnum_t0 5
+#define regnum_t1 6
+#define regnum_t2 7
+#define regnum_s0 8
+#define regnum_s1 9
+#define regnum_a0 10
+#define regnum_a1 11
+#define regnum_a2 12
+#define regnum_a3 13
+#define regnum_a4 14
+#define regnum_a5 15
+#define regnum_a6 16
+#define regnum_a7 17
+#define regnum_s2 18
+#define regnum_s3 19
+#define regnum_s4 20
+#define regnum_s5 21
+#define regnum_s6 22
+#define regnum_s7 23
+#define regnum_s8 24
+#define regnum_s9 25
+#define regnum_s10 26
+#define regnum_s11 27
+#define regnum_t3 28
+#define regnum_t4 29
+#define regnum_t5 30
+#define regnum_t6 31
+
+// x8 is s0 and also fp
+#define regnum_fp 8
+
+#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \
+.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0))
+
+#define picorv32_getq_insn(_rd, _qs) \
+r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011)
+
+#define picorv32_setq_insn(_qd, _rs) \
+r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011)
+
+#define picorv32_retirq_insn() \
+r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011)
+
+#define picorv32_maskirq_insn(_rd, _rs) \
+r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011)
+
+#define picorv32_waitirq_insn(_rd) \
+r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011)
+
+#define picorv32_timer_insn(_rd, _rs) \
+r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011)
+
diff --git a/firmware/firmware.h b/firmware/firmware.h
new file mode 100644
index 0000000..59d3f11
--- /dev/null
+++ b/firmware/firmware.h
@@ -0,0 +1,43 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+
+#ifndef FIRMWARE_H
+#define FIRMWARE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+// irq.c
+uint32_t *irq(uint32_t *regs, uint32_t irqs);
+
+// print.c
+void print_chr(char ch);
+void print_str(const char *p);
+void print_dec(unsigned int val);
+void print_hex(unsigned int val, int digits);
+
+// hello.c
+void hello(void);
+
+// sieve.c
+void sieve(void);
+
+// multest.c
+uint32_t hard_mul(uint32_t a, uint32_t b);
+uint32_t hard_mulh(uint32_t a, uint32_t b);
+uint32_t hard_mulhsu(uint32_t a, uint32_t b);
+uint32_t hard_mulhu(uint32_t a, uint32_t b);
+uint32_t hard_div(uint32_t a, uint32_t b);
+uint32_t hard_divu(uint32_t a, uint32_t b);
+uint32_t hard_rem(uint32_t a, uint32_t b);
+uint32_t hard_remu(uint32_t a, uint32_t b);
+void multest(void);
+
+// stats.c
+void stats(void);
+
+#endif
diff --git a/firmware/hello.c b/firmware/hello.c
new file mode 100644
index 0000000..b7e0b96
--- /dev/null
+++ b/firmware/hello.c
@@ -0,0 +1,14 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+
+#include "firmware.h"
+
+void hello(void)
+{
+ print_str("hello world\n");
+}
+
diff --git a/firmware/irq.c b/firmware/irq.c
new file mode 100644
index 0000000..9fc1735
--- /dev/null
+++ b/firmware/irq.c
@@ -0,0 +1,140 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+
+#include "firmware.h"
+
+uint32_t *irq(uint32_t *regs, uint32_t irqs)
+{
+ static unsigned int ext_irq_4_count = 0;
+ static unsigned int ext_irq_5_count = 0;
+ static unsigned int timer_irq_count = 0;
+
+ // checking compressed isa q0 reg handling
+ if ((irqs & 6) != 0) {
+ uint32_t pc = (regs[0] & 1) ? regs[0] - 3 : regs[0] - 4;
+ uint32_t instr = *(uint16_t*)pc;
+
+ if ((instr & 3) == 3)
+ instr = instr | (*(uint16_t*)(pc + 2)) << 16;
+
+ if (((instr & 3) != 3) != (regs[0] & 1)) {
+ print_str("Mismatch between q0 LSB and decoded instruction word! q0=0x");
+ print_hex(regs[0], 8);
+ print_str(", instr=0x");
+ if ((instr & 3) == 3)
+ print_hex(instr, 8);
+ else
+ print_hex(instr, 4);
+ print_str("\n");
+ __asm__ volatile ("ebreak");
+ }
+ }
+
+ if ((irqs & (1<<4)) != 0) {
+ ext_irq_4_count++;
+ // print_str("[EXT-IRQ-4]");
+ }
+
+ if ((irqs & (1<<5)) != 0) {
+ ext_irq_5_count++;
+ // print_str("[EXT-IRQ-5]");
+ }
+
+ if ((irqs & 1) != 0) {
+ timer_irq_count++;
+ // print_str("[TIMER-IRQ]");
+ }
+
+ if ((irqs & 6) != 0)
+ {
+ uint32_t pc = (regs[0] & 1) ? regs[0] - 3 : regs[0] - 4;
+ uint32_t instr = *(uint16_t*)pc;
+
+ if ((instr & 3) == 3)
+ instr = instr | (*(uint16_t*)(pc + 2)) << 16;
+
+ print_str("\n");
+ print_str("------------------------------------------------------------\n");
+
+ if ((irqs & 2) != 0) {
+ if (instr == 0x00100073 || instr == 0x9002) {
+ print_str("EBREAK instruction at 0x");
+ print_hex(pc, 8);
+ print_str("\n");
+ } else {
+ print_str("Illegal Instruction at 0x");
+ print_hex(pc, 8);
+ print_str(": 0x");
+ print_hex(instr, ((instr & 3) == 3) ? 8 : 4);
+ print_str("\n");
+ }
+ }
+
+ if ((irqs & 4) != 0) {
+ print_str("Bus error in Instruction at 0x");
+ print_hex(pc, 8);
+ print_str(": 0x");
+ print_hex(instr, ((instr & 3) == 3) ? 8 : 4);
+ print_str("\n");
+ }
+
+ for (int i = 0; i < 8; i++)
+ for (int k = 0; k < 4; k++)
+ {
+ int r = i + k*8;
+
+ if (r == 0) {
+ print_str("pc ");
+ } else
+ if (r < 10) {
+ print_chr('x');
+ print_chr('0' + r);
+ print_chr(' ');
+ print_chr(' ');
+ } else
+ if (r < 20) {
+ print_chr('x');
+ print_chr('1');
+ print_chr('0' + r - 10);
+ print_chr(' ');
+ } else
+ if (r < 30) {
+ print_chr('x');
+ print_chr('2');
+ print_chr('0' + r - 20);
+ print_chr(' ');
+ } else {
+ print_chr('x');
+ print_chr('3');
+ print_chr('0' + r - 30);
+ print_chr(' ');
+ }
+
+ print_hex(regs[r], 8);
+ print_str(k == 3 ? "\n" : " ");
+ }
+
+ print_str("------------------------------------------------------------\n");
+
+ print_str("Number of fast external IRQs counted: ");
+ print_dec(ext_irq_4_count);
+ print_str("\n");
+
+ print_str("Number of slow external IRQs counted: ");
+ print_dec(ext_irq_5_count);
+ print_str("\n");
+
+ print_str("Number of timer IRQs counted: ");
+ print_dec(timer_irq_count);
+ print_str("\n");
+
+ __asm__ volatile ("ebreak");
+ }
+
+ return regs;
+}
+
diff --git a/firmware/makehex.py b/firmware/makehex.py
new file mode 100644
index 0000000..419b378
--- /dev/null
+++ b/firmware/makehex.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+#
+# This is free and unencumbered software released into the public domain.
+#
+# Anyone is free to copy, modify, publish, use, compile, sell, or
+# distribute this software, either in source code form or as a compiled
+# binary, for any purpose, commercial or non-commercial, and by any
+# means.
+
+from sys import argv
+
+binfile = argv[1]
+nwords = int(argv[2])
+
+with open(binfile, "rb") as f:
+ bindata = f.read()
+
+assert len(bindata) < 4*nwords
+assert len(bindata) % 4 == 0
+
+for i in range(nwords):
+ if i < len(bindata) // 4:
+ w = bindata[4*i : 4*i+4]
+ print("%02x%02x%02x%02x" % (w[3], w[2], w[1], w[0]))
+ else:
+ print("0")
+
diff --git a/firmware/multest.c b/firmware/multest.c
new file mode 100644
index 0000000..1706400
--- /dev/null
+++ b/firmware/multest.c
@@ -0,0 +1,151 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+
+#include "firmware.h"
+
+static uint32_t xorshift32(void) {
+ static uint32_t x = 314159265;
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ return x;
+}
+
+void multest(void)
+{
+ for (int i = 0; i < 15; i++)
+ {
+ uint32_t a = xorshift32();
+ uint32_t b = xorshift32();
+
+ switch (i)
+ {
+ case 0:
+ a = 0x80000000;
+ b = 0xFFFFFFFF;
+ break;
+ case 1:
+ a = 0;
+ b = 0;
+ break;
+ case 2:
+ a |= 0x80000000;
+ b = 0;
+ break;
+ case 3:
+ a &= 0x7FFFFFFF;
+ b = 0;
+ break;
+ }
+
+ uint64_t au = a, bu = b;
+ int64_t as = (int32_t)a, bs = (int32_t)b;
+
+ print_str("input [");
+ print_hex(as >> 32, 8);
+ print_str("] ");
+ print_hex(a, 8);
+ print_str(" [");
+ print_hex(bs >> 32, 8);
+ print_str("] ");
+ print_hex(b, 8);
+ print_chr('\n');
+
+ uint32_t h_mul, h_mulh, h_mulhsu, h_mulhu;
+ print_str("hard mul ");
+
+ h_mul = hard_mul(a, b);
+ print_hex(h_mul, 8);
+ print_str(" ");
+
+ h_mulh = hard_mulh(a, b);
+ print_hex(h_mulh, 8);
+ print_str(" ");
+
+ h_mulhsu = hard_mulhsu(a, b);
+ print_hex(h_mulhsu, 8);
+ print_str(" ");
+
+ h_mulhu = hard_mulhu(a, b);
+ print_hex(h_mulhu, 8);
+ print_chr('\n');
+
+ uint32_t s_mul, s_mulh, s_mulhsu, s_mulhu;
+ print_str("soft mul ");
+
+ s_mul = a * b;
+ print_hex(s_mul, 8);
+ print_str(" ");
+
+ s_mulh = (as * bs) >> 32;
+ print_hex(s_mulh, 8);
+ print_str(" ");
+
+ s_mulhsu = (as * bu) >> 32;
+ print_hex(s_mulhsu, 8);
+ print_str(" ");
+
+ s_mulhu = (au * bu) >> 32;
+ print_hex(s_mulhu, 8);
+ print_str(" ");
+
+ if (s_mul != h_mul || s_mulh != h_mulh || s_mulhsu != h_mulhsu || s_mulhu != h_mulhu) {
+ print_str("ERROR!\n");
+ __asm__ volatile ("ebreak");
+ return;
+ }
+
+ print_str(" OK\n");
+
+ uint32_t h_div, h_divu, h_rem, h_remu;
+ print_str("hard div ");
+
+ h_div = hard_div(a, b);
+ print_hex(h_div, 8);
+ print_str(" ");
+
+ h_divu = hard_divu(a, b);
+ print_hex(h_divu, 8);
+ print_str(" ");
+
+ h_rem = hard_rem(a, b);
+ print_hex(h_rem, 8);
+ print_str(" ");
+
+ h_remu = hard_remu(a, b);
+ print_hex(h_remu, 8);
+ print_chr('\n');
+
+ uint32_t s_div, s_divu, s_rem, s_remu;
+ print_str("soft div ");
+
+ s_div = b ? as / bs : 0xffffffff;
+ print_hex(s_div, 8);
+ print_str(" ");
+
+ s_divu = b ? au / bu : 0xffffffff;
+ print_hex(s_divu, 8);
+ print_str(" ");
+
+ s_rem = b ? as % bs : a;
+ print_hex(s_rem, 8);
+ print_str(" ");
+
+ s_remu = b ? au % bu : a;
+ print_hex(s_remu, 8);
+ print_str(" ");
+
+ if (s_div != h_div || s_divu != h_divu || s_rem != h_rem || s_remu != h_remu) {
+ print_str("ERROR!\n");
+ __asm__ volatile ("ebreak");
+ return;
+ }
+
+ print_str(" OK\n");
+ }
+}
+
diff --git a/firmware/print.c b/firmware/print.c
new file mode 100644
index 0000000..accce26
--- /dev/null
+++ b/firmware/print.c
@@ -0,0 +1,41 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+
+#include "firmware.h"
+
+#define OUTPORT 0x10000000
+
+void print_chr(char ch)
+{
+ *((volatile uint32_t*)OUTPORT) = ch;
+}
+
+void print_str(const char *p)
+{
+ while (*p != 0)
+ *((volatile uint32_t*)OUTPORT) = *(p++);
+}
+
+void print_dec(unsigned int val)
+{
+ char buffer[10];
+ char *p = buffer;
+ while (val || p == buffer) {
+ *(p++) = val % 10;
+ val = val / 10;
+ }
+ while (p != buffer) {
+ *((volatile uint32_t*)OUTPORT) = '0' + *(--p);
+ }
+}
+
+void print_hex(unsigned int val, int digits)
+{
+ for (int i = (4*digits)-4; i >= 0; i -= 4)
+ *((volatile uint32_t*)OUTPORT) = "0123456789ABCDEF"[(val >> i) % 16];
+}
+
diff --git a/firmware/riscv.ld b/firmware/riscv.ld
new file mode 100644
index 0000000..16fde8d
--- /dev/null
+++ b/firmware/riscv.ld
@@ -0,0 +1,200 @@
+/* ---- Original Script: /opt/riscv32i/riscv32-unknown-elf/lib/ldscripts/elf32lriscv.x ---- */
+/* Default linker script, for normal executables */
+/* Copyright (C) 2014-2017 Free Software Foundation, Inc.
+ Copying and distribution of this script, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. */
+OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv",
+ "elf32-littleriscv")
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+SECTIONS
+{
+ . = 0x00010000;
+ .text :
+ {
+ *(.text)
+ *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+ *(.text.exit .text.exit.*)
+ *(.text.startup .text.startup.*)
+ *(.text.hot .text.hot.*)
+ *(.stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ }
+ .init :
+ {
+ KEEP (*(SORT_NONE(.init)))
+ }
+ .plt : { *(.plt) }
+ .iplt : { *(.iplt) }
+ .fini :
+ {
+ KEEP (*(SORT_NONE(.fini)))
+ }
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 :
+ {
+ *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+ }
+ .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table
+ .gcc_except_table.*) }
+ .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) }
+ /* These sections are generated by the Sun/Oracle C++ compiler. */
+ .exception_ranges : ONLY_IF_RO { *(.exception_ranges
+ .exception_ranges*) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
+ /* Exception handling */
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+ .gnu_extab : ONLY_IF_RW { *(.gnu_extab) }
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+ .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
+ /* Thread Local Storage sections */
+ .tdata : { *(.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 = .);
+ }
+ .fini_array :
+ {
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+ KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ }
+ .ctors :
+ {
+ /* 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
+ 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))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*crtbegin?.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .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.*) }
+ .dynamic : { *(.dynamic) }
+ . = DATA_SEGMENT_RELRO_END (0, .);
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
+ /* 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 :
+ {
+ __global_pointer$ = . + 0x800;
+ *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+ _edata = .; PROVIDE (edata = .);
+ . = .;
+ __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 don't
+ pad the .data section. */
+ . = ALIGN(. != 0 ? 32 / 8 : 1);
+ }
+ . = ALIGN(32 / 8);
+ . = SEGMENT_START("ldata-segment", .);
+ . = ALIGN(32 / 8);
+ _end = .; PROVIDE (end = .);
+ . = DATA_SEGMENT_END (.);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* DWARF 3 */
+ .debug_pubtypes 0 : { *(.debug_pubtypes) }
+ .debug_ranges 0 : { *(.debug_ranges) }
+ /* DWARF Extension. */
+ .debug_macro 0 : { *(.debug_macro) }
+ .debug_addr 0 : { *(.debug_addr) }
+ .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+ /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
+}
diff --git a/firmware/riscv.ld.orig b/firmware/riscv.ld.orig
new file mode 100644
index 0000000..6d40e3d
--- /dev/null
+++ b/firmware/riscv.ld.orig
@@ -0,0 +1,236 @@
+/* ---- Original Script: /opt/riscv32i/riscv32-unknown-elf/lib/ldscripts/elf32lriscv.x ---- */
+/* Default linker script, for normal executables */
+/* Copyright (C) 2014-2017 Free Software Foundation, Inc.
+ Copying and distribution of this script, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. */
+OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv",
+ "elf32-littleriscv")
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+SEARCH_DIR("/opt/riscv32i/riscv32-unknown-elf/lib");
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x10000)); . = SEGMENT_START("text-segment", 0x10000) + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .note.gnu.build-id : { *(.note.gnu.build-id) }
+ .hash : { *(.hash) }
+ .gnu.hash : { *(.gnu.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rela.init : { *(.rela.init) }
+ .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+ .rela.fini : { *(.rela.fini) }
+ .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+ .rela.data.rel.ro : { *(.rela.data.rel.ro .rela.data.rel.ro.* .rela.gnu.linkonce.d.rel.ro.*) }
+ .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+ .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+ .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rela.got : { *(.rela.got) }
+ .rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) }
+ .rela.sbss : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) }
+ .rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) }
+ .rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) }
+ .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+ .rela.iplt :
+ {
+ PROVIDE_HIDDEN (__rela_iplt_start = .);
+ *(.rela.iplt)
+ PROVIDE_HIDDEN (__rela_iplt_end = .);
+ }
+ .rela.plt :
+ {
+ *(.rela.plt)
+ }
+ .init :
+ {
+ KEEP (*(SORT_NONE(.init)))
+ }
+ .plt : { *(.plt) }
+ .iplt : { *(.iplt) }
+ .text :
+ {
+ *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+ *(.text.exit .text.exit.*)
+ *(.text.startup .text.startup.*)
+ *(.text.hot .text.hot.*)
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ }
+ .fini :
+ {
+ KEEP (*(SORT_NONE(.fini)))
+ }
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 :
+ {
+ *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+ }
+ .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table
+ .gcc_except_table.*) }
+ .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) }
+ /* These sections are generated by the Sun/Oracle C++ compiler. */
+ .exception_ranges : ONLY_IF_RO { *(.exception_ranges
+ .exception_ranges*) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
+ /* Exception handling */
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+ .gnu_extab : ONLY_IF_RW { *(.gnu_extab) }
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+ .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
+ /* Thread Local Storage sections */
+ .tdata : { *(.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 = .);
+ }
+ .fini_array :
+ {
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+ KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ }
+ .ctors :
+ {
+ /* 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
+ 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))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*crtbegin?.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .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.*) }
+ .dynamic : { *(.dynamic) }
+ . = DATA_SEGMENT_RELRO_END (0, .);
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
+ /* 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 :
+ {
+ __global_pointer$ = . + 0x800;
+ *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+ _edata = .; PROVIDE (edata = .);
+ . = .;
+ __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 don't
+ pad the .data section. */
+ . = ALIGN(. != 0 ? 32 / 8 : 1);
+ }
+ . = ALIGN(32 / 8);
+ . = SEGMENT_START("ldata-segment", .);
+ . = ALIGN(32 / 8);
+ _end = .; PROVIDE (end = .);
+ . = DATA_SEGMENT_END (.);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* DWARF 3 */
+ .debug_pubtypes 0 : { *(.debug_pubtypes) }
+ .debug_ranges 0 : { *(.debug_ranges) }
+ /* DWARF Extension. */
+ .debug_macro 0 : { *(.debug_macro) }
+ .debug_addr 0 : { *(.debug_addr) }
+ .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+ /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
+}
diff --git a/firmware/sections.lds b/firmware/sections.lds
new file mode 100644
index 0000000..f914b46
--- /dev/null
+++ b/firmware/sections.lds
@@ -0,0 +1,25 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+*/
+
+MEMORY {
+ /* the memory in the testbench is 128k in size;
+ * set LENGTH=96k and leave at least 32k for stack */
+ mem : ORIGIN = 0x00000000, LENGTH = 0x00018000
+}
+
+SECTIONS {
+ .memory : {
+ . = 0x000000;
+ start*(.text);
+ *(.text);
+ *(*);
+ end = .;
+ . = ALIGN(4);
+ } > mem
+}
diff --git a/firmware/sieve.c b/firmware/sieve.c
new file mode 100644
index 0000000..ff945eb
--- /dev/null
+++ b/firmware/sieve.c
@@ -0,0 +1,84 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+
+// A simple Sieve of Eratosthenes
+
+#include "firmware.h"
+
+#define BITMAP_SIZE 64
+
+static uint32_t bitmap[BITMAP_SIZE/32];
+static uint32_t hash;
+
+static uint32_t mkhash(uint32_t a, uint32_t b)
+{
+ // The XOR version of DJB2
+ return ((a << 5) + a) ^ b;
+}
+
+static void bitmap_set(int idx)
+{
+ bitmap[idx/32] |= 1 << (idx % 32);
+}
+
+static bool bitmap_get(int idx)
+{
+ return (bitmap[idx/32] & (1 << (idx % 32))) != 0;
+}
+
+static void print_prime(int idx, int val)
+{
+ if (idx < 10)
+ print_str(" ");
+ print_dec(idx);
+ if (idx / 10 == 1)
+ goto force_th;
+ switch (idx % 10) {
+ case 1: print_str("st"); break;
+ case 2: print_str("nd"); break;
+ case 3: print_str("rd"); break;
+ force_th:
+ default: print_str("th"); break;
+ }
+ print_str(" prime is ");
+ print_dec(val);
+ print_str(".\n");
+
+ hash = mkhash(hash, idx);
+ hash = mkhash(hash, val);
+}
+
+void sieve(void)
+{
+ int idx = 1;
+ hash = 5381;
+ print_prime(idx++, 2);
+ for (int i = 0; i < BITMAP_SIZE; i++) {
+ if (bitmap_get(i))
+ continue;
+ print_prime(idx++, 3+2*i);
+ for (int j = 2*(3+2*i);; j += 3+2*i) {
+ if (j%2 == 0)
+ continue;
+ int k = (j-3)/2;
+ if (k >= BITMAP_SIZE)
+ break;
+ bitmap_set(k);
+ }
+ }
+
+ print_str("checksum: ");
+ print_hex(hash, 8);
+
+ if (hash == 0x1772A48F) {
+ print_str(" OK\n");
+ } else {
+ print_str(" ERROR\n");
+ __asm__ volatile ("ebreak");
+ }
+}
+
diff --git a/firmware/start.S b/firmware/start.S
new file mode 100644
index 0000000..d95f04c
--- /dev/null
+++ b/firmware/start.S
@@ -0,0 +1,537 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+
+#define ENABLE_QREGS
+#define ENABLE_HELLO
+#define ENABLE_RVTST
+#define ENABLE_SIEVE
+#define ENABLE_MULTST
+#define ENABLE_STATS
+
+#ifndef ENABLE_QREGS
+# undef ENABLE_RVTST
+#endif
+
+// Only save registers in IRQ wrapper that are to be saved by the caller in
+// the RISC-V ABI, with the excpetion of the stack pointer. The IRQ handler
+// will save the rest if necessary. I.e. skip x3, x4, x8, x9, and x18-x27.
+#undef ENABLE_FASTIRQ
+
+#include "custom_ops.S"
+
+ .section .text
+ .global irq
+ .global hello
+ .global sieve
+ .global multest
+ .global hard_mul
+ .global hard_mulh
+ .global hard_mulhsu
+ .global hard_mulhu
+ .global hard_div
+ .global hard_divu
+ .global hard_rem
+ .global hard_remu
+ .global stats
+
+reset_vec:
+ // no more than 16 bytes here !
+ picorv32_waitirq_insn(zero)
+ picorv32_maskirq_insn(zero, zero)
+ j start
+
+
+/* Interrupt handler
+ **********************************/
+
+.balign 16
+irq_vec:
+ /* save registers */
+
+#ifdef ENABLE_QREGS
+
+ picorv32_setq_insn(q2, x1)
+ picorv32_setq_insn(q3, x2)
+
+ lui x1, %hi(irq_regs)
+ addi x1, x1, %lo(irq_regs)
+
+ picorv32_getq_insn(x2, q0)
+ sw x2, 0*4(x1)
+
+ picorv32_getq_insn(x2, q2)
+ sw x2, 1*4(x1)
+
+ picorv32_getq_insn(x2, q3)
+ sw x2, 2*4(x1)
+
+#ifdef ENABLE_FASTIRQ
+ sw x5, 5*4(x1)
+ sw x6, 6*4(x1)
+ sw x7, 7*4(x1)
+ sw x10, 10*4(x1)
+ sw x11, 11*4(x1)
+ sw x12, 12*4(x1)
+ sw x13, 13*4(x1)
+ sw x14, 14*4(x1)
+ sw x15, 15*4(x1)
+ sw x16, 16*4(x1)
+ sw x17, 17*4(x1)
+ sw x28, 28*4(x1)
+ sw x29, 29*4(x1)
+ sw x30, 30*4(x1)
+ sw x31, 31*4(x1)
+#else
+ sw x3, 3*4(x1)
+ sw x4, 4*4(x1)
+ sw x5, 5*4(x1)
+ sw x6, 6*4(x1)
+ sw x7, 7*4(x1)
+ sw x8, 8*4(x1)
+ sw x9, 9*4(x1)
+ sw x10, 10*4(x1)
+ sw x11, 11*4(x1)
+ sw x12, 12*4(x1)
+ sw x13, 13*4(x1)
+ sw x14, 14*4(x1)
+ sw x15, 15*4(x1)
+ sw x16, 16*4(x1)
+ sw x17, 17*4(x1)
+ sw x18, 18*4(x1)
+ sw x19, 19*4(x1)
+ sw x20, 20*4(x1)
+ sw x21, 21*4(x1)
+ sw x22, 22*4(x1)
+ sw x23, 23*4(x1)
+ sw x24, 24*4(x1)
+ sw x25, 25*4(x1)
+ sw x26, 26*4(x1)
+ sw x27, 27*4(x1)
+ sw x28, 28*4(x1)
+ sw x29, 29*4(x1)
+ sw x30, 30*4(x1)
+ sw x31, 31*4(x1)
+#endif
+
+#else // ENABLE_QREGS
+
+#ifdef ENABLE_FASTIRQ
+ sw gp, 0*4+0x200(zero)
+ sw x1, 1*4+0x200(zero)
+ sw x2, 2*4+0x200(zero)
+ sw x5, 5*4+0x200(zero)
+ sw x6, 6*4+0x200(zero)
+ sw x7, 7*4+0x200(zero)
+ sw x10, 10*4+0x200(zero)
+ sw x11, 11*4+0x200(zero)
+ sw x12, 12*4+0x200(zero)
+ sw x13, 13*4+0x200(zero)
+ sw x14, 14*4+0x200(zero)
+ sw x15, 15*4+0x200(zero)
+ sw x16, 16*4+0x200(zero)
+ sw x17, 17*4+0x200(zero)
+ sw x28, 28*4+0x200(zero)
+ sw x29, 29*4+0x200(zero)
+ sw x30, 30*4+0x200(zero)
+ sw x31, 31*4+0x200(zero)
+#else
+ sw gp, 0*4+0x200(zero)
+ sw x1, 1*4+0x200(zero)
+ sw x2, 2*4+0x200(zero)
+ sw x3, 3*4+0x200(zero)
+ sw x4, 4*4+0x200(zero)
+ sw x5, 5*4+0x200(zero)
+ sw x6, 6*4+0x200(zero)
+ sw x7, 7*4+0x200(zero)
+ sw x8, 8*4+0x200(zero)
+ sw x9, 9*4+0x200(zero)
+ sw x10, 10*4+0x200(zero)
+ sw x11, 11*4+0x200(zero)
+ sw x12, 12*4+0x200(zero)
+ sw x13, 13*4+0x200(zero)
+ sw x14, 14*4+0x200(zero)
+ sw x15, 15*4+0x200(zero)
+ sw x16, 16*4+0x200(zero)
+ sw x17, 17*4+0x200(zero)
+ sw x18, 18*4+0x200(zero)
+ sw x19, 19*4+0x200(zero)
+ sw x20, 20*4+0x200(zero)
+ sw x21, 21*4+0x200(zero)
+ sw x22, 22*4+0x200(zero)
+ sw x23, 23*4+0x200(zero)
+ sw x24, 24*4+0x200(zero)
+ sw x25, 25*4+0x200(zero)
+ sw x26, 26*4+0x200(zero)
+ sw x27, 27*4+0x200(zero)
+ sw x28, 28*4+0x200(zero)
+ sw x29, 29*4+0x200(zero)
+ sw x30, 30*4+0x200(zero)
+ sw x31, 31*4+0x200(zero)
+#endif
+
+#endif // ENABLE_QREGS
+
+ /* call interrupt handler C function */
+
+ lui sp, %hi(irq_stack)
+ addi sp, sp, %lo(irq_stack)
+
+ // arg0 = address of regs
+ lui a0, %hi(irq_regs)
+ addi a0, a0, %lo(irq_regs)
+
+ // arg1 = interrupt type
+#ifdef ENABLE_QREGS
+ picorv32_getq_insn(a1, q1)
+#else
+ addi a1, tp, 0
+#endif
+
+ // call to C function
+ jal ra, irq
+
+ /* restore registers */
+
+#ifdef ENABLE_QREGS
+
+ // new irq_regs address returned from C code in a0
+ addi x1, a0, 0
+
+ lw x2, 0*4(x1)
+ picorv32_setq_insn(q0, x2)
+
+ lw x2, 1*4(x1)
+ picorv32_setq_insn(q1, x2)
+
+ lw x2, 2*4(x1)
+ picorv32_setq_insn(q2, x2)
+
+#ifdef ENABLE_FASTIRQ
+ lw x5, 5*4(x1)
+ lw x6, 6*4(x1)
+ lw x7, 7*4(x1)
+ lw x10, 10*4(x1)
+ lw x11, 11*4(x1)
+ lw x12, 12*4(x1)
+ lw x13, 13*4(x1)
+ lw x14, 14*4(x1)
+ lw x15, 15*4(x1)
+ lw x16, 16*4(x1)
+ lw x17, 17*4(x1)
+ lw x28, 28*4(x1)
+ lw x29, 29*4(x1)
+ lw x30, 30*4(x1)
+ lw x31, 31*4(x1)
+#else
+ lw x3, 3*4(x1)
+ lw x4, 4*4(x1)
+ lw x5, 5*4(x1)
+ lw x6, 6*4(x1)
+ lw x7, 7*4(x1)
+ lw x8, 8*4(x1)
+ lw x9, 9*4(x1)
+ lw x10, 10*4(x1)
+ lw x11, 11*4(x1)
+ lw x12, 12*4(x1)
+ lw x13, 13*4(x1)
+ lw x14, 14*4(x1)
+ lw x15, 15*4(x1)
+ lw x16, 16*4(x1)
+ lw x17, 17*4(x1)
+ lw x18, 18*4(x1)
+ lw x19, 19*4(x1)
+ lw x20, 20*4(x1)
+ lw x21, 21*4(x1)
+ lw x22, 22*4(x1)
+ lw x23, 23*4(x1)
+ lw x24, 24*4(x1)
+ lw x25, 25*4(x1)
+ lw x26, 26*4(x1)
+ lw x27, 27*4(x1)
+ lw x28, 28*4(x1)
+ lw x29, 29*4(x1)
+ lw x30, 30*4(x1)
+ lw x31, 31*4(x1)
+#endif
+
+ picorv32_getq_insn(x1, q1)
+ picorv32_getq_insn(x2, q2)
+
+#else // ENABLE_QREGS
+
+ // new irq_regs address returned from C code in a0
+ addi a1, zero, 0x200
+ beq a0, a1, 1f
+ ebreak
+1:
+
+#ifdef ENABLE_FASTIRQ
+ lw gp, 0*4+0x200(zero)
+ lw x1, 1*4+0x200(zero)
+ lw x2, 2*4+0x200(zero)
+ lw x5, 5*4+0x200(zero)
+ lw x6, 6*4+0x200(zero)
+ lw x7, 7*4+0x200(zero)
+ lw x10, 10*4+0x200(zero)
+ lw x11, 11*4+0x200(zero)
+ lw x12, 12*4+0x200(zero)
+ lw x13, 13*4+0x200(zero)
+ lw x14, 14*4+0x200(zero)
+ lw x15, 15*4+0x200(zero)
+ lw x16, 16*4+0x200(zero)
+ lw x17, 17*4+0x200(zero)
+ lw x28, 28*4+0x200(zero)
+ lw x29, 29*4+0x200(zero)
+ lw x30, 30*4+0x200(zero)
+ lw x31, 31*4+0x200(zero)
+#else
+ lw gp, 0*4+0x200(zero)
+ lw x1, 1*4+0x200(zero)
+ lw x2, 2*4+0x200(zero)
+ // do not restore x3 (gp)
+ lw x4, 4*4+0x200(zero)
+ lw x5, 5*4+0x200(zero)
+ lw x6, 6*4+0x200(zero)
+ lw x7, 7*4+0x200(zero)
+ lw x8, 8*4+0x200(zero)
+ lw x9, 9*4+0x200(zero)
+ lw x10, 10*4+0x200(zero)
+ lw x11, 11*4+0x200(zero)
+ lw x12, 12*4+0x200(zero)
+ lw x13, 13*4+0x200(zero)
+ lw x14, 14*4+0x200(zero)
+ lw x15, 15*4+0x200(zero)
+ lw x16, 16*4+0x200(zero)
+ lw x17, 17*4+0x200(zero)
+ lw x18, 18*4+0x200(zero)
+ lw x19, 19*4+0x200(zero)
+ lw x20, 20*4+0x200(zero)
+ lw x21, 21*4+0x200(zero)
+ lw x22, 22*4+0x200(zero)
+ lw x23, 23*4+0x200(zero)
+ lw x24, 24*4+0x200(zero)
+ lw x25, 25*4+0x200(zero)
+ lw x26, 26*4+0x200(zero)
+ lw x27, 27*4+0x200(zero)
+ lw x28, 28*4+0x200(zero)
+ lw x29, 29*4+0x200(zero)
+ lw x30, 30*4+0x200(zero)
+ lw x31, 31*4+0x200(zero)
+#endif
+
+#endif // ENABLE_QREGS
+
+ picorv32_retirq_insn()
+
+#ifndef ENABLE_QREGS
+.balign 0x200
+#endif
+irq_regs:
+ // registers are saved to this memory region during interrupt handling
+ // the program counter is saved as register 0
+ .fill 32,4
+
+ // stack for the interrupt handler
+ .fill 128,4
+irq_stack:
+
+
+/* Main program
+ **********************************/
+
+start:
+ /* zero-initialize all registers */
+
+ addi x1, zero, 0
+ addi x2, zero, 0
+ addi x3, zero, 0
+ addi x4, zero, 0
+ addi x5, zero, 0
+ addi x6, zero, 0
+ addi x7, zero, 0
+ addi x8, zero, 0
+ addi x9, zero, 0
+ addi x10, zero, 0
+ addi x11, zero, 0
+ addi x12, zero, 0
+ addi x13, zero, 0
+ addi x14, zero, 0
+ addi x15, zero, 0
+ addi x16, zero, 0
+ addi x17, zero, 0
+ addi x18, zero, 0
+ addi x19, zero, 0
+ addi x20, zero, 0
+ addi x21, zero, 0
+ addi x22, zero, 0
+ addi x23, zero, 0
+ addi x24, zero, 0
+ addi x25, zero, 0
+ addi x26, zero, 0
+ addi x27, zero, 0
+ addi x28, zero, 0
+ addi x29, zero, 0
+ addi x30, zero, 0
+ addi x31, zero, 0
+
+#ifdef ENABLE_HELLO
+ /* set stack pointer */
+ lui sp,(128*1024)>>12
+
+ /* call hello C code */
+ jal ra,hello
+#endif
+
+ /* running tests from riscv-tests */
+
+#ifdef ENABLE_RVTST
+# define TEST(n) \
+ .global n; \
+ addi x1, zero, 1000; \
+ picorv32_timer_insn(zero, x1); \
+ jal zero,n; \
+ .global n ## _ret; \
+ n ## _ret:
+#else
+# define TEST(n) \
+ .global n ## _ret; \
+ n ## _ret:
+#endif
+
+ TEST(lui)
+ TEST(auipc)
+ TEST(j)
+ TEST(jal)
+ TEST(jalr)
+
+ TEST(beq)
+ TEST(bne)
+ TEST(blt)
+ TEST(bge)
+ TEST(bltu)
+ TEST(bgeu)
+
+ TEST(lb)
+ TEST(lh)
+ TEST(lw)
+ TEST(lbu)
+ TEST(lhu)
+
+ TEST(sb)
+ TEST(sh)
+ TEST(sw)
+
+ TEST(addi)
+ TEST(slti) // also tests sltiu
+ TEST(xori)
+ TEST(ori)
+ TEST(andi)
+ TEST(slli)
+ TEST(srli)
+ TEST(srai)
+
+ TEST(add)
+ TEST(sub)
+ TEST(sll)
+ TEST(slt) // what is with sltu ?
+ TEST(xor)
+ TEST(srl)
+ TEST(sra)
+ TEST(or)
+ TEST(and)
+
+ TEST(mulh)
+ TEST(mulhsu)
+ TEST(mulhu)
+ TEST(mul)
+
+ TEST(div)
+ TEST(divu)
+ TEST(rem)
+ TEST(remu)
+
+ TEST(simple)
+
+ /* set stack pointer */
+ lui sp,(128*1024)>>12
+
+ /* set gp and tp */
+ lui gp, %hi(0xdeadbeef)
+ addi gp, gp, %lo(0xdeadbeef)
+ addi tp, gp, 0
+
+#ifdef ENABLE_SIEVE
+ /* call sieve C code */
+ jal ra,sieve
+#endif
+
+#ifdef ENABLE_MULTST
+ /* call multest C code */
+ jal ra,multest
+#endif
+
+#ifdef ENABLE_STATS
+ /* call stats C code */
+ jal ra,stats
+#endif
+
+ /* print "DONE\n" */
+ lui a0,0x10000000>>12
+ addi a1,zero,'D'
+ addi a2,zero,'O'
+ addi a3,zero,'N'
+ addi a4,zero,'E'
+ addi a5,zero,'\n'
+ sw a1,0(a0)
+ sw a2,0(a0)
+ sw a3,0(a0)
+ sw a4,0(a0)
+ sw a5,0(a0)
+
+ li a0, 0x20000000
+ li a1, 123456789
+ sw a1,0(a0)
+
+ /* trap */
+ ebreak
+
+
+/* Hard mul functions for multest.c
+ **********************************/
+
+hard_mul:
+ mul a0, a0, a1
+ ret
+
+hard_mulh:
+ mulh a0, a0, a1
+ ret
+
+hard_mulhsu:
+ mulhsu a0, a0, a1
+ ret
+
+hard_mulhu:
+ mulhu a0, a0, a1
+ ret
+
+hard_div:
+ div a0, a0, a1
+ ret
+
+hard_divu:
+ divu a0, a0, a1
+ ret
+
+hard_rem:
+ rem a0, a0, a1
+ ret
+
+hard_remu:
+ remu a0, a0, a1
+ ret
+
diff --git a/firmware/stats.c b/firmware/stats.c
new file mode 100644
index 0000000..80e22dd
--- /dev/null
+++ b/firmware/stats.c
@@ -0,0 +1,42 @@
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+
+#include "firmware.h"
+
+static void stats_print_dec(unsigned int val, int digits, bool zero_pad)
+{
+ char buffer[32];
+ char *p = buffer;
+ while (val || digits > 0) {
+ if (val)
+ *(p++) = '0' + val % 10;
+ else
+ *(p++) = zero_pad ? '0' : ' ';
+ val = val / 10;
+ digits--;
+ }
+ while (p != buffer) {
+ if (p[-1] == ' ' && p[-2] == ' ') p[-1] = '.';
+ print_chr(*(--p));
+ }
+}
+
+void stats(void)
+{
+ unsigned int num_cycles, num_instr;
+ __asm__ volatile ("rdcycle %0; rdinstret %1;" : "=r"(num_cycles), "=r"(num_instr));
+ print_str("Cycle counter ........");
+ stats_print_dec(num_cycles, 8, false);
+ print_str("\nInstruction counter ..");
+ stats_print_dec(num_instr, 8, false);
+ print_str("\nCPI: ");
+ stats_print_dec((num_cycles / num_instr), 0, false);
+ print_str(".");
+ stats_print_dec(((100 * num_cycles) / num_instr) % 100, 2, true);
+ print_str("\n");
+}
+