URL
https://opencores.org/ocsvn/pcie_ds_dma/pcie_ds_dma/trunk
Subversion Repositories pcie_ds_dma
[/] [pcie_ds_dma/] [trunk/] [core/] [ds_dma64/] [pcie_src/] [pcie_core64_m1/] [source/] [pcie_blk_ll_tx.v] - Rev 40
Go to most recent revision | Compare with Previous | Blame | View Log
//----------------------------------------------------------------------------- // // (c) Copyright 2009-2010 Xilinx, Inc. All rights reserved. // // This file contains confidential and proprietary information // of Xilinx, Inc. and is protected under U.S. and // international copyright and other intellectual property // laws. // // DISCLAIMER // This disclaimer is not a license and does not grant any // rights to the materials distributed herewith. Except as // otherwise provided in a valid license issued to you by // Xilinx, and to the maximum extent permitted by applicable // law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND // WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES // AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING // BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON- // INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and // (2) Xilinx shall not be liable (whether in contract or tort, // including negligence, or under any other theory of // liability) for any loss or damage of any kind or nature // related to, arising under or in connection with these // materials, including for any direct, or any indirect, // special, incidental, or consequential loss or damage // (including loss of data, profits, goodwill, or any type of // loss or damage suffered as a result of any action brought // by a third party) even if such damage or loss was // reasonably foreseeable or Xilinx had been advised of the // possibility of the same. // // CRITICAL APPLICATIONS // Xilinx products are not designed or intended to be fail- // safe, or for use in any application requiring fail-safe // performance, such as life-support or safety devices or // systems, Class III medical devices, nuclear facilities, // applications related to the deployment of airbags, or any // other applications that could lead to death, personal // injury, or severe property or environmental damage // (individually and collectively, "Critical // Applications"). Customer assumes the sole risk and // liability of any use of Xilinx products in Critical // Applications, subject only to applicable laws and // regulations governing limitations on product liability. // // THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS // PART OF THIS FILE AT ALL TIMES. // //----------------------------------------------------------------------------- // Project : V5-Block Plus for PCI Express // File : pcie_blk_ll_tx.v //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- /***************************************************************************** * Description : PCIe Block LocalLink Tx Bridge - adds a LocalLink interface * compatible with the PCIe soft core to the V-5 PCIe Hard * Block * * NOTE: Search for "FIXME" tags for high-priority changes to be made ****************************************************************************/ `timescale 1ns/1ns `ifndef TCQ `define TCQ 1 `endif module pcie_blk_ll_tx //{{{ Module Port declarations #( parameter TX_CPL_STALL_THRESHOLD = 6, parameter TX_DATACREDIT_FIX_EN = 1, parameter TX_DATACREDIT_FIX_1DWONLY= 1, parameter TX_DATACREDIT_FIX_MARGIN = 6, parameter MPS = 0, parameter LEGACY_EP = 0 ) ( // Clock and reset input wire clk, input wire rst_n, // Transaction Link Up input wire trn_lnk_up_n, // PCIe Block Tx Ports output wire [63:0] llk_tx_data, output reg llk_tx_src_rdy_n = 1'b1, output wire llk_tx_src_dsc_n, output wire llk_tx_sof_n, output wire llk_tx_eof_n, output wire llk_tx_sop_n, output wire llk_tx_eop_n, output wire [1:0] llk_tx_enable_n, output reg [2:0] llk_tx_ch_tc = 0, output reg [1:0] llk_tx_ch_fifo = 2'b11, input wire llk_tx_dst_rdy_n, input wire [9:0] llk_tx_chan_space, input wire [7:0] llk_tx_ch_posted_ready_n, // ignored input input wire [7:0] llk_tx_ch_non_posted_ready_n, // ignored input input wire [7:0] llk_tx_ch_completion_ready_n, // ignored input // LocalLink Tx Ports input wire [63:0] trn_td, input wire [7:0] trn_trem_n, input wire trn_tsof_n, input wire trn_teof_n, input wire trn_tsrc_rdy_n, input wire trn_tsrc_dsc_n, input wire trn_terrfwd_n, // NOTE: this input is ignored (* XIL_PAR_PATH = "pcie_ep.LLKTXDSTRDYN->D", XIL_PAR_IP_NAME = "PCIE", syn_keep = "1", keep = "TRUE" *) output reg trn_tdst_rdy_n = 1'b0, output wire trn_tdst_dsc_n, output wire trn_tbuf_av_cpl, input wire [7:0] tx_ch_credits_consumed, input wire [11:0] tx_pd_credits_available, input wire [11:0] tx_pd_credits_consumed, input wire [11:0] tx_npd_credits_available, input wire [11:0] tx_npd_credits_consumed, input wire [11:0] tx_cd_credits_available, input wire [11:0] tx_cd_credits_consumed, output wire clear_cpl_count, input wire pd_credit_limited, input wire npd_credit_limited, input wire cd_credit_limited, input wire trn_pfc_cplh_cl_upd, //used to cause initialization of CPLD fix Margin input wire [7:0] trn_pfc_cplh_cl, //CPLD fix Margin value input wire l0_stats_cfg_transmitted ); //}}} //{{{ Parameters, regs, wires // FIFO encodings localparam POSTED_CAT = 2'b00; localparam NONPOSTED_CAT = 2'b01; localparam COMPLETION_CAT = 2'b10; // Format/Type encoding for message types localparam MRD = 7'b0X_00000; // Memory Read localparam MRDLK = 7'b0X_00001; // Memory Read Lock localparam MWR = 7'b1X_00000; // Memory Write localparam MSG = 7'bX1_10XXX; // Message localparam IORD = 7'b00_00010; // I/O Read localparam IOWR = 7'b10_00010; // I/O Write localparam CFGRD = 7'b00_0010X; // Config Read localparam CFGWR = 7'b10_0010X; // Config Write localparam CPL = 7'bX0_0101X; // Completion localparam CHANSPACE_CPLEMPTY = (MPS==0)? 8'h48 : (MPS==1)? 8'h80 : 8'h80; // Static outputs assign trn_tdst_dsc_n = 1'b1; // Signals for pipeline storage. Note that all signals are active-high, // even though some inputs & outputs are active-low. Also, signals are // added to the pipeline in later stages (sof_gap for example) that are // not present in earlier stages. reg [63:0] td_q1 = 0; // Data reg sof_q1 = 0; // Start of frame reg eof_q1 = 0; // End of frame reg rem_q1 = 0; // 1-bit encoding of remainder/enable reg dsc_q1 = 0; // Discontinue reg vld_q1 = 0; // Valid (* XIL_PAR_PATH = "pcie_ep.LLKTXDSTRDYN->D", XIL_PAR_IP_NAME = "PCIE", syn_keep = "1", keep = "TRUE" *) reg [63:0] td_q2 = 0; (* XIL_PAR_PATH = "pcie_ep.LLKTXDSTRDYN->CE", XIL_PAR_IP_NAME = "PCIE", syn_keep = "1", keep = "TRUE" *) reg [5:0] td_q2_credits = 0; //assume 512byte MPS; 512by*(1dw/4by)*(1cr/4dw)=32 (6 bits needed) reg td_q2_credits_prev = 0; reg td_q2_posted = 0; reg td_q2_iowr = 0; reg td_q2_cpl = 0; reg sof_q2_reg = 0; wire sofpd_q2_rose; wire sofnpd_q2_rose; wire sofcpl_q2_rose; reg pd_q1_reg = 0; reg npd_q1_reg = 0; reg cpl_q1_reg = 0; reg sof_q2 = 0; reg eof_q2 = 0; reg rem_q2 = 0; reg dsc_q2 = 0; reg vld_q2 = 0; reg sof_gap_q2 = 0; // Insert gap before SOF // (due to change of tc and/or fifo) reg [1:0] fifo_q2 = 0; // FIFO (Posted, Non-posted, or Completion) reg [2:0] tc_q2 = 0; // Traffic class reg [63:0] td_q3 = 0; reg sof_q3 = 0; reg eof_q3 = 0; reg rem_q3 = 0; reg dsc_q3 = 0; reg vld_q3 = 0; reg sof_gap_q3 = 0; (* XIL_PAR_PATH = "pcie_ep.LLKTXDSTRDYN->CE", XIL_PAR_IP_NAME = "PCIE", syn_keep = "1", keep = "TRUE" *) reg [1:0] fifo_q3 = 0; reg [2:0] tc_q3 = 0; // FIFO and TC of previous packet - used to determine whether to insert a // gap between EOF and SOF reg [1:0] fifo_last = 0; reg [2:0] tc_last = 0; //(* keep = "true" *) reg shift_pipe; // Move data through pipeline wire shift_pipe; wire only_eof; // Asserted to flush pipeline at EOF reg sof_gap_q3_and_block = 1; reg block_sof = 1; // Asserted to indicate when an SOF with // a new TC or FIFO should be held off reg [2:0] block_cnt = 0; // Control changing of TC/FIFO outputs // Signals comprising the shunt buffer used to decouple the trn_tdst_rdy_n // output from the llk_tx_dst_rdy_n input reg [63:0] td_buf; reg sof_buf; reg eof_buf; reg rem_buf; reg dsc_buf; reg vld_buf = 1'b0; // Shunt buffer contents valid wire buf_divert; // Current cycle to be stored in shunt buf wire buf_rd; // Xfer shunt buffer to pipeline this cycle reg [ 4:0] cpl_in_count; wire [4:0] cpls_buffered; reg llk_tlp_halt; reg llk_tx_src_rdy_n_int; reg [11:0] user_pd_data_credits_in = 0; reg [11:0] user_npd_data_credits_in = 0; reg [11:0] user_cd_data_credits_in = 0; reg [11:0] all_cd_data_credits_in = 0; reg [11:0] l0_stats_cfg_transmitted_cnt=0; reg [11:0] user_cd_data_credits_in_minus_trn_pfc_cplh_cl_plus1 = 0; reg pd_credits_near_gte_far = 0; reg npd_credits_near_gte_far = 0; reg llk_cpl_second_cycle = 0; reg llk_tx_ch_fifo_d = 2'b11; reg q2_cpl_second_cycle = 0; wire [11:0] near_end_pd_credits_buffered; wire [11:0] near_end_npd_credits_buffered; wire [11:0] near_end_cd_credits_buffered; reg near_end_cd_credits_buffered_11_d; wire packet_in_progress; reg packet_in_progress_reg = 0; reg len_eq1_q2 = 0; reg llk_tx_chan_space_cpl_empty= 0; reg eof_q3_only = 0; reg eof_q2_only = 0; reg eof_q1_or_eof_q2_only = 0; reg tc_fifo_change = 1'b0; reg block_fifo = 1'b1; reg [11:0] cd_space_remaining_int = 0; reg cd_space_remaining_int_zero= 0; reg [8:0] trn_pfc_cplh_cl_plus1 = 9; reg trn_pfc_cplh_cl_upd_d1 = 0; reg trn_pfc_cplh_cl_upd_d2 = 0; //}}} //{{{ Shunt Buffer // Input shunt buffer - absorb one cycle of data to decouple // trn_tdst_rdy_n from other signals (and therefore make it a // registered output) // vld_buf has to be set and cleared every cycle something changes always @(posedge clk) begin if (!rst_n) begin vld_buf <= #`TCQ 1'b0; end else begin if (!trn_tdst_rdy_n && !trn_tsrc_rdy_n && buf_divert) begin vld_buf <= #`TCQ 1'b1; end else if (buf_rd) begin vld_buf <= #`TCQ 1'b0; end end end // All the rest of the data & control signals only need to be changed // when new data is available since they're masked with vld_buf always @(posedge clk) begin if (!trn_tdst_rdy_n && !trn_tsrc_rdy_n) begin td_buf <= #`TCQ trn_td; sof_buf <= #`TCQ !trn_tsof_n; eof_buf <= #`TCQ !trn_teof_n; rem_buf <= #`TCQ !trn_trem_n[0]; dsc_buf <= #`TCQ !trn_tsrc_dsc_n; end end // Control when the shunt buffer is written and read // Writes go to the shunt buffer when the first pipeline stage is full // and not emptying // attribute fast of vld_q1 is "true"; assign buf_divert = vld_q1 && !shift_pipe; // The shunt buffer gets read when the pipeline is first shifted after // the shuny buffer is filled // attribute fast of vld_buf is "true"; assign buf_rd = vld_buf && shift_pipe; // Generate trn_tdst_rdy output. It is always asserted unless the shunt // buffer is full or going full. When the shunt buffer goes full, input // is cut off until it goes empty, since the buffer can only absorb a // single cycle of data. always @(posedge clk) begin if (!rst_n) begin trn_tdst_rdy_n <= #`TCQ 1'b0; end else begin trn_tdst_rdy_n <= #`TCQ !((!vld_buf && // !(buf_divert && !trn_tdst_rdy_n)) || // fix for CR472508-add additional qualifier !(buf_divert && !trn_tdst_rdy_n && !trn_tsrc_rdy_n)) || buf_rd); end end //}}} //{{{ Stage 1 // Data & control pipeline, first stage. First stage can receive data // from either the user or the shunt buffer. always @(posedge clk) begin // Only SOF, and vld are reset - to allow inferring of SRL16s if (!rst_n) begin vld_q1 <= #`TCQ 1'b0; end else begin vld_q1 <= #`TCQ (((!trn_tdst_rdy_n && !trn_tsrc_rdy_n && !buf_divert) || buf_rd) || (vld_q1 && !shift_pipe)); end if (!rst_n) begin sof_q1 <= #`TCQ 1'b0; end else if (!trn_tdst_rdy_n && !trn_tsrc_rdy_n && !buf_divert) begin sof_q1 <= #`TCQ !trn_tsof_n; end else if (buf_rd) begin sof_q1 <= #`TCQ sof_buf; //end else if (shift_pipe) begin // sof_q1 <= #`TCQ 1'b0; // SOF implies vld end else begin sof_q1 <= #`TCQ (!shift_pipe && sof_q1); end if (!rst_n) begin eof_q1 <= #`TCQ 0; dsc_q1 <= #`TCQ 0; end else if (!trn_tdst_rdy_n && !trn_tsrc_rdy_n && !buf_divert) begin eof_q1 <= #`TCQ !trn_teof_n; dsc_q1 <= #`TCQ !trn_tsrc_dsc_n; end else if (buf_rd) begin eof_q1 <= #`TCQ eof_buf; dsc_q1 <= #`TCQ dsc_buf; end end always @(posedge clk) begin //If user data available, buffer empty, and either pipe isn't stalled OR stage 1 isn't valid, // grab user data (note: tdst_rdy won't assert if buffer is full) if (!trn_tdst_rdy_n && !trn_tsrc_rdy_n && !buf_divert) begin // !buf_divert = shift_pipe || !vld_q1 td_q1 <= #`TCQ trn_td; rem_q1 <= #`TCQ !trn_trem_n[0]; //If pipe isn't stalled and buffer is valid, pull from buffer //end else if (buf_rd) begin // buf_rd = shift_pipe && vld_buf end else if (shift_pipe) begin // buf_rd = shift_pipe && vld_buf td_q1 <= #`TCQ td_buf; rem_q1 <= #`TCQ rem_buf; end end //}}} //{{{ Stages 2 and 3 // 2nd & 3rd pipeline stages - note that each stage must remain full // in order to correctly generate EOP. Once an EOF is received, the // pipeline can be flushed unless an SOF is received. This allows a // single packet to pass all the way through the pipeline without being // "pushed" by another packet. always @(posedge clk) begin // Only SOF and vld are reset - to allow inferring of SRL16s on other // signals if (!rst_n) begin sof_q2 <= #`TCQ 1'b0; vld_q2 <= #`TCQ 1'b0; len_eq1_q2 <= #`TCQ 1'b0; sof_q3 <= #`TCQ 1'b0; eof_q3 <= #`TCQ 1'b0; vld_q3 <= #`TCQ 1'b0; end else begin if (shift_pipe) begin // Second stage sof_q2 <= #`TCQ sof_q1; vld_q2 <= #`TCQ vld_q1; if (sof_q1) len_eq1_q2 <= #`TCQ (td_q1[41:32] == 10'h001) && !(!td_q1[62] && td_q1[60:57] != 4'b0000); // Third stage - this stage is also the output data sof_q3 <= #`TCQ sof_q2; eof_q3 <= #`TCQ eof_q2 && vld_q2; // Need EOF to be gated with vld // for gapping below vld_q3 <= #`TCQ vld_q2; end end end always @(posedge clk) begin if (!rst_n) begin td_q2 <= #`TCQ 0; td_q2_credits <= #`TCQ 'h0; td_q2_posted <= #`TCQ 0; td_q2_iowr <= #`TCQ 0; td_q2_cpl <= #`TCQ 0; pd_q1_reg <= #`TCQ 0; npd_q1_reg <= #`TCQ 0; cpl_q1_reg <= #`TCQ 0; eof_q2 <= #`TCQ 0; rem_q2 <= #`TCQ 0; dsc_q2 <= #`TCQ 0; end else if (shift_pipe) begin // Second stage td_q2 <= #`TCQ td_q1; if (sof_q1) td_q2_credits <= #`TCQ (td_q1[39:34] + (|td_q1[33:32])); //assume MPS<=512 td_q2_posted <= #`TCQ (td_q1[62:57] == 'b10_0000) || (td_q1[62:57] == 'b11_0000) || (td_q1[62:59] == 'b11_10); td_q2_iowr <= #`TCQ (td_q1[62:57] == 'b10_0001); td_q2_cpl <= #`TCQ (td_q1[62:57] == 'b10_0101); pd_q1_reg <= #`TCQ (td_q1[62:57] == 'b100_000) || (td_q1[62:57] == 'b110_000) || (td_q1[62:59] == 'b111_0); npd_q1_reg <= #`TCQ (td_q1[62:57] == 'b10_0001); cpl_q1_reg <= #`TCQ (td_q1[62:57] == 'b10_0101); eof_q2 <= #`TCQ eof_q1; rem_q2 <= #`TCQ rem_q1 || !eof_q1; // Mask off REM when not EOF dsc_q2 <= #`TCQ dsc_q1; end end always @(posedge clk) begin if (!rst_n) begin sof_q2_reg <= #`TCQ 0; end else begin sof_q2_reg <= #`TCQ sof_q2; end end assign sofpd_q2_rose = (sof_q2 && !sof_q2_reg && pd_q1_reg); assign sofnpd_q2_rose = (sof_q2 && !sof_q2_reg && npd_q1_reg); assign sofcpl_q2_rose = (sof_q2 && !sof_q2_reg && cpl_q1_reg); always @(posedge clk) begin if (!rst_n) begin td_q3 <= #`TCQ 0; rem_q3 <= #`TCQ 0; dsc_q3 <= #`TCQ 0; tc_q3 <= #`TCQ 0; fifo_q3 <= #`TCQ 0; end else if (shift_pipe) begin // Third stage - this stage is also the output data td_q3 <= #`TCQ td_q2; rem_q3 <= #`TCQ rem_q2; dsc_q3 <= #`TCQ dsc_q2; // TC and FIFO only change at SOF if (sof_q2) begin tc_q3 <= #`TCQ tc_q2; fifo_q3 <= #`TCQ fifo_q2; end end end // Calculate and latch "FIFO" (Posted, Non-posted, or Completion) and TC // Calculated from first stage and valid in parallel with sof_q2 always @(posedge clk) begin // No reset neccesary if (sof_q1 && shift_pipe) begin casex (td_q1[62:56]) // Posted MWR, // Memory Write MSG: // Message fifo_q2 <= #`TCQ POSTED_CAT; // Non-Posted MRD, // Memory Read MRDLK, // Memory Read Lock IORD, // I/O Read IOWR, // I/O Write CFGRD, // Config Read CFGWR: // Config Write fifo_q2 <= #`TCQ NONPOSTED_CAT; // Completion CPL: // Completion fifo_q2 <= #`TCQ COMPLETION_CAT; // Frame should _always_ be one of the above, so this is just // here to ensure the synthesizer doesn't do anything weird default: fifo_q2 <= #`TCQ POSTED_CAT; endcase tc_q2 <= #`TCQ td_q1[54:52]; end end //}}} // {{{ TC/FIFO Outputs; Enforce 2-cycle FIFO Change // Generate TC and FIFO output, as well as the block_sof signal. This // prevents changing of TC/FIFO for 2 cycles after EOF and asserting of // a valid SOF beat for one cycle after changing of TC/FIFO. // This does not affect SOF when TC/FIFO doesn't change. // // FIXME comment better // Requirements: // 1) Hold TC/FIFO steady for two cycles WITH DST_RDY ASSERTED after EOF. // 2) Don't assert SOF for one cycle after changing TC/FIFO. // // #1 means asserting block_sof for (at least) two cycles after valid // EOF output. // #2 means continuing to assert block_sof until // a) a valid (non tc/fifo change) sof is transmitted // b) one cycle after tc/fifo changes // synthesis attribute use_clock_enable of block_cnt is no; // attribute fast of eof_q3 is "true"; // Generate sof_gap signal. sof_gap directs the output logic to delay // changing TC/FIFO outputs and starting a new packet when either TC or // FIFO changes. always @(posedge clk) begin if (!rst_n) begin sof_gap_q3 <= #`TCQ 1'b0; fifo_last <= #`TCQ 2'b00; tc_last <= #`TCQ 0; end else if (sof_q2 && shift_pipe) begin // sof_q2 implies vld_q2 // If FIFO or TC has changed, assert sof_gap if ((tc_q2 != tc_last) || (fifo_q2 != fifo_last)) begin sof_gap_q3 <= #`TCQ 1'b1; end else begin sof_gap_q3 <= #`TCQ 1'b0; end // Latch current TC & FIFO at SOF fifo_last <= #`TCQ fifo_q2; tc_last <= #`TCQ tc_q2; end else if (vld_q2 && shift_pipe) begin // Sof_gap is only asserted with SOF sof_gap_q3 <= #`TCQ 1'b0; end end always @(posedge clk) begin if (!rst_n) begin block_sof <= #`TCQ 1'b1; end else begin if (tc_fifo_change) begin block_sof <= #`TCQ 1'b0; end else if (sof_q2) begin block_sof <= #`TCQ 1'b1; end end end always @(posedge clk) begin if (!rst_n) sof_gap_q3_and_block <= #`TCQ 1'b0; else begin if (tc_fifo_change) sof_gap_q3_and_block <= #`TCQ 1'b0; else if ( sof_q2 && shift_pipe ) sof_gap_q3_and_block <= #`TCQ ((tc_q2 != tc_last) || (fifo_q2 != fifo_last)) && (block_sof || sof_q2); else if ( vld_q2 && shift_pipe ) sof_gap_q3_and_block <= #`TCQ 1'b0; else sof_gap_q3_and_block <= #`TCQ sof_gap_q3 && (block_sof || sof_q2); end end always @(posedge clk) begin if (!rst_n) begin block_cnt <= #`TCQ 0; block_fifo <= #`TCQ 1'b0; tc_fifo_change <= #`TCQ 1'b0; llk_tx_ch_tc <= #`TCQ 0; llk_tx_ch_fifo <= #`TCQ 2'b11; llk_tx_ch_fifo_d <= #`TCQ 2'b11; end else begin if (!llk_tx_dst_rdy_n && !llk_tlp_halt) begin block_cnt[0] <= #`TCQ !llk_tx_eof_n && !llk_tx_src_rdy_n_int; end if (!llk_tx_dst_rdy_n && !llk_tlp_halt) begin block_cnt[2:1] <= #`TCQ block_cnt[1:0]; end if (eof_q3) begin block_fifo <= #`TCQ 1'b1; end else if (block_cnt[1] && !llk_tx_dst_rdy_n && !llk_tlp_halt) begin block_fifo <= #`TCQ 1'b0; end if (!block_fifo && (llk_tx_ch_tc != tc_q3 || llk_tx_ch_fifo != fifo_q3)) begin llk_tx_ch_tc <= #`TCQ tc_q3; llk_tx_ch_fifo <= #`TCQ fifo_q3; tc_fifo_change <= #`TCQ 1'b1; end else begin tc_fifo_change <= #`TCQ 1'b0; end llk_tx_ch_fifo_d <= #`TCQ llk_tx_ch_fifo; end end //}}} //{{{ LLK TX SRC RDY Logic // Generate src_rdy to the PCIe block always @* begin llk_tx_src_rdy_n_int <= sof_gap_q3_and_block || !(vld_q3 && (only_eof || !trn_tsrc_rdy_n || vld_buf)); end always @* begin llk_tx_src_rdy_n <= sof_gap_q3_and_block || llk_tlp_halt || !(vld_q3 && (only_eof || !trn_tsrc_rdy_n || vld_buf)); end // only_eof indicates when the end of a packet is in the pipeline and // can be flushed. Flushing is allowed when no SOF follows the EOF. // NOTE: It would be a -very- good idea to make this the output of a // register, to improve timing, if practical. assign only_eof = eof_q1_or_eof_q2_only || eof_q3_only; //}}} //{{{ Shift Pipe Logic // Shift pipeline when: // 1) Data is read out. This can only happen if: // A) There is new data available to be shifted into the pipeline -or- // B) There is an EOF at the beginning of the pipeline (*) // -or- // 2) There's an EOF at the beginning of the pipeline and the end of the // pipeline doesn't contain valid data (*) // -or- // 3) New data is available to be written into the pipeline and the // end of the pipeline doesn't contain valid data // // (*) NOTE - this is to ensure that a single packet (or the end of a // packet) can make it all the way to the output even if there // isn't new data "pushing" it through. // //{{{ Old shift pipe logic //synthesis translate_off // Keep this around for Assertion comparison wire shift_pipe_old = (!llk_tx_src_rdy_n_int && !llk_tx_dst_rdy_n && !llk_tlp_halt) || ((!trn_tsrc_rdy_n || vld_buf) && !vld_q3) || (!vld_q3 && (eof_q1 || (eof_q2 && !sof_q1) || (eof_q3 && !sof_q2 && !sof_q1))); //synthesis translate_on //}}} wire shift_pipe_input0 = (vld_buf || eof_q1 || eof_q2_only || eof_q3_only); LUT6 #(.INIT(64'b00100011_00100011_00100011_00100011_00100011_00100011_00100011_11111111)) shift_pipe1 (.O (shift_pipe), .I5(llk_tx_dst_rdy_n), .I4(llk_tlp_halt), .I3(llk_tx_src_rdy_n_int), .I2(trn_tsrc_rdy_n), .I1(vld_q3), .I0(shift_pipe_input0)); always @(posedge clk) begin if (!rst_n) begin eof_q3_only <= #`TCQ 0; eof_q2_only <= #`TCQ 0; eof_q1_or_eof_q2_only <= #`TCQ 0; end else begin if (!trn_tdst_rdy_n && !trn_tsrc_rdy_n && !buf_divert && shift_pipe) eof_q3_only <= #`TCQ (eof_q2 && vld_q2) && !sof_q1 && trn_tsof_n; else if (!trn_tdst_rdy_n && !trn_tsrc_rdy_n && !buf_divert && !shift_pipe) eof_q3_only <= #`TCQ (eof_q3) && !sof_q2 && trn_tsof_n; else if (buf_rd && shift_pipe) eof_q3_only <= #`TCQ (eof_q2 && vld_q2) && !sof_q1 && !sof_buf; else if (buf_rd && !shift_pipe) eof_q3_only <= #`TCQ (eof_q3) && !sof_q2 && !sof_buf; else if (shift_pipe) eof_q3_only <= #`TCQ (eof_q2 && vld_q2) && !sof_q1; if (!trn_tdst_rdy_n && !trn_tsrc_rdy_n && !buf_divert && shift_pipe) eof_q2_only <= #`TCQ eof_q1 && trn_tsof_n; else if (!trn_tdst_rdy_n && !trn_tsrc_rdy_n && !buf_divert && !shift_pipe) eof_q2_only <= #`TCQ eof_q2 && trn_tsof_n; else if (buf_rd && shift_pipe) eof_q2_only <= #`TCQ eof_q1 && !sof_buf; else if (buf_rd && !shift_pipe) eof_q2_only <= #`TCQ eof_q2 && !sof_buf; else if (shift_pipe) eof_q2_only <= #`TCQ eof_q1; //eof_q2 <= #`TCQ eof_q1; if (!trn_tdst_rdy_n && !trn_tsrc_rdy_n && !buf_divert && shift_pipe) eof_q1_or_eof_q2_only <= #`TCQ (eof_q1 && trn_tsof_n) || !trn_teof_n; else if (!trn_tdst_rdy_n && !trn_tsrc_rdy_n && !buf_divert && !shift_pipe) eof_q1_or_eof_q2_only <= #`TCQ (eof_q2 && trn_tsof_n) || !trn_teof_n; else if (buf_rd && shift_pipe) eof_q1_or_eof_q2_only <= #`TCQ (eof_q1 && !sof_buf) || eof_buf; else if (buf_rd && !shift_pipe) eof_q1_or_eof_q2_only <= #`TCQ (eof_q2 && !sof_buf) || eof_buf; else if (shift_pipe) eof_q1_or_eof_q2_only <= #`TCQ eof_q1; else eof_q1_or_eof_q2_only <= #`TCQ eof_q2_only || eof_q1; end end //}}} //{{{ LLK Output assignments // Outputs to the PCIe block are just polarity-fixed versions of the last // pipeline stage (except for TC/FIFO, which change at different times than // the data) assign llk_tx_data = td_q3; assign llk_tx_enable_n = {1'b0, !rem_q3}; assign llk_tx_sof_n = !(sof_q3);// || (!packet_in_progress && cd_credits_near_gte_far)); assign llk_tx_eof_n = !(eof_q3);// || (!packet_in_progress && cd_credits_near_gte_far)); assign llk_tx_sop_n = 1'b1; //not needed assign llk_tx_eop_n = 1'b1; //not needed assign llk_tx_src_dsc_n = !(dsc_q3);// || (!packet_in_progress && cd_credits_near_gte_far)); //}}} //{{{ Completion Fix and Datacredit Fix Logic always @(posedge clk) begin if (!rst_n) begin cpl_in_count <= #`TCQ 'b0; llk_tlp_halt <= #`TCQ 'b0; end else begin //Counts number of valid (not-discontinued) Completions put into the TRN interface cpl_in_count <= #`TCQ cpl_in_count + l0_stats_cfg_transmitted + (!llk_tx_sof_n && !llk_tx_src_rdy_n_int && !llk_tx_dst_rdy_n && !llk_tlp_halt && (llk_tx_data[61:57] == 5'b0_0101)); //Halt the LLK interface if: // 1: There are 8 (or close to 8) CPLs buffered, and the next CPL is // being presented, or is about to. // --or -- // 2: The number of data credits is about to run out, if the link // parter advertised credits such that it is data-credit limited llk_tlp_halt <= #`TCQ ((cpls_buffered >= TX_CPL_STALL_THRESHOLD) && fifo_q2[1] && (llk_tlp_halt || (sof_q2 && shift_pipe))) || // (TX_DATACREDIT_FIX_EN && // (({6'b000000,td_q2_credits} > cd_space_remaining_int)||cd_space_remaining_int_zero) // && fifo_q2[1] && (llk_tlp_halt || (sof_q2 && shift_pipe))) // || (TX_DATACREDIT_FIX_EN && (len_eq1_q2 || !TX_DATACREDIT_FIX_1DWONLY) && ( (pd_credits_near_gte_far && ~|fifo_q2[1:0] && (llk_tlp_halt || (sof_q2 && shift_pipe))) || (npd_credits_near_gte_far && fifo_q2[0] && (llk_tlp_halt || (sof_q2 && shift_pipe)) && LEGACY_EP)) ); end end //synthesis translate_off reg llk_tlp_halt_cpl8buf; reg llk_tlp_halt_cpldatacredit; reg llk_tlp_halt_pdatacredit; reg llk_tlp_halt_npdatacredit; always @(posedge clk) begin if (!rst_n) begin llk_tlp_halt_cpl8buf <= #`TCQ 1'b0; llk_tlp_halt_cpldatacredit <= #`TCQ 1'b0; llk_tlp_halt_pdatacredit <= #`TCQ 1'b0; llk_tlp_halt_npdatacredit <= #`TCQ 1'b0; end else begin llk_tlp_halt_cpl8buf <= #`TCQ ((cpls_buffered >= TX_CPL_STALL_THRESHOLD) && fifo_q2[1] && (llk_tlp_halt || (sof_q2 && shift_pipe))); llk_tlp_halt_cpldatacredit <= #`TCQ (TX_DATACREDIT_FIX_EN && ({6'b0,td_q2_credits} > cd_space_remaining_int)); llk_tlp_halt_pdatacredit <= #`TCQ (TX_DATACREDIT_FIX_EN && (len_eq1_q2 || !TX_DATACREDIT_FIX_1DWONLY) && (pd_credits_near_gte_far && ~|fifo_q2[1:0] && (llk_tlp_halt || (sof_q2 && shift_pipe)))); llk_tlp_halt_npdatacredit <= #`TCQ (TX_DATACREDIT_FIX_EN && (len_eq1_q2 || !TX_DATACREDIT_FIX_1DWONLY) && (npd_credits_near_gte_far && fifo_q2[0] && (llk_tlp_halt || (sof_q2 && shift_pipe)) && LEGACY_EP)); end end //synthesis translate_on assign cpls_buffered = cpl_in_count - tx_ch_credits_consumed[4:0]; assign trn_tbuf_av_cpl = (cpls_buffered < (TX_CPL_STALL_THRESHOLD - 2)); assign near_end_pd_credits_buffered = (user_pd_data_credits_in - tx_pd_credits_consumed); assign near_end_npd_credits_buffered = (user_npd_data_credits_in - tx_npd_credits_consumed); //This is an estimate of the number of Cpls buffered in the TLM's Tx buffer. //It makes a conservate estimate of Cfg Cpls. //#Cpls presented on TRN - # Cpls sent (subtract conservate estimate for internal Cfg Cpls) assign near_end_cd_credits_buffered = (all_cd_data_credits_in - tx_cd_credits_consumed); assign clear_cpl_count = llk_tx_chan_space_cpl_empty; assign packet_in_progress = packet_in_progress_reg || (sof_q3 && !llk_tx_src_rdy_n); always @(posedge clk) begin if (!rst_n) begin packet_in_progress_reg <= #`TCQ 0; end else if (sof_q3 && !llk_tx_src_rdy_n) begin packet_in_progress_reg <= #`TCQ 1; end else if (eof_q3 && !llk_tx_src_rdy_n) begin packet_in_progress_reg <= #`TCQ 0; end end always @(posedge clk) begin if (!rst_n) begin user_pd_data_credits_in <= #`TCQ TX_DATACREDIT_FIX_MARGIN; user_npd_data_credits_in <= #`TCQ 'h1; //NPD is different; must account for itself only user_cd_data_credits_in <= #`TCQ TX_DATACREDIT_FIX_MARGIN; all_cd_data_credits_in <= #`TCQ TX_DATACREDIT_FIX_MARGIN; l0_stats_cfg_transmitted_cnt<= #`TCQ 0; near_end_cd_credits_buffered_11_d<= #`TCQ 0; end else begin near_end_cd_credits_buffered_11_d<= #`TCQ near_end_cd_credits_buffered[11]; // POSTED FIX if (sofpd_q2_rose) user_pd_data_credits_in <= #`TCQ user_pd_data_credits_in + td_q2_credits; // NON-POSTED FIX if (sofnpd_q2_rose) user_npd_data_credits_in <= #`TCQ user_npd_data_credits_in + 1; // COMPLETION FIX if (sofcpl_q2_rose) begin user_cd_data_credits_in <= #`TCQ user_cd_data_credits_in + td_q2_credits; user_cd_data_credits_in_minus_trn_pfc_cplh_cl_plus1 <= #`TCQ user_cd_data_credits_in + td_q2_credits - trn_pfc_cplh_cl_plus1; end //If Cpl queue goes empty, then near_end_cd_credits_buffered s/b 0 (or MARGIN). The //Cfg Cpl estimate (possibly high) s/b adjusted to reflect the delta //between total consumed and user to return "near_end.." to MARGIN (trn_pfc_cplh_cl_plus1) // if (llk_tx_chan_space_cpl_empty || near_end_cd_credits_buffered[11]) if (llk_tx_chan_space_cpl_empty || near_end_cd_credits_buffered_11_d) l0_stats_cfg_transmitted_cnt <= #`TCQ tx_cd_credits_consumed - user_cd_data_credits_in_minus_trn_pfc_cplh_cl_plus1; else l0_stats_cfg_transmitted_cnt <= #`TCQ l0_stats_cfg_transmitted_cnt + l0_stats_cfg_transmitted; if (sofcpl_q2_rose) all_cd_data_credits_in <= #`TCQ user_cd_data_credits_in + l0_stats_cfg_transmitted_cnt + td_q2_credits; else all_cd_data_credits_in <= #`TCQ user_cd_data_credits_in + l0_stats_cfg_transmitted_cnt; end end always @(posedge clk) begin if (!rst_n) begin td_q2_credits_prev <= #`TCQ 'h0; llk_tx_chan_space_cpl_empty <= #`TCQ 1'b0; llk_cpl_second_cycle <= #`TCQ 1'b0; q2_cpl_second_cycle <= #`TCQ 1'b0; trn_pfc_cplh_cl_upd_d1 <= #`TCQ 1'b0; trn_pfc_cplh_cl_upd_d2 <= #`TCQ 1'b0; trn_pfc_cplh_cl_plus1 <= #`TCQ 'd9; //default of 8 headers + 1 end else begin if (sofcpl_q2_rose) td_q2_credits_prev <= #`TCQ td_q2_credits[0]; //llk_tx_chan_space_cpl_empty <= #`TCQ (llk_tx_chan_space[7:0] == 8'hb1) && llk_cpl_second_cycle; llk_tx_chan_space_cpl_empty <= #`TCQ (llk_tx_chan_space[7:0] == CHANSPACE_CPLEMPTY) && (llk_tx_ch_fifo==2'b10); llk_cpl_second_cycle <= #`TCQ !llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n && llk_tx_ch_fifo[1]; q2_cpl_second_cycle <= #`TCQ sof_q2 && vld_q2 && shift_pipe && td_q2_cpl; trn_pfc_cplh_cl_upd_d1 <= #`TCQ trn_pfc_cplh_cl_upd; trn_pfc_cplh_cl_upd_d2 <= #`TCQ trn_pfc_cplh_cl_upd_d1; if (trn_pfc_cplh_cl_upd && !trn_pfc_cplh_cl_upd_d1) trn_pfc_cplh_cl_plus1 <= #`TCQ {1'b0,trn_pfc_cplh_cl} + 1; end end always @(posedge clk) begin //SOFq2___|A|_|B|_____ assume "A" is final good packet (first pkt to violate inequality) //EOFq2_____|A|_|B|___ //userpd____(+A (+B //gte _______|^^^^^^^ //halt _________|^^^^^ //SOFq3_____|A|_|B|___ //EOFq3_______|A|_|B|_ if (!pd_credit_limited) pd_credits_near_gte_far <= #`TCQ 1'b0; else pd_credits_near_gte_far <= #`TCQ (near_end_pd_credits_buffered >= tx_pd_credits_available); if (!npd_credit_limited) npd_credits_near_gte_far <= #`TCQ 1'b0; else npd_credits_near_gte_far <= #`TCQ (near_end_npd_credits_buffered > tx_npd_credits_available); //SOFq2___|A|_|B|_____ assume "A" is final good packet (does NOT to violate inequality) //EOFq2_____|A|_|B|___ //usercd____(+A (+B //cdrema______(+A (+B //gte _______|^^^^^^^ //halt _________|^^^^^ //SOFq3_____|A|_|B|___ //EOFq3_______|A|_|B|_ end always @(posedge clk) begin if (!cd_credit_limited) begin cd_space_remaining_int <= #`TCQ 12'hFFF; cd_space_remaining_int_zero <= #`TCQ 1'b0; // else if (tx_cd_credits_available < near_end_cd_credits_buffered) //underflow protection // cd_space_remaining_int <= #`TCQ 0; end else begin //tx_cd_credits_available is how many credits the link partner can // accept, and no more than that should exist in the Tx buffer (otherwise // data credit bug c/b triggered, and TLPs c/b sent). It is CPL DATA LIMIT-CONSUMED cd_space_remaining_int <= #`TCQ (tx_cd_credits_available - near_end_cd_credits_buffered); cd_space_remaining_int_zero <= #`TCQ (tx_cd_credits_available <= near_end_cd_credits_buffered); end end //}}} //{{{ Debug //synthesis translate_off reg [11:0] llk_pd_credit_count; reg [11:0] llk_pd_credit_count_reg; reg [11:0] llk_npd_credit_count; reg [11:0] llk_npd_credit_count_reg; reg [11:0] llk_cpl_credit_count; reg [11:0] llk_cpl_credit_count_reg; always @(posedge clk) begin if (!rst_n) llk_pd_credit_count_reg <= #`TCQ TX_DATACREDIT_FIX_MARGIN; else if (!llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n && ((llk_tx_data[62] && (llk_tx_data[60:56]==5'b00000)) || (llk_tx_data[62:59] == 4'b11_10))) if (llk_tx_data[41:32]==10'h0) llk_pd_credit_count_reg <= #`TCQ llk_pd_credit_count_reg + {1'b1,llk_tx_data[41:34]}; else llk_pd_credit_count_reg <= #`TCQ llk_pd_credit_count_reg + llk_tx_data[41:34] + |llk_tx_data[33:32]; end always @* begin if (!llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n && ((llk_tx_data[62] && (llk_tx_data[60:56]==5'b00000)) || (llk_tx_data[62:59] == 4'b11_10))) if (llk_tx_data[41:32]==10'h0) llk_pd_credit_count = llk_pd_credit_count_reg + {1'b1,llk_tx_data[41:34]}; else llk_pd_credit_count = llk_pd_credit_count_reg + llk_tx_data[41:34] + |llk_tx_data[33:32]; else llk_pd_credit_count = llk_pd_credit_count_reg; end always @(posedge clk) begin if (!rst_n) llk_npd_credit_count_reg <= #`TCQ 'h1; else if (!llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n && (llk_tx_data[62:56]==7'b100_0010)) llk_npd_credit_count_reg <= #`TCQ llk_npd_credit_count_reg + llk_tx_data[41:34] + |llk_tx_data[33:32]; end always @* begin if (!llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n && (llk_tx_data[62:56]==7'b100_0010)) llk_npd_credit_count = llk_npd_credit_count_reg + llk_tx_data[41:34] + |llk_tx_data[33:32]; else llk_npd_credit_count = llk_npd_credit_count_reg; end always @(posedge clk) begin if (!trn_pfc_cplh_cl_upd_d2) llk_cpl_credit_count_reg <= #`TCQ {3'b000, trn_pfc_cplh_cl_plus1}; else if (!llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n && (llk_tx_data[62:57]==6'b100_101)) llk_cpl_credit_count_reg <= #`TCQ llk_cpl_credit_count_reg + llk_tx_data[41:34] + |llk_tx_data[33:32]; end always @* begin if (!llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n && (llk_tx_data[62:57]==6'b100_101)) llk_cpl_credit_count = llk_cpl_credit_count_reg + llk_tx_data[41:34] + |llk_tx_data[33:32]; else llk_cpl_credit_count = llk_cpl_credit_count_reg; end wire [11:0] user_pd_data_credits_in_raw = user_pd_data_credits_in - TX_DATACREDIT_FIX_MARGIN; wire [11:0] user_npd_data_credits_in_raw = user_npd_data_credits_in - 1; wire [11:0] user_cd_data_credits_in_raw = all_cd_data_credits_in - TX_DATACREDIT_FIX_MARGIN; //synthesis translate_on //}}} //{{{ Assertions `ifdef SV //synthesis translate_off //During SOF, either LLK Tx is inactive, or active transmitting non-Cpl, or active // transmitting a Cpl and is below threshold ASSERT_LLK_TX_NOCPL_BEYOND_THRESHOLD: assert property (@(posedge clk) !llk_tx_sof_n && !llk_tx_src_rdy_n && llk_tx_ch_fifo[1] |-> (cpls_buffered <= TX_CPL_STALL_THRESHOLD) ) else $fatal; ASSERT_SHIFT_PIPE_LUT_EQ_EQUATION: assert property (@(posedge clk) rst_n |-> (shift_pipe == shift_pipe_old) ) else $fatal; ASSERT_EOFQ1Q2_REPLACEMENT: assert property (@(posedge clk) rst_n |-> (eof_q1_or_eof_q2_only == (eof_q1 || (eof_q2 && !sof_q1))) ) else $fatal; ASSERT_LLKHALT_RISES_ONLY_ON_LLKSOF: assert property (@(posedge clk) rst_n && llk_tlp_halt |-> !llk_tx_sof_n ) else $fatal; ASSERT_LLK_PD_CREDITCNT_INCONSISTENT: assert property (@(posedge clk) rst_n && !llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n |-> (llk_pd_credit_count != user_pd_data_credits_in) ) else $fatal; ASSERT_LLK_NPD_CREDITCNT_INCONSISTENT:assert property (@(posedge clk) rst_n && !llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n |-> (llk_npd_credit_count!= user_npd_data_credits_in) ) else $fatal; //ASSERT_LLK_CPL_CREDITCNT_INCONSISTENT:assert property (@(posedge clk) // rst_n && !llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n |-> (llk_cpl_credit_count!= user_cd_data_credits_in) // ) else $fatal; //synthesis translate_on `else //synthesis translate_off always @(posedge clk) if (rst_n && !llk_tx_sof_n && !llk_tx_src_rdy_n && llk_tx_ch_fifo[1] && (cpls_buffered > TX_CPL_STALL_THRESHOLD)) begin $display("ASSERT_LLK_TX_NOCPL_BEYOND_THRESHOLD"); $finish; end always @(posedge clk) if (rst_n && (shift_pipe != shift_pipe_old)) begin $display("ASSERT_SHIFT_PIPE_LUT_EQ_EQUATION"); $finish; end always @(posedge clk) if (rst_n && (eof_q1_or_eof_q2_only != (eof_q1 || (eof_q2 && !sof_q1)))) begin $display("ASSERT_EOFQ1Q2_REPLACEMENT"); $finish; end always @(posedge clk) if (rst_n && llk_tlp_halt && llk_tx_sof_n) begin $display("ASSERT_LLKHALT_RISES_ONLY_ON_LLKSOF"); $finish; end always @(posedge clk) if (rst_n && llk_tlp_halt && llk_tx_sof_n) begin $display("ASSERT_LLKHALT_RISES_ONLY_ON_LLKSOF"); $finish; end always @(posedge clk) if (rst_n && !llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n && (llk_pd_credit_count != user_pd_data_credits_in)) begin $display("ASSERT_LLK_PD_CREDITCNT_INCONSISTENT"); $finish; end always @(posedge clk) if (rst_n && !llk_tx_sof_n && !llk_tx_src_rdy_n && !llk_tx_dst_rdy_n && (llk_npd_credit_count != user_npd_data_credits_in)) begin $display("ASSERT_LLK_NPD_CREDITCNT_INCONSISTENT"); $finish; end reg [9:0] initcnt = 0; always @(posedge clk) begin if (!rst_n) initcnt <= #`TCQ 0; else if (trn_pfc_cplh_cl_upd && !(&initcnt)) initcnt <= #`TCQ initcnt + 1; end //Check that Posteds do not go over the limit by more than MPS always @(posedge clk) if (rst_n && pd_credit_limited && (near_end_pd_credits_buffered > (tx_pd_credits_available + 512))) begin $display("ASSERT_TX_TOOMUCH_POSTEDDATA_OUTSTANDING"); $finish; end always @(posedge clk) if (rst_n && npd_credit_limited && (near_end_npd_credits_buffered > (tx_npd_credits_available + 1))) begin $display("ASSERT_TX_TOOMUCH_NONPOSTEDDATA_OUTSTANDING"); $finish; end //synthesis translate_on `endif //}}} endmodule // pcie_blk_ll_tx
Go to most recent revision | Compare with Previous | Blame | View Log