From 7e4399e3acd64bd5430dd427dc914484eb4c7071 Mon Sep 17 00:00:00 2001 From: Blaise Tine Date: Thu, 2 Apr 2020 05:10:51 -0400 Subject: [PATCH] OPAE HW full redesign - basic test passing --- driver/hw/Makefile | 3 + driver/hw/ccip_std_afu.sv | 2 +- driver/hw/sources.txt | 3 + driver/hw/vortex_afu.json | 14 +- driver/hw/vortex_afu.sv | 1031 ++++++++++++++++------------------ driver/sw/opae/Makefile | 6 +- driver/sw/opae/vortex.cpp | 303 +++++----- driver/sw/rtlsim/vortex.cpp | 21 +- driver/sw/simx/Makefile | 2 +- driver/sw/simx/vortex.cpp | 19 +- driver/tests/basic/Makefile | 2 +- driver/tests/basic/basic | Bin 18968 -> 40960 bytes driver/tests/basic/basic.cpp | 146 +++-- driver/tests/demo/Makefile | 2 +- driver/tests/demo/demo | Bin 45248 -> 40192 bytes driver/tests/demo/demo.cpp | 193 +++---- 16 files changed, 844 insertions(+), 903 deletions(-) diff --git a/driver/hw/Makefile b/driver/hw/Makefile index cd91bd11..74d99109 100644 --- a/driver/hw/Makefile +++ b/driver/hw/Makefile @@ -17,6 +17,9 @@ $(BUILD_DIR)/Makefile: run-ase: cd $(BUILD_DIR) && MENT_VSIM_OPT="-dpicpppath /usr/bin/gcc" make sim +wave: + vsim -view $(BUILD_DIR)/work/vsim.wlf -do wave.do + run-fpga: # TODO diff --git a/driver/hw/ccip_std_afu.sv b/driver/hw/ccip_std_afu.sv index aaf4cd23..e9791b60 100644 --- a/driver/hw/ccip_std_afu.sv +++ b/driver/hw/ccip_std_afu.sv @@ -108,7 +108,7 @@ module ccip_std_afu #( .NUM_LOCAL_MEM_BANKS(NUM_LOCAL_MEM_BANKS) ) - hello_mem_afu_inst + vortex_afu_inst ( .clk (clk), .SoftReset (reset_T1), diff --git a/driver/hw/sources.txt b/driver/hw/sources.txt index 23d2cbf7..be07bef5 100644 --- a/driver/hw/sources.txt +++ b/driver/hw/sources.txt @@ -1,5 +1,7 @@ vortex_afu.json ++define+GLOBAL_BLOCK_SIZE_BYTES=64 + +incdir+. +incdir+../../rtl +incdir+../../rtl/shared_memory @@ -13,6 +15,7 @@ vortex_afu.json ../../rtl/VX_define.v ../../rtl/VX_cache/VX_cache_config.v ../../rtl/Vortex_SOC.v +../../rtl/Vortex_Cluster.v ../../rtl/Vortex.v ../../rtl/VX_front_end.v ../../rtl/VX_back_end.v diff --git a/driver/hw/vortex_afu.json b/driver/hw/vortex_afu.json index 3c9b3bb3..c8adc2e0 100644 --- a/driver/hw/vortex_afu.json +++ b/driver/hw/vortex_afu.json @@ -3,7 +3,19 @@ "afu-image": { "power": 0, "clock-frequency-high": "auto", - "clock-frequency-low": "auto", + "clock-frequency-low": "auto", + + "mmio-csr-cmd": 10, + "mmio-csr-status": 12, + "mmio-csr-io-addr": 14, + "mmio-csr-mem-addr": 16, + "mmio-csr-data-size": 18, + + "cmd-type-read": 1, + "cmd-type-write": 2, + "cmd-type-run": 3, + "cmd-type-snoop": 4, + "afu-top-interface": { "class": "ccip_std_afu_avalon_mm", diff --git a/driver/hw/vortex_afu.sv b/driver/hw/vortex_afu.sv index 0ef03275..11ae0ddb 100644 --- a/driver/hw/vortex_afu.sv +++ b/driver/hw/vortex_afu.sv @@ -1,5 +1,3 @@ -// Code reused from Intel OPAE's 04_local_memory sample program with changes made to fit Vortex - // Interface between CSR and FSM // All the MMIOs read/write are done from CSR and passed to the FSM for state transitions @@ -21,618 +19,565 @@ module vortex_afu #( input t_if_ccip_Rx cp2af_sRxPort, output t_if_ccip_Tx af2cp_sTxPort, - // Avalong signals for local memory access - output t_local_mem_data avs_writedata, - input t_local_mem_data avs_readdata, - output t_local_mem_addr avs_address, - input logic avs_waitrequest, - output logic avs_write, - output logic avs_read, - output t_local_mem_byte_mask avs_byteenable, - output t_local_mem_burst_cnt avs_burstcount, - input avs_readdatavalid, + // Avalon signals for local memory access + output t_local_mem_data avs_writedata, + input t_local_mem_data avs_readdata, + output t_local_mem_addr avs_address, + input logic avs_waitrequest, + output logic avs_write, + output logic avs_read, + output t_local_mem_byte_mask avs_byteenable, + output t_local_mem_burst_cnt avs_burstcount, + input avs_readdatavalid, output logic [$clog2(NUM_LOCAL_MEM_BANKS)-1:0] mem_bank_select ); +localparam AVS_RD_QUEUE_SIZE = 16; +localparam VX_SNOOPING_DELAY = 300; + localparam AFU_ID_L = 16'h0002; // AFU ID Lower localparam AFU_ID_H = 16'h0004; // AFU ID Higher -localparam MEM_ADDRESS = 16'h0040; // AVMM Master Address -localparam MEM_BURSTCOUNT = 16'h0042; // AVMM Master Burst Count -localparam MEM_RDWR = 16'h0044; // AVMM Master Read/Write -localparam MEM_BANK_SELECT = 16'h0064; // Memory bank selection register -localparam READY_FOR_SW_CMD = 16'h0066; // "Ready for sw cmd" register. S/w must poll this register before issuing a read/write command to fsm -localparam MEM_BYTEENABLE = 16'h0068; // Test byteenable -// Added by Apurve to supporead and writeChange address size to buffer's address size -localparam DATA_SIZE = 16'h0046; // MMIO set by SW to denote the size od data to read/write -localparam BUFFER_IO_ADDRESS = 16'h0048; // MMIO set by SW to denote the buffer address space +localparam CMD_TYPE_READ = `AFU_IMAGE_CMD_TYPE_READ; +localparam CMD_TYPE_WRITE = `AFU_IMAGE_CMD_TYPE_WRITE; +localparam CMD_TYPE_RUN = `AFU_IMAGE_CMD_TYPE_RUN; +localparam CMD_TYPE_SNOOP = `AFU_IMAGE_CMD_TYPE_SNOOP; + +localparam MMIO_CSR_CMD = `AFU_IMAGE_MMIO_CSR_CMD; +localparam MMIO_CSR_STATUS = `AFU_IMAGE_MMIO_CSR_STATUS; +localparam MMIO_CSR_IO_ADDR = `AFU_IMAGE_MMIO_CSR_IO_ADDR; +localparam MMIO_CSR_MEM_ADDR = `AFU_IMAGE_MMIO_CSR_MEM_ADDR; +localparam MMIO_CSR_DATA_SIZE = `AFU_IMAGE_MMIO_CSR_DATA_SIZE; logic [127:0] afu_id = `AFU_ACCEL_UUID; -// cast c0 header into ReqMmioHdr -t_ccip_c0_ReqMmioHdr mmioHdr; -assign mmioHdr = t_ccip_c0_ReqMmioHdr'(cp2af_sRxPort.c0.hdr); +typedef enum logic[2:0] { + STATE_IDLE, + STATE_READ, + STATE_WRITE, + STATE_RUN, + STATE_SNOOP1, + STATE_SNOOP2 +} state_t; -logic [2:0] mem_RDWR = '0; - -//-- -logic ready_for_sw_cmd; -logic run_vortex; - -logic [15:0] avm_data_size; -t_ccip_clAddr avm_write_buffer_address; -t_ccip_clAddr avm_read_buffer_address; -logic avm_read; -logic avm_write; -t_local_mem_addr avm_address; -t_local_mem_burst_cnt avm_burstcount; -t_local_mem_byte_mask avm_byteenable; - -// Vortex signals - -logic vx_reset; -logic vx_dram_req; -logic vx_dram_req_write; -logic vx_dram_req_read; -logic vx_ebreak; -logic [31:0] vx_dram_req_addr; -logic [31:0] vx_local_addr; -logic [31:0] vx_dram_req_size; -logic [31:0] vx_count; -logic vx_dram_fill_rsp; - -logic [31:0] vx_dram_req_data[15:0]; -logic [31:0] vx_dram_fill_rsp_data[15:0]; -logic vx_dram_fill_accept; -logic [31:0] vx_dram_fill_rsp_addr; -logic [31:0] vx_dram_expected_lat; - -// -// MMIO control threads -// -always@(posedge clk) begin - if(SoftReset) begin - af2cp_sTxPort.c2.hdr <= '0; - af2cp_sTxPort.c2.data <= '0; - af2cp_sTxPort.c2.mmioRdValid <= '0; - avm_address <= '0; - avm_read <= '0; - avm_write <= '0; - avm_burstcount <= 12'd1; - mem_RDWR <= '0; - mem_bank_select <= 1'b1; - - // Change address size to buffer's address size - avm_data_size <= '0; - avm_write_buffer_address <= '0; - avm_read_buffer_address <= '0; - run_vortex <= '0; - end - else begin - af2cp_sTxPort.c2.mmioRdValid <= 0; - avm_read <= mem_RDWR[0] & mem_RDWR[1]; //[0] enable [1] 0-WR,1-RD - avm_write <= mem_RDWR[0] & !mem_RDWR[1]; - - // Added by Apurve. Run vortex whem RDWR is 7 - run_vortex <= mem_RDWR[0] & mem_RDWR[1] & mem_RDWR[2]; - - // set the registers on MMIO write request - // these are user-defined AFU registers at offset 0x40 and 0x41 - if(cp2af_sRxPort.c0.mmioWrValid == 1) - begin - case(mmioHdr.address) - MEM_ADDRESS: avm_address <= t_local_mem_addr'(cp2af_sRxPort.c0.data); - MEM_BURSTCOUNT: avm_burstcount <= cp2af_sRxPort.c0.data[11:0]; - MEM_RDWR: mem_RDWR <= cp2af_sRxPort.c0.data[2:0]; - MEM_BANK_SELECT: mem_bank_select <= $bits(mem_bank_select)'(cp2af_sRxPort.c0.data); - // Added by Apurve to support read and write buffers. Change address size to buffer's address size - DATA_SIZE:avm_data_size <= cp2af_sRxPort.c0.data[15:0]; - - BUFFER_IO_ADDRESS: begin - avm_write_buffer_address <= t_ccip_clAddr'(cp2af_sRxPort.c0.data); - avm_read_buffer_address <= t_ccip_clAddr'(cp2af_sRxPort.c0.data); - end - endcase - end - - // serve MMIO read requests - if(cp2af_sRxPort.c0.mmioRdValid == 1) - begin - af2cp_sTxPort.c2.hdr.tid <= mmioHdr.tid; // copy TID - case(mmioHdr.address) - // AFU header - 16'h0000: af2cp_sTxPort.c2.data <= { - 4'b0001, // Feature type = AFU - 8'b0, // reserved - 4'b0, // afu minor revision = 0 - 7'b0, // reserved - 1'b1, // end of DFH list = 1 - 24'b0, // next DFH offset = 0 - 4'b0, // afu major revision = 0 - 12'b0 // feature ID = 0 - }; - AFU_ID_L: af2cp_sTxPort.c2.data <= afu_id[63:0]; // afu id low - AFU_ID_H: af2cp_sTxPort.c2.data <= afu_id[127:64]; // afu id hi - 16'h0006: af2cp_sTxPort.c2.data <= 64'h0; // next AFU - 16'h0008: af2cp_sTxPort.c2.data <= 64'h0; // reserved - MEM_ADDRESS: af2cp_sTxPort.c2.data <= 64'(avm_address); - MEM_BURSTCOUNT: af2cp_sTxPort.c2.data <= 64'(avm_burstcount); - MEM_RDWR: af2cp_sTxPort.c2.data <= {62'd0, mem_RDWR}; - READY_FOR_SW_CMD: af2cp_sTxPort.c2.data <= ready_for_sw_cmd; - MEM_BANK_SELECT: af2cp_sTxPort.c2.data <= 64'(mem_bank_select); - default: af2cp_sTxPort.c2.data <= 64'h0; - endcase - af2cp_sTxPort.c2.mmioRdValid <= 1; // post response - end else - begin - if (avm_read | avm_write | run_vortex) mem_RDWR[0] <= 0; - end - end -end - - - - - -// FSM - -// Code reused from Intel OPAE's 04_local_memory sample program with changes made to fit Vortex - -// Interface between CSR and FSM -// All the MMIOs read/write passed from csr are used for state transitions -// Read: local memory to shared buffer -// Write: shared buffer to local memory - -// To be done: -// Review the FSM and implement read/write to shared buffer -// Vortex on/off signal -// check on byteenable and burst signals - -//cp2af_sRxPort -> sRx -//af2cp_sTxPort -> sTx - - -typedef enum logic[3:0] { IDLE, - VX_REQ, - VX_WR_REQ, - VX_RD_REQ, - VX_RSP, - RD_REQ, - RD_RSP, - WR_REQ, - WR_RSP } state_t; - - -// Added by Apurve for shared memory space write/read -t_ccip_clAddr wr_addr; -t_ccip_clAddr rd_addr; -logic [15:0] count; -logic [15:0] count_rsp; -logic start_read; -logic start_write; -t_local_mem_addr local_address; -logic init_avs_read; - -parameter ADDRESS_MAX_BIT = 10; state_t state; -assign avs_burstcount = avm_burstcount; -t_local_mem_burst_cnt burstcount; +// Vortex signals ///////////////////////////////////////////////////////////// -assign avs_byteenable = avm_byteenable; +logic vx_dram_req_read; +logic vx_dram_req_write; +logic [31:0] vx_dram_req_addr; +logic [31:0] vx_dram_req_data[15:0]; +logic vx_dram_req_delay; -always_ff @(posedge clk) begin - if(SoftReset) begin - local_address <= '0; - avs_write <= '0; - avs_read <= '0; - state <= IDLE; - burstcount <= 1; - ready_for_sw_cmd <= 0; - count <= 0; - count_rsp <= 0; - vx_reset <= 1'b0; - vx_count <= 0; +logic vx_dram_fill_accept; +logic vx_dram_fill_rsp; +logic [31:0] vx_dram_fill_rsp_addr; +logic [31:0] vx_dram_fill_rsp_data[15:0]; + +logic vx_snp_req; +logic [31:0] vx_snp_req_addr; +logic vx_snp_req_delay; + +logic vx_ebreak; + +// AVS Queues ///////////////////////////////////////////////////////////////// + +logic avs_raq_push; +t_local_mem_addr avs_raq_din; +logic avs_raq_pop; +t_local_mem_addr avs_raq_dout; +logic avs_raq_empty; +logic avs_raq_full; + +logic avs_rdq_push; +t_local_mem_data avs_rdq_din; +logic avs_rdq_pop; +t_local_mem_data avs_rdq_dout; +logic avs_rdq_empty; +logic avs_rdq_full; + +// CSR variables ////////////////////////////////////////////////////////////// + +logic [2:0] csr_cmd; +t_ccip_clAddr csr_io_addr; +t_local_mem_addr csr_mem_addr; +logic [31:0] csr_data_size; + +// MMIO controller //////////////////////////////////////////////////////////// + +t_ccip_c0_ReqMmioHdr mmioHdr; + +always_comb +begin + mmioHdr = t_ccip_c0_ReqMmioHdr'(cp2af_sRxPort.c0.hdr); +end + +always_ff @(posedge clk) +begin + if (SoftReset) + begin + af2cp_sTxPort.c2.hdr <= 0; + af2cp_sTxPort.c2.data <= 0; + af2cp_sTxPort.c2.mmioRdValid <= 0; + csr_cmd <= 0; + csr_io_addr <= 0; + csr_mem_addr <= 0; + csr_data_size <= 0; end else begin - case(state) - IDLE: begin - ready_for_sw_cmd <= 1; - if (avm_write) begin - state <= WR_REQ; - ready_for_sw_cmd <= 0; - count <= 0; - count_rsp <= 0; - end else if (avm_read) begin - init_avs_read <= 1; - state <= RD_REQ; - ready_for_sw_cmd <= 0; - count <= 0; - count_rsp <= 0; - end else if (run_vortex) begin - state <= VX_REQ; - vx_reset <= 1'b1; - ready_for_sw_cmd <= 0; + csr_cmd <= 0; + af2cp_sTxPort.c2.mmioRdValid <= 0; + + // serve MMIO write request + if (cp2af_sRxPort.c0.mmioWrValid) + begin + case (mmioHdr.address) + MMIO_CSR_IO_ADDR: begin + csr_io_addr <= t_ccip_clAddr'(cp2af_sRxPort.c0.data >> 6); + $display("%t: CSR_IO_ADDR: 0x%h", $time, t_ccip_clAddr'(cp2af_sRxPort.c0.data >> 6)); end - end + MMIO_CSR_MEM_ADDR: begin + csr_mem_addr <= t_local_mem_addr'(cp2af_sRxPort.c0.data >> 6); + $display("%t: CSR_MEM_ADDR: 0x%h", $time, t_local_mem_addr'(cp2af_sRxPort.c0.data >> 6)); + end + MMIO_CSR_DATA_SIZE: begin + csr_data_size <= $bits(csr_data_size)'((cp2af_sRxPort.c0.data + 63) >> 6); + $display("%t: CSR_DATA_SIZE: %d", $time, $bits(csr_data_size)'((cp2af_sRxPort.c0.data + 63) >> 6)); + end + MMIO_CSR_CMD: begin + csr_cmd <= $bits(csr_cmd)'(cp2af_sRxPort.c0.data); + $display("%t: CSR_CMD: %d", $time, $bits(csr_cmd)'(cp2af_sRxPort.c0.data)); + end + endcase + end - WR_REQ: begin //AVL MM Posted Write - af2cp_sTxPort.c0.valid <= 1'b0; - avs_write <= 0; - if (~avs_waitrequest) + // serve MMIO read requests + if (cp2af_sRxPort.c0.mmioRdValid) + begin + af2cp_sTxPort.c2.hdr.tid <= mmioHdr.tid; // copy TID + case (mmioHdr.address) + // AFU header + 16'h0000: af2cp_sTxPort.c2.data <= { + 4'b0001, // Feature type = AFU + 8'b0, // reserved + 4'b0, // afu minor revision = 0 + 7'b0, // reserved + 1'b1, // end of DFH list = 1 + 24'b0, // next DFH offset = 0 + 4'b0, // afu major revision = 0 + 12'b0 // feature ID = 0 + }; + AFU_ID_L: af2cp_sTxPort.c2.data <= afu_id[63:0]; // afu id low + AFU_ID_H: af2cp_sTxPort.c2.data <= afu_id[127:64]; // afu id hi + 16'h0006: af2cp_sTxPort.c2.data <= 64'h0; // next AFU + 16'h0008: af2cp_sTxPort.c2.data <= 64'h0; // reserved + MMIO_CSR_STATUS: begin + $display("%t: STATUS: state=%d", $time, state); + af2cp_sTxPort.c2.data <= state; + end + default: af2cp_sTxPort.c2.data <= 64'h0; + endcase + af2cp_sTxPort.c2.mmioRdValid <= 1; // post response + end + end +end + +// COMMAND FSM //////////////////////////////////////////////////////////////// + +logic [31:0] cci_write_ctr; +logic [31:0] avs_read_ctr; +logic [31:0] avs_write_ctr; +logic [31:0] vx_snoop_ctr; +logic [31:0] vx_snoop_delay; +logic vx_reset; + +always_ff @(posedge clk) +begin + if (SoftReset) + begin + state <= STATE_IDLE; + vx_reset <= 0; + end + else begin + + vx_reset <= 0; + + case (state) + STATE_IDLE: begin + case (csr_cmd) + CMD_TYPE_READ: begin + $display("%t: CMD READ: ia=%h da=%h sz=%d", $time, csr_io_addr, csr_mem_addr, csr_data_size); + state <= STATE_READ; + end + CMD_TYPE_WRITE: begin + $display("%t: CMD WRITE: ia=%h da=%h sz=%d", $time, csr_io_addr, csr_mem_addr, csr_data_size); + state <= STATE_WRITE; + end + CMD_TYPE_RUN: begin + $display("%t: CMD START", $time); + vx_reset <= 1; + state <= STATE_RUN; + end + CMD_TYPE_SNOOP: begin + $display("%t: CMD SNOOP: da=%h sz=%d", $time, csr_mem_addr, csr_data_size); + state <= STATE_SNOOP1; + end + endcase + end + + STATE_READ: begin + if (cci_write_ctr >= csr_data_size) begin - if (count_rsp >= avm_data_size) - begin - state <= WR_RSP; - avs_write <= 0; - end + state <= STATE_IDLE; end end - WR_RSP: begin // wait for write response - avm_byteenable <= 64'hffffffffffffffff; - state <= IDLE; - end - - RD_REQ: begin // AVL MM Read non-posted - af2cp_sTxPort.c1.valid <= 1'b0; - if (~avs_waitrequest) begin - if (count_rsp >= avm_data_size) - begin - state <= RD_RSP; - avs_read <= 0; - end + STATE_WRITE: begin + if (avs_write_ctr >= csr_data_size) + begin + state <= STATE_IDLE; end end - RD_RSP: begin - state <= IDLE; - end - - VX_REQ: begin - vx_reset <= 1'b0; - if (vx_dram_req_write) begin - vx_count <= 0; - avs_write <= 1'b1; - state <= VX_WR_REQ; - end - - if (vx_dram_req_read) begin - vx_count <= 0; - avs_read <= 1'b1; - state <= VX_RD_REQ; - end - - if (vx_ebreak) begin - state <= VX_RSP; + STATE_RUN: begin + if (vx_ebreak) + begin + state <= STATE_IDLE; end end - VX_WR_REQ: begin - avs_write <= 1'b0; - if (vx_count >= vx_dram_req_size) - begin - state <= VX_REQ; - vx_count <= 0; + STATE_SNOOP1: begin + if (vx_snoop_delay >= VX_SNOOPING_DELAY) + begin + state <= STATE_SNOOP2; end end - VX_RD_REQ: begin - avs_read <= 1'b0; - vx_dram_fill_rsp <= 1'b0; - if (vx_count >= vx_dram_req_size) - begin - state <= VX_REQ; - vx_count <= 0; + STATE_SNOOP2: begin + if (vx_snoop_delay >= VX_SNOOPING_DELAY) + begin + state <= STATE_IDLE; end end - VX_RSP: begin - vx_count <= 0; - state <= IDLE; - end - endcase - end // end else reset -end // posedge clk + end +end +// AVS Controller ///////////////////////////////////////////////////////////// -// Vortex call - Vortex_SOC #() - vx_soc ( - .clk (clk), - .reset (vx_reset), +always_ff @(posedge clk) +begin + if (SoftReset) + begin + mem_bank_select <= 0; + avs_burstcount <= 1; + avs_byteenable <= 64'hffffffffffffffff; + avs_address <= 0; + avs_writedata <= 0; + avs_read <= 0; + avs_write <= 0; - // IO - //.io_valid[`NUMBER_CORES-1:0] (), - //.io_data [`NUMBER_CORES-1:0] (), - //.number_cores (), + avs_read_ctr <= 0; + avs_write_ctr <= 0; + end + else begin - // DRAM Dcache Req - .out_dram_req (vx_dram_req), - .out_dram_req_write (vx_dram_req_write), - .out_dram_req_read (vx_dram_req_read), - .out_dram_req_addr (vx_dram_req_addr), - .out_dram_req_size (vx_dram_req_size), - .out_dram_req_data (vx_dram_req_data), - .out_dram_expected_lat (vx_dram_expected_lat), + avs_read <= 0; + avs_write <= 0; - // DRAM Dcache Res - .out_dram_fill_accept (vx_dram_fill_accept), - .out_dram_fill_rsp (vx_dram_fill_rsp), - .out_dram_fill_rsp_addr (vx_dram_fill_rsp_addr), - .out_dram_fill_rsp_data (vx_dram_fill_rsp_data), - - //.l3c_snp_req (), - //.l3c_snp_req_addr (), - //.l3c_snp_req_delay (), - - .out_ebreak (vx_ebreak) - ); - - -// Local memory read/write address -//assign avs_address = (vx_dram_req ? (vx_count ? vx_local_addr : vx_dram_req_addr) : (count ? local_address : avm_address)); -assign avs_address = (((state == VX_WR_REQ) || (state == VX_RD_REQ)) ? (vx_count ? vx_local_addr : vx_dram_req_addr) : (count ? local_address : avm_address)); - - - -// Vortex DRAM requests and responses -// Handling of read/write data and vx_dram_req_size -// Is vx_dram_fill_accept for backpressure? -always_ff @(posedge clk) begin - if (state == VX_WR_REQ) begin - if (!avs_waitrequest & (vx_count < vx_dram_req_size)) begin - avs_write <= 1'b1; - //avs_writedata <= vx_dram_req_data; - avs_writedata[31:0] = vx_dram_req_data[0]; - avs_writedata[63:32] = vx_dram_req_data[1]; - avs_writedata[95:64] = vx_dram_req_data[2]; - avs_writedata[127:96] = vx_dram_req_data[3]; - avs_writedata[159:128] = vx_dram_req_data[4]; - avs_writedata[191:160] = vx_dram_req_data[5]; - avs_writedata[223:192] = vx_dram_req_data[6]; - avs_writedata[255:224] = vx_dram_req_data[7]; - avs_writedata[287:256] = vx_dram_req_data[8]; - avs_writedata[319:288] = vx_dram_req_data[9]; - avs_writedata[351:320] = vx_dram_req_data[10]; - avs_writedata[383:352] = vx_dram_req_data[11]; - avs_writedata[415:384] = vx_dram_req_data[12]; - avs_writedata[447:416] = vx_dram_req_data[13]; - avs_writedata[479:448] = vx_dram_req_data[14]; - avs_writedata[511:480] = vx_dram_req_data[15]; - - vx_local_addr <= (vx_count ? vx_local_addr + 1 : vx_dram_req_addr + 1); - - // Update the count value based on the number of bytes written - vx_count <= vx_count + 64; - - if ((vx_dram_req_size - vx_count) < 64) - begin - avm_byteenable <= 64'hffffffffffffffff >> (64 - (vx_dram_req_size - vx_count)); - end else - begin - avm_byteenable <= 64'hffffffffffffffff; + case (state) + STATE_IDLE: begin + avs_read_ctr <= 0; + avs_write_ctr <= 0; end - end + STATE_READ: begin + if (!avs_raq_full + && !avs_rdq_full + && !avs_waitrequest + && avs_read_ctr < csr_data_size) + begin + avs_address <= csr_mem_addr + avs_read_ctr; + avs_read <= 1; + avs_read_ctr <= avs_read_ctr + 1; + $display("%t: AVS Rd Req: addr=%h", $time, csr_mem_addr + avs_read_ctr); + end + end + + STATE_WRITE: begin + if (cp2af_sRxPort.c0.rspValid + && avs_write_ctr < csr_data_size) + begin + avs_writedata <= cp2af_sRxPort.c0.data; + avs_address <= csr_mem_addr + avs_write_ctr; + avs_write <= 1; + avs_write_ctr <= avs_write_ctr + 1; + $display("%t: AVS Wr Req: addr=%h value=%h", $time, csr_mem_addr + avs_write_ctr, cp2af_sRxPort.c0.data[63:0]); + end + end + + STATE_RUN: begin + if (vx_dram_req_read + && !avs_waitrequest + && !avs_raq_full + && !avs_rdq_full) + begin + avs_address <= (vx_dram_req_addr >> 6); + avs_read <= 1; + $display("%t: AVS Rd Req: addr=%h", $time, vx_dram_req_addr >> 6); + end + + if (vx_dram_req_write + && !avs_waitrequest) + begin + avs_writedata <= {>>{vx_dram_req_data}}; + avs_address <= (vx_dram_req_addr >> 6); + avs_write <= 1; + $display("%t: AVS Wr Req: addr=%h value=%h", $time, vx_dram_req_addr >> 6, {vx_dram_req_data[1], vx_dram_req_data[0]}); + end + end + endcase + + if (avs_readdatavalid) + begin + $display("%t: AVS Rd Rsp: value=%h", $time, avs_readdata[63:0]); + end end end -always_ff @(posedge clk) begin - //if (SoftReset) begin - if (vx_reset) begin - vx_dram_fill_rsp <= 1'b0; - //vx_dram_fill_rsp_data <= 0; - vx_dram_fill_rsp_data[0] <= 0; - vx_dram_fill_rsp_data[1] <= 0; - vx_dram_fill_rsp_data[2] <= 0; - vx_dram_fill_rsp_data[3] <= 0; - vx_dram_fill_rsp_data[4] <= 0; - vx_dram_fill_rsp_data[5] <= 0; - vx_dram_fill_rsp_data[6] <= 0; - vx_dram_fill_rsp_data[7] <= 0; - vx_dram_fill_rsp_data[8] <= 0; - vx_dram_fill_rsp_data[9] <= 0; - vx_dram_fill_rsp_data[10] <= 0; - vx_dram_fill_rsp_data[11] <= 0; - vx_dram_fill_rsp_data[12] <= 0; - vx_dram_fill_rsp_data[13] <= 0; - vx_dram_fill_rsp_data[14] <= 0; - vx_dram_fill_rsp_data[15] <= 0; - end +// Vortex DRAM requests stalling +assign vx_dram_req_delay = !(avs_read || avs_write); - if (state == VX_RD_REQ) begin - if (avs_readdatavalid & vx_dram_fill_accept) begin - avs_read <= 1'b1; - vx_dram_fill_rsp <= 1'b1; - //vx_dram_fill_rsp_data <= avs_readdata; - vx_dram_fill_rsp_data[0] <= avs_readdata[31:0]; - vx_dram_fill_rsp_data[1] <= avs_readdata[63:32]; - vx_dram_fill_rsp_data[2] <= avs_readdata[95:64]; - vx_dram_fill_rsp_data[3] <= avs_readdata[127:96]; - vx_dram_fill_rsp_data[4] <= avs_readdata[159:128]; - vx_dram_fill_rsp_data[5] <= avs_readdata[191:160]; - vx_dram_fill_rsp_data[6] <= avs_readdata[223:192]; - vx_dram_fill_rsp_data[7] <= avs_readdata[255:224]; - vx_dram_fill_rsp_data[8] <= avs_readdata[287:256]; - vx_dram_fill_rsp_data[9] <= avs_readdata[319:288]; - vx_dram_fill_rsp_data[10] <= avs_readdata[351:320]; - vx_dram_fill_rsp_data[11] <= avs_readdata[383:352]; - vx_dram_fill_rsp_data[12] <= avs_readdata[415:384]; - vx_dram_fill_rsp_data[13] <= avs_readdata[447:416]; - vx_dram_fill_rsp_data[14] <= avs_readdata[479:448]; - vx_dram_fill_rsp_data[15] <= avs_readdata[511:480]; - vx_local_addr <= (vx_count ? vx_local_addr + 1 : vx_dram_req_addr + 1); - vx_dram_fill_rsp_addr <= vx_local_addr; - // Update the count value based on the number of bytes written - vx_count <= vx_count + 64; - - end - end -end - - - - -// Read from local memory (avs_readdata) and write to shared space -// Implement write header -always_ff @(posedge clk) begin - if (state == RD_REQ & avs_readdatavalid & !cp2af_sRxPort.c1TxAlmFull & count < avm_data_size & !avs_waitrequest & start_write) - begin - wr_addr <= (count? wr_addr + 1 : avm_write_buffer_address + 1); - local_address <= (count? local_address + 1 : avm_address + 1); - start_write <= 1'b0; - end -end - -// Write header defines the request to the FIU -t_ccip_c1_ReqMemHdr wr_hdr; - -always_comb +// Vortex DRAM fill response +always_comb begin - wr_hdr = t_ccip_c1_ReqMemHdr'(0); - - // Virtual address (MPF virtual addressing is enabled) - wr_hdr.address = (count? wr_addr: avm_write_buffer_address); - - // Start of packet is true (single line write) - wr_hdr.sop = 1'b1; + vx_dram_fill_rsp = (STATE_RUN == state) && !avs_rdq_empty && vx_dram_fill_accept; + vx_dram_fill_rsp_addr = avs_raq_dout; + {>>{vx_dram_fill_rsp_data}} = avs_rdq_dout; end -// Send write requests to the FIU -always_ff @(posedge clk) +// AVS address read request queue ///////////////////////////////////////////// + +logic cci_write_req; + +always_comb begin - if (SoftReset) - begin - af2cp_sTxPort.c1.hdr <= '0; - af2cp_sTxPort.c1.data <= '0; - af2cp_sTxPort.c1.valid <= '0; - end - - // Generate a write request when needed and the FIU isn't full - if (state == RD_REQ & avs_readdatavalid & !cp2af_sRxPort.c1TxAlmFull & count < avm_data_size & !avs_waitrequest & start_write) - begin - af2cp_sTxPort.c1.hdr <= wr_hdr; - af2cp_sTxPort.c1.data <= t_ccip_clData'(avs_readdata); - af2cp_sTxPort.c1.valid <= 1'b1; - start_write <= 1'b0; - count <= count + 64; - end + avs_raq_pop = vx_dram_fill_rsp || cci_write_req; + avs_raq_din = avs_address; + avs_raq_push = avs_write; end -// Write response -always_ff @(posedge clk) +VX_generic_queue_ll #( + .DATAW($bits(t_local_mem_addr)), + .SIZE(AVS_RD_QUEUE_SIZE) +) vx_rd_addr_queue ( + .clk (clk), + .reset (SoftReset), + .push (avs_raq_push), + .in_data (avs_raq_din), + .pop (avs_raq_pop), + .out_data (avs_raq_dout), + .empty (avs_raq_empty), + .full (avs_raq_full) +); + +// AVS data read response queue /////////////////////////////////////////////// + +always_comb begin - if (SoftReset) - begin - start_write <= 1'b1; - end - - // Generate a read request when needed and the FIU isn't full - if (state == RD_REQ & cp2af_sRxPort.c1.rspValid) - begin - count_rsp <= count_rsp + 64; - start_write <= 1'b1; - init_avs_read <= 1'b1; - end + avs_rdq_pop = avs_raq_pop; + avs_rdq_din = avs_readdata; + avs_rdq_push = avs_readdatavalid; end +VX_generic_queue_ll #( + .DATAW($bits(t_local_mem_data)), + .SIZE(AVS_RD_QUEUE_SIZE) +) vx_rd_data_queue ( + .clk (clk), + .reset (SoftReset), + .push (avs_rdq_push), + .in_data (avs_rdq_din), + .pop (avs_rdq_pop), + .out_data (avs_rdq_dout), + .empty (avs_rdq_empty), + .full (avs_rdq_full) +); -// avs_read control +// CCI Read Request /////////////////////////////////////////////////////////// -always_ff @(posedge clk) -begin - if (SoftReset) - begin - init_avs_read <= 1'b0; - end - - if (init_avs_read & state <= RD_REQ) - begin - avs_read <= 1'b1; - init_avs_read <= 1'b0; - end else - begin - avs_read <= 1'b0; - end -end - - - - -// Write to local memory (avs_writedata) and read from shared space -// Implement read header -always_ff @(posedge clk) begin - if (SoftReset) - begin - rd_addr <= 0; - local_address <= 0; - end - - if (state == WR_REQ & !cp2af_sRxPort.c0TxAlmFull & count < avm_data_size & !avs_waitrequest & start_read) - begin - // Read address + 1 gives address for next block. Each block is 64B - rd_addr <= (count? rd_addr + 1 : avm_read_buffer_address + 1); - local_address <= (count? local_address + 1 : avm_address); - start_read <= 1'b0; - end -end - -// Read header defines the request to the FIU t_ccip_c0_ReqMemHdr rd_hdr; -always_comb +logic cci_read_pending; + +always_comb begin rd_hdr = t_ccip_c0_ReqMemHdr'(0); - rd_hdr.address = (count? rd_addr : avm_read_buffer_address); + rd_hdr.address = csr_io_addr + avs_write_ctr; end -// Send read requests to the FIU -always_ff @(posedge clk) +// Send read requests to CCI +always_ff @(posedge clk) begin - if (SoftReset) + if (SoftReset) begin - af2cp_sTxPort.c0.hdr <= '0; - af2cp_sTxPort.c0.valid <= '0; - end + af2cp_sTxPort.c0.hdr <= 0; + af2cp_sTxPort.c0.valid <= 0; + cci_read_pending <= 0; + end + else begin + af2cp_sTxPort.c0.valid <= 0; - // Generate a read request when needed and the FIU isn't full - if (state == WR_REQ & !cp2af_sRxPort.c0TxAlmFull & count < avm_data_size & !avs_waitrequest & start_read) - begin - af2cp_sTxPort.c0.hdr <= rd_hdr; - af2cp_sTxPort.c0.valid <= 1'b1; - start_read <= 1'b0; - count <= count + 64; - end -end - -// Read response -always_ff @(posedge clk) -begin - if (SoftReset) - begin - start_read <= 1'b1; - avm_byteenable <= 64'hffffffffffffffff; - end - - // Generate a read request when needed and the FIU isn't full - if (state == WR_REQ & cp2af_sRxPort.c0.rspValid) - begin - if ((avm_data_size - count_rsp) < 64) + if (STATE_WRITE == state + && !cp2af_sRxPort.c0TxAlmFull // ensure read queue not full + && !avs_waitrequest // ensure AVS write queue not full + && !cci_read_pending // ensure no read pending + && avs_write_ctr < csr_data_size) // ensure not done begin - avm_byteenable <= 64'hffffffffffffffff >> (64 - (avm_data_size - count_rsp)); - end else - begin - avm_byteenable <= 64'hffffffffffffffff; + af2cp_sTxPort.c0.hdr <= rd_hdr; + af2cp_sTxPort.c0.valid <= 1; + cci_read_pending <= 1; + $display("%t: CCI Rd Req: addr=%h", $time, rd_hdr.address); end - avs_writedata <= cp2af_sRxPort.c0.data; - avs_write <= 1; - count_rsp <= count_rsp + 64; - start_read <= 1'b1; + + if (cci_read_pending + && cp2af_sRxPort.c0.rspValid) + begin + $display("%t: CCI Rd Rsp: value=%h", $time, cp2af_sRxPort.c0.data[63:0]); + cci_read_pending <= 0; + end end end +// CCI Write Request ////////////////////////////////////////////////////////// + +t_ccip_c1_ReqMemHdr wr_hdr; + +logic cci_write_pending; + +always_comb +begin + cci_write_req = (STATE_READ == state) + && !avs_rdq_empty + && !cp2af_sRxPort.c1TxAlmFull + && !cci_write_pending + && cci_write_ctr < csr_data_size; + + wr_hdr = t_ccip_c1_ReqMemHdr'(0); + wr_hdr.address = csr_io_addr + cci_write_ctr; + wr_hdr.sop = 1; // single line write mode +end + +// Send write requests to CCI +always_ff @(posedge clk) +begin + if (SoftReset) + begin + af2cp_sTxPort.c1.hdr <= 0; + af2cp_sTxPort.c1.data <= 0; + af2cp_sTxPort.c1.valid <= 0; + cci_write_ctr <= 0; + cci_write_pending <= 0; + end + else begin + af2cp_sTxPort.c1.valid <= 0; + + if (STATE_IDLE == state) + begin + cci_write_ctr <= 0; + end + + if (cci_write_req) + begin + af2cp_sTxPort.c1.hdr <= wr_hdr; + af2cp_sTxPort.c1.data <= t_ccip_clData'(avs_rdq_dout); + af2cp_sTxPort.c1.valid <= 1; + cci_write_pending <= 1; + $display("%t: CCI Wr Req: addr=%h value=%h", $time, wr_hdr.address, avs_rdq_dout[63:0]); + end + + if (cci_write_pending + && cp2af_sRxPort.c1.rspValid) + begin + cci_write_ctr <= cci_write_ctr + 1; + cci_write_pending <= 0; + $display("%t: CCI Wr Rsp", $time); + end + end +end + +// Vortex cache snooping ////////////////////////////////////////////////////// + +always_ff @(posedge clk) +begin + if (SoftReset) + begin + vx_snp_req <= 0; + vx_snoop_ctr <= 0; + vx_snoop_delay <= 0; + end + else begin + if (STATE_IDLE == state) + begin + vx_snoop_ctr <= 0; + vx_snoop_delay <= 0; + end + + vx_snp_req <= 0; + + if ((STATE_SNOOP1 == state + || STATE_SNOOP2 == state) + && vx_snoop_ctr < csr_data_size + && vx_snp_req_delay) + begin + vx_snp_req <= 1; + vx_snoop_ctr <= vx_snoop_ctr + 1; + end + + if ((vx_snoop_ctr >= csr_data_size) + && (vx_snoop_delay < VX_SNOOPING_DELAY)) + begin + vx_snoop_delay <= vx_snoop_delay + 1; + end + + if (vx_snoop_delay >= VX_SNOOPING_DELAY) + begin + vx_snoop_ctr <= 0; + vx_snoop_delay <= 0; + end + end +end + +// Vortex binding ///////////////////////////////////////////////////////////// + +Vortex_SOC #() vx_soc ( + .clk (clk), + .reset (SoftReset || vx_reset), + + // DRAM Req + .out_dram_req_write (vx_dram_req_write), + .out_dram_req_read (vx_dram_req_read), + .out_dram_req_addr (vx_dram_req_addr), + .out_dram_req_data (vx_dram_req_data), + .out_dram_req_delay (vx_dram_req_delay), + + // DRAM Rsp + .out_dram_fill_accept (vx_dram_fill_accept), + .out_dram_fill_rsp (vx_dram_fill_rsp), + .out_dram_fill_rsp_addr (vx_dram_fill_rsp_addr), + .out_dram_fill_rsp_data (vx_dram_fill_rsp_data), + + // Cache Snooping Req + .llc_snp_req (vx_snp_req), + .llc_snp_req_addr (vx_snp_req_addr), + .llc_snp_req_delay (vx_snp_req_delay), + + // program exit signal + .out_ebreak (vx_ebreak) +); + endmodule diff --git a/driver/sw/opae/Makefile b/driver/sw/opae/Makefile index 05694604..08397a99 100644 --- a/driver/sw/opae/Makefile +++ b/driver/sw/opae/Makefile @@ -1,7 +1,7 @@ CXXFLAGS += -std=c++11 -O0 -g -Wall -Wextra -pedantic -Wfatal-errors -CXXFLAGS += -I../include -I/tools/opae/1.4.0/include +CXXFLAGS += -I../include -I/tools/opae/1.4.0/include -I../../../runtime LDFLAGS += -L/tools/opae/1.4.0/lib @@ -17,6 +17,8 @@ CXXFLAGS +=-fstack-protector # Position independent code CXXFLAGS += -fPIC +CXXFLAGS += -DGLOBAL_BLOCK_SIZE_BYTES=64 + LDFLAGS += -luuid LDFLAGS += -shared @@ -50,7 +52,7 @@ $(PROJECT_ASE): $(SRCS) $(ASE_DIR) $(CXX) $(CXXFLAGS) -DUSE_ASE $(SRCS) $(LDFLAGS) $(ASE_LIBS) -o $@ vortex.o: vortex.cpp $(AFU_JSON_INFO) - $(CC) $(CXXFLAGS) -c vortex.cpp -o $@ + $(CXX) $(CXXFLAGS) -c vortex.cpp -o $@ $(ASE_DIR): mkdir -p ase diff --git a/driver/sw/opae/vortex.cpp b/driver/sw/opae/vortex.cpp index 1f62dcfc..11d29ee3 100755 --- a/driver/sw/opae/vortex.cpp +++ b/driver/sw/opae/vortex.cpp @@ -4,35 +4,35 @@ #include #include #include - #include #include #include "vortex_afu.h" -// MMIO Address Mappings -#define MMIO_COPY_IO_ADDRESS 0X120 -#define MMIO_COPY_AVM_ADDRESS 0x100 -#define MMIO_COPY_DATA_SIZE 0X118 - -#define MMIO_CMD_TYPE 0X110 -#define MMIO_READY_FOR_CMD 0X198 - -#define MMIO_CMD_TYPE_READ 0 -#define MMIO_CMD_TYPE_WRITE 1 -#define MMIO_CMD_TYPE_START 2 -#define MMIO_CMD_TYPE_SNOOP 3 - #define CHECK_RES(_expr) \ do { \ fpga_result res = _expr; \ if (res == FPGA_OK) \ break; \ - printf("OPAE Error: '%s' returned %d!\n", #_expr, (int)res); \ + printf("OPAE Error: '%s' returned %d, %s!\n", \ + #_expr, (int)res, fpgaErrStr(res)); \ return -1; \ } while (false) /////////////////////////////////////////////////////////////////////////////// +#define CMD_TYPE_READ AFU_IMAGE_CMD_TYPE_READ +#define CMD_TYPE_WRITE AFU_IMAGE_CMD_TYPE_WRITE +#define CMD_TYPE_RUN AFU_IMAGE_CMD_TYPE_RUN +#define CMD_TYPE_SNOOP AFU_IMAGE_CMD_TYPE_SNOOP + +#define MMIO_CSR_CMD (AFU_IMAGE_MMIO_CSR_CMD * 4) +#define MMIO_CSR_STATUS (AFU_IMAGE_MMIO_CSR_STATUS * 4) +#define MMIO_CSR_IO_ADDR (AFU_IMAGE_MMIO_CSR_IO_ADDR * 4) +#define MMIO_CSR_MEM_ADDR (AFU_IMAGE_MMIO_CSR_MEM_ADDR * 4) +#define MMIO_CSR_DATA_SIZE (AFU_IMAGE_MMIO_CSR_DATA_SIZE * 4) + +/////////////////////////////////////////////////////////////////////////////// + typedef struct vx_device_ { fpga_handle fpga; size_t mem_allocation; @@ -42,21 +42,19 @@ typedef struct vx_buffer_ { uint64_t wsid; volatile void* host_ptr; uint64_t io_addr; - fpga_handle fpga; + vx_device_h hdevice; size_t size; } vx_buffer_t; static size_t align_size(size_t size) { - uint32_t cache_block_size = vx_dev_caps(VX_CAPS_CACHE_LINESIZE); + uint32_t cache_block_size = vx_dev_caps(VX_CAPS_CACHE_LINESIZE); return cache_block_size * ((size + cache_block_size - 1) / cache_block_size); } /////////////////////////////////////////////////////////////////////////////// -// Search for an accelerator matching the requested UUID and connect to it -// Convert this to void if required as storing the fpga_handle to params variable extern int vx_dev_open(vx_device_h* hdevice) { - fpga_properties filter = NULL; + fpga_properties filter = nullptr; fpga_result res; fpga_guid guid; fpga_token accel_token; @@ -64,11 +62,14 @@ extern int vx_dev_open(vx_device_h* hdevice) { fpga_handle accel_handle; vx_device_t* device; - if (NULL == hdevice) + if (nullptr == hdevice) return -1; + // ensure that the block size 64 + assert(64 == vx_dev_caps(VX_CAPS_CACHE_LINESIZE)); + // Set up a filter that will search for an accelerator - fpgaGetProperties(NULL, &filter); + fpgaGetProperties(nullptr, &filter); fpgaPropertiesSetObjectType(filter, FPGA_ACCELERATOR); // Add the desired UUID to the filter @@ -84,13 +85,13 @@ extern int vx_dev_open(vx_device_h* hdevice) { if (num_matches < 1) { fprintf(stderr, "Accelerator %s not found!\n", AFU_ACCEL_UUID); - return NULL; + return -1; } // Open accelerator res = fpgaOpen(accel_token, &accel_handle, 0); if (FPGA_OK != res) { - return NULL; + return -1; } // Done with token @@ -98,9 +99,9 @@ extern int vx_dev_open(vx_device_h* hdevice) { // allocate device object device = (vx_device_t*)malloc(sizeof(vx_device_t)); - if (NULL == device) { + if (nullptr == device) { fpgaClose(accel_handle); - return NULL; + return -1; } device->fpga = accel_handle; @@ -111,9 +112,8 @@ extern int vx_dev_open(vx_device_h* hdevice) { return 0; } -// Close the fpga when all the operations are done extern int vx_dev_close(vx_device_h hdevice) { - if (NULL == hdevice) + if (nullptr == hdevice) return -1; vx_device_t *device = ((vx_device_t*)hdevice); @@ -126,15 +126,15 @@ extern int vx_dev_close(vx_device_h hdevice) { } extern int vx_alloc_dev_mem(vx_device_h hdevice, size_t size, size_t* dev_maddr) { - if (NULL == hdevice - || NULL == dev_maddr + if (nullptr == hdevice + || nullptr == dev_maddr || 0 >= size) return -1; vx_device_t *device = ((vx_device_t*)hdevice); size_t asize = align_size(size); - auto dev_mem_size = vx_dev_caps(VX_CAPS_LOCAL_MEM_SIZE); + size_t dev_mem_size = vx_dev_caps(VX_CAPS_LOCAL_MEM_SIZE); if (device->mem_allocation + asize > dev_mem_size) return -1; @@ -151,9 +151,9 @@ extern int vx_alloc_shared_mem(vx_device_h hdevice, size_t size, vx_buffer_h* hb uint64_t io_addr; vx_buffer_t* buffer; - if (NULL == hdevice + if (nullptr == hdevice || 0 >= size - || NULL == hbuffer) + || nullptr == hbuffer) return -1; vx_device_t *device = ((vx_device_t*)hdevice); @@ -174,7 +174,7 @@ extern int vx_alloc_shared_mem(vx_device_h hdevice, size_t size, vx_buffer_h* hb // allocate buffer object buffer = (vx_buffer_t*)malloc(sizeof(vx_buffer_t)); - if (NULL == buffer) { + if (nullptr == buffer) { fpgaReleaseBuffer(device->fpga, wsid); return -1; } @@ -182,7 +182,7 @@ extern int vx_alloc_shared_mem(vx_device_h hdevice, size_t size, vx_buffer_h* hb buffer->wsid = wsid; buffer->host_ptr = host_ptr; buffer->io_addr = io_addr; - buffer->fpga = device->fpga; + buffer->hdevice = hdevice; buffer->size = size; *hbuffer = buffer; @@ -191,136 +191,30 @@ extern int vx_alloc_shared_mem(vx_device_h hdevice, size_t size, vx_buffer_h* hb } extern volatile void* vx_host_ptr(vx_buffer_h hbuffer) { + if (nullptr == hbuffer) + return nullptr; + vx_buffer_t* buffer = ((vx_buffer_t*)hbuffer); - if (NULL == buffer) - return NULL; return buffer->host_ptr; } extern int vx_buf_release(vx_buffer_h hbuffer) { - vx_buffer_t* buffer = ((vx_buffer_t*)hbuffer); - if (NULL == buffer) + if (nullptr == hbuffer) return -1; - fpgaReleaseBuffer(buffer->fpga, buffer->wsid); + vx_buffer_t* buffer = ((vx_buffer_t*)hbuffer); + vx_device_t *device = ((vx_device_t*)buffer->hdevice); + + fpgaReleaseBuffer(device->fpga, buffer->wsid); free(buffer); return 0; } -// Check if HW is ready for SW -static int ready_for_sw(fpga_handle hdevice) { - uint64_t data = 0; - struct timespec sleep_time; - -#ifdef USE_ASE - sleep_time.tv_sec = 1; - sleep_time.tv_nsec = 0; -#else - sleep_time.tv_sec = 0; - sleep_time.tv_nsec = 1000000; -#endif - - do { - CHECK_RES(fpgaReadMMIO64(hdevice, 0, MMIO_READY_FOR_CMD, &data)); - nanosleep(&sleep_time, NULL); - } while (data != 0x1); - - return 0; -} - -extern int vx_copy_to_dev(vx_buffer_h hbuffer, size_t dev_maddr, size_t size, size_t src_offset) { - if (NULL == hbuffer - || 0 >= size) - return -1; - - vx_buffer_t* buffer = ((vx_buffer_t*)hbuffer); - - // bound checking - if (size + src_offset > buffer->size) - return -1; - - // Ensure ready for new command - if (ready_for_sw(buffer->fpga) != 0) - return -1; - - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_COPY_AVM_ADDRESS, dev_maddr)); - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_COPY_IO_ADDRESS, buffer->io_addr + src_offset); - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_COPY_DATA_SIZE, size)); - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_CMD_TYPE, MMIO_CMD_TYPE_WRITE)); - - // Wait for the write operation to finish - return ready_for_sw(buffer->fpga); -} - -extern int vx_copy_from_dev(vx_buffer_h hbuffer, size_t dev_maddr, size_t size, size_t dest_offset) { - if (NULL == hbuffer - || 0 >= size) - return -1; - - vx_buffer_t* buffer = ((vx_buffer_t*)hbuffer); - - // bound checking - if (size + dest_offset > buffer->size) - return -1; - - // Ensure ready for new command - if (ready_for_sw(buffer->fpga) != 0) - return -1; - - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_COPY_AVM_ADDRESS, dev_maddr)); - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_COPY_IO_ADDRESS, buffer->io_addr + dest_offset); - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_COPY_DATA_SIZE, size)); - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_CMD_TYPE, MMIO_CMD_TYPE_READ)); - - // Wait for the write operation to finish - return ready_for_sw(buffer->fpga); -} - -extern int vx_flush_caches(vx_device_h hdevice, size_t dev_maddr, size_t size) { - if (NULL == hbuffer - || 0 >= size) - return -1; - - vx_buffer_t* buffer = ((vx_buffer_t*)hbuffer); - - // bound checking - if (size + src_offset > buffer->size) - return -1; - - // Ensure ready for new command - if (ready_for_sw(buffer->fpga) != 0) - return -1; - - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_COPY_AVM_ADDRESS, dev_maddr)); - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_COPY_IO_ADDRESS, (buffer->io_addr + src_offset)/VX_CACHE_LINESIZE)); - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_COPY_DATA_SIZE, size)); - CHECK_RES(fpgaWriteMMIO64(buffer->fpga, 0, MMIO_CMD_TYPE, MMIO_CMD_TYPE_SNOOP)); - - // Wait for the write operation to finish - return ready_for_sw(buffer->fpga); - return 0; -} - -extern int vx_start(vx_device_h hdevice) { - if (NULL == hdevice) - return -1; - - vx_device_t *device = ((vx_device_t*)hdevice); - - // Ensure ready for new command - if (ready_for_sw(device->fpga) != 0) - return -1; - - CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CMD_TYPE, MMIO_CMD_TYPE_START)); - - return 0; -} - extern int vx_ready_wait(vx_device_h hdevice, long long timeout) { - if (NULL == hdevice) + if (nullptr == hdevice) return -1; vx_device_t *device = ((vx_device_t*)hdevice); @@ -328,7 +222,7 @@ extern int vx_ready_wait(vx_device_h hdevice, long long timeout) { uint64_t data = 0; struct timespec sleep_time; -#ifdef USE_ASE +#if defined(USE_ASE) sleep_time.tv_sec = 1; sleep_time.tv_nsec = 0; #else @@ -339,13 +233,106 @@ extern int vx_ready_wait(vx_device_h hdevice, long long timeout) { // to milliseconds long long sleep_time_ms = (sleep_time.tv_sec * 1000) + (sleep_time.tv_nsec / 1000000); - do { - CHECK_RES(fpgaReadMMIO64(device->fpga, 0, MMIO_READY_FOR_CMD, &data)); - nanosleep(&sleep_time, NULL); - sleep_time_ms -= sleep_time_ms; - if (timeout <= sleep_time_ms) - break; - } while (data != 0x1); + for (;;) { + CHECK_RES(fpgaReadMMIO64(device->fpga, 0, MMIO_CSR_STATUS, &data)); + if (0 == data || 0 == timeout) + break; + nanosleep(&sleep_time, nullptr); + timeout -= sleep_time_ms; + }; + + return 0; +} + +extern int vx_copy_to_dev(vx_buffer_h hbuffer, size_t dev_maddr, size_t size, size_t src_offset) { + if (nullptr == hbuffer + || 0 >= size) + return -1; + + vx_buffer_t* buffer = ((vx_buffer_t*)hbuffer); + vx_device_t *device = ((vx_device_t*)buffer->hdevice); + + // bound checking + if (size + src_offset > buffer->size) + return -1; + + // Ensure ready for new command + if (vx_ready_wait(buffer->hdevice, -1) != 0) + return -1; + + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_IO_ADDR, buffer->io_addr + src_offset)); + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_MEM_ADDR, dev_maddr)); + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_DATA_SIZE, size)); + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_CMD, CMD_TYPE_WRITE)); + + // Wait for the write operation to finish + if (vx_ready_wait(buffer->hdevice, -1) != 0) + return -1; + + return 0; +} + +extern int vx_copy_from_dev(vx_buffer_h hbuffer, size_t dev_maddr, size_t size, size_t dest_offset) { + if (nullptr == hbuffer + || 0 >= size) + return -1; + + vx_buffer_t* buffer = ((vx_buffer_t*)hbuffer); + vx_device_t *device = ((vx_device_t*)buffer->hdevice); + + // bound checking + if (size + dest_offset > buffer->size) + return -1; + + // Ensure ready for new command + if (vx_ready_wait(buffer->hdevice, -1) != 0) + return -1; + + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_IO_ADDR, buffer->io_addr + dest_offset)); + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_MEM_ADDR, dev_maddr)); + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_DATA_SIZE, size)); + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_CMD, CMD_TYPE_READ)); + + // Wait for the write operation to finish + if (vx_ready_wait(buffer->hdevice, -1) != 0) + return -1; + + return 0; +} + +extern int vx_flush_caches(vx_device_h hdevice, size_t dev_maddr, size_t size) { + if (nullptr == hdevice + || 0 >= size) + return -1; + + vx_device_t* device = ((vx_device_t*)hdevice); + + // Ensure ready for new command + if (vx_ready_wait(hdevice, -1) != 0) + return -1; + + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_MEM_ADDR, dev_maddr)); + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_DATA_SIZE, size)); + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_CMD, CMD_TYPE_SNOOP)); + + // Wait for the write operation to finish + if (vx_ready_wait(hdevice, -1) != 0) + return -1; + + return 0; +} + +extern int vx_start(vx_device_h hdevice) { + if (nullptr == hdevice) + return -1; + + vx_device_t *device = ((vx_device_t*)hdevice); + + // Ensure ready for new command + if (vx_ready_wait(hdevice, -1) != 0) + return -1; + + CHECK_RES(fpgaWriteMMIO64(device->fpga, 0, MMIO_CSR_CMD, CMD_TYPE_RUN)); return 0; } \ No newline at end of file diff --git a/driver/sw/rtlsim/vortex.cpp b/driver/sw/rtlsim/vortex.cpp index df816d87..58405f0e 100644 --- a/driver/sw/rtlsim/vortex.cpp +++ b/driver/sw/rtlsim/vortex.cpp @@ -11,17 +11,6 @@ #include #include -#define PAGE_SIZE 4096 - -#define CHECK_RES(_expr) \ - do { \ - fpga_result res = _expr; \ - if (res == FPGA_OK) \ - break; \ - printf("OPAE Error: '%s' returned %d!\n", #_expr, (int)res); \ - return -1; \ - } while (false) - /////////////////////////////////////////////////////////////////////////////// static size_t align_size(size_t size) { @@ -197,7 +186,7 @@ private: /////////////////////////////////////////////////////////////////////////////// extern int vx_dev_open(vx_device_h* hdevice) { - if (NULL == hdevice) + if (nullptr == hdevice) return -1; *hdevice = new vx_device(); @@ -217,8 +206,8 @@ extern int vx_dev_close(vx_device_h hdevice) { } extern int vx_alloc_dev_mem(vx_device_h hdevice, size_t size, size_t* dev_maddr) { - if (NULL == hdevice - || NULL == dev_maddr + if (nullptr == hdevice + || nullptr == dev_maddr || 0 >= size) return -1; @@ -227,7 +216,7 @@ extern int vx_alloc_dev_mem(vx_device_h hdevice, size_t size, size_t* dev_maddr) } extern int vx_flush_caches(vx_device_h hdevice, size_t dev_maddr, size_t size) { - if (NULL == hdevice + if (nullptr == hdevice || 0 >= size) return -1; @@ -240,7 +229,7 @@ extern int vx_flush_caches(vx_device_h hdevice, size_t dev_maddr, size_t size) { extern int vx_alloc_shared_mem(vx_device_h hdevice, size_t size, vx_buffer_h* hbuffer) { if (nullptr == hdevice || 0 >= size - || NULL == hbuffer) + || nullptr == hbuffer) return -1; vx_device *device = ((vx_device*)hdevice); diff --git a/driver/sw/simx/Makefile b/driver/sw/simx/Makefile index 8299fbc2..87eb39b0 100644 --- a/driver/sw/simx/Makefile +++ b/driver/sw/simx/Makefile @@ -1,7 +1,7 @@ CFLAGS += -std=c++11 -O3 -Wall -Wextra -pedantic -Wfatal-errors #CFLAGS += -std=c++11 -g -O0 -Wall -Wextra -pedantic -Wfatal-errors -CFLAGS += -I../../include -I../../../../simX/include -I../../../../runtime +CFLAGS += -I../../include -I../../../../simX/include -I../../../../runtime CFLAGS += -fPIC diff --git a/driver/sw/simx/vortex.cpp b/driver/sw/simx/vortex.cpp index a6d03433..ef5a4b6d 100644 --- a/driver/sw/simx/vortex.cpp +++ b/driver/sw/simx/vortex.cpp @@ -13,15 +13,6 @@ #define PAGE_SIZE 4096 -#define CHECK_RES(_expr) \ - do { \ - fpga_result res = _expr; \ - if (res == FPGA_OK) \ - break; \ - printf("OPAE Error: '%s' returned %d!\n", #_expr, (int)res); \ - return -1; \ - } while (false) - /////////////////////////////////////////////////////////////////////////////// static size_t align_size(size_t size) { @@ -206,7 +197,7 @@ private: /////////////////////////////////////////////////////////////////////////////// extern int vx_dev_open(vx_device_h* hdevice) { - if (NULL == hdevice) + if (nullptr == hdevice) return -1; *hdevice = new vx_device(); @@ -226,8 +217,8 @@ extern int vx_dev_close(vx_device_h hdevice) { } extern int vx_alloc_dev_mem(vx_device_h hdevice, size_t size, size_t* dev_maddr) { - if (NULL == hdevice - || NULL == dev_maddr + if (nullptr == hdevice + || nullptr == dev_maddr || 0 >= size) return -1; @@ -236,7 +227,7 @@ extern int vx_alloc_dev_mem(vx_device_h hdevice, size_t size, size_t* dev_maddr) } extern int vx_flush_caches(vx_device_h hdevice, size_t /*dev_maddr*/, size_t size) { - if (NULL == hdevice + if (nullptr == hdevice || 0 >= size) return -1; // this functionality is not need by simX @@ -246,7 +237,7 @@ extern int vx_flush_caches(vx_device_h hdevice, size_t /*dev_maddr*/, size_t siz extern int vx_alloc_shared_mem(vx_device_h hdevice, size_t size, vx_buffer_h* hbuffer) { if (nullptr == hdevice || 0 >= size - || NULL == hbuffer) + || nullptr == hbuffer) return -1; vx_device *device = ((vx_device*)hdevice); diff --git a/driver/tests/basic/Makefile b/driver/tests/basic/Makefile index 089739a1..91e1b9c9 100644 --- a/driver/tests/basic/Makefile +++ b/driver/tests/basic/Makefile @@ -18,7 +18,7 @@ run-fpga: $(PROJECT) LD_LIBRARY_PATH=../../sw/opae:$(LD_LIBRARY_PATH) ./$(PROJECT) run-ase: $(PROJECT) - LD_LIBRARY_PATH=../../sw/opae/ase:$(LD_LIBRARY_PATH) ./$(PROJECT) + LIBOPAE_LOG=1 LD_LIBRARY_PATH=../../sw/opae/ase:$(LD_LIBRARY_PATH) ./$(PROJECT) run-rtlsim: $(PROJECT) LD_LIBRARY_PATH=../../sw/rtlsim:$(LD_LIBRARY_PATH) ./$(PROJECT) diff --git a/driver/tests/basic/basic b/driver/tests/basic/basic index 1a2ab35ec79795ad8f94187c6a4ebc8e74bfcafe..f3a0e08b5d84422f2fb4667476ff79f1e9a0cf17 100755 GIT binary patch literal 40960 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 Hq2hl4p*@6D diff --git a/driver/tests/basic/basic.cpp b/driver/tests/basic/basic.cpp index 7f5b98f4..02a3d3d9 100755 --- a/driver/tests/basic/basic.cpp +++ b/driver/tests/basic/basic.cpp @@ -1,6 +1,4 @@ -#include -#include -#include +#include #include #include @@ -9,8 +7,8 @@ static void parse_args(int argc, char **argv) { while ((c = getopt(argc, argv, "?")) != -1) { switch (c) { case '?': { - printf("Test.\n"); - printf("Usage: [-h: help]\n"); + std::cout << "Test." << std::endl; + std::cout << "Usage: [-h: help]" << std::endl; exit(0); } break; default: @@ -20,12 +18,17 @@ static void parse_args(int argc, char **argv) { } uint64_t shuffle(int i, uint64_t value) { - return (value << i) | (value & ((1 << i)-1));; + //return (value << i) | (value & ((1 << i)-1));; + return 0x0badf00ddeadbeef; } -int run_test(vx_buffer_h sbuf, vx_buffer_h dbuf, uint32_t address, uint64_t value, int num_blocks) { - int err; - int num_failures = 0; +int run_test(vx_buffer_h sbuf, + vx_buffer_h dbuf, + uint32_t address, + uint64_t value, + int num_blocks) { + int ret; + int errors = 0; // write sbuf data for (int i = 0; i < 8 * num_blocks; ++i) { @@ -33,75 +36,114 @@ int run_test(vx_buffer_h sbuf, vx_buffer_h dbuf, uint32_t address, uint64_t valu } // write buffer to local memory - err = vx_copy_to_dev(sbuf, address, 64 * num_blocks, 0); - if (err != 0) - return -1; + std::cout << "write buffer to local memory" << std::endl; + ret = vx_copy_to_dev(sbuf, address, 64 * num_blocks, 0); + if (ret != 0) + return ret; // read buffer from local memory - err = vx_copy_from_dev(dbuf, address, 64 * num_blocks, 0); - if (err != 0) - return -1; + std::cout << "read buffer from local memory" << std::endl; + ret = vx_copy_from_dev(dbuf, address, 64 * num_blocks, 0); + if (ret != 0) + return ret; // verify result + std::cout << "verify result" << std::endl; for (int i = 0; i < 8 * num_blocks; ++i) { auto curr = ((uint64_t*)vx_host_ptr(dbuf))[i]; auto ref = shuffle(i, value); if (curr != ref) { - printf("error @ %x: actual %ld, expected %ld\n", address + 64 * i, curr, ref); - ++num_failures; + std::cout << "error @ " << std::hex << (address + 64 * i) + << ": actual " << curr << ", expected " << ref << std::endl; + ++errors; } - } - return num_failures; + } + + if (errors != 0) { + std::cout << "Found " << errors << " errors!" << std::endl; + std::cout << "FAILED!" << std::endl; + return 1; + } + + return 0; +} + +vx_device_h device = nullptr; +vx_buffer_h sbuf = nullptr; +vx_buffer_h dbuf = nullptr; + +void cleanup() { + if (sbuf) { + vx_buf_release(sbuf); + } + if (dbuf) { + vx_buf_release(dbuf); + } + if (device) { + vx_dev_close(device); + } } int main(int argc, char *argv[]) { - int err; - int num_failures = 0; + int ret; // parse command arguments parse_args(argc, argv); // open device connection + std::cout << "open device connection" << std::endl; vx_device_h device; - err = vx_dev_open(&device); - if (err != 0) - return -1; + ret = vx_dev_open(&device); + if (ret != 0) + return ret; // create source buffer - vx_buffer_h sbuf; - err = vx_alloc_shared_mem(device, 4096, &sbuf); - if (err != 0) { - vx_dev_close(device); - return -1; + std::cout << "create source buffer" << std::endl; + ret = vx_alloc_shared_mem(device, 4096, &sbuf); + if (ret != 0) { + cleanup(); + return ret; } // create destination buffer - vx_buffer_h dbuf; - err = vx_alloc_shared_mem(device, 4096, &dbuf); - if (err != 0) { - vx_buf_release(sbuf); - vx_dev_close(device); - return -1; + std::cout << "create destination buffer" << std::endl; + ret = vx_alloc_shared_mem(device, 4096, &dbuf); + if (ret != 0) { + cleanup(); + return ret; } // run tests - num_failures += run_test(sbuf, dbuf, 0x10000000, 0x0badf00d00ff00ff, 1); - num_failures += run_test(sbuf, dbuf, 0x10000000, 0x0badf00d00ff00ff, 2); - num_failures += run_test(sbuf, dbuf, 0x20000000, 0xff00ff00ff00ff00, 4); - num_failures += run_test(sbuf, dbuf, 0x20000000, 0x0badf00d40ff40ff, 8); - - // releae buffers - vx_buf_release(sbuf); - vx_buf_release(dbuf); - - // close device - vx_dev_close(device); - - if (0 == num_failures) { - printf("Test PASSED\n"); - } else { - printf("Test FAILED\n"); + std::cout << "run tests" << std::endl; + ret = run_test(sbuf, dbuf, 0x10000000, 0x0badf00d00ff00ff, 1); + if (ret != 0) { + cleanup(); + return ret; } - return num_failures; + ret = run_test(sbuf, dbuf, 0x10000000, 0x0badf00d00ff00ff, 2); + if (ret != 0) { + cleanup(); + return ret; + } + + ret = run_test(sbuf, dbuf, 0x20000000, 0xff00ff00ff00ff00, 4); + if (ret != 0) { + cleanup(); + return ret; + } + + ret = run_test(sbuf, dbuf, 0x20000000, 0x0badf00d40ff40ff, 8); + if (ret != 0) { + cleanup(); + return ret; + } + + // cleanup + std::cout << "cleanup" << std::endl; + cleanup(); + + std::cout << "Test PASSED" << std::endl; + + return 0; } diff --git a/driver/tests/demo/Makefile b/driver/tests/demo/Makefile index 48effb87..b5e3e2a9 100644 --- a/driver/tests/demo/Makefile +++ b/driver/tests/demo/Makefile @@ -46,7 +46,7 @@ run-fpga: $(PROJECT) LD_LIBRARY_PATH=../../sw/opae:$(LD_LIBRARY_PATH) ./$(PROJECT) -f kernel.bin -n 16 run-ase: $(PROJECT) - LD_LIBRARY_PATH=../../sw/opae/ase:$(LD_LIBRARY_PATH) ./$(PROJECT) -f kernel.bin -n 16 + LIBOPAE_LOG=1 LD_LIBRARY_PATH=../../sw/opae/ase:$(LD_LIBRARY_PATH) ./$(PROJECT) -f kernel.bin -n 16 run-rtlsim: $(PROJECT) LD_LIBRARY_PATH=../../sw/rtlsim:$(LD_LIBRARY_PATH) ./$(PROJECT) -f kernel.bin -n 16 diff --git a/driver/tests/demo/demo b/driver/tests/demo/demo index 72482b2365fe251afa78e95154befc38683e5b9d..513c7ed8a75562c67514ee7724791fc6316b29dc 100755 GIT binary patch literal 40192 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 HalHQD#1J+| literal 45248 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 HlG^_PH`=hY diff --git a/driver/tests/demo/demo.cpp b/driver/tests/demo/demo.cpp index 386b8ff4..3e84bbd0 100644 --- a/driver/tests/demo/demo.cpp +++ b/driver/tests/demo/demo.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include "common.h" @@ -40,21 +39,77 @@ static void parse_args(int argc, char **argv) { } } -vx_device_h device; -vx_buffer_h buffer; +int run_test(vx_device_h device, + vx_buffer_h buffer, + const kernel_arg_t& kernel_arg, + uint32_t buf_size, + uint32_t num_points) { + int ret; + + // start device + std::cout << "start device" << std::endl; + ret = vx_start(device); + if (ret != 0) { + return ret; + } + + // wait for completion + std::cout << "wait for completion" << std::endl; + ret = vx_ready_wait(device, -1); + if (ret != 0) { + return ret; + } + + // flush the destination buffer caches + std::cout << "flush the destination buffer caches" << std::endl; + ret = vx_flush_caches(device, kernel_arg.dst_ptr, buf_size); + if (ret != 0) { + return ret; + } + + // download destination buffer + std::cout << "download destination buffer" << std::endl; + ret = vx_copy_from_dev(buffer, kernel_arg.dst_ptr, buf_size, 0); + if (ret != 0) { + return ret; + } + + // verify result + std::cout << "verify result" << std::endl; + { + int errors = 0; + auto buf_ptr = (int*)vx_host_ptr(buffer); + for (uint32_t i = 0; i < num_points; ++i) { + int ref = i * i; + int cur = buf_ptr[i]; + if (cur != ref) { + ++errors; + } + } + if (errors != 0) { + std::cout << "Found " << errors << " errors!" << std::endl; + std::cout << "FAILED!" << std::endl; + return 1; + } + } + + return 0; +} + +vx_device_h device = nullptr; +vx_buffer_h buffer = nullptr; void cleanup() { - if (device) { - vx_dev_close(device); - } if (buffer) { vx_buf_release(buffer); } + if (device) { + vx_dev_close(device); + } } int main(int argc, char *argv[]) { int ret; - int errors = 0; size_t value; kernel_arg_t kernel_arg; @@ -79,14 +134,14 @@ int main(int argc, char *argv[]) { std::cout << "open device connection" << std::endl; ret = vx_dev_open(&device); if (ret != 0) - return -1; + return ret; // upload program std::cout << "upload program" << std::endl; ret = vx_upload_kernel_file(device, program_file); if (ret != 0) { cleanup(); - return -1; + return ret; } // allocate device memory @@ -95,21 +150,21 @@ int main(int argc, char *argv[]) { ret = vx_alloc_dev_mem(device, buf_size, &value); if (ret != 0) { cleanup(); - return -1; + return ret; } kernel_arg.src0_ptr = value; ret = vx_alloc_dev_mem(device, buf_size, &value); if (ret != 0) { cleanup(); - return -1; + return ret; } kernel_arg.src1_ptr = value; ret = vx_alloc_dev_mem(device, buf_size, &value); if (ret != 0) { cleanup(); - return -1; + return ret; } kernel_arg.dst_ptr = value; @@ -119,7 +174,7 @@ int main(int argc, char *argv[]) { ret = vx_alloc_shared_mem(device, alloc_size, &buffer); if (ret != 0) { cleanup(); - return -1; + return ret; } // populate source buffer values @@ -137,13 +192,13 @@ int main(int argc, char *argv[]) { ret = vx_copy_to_dev(buffer, kernel_arg.src0_ptr, buf_size, 0); if (ret != 0) { cleanup(); - return -1; + return ret; } ret = vx_copy_to_dev(buffer, kernel_arg.src1_ptr, buf_size, 0); if (ret != 0) { cleanup(); - return -1; + return ret; } // upload kernel argument @@ -158,117 +213,29 @@ int main(int argc, char *argv[]) { ret = vx_copy_to_dev(buffer, KERNEL_ARG_DEV_MEM_ADDR, sizeof(kernel_arg_t), 0); if (ret != 0) { cleanup(); - return -1; + return ret; } } - // start device - std::cout << "start device" << std::endl; - ret = vx_start(device); + // run tests + std::cout << "run tests" << std::endl; + ret = run_test(device, buffer, kernel_arg, buf_size, num_points); if (ret != 0) { cleanup(); - return -1; + return ret; } - - // wait for completion - std::cout << "wait for completion" << std::endl; - ret = vx_ready_wait(device, -1); + + ret = run_test(device, buffer, kernel_arg, buf_size, num_points); if (ret != 0) { cleanup(); - return -1; - } - - // flush the destination buffer caches - std::cout << "flush the destination buffer caches" << std::endl; - ret = vx_flush_caches(device, kernel_arg.dst_ptr, buf_size); - if (ret != 0) { - cleanup(); - return -1; - } - - // download destination buffer - std::cout << "download destination buffer" << std::endl; - ret = vx_copy_from_dev(buffer, kernel_arg.dst_ptr, buf_size, 0); - if (ret != 0) { - cleanup(); - return -1; - } - - // verify result - std::cout << "verify result" << std::endl; - { - auto buf_ptr = (int*)vx_host_ptr(buffer); - for (uint32_t i = 0; i < num_points; ++i) { - int ref = i * i; - int cur = buf_ptr[i]; - if (cur != ref) { - ++errors; - } - } - } - - if (errors != 0) { - printf("Found %d errors!\n", errors); - printf("FAILED!\n"); - cleanup(); - return -1; - } - - // start device - std::cout << "start device" << std::endl; - ret = vx_start(device); - if (ret != 0) { - cleanup(); - return -1; - } - - // wait for completion - std::cout << "wait for completion" << std::endl; - ret = vx_ready_wait(device, -1); - if (ret != 0) { - cleanup(); - return -1; - } - - // flush the destination buffer caches - std::cout << "flush the destination buffer caches" << std::endl; - ret = vx_flush_caches(device, kernel_arg.dst_ptr, buf_size); - if (ret != 0) { - cleanup(); - return -1; - } - - // download destination buffer - std::cout << "download destination buffer" << std::endl; - ret = vx_copy_from_dev(buffer, kernel_arg.dst_ptr, buf_size, 0); - if (ret != 0) { - cleanup(); - return -1; - } - - // verify result - std::cout << "verify result" << std::endl; - { - auto buf_ptr = (int*)vx_host_ptr(buffer); - for (uint32_t i = 0; i < num_points; ++i) { - int ref = i * i; - int cur = buf_ptr[i]; - if (cur != ref) { - ++errors; - } - } + return ret; } // cleanup std::cout << "cleanup" << std::endl; cleanup(); - if (0 == errors) { - printf("PASSED!\n"); - } else { - printf("Found %d errors!\n", errors); - printf("FAILED!\n"); - } + std::cout << "PASSED!" << std::endl; - return errors; + return 0; } \ No newline at end of file