aboutsummaryrefslogtreecommitdiffstats
path: root/sources/api
diff options
context:
space:
mode:
authorAtaberk <olgunataberk@gmail.com>2022-09-25 17:22:03 +0200
committerAtaberk <olgunataberk@gmail.com>2022-09-25 17:22:03 +0200
commitdc0b3db1b4f1895a07e5fe280ee3790e87f97b9f (patch)
treeb47203aa281bdd959def4451c84d310cd9cf2e12 /sources/api
downloaddram-bender-dc0b3db1b4f1895a07e5fe280ee3790e87f97b9f.tar.gz
Initial commit
Diffstat (limited to 'sources/api')
-rw-r--r--sources/api/.gitignore2
-rw-r--r--sources/api/board.cpp132
-rw-r--r--sources/api/board.h35
-rw-r--r--sources/api/instruction.cpp586
-rw-r--r--sources/api/instruction.h200
-rw-r--r--sources/api/lexyacc/Makefile29
-rw-r--r--sources/api/lexyacc/example.smc102
-rw-r--r--sources/api/lexyacc/smc.l77
-rw-r--r--sources/api/lexyacc/smc.y427
-rw-r--r--sources/api/platform.cpp296
-rw-r--r--sources/api/platform.h83
-rw-r--r--sources/api/prog.cpp675
-rw-r--r--sources/api/prog.h120
-rw-r--r--sources/api/pySoftMC/.gitignore1
-rw-r--r--sources/api/pySoftMC/Makefile36
-rw-r--r--sources/api/pySoftMC/example.py153
-rw-r--r--sources/api/pySoftMC/pySoftMC.cpp95
17 files changed, 3049 insertions, 0 deletions
diff --git a/sources/api/.gitignore b/sources/api/.gitignore
new file mode 100644
index 0000000..be60ba4
--- /dev/null
+++ b/sources/api/.gitignore
@@ -0,0 +1,2 @@
+*.o
+.vscode/ \ No newline at end of file
diff --git a/sources/api/board.cpp b/sources/api/board.cpp
new file mode 100644
index 0000000..db990ca
--- /dev/null
+++ b/sources/api/board.cpp
@@ -0,0 +1,132 @@
+#include "board.h"
+#include <unistd.h>
+#include <fstream>
+#include <iostream>
+#include <cassert>
+#include <fcntl.h>
+#include <string.h>
+
+BoardInterface::BoardInterface(IFACE iface_type)
+{
+ this -> iface_type = iface_type;
+}
+BoardInterface::~BoardInterface()
+{
+ free(send_buf);
+ free(recv_buf);
+ close(to_card);
+ close(from_card);
+}
+
+int BoardInterface::init()
+{
+ switch(iface_type)
+ {
+ case IFACE::XDMA:
+ {
+ int fpga_fd = open(TO_FPGA_DEFAULT.c_str(), O_RDWR);
+ if(fpga_fd<0)
+ {
+ std::cerr << "Open to card failed!" << std::endl;
+ return 1;
+ }
+ else
+ std::cout << "Opened " << TO_FPGA_DEFAULT << " -> " << fpga_fd << std::endl;
+ to_card = fpga_fd;
+ fpga_fd = open(FROM_FPGA_DEFAULT.c_str(), O_RDWR);
+ if(fpga_fd<0)
+ {
+ std::cerr << "Open to host failed!" << std::endl;
+ return 1;
+ }
+ else
+ std::cout << "Opened " << FROM_FPGA_DEFAULT << " -> " << fpga_fd << std::endl;
+ from_card = fpga_fd;
+ // allocate page size aligned X page size regions to our buffers
+ if (posix_memalign((void **)&send_buf, 4096 /*alignment */ , SEND_BUF_SIZE + (4096-(SEND_BUF_SIZE % 4096))) != 0)
+ {
+ std::cerr << "Send buffer allocation failed!" << std::endl;
+ return 1;
+ }
+ if (posix_memalign((void **)&recv_buf, 4096 /*alignment */ , RECV_BUF_SIZE + (4096-(RECV_BUF_SIZE % 4096))))
+ {
+ std::cerr << "Receive buffer allocation failed!" << std::endl;
+ return 1;
+ }
+ if( (!send_buf) || (!recv_buf) )
+ {
+ std::cerr << "Buffers cannot be allocated!" << std::endl;
+ return 1;
+ }
+ return 0;
+ }
+ default:
+ std::cerr << "Unknown iface_type!" << std::endl;
+ return 1;
+ }
+}
+
+int BoardInterface::sendData(void* data, const uint size)
+{
+ switch(iface_type)
+ {
+ case IFACE::XDMA:
+ return xdma_send(data,size);
+ break;
+ default:
+ std::cerr << "Unknown iface_type!" << std::endl;
+ return 1;
+ }
+}
+
+int BoardInterface::recvData(void* buf, const uint size)
+{
+ switch(iface_type)
+ {
+ case IFACE::XDMA:
+ return xdma_recv(buf,size);
+ break;
+ default:
+ std::cerr << "Unknown iface_type!" << std::endl;
+ return 1;
+ }
+}
+
+int BoardInterface::xdma_send(void* data, const uint size)
+{
+ memcpy((char*)send_buf, (char*)data, size);
+
+ int fd = to_card;
+ ssize_t rc;
+ uint64_t count = 0;
+ char *buf = (char*) send_buf;
+
+ while (count < size) {
+ /* write data to file from memory buffer */
+ rc = write(fd, buf, size);
+ assert(rc == size || rc == 0);
+ count += rc;
+ }
+
+ // We wrote more than we supposed to
+ if (count != size)
+ return 1;
+
+ return 0;
+}
+
+int BoardInterface::xdma_recv(void* buf, const uint size)
+{
+ assert(size <= RECV_BUF_SIZE && "given read size is too large");
+
+ int fd = from_card;
+ uint64_t count = 0;
+ // Try to read from the card.
+ count = read(fd, (char*) recv_buf, size);
+
+ // We read more than we were supposed to
+ assert(count <= size);// || rc == 0);
+
+ memcpy(buf, (char*) recv_buf, count);
+ return count;
+}
diff --git a/sources/api/board.h b/sources/api/board.h
new file mode 100644
index 0000000..0935392
--- /dev/null
+++ b/sources/api/board.h
@@ -0,0 +1,35 @@
+#include <string>
+
+#ifndef BOARD_H
+#define BOARD_H
+/** This class defines how the host
+ * interfaces with the board.
+ */
+class BoardInterface{
+ const uint SEND_BUF_SIZE = 32*2048;
+ // send each instruction as a 256-bit packet
+ const uint RECV_BUF_SIZE = 1024*32;
+ const std::string TO_FPGA_DEFAULT = "/dev/xdma0_h2c_0";
+ const std::string FROM_FPGA_DEFAULT = "/dev/xdma0_c2h_0";
+public:
+ enum class IFACE {
+ XDMA = 0
+ };
+ BoardInterface(IFACE);
+ ~BoardInterface();
+ int init();
+ int sendData(void* data, const uint size);
+ int recvData(void* buf , const uint size);
+private:
+ IFACE iface_type;
+ // XDMA related constructs
+ int to_card;
+ int from_card;
+ void* send_buf;
+ void* recv_buf;
+ int xdma_send(void* data, const uint size);
+ int xdma_recv(void* buf, const uint size);
+ // end XDMA related constructs
+};
+
+#endif
diff --git a/sources/api/instruction.cpp b/sources/api/instruction.cpp
new file mode 100644
index 0000000..432188f
--- /dev/null
+++ b/sources/api/instruction.cpp
@@ -0,0 +1,586 @@
+#include "instruction.h"
+#include <assert.h>
+#include <stdio.h>
+
+Inst SMC_ADD(int rs1, int rs2, int rt)
+{
+ Inst fu_code = (uint64_t)__ADD << __FU_CODE;
+ Inst s_regs = (rs2 << __RS2) | rs1;
+ Inst t_reg = rt << __RT;
+
+ Inst inst = fu_code | s_regs | t_reg;
+
+ return inst;
+}
+Inst SMC_ADDI(int rs1, uint32_t imd, int rt)
+{
+ Inst fu_code = (uint64_t)__ADDI << __FU_CODE;
+ Inst s_reg = rs1;
+ Inst imd1 = imd << __IMD1;
+ Inst t_reg = rt << __RT;
+
+ Inst inst = fu_code | s_reg | imd1 | t_reg;
+
+ return inst;
+}
+Inst SMC_SUB(int rs1, int rs2, int rt)
+{
+ Inst fu_code = (uint64_t)__SUB << __FU_CODE;
+ Inst s_regs = (rs2 << __RS2) | rs1;
+ Inst t_reg = rt << __RT;
+
+ Inst inst = fu_code | s_regs | t_reg;
+
+ return inst;
+}
+Inst SMC_SUBI(int rs1, uint32_t imd, int rt)
+{
+ Inst fu_code = (uint64_t)__SUBI << __FU_CODE;
+ Inst s_reg = rs1;
+ Inst imd1 = imd << __IMD1;
+ Inst t_reg = rt << __RT;
+
+ Inst inst = fu_code | s_reg | imd1 | t_reg;
+
+ return inst;
+}
+Inst SMC_LI(uint32_t imd, int rt)
+{
+ Inst fu_code = (uint64_t)__LI << __FU_CODE;
+ Inst imd1 = ((uint32_t)(imd<<16)>>16) << __IMD1;
+ Inst imd2 = (uint64_t)(((uint32_t)imd)>>16) << __IMD3;
+ Inst t_reg = rt << __RT;
+
+ Inst inst = fu_code | imd1 | imd2 | t_reg;
+
+ return inst;
+}
+Inst SMC_MV(int rs1, int rt)
+{
+ Inst fu_code = (uint64_t)__MV << __FU_CODE;
+ Inst s_reg = rs1;
+ Inst t_reg = rt << __RT;
+
+ Inst inst = fu_code | s_reg | t_reg;
+
+ return inst;
+}
+Inst SMC_SRC(int rs1, int rt)
+{
+ Inst fu_code = (uint64_t)__SRC << __FU_CODE;
+ Inst s_reg = rs1;
+ Inst t_reg = rt << __RT;
+
+ Inst inst = fu_code | s_reg | t_reg;
+
+ return inst;
+}
+Inst SMC_LDWD(int rs1, int off)
+{
+ Inst fu_code = (uint64_t)__LDWD << __FU_CODE;
+ Inst s_reg = rs1;
+ Inst offset = off << __RT;
+
+ Inst inst = fu_code | s_reg | offset;
+
+ return inst;
+}
+Inst SMC_LDPC(PC_TYPE pc_type, int rt)
+{
+ Inst fu_code = (uint64_t)__LDPC << __FU_CODE;
+ Inst pc_reg = 2;
+ Inst t_reg = rt << __RT;
+ switch(pc_type){
+ case PC_TYPE::WRITE:
+ pc_reg = 0;
+ break;
+ case PC_TYPE::READ:
+ pc_reg = 1;
+ break;
+ case PC_TYPE::PRE:
+ pc_reg = 2;
+ break;
+ case PC_TYPE::ACT:
+ pc_reg = 3;
+ break;
+ case PC_TYPE::ZQ:
+ pc_reg = 4;
+ break;
+ case PC_TYPE::REF:
+ pc_reg = 5;
+ break;
+ case PC_TYPE::CYC:
+ pc_reg = 6;
+ break;
+ }
+
+ Inst inst = fu_code | pc_reg | t_reg;
+
+ return inst;
+}
+Inst SMC_BL(int rs1, int rs2, int tgt)
+{
+ Inst op_code = (uint64_t)0x1 << __IS_BR;
+ Inst fu_code = (uint64_t)__BL << __FU_CODE;
+ Inst s_regs = (rs2 << __RS2) | rs1;
+ Inst target = (uint64_t)tgt << __BR_TGT;
+
+ Inst inst = op_code | fu_code | s_regs | target;
+
+ return inst;
+}
+Inst SMC_BEQ(int rs1, int rs2, int tgt)
+{
+ Inst op_code = (uint64_t)0x1 << __IS_BR;
+ Inst fu_code = (uint64_t)__BEQ << __FU_CODE;
+ Inst s_regs = (rs2 << __RS2) | rs1;
+ Inst target = (uint64_t)tgt << __BR_TGT;
+
+ Inst inst = op_code | fu_code | s_regs | target;
+
+ return inst;
+}
+Inst SMC_JUMP(int tgt)
+{
+ Inst op_code = (uint64_t)0x1 << __IS_BR;
+ Inst fu_code = (uint64_t)__JUMP << __FU_CODE;
+ Inst target = tgt;
+
+ Inst inst = op_code | fu_code | target;
+
+ return inst;
+}
+Inst SMC_SLEEP(uint32_t samt)
+{
+ assert(samt > 2 && "Cannot sleep for less than 3 cycles.");
+ Inst op_code = (uint64_t)0x1 << __IS_BR;
+ Inst fu_code = (uint64_t)__SLEEP << __FU_CODE;
+
+ samt -= 2;
+
+ Inst inst = op_code | fu_code | samt;
+
+ return inst;
+}
+Inst SMC_LD(int rb, int offset, int rt)
+{
+ Inst op_code = (uint64_t)0x1 << __IS_MEM;
+ Inst fu_code = (uint64_t)__LD << __FU_CODE;
+ Inst s_reg = rb;
+ Inst imd1 = offset << __IMD1;
+ Inst t_reg = rt << __RT;
+
+ Inst inst = op_code | fu_code | s_reg | imd1 | t_reg;
+
+ return inst;
+}
+Inst SMC_ST(int rb, int offset, int rv)
+{
+ Inst op_code = (uint64_t)0x1 << __IS_MEM;
+ Inst fu_code = (uint64_t)__ST << __FU_CODE;
+ Inst b_reg = rb;
+ Inst imd1 = offset << __IMD1;
+ Inst v_reg = rv << __RT; // We cannot have imd1 and rs2 present simultaneously
+
+ Inst inst = op_code | fu_code | b_reg | imd1 | v_reg;
+
+ return inst;
+}
+Inst SMC_AND(int rs1, int rs2, int rt)
+{
+ Inst op_code = (uint64_t)0x1 << __IS_BW;
+ Inst fu_code = (uint64_t)__AND << __FU_CODE;
+ Inst s_regs = (rs2 << __RS2) | rs1;
+ Inst t_reg = rt << __RT;
+
+ Inst inst = op_code | fu_code | s_regs | t_reg;
+
+ return inst;
+}
+Inst SMC_OR(int rs1, int rs2, int rt)
+{
+ Inst op_code = (uint64_t)0x1 << __IS_BW;
+ Inst fu_code = (uint64_t)__OR << __FU_CODE;
+ Inst s_regs = (rs2 << __RS2) | rs1;
+ Inst t_reg = rt << __RT;
+
+ Inst inst = op_code | fu_code | s_regs | t_reg;
+
+ return inst;
+}
+Inst SMC_XOR(int rs1, int rs2, int rt)
+{
+ Inst op_code = (uint64_t)0x1 << __IS_BW;
+ Inst fu_code = (uint64_t)__XOR << __FU_CODE;
+ Inst s_regs = (rs2 << __RS2) | rs1;
+ Inst t_reg = rt << __RT;
+
+ Inst inst = op_code | fu_code | s_regs | t_reg;
+
+ return inst;
+}
+Inst SMC_END()
+{
+ return 0;
+}
+Inst SMC_INFO(int rdcnt)
+{
+ Inst op_code = (uint64_t)0x1 << __IS_MISC;
+ Inst fu_code = (uint64_t)__INFO << __FU_CODE;
+ return op_code | fu_code | (uint64_t) rdcnt;
+}
+Mininst SMC_WRITE(int bar, int ibar, int car, int icar, int BL4, int ap)
+{
+ Mininst fu_code = ((uint64_t)__WRITE) << __DDR_CMD;
+ Mininst i_bar = bar;
+ Mininst i_car = car << __DDR_CAR;
+ Mininst i_ibar = ibar << __DDR_IBAR;
+ Mininst i_icar = icar << __DDR_ICAR;
+ Mininst i_BL4 = BL4 << __DDR_BL4;
+ Mininst i_ap = ap <<__DDR_AP;
+
+ Mininst inst = fu_code | i_bar | i_car | i_ibar |
+ i_icar | i_BL4 | i_ap;
+
+ return inst;
+}
+Mininst SMC_READ(int bar, int ibar, int car, int icar, int BL4, int ap)
+{
+ Mininst fu_code = ((uint64_t)__READ) << __DDR_CMD;
+ Mininst i_bar = bar;
+ Mininst i_car = car << __DDR_CAR;
+ Mininst i_ibar = ibar << __DDR_IBAR;
+ Mininst i_icar = icar << __DDR_ICAR;
+ Mininst i_BL4 = BL4 << __DDR_BL4;
+ Mininst i_ap = ap <<__DDR_AP;
+
+ Mininst inst = fu_code | i_bar | i_car | i_ibar |
+ i_icar | i_BL4 | i_ap;
+
+ return inst;
+}
+Mininst SMC_PRE(int bar, int ibar, int pall)
+{
+ Mininst fu_code = ((uint64_t)__PRE) << __DDR_CMD;
+ Mininst i_bar = bar;
+ Mininst i_ibar = ibar << __DDR_IBAR;
+ Mininst i_pall = pall << __DDR_PALL;
+
+ Mininst inst = fu_code | i_bar | i_ibar | i_pall;
+
+ return inst;
+}
+Mininst SMC_ACT(int bar, int ibar, int rar, int irar)
+{
+ Mininst fu_code = ((uint64_t)__ACT) << __DDR_CMD;
+ Mininst i_bar = bar;
+ Mininst i_rar = rar << __DDR_RAR;
+ Mininst i_ibar = ibar << __DDR_IBAR;
+ Mininst i_irar = irar << __DDR_IRAR;
+
+ Mininst inst = fu_code | i_bar | i_rar | i_ibar | i_irar;
+
+ return inst;
+}
+Mininst SMC_ZQ()
+{
+ Mininst fu_code = ((uint64_t)__ZQ) << __DDR_CMD;
+
+ return fu_code;
+}
+Mininst SMC_REF()
+{
+ Mininst fu_code = ((uint64_t)__REF) << __DDR_CMD;
+
+ return fu_code;
+}
+Mininst SMC_NOP()
+{
+ Mininst fu_code = ((uint64_t)__NOP) << __DDR_CMD;
+
+ return fu_code;
+}
+Inst SMC_SRE()
+{
+ Inst op_code = (uint64_t) 0x1 << 56;
+ Inst fu_code = (uint64_t)__SRE << __FU_CODE;
+
+ return fu_code | op_code;
+}
+Inst SMC_SRX()
+{
+ Inst op_code = (uint64_t) 0x1 << 56;
+ Inst fu_code = (uint64_t)__SRX << __FU_CODE;
+
+ return fu_code | op_code;
+}
+Inst __pack_mininsts(Mininst i1, Mininst i2, Mininst i3, Mininst i4)
+{
+ return (uint64_t) i4 << 48 |
+ (uint64_t) i3 << 32 |
+ (uint64_t) i2 << 16 |
+ i1 ;
+}
+
+int is_conditional(Inst i)
+{
+ uint64_t fcode = (i >> __FU_CODE) & 0x7ff;
+ switch (fcode) {
+ case 0:
+ return 1;
+ case 1:
+ return 1;
+ }
+ return 0;
+}
+
+int is_branch(Inst i)
+{
+ uint64_t fcode = (i >> __OP_CODE);
+ //printf("%ld\n",fcode);
+ switch (fcode) {
+ case 8:
+ return 1;
+ }
+ return 0;
+}
+
+int is_ddr(Inst i)
+{
+ uint64_t fcode = i >> __IS_DDR;
+ return fcode == 1;
+}
+
+int is_load(Inst i)
+{
+ uint64_t fcode = i >> __FU_CODE;
+ return fcode == 4096;
+}
+
+int is_sleep(Inst i)
+{
+ uint64_t fcode = i >> __FU_CODE;
+ return fcode == 19;
+}
+
+int is_ddr_read(Inst inst)
+{
+ int ctr = 0;
+ for (size_t i = 0 ; i < 4 ; i++)
+ {
+ Mininst min = inst >> (i*16);
+ ctr += (min >> __DDR_CMD) == (Mininst) __READ;
+ }
+ return ctr;
+}
+
+void decode_inst(Inst inst)
+{
+ if(is_ddr(inst))
+ {
+ for(int i = 0 ; i < 4 ; i++)
+ {
+ Mininst mini = (inst >> i*16);
+ decode_ddr(mini);
+ if (i<3) printf(" : ");
+ }
+ }
+ else
+ {
+ int fu_code = inst >> __FU_CODE;
+ int fc_mask = 0x7ff;
+ fu_code = fu_code & fc_mask;
+ int op_code = inst >> __OP_CODE;
+ int is_br = op_code == 0x8;
+ int is_mem = op_code == 0x2;
+ int is_bw = op_code == 0x1;
+ int is_misc = op_code == 0x4;
+ int is_arit = op_code == 0x0;
+ uint64_t rid_mask = 0xf;
+ uint64_t imd_mask = 0xffff;
+ uint64_t br_tgt_mask = 0x7ffff;
+ uint64_t jmp_tgt_mask = 0x7ffffff;
+ int rs1 = (inst >> __RS1) & rid_mask;
+ int rs2 = (inst >> __RS2) & rid_mask;
+ int rt = (inst >> __RT) & rid_mask;
+ int imd1 = (inst >> __IMD1) & imd_mask;
+ int imd3 = (inst >> __IMD3) & imd_mask;
+ int bt = (inst >> __BR_TGT) & br_tgt_mask;
+ int jt = (inst >> __J_TGT) & jmp_tgt_mask;
+ int samt = (uint32_t)inst;
+ uint64_t imd_concat = ((uint64_t)imd3 << 16) + imd1;
+ if (is_arit)
+ {
+ switch (fu_code)
+ {
+ case __ADD:
+ if(inst == 0)
+ printf("END");
+ else
+ printf("ADD r%d r%d r%d", rt, rs1, rs2);
+ break;
+ case __ADDI:
+ printf("ADDI r%d r%d %d", rt, rs1, imd1);
+ break;
+ case __SUB:
+ printf("SUB r%d r%d r%d", rt, rs1, rs2);
+ break;
+ case __SUBI:
+ printf("SUBI r%d r%d %d", rt, rs1, imd1);
+ break;
+ case __MV:
+ printf("MOV r%d r%d", rt, rs1);
+ break;
+ case __LI:
+ printf("LI r%d %ld", rt, imd_concat);
+ break;
+ case __SRC:
+ printf("SRC r%d r%d", rt, rs1);
+ break;
+ case __LDWD:
+ printf("LDWD %d r%d", rt, rs1);
+ break;
+ case __LDPC:
+ switch(rs1){
+ case 0:
+ printf("LDPC r%d WRITE_COUNTER", rt);
+ break;
+ case 1:
+ printf("LDPC r%d READ_COUNTER", rt);
+ break;
+ case 2:
+ printf("LDPC r%d PRE_COUNTER", rt);
+ break;
+ case 3:
+ printf("LDPC r%d ACT_COUNTER", rt);
+ break;
+ case 4:
+ printf("LDPC r%d ZQ_COUNTER", rt);
+ break;
+ case 5:
+ printf("LDPC r%d REF_COUNTER", rt);
+ break;
+ case 6:
+ printf("LDPC r%d TOTAL_CYCLE", rt);
+ break;
+ }
+ break;
+ case __SRE:
+ printf("SRE");
+ break;
+ case __SRX:
+ printf("SRX");
+ break;
+ }
+ } else if (is_br)
+ {
+ switch (fu_code)
+ {
+ case __BL:
+ printf("BL PC:%d r%d r%d", bt, rs1, rs2);
+ break;
+ case __BEQ:
+ printf("BEQ PC:0x%x r%d r%d", bt, rs1, rs2);
+ break;
+ case __JUMP:
+ printf("JUMP PC:%d", jt);
+ break;
+ case __SLEEP:
+ printf("SLEEP %d cycles", samt);
+ break;
+ }
+ } else if (is_misc)
+ {
+ switch (fu_code)
+ {
+ case __INFO:
+ printf("Auto-generated instruction.");
+ break;
+ }
+ } else if (is_mem)
+ {
+ switch (fu_code)
+ {
+ case __LD:
+ printf("LD r%d [r%d]%d", rt, rs1, imd1);
+ break;
+ case __ST:
+ printf("ST [r%d]%d r%d", rs1, imd1, rt);
+ break;
+ }
+ } else if (is_bw)
+ {
+ switch (fu_code)
+ {
+ case __AND:
+ printf("AND r%d r%d r%d", rt, rs1, rs2);
+ break;
+ case __OR:
+ printf("OR r%d r%d r%d", rt, rs1, rs2);
+ break;
+ case __XOR:
+ printf("XOR r%d r%d r%d", rt, rs1, rs2);
+ break;
+ }
+ }
+ }
+}
+
+void decode_ddr(Mininst i)
+{
+ int ddr_code = i >> __DDR_CMD;
+ uint64_t rid_mask = 0xf;
+ int car = (i >> __DDR_CAR) & rid_mask;
+ int bar = (i >> __DDR_BAR) & rid_mask;
+ int rar = (i >> __DDR_RAR) & rid_mask;
+ int icar = (i >> __DDR_ICAR) & 0x1;
+ int ibar = (i >> __DDR_IBAR) & 0x1;
+ int irar = (i >> __DDR_IRAR) & 0x1;
+ int pall = (i >> __DDR_PALL) & 0x1;
+ int ap = (i >> __DDR_AP) & 0x1;
+ int bc = (i >> __DDR_BL4) & 0x1;
+
+ switch(ddr_code)
+ {
+ case __WRITE:
+ printf("WR r%d%s r%d%s%s%s", bar, ibar?"++":"", car, icar?"++":"",
+ ap?" AP":"",bc?" BC":"");
+ break;
+ case __READ:
+ printf("RD r%d%s r%d%s%s%s", bar, ibar?"++":"", car, icar?"++":"",
+ ap?" AP":"",bc?" BC":"");
+ break;
+ case __PRE:
+ printf("PRE r%d%s%s", bar, ibar?"++":"", pall?" PALL":"");
+ break;
+ case __ACT:
+ printf("ACT r%d%s r%d%s", bar, ibar?"++":"", rar, irar?"++":"");
+ break;
+ case __ZQ:
+ printf("ZQ");
+ break;
+ case __REF:
+ printf("REF");
+ break;
+ case __NOP:
+ printf("NOP");
+ break;
+ }
+}
+
+void print_bits(size_t const size, void const * const ptr)
+{
+ unsigned char *b = (unsigned char*) ptr;
+ unsigned char byte;
+ int i, j;
+
+ for (i=size-1;i>=0;i--)
+ {
+ for (j=7;j>=0;j--)
+ {
+ byte = (b[i] >> j) & 1;
+ printf("%u", byte);
+ }
+ }
+ printf("\n");
+}
diff --git a/sources/api/instruction.h b/sources/api/instruction.h
new file mode 100644
index 0000000..1be8691
--- /dev/null
+++ b/sources/api/instruction.h
@@ -0,0 +1,200 @@
+#ifndef INSTRUCTION_H
+#define INSTRUCTION_H
+
+#include <stdint.h>
+#include <stdlib.h>
+// SoftMC decode
+#define __OP_CODE 59
+#define __FU_CODE 48
+#define __IS_BR 62
+#define __IS_DDR 63
+#define __IS_MISC 61
+#define __IS_MEM 60
+#define __IS_BW 59
+// EXE decode
+#define __RS1 0
+#define __RS2 4
+#define __RT 20
+#define __IMD1 4
+#define __IMD2 0
+#define __IMD3 24
+#define __BR_TGT 8
+#define __J_TGT 0
+#define __SLP_AMT 0
+// DDR decode
+#define __DDR_CMD 12
+#define __DDR_CAR 4
+#define __DDR_BAR 0
+#define __DDR_RAR 4
+#define __DDR_IBAR 10
+#define __DDR_ICAR 11
+#define __DDR_IRAR 11
+#define __DDR_PALL 11
+#define __DDR_AP 9
+#define __DDR_BL4 8
+// EXE function codes
+#define __ADD 0
+#define __ADDI 1
+#define __SUB 2
+#define __SUBI 3
+#define __MV 4
+#define __SRC 5
+#define __LI 6
+#define __LDWD 7
+#define __LDPC 8
+#define __SRE 0x100
+#define __SRX 0x101
+#define __BL 0
+#define __BEQ 1
+#define __JUMP 2
+#define __SLEEP 3
+#define __INFO 0 // Various information about upcoming block
+#define __AND 0
+#define __OR 1
+#define __XOR 2
+#define __LD 0
+#define __ST 1
+// DDR function codes
+#define __WRITE 8
+#define __READ 9
+#define __PRE 10
+#define __ACT 11
+#define __ZQ 12
+#define __REF 13
+#define __NOP 15
+
+// 64 bit instructions
+typedef uint64_t Inst;
+// 16 bit mini ddr-instructions
+typedef uint16_t Mininst;
+
+//Counter types for LDPC Inst
+enum class PC_TYPE { WRITE, READ, PRE, ACT, ZQ, REF, CYC };
+
+/**
+ * Load a word from memory into rt
+ * rt = [rb] + offset
+ * To handle structural hazards, the API adds a
+ * "buffer" NOP instruction following loads.
+ * @param rb register holding the base address
+ * @param offset memory address offset
+ * @param rt register to load the value with
+ */
+Inst SMC_LD(int rb, int offset, int rt);
+/**
+ * Store a word to memory
+ * [rb] + offset = rt
+ * @param rb register holding the base address
+ * @param offset memory address offset
+ * @param rv register holding the value to store
+ */
+Inst SMC_ST(int rb, int offset, int rv);
+
+Inst SMC_AND(int rs1, int rs2, int rt);
+Inst SMC_OR(int rs1, int rs2, int rt);
+Inst SMC_XOR(int rs1, int rs2, int rt);
+
+Inst SMC_ADD(int rs1, int rs2, int rt);
+Inst SMC_ADDI(int rs1, uint32_t imd, int rt);
+Inst SMC_SUB(int rs1, int rs2, int rt);
+Inst SMC_SUBI(int rs1, uint32_t imd, int rt);
+Inst SMC_LI(uint32_t imd, int rt);
+Inst SMC_MV(int rs1, int rt);
+/**
+ * Shift right circular, shift one bit to right and copy
+ * the rightmost bit to the leftmost bit of the result.
+ * @param rs1 register to shift
+ * @param rt register to load the shifted value into
+ */
+Inst SMC_SRC(int rs1, int rt);
+/**
+ * Move 32 bit data to wide register's specified offset
+ * @param rs1 source register where 32-bit data resides
+ * @param off 32-bit offset (e.g. 0 = bytes(0,4) - 5 = bytes(20,24))
+ */
+Inst SMC_LDWD(int rs1, int off);
+Inst SMC_LDPC(PC_TYPE pc_type, int rt);
+Inst SMC_BL(int rs1, int rs2, int tgt);
+Inst SMC_BEQ(int rs1, int rs2, int tgt);
+Inst SMC_JUMP(int tgt);
+Inst SMC_END();
+Inst SMC_INFO(int rdcnt);
+
+/**
+ * Wait for a specified amount of fabric cycles (6ns by default) before
+ * executing the next instruction
+ * @param samt how many cycles to wait for, must be greater than 2
+ */
+Inst SMC_SLEEP(uint32_t samt);
+
+/**
+ * Generate a DDR-WR command
+ * @param bar bank address register ID
+ * @param ibar increment BAR (BAR=BAR+BASR) after issuing the write
+ * @param car column address register ID
+ * @param icar increment CAR (CAR=CAR+CASR) after issuing the write
+ * @param BL4 burst-chop write
+ * @param ap auto-precharge after issuing write
+ */
+Mininst SMC_WRITE(int bar, int ibar, int car, int icar, int BL4, int ap);
+/**
+ * Generate a DDR-RD command
+ * @param bar bank address register ID
+ * @param ibar increment BAR (BAR=BAR+BASR) after issuing the read
+ * @param car column address register ID
+ * @param icar increment CAR (CAR=CAR+CASR) after issuing the read
+ * @param BL4 burst-chop read
+ * @param ap auto-precharge after issuing read
+ */
+Mininst SMC_READ(int bar, int ibar, int car, int icar, int BL4, int ap);
+/**
+ * Generate a DDR-PRE command
+ * @param bar bank address register ID
+ * @param ibar increment BAR (BAR=BAR+BASR) after issuing the write
+ * @param pall precharge all banks
+ */
+Mininst SMC_PRE(int bar, int ibar, int pall);
+/**
+ * Generate a DDR-RD command
+ * @param bar bank address register ID
+ * @param ibar increment BAR (BAR=BAR+BASR) after issuing the activate
+ * @param rar row address register ID
+ * @param irar increment RAR (RAR=RAR+RASR) after issuing the activate
+ */
+Mininst SMC_ACT(int bar, int ibar, int rar, int irar);
+/**
+ * Generate a zq-calibration command
+ */
+Mininst SMC_ZQ();
+/**
+ * Generate a refresh command
+ */
+Mininst SMC_REF();
+/**
+ * Generate a no-operation command
+ */
+Mininst SMC_NOP();
+/**
+ * Enters Self-Refresh Mode
+ */
+Inst SMC_SRE();
+/**
+ * Exits Self-Refresh Mode
+ */
+Inst SMC_SRX();
+/**
+ * Packs four DDR commands together
+ */
+Inst __pack_mininsts(Mininst i1, Mininst i2, Mininst i3, Mininst i4);
+
+int is_branch(Inst i);
+int is_conditional(Inst i);
+int is_load(Inst i);
+int is_ddr(Inst i);
+int is_sleep(Inst i);
+int is_ddr_read(Inst i);
+void decode_inst(Inst inst);
+void decode_ddr(Mininst i);
+void print_bits(size_t const size, void const * const ptr);
+
+#endif
diff --git a/sources/api/lexyacc/Makefile b/sources/api/lexyacc/Makefile
new file mode 100644
index 0000000..67a05fa
--- /dev/null
+++ b/sources/api/lexyacc/Makefile
@@ -0,0 +1,29 @@
+program_NAME := smc_parser
+program_CXX_SRCS := lex.yy.c y.tab.c $(wildcard ../*.c*)
+program_CXX_OBJS := ${program_CXX_SRCS:.cpp=.o}
+program_OBJS := $(program_CXX_OBJS)
+program_INCLUDE_DIRS := ../ ../../../boost-lib
+program_LIBRARY_DIRS :=
+program_LIBRARIES :=
+CPPFLAGS += -g -std=c++11 -pthread
+
+CPPFLAGS += $(foreach includedir,$(program_INCLUDE_DIRS),-I$(includedir))
+LDFLAGS += $(foreach librarydir,$(program_LIBRARY_DIRS),-L$(librarydir))
+LDFLAGS += $(foreach library,$(program_LIBRARIES),-l$(library))
+
+CC=g++
+
+.PHONY: all clean distclean
+
+all: $(program_NAME)
+
+$(program_NAME): $(program_OBJS) lex.yy.c
+ $(CC) $(CPPFLAGS) $(program_OBJS) -o $(program_NAME) $(LDFLAGS)
+
+lex.yy.c: y.tab.c smc.l
+ flex smc.l
+
+y.tab.c: smc.y
+ yacc -d smc.y
+
+distclean: clean
diff --git a/sources/api/lexyacc/example.smc b/sources/api/lexyacc/example.smc
new file mode 100644
index 0000000..f9cc4f6
--- /dev/null
+++ b/sources/api/lexyacc/example.smc
@@ -0,0 +1,102 @@
+// Initialize register ids
+// Similar to #define
+casr = 0
+basr = 1
+rasr = 2
+bar = 2
+rar = 3
+car = 4
+pattern_reg = 5
+no_row_reg = 6
+no_bank_reg = 7
+
+// Begin writing the program
+LI no_row_reg 32768 // Write 2^15 to no_row_Reg
+LI no_bank_reg 16 // write 16 to no_bank_reg
+
+// Set stride registers (not required if you
+// are not going to increment address registers
+// with ddr commands)
+LI casr 8 // set column address stride to 8
+LI basr 1 // set bas to 1
+LI rasr 1 // set ras to 1
+
+LI bar 0 // Initialize bank address register
+
+// Init pattern register, we are going to fill
+// dram with 0xffs.
+LI pattern_reg 0xffffffff
+
+// Load the data register with the pattern
+// second argument here is the word offset
+// we are writing to in the data register
+// e.g. LDWD 0 pattern reg fills the first four bytes
+
+LDWD 0 pattern_reg
+LDWD 1 pattern_reg
+LDWD 2 pattern_reg
+LDWD 3 pattern_reg
+LDWD 4 pattern_reg
+LDWD 5 pattern_reg
+LDWD 6 pattern_reg
+LDWD 7 pattern_reg
+LDWD 8 pattern_reg
+LDWD 9 pattern_reg
+LDWD 10 pattern_reg
+LDWD 11 pattern_reg
+LDWD 12 pattern_reg
+LDWD 13 pattern_reg
+LDWD 14 pattern_reg
+LDWD 15 pattern_reg
+
+BANK_BEGIN: // Add label to loop over banks
+LI rar 0 // Reload rar per bank loop
+
+ROW_BEGIN: // Add label to loop over rows
+LI car 0 // Reload car per row loop
+
+PRE bar // PREcharge bar, don't increment, no precharge-all
+WAIT 7 // Insert seven cycles of NOPs
+
+ACT bar rar // ACTivate bar, rar
+WAIT 7
+
+// This is a basic primitive that can be used
+// to replicate instructions-commands
+// this block replicates the WR command 128 times
+// and adds it to the program
+times 128 {
+ WR bar car++
+ WAIT 3
+}
+
+// Reload car, we are now going to read from DRAM
+LI car 0
+
+// Wait tWR, might not be precise.
+WAIT 8
+PRE bar
+WAIT 7
+
+ACT bar rar
+WAIT 7
+
+times 128 {
+ RD bar car++
+ WAIT 3
+}
+
+WAIT 8
+PRE bar
+
+// Increment rar per row loop
+ADDI rar rar 1
+// if rar < no_row_reg, execute the "row loop" once more
+BL ROW_BEGIN rar no_row_reg
+
+ADDI bar bar 1
+BL BANK_BEGIN bar no_bank_reg
+
+// Tell softmc that the program has ended
+// This is important!
+END \ No newline at end of file
diff --git a/sources/api/lexyacc/smc.l b/sources/api/lexyacc/smc.l
new file mode 100644
index 0000000..4d9e478
--- /dev/null
+++ b/sources/api/lexyacc/smc.l
@@ -0,0 +1,77 @@
+%{
+ #include <stdlib.h>
+ #include "instruction.h"
+ #include "y.tab.h"
+%}
+
+%%
+
+"//"[^\n]* ; /* Skip comments */
+[%\t ]+ ; /* Skip whitespace */
+
+0x[0-9a-f]+ {
+ yylval.lit = yytext+2;
+ return HEX_NUMBER;
+ }
+
+[0-9]+ {
+ yylval.num = atoi(yytext);
+ return DEC_NUMBER;
+ }
+
+r[0-9][0-9]? {
+ yylval.num = atoi(++yytext); /* Skip the first character? */
+ return REG_ID;
+ }
+
+"wait"|"WAIT" return WAIT;
+
+"add"|"ADD" return ADD;
+"addi"|"ADDI" return ADDI;
+"sub"|"SUB" return SUB;
+"subi"|"SUBI" return SUBI;
+"mov"|"MOV" return MOV;
+"src"|"SRC" return SRC;
+"li"|"LI" return LI;
+"ldwd"|"LDWD" return LDWD;
+
+"bl"|"BL" return BRL;
+"beq"|"beq" return BREQ;
+"jmp"|"JMP" return JMP;
+
+"wr"|"WR" return WR;
+"rd"|"RD" return RD;
+"act"|"ACT" return ACT;
+"pre"|"PRE" return PRE;
+"ref"|"REF" return REF;
+"zq"|"ZQ" return ZQ;
+"end"|"END" return END;
+
+
+"++" return INCREMENT;
+"AP"|"ap" return AUTO_PRECHARGE;
+"BC"|"bc" return BURST_CHOP;
+"PA"|"pa" return PRECHARGE_ALL;
+
+"times" return GENFOR;
+"ENDPROGRAM" return ENDPROGRAM;
+
+ [a-z_]+ {
+ char* var_name = (char*) malloc((yyleng+1)*sizeof(char));
+ strcpy(var_name, yytext);
+ yylval.lit = var_name;
+ return VARIABLE;
+ }
+
+ [A-Z_]+ {
+ char* lbl_name = (char*) malloc((yyleng+1)*sizeof(char));
+ strcpy(lbl_name, yytext);
+ yylval.lit = lbl_name;
+ return LABEL;
+ }
+
+ . return *yytext;
+
+%%
+
+int yywrap(void) { return 1; }
diff --git a/sources/api/lexyacc/smc.y b/sources/api/lexyacc/smc.y
new file mode 100644
index 0000000..f9ff06d
--- /dev/null
+++ b/sources/api/lexyacc/smc.y
@@ -0,0 +1,427 @@
+%{
+ #include "prog.h"
+ #include "instruction.h"
+ #include <iostream>
+ #include <stdlib.h>
+ #include <stdint.h>
+ #include <string>
+ #include <string.h>
+ #include <queue>
+ #include <map>
+ #include <stack>
+
+ extern char* yytext;
+ extern int yyleng;
+ extern int yylex(void);
+ extern "C" void yyerror(char const*);
+
+ int block_levels = 0;
+
+ Program* prog;
+ std::stack <Program*> prog_addr_stack;
+ void process_ddr_command_queue();
+ std::stack <uint32_t> num_generate_stack;
+ std::queue <Mininst> ddr_command_queue;
+ std::map<std::string, uint32_t> variable_map;
+%}
+
+%start program
+
+%union { void* prg;
+ Mininst mini_i_type;
+ Inst i_type;
+ uint32_t num;
+ char* lit;
+ }
+
+%token <lit> HEX_NUMBER
+%token <num> DEC_NUMBER
+%token <num> REG_ID
+%token <lit> LABEL
+%token <lit> VARIABLE
+
+/* Instruction OP-Codes */
+%token WAIT
+
+%token ADD
+%token ADDI
+%token SUB
+%token SUBI
+%token MOV
+%token SRC
+%token LI
+%token LDWD
+
+%token BRL
+%token BREQ
+%token JMP
+
+%token WR
+%token RD
+%token ACT
+%token PRE
+%token REF
+%token ZQ
+%token END
+
+/* Misc. operators */
+%token INCREMENT
+%token AUTO_PRECHARGE
+%token BURST_CHOP
+%token PRECHARGE_ALL
+
+/* Other Expressions */
+%token GENFOR
+%token ENDPROGRAM
+
+%type <num> gen_begin
+%type <num> immediate
+%type <i_type> instruction
+%type <mini_i_type> ddr_command
+%type <num> address_register
+%type <num> reg_id
+%type <num> rw_args
+%type <num> pre_args
+
+%%
+
+program:
+ expression_list
+ | program ENDPROGRAM
+ {
+ }
+ ;
+
+expression_list:
+ expression {}
+ | expression_list expression {}
+
+expression:
+ instruction
+ {
+ // See if DDR command queue is empty
+ if (ddr_command_queue.size() > 0)
+ {
+ printf("WARNING: I just appended some NOPs to your code\n");
+ int nops_to_append = 4 - ddr_command_queue.size();
+ Mininst ddr_cmd_batch[4];
+ for (int i = 0 ; i < ddr_command_queue.size() ; i++)
+ {
+ ddr_cmd_batch[i] = ddr_command_queue.front();
+ ddr_command_queue.pop();
+ }
+ for (int i = ddr_command_queue.size() ; i < 4 ; i++)
+ ddr_cmd_batch[i] = SMC_NOP();
+ prog->add_inst(__pack_mininsts(ddr_cmd_batch[0],ddr_cmd_batch[1],
+ ddr_cmd_batch[2],ddr_cmd_batch[3]));
+ }
+ prog->add_inst($1);
+ }
+ | branch
+ {
+
+ }
+ | pseudo_instruction
+ {
+
+ }
+ | LABEL ':'
+ {
+ prog->add_label(std::string($1));
+ }
+ | ddr_command
+ {
+ ddr_command_queue.push($1);
+ process_ddr_command_queue();
+ }
+ | definition
+ {
+
+ }
+ | gen_begin
+ {
+ num_generate_stack.push($1);
+ }
+ | '}' // end of a generate block
+ {
+ if(block_levels == 0) // pop an error: no generate blocks present, cannot end a block definition
+ printf("Expected \'}\' somewhere.\n"); // TODO change this with a correct error displaying function
+ else
+ {
+ Program* previous_in_stack = prog_addr_stack.top();
+ prog_addr_stack.pop();
+ int num_gen = num_generate_stack.top();
+ num_generate_stack.pop();
+ for (int i = 0 ; i < num_gen ; i++)
+ previous_in_stack->add_below(*prog);
+ free(prog);
+ prog = previous_in_stack;
+ }
+ }
+ ;
+
+instruction:
+ ADD reg_id reg_id reg_id
+ {
+ $$ = SMC_ADD($3, $4, $2);
+ }
+ | ADDI reg_id reg_id immediate
+ {
+ $$ = SMC_ADDI($3, $4, $2);
+ }
+ | SUB reg_id reg_id reg_id
+ {
+ $$ = SMC_SUB($3, $4, $2);
+ }
+ | SUBI reg_id reg_id immediate
+ {
+ $$ = SMC_SUBI($3, $4, $2);
+ }
+ | MOV reg_id reg_id
+ {
+ $$ = SMC_MV($3, $2);
+ }
+ | SRC reg_id reg_id
+ {
+ $$ = SMC_SRC($3, $2);
+ }
+ | LI reg_id immediate
+ {
+ $$ = SMC_LI($3, $2);
+ }
+ | LDWD immediate reg_id
+ {
+ $$ = SMC_LDWD($3, $2);
+ }
+ | END
+ {
+ $$ = SMC_END();
+ }
+ ;
+
+branch:
+ BRL LABEL reg_id reg_id
+ {
+ prog->add_branch(prog->BR_TYPE::BL, $3, $4, $2);
+ }
+ | BREQ LABEL reg_id reg_id
+ {
+ prog->add_branch(prog->BR_TYPE::BEQ, $3, $4, $2);
+ }
+ | JMP LABEL
+ {
+ prog->add_branch(prog->BR_TYPE::JUMP, 0, 0, $2);
+ }
+ ;
+
+pseudo_instruction:
+ WAIT immediate
+ {
+ // currently functions only by adding NOPs
+ // TODO depending on the # of cycles specified
+ // we can instead generate a "loop" which enables
+ // us to wait for more with less instructions.
+ for(int i = 0 ; i < $2 ; i++)
+ ddr_command_queue.push(SMC_NOP());
+ process_ddr_command_queue();
+ }
+ ;
+
+immediate:
+ HEX_NUMBER
+ {
+ $$ = (uint32_t) strtol($1, NULL, 16);
+ }
+ | DEC_NUMBER
+ {
+ $$ = $1;
+ }
+ ;
+ddr_command:
+ WR address_register address_register rw_args
+ {
+ int reg1_id = $2;
+ int reg2_id = $3;
+ int do_increment1 = reg1_id >= 100 ? 1 : 0;
+ int do_increment2 = reg2_id >= 100 ? 1 : 0;
+ if(do_increment1)
+ reg1_id -= 100;
+ if(do_increment2)
+ reg2_id -= 100;
+ int do_ap = $4 == 3 ? 1 : $4 == 1 ? 1 : 0;
+ int do_bc = $4 == 3 ? 1 : $4 == 2 ? 1 : 0;
+ $$ = SMC_WRITE(reg1_id, do_increment1, reg2_id, do_increment2, do_bc, do_ap);
+ }
+ | RD address_register address_register rw_args
+ {
+ int reg1_id = $2;
+ int reg2_id = $3;
+ int do_increment1 = reg1_id >= 100 ? 1 : 0;
+ int do_increment2 = reg2_id >= 100 ? 1 : 0;
+ if(do_increment1)
+ reg1_id -= 100;
+ if(do_increment2)
+ reg2_id -= 100;
+ int do_ap = $4 == 3 ? 1 : $4 == 1 ? 1 : 0;
+ int do_bc = $4 == 3 ? 1 : $4 == 2 ? 1 : 0;
+ $$ = SMC_READ(reg1_id, do_increment1, reg2_id, do_increment2, do_bc, do_ap);
+ }
+ | ACT address_register address_register
+ {
+ int reg1_id = $2;
+ int reg2_id = $3;
+ int do_increment1 = reg1_id >= 100 ? 1 : 0;
+ int do_increment2 = reg2_id >= 100 ? 1 : 0;
+ if(do_increment1)
+ reg1_id -= 100;
+ if(do_increment2)
+ reg2_id -= 100;
+ $$ = SMC_ACT(reg1_id, do_increment1, reg2_id, do_increment2);
+ }
+ | PRE address_register pre_args
+ {
+ int reg_id = $2;
+ int do_increment = reg_id>=100 ? 1 : 0;
+ if(do_increment)
+ reg_id -= 100;
+ $$ = SMC_PRE(reg_id, do_increment, $3);
+ }
+ | REF
+ {
+ $$ = SMC_REF();
+ }
+ | ZQ
+ {
+ $$ = SMC_ZQ();
+ }
+ ;
+
+address_register:
+ reg_id
+ {
+ $$ = $1;
+ }
+ | reg_id INCREMENT
+ {
+ $$ = 100 + $1;
+ }
+ ;
+
+definition:
+ VARIABLE '=' immediate
+ {
+ std::string key = std::string($1);
+ variable_map[key] = $3;
+ }
+ ;
+
+reg_id:
+ REG_ID
+ {
+ $$ = $1;
+ }
+ | VARIABLE
+ {
+ std::string key = std::string($1);
+ if (variable_map.find(key) == variable_map.end())
+ {
+ // TODO proper error handling
+ std::cout << "Variable " << key << " is not defined." << std::endl;
+ $$ = 0;
+ } else
+ $$ = variable_map[key];
+ }
+ ;
+
+rw_args:
+ %empty
+ {
+ $$ = 0;
+ }
+ | AUTO_PRECHARGE
+ {
+ $$ = 1;
+ }
+ | BURST_CHOP
+ {
+ $$ = 2;
+ }
+ | AUTO_PRECHARGE BURST_CHOP
+ {
+ $$ = 3;
+ }
+ | BURST_CHOP AUTO_PRECHARGE
+ {
+ $$ = 3;
+ }
+ ;
+
+pre_args:
+ %empty
+ {
+ $$ = 0;
+ }
+ | PRECHARGE_ALL
+ {
+ $$ = 1;
+ }
+ ;
+
+gen_begin:
+ GENFOR immediate '{'
+ {
+ // See if DDR command queue is empty
+ if (ddr_command_queue.size() > 0)
+ {
+ printf("WARNING: I just appended some NOPs to your code\n");
+ int nops_to_append = 4 - ddr_command_queue.size();
+ Mininst ddr_cmd_batch[4];
+ for (int i = 0 ; i < ddr_command_queue.size() ; i++)
+ {
+ ddr_cmd_batch[i] = ddr_command_queue.front();
+ ddr_command_queue.pop();
+ }
+ for (int i = ddr_command_queue.size() ; i < 4 ; i++)
+ ddr_cmd_batch[i] = SMC_NOP();
+ prog->add_inst(__pack_mininsts(ddr_cmd_batch[0],ddr_cmd_batch[1],
+ ddr_cmd_batch[2],ddr_cmd_batch[3]));
+ }
+ prog_addr_stack.push(prog);
+ prog = new Program();
+ block_levels++;
+ $$ = $2; // Number of times this block is generated
+ }
+ ;
+
+%%
+
+void process_ddr_command_queue()
+{
+ while (ddr_command_queue.size() >= 4)
+ {
+ Mininst ddr_cmd_batch[4];
+ for (int i = 0 ; i < 4 ; i++)
+ {
+ ddr_cmd_batch[i] = ddr_command_queue.front();
+ ddr_command_queue.pop();
+ }
+ prog->add_inst(__pack_mininsts(ddr_cmd_batch[0],ddr_cmd_batch[1],
+ ddr_cmd_batch[2],ddr_cmd_batch[3]));
+ }
+}
+
+void yyerror (char const *s) {
+ fprintf (stderr, "%s\n", s);
+}
+
+int main(int argc, char* argv[])
+{
+ if(argc != 2){
+ printf("I need you to supply me with the filename!\n");
+ exit(1);
+ }
+ prog = new Program();
+ yyparse();
+ prog->save_bin(std::string(argv[1]));
+}
+
diff --git a/sources/api/platform.cpp b/sources/api/platform.cpp
new file mode 100644
index 0000000..2dae891
--- /dev/null
+++ b/sources/api/platform.cpp
@@ -0,0 +1,296 @@
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <cassert>
+#include <stdint.h>
+#include <stdio.h>
+#include <thread>
+#include <iostream>
+#include <unistd.h>
+#include <boost/lockfree/spsc_queue.hpp>
+
+#include "platform.h"
+#include "board.h"
+#include "prog.h"
+
+[[maybe_unused]] static int consume_total = 0;
+[[maybe_unused]] static int receive_total = 0;
+
+SoftMCPlatform::SoftMCPlatform()
+{
+ // 32 KB large read buffer
+ is_dummy = false;
+ xdma_recv_buf = malloc(32*1024);
+
+ #ifdef PYSMC
+ py_data_buffer = (uint8_t*)malloc(32*1024*sizeof(uint8_t));
+ #endif
+}
+
+SoftMCPlatform::SoftMCPlatform(bool sandbox)
+{
+ // 32 KB large read buffer
+ is_dummy = sandbox;
+ xdma_recv_buf = malloc(32*1024);
+}
+
+SoftMCPlatform::~SoftMCPlatform(){
+ if (receiver.joinable())
+ receiver.join();
+
+ if (xdma_recv_buf)
+ free(xdma_recv_buf);
+
+ if (instr_buf)
+ free(instr_buf);
+
+ if (iface)
+ delete iface;
+
+ #ifdef PYSMC
+ if (py_data_buffer)
+ free(py_data_buffer);
+ #endif
+}
+
+int SoftMCPlatform::init(){
+ if(is_dummy)
+ {
+ instr_buf = malloc(INSTR_BUF_SIZE);
+ memset(instr_buf, 0, INSTR_BUF_SIZE);
+ return SOFTMC_SUCCESS;
+ }
+ else
+ {
+ instr_buf = malloc(INSTR_BUF_SIZE);
+ memset(instr_buf, 0, INSTR_BUF_SIZE);
+
+ iface = new BoardInterface(BoardInterface::IFACE::XDMA);
+ if(!iface -> init())
+ return SOFTMC_SUCCESS;
+ else
+ return SOFTMC_ERR;
+ }
+}
+
+/**
+ * This sends a 256 bit data which has it's
+ * 33rd bit set to '1'.
+ */
+void SoftMCPlatform::reset_fpga()
+{
+ if(is_dummy)
+ {
+ return;
+ }
+ else
+ {
+ ((uint8_t*) instr_buf)[8] = (uint8_t) 1;
+ int sent = iface -> sendData(instr_buf, 32 /*in bytes*/);
+ // We do not need to zero out the whole buffer
+ memset(instr_buf, 0, 32);
+ if(sent)
+ std::cerr << "Could not reset the FPGA!" << std::endl;
+ else
+ std::cout << "Successfully reset the FPGA!" << std::endl;
+ }
+}
+
+void SoftMCPlatform::execute(Program &prog)
+{
+ if(is_dummy)
+ {
+ [[maybe_unused]] uint64_t* iseq = (uint64_t*) prog.get_inst_array();
+ int bytes = prog.size();
+ assert (bytes <= INSTR_BUF_SIZE/4 && " too many instructions in the buffer, the limit is 2048.");
+ return;
+ }
+ else
+ {
+ uint64_t* iseq = (uint64_t*) prog.get_inst_array();
+ uint64_t* temp_ptr = (uint64_t*) instr_buf;
+ int bytes = prog.size();
+ assert (bytes <= INSTR_BUF_SIZE/4 && " too many instructions in the buffer, the limit is 2048.");
+
+ for(int i = 0 ; i < bytes/8 ; i++)
+ temp_ptr[i*4] = iseq[i];
+
+ if(receiver.joinable())
+ receiver.join();
+
+ receiver = std::thread(&SoftMCPlatform::consumeData, this);
+ int sent = iface -> sendData(instr_buf, bytes*4 /*in bytes*/);
+ memset(instr_buf, 0, bytes*4);
+ free(iseq);
+ assert(!sent && "could not send instructions");
+ }
+}
+
+void SoftMCPlatform::consumeData()
+{
+ while(true)
+ {
+ int xdma_read_intent = 32*1024;
+ int xdma_read_size = xdma_read_intent;
+ int recvd = iface -> recvData((void*)xdma_recv_buf, xdma_read_size);
+ if(recvd == 0)
+ break;
+ // xdma read size in words
+ // If we did not read 32KBs then the program probably ended
+ // and the last transfer is trash so we discard it
+ if(recvd != xdma_read_intent)
+ xdma_read_size = recvd-32;
+ xdma_read_size /= 4;
+ int total_size = xdma_read_size;
+ int pushsz = api_recv_buf.push(((int*) xdma_recv_buf), xdma_read_size);
+ while(pushsz < total_size)
+ pushsz += api_recv_buf.push(((int*) xdma_recv_buf) + pushsz, xdma_read_size = (total_size - pushsz));
+
+// printf("SoftMCPlatform::consumedata(): spsc filled with %d beats of data\n", pushsz);
+// printf("Consume total: %d\n",++consume_total);
+
+ assert(pushsz == total_size &&
+ "Unexpected amount of data pushed into spsc\n");
+
+ if(recvd != xdma_read_intent)
+ break;
+
+ }
+}
+
+/**
+* Try to read param(size) bytes from FPGA, function will block until
+* all data is read
+* @param recv_buf where to copy read data
+* @param size number of bytes to read
+* returns the number of bytes read on success
+*/
+int SoftMCPlatform::receiveData(void* recv_buf, int size){
+ if(is_dummy)
+ {
+ assert(size>0 && size%4 == 0 && "size is expected to be a multiple of four\n");
+ size /= 4;
+ int * my_buf = (int *) recv_buf;
+ for(int i = 0; i < size ; ++i) {
+ my_buf[i] = 0x0;
+ }
+ return size * 4;
+ }
+ else
+ {
+ assert(size>0 && size%4 == 0 && "size is expected to be a multiple of four\n");
+
+ size /= 4;
+ int total_size = size;
+ int rdsz = api_recv_buf.pop((int*) recv_buf, size);
+ while (rdsz < total_size)
+ rdsz += api_recv_buf.pop(((int*) recv_buf) + rdsz, size = (total_size-rdsz));
+
+ assert(rdsz == total_size && "Unexpected amount of data popped from spsc\n");
+ return total_size*4;
+ }
+}
+
+#ifdef PYSMC
+int SoftMCPlatform::py_receiveData(int size){
+ if (size > 32 * 1024)
+ {
+ std::cerr << "Python version only supports read buffer size of up to 32KB!" << std::endl;
+ return 0;
+ }
+
+ if(is_dummy)
+ {
+ assert(size>0 && size%4 == 0 && "size is expected to be a multiple of four\n");
+ size /= 4;
+ int * my_buf = (int *) py_data_buffer;
+ for(int i = 0; i < size ; ++i) {
+ my_buf[i] = 0x0;
+ }
+ return size * 4;
+ }
+ else
+ {
+ assert(size>0 && size%4 == 0 && "size is expected to be a multiple of four\n");
+
+ size /= 4;
+ int total_size = size;
+ int rdsz = api_recv_buf.pop((int*) py_data_buffer, size);
+ while (rdsz < total_size)
+ rdsz += api_recv_buf.pop(((int*) py_data_buffer) + rdsz, size = (total_size-rdsz));
+
+ assert(rdsz == total_size && "Unexpected amount of data popped from spsc\n");
+ return total_size*4;
+ }
+}
+
+py::memoryview SoftMCPlatform::get_buffer_memoryview()
+{
+ return py::memoryview::from_memory((uint64_t*) py_data_buffer, sizeof(uint8_t) * 32 * 1024, true);
+}
+#endif
+
+int SoftMCPlatform::count_bitflips_in_row(unsigned char comp_pattern){
+ int num_bitflips = 0;
+ unsigned char buf[8192];
+ receiveData(buf, 8192); // read one row each iteration
+
+ for(int j = 0 ; j < 8192 ; j++){
+ if(comp_pattern != buf[j])
+ {
+ for(int i = 0 ; i < 8 ; i++)
+ {
+ if(((comp_pattern >> i) & 1) != ((buf[j] >> i) & 1))
+ num_bitflips ++;
+ }
+ }
+ }
+
+ return num_bitflips;
+}
+
+void SoftMCPlatform::set_aref(const bool on)
+{
+ if(is_dummy)
+ {
+ std::cout << (on ? "Enabled" : "Disabled") << " autorefresh!" << std::endl;
+ return;
+ }
+ else
+ {
+ ((uint8_t*) instr_buf)[8] = (uint8_t) 0x8;
+ ((uint8_t*) instr_buf)[0] = on;
+ int sent = iface -> sendData(instr_buf, 32 /*in bytes*/);
+ // We do not need to zero out the whole buffer
+ memset(instr_buf, 0, 32);
+
+ if(sent)
+ std::cerr << "Could not set auto refresh!" << std::endl;
+ // else
+ // std::cout << (on ? "Enabled" : "Disabled") << " autorefresh!" << std::endl;
+ }
+}
+
+void SoftMCPlatform::readRegisterDump()
+{
+ if(is_dummy)
+ return;
+
+ /** The author decided to use printfs explicitly within this function
+ * because printing unsigned hex bytes is uglier with stdio
+ */
+ uint8_t readData[64];
+ this -> receiveData((void*)readData, 64); // first read wdata content
+ printf("WDATA: 0x");
+ for(int i = 63 ; i >= 0 ; i--)
+ printf("%x", readData[i]);
+ printf("\n");
+ this -> receiveData((void*)readData, 64); // read register content
+ for(int r = 0 ; r < 16 ; r++)
+ {
+ printf("R%d: 0x", r);
+ printf("%x", ((uint32_t*)readData)[r]);
+ printf("\n");
+ }
+} \ No newline at end of file
diff --git a/sources/api/platform.h b/sources/api/platform.h
new file mode 100644
index 0000000..277dcdf
--- /dev/null
+++ b/sources/api/platform.h
@@ -0,0 +1,83 @@
+#include "board.h"
+#include "prog.h"
+#include <thread>
+#include <boost/lockfree/spsc_queue.hpp>
+
+#ifdef PYSMC
+#include "ext/pybind11/include/pybind11/pybind11.h"
+namespace py = pybind11;
+#endif
+
+//ERROR CODES
+#define SOFTMC_SUCCESS 0
+#define SOFTMC_ERR -1
+#define SOFTMC_NO_PLATFORM -2
+#define SOFTMC_ERR_OPEN_FPGA -3
+#define SOFTMC_NO_SUCH_FPGA -4
+
+class SoftMCPlatform{
+ #define INSTR_BUF_SIZE 32*2048
+ #define API_BUF_SIZE 1024*1024*2
+ public:
+ SoftMCPlatform();
+ SoftMCPlatform(bool);
+ ~SoftMCPlatform();
+ /**
+ * Initializes the whole platform
+ * @return SOFTMC_SUCCESS on sucessful initialization
+ */
+ int init();
+ /**
+ * Resets SoftMC logic, won't reset PCI-E endpoint or the PHY interface
+ */
+ void reset_fpga();
+ /**
+ * Sends SoftMC program to the FPGA board over PCI-E
+ * @param prog reference to the program object to send
+ */
+ void execute(Program & prog);
+ /**
+ * Receive data from the FPGA board over PCI-E
+ * @param dst_buf pointer to the buffer that will receive the data
+ * @param num_words number of bytes to read
+ */
+ int receiveData(void* dst_buf, int num_words);
+
+ #ifdef PYSMC
+ int py_receiveData(int num_words);
+ #endif
+
+ /**
+ * Compare data with a given data pattern (repeating bytes)
+ * and return number of bitflips in 8KB of data
+ * @param comp_pattern one byte data pattern to compare the read data
+ */
+ int count_bitflips_in_row(unsigned char comp_pattern);
+
+ /**
+ * Turn auto-refresh on-off
+ * @param on true to turn aref on false otherwise
+ */
+ void set_aref(bool on);
+
+ /**
+ * Used along with Program::dumpRegisters to read register content
+ */
+ void readRegisterDump();
+
+ private:
+ bool is_dummy;
+
+ BoardInterface *iface;
+ void* instr_buf;
+ std::thread receiver;
+ boost::lockfree::spsc_queue<int, boost::lockfree::capacity<API_BUF_SIZE/4>> api_recv_buf;
+ void* xdma_recv_buf;
+ void consumeData();
+
+ #ifdef PYSMC
+ public:
+ uint8_t* py_data_buffer = nullptr;
+ py::memoryview get_buffer_memoryview();
+ #endif
+};
diff --git a/sources/api/prog.cpp b/sources/api/prog.cpp
new file mode 100644
index 0000000..781c079
--- /dev/null
+++ b/sources/api/prog.cpp
@@ -0,0 +1,675 @@
+#include "instruction.h"
+#include "prog.h"
+#include <string>
+#include <vector>
+#include <cassert>
+#include <iostream>
+#include <fstream>
+#include <bitset>
+#include <algorithm>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+Program::Program()
+{
+ dumpRegsCalled = false;
+}
+
+Program::Program(std::string fname)
+{
+ std::cout << "WARNING: make sure that the executable \"smc_parser\" exists in the working directory" << std::endl;
+
+ std::string correctFileName = "./smc_parser " + fname + " < " + fname + " >/dev/null";
+ if (system(correctFileName.c_str()) != 0)
+ {
+ std::cerr << "Error parsing SMC program, abort." << std::endl;
+ exit(-1);
+ }
+
+ // Read instruction binary data from fname.inst
+ std::ifstream binFile (fname + ".bin", std::ios::in | std::ios::binary);
+ int no_insts = 0;
+ // Read no of insts
+ binFile.read((char*) &no_insts, 4);
+ Inst insts[no_insts];
+ // Read all insts
+ binFile.read((char*) insts, sizeof(Inst)*no_insts);
+ for(int i = 0 ; i < no_insts ; i++)
+ {
+ program.push_back(insts[i]);
+ spc++;
+ }
+ binFile.close();
+ // Read branch label positions from fname.meta
+ std::ifstream metaFile (fname + ".meta", std::ios::in);
+ std::string line;
+ bool parseLabels = true;
+ while(getline(metaFile,line))
+ {
+ if(parseLabels)
+ {
+ if (line == "-")
+ {
+ parseLabels = false;
+ continue;
+ }
+ std::string label = line.substr(0,line.find(" "));
+ std::string target = line.substr(line.find(" ")+1,line.length());
+ (labels)[label] = stoi(target);
+ }
+ else
+ {
+ std::string target = line.substr(0,line.find(" "));
+ std::string label = line.substr(line.find(" ")+1,line.length());
+ (branches)[stoi(target)] = label;
+ }
+ }
+ metaFile.close();
+}
+
+
+void Program::add_label(std::string name)
+{
+ if(labels.count(name))
+ std::cerr << "Trying to add label " << name << " multiple times!" << std::endl;
+ (labels)[name] = spc;
+}
+void Program::add_wait(int wait_cycles)
+{
+ for(; wait_cycles > 0 ; wait_cycles--)
+ {
+ minprogram.push_back(SMC_NOP());
+ }
+}
+
+void Program::add_mininst(Mininst mi, int wait_after)
+{
+ minprogram.push_back(mi);
+ for ( ; wait_after>0 ; wait_after--)
+ minprogram.push_back(SMC_NOP());
+ // if (verbose) {
+ // std::cout << "Added new mininst with wait." << std::endl;
+ // for(auto& mininst : minprogram){
+ // std::cout << std::hex << mininst << " ";
+ // }
+ // std::cout << std::endl;
+ // }
+}
+
+void Program::pack_minprogram()
+{
+ // if (verbose) std::cout << "Packing minprogram" << std::endl;
+ while(minprogram.size() >= 4)
+ {
+ this->add_inst(__pack_mininsts(minprogram[0], minprogram[1], minprogram[2], minprogram[3]));
+ minprogram.erase(minprogram.begin(), minprogram.begin()+4);
+ }
+
+ switch (minprogram.size())
+ {
+ case 0:
+ return;
+ case 1:
+ this->add_inst(__pack_mininsts(minprogram[0], SMC_NOP(), SMC_NOP(), SMC_NOP()));
+ break;
+ case 2:
+ this->add_inst(__pack_mininsts(minprogram[0], minprogram[1], SMC_NOP(), SMC_NOP()));
+ break;
+ case 3:
+ this->add_inst(__pack_mininsts(minprogram[0], minprogram[1], minprogram[2], SMC_NOP()));
+ break;
+ }
+ // std::cerr << "WARNING: Number of mininsts is not multiple of four. "
+ // << "Appending "<< 4-minprogram.size() << " extra NOPs for alignment."
+ // << std::endl;
+ minprogram.erase(minprogram.begin(), minprogram.end());
+}
+
+void Program::add_inst(Inst i)
+{
+ program.push_back(i);
+ if (is_load(i))
+ this->add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP());
+ spc += 1;
+}
+
+void Program::add_inst(Mininst m1, Mininst m2, Mininst m3, Mininst m4)
+{
+ Inst i = (uint64_t) m4 << 48 |
+ (uint64_t) m3 << 32 |
+ (uint64_t) m2 << 16 |
+ m1;
+ program.push_back(i);
+ spc += 1;
+}
+
+void Program::add_branch(BR_TYPE bt, int rs1, int rs2, std::string tgt)
+{
+ Inst place_holder = 2;
+ switch(bt)
+ {
+ case BR_TYPE::BL:
+ place_holder = SMC_BL(rs1, rs2, 0);
+ break;
+ case BR_TYPE::BEQ:
+ place_holder = SMC_BEQ(rs1, rs2, 0);
+ break;
+ case BR_TYPE::JUMP:
+ place_holder = SMC_JUMP(0);
+ break;
+ }
+ (branches)[spc] = tgt;
+ add_inst(place_holder);
+}
+
+void Program::add_below(const Program &p)
+{
+ // move instructions from p to the end of
+ // this program
+ program.insert(program.end(), p.program.begin(), p.program.end());
+
+ // update branch targets and branch instruction pcs
+ for ( const auto &branch : (p.branches) )
+ ((this->branches))[branch.first + this -> spc] = branch.second;
+
+ for ( const auto &label : (p.labels) )
+ ((this->labels))[label.first] = label.second + this -> spc;
+
+ // update pc
+ this -> spc += p.spc;
+}
+
+Inst* Program::get_inst_array()
+{
+ linear_analysis();
+ insert_generated();
+// printf("Inserted auto-generated info instructions\n");
+ preprocess_branches();
+// printf("Assigned branch targets to labels\n");
+ //pretty_print();
+ int bytes = size();
+ Inst* arr = (Inst*) malloc(bytes);
+ int ind = 0;
+ for (auto & element : (program))
+ arr[ind++] = element;
+ return arr;
+}
+
+Inst* Program::get_insts()
+{
+ int bytes = size();
+ Inst* arr = (Inst*) malloc(bytes);
+ int ind = 0;
+ for (auto & element : (program))
+ arr[ind++] = element;
+ return arr;
+}
+
+int Program::size()
+{
+ return program.size() * 8;
+}
+
+void Program::preprocess_branches()
+{
+ for ( const auto &branch : (branches) ) {
+ Inst br = (program)[branch.first];
+ int lbl = (labels)[branch.second];
+// printf("branch addr: %d branch label: %s lbl_adr: %d is_cond: %d\n", branch.first, branch.second.c_str(),
+// lbl,is_conditional(br));
+ assert(is_branch(br) &&
+ "preprocess_branches(): expected a branch but got something else");
+ uint64_t target_b = ((uint64_t)lbl) << 8;
+ uint64_t target_j = (uint64_t) lbl;
+ br |= is_conditional(br) ? target_b : target_j;
+ (program)[branch.first] = br;
+ }
+}
+
+/**
+ * Currently only goes through the whole program
+ * to see if any constraint is violated
+ */
+void Program::linear_analysis()
+{
+ bool inSequence = false;
+ uint seqPc = 0;
+ int readCtr = 0;
+ for (uint pc = 0 ; pc < program.size() ; pc++)
+ {
+ Inst cur_inst = (program)[pc];
+ if(!inSequence)
+ {
+ if(is_ddr(cur_inst))
+ {
+ //printf("Program::linear_analysis(): enter segment at pc: %d\n", pc);
+ inSequence = true;
+ seqPc = pc;
+ readCtr = is_ddr_read(cur_inst);
+ }
+ }else
+ {
+ // we see a non-ddr inst,
+ // can commit whatever info we want
+ // related to the previous segment
+ if(!is_ddr(cur_inst) && !is_sleep(cur_inst))
+ {
+ //printf("Program::linear_analysis(): exit segment at pc: %d with %d reads \n", pc, readCtr);
+ inSequence = false;
+ // TODO either directly insert into the vector
+ // or keep these records elsewhere
+ if(readCtr > 1024)
+ {
+ // TODO Add code that will warn user violently here.
+ // maybe even an assertion?
+ std::cerr << "WARNING: SoftMC won't be able to run your program timing-critically!" << std::endl;
+ }else if(readCtr > 0)
+ {
+ // Insert info-encapsulating packet into program
+ Inst warn_pipeline = SMC_INFO(readCtr);
+ (warnings)[seqPc] = warn_pipeline;
+ }
+ }else
+ {
+ readCtr += is_ddr_read(cur_inst);
+ }
+ }
+ }
+}
+
+
+void Program::insert_generated()
+{
+ // WARNING this assumes that map keys are iterated
+ // in ascending order
+
+ std::vector<uint> warn_keys;
+ std::vector<Inst> warn_values;
+
+ for ( const auto &warn : (this->warnings))
+ {
+ warn_keys.push_back(warn.first);
+ warn_values.push_back(warn.second);
+ }
+
+ while (warn_keys.size()>0)
+ {
+ // new branch and label mappings
+ uint warn_key = warn_keys.front();
+ Inst warn_value = warn_values.front();
+ warn_keys.erase(warn_keys.begin());
+ warn_values.erase(warn_values.begin());
+
+ std::map<std::string, uint> n_labels;
+ std::map<uint, std::string> n_branches;
+ program.insert(program.begin() + warn_key, warn_value);
+ for ( const auto &branch : (this->branches) )
+ {
+ // branch target > inserted instruction pc
+ if((labels)[branch.second] > warn_key)
+ {
+ (n_labels)[branch.second] = (labels)[branch.second] + 1;
+ }else
+ {
+ (n_labels)[branch.second] = (labels)[branch.second];
+ }
+ // branching instruction pc > inserted instruction pc
+ if(branch.first >= warn_key)
+ {
+ (n_branches)[branch.first + 1] = branch.second;
+ }else
+ {
+ (n_branches)[branch.first] = branch.second;
+ }
+ }
+ for (uint i = 0 ; i < warn_keys.size() ; i++)
+ warn_keys[i] += 1;
+
+ labels = n_labels;
+ branches = n_branches;
+ }
+}
+
+void Program::pretty_print()
+{
+ for (uint pc = 0 ; pc < program.size() ; pc++)
+ {
+ for ( const auto &label : (labels) )
+ {
+ if(label.second == pc)
+ std::cout << label.first << std::endl;
+ }
+ printf("%04d: ", pc);
+ decode_inst((program)[pc]);
+ for ( const auto &branch : (branches) )
+ {
+ if(branch.first == pc)
+ std::cout << " (TARGET LABEL: " << branch.second << ")";
+ }
+ printf("\n");
+ }
+
+}
+
+void Program::bin_dump()
+{
+ for (uint pc = 0 ; pc < program.size() ; pc++)
+ {
+ for ( const auto &label : (labels) )
+ {
+ if(label.second == pc)
+ std::cout << label.first << std::endl;
+ }
+ std::cout << "PC: " << pc << " Inst: " << std::hex << (program)[pc] << std::dec << std::endl;
+
+ for ( const auto &branch : (branches) )
+ {
+ if(branch.first == pc)
+ std::cout << branch.second;
+ }
+ printf("\n");
+ }
+}
+
+void Program::save_bin(const std::string &fname)
+{
+ // Write instruction binary data to fname.inst
+ std::ofstream binFile (fname + ".bin", std::ios::out | std::ios::binary);
+ Inst* insts = this -> get_insts();
+ int no_insts = spc;
+ binFile.write ((char*)&no_insts, sizeof(no_insts));
+ binFile.write ((char*) insts, sizeof(Inst)*no_insts);
+ binFile.close();
+ // Write branch label positions to fname.meta
+ std::ofstream metaFile (fname + ".meta", std::ios::out);
+ for ( const auto &label : (labels) )
+ metaFile << label.first << " " << label.second << "\n";
+ metaFile << "-" << "\n";
+ for ( const auto &branch : (branches) )
+ metaFile << branch.first << " " << branch.second << "\n";
+ metaFile.close();
+}
+
+void Program::save_coe_here(const std::string &prj_dir)
+{
+ std::ofstream coeFile ("program.coe", std::ios::out | std::ios::binary);
+ uint64_t* insts = (uint64_t*) this -> get_inst_array();
+ coeFile << "memory_initialization_radix=2;\n";
+ coeFile << "memory_initialization_vector=\n";
+ for(uint i = 0 ; i < program.size() ; i++){
+ coeFile << std::bitset<64>(insts[i]);
+ if( i == (program.size()) -1){
+ coeFile << ";";
+ }else{
+ coeFile << ",\n";
+ }
+ }
+ coeFile.close();
+}
+
+
+void Program::save_coe(const std::string &prj_dir)
+{
+ std::ofstream coeFile (prj_dir + "/VU095.sim/sim_1/behav/xsim/simmem.coe", std::ios::out | std::ios::binary);
+ std::ofstream coeFile2 (prj_dir + "/coe/simmem.coe", std::ios::out | std::ios::binary);
+ std::ofstream mifFile (prj_dir + "/VU095.sim/sim_1/behav/xsim/instr_blk_mem_sim.mif", std::ios::out | std::ios::binary);
+ uint64_t* insts = (uint64_t*) this -> get_inst_array();
+ coeFile << "memory_initialization_radix=2;\n";
+ coeFile2 << "memory_initialization_radix=2;\n";
+ coeFile << "memory_initialization_vector=\n";
+ coeFile2 << "memory_initialization_vector=\n";
+ for(uint i = 0 ; i < program.size() ; i++){
+ coeFile << std::bitset<64>(insts[i]);
+ coeFile2 << std::bitset<64>(insts[i]);
+ mifFile << std::bitset<64>(insts[i])<<"\n";
+ if( i == (program.size()) -1){
+ coeFile << ";";
+ coeFile2 << ";";
+ }else{
+ coeFile << ",\n";
+ coeFile2 << ",\n";
+ }
+ }
+ coeFile.close();
+ coeFile2.close();
+ mifFile.close();
+}
+
+
+void Program::dump_registers()
+{
+ if(dumpRegsCalled)
+ {
+ std::cerr << "You can use your trump card (dump_register) only once per program!" << std::endl;
+ exit(1);
+ }
+ dumpRegsCalled = true;
+
+ this -> add_inst(SMC_LI(15,13)); // BAR
+ this -> add_inst(SMC_LI(0,14)); // RAR
+ this -> add_inst(SMC_LI(0,15)); // CAR
+
+ this -> add_inst(__pack_mininsts(SMC_PRE(13,0,1), // PRECHARGE ALL
+ SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_ACT(13,0,14,0), // ACT B15 R0
+ SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_WRITE(13,0,15,0,0,0), // WRITE WDATA CONTENT
+ SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_READ(13,0,15,0,0,0), // READ WDATA CONTENT
+ SMC_NOP(),SMC_NOP(),SMC_NOP()));
+
+ for (int i = 0 ; i < 16 ; i++) // SWAP WDATA WITH REG VALUES
+ this -> add_inst(SMC_LDWD(i,i));
+
+ this -> add_inst(__pack_mininsts(SMC_WRITE(13,0,15,0,0,0), // WRITE WDATA CONTENT
+ SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_READ(13,0,15,0,0,0), // READ WDATA CONTENT
+ SMC_NOP(),SMC_NOP(),SMC_NOP()));
+
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_NOP(),SMC_NOP(),SMC_NOP(),SMC_NOP()));
+ this -> add_inst(__pack_mininsts(SMC_PRE(13,0,1), // PRECHARGE ALL
+ SMC_NOP(),SMC_NOP(),SMC_NOP()));
+}
+
+bool Program::isDumpRegsCalled()
+{
+ return dumpRegsCalled;
+}
+
+void Program::debug(const std::string &prj_dir, bool first)
+{
+ int pid = fork();
+ if(pid == 0){
+
+ std::string source = prj_dir + "/source.tcl";
+ std::string project = prj_dir + "/VU095.xpr";
+ std::string arg;
+ int fd = open("/dev/null", O_WRONLY);
+ dup2(fd, 1);
+ if(first)
+ arg = "first";
+ else
+ arg = "debug";
+
+ execl("/opt/Xilinx/Vivado/2018.2/bin/vivado",
+ "vivado","-mode","tcl","-nojournal","-applog","-log","/tmp/asmc.log",
+ "-source", source.c_str(), project.c_str(),"-tclargs", arg.c_str(), NULL);
+
+ }else if(pid > 0){
+ std::cout << "Debugger starting...\n";
+
+ mkfifo("/tmp/fifo_1", 0666);
+ int f1 = open("/tmp/fifo_1", O_WRONLY);
+ mkfifo("/tmp/fifo_2", 0666);
+ int f2 = open("/tmp/fifo_2", O_RDONLY);
+
+ char buf[100];
+ [[maybe_unused]] int rv;
+
+ std::vector<std::string> vios;
+ std::string line, start, end, vio;
+
+ start = "sim_tb_top.mem_model_x8.memModels_Ri1[0].memModel1[0].ddr4_model.always_diff_ck.if_diff_ck:VIOLATION:";
+ end = "sim_tb_top.mem_model_x8.memModels_Ri1[0].memModel1[1].ddr4_model.always_diff_ck.if_diff_ck:VIOLATION:";
+
+ std::string msg, com, param,param2;
+ std::vector<std::string> commands {"reg", "mem", "time", "step", "run",
+ "until", "exit", "btwn","stat"};
+
+ int state = 0;
+
+ while(state != 5){
+ switch (state){
+ case 0:
+ std::cout << "Enter command: ";
+ std::cin >> com;
+ if(std::find(commands.begin(), commands.end(), com) != commands.end()){
+ if(com == "step" || com == "exit" || com == "time" || com == "stat"){
+ msg = com + "_\n";
+ }else if(com == "btwn"){
+ std::cin >> param;
+ std::cin >> param2;
+ msg = com + "_" + param + "_" + param2 + "\n";
+ }else{
+ std::cin >> param;
+ msg = com + "_" + param + "\n";
+ }
+ state = 1;
+ }else{
+ std::cout << "Please enter a valid command...\n";
+ }
+ break;
+
+ case 1:
+ rv = write( f1, (char*)msg.c_str(), msg.size());
+ memset(buf, 0, sizeof(buf));
+ while(read(f2, buf, sizeof(buf)) == 0);
+ state = 2;
+ break;
+
+ case 2:
+ if(com == "reg" || com == "mem")
+ std::cout << com << " " << param << ": " << buf;
+ else if(com == "btwn")
+ std::cout << "Time between "<< param << "-" << param2 << ": " << buf;
+ else if(com == "time")
+ std::cout << "Current time: " << buf;
+ else if(com == "stat")
+ std::cout << "Stats:\n" << buf;
+ else if(com == "exit"){
+ state = 5;
+ break;
+ }
+ state = 3;
+ break;
+
+ case 3:
+ std::ifstream logfile ("/tmp/asmc.log");
+ while (std::getline(logfile, line))
+ {
+ if(line.find(start) != std::string::npos)
+ {
+ vio = line;
+ if(std::find(vios.begin(), vios.end(), vio) != vios.end())
+ continue;
+ std::cout << line.substr(91) << "\n";
+ while (std::getline(logfile, line))
+ {
+ if (line.find(end) != std::string::npos)
+ break;
+ std::cout << line << "\n";
+ }
+ vios.insert(vios.begin(), vio);
+ }
+ }
+ logfile.close();
+ state = 0;
+ break;
+
+ }
+ }
+ close(f1);
+ close(f2);
+ wait(NULL);
+ remove("/tmp/fifo_1");
+ remove("/tmp/fifo_2");
+ remove("/tmp/asmc.log");
+ std::cout << "Exiting debugger.\n";
+ }
+}
+
+void Program::debug(const std::string &prj_dir, const std::string &filename)
+{
+ int pid = fork();
+ if(pid == 0){
+
+ std::string source = prj_dir + "/source.tcl";
+ std::string project = prj_dir + "/VU095.xpr";
+ int fd = open("/dev/null", O_WRONLY);
+ dup2(fd, 1);
+ execl("/opt/Xilinx/Vivado/2018.2/bin/vivado",
+ "vivado","-mode","tcl","-nojournal","-applog","-log","/tmp/asmc.log",
+ "-source", source.c_str(), project.c_str(),"-tclargs", "update", NULL);
+
+ }else if(pid > 0){
+
+ std::string path, line, param, value, time_path, temp_path;
+
+ path = prj_dir.substr(0, prj_dir.find("VU095")) + "phy_ddr4_ex/imports/";
+ time_path = path + "timing_tasks.sv";
+ temp_path = path + "temp.sv";
+
+ std::ifstream timing_tasks (time_path);
+ std::ifstream param_file (filename);
+ std::ofstream temp_file (temp_path);
+
+ std::map <std::string, std::string> params;
+
+ while (std::getline(param_file, line)){
+ param = line.substr(0, line.find("="));
+ param = param.substr(param.find_first_not_of(" \n\r\t\f\v"),param.find_last_not_of(" \n\r\t\f\v") + 1);
+ param = " SetTSArray (i" + param;
+ value = line.substr(line.find("=") + 1);
+ value = value.substr(value.find_first_not_of(" \n\r\t\f\v"),value.find_last_not_of(" \n\r\t\f\v") + 1);
+ value = std::string((7- value.size()), ' ') + value;
+ params[param] = value;
+ }
+ param_file.close();
+
+ while (std::getline(timing_tasks, line)){
+ if(line.find(" SetTSArray (i") == std::string::npos){
+ temp_file << line << "\n";
+ continue;
+ }
+ for(const auto & param : params){
+ if(line.find(param.first) != std::string::npos){
+ line = line.replace(35, 7, param.second);
+ break;
+ }
+ }
+ temp_file << line << "\n";
+ }
+
+ timing_tasks.close();
+ temp_file.close();
+ remove(time_path.c_str());
+ rename(temp_path.c_str(), time_path.c_str());
+ wait(NULL);
+ std::cout << "Timing parameters are updated...\n";
+ }
+}
diff --git a/sources/api/prog.h b/sources/api/prog.h
new file mode 100644
index 0000000..d9febd7
--- /dev/null
+++ b/sources/api/prog.h
@@ -0,0 +1,120 @@
+#ifndef PROG_H
+#define PROG_H
+
+#include <vector>
+#include <map>
+#include <string>
+#include "instruction.h"
+
+class Program{
+private:
+ std::map<std::string, uint> labels;
+ std::map<uint, std::string> branches;
+ std::map<uint, Inst> warnings;
+ std::vector<Inst> program;
+ std::vector<Mininst> minprogram;
+ int spc = 0;
+
+ bool dumpRegsCalled;
+
+ void preprocess_branches();
+ void linear_analysis();
+ void insert_generated();
+ Inst* get_insts();
+
+public:
+ enum class SEQ_TYPE { WRITE, READ };
+ enum BR_TYPE { BEQ, BL, JUMP };
+
+
+ Program();
+
+ /**
+ * Load an instruction stream from file
+ * @param fname name of the file
+ */
+ Program(std::string fname);
+
+ void add_mininst(Mininst mi, int wait_after);
+ void add_wait(int wait_cycles);
+ void pack_minprogram();
+ /**
+ * Add an instruction to the program
+ * @param i instruction to add, usually generated by instruction.h functions (e.g. SMC_ADD())
+ */
+ void add_inst(Inst i);
+ /**
+ * Add four mininsts to the program
+ */
+ void add_inst(Mininst,Mininst,Mininst,Mininst);
+ void add_label(std::string name);
+ /**
+ * Add a conditional branch to the program
+ * @bt the type of branch instruction to be executed:
+ * either one of: BR_TYPE.BEQ, BR_TYPE.BL or BR_TYPE.JUMP
+ * @rs1 branch instr. operand 1
+ * @rs2 branch instr. operand 2
+ * @tgt branch target to jump to if condition is satisfied
+ */
+ void add_branch(BR_TYPE bt, int rs1, int rs2, std::string tgt);
+
+ /**
+ * Append another program to this program.
+ * @param p program to append to the end of this program.
+ */
+ void add_below(const Program &p);
+
+ /**
+ * Parse SMC programs
+ * @param fname file to read from
+ */
+ void parse_from_file(std::string fname);
+
+ Inst* get_inst_array();
+
+ /**
+ * @return size of the program in bytes
+ */
+ int size();
+
+ /**
+ * Print instructions in a human readable form
+ */
+ void pretty_print();
+
+ /**
+ * Print instruction binaries
+ */
+ void bin_dump();
+ /**
+ * Save this instruction stream to file
+ * @param fname name of the file
+ */
+ void save_bin(const std::string &fname);
+ void save_coe_here(const std::string &prj_dir);
+ void save_coe(const std::string &prj_dir);
+ void debug(const std::string &prj_dir, bool first);
+ void debug(const std::string &prj_dir, const std::string &filename);
+
+ /**
+ * Insert routine required to dump register file content.
+ * The generated routine requires registers 13-15 to operate correctly.
+ * It won't do any context-switch and registers 13-15 will have garbage
+ * values when this routine is executed. It will also use DRAM bank
+ * 15 row 0-1 to store and readback data, it will also precharge all banks.
+ * This routine will also mess with WDATA reg content *after* dumping it.
+ *
+ * Aside from all the above, you need to intercept reads yourself. Make sure
+ * that there is no other data left in the buffers before this routine executes
+ * and call platform::readRegisterDump().
+ * You have been warned.
+ * You have been warned twice.
+ * You did not hear that wrong, this will dump all register content.
+ */
+ void dump_registers();
+
+ bool isDumpRegsCalled();
+
+};
+
+#endif
diff --git a/sources/api/pySoftMC/.gitignore b/sources/api/pySoftMC/.gitignore
new file mode 100644
index 0000000..f1fe8d1
--- /dev/null
+++ b/sources/api/pySoftMC/.gitignore
@@ -0,0 +1 @@
+*.so \ No newline at end of file
diff --git a/sources/api/pySoftMC/Makefile b/sources/api/pySoftMC/Makefile
new file mode 100644
index 0000000..771df53
--- /dev/null
+++ b/sources/api/pySoftMC/Makefile
@@ -0,0 +1,36 @@
+# https://stackoverflow.com/questions/4933285/how-to-determine-python-version-in-makefile
+python_version_full := $(wordlist 2,4,$(subst ., ,$(shell python3 --version 2>&1)))
+python_version := $(word 1,${python_version_full}).$(word 2,${python_version_full})
+
+program_NAME := pySoftMC
+program_CXX_SRCS := $(wildcard ../*.c) $(wildcard ../*.cpp) ./pySoftMC.cpp
+program_CXX_OBJS := ${program_CXX_SRCS:.cpp=.o}
+program_CXX_OBJS := ${program_CXX_OBJS:.c=.o}
+program_OBJS := $(program_CXX_OBJS)
+
+program_INCLUDE_DIRS := . .. /usr/include/python${python_version} ../ext/pybind11/include/pybind11
+program_LIBRARY_DIRS :=
+program_LIBRARIES := python${python_version}
+CPPFLAGS += -g -std=c++14 -pthread -O3 -fPIC -Wall
+
+CPPFLAGS += $(foreach includedir,$(program_INCLUDE_DIRS),-I$(includedir)) -DPYSMC
+LDFLAGS += $(foreach librarydir,$(program_LIBRARY_DIRS),-L$(librarydir))
+LDFLAGS += $(foreach library,$(program_LIBRARIES),-l$(library))
+
+SHARED_LIB_FLAGS := -shared -fPIC
+
+CC=g++
+
+.PHONY: all clean distclean
+
+all: $(program_NAME)
+ @- $(RM) $(program_OBJS)
+
+$(program_NAME): $(program_OBJS)
+ $(CC) $(CPPFLAGS) ${SHARED_LIB_FLAGS} $(program_OBJS) -o $(program_NAME)$(shell python3-config --extension-suffix) $(LDFLAGS)
+
+clean:
+ @- $(RM) $(program_NAME)$(shell python3-config --extension-suffix)
+ @- $(RM) $(program_OBJS)
+
+distclean: clean
diff --git a/sources/api/pySoftMC/example.py b/sources/api/pySoftMC/example.py
new file mode 100644
index 0000000..2dd2615
--- /dev/null
+++ b/sources/api/pySoftMC/example.py
@@ -0,0 +1,153 @@
+from pySoftMC import *
+import numpy as np
+
+# Initialize SoftMC platform
+platform = SoftMCPlatform()
+err = platform.init()
+if (err != 0):
+ print("SoftMC platform initialization failed!")
+ exit(-1)
+platform.reset_fpga()
+
+# Define registers
+CASR = 0
+BASR = 1
+RASR = 2
+BAR = 3
+RAR = 4
+CAR = 5
+PATTERN_REG = 6
+
+
+def initialize_row(platform: SoftMCPlatform, data32: int, bank: int, row: int):
+ p = Program()
+ p.add_inst(SMC_LI(bank, BAR))
+ p.add_inst(SMC_LI(row, RAR))
+ p.add_inst(SMC_LI(8, CASR))
+
+ p.add_inst(SMC_LI(data32, PATTERN_REG))
+ for i in range(16):
+ p.add_inst(SMC_LDWD(PATTERN_REG, i))
+
+ p.add_inst(SMC_PRE(BAR, 0, 0), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_LI(0, CAR))
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+
+ p.add_inst(SMC_ACT(BAR, 0, RAR, 0), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+
+ for i in range(128):
+ p.add_inst(SMC_WRITE(BAR, 0, CAR, 1, 0, 0), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_SLEEP(3))
+
+ p.add_inst(SMC_PRE(BAR, 0, 0), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+
+ p.add_inst(SMC_END())
+ platform.execute(p)
+
+def read_row(platform: SoftMCPlatform, bank: int, row: int):
+ p = Program()
+ p.add_inst(SMC_LI(bank, BAR))
+ p.add_inst(SMC_LI(row, RAR))
+ p.add_inst(SMC_LI(8, CASR))
+
+ p.add_inst(SMC_PRE(BAR, 0, 0), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_LI(0, CAR))
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+
+ p.add_inst(SMC_ACT(BAR, 0, RAR, 0), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+
+ for i in range(128):
+ p.add_inst(SMC_READ(BAR, 0, CAR, 1, 0, 0), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_SLEEP(3))
+
+ p.add_inst(SMC_PRE(BAR, 0, 0), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+
+ p.add_inst(SMC_END())
+ platform.execute(p)
+
+def hammer_row_double(bank, aggressor_row_1, aggressor_row_2, hammer_count, RAS_scale, RP_scale):
+ HMR_COUNTER_REG = 7
+ NUM_HMR_REG = 8
+
+ p = Program()
+ p.add_inst(SMC_LI(bank, BAR))
+ p.add_inst(SMC_LI(aggressor_row_1, RAR))
+
+ p.add_inst(SMC_LI(0, HMR_COUNTER_REG))
+ p.add_inst(SMC_LI(hammer_count, NUM_HMR_REG))
+
+ p.add_label("HMR_BEGIN")
+ if (RAS_scale > 1):
+ p.add_inst(SMC_SLEEP(5 * (RAS_scale - 1)))
+
+ p.add_inst(SMC_PRE(BAR, 0, 0), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_LI(aggressor_row_1, RAR))
+ if (RP_scale > 1):
+ p.add_inst(SMC_SLEEP(3 * (RP_scale - 1)))
+
+
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_ACT(BAR, 0, RAR, 0))
+ p.add_inst(SMC_LI(aggressor_row_2, RAR))
+ p.add_inst(SMC_SLEEP(5))
+ if (RAS_scale > 1):
+ p.add_inst(SMC_SLEEP(5 * (RAS_scale - 1)))
+
+ p.add_inst(SMC_PRE(BAR, 0, 0), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_ADDI(HMR_COUNTER_REG, 1, HMR_COUNTER_REG))
+ if (RP_scale > 1):
+ p.add_inst(SMC_SLEEP(3 * (RP_scale - 1)))
+
+
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_ACT(BAR, 0, RAR, 0))
+ p.add_branch(Program.BR_TYPE.BL, HMR_COUNTER_REG, NUM_HMR_REG, "HMR_BEGIN")
+
+
+ p.add_inst(SMC_NOP(), SMC_NOP(), SMC_NOP(), SMC_NOP())
+ p.add_inst(SMC_END())
+
+ platform.execute(p)
+
+def check_flips(buffer, pattern):
+ d = []
+ out = np.bitwise_xor(buffer, pattern)
+ for cl in range(128):
+ for w in range(8):
+ if (out[cl * 8 + w]) != 0:
+ _w = out[cl * 8 + w]
+ for byte in range(8):
+ for bit in range(8):
+ bit_id = np.uint64(byte * 8 + bit)
+ if ((np.right_shift(_w, bit_id)) & np.uint64(0x1)):
+ d.append((i, cl, w, byte, bit))
+ print(f"ITR {i}\tCL {cl}\t Word {w}\t Byte {byte}\t Bit {bit}")
+
+
+# Get the buffer from the underlying C++ API
+buffer = np.frombuffer(platform.get_buffer_memoryview()[:8192], dtype=np.uint64)
+
+ITR_COUNT=10
+ACT_COUNT = 100000
+pattern = 0xFFFFFFFF
+
+for i in range(ITR_COUNT):
+ initialize_row(platform, 0xFFFFFFFF, 0, 233)
+ initialize_row(platform, 0x0, 0, 234)
+ initialize_row(platform, 0xFFFFFFFF, 0, 235)
+
+ hammer_row_double(0, 233, 235, ACT_COUNT, 1, 1)
+
+ read_row(platform, 0, 234)
+ platform.receiveData(8*1024)
+
+ check_flips(buffer, 0x0)
+
diff --git a/sources/api/pySoftMC/pySoftMC.cpp b/sources/api/pySoftMC/pySoftMC.cpp
new file mode 100644
index 0000000..992d9a5
--- /dev/null
+++ b/sources/api/pySoftMC/pySoftMC.cpp
@@ -0,0 +1,95 @@
+#include "platform.h"
+#include "instruction.h"
+#include "prog.h"
+
+#include "ext/pybind11/include/pybind11/pybind11.h"
+namespace py = pybind11;
+
+PYBIND11_MODULE(pySoftMC, m)
+{
+ py::enum_<PC_TYPE>(m, "PC_TYPE")
+ .value("WRITE", PC_TYPE::WRITE)
+ .value("READ", PC_TYPE::READ)
+ .value("PRE", PC_TYPE::PRE)
+ .value("ACT", PC_TYPE::ACT)
+ .value("ZQ", PC_TYPE::ZQ)
+ .value("REF", PC_TYPE::REF)
+ .value("CYC", PC_TYPE::CYC)
+ .export_values();
+
+ m.def("SMC_LD", &SMC_LD, py::arg("rb"), py::arg("offset"), py::arg("rt"));
+ m.def("SMC_ST", &SMC_ST, py::arg("rb"), py::arg("offset"), py::arg("rt"));
+
+ m.def("SMC_AND", &SMC_AND, py::arg("rs1"), py::arg("rs2"), py::arg("rt"));
+ m.def("SMC_OR", &SMC_OR, py::arg("rs1"), py::arg("rs2"), py::arg("rt"));
+ m.def("SMC_XOR", &SMC_XOR, py::arg("rs1"), py::arg("rs2"), py::arg("rt"));
+ m.def("SMC_ADD", &SMC_ADD, py::arg("rs1"), py::arg("rs2"), py::arg("rt"));
+ m.def("SMC_ADDI", &SMC_ADDI, py::arg("rs1"), py::arg("imd"), py::arg("rt"));
+ m.def("SMC_SUB", &SMC_SUB, py::arg("rs1"), py::arg("rs2"), py::arg("rt"));
+ m.def("SMC_SUBI", &SMC_SUBI, py::arg("rs1"), py::arg("imd"), py::arg("rt"));
+ m.def("SMC_SRC", &SMC_SRC, py::arg("rs1"), py::arg("rt"));
+
+ m.def("SMC_LI", &SMC_LI, py::arg("imd"), py::arg("rt"));
+ m.def("SMC_MV", &SMC_MV, py::arg("rs1"), py::arg("rt"));
+
+ m.def("SMC_LDWD", &SMC_LDWD, py::arg("rs1"), py::arg("offset"));
+ m.def("SMC_LDPC", &SMC_LDPC, py::arg("type"), py::arg("rt"));
+
+ m.def("SMC_BL", &SMC_BL, py::arg("rs1"), py::arg("rs2"), py::arg("tgt"));
+ m.def("SMC_BEQ", &SMC_BEQ, py::arg("rs1"), py::arg("rs2"), py::arg("tgt"));
+ m.def("SMC_JUMP", &SMC_JUMP, py::arg("tgt"));
+
+ m.def("SMC_END", &SMC_END);
+ m.def("SMC_INFO", &SMC_INFO, py::arg("rdcnt"));
+ m.def("SMC_SLEEP", &SMC_SLEEP, py::arg("samt"));
+
+ m.def("SMC_WRITE", &SMC_WRITE, py::arg("bar"), py::arg("ibar"), py::arg("car"), py::arg("icar"), py::arg("BL4"), py::arg("ap"));
+ m.def("SMC_READ", &SMC_READ, py::arg("bar"), py::arg("ibar"), py::arg("car"), py::arg("icar"), py::arg("BL4"), py::arg("ap"));
+ m.def("SMC_PRE", &SMC_PRE, py::arg("bar"), py::arg("ibar"), py::arg("pall"));
+ m.def("SMC_ACT", &SMC_ACT, py::arg("bar"), py::arg("ibar"), py::arg("rar"), py::arg("irar"));
+
+ m.def("SMC_ZQ", &SMC_ZQ);
+ m.def("SMC_REF", &SMC_REF);
+ m.def("SMC_NOP", &SMC_NOP);
+ m.def("SMC_SRE", &SMC_SRE);
+ m.def("SMC_SRX", &SMC_SRX);
+
+ m.def("__pack_mininsts", &__pack_mininsts, py::arg("i1"), py::arg("i2"), py::arg("i3"), py::arg("i4"));
+
+ py::class_<SoftMCPlatform>(m, "SoftMCPlatform")
+ .def(py::init())
+ .def("init", &SoftMCPlatform::init)
+ .def("reset_fpga", &SoftMCPlatform::reset_fpga)
+ .def("execute", &SoftMCPlatform::execute, py::arg("program"))
+ .def("receiveData", &SoftMCPlatform::py_receiveData, py::arg("num_words"))
+ .def("get_buffer_memoryview", &SoftMCPlatform::get_buffer_memoryview)
+ .def("set_aref", &SoftMCPlatform::set_aref, py::arg("on"))
+ .def("readRegisterDump", &SoftMCPlatform::readRegisterDump)
+ .def("count_bitflips_in_row", &SoftMCPlatform::count_bitflips_in_row, py::arg("char_data"));
+
+ py::class_<Program> program(m, "Program");
+
+ py::enum_<Program::SEQ_TYPE>(program, "SEQ_TYPE")
+ .value("WRITE", Program::SEQ_TYPE::WRITE)
+ .value("READ", Program::SEQ_TYPE::READ)
+ .export_values();
+
+ py::enum_<Program::BR_TYPE>(program, "BR_TYPE")
+ .value("BEQ", Program::BR_TYPE::BEQ)
+ .value("BL", Program::BR_TYPE::BL)
+ .value("JUMP", Program::BR_TYPE::JUMP)
+ .export_values();
+
+ program
+ .def(py::init())
+ .def("add_mininst", &Program::add_mininst, py::arg("mi"), py::arg("wait_after"))
+ .def("add_wait", &Program::add_wait, py::arg("wait_cycles"))
+ .def("pack_minprogram", &Program::pack_minprogram)
+ .def("add_inst", py::overload_cast<Inst>(&Program::add_inst), py::arg("inst"))
+ .def("add_inst", py::overload_cast<Mininst, Mininst, Mininst, Mininst>(&Program::add_inst))
+ .def("add_label", &Program::add_label, py::arg("name"))
+ .def("add_branch", &Program::add_branch, py::arg("type"), py::arg("rs1"), py::arg("rs2"), py::arg("tgt"))
+ .def("add_below", &Program::add_below)
+ .def("pretty_print", &Program::pretty_print)
+ .def("dump_registers", &Program::dump_registers);
+}