285 lines
5.5 KiB
C
285 lines
5.5 KiB
C
|
|
||
|
#include <stdio.h>
|
||
|
#include <iostream>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdint.h>
|
||
|
#include <cstring>
|
||
|
#include <string.h>
|
||
|
#include <iostream>
|
||
|
#include <fstream>
|
||
|
#include <vector>
|
||
|
#include <iomanip>
|
||
|
#include <time.h>
|
||
|
#include <unistd.h>
|
||
|
#include "verilated_fst_c.h"
|
||
|
|
||
|
using namespace std;
|
||
|
|
||
|
class SimElement{
|
||
|
public:
|
||
|
virtual ~SimElement(){}
|
||
|
virtual void onReset(){}
|
||
|
virtual void postReset(){}
|
||
|
virtual void preCycle(){}
|
||
|
virtual void postCycle(){}
|
||
|
};
|
||
|
|
||
|
//#include <functional>
|
||
|
class TimeProcess{
|
||
|
public:
|
||
|
uint64_t wakeDelay = 0;
|
||
|
bool wakeEnable = false;
|
||
|
// std::function<int(double)> lambda;
|
||
|
virtual ~TimeProcess(){}
|
||
|
virtual void schedule(uint64_t delay){
|
||
|
wakeDelay = delay;
|
||
|
wakeEnable = true;
|
||
|
}
|
||
|
virtual void tick(){
|
||
|
// lambda = [this](double x) { return x+1 + this->wakeDelay; };
|
||
|
// lambda(1.0);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
class SensitiveProcess{
|
||
|
public:
|
||
|
|
||
|
virtual ~SensitiveProcess(){}
|
||
|
virtual void tick(uint64_t time){
|
||
|
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class ClockDomain : public TimeProcess{
|
||
|
public:
|
||
|
CData* clk;
|
||
|
CData* reset;
|
||
|
uint64_t tooglePeriod;
|
||
|
vector<SimElement*> simElements;
|
||
|
ClockDomain(CData *clk, CData *reset, uint64_t period, uint64_t delay){
|
||
|
this->clk = clk;
|
||
|
this->reset = reset;
|
||
|
*clk = 0;
|
||
|
this->tooglePeriod = period/2;
|
||
|
schedule(delay);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool postCycle = false;
|
||
|
virtual void tick(){
|
||
|
if(*clk == 0){
|
||
|
for(SimElement* simElement : simElements){
|
||
|
simElement->preCycle();
|
||
|
}
|
||
|
postCycle = true;
|
||
|
*clk = 1;
|
||
|
schedule(0);
|
||
|
}else{
|
||
|
if(postCycle){
|
||
|
postCycle = false;
|
||
|
for(SimElement* simElement : simElements){
|
||
|
simElement->postCycle();
|
||
|
}
|
||
|
}else{
|
||
|
*clk = 0;
|
||
|
}
|
||
|
schedule(tooglePeriod);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void add(SimElement *that){
|
||
|
simElements.push_back(that);
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
class AsyncReset : public TimeProcess{
|
||
|
public:
|
||
|
CData* reset;
|
||
|
uint32_t state;
|
||
|
uint64_t duration;
|
||
|
AsyncReset(CData *reset, uint64_t duration){
|
||
|
this->reset = reset;
|
||
|
*reset = 0;
|
||
|
state = 0;
|
||
|
this->duration = duration;
|
||
|
schedule(0);
|
||
|
}
|
||
|
|
||
|
virtual void tick(){
|
||
|
switch(state){
|
||
|
case 0:
|
||
|
*reset = 1;
|
||
|
state = 1;
|
||
|
schedule(duration);
|
||
|
break;
|
||
|
case 1:
|
||
|
*reset = 0;
|
||
|
state = 2;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
class success : public std::exception { };
|
||
|
static uint32_t workspaceCycles = 0;
|
||
|
template <class T> class Workspace{
|
||
|
public:
|
||
|
|
||
|
vector<TimeProcess*> timeProcesses;
|
||
|
vector<SensitiveProcess*> checkProcesses;
|
||
|
T* top;
|
||
|
bool resetDone = false;
|
||
|
double timeToSec = 1e-12;
|
||
|
double speedFactor = 1.0;
|
||
|
uint64_t allowedTime = 0;
|
||
|
string name;
|
||
|
uint64_t time = 0;
|
||
|
#ifdef TRACE
|
||
|
VerilatedFstC* tfp;
|
||
|
#endif
|
||
|
|
||
|
ofstream logTraces;
|
||
|
|
||
|
Workspace(string name){
|
||
|
this->name = name;
|
||
|
top = new T;
|
||
|
logTraces.open (name + ".logTrace");
|
||
|
}
|
||
|
|
||
|
virtual ~Workspace(){
|
||
|
delete top;
|
||
|
#ifdef TRACE
|
||
|
delete tfp;
|
||
|
#endif
|
||
|
|
||
|
for(auto* p : timeProcesses) delete p;
|
||
|
for(auto* p : checkProcesses) delete p;
|
||
|
|
||
|
}
|
||
|
|
||
|
Workspace* setSpeedFactor(double value){
|
||
|
speedFactor = value;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
|
||
|
virtual void postReset() {}
|
||
|
virtual void checks(){}
|
||
|
virtual void pass(){ throw success();}
|
||
|
virtual void fail(){ throw std::exception();}
|
||
|
|
||
|
virtual void dump(uint64_t i){
|
||
|
#ifdef TRACE
|
||
|
if(i >= TRACE_START) tfp->dump(i);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
Workspace* run(uint32_t timeout = 5000){
|
||
|
|
||
|
// init trace dump
|
||
|
#ifdef TRACE
|
||
|
Verilated::traceEverOn(true);
|
||
|
tfp = new VerilatedFstC;
|
||
|
top->trace(tfp, 99);
|
||
|
tfp->open((string(name)+ ".fst").c_str());
|
||
|
#endif
|
||
|
|
||
|
struct timespec start_time,tick_time;
|
||
|
uint64_t tickLastSimTime = 0;
|
||
|
top->eval();
|
||
|
|
||
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_time);
|
||
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tick_time);
|
||
|
|
||
|
uint32_t flushCounter = 0;
|
||
|
try {
|
||
|
while(1){
|
||
|
uint64_t delay = ~0l;
|
||
|
for(TimeProcess* p : timeProcesses)
|
||
|
if(p->wakeEnable && p->wakeDelay < delay)
|
||
|
delay = p->wakeDelay;
|
||
|
|
||
|
if(delay == ~0l){
|
||
|
fail();
|
||
|
}
|
||
|
if(delay != 0){
|
||
|
dump(time);
|
||
|
}
|
||
|
for(TimeProcess* p : timeProcesses) {
|
||
|
p->wakeDelay -= delay;
|
||
|
if(p->wakeDelay == 0){
|
||
|
p->wakeEnable = false;
|
||
|
p->tick();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
top->eval();
|
||
|
for(auto* p : checkProcesses) p->tick(time);
|
||
|
|
||
|
if(delay != 0){
|
||
|
if(time - tickLastSimTime > 1000*400000 || time - tickLastSimTime > 1.0*speedFactor/timeToSec){
|
||
|
struct timespec end_time;
|
||
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_time);
|
||
|
uint64_t diffInNanos = end_time.tv_sec*1e9 + end_time.tv_nsec - tick_time.tv_sec*1e9 - tick_time.tv_nsec;
|
||
|
tick_time = end_time;
|
||
|
double dt = diffInNanos*1e-9;
|
||
|
#ifdef PRINT_PERF
|
||
|
printf("Simulation speed : %f ms/realTime\n",(time - tickLastSimTime)/dt*timeToSec*1e3);
|
||
|
#endif
|
||
|
tickLastSimTime = time;
|
||
|
}
|
||
|
time += delay;
|
||
|
while(allowedTime < delay){
|
||
|
struct timespec end_time;
|
||
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_time);
|
||
|
uint64_t diffInNanos = end_time.tv_sec*1e9 + end_time.tv_nsec - start_time.tv_sec*1e9 - start_time.tv_nsec;
|
||
|
start_time = end_time;
|
||
|
double dt = diffInNanos*1e-9;
|
||
|
allowedTime += dt*speedFactor/timeToSec;
|
||
|
if(allowedTime > 0.01*speedFactor/timeToSec)
|
||
|
allowedTime = 0.01*speedFactor/timeToSec;
|
||
|
|
||
|
}
|
||
|
allowedTime-=delay;
|
||
|
|
||
|
flushCounter++;
|
||
|
if(flushCounter > 100000){
|
||
|
#ifdef TRACE
|
||
|
tfp->flush();
|
||
|
//printf("flush\n");
|
||
|
#endif
|
||
|
flushCounter = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (Verilated::gotFinish())
|
||
|
exit(0);
|
||
|
}
|
||
|
cout << "timeout" << endl;
|
||
|
fail();
|
||
|
} catch (const success e) {
|
||
|
cout <<"SUCCESS " << name << endl;
|
||
|
} catch (const std::exception& e) {
|
||
|
cout << "FAIL " << name << endl;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
dump(time);
|
||
|
dump(time+10);
|
||
|
#ifdef TRACE
|
||
|
tfp->close();
|
||
|
#endif
|
||
|
return this;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|