/* -*- c++ -*-
  * File : vci_vdspin_initiator_wrapper.cpp
  * Copyright (c) UPMC, Lip6
  * Authors : Alain Greiner
  *
  * 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
  */

#include "../include/vci_vdspin_initiator_wrapper.h"

namespace soclib { namespace caba {

#define tmpl(x) template<typename vci_param, int dspin_cmd_width, int dspin_rsp_width> x VciVdspinInitiatorWrapper<vci_param, dspin_cmd_width, dspin_rsp_width>

//////////////////////////////////////////////////////////:////////////////////////////////
tmpl(/**/)::VciVdspinInitiatorWrapper(sc_module_name 			name,
				      size_t				cmd_fifo_depth,
				      size_t				rsp_fifo_depth)
	       : soclib::caba::BaseModule(name),
                 p_clk("p_clk"),
                 p_resetn("p_resetn"),
                 p_dspin_out("p_dspin_out"),
                 p_dspin_in("p_dspin_in"),
                 p_vci("p_vci"),
                 r_cmd_fsm("r_cmd_fsm"),
                 r_rsp_fsm("r_rsp_fsm"),
	         r_fifo_cmd("r_fifo_cmd", cmd_fifo_depth),
	         r_fifo_rsp("r_fifo_rsp", rsp_fifo_depth)
    {
	SC_METHOD (transition);
	dont_initialize();
	sensitive << p_clk.pos();
	SC_METHOD (genMoore);
	dont_initialize();
	sensitive  << p_clk.neg();

        assert( (dspin_cmd_width == 40) && "The DSPIN CMD flit width must have 40 bits");
        assert( (dspin_rsp_width == 33) && "The DSPIN RSP flit width must have 33 bits");
        assert( (vci_param::N    <= 40) && "The VCI ADDRESS field cannot have more than 40 bits"); 
        assert( (vci_param::B    == 4) && "The VCI DATA filds must have 32 bits");
        assert( (vci_param::K    == 8) && "The VCI PLEN field cannot have more than 8 bits");
        assert( (vci_param::S    <= 14) && "The VCI SRCID field cannot have more than 8 bits");
        assert( (vci_param::T    <= 8) && "The VCI TRDID field cannot have more than 8 bits");
        assert( (vci_param::E    == 2) && "The VCI RERROR field cannot have more than 2 bits");

    } //  end constructor

/////////////////////////
tmpl(void)::transition()
{
	sc_uint<dspin_cmd_width>	cmd_fifo_data;
	bool				cmd_fifo_write;
	bool				cmd_fifo_read;

	sc_uint<dspin_rsp_width>	rsp_fifo_data;
	bool				rsp_fifo_write;
	bool				rsp_fifo_read;

	if (p_resetn == false) 
        {
	    r_fifo_cmd.init();
	    r_fifo_rsp.init();
	    r_cmd_fsm = CMD_IDLE;
	    r_rsp_fsm = RSP_IDLE;
	    return;
	} // end reset

        /////////////////////////////////////////////////////////////
	// VCI command packet to DSPIN command packet
	// The VCI packet is analysed, translated,
	// and the DSPIN packet is stored in the fifo_cmd
        /////////////////////////////////////////////////////////////
        // - A N flits VCI write command packet is translated
        //   to a N+2 flits DSPIN command.
        // - A single flit VCI read command packet is translated 
        //   to a 2 flits DSPIN command.
        // - A single flit VCI broadcast packet is translated to
        //   a 2 flits DSPIN command.
        // A DSPIN flit is written in the fifo_cmd in all states
        // but a VCI flit is consumed only in the CMD_READ,
        // CMD_BROACAST,  and CMD_WDATA states.
        //////////////////////////////////////////////////////////////

	// cmd_fifo_read
	cmd_fifo_read = p_dspin_out.read.read();

	// r_cmd_fsm, cmd_fifo_write and cmd_fifo_data
        cmd_fifo_write = false;		// default value

	switch(r_cmd_fsm) {
	    case CMD_IDLE:		// write first DSPIN flit into fifo_cmd
            {
		if( p_vci.cmdval && r_fifo_cmd.wok() ) 
                {
		    cmd_fifo_write = true;
                    sc_uint<dspin_cmd_width> address = (sc_uint<dspin_cmd_width>)p_vci.address.read();
                    sc_uint<dspin_cmd_width> srcid   = (sc_uint<dspin_cmd_width>)p_vci.srcid.read();
                    sc_uint<dspin_cmd_width> trdid   = (sc_uint<dspin_cmd_width>)p_vci.trdid.read();
                    sc_uint<dspin_cmd_width> cmd     = (sc_uint<dspin_cmd_width>)p_vci.cmd.read();

                    bool is_broadcast = ( (address & 0x3) != 0);
                    bool is_read = ((cmd == vci_param::CMD_READ) || (cmd == vci_param::CMD_LOCKED_READ));

                    if ( vci_param::N == 40 ) address = address >> 1;
                    else                      address = address << (39 - vci_param::N);              

                    if ( is_broadcast )	// VCI broacast command
                    {
                        r_cmd_fsm     = CMD_BROADCAST;
                        cmd_fifo_data = (address      & 0x7FFFF80000) |
                                        ((srcid << 5) & 0x000007FFE0) | 
                                        ((trdid << 1) & 0x000000001E) | 
                                                        0x0000000001;		
                    }
                    else if (is_read )			// VCI READ  command
                    {
		        r_cmd_fsm     = CMD_READ;
                        cmd_fifo_data = address & 0x7FFFFFFFFE;
		    }
                    else 				// VCI WRITE command
                    {
		        r_cmd_fsm     = CMD_WRITE;
                        cmd_fifo_data = address & 0x7FFFFFFFFE;
		    }
		}
	 	break;
            }
	    case CMD_BROADCAST:		// write second DSPIN flit in case of broadcast
            {    
		if( p_vci.cmdval && r_fifo_cmd.wok() ) 
                {
                    cmd_fifo_write   = true;
                    sc_uint<dspin_cmd_width> data = (sc_uint<dspin_cmd_width>)p_vci.wdata.read();
                    sc_uint<dspin_cmd_width> be   = (sc_uint<dspin_cmd_width>)p_vci.be.read();
                    cmd_fifo_data    = (data       & 0x00FFFFFFFF) | 
                                       ((be << 32) & 0x0300000000) |
                                                     0x8000000000; 
                    r_cmd_fsm = CMD_IDLE;
                }
                break;
            }
            case CMD_READ:	// write second DSPIN flit in case of read/write
            case CMD_WRITE:
            {
		if( p_vci.cmdval && r_fifo_cmd.wok() ) 
                {
		    cmd_fifo_write      = true;
                    sc_uint<dspin_cmd_width> srcid   = (sc_uint<dspin_cmd_width>)p_vci.srcid.read();
                    sc_uint<dspin_cmd_width> trdid   = (sc_uint<dspin_cmd_width>)p_vci.trdid.read();
                    sc_uint<dspin_cmd_width> cmd     = (sc_uint<dspin_cmd_width>)p_vci.cmd.read();
                    sc_uint<dspin_cmd_width> plen    = (sc_uint<dspin_cmd_width>)p_vci.plen.read();
                    sc_uint<dspin_cmd_width> be      = (sc_uint<dspin_cmd_width>)p_vci.be.read();
		    cmd_fifo_data       = ((be    << 1 ) & 0x000000001E) |
                                          ((trdid << 5 ) & 0x0000001FE0) |
                                          ((plen  << 13) & 0x00001FE000) |
                                          ((cmd   << 23) & 0x0001800000) |
                                          ((srcid << 25) & 0x7FFE000000) ;
                    if ( p_vci.contig.read() ) cmd_fifo_data = cmd_fifo_data | 0x0000400000 ;
                    if ( p_vci.cons.read()   ) cmd_fifo_data = cmd_fifo_data | 0x0000200000 ;

                    if( r_cmd_fsm == CMD_READ )  // read command
                    {
                        r_cmd_fsm = CMD_IDLE;
                        cmd_fifo_data = cmd_fifo_data    | 0x8000000000 ;
                    }
                    else			// write command
                    {
                        r_cmd_fsm = CMD_WDATA;
                    }
		}
		break;
            }
	    case CMD_WDATA:
            {
		if( p_vci.cmdval && r_fifo_cmd.wok() ) 
                {
		    cmd_fifo_write = true;
                    sc_uint<dspin_cmd_width> data = (sc_uint<dspin_cmd_width>)p_vci.wdata.read();
                    sc_uint<dspin_cmd_width> be   = (sc_uint<dspin_cmd_width>)p_vci.be.read();
		    cmd_fifo_data    = (data       & 0x00FFFFFFFF) | 
                                       ((be << 32) & 0x0F00000000) ;
                                       
                    if ( p_vci.eop.read() )
                    {
                        cmd_fifo_data = cmd_fifo_data | 0x8000000000;
                        r_cmd_fsm = CMD_IDLE;
                    }
                }                
                break;
            }
	} // end switch r_cmd_fsm
	
	// fifo_cmd
	if((cmd_fifo_write == true)  && (cmd_fifo_read == false)) { r_fifo_cmd.simple_put(cmd_fifo_data); } 
	if((cmd_fifo_write == true)  && (cmd_fifo_read == true))  { r_fifo_cmd.put_and_get(cmd_fifo_data); } 
	if((cmd_fifo_write == false) && (cmd_fifo_read == true))  { r_fifo_cmd.simple_get(); }

        //////////////////////////////////////////////////////////////
	// DSPIN response packet to VCI response packet
	// The DSPIN packet is stored in the fifo_rsp
	// The FIFO output is analysed and translated to a VCI packet
        //////////////////////////////////////////////////////////////
        // - A N+1 flits DSPIN read response packet is translated
        //   to a N flits VCI response.
        // - A single flit DSPIN write response packet is translated
        //   to a single flit VCI response.
        // A valid DSPIN flit in the fifo_rsp is always consumed   
        // in the CMD_IDLE state, but no VCI flit is transmitted.
        // The VCI flits are sent in the RSP_READ & RSP_WRITE states.
        //////////////////////////////////////////////////////////////

	// rsp_fifo_write, rsp_fifo_data
	rsp_fifo_write = p_dspin_in.write.read();
	rsp_fifo_data  = p_dspin_in.data.read();

	// r_rsp_fsm, rsp_fifo_read
        rsp_fifo_read = false;		// default value

	switch(r_rsp_fsm) {
	    case RSP_IDLE:
            {
		if( r_fifo_rsp.rok() )
                {
		    rsp_fifo_read = true;
                    r_rsp_buf = r_fifo_rsp.read();
                    if ( (r_fifo_rsp.read() & 0x000020000) == 0 )  r_rsp_fsm = RSP_READ;
                    else 					   r_rsp_fsm = RSP_WRITE;
		}
		break;
            }
	    case RSP_READ:		
	    {
		if( r_fifo_rsp.rok() && p_vci.rspack.read() )
                {
		    rsp_fifo_read = true;
                    if ( (r_fifo_rsp.read() & 0x100000000) ) r_rsp_fsm = RSP_IDLE;
		}
		break;
            }
            case RSP_WRITE:
            {
                if ( p_vci.rspack.read() ) r_rsp_fsm = RSP_IDLE;
            }
	} // end switch r_rsp_fsm

	// fifo_rsp
	if((rsp_fifo_write == true)  && (rsp_fifo_read == false)) { r_fifo_rsp.simple_put(rsp_fifo_data); } 
	if((rsp_fifo_write == true)  && (rsp_fifo_read == true))  { r_fifo_rsp.put_and_get(rsp_fifo_data); } 
	if((rsp_fifo_write == false) && (rsp_fifo_read == true))  { r_fifo_rsp.simple_get(); }

}; // end transition

//////////////////////
tmpl(void)::genMoore()
{
	// VCI CMD interface
        if ( ( r_cmd_fsm.read() == CMD_IDLE ) || ( r_cmd_fsm.read() == CMD_WRITE ) ) 
        {
            p_vci.cmdack = false; 
        }
	else
        {
            p_vci.cmdack = r_fifo_cmd.wok();
        }

	// VCI RSP interface
	if ( r_rsp_fsm.read() == RSP_IDLE )
        {
            p_vci.rspval = false;
        }
        else if ( r_rsp_fsm.read() == RSP_WRITE )
        {
            p_vci.rspval = true;
	    p_vci.rdata  = 0;
            p_vci.rsrcid = (sc_uint<vci_param::S>)((r_rsp_buf.read() & 0x0FFFC0000) >> 18);
            p_vci.rtrdid = (sc_uint<vci_param::T>)((r_rsp_buf.read() & 0x00000FF00) >> 8);
            p_vci.rpktid = 0;
            p_vci.rerror = (sc_uint<vci_param::E>)((r_rsp_buf.read() & 0x000030000) >> 16);
            p_vci.reop   = true;
        }
        else if ( r_rsp_fsm.read() == RSP_READ )
        {
            p_vci.rspval = r_fifo_rsp.rok();
            p_vci.rdata  = (sc_uint<8*vci_param::B>)(r_fifo_rsp.read() & 0x0FFFFFFFF);
            p_vci.rsrcid = (sc_uint<vci_param::S>)((r_rsp_buf.read()   & 0x0FFFC0000) >> 18);
            p_vci.rtrdid = (sc_uint<vci_param::T>)((r_rsp_buf.read()   & 0x00000FF00) >> 8);
            p_vci.rpktid = 0;
            p_vci.rerror = (sc_uint<vci_param::E>)((r_rsp_buf.read()   & 0x000030000) >> 16);
            p_vci.reop   = ((r_fifo_rsp.read() & 0x100000000) == 0x100000000); 
        }

	// DSPIN_OUT interface
	p_dspin_out.write = r_fifo_cmd.rok();
	p_dspin_out.data  = r_fifo_cmd.read();

	// DSPIN_IN interface
	p_dspin_in.read = r_fifo_rsp.wok();

}; // end genMoore

/////////////////////////
tmpl(void)::print_trace()
{
    const char* cmd_str[] = {
    "CMD_IDLE     ",
    "CMD_BROADCAST",
    "CMD_READ     ",
    "CMD_WRITE    ",
    "CMD_WDATA    ",
    };
    const char* rsp_str[] = {
    "RSP_IDLE     ",
    "RSP_READ     ",
    "RSP_WRITE    ",
    };

    std::cout << name() << " : " << cmd_str[r_cmd_fsm.read()]
                        << " | " << rsp_str[r_rsp_fsm.read()] 
                        << " | fifo_cmd = " << r_fifo_cmd.filled_status()
                        << " | fifo_rsp = " << r_fifo_rsp.filled_status()
                        << std::endl;
}

}} // 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
