From 77a52ea20bf142d13ed33295d5bbbb5f9d99cac9 Mon Sep 17 00:00:00 2001 From: Blaise Tine Date: Thu, 23 Apr 2020 01:30:45 -0700 Subject: [PATCH] optimized opae cci to dev memcpy using double buffering and request window to work around unordered read requests --- driver/include/vortex.h | 6 + driver/tests/basic/basic | Bin 47312 -> 44352 bytes hw/opae/README | 12 +- hw/opae/sources.txt | 2 +- hw/opae/vortex_afu.sv | 224 ++++++++++++++++++++------------- hw/rtl/VX_define.vh | 7 +- hw/rtl/Vortex.v | 9 ++ hw/rtl/libs/VX_generic_queue.v | 30 ++--- hw/rtl/tex_unit/VX_tex_mgr.v | 19 +++ hw/rtl/tex_unit/VX_tex_unit.v | 50 ++++++++ 10 files changed, 249 insertions(+), 110 deletions(-) create mode 100644 hw/rtl/tex_unit/VX_tex_mgr.v create mode 100644 hw/rtl/tex_unit/VX_tex_unit.v diff --git a/driver/include/vortex.h b/driver/include/vortex.h index ea37461b..2f379b11 100644 --- a/driver/include/vortex.h +++ b/driver/include/vortex.h @@ -57,6 +57,12 @@ int vx_start(vx_device_h hdevice); // Wait for device ready with milliseconds timeout int vx_ready_wait(vx_device_h hdevice, long long timeout); +// set device constant registers +int vx_set_regiters(int state, int value); + +// get device constant registers +int vx_get_regiters(int state, int* value); + ////////////////////////////// UTILITY FUNCIONS /////////////////////////////// // upload kernel bytes to device diff --git a/driver/tests/basic/basic b/driver/tests/basic/basic index afd8a1ecb29c327eb79c24173af71445d5b04fcb..82640cc5ba6bcb6dbe7c6d9d6ca9d4e2ef7be25a 100755 GIT binary patch literal 44352 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 MMIO_CSR_USER) begin + // write Vortex CRS + //end + end endcase end // serve MMIO read requests - if (cp2af_sRxPort.c0.mmioRdValid) - begin + if (cp2af_sRxPort.c0.mmioRdValid) begin af2cp_sTxPort.c2.hdr.tid <= mmioHdr.tid; // copy TID case (mmioHdr.address) // AFU header @@ -176,8 +181,9 @@ begin 16'h0006: af2cp_sTxPort.c2.data <= 64'h0; // next AFU 16'h0008: af2cp_sTxPort.c2.data <= 64'h0; // reserved MMIO_CSR_STATUS: begin - if (state != af2cp_sTxPort.c2.data) + if (state != af2cp_sTxPort.c2.data) begin $display("%t: STATUS: state=%0d", $time, state); + end af2cp_sTxPort.c2.data <= state; end default: af2cp_sTxPort.c2.data <= 64'h0; @@ -198,8 +204,7 @@ logic vx_reset; always_ff @(posedge clk) begin - if (SoftReset) - begin + if (SoftReset) begin state <= STATE_IDLE; vx_reset <= 0; end @@ -217,7 +222,7 @@ begin CMD_TYPE_WRITE: begin $display("%t: STATE WRITE: ia=%h da=%h sz=%0d", $time, csr_io_addr, csr_mem_addr, csr_data_size); state <= STATE_WRITE; - end + end CMD_TYPE_RUN: begin $display("%t: STATE START", $time); vx_reset <= 1; @@ -231,29 +236,25 @@ begin end STATE_READ: begin - if (cci_write_ctr >= csr_data_size) - begin + if (cci_write_ctr >= csr_data_size) begin state <= STATE_IDLE; end end STATE_WRITE: begin - if (avs_write_ctr >= csr_data_size) - begin + if (avs_write_ctr >= csr_data_size) begin state <= STATE_IDLE; end end STATE_RUN: begin - if (vx_ebreak) - begin + if (vx_ebreak) begin state <= STATE_IDLE; end end STATE_CLFLUSH: begin - if (vx_snoop_delay >= VX_SNOOP_DELAY) - begin + if (vx_snoop_delay >= VX_SNOOP_DELAY) begin state <= STATE_IDLE; end end @@ -264,6 +265,20 @@ end // AVS Controller ///////////////////////////////////////////////////////////// +logic cci_rdq_empty; +t_cci_rdq_data cci_rdq_dout; +logic cci_rdq_pop; + +t_ccip_clAddr next_avs_address; +always_comb +begin + next_avs_address = csr_mem_addr + {avs_write_ctr[31:$bits(t_cci_rdq_tag)], t_cci_rdq_tag'(cci_rdq_dout)}; + cci_rdq_pop = (state == STATE_WRITE + && !cci_rdq_empty + && !avs_waitrequest + && avs_write_ctr < csr_data_size); +end + always_ff @(posedge clk) begin if (SoftReset) @@ -295,22 +310,21 @@ begin && !avs_waitrequest && avs_read_ctr < csr_data_size) begin - avs_address <= csr_mem_addr + avs_read_ctr; - avs_read <= 1; + avs_address <= csr_mem_addr + avs_read_ctr; avs_read_ctr <= avs_read_ctr + 1; + avs_read <= 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; + if (cci_rdq_pop) + begin + avs_writedata <= cci_rdq_dout[$bits(t_ccip_clData) + $bits(t_cci_rdq_tag)-1:$bits(t_cci_rdq_tag)]; + avs_address <= next_avs_address; avs_write_ctr <= avs_write_ctr + 1; - $display("%t: AVS Wr Req: addr=%h (%0d/%0d)", $time, csr_mem_addr + avs_write_ctr, avs_write_ctr + 1, csr_data_size); + avs_write <= 1; + $display("%t: AVS Wr Req: addr=%h (%0d/%0d)", $time, next_avs_address, avs_write_ctr + 1, csr_data_size); end end @@ -319,7 +333,7 @@ begin && vx_dram_req_ready) begin avs_address <= (vx_dram_req_addr >> 6); - avs_read <= 1; + avs_read <= 1; $display("%t: AVS Rd Req: addr=%h", $time, vx_dram_req_addr >> 6); end @@ -327,8 +341,8 @@ begin && vx_dram_req_ready) begin avs_writedata <= vx_dram_req_data; - avs_address <= (vx_dram_req_addr >> 6); - avs_write <= 1; + avs_address <= (vx_dram_req_addr >> 6); + avs_write <= 1; $display("%t: AVS Wr Req: addr=%h", $time, vx_dram_req_addr >> 6); end end @@ -362,11 +376,11 @@ end // AVS address read request queue ///////////////////////////////////////////// -logic cci_write_req; +logic cci_wr_req; always_comb begin - avs_raq_pop = vx_dram_rsp_valid || cci_write_req; + avs_raq_pop = vx_dram_rsp_valid || cci_wr_req; avs_raq_din = avs_address; avs_raq_push = avs_read; end @@ -374,7 +388,7 @@ end VX_generic_queue #( .DATAW($bits(t_local_mem_addr)), .SIZE(AVS_RD_QUEUE_SIZE) -) vx_rd_addr_queue ( +) avs_rd_req_queue ( .clk (clk), .reset (SoftReset), .push (avs_raq_push), @@ -397,7 +411,7 @@ end VX_generic_queue #( .DATAW($bits(t_local_mem_data)), .SIZE(AVS_RD_QUEUE_SIZE) -) vx_rd_data_queue ( +) avs_rd_rsp_queue ( .clk (clk), .reset (SoftReset), .push (avs_rdq_push), @@ -410,101 +424,134 @@ VX_generic_queue #( // CCI Read Request /////////////////////////////////////////////////////////// -t_ccip_c0_ReqMemHdr rd_hdr; +t_ccip_c0_ReqMemHdr cci_read_hdr; -logic cci_read_pending; +logic [31:0] cci_read_ctr; +t_cci_rdq_tag cci_rdq_ctr; + +logic cci_rdq_full; +logic cci_rdq_push; +t_cci_rdq_data cci_rdq_din; + +logic cci_read_wait; always_comb begin - rd_hdr = t_ccip_c0_ReqMemHdr'(0); - rd_hdr.address = csr_io_addr + avs_write_ctr; + cci_read_hdr = t_ccip_c0_ReqMemHdr'(0); + cci_read_hdr.address = csr_io_addr + cci_read_ctr; + cci_read_hdr.mdata = t_cci_rdq_tag'(cci_read_ctr); + + cci_rdq_push = (STATE_WRITE == state) && cp2af_sRxPort.c0.rspValid; + cci_rdq_din = {cp2af_sRxPort.c0.data, t_cci_rdq_tag'(cp2af_sRxPort.c0.hdr.mdata)}; end // Send read requests to CCI always_ff @(posedge clk) begin - if (SoftReset) - begin + if (SoftReset) begin af2cp_sTxPort.c0.hdr <= 0; af2cp_sTxPort.c0.valid <= 0; - cci_read_pending <= 0; + cci_read_ctr <= 0; + cci_rdq_ctr <= 0; + cci_read_wait <= 0; end else begin af2cp_sTxPort.c0.valid <= 0; - 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 - 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); + if (STATE_IDLE == state) begin + cci_read_ctr <= 0; + cci_rdq_ctr <= 0; + cci_read_wait <= 0; end - if (cci_read_pending - && cp2af_sRxPort.c0.rspValid) + if (STATE_WRITE == state + && !cp2af_sRxPort.c0TxAlmFull // ensure read queue not full + && !cci_rdq_full // ensure destination queue not full + && !cci_read_wait // ensure the last batch has arrived + && cci_read_ctr < csr_data_size) // ensure not done begin - $display("%t: CCI Rd Rsp", $time); - cci_read_pending <= 0; - end + af2cp_sTxPort.c0.hdr <= cci_read_hdr; + af2cp_sTxPort.c0.valid <= 1; + cci_read_ctr <= cci_read_ctr + 1; + if (cci_read_ctr == (CCI_RD_WINDOW_SIZE-1)) begin + cci_read_wait <= 1; // end current request batch + end + $display("%t: CCI Rd Req: addr=%h", $time, cci_read_hdr.address); + end + + if (cci_rdq_push) begin + cci_rdq_ctr <= cci_rdq_ctr + 1; + if (cci_rdq_ctr == (CCI_RD_WINDOW_SIZE-1)) begin + cci_read_wait <= 0; // restart new request batch + end + $display("%t: CCI Rd Rsp: idx=%d, ctr=%d", $time, t_cci_rdq_tag'(cp2af_sRxPort.c0.hdr.mdata), cci_rdq_ctr); + end end end +VX_generic_queue #( + .DATAW($bits(t_ccip_clData) + $bits(t_cci_rdq_tag)), + .SIZE(CCI_RD_QUEUE_SIZE) +) cci_rd_req_queue ( + .clk (clk), + .reset (SoftReset), + .push (cci_rdq_push), + .data_in (cci_rdq_din), + .pop (cci_rdq_pop), + .data_out (cci_rdq_dout), + .empty (cci_rdq_empty), + .full (cci_rdq_full) +); + // CCI Write Request ////////////////////////////////////////////////////////// -t_ccip_c1_ReqMemHdr wr_hdr; +t_ccip_c1_ReqMemHdr cci_write_hdr; -logic cci_write_pending; +logic cci_write_wait; always_comb begin - cci_write_req = (STATE_READ == state) - && !avs_rdq_empty - && !cp2af_sRxPort.c1TxAlmFull - && !cci_write_pending - && cci_write_ctr < csr_data_size; + cci_wr_req = (STATE_READ == state) + && !avs_rdq_empty + && !cp2af_sRxPort.c1TxAlmFull + && !cci_write_wait + && 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 + cci_write_hdr = t_ccip_c1_ReqMemHdr'(0); + cci_write_hdr.address = csr_io_addr + cci_write_ctr; + cci_write_hdr.sop = 1; // single line write mode end // Send write requests to CCI always_ff @(posedge clk) begin - if (SoftReset) - 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; + cci_write_wait <= 0; end else begin af2cp_sTxPort.c1.valid <= 0; - if (STATE_IDLE == state) - begin + if (STATE_IDLE == state) begin cci_write_ctr <= 0; end - if (cci_write_req) - begin - af2cp_sTxPort.c1.hdr <= wr_hdr; + if (cci_wr_req) begin + af2cp_sTxPort.c1.hdr <= cci_write_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", $time, wr_hdr.address); + cci_write_wait <= 1; + $display("%t: CCI Wr Req: addr=%h", $time, cci_write_hdr.address); end - if (cci_write_pending + if (cci_write_wait && cp2af_sRxPort.c1.rspValid) begin - cci_write_ctr <= cci_write_ctr + 1; - cci_write_pending <= 0; + cci_write_ctr <= cci_write_ctr + 1; + cci_write_wait <= 0; $display("%t: CCI Wr Rsp (%0d/%0d)", $time, cci_write_ctr + 1, csr_data_size); end end @@ -514,15 +561,13 @@ end always_ff @(posedge clk) begin - if (SoftReset) - begin + if (SoftReset) begin vx_snp_req <= 0; vx_snoop_ctr <= 0; vx_snoop_delay <= 0; end else begin - if (STATE_IDLE == state) - begin + if (STATE_IDLE == state) begin vx_snoop_ctr <= 0; vx_snoop_delay <= 0; end @@ -532,14 +577,13 @@ begin if ((STATE_CLFLUSH == state) && vx_snoop_ctr < csr_data_size && vx_snp_req_ready) - begin + begin vx_snp_req_addr <= (csr_mem_addr + vx_snoop_ctr) << 6; vx_snp_req <= 1; vx_snoop_ctr <= vx_snoop_ctr + 1; end - if (vx_snoop_ctr == csr_data_size) - begin + if (vx_snoop_ctr == csr_data_size) begin vx_snoop_delay <= vx_snoop_delay + 1; end end diff --git a/hw/rtl/VX_define.vh b/hw/rtl/VX_define.vh index 447a4010..37e1eb1f 100644 --- a/hw/rtl/VX_define.vh +++ b/hw/rtl/VX_define.vh @@ -29,7 +29,12 @@ if (!(cond)) $error(msg); \ endgenerate -`define LOG2UP(x) ((x > 1) ? $clog2(x) : 1) +`define CLOG2(x) $clog2(x); +`define FLOG2(x) ($clog2(x) - (((1 << $clog2(x)) > x) ? 1 : 0)) +`define LOG2UP(x) ((x > 1) ? $clog2(x) : 1) + +`define MIN(x, y) ((x < y) ? x : y); +`define MAX(x, y) ((x > y) ? x : y); /////////////////////////////////////////////////////////////////////////////// diff --git a/hw/rtl/Vortex.v b/hw/rtl/Vortex.v index afc703d7..191feeb7 100644 --- a/hw/rtl/Vortex.v +++ b/hw/rtl/Vortex.v @@ -43,6 +43,15 @@ module Vortex #( input wire [31:0] llc_snp_req_addr, output wire llc_snp_req_ready, + + // CSR request + //input wire csr_read_valid; + //input wire csr_write_valid; + //input wire [`CSR_WIDTH-1:0 csr_index; + //input wire csr_data_in; + //output wire [15:0] csr_data_out; + + output wire ebreak ); `DEBUG_BEGIN diff --git a/hw/rtl/libs/VX_generic_queue.v b/hw/rtl/libs/VX_generic_queue.v index 400f73d4..24c25c00 100644 --- a/hw/rtl/libs/VX_generic_queue.v +++ b/hw/rtl/libs/VX_generic_queue.v @@ -10,16 +10,16 @@ module VX_generic_queue #( input wire push, input wire pop, output wire empty, - output wire full, + output wire full, `IGNORE_WARNINGS_END input wire [DATAW-1:0] data_in, output wire [DATAW-1:0] data_out ); if (SIZE == 0) begin - assign empty = 1; - assign data_out = data_in; - assign full = 0; + assign empty = 1; + assign data_out = data_in; + assign full = 0; end else begin // (SIZE > 0) @@ -56,10 +56,9 @@ module VX_generic_queue #( end end - assign data_out = head_r; - assign empty = (size_r == 0); - assign full = (size_r != 0) && !pop; - + assign data_out = head_r; + assign empty = (size_r == 0); + assign full = (size_r != 0); end else begin // (SIZE > 1) reg [DATAW-1:0] curr_r; @@ -82,18 +81,21 @@ module VX_generic_queue #( always @(posedge clk) begin if (reset) begin size_r <= 0; - empty_r <= 1; + empty_r <= 1; full_r <= 0; end else begin if (writing && !reading) begin size_r <= size_r + 1; empty_r <= 0; - if (size_r == SIZE-1) + if (size_r == SIZE-1) begin full_r <= 1; - end else if (reading && !writing) begin + end + end else + if (reading && !writing) begin size_r <= size_r - 1; - if (size_r == 1) - empty_r <= 1; + if (size_r == 1) begin + empty_r <= 1; + end; full_r <= 0; end end @@ -133,5 +135,5 @@ module VX_generic_queue #( assign full = full_r; end end - + endmodule \ No newline at end of file diff --git a/hw/rtl/tex_unit/VX_tex_mgr.v b/hw/rtl/tex_unit/VX_tex_mgr.v new file mode 100644 index 00000000..42184037 --- /dev/null +++ b/hw/rtl/tex_unit/VX_tex_mgr.v @@ -0,0 +1,19 @@ +`include "VX_define.vh" + +module VX_tex_mgr ( + input wire clk, + input wire reset, +); + + //-- + +endmodule + + + + + + + + + diff --git a/hw/rtl/tex_unit/VX_tex_unit.v b/hw/rtl/tex_unit/VX_tex_unit.v new file mode 100644 index 00000000..b7eef8d8 --- /dev/null +++ b/hw/rtl/tex_unit/VX_tex_unit.v @@ -0,0 +1,50 @@ +`include "VX_define.vh" + +module VX_tex_unit #( + parameter TADDRW = 32, + parameter MADDRW = 32, + parameter DATAW = 32, + parameter MAXWTW = 8, + parameter MAXHTW = 8, + parameter MAXFTW = 2, + parameter MAXFMW = 1, + parameter MAXAMW = 2, + parameter TAGW = 16, + + parameter NUMCRQS = 32, +) ( + input wire clk, + input wire reset, + + // Texture Request + input wire tex_req_valid, + input wire [TADDRW-1:0] tex_req_u, + input wire [TADDRW-1:0] tex_req_v, + input wire [MADDRW-1:0] tex_req_addr, + input wire [MAXWTW-1:0] tex_req_width, + input wire [MAXHTW-1:0] tex_req_height, + input wire [MAXFTW-1:0] tex_req_format, + input wire [MAXFMW-1:0] tex_req_filter, + input wire [MAXAMW-1:0] tex_req_clamp, + input wire [TAGW-1:0] tex_req_tag, + output wire tex_req_ready, + + // Texture Response + output wire tex_rsp_valid, + output wire [TAGW-1:0] tex_rsp_tag, + input wire [DATAW-1:0] tex_rsp_data, + input wire tex_rsp_ready, + + // Cache Request + output wire [NUMCRQS-1:0] cache_req_valids, + output wire [NUMCRQS-1:0][MADDRW-1:0] cache_req_addrs, + input wire cache_req_ready, + + // Cache Response + input wire cache_rsp_valid, + input wire [MADDRW-1:0] cache_rsp_addr, + input wire [DATAW-1:0] cache_rsp_data, + output wire cache_rsp_ready +); + +endmodule \ No newline at end of file