/*******************************************************************************
 * Module: ahci_top
 * Date:2016-01-09  
 * Author: Andrey Filippov     
 * Description: Top module of the AHCI implementation
 * 
 * Copyright (c) 2016 Elphel, Inc .
 * ahci_top.v is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  ahci_top.v is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/> .
 *******************************************************************************/
`timescale 1ns/1ps

module  ahci_top#(
    parameter PREFETCH_ALWAYS =       0,
    parameter READ_REG_LATENCY =      2, // 0 if  reg_rdata is available with reg_re/reg_addr, 2 with re/regen
//    parameter READ_CT_LATENCY =       1, // 0 if  ct_rdata is available with reg_re/reg_addr, 2 with re/regen
    parameter READ_CT_LATENCY =       2, // 0 if  ct_rdata is available with reg_re/reg_addr, 2 with re/regen
    parameter ADDRESS_BITS =         10, // number of memory address bits - now fixed. Low half - RO/RW/RWC,RW1 (2-cycle write), 2-nd just RW (single-cycle)
    parameter HBA_RESET_BITS =        9, // duration of HBA reset in aclk periods (9: ~10usec)
    parameter RESET_TO_FIRST_ACCESS = 1, // keep port reset until first R/W any register by software
    parameter FREQ_METER_WIDTH =     12
    
)(
    input             aclk,    // clock - should be buffered
    input             arst,    // @aclk sync reset, active high
    input             mclk,    // SATA system clock (current 75MHz for SATA2)
    input             mrst,    // reset in mclk clock domain (after SATA PLL is on)
    // async reset for SATA (mrst will be response to it)
    output            hba_arst,          // hba async reset (currently does ~ the same as port reset)
    output            port_arst,         // port0 async set by software (does not include arst)
    output            port_arst_any,     // port0 async set by software and by arst
    input             hclk,    // AXI HP interface clock for 64-bit DMA (current - 150MHz
    input             hrst,    // reset in hclk clock domain
// MAXIGP1   
// AXI Write Address
    input      [31:0] awaddr,  // AWADDR[31:0], input
    input             awvalid, // AWVALID, input
    output            awready, // AWREADY, output
    input      [11:0] awid,    // AWID[11:0], input
    input      [ 3:0] awlen,   // AWLEN[3:0], input
    input      [ 1:0] awsize,  // AWSIZE[1:0], input
    input      [ 1:0] awburst, // AWBURST[1:0], input
// AXI PS Master GP0: Write Data
    input      [31:0] wdata,   // WDATA[31:0], input
    input             wvalid,  // WVALID, input
    output            wready,  // WREADY, output
    input      [11:0] wid,     // WID[11:0], input
    input             wlast,   // WLAST, input
    input      [ 3:0] wstb,    // WSTRB[3:0], input
// AXI PS Master GP0: Write response
    output            bvalid,  // BVALID, output
    input             bready,  // BREADY, input
    output     [11:0] bid,     // BID[11:0], output
    output     [ 1:0] bresp,    // BRESP[1:0], output
// AXI Read Address   
    input      [31:0] araddr,  // ARADDR[31:0], input 
    input             arvalid, // ARVALID, input
    output            arready, // ARREADY, output
    input      [11:0] arid,    // ARID[11:0], input
    input      [ 3:0] arlen,   // ARLEN[3:0], input
    input      [ 1:0] arsize,  // ARSIZE[1:0], input
    input      [ 1:0] arburst, // ARBURST[1:0], input
// AXI Read Data
    output     [31:0] rdata,   // RDATA[31:0], output
    output            rvalid,  // RVALID, output
    input             rready,  // RREADY, input
    output     [11:0] rid,     // RID[11:0], output
    output            rlast,   // RLAST, output
    output     [ 1:0] rresp,   // RRESP
// SAXIHP3    
    // axi_hp signals write channel
    // write address
    output     [31:0] afi_awaddr,
    output            afi_awvalid,
    input             afi_awready, // @SuppressThisWarning VEditor unused - used FIF0 level
    output     [ 5:0] afi_awid,
    output     [ 1:0] afi_awlock,
    output     [ 3:0] afi_awcache,
    output     [ 2:0] afi_awprot,
    output     [ 3:0] afi_awlen,
    output     [ 1:0] afi_awsize,
    output     [ 1:0] afi_awburst,
    output     [ 3:0] afi_awqos,
    // write data
    output     [63:0] afi_wdata,
    output            afi_wvalid,
    input             afi_wready,  // @ SuppressThisWarning VEditor unused - used FIF0 level
    output     [ 5:0] afi_wid,
    output            afi_wlast,
    output     [ 7:0] afi_wstrb,
    // write response
    input             afi_bvalid,   // @SuppressThisWarning VEditor unused
    output            afi_bready,
    input      [ 5:0] afi_bid,      // @SuppressThisWarning VEditor unused
    input      [ 1:0] afi_bresp,    // @SuppressThisWarning VEditor unused
    // PL extra (non-AXI) signals
    input      [ 7:0] afi_wcount,
    input      [ 5:0] afi_wacount,
    output            afi_wrissuecap1en,
    // AXI_HP signals - read channel
    // read address
    output     [31:0] afi_araddr,
    output               afi_arvalid,
    input                afi_arready,  // @SuppressThisWarning VEditor unused - used FIF0 level
    output     [ 5:0] afi_arid,
    output     [ 1:0] afi_arlock,
    output     [ 3:0] afi_arcache,
    output     [ 2:0] afi_arprot,
    output     [ 3:0] afi_arlen,
    output     [ 1:0] afi_arsize,
    output     [ 1:0] afi_arburst,
    output     [ 3:0] afi_arqos,
    // read data
    input      [63:0] afi_rdata,
    input             afi_rvalid,
    output            afi_rready,
    input      [ 5:0] afi_rid,     // @SuppressThisWarning VEditor unused
    input             afi_rlast,   // @SuppressThisWarning VEditor unused
    input      [ 1:0] afi_rresp,   // @SuppressThisWarning VEditor unused
    // PL extra (non-AXI) signals
    input      [ 7:0] afi_rcount,
    input      [ 2:0] afi_racount,
    output            afi_rdissuecap1en,
// Data/type FIFO, host -> device   
    // Data System memory or FIS -> device
    output      [31:0] h2d_data,     // 32-bit data from the system memory to HBA (dma data)
    output      [ 1:0] h2d_type,     // 0 - data, 1 - FIS head, 2 - FIS END (make FIS_Last?)
    output             h2d_valid,    // output register full
    input              h2d_ready,     // send FIFO has room for data (>= 8? dwords)
 
// Data/type FIFO, device -> host
    input       [31:0] d2h_data,         // FIFO output data
    input       [ 1:0] d2h_type,    // 0 - data, 1 - FIS head, 2 - R_OK, 3 - R_ERR
    input              d2h_valid,  // Data available from the transport layer in FIFO                
    input              d2h_many,    // Multiple DWORDs available from the transport layer in FIFO           
    output             d2h_ready,   // This module or DMA consumes DWORD
    
    // communication with transport/link/phys layers
//    input              phy_rst,      // frome phy, as a response to hba_arst || port_arst. It is deasserted when clock is stable
    input       [ 1:0] phy_ready, // 0 - not ready, 1..3 - negotiated speed
    input              xmit_ok,      // FIS transmission acknowledged OK
    input              xmit_err,     // Error during sending of a FIS
    input              syncesc_recv, // These two inputs interrupt transmit
    output             pcmd_st_cleared, // bit was cleared by software    
    output             syncesc_send,  // Send sync escape
    input              syncesc_send_done, // "SYNC escape until the interface is quiescent..."
    output             comreset_send,     // Not possible yet?
    input              cominit_got,
    output             set_offline, // electrically idle
    input              x_rdy_collision, // X_RDY/X_RDY collision on interface 
    
    output             send_R_OK,    // Should it be originated in this layer SM?
    output             send_R_ERR,
    
    // additional errors from SATA layers (single-clock pulses):
    input              serr_DT,   // RWC: Transport state transition error
    input              serr_DS,   // RWC: Link sequence error
    input              serr_DH,   // RWC: Handshake Error (i.e. Device got CRC error)
    input              serr_DC,   // RWC: CRC error in Link layer
    input              serr_DB,   // RWC: 10B to 8B decode error
    input              serr_DW,   // RWC: COMMWAKE signal was detected
    input              serr_DI,   // RWC: PHY Internal Error
                                  // sirq_PRC,
                                  // sirq_IF || // sirq_INF  
    input              serr_EE,   // RWC: Internal error (such as elastic buffer overflow or primitive mis-alignment)
    input              serr_EP,   // RWC: Protocol Error - a violation of SATA protocol detected
    input              serr_EC,   // RWC: Persistent Communication or Data Integrity Error
    input              serr_ET,   // RWC: Transient Data Integrity Error (error not recovered by the interface)
    input              serr_EM,   // RWC: Communication between the device and host was lost but re-established
    input              serr_EI,   // RWC: Recovered Data integrity Error
    // additional control signals for SATA layers
    output       [3:0] sctl_ipm,          // Interface power management transitions allowed
    output       [3:0] sctl_spd,          // Interface maximal speed
    
    

    output             irq, // CPU interrupt request
    
`ifdef USE_DATASCOPE
// Datascope interface (write to memory that can be software-read)
    input                    datascope1_clk,
    input [ADDRESS_BITS-1:0] datascope1_waddr,      
    input                    datascope1_we,
    input             [31:0] datascope1_di,
`endif    
    
    
`ifdef USE_DRP
    output                    drp_en, // @aclk strobes drp_ad
    output                    drp_we,
    output             [14:0] drp_addr,       
    output             [15:0] drp_di,
    input                     drp_rdy,
    input              [15:0] drp_do, 
`endif    
    input  [FREQ_METER_WIDTH - 1:0] xclk_period,      // relative (to 2*clk) xclk period
    input       [31:0] debug_in_phy,
    input       [31:0] debug_in_link
    
    
);
`ifdef USE_DATASCOPE
// Datascope interface (write to memory that can be software-read)
   wire                     datascope_clk;
   wire  [ADDRESS_BITS-1:0] datascope_waddr;      
   wire                     datascope_we;
   wire              [31:0] datascope_di;
`endif    

// axi_ahci_regs signals:
// 1. Notification of data written @ hba_clk
    wire [ADDRESS_BITS-1:0] soft_write_addr;  // register address written by software
    wire             [31:0] soft_write_data;  // register data written (after applying wstb and type (RO, RW, RWC, RW1)
    wire                    soft_write_en;    // write enable for data write
//    wire                    hba_arst;       // hba async reset (currently does ~ the same as port reset)
//    wire                    port_arst;      // port0 async reset by software
// 2. HBA R/W registers, use hba clock
//    wire                    hba_rst;
    wire                    regs_we_acs;
//    wire              [1:0] regs_re_fsm;
    wire             [31:0] regs_din_from_acs; // from fsm
    wire                    regs_we_freceive;
    wire              [1:0] regs_re_ftransmit; // [0] - re, [1] - regen
    wire [ADDRESS_BITS-1:0] regs_saddr; // read/write adderss from ahci_fsm
    wire [ADDRESS_BITS-1:0] regs_waddr;
    wire [ADDRESS_BITS-1:0] regs_raddr;
    wire             [31:0] regs_din_from_freceive;
    wire             [31:0] regs_dout;

    reg                     en_port;
    wire              [1:0] regs_re = en_port ?  regs_re_ftransmit : 2'b0; // [0] - re, [1] - regen
    wire                    regs_we = en_port ? ( regs_we_freceive | regs_we_acs) : 1'b0;


    wire [ADDRESS_BITS-1:0] regs_addr = ({ADDRESS_BITS{regs_we_freceive}} & regs_waddr) |
                                        ({ADDRESS_BITS{regs_re_ftransmit[0]}} & regs_raddr) |
                                        ({ADDRESS_BITS{regs_we_acs}} & regs_saddr);
                                        

/*
    wire [ADDRESS_BITS-1:0] regs_addr = ({ADDRESS_BITS{en_port & regs_we_freceive}} & regs_waddr) |
                                        ({ADDRESS_BITS{en_port & regs_re_ftransmit[0]}} & regs_raddr) |
                                        ({ADDRESS_BITS{en_port & regs_we_acs}} & regs_saddr);
*/                                        
    wire             [31:0] regs_din =  ({32{regs_we_freceive}} & regs_din_from_freceive) |
                                        ({32{regs_we_acs}} &      regs_din_from_acs);
//    wire              [1:0] regs_re = regs_re_ftransmit | regs_re_fsm; // [0] - re, [1] - regen
    
    
//---------------------    

//    wire             [31:7] ctba; // input[31:7] 
    wire                    ctba_ld; // input
    wire             [15:0] prdtl; // input[15:0]
     
    wire                    dev_wr; // input
    wire                    dma_cmd_start; // input
    wire                    dma_prd_start; // input
    wire                    dma_cmd_abort_xmit; // input
    wire                    dma_cmd_abort_fsm;  // abort from FSM (also from ahci_fis_transmit)
        
// Use some of the custom registers in the address space?    
    wire             [17:0] fsm_pgm_ad; // @aclk, address/data to program the AHCI FSM
    wire                    fsm_pgm_wa; // @aclk, address strobe to program the AHCI FSM
    wire                    fsm_pgm_wd; // @aclk, data strobe to program the AHCI FSM
    
    
    wire             [ 3:0] axi_wr_cache_mode; // input[3:0] 
    wire             [ 3:0] axi_rd_cache_mode; // input[3:0] 
    wire                    set_axi_cache_mode; // input (both axi_wr_cache_mode and axi_rd_cache_mode)
    wire                    dma_ct_busy; // output reg 
    wire             [ 4:0] dma_ct_addr; // input[4:0] 
    wire             [ 1:0] dma_ct_re; // input
    wire             [31:0] dma_ct_data; // output[31:0] reg 
///    wire                    dma_prd_done; // output (finished next prd)
    wire                    dma_prd_irq_clear; // reset pending prd_irq
    wire                    dma_prd_irq_pend;  // prd interrupt pending. This is just a condition for irq - actual will be generated after FIS OK
    wire                    dma_cmd_busy; // output reg (DMA engine is processing PRDs)
    wire                    dma_cmd_done; // output (last PRD is over)

    wire                    dma_abort_busy;
    wire                    dma_abort_done;
    wire                    axi_mismatch;
    
    wire             [31:0] dma_dout;    // output[31:0] 
    wire                    dma_dav; // output
    wire                    dma_re;      // input
    wire                    last_h2d_data;// when active and no new data for 2 clocks - that was the last one
    
    wire                    dma_in_ready; // output
    wire                    dma_we;      // input
    wire                    dma_extra_din;    // all DRDs are transferred to memory, but FIFO has some data. Valid when transfer is stopped
    
    
// ---------------------------------------
    // fsm <-> ahc_fis_receive
    // fsm ->
    wire                    frcv_first_vld;
    // To debug/recover - 
    wire                    frcv_first_invalid; // Some data available from FIFO, but not FIS head
    wire                    frcv_first_flush;   // Skip FIFO data until empty or FIS head
    
    wire                    frcv_get_dsfis;
    wire                    frcv_get_psfis;
    wire                    frcv_get_rfis;
    wire                    frcv_get_sdbfis;
    wire                    frcv_get_ufis;
    wire                    frcv_get_data_fis;
    wire                    frcv_get_ignore;    // ignore whatever FIS (use for DMA activate too?)
    // short commands:
    // next commands use register address/data/we for 1 clock cycle - after next to command (commnd - t0, we - t2)
    wire                    frcv_update_err_sts;// update PxTFD.STS and PxTFD.ERR from the last received regs d2h
    wire                    frcv_update_pio;    // update PxTFD.STS and PxTFD.ERR from pio_* (entry PIO:Update)
    
    wire                    frcv_update_prdbc;  // update PRDBC in registers
    wire                    frcv_clear_bsy_drq; // clear PxTFD.STS.BSY and PxTFD.STS.DRQ, update
    wire                    frcv_clear_bsy_set_drq; // clear PxTFD.STS.BSY and sets PxTFD.STS.DRQ, update
    
    wire                    frcv_set_bsy;       // set PxTFD.STS.BSY, update
    wire                    frcv_set_sts_7f;    // set PxTFD.STS = 0x7f, update
    wire                    frcv_set_sts_80;    // set PxTFD.STS = 0x80 (may be combined with set_sts_7f), update
    wire                    frcv_decr_dwcr;      // decrement DMA Xfer counter after read // need pulse to 'update_prdbc' to write to registers
    wire                    frcv_decr_dwcw;      // decrement DMA Xfer counter after write // need pulse to 'update_prdbc' to write to registers
    wire                    frcv_clear_xfer_cntr; // Clear pXferCntr to 0
    
    // fsm <-
    wire                    frcv_busy;          // busy processing FIS 
    wire                    frcv_done;          // done processing FIS (see fis_ok, fis_err, fis_ferr)
    wire                    frcv_ok;            // FIS done,  checksum OK reset by starting a new get FIS
    wire                    frcv_err;           // FIS done, checksum ERROR reset by starting a new get FIS
    wire                    frcv_ferr;          // FIS done, fatal error - FIS too long
    wire                    frcv_extra;         // DMA all transferred, but some data is still in left. . Does not deny frcv_ok
    
    wire                    frcv_set_update_sig; // when set, enables get_sig (and resets itself)
///    wire                    frcv_pUpdateSig;     // state variable
///    wire                    frcv_sig_available;  // signature data available
    wire                    frcv_update_sig;        // update signature

    
    // fsm <- state variables that are maintained inside 'ahc_fis_receive'
    wire              [7:0] tfd_sts;       // Current PxTFD status field (updated after regFIS and SDB - certain fields)
                                           // tfd_sts[7] - BSY, tfd_sts[3] - DRQ, tfd_sts[0] - ERR
//    wire              [7:0] tfd_err;       // Current PxTFD error field (updated after regFIS and SDB)
    wire                    fis_i;         // value of "I" field in received regsD2H or SDB FIS
///    wire                    sdb_n;         // value of "N" field in received SDB FIS 
    wire                    dma_a;         // value of "A" field in received DMA Setup FIS 
///    wire                    dma_d;         // value of "D" field in received DMA Setup FIS
    wire                    pio_i;         // value of "I" field in received PIO Setup FIS
    wire                    pio_d;         // value of "D" field in received PIO Setup FIS
///    wire              [7:0] pio_es;        // value of PIO E_Status
    wire                    pPioXfer;
///    wire                    sactive0;      // bit 0 of sActive DWORD received in SDB FIS
    // Using even word count (will be rounded up), partial DWORD (last) will be handled by PRD length if needed
    
    wire             [31:2] xfer_cntr; 
    wire                    xfer_cntr_zero; 
                                             
///    wire             [11:0] data_in_dwords;  // number of DWORDs received in data FIS (can be updated internally). Is it needed?

    // fsm <-> ahc_fis_transmit
    // Command pulses to execute states fsm -> ahc_fis_transmit
    wire                    fsnd_fetch_cmd;    // Enter p:FetchCmd, fetch command header (from the register memory, prefetch command FIS)
                                               // wait for either fetch_cmd_busy == 0 or pCmdToIssue ==1 after fetch_cmd
    wire                    fsnd_cfis_xmit;    // transmit command (wait for dma_ct_busy == 0)
    wire                    fsnd_dx_xmit;      // send FIS header DWORD, (just 0x46), then forward DMA data
                                               // transmit until error, 2048DWords or pDmaXferCnt 
    wire                    fsnd_atapi_xmit;   // tarsmit ATAPI command FIS
    // responses fsm <- ahc_fis_transmit
    wire                    fsnd_done;
///    wire                    fsnd_busy;
    // Short action pulses fsm -> ahc_fis_transmit
    wire                    fsnd_clearCmdToIssue; // From CFIS:SUCCESS 
    // State variables fsm <- ahc_fis_transmit 
    wire                    fsnd_pCmdToIssue; // AHCI port variable
    wire             [ 2:0] fsnd_dx_err;       // bit 0 - syncesc_recv, 1 - R_ERR (was xmit_err)  2 - X-RDY/X_RDY collision (valid @ xmit_err and later, reset by new command)
    wire                    fsnd_ch_c;        // Clear busy upon R_OK for this FIS
    wire                    fsnd_ch_b;        // Built-in self test command
    wire                    fsnd_ch_r;        // reset - may need to send SYNC escape before this command
    wire                    fsnd_ch_p;        // prefetchable - only used with non-zero PRDTL or ATAPI bit set
    wire                    fsnd_ch_w;        // Write: system memory -> device
    wire                    fsnd_ch_a;        // ATAPI: 1 means device should send PIO setup FIS for ATAPI command
///    wire              [4:0] fsnd_ch_cfl;      // length of the command FIS in DW, 0 means none. 0 and 1 - illegal, ... Maybe not needed outside ahc_fis_transmit

    wire             [11:0] data_out_dwords; // number of DWORDs sent in data FIS

    wire                    was_hba_rst; 
    wire                    was_port_rst; 

    // signals between ahci_fsm and ahci_ctrl_stat
///    wire                          update_regs_pending;
    wire                          update_all_regs;
    wire                          update_regs_busy; // valid same cycle as update_all_regs

///    wire                          st01_pending;    // software turned PxCMD.ST from 0 to 1
///    wire                          st10_pending;    // software turned PxCMD.ST from 1 to 0
///    wire                          st_pending_reset;// reset both st01_pending and st10_pending

    
    // these following individual signals may be unneded - use update_all_regs -> update_regs_busy
//    wire                          update_GHC__IS;
//    wire                          update_HBA_PORT__PxIS;
//    wire                          update_HBA_PORT__PxSSTS;
//    wire                          update_HBA_PORT__PxSERR;
//    wire                          update_HBA_PORT__PxCMD;
//    wire                          update_HBA_PORT__PxCI;

// PxCMD
//    wire                          pcmd_clear_icc; // clear PxCMD.ICC field
    wire                          pcmd_esp = 1'b0;       // external SATA port (just forward value)
///    wire                          pcmd_cr;        // command list run - current - read only by software (set by HBA)
    wire                          pcmd_cr_set;    // command list run set
    wire                          pcmd_cr_reset;  // command list run reset
//    wire                          pcmd_fr;        // ahci_fis_receive:get_fis_busy - use frcv_busy

    wire                          pcmd_fre0;      // FIS enable copy to memory
    wire                          pcmd_fre = pcmd_fre0 || 1;     // FIS enable copy to memory
//    wire                          pcmd_clear_bsy_drq; // == ahci_fis_receive:clear_bsy_drq
    wire                          pcmd_clo;       // RW1, causes ahci_fis_receive:clear_bsy_drq, that in turn resets this bit
//    wire                          pcmd_clear_st;  // RW clear ST (start) bit
    wire                          pcmd_st;        // current value
    wire                          pfsm_started;   // H: FSM done, P: FSM started (enable sensing pcmd_st_cleared)
//clear_bsy_drq    
// Interrupt inputs
    wire                          sirq_TFE; // RWC: Task File Error Status
    wire                          sirq_IF;  // RWC: Interface Fatal Error Status (sect. 6.1.2)
    wire                          sirq_INF; // RWC: Interface Non-Fatal Error Status (sect. 6.1.2)
    wire                          sirq_OF;  // RWC: Overflow Status
    wire                          sirq_PRC; // RO:  PhyRdy changed Status
    wire                          sirq_PC;  // RO:  Port Connect Change Status
    wire                          sirq_DP;  // RWC: Descriptor Processed with "I" bit on
    wire                          sirq_UF;  // RO:  Unknown FIS
    wire                          sirq_SDB; // RWC: Set Device Bits Interrupt - Set Device bits FIS with 'I' bit set
    wire                          sirq_DS;  // RWC: DMA Setup FIS Interrupt - DMA Setup FIS received with 'I' bit set
    wire                          sirq_PS;  // RWC: PIO Setup FIS Interrupt - PIO Setup FIS received with 'I' bit set
    wire                          sirq_DHR; // RWC: D2H Register FIS Interrupt - D2H Register FIS received with 'I' bit set
// SCR1:SError (only inputs that are not available in sirq_* ones
                                  //sirq_PC;
                                  //sirq_UF
    wire                          serr_diag_X; // value of PxSERR.DIAG.X

     
    
// SCR0: SStatus
    wire                          ssts_ipm_dnp;      // device not present or communication not established
    wire                          ssts_ipm_active;   // device in active state
    wire                          ssts_ipm_part;     // device in partial state
    wire                          ssts_ipm_slumb;    // device in slumber state
    wire                          ssts_ipm_devsleep; // device in DevSleep state
    
    wire                          ssts_spd_dnp;      // device not present or communication not established
    wire                          ssts_spd_gen1;     // Gen 1 rate negotiated
    wire                          ssts_spd_gen2;     // Gen 2 rate negotiated
    wire                          ssts_spd_gen3;     // Gen 3 rate negotiated
    
    wire                          ssts_det_ndnp;     // no device detected, phy communication not established
    wire                          ssts_det_dnp;      // device detected, but phy communication not established
    wire                          ssts_det_dp;       // device detected, phy communication established
    wire                          ssts_det_offline;  // device detected, phy communication established
    wire                    [3:0] ssts_det;          // current value of PxSSTS.DET
    
 // SCR2:SControl (written by software only)
    wire                    [3:0] sctl_det;          // Device detection initialization requested
    wire                          sctl_det_changed;  // Software had written new value to sctl_det
    wire                          sctl_det_reset;    // clear sctl_det_changed
    
    wire                          pxci0_clear;       // PxCI clear
    wire                          pxci0;             // pxCI current value
    wire                          hba_rst_done;      // HBA reset done - clear GHC.HR (and some other regs)
    
    wire                          comreset_send0; // just disabling it
    
    
    wire                    [9:0] last_jump_addr;
    wire                   [31:0] debug_dma;
    wire                   [31:0] debug_dma1;
    wire                   [31:0] debug_dma_h2d;

    wire                          unsolicited_en;    // enable processing of cominit_got and PxERR.DIAG.W interrupts from
                                                     // this bit is reset at reset, set when PxSSTS.DET==3 or PxSCTL.DET==4

    
    assign comreset_send = comreset_send0 && 0;
    
    // Async FF
    always @ (posedge mrst or posedge mclk) begin
        if (mrst) en_port <= 0;
        else      en_port <= 1;
    end
    
/*
    reg                     [1:0] port_en;           //disable port signals until initialized from the hardware (currently - PLL)
    wire                          ports_rst = ~port_en[1];
    always @ (posedge mclk) begin
        if      (port_arst_any)       port_en[0] <= 0;
        else if (mrst)                port_en[0] <= 1;
        
        if      (port_arst_any)       port_en[1] <= 0;
        else if (!mrst && port_en[0]) port_en[1] <= 1;
        
    end
*/    


    ahci_fsm// #(
//        .READ_REG_LATENCY(2),
//        .ADDRESS_BITS(10)
//    ) 
    ahci_fsm_i (
        .hba_rst                 (mrst),               // input
        .mclk                     (mclk),              // input
        .was_hba_rst              (was_hba_rst),       // input 
        .was_port_rst             (was_port_rst),      // input

        .aclk                     (aclk),              // input
        .arst                     (arst),              // input
        .pgm_ad                   (fsm_pgm_ad),        // input[17:0] 
        .pgm_wa                   (fsm_pgm_wa),        // input
        .pgm_wd                   (fsm_pgm_wd),        // input

        .phy_ready                (phy_ready),         // input
        .syncesc_send             (syncesc_send),      // output
        .comreset_send            (comreset_send0),     // output
        .syncesc_send_done        (syncesc_send_done), // input
        .cominit_got              (cominit_got),       // input
        .set_offline              (set_offline),       // output
//        .x_rdy_collision          (x_rdy_collision),   // input 
        
        .send_R_OK                (send_R_OK),         // output
        .send_R_ERR               (send_R_ERR),        // output
        
///        .update_pending           (update_regs_pending),// input
        .update_all               (update_all_regs),   // output
        .update_busy              (update_regs_busy),  // input
///        .update_gis               (update_GHC__IS),    // output
///        .update_pis               (update_HBA_PORT__PxIS),   // output
///        .update_ssts              (update_HBA_PORT__PxSSTS), // output
///        .update_serr              (update_HBA_PORT__PxSERR), // output
///        .update_pcmd              (update_HBA_PORT__PxCMD),  // output
///        .update_pci               (update_HBA_PORT__PxCI),   // output
///        .st01_pending             (st01_pending),      // input 
///        .st10_pending             (st10_pending),      // input 
///        .st_pending_reset         (st_pending_reset),  // output
//        .pcmd_clear_icc           (pcmd_clear_icc),    // output
//        .pcmd_esp                 (pcmd_esp),          // output
//        .pcmd_cr                  (pcmd_cr),           // input
        .pcmd_cr_set              (pcmd_cr_set),       // output
        .pcmd_cr_reset            (pcmd_cr_reset),     // output
//        .pcmd_fr                  (pcmd_fr),           // output
//        .pcmd_clear_bsy_drq       (pcmd_clear_bsy_drq),// output
        .pcmd_clo                 (pcmd_clo),          // input
//        .pcmd_clear_st            (pcmd_clear_st),     // output
        .pcmd_st                  (pcmd_st),           // input
        .pfsm_started             (pfsm_started),      // output
        .pcmd_st_cleared          (pcmd_st_cleared),   // input 
        .sirq_TFE                 (sirq_TFE),          // output
        .sirq_IF                  (sirq_IF),           // output
        .sirq_INF                 (sirq_INF),          // output
        .sirq_OF                  (sirq_OF),           // output
        .sirq_PRC                 (sirq_PRC),          // output
        .sirq_PC                  (sirq_PC),           // output
        .sirq_DP                  (sirq_DP),           // output
        .sirq_UF                  (sirq_UF),           // output
        .sirq_SDB                 (sirq_SDB),          // output
        .sirq_DS                  (sirq_DS),           // output
        .sirq_PS                  (sirq_PS),           // output
        .sirq_DHR                 (sirq_DHR),          // output
        .serr_diag_X              (serr_diag_X),       // input
        .ssts_ipm_dnp             (ssts_ipm_dnp),      // output
        .ssts_ipm_active          (ssts_ipm_active),   // output
        .ssts_ipm_part            (ssts_ipm_part),     // output
        .ssts_ipm_slumb           (ssts_ipm_slumb),    // output
        .ssts_ipm_devsleep        (ssts_ipm_devsleep), // output
        .ssts_spd_dnp             (ssts_spd_dnp),      // output
        .ssts_spd_gen1            (ssts_spd_gen1),     // output
        .ssts_spd_gen2            (ssts_spd_gen2),     // output
        .ssts_spd_gen3            (ssts_spd_gen3),     // output
        .ssts_det_ndnp            (ssts_det_ndnp),     // output
        .ssts_det_dnp             (ssts_det_dnp),      // output
        .ssts_det_dp              (ssts_det_dp),       // output
        .ssts_det_offline         (ssts_det_offline),  // output
        .ssts_det                 (ssts_det),          // input[3:0]
///        .sctl_ipm                 (sctl_ipm),          // input[3:0] 
///        .sctl_spd                 (sctl_spd),          // input[3:0] 
        .sctl_det                 (sctl_det),          // input[3:0] 
        .sctl_det_changed         (sctl_det_changed),  // input 
        .sctl_det_reset           (sctl_det_reset),    // output
        .hba_rst_done             (hba_rst_done),      // output
        .pxci0_clear              (pxci0_clear),       // output
        .pxci0                    (pxci0),             // input

///        .dma_prd_done             (dma_prd_done),       // input
        .dma_prd_irq_clear        (dma_prd_irq_clear),  // output
        .dma_prd_irq_pend         (dma_prd_irq_pend),   // input
        
        .dma_cmd_busy             (dma_cmd_busy),       // input
///        .dma_cmd_done             (dma_cmd_done),       // input
        .dma_cmd_abort            (dma_cmd_abort_fsm),  // output
        .dma_abort_done           (dma_abort_done),     // input
        .fis_first_invalid        (frcv_first_invalid),// input
        .fis_first_flush          (frcv_first_flush),  // output
        
        .fis_first_vld            (frcv_first_vld),     // input
        .fis_type                 (d2h_data[7:0]),      // input[7:0] FIS type (low byte in the first FIS DWORD), valid with  'fis_first_vld'
        .bist_bits                (d2h_data[23:16]),    // bits that define built-in self test

        .get_dsfis                (frcv_get_dsfis),     // output
        .get_psfis                (frcv_get_psfis),     // output
        .get_rfis                 (frcv_get_rfis),      // output
        .get_sdbfis               (frcv_get_sdbfis),    // output
        .get_ufis                 (frcv_get_ufis),      // output
        .get_data_fis             (frcv_get_data_fis),  // output
        .get_ignore               (frcv_get_ignore),    // output
///     .get_fis_busy             (frcv_busy),          // input
        .get_fis_done             (frcv_done),          // input
        .fis_ok                   (frcv_ok),            // input
        .fis_err                  (frcv_err),           // input
        .fis_ferr                 (frcv_ferr),          // input
        .fis_extra                (frcv_extra || dma_extra_din), // input // more data got from FIS than DMA can accept. Does not deny fis_ok. May have latency
        
        .set_update_sig           (frcv_set_update_sig),// output
///        .pUpdateSig            (frcv_pUpdateSig),    // input
///        .sig_available         (frcv_sig_available), // input
        .update_sig               (frcv_update_sig),    // output
        
        .update_err_sts           (frcv_update_err_sts),// output
        .update_pio               (frcv_update_pio),    // output 
        .update_prdbc             (frcv_update_prdbc),  // output
        .clear_bsy_drq            (frcv_clear_bsy_drq), // output
        .clear_bsy_set_drq        (frcv_clear_bsy_set_drq), //output
        .set_bsy                  (frcv_set_bsy),       // output
        .set_sts_7f               (frcv_set_sts_7f),    // output
        .set_sts_80               (frcv_set_sts_80),    // output
        .clear_xfer_cntr          (frcv_clear_xfer_cntr), //output Clear pXferCntr
        .decr_dwcr                (frcv_decr_dwcr),     // output increment pXferCntr after transmit by data transmitted)
        .decr_dwcw                (frcv_decr_dwcw),     // output increment pXferCntr after transmit by data transmitted)
//      .decr_DXC_dw     (data_out_dwords),    // output[11:2] **** Probably not needed
        .pxcmd_fre                ( pcmd_fre), // input
        .pPioXfer                 (pPioXfer),           // input      
        .tfd_sts                  (tfd_sts),            // input[7:0] 
///        .tfd_err            (tfd_err),            // input[7:0] 
        .fis_i                    (fis_i),              // input
///        .sdb_n           (sdb_n),              // input
        .dma_a                    (dma_a),              // input
///        .dma_d           (dma_d),              // input
        .pio_i                    (pio_i),              // input
        .pio_d                    (pio_d),              // input
///        .sactive0        (sactive0),            // input
///        .pio_es          (pio_es),             // input[7:0] 
///        .xfer_cntr       (xfer_cntr[31:2]),    // input[31:2] 
        .xfer_cntr_zero           (xfer_cntr_zero),     // input
        
        .fetch_cmd                (fsnd_fetch_cmd),     // output
        .cfis_xmit                (fsnd_cfis_xmit),     // output
        .dx_xmit                  (fsnd_dx_xmit),       // output
        .atapi_xmit               (fsnd_atapi_xmit),    // output
        .xmit_done                (fsnd_done),          // input
///        .xmit_busy       (fsnd_busy),          // input
        .clearCmdToIssue          (fsnd_clearCmdToIssue),// output // From CFIS:SUCCESS 
        .pCmdToIssue              (fsnd_pCmdToIssue),   // input
        .dx_err                   (fsnd_dx_err),        // input[2:0] 
///        .ch_prdtl        (prdtl),              // input[15:0] 
        .ch_c                     (fsnd_ch_c),          // input
        .ch_b                     (fsnd_ch_b),          // input
        .ch_r                     (fsnd_ch_r),          // input
        .ch_p                     (fsnd_ch_p),          // input
        .ch_w                     (fsnd_ch_w),          // input
        .ch_a                     (fsnd_ch_a),          // input
///        .ch_cfl          (fsnd_ch_cfl),        // input[4:0] 
///        .dwords_sent     (data_out_dwords)     // input[11:0] ????
        .unsolicited_en           (unsolicited_en),     // input
        .last_jump_addr           (last_jump_addr)
    );

wire debug_data_in_ready;       // output
wire debug_fis_end_w;           // output
wire[1:0] debug_fis_end_r;      // output[1:0] 
wire[1:0] debug_get_fis_busy_r; // output[1:0]
 

localparam DATA_TYPE_DMA =      0;
localparam DATA_TYPE_FIS_HEAD = 1;
localparam DATA_TYPE_OK =       2;
localparam DATA_TYPE_ERR =      3;

reg [12:0] debug_d2h_length;
reg [12:0] debug_d2h_length_prev;
reg        was_good_bad;
reg        was_good_bad_prev;

always @(posedge mclk) if (d2h_ready && d2h_valid) begin
    if      (d2h_type == DATA_TYPE_FIS_HEAD) debug_d2h_length_prev <= debug_d2h_length;

    if      (d2h_type == DATA_TYPE_FIS_HEAD) debug_d2h_length <= 0;
    else if (d2h_type == DATA_TYPE_DMA)      debug_d2h_length <= debug_d2h_length  + 1;

    if      (d2h_type == DATA_TYPE_FIS_HEAD) was_good_bad_prev <= was_good_bad;

    if      ((d2h_type == DATA_TYPE_OK) || (d2h_type == DATA_TYPE_ERR)) was_good_bad <= (d2h_type == DATA_TYPE_OK);
    
end

    axi_ahci_regs #(
        .ADDRESS_BITS          (ADDRESS_BITS),
        .HBA_RESET_BITS        (HBA_RESET_BITS),
        .RESET_TO_FIRST_ACCESS (RESET_TO_FIRST_ACCESS)
    ) axi_ahci_regs_i (
        .aclk             (aclk),            // input
        .arst             (arst),            // input
        .awaddr           (awaddr),          // input[31:0] 
        .awvalid          (awvalid),         // input
        .awready          (awready),         // output
        .awid             (awid),            // input[11:0] 
        .awlen            (awlen),           // input[3:0] 
        .awsize           (awsize),          // input[1:0] 
        .awburst          (awburst),         // input[1:0] 
        .wdata            (wdata),           // input[31:0] 
        .wvalid           (wvalid),          // input
        .wready           (wready),          // output
        .wid              (wid),             // input[11:0] 
        .wlast            (wlast),           // input
        .wstb             (wstb),            // input[3:0] 
        .bvalid           (bvalid),          // output
        .bready           (bready),          // input
        .bid              (bid),             // output[11:0] 
        .bresp            (bresp),           // output[1:0] 
        .araddr           (araddr),          // input[31:0] 
        .arvalid          (arvalid),         // input
        .arready          (arready),         // output
        .arid             (arid),            // input[11:0] 
        .arlen            (arlen),           // input[3:0] 
        .arsize           (arsize),          // input[1:0] 
        .arburst          (arburst),         // input[1:0] 
        .rdata            (rdata),           // output[31:0] 
        .rvalid           (rvalid),          // output
        .rready           (rready),          // input
        .rid              (rid),             // output[11:0] 
        .rlast            (rlast),           // output
        .rresp            (rresp),           // output[1:0] 
        .soft_write_addr  (soft_write_addr), // output[9:0] 
        .soft_write_data  (soft_write_data), // output[31:0] 
        .soft_write_en    (soft_write_en),   // output
        .hba_arst         (hba_arst),        // output // does not include arst
        .port_arst_any    (port_arst_any),   // async set by arst
        .port_arst        (port_arst),       // output // does not include arst
        .hba_clk          (mclk),            // input
        .hba_rst          (mrst),            // input   // deasserted when mclk is stable
        .hba_addr         (regs_addr),       // input[9:0] 
        .hba_we           (regs_we),         // input
        .hba_re           (regs_re),         // input[1:0] 
        .hba_din          (regs_din),        // input[31:0] 
        .hba_dout         (regs_dout),       // output[31:0] 
        .pgm_ad           (fsm_pgm_ad),      // output[17:0] reg 
        .pgm_wa           (fsm_pgm_wa),      // output reg 
        .pgm_wd           (fsm_pgm_wd),      // output reg 
        .afi_wcache       (axi_wr_cache_mode),// output[3:0] reg 
        .afi_rcache       (axi_rd_cache_mode),// output[3:0] reg 
        .afi_cache_set    (set_axi_cache_mode), // output
        .was_hba_rst      (was_hba_rst),     // output 
        .was_port_rst     (was_port_rst),    // output 
        .debug_in0        ({ 2'b0,
                             was_good_bad_prev,
                             debug_d2h_length_prev[12:0],
                             2'b0,
                             was_good_bad,
                             debug_d2h_length[12:0]
                             }),
                             
//        .debug_in1        ({xclk_period[7:0], // lower 8 bits of 12-bit value. Same frequency would be 0x800 (msb opposite to 3 next bits)
//                            debug_dma1[23:0]}),      // debug_in_link),   // input[31:0]
        .debug_in1        ({debug_in_link[15:8],
                            debug_dma1[23:0]}),      // debug_in_link),   // input[31:0]
        .debug_in2        (debug_in_phy),    // input[31:0]     // debug from phy/link
//        .debug_in3        ({22'b0, last_jump_addr[9:0]}) // input[31:0]// Last jump address in the AHDCI sequencer
        .debug_in3        ({debug_in_link[7:0],
                            frcv_busy,frcv_ok, // 2'b0,
`ifdef USE_DATASCOPE
                             datascope_waddr[9:0],
`else
                             10'b0,
`endif                             
                            frcv_err,frcv_ferr, // 2'b0,
                             last_jump_addr[9:0]}) // input[31:0]// Last jump address in the AHDCI sequencer
`ifdef USE_DRP
       ,.drp_en           (drp_en),          // output reg 
        .drp_we           (drp_we),          // output reg 
        .drp_addr         (drp_addr),        // output[14:0] reg 
        .drp_di           (drp_di),          // output[15:0] reg 
        .drp_rdy          (drp_rdy),         // input
        .drp_do           (drp_do)           // input[15:0] 
`endif    
        
        
`ifdef USE_DATASCOPE
        ,.datascope_clk   (datascope_clk),   // input
        .datascope_waddr  (datascope_waddr), // input[9:0] 
        .datascope_we     (datascope_we),    // input
        .datascope_di     (datascope_di),    // input[31:0] 
        
        .datascope1_clk   (datascope1_clk),  // input
        .datascope1_waddr (datascope1_waddr),// input[9:0] 
        .datascope1_we    (datascope1_we),   // input
        .datascope1_di    (datascope1_di)    // input[31:0] 
`endif        
///        .debug_in         (debug_in[31:0])
    );
    ahci_ctrl_stat #(
        .ADDRESS_BITS            (ADDRESS_BITS)
    ) ahci_ctrl_stat_i (
        .mrst                    (mrst),                    // input
        .mclk                    (mclk),                    // input
        .was_hba_rst             (was_hba_rst),             // input
        .was_port_rst            (was_port_rst),            // input
        .soft_write_addr         (soft_write_addr),         // input[9:0] 
        .soft_write_data         (soft_write_data),         // input[31:0] 
        .soft_write_en           (soft_write_en),           // input
        .regs_addr               (regs_saddr),              // output[9:0] reg 
        .regs_we                 (regs_we_acs),             // output reg 
        .regs_din                (regs_din_from_acs),       // output[31:0] reg 
        .update_pending          (), /// update_regs_pending),     // output
        .update_all              (update_all_regs),         // input
        .update_busy             (update_regs_busy),        // output
///        .st01_pending            (st01_pending),            // output reg 
///        .st10_pending            (st10_pending),            // output reg 
///        .st_pending_reset        (st_pending_reset),        // input
        
        .update_gis              (1'b0), // update_GHC__IS),          // input
        .update_pis              (1'b0), // update_HBA_PORT__PxIS),   // input
        .update_ssts             (1'b0), // update_HBA_PORT__PxSSTS), // input
        .update_serr             (1'b0), // update_HBA_PORT__PxSERR), // input
        .update_pcmd             (1'b0), // update_HBA_PORT__PxCMD),  // input
        .update_pci              (1'b0), // update_HBA_PORT__PxCI),   // input
        .update_ghc              (1'b0), // update _GHC_GHC,          // input
        
//        .pcmd_clear_icc          (1'b0), // pcmd_clear_icc),          // input
        .pcmd_esp                (pcmd_esp),                // input
        .pcmd_cr                 (), //pcmd_cr),                 // output
        .pcmd_cr_set             (pcmd_cr_set),             // input
        .pcmd_cr_reset           (pcmd_cr_reset),           // input
        .pcmd_fr                 (frcv_busy),               // input
        .pcmd_fre                (pcmd_fre0),               // output
        .pcmd_clear_bsy_drq      (frcv_clear_bsy_drq),      // input
        .pcmd_clo                (pcmd_clo),                // output
        .pcmd_clear_st           (1'b0), // pcmd_clear_st),           // input
        .pcmd_st                 (pcmd_st),                 // output
        .pfsm_started            (pfsm_started),            // input
        .pcmd_st_cleared         (pcmd_st_cleared),         // output reg 
        .sirq_TFE                (sirq_TFE),                // input
        .sirq_IF                 (sirq_IF),                 // input
        .sirq_INF                (sirq_INF),                // input
        .sirq_OF                 (sirq_OF),                 // input
        .sirq_PRC                (sirq_PRC),                // input
        .sirq_PC                 (sirq_PC),                 // input
        .sirq_DP                 (sirq_DP),                 // input
        .sirq_UF                 (sirq_UF),                 // input
        .sirq_SDB                (sirq_SDB),                // input
        .sirq_DS                 (sirq_DS),                 // input
        .sirq_PS                 (sirq_PS),                 // input
        .sirq_DHR                (sirq_DHR),                // input
        .serr_DT                 (serr_DT),                 // input
        .serr_DS                 (serr_DS),                 // input
        .serr_DH                 (serr_DH),                 // input
        .serr_DC                 (serr_DC),                 // input
        .serr_DB                 (serr_DB),                 // input
        .serr_DW                 (serr_DW),                 // input
        .serr_DI                 (serr_DI),                 // input
        .serr_EE                 (serr_EE),                 // input
        .serr_EP                 (serr_EP),                 // input
        .serr_EC                 (serr_EC),                 // input
        .serr_ET                 (serr_ET),                 // input
        .serr_EM                 (serr_EM),                 // input
        .serr_EI                 (serr_EI),                 // input
        .serr_diag_X             (serr_diag_X),             // output
        .ssts_ipm_dnp            (ssts_ipm_dnp),            // input
        .ssts_ipm_active         (ssts_ipm_active),         // input
        .ssts_ipm_part           (ssts_ipm_part),           // input
        .ssts_ipm_slumb          (ssts_ipm_slumb),          // input
        .ssts_ipm_devsleep       (ssts_ipm_devsleep),       // input
        .ssts_spd_dnp            (ssts_spd_dnp),            // input
        .ssts_spd_gen1           (ssts_spd_gen1),           // input
        .ssts_spd_gen2           (ssts_spd_gen2),           // input
        .ssts_spd_gen3           (ssts_spd_gen3),           // input
        .ssts_det_ndnp           (ssts_det_ndnp),           // input
        .ssts_det_dnp            (ssts_det_dnp),            // input
        .ssts_det_dp             (ssts_det_dp),             // input
        .ssts_det_offline        (ssts_det_offline),        // input
        .ssts_det                (ssts_det),                // output[3:0]
        .sctl_ipm                (sctl_ipm),                // output[3:0] reg 
        .sctl_spd                (sctl_spd),                // output[3:0] reg 
        .sctl_det                (sctl_det),                // output[3:0] reg
        .sctl_det_changed        (sctl_det_changed),        // output reg 
        .sctl_det_reset          (sctl_det_reset),          // input
        .pxci0_clear             (pxci0_clear),             // input
        .pxci0                   (pxci0),                   // output
        .hba_reset_done          (hba_rst_done),            // input
        .unsolicited_en          (unsolicited_en),          // output
        .irq                     (irq)                      // output reg 
    );

    ahci_dma ahci_dma_i (
        .mrst                  (mrst),          // input
        .hrst                  (hrst),          // input
        .mclk                  (mclk),          // input
        .hclk                  (hclk),          // input
//        .ctba                  (regs_dout[31:7]),// input[31:7] 
        .ctba                  (regs_dout[31:4]),// input[31:4] 
        .ctba_ld               (ctba_ld),       // input
        .prdtl                 (prdtl),         // input[15:0] 
        .dev_wr                (dev_wr),        // input
        .cmd_start             (dma_cmd_start), // input
        .prd_start             (dma_prd_start), // input
        .cmd_abort             (dma_cmd_abort_xmit || dma_cmd_abort_fsm), // input
        .axi_wr_cache_mode     (axi_wr_cache_mode), // input[3:0] 
        .axi_rd_cache_mode     (axi_rd_cache_mode), // input[3:0] 
        .set_axi_wr_cache_mode (set_axi_cache_mode), // input
        .set_axi_rd_cache_mode (set_axi_cache_mode), // input
        .ct_busy               (dma_ct_busy),   // output reg 
        .ct_addr               (dma_ct_addr),   // input[4:0] 
        .ct_re                 (dma_ct_re),     // input[1:0]
        .ct_data               (dma_ct_data),   // output[31:0] reg 
        .prd_done              (), /// dma_prd_done),  // output
        
        .prd_irq_clear         (dma_prd_irq_clear),// input
        .prd_irq_pend          (dma_prd_irq_pend), // output reg
        
        .cmd_busy              (dma_cmd_busy), // dma_cmd_busy),  // output reg Some data to transmit!
        .cmd_done              (dma_cmd_done),  // output
        .abort_busy            (dma_abort_busy),
        .abort_done            (dma_abort_done),
        .axi_mismatch          (axi_mismatch),  // handled, but may report as an error - axi counters are 0, but calculated ones are not
        .sys_out               (dma_dout),      // output[31:0] 
        .sys_dav               (dma_dav),       // output
        .sys_re                (dma_re),        // input
        .last_h2d_data         (last_h2d_data), // output
        .sys_in                (d2h_data),      // input[31:0] 
        .sys_nfull             (dma_in_ready),  // output
        .sys_we                (dma_we),        // input
        .extra_din             (dma_extra_din), // output reg
        .afi_awaddr        (afi_awaddr),        // output[31:0] 
        .afi_awvalid       (afi_awvalid),       // output
        .afi_awready       (afi_awready),       // input
        .afi_awid          (afi_awid),          // output[5:0] 
        .afi_awlock        (afi_awlock),        // output[1:0] 
        .afi_awcache       (afi_awcache),       // output[3:0] reg 
        .afi_awprot        (afi_awprot),        // output[2:0] 
        .afi_awlen         (afi_awlen),         // output[3:0] 
        .afi_awsize        (afi_awsize),        // output[1:0] 
        .afi_awburst       (afi_awburst),       // output[1:0] 
        .afi_awqos         (afi_awqos),         // output[3:0] 
        .afi_wdata         (afi_wdata),         // output[63:0] 
        .afi_wvalid        (afi_wvalid),        // output
        .afi_wready        (afi_wready),        // input
        .afi_wid           (afi_wid),           // output[5:0] 
        .afi_wlast         (afi_wlast),         // output
        .afi_wstrb         (afi_wstrb),         // output[7:0] 
        .afi_bvalid        (afi_bvalid),        // input
        .afi_bready        (afi_bready),        // output
        .afi_bid           (afi_bid),           // input[5:0] 
        .afi_bresp         (afi_bresp),         // input[1:0] 
        .afi_wcount        (afi_wcount),        // input[7:0] 
        .afi_wacount       (afi_wacount),       // input[5:0] 
        .afi_wrissuecap1en (afi_wrissuecap1en), // output
        .afi_araddr        (afi_araddr),        // output[31:0] 
        .afi_arvalid       (afi_arvalid),       // output
        .afi_arready       (afi_arready),       // input
        .afi_arid          (afi_arid),          // output[5:0] 
        .afi_arlock        (afi_arlock),        // output[1:0] 
        .afi_arcache       (afi_arcache),       // output[3:0] reg 
        .afi_arprot        (afi_arprot),        // output[2:0] 
        .afi_arlen         (afi_arlen),         // output[3:0] 
        .afi_arsize        (afi_arsize),        // output[1:0] 
        .afi_arburst       (afi_arburst),       // output[1:0] 
        .afi_arqos         (afi_arqos),         // output[3:0] 
        .afi_rdata         (afi_rdata),         // input[63:0] 
        .afi_rvalid        (afi_rvalid),        // input
        .afi_rready        (afi_rready),        // output
        .afi_rid           (afi_rid),           // input[5:0] 
        .afi_rlast         (afi_rlast),         // input
        .afi_rresp         (afi_rresp),         // input[1:0] 
        .afi_rcount        (afi_rcount),        // input[7:0] 
        .afi_racount       (afi_racount),       // input[2:0] 
        .afi_rdissuecap1en (afi_rdissuecap1en), // output
        .debug_out         (debug_dma),          // output[31:0]
        .debug_out1        (debug_dma1)          // output[31:0]
        ,.debug_dma_h2d    (debug_dma_h2d)
    );

    ahci_fis_receive #(
        .ADDRESS_BITS      (ADDRESS_BITS)
    ) ahci_fis_receive_i (
        .hba_rst           (mrst),                   // input
        .mclk              (mclk),                   // input
        .pcmd_st_cleared   (pcmd_st_cleared),        // input
        .fis_first_vld     (frcv_first_vld),         // output reg 
        .fis_first_invalid (frcv_first_invalid),     // output
        .fis_first_flush   (frcv_first_flush),       // input

        .get_dsfis         (frcv_get_dsfis),         // input
        .get_psfis         (frcv_get_psfis),         // input
        .get_rfis          (frcv_get_rfis),          // input
        .get_sdbfis        (frcv_get_sdbfis),        // input
        .get_ufis          (frcv_get_ufis),          // input
        .get_data_fis      (frcv_get_data_fis),      // input
        .get_ignore        (frcv_get_ignore),        // input
        
        .get_fis_busy      (frcv_busy),              // output reg 
        .get_fis_done      (frcv_done),              // output reg 
        .fis_ok            (frcv_ok),                // output reg 
        .fis_err           (frcv_err),               // output reg 
        .fis_ferr          (frcv_ferr),              // output

        .dma_prds_done     (dma_cmd_done),           // input
        .fis_extra         (frcv_extra),             // output

        .set_update_sig    (frcv_set_update_sig),    // input
        .pUpdateSig        (), /// frcv_pUpdateSig),        // output
        .sig_available     (), ///frcv_sig_available),     // output reg 
        .update_sig        (frcv_update_sig),        // input
        
        .update_err_sts    (frcv_update_err_sts),    // input
        .update_pio        (frcv_update_pio),        // input  update PxTFD.STS and PxTFD.ERR from pio_* (entry PIO:Update)
        
        .update_prdbc      (frcv_update_prdbc),      // input
        .clear_prdbc       (fsnd_fetch_cmd),         // input save resources - clear prdbc for every commnad
        
        .clear_bsy_drq     (frcv_clear_bsy_drq),     // input
        .clear_bsy_set_drq (frcv_clear_bsy_set_drq), // input
        
        .set_bsy           (frcv_set_bsy),           // input
        .set_sts_7f        (frcv_set_sts_7f),        // input
        .set_sts_80        (frcv_set_sts_80),        // input
        .clear_xfer_cntr (frcv_clear_xfer_cntr),     // input Clear pXferCntr
        .decr_dwcr         (frcv_decr_dwcr),         // input
        .decr_dwcw         (frcv_decr_dwcw),         // input
        .decr_DXC_dw       (data_out_dwords),        // input[11:2]
        .pcmd_fre          (pcmd_fre),               // input
         
        .pPioXfer          (pPioXfer),               // output reg 
        
        .tfd_sts           (tfd_sts),                // output[7:0] 
        .tfd_err           (), /// tfd_err),                // output[7:0] 
        .fis_i             (fis_i),                  // output reg 
        .sdb_n             (), /// sdb_n),                  // output reg 
        .dma_a             (dma_a),                  // output reg 
        .dma_d             (), /// dma_d),                  // output reg 
        .pio_i             (pio_i),                  // output reg 
        .pio_d             (pio_d),                  // output reg 
        .pio_es            (), /// pio_es),                 // output[7:0] reg 
        .sactive0          (), /// sactive0),               // output reg 
        .xfer_cntr         (xfer_cntr[31:2]),        // output[31:2] 
        .xfer_cntr_zero    (xfer_cntr_zero),         // output reg
        .data_in_dwords    (), /// data_in_dwords),         // output[11:0] 
         
        .reg_addr          (regs_waddr),             // output[9:0] reg 
        .reg_we            (regs_we_freceive),       // output reg 
        .reg_data          (regs_din_from_freceive), // output[31:0] reg 
        .hba_data_in       (d2h_data),               // input[31:0] 
        .hba_data_in_type  (d2h_type),               // input[1:0] 
        .hba_data_in_valid (d2h_valid),              // input
        .hba_data_in_many  (d2h_many),               // input
        .hba_data_in_ready (d2h_ready),              // output
        .dma_in_ready      (dma_in_ready),           // input
        .dma_in_valid      (dma_we)                  // output
        
        ,.debug_data_in_ready (debug_data_in_ready), // output
        .debug_fis_end_w      (debug_fis_end_w),     // output
        .debug_fis_end_r      (debug_fis_end_r),     // output[1:0] 
        .debug_get_fis_busy_r (debug_get_fis_busy_r) // output[1:0] 
    );
wire ahci_fis_transmit_busy;
wire [9:0] xmit_dbg_01;
    ahci_fis_transmit #(
        .PREFETCH_ALWAYS  (PREFETCH_ALWAYS),
        .READ_REG_LATENCY (READ_REG_LATENCY),
        .READ_CT_LATENCY  (READ_CT_LATENCY),
        .ADDRESS_BITS     (ADDRESS_BITS)
    ) ahci_fis_transmit_i (
//        .hba_rst           (mrst),                 // input TODO: Reset when !PxCMD.ST? pcmd_st
        .hba_rst           (mrst || !pcmd_st),     // input TODO: Reset when !PxCMD.ST? pcmd_st
        .mclk              (mclk),                 // input
        .pcmd_st_cleared   (pcmd_st_cleared),      // input
        .fetch_cmd         (fsnd_fetch_cmd),       // input
        .cfis_xmit         (fsnd_cfis_xmit),       // input
        .dx_xmit           (fsnd_dx_xmit),         // input
        .atapi_xmit        (fsnd_atapi_xmit),      // input
        
        .done              (fsnd_done),            // output reg 
        .busy              (ahci_fis_transmit_busy), /// fsnd_busy),            // output reg 
        .clearCmdToIssue   (fsnd_clearCmdToIssue), // input
        .pCmdToIssue       (fsnd_pCmdToIssue),     // output
        .xmit_ok           (xmit_ok),              // input
        .xmit_err          (xmit_err),             // input
        .syncesc_recv      (syncesc_recv),         // input
        .xrdy_collision    (x_rdy_collision),      // input
        .dx_err            (fsnd_dx_err),          // output[1:0] 
        .ch_prdtl          (prdtl),                // output[15:0]
        .ch_c              (fsnd_ch_c),            // output
        .ch_b              (fsnd_ch_b),            // output
        .ch_r              (fsnd_ch_r),            // output
        .ch_p              (fsnd_ch_p),            // output
        .ch_w              (fsnd_ch_w),            // output
        .ch_a              (fsnd_ch_a),            // output
        .ch_cfl            (), /// fsnd_ch_cfl),          // output[4:0] 

        .dwords_sent       (data_out_dwords),      // output[11:0] reg
        .reg_addr          (regs_raddr),           // output[9:0] reg 
        .reg_re            (regs_re_ftransmit),    // output[1:0]
        .reg_rdata         (regs_dout),            // input[31:0] 
        .xfer_cntr         (xfer_cntr[31:2]),      // input[31:2] 
        .xfer_cntr_zero    (xfer_cntr_zero),       // input 
        .dma_ctba_ld       (ctba_ld),              // output
        .dma_start         (dma_cmd_start),        // output
        .dma_dev_wr        (dev_wr),               // output
        .dma_ct_busy       (dma_ct_busy),          // input
        .dma_prd_start     (dma_prd_start),        // output reg 
        .dma_cmd_abort     (dma_cmd_abort_xmit),   // output reg
        .ct_addr           (dma_ct_addr),          // output[4:0] reg 
        .ct_re             (dma_ct_re),            // output[1:0]
        .ct_data           (dma_ct_data),          // input[31:0] 
        .dma_out           (dma_dout),             // input[31:0] 
        .dma_dav           (dma_dav),              // input
        .dma_re            (dma_re),               // output
        .last_h2d_data     (last_h2d_data),        // input
        .todev_data        (h2d_data),             // output[31:0] reg 
        .todev_type        (h2d_type),             // output[1:0] reg 
        .todev_valid       (h2d_valid),            // output
        .todev_ready       (h2d_ready)             // input
       ,.debug_01(xmit_dbg_01)
    );

// Datascope code
//`define DATASCOPE_V2
// Datascope interface (write to memory that can be software-read)
`define DATASCOPE_FIS_DATA 1

`ifdef USE_DATASCOPE

    `ifdef DATASCOPE_V2
        reg    [ADDRESS_BITS-1:0] datascope_waddr_r;
        reg                 [1:0] datascope_run;
    //    reg                 [1:0] datascope_run;
        
        assign datascope_we = datascope_run[1];
        assign datascope_clk = mclk;
        assign datascope_waddr = datascope_waddr_r;
        
         assign datascope_di = {
                                 debug_dma_h2d[3], // done_flush_mclk,
                                 debug_dma_h2d[2], // dout_vld,
                                 debug_dma_h2d[1], // dout_re,
                                 debug_dma_h2d[0], // last_DW,
                                 
                                 dma_dout[27:16],
                                 debug_dma_h2d[19:18], // 2'b0
                                 debug_dma_h2d[17],    // fifo_rd
                                 debug_dma_h2d[16:12], // raddr[4:0]
                                 debug_dma_h2d[11:8],  //fifo_do_vld[3:0]
    
                                 debug_dma_h2d[7],    // fifo_dav
                                 debug_dma_h2d[6],    // fifo_dav2_w
                                 debug_dma_h2d[5],    // fifo_dav2
                                 debug_dma_h2d[4]     // flushing_mclk
         };
         
     //    dma_dout[
        
        always @ (posedge mclk) begin
            if      (mrst)                  datascope_run[0] <= 0;
            else if (dma_cmd_start)         datascope_run[0] <= 1;
            else if (dma_cmd_done)          datascope_run[0] <= 0;
            
            if (mrst || !datascope_run[0])  datascope_run[1] <= 0;
            else if (dma_dav)               datascope_run[1] <= 1;
    
            if    (fsnd_cfis_xmit)          datascope_waddr_r <= 0;
            else if (datascope_we)          datascope_waddr_r <= datascope_waddr_r + 1;
    
            
        end
    //`endif // DATASCOPE_V2
    `else
    //`ifdef DATASCOPE_V1
        `ifdef DATASCOPE_FIS_DATA
            datascope_timing #(
                .ADDRESS_BITS(10),
                .FIS_LEN(5)
            ) datascope_timing_i (
                .clk             (mclk), // input
                .rst             (mrst), // input
                .soft_write_addr (soft_write_addr), // input[9:0] 
                .soft_write_data (soft_write_data), // input[31:0] 
                .soft_write_en   (soft_write_en), // input
                .h2d_data        (h2d_data), // input[31:0] 
                .h2d_type        (h2d_type), // input[1:0] 
                .h2d_valid       (h2d_valid), // input
                .h2d_ready       (h2d_ready), // input
                .d2h_data        (d2h_data), // input[31:0] 
                .d2h_type        (d2h_type), // input[1:0] 
                .d2h_valid       (d2h_valid), // input
                .d2h_ready       (d2h_ready), // input
                .datascope_clk   (datascope_clk), // output
                .datascope_waddr (datascope_waddr), // output[9:0] reg 
                .datascope_we    (datascope_we), // output
                .datascope_di    (datascope_di) // output[31:0] reg 
            );
        
        `else  // DATASCOPE_FIS_DATA  
            localparam DATASCOPE_CFIS_START=0;
            localparam DATASCOPE_INCOMING_POST=32;
            
            reg    [ADDRESS_BITS-1:0] datascope_waddr_r=0;
            reg                 [1:0] datascope_run;
            
            reg                       datascope_link_run;
            wire                      datascope_is_state_send_ready = (debug_in_link[4:0] == 16);
            wire                      datascope_is_state_idle =       (debug_in_link[4:0] == 22);
            reg                       datascope_was_state_send_ready;
            reg                 [3:0] datascope_id;
            
            wire                      datascope_incoming_start = debug_in_link[22];  // set_rcvr_wait; // start logging
            wire                      datascope_incoming_started = debug_in_phy[21:20] == 1;  // 
            wire                      datascope_incomining_preend = debug_in_phy[21]; // d2h_type_in[1
            reg                 [2:0] datascope_incoming_run;
            reg                 [7:0] datascope_incoming_cntr;
            reg                       datascope_receive_fis;
            reg                 [9:0] datascope_last_jump_addr=0;
            reg                 [1:0] datascope_new_jump = 0;
            reg                [15:0] datascope_jump_cntr = 0;
             
        //last_jump_addr[9:0]    
            always @(posedge mclk) begin
                if (mrst)  datascope_new_jump[0] <=  0;
                else       datascope_new_jump[0] <=   datascope_last_jump_addr != last_jump_addr;
                
                if (mrst)  datascope_new_jump[1] <=  0;
                else       datascope_new_jump[1] <=  datascope_new_jump[0];
                
                if (mrst)               datascope_last_jump_addr <=  0;
                if (datascope_new_jump) datascope_last_jump_addr <= last_jump_addr;
                
                if (datascope_we) datascope_jump_cntr <= datascope_jump_cntr+1;
                
                
            
                if      (mrst)                                                           datascope_receive_fis <= 0;
                else if (datascope_incoming_start)                                       datascope_receive_fis <= 1;
                else if (frcv_get_dsfis ||
                         frcv_get_psfis ||
                         frcv_get_rfis || 
                         frcv_get_sdbfis ||
                         frcv_get_ufis ||
                         frcv_get_data_fis ||
                         frcv_get_ignore)                                                datascope_receive_fis <= 0;
        
                if      (mrst)                                                           datascope_incoming_run[0] <= 0;
                else if (datascope_incoming_start || datascope_receive_fis)              datascope_incoming_run[0] <= 1;
                else if (datascope_incoming_cntr == 0)                                   datascope_incoming_run[0] <= 0;
        
                if      (mrst || datascope_incoming_start)                               datascope_incoming_run[1] <= 0;
                else if (datascope_incoming_run[0] && datascope_incoming_started)        datascope_incoming_run[1] <= 1;
                else if (datascope_incoming_run[2])                                      datascope_incoming_run[1] <= 0;
                
                if      (mrst || datascope_incoming_start)                               datascope_incoming_run[2] <= 0;
                else if (datascope_incoming_run[1] && datascope_incomining_preend)       datascope_incoming_run[2] <= 1;
                else if (datascope_incoming_cntr == 0)                                   datascope_incoming_run[2] <= 0;
                
                if      (mrst || !datascope_incoming_run[2] ||
                                  datascope_incoming_start ||
                                  datascope_receive_fis)                                 datascope_incoming_cntr <= DATASCOPE_INCOMING_POST;
                else if (|datascope_incoming_cntr)                                       datascope_incoming_cntr <=  datascope_incoming_cntr - 1;
                
            end    
            
            assign datascope_clk = mclk;
            assign datascope_waddr = last_jump_addr;
            assign datascope_we = &datascope_new_jump;
            assign datascope_di = {2'h3, fsnd_pCmdToIssue,  xfer_cntr_zero, 2'b0, last_jump_addr[9:0],datascope_jump_cntr};  
          always @(posedge mclk) begin
                if      (mrst)                                      datascope_run[0] <= 0;
                else if (fsnd_cfis_xmit)                            datascope_run[0] <= 1;
                else if (h2d_valid && h2d_ready && (h2d_type == 2)) datascope_run[0] <= 0;
        
                if      (mrst)                                                             datascope_link_run <= 0;
                else if (datascope_is_state_send_ready && !datascope_was_state_send_ready) datascope_link_run <= 1; // state_send_sof
                else if (datascope_is_state_idle)                                          datascope_link_run <= 0; // state_idle
                
                datascope_was_state_send_ready <= datascope_is_state_send_ready;
        
                
                datascope_run[1] <= datascope_run[0];
                
                
                
                if      (mrst)           datascope_id <= 0;
                else if (fsnd_cfis_xmit) datascope_id <=  datascope_id + 1;
                
            end
        `endif // DATASCOPE_FIS_DATA
    
    `endif // DATASCOPE_V1   
    
`endif  // USE_DATASCOPE   
endmodule

module datascope_timing #(
        parameter ADDRESS_BITS = 10, // for datascope
        parameter FIS_LEN = 5        // Record this number of DWORDS in each FIS
    )(
    input                     clk,
    input                     rst,
    // receiving time punch command and 3-bit tag
    input  [ADDRESS_BITS-1:0] soft_write_addr, 
    input              [31:0] soft_write_data, 
    input                     soft_write_en,
    
    // outgoing FISes
    input              [31:0] h2d_data,     // 32-bit data from the system memory to HBA (dma data)
    input              [ 1:0] h2d_type,     // 0 - data, 1 - FIS head, 2 - FIS END (make FIS_Last?)
    input                     h2d_valid,    // output register full
    input                     h2d_ready,    // send FIFO has room for data (>= 8? dwords)
    
    // Incoming FISes
 
// Data/type FIFO, device -> host
    input              [31:0] d2h_data,    // FIFO output data
    input              [ 1:0] d2h_type,    // 0 - data, 1 - FIS head, 2 - R_OK, 3 - R_ERR
    input                     d2h_valid,   // Data available from the transport layer in FIFO                
    input                     d2h_ready,   // This module or DMA consumes DWORD
    
    output                        datascope_clk,
    output reg [ADDRESS_BITS-1:0] datascope_waddr,
    output                        datascope_we,
    output reg             [31:0] datascope_di
    );

`include "includes/ahci_localparams.vh" // @SuppressThisWarning VEditor : Unused localparams
    reg                 [2:0] punch_tag;
    wire                      write_tag_w = soft_write_en && (soft_write_addr[ADDRESS_BITS-1:0] == HBA_PORT__PunchTime__TAG__ADDR);
    reg                       pend_punch_time;
    wire                      write_punch_time = pend_punch_time && !fis_start && !fis_run && !fis_run_d;
    reg                       fis_run;
    reg                       fis_run_d;
    reg                       fis_we; // recording FIS data (until end or max len)
    reg                [12:0] fis_len;
    reg                [12:0] fis_left;
    reg                [31:0] fis_data;
    reg                [27:0] cur_time;
    reg                       was_h2d_last;
    
    wire                      fis_start = (h2d_valid && h2d_ready && (h2d_type == 1)) ||
                                          (d2h_valid && d2h_ready && (d2h_type == 1));
    wire                      fis_end =   (d2h_valid? (d2h_valid && d2h_ready && d2h_type[1]): was_h2d_last);
//    wire                      fis_end_we = (fis_left == 0) || fis_end;

    wire                      pre_we_w =  fis_run && (d2h_valid?(d2h_valid && d2h_ready):(h2d_valid && h2d_ready));
    reg                       pre_we_r;                                      
    reg                       we_r;                                      
    assign datascope_we =  we_r;
    assign datascope_clk = clk;
    
    always @ (posedge clk) begin
        was_h2d_last <= h2d_type[1] && h2d_valid && h2d_ready;
    
        if (rst) cur_time <= 0;
        else     cur_time <= cur_time + 1;
    
        if (write_tag_w) punch_tag <= soft_write_data[2:0];
        
        if      (rst)                 pend_punch_time <= 0;
        else if (write_tag_w)         pend_punch_time <= 1;
        else if (write_punch_time)    pend_punch_time <= 0;
        
        if (write_punch_time || fis_start) datascope_di <= {write_punch_time?{1'b1,punch_tag}:{3'b0,d2h_valid},cur_time};
        else if (fis_we)                   datascope_di <= fis_data;
        else if (!fis_run && fis_run_d)    datascope_di <= {19'h7fff8, fis_len};
        
        pre_we_r <= pre_we_w || fis_start ;
    
        we_r <= write_punch_time || fis_start || (fis_we ? pre_we_r : (!fis_run && fis_run_d));
        
        if     (fis_start) fis_left <= FIS_LEN - 1;
        else if (pre_we_w) fis_left <= fis_left - 1;
        
        if      (rst)                        fis_we <= 0;
        else if (fis_start)                  fis_we <= 1;
        else if ((fis_left == 0) || fis_end) fis_we <= 0;
        
        if      (rst)        fis_run <= 0;
        else if (fis_start)  fis_run <= 1;
        else if (fis_end)    fis_run <= 0;

        fis_run_d <= fis_run;
        
        if     (fis_start) fis_len <= d2h_valid? 0 : 1;
        else if (pre_we_w) fis_len <= fis_len + 1;
        
        if (fis_start || pre_we_w) fis_data <= d2h_valid ? d2h_data : h2d_data;
        
        if      (rst)  datascope_waddr <= 0;
        else if (we_r) datascope_waddr <= datascope_waddr + 1;
        
    end
    
endmodule

