mirror of
https://github.com/openhwgroup/cve2.git
synced 2025-04-22 04:57:25 -04:00
[DV] Add registers testbench
- Sample C++ unit testbench for system registers module - Only tests a few PMP registers at the moment
This commit is contained in:
parent
e5cf0c0fcf
commit
70b53068db
28 changed files with 1525 additions and 64 deletions
84
dv/cs_registers/Makefile
Normal file
84
dv/cs_registers/Makefile
Normal file
|
@ -0,0 +1,84 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
TOOL = verilator
|
||||
|
||||
SO_NAME = reg_dpi.so
|
||||
|
||||
BUILDDIR = build
|
||||
SRCEXT = cc
|
||||
OBJDIR = $(BUILDDIR)/obj
|
||||
BINDIR = $(BUILDDIR)/bin
|
||||
|
||||
SRCS := $(shell find . -name '*.$(SRCEXT)' ! -path './tb/*' ! -path './build/*')
|
||||
SRCDIRS := $(shell find . -name '*.$(SRCEXT)' ! -name 'tb*' ! -name 'build*' -exec dirname {} \; | uniq)
|
||||
OBJS := $(patsubst %.$(SRCEXT),$(OBJDIR)/%.o,$(SRCS))
|
||||
|
||||
DEBUG = -g
|
||||
INCLUDES = -I./env -I./rst_driver -I./reg_driver -I./model
|
||||
CFLAGS = -std=c++14 -m64 -fPIC -Wall -pedantic $(DEBUG) $(INCLUDES)
|
||||
LDFLAGS = -shared
|
||||
|
||||
CC = $(CXX)
|
||||
|
||||
# Reg TB specific parameters / defines
|
||||
PMP_ENABLE = 1
|
||||
PMP_NUM_REGIONS = 4
|
||||
PMP_GRANULARITY = 0
|
||||
|
||||
CFLAGS += -DPMP_ENABLE=$(PMP_ENABLE) -DPMP_NUM_REGIONS=$(PMP_NUM_REGIONS)
|
||||
CFLAGS += -DPMP_GRANULARITY=$(PMP_GRANULARITY)
|
||||
|
||||
# Add svdpi include
|
||||
TOOLDIR = $(subst bin/$(TOOL),,$(shell which $(TOOL)))
|
||||
ifeq ($(TOOL),vcs)
|
||||
INCLUDES += -I$(TOOLDIR)include
|
||||
else
|
||||
INCLUDES += -I$(TOOLDIR)share/verilator/include/vltstd
|
||||
endif
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: build
|
||||
|
||||
build: $(BINDIR)/$(SO_NAME)
|
||||
fusesoc --cores-root=../../ run --setup --build --target=sim --tool=$(TOOL) \
|
||||
lowrisc:ibex:tb_cs_registers --PMPEnable \
|
||||
--PMPNumRegions=$(PMP_NUM_REGIONS) --PMPGranularity=$(PMP_GRANULARITY)
|
||||
|
||||
$(BINDIR)/$(SO_NAME): buildrepo $(OBJS)
|
||||
@mkdir -p `dirname $@`
|
||||
@echo "Linking $@..."
|
||||
@$(CC) $(OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
$(OBJDIR)/%.o: %.$(SRCEXT)
|
||||
@echo "Generating dependencies for $<..."
|
||||
@$(call make-depend,$<,$@,$(subst .o,.d,$@))
|
||||
@echo "Compiling $<..."
|
||||
@$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
$(RM) -r $(BUILDDIR)
|
||||
|
||||
buildrepo:
|
||||
@$(call make-repo)
|
||||
|
||||
define make-repo
|
||||
for dir in $(SRCDIRS); \
|
||||
do \
|
||||
mkdir -p $(OBJDIR)/$$dir; \
|
||||
done
|
||||
endef
|
||||
|
||||
|
||||
# usage: $(call make-depend,source-file,object-file,depend-file)
|
||||
define make-depend
|
||||
$(CC) -MM \
|
||||
-MF $3 \
|
||||
-MP \
|
||||
-MT $2 \
|
||||
$(CFLAGS) \
|
||||
$1
|
||||
endef
|
||||
|
55
dv/cs_registers/README.md
Normal file
55
dv/cs_registers/README.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
Ibex simulation Control/Status Registers Testbench
|
||||
==================================================
|
||||
|
||||
This directory contains a basic sample testbench in C++ for testing correctness of some CS registers implemented in Ibex.
|
||||
|
||||
It is a work in progress and only tests a handful of registers, and is missing many features.
|
||||
|
||||
How to build and run the testbench
|
||||
----------------------------------
|
||||
|
||||
(from this directory)
|
||||
|
||||
Verilator version:
|
||||
|
||||
```sh
|
||||
make TOOL=verilator
|
||||
./build/lowrisc_ibex_tb_cs_registers_0/sim-verilator/Vtb_cs_registers
|
||||
```
|
||||
VCS version:
|
||||
|
||||
```sh
|
||||
make TOOL=vcs
|
||||
./build/lowrisc_ibex_tb_cs_registers_0/sim-vcs/lowrisc_ibex_tb_cs_registers_0
|
||||
```
|
||||
|
||||
Testbench file structure
|
||||
------------------------
|
||||
|
||||
`tb/tb_cs_registers.sv` - Is the verilog top level, it instantiates the DUT and DPI calls
|
||||
|
||||
`tb/tb_cs_registers.cc` - Is the C++ top level, it sets up the testbench components
|
||||
|
||||
`driver/` - Contains components to generate and drive random register transactions
|
||||
|
||||
`env/` - Contains components to generate and drive other signals
|
||||
|
||||
`model/` - Contains a model of the register state and checking functions
|
||||
|
||||
DPI methodology
|
||||
---------------
|
||||
|
||||
The testbench relies on DPI calls to interface between the RTL and the C++ driver/checker components.
|
||||
This methodology allows the testbench to support both Verilator and commercial simulators.
|
||||
|
||||
Each DPI call has a function in SV (which is called in the SV top-level), and a corresponding C function.
|
||||
To support the instantiation of multiple instances of TB components, some DPI modules can register their objects referenced by name.
|
||||
Each DPI call from the RTL then calls into the correct instance by matching the name.
|
||||
|
||||
Testbench structure
|
||||
-------------------
|
||||
|
||||
The driver component contains one DPI "interface" to drive new random transactions into the DUT, and another to monitor issued transactions including the DUT's responses.
|
||||
Each time a new transaction is detected by the monitor component, it is captured and sent to the model.
|
||||
The model reads incoming transactions, updates its internal state and checks that the DUT outputs matches its own predictions.
|
||||
|
35
dv/cs_registers/env/env_dpi.cc
vendored
Normal file
35
dv/cs_registers/env/env_dpi.cc
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "register_environment.h"
|
||||
|
||||
#include "svdpi.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
RegisterEnvironment *reg_env;
|
||||
|
||||
void env_initial(svBitVecVal *seed) {
|
||||
// Create TB environment
|
||||
reg_env = new RegisterEnvironment();
|
||||
|
||||
// Initial setup
|
||||
reg_env->OnInitial(*seed);
|
||||
}
|
||||
|
||||
void env_final() {
|
||||
reg_env->OnFinal();
|
||||
delete reg_env;
|
||||
}
|
||||
|
||||
void env_tick(svBit *stop_req) { reg_env->GetStopReq(stop_req); }
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
19
dv/cs_registers/env/env_dpi.sv
vendored
Normal file
19
dv/cs_registers/env/env_dpi.sv
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package env_dpi;
|
||||
|
||||
import "DPI-C"
|
||||
function void env_initial(input bit [31:0] seed);
|
||||
|
||||
import "DPI-C"
|
||||
function void env_final();
|
||||
|
||||
import "DPI-C"
|
||||
function void env_tick(
|
||||
output bit stop_req);
|
||||
|
||||
endpackage
|
||||
|
||||
|
30
dv/cs_registers/env/register_environment.cc
vendored
Normal file
30
dv/cs_registers/env/register_environment.cc
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "register_environment.h"
|
||||
|
||||
RegisterEnvironment::RegisterEnvironment()
|
||||
: simctrl_(new SimCtrl()),
|
||||
reg_model_(new RegisterModel(simctrl_)),
|
||||
reg_driver_(new RegisterDriver("reg_driver", reg_model_, simctrl_)),
|
||||
rst_driver_(new ResetDriver("rstn_driver")) {}
|
||||
|
||||
void RegisterEnvironment::OnInitial(unsigned int seed) {
|
||||
rst_driver_->OnInitial(seed);
|
||||
reg_driver_->OnInitial(seed);
|
||||
}
|
||||
|
||||
void RegisterEnvironment::OnFinal() {
|
||||
reg_driver_->OnFinal();
|
||||
rst_driver_->OnFinal();
|
||||
simctrl_->OnFinal();
|
||||
delete rst_driver_;
|
||||
delete reg_driver_;
|
||||
delete reg_model_;
|
||||
delete simctrl_;
|
||||
}
|
||||
|
||||
void RegisterEnvironment::GetStopReq(unsigned char *stop_req) {
|
||||
*stop_req = simctrl_->StopRequested();
|
||||
}
|
32
dv/cs_registers/env/register_environment.h
vendored
Normal file
32
dv/cs_registers/env/register_environment.h
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef REGISTER_ENVIRONMENT_H_
|
||||
#define REGISTER_ENVIRONMENT_H_
|
||||
|
||||
#include "register_driver.h"
|
||||
#include "register_model.h"
|
||||
#include "reset_driver.h"
|
||||
#include "simctrl.h"
|
||||
|
||||
/**
|
||||
* Class to instantiate all tb components
|
||||
*/
|
||||
class RegisterEnvironment {
|
||||
public:
|
||||
RegisterEnvironment();
|
||||
|
||||
void OnInitial(unsigned int seed);
|
||||
void OnFinal();
|
||||
|
||||
void GetStopReq(unsigned char *stop_req);
|
||||
|
||||
private:
|
||||
SimCtrl *simctrl_;
|
||||
RegisterModel *reg_model_;
|
||||
RegisterDriver *reg_driver_;
|
||||
ResetDriver *rst_driver_;
|
||||
};
|
||||
|
||||
#endif // REGISTER_ENVIRONMENT_H_
|
24
dv/cs_registers/env/simctrl.cc
vendored
Normal file
24
dv/cs_registers/env/simctrl.cc
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "simctrl.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
SimCtrl::SimCtrl() : stop_requested_(false), success_(true) {}
|
||||
|
||||
void SimCtrl::RequestStop(bool success) {
|
||||
stop_requested_ = true;
|
||||
success_ &= success;
|
||||
}
|
||||
|
||||
bool SimCtrl::StopRequested() { return stop_requested_; }
|
||||
|
||||
void SimCtrl::OnFinal() {
|
||||
std::cout << std::endl
|
||||
<< "//-------------//" << std::endl
|
||||
<< (success_ ? "// TEST PASSED //" : "// TEST FAILED //")
|
||||
<< std::endl
|
||||
<< "//-------------//" << std::endl;
|
||||
}
|
20
dv/cs_registers/env/simctrl.h
vendored
Normal file
20
dv/cs_registers/env/simctrl.h
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef SIMCTRL_H_
|
||||
#define SIMCTRL_H_
|
||||
|
||||
class SimCtrl {
|
||||
public:
|
||||
SimCtrl();
|
||||
void RequestStop(bool success);
|
||||
bool StopRequested();
|
||||
void OnFinal();
|
||||
|
||||
private:
|
||||
bool stop_requested_;
|
||||
bool success_;
|
||||
};
|
||||
|
||||
#endif
|
167
dv/cs_registers/model/base_register.cc
Normal file
167
dv/cs_registers/model/base_register.cc
Normal file
|
@ -0,0 +1,167 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "base_register.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
BaseRegister::BaseRegister(
|
||||
uint32_t addr, std::vector<std::unique_ptr<BaseRegister>> *map_pointer)
|
||||
: register_value_(0), register_address_(addr), map_pointer_(map_pointer) {}
|
||||
|
||||
uint32_t BaseRegister::RegisterWrite(uint32_t newval) {
|
||||
uint32_t lock_mask = GetLockMask();
|
||||
uint32_t read_value = register_value_;
|
||||
register_value_ &= lock_mask;
|
||||
register_value_ |= (newval & ~lock_mask);
|
||||
return read_value;
|
||||
}
|
||||
|
||||
uint32_t BaseRegister::RegisterSet(uint32_t newval) {
|
||||
uint32_t lock_mask = GetLockMask();
|
||||
uint32_t read_value = register_value_;
|
||||
register_value_ |= (newval & ~lock_mask);
|
||||
return read_value;
|
||||
}
|
||||
|
||||
uint32_t BaseRegister::RegisterClear(uint32_t newval) {
|
||||
uint32_t lock_mask = GetLockMask();
|
||||
uint32_t read_value = register_value_;
|
||||
register_value_ &= (~newval | lock_mask);
|
||||
return read_value;
|
||||
}
|
||||
|
||||
bool BaseRegister::MatchAddr(uint32_t addr) {
|
||||
return (addr == register_address_);
|
||||
}
|
||||
|
||||
bool BaseRegister::ProcessTransaction(bool *match, RegisterTransaction *trans) {
|
||||
uint32_t read_val;
|
||||
if (!MatchAddr(trans->csr_addr)) {
|
||||
return false;
|
||||
}
|
||||
*match = true;
|
||||
switch (trans->csr_op) {
|
||||
case kCSRRead:
|
||||
read_val = RegisterRead();
|
||||
break;
|
||||
case kCSRWrite:
|
||||
read_val = RegisterWrite(trans->csr_wdata);
|
||||
break;
|
||||
case kCSRSet:
|
||||
read_val = RegisterSet(trans->csr_wdata);
|
||||
break;
|
||||
case kCSRClear:
|
||||
read_val = RegisterClear(trans->csr_wdata);
|
||||
break;
|
||||
}
|
||||
if (read_val != trans->csr_rdata) {
|
||||
std::cout << "Error, transaction:" << std::endl;
|
||||
trans->Print();
|
||||
std::cout << "Expected rdata: " << std::hex << read_val << std::dec
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BaseRegister::RegisterReset() { register_value_ = 0; }
|
||||
|
||||
uint32_t BaseRegister::RegisterRead() { return register_value_; }
|
||||
|
||||
uint32_t BaseRegister::GetLockMask() { return 0; }
|
||||
|
||||
uint32_t PmpCfgRegister::GetLockMask() {
|
||||
uint32_t lock_mask = 0;
|
||||
if (register_value_ & 0x80)
|
||||
lock_mask |= 0xFF;
|
||||
if (register_value_ & 0x8000)
|
||||
lock_mask |= 0xFF00;
|
||||
if (register_value_ & 0x800000)
|
||||
lock_mask |= 0xFF0000;
|
||||
if (register_value_ & 0x80000000)
|
||||
lock_mask |= 0xFF000000;
|
||||
return lock_mask;
|
||||
}
|
||||
|
||||
uint32_t PmpCfgRegister::RegisterWrite(uint32_t newval) {
|
||||
uint32_t lock_mask = GetLockMask();
|
||||
uint32_t read_value = register_value_;
|
||||
register_value_ &= lock_mask;
|
||||
register_value_ |= (newval & ~lock_mask);
|
||||
register_value_ &= raz_mask_;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// Reserved check, W = 1, R = 0
|
||||
if (((register_value_ >> (8 * i)) & 0x3) == 0x2) {
|
||||
register_value_ &= ~(0x3 << (8 * i));
|
||||
}
|
||||
}
|
||||
return read_value;
|
||||
}
|
||||
|
||||
uint32_t PmpCfgRegister::RegisterSet(uint32_t newval) {
|
||||
uint32_t lock_mask = GetLockMask();
|
||||
uint32_t read_value = register_value_;
|
||||
register_value_ |= (newval & ~lock_mask);
|
||||
register_value_ &= raz_mask_;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// Reserved check, W = 1, R = 0
|
||||
if (((register_value_ >> (8 * i)) & 0x3) == 0x2) {
|
||||
register_value_ &= ~(0x3 << (8 * i));
|
||||
}
|
||||
}
|
||||
return read_value;
|
||||
}
|
||||
|
||||
uint32_t PmpCfgRegister::RegisterClear(uint32_t newval) {
|
||||
uint32_t lock_mask = GetLockMask();
|
||||
uint32_t read_value = register_value_;
|
||||
register_value_ &= (~newval | lock_mask);
|
||||
register_value_ &= raz_mask_;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// Reserved check, W = 1, R = 0
|
||||
if (((register_value_ >> (8 * i)) & 0x3) == 0x2) {
|
||||
register_value_ &= ~(0x3 << (8 * i));
|
||||
}
|
||||
}
|
||||
return read_value;
|
||||
}
|
||||
|
||||
uint32_t PmpAddrRegister::GetLockMask() {
|
||||
// Calculate which region this is
|
||||
uint32_t pmp_region = (register_address_ & 0xF);
|
||||
// Form the address of the corresponding CFG register
|
||||
uint32_t pmp_cfg_addr = 0x3A0 + (pmp_region / 4);
|
||||
// Form the address of the CFG registerfor the next region
|
||||
// For region 15, this will point to a non-existant register, which is fine
|
||||
uint32_t pmp_cfg_plus1_addr = 0x3A0 + ((pmp_region + 1) / 4);
|
||||
uint32_t cfg_value = 0;
|
||||
uint32_t cfg_plus1_value = 0;
|
||||
// Find and read the two CFG registers
|
||||
for (auto it = map_pointer_->begin(); it != map_pointer_->end(); ++it) {
|
||||
if ((*it)->MatchAddr(pmp_cfg_addr)) {
|
||||
cfg_value = (*it)->RegisterRead();
|
||||
}
|
||||
if ((*it)->MatchAddr(pmp_cfg_plus1_addr)) {
|
||||
cfg_plus1_value = (*it)->RegisterRead();
|
||||
}
|
||||
}
|
||||
// Shift to the relevant bits in the CFG registers
|
||||
cfg_value >>= ((pmp_region & 0x3) * 8);
|
||||
cfg_plus1_value >>= (((pmp_region + 1) & 0x3) * 8);
|
||||
// Locked if the lock bit is set, or the next region is TOR
|
||||
if ((cfg_value & 0x80) || ((cfg_plus1_value & 0x18) == 0x8)) {
|
||||
return 0xFFFFFFFF;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t NonImpRegister::RegisterRead() { return 0; }
|
||||
|
||||
uint32_t NonImpRegister::RegisterWrite(uint32_t newval) { return 0; }
|
||||
|
||||
uint32_t NonImpRegister::RegisterSet(uint32_t newval) { return 0; }
|
||||
|
||||
uint32_t NonImpRegister::RegisterClear(uint32_t newval) { return 0; }
|
77
dv/cs_registers/model/base_register.h
Normal file
77
dv/cs_registers/model/base_register.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef BASE_REGISTER_H_
|
||||
#define BASE_REGISTER_H_
|
||||
|
||||
#include "register_transaction.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Base register class, can be specialized to add advanced functionality
|
||||
* required by different types of register
|
||||
*/
|
||||
class BaseRegister {
|
||||
public:
|
||||
BaseRegister(uint32_t addr,
|
||||
std::vector<std::unique_ptr<BaseRegister>> *map_pointer);
|
||||
virtual ~BaseRegister() = default;
|
||||
virtual void RegisterReset();
|
||||
virtual uint32_t RegisterWrite(uint32_t newval);
|
||||
virtual uint32_t RegisterSet(uint32_t newval);
|
||||
virtual uint32_t RegisterClear(uint32_t newval);
|
||||
virtual uint32_t RegisterRead();
|
||||
virtual bool ProcessTransaction(bool *match, RegisterTransaction *trans);
|
||||
virtual bool MatchAddr(uint32_t addr);
|
||||
virtual uint32_t GetLockMask();
|
||||
|
||||
protected:
|
||||
uint32_t register_value_;
|
||||
uint32_t register_address_;
|
||||
std::vector<std::unique_ptr<BaseRegister>> *map_pointer_;
|
||||
};
|
||||
|
||||
/**
|
||||
* PMP configuration register class
|
||||
*/
|
||||
class PmpCfgRegister : public BaseRegister {
|
||||
using BaseRegister::BaseRegister;
|
||||
|
||||
public:
|
||||
uint32_t GetLockMask();
|
||||
uint32_t RegisterWrite(uint32_t newval);
|
||||
uint32_t RegisterSet(uint32_t newval);
|
||||
uint32_t RegisterClear(uint32_t newval);
|
||||
|
||||
private:
|
||||
const uint32_t raz_mask_ = 0x9F9F9F9F;
|
||||
};
|
||||
|
||||
/**
|
||||
* PMP address register class
|
||||
*/
|
||||
class PmpAddrRegister : public BaseRegister {
|
||||
using BaseRegister::BaseRegister;
|
||||
|
||||
public:
|
||||
uint32_t GetLockMask();
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic class to model non-implemented (read as zero) registers
|
||||
*/
|
||||
class NonImpRegister : public BaseRegister {
|
||||
using BaseRegister::BaseRegister;
|
||||
|
||||
public:
|
||||
uint32_t RegisterRead();
|
||||
uint32_t RegisterWrite(uint32_t newval);
|
||||
uint32_t RegisterSet(uint32_t newval);
|
||||
uint32_t RegisterClear(uint32_t newval);
|
||||
};
|
||||
|
||||
#endif // BASE_REGISTER_H_
|
56
dv/cs_registers/model/register_model.cc
Normal file
56
dv/cs_registers/model/register_model.cc
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "register_model.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
RegisterModel::RegisterModel(SimCtrl *sc) : simctrl_(sc) {
|
||||
// Instantiate all the registers
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint32_t reg_addr = 0x3A0 + i;
|
||||
if (PMP_ENABLE && (i < (PMP_NUM_REGIONS / 4))) {
|
||||
register_map_.push_back(
|
||||
std::make_unique<PmpCfgRegister>(reg_addr, ®ister_map_));
|
||||
} else {
|
||||
register_map_.push_back(
|
||||
std::make_unique<NonImpRegister>(reg_addr, ®ister_map_));
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
uint32_t reg_addr = 0x3B0 + i;
|
||||
if (PMP_ENABLE && (i < PMP_NUM_REGIONS)) {
|
||||
register_map_.push_back(
|
||||
std::make_unique<PmpAddrRegister>(reg_addr, ®ister_map_));
|
||||
} else {
|
||||
register_map_.push_back(
|
||||
std::make_unique<NonImpRegister>(reg_addr, ®ister_map_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterModel::RegisterReset() {
|
||||
for (auto it = register_map_.begin(); it != register_map_.end(); ++it) {
|
||||
(*it)->RegisterReset();
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterModel::NewTransaction(std::unique_ptr<RegisterTransaction> trans) {
|
||||
// TODO add machine mode permissions to registers
|
||||
bool matched = false;
|
||||
for (auto it = register_map_.begin(); it != register_map_.end(); ++it) {
|
||||
if ((*it)->ProcessTransaction(&matched, trans.get())) {
|
||||
simctrl_->RequestStop(false);
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
// Non existant register
|
||||
if (!trans->illegal_csr) {
|
||||
std::cout << "Non-existant register:" << std::endl;
|
||||
trans->Print();
|
||||
std::cout << "Should have signalled an error." << std::endl;
|
||||
simctrl_->RequestStop(false);
|
||||
}
|
||||
}
|
||||
}
|
31
dv/cs_registers/model/register_model.h
Normal file
31
dv/cs_registers/model/register_model.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef REGISTER_MODEL_H_
|
||||
#define REGISTER_MODEL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base_register.h"
|
||||
#include "register_transaction.h"
|
||||
#include "simctrl.h"
|
||||
|
||||
/**
|
||||
* Class modelling CS register state and checking against RTL
|
||||
*/
|
||||
class RegisterModel {
|
||||
public:
|
||||
RegisterModel(SimCtrl *sc);
|
||||
|
||||
void NewTransaction(std::unique_ptr<RegisterTransaction> trans);
|
||||
void RegisterReset();
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<BaseRegister>> register_map_;
|
||||
SimCtrl *simctrl_;
|
||||
};
|
||||
|
||||
#endif // REGISTER_MODEL_H_
|
50
dv/cs_registers/reg_driver/reg_dpi.cc
Normal file
50
dv/cs_registers/reg_driver/reg_dpi.cc
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "register_driver.h"
|
||||
#include "svdpi.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static std::map<std::string, RegisterDriver *> intfs;
|
||||
|
||||
void reg_register_intf(std::string name, RegisterDriver *intf) {
|
||||
intfs.insert({name, intf});
|
||||
}
|
||||
|
||||
void reg_deregister_intf(std::string name) { intfs.erase(name); }
|
||||
|
||||
void monitor_tick(const char *name, svBit rst_n, svBit illegal_csr,
|
||||
svBit csr_access, const svBitVecVal *csr_op,
|
||||
const svBitVecVal *csr_addr, const svBitVecVal *csr_wdata,
|
||||
const svBitVecVal *csr_rdata) {
|
||||
auto ptr = intfs.find(name);
|
||||
if (ptr != intfs.end()) {
|
||||
// Send inputs to monitor
|
||||
if (csr_access || !rst_n) {
|
||||
ptr->second->CaptureTransaction(rst_n, illegal_csr, *csr_op, *csr_addr,
|
||||
*csr_rdata, *csr_wdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void driver_tick(const char *name, svBit *csr_access, svBitVecVal *csr_op,
|
||||
svBitVecVal *csr_addr, svBitVecVal *csr_wdata) {
|
||||
auto ptr = intfs.find(name);
|
||||
if (ptr != intfs.end()) {
|
||||
// Call OnClock method
|
||||
ptr->second->OnClock();
|
||||
// Drive outputs
|
||||
ptr->second->DriveOutputs(csr_access, csr_op, csr_addr, csr_wdata);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
27
dv/cs_registers/reg_driver/reg_dpi.sv
Normal file
27
dv/cs_registers/reg_driver/reg_dpi.sv
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package reg_dpi;
|
||||
|
||||
import "DPI-C"
|
||||
function void monitor_tick (
|
||||
input string name,
|
||||
input bit rst_n,
|
||||
input bit illegal_csr,
|
||||
input bit csr_access,
|
||||
input bit [1:0] csr_op,
|
||||
input bit [11:0] csr_addr,
|
||||
input bit [31:0] csr_wdata,
|
||||
input bit [31:0] csr_rdata);
|
||||
|
||||
import "DPI-C"
|
||||
function void driver_tick (
|
||||
input string name,
|
||||
output bit csr_access,
|
||||
output bit [1:0] csr_op,
|
||||
output bit [11:0] csr_addr,
|
||||
output bit [31:0] csr_wdata);
|
||||
|
||||
endpackage
|
||||
|
76
dv/cs_registers/reg_driver/register_driver.cc
Normal file
76
dv/cs_registers/reg_driver/register_driver.cc
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "register_driver.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
extern "C" void reg_register_intf(std::string name, RegisterDriver *intf);
|
||||
extern "C" void reg_deregister_intf(std::string name);
|
||||
|
||||
RegisterDriver::RegisterDriver(std::string name, RegisterModel *model,
|
||||
SimCtrl *sc)
|
||||
: name_(name), reg_model_(model), simctrl_(sc) {}
|
||||
|
||||
void RegisterDriver::OnInitial(unsigned int seed) {
|
||||
transactions_driven_ = 0;
|
||||
delay_ = 1;
|
||||
reg_access_ = false;
|
||||
generator_.seed(seed);
|
||||
delay_dist_ = std::uniform_int_distribution<int>(1, 20);
|
||||
reg_register_intf(name_, this);
|
||||
}
|
||||
|
||||
void RegisterDriver::OnFinal() {
|
||||
reg_deregister_intf(name_);
|
||||
std::cout << "[Reg driver] drove: " << transactions_driven_
|
||||
<< " register transactions" << std::endl;
|
||||
}
|
||||
|
||||
void RegisterDriver::Randomize() {
|
||||
// generate new transaction
|
||||
next_transaction_.Randomize(generator_);
|
||||
// generate new delay
|
||||
delay_ = delay_dist_(generator_);
|
||||
|
||||
reg_access_ = true;
|
||||
}
|
||||
|
||||
void RegisterDriver::CaptureTransaction(unsigned char rst_n,
|
||||
unsigned char illegal_csr, uint32_t op,
|
||||
uint32_t addr, uint32_t rdata,
|
||||
uint32_t wdata) {
|
||||
if (!rst_n) {
|
||||
reg_model_->RegisterReset();
|
||||
} else {
|
||||
auto trans = std::make_unique<RegisterTransaction>();
|
||||
trans->illegal_csr = illegal_csr;
|
||||
trans->csr_op = (CSRegisterOperation)op;
|
||||
trans->csr_addr = addr;
|
||||
trans->csr_rdata = rdata;
|
||||
trans->csr_wdata = wdata;
|
||||
// Ownership of trans is passed to the model
|
||||
reg_model_->NewTransaction(std::move(trans));
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterDriver::DriveOutputs(unsigned char *access, uint32_t *op,
|
||||
uint32_t *addr, uint32_t *wdata) {
|
||||
*access = reg_access_;
|
||||
*op = next_transaction_.csr_op;
|
||||
*addr = next_transaction_.csr_addr;
|
||||
*wdata = next_transaction_.csr_wdata;
|
||||
}
|
||||
|
||||
void RegisterDriver::OnClock() {
|
||||
if (transactions_driven_ >= 10000) {
|
||||
simctrl_->RequestStop(true);
|
||||
}
|
||||
if (--delay_ == 0) {
|
||||
Randomize();
|
||||
++transactions_driven_;
|
||||
} else {
|
||||
reg_access_ = false;
|
||||
}
|
||||
}
|
50
dv/cs_registers/reg_driver/register_driver.h
Normal file
50
dv/cs_registers/reg_driver/register_driver.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef REGISTER_DRIVER_H_
|
||||
#define REGISTER_DRIVER_H_
|
||||
|
||||
#include "register_model.h"
|
||||
#include "register_transaction.h"
|
||||
#include "simctrl.h"
|
||||
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Class to randomize and drive CS register reads/writes
|
||||
*/
|
||||
class RegisterDriver {
|
||||
public:
|
||||
RegisterDriver(std::string name, RegisterModel *model, SimCtrl *sc);
|
||||
|
||||
void OnInitial(unsigned int seed);
|
||||
void OnClock();
|
||||
void OnFinal();
|
||||
|
||||
void CaptureTransaction(unsigned char rst_n, unsigned char illegal_csr,
|
||||
uint32_t op, uint32_t addr, uint32_t rdata,
|
||||
uint32_t wdata);
|
||||
void DriveOutputs(unsigned char *access, uint32_t *op, uint32_t *addr,
|
||||
uint32_t *wdata);
|
||||
|
||||
private:
|
||||
void Randomize();
|
||||
|
||||
std::default_random_engine generator_;
|
||||
int delay_;
|
||||
bool reg_access_;
|
||||
CSRegisterOperation reg_op_;
|
||||
std::uniform_int_distribution<int> delay_dist_;
|
||||
uint32_t reg_addr_;
|
||||
uint32_t reg_wdata_;
|
||||
int transactions_driven_;
|
||||
RegisterTransaction next_transaction_;
|
||||
|
||||
std::string name_;
|
||||
RegisterModel *reg_model_;
|
||||
SimCtrl *simctrl_;
|
||||
};
|
||||
|
||||
#endif // REGISTER_DRIVER_H_
|
93
dv/cs_registers/reg_driver/register_transaction.cc
Normal file
93
dv/cs_registers/reg_driver/register_transaction.cc
Normal file
|
@ -0,0 +1,93 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "register_transaction.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void RegisterTransaction::Randomize(std::default_random_engine &gen) {
|
||||
std::uniform_int_distribution<int> addr_dist_ =
|
||||
std::uniform_int_distribution<int>(kCSRPMPCfg0, kCSRPMPAddr15);
|
||||
std::uniform_int_distribution<int> wdata_dist_ =
|
||||
std::uniform_int_distribution<int>(0, 0xFFFFFFFF);
|
||||
std::uniform_int_distribution<int> operation_dist_ =
|
||||
std::uniform_int_distribution<int>(kCSRRead, kCSRClear);
|
||||
csr_addr = addr_dist_(gen);
|
||||
csr_op = static_cast<CSRegisterOperation>(operation_dist_(gen));
|
||||
if (csr_op != kCSRRead) {
|
||||
csr_wdata = wdata_dist_(gen);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterTransaction::Print() {
|
||||
std::cout << "Register transaction:" << std::endl
|
||||
<< "Operation: " << RegOpString() << std::endl
|
||||
<< "Address: " << RegAddrString() << std::endl;
|
||||
if (csr_op != kCSRRead) {
|
||||
std::cout << "Write data: " << std::hex << csr_wdata << std::endl;
|
||||
}
|
||||
std::cout << "Read data: " << std::hex << csr_rdata << std::dec << std::endl;
|
||||
}
|
||||
|
||||
std::string RegisterTransaction::RegOpString() {
|
||||
switch (csr_op) {
|
||||
case kCSRRead:
|
||||
return "CSR Read";
|
||||
case kCSRWrite:
|
||||
return "CSR Write";
|
||||
case kCSRSet:
|
||||
return "CSR Set";
|
||||
case kCSRClear:
|
||||
return "CSR Clear";
|
||||
default:
|
||||
return "Unknown op";
|
||||
}
|
||||
}
|
||||
|
||||
std::string RegisterTransaction::RegAddrString() {
|
||||
switch (csr_addr) {
|
||||
case kCSRPMPCfg0:
|
||||
return "PMP Cfg 0";
|
||||
case kCSRPMPCfg1:
|
||||
return "PMP Cfg 1";
|
||||
case kCSRPMPCfg2:
|
||||
return "PMP Cfg 2";
|
||||
case kCSRPMPCfg3:
|
||||
return "PMP Cfg 3";
|
||||
case kCSRPMPAddr0:
|
||||
return "PMP Addr 0";
|
||||
case kCSRPMPAddr1:
|
||||
return "PMP Addr 1";
|
||||
case kCSRPMPAddr2:
|
||||
return "PMP Addr 2";
|
||||
case kCSRPMPAddr3:
|
||||
return "PMP Addr 3";
|
||||
case kCSRPMPAddr4:
|
||||
return "PMP Addr 4";
|
||||
case kCSRPMPAddr5:
|
||||
return "PMP Addr 5";
|
||||
case kCSRPMPAddr6:
|
||||
return "PMP Addr 6";
|
||||
case kCSRPMPAddr7:
|
||||
return "PMP Addr 7";
|
||||
case kCSRPMPAddr8:
|
||||
return "PMP Addr 8";
|
||||
case kCSRPMPAddr9:
|
||||
return "PMP Addr 9";
|
||||
case kCSRPMPAddr10:
|
||||
return "PMP Addr 10";
|
||||
case kCSRPMPAddr11:
|
||||
return "PMP Addr 11";
|
||||
case kCSRPMPAddr12:
|
||||
return "PMP Addr 12";
|
||||
case kCSRPMPAddr13:
|
||||
return "PMP Addr 13";
|
||||
case kCSRPMPAddr14:
|
||||
return "PMP Addr 14";
|
||||
case kCSRPMPAddr15:
|
||||
return "PMP Addr 15";
|
||||
default:
|
||||
return "Undef reg: " + std::to_string(csr_addr);
|
||||
}
|
||||
}
|
60
dv/cs_registers/reg_driver/register_transaction.h
Normal file
60
dv/cs_registers/reg_driver/register_transaction.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef REGISTER_TRANSACTION_H_
|
||||
#define REGISTER_TRANSACTION_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
// Enumerate the four register operation types
|
||||
enum CSRegisterOperation : int {
|
||||
kCSRRead = 0,
|
||||
kCSRWrite = 1,
|
||||
kCSRSet = 2,
|
||||
kCSRClear = 3
|
||||
};
|
||||
|
||||
// Enumerate the supported register types
|
||||
enum CSRegisterAddr : int {
|
||||
kCSRPMPCfg0 = 0x3A0,
|
||||
kCSRPMPCfg1 = 0x3A1,
|
||||
kCSRPMPCfg2 = 0x3A2,
|
||||
kCSRPMPCfg3 = 0x3A3,
|
||||
kCSRPMPAddr0 = 0x3B0,
|
||||
kCSRPMPAddr1 = 0x3B1,
|
||||
kCSRPMPAddr2 = 0x3B2,
|
||||
kCSRPMPAddr3 = 0x3B3,
|
||||
kCSRPMPAddr4 = 0x3B4,
|
||||
kCSRPMPAddr5 = 0x3B5,
|
||||
kCSRPMPAddr6 = 0x3B6,
|
||||
kCSRPMPAddr7 = 0x3B7,
|
||||
kCSRPMPAddr8 = 0x3B8,
|
||||
kCSRPMPAddr9 = 0x3B9,
|
||||
kCSRPMPAddr10 = 0x3BA,
|
||||
kCSRPMPAddr11 = 0x3BB,
|
||||
kCSRPMPAddr12 = 0x3BC,
|
||||
kCSRPMPAddr13 = 0x3BD,
|
||||
kCSRPMPAddr14 = 0x3BE,
|
||||
kCSRPMPAddr15 = 0x3BF
|
||||
};
|
||||
|
||||
struct RegisterTransaction {
|
||||
public:
|
||||
void Randomize(std::default_random_engine &gen);
|
||||
void Print();
|
||||
|
||||
CSRegisterOperation csr_op;
|
||||
bool illegal_csr;
|
||||
uint32_t csr_addr;
|
||||
uint32_t csr_rdata;
|
||||
uint32_t csr_wdata;
|
||||
|
||||
private:
|
||||
std::string RegOpString();
|
||||
std::string RegAddrString();
|
||||
};
|
||||
|
||||
#endif // REGISTER_TRANSACTION_H_
|
34
dv/cs_registers/rst_driver/reset_driver.cc
Normal file
34
dv/cs_registers/rst_driver/reset_driver.cc
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "reset_driver.h"
|
||||
|
||||
extern "C" void rst_register_intf(std::string name, ResetDriver *intf);
|
||||
extern "C" void rst_deregister_intf(std::string name);
|
||||
|
||||
ResetDriver::ResetDriver(std::string name)
|
||||
: reset_delay_(1), reset_duration_(0), name_(name) {}
|
||||
|
||||
void ResetDriver::OnInitial(unsigned int seed) {
|
||||
generator_.seed(seed);
|
||||
// 100 to 1000 cycles between resets
|
||||
delay_dist_ = std::uniform_int_distribution<int>(100, 1000);
|
||||
rst_register_intf(name_, this);
|
||||
}
|
||||
|
||||
void ResetDriver::OnFinal() { rst_deregister_intf(name_); }
|
||||
|
||||
void ResetDriver::DriveReset(unsigned char *rst_n) {
|
||||
reset_delay_--;
|
||||
if (reset_delay_ == 0) {
|
||||
reset_delay_ = delay_dist_(generator_);
|
||||
reset_duration_ = 0;
|
||||
}
|
||||
if (reset_duration_ < 3) {
|
||||
reset_duration_++;
|
||||
*rst_n = false;
|
||||
} else {
|
||||
*rst_n = true;
|
||||
}
|
||||
}
|
29
dv/cs_registers/rst_driver/reset_driver.h
Normal file
29
dv/cs_registers/rst_driver/reset_driver.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef RESET_DRIVER_H_
|
||||
#define RESET_DRIVER_H_
|
||||
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Class to randomize and drive reset signals
|
||||
*/
|
||||
class ResetDriver {
|
||||
public:
|
||||
ResetDriver(std::string name);
|
||||
void OnInitial(unsigned int seed);
|
||||
void OnFinal();
|
||||
void DriveReset(unsigned char *rst_n);
|
||||
|
||||
private:
|
||||
int reset_delay_;
|
||||
int reset_duration_;
|
||||
std::string name_;
|
||||
std::default_random_engine generator_;
|
||||
std::uniform_int_distribution<int> delay_dist_;
|
||||
};
|
||||
|
||||
#endif // RESET_DRIVER_H_
|
33
dv/cs_registers/rst_driver/rst_dpi.cc
Normal file
33
dv/cs_registers/rst_driver/rst_dpi.cc
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "register_environment.h"
|
||||
|
||||
#include "svdpi.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static std::map<std::string, ResetDriver *> intfs;
|
||||
|
||||
void rst_register_intf(std::string name, ResetDriver *intf) {
|
||||
intfs.insert({name, intf});
|
||||
}
|
||||
|
||||
void rst_deregister_intf(std::string name) { intfs.erase(name); }
|
||||
|
||||
void rst_tick(const char *name, svBit *rst_n) {
|
||||
auto ptr = intfs.find(name);
|
||||
if (ptr != intfs.end()) {
|
||||
ptr->second->DriveReset(rst_n);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
12
dv/cs_registers/rst_driver/rst_dpi.sv
Normal file
12
dv/cs_registers/rst_driver/rst_dpi.sv
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package rst_dpi;
|
||||
|
||||
import "DPI-C"
|
||||
function void rst_tick (
|
||||
input string name,
|
||||
output bit rst_n);
|
||||
|
||||
endpackage
|
35
dv/cs_registers/tb/tb_cs_registers.cc
Normal file
35
dv/cs_registers/tb/tb_cs_registers.cc
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "Vtb_cs_registers.h"
|
||||
#include "verilated_toplevel.h"
|
||||
#include "verilator_sim_ctrl.h"
|
||||
|
||||
tb_cs_registers *top;
|
||||
VerilatorSimCtrl *simctrl;
|
||||
|
||||
// dummy definition since this DPI call doesn't exist
|
||||
// TODO : remove this - see Ibex #317
|
||||
extern "C" {
|
||||
void simutil_verilator_memload(const char *file) {}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int retcode = 0;
|
||||
// init top verilog instance
|
||||
top = new tb_cs_registers;
|
||||
// Create SimCtrl instance
|
||||
simctrl = new VerilatorSimCtrl(top, top->clk_i, top->in_rst_ni,
|
||||
VerilatorSimCtrlFlags::ResetPolarityNegative);
|
||||
|
||||
// Setup the simulation
|
||||
retcode = simctrl->SetupSimulation(argc, argv);
|
||||
|
||||
// Run the simulation
|
||||
simctrl->RunSimulation();
|
||||
|
||||
delete top;
|
||||
delete simctrl;
|
||||
return retcode;
|
||||
}
|
166
dv/cs_registers/tb/tb_cs_registers.sv
Normal file
166
dv/cs_registers/tb/tb_cs_registers.sv
Normal file
|
@ -0,0 +1,166 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
module tb_cs_registers #(
|
||||
parameter int unsigned MHPMCounterNum = 8,
|
||||
parameter int unsigned MHPMCounterWidth = 40,
|
||||
parameter bit PMPEnable = 0,
|
||||
parameter int unsigned PMPGranularity = 0,
|
||||
parameter int unsigned PMPNumRegions = 4,
|
||||
parameter bit RV32E = 0,
|
||||
parameter bit RV32M = 0
|
||||
) (
|
||||
// Clock and Reset
|
||||
inout wire clk_i,
|
||||
inout wire in_rst_ni
|
||||
);
|
||||
|
||||
logic dpi_rst_ni;
|
||||
logic rst_ni;
|
||||
logic [31:0] hart_id_i;
|
||||
|
||||
// Privilege mode
|
||||
ibex_pkg::priv_lvl_e priv_mode_id_o;
|
||||
ibex_pkg::priv_lvl_e priv_mode_if_o;
|
||||
ibex_pkg::priv_lvl_e priv_mode_lsu_o;
|
||||
logic csr_mstatus_tw_o;
|
||||
|
||||
// mtvec
|
||||
logic [31:0] csr_mtvec_o;
|
||||
logic csr_mtvec_init_i;
|
||||
logic [31:0] boot_addr_i;
|
||||
|
||||
// Interface to registers (SRAM like)
|
||||
logic csr_access_i;
|
||||
ibex_pkg::csr_num_e csr_addr_i;
|
||||
logic [31:0] csr_wdata_i;
|
||||
ibex_pkg::csr_op_e csr_op_i;
|
||||
logic [31:0] csr_rdata_o;
|
||||
|
||||
// interrupts
|
||||
logic irq_software_i;
|
||||
logic irq_timer_i;
|
||||
logic irq_external_i;
|
||||
logic [14:0] irq_fast_i;
|
||||
logic irq_pending_o; // interupt request pending
|
||||
logic csr_msip_o; // software interrupt pending
|
||||
logic csr_mtip_o; // timer interrupt pending
|
||||
logic csr_meip_o; // external interrupt pending
|
||||
logic [14:0] csr_mfip_o; // fast interrupt pending
|
||||
logic csr_mstatus_mie_o;
|
||||
logic [31:0] csr_mepc_o;
|
||||
|
||||
// PMP
|
||||
ibex_pkg::pmp_cfg_t csr_pmp_cfg_o [PMPNumRegions];
|
||||
logic [33:0] csr_pmp_addr_o [PMPNumRegions];
|
||||
|
||||
// debug
|
||||
logic debug_mode_i;
|
||||
ibex_pkg::dbg_cause_e debug_cause_i;
|
||||
logic debug_csr_save_i;
|
||||
logic [31:0] csr_depc_o;
|
||||
logic debug_single_step_o;
|
||||
logic debug_ebreakm_o;
|
||||
|
||||
logic [31:0] pc_if_i;
|
||||
logic [31:0] pc_id_i;
|
||||
|
||||
logic csr_save_if_i;
|
||||
logic csr_save_id_i;
|
||||
logic csr_restore_mret_i;
|
||||
logic csr_save_cause_i;
|
||||
ibex_pkg::exc_cause_e csr_mcause_i;
|
||||
logic [31:0] csr_mtval_i;
|
||||
logic illegal_csr_insn_o; // access to non-existent CSR,
|
||||
// with wrong priviledge level, or
|
||||
// missing write permissions
|
||||
logic instr_new_id_i; // ID stage sees a new instr
|
||||
|
||||
// Performance Counters
|
||||
logic instr_ret_i; // instr retired in ID/EX stage
|
||||
logic instr_ret_compressed_i; // compressed instr retired
|
||||
logic imiss_i; // instr fetch
|
||||
logic pc_set_i; // PC was set to a new value
|
||||
logic jump_i; // jump instr seen (j, jr, jal, jalr)
|
||||
logic branch_i; // branch instr seen (bf, bnf)
|
||||
logic branch_taken_i; // branch was taken
|
||||
logic mem_load_i; // load from memory in this cycle
|
||||
logic mem_store_i; // store to memory in this cycle
|
||||
logic lsu_busy_i;
|
||||
|
||||
//-----------------
|
||||
// Reset generation
|
||||
//-----------------
|
||||
// Allow reset to be toggled by the top-level (in Verilator)
|
||||
// or a DPI call
|
||||
assign rst_ni = in_rst_ni & dpi_rst_ni;
|
||||
|
||||
//----------------------------------------
|
||||
// Clock generation (not used in Verilator
|
||||
//----------------------------------------
|
||||
`ifndef VERILATOR
|
||||
logic local_clk_i;
|
||||
initial begin
|
||||
local_clk_i = 1'b0;
|
||||
while (1) begin
|
||||
#10
|
||||
local_clk_i = !local_clk_i;
|
||||
end
|
||||
end
|
||||
assign clk_i = local_clk_i;
|
||||
assign in_rst_ni = 1'b1;
|
||||
`endif
|
||||
|
||||
ibex_cs_registers #(
|
||||
.MHPMCounterNum (MHPMCounterNum),
|
||||
.MHPMCounterWidth (MHPMCounterWidth),
|
||||
.PMPEnable (PMPEnable),
|
||||
.PMPGranularity (PMPGranularity),
|
||||
.PMPNumRegions (PMPNumRegions),
|
||||
.RV32E (RV32E),
|
||||
.RV32M (RV32M)
|
||||
) i_cs_regs (.*);
|
||||
|
||||
// DPI calls
|
||||
bit stop_simulation;
|
||||
bit [31:0] seed;
|
||||
|
||||
initial begin
|
||||
if (!$value$plusargs ("ntb_random_seed=%d", seed)) begin
|
||||
seed = 32'd0;
|
||||
end
|
||||
env_dpi::env_initial(seed);
|
||||
end
|
||||
|
||||
final begin
|
||||
env_dpi::env_final();
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
env_dpi::env_tick(stop_simulation);
|
||||
rst_dpi::rst_tick("rstn_driver", dpi_rst_ni);
|
||||
if (stop_simulation) begin
|
||||
$finish();
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
reg_dpi::monitor_tick("reg_driver",
|
||||
rst_ni,
|
||||
illegal_csr_insn_o,
|
||||
csr_access_i,
|
||||
csr_op_i,
|
||||
csr_addr_i,
|
||||
csr_wdata_i,
|
||||
csr_rdata_o);
|
||||
reg_dpi::driver_tick("reg_driver",
|
||||
csr_access_i,
|
||||
csr_op_i,
|
||||
csr_addr_i,
|
||||
csr_wdata_i);
|
||||
end
|
||||
// Note that CSR accesses only happen if instr_new_id_i is high
|
||||
assign instr_new_id_i = csr_access_i;
|
||||
|
||||
endmodule
|
83
dv/cs_registers/tb_cs_registers.core
Normal file
83
dv/cs_registers/tb_cs_registers.core
Normal file
|
@ -0,0 +1,83 @@
|
|||
CAPI=2:
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: "lowrisc:ibex:tb_cs_registers"
|
||||
description: "CS registers testbench"
|
||||
filesets:
|
||||
files_sim_verilator:
|
||||
depend:
|
||||
- lowrisc:dv_verilator:simutil_verilator
|
||||
files:
|
||||
- tb/tb_cs_registers.cc: { file_type: cppSource }
|
||||
|
||||
files_sim:
|
||||
depend:
|
||||
- lowrisc:ibex:ibex_core
|
||||
files:
|
||||
- env/env_dpi.sv
|
||||
- rst_driver/rst_dpi.sv
|
||||
- reg_driver/reg_dpi.sv
|
||||
- tb/tb_cs_registers.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
parameters:
|
||||
PMPEnable:
|
||||
datatype: bool
|
||||
paramtype: vlogparam
|
||||
default: true
|
||||
description: PMP enabled [true/false]
|
||||
PMPNumRegions:
|
||||
datatype: int
|
||||
paramtype: vlogparam
|
||||
default: 4
|
||||
description: Number of implemented PMP regions [0/16]
|
||||
PMPGranularity:
|
||||
datatype: int
|
||||
paramtype: vlogparam
|
||||
default: 0
|
||||
description: Minimum PMP matching granularity [0/31]
|
||||
|
||||
targets:
|
||||
sim:
|
||||
default_tool: verilator
|
||||
toplevel: tb_cs_registers
|
||||
filesets:
|
||||
- files_sim
|
||||
- files_sim_verilator
|
||||
parameters:
|
||||
- PMPEnable
|
||||
- PMPNumRegions
|
||||
- PMPGranularity
|
||||
|
||||
tools:
|
||||
vcs:
|
||||
vcs_options:
|
||||
- '${PWD}/build/bin/reg_dpi.so'
|
||||
- '-debug_access+all'
|
||||
|
||||
verilator:
|
||||
mode: cc
|
||||
libs:
|
||||
- '${PWD}/build/bin/reg_dpi.so'
|
||||
verilator_options:
|
||||
# Disabling tracing reduces compile times by multiple times, but doesn't have a
|
||||
# huge influence on runtime performance. (Based on early observations.)
|
||||
- '--trace'
|
||||
- '--trace-fst' # this requires -DVM_TRACE_FMT_FST in CFLAGS below!
|
||||
- '--trace-structs'
|
||||
- '--trace-params'
|
||||
- '--trace-max-array 1024'
|
||||
# compiler flags
|
||||
#
|
||||
# -O
|
||||
# Optimization levels have a large impact on the runtime performance of the
|
||||
# simulation model. -O2 and -O3 are pretty similar, -Os is slower than -O2/-O3
|
||||
- '-CFLAGS "-std=c++14 -Wall -DTOPLEVEL_NAME=tb_cs_registers -DVM_TRACE_FMT_FST -g -O0"'
|
||||
- '-LDFLAGS "-pthread -lutil"'
|
||||
- "-Wall"
|
||||
- "-Wno-PINCONNECTEMPTY"
|
||||
# XXX: Cleanup all warnings and remove this option
|
||||
# (or make it more fine-grained at least)
|
||||
- "-Wno-fatal"
|
|
@ -12,78 +12,21 @@
|
|||
ibex_riscv_compliance *top;
|
||||
VerilatorSimCtrl *simctrl;
|
||||
|
||||
static void SignalHandler(int sig) {
|
||||
if (!simctrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (sig) {
|
||||
case SIGINT:
|
||||
simctrl->RequestStop();
|
||||
break;
|
||||
case SIGUSR1:
|
||||
if (simctrl->TracingEnabled()) {
|
||||
simctrl->TraceOff();
|
||||
} else {
|
||||
simctrl->TraceOn();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void SetupSignalHandler() {
|
||||
struct sigaction sigIntHandler;
|
||||
|
||||
sigIntHandler.sa_handler = SignalHandler;
|
||||
sigemptyset(&sigIntHandler.sa_mask);
|
||||
sigIntHandler.sa_flags = 0;
|
||||
|
||||
sigaction(SIGINT, &sigIntHandler, NULL);
|
||||
sigaction(SIGUSR1, &sigIntHandler, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current simulation time
|
||||
*
|
||||
* Called by $time in Verilog, converts to double, to match what SystemC does
|
||||
*/
|
||||
double sc_time_stamp() { return simctrl->GetTime(); }
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int retcode;
|
||||
top = new ibex_riscv_compliance;
|
||||
simctrl = new VerilatorSimCtrl(top, top->IO_CLK, top->IO_RST_N,
|
||||
VerilatorSimCtrlFlags::ResetPolarityNegative);
|
||||
|
||||
SetupSignalHandler();
|
||||
|
||||
if (!simctrl->ParseCommandArgs(argc, argv, retcode)) {
|
||||
goto free_return;
|
||||
}
|
||||
|
||||
std::cout << "Simulation of Ibex" << std::endl
|
||||
<< "==================" << std::endl
|
||||
<< std::endl;
|
||||
|
||||
if (simctrl->TracingPossible()) {
|
||||
std::cout << "Tracing can be toggled by sending SIGUSR1 to this process:"
|
||||
<< std::endl
|
||||
<< "$ kill -USR1 " << getpid() << std::endl;
|
||||
}
|
||||
// Setup simctrl
|
||||
retcode = simctrl->SetupSimulation(argc, argv);
|
||||
|
||||
// Initialize RAM
|
||||
simctrl->InitRam("TOP.ibex_riscv_compliance.u_ram");
|
||||
|
||||
simctrl->Run();
|
||||
simctrl->PrintStatistics();
|
||||
// Run the simulation
|
||||
simctrl->RunSimulation();
|
||||
|
||||
if (simctrl->TracingEverEnabled()) {
|
||||
std::cout << std::endl
|
||||
<< "You can view the simulation traces by calling" << std::endl
|
||||
<< "$ gtkwave " << simctrl->GetSimulationFileName() << std::endl;
|
||||
}
|
||||
|
||||
free_return:
|
||||
delete top;
|
||||
delete simctrl;
|
||||
return retcode;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "verilator_sim_ctrl.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <vltstd/svdpi.h>
|
||||
|
@ -16,6 +17,42 @@
|
|||
#define VM_TRACE 0
|
||||
#endif
|
||||
|
||||
// Static pointer to a single simctrl instance
|
||||
// used by SignalHandler
|
||||
static VerilatorSimCtrl *simctrl;
|
||||
|
||||
static void SignalHandler(int sig) {
|
||||
if (!simctrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (sig) {
|
||||
case SIGINT:
|
||||
simctrl->RequestStop(true);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
if (simctrl->TracingEnabled()) {
|
||||
simctrl->TraceOff();
|
||||
} else {
|
||||
simctrl->TraceOn();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current simulation time
|
||||
*
|
||||
* Called by $time in Verilog, converts to double, to match what SystemC does
|
||||
*/
|
||||
double sc_time_stamp() {
|
||||
if (simctrl) {
|
||||
return simctrl->GetTime();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// DPI Exports
|
||||
extern "C" {
|
||||
extern void simutil_verilator_memload(const char *file);
|
||||
|
@ -38,10 +75,60 @@ VerilatorSimCtrl::VerilatorSimCtrl(VerilatedToplevel *top, CData &sig_clk,
|
|||
initial_reset_delay_cycles_(2),
|
||||
reset_duration_cycles_(2),
|
||||
request_stop_(false),
|
||||
simulation_success_(true),
|
||||
tracer_(VerilatedTracer()),
|
||||
term_after_cycles_(0) {}
|
||||
term_after_cycles_(0),
|
||||
callback_(nullptr) {}
|
||||
|
||||
void VerilatorSimCtrl::RequestStop() { request_stop_ = true; }
|
||||
int VerilatorSimCtrl::SetupSimulation(int argc, char **argv) {
|
||||
int retval;
|
||||
// Setup the signal handler for this instance
|
||||
RegisterSignalHandler();
|
||||
// Parse the command line argumanets
|
||||
if (!ParseCommandArgs(argc,argv,retval)) {
|
||||
return retval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::RunSimulation() {
|
||||
// Print helper message for tracing
|
||||
if (TracingPossible()) {
|
||||
std::cout << "Tracing can be toggled by sending SIGUSR1 to this process:"
|
||||
<< std::endl
|
||||
<< "$ kill -USR1 " << getpid() << std::endl;
|
||||
|
||||
}
|
||||
// Run the simulation
|
||||
Run();
|
||||
// Print simulation speed info
|
||||
PrintStatistics();
|
||||
// Print helper message for tracing
|
||||
if (TracingEverEnabled()) {
|
||||
std::cout << std::endl
|
||||
<< "You can view the simulation traces by calling" << std::endl
|
||||
<< "$ gtkwave " << GetSimulationFileName() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::RegisterSignalHandler() {
|
||||
struct sigaction sigIntHandler;
|
||||
|
||||
// Point the static simctrl pointer at this object
|
||||
simctrl = this;
|
||||
|
||||
sigIntHandler.sa_handler = SignalHandler;
|
||||
sigemptyset(&sigIntHandler.sa_mask);
|
||||
sigIntHandler.sa_flags = 0;
|
||||
|
||||
sigaction(SIGINT, &sigIntHandler, NULL);
|
||||
sigaction(SIGUSR1, &sigIntHandler, NULL);
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::RequestStop(bool simulation_success) {
|
||||
request_stop_ = true;
|
||||
simulation_success_ &= simulation_success;
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::TraceOn() {
|
||||
bool old_tracing_enabled = tracing_enabled_;
|
||||
|
@ -259,6 +346,10 @@ const char *VerilatorSimCtrl::GetSimulationFileName() const {
|
|||
#endif
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::SetOnClockCallback(SimCtrlCallBack callback) {
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::Run() {
|
||||
// We always need to enable this as tracing can be enabled at runtime
|
||||
if (tracing_possible_) {
|
||||
|
@ -285,6 +376,10 @@ void VerilatorSimCtrl::Run() {
|
|||
|
||||
sig_clk_ = !sig_clk_;
|
||||
|
||||
if (sig_clk_ && (callback_ != nullptr)) {
|
||||
callback_(time_);
|
||||
}
|
||||
|
||||
top_->eval();
|
||||
time_++;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include "verilated_toplevel.h"
|
||||
|
||||
|
@ -17,11 +18,43 @@ enum VerilatorSimCtrlFlags {
|
|||
ResetPolarityNegative = 1,
|
||||
};
|
||||
|
||||
// Callback function to be called every clock cycle
|
||||
// The parameter is simulation time
|
||||
typedef std::function<void(int /* sim time */)> SimCtrlCallBack;
|
||||
|
||||
class VerilatorSimCtrl {
|
||||
public:
|
||||
VerilatorSimCtrl(VerilatedToplevel *top, CData &clk, CData &rst_n,
|
||||
VerilatorSimCtrlFlags flags = Defaults);
|
||||
|
||||
/**
|
||||
* A helper function to execute some standard setup commands.
|
||||
*
|
||||
* This function performs the followind tasks:
|
||||
* 1. Sets up a signal handler to enable tracing to be turned on/off during
|
||||
* a run by sending SIGUSR1 to the process
|
||||
* 2. Parses a C-style set of command line arguments (see ParseCommandArgs)
|
||||
*
|
||||
* @return return code (0 = success)
|
||||
*/
|
||||
int SetupSimulation(int argc, char **argv);
|
||||
|
||||
/**
|
||||
* A helper function to execute a standard set of run commands.
|
||||
*
|
||||
* This function performs the followind tasks:
|
||||
* 1. Prints some tracer-related helper messages
|
||||
* 2. Runs the simulation
|
||||
* 3. Prints some further helper messages and statistics once the simulation
|
||||
* has run to completion
|
||||
*/
|
||||
void RunSimulation();
|
||||
|
||||
/**
|
||||
* Register the signal handler
|
||||
*/
|
||||
void RegisterSignalHandler();
|
||||
|
||||
/**
|
||||
* Print help how to use this tool
|
||||
*/
|
||||
|
@ -65,6 +98,11 @@ class VerilatorSimCtrl {
|
|||
*/
|
||||
unsigned long GetTime() { return time_; }
|
||||
|
||||
/**
|
||||
* Get the simulation result
|
||||
*/
|
||||
bool WasSimulationSuccessful() { return simulation_success_; }
|
||||
|
||||
/**
|
||||
* Set the number of clock cycles (periods) before the reset signal is
|
||||
* activated
|
||||
|
@ -79,7 +117,7 @@ class VerilatorSimCtrl {
|
|||
/**
|
||||
* Request the simulation to stop
|
||||
*/
|
||||
void RequestStop();
|
||||
void RequestStop(bool simulation_success);
|
||||
|
||||
/**
|
||||
* Enable tracing (if possible)
|
||||
|
@ -122,6 +160,11 @@ class VerilatorSimCtrl {
|
|||
|
||||
const char *GetSimulationFileName() const;
|
||||
|
||||
/**
|
||||
* Set a callback function to run every cycle
|
||||
*/
|
||||
void SetOnClockCallback(SimCtrlCallBack callback);
|
||||
|
||||
private:
|
||||
VerilatedToplevel *top_;
|
||||
CData &sig_clk_;
|
||||
|
@ -141,10 +184,12 @@ class VerilatorSimCtrl {
|
|||
unsigned int initial_reset_delay_cycles_;
|
||||
unsigned int reset_duration_cycles_;
|
||||
volatile unsigned int request_stop_;
|
||||
volatile bool simulation_success_;
|
||||
std::chrono::steady_clock::time_point time_begin_;
|
||||
std::chrono::steady_clock::time_point time_end_;
|
||||
VerilatedTracer tracer_;
|
||||
int term_after_cycles_;
|
||||
SimCtrlCallBack callback_;
|
||||
|
||||
unsigned int GetExecutionTimeMs();
|
||||
void SetReset();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue