
/* -*- 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, Asim
 *         alain.greiner@lip6.fr april 2011
 *
 * Maintainers: alain
 */

//////////////////////////////////////////////////////////////////////////////////////
// This component is a simplified disk controller with a VCI interface.
// This component can perform data transfers between one single file belonging 
// to the host system and a buffer in the memory of the virtual prototype.
// The file name is an argument of the constructor.
// This component has a DMA capability, and is both a target and an initiator.
// The block size (bytes), and the burst size (bytes) must be power of 2.
// The burst size is typically a cache line. 
// If the memory buffer is not constrained to be aligned on a burst boundary. 
// Both read and write transfers are supported. An IRQ is optionally
// asserted when the transfer is completed. 
//
// As a target this block device controler contains 8 memory mapped registers,
// taking 32 bytes in the address space.
// - BLOCK_DEVICE_BUFFER        0x00 (read/write)    Memory buffer base address.
// - BLOCK_DEVICE_COUNT         0x04 (read/write)    Number of blocks to be transfered.
// - BLOCK_DEVICE_LBA           0x08 (read/write)    Index of first block in the file.
// - BLOCK_DEVICE_OP            0x0C (write-only)    Writing here starts the operation.
// - BLOCK_DEVICE_STATUS        0x10 (read-only)     Block Device status.
// - BLOCK_DEVICE_IRQ_ENABLE    0x14 (read/write)    IRQ enabled if non zero.
// - BLOCK_DEVICE_SIZE          0x18 (read-only)     Number of addressable blocks.
// - BLOCK_DEVICE_BLOCK_SIZE    0x1C (read_only)     Block size in bytes.
//
// The following operations codes are supported: 
// - BLOCK_DEVICE_NOOP          No operation
// - BLOCK_DEVICE_READ          From block device to memory
// - BLOCK_DEVICE_WRITE         From memory to block device 
//
// The BLOCK_DEVICE_STATUS is actually defined by the initiator FSM state.
// The following values are defined for device status:
// -BLOCK_DEVICE_IDLE           0
// -BLOCK_DEVICE_BUSY           1
// -BLOCK_DEVICE_READ_SUCCESS   2
// -BLOCK_DEVICE_WRITE_SUCCESS  3
// -BLOCK_DEVICE_READ_ERROR     4
// -BLOCK_DEVICE_WRITE_ERROR    5
//
// In the 4 states READ_ERROR, READ_SUCCESS, WRITE_ERROR, WRITE_SUCCESS,
// the IRQ is asserted (if it is enabled).
// A read access to the BLOCK_DEVICE_STATUS in these 4 states reset 
// the initiator FSM state to IDLE, and acknowledge the IRQ.
// Any write access to registers BUFFER, COUNT, LBA, OP is ignored
// if the device is not IDLE.
///////////////////////////////////////////////////////////////////////////

#ifndef SOCLIB_VCI_BLOCK_DEVICE_TSAR_V4_H
#define SOCLIB_VCI_BLOCK_DEVICE_TSAR_V4_H

#include <stdint.h>
#include <systemc>
#include "caba_base_module.h"
#include "mapping_table.h"
#include "vci_target.h"

namespace soclib {
namespace caba {

using namespace sc_core;

template<typename vci_param>
class VciBlockDeviceTsarV4
	: public caba::BaseModule
{
private:

    // Registers
    sc_signal<int>            	 r_target_fsm;   	 // target fsm state register
    sc_signal<int>               r_initiator_fsm;    // initiator fsm state register
    sc_signal<bool>              r_irq_enable;   	 // default value is true
    sc_signal<uint32_t>          r_nblocks;      	 // number of blocks in transfer
    sc_signal<uint32_t>          r_buf_address;  	 // memory buffer address
    sc_signal<uint32_t>          r_lba;          	 // first block index
    sc_signal<bool>              r_read;         	 // requested operation
    sc_signal<uint32_t>          r_index;        	 // flit index in local buffer
    sc_signal<uint32_t>          r_latency_count;    // latency counter 
    sc_signal<uint32_t>          r_flit_count;   	 // flit counter (in a burst)
    sc_signal<uint32_t>          r_burst_count;   	 // burst counter (in a block)
    sc_signal<uint32_t>          r_block_count;  	 // block counter (in a transfer)
    sc_signal<uint32_t>          r_burst_offset;     // number of non aligned flits
    sc_signal<uint32_t>          r_burst_nflits;     // number of flits in a burst
    sc_signal<bool>              r_go;           	 // command from T_FSM to M_FSM

    sc_signal<sc_dt::sc_uint<vci_param::S> >	r_srcid;		// save srcid
    sc_signal<sc_dt::sc_uint<vci_param::T> >	r_trdid;		// save trdid
    sc_signal<sc_dt::sc_uint<vci_param::P> >	r_pktid;		// save pktid

    uint32_t*                    r_local_buffer; 	 // capacity is one block 

    // structural parameters
    soclib::common::Segment      m_segment;      	 // segment associated to target
    uint32_t                     m_srcid;        	 // initiator index
    int                          m_fd;           	 // File descriptor
    uint64_t                     m_device_size;  	 // Total number of blocks
    const uint32_t               m_flits_per_block;	 // number of flits in a block
    const uint32_t               m_flits_per_burst;	 // number of flits in a burst
    const uint32_t               m_bursts_per_block; // number of bursts in a block
    const uint32_t               m_latency;      	 // device latency

    // methods
    void transition();
    void genMoore();

    //  Master FSM states
    enum {
    M_IDLE              = 0,

    M_READ_BLOCK        = 1,
    M_READ_BURST        = 2,
    M_READ_CMD          = 3,
    M_READ_RSP          = 4,
    M_READ_SUCCESS      = 5,
    M_READ_ERROR        = 6,

    M_WRITE_BURST       = 7,
    M_WRITE_CMD         = 8,
    M_WRITE_RSP         = 9,
    M_WRITE_BLOCK       = 10,
    M_WRITE_SUCCESS     = 11,
    M_WRITE_ERROR       = 12,
    };

    // Target FSM states
    enum {
    T_IDLE              = 0,
    T_WRITE_BUFFER      = 1,
    T_READ_BUFFER       = 2,
    T_WRITE_COUNT       = 3,
    T_READ_COUNT        = 4,
    T_WRITE_LBA         = 5,
    T_READ_LBA          = 6,
    T_WRITE_OP          = 7,
    T_READ_STATUS       = 8,
    T_WRITE_IRQEN       = 9,
    T_READ_IRQEN        = 10,
    T_READ_SIZE         = 11,
    T_READ_BLOCK        = 12,
    T_READ_ERROR        = 13,
    T_WRITE_ERROR       = 14,
    };

    // Error codes values
    enum {
    VCI_READ_OK		= 0,
    VCI_READ_ERROR	= 1,
    VCI_WRITE_OK	= 2,
    VCI_WRITE_ERROR	= 3,
    };

protected:

    SC_HAS_PROCESS(VciBlockDeviceTsarV4);

public:

    // ports
    sc_in<bool> 					      p_clk;
    sc_in<bool> 					      p_resetn;
    soclib::caba::VciInitiator<vci_param> p_vci_initiator;
    soclib::caba::VciTarget<vci_param>    p_vci_target;
    sc_out<bool> 					      p_irq;

    void print_trace();

    // Constructor   
    VciBlockDeviceTsarV4(
		sc_module_name                      name,
		const soclib::common::MappingTable 	&mt,
		const soclib::common::IntTab 		&srcid,
		const soclib::common::IntTab 		&tgtid,
        const std::string                   &filename,
        const uint32_t 	                    block_size = 512,
        const uint32_t 	                    burst_size = 64,
        const uint32_t	                    latency = 0);
};

}}

#endif /* SOCLIB_VCI_BLOCK_DEVICE_TSAR_V4_H */

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

