/*
 * SecBus - This file is part of SecBus
 * Copyright (C) 2012 - Telecom ParisTech
 * 
 * This file must be used under the terms of the CeCILL.
 * This source file is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at    
 * http://www.cecill.info/licences/Licence_CeCILL_V1.1-US.txt
 *
 */

#include "vci_secbus_v2.h"
#include "debug.h"
#include "stats.h"
#include <stdlib.h>
#include <iostream>
#include <iomanip>

namespace soclib
{
  namespace caba
  {
#define tmpl(x) template<typename vci_param> x VciSecbusV2<vci_param>
    tmpl(/**/)::VciSecbusV2(
        sc_module_name name,
        const MappingTable &mt,
        const IntTab &tgtid)
      : 
        soclib::caba::BaseModule(name), 
        p_clk("clk"), 
        p_resetn("resetn"),
        p_vci_target("p_vci_target"),
        p_vci_initiator("p_vci_initiator"),
        p_vci_io_target("p_vci_io_target"), 
	m_vci_target_fsm(p_vci_io_target, mt.getSegmentList(tgtid))
    {
      m_vci_target_fsm.on_read_write(on_read, on_write);

      ADD_DEBUG(IO, "traces/io_access");
      ADD_DEBUG(IN, "traces/input_access");
      ADD_DEBUG(OUT, "traces/output_access");
      ADD_DEBUG(FSM, "traces/fsm");
      ADD_DEBUG(REGISTER, "traces/registers");
      ADD_DEBUG(STD, stderr);

/*
      ADD_DEBUG(IO, "traces/io_access");
      ADD_DEBUG(IN, stderr);
      ADD_DEBUG(OUT, stderr);
      ADD_DEBUG(FSM, "traces/fsm");
      ADD_DEBUG(REGISTER, "traces/registers");
      ADD_DEBUG(STD, stderr);
*/

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

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

      //PSPE cache
      strcpy(pspe_cache.name,"PSPE");
      pspe_cache.enable = true;
      pspe_cache.associativity = 4;
      pspe_cache.block_size = PSPE_SIZE_IN_WORDS;
      pspe_cache.replacement_policy = FIFO;
      pspe_cache.set_number = 1;
      pspe_cache.write_policy = WRITE_THROUGH;
      //SP Cache
      strcpy(sp_cache.name,"SP");
      sp_cache.enable = true;
      sp_cache.associativity = 2;
      sp_cache.block_size = SP_SIZE_IN_WORDS/2; // only half SP per entry
      sp_cache.replacement_policy = FIFO;
      sp_cache.set_number = 4;
      sp_cache.write_policy = WRITE_THROUGH;
      //MACTree cache
      strcpy(mactree_cache.name,"MACTree");
      mactree_cache.enable = true;
      mactree_cache.associativity = 4;
      mactree_cache.block_size = SIZE_64_IN_WORDS;
      mactree_cache.replacement_policy = FIFO;
      mactree_cache.set_number = 16;
      mactree_cache.write_policy = WRITE_BACK;
      mactree_cache.threshold = mactree_cache.associativity/2;
      //Mac Cache
      strcpy(mac_cache.name,"MAC");
      mac_cache.enable = true;
      mac_cache.associativity = 4;
      mac_cache.block_size = SIZE_64_IN_WORDS;
      mac_cache.replacement_policy = FIFO;
      mac_cache.set_number = 32;
      mac_cache.write_policy = WRITE_BACK;

      // modules instantiation
      vci_merge = new VciMerge<vci_param>("VciMerge", 2);
      vci_split = new VciSplit<vci_param>("VciSplit");
      vci_mem_ctrl = new VciMemCtrl<vci_param>("VciMemCtrl", 5);
      vci_input_ctrl = new VciInputCtrl<vci_param>("VciInputCtrl");
      crypto_engine_conf = new CryptoEngine_conf("CryptoEngineC", 4);
      crypto_engine_int = new CryptoEngine_int("CryptoEngineI");
      securityctx_ctrl = new SecurityCtx_Ctrl("SecurityCtx_Ctrl",
					      pspe_cache,
					      sp_cache);
      security_ctrl = new Security_Ctrl("Security_Ctrl");

      sc_arbiter = new ScArbiter("ScArbiter", 2);
      crypto_arbiter = new CryptoArbiter("CryptoArbiter", 2);

      mem_arbiter = new MemArbiter("MemArbiter", 6);
      mux_reg0 = new Mux<DATA_REGISTER_WRITE_IN, DATA_REGISTER_WRITE_OUT>("MuxReg0", 4);
      mux_reg1 = new Mux<DATA_REGISTER_WRITE_IN, DATA_REGISTER_WRITE_OUT>("MuxReg1", 2);
      mux_reg2 = new Mux<DATA_REGISTER_WRITE_IN, DATA_REGISTER_WRITE_OUT>("MuxReg2", 3);
      mux_reg3 = new Mux<DATA_REGISTER_WRITE_IN, DATA_REGISTER_WRITE_OUT>("MuxReg3", 3);

      mt_mem_cmd = new GenericCmdFifo<MEMORY_CTRL_CMD_IN, MEMORY_CTRL_CMD_OUT, MEMORY_CTRL_CMD_SIGNALS>("FifoCmdMemMT");
      mtcache_mem_cmd = new GenericCmdFifo<MEMORY_CTRL_CMD_IN, MEMORY_CTRL_CMD_OUT, MEMORY_CTRL_CMD_SIGNALS>("FifoCmdMemMTC");
      mt_crypto_cmd = new GenericCmdFifo<CRYPTO_CMD_IN, CRYPTO_CMD_OUT, CRYPTO_CMD_SIGNALS>("FifoCmdCryMT");
      ms_crypto_cmd = new GenericCmdFifo<CRYPTO_CMD_IN, CRYPTO_CMD_OUT, CRYPTO_CMD_SIGNALS>("FifoCmdCryMS");
      ms_mem_cmd = new GenericCmdFifo<MEMORY_CTRL_CMD_IN, MEMORY_CTRL_CMD_OUT, MEMORY_CTRL_CMD_SIGNALS>("FifoCmdMemMS");
      mscache_mem_cmd = new GenericCmdFifo<MEMORY_CTRL_CMD_IN, MEMORY_CTRL_CMD_OUT, MEMORY_CTRL_CMD_SIGNALS>("FifoCmdMemMSC");

      mux_int_direct_data = new Mux<DATA_IN<12>, DATA_OUT<12> >("MuxDirectWrite", 2);
//      mux_crypto = new Mux<CRYPTO_CMD_IN, CRYPTO_CMD_OUT>("MuxCrypto", 2);
      mux_scctx = new Mux<SECURITY_CTX_CMD_IN, SECURITY_CTX_CMD_OUT>("MuxScCtx", 2);
      mux_direct_write = new Mux<DATA_IN<2>, DATA_OUT<2> >("MuxDirectWrite", 4);
      mux_mt_cmd = new Mux<INTEGRITY_CTRL_CMD_IN, INTEGRITY_CTRL_CMD_OUT>("MuxMtCmd", 2);

      irq_handler = new IrqHandler("IrqHandler", 4);

      mt_ctrl = new MT_Ctrl("MACTreeCtrl");
      mtcache_ctrl = new MTCache_Ctrl("MACTreeCacheCtrl", mactree_cache);
      ms_ctrl = new MS_Ctrl("MACSetCtrl");
      mscache_ctrl = new MSCache_Ctrl("MACSetCacheCtrl", mac_cache);

/*

      mactree_ctrl = new MacTreeCtrl("MacTreeCtrl");
      mac_ctrl = new MacCtrl("MacCtrl", mac_cache.enable);

      maccache_ctrl = new MacCacheCtrl("MacCacheCtrl", mac_cache);
      mactreecache_ctrl = new MacTreeCacheCtrl("MacTreeCacheCtrl", mactree_cache);
*/
      register_0 = new DataRegister("register_0");
      register_1 = new DataRegister("register_1");
      register_2 = new DataRegister("register_2");
      register_3 = new DataRegister("register_3");
      register_4 = new DataRegister("register_4");
      
      // modules connection
      vci_split->p_clk               (p_clk);
      vci_split->p_resetn            (p_resetn);
      vci_split->p_vci_initiator     (p_vci_target);
      vci_split->p_vci_target_sec    (split_to_sec);
      vci_split->p_vci_target_unsec  (split_to_merge);
      vci_split->p_addr              (reg_b_addr_s);
      vci_split->p_size              (reg_size_s);
      vci_split->p_enable            (reg_enable_s);
				     
      vci_merge->p_clk               (p_clk);
      vci_merge->p_resetn            (p_resetn);
      vci_merge->p_vci_initiator[0]  (memctrl_to_merge);
      vci_merge->p_vci_initiator[1]  (split_to_merge);
      vci_merge->p_vci_target        (p_vci_initiator);
				     
      vci_mem_ctrl->p_clk            (p_clk);
      vci_mem_ctrl->p_resetn         (p_resetn);
      vci_mem_ctrl->p_vci_initiator  (memctrl_to_merge);
      vci_mem_ctrl->mem_rq           (mem_arbiter_to_mem_ctrl);
      vci_mem_ctrl->in[0]            (mem_arbiter_to_register[0]);
      vci_mem_ctrl->in[1]            (mem_arbiter_to_register[1]);
      vci_mem_ctrl->in[2]            (mem_arbiter_to_register[2]);
      vci_mem_ctrl->in[3]            (mem_arbiter_to_register[3]);
      vci_mem_ctrl->in[4]            (mem_arbiter_to_register[4]);
      vci_mem_ctrl->out[0]           (register_to_mem_arbiter[0]);
      vci_mem_ctrl->out[1]           (register_to_mem_arbiter[1]);
      vci_mem_ctrl->out[2]           (register_to_mem_arbiter[2]);
      vci_mem_ctrl->out[3]           (register_to_mem_arbiter[3]);
      vci_mem_ctrl->out[4]           (reg_to_mtcache_ctrl);
      vci_mem_ctrl->wr_data          (direct_data);
				     
      register_0->p_clk              (p_clk);
      register_0->p_resetn           (p_resetn);
      register_0->in                 (mux_to_reg0);
      register_0->out                (register_to_mem_arbiter[0]);
				     
      register_1->p_clk              (p_clk);
      register_1->p_resetn           (p_resetn);
      register_1->in                 (mux_to_reg1);
      register_1->out                (register_to_mem_arbiter[1]);
				     
      register_2->p_clk              (p_clk);
      register_2->p_resetn           (p_resetn);
      register_2->in                 (mux_to_reg2);
      register_2->out                (register_to_mem_arbiter[2]);

      register_3->p_clk              (p_clk);
      register_3->p_resetn           (p_resetn);
      register_3->in                 (mux_to_reg3);
      register_3->out                (register_to_mem_arbiter[3]);

      register_4->p_clk              (p_clk);
      register_4->p_resetn           (p_resetn);
      register_4->in                 (mem_arbiter_to_register[4]);
      register_4->out                (reg_to_mtcache_ctrl);
				     
      mem_arbiter->p_clk             (p_clk);
      mem_arbiter->p_resetn          (p_resetn);
      mem_arbiter->mem_ctrl          (mem_arbiter_to_mem_ctrl);
      mem_arbiter->mem_rq[0]         (mem_rq_to_mem_arbiter[0]);
      mem_arbiter->mem_rq[1]         (mem_rq_to_mem_arbiter[1]);
      mem_arbiter->mem_rq[2]         (mtfifo_to_memctrl);
      mem_arbiter->mem_rq[3]         (mtcache_to_memctrl);
      mem_arbiter->mem_rq[4]         (msfifo_to_memctrl);
      mem_arbiter->mem_rq[5]         (mscache_to_memctrl);
				     
      vci_input_ctrl->p_clk          (p_clk);
      vci_input_ctrl->p_resetn       (p_resetn);
      vci_input_ctrl->p_vci_initiator(split_to_sec);
      vci_input_ctrl->mem_rq         (mem_rq_to_mem_arbiter[0]);
      vci_input_ctrl->ctx_cmd        (input_ctrl_to_ctx_cmd);
      vci_input_ctrl->ctx_register   (ctx_reg_to_sc);
      vci_input_ctrl->mt_cmd         (input_to_mt);
      vci_input_ctrl->mt_ack         (mt_ack);
      vci_input_ctrl->ms_ack         (ms_ack);
      vci_input_ctrl->sc_cmd         (input_ctrl_to_sc);
      vci_input_ctrl->r_register_0   (register_to_mem_arbiter[0]);
      vci_input_ctrl->w_register_0   (input_ctrl_to_mux_reg_0);
      vci_input_ctrl->r_register_3   (register_to_mem_arbiter[3]);
      vci_input_ctrl->w_register_3   (input_ctrl_to_mux_reg_3);
      vci_input_ctrl->io_handler     (io_block);
      vci_input_ctrl->io_ack         (io_ack);
      vci_input_ctrl->irq            (irq_inputctrl);
      vci_input_ctrl->p_b_address    (reg_b_addr_s);
      vci_input_ctrl->p_size         (reg_size_s);
      vci_input_ctrl->p_mb_address   (reg_mb_addr_s);
      vci_input_ctrl->busy           (secbus_busy);
      vci_input_ctrl->sc_busy        (sc_busy_s);
      vci_input_ctrl->io_handler_write(io_data_wr);

      securityctx_ctrl->p_clk        (p_clk);
      securityctx_ctrl->p_resetn     (p_resetn);
      securityctx_ctrl->p_cache_sp   (reg_c_sp_s);
      securityctx_ctrl->p_cache_pspe (reg_c_pspe_s);
      securityctx_ctrl->p_mb_address (reg_mb_addr_s);
      securityctx_ctrl->p_size       (reg_size_s);
      securityctx_ctrl->p_b_address  (reg_b_addr_s);
      securityctx_ctrl->ctx_cmd      (mux_to_scctx);
      securityctx_ctrl->ctx_register (ctx_reg_to_sc);
      securityctx_ctrl->r_register_0 (register_to_mem_arbiter[0]);
      securityctx_ctrl->w_register_2 (securityctx_ctrl_to_reg_2);
      securityctx_ctrl->sc_out_cmd   (ctx_ctrl_to_sc);
      securityctx_ctrl->irq          (irq_ctxctrl);
      securityctx_ctrl->w_data       (direct_pspe);

      security_ctrl->p_clk           (p_clk);
      security_ctrl->p_resetn        (p_resetn);
      security_ctrl->p_b_address     (reg_b_addr_s);
      security_ctrl->p_size          (reg_size_s);
      security_ctrl->p_mb_address    (reg_mb_addr_s);
      security_ctrl->cmd             (sc_rq);
      security_ctrl->mt_cmd          (sc_ctrl_to_mt);
      security_ctrl->mt_ack          (mt_ack);
      security_ctrl->ms_cmd          (sc_ctrl_to_ms);
      security_ctrl->ms_ack          (ms_ack);
      security_ctrl->r_register_0    (register_to_mem_arbiter[0]);
      security_ctrl->w_register_0    (security_ctrl_to_mux_reg_0);
      security_ctrl->r_register_1    (register_to_mem_arbiter[1]);
      security_ctrl->r_register_2    (register_to_mem_arbiter[2]);
      security_ctrl->r_register_ad   (register_to_mem_arbiter[3]);
      security_ctrl->mem_rq          (mem_rq_to_mem_arbiter[1]);
      security_ctrl->crypto_cmd      (crypto_cmd);
      security_ctrl->ctx_register    (ctx_reg_to_sc);
      security_ctrl->master_ctx_init (master_ctx_from_input);
      security_ctrl->master_ctx      (master_ctx_to_ht);
      security_ctrl->master_update   (ht_to_master_ctx); 
      security_ctrl->secbus_enable   (reg_enable_s);
      security_ctrl->ack             (sc_busy_s);

      sc_arbiter->p_clk              (p_clk);
      sc_arbiter->p_resetn           (p_resetn);
      sc_arbiter->sc_ctrl            (sc_rq);
      sc_arbiter->sc_rq[0]           (ctx_ctrl_to_sc);
      sc_arbiter->sc_rq[1]           (input_ctrl_to_sc);

      crypto_engine_conf->p_clk      (p_clk);
      crypto_engine_conf->p_resetn   (p_resetn);
      crypto_engine_conf->p_crypto   (crypto_cmd);
      crypto_engine_conf->r_register[0](register_to_mem_arbiter[0]);
      crypto_engine_conf->w_register[0](crypto_module_to_register[0]);
      crypto_engine_conf->r_register[1](register_to_mem_arbiter[1]);
      crypto_engine_conf->w_register[1](crypto_module_to_register[1]);
      crypto_engine_conf->r_register[2](register_to_mem_arbiter[2]);
      crypto_engine_conf->w_register[2](crypto_module_to_register[2]);
      crypto_engine_conf->r_register[3](register_to_mem_arbiter[3]);
      crypto_engine_conf->w_register[3](crypto_module_to_register[3]);

      crypto_engine_int->p_clk       (p_clk);
      crypto_engine_int->p_resetn    (p_resetn);
      crypto_engine_int->p_crypto    (mux_to_crypto_cmd);
      crypto_engine_int->out_mac     (crypto_mac);
      crypto_engine_int->direct_data (integrity_direct_data);

      mt_ctrl->p_clk                 (p_clk);
      mt_ctrl->p_resetn              (p_resetn);
      mt_ctrl->p_b_address           (reg_b_addr_s);
      mt_ctrl->p_mb_address          (reg_mb_addr_s);
      mt_ctrl->p_size                (reg_size_s);
      mt_ctrl->cmd_in                (mux_to_mt);
      mt_ctrl->ack_out               (mt_ack);
      mt_ctrl->hash_out_cmd          (mt_to_cache_cmd);
      mt_ctrl->hash_out_wdata        (mt_to_cache_data);
      mt_ctrl->hash_in_ack           (mt_to_cache_ack);
      mt_ctrl->hash_in_rdata         (cache_to_mt_data);
      mt_ctrl->sc_out_cmd            (mt_to_scctx_cmd);
      mt_ctrl->sc_out_wdata          (direct_pspe);
      mt_ctrl->sc_register_in        (ctx_reg_to_sc);
      mt_ctrl->master_sc_reg_in      (master_ctx_to_ht);
      mt_ctrl->master_sc_reg_out     (ht_to_master_ctx);
      mt_ctrl->hash_generator_out_cmd(mt_to_genfifo);
      mt_ctrl->hash_generator_out_data(data_from_mt);
      mt_ctrl->hash_generator_in_data(crypto_mac);
      mt_ctrl->register_0_in         (register_to_mem_arbiter[0]);
      mt_ctrl->register_1_in         (register_to_mem_arbiter[1]);
      mt_ctrl->register_2_in         (register_to_mem_arbiter[2]);
      mt_ctrl->memory_out_cmd        (mt_to_memctrl);
      mt_ctrl->memory_out_wdata      (direct_data_mt);
      mt_ctrl->irq                   (irq_mtctrl);

      mtcache_ctrl->p_clk            (p_clk);
      mtcache_ctrl->p_resetn         (p_resetn);
      mtcache_ctrl->in_cmd           (mt_to_cache_cmd);
      mtcache_ctrl->in_wdata         (mt_to_cache_data);
      mtcache_ctrl->out_ack          (mt_to_cache_ack);
      mtcache_ctrl->out_rdata        (cache_to_mt_data);
      mtcache_ctrl->out_cmd          (mtcache_to_genfifo);
      mtcache_ctrl->out_wdata        (direct_data_mtcache);
      mtcache_ctrl->register_2_in    (reg_to_mtcache_ctrl);

      ms_ctrl->p_clk                 (p_clk);
      ms_ctrl->p_resetn              (p_resetn);
      ms_ctrl->p_cache               (reg_c_mac_s);
      ms_ctrl->cmd_in                (sc_ctrl_to_ms);
      ms_ctrl->ack_out               (ms_ack);
      ms_ctrl->cache_out_cmd         (ms_to_cache_cmd);
      ms_ctrl->cache_out_wdata       (ms_to_cache_wdata);
      ms_ctrl->cache_in_ack          (ms_to_cache_ack);
      ms_ctrl->cache_in_rdata        (cache_to_ms_rdata);
      ms_ctrl->mac_generator_out_cmd (ms_to_genfifo);
      ms_ctrl->mac_generator_out_data(data_from_ms);
      ms_ctrl->mac_generator_in_data (crypto_mac);
      ms_ctrl->sc_register           (ctx_reg_to_sc);
      ms_ctrl->register_0_in         (register_to_mem_arbiter[0]);
      ms_ctrl->register_1_in         (register_to_mem_arbiter[1]);
      ms_ctrl->register_2_in         (register_to_mem_arbiter[2]);
      ms_ctrl->memory_out_cmd        (ms_to_memctrl);
      ms_ctrl->memory_out_wdata      (direct_data_ms);
      ms_ctrl->irq                   (irq_msctrl);

      mscache_ctrl->p_clk            (p_clk);
      mscache_ctrl->p_resetn         (p_resetn);
      mscache_ctrl->in_cmd           (ms_to_cache_cmd);
      mscache_ctrl->in_wdata         (ms_to_cache_wdata);
      mscache_ctrl->out_ack          (ms_to_cache_ack);
      mscache_ctrl->out_rdata        (cache_to_ms_rdata);
      mscache_ctrl->out_cmd          (mscache_to_genfifo);
      mscache_ctrl->out_wdata        (direct_data_mscache);
      mscache_ctrl->register_2_in    (reg_to_mtcache_ctrl);//(register_to_mem_arbiter[2]);

      mt_mem_cmd->p_clk              (p_clk);
      mt_mem_cmd->p_resetn           (p_resetn);
      mt_mem_cmd->input              (mt_to_memctrl);
      mt_mem_cmd->output             (mtfifo_to_memctrl);

      mtcache_mem_cmd->p_clk         (p_clk);
      mtcache_mem_cmd->p_resetn      (p_resetn);
      mtcache_mem_cmd->input         (mtcache_to_genfifo);
      mtcache_mem_cmd->output        (mtcache_to_memctrl);

      ms_mem_cmd->p_clk              (p_clk);
      ms_mem_cmd->p_resetn           (p_resetn);
      ms_mem_cmd->input              (ms_to_memctrl);
      ms_mem_cmd->output             (msfifo_to_memctrl);

      mscache_mem_cmd->p_clk         (p_clk);
      mscache_mem_cmd->p_resetn      (p_resetn);
      mscache_mem_cmd->input         (mscache_to_genfifo);
      mscache_mem_cmd->output        (mscache_to_memctrl);

      mt_crypto_cmd->p_clk           (p_clk);
      mt_crypto_cmd->p_resetn        (p_resetn);
      mt_crypto_cmd->input           (mt_to_genfifo);
      mt_crypto_cmd->output          (mt_to_crypto_cmd);

      ms_crypto_cmd->p_clk           (p_clk);
      ms_crypto_cmd->p_resetn        (p_resetn);
      ms_crypto_cmd->input           (ms_to_genfifo);
      ms_crypto_cmd->output          (ms_to_crypto_cmd);				     

      crypto_arbiter->p_clk          (p_clk);
      crypto_arbiter->p_resetn       (p_resetn);
      crypto_arbiter->sc_ctrl        (mux_to_crypto_cmd);
      crypto_arbiter->sc_rq[0]       (mt_to_crypto_cmd);
      crypto_arbiter->sc_rq[1]       (ms_to_crypto_cmd);

/*      mux_crypto->input[0]           (mt_to_crypto_cmd);
      mux_crypto->input[1]           (ms_to_crypto_cmd);
      mux_crypto->output             (mux_to_crypto_cmd);
*/
      mux_int_direct_data->input[0]  (data_from_mt);
      mux_int_direct_data->input[1]  (data_from_ms);
      mux_int_direct_data->output    (integrity_direct_data);

      irq_handler->p_clk             (p_clk);
      irq_handler->p_resetn          (p_resetn);
      irq_handler->irq_out           (irq_out);
      irq_handler->irq_in[0]         (irq_ctxctrl);
      irq_handler->irq_in[1]         (irq_inputctrl);
      irq_handler->irq_in[2]         (irq_mtctrl);
      irq_handler->irq_in[3]         (irq_msctrl);

      mux_scctx->input[0]            (mt_to_scctx_cmd);
      mux_scctx->input[1]            (input_ctrl_to_ctx_cmd);
      mux_scctx->output              (mux_to_scctx);
      
      mux_direct_write->input[0]     (direct_data_mtcache);
      mux_direct_write->input[1]     (direct_data_mt);
      mux_direct_write->input[2]     (direct_data_mscache);
      mux_direct_write->input[3]     (direct_data_ms);
      mux_direct_write->output       (direct_data);

      mux_mt_cmd->input[0]           (sc_ctrl_to_mt);
      mux_mt_cmd->input[1]           (input_to_mt);
      mux_mt_cmd->output             (mux_to_mt);

      mux_reg0->input[0]             (crypto_module_to_register[0]);
      mux_reg0->input[1]             (mem_arbiter_to_register[0]);
      mux_reg0->input[2]             (input_ctrl_to_mux_reg_0);
      mux_reg0->input[3]             (security_ctrl_to_mux_reg_0);
      mux_reg0->output               (mux_to_reg0);

      mux_reg1->input[0]             (crypto_module_to_register[1]);
      mux_reg1->input[1]             (mem_arbiter_to_register[1]);
      mux_reg1->output               (mux_to_reg1);

      mux_reg2->input[0]             (crypto_module_to_register[2]);
      mux_reg2->input[1]             (mem_arbiter_to_register[2]);
      mux_reg2->input[2]             (securityctx_ctrl_to_reg_2);
      mux_reg2->output               (mux_to_reg2);

      mux_reg3->input[0]             (crypto_module_to_register[3]);
      mux_reg3->input[1]             (mem_arbiter_to_register[3]);
      mux_reg3->input[2]             (input_ctrl_to_mux_reg_3);
      mux_reg3->output               (mux_to_reg3);

      m_cycle = 0;

      DEBUG(NONE, STD, "[SECBUS] Builting completed\n");
    }

    tmpl(/**/)::~VciSecbusV2()
    {
      CLOSE_DEBUG();
      delete register_0;
      delete register_1;
      delete register_2;
      delete register_3;
      delete vci_input_ctrl;
      delete crypto_engine_conf;
      delete crypto_engine_int;
      delete security_ctrl;
      delete securityctx_ctrl;
      delete mem_arbiter;
      delete vci_mem_ctrl;
      delete vci_merge;
      delete vci_split;
      delete mux_reg0;
      delete mux_reg1;
      delete mux_reg2;
      delete mux_reg3;
    }

    tmpl(bool)::on_write(int seg, 
			 typename vci_param::addr_t addr,
			 typename vci_param::data_t data,
			 int be)
    {
      int cell = (int)addr / vci_param::B;
      DEBUG(TRACE, IO, "W 0x%08x %08x (cycle %u)\n", 
	    (addr_t)(m_vci_target_fsm.getBase(seg)+addr), (uint32_t)data, m_cycle); 
      switch((enum secbus_io_register_e)cell)
      {
      case secbus_io_config:
	io_config_s = data;
	return true;

      case secbus_io_sp_0:
      case secbus_io_sp_1:
      case secbus_io_sp_2:
      case secbus_io_sp_3:
      case secbus_io_sp_4:
      case secbus_io_sp_5:
      case secbus_io_sp_6:
      case secbus_io_sp_7:
      case secbus_io_sp_8:
      case secbus_io_sp_9:
      case secbus_io_sp_10:
      case secbus_io_sp_11:
      case secbus_io_sp_12:
      case secbus_io_sp_13:
      case secbus_io_sp_14:
      case secbus_io_sp_15:
	io_sp_s[cell - secbus_io_sp_0] = data;
	return true;

      case secbus_io_pspe_0:
      case secbus_io_pspe_1:
	io_pspe_s[cell - secbus_io_pspe_0] = data;
	return true;

      case secbus_io_block_data_0:
      case secbus_io_block_data_1:
      case secbus_io_block_data_2:
      case secbus_io_block_data_3:
      case secbus_io_block_data_4:
      case secbus_io_block_data_5:
      case secbus_io_block_data_6:
      case secbus_io_block_data_7:
	io_block_data_s[cell - secbus_io_block_data_0] = data;
	return true;

      case secbus_io_block_addr:
	io_block_addr_s = data;
	return true;

      case secbus_io_cmd:
	io_cmd_s = data;
	return true;

      default:
	DEBUG(ERROR, STD, "[SECBUS] SecBus IO register write error (addr = 0x%08x)\n", 
	      (addr_t)(m_vci_target_fsm.getBase(seg)+addr));
	return false;
      }
    }

    tmpl(bool)::on_read(int seg, 
			typename vci_param::addr_t addr,
			typename vci_param::data_t &data, 
			int be)
    {
      DEBUG(TRACE, IO, "R 0x%08x %08x (cycle %u)\n", 
	    (addr_t)(m_vci_target_fsm.getBase(seg)+addr), (uint32_t)data, m_cycle); 
      int cell = (int)addr / vci_param::B;
      switch((enum secbus_io_register_e)cell)
      {
      case secbus_io_config:
	data = io_config_s;
	return true;
	
      case secbus_io_status:
	irq_ack_s = true;
	data = io_status_s;
	return true;

      case secbus_io_block_data_0:
      case secbus_io_block_data_1:
      case secbus_io_block_data_2:
      case secbus_io_block_data_3:
      case secbus_io_block_data_4:
      case secbus_io_block_data_5:
      case secbus_io_block_data_6:
      case secbus_io_block_data_7:
//	data = io_block_data_s[cell - secbus_io_block_data_0];
	data = io_data_wr.data[cell - secbus_io_block_data_0];
	return true;

      default:
	DEBUG(ERROR, STD, "[SECBUS] SecBus IO register read error (addr = 0x%08x)\n", 
	      (addr_t)(m_vci_target_fsm.getBase(seg)+addr));
	return false;
      }
    }

    tmpl(void)::transition()
    {
      m_cycle++;

      if (!p_resetn.read())
      {
	io_config_s = 0;
	io_status_s = 0;
	io_cmd_s = 0;
	io_block_addr_s = 0;

	for (size_t i=0;i<SP_SIZE_IN_WORDS;i++)
	  io_sp_s[i] = 0;

	for (size_t i=0;i<PSPE_SIZE_IN_WORDS;i++)
	  io_pspe_s[i] = 0;

	for (size_t i=0;i<BLOCK_DATA_SIZE_IN_WORDS;i++)
	  io_block_data_s[i] = 0;

	m_cycle = 0;
	irq_ack_s = false;

	irq_signal_s = false;

	return;
      }

      DEBUG(TRACE, FSM, "-------- cycle %u --------\n", m_cycle);

      // reset command on block ack
      if (io_ack.read())
	io_cmd_s = 0;

      if (reg_int_s.read())
	irq_ack_s = false;
      else
	irq_ack_s = true;

      m_vci_target_fsm.transition();
    }

    tmpl(void)::genMoore()
    {
      // stats update;
      S_UPDATE();

      // IO signal update
      uint32_t bram = 0x1000000*io_config_getBRAM(&io_config_s);
      uint32_t sram = 0x4000000-1;//0x1000000*(io_config_getSRAM(&io_config_s) + 1) - 1;
      uint32_t mbaddr = io_config_getMBAddr(&io_config_s)<<24;

      uint32_t pspeBA = getPSPEBaseAddress(mbaddr, sram);
      uint32_t spBA = getSPBaseAddress(mbaddr, sram);
      uint32_t mtBA = getMMacTBaseAddress(mbaddr, sram);

      // debug print on signal update
      if (reg_enable_s.read() != io_config_getEN(&io_config_s))
      {
	DEBUG(NONE, STD, "[SECBUS] SecBus is now %s\n", 
	      (io_config_getEN(&io_config_s)) ? "ENABLE" : "DISABLE");
      }
      
      if (reg_b_addr_s.read() != bram ||
	   reg_size_s.read() != sram)
      {
	// avoid message printing on startup
	if (reg_size_s.read() != 0 || io_config_getSRAM(&io_config_s) != 0)
	  DEBUG(NONE, STD, "[SECBUS] protected memory begins at 0x%x (%u MBytes)\n", 
		bram, 
		/*16*(io_config_getSRAM(&io_config_s)+1)*/0x40);
      }
      
      if (reg_mb_addr_s.read() != (io_config_getMBAddr(&io_config_s)<<24))
      {
	DEBUG(NONE, STD, "[SECBUS] MasterBlock begins at address 0x%x (%u KBytes)\n",
	      mbaddr, (0x1000*((io_config_getSRAM(&io_config_s)+1))>>6));
	DEBUG(NONE, STD, "[SECBUS]\tPSPE range\t[0x%08x-0x%08x] %u PSPE\n",
	      pspeBA, spBA - 1, (spBA - pspeBA)/PSPE_SIZE_IN_BYTES);
	DEBUG(NONE, STD, "[SECBUS]\t\tPSPE Level\t");
	for (size_t i=0;i<getPageLevel();i++)
	{
	  if (i!=0)
	    DEBUG(NONE, STD, "[SECBUS]\t\t\t\t-");
	  else
	    DEBUG(NONE, STD, "-");
	  printWithUnit(getPageSize(i));
	  DEBUG(NONE, STD, "\n");
	}

	DEBUG(NONE, STD, "[SECBUS]\tSP range\t[0x%08x-0x%08x] %u SP\n",
	      spBA, mtBA - 1, (mtBA - spBA)/SP_SIZE_IN_BYTES);
	DEBUG(NONE, STD, "[SECBUS]\tMMTree range\t[0x%08x-0x%08x]\n",
	      mtBA, mtBA + (spBA - pspeBA)/2 - 1);
      }

      if (reg_c_sp_s.read() != io_config_getSPC(&io_config_s))
      {
	DEBUG(NONE, STD, "[SECBUS] SP Cache is now %s\n", 
	      (io_config_getSPC(&io_config_s)) ? "ENABLE" : "DISABLE");
      }

      if (reg_c_pspe_s.read() != io_config_getPC(&io_config_s))
      {
	DEBUG(NONE, STD, "[SECBUS] PSPE Cache is now %s\n", 
	      (io_config_getPC(&io_config_s)) ? "ENABLE" : "DISABLE");
      }

      if (reg_c_mac_s.read() != io_config_getMACC(&io_config_s))
      {
	DEBUG(NONE, STD, "[SECBUS] Mac Cache is now %s\n", 
	      (io_config_getMACC(&io_config_s)) ? "ENABLE" : "DISABLE");
      }

      if (reg_c_hmac_s.read() != io_config_getHMACC(&io_config_s))
      {
	DEBUG(NONE, STD, "[SECBUS] HashMac Cache is now %s\n", 
	      (io_config_getHMACC(&io_config_s)) ? "ENABLE" : "DISABLE");
      }

      if (reg_int_s.read() != io_config_getInt(&io_config_s))
      {
	DEBUG(NONE, STD, "[SECBUS] Interrupts are now %s\n",
	      (io_config_getInt(&io_config_s)) ? "ENABLE" : "DISABLE");
      }

      // enable signal update
      reg_enable_s = io_config_getEN(&io_config_s);
      // protected memory start address update
      reg_b_addr_s = bram;
      // protected memory size update
      reg_size_s = sram;
      // base address of masterblock
      reg_mb_addr_s = mbaddr;
      // sp cache
      reg_c_sp_s = io_config_getSPC(&io_config_s);
      // pspe cache
      reg_c_pspe_s = io_config_getPC(&io_config_s);
      // mac cache
      reg_c_mac_s = io_config_getMACC(&io_config_s);
      // hmac cacche
      reg_c_hmac_s = io_config_getHMACC(&io_config_s);
      // interrupt
      reg_int_s = io_config_getInt(&io_config_s);

      for (uint32_t i=0;i<SIZE_256_IN_WORDS;i++)
      {
	io_block.data[i] = io_block_data_s[i];
      }

      io_block.addr = io_block_addr_s;
      io_block.cmd  = io_cmd_getCmd(&io_cmd_s);

      // IO directly to input block
      for (uint32_t i=0;i<SP_SIZE_IN_WORDS;i++)
	master_ctx_from_input.sp[i] = io_sp_s[i];

      for (uint32_t i=0;i<PSPE_SIZE_IN_WORDS;i++)
	master_ctx_from_input.pspe[i] = io_pspe_s[i];

      // irq signals
      io_status_errt_t errt = (io_status_errt_t)irq_out.type.read();
      io_status_errc_t errc = (io_status_errc_t)irq_out.op.read();

      io_reg_32_t secbus_status = 0;

      io_status_setErrT(&secbus_status, errt);
      io_status_setErrC(&secbus_status, errc);
      io_status_setBusy(&secbus_status, (bool)secbus_busy.read());
      io_status_setErrA(&secbus_status, (addr_t)(irq_out.address.read())>>5);

      io_status_s = secbus_status;

      if (io_config_getInt(&io_config_s))
      {
	if (errt == io_status_e_none)
	{
//	  DEBUG(NONE, STD, "irq cmd %u\n", irq_out.irq.read());
	  p_irq_c = irq_out.irq.read();
	}
	else
	{
//	  DEBUG(NONE, STD, "irq err %u\n", irq_out.irq.read());
	  p_irq_e = irq_out.irq.read();
	}
      }
      else
      {
	p_irq_c = false;
	p_irq_e = false;
      }

      irq_out.ack = irq_ack_s.read();

      m_vci_target_fsm.genMoore();
    }
  }
}
