Commit 5cffb670 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

tb: automatized ISA testbench (for all feature variants), initial version

parent ecc50e05
/*
* This program source code file is part of MasterFip project.
*
* Copyright (C) 2013-2017 CERN
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/* logger.svh - implementation of test result logging classes */
`ifndef __LOGGER_SVH
`define __LOGGER_SVH
`include "serializable.svh"
// converts an array of bytes to a string with given format
function automatic string array2str(string fmt, uint8_t data[$]);
string rv ="";
for(int i =0 ;i<data.size();i++)
rv = {rv, $sformatf(fmt, data[i]) };
return rv;
endfunction // str
// represents a single test log:
class UnitTestMessage;
string m_msg;
int m_slot;
function new ( int slot, string msg );
m_msg = msg;
m_slot = slot;
endfunction // new
endclass // UnitTestMessage
typedef enum
{
TR_UNKNOWN,
TR_FAIL,
TR_PASS
} TestResult;
// represents a single test log:
class UnitTest;
// test ID
int m_id;
// final result (failure, pass, unknown)
TestResult m_result;
// name of the test
string m_name;
// detailed reason for the failure
string m_failureReason;
// message buffer
UnitTestMessage m_messages[$];
function automatic void msg( int slot, string str );
UnitTestMessage m = new( slot, str );
m_messages.push_back(m);
endfunction // msg
function new ( int id, string name );
m_id = id;
m_name = name;
m_result = TR_UNKNOWN;
endfunction // new
endclass // UnitTest
// class Logger
//
// A singleton class that handles all test result logging activities.
class Logger;
int m_id;
protected static Logger m_self;
protected int m_loggerId;
protected UnitTest m_currentTest;
protected UnitTest m_tests[$];
protected int m_passedTests;
protected int m_failedTests;
function new ( string log_file, int id = -1 );
m_id = 1;
m_loggerId = id;
m_currentTest = null;
m_passedTests = 0;
m_failedTests = 0;
endfunction
// returns the singleton instance
static function Logger get();
if (m_self == null) begin
m_self = new( "sim_log.txt" );
end
return m_self;
endfunction // get
// begins a test
function automatic void startTest( string name );
m_currentTest = new( m_id, name );
m_tests.push_back(m_currentTest);
$display("[*] Running test %d: %s", m_id, name);
m_id++;
endfunction // startTest
// marks the current test as passed
function automatic void pass();
if( m_currentTest.m_result == TR_UNKNOWN)
begin
$display("[*] Test %d PASSED", m_id);
m_currentTest.m_result = TR_PASS;
end
endfunction // pass
// marks the current test as failed
function automatic void fail ( string reason );
$display("[*] Test %d FAILED: %s", m_id, reason);
m_currentTest.m_result = TR_FAIL;
m_currentTest.m_failureReason = reason;
endfunction
// logs a message within the scope of the current test
function automatic void msg ( int slot, string m );
if(m_currentTest)
m_currentTest.msg(slot, m);
$display(" %s", m);
endfunction // msg
function automatic int getPassedTestCount();
automatic int cnt = 0;
foreach(m_tests[i])
if (m_tests[i].m_result == TR_PASS)
cnt++;
return cnt;
endfunction // getPassedTestCount
function automatic int getFailedTestCount();
automatic int cnt = 0;
foreach(m_tests[i])
if (m_tests[i].m_result == TR_FAIL)
cnt++;
return cnt;
endfunction // getPassedTestCount
function automatic void fprint(int fd, string str);
$fdisplay(fd, str);
$display(str);
endfunction // fprint
function automatic string getSystemDate();
automatic int fd;
string t;
void'($system("date +%X--%x > sys_time.tmp"));
fd = $fopen("sys_time.tmp","r");
void'($fscanf(fd,"%s",t));
$fclose(fd);
void'($system("rm sys_time.tmp"));
return t;
endfunction // getSystemDate
function automatic void writeTestReport( string filename );
automatic int fd = $fopen(filename,"wb");
fprint(fd, "MasterFIP SV Test Report");
fprint(fd, $sformatf("Test date: %s\n\n", getSystemDate() ));
fprint(fd, "Test Summary ");
fprint(fd, "-------------");
fprint(fd, $sformatf("%-02d tests PASSED", getPassedTestCount() ) );
fprint(fd, $sformatf("%-02d tests FAILED", getFailedTestCount() ) );
fprint(fd, "\nIndividual Test Results:" );
fprint(fd, "ID | Test Name | Status | Failure Reason" );
fprint(fd, "----------------------------------------------------------------------------------------------------------" );
foreach(m_tests[i])
begin
fprint(fd, $sformatf("%-3d | %-74s | %-6s | %s" , m_tests[i].m_id, m_tests[i].m_name, m_tests[i].m_result == TR_PASS ? "PASS" : "FAIL", m_tests[i].m_failureReason ) );
end
fprint(fd,"\n\n");
foreach(m_tests[i])
begin
automatic UnitTest test = m_tests[i];
$fdisplay(fd, "Test messages for test %d", test.m_id );
$fdisplay(fd, "---------------------------\n" );
foreach(test.m_messages[j])
begin
$fdisplay(fd, test.m_messages[j].m_msg);
end
$fdisplay(fd, "\n---------------------------\n" );
end
endfunction
endclass // Logger
class LoggerClient;
function automatic void startTest( string name );
automatic Logger l = Logger::get();
l.startTest( name );
endfunction // startTest
function automatic void pass();
automatic Logger l = Logger::get();
l.pass();
endfunction // pass
function automatic void fail ( string reason );
automatic Logger l = Logger::get();
l.fail(reason);
endfunction
function automatic void msg ( int slot, string m );
automatic Logger l = Logger::get();
l.msg(slot, m);
endfunction // msg
endclass // LoggerClient
`endif // `ifndef __LOGGER_SVH
`ifndef __SERIALIZABLE_SVH
`define __SERIALIZABLE_SVH
`include "simdrv_defs.svh"
typedef byte int8_t;
typedef byte unsigned uint8_t;
typedef uint8_t uint8_array_t[$];
class ByteBuffer;
uint8_t data[$];
int pos;
function new();
pos = 0;
endfunction // new
function ByteBuffer copy();
copy = new();
copy.pos = this.pos;
copy.data = this.data;
return copy;
endfunction // copy
task automatic clear();
data = '{};
pos = 0;
endtask
task dump();
int i;
$display("buffer has %d bytes", data.size());
for (i=0;i<data.size();i++)
$display("%d: %x", i, data[i]);
endtask // dump
function int size();
return data.size();
endfunction // size
function int getPos();
return pos;
endfunction // getPos
function automatic void setPos( int pos_ );
pos = pos_;
endfunction // setPos
function automatic void addByte ( uint8_t c );
data.push_back(c);
endfunction // addByte
function automatic void addShort ( uint32_t c );
data.push_back((c >> 8) & 'hff);
data.push_back(c & 'hff);
endfunction // addShort
function automatic void addWord ( uint32_t c );
data.push_back((c >> 24) & 'hff);
data.push_back((c >> 16) & 'hff);
data.push_back((c >> 8) & 'hff);
data.push_back(c & 'hff);
endfunction // addWord
function automatic void addBytes ( uint8_t d[$] );
for (int i=0;i<d.size();i++)
data.push_back(d[i]);
endfunction // addBytes
function automatic uint8_t getByte();
automatic uint8_t rv = data[pos++];
return rv;
endfunction // getByte
function automatic uint8_t at(int pos_);
return data[pos_];
endfunction
function automatic uint8_array_t getBytes(int count);
automatic uint8_array_t rv;
for (int i=0;i<count;i++)
rv.push_back(data[pos++]);
return rv;
endfunction // getBytes
function automatic uint32_t getWord();
automatic uint32_t rv;
rv = data[pos++];
rv <<= 8;
rv |= data[pos++];
rv <<= 8;
rv |= data[pos++];
rv <<= 8;
rv |= data[pos++];
return rv;
endfunction // getWord
function automatic void reset();
pos = 0;
endfunction
// reset
endclass // ByteBuffer
class Serializable;
protected ByteBuffer m_data;
virtual function automatic void serialize( ByteBuffer data );
endfunction // serialize
virtual function automatic void deserialize ( ByteBuffer data );
m_data = data;
endfunction // deserialize
virtual task automatic dump();
if(!m_data)
return;
m_data.dump();
endtask // dump
function automatic void deserializeBytes ( uint8_t data[$]);
automatic ByteBuffer b = new ;
b.data = data;
deserialize( b );
endfunction // deserializeBytes
endclass // Serializable
`endif // `ifndef __SERIALIZABLE_SVH
`ifndef SIMDRV_DEFS_SV
`define SIMDRV_DEFS_SV 1
typedef longint unsigned uint64_t;
typedef int unsigned uint32_t;
typedef shortint unsigned uint16_t;
typedef uint64_t u64_array_t[];
typedef byte byte_array_t[];
virtual class CBusAccessor;
static int _null = 0;
int m_default_xfer_size;
task set_default_xfer_size(int default_size);
m_default_xfer_size = default_size;
endtask // set_default_xfer_size
pure virtual task writem(uint64_t addr[], uint64_t data[], input int size, ref int result);
pure virtual task readm(uint64_t addr[], ref uint64_t data[], input int size, ref int result);
virtual task read(uint64_t addr, ref uint64_t data, input int size = m_default_xfer_size, ref int result = _null);
int res;
uint64_t aa[1], da[];
da= new[1];
aa[0] = addr;
readm(aa, da, size, res);
data = da[0];
endtask
virtual task write(uint64_t addr, uint64_t data, input int size = m_default_xfer_size, ref int result = _null);
uint64_t aa[1], da[1];
aa[0] = addr;
da[0] = data;
writem(aa, da, size, result);
endtask
endclass // CBusAccessor
class CSimUtils;
static function automatic u64_array_t pack(byte x[], int size, int big_endian = 1);
u64_array_t tmp;
int i, j;
int nwords, nbytes;
nwords = (x.size() + size - 1) / size;
tmp = new [nwords];
for(i=0;i<nwords;i++)
begin
uint64_t d;
d =0;
nbytes = (x.size() - i * nbytes > size ? size : x.size() - i*nbytes);
for(j=0;j<nbytes;j++)
begin
if(big_endian)
d = d | ((x[i*size+j] << (8*(size-1-j))));
else
d = d | ((x[i*size+j] << (8*j)));
end
tmp[i] = d;
end
return tmp;
endfunction // pack
static function automatic byte_array_t unpack(u64_array_t x, int entry_size, int size, int big_endian = 1);
byte_array_t tmp;
int i, n;
tmp = new[size];
n = 0;
i = 0;
while(n < size)
begin
tmp[n] = x[i] >> (8*(entry_size-1 - (n % entry_size)));
n++;
if(n % entry_size == 0)
i++;
end
return tmp;
endfunction // unpack
endclass // CSimUtils
static CSimUtils SimUtils;
`endif
\ No newline at end of file
......@@ -4,7 +4,7 @@ syn_device="xc6slx150t"
action = "simulation"
target = "xilinx"
include_dirs=["../../rtl"]
include_dirs=["../../rtl", "../include"]
vcom_opt="-mixedsvvh l"
......
......@@ -19,35 +19,89 @@
*/
`include "urv_defs.v"
`include "logger.svh"
`timescale 1ns/1ps
module main;
`define CFG_WITH_HW_MULH 'h1
`define CFG_WITH_HW_DIV 'h2
`define CFG_WITH_HW_DEBUG 'h4
`define CFG_WITH_COMPRESSED_INSNS 'h8
module ICpuTestWrapper
(
input clk_i
);
reg clk = 0;
reg rst = 1;
wire [31:0] im_addr;
reg r_with_hw_mulh = 0;
reg r_with_hw_divide = 0;
reg r_with_hw_debug = 0;
reg irq = 0;
parameter int n_configs = 8;
parameter int mem_size = 16384;
wire [31:0] im_addr_m[n_configs];
wire [31:0] dm_addr_m[n_configs];
wire [31:0] dm_data_s_m[n_configs];
wire [3:0] dm_data_select_m[n_configs];
wire dm_write_m[n_configs];
wire irq_m[n_configs];
int r_active_cpu = 0;
reg [31:0] im_addr;
reg [31:0] im_data;
reg im_valid;
wire [31:0] dm_addr;
wire [31:0] dm_data_s;
reg [31:0] dm_addr;
reg [31:0] dm_data_s;
reg [31:0] dm_data_l;
wire [3:0] dm_data_select;
wire dm_write;
reg [3:0] dm_data_select;
reg dm_write;
reg dm_valid_l = 1;
reg dm_ready;
localparam int mem_size = 16384;
reg [31:0] mem[0:mem_size - 1];
task automatic load_ram(string filename);
string current_msg;
int test_complete = 0;
task automatic selectConfiguration( int mask );
r_active_cpu = mask;
endtask // selectConfiguration
function automatic string getConfigurationString();
automatic string rv;
if( r_active_cpu & `CFG_WITH_HW_MULH )
rv = {rv, "hw_mulh"};
if( r_active_cpu & `CFG_WITH_HW_DIV )
rv = {rv, " hw_div"};
if( r_active_cpu & `CFG_WITH_HW_DEBUG )
rv = {rv, " hw_debug"};
return rv;
endfunction // getConfigurationString
task automatic runTest(string filename);
int f = $fopen(filename,"r");
int n, i;
int n, i;
current_msg = "";
test_complete = 0;
rst <= 1;
@(posedge clk_i);
@(posedge clk_i);
if( f == 0)
begin
......@@ -57,22 +111,41 @@ module main;
while(!$feof(f))
begin
int addr, data;
int addr, data, r;
string cmd;
void'($fscanf(f,"%s %08x %08x", cmd,addr,data));
r = $fscanf(f,"%s %08x %08x", cmd,addr,data);
if ( r < 0 )
break;
if(cmd == "write")
begin
mem[addr % mem_size] = data;
end
end
endtask // load_ram
$fclose(f);
@(posedge clk_i);
rst <= 0;
@(posedge clk_i);
endtask // runProgram
int seed;
function automatic string getTestResult();
return current_msg;
endfunction // getTestResult
function automatic int isTestComplete();
return test_complete;
endfunction // isTestComplete
int seed = 0;
always@(posedge clk)
always@(posedge clk_i)
begin
// Read memory for insn
if( $dist_uniform(seed, 0, 100 ) <= 100) begin
......@@ -96,109 +169,206 @@ module main;
dm_data_l <= mem[(dm_addr/4) % mem_size];
end // always@ (posedge clk)
urv_cpu DUT
(
.clk_i(clk),
.rst_i(rst),
.irq_i ( irq ),
// instruction mem I/F
.im_addr_o(im_addr),
.im_data_i(im_data),
.im_valid_i(im_valid),
// data mem I/F
.dm_addr_o(dm_addr),
.dm_data_s_o(dm_data_s),
.dm_data_l_i(dm_data_l),
.dm_data_select_o(dm_data_select),
.dm_store_o(dm_write),
.dm_load_o(),
.dm_store_done_i(1'b1),
.dm_load_done_i(1'b1),
.dm_ready_i(dm_ready),
// Debug
.dbg_force_i(1'b0),
.dbg_enabled_o(),
.dbg_insn_i(32'h0),
.dbg_insn_set_i(1'b0),
.dbg_insn_ready_o(),
// Debug mailbox
.dbg_mbx_data_i(0),
.dbg_mbx_write_i(1'b0),
.dbg_mbx_data_o()
);
genvar i;
generate
for(i = 0; i < n_configs; i++)
begin
urv_cpu
#(
.g_with_hw_mulh( i & `CFG_WITH_HW_MULH ? 1 : 0 ),
.g_with_hw_div( i & `CFG_WITH_HW_DIV ? 1 : 0 ),
.g_with_hw_debug( i & `CFG_WITH_HW_DEBUG ? 1 : 0 )
)
DUTx
(
.clk_i(i == r_active_cpu ? clk_i : 1'b0 ),
.rst_i(i == r_active_cpu ? rst : 1'b1 ),
.irq_i ( irq ),
// instruction mem I/F
.im_addr_o(im_addr_m[i]),
.im_data_i(im_data),
.im_valid_i(im_valid),
// data mem I/F
.dm_addr_o(dm_addr_m[i]),
.dm_data_s_o(dm_data_s_m[i]),
.dm_data_l_i(dm_data_l),
.dm_data_select_o(dm_data_select_m[i]),
.dm_store_o(dm_write_m[i]),
.dm_load_o(),
.dm_store_done_i(1'b1),
.dm_load_done_i(1'b1),
.dm_ready_i(dm_ready),
// Debug
.dbg_force_i(1'b0),
.dbg_enabled_o(),
.dbg_insn_i(32'h0),
.dbg_insn_set_i(1'b0),
.dbg_insn_ready_o(),
// Debug mailbox
.dbg_mbx_data_i(0),
.dbg_mbx_write_i(1'b0),
.dbg_mbx_data_o()
);
end // for (i = 0; i < n_configs; i++)
endgenerate
always@*
begin
im_addr <= im_addr_m[r_active_cpu];
dm_addr <= dm_addr_m[r_active_cpu];
dm_data_s <= dm_data_s_m[r_active_cpu];
dm_data_select <= dm_data_select_m[r_active_cpu];
dm_write <= dm_write_m[r_active_cpu];
end
always@(posedge clk_i)
if(dm_write)
begin
automatic bit [7:0] chr = dm_data_s[7:0];
if(dm_addr == 'h100000)
begin
current_msg = $sformatf("%s%c", current_msg, chr);
end
else if(DUT.dm_addr == 'h100004)
begin
test_complete = 1;
end
end
endmodule // ICpuTestWrapper
module main;
reg clk = 0;
always #5ns clk <= ~clk;
integer f_console, f_exec_log;
reg test_complete = 0;
ICpuTestWrapper DUT ( clk );
initial begin
string tests[$];
const string test_dir = "../../sw/testsuite/isa";
class ISATestRunner extends LoggerClient;
typedef enum
{
R_OK = 0,
R_FAIL = 1,
R_TIMEOUT = 2
} TestStatus;
const time c_test_timeout = 1ms;
task automatic runTest(string filename, ref TestStatus status, ref int failedTest );
automatic time t_start = $time;
DUT.runTest(filename);
failedTest = 0;
while(!DUT.isTestComplete() )
begin
#1us;
if ( $time - t_start > c_test_timeout )
begin
status = R_TIMEOUT;
return;
end
end
int f;
int n, i;
if ($sscanf( DUT.getTestResult(), "Test %d failed", failedTest ) == 1)
status = R_FAIL;
else
status = R_OK;
endtask // runTest
f_console = $fopen("console.txt","wb");
task automatic runAllTests( string test_dir, string list_file );
automatic string tests[$];
automatic int n, i, f, failCount = 0;
automatic string failedTests = "";
f = $fopen( $sformatf("%s/%s", test_dir, list_file ) ,"r");
f = $fopen( {test_dir,"/tests.lst"} ,"r");
while(!$feof(f))
begin
string fname;
automatic string fname;
void'($fscanf(f,"%s", fname));
tests.push_back(fname);
end
for (i=0;i<tests.size();i++)
begin
if (tests[i][0] == "#")
automatic int failedTest;
automatic TestStatus status;
automatic string s;
if (tests[i][0] == "#" || tests[i] == "")
continue;
rst = 1;
repeat(3) @(posedge clk);
$display("Loading %s", {test_dir,"/",tests[i]} );
load_ram({test_dir,"/",tests[i]});
repeat(3) @(posedge clk);
rst = 0;
test_complete= 0;
while(!test_complete)
#1us;
runTest({test_dir,"/",tests[i]}, status, failedTest );
if ( status == R_OK )
s = "PASS";
else if ( status == R_TIMEOUT )
begin
s = "Timeout (likely fail due to CPU freeze)";
failCount++;
end else begin
s = $sformatf ("FAIL (subtest %d)", failedTest );
failCount++;
end
msg(0, $sformatf("%s: %s", tests[i], s ) );
end
if(failCount)
fail ( $sformatf( "%d tests FAILED", failCount ) );
else
pass();
end
endtask // runAllTests
$display("End of tests");
$stop;
end // initial begin
endclass // ISATestRunner
always@(posedge clk)
if(dm_write)
begin
if(dm_addr == 'h100000)
begin
// $display("\n ****** TX '%c' \n", dm_data_s[7:0]) ;
// $fwrite(f_exec_log,"\n ****** TX '%c' \n", dm_data_s[7:0]) ;
$write("%c", dm_data_s[7:0]);
$fwrite(f_console,"%c", dm_data_s[7:0]);
$fflush(f_console);
end
else if(dm_addr == 'h100004)
begin
// $display("Test complete." );
test_complete = 1;
end
end
const int n_configs = 8;
initial begin
automatic int i;
automatic ISATestRunner testRunner = new;
automatic Logger l = Logger::get();
for(i=0;i<=7;i++)
begin
DUT.selectConfiguration(i);
l.startTest($sformatf( "Full ISA Test for feature set [%s]", DUT.getConfigurationString() ) );
testRunner.runAllTests("../../sw/testsuite/isa", "tests.lst" );
end
l.writeTestReport("report.txt");
end
endmodule // main
#vlog -sv main.sv +incdir+. +incdir+../../include/wb +incdir+../../include/vme64x_bfm +incdir+../../include +incdir+../include +incdir+../../sim
vsim -t 1ps work.main -novopt
vsim -L unisim -t 1ps work.main -novopt
set StdArithNoWarnings 1
set NumericStdNoWarnings 1
#do wave.do
do wave.do
radix -hexadecimal
run 2ms
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment