aboutsummaryrefslogtreecommitdiff
path: root/VexRiscv/src/test/cpp/common
diff options
context:
space:
mode:
Diffstat (limited to 'VexRiscv/src/test/cpp/common')
-rw-r--r--VexRiscv/src/test/cpp/common/framework.h287
-rw-r--r--VexRiscv/src/test/cpp/common/jtag.h177
-rw-r--r--VexRiscv/src/test/cpp/common/uart.h126
3 files changed, 590 insertions, 0 deletions
diff --git a/VexRiscv/src/test/cpp/common/framework.h b/VexRiscv/src/test/cpp/common/framework.h
new file mode 100644
index 0000000..ed419ad
--- /dev/null
+++ b/VexRiscv/src/test/cpp/common/framework.h
@@ -0,0 +1,287 @@
+
+#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 { };
+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(double timeout = 1e6){
+
+ // 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(time*timeToSec > timeout){
+ printf("Simulation timeout triggered (%f)\n", time*timeToSec);
+ fail();
+ }
+ 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;
+ }
+};
+
+
diff --git a/VexRiscv/src/test/cpp/common/jtag.h b/VexRiscv/src/test/cpp/common/jtag.h
new file mode 100644
index 0000000..868c745
--- /dev/null
+++ b/VexRiscv/src/test/cpp/common/jtag.h
@@ -0,0 +1,177 @@
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <netinet/tcp.h>
+
+/** Returns true on success, or false if there was an error */
+bool SetSocketBlockingEnabled(int fd, bool blocking)
+{
+ if (fd < 0) return false;
+
+#ifdef WIN32
+ unsigned long mode = blocking ? 0 : 1;
+ return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? true : false;
+#else
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0) return false;
+ flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
+ return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
+#endif
+}
+
+class Jtag : public TimeProcess{
+public:
+ CData *tms, *tdi, *tdo, *tck;
+ enum State {reset};
+ uint32_t state;
+
+ int serverSocket, clientHandle;
+ struct sockaddr_in serverAddr;
+ struct sockaddr_storage serverStorage;
+ socklen_t addr_size;
+ uint64_t tooglePeriod;
+// char buffer[1024];
+
+ Jtag(CData *tms, CData *tdi, CData *tdo, CData* tck,uint64_t period){
+ this->tms = tms;
+ this->tdi = tdi;
+ this->tdo = tdo;
+ this->tck = tck;
+ this->tooglePeriod = period/2;
+ *tms = 0;
+ *tdi = 0;
+ *tdo = 0;
+ *tck = 0;
+ state = 0;
+ schedule(0);
+
+ //---- Create the socket. The three arguments are: ----//
+ // 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) //
+ serverSocket = socket(PF_INET, SOCK_STREAM, 0);
+ assert(serverSocket != -1);
+ int flag = 1;
+ setsockopt( serverSocket, /* socket affected */
+ IPPROTO_TCP, /* set option at TCP level */
+ TCP_NODELAY, /* name of option */
+ (char *) &flag, /* the cast is historical
+ cruft */
+ sizeof(int)); /* length of option value */
+
+ /*int a = 0xFFF;
+ if (setsockopt(serverSocket, SOL_SOCKET, SO_RCVBUF, &a, sizeof(int)) == -1) {
+ fprintf(stderr, "Error setting socket opts: %s\n", strerror(errno));
+ }
+ a = 0xFFFFFF;
+ if (setsockopt(serverSocket, SOL_SOCKET, SO_SNDBUF, &a, sizeof(int)) == -1) {
+ fprintf(stderr, "Error setting socket opts: %s\n", strerror(errno));
+ }*/
+
+ SetSocketBlockingEnabled(serverSocket,0);
+
+
+ //---- Configure settings of the server address struct ----//
+ // Address family = Internet //
+ serverAddr.sin_family = AF_INET;
+ serverAddr.sin_port = htons(7894);
+ serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
+
+ //---- Bind the address struct to the socket ----//
+ bind(serverSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
+
+ //---- Listen on the socket, with 5 max connection requests queued ----//
+ listen(serverSocket,1);
+
+ //---- Accept call creates a new socket for the incoming connection ----//
+ addr_size = sizeof serverStorage;
+ clientHandle = -1;
+
+ }
+ void connectionReset(){
+ printf("CONNECTION RESET\n");
+ shutdown(clientHandle,SHUT_RDWR);
+ clientHandle = -1;
+ }
+
+
+ virtual ~Jtag(){
+ if(clientHandle != -1) {
+ shutdown(clientHandle,SHUT_RDWR);
+ usleep(100);
+ }
+ if(serverSocket != -1) {
+ close(serverSocket);
+ usleep(100);
+ }
+ }
+
+ uint32_t selfSleep = 0;
+ uint32_t checkNewConnectionsTimer = 0;
+ uint8_t rxBuffer[100];
+ int32_t rxBufferSize = 0;
+ int32_t rxBufferRemaining = 0;
+ virtual void tick(){
+ checkNewConnectionsTimer++;
+ if(checkNewConnectionsTimer == 5000){
+ checkNewConnectionsTimer = 0;
+ int newclientHandle = accept(serverSocket, (struct sockaddr *) &serverStorage, &addr_size);
+ if(newclientHandle != -1){
+ if(clientHandle != -1){
+ connectionReset();
+ }
+ clientHandle = newclientHandle;
+ printf("CONNECTED\n");
+ }
+ else{
+ if(clientHandle == -1)
+ selfSleep = 1000;
+ }
+ }
+ if(selfSleep)
+ selfSleep--;
+ else{
+ if(clientHandle != -1){
+ uint8_t buffer;
+ int n;
+
+ if(rxBufferRemaining == 0){
+ if(ioctl(clientHandle,FIONREAD,&n) != 0)
+ connectionReset();
+ else if(n >= 1){
+ rxBufferSize = read(clientHandle,&rxBuffer,100);
+ if(rxBufferSize < 0){
+ connectionReset();
+ }else {
+ rxBufferRemaining = rxBufferSize;
+ }
+ }else {
+ selfSleep = 30;
+ }
+ }
+
+ if(rxBufferRemaining != 0){
+ uint8_t buffer = rxBuffer[rxBufferSize - (rxBufferRemaining--)];
+ *tms = (buffer & 1) != 0;
+ *tdi = (buffer & 2) != 0;
+ *tck = (buffer & 8) != 0;
+ if(buffer & 4){
+ buffer = (*tdo != 0);
+ //printf("TDO=%d\n",buffer);
+ if(-1 == send(clientHandle,&buffer,1,0))
+ connectionReset();
+ }else {
+
+ // printf("\n");
+ }
+ }
+ }
+ }
+ schedule(tooglePeriod);
+ }
+
+}; \ No newline at end of file
diff --git a/VexRiscv/src/test/cpp/common/uart.h b/VexRiscv/src/test/cpp/common/uart.h
new file mode 100644
index 0000000..05021ca
--- /dev/null
+++ b/VexRiscv/src/test/cpp/common/uart.h
@@ -0,0 +1,126 @@
+
+
+
+class UartRx : public TimeProcess{
+public:
+
+ CData *rx;
+ uint32_t uartTimeRate;
+ UartRx(CData *rx, uint32_t uartTimeRate){
+ this->rx = rx;
+ this->uartTimeRate = uartTimeRate;
+ schedule(uartTimeRate);
+ }
+
+ enum State {START, DATA, STOP};
+ State state = START;
+ char data;
+ uint32_t counter;
+
+
+ virtual void tick(){
+ switch(state){
+ case START:
+ if(*rx == 0){
+ state = DATA;
+ counter = 0;
+ data = 0;
+ schedule(uartTimeRate*5/4);
+ } else {
+ schedule(uartTimeRate/4);
+ }
+ break;
+ case DATA:
+ data |= (*rx) << counter++;
+ if(counter == 8){
+ state = STOP;
+ }
+ schedule(uartTimeRate);
+ break;
+ case STOP:
+ if(*rx){
+ cout << data << flush;
+ } else {
+ cout << "UART RX FRAME ERROR at " << time << endl;
+ }
+
+ schedule(uartTimeRate/4);
+ state = START;
+ break;
+ }
+ }
+};
+
+#include<pthread.h>
+#include <mutex>
+#include <queue>
+
+class UartTx : public TimeProcess{
+public:
+
+ CData *tx;
+ uint32_t uartTimeRate;
+
+ enum State {START, DATA, STOP};
+ State state = START;
+ char data;
+ uint32_t counter;
+ pthread_t inputThreadId;
+ queue<uint8_t> inputsQueue;
+ mutex inputsMutex;
+
+ UartTx(CData *tx, uint32_t uartTimeRate){
+ this->tx = tx;
+ this->uartTimeRate = uartTimeRate;
+ schedule(uartTimeRate);
+ pthread_create(&inputThreadId, NULL, &inputThreadWrapper, this);
+ *tx = 1;
+ }
+
+ static void* inputThreadWrapper(void *uartTx){
+ ((UartTx*)uartTx)->inputThread();
+ return NULL;
+ }
+
+ void inputThread(){
+ while(1){
+ uint8_t c = getchar();
+ inputsMutex.lock();
+ inputsQueue.push(c);
+ inputsMutex.unlock();
+ }
+ }
+
+ virtual void tick(){
+ switch(state){
+ case START:
+ inputsMutex.lock();
+ if(!inputsQueue.empty()){
+ data = inputsQueue.front();
+ inputsQueue.pop();
+ inputsMutex.unlock();
+ state = DATA;
+ counter = 0;
+ *tx = 0;
+ schedule(uartTimeRate);
+ } else {
+ inputsMutex.unlock();
+ schedule(uartTimeRate*50);
+ }
+ break;
+ case DATA:
+ *tx = (data >> counter) & 1;
+ counter++;
+ if(counter == 8){
+ state = STOP;
+ }
+ schedule(uartTimeRate);
+ break;
+ case STOP:
+ *tx = 1;
+ schedule(uartTimeRate);
+ state = START;
+ break;
+ }
+ }
+};