mirror of https://github.com/YosysHQ/picorv32.git
Merge pull request #94 from hutch31/master
Added new testbench and linker file for ROM load
This commit is contained in:
commit
3a6ac16259
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue