#include #include #include #include extern "C" { #include } #include "Vinterface.h" #include "verilated.h" #ifdef TRACE_ENABLED #include "verilated_vcd_c.h" #endif #include "debug.h" #include "corundum.h" #include "coord.h" #include "dma.h" #include "mem.h" #define CLOCK_PERIOD (100 * 1000ULL) // 100ns -> 10MHz #define SYNC_PERIOD (500 * 1000ULL) // 100ns #define PCI_LATENCY (1 * 1000 * 1000ULL) // 1us #define ETH_LATENCY (1 * 1000 * 1000ULL) // 1us struct DMAOp; static volatile int exiting = 0; static uint64_t main_time = 0; static struct nicsim_params nsparams; #ifdef TRACE_ENABLED static VerilatedVcdC* trace; #endif static volatile union cosim_pcie_proto_d2h *d2h_alloc(void); static void sigint_handler(int dummy) { exiting = 1; } static void sigusr1_handler(int dummy) { fprintf(stderr, "main_time = %lu\n", main_time); } double sc_time_stamp() { return main_time; } static void reset_inputs(Vinterface *top) { top->clk = 0; top->rst = 0; top->m_axis_ctrl_dma_read_desc_ready = 0; top->s_axis_ctrl_dma_read_desc_status_tag = 0; top->s_axis_ctrl_dma_read_desc_status_valid = 0; top->m_axis_ctrl_dma_write_desc_ready = 0; top->s_axis_ctrl_dma_write_desc_status_tag = 0; top->s_axis_ctrl_dma_write_desc_status_valid = 0; top->m_axis_data_dma_read_desc_ready = 0; top->s_axis_data_dma_read_desc_status_tag = 0; top->s_axis_data_dma_read_desc_status_valid = 0; top->m_axis_data_dma_write_desc_ready = 0; top->s_axis_data_dma_write_desc_status_tag = 0; top->s_axis_data_dma_write_desc_status_valid = 0; top->s_axil_awaddr = 0; top->s_axil_awprot = 0; top->s_axil_awvalid = 0; top->s_axil_wdata = 0; top->s_axil_wstrb = 0; top->s_axil_wvalid = 0; top->s_axil_bready = 0; top->s_axil_araddr = 0; top->s_axil_arprot = 0; top->s_axil_arvalid = 0; top->s_axil_rready = 0; top->m_axil_csr_awready = 0; top->m_axil_csr_wready = 0; top->m_axil_csr_bresp = 0; top->m_axil_csr_bvalid = 0; top->m_axil_csr_arready = 0; top->m_axil_csr_rdata = 0; top->m_axil_csr_rresp = 0; top->m_axil_csr_rvalid = 0; top->ctrl_dma_ram_wr_cmd_sel = 0; //top->ctrl_dma_ram_wr_cmd_be = 0; //top->ctrl_dma_ram_wr_cmd_addr = 0; top->ctrl_dma_ram_wr_cmd_valid = 0; top->ctrl_dma_ram_rd_cmd_sel = 0; //top->ctrl_dma_ram_rd_cmd_addr = 0; top->ctrl_dma_ram_rd_cmd_valid = 0; top->ctrl_dma_ram_rd_resp_ready = 0; top->data_dma_ram_wr_cmd_sel = 0; //top->data_dma_ram_wr_cmd_be = 0; //top->data_dma_ram_wr_cmd_addr = 0; top->data_dma_ram_wr_cmd_valid = 0; top->data_dma_ram_rd_cmd_sel = 0; //top->data_dma_ram_rd_cmd_addr = 0; top->data_dma_ram_rd_cmd_valid = 0; top->data_dma_ram_rd_resp_ready = 0; top->tx_axis_tready = 0; top->s_axis_tx_ptp_ts_valid = 0; top->rx_axis_tkeep = 0; top->rx_axis_tvalid = 0; top->rx_axis_tlast = 0; top->rx_axis_tuser = 0; top->s_axis_rx_ptp_ts_valid = 0; top->ptp_ts_step = 0; } static void report_output(const char *label, uint64_t val) { if (val == 0) return; std::cout << " " << label << " = " << val << std::endl; } static void report_outputs(Vinterface *top) { report_output("m_axis_ctrl_dma_read_desc_dma_addr", top->m_axis_ctrl_dma_read_desc_dma_addr); report_output("m_axis_ctrl_dma_read_desc_ram_sel", top->m_axis_ctrl_dma_read_desc_ram_sel); report_output("m_axis_ctrl_dma_read_desc_ram_addr", top->m_axis_ctrl_dma_read_desc_ram_addr); report_output("m_axis_ctrl_dma_read_desc_len", top->m_axis_ctrl_dma_read_desc_len); report_output("m_axis_ctrl_dma_read_desc_tag", top->m_axis_ctrl_dma_read_desc_tag); report_output("m_axis_ctrl_dma_read_desc_valid", top->m_axis_ctrl_dma_read_desc_valid); report_output("m_axis_ctrl_dma_write_desc_dma_addr", top->m_axis_ctrl_dma_write_desc_dma_addr); report_output("m_axis_ctrl_dma_write_desc_ram_sel", top->m_axis_ctrl_dma_write_desc_ram_sel); report_output("m_axis_ctrl_dma_write_desc_ram_addr", top->m_axis_ctrl_dma_write_desc_ram_addr); report_output("m_axis_ctrl_dma_write_desc_len", top->m_axis_ctrl_dma_write_desc_len); report_output("m_axis_ctrl_dma_write_desc_tag", top->m_axis_ctrl_dma_write_desc_tag); report_output("m_axis_ctrl_dma_write_desc_valid", top->m_axis_ctrl_dma_write_desc_valid); report_output("m_axis_data_dma_read_desc_dma_addr", top->m_axis_data_dma_read_desc_dma_addr); report_output("m_axis_data_dma_read_desc_ram_sel", top->m_axis_data_dma_read_desc_ram_sel); report_output("m_axis_data_dma_read_desc_ram_addr", top->m_axis_data_dma_read_desc_ram_addr); report_output("m_axis_data_dma_read_desc_len", top->m_axis_data_dma_read_desc_len); report_output("m_axis_data_dma_read_desc_tag", top->m_axis_data_dma_read_desc_tag); report_output("m_axis_data_dma_read_desc_valid", top->m_axis_data_dma_read_desc_valid); report_output("m_axis_data_dma_write_desc_dma_addr", top->m_axis_data_dma_write_desc_dma_addr); report_output("m_axis_data_dma_write_desc_ram_sel", top->m_axis_data_dma_write_desc_ram_sel); report_output("m_axis_data_dma_write_desc_ram_addr", top->m_axis_data_dma_write_desc_ram_addr); report_output("m_axis_data_dma_write_desc_len", top->m_axis_data_dma_write_desc_len); report_output("m_axis_data_dma_write_desc_tag", top->m_axis_data_dma_write_desc_tag); report_output("m_axis_data_dma_write_desc_valid", top->m_axis_data_dma_write_desc_valid); report_output("s_axil_awready", top->s_axil_awready); report_output("s_axil_wready", top->s_axil_wready); report_output("s_axil_bresp", top->s_axil_bresp); report_output("s_axil_bvalid", top->s_axil_bvalid); report_output("s_axil_arready", top->s_axil_arready); report_output("s_axil_rdata", top->s_axil_rdata); report_output("s_axil_rresp", top->s_axil_rresp); report_output("s_axil_rvalid", top->s_axil_rvalid); report_output("m_axil_csr_awaddr", top->m_axil_csr_awaddr); report_output("m_axil_csr_awprot", top->m_axil_csr_awprot); report_output("m_axil_csr_awvalid", top->m_axil_csr_awvalid); report_output("m_axil_csr_wdata", top->m_axil_csr_wdata); report_output("m_axil_csr_wstrb", top->m_axil_csr_wstrb); report_output("m_axil_csr_wvalid", top->m_axil_csr_wvalid); report_output("m_axil_csr_bready", top->m_axil_csr_bready); report_output("m_axil_csr_araddr", top->m_axil_csr_araddr); report_output("m_axil_csr_arprot", top->m_axil_csr_arprot); report_output("m_axil_csr_arvalid", top->m_axil_csr_arvalid); report_output("m_axil_csr_rready", top->m_axil_csr_rready); report_output("ctrl_dma_ram_wr_cmd_ready", top->ctrl_dma_ram_wr_cmd_ready); report_output("ctrl_dma_ram_rd_cmd_ready", top->ctrl_dma_ram_rd_cmd_ready); report_output("ctrl_dma_ram_rd_resp_valid", top->ctrl_dma_ram_rd_resp_valid); report_output("data_dma_ram_wr_cmd_ready", top->data_dma_ram_wr_cmd_ready); report_output("data_dma_ram_rd_cmd_ready", top->data_dma_ram_rd_cmd_ready); report_output("data_dma_ram_rd_resp_valid", top->data_dma_ram_rd_resp_valid); report_output("tx_axis_tkeep", top->tx_axis_tkeep); report_output("tx_axis_tvalid", top->tx_axis_tvalid); report_output("tx_axis_tlast", top->tx_axis_tlast); report_output("tx_axis_tuser", top->tx_axis_tuser); report_output("s_axis_tx_ptp_ts_ready", top->s_axis_tx_ptp_ts_ready); report_output("rx_axis_tready", top->rx_axis_tready); report_output("s_axis_rx_ptp_ts_ready", top->s_axis_rx_ptp_ts_ready); report_output("msi_irq", top->msi_irq); } struct MMIOOp { uint64_t id; uint64_t addr; uint64_t value; size_t len; bool isWrite; }; class MMIOInterface { protected: enum OpState { AddrIssued, AddrAcked, AddrDone, }; Vinterface ⊤ PCICoordinator &coord; std::deque queue; MMIOOp *rCur, *wCur; enum OpState rState, wState; public: MMIOInterface(Vinterface &top_, PCICoordinator &coord_) : top(top_), coord(coord_), rCur(0), wCur(0) { } void step() { if (rCur) { /* work on active read operation */ if (rState == AddrIssued && top.s_axil_arready) { /* read handshake is complete */ top.s_axil_arvalid = 0; rState = AddrAcked; } if (rState == AddrAcked && top.s_axil_rvalid) { /* read data received */ top.s_axil_rready = 0; rCur->value = top.s_axil_rdata; coord.mmio_comp_enqueue(rCur); #ifdef MMIO_DEBUG std::cout << "MMIO: completed AXI read op=" << rCur << " val=" << rCur->value << std::endl; #endif rCur = 0; } } else if (wCur) { /* work on active write operation */ if (wState == AddrIssued && top.s_axil_awready) { /* write addr handshake is complete */ top.s_axil_awvalid = 0; wState = AddrAcked; } if (wState == AddrAcked && top.s_axil_wready) { /* write data handshake is complete */ top.s_axil_wvalid = 0; top.s_axil_bready = 1; wState = AddrDone; } if (wState == AddrDone && top.s_axil_bvalid) { /* write complete */ top.s_axil_bready = 0; // TODO: check top.s_axil_bresp #ifdef MMIO_DEBUG std::cout << "MMIO: completed AXI write op=" << wCur << std::endl; #endif coord.mmio_comp_enqueue(wCur); wCur = 0; } } else if (/*!top.clk &&*/ !queue.empty()) { /* issue new operation */ MMIOOp *op = queue.front(); queue.pop_front(); if (!op->isWrite) { /* issue new read */ rCur = op; rState = AddrIssued; top.s_axil_araddr = rCur->addr; top.s_axil_arprot = 0x0; top.s_axil_arvalid = 1; top.s_axil_rready = 1; } else { /* issue new write */ wCur = op; wState = AddrIssued; top.s_axil_awaddr = wCur->addr; top.s_axil_awprot = 0x0; top.s_axil_awvalid = 1; top.s_axil_wdata = wCur->value; top.s_axil_wstrb = 0xf; top.s_axil_wvalid = 1; } } } void issueRead(uint64_t id, uint64_t addr, size_t len) { MMIOOp *op = new MMIOOp; #ifdef MMIO_DEBUG std::cout << "MMIO: read id=" << id << " addr=" << std::hex << addr << " len=" << len << " op=" << op << std::endl; #endif op->id = id; op->addr = addr; op->len = len; op->isWrite = false; queue.push_back(op); } void issueWrite(uint64_t id, uint64_t addr, size_t len, uint64_t val) { MMIOOp *op = new MMIOOp; #ifdef MMIO_DEBUG std::cout << "MMIO: write id=" << id << " addr=" << std::hex << addr << " len=" << len << " val=" << val << " op=" << op << std::endl; #endif op->id = id; op->addr = addr; op->len = len; op->value = val; op->isWrite = true; queue.push_back(op); } }; void pci_rwcomp_issue(MMIOOp *op) { volatile union cosim_pcie_proto_d2h *msg = d2h_alloc(); volatile struct cosim_pcie_proto_d2h_readcomp *rc; volatile struct cosim_pcie_proto_d2h_writecomp *wc; if (!msg) throw "completion alloc failed"; if (op->isWrite) { wc = &msg->writecomp; wc->req_id = op->id; //WMB(); wc->own_type = COSIM_PCIE_PROTO_D2H_MSG_WRITECOMP | COSIM_PCIE_PROTO_D2H_OWN_HOST; } else { rc = &msg->readcomp; memcpy((void *) rc->data, &op->value, op->len); rc->req_id = op->id; //WMB(); rc->own_type = COSIM_PCIE_PROTO_D2H_MSG_READCOMP | COSIM_PCIE_PROTO_D2H_OWN_HOST; } delete op; } #if 0 class MemAccessor { protected: Vinterface ⊤ /* outputs to memory */ vluint8_t &p_mem_sel; vluint32_t (*p_mem_be)[4]; /* for write only */ vluint32_t (&p_mem_addr)[3]; vluint8_t &p_mem_valid; vluint8_t *p_mem_resp_ready; /* for read only */ /* direction depends */ vluint32_t (&p_mem_data)[32]; /* inputs from memory */ vluint8_t &p_mem_ready; vluint8_t *p_mem_resp_valid; /* for read only */ public: MemAccessor(Vinterface &top_, bool read, vluint8_t &p_mem_sel_, vluint32_t (*p_mem_be_)[4], vluint32_t (&p_mem_addr_)[3], vluint8_t &p_mem_valid_, vluint8_t *p_mem_resp_ready_, vluint32_t (&p_mem_data_)[32], vluint8_t &p_mem_ready_, vluint8_t *p_mem_resp_valid_) : top(top_), p_mem_sel(p_mem_sel_), p_mem_be(p_mem_be_), p_mem_addr(p_mem_addr_), p_mem_valid(p_mem_valid_), p_mem_resp_ready(p_mem_resp_ready_), p_mem_data(p_mem_data_), p_mem_ready(p_mem_ready_), p_mem_resp_valid(p_mem_resp_valid_) { } void step() { } }; #endif std::set pci_dma_pending; void pci_dma_issue(DMAOp *op) { volatile union cosim_pcie_proto_d2h *msg = d2h_alloc(); uint8_t ty; if (!msg) throw "completion alloc failed"; if (op->write) { volatile struct cosim_pcie_proto_d2h_write *write = &msg->write; write->req_id = (uintptr_t) op; write->offset = op->dma_addr; write->len = op->len; // TODO: check DMA length memcpy((void *) write->data, op->data, op->len); // WMB(); write->own_type = COSIM_PCIE_PROTO_D2H_MSG_WRITE | COSIM_PCIE_PROTO_D2H_OWN_HOST; } else { volatile struct cosim_pcie_proto_d2h_read *read = &msg->read; read->req_id = (uintptr_t) op; read->offset = op->dma_addr; read->len = op->len; // WMB(); read->own_type = COSIM_PCIE_PROTO_D2H_MSG_READ | COSIM_PCIE_PROTO_D2H_OWN_HOST; } pci_dma_pending.insert(op); } static void h2d_readcomp(volatile struct cosim_pcie_proto_h2d_readcomp *rc) { DMAOp *op = (DMAOp *) (uintptr_t) rc->req_id; if (pci_dma_pending.find(op) == pci_dma_pending.end()) throw "unexpected completion"; pci_dma_pending.erase(op); memcpy(op->data, (void *) rc->data, op->len); #if 0 std::cerr << "dma read comp: "; for (size_t i = 0; i < op->len; i++) std::cerr << (unsigned) op->data[i] << " "; std::cerr << std::endl; #endif op->engine->pci_op_complete(op); } static void h2d_writecomp(volatile struct cosim_pcie_proto_h2d_writecomp *wc) { DMAOp *op = (DMAOp *) (uintptr_t) wc->req_id; if (pci_dma_pending.find(op) == pci_dma_pending.end()) throw "unexpected completion"; pci_dma_pending.erase(op); op->engine->pci_op_complete(op); } static uint64_t csr_read(uint64_t off) { switch (off) { case 0x00: return 32; /* firmware id */ case 0x04: return 1; /* firmware version */ case 0x08: return 0x43215678; /* board id */ case 0x0c: return 0x1; /* board version */ case 0x10: return 1; /* phc count */ case 0x14: return 0x200; /* phc offset */ case 0x18: return 0x80; /* phc stride */ case 0x20: return 1; /* if_count */ case 0x24: return 0x80000; /* if stride */ case 0x2c: return 0x80000; /* if csr offset */ case 0x200: return 0x1; /* phc features */ default: std::cerr << "csr_read(" << off << ") unimplemented" << std::endl; return 0; } } static void csr_write(uint64_t off, uint64_t val) { } static void h2d_read(MMIOInterface &mmio, volatile struct cosim_pcie_proto_h2d_read *read) { //std::cout << "got read " << read->offset << std::endl; if (read->offset < 0x80000) { volatile union cosim_pcie_proto_d2h *msg = d2h_alloc(); volatile struct cosim_pcie_proto_d2h_readcomp *rc; if (!msg) throw "completion alloc failed"; rc = &msg->readcomp; memset((void *) rc->data, 0, read->len); uint64_t val = csr_read(read->offset); memcpy((void *) rc->data, &val, read->len); rc->req_id = read->req_id; //WMB(); rc->own_type = COSIM_PCIE_PROTO_D2H_MSG_READCOMP | COSIM_PCIE_PROTO_D2H_OWN_HOST; } else { /*printf("read(bar=%u, off=%lu, len=%u) = %lu\n", read->bar, read->offset, read->len, val);*/ mmio.issueRead(read->req_id, read->offset, read->len); } } static void h2d_write(MMIOInterface &mmio, volatile struct cosim_pcie_proto_h2d_write *write) { uint64_t val = 0; memcpy(&val, (void *) write->data, write->len); //std::cout << "got write " << write->offset << " = " << val << std::endl; if (write->offset < 0x80000) { volatile union cosim_pcie_proto_d2h *msg = d2h_alloc(); volatile struct cosim_pcie_proto_d2h_writecomp *wc; if (!msg) throw "completion alloc failed"; csr_write(write->offset, val); wc = &msg->writecomp; wc->req_id = write->req_id; //WMB(); wc->own_type = COSIM_PCIE_PROTO_D2H_MSG_WRITECOMP | COSIM_PCIE_PROTO_D2H_OWN_HOST; } else { mmio.issueWrite(write->req_id, write->offset, write->len, val); } } static void poll_h2d(MMIOInterface &mmio) { volatile union cosim_pcie_proto_h2d *msg = nicif_h2d_poll(&nsparams, main_time); uint8_t t; if (msg == NULL) return; t = msg->dummy.own_type & COSIM_PCIE_PROTO_H2D_MSG_MASK; //std::cerr << "poll_h2d: polled type=" << (int) t << std::endl; switch (t) { case COSIM_PCIE_PROTO_H2D_MSG_READ: h2d_read(mmio, &msg->read); break; case COSIM_PCIE_PROTO_H2D_MSG_WRITE: h2d_write(mmio, &msg->write); break; case COSIM_PCIE_PROTO_H2D_MSG_READCOMP: h2d_readcomp(&msg->readcomp); break; case COSIM_PCIE_PROTO_H2D_MSG_WRITECOMP: h2d_writecomp(&msg->writecomp); break; case COSIM_PCIE_PROTO_H2D_MSG_SYNC: break; default: std::cerr << "poll_h2d: unsupported type=" << t << std::endl; } nicif_h2d_done(msg); nicif_h2d_next(); }; static volatile union cosim_pcie_proto_d2h *d2h_alloc(void) { return nicsim_d2h_alloc(&nsparams, main_time); } class EthernetTx { protected: Vinterface ⊤ uint8_t packet_buf[2048]; size_t packet_len; public: EthernetTx(Vinterface &top_) : top(top_), packet_len(0) { } void packet_done() { volatile union cosim_eth_proto_d2n *msg = nicsim_d2n_alloc(&nsparams, main_time); volatile struct cosim_eth_proto_d2n_send *send; if (!msg) throw "completion alloc failed"; send = &msg->send; memcpy((void *) send->data, packet_buf, packet_len); send->len = packet_len; send->timestamp = main_time + ETH_LATENCY; //WMB(); send->own_type = COSIM_ETH_PROTO_D2N_MSG_SEND | COSIM_ETH_PROTO_D2N_OWN_NET; #ifdef ETH_DEBUG std::cerr << "EthernetTx: packet len=" << std::hex << packet_len << " "; for (size_t i = 0; i < packet_len; i++) { std::cerr << (unsigned) packet_buf[i] << " "; } std::cerr << std::endl; #endif } void step() { top.tx_axis_tready = 1; if (top.tx_axis_tvalid) { /* iterate over all 8 bytes */ for (size_t i = 0; i < 8; i++) { if ((top.tx_axis_tkeep & (1 << i)) != 0) { assert(packet_len < 2048); packet_buf[packet_len++] = (top.tx_axis_tdata >> (i * 8)); } } if (top.tx_axis_tlast) { packet_done(); packet_len = 0; } } } }; class EthernetRx { protected: Vinterface ⊤ static const size_t FIFO_SIZE = 32; uint8_t fifo_bufs[FIFO_SIZE][2048]; size_t fifo_lens[FIFO_SIZE]; size_t fifo_pos_rd; size_t fifo_pos_wr; size_t packet_off; public: EthernetRx(Vinterface &top_) : top(top_), fifo_pos_rd(0), fifo_pos_wr(0), packet_off(0) { for (size_t i = 0; i < FIFO_SIZE; i++) fifo_lens[i] = 0; } void packet_received(const void *data, size_t len) { if (fifo_lens[fifo_pos_wr] != 0) { std::cerr << "EthernetRx: dropping packet" << std::endl; return; } memcpy(fifo_bufs[fifo_pos_wr], data, len); fifo_lens[fifo_pos_wr] = len; #ifdef ETH_DEBUG std::cout << "rx into " << fifo_pos_wr << std::endl; std::cerr << "EthernetRx: packet len=" << std::hex << len << " "; for (size_t i = 0; i < len; i++) { std::cerr << (unsigned) fifo_bufs[fifo_pos_wr][i] << " "; } std::cerr << std::endl; #endif fifo_pos_wr = (fifo_pos_wr + 1) % FIFO_SIZE; } void step() { if (fifo_lens[fifo_pos_rd] != 0) { // we have data to send if (packet_off != 0 && !top.rx_axis_tready) { // no ready signal, can't advance std::cerr << "eth rx: no ready" << std::endl; } else if (packet_off == fifo_lens[fifo_pos_rd]) { // done with packet #ifdef ETH_DEBUG std::cerr << "EthernetRx: finished packet" << std::endl; #endif top.rx_axis_tvalid = 0; top.rx_axis_tlast = 0; packet_off = 0; fifo_lens[fifo_pos_rd] = 0; fifo_pos_rd = (fifo_pos_rd + 1) % FIFO_SIZE; } else { // put out more packet data #ifdef ETH_DEBUG std::cerr << "EthernetRx: push flit " << packet_off << std::endl; if (packet_off == 0) std::cout << "rx from " << fifo_pos_rd << std::endl; #endif top.rx_axis_tkeep = 0; top.rx_axis_tdata = 0; size_t i; for (i = 0; i < 8 && packet_off < fifo_lens[fifo_pos_rd]; i++) { top.rx_axis_tdata |= ((uint64_t) fifo_bufs[fifo_pos_rd][packet_off]) << (i * 8); top.rx_axis_tkeep |= (1 << i); packet_off++; } top.rx_axis_tvalid = 1; top.rx_axis_tlast = (packet_off == fifo_lens[fifo_pos_rd]); } //trace->dump(main_time); } else { // no data top.rx_axis_tvalid = 0; top.rx_axis_tlast = 0; } } }; static void n2d_recv(EthernetRx &rx, volatile struct cosim_eth_proto_n2d_recv *recv) { rx.packet_received((const void *) recv->data, recv->len); } static void poll_n2d(EthernetRx &rx) { volatile union cosim_eth_proto_n2d *msg = nicif_n2d_poll(&nsparams, main_time); uint8_t t; if (msg == NULL) return; t = msg->dummy.own_type & COSIM_ETH_PROTO_N2D_MSG_MASK; switch (t) { case COSIM_ETH_PROTO_N2D_MSG_RECV: n2d_recv(rx, &msg->recv); break; case COSIM_ETH_PROTO_N2D_MSG_SYNC: break; default: std::cerr << "poll_n2d: unsupported type=" << t << std::endl; } nicif_n2d_done(msg); nicif_n2d_next(); } #if 0 class PCICoordinator { protected: struct PCIOp { union { DMAOp *dma_op; uint32_t msi_vec; }; bool isDma; bool ready; }; Vinterface ⊤ std::deque queue; std::map dmamap; void process() { PCIOp *op; while (queue.empty()) { op = queue.front(); if (!op->ready) break; queue.pop_front(); if (!op->isDma) { pci_msi_issue(op->msi_vec); delete op; } else { pci_dma_issue(op->dma_op); dmamap.erase(op->dma_op); delete op; } } } public: PCICoordinator(Vinterface &top_) : top(top_) { } void dma_register(DMAOp *dma_op, bool ready) { PCIOp *op = new PCIOp; op->dma_op = vec; op->isDma = true; op->ready = ready; queue.push_back(op); dmamap[op] = dma_op; process(); } void dma_mark_ready(DMAOp *op) { dmamap[op]->ready = true; process(); } void msi_enqueue(uint32_t vec) { PCIOp *op = new PCIOp; op->msi_vec = vec; op->isDma = false; op->ready = true; queue.push_back(op); process(); } }; #endif void pci_msi_issue(uint8_t vec) { volatile union cosim_pcie_proto_d2h *msg = d2h_alloc(); volatile struct cosim_pcie_proto_d2h_interrupt *intr; #ifdef MSI_DEBUG std::cerr << "MSI interrupt vec=" << (int) vec << std::endl; #endif intr = &msg->interrupt; intr->vector = vec; intr->inttype = COSIM_PCIE_PROTO_INT_MSI; // WMB(); intr->own_type = COSIM_PCIE_PROTO_D2H_MSG_INTERRUPT | COSIM_PCIE_PROTO_D2H_OWN_HOST; } static void msi_step(Vinterface &top, PCICoordinator &coord) { if (!top.msi_irq) return; #ifdef MSI_DEBUG std::cerr << "msi_step: MSI interrupt raw vec=" << (int) top.msi_irq << std::endl; #endif for (size_t i = 0; i < 32; i++) { if (!((1ULL << i) & top.msi_irq)) continue; coord.msi_enqueue(i); } } int main(int argc, char *argv[]) { char *vargs[2] = { argv[0], NULL }; Verilated::commandArgs(1, vargs); #ifdef TRACE_ENABLED Verilated::traceEverOn(true); #endif if (argc != 4 && argc != 5) { fprintf(stderr, "Usage: corundum_verilator PCI-SOCKET ETH-SOCKET " "SHM [START-TICK]\n"); return EXIT_FAILURE; } if (argc == 5) main_time = strtoull(argv[4], NULL, 0); struct cosim_pcie_proto_dev_intro di; memset(&di, 0, sizeof(di)); di.bars[0].len = 1 << 24; di.bars[0].flags = COSIM_PCIE_PROTO_BAR_64; di.pci_vendor_id = 0x5543; di.pci_device_id = 0x1001; di.pci_class = 0x02; di.pci_subclass = 0x00; di.pci_revision = 0x00; di.pci_msi_nvecs = 32; nsparams.sync_pci = 1; nsparams.sync_eth = 1; nsparams.pci_socket_path = argv[1]; nsparams.eth_socket_path = argv[2]; nsparams.shm_path = argv[3]; nsparams.pci_latency = PCI_LATENCY; nsparams.eth_latency = ETH_LATENCY; nsparams.sync_delay = SYNC_PERIOD; if (nicsim_init(&nsparams, &di)) { return EXIT_FAILURE; } std::cout << "sync_pci=" << nsparams.sync_pci << " sync_eth=" << nsparams.sync_eth << std::endl; signal(SIGINT, sigint_handler); signal(SIGUSR1, sigusr1_handler); Vinterface *top = new Vinterface; #ifdef TRACE_ENABLED trace = new VerilatedVcdC; top->trace(trace, 99); trace->open("debug.vcd"); #endif MemWritePort p_mem_write_ctrl_dma( top->ctrl_dma_ram_wr_cmd_sel, top->ctrl_dma_ram_wr_cmd_be, top->ctrl_dma_ram_wr_cmd_addr, top->ctrl_dma_ram_wr_cmd_data, top->ctrl_dma_ram_wr_cmd_valid, top->ctrl_dma_ram_wr_cmd_ready); MemReadPort p_mem_read_ctrl_dma( top->ctrl_dma_ram_rd_cmd_sel, top->ctrl_dma_ram_rd_cmd_addr, top->ctrl_dma_ram_rd_cmd_valid, top->ctrl_dma_ram_rd_resp_ready, top->ctrl_dma_ram_rd_resp_data, top->ctrl_dma_ram_rd_cmd_ready, top->ctrl_dma_ram_rd_resp_valid); MemWritePort p_mem_write_data_dma( top->data_dma_ram_wr_cmd_sel, top->data_dma_ram_wr_cmd_be, top->data_dma_ram_wr_cmd_addr, top->data_dma_ram_wr_cmd_data, top->data_dma_ram_wr_cmd_valid, top->data_dma_ram_wr_cmd_ready); MemReadPort p_mem_read_data_dma( top->data_dma_ram_rd_cmd_sel, top->data_dma_ram_rd_cmd_addr, top->data_dma_ram_rd_cmd_valid, top->data_dma_ram_rd_resp_ready, top->data_dma_ram_rd_resp_data, top->data_dma_ram_rd_cmd_ready, top->data_dma_ram_rd_resp_valid); DMAPorts p_dma_read_ctrl( top->m_axis_ctrl_dma_read_desc_dma_addr, top->m_axis_ctrl_dma_read_desc_ram_sel, top->m_axis_ctrl_dma_read_desc_ram_addr, top->m_axis_ctrl_dma_read_desc_len, top->m_axis_ctrl_dma_read_desc_tag, top->m_axis_ctrl_dma_read_desc_valid, top->m_axis_ctrl_dma_read_desc_ready, top->s_axis_ctrl_dma_read_desc_status_tag, top->s_axis_ctrl_dma_read_desc_status_valid); DMAPorts p_dma_write_ctrl( top->m_axis_ctrl_dma_write_desc_dma_addr, top->m_axis_ctrl_dma_write_desc_ram_sel, top->m_axis_ctrl_dma_write_desc_ram_addr, top->m_axis_ctrl_dma_write_desc_len, top->m_axis_ctrl_dma_write_desc_tag, top->m_axis_ctrl_dma_write_desc_valid, top->m_axis_ctrl_dma_write_desc_ready, top->s_axis_ctrl_dma_write_desc_status_tag, top->s_axis_ctrl_dma_write_desc_status_valid); DMAPorts p_dma_read_data( top->m_axis_data_dma_read_desc_dma_addr, top->m_axis_data_dma_read_desc_ram_sel, top->m_axis_data_dma_read_desc_ram_addr, top->m_axis_data_dma_read_desc_len, top->m_axis_data_dma_read_desc_tag, top->m_axis_data_dma_read_desc_valid, top->m_axis_data_dma_read_desc_ready, top->s_axis_data_dma_read_desc_status_tag, top->s_axis_data_dma_read_desc_status_valid); DMAPorts p_dma_write_data( top->m_axis_data_dma_write_desc_dma_addr, top->m_axis_data_dma_write_desc_ram_sel, top->m_axis_data_dma_write_desc_ram_addr, top->m_axis_data_dma_write_desc_len, top->m_axis_data_dma_write_desc_tag, top->m_axis_data_dma_write_desc_valid, top->m_axis_data_dma_write_desc_ready, top->s_axis_data_dma_write_desc_status_tag, top->s_axis_data_dma_write_desc_status_valid); PCICoordinator pci_coord; MMIOInterface mmio(*top, pci_coord); MemWriter mem_control_writer(p_mem_write_ctrl_dma); MemReader mem_control_reader(p_mem_read_ctrl_dma); MemWriter mem_data_writer(p_mem_write_data_dma); MemReader mem_data_reader(p_mem_read_data_dma); DMAReader dma_read_ctrl("read ctrl", p_dma_read_ctrl, mem_control_writer, pci_coord); DMAWriter dma_write_ctrl("write ctrl", p_dma_write_ctrl, mem_control_reader, pci_coord); DMAReader dma_read_data("read data", p_dma_read_data, mem_data_writer, pci_coord); DMAWriter dma_write_data("write data", p_dma_write_data, mem_data_reader, pci_coord); EthernetTx tx(*top); EthernetRx rx(*top); reset_inputs(top); top->rst = 1; top->eval(); /* raising edge */ top->clk = !top->clk; top->eval(); top->rst = 0; while (!exiting) { while (nicsim_sync(&nsparams, main_time)) { std::cerr << "warn: nicsim_sync failed (t=" << main_time << ")" << std::endl; } do { poll_h2d(mmio); poll_n2d(rx); } while ((nsparams.sync_pci || nsparams.sync_eth) && netsim_next_timestamp(&nsparams) <= main_time && !exiting); /* falling edge */ top->clk = !top->clk; main_time += CLOCK_PERIOD / 2; top->eval(); mmio.step(); dma_read_ctrl.step(); dma_write_ctrl.step(); dma_read_data.step(); dma_write_data.step(); mem_control_writer.step(); mem_control_reader.step(); mem_data_writer.step(); mem_data_reader.step(); tx.step(); rx.step(); msi_step(*top, pci_coord); /* raising edge */ top->clk = !top->clk; main_time += CLOCK_PERIOD / 2; //top->s_axis_tx_ptp_ts_96 = main_time; top->s_axis_tx_ptp_ts_valid = 1; top->s_axis_rx_ptp_ts_valid = 1; top->eval(); } report_outputs(top); std::cout << std::endl << std::endl << "main_time:" << main_time << std::endl; #ifdef TRACE_ENABLED trace->dump(main_time + 1); trace->close(); #endif top->final(); delete top; return 0; }