/*!
 * <b>Module:</b>ahci_fsm
 * @file ahci_fsm.v
 * @date 2016-01-10  
 * @author Andrey Filippov     
 *
 * @brief AHCI host+port0 state machine
 *
 * @copyright Copyright (c) 2016 Elphel, Inc .
 *
 * <b>License:</b>
 *
 * ahci_fsm.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_fsm.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_fsm 
/*#(
//    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 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)
) */
(
    input                         hba_rst, // @posedge mclk
    input                         mclk, // for command/status
    input                         was_hba_rst,    // last reset was hba reset (not counting system reset)
    input                         was_port_rst,   // last reset was port reset
    
    // Writing FSM program memory
    input                         aclk,
    input                         arst,
    input                  [17:0] pgm_ad, // @aclk, address/data to program the AHCI FSM
    input                         pgm_wa,  // @aclk, address strobe to program the AHCI FSM
    input                         pgm_wd,  // @aclk, data strobe to program the AHCI FSM

    
    // direct communication with transposrt, link and phy layers
    input                   [1:0] phy_ready,     // goes up after comreset,cominit, align, ..., showing speed 
    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,   // asynchronously jumps to P:Cominit state
    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,
    
    // Other signals....
    
    // Communication with ahci_ctrl_stat (some are not needed)
    // update register inputs (will write to register memory current value of the corresponding register)
    output                        pfsm_started, // H: FSM done, P: FSM started (enable sensing pcmd_st_cleared)
    // update register inputs (will write to register memory current value of the corresponding register)
    // Removing - such updates are always done when startimng new state
///    input                         update_pending,
    output                        update_all,  // =fsm_jump[0]
    input                         update_busy, // valid same cycle as update_all
//    output                        update_gis,  // these following individual may be unneeded - just use universal update_all
//    output                        update_pis,
//    output                        update_ssts,
//    output                        update_serr,
//    output                        update_pcmd,
//    output                        update_pci,
///    input                         st01_pending,    // software turned PxCMD.ST from 0 to 1 - detected in the loop
///    input                         st10_pending,    // software turned PxCMD.ST from 1 to 0 - generates port reset
///    output                        st_pending_reset,// reset both st01_pending and st10_pending

// PxCMD
///   output                        pcmd_clear_icc, // clear PxCMD.ICC field
///   output                        pcmd_esp,       // external SATA port (just forward value)
///   input                         pcmd_cr,        // command list run - current
    output                        pcmd_cr_set,    // command list run set
    output                        pcmd_cr_reset,  // command list run reset
//    output                        pcmd_fr,        // ahci_fis_receive:get_fis_busy
///    output                        pcmd_clear_bsy_drq, // == ahci_fis_receive:clear_bsy_drq

// Command List override, not yet implemented (optional), keeping @SuppressWarnings VEditor
    input                         pcmd_clo,       //RW1, causes ahci_fis_receive:clear_bsy_drq, that in turn resets this bit
///    output                        pcmd_clear_st,  // RW clear ST (start) bit Seems it is software controlled only
    input                         pcmd_st,        // current value
    input                         pcmd_st_cleared,// ST bit cleared by software;
    
//clear_bsy_drq    
// Interrupt inputs
    output                        sirq_TFE, // RWC: Task File Error Status
    output                        sirq_IF,  // RWC: Interface Fatal Error Status (sect. 6.1.2)
    output                        sirq_INF, // RWC: Interface Non-Fatal Error Status (sect. 6.1.2)
    output                        sirq_OF,  // RWC: Overflow Status
    output                        sirq_PRC, // RO:  PhyRdy changed Status
    output                        sirq_PC,  // RO:  Port Connect Change Status
    output                        sirq_DP,  // RWC: Descriptor Processed with "I" bit on
    output                        sirq_UF,  // RO:  Unknown FIS
    output                        sirq_SDB, // RWC: Set Device Bits Interrupt - Set Device bits FIS with 'I' bit set
    output                        sirq_DS,  // RWC: DMA Setup FIS Interrupt - DMA Setup FIS received with 'I' bit set
    output                        sirq_PS,  // RWC: PIO Setup FIS Interrupt - PIO Setup FIS received with 'I' bit set
    output                        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
// 5.3.2.3  P:NotRunning.8 - can not be implemented now, keeping @SuppressWarnings VEditor
    input                         serr_diag_X, // value of PxSERR.DIAG.X

     
    
// SCR0: SStatus
    output                        ssts_ipm_dnp,      // device not present or communication not established
    output                        ssts_ipm_active,   // device in active state
    output                        ssts_ipm_part,     // device in partial state
    output                        ssts_ipm_slumb,    // device in slumber state
    output                        ssts_ipm_devsleep, // device in DevSleep state
    
    output                        ssts_spd_dnp,      // device not present or communication not established
    output                        ssts_spd_gen1,     // Gen 1 rate negotiated
    output                        ssts_spd_gen2,     // Gen 2 rate negotiated
    output                        ssts_spd_gen3,     // Gen 3 rate negotiated
    
    output                        ssts_det_ndnp,     // no device detected, phy communication not established
    output                        ssts_det_dnp,      // device detected, but phy communication not established
    output                        ssts_det_dp,       // device detected, phy communication established
    output                        ssts_det_offline,  // device detected, phy communication established
    input                   [3:0] ssts_det,          // current value of PxSSTS.DET

 // SCR2:SControl (written by software only)
 ///   input                   [3:0] sctl_ipm,          // Interface power management transitions allowed
 ///   input                   [3:0] sctl_spd,          // Interface maximal speed
    input                   [3:0] sctl_det,          // Device detection initialization requested
    input                         sctl_det_changed,  // Software had written new value to sctl_det
    output                        sctl_det_reset,    // clear sctl_det_changed
    
    output                        hba_rst_done,      // reset GHC.HR and other bits
    
    output                        pxci0_clear,       // PxCI clear
    input                         pxci0,             // pxCI current value
    
    // inputs from the DMA engine
///   input                         dma_prd_done, // output (finished next prd)
    output                        dma_prd_irq_clear, // reset pending prd_irq
    input                         dma_prd_irq_pend,  // prd interrupt pending. This is just a condition for irq - actual will be generated after FIS OK
    input                         dma_cmd_busy,      // output reg (DMA engine is processing PRDs)
///    input                         dma_cmd_done,   // output (last PRD is over)
    output                        dma_cmd_abort,     // try to abort a command
    input                         dma_abort_done,    // if abort is not needed, will generate dma_abort_done just next cycle
    
    // Communication with ahci_fis_receive (some are unused)
    // Debug features
    input                         fis_first_invalid, // Some data available from FIFO, but not FIS head
    output                        fis_first_flush,   // Skip FIFO data until empty or FIS head

    input                         fis_first_vld, // fis_first contains valid FIS header, reset by 'get_*'
    input                   [7:0] fis_type,      // FIS type (low byte in the first FIS DWORD), valid with  'fis_first_vld'
    input                   [7:0] bist_bits,    // bits that define built-in self test
    
    // Receiving FIS
    output                        get_dsfis,
    output                        get_psfis,
    output                        get_rfis,
    output                        get_sdbfis,
    output                        get_ufis,
    output                        get_data_fis,
    output                        get_ignore,    // ignore whatever FIS (use for DMA activate too?)
//    input                         get_fis_busy,  // busy processing FIS 
    input                         get_fis_done,  // done processing FIS (see fis_ok, fis_err, fis_ferr)
    input                         fis_ok,        // FIS done,  checksum OK reset by starting a new get FIS
    input                         fis_err,       // FIS done, checksum ERROR reset by starting a new get FIS
    input                         fis_ferr,      // FIS done, fatal error - FIS too long
    input                         fis_extra,     // more data got from FIS than DMA can accept. Does not deny fis_ok. May have latency

    output                        set_update_sig, // when set, enables get_sig (and resets itself)
///    input                         pUpdateSig,     // state variable
///    input                         sig_available,  // device signature available
    output                        update_sig,        // update signature


    // next commands use register address/data/we for 1 clock cycle - after next to command (commnd - t0, we - t2)
    output                        update_err_sts,// update PxTFD.STS and PxTFD.ERR from the last received regs d2h
    output                        update_pio,    // update PxTFD.STS and PxTFD.ERR from pio_* (entry PIO:Update)
    
    output                        update_prdbc,  // update PRDBC in registers
    output                        clear_bsy_drq, // clear PxTFD.STS.BSY and PxTFD.STS.DRQ, update
    output                        clear_bsy_set_drq, // clear PxTFD.STS.BSY and sets PxTFD.STS.DRQ, update
    
    output                        set_bsy,       // set PxTFD.STS.BSY, update
    output                        set_sts_7f,    // set PxTFD.STS = 0x7f, update
    output                        set_sts_80,    // set PxTFD.STS = 0x80 (may be combined with set_sts_7f), update
    output                        clear_xfer_cntr, // clear pXferCntr (is it needed as a separate input)?
    
    output                        decr_dwcr,     // decrement DMA Xfer counter after read // need pulse to 'update_prdbc' to write to registers
    output                        decr_dwcw,     // decrement DMA Xfer counter after write // need pulse to 'update_prdbc' to write to registers
//    output                 [11:0] decr_DXC_dw,   // decrement value (in DWORDs)
    input                         pxcmd_fre,     // control bit enables saving FIS to memory
    input                         pPioXfer,      // state variable
    input                   [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
///    input                   [7:0] tfd_err,       // Current PxTFD error field (updated after regFIS and SDB)
    input                         fis_i,         // value of "I" field in received regsD2H or SDB FIS
///    input                         sdb_n,         // value of "N" field in received SDB FIS 
    input                         dma_a,         // value of "A" field in received DMA Setup FIS 
///    input                         dma_d,         // value of "D" field in received DMA Setup FIS
    input                         pio_i,         // value of "I" field in received PIO Setup FIS
    input                         pio_d,         // value of "D" field in received PIO Setup FIS
///    input                   [7:0] pio_es,        // value of PIO E_Status
///    input                         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
///    input                  [31:2] xfer_cntr,     // transfer counter in words for both DMA (31 bit) and PIO (lower 15 bits), updated after decr_dwc
    input                         xfer_cntr_zero,// valid next cycle                   
    
    // Communication with ahci_fis_transmit
    // Command pulses to execute states
    output                        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
    output                        cfis_xmit,    // transmit command (wait for dma_ct_busy == 0)
    output                        dx_xmit,      // send FIS header DWORD, (just 0x46), then forward DMA data
                                                // transmit until error, 2048DWords or pDmaXferCnt 
    output                        atapi_xmit,   // tarsmit ATAPI command FIS
    input                         xmit_done,
//    input                         xmit_busy,

    output                        clearCmdToIssue, // From CFIS:SUCCESS 
    input                         pCmdToIssue, // AHCI port variable
//    output                        dmaCntrZero, // DMA counter is zero - would be a duplicate to the one in receive module and dwords_sent output
//    input                         syncesc_recv, // These two inputs interrupt transmit
//    input                         xmit_err,     // 
    input                  [ 2:0] 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)
    
///    input                  [15:0] ch_prdtl,    // Physical region descriptor table length (in entries, 0 is 0)
    input                         ch_c,        // Clear busy upon R_OK for this FIS
    input                         ch_b,        // Built-in self test command
    input                         ch_r,        // reset - may need to send SYNC escape before this command
    input                         ch_p,        // prefetchable - only used with non-zero PRDTL or ATAPI bit set
    input                         ch_w,        // Write: system memory -> device
    input                         ch_a,         // ATAPI: 1 means device should send PIO setup FIS for ATAPI command
    input                         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
    
    output reg             [ 9:0] last_jump_addr // debug feature
///    input                   [4:0] ch_cfl,      // length of the command FIS in DW, 0 means none. 0 and 1 - illegal,
                                               // maximal is 16 (0x10)
///    input                  [11:0] dwords_sent // number of DWORDs transmitted (up to 2048)                                 

);
`include "includes/ahci_localparams.vh" // @SuppressThisWarning VEditor : Unused localparams
`include "includes/fis_types.vh"        // @SuppressThisWarning VEditor : Some  localparams unused
    // Reset addresses - later use generated
    localparam LABEL_POR =        11'h000;
    localparam LABEL_HBA_RST =    11'h002;
    localparam LABEL_PORT_RST =   11'h004;
    localparam LABEL_COMINIT =    11'h006;
    localparam LABEL_ST_CLEARED = 11'h008;

    wire                           tfd_bsy =     tfd_sts[7];
    wire                           tfd_drq =     tfd_sts[3];
    wire                           tfd_sts_err = tfd_sts[0];
    
    reg                     [ 9:0] pgm_waddr;
//    wire                           pgm_ren;
//    wire                           pgm_regen;
    wire                           cond_met_w; // calculated from signals and program conditions decoder
    reg                     [ 9:0] pgm_jump_addr;
    reg                     [ 9:0] pgm_addr;
    wire                    [17:0] pgm_data;
    reg                            was_rst;
//    reg                            jump_r; 
    reg                      [2:0] fsm_jump;
    wire                           fsm_next;
//    reg                            fsm_next_r;
    reg                            fsm_actions;    // processing actions
    reg                            dis_actions;    // disable actions during async jump
    reg                            fsm_act_busy;
    reg                      [1:0] fsm_transitions; // processing transitions
    reg                            fsm_preload;    // read first sequence data (2 cycles for regen)
//    wire                     [7:0] precond_w = pgm_data[17:10];   // select what to use - cond_met_w valis after precond_w, same time as conditions
//    reg                      [7:0] conditions;
//    wire                           pre_jump_w =   (|async_pend_r) ? async_ackn : |(cond_met_w & fsm_transitions[1]);
    wire                           pre_jump_w =   (|async_pend_r) ? async_ackn : (cond_met_w & fsm_transitions[1]);
    wire                           fsm_act_done_w = get_fis_done ||
                                                  xmit_done ||
                                                  (syncesc_send_pend && syncesc_send_done) ||
                                                  dma_abort_done ||
                                                  asynq_rq; // cominit_got || pcmd_st_cleared
    reg                            fsm_act_done; // made later by 1 cycle so the new conditions are latched // TODO:check is enough ? Adding 1 extra                                              
    reg                            fsm_act_pre_done;                                              
    wire                           fsm_wait_act_w = pgm_data[16]; // this action requires waiting for done
    wire                           fsm_last_act_w = pgm_data[17];

    wire                           fsm_pre_act_w = !dis_actions && fsm_actions && fsm_next; // use it as CS for generated actions (registered)
    
    reg                      [1:0] async_pend_r; // waiting to process cominit_got
    reg                            async_from_st; // change to multi-bit if there will be more sources for async transitions
//    wire                           asynq_rq = (cominit_got && unsolicited_cominit_en) || pcmd_st_cleared;
    wire                           asynq_rq = (cominit_got && unsolicited_en) || pcmd_st_cleared;
                                   // OK to wait for some time fsm_act_busy is supposed to never hang up
    wire                           async_ackn = !fsm_preload && async_pend_r[0] && ((fsm_actions && !update_busy && !fsm_act_busy) || fsm_transitions[0]);   // OK to process async jump
//    reg                            x_rdy_collision_pend;
    reg                            syncesc_send_pend; // waiting for 'syncesc_send' confiramtion 'syncesc_send_done'
    reg                      [1:0] phy_ready_prev;    // previous state of phy_ready / speed
    reg                            phy_ready_chng_r;  // pulse when phy_ready changes
    wire                           phy_ready_chng_w = !hba_rst && !was_rst && (phy_ready != phy_ready_prev);
    reg                            was_last_action_r; // delay last action if it was fsm_wait_act;
    
    wire                           fsm_transitions_w =  // next will be transitions processing
                                       (fsm_last_act_w && fsm_actions && fsm_next && !fsm_wait_act_w) ||
                                       (fsm_act_busy && fsm_act_done && was_last_action_r);
    
    wire                           conditions_ce =  // copy all conditions to the register so they will not change while iterating through them
                                       !fsm_transitions_w && !fsm_transitions[0];
//    reg                            unsolicited_cominit_en; // allow unsolicited COMINITs
//    wire                           en_cominit; // en_cominit

    // New variable:
    reg                            pisn32; // pIssueSlot != 32
    wire                           clear_pisn32; // additional clear when in P:NotRunning state
    
    assign fsm_next = (fsm_preload || (fsm_actions && !update_busy && !fsm_act_busy) || fsm_transitions[0]) && !async_pend_r[0]; // quiet if received cominit is pending
    assign update_all = fsm_jump[0];

    assign ssts_ipm_dnp =      phy_ready_chng_r && (phy_ready_prev == 0);  // device not present or communication not established
    assign ssts_ipm_active =   phy_ready_chng_r && (phy_ready_prev != 0);  // device in active state
    assign ssts_ipm_part =     0;                                          // device in partial state
    assign ssts_ipm_slumb =    0;                                          // device in slumber state
    assign ssts_ipm_devsleep = 0;                                          // device in DevSleep state
    
    assign ssts_spd_dnp =      phy_ready_chng_r && (phy_ready_prev == 0);  // device not present or communication not established
    assign ssts_spd_gen1 =     phy_ready_chng_r && (phy_ready_prev == 1);  // Gen 1 rate negotiated
    assign ssts_spd_gen2 =     phy_ready_chng_r && (phy_ready_prev == 2);  // Gen 2 rate negotiated
    assign ssts_spd_gen3 =     phy_ready_chng_r && (phy_ready_prev == 3);  // Gen 3 rate negotiated
    
    assign ssts_det_ndnp =     phy_ready_chng_r && (phy_ready_prev == 0);  // no device detected, phy communication not established
//    assign ssts_det_dnp =      0;                                          // device detected, but phy communication not established
    assign ssts_det_dp =       phy_ready_chng_r && (phy_ready_prev != 0);  // device detected, phy communication established

    assign sirq_OF =           0;  // RWC: Overflow Status (buffer overrun - should not happen, add?)
    assign sirq_PRC =          phy_ready_chng_r; // RO:  PhyRdy changed Status



    // Writing to the FSM program memory
    always @ (posedge aclk) begin
        if      (arst)   pgm_waddr <= 0;
        else if (pgm_wa) pgm_waddr <= pgm_ad[ 9:0];
        else if (pgm_wd) pgm_waddr <=  pgm_waddr + 1;
    end

    always @ (posedge mclk) begin
        if (hba_rst || pxci0_clear || clear_pisn32) pisn32 <= 0; 
        else if (fetch_cmd)                         pisn32 <= 1;
        
    end

    always @ (posedge mclk) begin
///        if      (hba_rst)                     unsolicited_cominit_en <= !was_port_rst;
//      else if (en_cominit || comreset_send) unsolicited_cominit_en <= en_cominit;
    
    
        if      (hba_rst)                                                    pgm_jump_addr <= (was_hba_rst || was_port_rst) ? (was_hba_rst? LABEL_HBA_RST:LABEL_PORT_RST) : LABEL_POR;
//        else if (async_pend_r[1])                                            pgm_jump_addr <= async_from_st? LABEL_ST_CLEARED : LABEL_COMINIT;
        else if (async_pend_r[0])                                            pgm_jump_addr <= async_from_st? LABEL_ST_CLEARED : LABEL_COMINIT;
        else if (fsm_transitions[0] && (!cond_met_w || !fsm_transitions[1])) pgm_jump_addr <= pgm_data[9:0];
        
        was_rst <= hba_rst;
        
///     fsm_act_done <= fsm_act_done_w; // delay by 1 clock cycle
        fsm_act_pre_done <= fsm_act_done_w; // delay by 1 clock cycle
        fsm_act_done <= fsm_act_pre_done; // TODO - verify delay by 2 is needed to latch
        fsm_jump <= {fsm_jump[1:0], pre_jump_w | (was_rst & ~hba_rst)};
        
        if   (fsm_jump[0]) pgm_addr <= pgm_jump_addr;
        else if (fsm_next) pgm_addr <= pgm_addr + 1;
        
        if   (fsm_jump[0]) last_jump_addr <= pgm_jump_addr; // debug feature
        
//        if            (hba_rst) conditions <= 0; 
//        if (fsm_transitions[0]) conditions <= precond_w;
        
        if      (hba_rst)                                   fsm_actions <= 0;
        else if (fsm_jump[2])                               fsm_actions <= 1;
        else if (fsm_last_act_w && fsm_next)                fsm_actions <= 0;
        
        if      (hba_rst)                                   dis_actions <= 0;
        else if (|async_pend_r)                             dis_actions <= 1;
        else if (fsm_jump[2])                               dis_actions <= 0;
        
        
        if (fsm_actions && fsm_next)                        was_last_action_r <= fsm_last_act_w;
        
////    if      (hba_rst || pre_jump_w)                                fsm_transitions <= 0;
/// 2016.12.07 jumps were not disabled after async transitions, they came from the previously executed code
        if      (hba_rst || pre_jump_w || dis_actions)                 fsm_transitions <= 0;
        else if (fsm_transitions_w)                                    fsm_transitions <= 1; 
//        else if ((fsm_last_act_w && fsm_actions && fsm_next && !fsm_wait_act_w) ||
//                 (fsm_act_busy && fsm_act_done && was_last_action_r) ) fsm_transitions <= 1;
        else                                                           fsm_transitions <= {fsm_transitions[0],fsm_transitions[0]};
        
        if (hba_rst) fsm_preload <= 0;
        else         fsm_preload <= |fsm_jump[1:0];
        
        if      (hba_rst)       fsm_act_busy <= 0;
        else if (fsm_pre_act_w) fsm_act_busy <= fsm_wait_act_w;
        else if (fsm_act_done)  fsm_act_busy <= 0;
        
        if      (hba_rst)         async_from_st <= 0;
        else if (pcmd_st_cleared) async_from_st <= 1;
        else if (asynq_rq)        async_from_st <= 0;
        
        if (hba_rst) async_pend_r <= 0;
///        else async_pend_r <= {async_pend_r[0], asynq_rq | (async_pend_r[0] & ~async_ackn)};  
        else async_pend_r <= {async_pend_r[0], (asynq_rq | async_pend_r[0]) & ~async_ackn};  
        
        
//        if (hba_rst || pcmd_cr_set) x_rdy_collision_pend <= 0;
//        else if (x_rdy_collision)   x_rdy_collision_pend <= 1;
        
        if (hba_rst || syncesc_send_done) syncesc_send_pend <= 0;
        else if (syncesc_send)            syncesc_send_pend <= 1;
        
        if (was_rst && !hba_rst && !was_hba_rst && !was_port_rst) phy_ready_prev <= 0;
        else if (phy_ready_chng_w)                                phy_ready_prev <=  phy_ready;
        
        phy_ready_chng_r <= phy_ready_chng_w; 
        
    end

    ram18p_var_w_var_r #(
        .REGISTERS(1),
        .LOG2WIDTH_WR(4),
        .LOG2WIDTH_RD(4)
        `include "includes/ahxi_fsm_code.vh" 
    ) fsm_pgm_mem_i (
        .rclk     (mclk),      // input
        .raddr    (pgm_addr),  // input[10:0] 
        .ren      (fsm_next),  // input
        .regen    (fsm_next),  // input
        .data_out (pgm_data),  // output[17:0] 
        .wclk     (aclk),      // input
        .waddr    (pgm_waddr), // input[10:0] 
        .we       (pgm_wd),    // input
        .web      (4'hf),     // input[7:0] 
        .data_in  (pgm_ad)     // input[17:0] 
    );

    action_decoder action_decoder_i (
        .clk                (mclk),              // input
        .enable             (fsm_pre_act_w),     // input
        .data               (pgm_data[10:0]),    // input[10:0]
    // CTRL_STAT
        .PXSERR_DIAG_X      (sirq_PC),           // output reg 
        .SIRQ_DHR           (sirq_DHR),          // output reg 
        .SIRQ_DP            (sirq_DP),           // output reg 
        .SIRQ_DS            (sirq_DS),           // output reg 
        .SIRQ_IF            (sirq_IF),           // output reg 
        .SIRQ_INF           (sirq_INF),          // output reg 
        .SIRQ_PS            (sirq_PS),           // output reg 
        .SIRQ_SDB           (sirq_SDB),          // output reg 
        .SIRQ_TFE           (sirq_TFE),          // output reg 
        .SIRQ_UF            (sirq_UF),           // output reg 
        .PFSM_STARTED       (pfsm_started),      // output reg 
        .PCMD_CR_CLEAR      (pcmd_cr_reset),     // output reg 
        .PCMD_CR_SET        (pcmd_cr_set),       // output reg 
        .PXCI0_CLEAR        (pxci0_clear),       // output reg 
        .PXSSTS_DET_1       (ssts_det_dnp),      // output reg 
        .SSTS_DET_OFFLINE   (ssts_det_offline),  // output reg 
        .SCTL_DET_CLEAR     (sctl_det_reset),    // output reg 
        .HBA_RST_DONE       (hba_rst_done), // output reg 
    // FIS RECEIVE
        .SET_UPDATE_SIG     (set_update_sig),    // output reg 
        .UPDATE_SIG         (update_sig),        // output reg 
        .UPDATE_ERR_STS     (update_err_sts),    // output reg 
        .UPDATE_PIO         (update_pio),        // output reg 
        .UPDATE_PRDBC       (update_prdbc),      // output reg 
        .CLEAR_BSY_DRQ      (clear_bsy_drq),     // output reg 
        .CLEAR_BSY_SET_DRQ  (clear_bsy_set_drq), // output reg 
        .SET_BSY            (set_bsy),           // output reg 
        .SET_STS_7F         (set_sts_7f),        // output reg 
        .SET_STS_80         (set_sts_80),        // output reg 
        .XFER_CNTR_CLEAR    (clear_xfer_cntr),   // output reg 
        .DECR_DWCR          (decr_dwcr),         // output reg 
        .DECR_DWCW          (decr_dwcw),         // output reg 
        .FIS_FIRST_FLUSH    (fis_first_flush),   // output reg
    // FIS_TRANSMIT
        .CLEAR_CMD_TO_ISSUE (clearCmdToIssue),   // output reg
    // DMA
        .DMA_ABORT          (dma_cmd_abort),     // output reg 
        .DMA_PRD_IRQ_CLEAR  (dma_prd_irq_clear), // output reg
    // SATA TRANSPORT/LINK/PHY
        .XMIT_COMRESET      (comreset_send),     // output reg 
        .SEND_SYNC_ESC      (syncesc_send),      // output reg 
        .SET_OFFLINE        (set_offline),       // output reg 
        .R_OK               (send_R_OK),         // output reg 
        .R_ERR              (send_R_ERR),        // output reg
//        .EN_COMINIT         (en_cominit),      // output reg
        .EN_COMINIT         (clear_pisn32),      // output reg
    // FIS TRANSMIT/WAIT DONE
        .FETCH_CMD          (fetch_cmd),         // output reg 
        .ATAPI_XMIT         (atapi_xmit),        // output reg 
        .CFIS_XMIT          (cfis_xmit),         // output reg 
        .DX_XMIT            (dx_xmit),           // output reg
    //FIS RECEIVE/WAIT DONE
        .GET_DATA_FIS       (get_data_fis),      // output reg 
        .GET_DSFIS          (get_dsfis),         // output reg 
        .GET_IGNORE         (get_ignore),        // output reg 
        .GET_PSFIS          (get_psfis),         // output reg 
        .GET_RFIS           (get_rfis),          // output reg 
        .GET_SDBFIS         (get_sdbfis),        // output reg 
        .GET_UFIS           (get_ufis)           // output reg 
    );

// Condition inputs may be registered if needed
    condition_mux condition_mux_i (
        .clk                   (mclk),                                    // input
        .ce                    (conditions_ce),                           // input
        .sel                   (pgm_data[17:10]),                         // input[7:0]
        .condition             (cond_met_w),                              // output
    //COMPOSITE
        .ST_NB_ND              (pcmd_st && !tfd_bsy &&!tfd_drq),          // input PxCMD.ST & !PxTFD.STS.BSY & !PxTFD.STS.DRQ
//        .PXCI0_NOT_CMDTOISSUE  (pxci0 && !pCmdToIssue),                 // input pxci0 && !pCmdToIssue was pIssueSlot==32, -> p:SelectCmd
        .PXCI0_NOT_CMDTOISSUE  (pxci0 && !pisn32),                        // input pxci0 && !pCmdToIssue was pIssueSlot==32, -> p:SelectCmd
        .PCTI_CTBAR_XCZ        (pCmdToIssue && xfer_cntr_zero && ch_r ),  // input  pCmdToIssue && ch_r && xfer_cntr_zero
        .PCTI_XCZ              (pCmdToIssue && xfer_cntr_zero),           // input  pCmdToIssue && xfer_cntr_zero
        .NST_D2HR              (!pcmd_st && (fis_type == FIS_D2HR)),      // input !ST && (FIS == FIS_D2HR) TODO: does it mean either BSY or DRQ are 1?
        .NPD_NCA               (!pio_d && !ch_a),                         // input  pio_d = 0 && ch_a == 0
        .CHW_DMAA              (ch_w && dma_a),                           // input ch_w && dma_a
    // CTRL_STAT
        .SCTL_DET_CHANGED_TO_4 (sctl_det_changed && (sctl_det == 4)),     // input (requires sctl_det_reset after)
        .SCTL_DET_CHANGED_TO_1 (sctl_det_changed && (sctl_det == 1)),     // input (requires sctl_det_reset after)
        .PXSSTS_DET_NE_3       (ssts_det != 3),                           // input  ssts_det!=3, // device detected, phy communication not established
        .PXSSTS_DET_EQ_1       (ssts_det == 1),                           // input
        .NPCMD_FRE             (!pxcmd_fre),                              // input !pcmd_fre (docs: goto P:NotRunning, but we need to clear FIFO)
    // FIS RECEIVE
        .FIS_OK                (fis_ok),                                  // input
        .FIS_ERR               (fis_err),                                 // input
        .FIS_FERR              (fis_ferr),                                // input
        .FIS_EXTRA             (fis_extra),                               // input
        .FIS_FIRST_INVALID     (fis_first_invalid),                       // input
        .FR_D2HR               (fis_first_vld && (fis_type == FIS_D2HR)), // input fis_first_vld & fis_type == 0x34 (D2H Register)
        .FIS_DATA              (fis_first_vld && (fis_type == FIS_DATA)), // input fis_first_vld && (fis_type == 'h46)
        .FIS_ANY               (fis_first_vld),                           // input
        .NB_ND_D2HR_PIO        (((fis_type == FIS_D2HR) || (fis_type == FIS_PIOS)) && !tfd_bsy && !tfd_drq), // input ((FIS == FIS_D2HR) || (FIS == FIS_PIOS)) && !PxTFD.STS.BSY & !PxTFD.STS.DRQ
        .D2HR                  (  fis_type == FIS_D2HR),                  // input  FIS == FIS_D2HR
        .SDB                   (  fis_type == FIS_SDB),                   // input
        .DMA_ACT               (  fis_type == FIS_DMAA),                  // input
        .DMA_SETUP             (  fis_type == FIS_DMAS),                  // input
        .BIST_ACT_FE           (( fis_type == FIS_BIST) && (|bist_bits)), // input FIS == FIS_BIST && |bist_bits
        .BIST_ACT              (( fis_type == FIS_BIST)),                 // input FIS == FIS_BIST#  && !(|bist_bits)
        .PIO_SETUP             (  fis_type == FIS_PIOS),                  // input
        .NB_ND                 (!tfd_bsy &&!tfd_drq),                     // input PxTFD.STS.BSY =’0’ and PxTFD.STS.DRQ =’0’
        .TFD_STS_ERR           ( tfd_sts_err),                            // input  tfd_sts[0]
        .FIS_I                 (fis_i),                                   // input
        .PIO_I                 (pio_i),                                   // input
        .NPD                   (!pio_d),                                  // input pio_d = 0 , "ch_a == 1" is not needed
        .PIOX                  (pPioXfer),                                // input
        .XFER0                 (xfer_cntr_zero && !dma_cmd_busy),         // input  xfer_cntr_zero
        .PIOX_XFER0            (pPioXfer && xfer_cntr_zero &&!dma_cmd_busy), // input pPioXfer && xfer_cntr_zero
    // FIS_TRANSMIT
        .CTBAA_CTBAP           (ch_a && ch_p),                            // input
        .CTBAP                 (ch_p),                                    // input
        .CTBA_B                (ch_b),                                    // input
        .CTBA_C                (ch_c),                                    // input
        .TX_ERR                (dx_err[1]),                               // input  dx_err[1] (reset by new command)
        .SYNCESC_ERR           (dx_err[0]),                               // input
    // DMA
        .DMA_PRD_IRQ_PEND      (dma_prd_irq_pend),                        // input
    // SATA TRANSPORT/LINK/PHY        
        .X_RDY_COLLISION       (dx_err[2]) //x_rdy_collision_pend)                     // input
    );



/*
    output                        update_all,
    input                         update_busy, // valid same cycle as update_all


Notes:
 Implement sync esc request/ackn in TL (available in LL)
*/
endmodule