blob: cb901d1050c3a4796acaca65c3f6d62de5bae300 [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the 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.
#include "tests/verilator_sim/sysc_tb.h"
#ifndef L1DCACHEBANK
#include "VL1DCache.h"
constexpr int kDBusBankAdj = 0;
#else
constexpr int kDBusBankAdj = 1;
#endif
#include "tests/verilator_sim/kelvin/kelvin_cfg.h"
constexpr int kLineSize = kVector / 8;
constexpr int kLineBase = ~(kLineSize - 1);
constexpr int kLineOffset = kLineSize - 1;
constexpr int kVLenB = kVector / 8;
constexpr int kVLenW = kVLenB / sizeof(int32_t);
struct L1DCache_tb : Sysc_tb {
sc_out<bool> io_flush_valid;
sc_in<bool> io_flush_ready;
sc_out<bool> io_flush_all;
sc_out<bool> io_flush_clean;
sc_out<bool> io_dbus_valid;
sc_in<bool> io_dbus_ready;
sc_out<bool> io_dbus_write;
sc_out<sc_bv<kDbusBits> > io_dbus_size;
sc_out<sc_bv<32 - kDBusBankAdj> > io_dbus_addr;
sc_out<sc_bv<32 - kDBusBankAdj> > io_dbus_adrx;
sc_in<sc_bv<kVector> > io_dbus_rdata;
sc_out<sc_bv<kVector> > io_dbus_wdata;
sc_out<sc_bv<kVector / 8> > io_dbus_wmask;
sc_in<bool> io_axi_read_addr_valid;
sc_out<bool> io_axi_read_addr_ready;
sc_in<sc_bv<kL1DAxiId - kDBusBankAdj> > io_axi_read_addr_bits_id;
sc_in<sc_bv<32 - kDBusBankAdj> > io_axi_read_addr_bits_addr;
sc_out<bool> io_axi_read_data_valid;
sc_in<bool> io_axi_read_data_ready;
sc_out<sc_bv<2> > io_axi_read_data_bits_resp;
sc_out<sc_bv<kL1DAxiId - kDBusBankAdj> > io_axi_read_data_bits_id;
sc_out<sc_bv<kL1DAxiBits> > io_axi_read_data_bits_data;
sc_in<bool> io_axi_write_addr_valid;
sc_out<bool> io_axi_write_addr_ready;
sc_in<sc_bv<kL1DAxiId - kDBusBankAdj> > io_axi_write_addr_bits_id;
sc_in<sc_bv<32 - kDBusBankAdj> > io_axi_write_addr_bits_addr;
sc_in<bool> io_axi_write_data_valid;
sc_out<bool> io_axi_write_data_ready;
sc_in<sc_bv<kL1DAxiStrb> > io_axi_write_data_bits_strb;
sc_in<sc_bv<kL1DAxiBits> > io_axi_write_data_bits_data;
sc_out<bool> io_axi_write_resp_valid;
sc_in<bool> io_axi_write_resp_ready;
sc_out<sc_bv<2> > io_axi_write_resp_bits_resp;
sc_out<sc_bv<kL1DAxiId - kDBusBankAdj> > io_axi_write_resp_bits_id;
sc_in<bool> io_volt_sel;
using Sysc_tb::Sysc_tb;
void posedge() {
// dbus
#ifdef L1DCACHEBANK
// Checks a bank cache line.
if (dbus_resp_pipeline_) {
dbus_resp_pipeline_ = false;
uint32_t addr = dbus_resp_addr_;
uint32_t size = dbus_resp_size_;
for (uint32_t i = 0; i < kVLenB && size; ++i) {
uint8_t ref = dbus_resp_data_[i];
uint8_t dut = io_dbus_rdata.read().get_word(i / 4) >> (8 * i);
if (ref != dut) {
printf("DDD(%u) %08x : %02x %02x\n", i, (addr & ~(kVLenB - 1)) + i,
ref, dut);
}
check(ref == dut, "dbus read data");
}
}
#else
if (dbus_resp_pipeline_) {
dbus_resp_pipeline_ = false;
uint32_t addr = dbus_resp_addr_;
uint32_t size = dbus_resp_size_;
for (uint32_t j = addr; j < addr + size; ++j) {
uint32_t i = j & (kVLenB - 1);
uint8_t ref = dbus_resp_data_[i];
uint8_t dut = io_dbus_rdata.read().get_word(i / 4) >> (8 * i);
check(ref == dut, "dbus read data");
}
}
#endif
if (io_dbus_valid && io_dbus_ready && !io_dbus_write) {
dbus_active_ = false;
dbus_resp_pipeline_ = true;
dbus_resp_addr_ = io_dbus_addr.read().get_word(0);
dbus_resp_size_ = io_dbus_size.read().get_word(0);
#ifdef L1DCACHEBANK
ReadBus(dbus_resp_addr_ & kLineBase, kVLenB, dbus_resp_data_);
#else
ReadBus(dbus_resp_addr_, kVLenB, dbus_resp_data_);
#endif
history_t cmd({dbus_resp_addr_});
history_.write(cmd);
if (history_.count() > 16) {
history_.remove();
}
}
if (io_dbus_valid && io_dbus_ready && io_dbus_write) {
dbus_active_ = false;
uint32_t addr = io_dbus_addr.read().get_word(0);
int size = io_dbus_size.read().get_word(0);
uint8_t wdata[kVLenB];
uint32_t* p_wdata = reinterpret_cast<uint32_t*>(wdata);
for (int i = 0; i < kVLenW; ++i) {
p_wdata[i] = io_dbus_wdata.read().get_word(i);
}
const uint32_t linemask = kVLenB - 1;
for (int i = 0; i < size; ++i, ++addr) {
const uint32_t lineoffset = addr & linemask;
if (io_dbus_wmask.read().get_bit(lineoffset)) {
#ifdef L1DCACHEBANK
const uint32_t linebase = addr & ~linemask;
WriteBus(linebase + lineoffset, wdata[lineoffset]);
#else
WriteBus(addr, wdata[lineoffset]);
#endif
}
}
}
if (io_flush_valid && io_flush_ready) {
flush_valid_ = false;
flush_all_ = false;
flush_clean_ = false;
}
if (++flush_count_ > 5000 && !dbus_active_ && !flush_valid_) {
// Flush controls must not change during handshake.
flush_count_ = 0;
flush_valid_ = true;
flush_all_ = rand_bool();
flush_clean_ = rand_bool();
}
io_flush_valid = flush_valid_;
io_flush_all = flush_all_;
io_flush_clean = flush_clean_;
history_t dbus;
if (!io_dbus_valid || !dbus_active_) { // latch transaction
bool valid = rand_bool() && !flush_valid_;
bool write = rand_int(0, 3) == 0;
bool newaddr = rand_int(0, 3) == 0 || !history_.rand(dbus);
uint32_t addr =
newaddr ? rand_uint32() : (dbus.addr + rand_int(-kVLenB, kVLenB));
// TODO(b/295973540): avoids a raxi() crash.
addr = std::min(0xffffff00u, addr);
if (kDBusBankAdj) {
addr &= 0x7fffffff;
}
if (rand_int(0, 7) == 0) {
addr &= 0x3fff;
}
#ifdef L1DCACHEBANK
int size = rand_int(1, kVLenB);
#else
int size = rand_int(0, kVLenB);
#endif
io_dbus_valid = valid;
io_dbus_write = write;
io_dbus_addr = addr;
io_dbus_adrx = addr + kVLenB;
io_dbus_size = size;
if (valid) {
dbus_active_ = true;
CheckAddr(addr, size);
}
sc_bv<kVector> wdata = 0;
sc_bv<kVector / 8> wmask = 0;
if (write) {
for (int i = 0; i < kVLenW; ++i) {
wdata.set_word(i, rand_uint32());
}
const uint32_t linemask = kVLenB - 1;
const uint32_t lineoffset = addr & linemask;
const bool all = rand_bool();
for (int i = 0; i < size; ++i) {
if (all || rand_bool()) {
wmask.set_bit((i + lineoffset) & linemask, sc_dt::Log_1);
}
}
}
io_dbus_wdata.write(wdata);
io_dbus_wmask.write(wmask);
}
timeout_ = io_dbus_ready ? 0 : timeout_ + io_dbus_valid;
check(timeout_ < 10000, "dbus timeout");
// axi_read_addr
io_axi_read_addr_ready = rand_bool();
if (io_axi_read_addr_valid && io_axi_read_addr_ready) {
uint32_t id = io_axi_read_addr_bits_id.read().get_word(0);
uint32_t addr = io_axi_read_addr_bits_addr.read().get_word(0);
response_t resp({id, addr});
resp_.write(resp);
}
// axi_read_data
io_axi_read_data_valid = false;
io_axi_read_data_bits_id = 0;
io_axi_read_data_bits_data = 0;
if (io_axi_read_data_valid && io_axi_read_data_ready) {
check(resp_.remove(), "no response to erase");
}
response_t resp;
resp_.shuffle();
if (resp_.next(resp)) {
io_axi_read_data_valid = rand_bool();
io_axi_read_data_bits_id = resp.id;
uint32_t addr = resp.addr;
sc_bv<kL1DAxiBits> out;
for (int i = 0; i < axiw_; ++i) {
uint32_t data;
ReadAxi(addr, 4, reinterpret_cast<uint8_t*>(&data));
out.set_word(i, data);
addr += 4;
}
io_axi_read_data_bits_data = out;
}
// axi_write_addr
bool writedataready = rand_bool();
io_axi_write_addr_ready = writedataready;
if (io_axi_write_addr_valid && io_axi_write_addr_ready) {
axiwaddr_t p;
p.id = io_axi_write_addr_bits_id.read().get_word(0);
p.addr = io_axi_write_addr_bits_addr.read().get_word(0);
waddr_.write(p);
}
// axi_write_data
io_axi_write_data_ready = writedataready;
if (io_axi_write_data_valid && io_axi_write_data_ready) {
axiwdata_t p;
uint32_t* ptr = reinterpret_cast<uint32_t*>(p.data);
for (int i = 0; i < axiw_; ++i, ++ptr) {
ptr[0] = io_axi_write_data_bits_data.read().get_word(i);
}
for (int i = 0; i < axib_; ++i) {
p.mask[i] = io_axi_write_data_bits_strb.read().get_bit(i);
}
wdata_.write(p);
}
// axi_write_resp
if (io_axi_write_resp_valid && io_axi_write_resp_ready) {
wresp_.remove();
}
axiwaddr_t wr;
io_axi_write_resp_valid = rand_int(0, 4) == 0 && wresp_.next(wr);
io_axi_write_resp_bits_id = wr.id;
// Process axi data write, and populate response.
axiwaddr_t wa;
axiwdata_t wd;
if (waddr_.next(wa) && wdata_.next(wd)) {
waddr_.remove();
wdata_.remove();
wresp_.write(wa);
uint32_t addr = wa.addr;
for (int i = 0; i < axib_; ++i, ++addr) {
if (wd.mask[i]) {
WriteAxi(addr, wd.data[i]);
}
}
}
}
private:
struct history_t {
uint32_t addr;
};
struct response_t {
uint32_t id;
uint32_t addr;
};
struct axiwaddr_t {
uint32_t id;
uint32_t addr;
};
struct axiwdata_t {
uint8_t data[kL1DAxiBits / 8];
bool mask[kL1DAxiBits / 8];
};
const int axib_ = kL1DAxiBits / 8;
const int axiw_ = kL1DAxiBits / 32;
int timeout_ = 0;
int flush_count_ = 0;
bool flush_valid_ = false;
bool flush_all_ = false;
bool flush_clean_ = false;
bool dbus_active_ = false;
bool dbus_resp_pipeline_ = false;
uint32_t dbus_resp_addr_ = 0;
uint32_t dbus_resp_size_ = 0;
uint8_t dbus_resp_data_[kVector / 8];
fifo_t<response_t> resp_;
fifo_t<history_t> history_;
fifo_t<axiwaddr_t> waddr_;
fifo_t<axiwdata_t> wdata_;
fifo_t<axiwaddr_t> wresp_;
std::map<uint32_t, uint8_t[kLineSize]> mem_bus_;
std::map<uint32_t, uint8_t[kLineSize]> mem_axi_;
void _CheckAddr(uint32_t addr, uint8_t size) {
const uint32_t paddr = addr & kLineBase;
if (mem_bus_.find(paddr) == mem_bus_.end()) {
uint8_t data[kLineSize];
uint32_t* p_data = reinterpret_cast<uint32_t*>(data);
for (int i = 0; i < kLineSize / 4; ++i) {
p_data[i] = rand(); // NOLINT(runtime/threadsafe_fn)
}
memcpy(mem_bus_[paddr], data, kLineSize);
memcpy(mem_axi_[paddr], data, kLineSize);
}
}
void CheckAddr(uint32_t addr, uint8_t size) {
_CheckAddr(addr, size);
_CheckAddr(addr + kLineSize, size);
}
template <int outsz>
void _Read(uint32_t addr, uint8_t size, uint8_t* data,
std::map<uint32_t, uint8_t[kLineSize]>& m) {
const uint32_t laddr = addr & kLineBase;
const uint32_t loffset = addr & kLineOffset;
const uint32_t doffset = addr & (outsz - 1);
uint32_t start = addr;
uint32_t end = std::min(addr + size, laddr + kLineSize);
int size0 = end - start;
int size1 = size - size0;
memset(data, 0xCC, outsz);
#ifdef L1DCACHEBANK
assert(doffset == 0);
memcpy(data + doffset, m[laddr] + loffset, outsz);
#else
memcpy(data + doffset, m[laddr] + loffset, size0);
if (!size1) return;
memcpy(data, m[laddr + kLineSize], size1);
#endif
}
void _Write(uint32_t addr, uint8_t data,
std::map<uint32_t, uint8_t[kLineSize]>& m) {
const uint32_t laddr = addr & kLineBase;
const uint32_t loffset = addr & kLineOffset;
m[laddr][loffset] = data;
}
void ReadBus(uint32_t addr, uint8_t size, uint8_t* data) {
_Read<kVector / 8>(addr, size, data, mem_bus_);
}
void ReadAxi(uint32_t addr, uint8_t size, uint8_t* data) {
_Read<4>(addr, size, data, mem_axi_);
}
void WriteBus(uint32_t addr, uint8_t data) { _Write(addr, data, mem_bus_); }
void WriteAxi(uint32_t addr, uint8_t data) { _Write(addr, data, mem_axi_); }
};
static void L1DCache_test(char* name, int loops, bool trace) {
sc_signal<bool> clock;
sc_signal<bool> reset;
sc_signal<bool> io_flush_valid;
sc_signal<bool> io_flush_ready;
sc_signal<bool> io_flush_all;
sc_signal<bool> io_flush_clean;
sc_signal<bool> io_dbus_valid;
sc_signal<bool> io_dbus_ready;
sc_signal<bool> io_dbus_write;
sc_signal<sc_bv<kDbusBits> > io_dbus_size;
sc_signal<sc_bv<32 - kDBusBankAdj> > io_dbus_addr;
sc_signal<sc_bv<32 - kDBusBankAdj> > io_dbus_adrx;
sc_signal<sc_bv<kVector> > io_dbus_rdata;
sc_signal<sc_bv<kVector> > io_dbus_wdata;
sc_signal<sc_bv<kVector / 8> > io_dbus_wmask;
sc_signal<bool> io_axi_read_addr_valid;
sc_signal<bool> io_axi_read_addr_ready;
sc_signal<sc_bv<kL1DAxiId - kDBusBankAdj> > io_axi_read_addr_bits_id;
sc_signal<sc_bv<32 - kDBusBankAdj> > io_axi_read_addr_bits_addr;
sc_signal<bool> io_axi_read_data_valid;
sc_signal<bool> io_axi_read_data_ready;
sc_signal<sc_bv<2> > io_axi_read_data_bits_resp;
sc_signal<sc_bv<kL1DAxiId - kDBusBankAdj> > io_axi_read_data_bits_id;
sc_signal<sc_bv<kL1DAxiBits> > io_axi_read_data_bits_data;
sc_signal<bool> io_axi_write_addr_valid;
sc_signal<bool> io_axi_write_addr_ready;
sc_signal<sc_bv<kL1DAxiId - kDBusBankAdj> > io_axi_write_addr_bits_id;
sc_signal<sc_bv<32 - kDBusBankAdj> > io_axi_write_addr_bits_addr;
sc_signal<bool> io_axi_write_data_valid;
sc_signal<bool> io_axi_write_data_ready;
sc_signal<sc_bv<kL1DAxiStrb> > io_axi_write_data_bits_strb;
sc_signal<sc_bv<kL1DAxiBits> > io_axi_write_data_bits_data;
sc_signal<bool> io_axi_write_resp_valid;
sc_signal<bool> io_axi_write_resp_ready;
sc_signal<sc_bv<2> > io_axi_write_resp_bits_resp;
sc_signal<sc_bv<kL1DAxiId - kDBusBankAdj> > io_axi_write_resp_bits_id;
sc_signal<bool> io_volt_sel;
L1DCache_tb tb("L1DCache_tb", loops, true /*random*/);
#ifdef L1DCACHEBANK
VL1DCacheBank l1dcache(name);
#else
VL1DCache l1dcache(name);
#endif
if (trace) {
tb.trace(l1dcache);
}
l1dcache.clock(tb.clock);
l1dcache.reset(tb.reset);
BIND2(tb, l1dcache, io_flush_valid);
BIND2(tb, l1dcache, io_flush_ready);
BIND2(tb, l1dcache, io_flush_all);
BIND2(tb, l1dcache, io_flush_clean);
BIND2(tb, l1dcache, io_dbus_valid);
BIND2(tb, l1dcache, io_dbus_ready);
BIND2(tb, l1dcache, io_dbus_write);
BIND2(tb, l1dcache, io_dbus_size);
BIND2(tb, l1dcache, io_dbus_addr);
BIND2(tb, l1dcache, io_dbus_adrx);
BIND2(tb, l1dcache, io_dbus_rdata);
BIND2(tb, l1dcache, io_dbus_wdata);
BIND2(tb, l1dcache, io_dbus_wmask);
BIND2(tb, l1dcache, io_axi_read_addr_valid);
BIND2(tb, l1dcache, io_axi_read_addr_ready);
BIND2(tb, l1dcache, io_axi_read_addr_bits_id);
BIND2(tb, l1dcache, io_axi_read_addr_bits_addr);
BIND2(tb, l1dcache, io_axi_read_data_valid);
BIND2(tb, l1dcache, io_axi_read_data_ready);
BIND2(tb, l1dcache, io_axi_read_data_bits_resp);
BIND2(tb, l1dcache, io_axi_read_data_bits_id);
BIND2(tb, l1dcache, io_axi_read_data_bits_data);
BIND2(tb, l1dcache, io_axi_write_addr_valid);
BIND2(tb, l1dcache, io_axi_write_addr_ready);
BIND2(tb, l1dcache, io_axi_write_addr_bits_id);
BIND2(tb, l1dcache, io_axi_write_addr_bits_addr);
BIND2(tb, l1dcache, io_axi_write_data_valid);
BIND2(tb, l1dcache, io_axi_write_data_ready);
BIND2(tb, l1dcache, io_axi_write_data_bits_strb);
BIND2(tb, l1dcache, io_axi_write_data_bits_data);
BIND2(tb, l1dcache, io_axi_write_resp_valid);
BIND2(tb, l1dcache, io_axi_write_resp_ready);
BIND2(tb, l1dcache, io_axi_write_resp_bits_resp);
BIND2(tb, l1dcache, io_axi_write_resp_bits_id);
BIND2(tb, l1dcache, io_volt_sel);
tb.start();
}
int sc_main(int argc, char* argv[]) {
L1DCache_test(Sysc_tb::get_name(argv[0]), 1000000, false);
return 0;
}