diff options
| author | Ataberk <olgunataberk@gmail.com> | 2022-09-25 17:22:03 +0200 |
|---|---|---|
| committer | Ataberk <olgunataberk@gmail.com> | 2022-09-25 17:22:03 +0200 |
| commit | dc0b3db1b4f1895a07e5fe280ee3790e87f97b9f (patch) | |
| tree | b47203aa281bdd959def4451c84d310cd9cf2e12 /sources/api | |
| download | dram-bender-dc0b3db1b4f1895a07e5fe280ee3790e87f97b9f.tar.gz | |
Initial commit
Diffstat (limited to 'sources/api')
| -rw-r--r-- | sources/api/.gitignore | 2 | ||||
| -rw-r--r-- | sources/api/board.cpp | 132 | ||||
| -rw-r--r-- | sources/api/board.h | 35 | ||||
| -rw-r--r-- | sources/api/instruction.cpp | 586 | ||||
| -rw-r--r-- | sources/api/instruction.h | 200 | ||||
| -rw-r--r-- | sources/api/lexyacc/Makefile | 29 | ||||
| -rw-r--r-- | sources/api/lexyacc/example.smc | 102 | ||||
| -rw-r--r-- | sources/api/lexyacc/smc.l | 77 | ||||
| -rw-r--r-- | sources/api/lexyacc/smc.y | 427 | ||||
| -rw-r--r-- | sources/api/platform.cpp | 296 | ||||
| -rw-r--r-- | sources/api/platform.h | 83 | ||||
| -rw-r--r-- | sources/api/prog.cpp | 675 | ||||
| -rw-r--r-- | sources/api/prog.h | 120 | ||||
| -rw-r--r-- | sources/api/pySoftMC/.gitignore | 1 | ||||
| -rw-r--r-- | sources/api/pySoftMC/Makefile | 36 | ||||
| -rw-r--r-- | sources/api/pySoftMC/example.py | 153 | ||||
| -rw-r--r-- | sources/api/pySoftMC/pySoftMC.cpp | 95 |
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); +} |
