Merge pull request #94 from hutch31/master

Added new testbench and linker file for ROM load
This commit is contained in:
Clifford Wolf 2018-10-19 13:04:41 +02:00 committed by GitHub
commit 3a6ac16259
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 469 additions and 0 deletions

11
scripts/romload/.gitignore vendored Normal file
View File

@ -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

41
scripts/romload/Makefile Normal file
View File

@ -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

View File

@ -0,0 +1,21 @@
#include <stdio.h>
#include <stdlib.h>
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);
}

View File

@ -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()

View File

@ -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))

View File

@ -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
}

52
scripts/romload/start.S Normal file
View File

@ -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

View File

@ -0,0 +1,95 @@
// An extremely minimalist syscalls.c for newlib
// Based on riscv newlib libgloss/riscv/sys_*.c
// Written by Clifford Wolf.
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#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();
}

140
scripts/romload/testbench.v Normal file
View File

@ -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<MEM_SIZE/4; x=x+1) memory[x] = 0;
// load rom contents
$readmemh("firmware32.hex", memory);
// copy .data section
for (x=0; x<(`C_SYM__BSS_START - `C_SYM___GLOBAL_POINTER); x=x+4)
memory[(`C_SYM___GLOBAL_POINTER+x)/4] = memory[(`C_SYM__DATA_LMA+x)/4];
end
`endif
always @(posedge clk) begin
mem_ready <= 0;
if (mem_valid && !mem_ready) begin
mem_ready <= 1;
mem_rdata <= 'bx;
case (1)
mem_addr < MEM_SIZE: begin
`ifdef MEM8BIT
if (|mem_wstrb) begin
if (mem_wstrb[0]) memory[mem_addr + 0] <= mem_wdata[ 7: 0];
if (mem_wstrb[1]) memory[mem_addr + 1] <= mem_wdata[15: 8];
if (mem_wstrb[2]) memory[mem_addr + 2] <= mem_wdata[23:16];
if (mem_wstrb[3]) memory[mem_addr + 3] <= mem_wdata[31:24];
end else begin
mem_rdata <= {memory[mem_addr+3], memory[mem_addr+2], memory[mem_addr+1], memory[mem_addr]};
end
`else
if ((|mem_wstrb) && (mem_addr >= `ROM_SIZE)) begin
if (mem_wstrb[0]) memory[mem_addr >> 2][ 7: 0] <= mem_wdata[ 7: 0];
if (mem_wstrb[1]) memory[mem_addr >> 2][15: 8] <= mem_wdata[15: 8];
if (mem_wstrb[2]) memory[mem_addr >> 2][23:16] <= mem_wdata[23:16];
if (mem_wstrb[3]) memory[mem_addr >> 2][31:24] <= mem_wdata[31:24];
end else begin
mem_rdata <= memory[mem_addr >> 2];
end
`endif
end
mem_addr == 32'h 1000_0000: begin
$write("%c", mem_wdata[7:0]);
end
endcase
end
if (mem_valid && mem_ready) begin
`ifdef FIRMWARE_DEBUG_ADDR
firmware_dbg(mem_addr);
`endif
if ((mem_wstrb == 4'h0) && (mem_rdata === 32'bx)) $display("READ FROM UNITIALIZED ADDR=%x", mem_addr);
`ifdef VERBOSE_MEM
if (|mem_wstrb)
$display("WR: ADDR=%x DATA=%x MASK=%b", mem_addr, mem_wdata, mem_wstrb);
else
$display("RD: ADDR=%x DATA=%x%s", mem_addr, mem_rdata, mem_instr ? " INSN" : "");
`endif
if (^mem_addr === 1'bx ||
(mem_wstrb[0] && ^mem_wdata[ 7: 0] == 1'bx) ||
(mem_wstrb[1] && ^mem_wdata[15: 8] == 1'bx) ||
(mem_wstrb[2] && ^mem_wdata[23:16] == 1'bx) ||
(mem_wstrb[3] && ^mem_wdata[31:24] == 1'bx)) begin
$display("CRITICAL UNDEF MEM TRANSACTION");
$finish;
end
end
end
`ifdef WRITE_VCD
initial begin
$dumpfile("testbench.vcd");
$dumpvars(0, testbench);
end
`endif
always @(posedge clk) begin
if (resetn && trap) begin
repeat (10) @(posedge clk);
$display("TRAP");
$finish;
end
end
endmodule