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