/* -*- c++ -*-
 *
 * SOCLIB_LGPL_HEADER_BEGIN
 * 
 * This file is part of SoCLib, GNU LGPLv2.1.
 * 
 * SoCLib is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; version 2.1 of the License.
 * 
 * SoCLib 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with SoCLib; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * SOCLIB_LGPL_HEADER_END
 *
 * Copyright (c) UPMC, Lip6, SoC
 *         Alain Greiner <alain.greiner@lip6.fr>, 2008
 *
 * Maintainers: alain eric.guthmuller@polytechnique.edu nipo
 */

/////////////////////////////////////////////////////////////////////////////
// History
// - 25/04/2008
//   The existing vci_xcache component has been extended to include 
//   a VCI target port to support a directory based coherence protocol.
//   Two types of packets can be send by the L2 cache to the L1 cache
//   * INVALIDATE packets : length = 1
//   * UPDATE packets : length = n + 2   
//   The CLEANUP packets are sent by the L1 cache to the L2 cache, 
//   to signal a replaced cache line.
// - 12/08/2008
//   The vci_cc_xcache_wrapper component instanciates directly the processsor 
//   iss, in order to supress the processor/cache interface.
//   According to the VCI advanced specification, this component uses one 
//   word VCI CMD packets for MISS transactions, and accept one word VCI RSP
//   packets for Write burst  transactions.
//   The write buffer has been modified to use the WriteBuffer object. 
//   A VCI write burst is constructed when two conditions are satisfied :
//   The processor make strictly successive write requests, and they are
//   in the same cache line. The write buffer performs re-ordering, to
//   respect the contiguous addresses VCI constraint. In case of several
//   WRITE_WORD requests in the same word, only the last request is conserved.
//   In case of several WRITE_HALF or WRITE_WORD requests in the same word,
//   the requests are merged in the same word. In case of uncached write
//   requests, each request is transmited as a single VCI transaction.
//   Both the data & instruction caches can be flushed in one single cycle.
///////////////////////////////////////////////////////////////////////////////

#include <cassert>
#include <iomanip>
#include "arithmetics.h"
#include "../include/vci_cc_xcache_wrapper_v4.h"

#if CC_XCACHE_WRAPPER_DEBUG
# define PRINTF(msg...) do { if (m_cpt_total_cycles>=CC_XCACHE_WRAPPER_DEBUG_CYCLE_MIN) printf(msg); } while (0); 
#else
# define PRINTF(msg...)
#endif

# define ASSERT(cond,msg) assert ((cond) and msg);

#if CC_XCACHE_WRAPPER_FIFO_RSP
# define CACHE_MISS_BUF_ALLOC
# define CACHE_MISS_BUF_DEALLOC
# define CACHE_MISS_BUF_RESET(c)        while (r_##c##cache_miss_buf.size()>0) {r_##c##cache_miss_buf.pop();}
# define CACHE_MISS_BUF_REQ_INIT(c)
# define CACHE_MISS_BUF_RSP_VAL(c,n)    (r_##c##cache_miss_buf.size()>0)
# define CACHE_MISS_BUF_RSP_ACK(c)      (r_##c##cache_miss_buf.size()<2)
# define CACHE_MISS_BUF_RSP_DATA(c,n)   r_##c##cache_miss_buf.front()
# define CACHE_MISS_BUF_RSP_POP(c)      do { r_##c##cache_miss_buf.pop();} while (0)
# define CACHE_MISS_BUF_RSP_PUSH(c,n,d) do { r_##c##cache_miss_buf.push(d);} while (0)
# define CACHE_MISS_BUF_RSP_PRINT(c)    do { PRINTF("    * cache_miss_buf - size : %d\n",r_##c##cache_miss_buf.size());} while (0)
#else
# define CACHE_MISS_BUF_ALLOC           do { \
                                        r_icache_miss_val = new bool  [m_icache_words]; \
                                        r_icache_miss_buf = new data_t[m_icache_words]; \
                                        r_dcache_miss_val = new bool  [m_dcache_words]; \
                                        r_dcache_miss_buf = new data_t[m_dcache_words]; \
                                        } while (0)
# define CACHE_MISS_BUF_DEALLOC         do { \
                                        delete [] r_icache_miss_val; \
                                        delete [] r_icache_miss_buf; \
                                        delete [] r_dcache_miss_val; \
                                        delete [] r_dcache_miss_buf; \
                                        } while (0)
# define CACHE_MISS_BUF_RESET(c)
# define CACHE_MISS_BUF_REQ_INIT(c)     do {for (uint32_t i=0; i<m_##c##cache_words;++i) r_##c##cache_miss_val[i] = false;} while (0)
# define CACHE_MISS_BUF_RSP_VAL(c,n)    r_##c##cache_miss_val[n]
# define CACHE_MISS_BUF_RSP_ACK(c)      true
# define CACHE_MISS_BUF_RSP_DATA(c,n)   r_##c##cache_miss_buf[n]
# define CACHE_MISS_BUF_RSP_POP(c)
# define CACHE_MISS_BUF_RSP_PUSH(c,n,d) do {r_##c##cache_miss_val[n] = true; r_##c##cache_miss_buf[n] = d;} while (0)
# define CACHE_MISS_BUF_RSP_PRINT(c)    do {for (uint32_t i=0; i<m_##c##cache_words;++i) PRINTF("%d %x |",r_##c##cache_miss_val[i],r_##c##cache_miss_buf[i]); PRINTF("\n");} while (0)
#endif

namespace soclib { 
namespace caba {
    namespace {

        const char *dcache_fsm_state_str[] = {
            "DCACHE_IDLE",
            "DCACHE_WRITE_UPDT",
#if CC_XCACHE_WRAPPER_SELECT_VICTIM
            "DCACHE_MISS_VICTIM",
#endif
            "DCACHE_MISS_WAIT",
            "DCACHE_MISS_UPDT",
            "DCACHE_UNC_WAIT",
            "DCACHE_SC_WAIT",
            "DCACHE_INVAL",
            "DCACHE_SYNC",
            "DCACHE_ERROR",
            "DCACHE_CC_CHECK",
            "DCACHE_CC_INVAL",
            "DCACHE_CC_UPDT",
            "DCACHE_CC_CLEANUP",
        };
        const char *icache_fsm_state_str[] = {
            "ICACHE_IDLE",
#if CC_XCACHE_WRAPPER_SELECT_VICTIM
            "ICACHE_MISS_VICTIM",
#endif
            "ICACHE_MISS_WAIT",
            "ICACHE_MISS_UPDT",
            "ICACHE_UNC_WAIT",
            "ICACHE_ERROR",
            "ICACHE_CC_CLEANUP",
            "ICACHE_CC_CHECK",
            "ICACHE_CC_INVAL",
            "ICACHE_CC_UPDT",
        };
        const char *cmd_fsm_state_str[] = {
            "CMD_IDLE",
            "CMD_INS_MISS",
            "CMD_INS_UNC",
            "CMD_DATA_MISS",
            "CMD_DATA_UNC",
            "CMD_DATA_WRITE",
            "CMD_DATA_SC",
        };
        const char *rsp_fsm_state_str[] = {
            "RSP_IDLE",
            "RSP_INS_MISS",
            "RSP_INS_UNC",
            "RSP_DATA_MISS",
            "RSP_DATA_UNC",
            "RSP_DATA_WRITE",
            "RSP_DATA_SC",
        };
        const char *tgt_fsm_state_str[] = {
            "TGT_IDLE",
            "TGT_UPDT_WORD",
            "TGT_UPDT_DATA",
            "TGT_REQ_BROADCAST",
            "TGT_REQ_ICACHE",
            "TGT_REQ_DCACHE",
            "TGT_RSP_BROADCAST",
            "TGT_RSP_ICACHE",
            "TGT_RSP_DCACHE",
        };

        const char *cleanup_fsm_state_str[] = {
            "CLEANUP_IDLE",
            "CLEANUP_DCACHE",
            "CLEANUP_ICACHE",
        };
    }

#define tmpl(...)  template<typename vci_param, typename iss_t> __VA_ARGS__ VciCcXCacheWrapperV4<vci_param, iss_t>

    using soclib::common::uint32_log2;

    /////////////////////////////////
    tmpl(/**/)::VciCcXCacheWrapperV4(
    /////////////////////////////////
            sc_module_name name,
            int proc_id,
            const soclib::common::MappingTable &mtp,
            const soclib::common::MappingTable &mtc,
            const soclib::common::IntTab &initiator_index_rw,
            const soclib::common::IntTab &initiator_index_c,
            const soclib::common::IntTab &target_index,
            size_t icache_ways,
            size_t icache_sets,
            size_t icache_words,
            size_t dcache_ways,
            size_t dcache_sets,
            size_t dcache_words,
            size_t wbuf_nwords,
            size_t wbuf_nlines,
            size_t wbuf_timeout
                                     )
        : 
            soclib::caba::BaseModule(name),

            p_clk       ("clk"),
            p_resetn    ("resetn"),
            p_vci_ini_rw("vci_ini_rw"),
            p_vci_ini_c ("vci_ini_c"),
            p_vci_tgt   ("vci_tgt"),

            m_cacheability_table(mtp.getCacheabilityTable<vci_addr_t>()),
            m_segment(mtc.getSegment(target_index)),
            m_iss(this->name(), proc_id),
            m_srcid_rw(mtp.indexForId(initiator_index_rw)),
            m_srcid_c(mtc.indexForId(initiator_index_c)),

            m_dcache_ways(dcache_ways),
            m_dcache_words(dcache_words),
            m_dcache_words_shift(uint32_log2(dcache_words)+2),
            m_dcache_yzmask((~0)<<m_dcache_words_shift),
            m_icache_ways(icache_ways),
            m_icache_words(icache_words),
            m_icache_words_shift(uint32_log2(icache_words)+2),
            m_icache_yzmask((~0)<<m_icache_words_shift),
            m_cache_words((dcache_words)?dcache_words:icache_words),

            r_dcache_fsm("r_dcache_fsm"),
            r_dcache_fsm_save("r_dcache_fsm_save"),
            r_dcache_addr_save("r_dcache_addr_save"),
            r_dcache_wdata_save("r_dcache_wdata_save"),
            r_dcache_rdata_save("r_dcache_rdata_save"),
            r_dcache_type_save("r_dcache_type_save"),
            r_dcache_be_save("r_dcache_be_save"),
            r_dcache_cached_save("r_dcache_cached_save"),
            r_dcache_cleanup_req("r_dcache_cleanup_req"),
            r_dcache_cleanup_line("r_dcache_cleanup_line"),
            r_dcache_miss_req("r_dcache_miss_req"),
            r_dcache_miss_way("r_dcache_miss_way"),
            r_dcache_miss_set("r_dcache_miss_set"),
            r_dcache_unc_req("r_dcache_unc_req"),
            r_dcache_sc_req("r_dcache_sc_req"),
            r_dcache_inval_rsp("r_dcache_inval_rsp"),
            r_dcache_update_addr("r_dcache_update_addr"),
            r_dcache_ll_data("r_dcache_ll_data"),
            r_dcache_ll_addr("r_dcache_ll_addr"),
            r_dcache_ll_valid("r_dcache_ll_valid"),
            r_dcache_previous_unc("r_dcache_previous_unc"),

            r_icache_fsm("r_icache_fsm"),
            r_icache_fsm_save("r_icache_fsm_save"),
            r_icache_addr_save("r_icache_addr_save"),
            r_icache_miss_req("r_icache_miss_req"),
            r_icache_miss_way("r_icache_miss_way"),
            r_icache_miss_set("r_icache_miss_set"),
            r_icache_unc_req("r_icache_unc_req"),
            r_icache_cleanup_req("r_icache_cleanup_req"),
            r_icache_cleanup_line("r_icache_cleanup_line"),
            r_icache_inval_rsp("r_icache_inval_rsp"),
            r_icache_update_addr("r_icache_update_addr"),

            r_vci_cmd_fsm("r_vci_cmd_fsm"),
            r_vci_cmd_min("r_vci_cmd_min"),
            r_vci_cmd_max("r_vci_cmd_max"),
            r_vci_cmd_cpt("r_vci_cmd_cpt"),
            r_vci_cmd_dcache_prior("r_vci_cmd_dcache_prior"),

            r_vci_rsp_fsm("r_vci_rsp_fsm"),
            r_vci_rsp_ins_error("r_vci_rsp_ins_error"),
            r_vci_rsp_data_error("r_vci_rsp_data_error"),
            r_vci_rsp_cpt("r_vci_rsp_cpt"),
            r_vci_rsp_ack("r_vci_rsp_ack"),

            r_icache_buf_unc_valid("r_icache_buf_unc_valid"),

#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
            r_cache_word("r_cache_word"),
#endif

            r_vci_tgt_fsm("r_vci_tgt_fsm"),
            r_tgt_addr("r_tgt_addr"),
            r_tgt_word("r_tgt_word"),
            r_tgt_update("r_tgt_update"),
            r_tgt_update_data("r_tgt_update_data"),
         // r_tgt_brdcast("r_tgt_brdcast"),
            r_tgt_srcid("r_tgt_srcid"),
            r_tgt_pktid("r_tgt_pktid"),
            r_tgt_trdid("r_tgt_trdid"),
         // r_tgt_plen("r_tgt_plen"),
            r_tgt_icache_req("r_tgt_icache_req"),
            r_tgt_dcache_req("r_tgt_dcache_req"),
            r_tgt_icache_rsp("r_tgt_icache_rsp"),
            r_tgt_dcache_rsp("r_tgt_dcache_rsp"),

            r_cleanup_fsm("r_cleanup_fsm"),

            r_wbuf("r_wbuf", wbuf_nwords, wbuf_nlines, wbuf_timeout, dcache_words),
            r_icache("icache", icache_ways, icache_sets, icache_words),
            r_dcache("dcache", dcache_ways, dcache_sets, dcache_words)
            {
                // Size of word is 32 bits
                ASSERT( (icache_words*vci_param::B) < (1<<vci_param::K),
                        "I need more PLEN bits");

                ASSERT( (vci_param::T > 2) and ((1<<(vci_param::T-1)) >= wbuf_nlines),
                        "I need more TRDID bits");

                CACHE_MISS_BUF_ALLOC;

                r_tgt_buf = new data_t[m_cache_words];
                r_tgt_be  = new be_t  [m_cache_words];

                SC_METHOD(transition);
                dont_initialize();
                sensitive << p_clk.pos();

                SC_METHOD(genMoore);
                dont_initialize();
                sensitive << p_clk.neg();


                typename iss_t::CacheInfo cache_info;
                cache_info.has_mmu          = false;
                cache_info.icache_line_size = icache_words*sizeof(data_t);
                cache_info.icache_assoc     = icache_ways;
                cache_info.icache_n_lines   = icache_sets;
                cache_info.dcache_line_size = dcache_words*sizeof(data_t);
                cache_info.dcache_assoc     = dcache_ways;
                cache_info.dcache_n_lines   = dcache_sets;
                m_iss.setCacheInfo(cache_info);

#if CC_XCACHE_WRAPPER_STOP_SIMULATION
                m_stop_simulation               = false;
                m_stop_simulation_nb_frz_cycles = 0;
#endif // CC_XCACHE_WRAPPER_STOP_SIMULATION

#if CC_XCACHE_WRAPPER_DEBUG_DCACHE_TRANSACTION
                std::ostringstream filename("");
                filename << "Instruction_flow_" << proc_id << ".log";
                
                log_dcache_transaction_file.open(filename.str().c_str() ,std::ios::out | std::ios::trunc);
#endif
            } // end constructor

    ///////////////////////////////////
    tmpl(/**/)::~VciCcXCacheWrapperV4()
    ///////////////////////////////////
    {
#if CC_XCACHE_WRAPPER_DEBUG_DCACHE_TRANSACTION
        log_dcache_transaction_file.close();
#endif

        delete [] r_tgt_buf;
        delete [] r_tgt_be ;

        CACHE_MISS_BUF_DEALLOC;
    }

    ////////////////////////
    tmpl(void)::print_cpi()
    ////////////////////////
    {
        std::cout << "CPU " << m_srcid_rw << " : CPI = " 
            << (float)m_cpt_total_cycles/(m_cpt_total_cycles - m_cpt_frz_cycles) << std::endl ;
    }
    ////////////////////////
    tmpl(void)::print_stats()
    ////////////////////////
    {
        float run_cycles = (float)(m_cpt_total_cycles - m_cpt_frz_cycles);

        uint32_t m_cpt_data_read_cached  = m_cpt_data_read-m_cpt_data_read_uncached;
        uint32_t m_cpt_data_write_cached = m_cpt_data_write-m_cpt_data_write_uncached;
        std::cout << "------------------------------------" << std:: dec << std::endl;
        std::cout << "CPU " << m_srcid_rw << " / Time = " << m_cpt_total_cycles << std::endl;
        std::cout << "- CPI                            : " << (float)m_cpt_total_cycles/run_cycles << std::endl ;
        std::cout << "- IPC                            : " << (float)run_cycles/m_cpt_total_cycles << std::endl ;
        std::cout << "- DATA READ *                    : " << m_cpt_data_read << std::endl ;
        std::cout << "  + Uncached                     : " << m_cpt_data_read_uncached << " (" << (float)m_cpt_data_read_uncached*100.0/(float)m_cpt_data_read << "%)" << std::endl ;
        std::cout << "  + Cached and miss              : " << m_cpt_data_read_miss << " (" << (float)m_cpt_data_read_miss*100.0/(float)m_cpt_data_read_cached << "%)" << std::endl;
        std::cout << "- DATA WRITE *                   : " << m_cpt_data_write << std::endl ;
        std::cout << "  + Uncached                     : " << m_cpt_data_write_uncached << " (" << (float)m_cpt_data_write_uncached*100.0/(float)m_cpt_data_write << "%)" << std::endl ;
        std::cout << "  + Cached and miss              : " << m_cpt_data_write_miss << " (" << (float)m_cpt_data_write_miss*100.0/(float)m_cpt_data_write_cached << "%)" << std::endl;
        // std::cout << "- WRITE RATE                     : " << (float)m_cpt_data_write/run_cycles << std::endl;
        // std::cout << "- UNCACHED READ RATE             : " << (float)m_cpt_data_read_uncached/m_cpt_data_read << std::endl ;
        // std::cout << "- CACHED WRITE RATE              : " << (float)m_cpt_data_write_cached/m_cpt_data_write << std::endl ;
        // std::cout << "- IMISS_RATE                     : " << (float)m_cpt_ins_miss/run_cycles << std::endl;
        // std::cout << "- DMISS RATE                     : " << (float)m_cpt_data_miss/(m_cpt_data_read-m_cpt_data_read_uncached) << std::endl ;
        // std::cout << "- INS MISS COST                  : " << (float)m_cost_ins_miss_frz/m_cpt_ins_miss << std::endl;
        // std::cout << "- IMISS TRANSACTION              : " << (float)m_cost_imiss_transaction/m_cpt_imiss_transaction << std::endl;
        // std::cout << "- DMISS COST                     : " << (float)m_cost_data_miss_frz/m_cpt_data_miss << std::endl;
        // std::cout << "- DMISS TRANSACTION              : " << (float)m_cost_dmiss_transaction/m_cpt_dmiss_transaction << std::endl;
        // std::cout << "- UNC COST                       : " << (float)m_cost_unc_read_frz/m_cpt_data_read_uncached << std::endl;
        // std::cout << "- UNC TRANSACTION                : " << (float)m_cost_unc_transaction/m_cpt_unc_transaction << std::endl;
        // std::cout << "- WRITE COST                     : " << (float)m_cost_write_frz/m_cpt_data_write << std::endl;
        // std::cout << "- WRITE TRANSACTION              : " << (float)m_cost_write_transaction/m_cpt_data_write_transaction << std::endl;
        // std::cout << "- WRITE LENGTH                   : " << (float)m_length_write_transaction/m_cpt_data_write_transaction << std::endl;

        std::cout << "- CC_UPDATE_ICACHE               : " << m_cpt_cc_update_icache  << std::endl;
        std::cout << "  + AVERAGE WORD USEFUL          : " << (float)m_cpt_cc_update_icache_word_useful/(float)m_cpt_cc_update_icache << " on " << m_icache_words << " words" << std::endl; 
        std::cout << "- CC_UPDATE_DCACHE               : " << m_cpt_cc_update_dcache  << std::endl;
        std::cout << "  + AVERAGE WORD USEFUL          : " << (float)m_cpt_cc_update_dcache_word_useful/(float)m_cpt_cc_update_dcache << " on " << m_dcache_words << " words" << std::endl; 
        uint32_t m_cpt_cc_inval = m_cpt_cc_inval_broadcast+m_cpt_cc_inval_icache+m_cpt_cc_inval_dcache;
        std::cout << "- CC_INVALID                     : " << m_cpt_cc_inval << std::endl;
        std::cout << "  + ICACHE Only                  : " << (float)m_cpt_cc_inval_icache   *100.0/(float)m_cpt_cc_inval << "%" << std::endl;
        std::cout << "  + DCACHE Only                  : " << (float)m_cpt_cc_inval_dcache   *100.0/(float)m_cpt_cc_inval << "%" << std::endl;
        std::cout << "  + BROADCAST                    : " << (float)m_cpt_cc_inval_broadcast*100.0/(float)m_cpt_cc_inval << "%" << std::endl;
        std::cout << "* : accepted or not by the cache" << std::endl ;

        r_wbuf.printStatistics();
    }

    ////////////////////////////////////
    tmpl(void)::print_trace(size_t mode)
    ////////////////////////////////////
    {
        // b0 : write buffer print trace
        // b1 : write buffer verbose
        // b2 : dcache print trace
        // b3 : icache print trace

        typename iss_t::InstructionRequest  ireq;
        typename iss_t::DataRequest         dreq;

        m_iss.getRequests( ireq, dreq );
        std::cout << std::dec << "Proc \"" << name() << "\"" << std::endl;
        std::cout << ireq << std::endl;
        std::cout << dreq << std::endl;
        std::cout << "  " << dcache_fsm_state_str[r_dcache_fsm]
                  << "  " << icache_fsm_state_str[r_icache_fsm]
                  << "  " << cmd_fsm_state_str[r_vci_cmd_fsm]
                  << "  " << rsp_fsm_state_str[r_vci_rsp_fsm]
                  << "  " << tgt_fsm_state_str[r_vci_tgt_fsm]
                  << "  " << cleanup_fsm_state_str[r_cleanup_fsm] << std::endl;

        if(mode & 0x1)
        {
            r_wbuf.printTrace((mode>>1)&1);
        }
        if(mode & 0x4)
        {
            std::cout << "  Data cache" << std::endl;
            r_dcache.printTrace();
        }
        if(mode & 0x8)
        {
            std::cout << "  Instruction cache" << std::endl;
            r_icache.printTrace();
        }

        // if(mode & 0x10)
        // {
        //     std::cout << "  Icache miss buffer : ";
        //     CACHE_MISS_BUF_RSP_PRINT(i);
        //     std::cout << std::endl;

        //     std::cout << "  Dcache miss buffer : ";
        //     CACHE_MISS_BUF_RSP_PRINT(d);
        //     std::cout << std::endl;
        // }
    }

    //////////////////////////
    tmpl(void)::transition()
    //////////////////////////
    {
        if ( not p_resetn.read() ) {

            m_iss.reset();

            // FSM states
            r_dcache_fsm  = DCACHE_IDLE;
            r_icache_fsm  = ICACHE_IDLE;
            r_vci_cmd_fsm = CMD_IDLE;
            r_vci_rsp_fsm = RSP_IDLE;
            r_vci_tgt_fsm = TGT_IDLE;
            r_cleanup_fsm = CLEANUP_IDLE;

            // write buffer & caches
            r_wbuf.reset();
            r_icache.reset();
            r_dcache.reset();

            // synchronisation flip-flops from ICACHE & DCACHE FSMs to VCI  FSMs
            r_icache_miss_req    = false;
            r_icache_unc_req     = false;
            r_icache_cleanup_req = false;
            r_dcache_miss_req    = false;
            r_dcache_unc_req     = false;
            r_dcache_sc_req      = false;
            r_dcache_cleanup_req = false;
            r_dcache_previous_unc= false;

            // synchronisation flip-flops from TGT FSM to ICACHE & DCACHE FSMs
            r_tgt_icache_req     = false;
            r_tgt_dcache_req     = false;
            r_tgt_icache_rsp     = false;
            r_tgt_dcache_rsp     = false;

#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
            r_cache_word         = 0;
#endif


            // internal messages in DCACHE et ICACHE FSMs
            r_icache_inval_rsp   = false;
            r_dcache_inval_rsp   = false;

            // error signals from the VCI RSP FSM to the ICACHE or DCACHE FSMs
            r_dcache_ll_valid      = false;
            r_icache_buf_unc_valid = false;

            r_vci_cmd_dcache_prior = false;

            r_vci_rsp_data_error   = false;
            r_vci_rsp_ins_error    = false;

            CACHE_MISS_BUF_RESET(i);
            CACHE_MISS_BUF_RESET(d);

            // activity counters
            m_cpt_dcache_data_read  = 0;
            m_cpt_dcache_data_write = 0;
            m_cpt_dcache_dir_read   = 0;
            m_cpt_dcache_dir_write  = 0;
            m_cpt_icache_data_read  = 0;
            m_cpt_icache_data_write = 0;
            m_cpt_icache_dir_read   = 0;
            m_cpt_icache_dir_write  = 0;

            m_cpt_cc_update_icache             = 0;
            m_cpt_cc_update_dcache             = 0;
            m_cpt_cc_inval_broadcast           = 0;
            m_cpt_cc_inval_icache              = 0;
            m_cpt_cc_inval_dcache              = 0;
            m_cpt_cc_update_icache_word_useful = 0;
            m_cpt_cc_update_dcache_word_useful = 0;

            m_cpt_frz_cycles   = 0;
            m_cpt_total_cycles = 0;

            m_cpt_data_read            = 0;
            m_cpt_data_read_miss       = 0;
            m_cpt_data_read_uncached   = 0;
            m_cpt_data_write           = 0;
            m_cpt_data_write_miss      = 0;
            m_cpt_data_write_uncached  = 0;

            m_cpt_ins_miss     = 0;

            m_cost_write_frz = 0;
            m_cost_data_miss_frz = 0;
            m_cost_unc_read_frz = 0;
            m_cost_ins_miss_frz = 0;

            m_cpt_imiss_transaction = 0;
            m_cpt_dmiss_transaction = 0;
            m_cpt_unc_transaction = 0;
            m_cpt_data_write_transaction = 0;

            m_cost_imiss_transaction = 0;
            m_cost_dmiss_transaction = 0;
            m_cost_unc_transaction = 0;
            m_cost_write_transaction = 0;
            m_length_write_transaction = 0;

            return;
        }

        // printf("%d\n",(uint32_t)m_cpt_total_cycles);

        PRINTF("--------------------------------------------\n");
        PRINTF("  * CC_XCACHE_WRAPPER \"%s\" - Time = %d\n",name().c_str(),(uint32_t)m_cpt_total_cycles);
        PRINTF("    * fsm dcache          = %s\n",dcache_fsm_state_str[r_dcache_fsm]);
        PRINTF("    * fsm icache          = %s\n",icache_fsm_state_str[r_icache_fsm]);
        PRINTF("    * fsm cmd             = %s\n",cmd_fsm_state_str[r_vci_cmd_fsm]);
        PRINTF("    * fsm rsp             = %s\n",rsp_fsm_state_str[r_vci_rsp_fsm]);
        PRINTF("    * fsm tgt             = %s\n",tgt_fsm_state_str[r_vci_tgt_fsm]);
        PRINTF("    * fsm cleanup         = %s\n",cleanup_fsm_state_str[r_cleanup_fsm]);
        PRINTF("    * ll info             : %d %llx %llx\n",r_dcache_ll_valid.read(), (uint64_t)r_dcache_ll_addr.read(), (uint64_t)r_dcache_ll_data.read());
        PRINTF("    * dcache_previous_unc : %d\n",r_dcache_previous_unc.read());
        // CACHE_MISS_BUF_RSP_PRINT(i);
        // CACHE_MISS_BUF_RSP_PRINT(d);

#if CC_XCACHE_WRAPPER_DEBUG
        if (m_cpt_total_cycles>=CC_XCACHE_WRAPPER_DEBUG_CYCLE_MIN)
            {
                r_wbuf.printTrace(1);
            }
#endif

        m_cpt_total_cycles++;

        /////////////////////////////////////////////////////////////////////
        // The TGT_FSM controls the following ressources:
        // - r_vci_tgt_fsm
        // - r_tgt_buf[nwords]
        // - r_tgt_be[nwords]
        // - r_tgt_update
        // - r_tgt_word
        // - r_tgt_addr
        // - r_tgt_srcid
        // - r_tgt_trdid
        // - r_tgt_pktid
        // All VCI commands must be CMD_WRITE.
        // If the VCI address offset is null, the command is an invalidate 
        // request. It is an update request otherwise.
        // The VCI_TGT FSM stores the external request arguments in the
        // IDLE, UPDT_WORD & UPDT_DATA states. It sets the r_tgt_icache_req 
        // & r_tgt_dcache_req flip-flops to signal the external request to 
        // the ICACHE & DCACHE FSMs in the REQ state. It waits the completion
        // of the update or invalidate request in the RSP state.
        // -  for an invalidate request the VCI packet length is 1 word.
        // The WDATA field contains the line index (i.e. the Z & Y fields).
        // -  for an update request the VCI packet length is (n+2) words.
        // The WDATA field of the first VCI word contains the line number.
        // The WDATA field of the second VCI word contains the word index.
        // The WDATA field of the n following words contains the values.
        // -  for both invalidate & update requests, the VCI response
        // is one single word.
        // In case of errors in the VCI command packet, the simulation
        // is stopped with an error message.
        /////////////////////////////////////////////////////////////////////

        switch(r_vci_tgt_fsm) {

            case TGT_IDLE:
                if ( p_vci_tgt.cmdval.read() ) 
                {
                    PRINTF("    * <TGT> request\n");

                    addr_40 address = p_vci_tgt.address.read();

                    if ( p_vci_tgt.cmd.read() != vci_param::CMD_WRITE) 
                    {
                        std::cout << "error in component VCI_CC_XCACHE_WRAPPER " << name() << std::endl;
                        std::cout << "coherence request is not a write" << std::endl;
                        std::cout << "oddress = " << std::hex << address << std::dec << std::endl;
                        std::cout << "srcid   = " << p_vci_tgt.srcid.read() << std::endl;
                        exit(0);
                    }

                    // multi-update or multi-invalidate for data type
                    if ( ((address&0x3) != 0x3) and (not m_segment.contains(address)) ) 
                    {
                        std::cout << "error in component VCI_CC_XCACHE_WRAPPER " << name() << std::endl;
                        std::cout << "out of segment coherence request" << std::endl;
                        std::cout << "oddress = " << std::hex << address << std::dec << std::endl;
                        std::cout << "srcid   = " << p_vci_tgt.srcid.read() << std::endl;
                        exit(0);
                    }

                    r_tgt_addr = (((addr_40) ((p_vci_tgt.be.read() & 0x3) << 32)) | 
                                 ((addr_40) (p_vci_tgt.wdata.read()))) * m_dcache_words * 4;      
                    r_tgt_srcid = p_vci_tgt.srcid.read();
                    r_tgt_trdid = p_vci_tgt.trdid.read();
                    r_tgt_pktid = p_vci_tgt.pktid.read();
                 // r_tgt_plen  = p_vci_tgt.plen.read();
                    
                    PRINTF("    * <TGT> address : %llx\n",(uint64_t)address);
                    PRINTF("    * <TGT> address : %llx\n",(uint64_t)((((addr_40) ((p_vci_tgt.be.read() & 0x3) << 32)) | 
                                                                      ((addr_40) (p_vci_tgt.wdata.read()))) * m_dcache_words * 4));

                    if ( (address&0x3) == 0x3 )   // broadcast invalidate for data or instruction type
                    {
                        if ( not p_vci_tgt.eop.read() ) 
                        {
                            std::cout << "error in component VCI_CC_XCACHE_WRAPPER " << name() << std::endl;
                            std::cout << "the BROADCAST INVALIDATE command length must be one word" << std::endl;
                            exit(0);
                        }
                        r_tgt_update = false; 
                        // r_tgt_brdcast= true;
                        r_vci_tgt_fsm = TGT_REQ_BROADCAST;
                        m_cpt_cc_inval_broadcast++ ;
                    }
                    else                    // multi-update or multi-invalidate for data type
                    { 
                        uint32_t cell = address - m_segment.baseAddress(); // addr_40
                        // r_tgt_brdcast = false;
                        if (cell == 0) 
                        {                                       // invalidate data
                            if ( not p_vci_tgt.eop.read() ) 
                            {
                                std::cout << "error in component VCI_CC_XCACHE_WRAPPER " << name() << std::endl;
                                std::cout << "the MULTI-INVALIDATE command length must be one word" << std::endl;
                                exit(0);
                            }
                            r_tgt_update = false; 
                            r_vci_tgt_fsm = TGT_REQ_DCACHE;
                            m_cpt_cc_inval_dcache++ ;
                        } 
                        else if (cell == 4)                     // invalidate instruction
                        {                         
                            if ( not p_vci_tgt.eop.read() ) 
                            {
                                std::cout << "error in component VCI_CC_VCACHE_WRAPPER " << name() << std::endl;
                                std::cout << "the MULTI-INVALIDATE command length must be one word" << std::endl;
                                exit(0);
                            }
                            r_tgt_update = false; 
                            r_vci_tgt_fsm = TGT_REQ_ICACHE;
                            m_cpt_cc_inval_icache++ ;
                        } 
                        else if ( (cell == 8) or (cell==12) )    // update data or instruction
                        {                                
                            if ( p_vci_tgt.eop.read() ) 
                            {
                                std::cout << "error in component VCI_CC_VCACHE_WRAPPER " << name() << std::endl;
                                std::cout << "the MULTI-UPDATE command length must be N+2 words" << std::endl;
                                exit(0);
                            }
                            if(cell == 8)
                            {
                                m_cpt_cc_update_dcache++;
                                r_tgt_update_data = true;
                            }
                            else
                            {
                                m_cpt_cc_update_icache++;
                                r_tgt_update_data = false;
                            }
                            r_tgt_update = true; 
                            r_vci_tgt_fsm = TGT_UPDT_WORD;
                        } 

                    } // end if address
                } // end if cmdval
                break;

            case TGT_UPDT_WORD:
                if (p_vci_tgt.cmdval.read()) 
                {
                    if ( p_vci_tgt.eop.read() ) 
                    {
                        std::cout << "error in component VCI_CC_XCACHE_WRAPPER " << name() << std::endl;
                        std::cout << "the MULTI-UPDATE command length must be N+2 words" << std::endl;
                        exit(0);
                    }
                    for ( size_t i=0 ; i<m_dcache_words ; i++ ) r_tgt_be[i] = 0;
                    r_tgt_word = p_vci_tgt.wdata.read(); // the first modified word index
#ifdef COHERENCE_DEBUG
                    std::cout << "PROC " << m_srcid_rw << " update, line : " << std::hex << r_tgt_addr.read() << " word : " << p_vci_tgt.wdata.read() << std::dec << std::endl;
#endif
                    r_vci_tgt_fsm = TGT_UPDT_DATA;
                }
                break;

            case TGT_UPDT_DATA:
                if (p_vci_tgt.cmdval.read()) 
                {
                    size_t word = r_tgt_word.read();
                    if ( word >= m_cache_words ) 
                    {
                        std::cout << "error in component VCI_CC_XCACHE_WRAPPER " << name() << std::endl;
                        std::cout << "the reveived MULTI-UPDATE command length is wrong" << std::endl;
                        exit(0);
                    }
#ifdef COHERENCE_DEBUG
                    std::cout << "PROC " << m_srcid_rw << " update, data : " << p_vci_tgt.wdata.read() << " be : " << std::hex << p_vci_tgt.be.read() << std::dec << std::endl;
#endif

                    r_tgt_buf[word] = p_vci_tgt.wdata.read();
                    r_tgt_be [word] = p_vci_tgt.be.read();

                    if (p_vci_tgt.be.read())
                    {
                        if(r_tgt_update_data.read())
                            m_cpt_cc_update_dcache_word_useful++ ;
                        else
                            m_cpt_cc_update_icache_word_useful++ ;
                    }

                    r_tgt_word = word + 1;
                    if (p_vci_tgt.eop.read()){
#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE_OPT
                      uint32_t word=0;
                      for (; word<m_cache_words; ++word)
                          if (r_tgt_be[word] != 0)
                              break;
                      r_cache_word = word;
#endif
                      if(r_tgt_update_data.read()){
                        r_vci_tgt_fsm = TGT_REQ_DCACHE;
                      } else {
                        r_vci_tgt_fsm = TGT_REQ_ICACHE;
                      }
                    }
                }
                break;

            case TGT_REQ_BROADCAST:
                if ( not r_tgt_icache_req.read() and not r_tgt_dcache_req.read() ) 
                {
                    r_vci_tgt_fsm = TGT_RSP_BROADCAST; 
                    r_tgt_icache_req = true;
                    r_tgt_dcache_req = true;
                }
                break;
                ////////////////////
            case TGT_REQ_ICACHE:
                {
                    if ( not r_tgt_icache_req.read() ) 
                    {
                        r_vci_tgt_fsm = TGT_RSP_ICACHE; 
                        r_tgt_icache_req = true;
                    }
                    break;
                }

            case TGT_REQ_DCACHE:
                if ( not r_tgt_dcache_req.read() ) 
                {
                    r_vci_tgt_fsm = TGT_RSP_DCACHE; 
                    r_tgt_dcache_req = true;
                }
                break;

            case TGT_RSP_BROADCAST:
                if ( not r_tgt_icache_req.read() and not r_tgt_dcache_req.read() ) 
                {
                    // one response
                    if ( not r_tgt_icache_rsp or not r_tgt_dcache_rsp )
                    {
                        if ( p_vci_tgt.rspack.read() )
                        {
                            r_vci_tgt_fsm = TGT_IDLE; 
                            r_tgt_icache_rsp = false;
                            r_tgt_dcache_rsp = false;
                        }
                    }

                    // if data and instruction have the inval line, need two responses  
                    if ( r_tgt_icache_rsp and r_tgt_dcache_rsp )
                    {
                        if ( p_vci_tgt.rspack.read() )
                        {
                            r_tgt_icache_rsp = false; // only reset one for respond the second time 
                        }
                    }

                    // if there is no need for a response
                    if ( not r_tgt_icache_rsp and not r_tgt_dcache_rsp )
                    {
                        r_vci_tgt_fsm = TGT_IDLE;
                    }

                }
                break;
                ////////////////////
            case TGT_RSP_ICACHE:
                {
                    if ( (p_vci_tgt.rspack.read() or not r_tgt_icache_rsp.read()) and not r_tgt_icache_req.read() ) 
                    {
                        r_vci_tgt_fsm = TGT_IDLE;
                        r_tgt_icache_rsp = false; 
                    }
                    break;
                }

            case TGT_RSP_DCACHE:
                {
                    if ( (p_vci_tgt.rspack.read() or not r_tgt_dcache_rsp.read()) and not r_tgt_dcache_req.read() ) 
                    {
                        r_vci_tgt_fsm = TGT_IDLE;
                        r_tgt_dcache_rsp = false; 
                    }
                    break;
                }
        } // end switch TGT_FSM

        /////////////////////////////////////////////////////////////////////
        // The ICACHE FSM controls the following ressources:
        // - r_icache_fsm
        // - r_icache_fsm_save
        // - r_icache instruction cache access
        // - r_icache_addr_save
        // - r_icache_miss_req set
        // - r_icache_unc_req set
        // - r_icache_buf_unc_valid set
        // - r_vci_rsp_icache_miss_ok reset
        // - r_vci_rsp_ins_error reset
        // - r_tgt_icache_req reset
        // - ireq & irsp structures for communication with the processor
        //
        // 1/ External requests (update or invalidate) have highest priority.
        //    They are taken into account in the IDLE and WAIT states.
        //    As external hit should be extremly rare on the ICACHE,
        //    all external requests are handled as invalidate...
        //    In case of external request the ICACHE FSM goes to the CC_CHECK
        //    state to test the external hit, and returns in the
        //    pre-empted state after completion.
        // 2/ Processor requests are taken into account only in the IDLE state.
        //    In case of MISS, or in case of uncached instruction, the FSM 
        //    writes the missing address line in the  r_icache_addr_save register 
        //    and sets the r_icache_miss_req or the r_icache_unc_req flip-flops.
        //    These request flip-flops are reset by the VCI_RSP FSM
        //    when the VCI transaction is completed and the r_icache_buf_unc_valid
        //    is set in case of uncached access.
        //    In case of bus error, the VCI_RSP FSM sets the r_vci_rsp_ins_error 
        //    flip-flop. It is reset by the ICACHE FSM.
        ///////////////////////////////////////////////////////////////////////

        typename iss_t::InstructionRequest  ireq = ISS_IREQ_INITIALIZER;
        typename iss_t::InstructionResponse irsp = ISS_IRSP_INITIALIZER;

        typename iss_t::DataRequest         dreq = ISS_DREQ_INITIALIZER;
        typename iss_t::DataResponse        drsp = ISS_DRSP_INITIALIZER;

        m_iss.getRequests( ireq, dreq );

#if CC_XCACHE_WRAPPER_DEBUG
        if (m_cpt_total_cycles>=CC_XCACHE_WRAPPER_DEBUG_CYCLE_MIN)
            std::cout << "    * Instruction Request  : " << ireq << std::endl;
#endif

        switch(r_icache_fsm) {
            /////////////////
            case ICACHE_IDLE:
                {
                    if ( r_tgt_icache_req ) {   // external request
                        if ( ireq.valid ) m_cost_ins_miss_frz++;
                        r_icache_fsm = ICACHE_CC_CHECK;
                        r_icache_fsm_save = r_icache_fsm.read();
                        break;
                    } 
                    if ( ireq.valid ) {
                        data_t  icache_ins = 0;
                        bool    icache_hit = false;
                        bool    icache_cached = m_cacheability_table[(vci_addr_t)ireq.addr];
                        bool    icache_cleanup_hit = r_icache_cleanup_req and (((addr_40)ireq.addr >> (addr_40)m_icache_words_shift) == r_icache_cleanup_line.read());

                        // icache_hit & icache_ins evaluation
                        if ( icache_cached ) {
                            icache_hit = r_icache.read((vci_addr_t) ireq.addr, &icache_ins);
                        } else {
                            // if uncache, again in the icache_miss_buf
                            icache_hit = ( r_icache_buf_unc_valid and ((addr_40) ireq.addr == (addr_40)r_icache_addr_save));
                            icache_ins = CACHE_MISS_BUF_RSP_DATA(i,0);
                            CACHE_MISS_BUF_RSP_POP(i);
                        }

                        PRINTF("    * <ICACHE> hit %d - cached %d - cleanup_hit %d\n",icache_hit, icache_cached, icache_cleanup_hit);

                        ASSERT( not (icache_hit and icache_cleanup_hit),
                                "Icache hit and icache_cleanup_hit");

                        if ( not icache_hit and not icache_cleanup_hit) {
                            m_cpt_ins_miss++;
                            m_cost_ins_miss_frz++;
                            r_icache_addr_save = (addr_40) ireq.addr;

                            CACHE_MISS_BUF_REQ_INIT(i);

                            if ( icache_cached ) {
#if CC_XCACHE_WRAPPER_SELECT_VICTIM
                                r_icache_fsm = ICACHE_MISS_VICTIM;
#else
                                r_icache_fsm = ICACHE_MISS_WAIT;
#endif
                                r_icache_miss_req = true;
                               
                            } else {
                                r_icache_fsm = ICACHE_UNC_WAIT;
                                r_icache_unc_req = true;
                            }
                        } else {
                            r_icache_buf_unc_valid = false;
                        }
                        m_cpt_icache_dir_read += m_icache_ways;
                        m_cpt_icache_data_read += m_icache_ways;
                        irsp.valid          = icache_hit;
                        irsp.instruction    = icache_ins;
                    }
                    break;
                }
                //////////////////////
#if CC_XCACHE_WRAPPER_SELECT_VICTIM
            case ICACHE_MISS_VICTIM:
                {
                    if (not r_icache_cleanup_req)
                    {
                        size_t     way;
                        size_t     set;
                        vci_addr_t addr = (vci_addr_t) r_icache_addr_save.read();
                        vci_addr_t victim;
                        
                        r_icache_cleanup_req  = r_icache.victim_select(addr, &victim, &way, &set );
                        r_icache_cleanup_line = (addr_40) victim;
                        r_icache_miss_way     = way;
                        r_icache_miss_set     = set;
                        
                        r_icache_fsm = ICACHE_MISS_WAIT;
                    }
                    break;
                }
#endif
                //////////////////////
            case ICACHE_MISS_WAIT:
                {
                    m_cost_ins_miss_frz++;
                    if ( r_tgt_icache_req ) {   // external request
                        r_icache_fsm = ICACHE_CC_CHECK;
                        r_icache_fsm_save = r_icache_fsm.read();
                        break;
                    }

                    bool val = CACHE_MISS_BUF_RSP_VAL(i,0);

                    PRINTF("    * <ICACHE> val                  : %d\n",val);

                    if (val)
                    {
                        PRINTF("    * <ICACHE> r_icache_inval_rsp   : %d\n",(int) r_icache_inval_rsp  );
                        PRINTF("    * <ICACHE> r_vci_rsp_ins_error  : %d\n",(int) r_vci_rsp_ins_error );
                        PRINTF("    * <ICACHE> r_icache_cleanup_req : %d\n",(int) r_icache_cleanup_req);

                        // if (not r_icache_inval_rsp )
                        // {
                            // Miss read response and no invalidation
                            if ( r_vci_rsp_ins_error ) {
                                r_icache_fsm = ICACHE_ERROR;
                            } else {
#if not CC_XCACHE_WRAPPER_SELECT_VICTIM
                                if (not r_icache_cleanup_req.read())
#endif
                                {
                                    r_icache_update_addr = 0;
                                    r_icache_fsm         = ICACHE_MISS_UPDT;
                                }
                            }
                        // }
                        // else
                        // {
                        //     r_icache_inval_rsp = false;
                            
                        //     // Miss read response and invalidation
                        //     if ( r_vci_rsp_ins_error )
                        //         r_icache_fsm = ICACHE_ERROR;
                        //     else
                        //         r_icache_fsm = ICACHE_CC_CLEANUP;
                        // }
                    }
                    break;
                }
                /////////////////////
            case ICACHE_UNC_WAIT:
                {
                    m_cost_ins_miss_frz++;
                    if ( r_tgt_icache_req ) {   // external request
                        r_icache_fsm = ICACHE_CC_CHECK;
                        r_icache_fsm_save = r_icache_fsm.read();
                        break;
                    }

                    bool ok = CACHE_MISS_BUF_RSP_VAL(i,0);

                    if (ok)
                    {
                        if ( r_vci_rsp_ins_error ) {
                            r_icache_fsm = ICACHE_ERROR;
                        } else {
                            r_icache_fsm = ICACHE_IDLE;
                            r_icache_buf_unc_valid = true;
                        }
                    }
                    break;
                }
                //////////////////
            case ICACHE_ERROR:
                {
                    if ( (addr_40)ireq.addr == (addr_40)r_icache_addr_save ) {
                        irsp.error          = true;
                        irsp.valid          = true;
                    }
                    r_icache_fsm = ICACHE_IDLE;
                    r_vci_rsp_ins_error = false;
                    break;
                }
                //////////////////////
            case ICACHE_MISS_UPDT: 
                {
                    size_t     word = r_icache_update_addr.read();
                    vci_addr_t addr = (vci_addr_t) r_icache_addr_save.read();
                    size_t     way  = 0;
                    size_t     set  = 0;

                    // need invalid rsp, don't select a victim
                    if (not r_icache_inval_rsp )
                    {
#if CC_XCACHE_WRAPPER_SELECT_VICTIM
                        way = r_icache_miss_way.read();
                        set = r_icache_miss_set.read();
#else

                        // First word : select an victim !
                        if (word == 0)
                        {
                            vci_addr_t victim;
                        
                            // r_icache_cleanup_req is false because is the transition condition to go in ICACHE_MISS_UPDT state
                            r_icache_cleanup_req  = r_icache.victim_select(addr, &victim, &way, &set );
                            r_icache.victim_update_tag(addr, way, set);
                        
                            r_icache_cleanup_line = (addr_40) victim;
                            r_icache_miss_way     = way;
                            r_icache_miss_set     = set;
                        }
                        else
                        {
                            way = r_icache_miss_way.read();
                            set = r_icache_miss_set.read();
                        }
#endif
                    }
                    bool val = CACHE_MISS_BUF_RSP_VAL(i,word);

                    if (val)
                    {
                        PRINTF("    * <ICACHE> rsp_val            : %d/%d\n",(int)r_icache_update_addr,m_icache_words);
                        PRINTF("    * <ICACHE> r_icache_inval_rsp : %d\n",(int)r_icache_inval_rsp);
                        PRINTF("    * <ICACHE> ins : %x\n",(int)CACHE_MISS_BUF_RSP_DATA(i,word));

                        // m_cpt_icache_dir_write++;
                        // m_cpt_icache_data_write++;
                        // if ( ireq.valid ) m_cost_ins_miss_frz++;

                        // if need invalid rsp, don't modify the cache, but pop the buf_rsp
                        if (not r_icache_inval_rsp )
                            r_icache.write(way, set, word, CACHE_MISS_BUF_RSP_DATA(i,word));
                        CACHE_MISS_BUF_RSP_POP(i);

                        r_icache_update_addr = ++word;
                            
                        // if last word, finish the update
                        if (word >= m_icache_words)
                        {
                            // Last word : if previous invalid_rsp, can cleanup, else update the TAG
                            if (r_icache_inval_rsp)
                            {
                                r_icache_inval_rsp  = false;
                                r_icache_fsm = ICACHE_CC_CLEANUP;
                            }
                            else
                            {
#if CC_XCACHE_WRAPPER_SELECT_VICTIM
                                r_icache.victim_update_tag(addr, way, set);
#endif
                                r_icache_fsm = ICACHE_IDLE;
                            }
                        }
                    }

                    break;
                }
                ////////////////////
            case ICACHE_CC_CLEANUP:
                {
                    // external cache invalidate request
                    if ( r_tgt_icache_req )     
                    {
                        r_icache_fsm = ICACHE_CC_CHECK;
                        r_icache_fsm_save = r_icache_fsm.read();
                        break;
                    }
                    // cleanup
                    if(not r_icache_cleanup_req){
                        r_icache_cleanup_req = true;
                        r_icache_cleanup_line = r_icache_addr_save.read() >> m_icache_words_shift;
                        r_icache_fsm = ICACHE_IDLE;
                    }
                    break;
                }
                /////////////////////
            case ICACHE_CC_CHECK:   // read directory in case of invalidate or update request
                {

                    m_cpt_icache_dir_read  += m_icache_ways;
                    m_cpt_icache_data_read += m_icache_ways;
                    addr_40 ad = r_tgt_addr;
                    data_t  icache_rdata = 0;

                    if((r_icache_fsm_save == ICACHE_MISS_WAIT) and 
                            ( (r_icache_addr_save.read() & ~((m_icache_words<<2)-1)) == (ad & ~((m_icache_words<<2)-1)))) {
                        r_icache_inval_rsp = true;
                        r_tgt_icache_req = false;
                        if(r_tgt_update){    // Also send a cleanup and answer
                            r_tgt_icache_rsp     = true;
                        } else {            // Also send a cleanup but don't answer
                            r_tgt_icache_rsp     = false;
                        }
                        r_icache_fsm = r_icache_fsm_save;
                    } else {
                        bool    icache_hit   = r_icache.read(ad, &icache_rdata);
                        if ( icache_hit and r_tgt_update ) 
                        {
#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
                            uint32_t word  = r_cache_word;
                            data_t   mask  = vci_param::be2mask(r_tgt_be[word]);
                            data_t   rdata = 0;

                            r_icache.read(ad+word*4,&rdata);
                            r_tgt_buf[word] = (mask & r_tgt_buf[word]) | (~mask & rdata);
                            
                            word ++;
#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE_OPT
                            for (; word<m_icache_words; ++word)
                                if (r_tgt_be[word] != 0)
                                    break;
#endif

                            if (word==m_icache_words)
                            {
                                r_icache_fsm = ICACHE_CC_UPDT;

#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE_OPT
                                for (word=0; word<m_icache_words; ++word)
                                    if (r_tgt_be[word] != 0)
                                        break;
#else
                                word = 0;
#endif
                            }
                            r_cache_word = word;
#else //CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
                            r_icache_fsm = ICACHE_CC_UPDT;
                            // complete the line buffer in case of update
                            for(size_t i=0; i<m_icache_words; i++){
                                data_t rdata = 0;
                                r_icache.read(ad + i*4,&rdata);
                                data_t mask = vci_param::be2mask(r_tgt_be[i]);
                                r_tgt_buf[i] = (mask & r_tgt_buf[i]) | (~mask & rdata);
                            }
#endif //CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
                        } else if ( icache_hit and not r_tgt_update ) {
                            r_icache_fsm = ICACHE_CC_INVAL;
                        } else { // instruction not found (can happen)
                            r_tgt_icache_req = false;
                            if(r_tgt_update){
                                r_tgt_icache_rsp = true;
                            } else {
                                r_tgt_icache_rsp = false;
                            }
                            r_icache_fsm = r_icache_fsm_save;
                        }
                    }
                    break;
                }
                /////////////////////
            case ICACHE_CC_INVAL:  
                {                       
                    addr_40    ad  = r_tgt_addr;
                    if ( ireq.valid ) m_cost_ins_miss_frz++;
                    m_cpt_icache_dir_read += m_icache_ways;
                    r_tgt_icache_rsp = true;
                    r_icache.inval(ad);
                    r_tgt_icache_req = false;
                    r_icache_fsm = r_icache_fsm_save;
                    break;
                }    
                /////////////////////
            case ICACHE_CC_UPDT:
                {                       
                    addr_40 ad = r_tgt_addr.read();
                    m_cpt_icache_dir_write++;
                    m_cpt_icache_data_write++;

#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
                    uint32_t word  = r_cache_word;

                    if(r_tgt_be[word])
                        r_icache.write(ad+word*4, r_tgt_buf[word]);

                    word ++;
#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE_OPT
                    for (; word<m_icache_words; ++word)
                        if (r_tgt_be[word] != 0)
                            break;
#endif

                    if (word==m_icache_words)
                    {
                        r_tgt_icache_req = false;
                        r_tgt_icache_rsp = true;
                        r_icache_fsm     = r_icache_fsm_save.read();
                        word = 0;
                    }
                    r_cache_word = word;
#else //CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
                    data_t* buf    = r_tgt_buf;
                    for(size_t i=0; i<m_icache_words;i++){
                        if(r_tgt_be[i]) r_icache.write( ad + i*4, buf[i]);
                    }
                    r_tgt_icache_req = false;
                    r_tgt_icache_rsp = true;
                    r_icache_fsm     = r_icache_fsm_save.read();
#endif //CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE

                    break;
                }    

        } // end switch r_icache_fsm

#if CC_XCACHE_WRAPPER_DEBUG
        if (m_cpt_total_cycles>=CC_XCACHE_WRAPPER_DEBUG_CYCLE_MIN)
            std::cout << "    * Instruction Response : " << irsp << std::endl;
#endif

        //////////////////////////////////////////////////////////////////////://///////////
        // The DCACHE FSM controls the following ressources:
        // - r_dcache_fsm
        // - r_dcache_fsm_save
        // - r_dcache (data cache access)
        // - r_dcache_addr_save
        // - r_dcache_wdata_save
        // - r_dcache_rdata_save
        // - r_dcache_type_save
        // - r_dcache_be_save
        // - r_dcache_cached_save
        // - r_dcache_miss_req set
        // - r_dcache_unc_req set
        // - r_dcache_cleanup_req set
        // - r_vci_rsp_data_error reset
        // - r_tgt_dcache_req reset
        // - r_wbuf write
        // - dreq & drsp structures for communication with the processor
        //
        // 1/ EXTERNAL REQUEST : 
        //    There is an external request when the tgt_dcache req flip-flop is set,
        //    requesting a line invalidation or a line update. 
        //    External requests are taken into account in the states  IDLE, WRITE_REQ,  
        //    UNC_WAIT, MISS_WAIT, and have the highest priority :
        //    The actions associated to the pre-empted state are not executed, the DCACHE FSM
        //    goes to the CC_CHECK state to execute the requested action, and returns to the
        //    pre-empted state.
        //  2/ PROCESSOR REQUEST : 
        //   In order to support VCI write burst, the processor requests are taken into account
        //   in the WRITE_REQ state as well as in the IDLE state.
        //   - In the IDLE state, the processor request cannot be satisfied if
        //   there is a cached read miss, or an uncached read.
        //   - In the WRITE_REQ state, the request cannot be satisfied if
        //   there is a cached read miss, or an uncached read,
        //   or when the write buffer is full.
        //   - In all other states, the processor request is not satisfied.
        //
        //   The cache access takes into account the cacheability_table.
        //   In case of processor request, there is five conditions to exit the IDLE state:
        //   - CACHED READ MISS => to the MISS_WAIT state (waiting the r_miss_ok signal), 
        //     then to the MISS_UPDT state, and finally to the IDLE state.
        //   - UNCACHED READ  => to the UNC_WAIT state (waiting the r_miss_ok signal), 
        //     and to the IDLE state.
        //   - CACHE INVALIDATE HIT => to the INVAL state for one cycle, then to IDLE state.
        //   - WRITE MISS => directly to the WRITE_REQ state to access the write buffer.
        //   - WRITE HIT => to the WRITE_UPDT state, then to the WRITE_REQ state.
        //
        // Error handling :  Read Bus Errors are synchronous events, but
        // Write Bus Errors are asynchronous events (processor is not frozen).
        // - If a Read Bus Error is detected, the VCI_RSP FSM sets the
        //   r_vci_rsp_data_error flip-flop, and the synchronous error is signaled
        //   by the DCACHE FSM.
        // - If a Write Bus Error is detected, the VCI_RSP FSM  signals
        //   the asynchronous error using the setWriteBerr() method.
        ///////////////////////////////////////////////////////////////////////////////////

#if CC_XCACHE_WRAPPER_DEBUG
        if (m_cpt_total_cycles>=CC_XCACHE_WRAPPER_DEBUG_CYCLE_MIN)
            std::cout << "    * Data        Request  : " << dreq << std::endl;
#endif

        switch ( r_dcache_fsm ) {

                /////////////////
            case DCACHE_IDLE:
                {
                    if ( r_tgt_dcache_req ) {   // external request
                        r_dcache_fsm = DCACHE_CC_CHECK;
                        r_dcache_fsm_save = r_dcache_fsm;
                        break;
                    } 

                    if ( dreq.valid ) {
                        PRINTF("    * <DCACHE> Have dreq\n");

                        data_t      dcache_rdata       = 0;
                        // dcache_cached and dcache_hit don't used with dreq.type == {DATA_SC, XTN_READ, XTN_WRITE}
                        bool        dcache_cached      = m_cacheability_table[(vci_addr_t)dreq.addr];
                        bool        dcache_hit         = r_dcache.read((vci_addr_t) dreq.addr, &dcache_rdata);
                        bool        dcache_cleanup_hit = r_dcache_cleanup_req and (((addr_40)dreq.addr >> (addr_40)m_dcache_words_shift) == r_dcache_cleanup_line.read());

                        PRINTF("    * <DCACHE> hit %d - cached %d - cleanup_hit %d\n",dcache_hit, dcache_cached, dcache_cleanup_hit);
                        
                        m_cpt_dcache_data_read += m_dcache_ways;
                        m_cpt_dcache_dir_read  += m_dcache_ways;

                        switch( dreq.type ) {
                            case iss_t::DATA_READ:
                            case iss_t::DATA_LL:
                                {
                                    m_cpt_data_read++; // new dcache read

                                    if (dcache_hit) // no special test for uncached read, because it's always miss
                                        {
                                            // address is in the cache : return the word
                                            r_dcache_fsm = DCACHE_IDLE;
                                            drsp.valid   = true;
                                            drsp.rdata   = dcache_rdata; // return read data (cf dcache_hit)
                                            
                                            // if the request is a Load Linked instruction, save request information
                                            if(dreq.type == iss_t::DATA_LL)
                                                {
                                                    PRINTF("    * <DCACHE> ll_valid = true\n");

                                                    r_dcache_ll_valid = true;
                                                    r_dcache_ll_data = dcache_rdata;
                                                    r_dcache_ll_addr = (vci_addr_t) dreq.addr;
#ifdef COHERENCE_DEBUG
                                                    std::cout << "Value returned for LL at address : " << std::hex << dreq.addr << " data : " << std::dec << dcache_rdata<< std::endl;
                                                    r_dcache.read((vci_addr_t) dreq.addr, &dcache_rdata);
                                                    std::cout << "Value stored at this  address : " << std::hex << dreq.addr << " data : " << std::dec << dcache_rdata<< std::endl;
#endif
                                                }
                                        }
                                    else
                                        {
                                            if (not dcache_cleanup_hit)
                                            {
                                                CACHE_MISS_BUF_REQ_INIT(d);
                                                
                                                // Miss : send signal at the CMD_FSM (via r_dcache_miss_req or r_dcache_unc_req)
                                                if ( dcache_cached ) {
                                                    m_cpt_data_read_miss++;
                                                    m_cost_data_miss_frz++;
                                                    r_dcache_miss_req = true;
#if CC_XCACHE_WRAPPER_SELECT_VICTIM
                                                    r_dcache_fsm = DCACHE_MISS_VICTIM;
#else
                                                    r_dcache_fsm = DCACHE_MISS_WAIT;
#endif
                                                    
                                                } else {
                                                    if (not r_dcache_previous_unc.read()) // strongly order to the uncached access
                                                    {
                                                        r_dcache_previous_unc = true;
                                                        
                                                        m_cpt_data_read_uncached++;
                                                        m_cost_unc_read_frz++;
                                                        r_dcache_unc_req = true;
                                                        r_dcache_fsm = DCACHE_UNC_WAIT;
                                                    }
                                                }
                                            }
                                        }
                                }
                                break;
                            case iss_t::DATA_SC:
                            {
                                PRINTF("    * <DCACHE> DATA_SC - ll_valid = %d\n",r_dcache_ll_valid.read());

                                if (not r_dcache_previous_unc.read() and not dcache_cleanup_hit) // strongly order to the uncached access
                                {
                                    //m_cpt_data_read_unc++; // instruction must read the memory in uncached mode
                                    m_cost_unc_read_frz++;
                                    
                                    // if previous load linked (with the same address), make a transaction
                                    // else, keep in IDLE state and return 1 (no OK)
                                    if(r_dcache_ll_valid.read() and (r_dcache_ll_addr.read() == (vci_addr_t)dreq.addr)){
                                        PRINTF("    * <DCACHE> have previous load linked\n");
                                        
                                        r_dcache_previous_unc = true;

                                        r_dcache_sc_req   = true;

                                        CACHE_MISS_BUF_REQ_INIT(d);

                                        r_dcache_fsm = DCACHE_SC_WAIT;
                                    } else {
                                        PRINTF("    * <DCACHE> don't have previous load linked\n");
                                        
                                        drsp.valid = true;
                                        drsp.rdata = 1; // SC rsp NOK
                                        r_dcache_ll_valid = false;
                                    }
                                }

                                break;
                            }
                            case iss_t::XTN_READ:
                            case iss_t::XTN_WRITE:
                                {
                                    bool drsp_valid = false;
                                    // only DCACHE INVALIDATE and SYNC request are supported
                                    switch (dreq.addr>>2)
                                        {
                                        case iss_t::XTN_DCACHE_INVAL : 
                                            {
                                                drsp_valid = true;
                                                r_dcache_fsm = DCACHE_INVAL;
                                                break;
                                            }
                                        case iss_t::XTN_SYNC :
                                            {
                                                // Test if write buffer is already empty
                                                //  * gain : 1 cycle
                                                //  * cost : can be on the critical path
                                                if (r_wbuf.empty())
                                                    {
                                                        drsp_valid = true;
                                                        r_dcache_fsm = DCACHE_IDLE;
                                                    }
                                                else
                                                    {
                                                        drsp_valid = false;
                                                        r_dcache_fsm = DCACHE_SYNC;
                                                    }
                                                break;
                                            }
                                        default :
                                            {
                                                // std::cout << "Warning in VCI_CC_XCACHE_WRAPPER " << name() << std::endl;
                                                // std::cout << "Unsupported  external access : " << (dreq.addr>>2) << std::endl;

                                                r_dcache_fsm = DCACHE_IDLE;
                                            }
                                        }//end switch (dreq.addr>>2)

                                    drsp.valid = drsp_valid;
                                    drsp.rdata = 0;
                                    break;
                                }
                            case iss_t::DATA_WRITE:

                                if (dcache_cached or not r_dcache_previous_unc.read()) // strongly order to the uncached access
                                {
                                    bool drsp_valid;

                                    drsp_valid = r_wbuf.write((addr_40) dreq.addr, dreq.be, dreq.wdata, dcache_cached);

                                    if (drsp_valid)
                                    {
                                        m_cpt_data_write++;
                                        
                                        if (not dcache_cached)
                                        {
                                            r_dcache_previous_unc = true;
                                            m_cpt_data_write_uncached++;
                                        }
                                        else if (not dcache_hit)
                                            m_cpt_data_write_miss++;
                                        
                                        if ( dcache_hit) {
                                            r_dcache_fsm = DCACHE_WRITE_UPDT;
                                        } else {
                                            r_dcache_fsm = DCACHE_IDLE;
                                        }
                                    }

                                    drsp.valid = drsp_valid;
                                    drsp.rdata = 0;
                                }
                                break;
                        } // end switch dreq.type

                        r_dcache_addr_save      = (addr_40) dreq.addr;
                        r_dcache_type_save      = dreq.type;
                        r_dcache_wdata_save     = dreq.wdata;
                        r_dcache_be_save        = dreq.be;
                        r_dcache_rdata_save     = dcache_rdata;
                        r_dcache_cached_save    = dcache_cached;
                        
                    } else {    // end if dreq.valid
                        r_dcache_fsm = DCACHE_IDLE;
                    }
                    
                    break;
                }
                ///////////////////////
            case DCACHE_WRITE_UPDT:
                {
                    m_cpt_dcache_data_write++;
                    data_t mask = vci_param::be2mask(r_dcache_be_save);
                    data_t wdata = (mask & r_dcache_wdata_save) | (~mask & r_dcache_rdata_save);
                    vci_addr_t ad = r_dcache_addr_save.read();
                    r_dcache.write(ad, wdata);

                    r_dcache_fsm = DCACHE_IDLE;

                    break;
                }
                //////////////////////
#if CC_XCACHE_WRAPPER_SELECT_VICTIM
            case DCACHE_MISS_VICTIM:
                {
                    if (not r_dcache_cleanup_req.read())
                     {
                         size_t     way;
                         size_t     set;
                         vci_addr_t addr = (vci_addr_t) r_dcache_addr_save.read();
                         vci_addr_t victim;
                         
                         r_dcache_cleanup_req  = r_dcache.victim_select(addr, &victim, &way, &set );
                         r_dcache_cleanup_line = (addr_40) victim;
                         r_dcache_miss_way     = way;
                         r_dcache_miss_set     = set;
                         
                         r_dcache_fsm = DCACHE_MISS_WAIT;
                     }
                    
                    break;
                }
#endif
                //////////////////////
            case DCACHE_MISS_WAIT:
                {

                    if ( dreq.valid ) m_cost_data_miss_frz++;
                    if ( r_tgt_dcache_req.read() ) {   // external request
                        r_dcache_fsm = DCACHE_CC_CHECK;
                        r_dcache_fsm_save = r_dcache_fsm;
                        break;
                    }

                    bool val = CACHE_MISS_BUF_RSP_VAL(d,0);
                    if (val)
                    {
                        // if (not r_dcache_inval_rsp )
                        //  {

                        // Miss read response and no invalidation
                        if ( r_vci_rsp_data_error )
                        {
                            r_dcache_fsm = DCACHE_ERROR;
                        } 
                        else 
                        {
#if not CC_XCACHE_WRAPPER_SELECT_VICTIM
                            if (not r_dcache_cleanup_req.read())
#endif
                            {
                                r_dcache_update_addr = 0;
                                r_dcache_fsm         = DCACHE_MISS_UPDT;
                            }
                        }
                        //  }
                        // else
                        // {
                        //     r_dcache_inval_rsp  = false;

                        //     // Miss read response and invalidation
                        //     if ( r_vci_rsp_data_error ) {
                        //         r_dcache_fsm = DCACHE_ERROR;
                        //     } else {
                        //         r_dcache_fsm = DCACHE_CC_CLEANUP;
                        //     }
                        // }
                    }
                    break;
                }
                //////////////////////
            case DCACHE_MISS_UPDT:
                {
                    size_t     word = r_dcache_update_addr.read();
                    vci_addr_t addr = (vci_addr_t) r_dcache_addr_save.read();
                    size_t     way  = 0;
                    size_t     set  = 0;
                    
                    // need invalid rsp, don't select a victim
                    if (not r_dcache_inval_rsp )
                    {
#if CC_XCACHE_WRAPPER_SELECT_VICTIM
                        way = r_dcache_miss_way.read();
                        set = r_dcache_miss_set.read();
#else
                        // First word : select an victim !
                        if (word == 0)
                        {
                            vci_addr_t victim;
                        
                            // r_dcache_cleanup_req is false (condition to enter in DCACHE_MISS_UPDT
                            r_dcache_cleanup_req  = r_dcache.victim_select(addr, &victim, &way, &set );
                            r_dcache.victim_update_tag(addr, way, set);
                            r_dcache_cleanup_line = (addr_40) victim;

                            r_dcache_miss_way     = way;
                            r_dcache_miss_set     = set;
                        }
                        else
                        {
                            way = r_dcache_miss_way.read();
                            set = r_dcache_miss_set.read();
                        }
#endif
                    }

                    bool val = CACHE_MISS_BUF_RSP_VAL(d,word);
                    if (val)
                    {
                        // m_cpt_dcache_dir_write++;
                        // if ( ireq.valid ) m_cost_data_miss_frz++;

                        // if need invalid rsp, don't modify the cache, but pop the buf_rsp
                        if (not r_dcache_inval_rsp )
                        {
                            r_dcache.write(way, set, word, CACHE_MISS_BUF_RSP_DATA(d,word));
                            m_cpt_dcache_data_write++;
                        }

                        CACHE_MISS_BUF_RSP_POP(d);
                        r_dcache_update_addr = ++word;
                            
                        // if last word, finish the update
                        if (word >= m_dcache_words)
                        {
                            // Last word : if previous invalid_rsp, can cleanup, else update the TAG
                            if (r_dcache_inval_rsp)
                            {
                                r_dcache_inval_rsp  = false;
                                r_dcache_fsm = DCACHE_CC_CLEANUP;
                            }
                            else
                            {
#if CC_XCACHE_WRAPPER_SELECT_VICTIM
                                r_dcache.victim_update_tag(addr, way, set);
#endif
                                r_dcache_fsm = DCACHE_IDLE;
                            }
                        }
                    }
                
                    break;
                }
                ////////////////////
            case DCACHE_UNC_WAIT:
                {
                    if ( dreq.valid ) m_cost_unc_read_frz++;
                    if ( r_tgt_dcache_req ) {   // external request
                        r_dcache_fsm = DCACHE_CC_CHECK;
                        r_dcache_fsm_save = r_dcache_fsm;
                        break;
                    } 

                    bool ok = CACHE_MISS_BUF_RSP_VAL(d,0);

                    if (ok) {
                        if ( r_vci_rsp_data_error ) {
                            r_dcache_fsm = DCACHE_ERROR;
                        } else {
                            data_t rdata = CACHE_MISS_BUF_RSP_DATA(d,0);
                            CACHE_MISS_BUF_RSP_POP(d);

                            if(dreq.type == iss_t::DATA_LL){
                                PRINTF("    * <DCACHE> ll_valid = true\n");

                                r_dcache_ll_valid = true;
                                r_dcache_ll_data = rdata;
                                r_dcache_ll_addr = (vci_addr_t) dreq.addr;
                            }
                            r_dcache_fsm = DCACHE_IDLE;
                            drsp.valid = true;
                            drsp.rdata = rdata;
                        }
                    }
                    break;
                }
                ////////////////////
            case DCACHE_SC_WAIT:
                {
                    if ( dreq.valid ) m_cost_unc_read_frz++;
                    if ( r_tgt_dcache_req ) {   // external request
                        r_dcache_fsm = DCACHE_CC_CHECK;
                        r_dcache_fsm_save = r_dcache_fsm;
                        break;
                    } 

                    bool ok = CACHE_MISS_BUF_RSP_VAL(d,0);

                    if (ok) {
                        if ( r_vci_rsp_data_error ) {
                            r_dcache_fsm = DCACHE_ERROR;
                        } else {
                            r_dcache_fsm = DCACHE_IDLE;
                            drsp.valid = true;
                            drsp.rdata = CACHE_MISS_BUF_RSP_DATA(d,0);
                            CACHE_MISS_BUF_RSP_POP(d);
                            r_dcache_ll_valid = false;
                        }
                    }
                    break;
                }

                //////////////////
            case DCACHE_ERROR:
                {
                    r_dcache_fsm = DCACHE_IDLE;
                    r_vci_rsp_data_error = false;
                    drsp.error = true;
                    drsp.valid = true;
                    break;
                }
                /////////////////    
            case DCACHE_INVAL:
                {
                    if ( r_tgt_dcache_req.read() ) {   // external request
                        r_dcache_fsm = DCACHE_CC_CHECK;
                        r_dcache_fsm_save = r_dcache_fsm;
                        break;
                    }
                    if( not r_dcache_cleanup_req.read() ){
                        m_cpt_dcache_dir_read += m_dcache_ways;
                        vci_addr_t  ad  = r_dcache_addr_save.read();
                        r_dcache_cleanup_req = r_dcache.inval(ad);
                        r_dcache_cleanup_line = r_dcache_addr_save.read() >> m_dcache_words_shift;

                        r_dcache_fsm = DCACHE_IDLE;
                    }
                    break;
                }
            case DCACHE_SYNC :
                {
                    if ( r_tgt_dcache_req ) {   // external request
                        r_dcache_fsm = DCACHE_CC_CHECK;
                        r_dcache_fsm_save = r_dcache_fsm;
                        break;
                    } 

                    if (r_wbuf.empty())
                        {
                            drsp.valid = true; // end, can accept the sync request
                            r_dcache_fsm = DCACHE_IDLE;
                        }
                    break;
                }
                /////////////////////
            case DCACHE_CC_CHECK:   // read directory in case of invalidate or update request
                {
                    addr_40  ad          = r_tgt_addr;
                    data_t  dcache_rdata = 0;

                    if((r_dcache_fsm_save == DCACHE_MISS_WAIT) and 
                            ( (r_dcache_addr_save.read() & ~((m_dcache_words<<2)-1)) == (ad & ~((m_dcache_words<<2)-1)))) {
                        r_dcache_inval_rsp = true;
                        r_tgt_dcache_req = false;
                        if(r_tgt_update){    // Also send a cleanup and answer
                            r_tgt_dcache_rsp     = true;
                        } else {            // Also send a cleanup but don't answer
                            r_tgt_dcache_rsp     = false;
                        }
                        r_dcache_fsm = r_dcache_fsm_save;
                    } else {
                        bool    dcache_hit   = r_dcache.read(ad, &dcache_rdata);

                        m_cpt_dcache_data_read += m_dcache_ways;
                        m_cpt_dcache_dir_read += m_dcache_ways;

#ifdef COHERENCE_DEBUG
                        std::cout << "PROC " << m_srcid_rw << " DCACHE_CC_CHECK, hit ? : " << dcache_hit << std::endl;
#endif
                        if ( dcache_hit and r_tgt_update ) 
                        {
#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
                            uint32_t word  = r_cache_word;
                            data_t   mask  = vci_param::be2mask(r_tgt_be[word]);
                            data_t   rdata = 0;

                            r_dcache.read(ad+word*4,&rdata);
                            
                            r_tgt_buf[word] = (mask & r_tgt_buf[word]) | (~mask & rdata);

                            word ++;
#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE_OPT
                            for (; word<m_dcache_words; ++word)
                                if (r_tgt_be[word] != 0)
                                    break;
#endif

                            if (word==m_dcache_words)
                            {
                                r_dcache_fsm = DCACHE_CC_UPDT;
#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE_OPT
                                for (word=0; word<m_dcache_words; ++word)
                                    if (r_tgt_be[word] != 0)
                                        break;
#else
                                word = 0;
#endif
                            }
                            r_cache_word = word;
#else //CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
                            // complete the line buffer in case of update
                            for(size_t i=0; i<m_dcache_words; i++){
                                data_t rdata = 0;
                                r_dcache.read(ad + i*4,&rdata);
                                data_t mask = vci_param::be2mask(r_tgt_be[i]);
                                r_tgt_buf[i] = (mask & r_tgt_buf[i]) | (~mask & rdata);
                            }
                            r_dcache_fsm = DCACHE_CC_UPDT;
#endif //CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
                        } else if ( dcache_hit and not r_tgt_update ) {
                            r_dcache_fsm = DCACHE_CC_INVAL;
                        } else {
                            if(r_tgt_update){
                                r_tgt_dcache_rsp = true;
                            } else {
                                r_tgt_dcache_rsp = false;
                            }
                            r_tgt_dcache_req = false;
                            r_dcache_fsm = r_dcache_fsm_save;
                        }
                    }
                    break;
                }
                ///////////////////
            case DCACHE_CC_UPDT:    // update directory and data cache        
                {
                    addr_40 ad = r_tgt_addr;

                    m_cpt_dcache_dir_write++;
                    m_cpt_dcache_data_write++;

# ifdef COHERENCE_DEBUG
                    std::cout << "PROC " << m_srcid_rw << " DCACHE_CC_UPDT, update : " << std::endl;
# endif

#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
                    uint32_t word  = r_cache_word;
                    
                    if(r_tgt_be[word])
                        r_dcache.write(ad+word*4, r_tgt_buf[word]);
# ifdef COHERENCE_DEBUG
                    std::cout << " address " << std::hex << ad+word*4 << " data " << std::dec << r_tgt_buf[word] << std::endl;
                    data_t rdata = 0xAAAAAAAA;
                    r_dcache.read(ad+word*4,&rdata);
                    std::cout << "data written " << rdata << std::endl; 
# endif

                    word ++;
#if CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE_OPT
                    for (; word<m_dcache_words; ++word)
                        if (r_tgt_be[word] != 0)
                            break;
#endif
                    
                    if (word==m_dcache_words)
                    {
                        r_tgt_dcache_req = false;
                        r_tgt_dcache_rsp = true;
                        r_dcache_fsm = r_dcache_fsm_save;
                        word = 0;
                    }
                    r_cache_word = word;
#else //CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
                    data_t* buf = r_tgt_buf;
                    for(size_t i=0; i<m_dcache_words; i++){
                        if(r_tgt_be[i]) {
                            r_dcache.write( ad + i*4, buf[i]);
# ifdef COHERENCE_DEBUG
                            std::cout << " address " << std::hex << ad+i*4 << " data " << std::dec << buf[i] << std::endl;
                            data_t rdata = 0xAAAAAAAA;
                            r_dcache.read(ad + i*4,&rdata);
                            std::cout << "data written " << rdata << std::endl; 
# endif //CC_XCACHE_WRAPPER_CC_UPDATE_MULTI_CYCLE
                        }
                    }
                    r_tgt_dcache_req = false;
                    r_tgt_dcache_rsp = true;
                    r_dcache_fsm = r_dcache_fsm_save;
#endif
                    break;
                }
                /////////////////////
            case DCACHE_CC_INVAL:   // invalidate a cache line
                {
                    addr_40  ad      = r_tgt_addr;
                    r_tgt_dcache_rsp = true;
                    r_dcache.inval(ad);
                    r_tgt_dcache_req = false;
                    r_dcache_fsm = r_dcache_fsm_save;
                    break;
                }
                ///////////////////
            case DCACHE_CC_CLEANUP:   
                {
                    // external cache invalidate request
                    if ( r_tgt_dcache_req )   
                    {
                        r_dcache_fsm = DCACHE_CC_CHECK;
                        r_dcache_fsm_save = r_dcache_fsm;
                        break;
                    }        
                    // cleanup
                    if(not r_dcache_cleanup_req){
                        r_dcache_cleanup_req = true;
                        r_dcache_cleanup_line = r_dcache_addr_save.read() >> m_dcache_words_shift;
                        r_dcache_fsm = DCACHE_IDLE;
                    }
                    break;
                }    

        } // end switch r_dcache_fsm
        
        ////////// write buffer state update  /////////////
        // The update() method must be called at each cycle to update the internal state.
        // All pending write requests must be locked in case of SYNC
        bool wbuf_flush=(r_dcache_fsm == DCACHE_SYNC);
#if   (CC_XCACHE_WRAPPER_WBUF_UPDATE_SCHEME==1)
        r_wbuf.update_multi_scan      (wbuf_flush);
#elif (CC_XCACHE_WRAPPER_WBUF_UPDATE_SCHEME==2)
        r_wbuf.update_round_robin_scan(wbuf_flush);
#elif (CC_XCACHE_WRAPPER_WBUF_UPDATE_SCHEME==3)
        r_wbuf.update_one_scan        (wbuf_flush);
#else
        r_wbuf.update                 (wbuf_flush);
#endif

#if CC_XCACHE_WRAPPER_DEBUG
        if (m_cpt_total_cycles>=CC_XCACHE_WRAPPER_DEBUG_CYCLE_MIN)
            std::cout << "    * Data        Response : " << drsp << std::endl;
#endif

        /////////// execute one iss cycle /////////////////////////////////////////////
        {
            uint32_t it = 0;
            for (size_t i=0; i<(size_t)iss_t::n_irq; i++) 
                if(p_irq[i].read()) it |= (1<<i);
            m_iss.executeNCycles(1, irsp, drsp, it);
        }

        if ( (ireq.valid and not irsp.valid) or 
             (dreq.valid and not drsp.valid))
        {
            m_cpt_frz_cycles++;
#if CC_XCACHE_WRAPPER_STOP_SIMULATION
            m_stop_simulation_nb_frz_cycles ++;
            
            if (m_stop_simulation and (m_stop_simulation_nb_frz_cycles >= m_stop_simulation_nb_frz_cycles_max))
            {
                std::cout << std::dec << "CC_XCACHE_WRAPPER \"" << name() << "\" : cycle " << m_cpt_total_cycles << ", the cpu is frozen since " << m_stop_simulation_nb_frz_cycles << " cycles." << std::endl;
                ASSERT(false,"CPU : anormal activity"); // exit
            }
        }
        else
        {
            m_stop_simulation_nb_frz_cycles = 0; // reinit counter
#endif //CC_XCACHE_WRAPPER_STOP_SIMULATION
        }


#if CC_XCACHE_WRAPPER_DEBUG_DCACHE_TRANSACTION
        if (dreq.valid and drsp.valid)
        {
            log_dcache_transaction_file 
                << "[" << m_cpt_total_cycles << "]"
                << std::hex
                << " @ "     << std::setw(8) << (uint32_t)dreq.addr
                << " be "    << std::setw(1) << (uint32_t)dreq.be
                << " W "     << std::setw(8) << (uint32_t)dreq.wdata
                << " R "     << std::setw(8) << (uint32_t)drsp.rdata
                << " error "            << (uint32_t)drsp.error
                << std::dec
                << " "  << type_str(dreq.type);

            if ((dreq.type == iss_t::XTN_READ) or 
                (dreq.type == iss_t::XTN_WRITE))
                //log_dcache_transaction_file << xtn_str(dreq.addr>>2);
                switch (dreq.addr>>2)
                {
                case iss_t::XTN_DCACHE_INVAL : log_dcache_transaction_file << " INVAL"; break;
                case iss_t::XTN_SYNC         : log_dcache_transaction_file << " SYNC"; break;
                default                      : log_dcache_transaction_file << " invalid"; break;
                }                                                                            

            log_dcache_transaction_file << std::endl;

            // printf("[%d] @ %.8x be %.1x W %.8x R %.8x error %d - %s"
            //        ,(uint32_t)m_cpt_total_cycles
            //        ,(uint32_t)dreq.addr
            //        ,(uint32_t)dreq.be
            //        ,(uint32_t)dreq.wdata
            //        ,(uint32_t)drsp.rdata
            //        ,(uint32_t)drsp.error
            //        ,type_str(dreq.type));
            // if ((dreq.type == iss_t::XTN_READ) or 
            //     (dreq.type == iss_t::XTN_WRITE))
            //     //     printf(" %s",xtn_str(dreq.addr>>2));
            //     switch (dreq.addr>>2)
            //     {
            //     case iss_t::XTN_DCACHE_INVAL : printf(" INVAL"); break;
            //     case iss_t::XTN_SYNC         : printf(" SYNC"); break;
            //     default                      : printf(" invalid"); break;
            //     }                                                                            
            // printf("\n");
        }
#endif //CC_XCACHE_WRAPPER_DEBUG_DCACHE_TRANSACTION

        ////////////////////////////////////////////////////////////////////////////
        // This CLEANUP FSM controls the transmission of the cleanup transactions
        // on the coherence network. It controls the following ressources:
        // - r_cleanup_fsm
        // - r_dcache_cleanup_req (reset)
        // - r_icache_cleanup_req (reset)
        // 
        // This FSM handles cleanup requests from both the DCACHE FSM & ICACHE FSM
        // - Instruction Cleanup  : r_icache_cleanup_req 
        // - Data Cleanup         : r_dcache_cleanup_req 
        // In case of simultaneous requests, the data request have highest priority.
        // There is only one cleanup transaction at a given time (sequencial behavior)
        // because the same FSM controls both command & response. 
        // The the r_icache_cleanup_req & r_dcache_cleanup_req are reset only
        // when the response packet is received.
        // Error handling :
        // As the coherence trafic is controled by hardware, errors are not reported
        // to software : In case of errors, the simulation stops.
        ////////////////////////////////////////////////////////////////////////////

        switch (r_cleanup_fsm) {

            case CLEANUP_IDLE:
            {    
                if ( p_vci_ini_c.cmdack )
                {
                    if      (r_dcache_cleanup_req) 	r_cleanup_fsm = CLEANUP_DCACHE;
                    else if (r_icache_cleanup_req) 	r_cleanup_fsm = CLEANUP_ICACHE;
                }
                break;
            }
            case CLEANUP_DCACHE:
            {
                if ( p_vci_ini_c.rspval )
                {
                    PRINTF("      * <CLEANUP> rerror : %d (%d)\n",(uint32_t)p_vci_ini_c.rerror.read(),0x2 & ( (1 << vci_param::E) - 1));

                    ASSERT(p_vci_ini_c.reop and (p_vci_ini_c.rtrdid.read() == TYPE_DATA_CLEANUP),
                            "illegal response packet received for a cleanup transaction");
                    ASSERT(p_vci_ini_c.rerror.read() == vci_param::ERR_NORMAL,
                           "error signaled in a cleanup response" );
                    
                    r_cleanup_fsm = CLEANUP_IDLE;
                    r_dcache_cleanup_req = false;
                    // m_cpt_cc_cleanup_data++; TODO
                }
                break;
            }
            case CLEANUP_ICACHE:
            {
                if ( p_vci_ini_c.rspval )
                {
                    PRINTF("      * <CLEANUP> rerror : %d (%d)\n",(uint32_t)p_vci_ini_c.rerror.read(),0x2 & ( (1 << vci_param::E) - 1));

                    ASSERT(p_vci_ini_c.reop and (p_vci_ini_c.rtrdid.read() == TYPE_INS_CLEANUP),
                           "illegal response packet received for a cleanup transaction");
                    ASSERT(p_vci_ini_c.rerror.read() == vci_param::ERR_NORMAL, 
                           "error signaled in a cleanup response" );
                    
                    r_cleanup_fsm = CLEANUP_IDLE;
                    r_icache_cleanup_req = false;
                    // m_cpt_cc_cleanup_ins++; TODO
                }
                break;
            }
        } // end switch r_cleanup_fsm    

        ////////////////////////////////////////////////////////////////////////////
        // The VCI_CMD FSM controls the following ressources:
        // - r_vci_cmd_fsm
        // - r_vci_cmd_min
        // - r_vci_cmd_max
        // - r_vci_cmd_cpt
        // - wbuf (reset)
        // - r_icache_miss_req (reset)
        // - r_icache_unc_req (reset)
        // - r_dcache_miss_req (reset)
        // - r_dcache_sc_req (reset)
        //
        // This FSM handles requests from both the DCACHE FSM & the ICACHE FSM.
        // There is 7 request types, with the following priorities : 
        // 1 - Data Read Miss         : r_dcache_miss_req and miss in the write buffer
        // 2 - Data Read Uncachable   : r_dcache_unc_req  and miss in the write buffer
        // 3 - Instruction Miss       : r_icache_miss_req and miss in the write buffer
        // 4 - Instruction Uncachable : r_icache_unc_req  and miss in the write buffer
        // 5 - Data Write             : r_wbuf.rok()      
        // 6 - Data Store Conditionnal: r_dcache_sc_req
        // There is at most one (CMD/RSP) VCI transaction, as both CMD_FSM 
        // and RSP_FSM exit simultaneously the IDLE state.
        //
        // VCI formats:
        // According to the VCI advanced specification, all read requests packets 
        // (read Uncached, Miss data, Miss instruction) are one word packets.
        // For write burst packets, all words must be in the same cache line,
        // and addresses must be contiguous (the BE field is 0 in case of "holes").
        //////////////////////////////////////////////////////////////////////////////

        r_vci_cmd_dcache_prior = not r_vci_cmd_dcache_prior;

        switch (r_vci_cmd_fsm) {

            case CMD_IDLE:
                {
                // if (r_vci_rsp_fsm != RSP_IDLE) break;

                size_t	min;
                size_t	max;

                r_vci_cmd_cpt = 0;

                // Requests :

                // multi_write_buffer access is conditionnal with dcache_miss_req and icache_miss_req

#if   (CC_XCACHE_WRAPPER_VCI_CMD_PRIORITY==1)
                //  1) two access authorized
                bool dcache_miss_req =  r_dcache_miss_req;
                bool icache_miss_req =  r_icache_miss_req;
#elif (CC_XCACHE_WRAPPER_VCI_CMD_PRIORITY==2)
                //  2) one access with static priority (dcache prior)
                bool dcache_miss_req = r_dcache_miss_req; // dcache prior
                bool icache_miss_req = not dcache_miss_req and r_icache_miss_req;
#elif (CC_XCACHE_WRAPPER_VCI_CMD_PRIORITY==3)
                //  3) one access with static priority (icache prior)
                bool icache_miss_req = r_icache_miss_req;
                bool dcache_miss_req = not icache_miss_req and r_dcache_miss_req; // dcache prior
#elif (CC_XCACHE_WRAPPER_VCI_CMD_PRIORITY==4)
                //  4) one access with round robin priority
                bool dcache_miss_req = ((r_dcache_miss_req and not r_icache_miss_req) or // only dcache
                                        (r_dcache_miss_req and r_vci_cmd_dcache_prior)); // dcache prior
                bool icache_miss_req = not dcache_miss_req and r_icache_miss_req;
#else
#error "Invalid value to CC_XCACHE_WRAPPER_VCI_CMD_PRIORITY"
#endif
                // 1 - Data Read
                if (dcache_miss_req and r_wbuf.miss(r_dcache_addr_save))
                { 
                    r_vci_cmd_fsm = CMD_DATA_MISS;
                    r_dcache_miss_req = false;
                    m_cpt_dmiss_transaction++; 
                } 

                // 2 - Data Read Uncachable
                else if ( r_dcache_unc_req )
                { 
                    r_vci_cmd_fsm = CMD_DATA_UNC;
                    r_dcache_unc_req = false;
                 // m_cpt_data_unc_transaction++; 
                }

                // 3 - Instruction Miss
                else if (icache_miss_req and r_wbuf.miss(r_icache_addr_save))
                { 
                    r_vci_cmd_fsm = CMD_INS_MISS; 
                    r_icache_miss_req = false;
                    m_cpt_imiss_transaction++;
                } 

                // 4 - Instruction Uncachable
                else if ( r_icache_unc_req )
                { 
                    r_vci_cmd_fsm = CMD_INS_UNC; 
                    r_icache_unc_req = false;
                 // m_cpt_ins_unc_transaction++;
                }

                // 5 - Data Write
                else if ( r_wbuf.rok(&min, &max) ) 
                { 
                    r_vci_cmd_fsm = CMD_DATA_WRITE;
                    r_vci_cmd_cpt = min;
                    r_vci_cmd_min = min;
                    r_vci_cmd_max = max;
                    m_cpt_data_write_transaction++; 
                    m_length_write_transaction += (max-min+1); 
                } 

                // 6 - Data Store Conditionnal
                else if ( r_dcache_sc_req ) 
                { 
                    r_vci_cmd_fsm = CMD_DATA_SC;
                    r_vci_cmd_max = 1; 
                    m_cpt_unc_transaction++; 
                    r_dcache_sc_req = false;
                }

                break;
                }
            case CMD_DATA_WRITE:
                if ( p_vci_ini_rw.cmdack.read() ) {
                    r_vci_cmd_cpt = r_vci_cmd_cpt + 1;
                    if (r_vci_cmd_cpt == r_vci_cmd_max) {
                        r_vci_cmd_fsm = CMD_IDLE ;
                        r_wbuf.sent() ;
                    }
                }
                break;

            case CMD_DATA_SC:
                if ( p_vci_ini_rw.cmdack.read() ) {
                    r_vci_cmd_cpt = r_vci_cmd_cpt + 1;
                    if (r_vci_cmd_cpt == r_vci_cmd_max) {
                        r_vci_cmd_fsm = CMD_IDLE ;
                    }
                }
                break;
            case CMD_INS_MISS:
            case CMD_INS_UNC:
            case CMD_DATA_MISS:
            case CMD_DATA_UNC:
                if ( p_vci_ini_rw.cmdack.read() ) {
                    r_vci_cmd_fsm = CMD_IDLE;
                }
                break;

        } // end  switch r_vci_cmd_fsm

        //////////////////////////////////////////////////////////////////////////
        // The VCI_RSP FSM controls the following ressources:
        // - r_vci_rsp_fsm:
        // - r_icache_miss_buf[m_icache_words]
        // - r_dcache_miss_buf[m_dcache_words]
        // - r_vci_rsp_data_error set
        // - r_vci_rsp_ins_error set
        // - r_vci_rsp_cpt
        // In order to have only one active VCI transaction, this VCI_RSP_FSM 
        // is synchronized with the VCI_CMD FSM, and both FSMs exit the
        // IDLE state simultaneously.
        // 
        // VCI formats:
        // This component accepts single word or multi-word response packets for
        // write response packets. 
        //
        // Error handling:
        // This FSM analyzes the VCI error code and signals directly the
        // Write Bus Error. 
        // In case of Read Data Error, the VCI_RSP FSM sets the r_vci_rsp_data_error 
        // flip_flop and the error is signaled by the DCACHE FSM.  
        // In case of Instruction Error, the VCI_RSP FSM sets the r_vci_rsp_ins_error 
        // flip_flop and the error is signaled by the DCACHE FSM.  
        // In case of Cleanup Error, the simulation stops with an error message...
        //////////////////////////////////////////////////////////////////////////

        switch (r_vci_rsp_fsm) {

            case RSP_IDLE:

                if( p_vci_ini_rw.rspval.read() )
                {
                    PRINTF("      * <RSP> have rsp - trdid : %x\n",(uint32_t)p_vci_ini_rw.rtrdid.read());

                    r_vci_rsp_cpt = 0;

                    if ((p_vci_ini_rw.rtrdid.read()>>(vci_param::T-1)) != 0 )
                        r_vci_rsp_fsm = RSP_DATA_WRITE;
                    else
                    {
                        switch (p_vci_ini_rw.rtrdid.read())
                        {
                        case TYPE_INS_MISS     : r_vci_rsp_fsm = RSP_INS_MISS;     break;
                        case TYPE_INS_UNC      : r_vci_rsp_fsm = RSP_INS_UNC;      break;
                        case TYPE_DATA_MISS    : r_vci_rsp_fsm = RSP_DATA_MISS;    break;
                        case TYPE_DATA_UNC     : r_vci_rsp_fsm = RSP_DATA_UNC;     break;
                        case TYPE_DATA_SC      : r_vci_rsp_fsm = RSP_DATA_SC;      break;
                        default : 
                            {
                                ASSERT(false, "Unexpected response");
                            }
                        }
                    }
                }
                break;

            case RSP_INS_MISS:

                m_cost_imiss_transaction++;
                PRINTF("      * <RSP> rspval/ack : %d - %d\n",(uint32_t)p_vci_ini_rw.rspval.read(), (uint32_t)r_vci_rsp_ack);

                if (p_vci_ini_rw.rspval.read() and r_vci_rsp_ack)
                {
                    PRINTF("      * <RSP> have rsp - r_vci_rsp_cpt : %d/%d\n",(uint32_t)r_vci_rsp_cpt.read(),(uint32_t)m_icache_words);
                    PRINTF("      * <RSP> ins : %x\n",(int)p_vci_ini_rw.rdata.read());

                    ASSERT( (r_vci_rsp_cpt < m_icache_words),
                            "The VCI response packet for instruction miss is too long" );
                    r_vci_rsp_cpt = r_vci_rsp_cpt + 1;
                    CACHE_MISS_BUF_RSP_PUSH(i,r_vci_rsp_cpt,(data_t)p_vci_ini_rw.rdata.read());
                    if ( p_vci_ini_rw.reop.read() ) 
                    {
                        PRINTF("      * <RSP> have reop\n");

                        ASSERT( ((r_vci_rsp_cpt == m_icache_words - 1) or
                                 p_vci_ini_rw.rerror.read() or
                                 (r_vci_rsp_ins_error.read()&0x1)),
                                "The VCI response packet for instruction miss is too short");
                        r_vci_rsp_cpt    = 0;
                        r_vci_rsp_fsm    = RSP_IDLE;

                    }
                    if ( (p_vci_ini_rw.rerror.read()&0x1) != vci_param::ERR_NORMAL ) r_vci_rsp_ins_error = true;
                }
                break;

            case RSP_INS_UNC:

                m_cost_imiss_transaction++;
                if (p_vci_ini_rw.rspval.read() and r_vci_rsp_ack)
                {
                    ASSERT(p_vci_ini_rw.reop.read(),
                           "illegal VCI response packet for uncached instruction");

                    CACHE_MISS_BUF_RSP_PUSH(i,0,(data_t)p_vci_ini_rw.rdata.read());

                    r_vci_rsp_fsm = RSP_IDLE;

                    if ( (p_vci_ini_rw.rerror.read()&0x1) != vci_param::ERR_NORMAL ) r_vci_rsp_ins_error = true;
                }
                break;

            case RSP_DATA_MISS:

                m_cost_dmiss_transaction++;
                if (p_vci_ini_rw.rspval.read() and r_vci_rsp_ack)
                {
                    PRINTF("      * <RSP> have rspval - error : %d\n",(int)p_vci_ini_rw.rerror.read());

                    ASSERT(r_vci_rsp_cpt < m_dcache_words,
                           "illegal VCI response packet for data read miss");
                    r_vci_rsp_cpt = r_vci_rsp_cpt + 1;

                    CACHE_MISS_BUF_RSP_PUSH(d,r_vci_rsp_cpt,(data_t)p_vci_ini_rw.rdata.read());

                    if ( p_vci_ini_rw.reop.read() ) {
                        ASSERT( ((r_vci_rsp_cpt == m_dcache_words - 1) 
                                 or (p_vci_ini_rw.rerror.read()&0x1)
                                 or r_vci_rsp_data_error.read()),
                                "illegal VCI response packet for data read miss");
                        r_vci_rsp_cpt     = 0;
                        r_vci_rsp_fsm     = RSP_IDLE;
                    }
                    if ( (p_vci_ini_rw.rerror.read()&0x1) != vci_param::ERR_NORMAL ) r_vci_rsp_data_error = true;
                }
                break;

            case RSP_DATA_WRITE:

                m_cost_write_transaction++;
                if (p_vci_ini_rw.rspval.read())
                {
                    PRINTF("      * <RSP> have rspval - error : %d\n",(int)p_vci_ini_rw.rerror.read());

                    ASSERT(p_vci_ini_rw.reop.read(),
                           "A VCI response packet must contain one flit for a write transaction");
                    r_vci_rsp_fsm = RSP_IDLE;
                    bool cached = r_wbuf.completed(p_vci_ini_rw.rtrdid.read() - (1<<(vci_param::T-1)) );

                    PRINTF("      * <RSP> cached : %d\n",cached);

                    if (not cached)
                        r_dcache_previous_unc = false;

                    if ((p_vci_ini_rw.rerror.read()&0x1) != vci_param::ERR_NORMAL) m_iss.setWriteBerr();
                }
                break;

            case RSP_DATA_UNC:
                m_cost_unc_transaction++;
                if (p_vci_ini_rw.rspval.read() and r_vci_rsp_ack)
                {
                    ASSERT(p_vci_ini_rw.reop.read(),
                           "illegal VCI response packet for data read uncached");

                    CACHE_MISS_BUF_RSP_PUSH(d,0,(data_t)p_vci_ini_rw.rdata.read());

                    r_vci_rsp_fsm = RSP_IDLE;
                    r_dcache_previous_unc = false;

                    if ( (p_vci_ini_rw.rerror.read()&0x1) != vci_param::ERR_NORMAL ) r_vci_rsp_data_error = true;
                }
                break;

            case RSP_DATA_SC:
                m_cost_unc_transaction++;
                if (p_vci_ini_rw.rspval.read() and r_vci_rsp_ack)
                {
                    ASSERT(p_vci_ini_rw.reop.read(),
                           "illegal VCI response packet for data SC");

                    CACHE_MISS_BUF_RSP_PUSH(d,0,(data_t)p_vci_ini_rw.rdata.read());

                    r_vci_rsp_fsm = RSP_IDLE;
                    r_dcache_previous_unc = false;

                    if ( (p_vci_ini_rw.rerror.read()&0x1) != vci_param::ERR_NORMAL ) r_vci_rsp_data_error = true;
                }
                break;

        } // end switch r_vci_rsp_fsm

    } // end transition()

    //////////////////////////////////////////////////////////////////////////////////
    tmpl(void)::genMoore()
    //////////////////////////////////////////////////////////////////////////////////
    {

        // VCI initiator response
        switch ( r_cleanup_fsm.read() ) {

            case CLEANUP_IDLE:
                p_vci_ini_c.rspack  = false;
                p_vci_ini_c.cmdval  = r_icache_cleanup_req || r_dcache_cleanup_req;
                if ( r_dcache_cleanup_req )
                {
                    p_vci_ini_c.address =  r_dcache_cleanup_line.read() * (m_dcache_words << 2);
                    p_vci_ini_c.trdid   = TYPE_DATA_CLEANUP;
                }
                else
                {
                    p_vci_ini_c.address =  r_icache_cleanup_line.read() * (m_icache_words << 2); 
                    p_vci_ini_c.trdid   = TYPE_INS_CLEANUP;
                }
                p_vci_ini_c.wdata  = 0;
                p_vci_ini_c.be     = 0xF;
                p_vci_ini_c.plen   = 4;
                p_vci_ini_c.cmd    = vci_param::CMD_WRITE;
                p_vci_ini_c.pktid  = 0;
                p_vci_ini_c.srcid  = m_srcid_c;
                p_vci_ini_c.cons   = false;
                p_vci_ini_c.wrap   = false;
                p_vci_ini_c.contig = false;
                p_vci_ini_c.clen   = 0;
                p_vci_ini_c.cfixed = false;
                p_vci_ini_c.eop    = true;
                break;

           case CLEANUP_DCACHE:
                p_vci_ini_c.rspack  = true;
                p_vci_ini_c.cmdval  = false;
                p_vci_ini_c.address = 0;
                p_vci_ini_c.wdata  = 0;
                p_vci_ini_c.be     = 0;
                p_vci_ini_c.plen   = 0;
                p_vci_ini_c.cmd    = vci_param::CMD_WRITE;
                p_vci_ini_c.trdid  = 0; 
                p_vci_ini_c.pktid  = 0;
                p_vci_ini_c.srcid  = 0;
                p_vci_ini_c.cons   = false;
                p_vci_ini_c.wrap   = false;
                p_vci_ini_c.contig = false;
                p_vci_ini_c.clen   = 0;
                p_vci_ini_c.cfixed = false;
                p_vci_ini_c.eop = false;
                break;

           case CLEANUP_ICACHE:
                p_vci_ini_c.rspack  = true;
                p_vci_ini_c.cmdval  = false;
                p_vci_ini_c.address = 0;
                p_vci_ini_c.wdata  = 0;
                p_vci_ini_c.be     = 0;
                p_vci_ini_c.plen   = 0;
                p_vci_ini_c.cmd    = vci_param::CMD_WRITE;
                p_vci_ini_c.trdid  = 0; 
                p_vci_ini_c.pktid  = 0;
                p_vci_ini_c.srcid  = 0;
                p_vci_ini_c.cons   = false;
                p_vci_ini_c.wrap   = false;
                p_vci_ini_c.contig = false;
                p_vci_ini_c.clen   = 0;
                p_vci_ini_c.cfixed = false;
                p_vci_ini_c.eop = false;
                break;
           } // end switch r_cleanup_fsm

        // VCI initiator command

        switch (r_vci_cmd_fsm.read() ) {

            case CMD_IDLE:
                p_vci_ini_rw.cmdval  = false;
                p_vci_ini_rw.address = 0;
                p_vci_ini_rw.wdata   = 0;
                p_vci_ini_rw.be      = 0;
                p_vci_ini_rw.plen    = 0;
                p_vci_ini_rw.cmd     = vci_param::CMD_NOP;
                p_vci_ini_rw.trdid   = 0;
                p_vci_ini_rw.pktid   = 0;
                p_vci_ini_rw.srcid   = 0;
                p_vci_ini_rw.cons    = false;
                p_vci_ini_rw.wrap    = false;
                p_vci_ini_rw.contig  = false;
                p_vci_ini_rw.clen    = 0;
                p_vci_ini_rw.cfixed  = false;
                p_vci_ini_rw.eop     = false;

                break;

            case CMD_DATA_UNC:
                p_vci_ini_rw.cmdval = true;
                p_vci_ini_rw.address = (addr_40) r_dcache_addr_save.read() & ~0x3;
                switch( r_dcache_type_save ) {
                    case iss_t::DATA_READ:
                        p_vci_ini_rw.wdata = 0;
                        p_vci_ini_rw.be  = r_dcache_be_save.read();
                        p_vci_ini_rw.cmd = vci_param::CMD_READ;
                        break;
                    case iss_t::DATA_LL:
                        p_vci_ini_rw.wdata = 0;
                        p_vci_ini_rw.be  = 0xF;
                        p_vci_ini_rw.cmd = vci_param::CMD_LOCKED_READ;
                        break;
                    default:
                        ASSERT(false,"this should not happen");
                }
                p_vci_ini_rw.plen = 4;
                p_vci_ini_rw.trdid  = TYPE_DATA_UNC;   // data cache uncached read
                p_vci_ini_rw.pktid  = 0;
                p_vci_ini_rw.srcid  = m_srcid_rw;
                p_vci_ini_rw.cons   = false;
                p_vci_ini_rw.wrap   = false;
                p_vci_ini_rw.contig = true;
                p_vci_ini_rw.clen   = 0;
                p_vci_ini_rw.cfixed = false;
                p_vci_ini_rw.eop    = true;

                break;

            case CMD_DATA_SC:
                p_vci_ini_rw.cmdval = true;
                p_vci_ini_rw.address = (addr_40) r_dcache_addr_save.read() & ~0x3;
                if(r_vci_cmd_max.read() == 3){
                    ASSERT(false, "Not handled yet");
                } else { // r_vci_cmd_cpt == 1
                    switch(r_vci_cmd_cpt.read()){
                        case 0:
                            p_vci_ini_rw.wdata = (uint32_t)(r_dcache_ll_data.read() & 0xFFFFFFFF);
                            break;
                        case 1:
                            p_vci_ini_rw.wdata = r_dcache_wdata_save.read();
                            break;
                    }
                }
                p_vci_ini_rw.be     = 0xF;
                p_vci_ini_rw.cmd    = vci_param::CMD_STORE_COND;
                p_vci_ini_rw.plen   = 4*(r_vci_cmd_max.read()+1);
                p_vci_ini_rw.trdid  = TYPE_DATA_SC;   // data cache uncached read
                p_vci_ini_rw.pktid  = 0;
                p_vci_ini_rw.srcid  = m_srcid_rw;
                p_vci_ini_rw.cons   = true;
                p_vci_ini_rw.wrap   = false;
                p_vci_ini_rw.contig = false;
                p_vci_ini_rw.clen   = 0;
                p_vci_ini_rw.cfixed = false;
                p_vci_ini_rw.eop    = (r_vci_cmd_cpt.read() == r_vci_cmd_max.read());

                break;

            case CMD_DATA_WRITE:
                p_vci_ini_rw.cmdval  = true;
                p_vci_ini_rw.address = r_wbuf.getAddress(r_vci_cmd_cpt)&~0x3;
                p_vci_ini_rw.wdata   = r_wbuf.getData(r_vci_cmd_cpt);
                p_vci_ini_rw.be      = r_wbuf.getBe(r_vci_cmd_cpt);
                p_vci_ini_rw.plen    = (r_vci_cmd_max - r_vci_cmd_min + 1)<<2;
                p_vci_ini_rw.cmd     = vci_param::CMD_WRITE;
                p_vci_ini_rw.trdid   = r_wbuf.getIndex() + (1<<(vci_param::T-1));
                p_vci_ini_rw.pktid   = 0;
                p_vci_ini_rw.srcid   = m_srcid_rw;
                p_vci_ini_rw.cons    = false;
                p_vci_ini_rw.wrap    = false;
                p_vci_ini_rw.contig  = true;
                p_vci_ini_rw.clen    = 0;
                p_vci_ini_rw.cfixed  = false;
                p_vci_ini_rw.eop     = (r_vci_cmd_cpt == r_vci_cmd_max);

                break;

            case CMD_DATA_MISS:
                p_vci_ini_rw.cmdval = true;
                p_vci_ini_rw.address = r_dcache_addr_save.read() & (addr_40) m_dcache_yzmask;
                p_vci_ini_rw.be     = 0xF;
                p_vci_ini_rw.plen   = m_dcache_words << 2;
                p_vci_ini_rw.cmd    = vci_param::CMD_READ;
                p_vci_ini_rw.trdid  = TYPE_DATA_MISS;   // data cache cached read
                p_vci_ini_rw.pktid  = 0;
                p_vci_ini_rw.srcid  = m_srcid_rw;
                p_vci_ini_rw.cons   = false;
                p_vci_ini_rw.wrap   = false;
                p_vci_ini_rw.contig = true;
                p_vci_ini_rw.clen   = 0;
                p_vci_ini_rw.cfixed = false;
                p_vci_ini_rw.eop = true;

                break;

            case CMD_INS_MISS:
                p_vci_ini_rw.cmdval = true;
                p_vci_ini_rw.address = r_icache_addr_save.read() & (addr_40) m_icache_yzmask;
                p_vci_ini_rw.be     = 0xF;
                p_vci_ini_rw.plen   = m_icache_words << 2;
                p_vci_ini_rw.cmd    = vci_param::CMD_READ;
                p_vci_ini_rw.trdid  = TYPE_INS_MISS;   // ins cache cached read
                p_vci_ini_rw.pktid  = 0;
                p_vci_ini_rw.srcid  = m_srcid_rw;
                p_vci_ini_rw.cons   = false;
                p_vci_ini_rw.wrap   = false;
                p_vci_ini_rw.contig = true;
                p_vci_ini_rw.clen   = 0;
                p_vci_ini_rw.cfixed = false;
                p_vci_ini_rw.eop = true;

                break;

            case CMD_INS_UNC:
                p_vci_ini_rw.cmdval = true;
                p_vci_ini_rw.address = r_icache_addr_save.read() & ~0x3;
                p_vci_ini_rw.be     = 0xF;
                p_vci_ini_rw.plen   = 4;
                p_vci_ini_rw.cmd    = vci_param::CMD_READ;
                p_vci_ini_rw.trdid  = TYPE_INS_UNC;   // ins cache uncached read
                p_vci_ini_rw.pktid  = 0;
                p_vci_ini_rw.srcid  = m_srcid_rw;
                p_vci_ini_rw.cons   = false;
                p_vci_ini_rw.wrap   = false;
                p_vci_ini_rw.contig = true;
                p_vci_ini_rw.clen   = 0;
                p_vci_ini_rw.cfixed = false;
                p_vci_ini_rw.eop = true;

                break;

        } // end switch r_vci_cmd_fsm

        bool ack;

        switch (r_vci_rsp_fsm.read() ) {
        case RSP_IDLE       : ack = false; break;
        case RSP_DATA_WRITE : ack = true; break;
        case RSP_INS_MISS   :
        case RSP_INS_UNC    : ack = CACHE_MISS_BUF_RSP_ACK(i); break;
        case RSP_DATA_MISS  :
        case RSP_DATA_UNC   :
        case RSP_DATA_SC    : ack = CACHE_MISS_BUF_RSP_ACK(d); break;
        } // end switch r_vci_cmd_fsm

        r_vci_rsp_ack       = ack;
        p_vci_ini_rw.rspack = ack;
        
        // VCI_TGT

        switch ( r_vci_tgt_fsm.read() ) {

            case TGT_IDLE:
            case TGT_UPDT_WORD:
            case TGT_UPDT_DATA:
                p_vci_tgt.cmdack  = true;
                p_vci_tgt.rspval  = false;
                break;

            case TGT_RSP_BROADCAST:
                p_vci_tgt.cmdack  = false;
                p_vci_tgt.rspval  = not r_tgt_icache_req.read() and not r_tgt_dcache_req.read() and ( r_tgt_icache_rsp | r_tgt_dcache_rsp );
                p_vci_tgt.rsrcid  = r_tgt_srcid.read();
                p_vci_tgt.rpktid  = r_tgt_pktid.read();
                p_vci_tgt.rtrdid  = r_tgt_trdid.read();
                p_vci_tgt.rdata   = 0;
                p_vci_tgt.rerror  = 0x2 & ( (1 << vci_param::E) - 1); // Write OK
                p_vci_tgt.reop    = true;
                break;

            case TGT_RSP_ICACHE:
                p_vci_tgt.cmdack  = false;
                p_vci_tgt.rspval  = not r_tgt_icache_req.read() and r_tgt_icache_rsp.read();
                p_vci_tgt.rsrcid  = r_tgt_srcid.read();
                p_vci_tgt.rpktid  = r_tgt_pktid.read();
                p_vci_tgt.rtrdid  = r_tgt_trdid.read();
                p_vci_tgt.rdata   = 0;
                p_vci_tgt.rerror  = 0x2 & ( (1 << vci_param::E) - 1); // Write OK
                p_vci_tgt.reop    = true;
                break;

            case TGT_RSP_DCACHE:
                p_vci_tgt.cmdack  = false;
                p_vci_tgt.rspval  = not r_tgt_dcache_req.read() and r_tgt_dcache_rsp.read();
                p_vci_tgt.rsrcid  = r_tgt_srcid.read();
                p_vci_tgt.rpktid  = r_tgt_pktid.read();
                p_vci_tgt.rtrdid  = r_tgt_trdid.read();
                p_vci_tgt.rdata   = 0;
                p_vci_tgt.rerror  = 0x2 & ( (1 << vci_param::E) - 1); // Write OK
                p_vci_tgt.reop    = true;
                break;

            case TGT_REQ_BROADCAST:
            case TGT_REQ_ICACHE:
            case TGT_REQ_DCACHE:
                p_vci_tgt.cmdack  = false;
                p_vci_tgt.rspval  = false;
                break;

        } // end switch TGT_FSM
    } // end genMoore()

    //////////////////////////////////////////////////////////////////////////////////
    tmpl(void)::stop_simulation (uint32_t nb_frz_cycles)
    //////////////////////////////////////////////////////////////////////////////////
    {
#if CC_XCACHE_WRAPPER_STOP_SIMULATION
        if (nb_frz_cycles == 0)
            {
                PRINTF("CC_XCACHE_WRAPPER \"%s\" : don't stop the simulation.\n",name().c_str());
                m_stop_simulation = false;
            }
        else
            {
                PRINTF("CC_XCACHE_WRAPPER \"%s\" : stop the simulation after %d cycles.\n",name().c_str(),nb_frz_cycles);
                m_stop_simulation = true;
                m_stop_simulation_nb_frz_cycles_max = nb_frz_cycles;
            }
#else
        std::cout << "CC_XCACHE_WRAPPER \"" << name() << "\" : flag CC_XCACHE_WRAPPER_STOP_SIMULATION is unset, you can't use stop_simulation." << std::endl;
#endif // CC_XCACHE_WRAPPER_STOP_SIMULATION
        
    }

}} // end namespace

// Local Variables:
// tab-width: 4
// c-basic-offset: 4
// c-file-offsets:((innamespace . 0)(inline-open . 0))
// indent-tabs-mode: nil
// End:

// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
