diff --git a/scripts/romload/.gitignore b/scripts/romload/.gitignore new file mode 100644 index 0000000..6f1295b --- /dev/null +++ b/scripts/romload/.gitignore @@ -0,0 +1,11 @@ +firmware.d +firmware.elf +firmware.hex +firmware32.hex +firmware.o +firmware_addr.txt +firmware_dbg.v +syscalls.o +testbench.vvp +testbench.vcd +start.elf diff --git a/scripts/romload/Makefile b/scripts/romload/Makefile new file mode 100644 index 0000000..d510fa8 --- /dev/null +++ b/scripts/romload/Makefile @@ -0,0 +1,41 @@ +ifndef RISCV_TOOLS_PREFIX +RISCV_TOOLS_PREFIX = /opt/riscv32ic/bin/riscv32-unknown-elf- +endif +CXX = $(RISCV_TOOLS_PREFIX)g++ -march=rv32i +CC = $(RISCV_TOOLS_PREFIX)gcc -march=rv32i +AS = $(RISCV_TOOLS_PREFIX)gcc -march=rv32i +CXXFLAGS = -MD -Os -Wall -std=c++11 +CCFLAGS = -MD -Os -Wall +#LDFLAGS = -Wl,--gc-sections,--no-relax +LDFLAGS = -Wl,--gc-sections +LDLIBS = + +test: testbench.vvp firmware32.hex + vvp -l testbench.log -N testbench.vvp + +testbench.vvp: testbench.v ../../picorv32.v firmware_dbg.v + iverilog -o testbench.vvp testbench.v ../../picorv32.v + chmod -x testbench.vvp + +firmware32.hex: firmware.elf hex8tohex32.py + $(RISCV_TOOLS_PREFIX)objcopy -O verilog firmware.elf firmware.tmp + python3 hex8tohex32.py firmware.tmp > firmware32.hex + + +firmware_dbg.v: firmware.map + python3 map2debug.py + +start.o: start.S + $(CC) -c -nostdlib start.S $(LDLIBS) + +firmware.elf: firmware.o syscalls.o start.o + $(CC) $(LDFLAGS),-Map=firmware.map -o $@ $^ -T sections.ld $(LDLIBS) + chmod -x firmware.elf + +clean: + rm -f *.o *.d *.tmp start.elf + rm -f firmware.elf firmware.hex firmware32.hex + rm -f testbench.vvp testbench.vcd + +-include *.d +.PHONY: test clean diff --git a/scripts/romload/firmware.c b/scripts/romload/firmware.c new file mode 100644 index 0000000..72bb9a1 --- /dev/null +++ b/scripts/romload/firmware.c @@ -0,0 +1,21 @@ +#include +#include + +int x1 = 1000; +int x2 = 2000; + +void main() +{ + int z; + x1 = 50; + x2 = 50; + + printf("hello\n"); + z = (x1 + x2); + if (z == 100) + printf("TEST PASSED\n"); + else + printf("TEST FAILED, z=%d\n", z); + exit(0); +} + diff --git a/scripts/romload/hex8tohex32.py b/scripts/romload/hex8tohex32.py new file mode 100644 index 0000000..ae44101 --- /dev/null +++ b/scripts/romload/hex8tohex32.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import fileinput +import itertools + +ptr = 0 +data = [] + +def write_data(): + if len(data) != 0: + print("@%08x" % (ptr >> 2)) + while len(data) % 4 != 0: + data.append(0) + for word_bytes in zip(*([iter(data)]*4)): + print("".join(["%02x" % b for b in reversed(word_bytes)])) + +for line in fileinput.input(): + if line.startswith("@"): + addr = int(line[1:], 16) + if addr > ptr+4: + write_data() + ptr = addr + data = [] + while ptr % 4 != 0: + data.append(0) + ptr -= 1 + else: + while ptr + len(data) < addr: + data.append(0) + else: + data += [int(tok, 16) for tok in line.split()] + +write_data() + diff --git a/scripts/romload/map2debug.py b/scripts/romload/map2debug.py new file mode 100644 index 0000000..fc5c97c --- /dev/null +++ b/scripts/romload/map2debug.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +import re + +symbol = re.compile("\s*0x([0-9a-f]+)\s+([\w_]+)") +symbol_map = {} +with open("firmware.map", "r") as fh: + for fd in fh: + sym = symbol.match(fd) + if (sym): + addr = int(sym.group(1), 16) + symbol_map[addr] = sym.group(2) + +with open("firmware_dbg.v", "w") as fh: + for k, v in symbol_map.items(): + fh.write("`define C_SYM_{1:s} 32'h{0:08x}\n".format(k, v.upper())) + fh.write(" task firmware_dbg;\n") + fh.write(" input [31:0] addr;\n"); + fh.write(" begin\n"); + fh.write(" case (addr)\n"); + for k, v in symbol_map.items(): + fh.write(" 32'h{0:08x} : $display(\"%t: FCALL: {1:s}\", $time);\n".format(k, v)) + fh.write(" endcase\n"); + fh.write(" end\n"); + fh.write(" endtask\n"); + +with open("firmware_addr.txt", "w") as fh: + for k, v in symbol_map.items(): + fh.write("{0:08x} {1:s}\n".format(k,v)) + diff --git a/scripts/romload/sections.ld b/scripts/romload/sections.ld new file mode 100644 index 0000000..2ec3954 --- /dev/null +++ b/scripts/romload/sections.ld @@ -0,0 +1,45 @@ +/* +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. +*/ + +/* starting address needs to be > 0 due to known bug in RISCV/GNU linker */ +MEMORY { + rom(rwx) : ORIGIN = 0x00000100, LENGTH = 63k + ram(rwx) : ORIGIN = 0x00020000, LENGTH = 16k +} + +ENTRY(_pvstart); + +SECTIONS { + .rom : { + _pvstart*(.text); + start*(.text); + . = 0x100; + . = ALIGN(4); + *(.text); + } > rom + + .data : { + _data_lma = LOADADDR(.data); + _data = .; + __global_pointer$ = . ; + *(.data .data.* ) + *(.sdata .sdata.*) + . = ALIGN(4); + _edata = .; + } >ram AT>rom + + .bss : { + _bss_start = .; + *(.bss .bss.*) + . = ALIGN(4); + _bss_end = .; + _end = .; + } >ram + +} diff --git a/scripts/romload/start.S b/scripts/romload/start.S new file mode 100644 index 0000000..be59808 --- /dev/null +++ b/scripts/romload/start.S @@ -0,0 +1,52 @@ +.section .text +.global _start +.global _pvstart + +_pvstart: +/* 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 + +/* set stack pointer */ + +lui sp, %hi(4*1024*1024) +addi sp, sp, %lo(4*1024*1024) + +/* push zeros on the stack for argc and argv */ +/* (stack is aligned to 16 bytes in riscv calling convention) */ +addi sp,sp,-16 +sw zero,0(sp) +sw zero,4(sp) +sw zero,8(sp) +sw zero,12(sp) + +j _start diff --git a/scripts/romload/syscalls.c b/scripts/romload/syscalls.c new file mode 100644 index 0000000..82e633b --- /dev/null +++ b/scripts/romload/syscalls.c @@ -0,0 +1,95 @@ +// An extremely minimalist syscalls.c for newlib +// Based on riscv newlib libgloss/riscv/sys_*.c +// Written by Clifford Wolf. + +#include +#include +#include + +#define UNIMPL_FUNC(_f) ".globl " #_f "\n.type " #_f ", @function\n" #_f ":\n" + +asm ( + ".text\n" + ".align 2\n" + UNIMPL_FUNC(_open) + UNIMPL_FUNC(_openat) + UNIMPL_FUNC(_lseek) + UNIMPL_FUNC(_stat) + UNIMPL_FUNC(_lstat) + UNIMPL_FUNC(_fstatat) + UNIMPL_FUNC(_isatty) + UNIMPL_FUNC(_access) + UNIMPL_FUNC(_faccessat) + UNIMPL_FUNC(_link) + UNIMPL_FUNC(_unlink) + UNIMPL_FUNC(_execve) + UNIMPL_FUNC(_getpid) + UNIMPL_FUNC(_fork) + UNIMPL_FUNC(_kill) + UNIMPL_FUNC(_wait) + UNIMPL_FUNC(_times) + UNIMPL_FUNC(_gettimeofday) + UNIMPL_FUNC(_ftime) + UNIMPL_FUNC(_utime) + UNIMPL_FUNC(_chown) + UNIMPL_FUNC(_chmod) + UNIMPL_FUNC(_chdir) + UNIMPL_FUNC(_getcwd) + UNIMPL_FUNC(_sysconf) + "j unimplemented_syscall\n" +); + +void unimplemented_syscall() +{ + const char *p = "Unimplemented system call called!\n"; + while (*p) + *(volatile int*)0x10000000 = *(p++); + asm volatile ("ebreak"); + __builtin_unreachable(); +} + +ssize_t _read(int file, void *ptr, size_t len) +{ + // always EOF + return 0; +} + +ssize_t _write(int file, const void *ptr, size_t len) +{ + const void *eptr = ptr + len; + while (ptr != eptr) + *(volatile int*)0x10000000 = *(char*)(ptr++); + return len; +} + +int _close(int file) +{ + // close is called before _exit() + return 0; +} + +int _fstat(int file, struct stat *st) +{ + // fstat is called during libc startup + errno = ENOENT; + return -1; +} + +void *_sbrk(ptrdiff_t incr) +{ + extern unsigned char _end[]; // Defined by linker + static unsigned long heap_end; + + if (heap_end == 0) + heap_end = (long)_end; + + heap_end += incr; + return (void *)(heap_end - incr); +} + +void _exit(int exit_status) +{ + asm volatile ("ebreak"); + __builtin_unreachable(); +} + diff --git a/scripts/romload/testbench.v b/scripts/romload/testbench.v new file mode 100644 index 0000000..e38819d --- /dev/null +++ b/scripts/romload/testbench.v @@ -0,0 +1,140 @@ +`timescale 1 ns / 1 ps +`undef VERBOSE_MEM +//`undef WRITE_VCD +`undef MEM8BIT + +// define the size of our ROM +// simulates ROM by suppressing writes below this address +`define ROM_SIZE 32'h0001_00FF + +module testbench; + reg clk = 1; + reg resetn = 0; + wire trap; + + always #5 clk = ~clk; + + initial begin + repeat (100) @(posedge clk); + resetn <= 1; + end + + wire mem_valid; + wire mem_instr; + reg mem_ready; + wire [31:0] mem_addr; + wire [31:0] mem_wdata; + wire [3:0] mem_wstrb; + reg [31:0] mem_rdata; + +`include "firmware_dbg.v" + + picorv32 #( + .COMPRESSED_ISA(1), + .PROGADDR_RESET(32'h100) + ) uut ( + .clk (clk ), + .resetn (resetn ), + .trap (trap ), + .mem_valid (mem_valid ), + .mem_instr (mem_instr ), + .mem_ready (mem_ready ), + .mem_addr (mem_addr ), + .mem_wdata (mem_wdata ), + .mem_wstrb (mem_wstrb ), + .mem_rdata (mem_rdata ) + ); + + localparam MEM_SIZE = 4*1024*1024; +`ifdef MEM8BIT + reg [7:0] memory [0:MEM_SIZE-1]; + initial + $readmemh("firmware.hex", memory); + end +`else + reg [31:0] memory [0:MEM_SIZE/4-1]; + integer x; + + // simulate hardware assist of clearing RAM and copying ROM data into + // memory + initial + begin + // clear memory + for (x=0; x