/* -*- 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) Telecom ParisTech, Comelec 
 *         Jeremie Brunel <jeremie.brunel@telecom-paristech.fr>, 2013
 *
 */

#include "vci_tester_tgt.h"
#include "vcd_parser.h"
#include "vci_initiator.h"

#define tmpl(x) template<typename vci_param> x VciTesterTgt<vci_param>

namespace soclib
{
  namespace caba
  {
    tmpl(/* */)::VciTesterTgt(sc_module_name insname, const std::string &filename, const std::string &name)
      : BaseModule(insname),
      p_resetn("resetn"),
      p_clk("clk"),
      p_vci("vci"),
      p_synchro("synchro"),
      p_time("time"),
      m_parser(filename),
      m_name(name),
      m_time(0)
    {
      SC_METHOD(transition);
      sensitive << p_clk.pos();
      dont_initialize();
      
      SC_METHOD(genMoore);
      sensitive << p_clk.neg();
      dont_initialize();
    }

    tmpl(/* */)::~VciTesterTgt()
    {

    }

/* transition */
    tmpl(void)::transition()
    {
      std::cout << "tester transition (" << this << ") " << m_tester_fsm << std::endl;
      if (!p_resetn)
      {
        m_tester_fsm = TESTER_IDLE;
        return;
      }

      switch (m_tester_fsm)
      {
        case TESTER_IDLE:
          m_tester_fsm = TESTER_FIRST_READ;
          break;

        case TESTER_FIRST_READ:
          current_io_level = m_parser.get_next_wave(&m_time, false, "", 0);
          p_time = m_time;
          old_io_level = current_io_level;
          next_io_level = current_io_level;
          m_tester_fsm = TESTER_HANDLE_RESET;
          break;

        case TESTER_HANDLE_RESET:
          m_tester_fsm = TESTER_WAIT_INIT;
          break;

        case TESTER_WAIT_INIT:
          if (test_my_io()==O_TRUE)
            m_tester_fsm = TESTER_WPLAY;
          break;

        case TESTER_WPLAY:
          if (p_synchro.read())
          {
            m_tester_fsm = TESTER_PLAY;
          }
          break;

        case TESTER_PLAYSENARIO:
          if (p_vci.rdata.read()==0)
          {
            old_io_level = current_io_level;
            next_io_level = m_parser.get_next_wave(&m_time, true, merge_sc_name("rdata"), 0);
            current_io_level = next_io_level;
            if (current_io_level.empty())
            {
              m_tester_fsm = TESTER_END;
              break;
            }
            p_time = m_time;
            m_tester_fsm = TESTER_RSPWAIT;
            m_senario.clear();
            break;
          }
          if (m_it != m_senario.size()-1)
            m_it++;
          else
            m_it = 0;
          break;

        case TESTER_PLAY:
          old_io_level = current_io_level;
          current_io_level = next_io_level;
          next_io_level = m_parser.get_next_wave(&m_time, false, "", 0);
          p_time = m_time;
          if (current_io_level.empty())
            m_tester_fsm = TESTER_END;
          else
            m_tester_fsm = TESTER_RSPWAIT;
          break;

        case TESTER_RSPWAIT:
          if (old_io_level[merge_sc_name("address")]==0xd4200014 &&
              old_io_level[merge_sc_name("cmdval")]==1 
              )
          {
            if (p_vci.rdata.read()==1) // boucle
            {
              m_senario.clear();
              m_senario.push_back(next_io_level);
              m_senario.push_back(old_io_level);
              m_senario.push_back(next_io_level);
              m_it = 0;

              m_tester_fsm = TESTER_PLAYSENARIO;
              break;
            }
          }

          switch(test_my_io())
          {
            case O_FALSE:
              m_tester_fsm = TESTER_ERROR;
              break;
            case O_TRUE:
              if (p_synchro.read())
              {
                old_io_level = current_io_level;
                current_io_level = next_io_level;
                next_io_level = m_parser.get_next_wave(&m_time, false, "", 0);
                p_time = m_time;
                if (current_io_level.empty())
                  m_tester_fsm = TESTER_END;
                else
                  m_tester_fsm = TESTER_RSPWAIT;
              }
              else
                m_tester_fsm = TESTER_WPLAY;

              break;
            case O_WAIT:
              m_tester_fsm = TESTER_RSPWAIT;
              break;
          }
          break;

        case TESTER_END:
          p_time = (uint32_t)-1;
          //std::cout << "sleep\n";
          break;

        case TESTER_ERROR:
          for (std::map<std::string, uint32_t>::iterator it=current_io_level.begin()
              ;it!=current_io_level.end();it++)
          {
            std::cout << it->first << " " << it->second << std::endl;
          }
          std::cout << "rspval " << p_vci.rspval.read() << std::endl;
          std::cout << "rdata " << p_vci.rdata.read() << std::endl; 
          std::cout << "reop " << p_vci.reop.read() << std::endl;    
          std::cout << "rerror " << p_vci.rerror.read() << std::endl; 
          std::cout << "rsrcid " << p_vci.rsrcid.read() << std::endl; 
          std::cout << "rtrdid " << p_vci.rtrdid.read() << std::endl; 
          std::cout << "rpktid " << p_vci.rpktid.read() << std::endl; 
          std::cout << "cmdack " << p_vci.cmdack.read() << std::endl; 
sc_stop();
          break;
      }

    }

/* output generation */
    tmpl(void)::genMoore()
    {
      switch(m_tester_fsm)
      {
        case TESTER_IDLE:
          p_vci.eop = 0;
          break;

        case TESTER_HANDLE_RESET:
          gen_io(current_io_level);
          break;

        case TESTER_WAIT_INIT:
          gen_io(current_io_level);
          break;

        case TESTER_WPLAY:
          break;

        case TESTER_PLAYSENARIO:
          if (!m_senario.empty())
            gen_io(m_senario[m_it]);
          else
          {
            if (!current_io_level.empty())
              gen_io(current_io_level);
          }
          break;

        case TESTER_PLAY:
          if (!current_io_level.empty())
            gen_io(current_io_level);
          break;

        case TESTER_RSPWAIT:
          if (!m_senario.empty())
          {
            gen_io(m_senario[m_it]);
            break;
          }
          if (p_synchro.read())
          {
            if (!current_io_level.empty())
              gen_io(current_io_level);
          }
          break;

        case TESTER_END:
          p_vci.cmdval = 0;
          break;

        case TESTER_ERROR:
          break;
      }
    }
    
    tmpl(std::string)::merge_sc_name(const char *sc_name)
    {
      std::string ret(m_name);
      ret.append("_");
      ret.append(sc_name);
      return ret;
    }

    tmpl(void)::gen_io(const std::map<std::string, uint32_t> &m)
    {
      p_vci.rspack  = m.at(merge_sc_name("rspack"));
      p_vci.cmdval  = m.at(merge_sc_name("cmdval"));
      p_vci.address = m.at(merge_sc_name("address"));
      p_vci.be      = m.at(merge_sc_name("be"));
      p_vci.cmd     = m.at(merge_sc_name("cmd"));
      p_vci.contig  = m.at(merge_sc_name("contig"));
      p_vci.wdata   = m.at(merge_sc_name("wdata"));
      p_vci.eop     = m.at(merge_sc_name("eop"));
      p_vci.cons    = m.at(merge_sc_name("cons"));
      p_vci.plen    = m.at(merge_sc_name("plen"));
      p_vci.wrap    = m.at(merge_sc_name("wrap"));
      p_vci.cfixed  = m.at(merge_sc_name("cfixed"));
      p_vci.clen    = m.at(merge_sc_name("clen"));
      p_vci.srcid   = m.at(merge_sc_name("srcid"));
      p_vci.trdid   = m.at(merge_sc_name("trdid"));
      p_vci.pktid   = m.at(merge_sc_name("pktid"));
    }

    tmpl(bool)::compare_io(const std::map<std::string, uint32_t> &m)
    {
      if ((uint32_t)p_vci.rspval.read() != m.at(merge_sc_name("rspval"))) 
        return false; 
      if ((uint32_t)p_vci.rdata.read() != m.at(merge_sc_name("rdata"))) 
        return false; 
      if ((uint32_t)p_vci.reop.read() != m.at(merge_sc_name("reop"))) 
        return false; 
      if ((uint32_t)p_vci.rerror.read() != m.at(merge_sc_name("rerror"))) 
        return false; 
      if ((uint32_t)p_vci.rsrcid.read() != m.at(merge_sc_name("rsrcid"))) 
        return false; 
      if ((uint32_t)p_vci.rtrdid.read() != m.at(merge_sc_name("rtrdid"))) 
        return false; 
      if ((uint32_t)p_vci.rpktid.read() != m.at(merge_sc_name("rpktid"))) 
        return false; 
      if ((uint32_t)p_vci.cmdack.read() != m.at(merge_sc_name("cmdack"))) 
        return false; 
      
      return true;
    }

    tmpl(uint32_t)::test_my_io()
    {
      if (compare_io(current_io_level))
        return O_TRUE;
      else if (compare_io(old_io_level))
        return O_WAIT;
      else
        return O_FALSE;
    }
  }
}

