Commit 37571aa1 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

sim: Redesign and cleanup of the common SystemVerilog testing code:

- Rework simdrv_defs into a package
- Use SV queues instead of dynamic arrays in the APIs (as they resemble C++'s std::vector a bit more, hence are more convenient to use)
- Added AXI4 BFMs from the PULP project library
- Added a bunch of simulation drivers (for the VUART & LM32 MCS cores)
- Added a trivial unit test/logging "framework" (logger.svh)

Note these changes will break your legacy testbenches, here's how to fix the most common issues:
- Replace the includes of simdrv_defs.svh indo an include of "gencores_sim_defs.svh" followed by
  import of gencores_sim_pkg package
- If your code uses CBusAccessor::readm/writem, change the addr/data parameters to use SV queues instead of dynamic arrays
parent 074d4468
files = [
"axi/axi_intf.sv",
"axi/axi_test.sv",
"axi/axi_pkg.sv",
"axi/axi_utils.sv",
"axi/rand_id_queue.sv",
"gencores_sim_pkg.sv" ];
modules = {"local" : [ "drivers" ] };
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
`include "gencores_sim_defs.svh"
interface ISimpleMemWriteIF (
input clk_i
);
parameter g_AW = 32;
parameter g_DW = 512;
logic [g_AW-1:0] addr;
logic [g_DW-1:0] data;
logic req;
logic ready;
logic [ 7:0] burst_len;
logic last;
modport master(output addr, data, req, burst_len, last, input ready);
modport slave(input addr, data, req, burst_len, last, output ready);
endinterface // ISimpleMemWriteIF
package axi_utils;
import gencores_sim_pkg::*;
import axi_test::*;
class CAXI4LiteAccessor extends CBusAccessor;
parameter g_AW = 32;
parameter g_DW = 32;
static int _null = 0;
typedef virtual AXI_LITE_DV #(
.AXI_ADDR_WIDTH(g_AW),
.AXI_DATA_WIDTH(g_DW)
) bus_t;
protected axi_lite_driver #(g_AW, g_DW) m_driver;
function new(bus_t bus);
m_driver = new(bus);
endfunction // new
task automatic reset();
m_driver.reset_master();
endtask // reset
// [master only] generic write(s), blocking
virtual task automatic writem( input u64_vector_t addr, u64_vector_t data, int size = 4,
ref int result = _null);
axi_pkg::resp_t resp;
m_driver.send_aw(addr[0], 0);
m_driver.send_w(data[0], 'hf);
m_driver.recv_b(resp);
// $display("Write!\n");
endtask // write
// [master only] generic read(s), blocking
virtual task automatic readm( input u64_vector_t addr, ref u64_vector_t data, input int size = 4,
ref int result = _null);
logic [31:0] d;
axi_pkg::resp_t resp;
m_driver.send_ar(addr[0], 0);
m_driver.recv_r(d, resp);
data[0] = d;
endtask // readm
endclass // CAXI4LiteAccessor
class CAXI4FullAccessor #(
parameter g_AW = 32,
parameter g_DW = 64,
parameter g_IW = 4,
parameter g_UW = 2
) extends CBusAccessor;
static int _null = 0;
typedef virtual AXI_BUS_DV #(
.AXI_ADDR_WIDTH(g_AW),
.AXI_DATA_WIDTH(g_DW),
.AXI_ID_WIDTH (g_IW),
.AXI_USER_WIDTH(g_UW)
) bus_t;
typedef axi_ax_beat#(
.AW(g_AW),
.IW(g_IW),
.UW(g_UW)
) ax_beat_t;
typedef axi_w_beat#(
.DW(g_DW),
.UW(g_UW)
) w_beat_t;
typedef axi_b_beat#(
.IW(g_IW),
.UW(g_UW)
) b_beat_t;
typedef axi_r_beat#(
.DW(g_DW),
.IW(g_IW),
.UW(g_UW)
) r_beat_t;
protected bus_t m_bus;
protected axi_driver #(g_AW, g_DW, g_IW, g_UW, 0ns, 1ns) m_driver;
function new(bus_t bus);
m_bus = bus;
m_driver = new(bus);
endfunction // new
task automatic reset();
m_driver.reset_master();
endtask // reset
virtual task write_burst(uint64_t addr, int count, uint64_t data[$], bit insert_gaps = 0,
ref int result = _null);
automatic axi_pkg::resp_t resp;
automatic ax_beat_t aw_beat = new;
automatic w_beat_t w_beat = new;
automatic b_beat_t b_beat = new;
aw_beat.ax_addr = addr;
aw_beat.ax_len = count - 1;
aw_beat.ax_burst = axi_pkg::BURST_INCR;
m_driver.send_aw(aw_beat);
fork
begin
automatic int i;
for (i = 0; i < count; i++) begin
w_beat.w_data = data[i];
w_beat.w_strb = 'hffffffff;
w_beat.w_last = (i == count - 1) ? 1 : 0;
m_driver.send_w(w_beat);
if (insert_gaps) @(posedge m_bus.clk_i);
end
end
m_driver.recv_b(b_beat);
join
endtask
virtual task read_burst(uint64_t addr, int count, ref uint64_t data[$],
input bit insert_gaps = 0, ref int result = _null);
automatic axi_pkg::resp_t resp;
automatic ax_beat_t ar_beat = new;
automatic r_beat_t r_beat = new;
ar_beat.ax_addr = addr;
ar_beat.ax_len = count - 1;
ar_beat.ax_burst = axi_pkg::BURST_INCR;
m_driver.send_ar(ar_beat);
data = '{};
forever begin
if (insert_gaps) @(posedge m_bus.clk_i);
m_driver.recv_r(r_beat);
// $display("RData %x RLast %d", r_beat.r_data, r_beat.r_last);
data.push_back(r_beat.r_data);
if (r_beat.r_last) break;
end
endtask
// [master only] generic write(s), blocking
virtual task automatic writem( input u64_vector_t addr, u64_vector_t data, int size = 4,
ref int result = _null);
/* axi_pkg::resp_t resp;
m_driver.send_aw( addr[0], 0 );
m_driver.send_w( data[0], 'hf );
m_driver.recv_b( resp );*/
endtask // write
// [master only] generic read(s), blocking
virtual task automatic readm(input u64_vector_t addr, ref u64_vector_t data, input int size = 4,
ref int result = _null);
/* logic [31:0] d;
axi_pkg::resp_t resp;
m_driver.send_ar( addr[0], 0 );
m_driver.recv_r( d, resp );
data[0] = d;*/
endtask // readm
endclass // CAXI4FullAccessor
class CAXI4MemSlave extends CMonitorableMemory;
parameter g_AW = 32;
parameter g_DW = 512;
parameter g_IW = 4;
parameter g_UW = 2;
typedef virtual AXI_BUS_DV #(
.AXI_ADDR_WIDTH(g_AW),
.AXI_DATA_WIDTH(g_DW),
.AXI_ID_WIDTH (g_IW),
.AXI_USER_WIDTH(g_UW)
) bus_t;
typedef axi_driver#(g_AW, g_DW, g_IW, g_UW, 1ns, 0ns) driver_t;
protected driver_t m_driver;
function new(bus_t bus);
super.new(g_DW);
m_driver = new(bus);
endfunction // new
virtual task automatic reset();
m_driver.reset_slave();
endtask // reset
virtual task automatic run();
automatic driver_t::b_beat_t b_beat = new;
automatic driver_t::ax_beat_t aw_beat = new;
automatic driver_t::w_beat_t w_beat = new;
automatic bit [g_AW-1:0] w_addr;
automatic int i;
forever
fork
begin
m_driver.recv_aw(aw_beat);
w_addr = aw_beat.ax_addr;
// $display("Got AWADDR %x", aw_beat.ax_addr );
i = 0;
end
begin
m_driver.recv_w(w_beat);
m_mem[w_addr] = w_beat.w_data;
w_addr += (g_DW / 8);
i++;
while (!w_beat.w_last) begin
m_driver.recv_w(w_beat);
// $display("WADDR %x", w_addr);
m_mem[w_addr] = w_beat.w_data;
w_addr += (g_DW / 8);
i++;
end
// $display("Burst: addr %x count %d", aw_beat.ax_addr, i );
// $display("Got W");
b_beat.b_id = aw_beat.ax_id;
b_beat.b_resp = 0;
m_driver.send_b(b_beat);
end
join
endtask // run
endclass // CAXI4MemSlave
class CSimpleIFMemSlave extends CMonitorableMemory;
parameter g_AW = 32;
parameter g_DW = 512;
typedef virtual ISimpleMemWriteIF #(
.g_AW(g_AW),
.g_DW(g_DW)
) bus_t;
protected bus_t m_bus;
function new(bus_t bus);
super.new(g_DW);
this.m_bus = bus;
endfunction // new
virtual task automatic reset();
m_bus.ready <= 0;
@(posedge m_bus.clk_i);
endtask // reset
virtual task automatic run();
uint64_t addr;
const int latency = 3;
int burst_cnt;
forever begin
if (m_bus.req) begin
addr = m_bus.addr;
burst_cnt = m_bus.burst_len;
$display("GotReq [0x%x, burst=%d, last=%d]", addr, burst_cnt, m_bus.last);
for (int i = 0; i < 3; i++) @(posedge m_bus.clk_i);
m_bus.ready <= 1;
while (!m_bus.last && burst_cnt > 0) begin
if (m_bus.req && m_bus.ready) begin
$display("Write %x: %x", addr, m_bus.data);
m_mem[addr*g_DW/8] = m_bus.data;
addr++;
burst_cnt--;
end
@(posedge m_bus.clk_i);
end
m_bus.ready <= 0;
end
@(posedge m_bus.clk_i);
end
endtask // run
endclass // CSimpleIFMemSlave
endpackage // axi_drivers
// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// Wrapper package so class can be used after `import rand_id_queue_pkg::*;`.
package rand_id_queue_pkg;
// ID Queue with Randomizing Output
class rand_id_queue #(
type data_t = logic,
int unsigned ID_WIDTH = 0
);
localparam int unsigned N_IDS = 2**ID_WIDTH;
localparam type id_t = logic[ID_WIDTH-1:0];
data_t queues[N_IDS-1:0][$];
int unsigned size;
function new();
size = 0;
endfunction
// Push a data element to the queue with the given ID.
function void push(id_t id, data_t data);
queues[id].push_back(data);
size++;
endfunction
// Determine if the ID queue is empty.
function bit empty();
return (size == 0);
endfunction
// Pick a non-empty queue at random and return the front element. Not defined if the ID queue is
// empty.
function data_t peek();
return queues[rand_id()][0];
endfunction
// Pick a non-empty queue at random, remove the front element from that queue, and return that
// element. Not defined if the ID queue is empty.
function data_t pop();
return pop_id(rand_id());
endfunction
// Remove the front element of the queue with the given ID and return that element. Not defined
// if the queue with the given ID is empty.
function data_t pop_id(id_t id);
size--;
return queues[id].pop_front();
endfunction
// Pick a non-empty queue at random and return the ID of that queue. Not defined if the ID queue
// is empty.
function id_t rand_id();
if (!empty()) begin
id_t id;
do begin
void'(std::randomize(id));
end while (queues[id].size() == 0);
return id;
end else begin
return 'x;
end
endfunction
// Set the front element of the queue with the given ID. Not defined if the queue with the given
// ID is empty; instead use `push()` to insert a new element.
function void set(id_t id, data_t data);
queues[id][0] = data;
endfunction
// Get the front element of the queue with the given ID. Not defined if the queue with the given
// ID is empty.
function data_t get(id_t id);
return queues[id][0];
endfunction
endclass
endpackage
This diff is collapsed.
files = ["lm32_mcs_driver.sv",
"vuart_driver.sv" ];
package lm32_mcs_driver_pkg;
`include "gencores_sim_defs.svh"
import gencores_sim_pkg::*;
class LM32MCSDriver;
CBusAccessor m_acc;
uint64_t m_base;
const uint64_t c_reg_csr = 'h0;
const uint64_t c_reg_udata = 'h4;
const uint64_t c_reg_uaddr = 'h8;
function new ( CBusAccessor acc, uint64_t base );
m_acc = acc;
m_base = base;
endfunction // new
task automatic reset();
$error("Called reset!");
m_acc.write( m_base + c_reg_csr, 0 ); // reset
m_acc.write( m_base + c_reg_csr, 1 ); // un-reset
endtask // reset
task automatic load_firmware( string filename );
int fd, nread, addr = 0;
uint32_t tmp;
uint64_t tmp2;
m_acc.write( m_base + c_reg_csr, 0 ); // reset
fd = $fopen(filename, "rb");
$display("lm32_mcs: loading %s", filename );
while(!$feof(fd))
begin
nread = $fread( tmp, fd );
$display("%x %d", tmp, nread);
m_acc.write( m_base + c_reg_uaddr, addr );
m_acc.write( m_base + c_reg_udata, tmp );
// m_acc.write( m_base + c_reg_uaddr, addr );
// m_acc.read( m_base + c_reg_udata, tmp2);
// if(tmp != tmp2)
// $error("verify failed %x %x", tmp, tmp2);
addr++;
end
$fclose(fd);
m_acc.write( m_base + c_reg_csr, 1 ); // un-reset
endtask // load_firmware
endclass // LM32MCSDriver
endpackage
\ No newline at end of file
package vuart_driver_pkg;
`include "gencores_sim_defs.svh"
import gencores_sim_pkg::*;
class VUARTDriver;
CBusAccessor m_acc;
uint64_t m_base;
string m_current;
function new ( CBusAccessor acc, uint64_t base );
m_acc = acc;
m_base = base;
endfunction
task update();
int c_reg_host_rdr = 'h14;
int c_host_rdr_ready = 'h100;
uint64_t rdr;
m_acc.read (m_base + c_reg_host_rdr, rdr);
// if(rdr != 0)
// $display("rdr %x", rdr);
if (rdr & c_host_rdr_ready)
begin
rdr &= 'hff;
$display("RX %c", rdr);
if ( rdr == 13 || rdr == 10 )
begin
$display ("VUART: %s", m_current);
m_current = "";
end else
m_current = $sformatf("%s%c", m_current, rdr & 'hff);
end
endtask // update
endclass // VUARTDriver
endpackage
`ifndef GENCORES_SIM_DEFS_SV
`define GENCORES_SIM_DEFS_SV 1
typedef byte unsigned uint8_t;
typedef longint unsigned uint64_t;
typedef int unsigned uint32_t;
typedef shortint unsigned uint16_t;
typedef uint64_t u64_vector_t[$];
typedef uint32_t u32_vector_t[$];
typedef uint16_t u16_vector_t[$];
typedef byte u8_vector_t[$];
`endif
package gencores_sim_pkg;
`include "gencores_sim_defs.svh"
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 automatic writem(input u64_vector_t addr, input u64_vector_t data,
input int size, ref int result = _null);
pure virtual task automatic readm(input u64_vector_t addr, ref u64_vector_t data,
input int size, ref int result = _null);
virtual task automatic read(uint64_t addr, ref uint64_t data,
input int size = m_default_xfer_size, ref int result = _null);
int res;
u64_vector_t aa = '{addr}, da = {0};
readm(aa, da, size, res);
data = da[0];
endtask
virtual task automatic write(uint64_t addr, uint64_t data, input int size = m_default_xfer_size,
ref int result = _null);
u64_vector_t aa = '{addr}, da = {data};
writem(aa, da, size, result);
endtask
endclass // CBusAccessor
class CSimUtils;
static function automatic u64_vector_t pack(input u8_vector_t x, int size, int big_endian = 1);
u64_vector_t tmp;
int i, j;
int nwords, nbytes;
nwords = (x.size() + size - 1) / size;
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.push_back( d );
end
return tmp;
endfunction // pack
static function automatic u8_vector_t unpack(input u64_vector_t x, int entry_size, int size,
int big_endian = 1);
u8_vector_t tmp;
int i, n;
n = 0;
i = 0;
while (n < size) begin
tmp.push_back( x[i] >> (8 * (entry_size - 1 - (n % entry_size))) );
n++;
if (n % entry_size == 0) i++;
end
return tmp;
endfunction // unpack
endclass
class CBusDevice;
protected CBusAccessor m_acc;
protected uint64_t m_base;
function new(CBusAccessor acc, uint64_t base);
m_acc = acc;
m_base = base;
endfunction // new
virtual task automatic writel(uint32_t addr, uint32_t val);
m_acc.write(m_base + addr, val);
endtask // writel
virtual task automatic readl(uint32_t addr, output uint32_t val);
automatic uint64_t val64;
m_acc.read(m_base + addr, val64);
val = val64;
endtask // readl
virtual task automatic set_bits(uint32_t addr, uint32_t bits);
uint32_t r;
readl(addr, r);
r |= bits;
writel(addr, r);
endtask
virtual task automatic clear_bits(uint32_t addr, uint32_t bits);
uint32_t r;
readl(addr, r);
r &= ~bits;
writel(addr, r);
endtask
endclass // CBusDevice
virtual class CMonitorableMemory;
parameter g_MAX_WIDTH = 1024;
protected int m_width;
typedef bit [g_MAX_WIDTH-1:0] mem_array_t[uint32_t];
mem_array_t m_mem;
function new(int width);
m_width = width;
endfunction // new
virtual task automatic read_mem(uint32_t addr, int d_size, int count, output uint64_t data[$]);
int i;
int word_size = m_width / 8;
data = '{};
for (i = 0; i < count; i++) begin
uint32_t a = (addr + i * d_size) / word_size * word_size;
uint32_t shift = (word_size - d_size - ((addr + i * d_size) % word_size)) * 8;
uint32_t mask = ((1 << (8 * d_size)) - 1);
// $display("RD %x addr %x shift %x mask %x res %04x", m_mem[a], a, shift, mask, (m_mem[a] >> shift) & mask );
data.push_back((m_mem[a] >> shift) & mask);
end
endtask // read_mem
pure virtual task automatic reset();
pure virtual task automatic run();
endclass // CMonitorableMemory
`include "logger.svh"
static CSimUtils SimUtils;
endpackage
This diff is collapsed.
`include "gencores_sim_defs.svh"
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 u8_vector_t getBytes(int count);
automatic u8_vector_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
This diff is collapsed.
......@@ -22,7 +22,7 @@
// and limitations under the License.
//------------------------------------------------------------------------------
`include "simdrv_defs.svh"
`include "gencores_sim_defs.svh"
`include "if_wishbone_types.svh"
`include "if_wishbone_accessor.svh"
......
......@@ -7,10 +7,10 @@
// Standard : Verilog 2001
//
`ifndef __IF_WB_DEFS_SV
`define __IF_WB_DEFS_SV
`ifndef __IF_WB_DEFS_SVH
`define __IF_WB_DEFS_SVH
`include "simdrv_defs.sv"
`include "gencores_sim_defs.svh"
typedef enum
{
......
......@@ -10,7 +10,7 @@
`ifndef __IF_WB_TYPES_SVH
`define __IF_WB_TYPES_SVH
`include "simdrv_defs.svh"
`include "gencores_sim_defs.svh"
typedef enum
{
......
`ifndef __VHD_WISHBONE_MASTER_INCLUDED
`define __VHD_WISHBONE_MASTER_INCLUDED
`include "simdrv_defs.svh"
`include "gencores_sim_defs.svh"
`include "if_wb_master.svh"
import wishbone_pkg::*;
......
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