Loading files
92595157eThis file is pinned to the selected release. Download the original to open it in your design tool.
#include "VMain_Zynq_Wrapper.h"
#include "VMain_Zynq_Wrapper__Syms.h" // To see all public symbols
#include "VerilatorHelper.h"
#include "Dumper.h"
#include <verilated.h> // Defines common routines
#include <verilated_vcd_c.h>
#include <stdexcept>
#include <regex>
#include <fstream>
#include <iostream>
using namespace std;
unsigned int main_time = 0; // Current simulation time
double sc_time_stamp () { // Called by $time in Verilog
return main_time;
}
int GetCommittedRegisterValue(
VMain_Zynq_Wrapper* top,
int commitNumInThisCycle,
DataPath* regData
){
auto* core = top->Main_Zynq_Wrapper->main->core;
auto* helper = top->VerilatorHelper;
static const int LSCALAR_NUM = helper->LSCALAR_NUM;
typeof (core->retirementRMT->regRMT->debugValue) phyRegNum;
// Copy RMT to local variable.
for (int i = 0; i < LREG_NUM; i++) {
phyRegNum[i] = core->retirementRMT->regRMT->debugValue[i];
}
// Update RRMT
//ActiveListIndexPath alHeadPtr;
auto alHeadPtr = core->activeList->headPtr;
for (int i = 0; i < commitNumInThisCycle; i++) {
// ActiveListEntry alHead;
const auto& alHead = core->activeList->activeList->debugValue[alHeadPtr];
if (helper->ActiveListEntry_writeReg(alHead)) {
phyRegNum[helper->ActiveListEntry_logDstRegNum(alHead)] = helper->ActiveListEntry_phyDstRegNum_regNum(alHead);
}
alHeadPtr++;
}
// Get regData
for(int i = 0; i < LSCALAR_NUM; i++) {
regData[i] = core->registerFile->phyReg->debugValue[phyRegNum[i]];
}
#ifdef RSD_MARCH_FP_PIPE
for(int i = LSCALAR_NUM; i < LSCALAR_NUM + LSCALAR_FP_NUM; i++) {
regData[i] = core->registerFile->phyFPReg->debugValue[phyRegNum[i]];
}
#endif
return 0;
}
int main(int argc, char** argv) {
// Initialize verilated modules
Verilated::commandArgs(argc, argv); // Remember args
auto *top = new VMain_Zynq_Wrapper();
auto* core = top->Main_Zynq_Wrapper->main->core;
auto* helper = top->VerilatorHelper;
// Initialize test bench
int64_t MAX_TEST_CYCLES = 100;
bool ENABLE_PC_GOAL = true;
bool SHOW_SERIAL_OUT = false;
string TEST_CODE = "Verification/TestCode/C/Fibonacci";
string REG_CSV_FILE = "";
string RSD_LOG_FILE = "";
string WAVE_LOG_FILE = "";
// Parse command line parameters
// MAX_TEST_CYCLES=<cycles>
// TEST_CODE=<path to code>
regex re("([^=]+)=([^=]+)");
for (int i = 1; i < argc; i++) {
cmatch results;
if (regex_match(argv[i], results, re)) {
auto name = results[1].str();
auto value = results[2].str();
if (name == "MAX_TEST_CYCLES") {
MAX_TEST_CYCLES = stoi(value);
}
else if (name == "ENABLE_PC_GOAL") {
ENABLE_PC_GOAL = stoi(value) ? true : false;
}
else if (name == "SHOW_SERIAL_OUT") {
SHOW_SERIAL_OUT = stoi(value) ? true : false;
}
else if (name == "REG_CSV_FILE") {
REG_CSV_FILE = value;
}
else if (name == "RSD_LOG_FILE") {
RSD_LOG_FILE = value;
}
else if (name == "TEST_CODE") {
TEST_CODE = value;
}
else if (name == "WAVE_LOG_FILE") {
WAVE_LOG_FILE = value;
}
else {
printf("Unknown parameter:%s\n", argv[i]);
}
}
else {
printf("Unknown parameter:%s\n", argv[i]);
}
}
#ifdef RSD_VERILATOR_TRACE
VerilatedVcdC* tfp = nullptr;
if (WAVE_LOG_FILE != "") {
Verilated::traceEverOn(true);
tfp = new VerilatedVcdC();
top->trace(tfp, 99);
tfp->open("simx.vcd");
}
#endif
// Initialize dumpers
bool enableDumpKanata = false;
KanataDumper kanataDumper;
if (RSD_LOG_FILE != "") {
enableDumpKanata = true;
kanataDumper.Open(RSD_LOG_FILE);
}
bool enableDumpRegCSV = false;
RegisterFileCSV_Dumper registerFileCSV_Dumper;
if (REG_CSV_FILE != "") {
enableDumpRegCSV = true;
registerFileCSV_Dumper.Open(REG_CSV_FILE);
}
string codeFileName = TEST_CODE + "/" + "code.hex";
string regOutFileName = TEST_CODE + "/" + "reg.out.hex";
string serialDumpFileName = TEST_CODE + "/" + "serial.out.txt";
SerialDumper serialDumper(SHOW_SERIAL_OUT); // サイクルダンプ無効時は標準出力にシリアルの結果を出す
serialDumper.Open(serialDumpFileName);
// The word of the main memory is 128 bits.
// A word is implemented as uint32_t[4] in verilated modules.
//int MEMORY_ENTRY_NUM = top->MemoryTypes->MEMORY_ENTRY_NUM;
//uint32_t* mainMem = &(top->Main_Zynq_Wrapper->main->memory->body->array[0][0]);
//assert(top->MemoryTypes->MEMORY_ENTRY_BIT_NUM == 128);
//assert(sizeof(top->Main_Zynq_Wrapper->main->memory->body->array) == 128/8 * MEMORY_ENTRY_NUM);
// To access the module generated in generate,
// use (Label given in generate section)__DOT__(module name)
size_t mainMemWordSize = sizeof(top->Main_Zynq_Wrapper->main->memory->body->body__DOT__ram->array) / sizeof(uint32_t);
uint32_t* mainMem = reinterpret_cast<uint32_t*>(&top->Main_Zynq_Wrapper->main->memory->body->body__DOT__ram->array);
// Fill dummy data
for (int i = 0; i < mainMemWordSize; i++) {
mainMem[i] = 0xcdcdcdcd;
}
// Load code data
auto loadHexFile = [&](
const string& fileName,
uint32_t* mainMem,
uint32_t offset,
bool neccesary
) {
ifstream ifs(fileName);
if (ifs.fail()) {
if (neccesary) {
printf("Fail to load \"%s\".\n", fileName.c_str());
exit(1);
}
}
string line;
uint32_t wordAddr = offset / sizeof(uint32_t);
while (getline(ifs, line)) {
if (wordAddr >= mainMemWordSize) {
printf("\"%s\" is too large.\n", fileName.c_str());
exit(1);
}
for (int i = 0; i < 4; i++) {
string wordStr = line.substr((3 - i) * 8, 8);
uint32_t word = strtoul(wordStr.c_str(), nullptr, 16);
mainMem[wordAddr + i] = word;
}
wordAddr += 4;
}
printf(
"Loaded %s into a physical memory region [%x-%x].\n",
fileName.c_str(),
(uint32_t)offset,
(uint32_t)(offset + wordAddr * sizeof(uint32_t) - 1)
);
};
// ファイル内容は物理メモリ空間の先頭から連続して展開される
// ファイル先頭 64KB は ROM とみなされ,残りが RAM の空間に展開される
// Physical 0x0_0000 - 0x0_ffff -> Logical 0x0000_0000 - 0x0000_ffff: ROM (64KB)
// Physical 0x1_0000 - 0x4_ffff -> Logical 0x8000_0000 - 0x8003_ffff: RAM (256KB)
// たとえば 128KB のファイルの場合,
// 先頭 64KB は 論理空間の 0x0000_0000 - 0x0000_FFFF に,
// 後続 64KB は 論理空間の 0x8000_0000 - 0x8000_FFFF に展開されることになる
loadHexFile(codeFileName, mainMem, 0, true);
int numCommittedARM_Op = 0;
int numCommittedMicroOp = 0;
LED_Path lastCommittedPC = 0;
DebugRegister debugRegister;
memset(&debugRegister, 0x0, sizeof(DebugRegister));
// TestBenchClockGenerator にあわせる
const int RSD_STEP = 8;
const int RSD_KANATA_CYCLE_DISPLACEMENT = -1;
const int RSD_INITIALIZATION_CYCLE = 8;
int64_t cycle = -1;
int64_t kanataCycle = cycle - RSD_KANATA_CYCLE_DISPLACEMENT;
bool start = false; // タイミングを TestMain.sv にあわせるため
try{
top->negResetIn = 0; // Set some inputs
top->clk_p = 0;
top->clk_n = 1;
top->rxd = 0;
top->eval();
#ifdef RSD_VERILATOR_TRACE
if (tfp) tfp->dump(main_time);
#endif
main_time += RSD_STEP*2;
while (!Verilated::gotFinish()) {
// クロック更新
top->clk_p = !top->clk_p;
top->clk_n = !top->clk_n;
top->eval(); // 評価
// ダンプ
#ifdef RSD_VERILATOR_TRACE
if (tfp)
tfp->dump(main_time);
#endif
// 実行が開始されていたらクロックをインクリメント
if (top->clk_p && start){
kanataCycle++;
cycle++;
// ダンプ
GetDebugRegister(&debugRegister, top);
if (!SHOW_SERIAL_OUT && (kanataCycle < 10000 || kanataCycle % 10000 == 0)){
printf("%d cycle, %d KanataCycle, %d ns\n", (uint32_t)cycle, (uint32_t)kanataCycle, (uint32_t)main_time);
}
serialDumper.CheckSignal(
top->serialWE,
top->serialWriteData
);
// Dump RSD.log for Kanata
if (enableDumpKanata){
kanataDumper.DumpCycle(debugRegister);
}
if (enableDumpRegCSV) {
registerFileCSV_Dumper.ProceedCycle();
for (int i = 0; i < COMMIT_WIDTH; i++) {
// 1命令ずつコミットを追ってレジスタ状態をダンプする
if (core->cmStage->commit[i]) {
DataPath regData[LREG_NUM];
GetCommittedRegisterValue(top, i, regData);
registerFileCSV_Dumper.Dump(
helper->ActiveListEntry_pc(core->cmStage->alReadData[i]),
regData
);
}
}
}
// Count number of committed Ops.
for (int i = 0; i < COMMIT_WIDTH; i++) {
if (debugRegister.cmReg[i].commit) {
numCommittedMicroOp += 1;
if ( debugRegister.cmReg[i].opId.mid == 0 ){
numCommittedARM_Op += 1;
}
}
}
// Check end of simulation.
if (ENABLE_PC_GOAL) {
lastCommittedPC = top->ledOut;
if (lastCommittedPC == static_cast<typeof(top->ledOut)>(PC_GOAL)) {
// lastCommittedPC は 16bit 分しか外に出てきていないので,下位で判定しておく
printf("PC reached PC_GOAL: %08x\n", PC_GOAL);
break;
}
}
}
start = !top->posResetOut;
main_time += RSD_STEP; // 62.5MHz
if (main_time == RSD_INITIALIZATION_CYCLE*RSD_STEP*2) {
top->negResetIn = 1; // リセット解除
}
// 指定サイクル数の実行で終了
// Modelsim 側とクロック数の計算をあわすため +1
if (cycle + 1 >= MAX_TEST_CYCLES)
break;
}
} catch (const std::runtime_error& error) {
}
// Count the number of commit in the last cycle.
int commitNumInLastCycle = 0;
for (int count = 0; count < COMMIT_WIDTH; count++) {
if (!core->cmStage->commit[count])
break;
commitNumInLastCycle++;
}
// Close Dumpers
//serialDumper.DumpToFile(serialDumpFileName);
serialDumper.Close();
kanataDumper.Close();
registerFileCSV_Dumper.Close();
// Simulation Result
printf("Num of I$ misses: %d\n", debugRegister.perfCounter.numIC_Miss);
printf("Num of D$ load misses: %d\n", debugRegister.perfCounter.numLoadMiss);
printf("Num of D$ store misses: %d\n", debugRegister.perfCounter.numStoreMiss);
printf("Num of branch prediction misses: %d\n", debugRegister.perfCounter.numBranchPredMiss);
printf("Num of branch prediction misses detected on decode: %d\n", debugRegister.perfCounter.numBranchPredMissDetectedOnDecode);
printf("Num of store-load-forwanind misses: %d\n", debugRegister.perfCounter.numStoreLoadForwardingFail);
printf("Num of memory dependency prediction misses: %d\n", debugRegister.perfCounter.numMemDepPredMiss);
printf("Num of committed RISC-V-ops: %d\n", numCommittedARM_Op);
printf("Num of committed micro-ops: %d\n", numCommittedMicroOp);
if (cycle != 0) {
printf("IPC (RISC-V instruction): %f\n", (double)numCommittedARM_Op / (double)cycle);
printf("IPC (micro-op): %f\n", (double)numCommittedMicroOp / (double)cycle);
}
printf("Elapsed cycles: %d\n", (int32_t)cycle);
// Dump Register File
RegisterFileHexDumper registerFileHexDumper;
DataPath regData[LREG_NUM];
GetCommittedRegisterValue(top, commitNumInLastCycle, regData);
registerFileHexDumper.Open(regOutFileName);
registerFileHexDumper.Dump(lastCommittedPC, regData);
registerFileHexDumper.Close();
#ifdef RSD_VERILATOR_TRACE
if (tfp) {
tfp->close();
tfp = nullptr;
}
#endif
top->final(); // シミュレーション終了
}