/*
 * Copyright (c) 2015, University of Kaiserslautern
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors:
 *    Janik Schlemminger
 *    Robert Gernhardt
 *    Matthias Jung
 */

#include "xmlAddressdecoder.h"
#include <systemc.h>
#include "Utils.h"
#include "bitset"
#include "../controller/core/configuration/Configuration.h"

using namespace std;
using namespace tinyxml2;

xmlAddressDecoder::xmlAddressDecoder()
{
    addressmapping = NULL;
}

void xmlAddressDecoder::setConfiguration(std::string addressConfigURI)
{
    tinyxml2::XMLDocument doc;
    loadXML(addressConfigURI, doc);
    tinyxml2::XMLElement *addressMap = doc.RootElement();
    string xmlNodeName(addressMap->Name());

    if ( xmlNodeName != "addressmapping") {
        reportFatal("AddressDecorder", "addressmap node expected");
    }

    for (XMLElement *child = addressMap->FirstChildElement();
            child != NULL;
            child = child->NextSiblingElement()) {
        int from;
        int to;

        child->QueryAttribute("from", &from);
        child->QueryAttribute("to", &to);

        shifts[child->Name()] = from;
        masks[child->Name()] = pow(2.0, to + 1.0) - pow(2.0, from + 0.0);
        amount[child->Name()] = pow(2.0, to - from + 1.0);
    }
}


DecodedAddress xmlAddressDecoder::decodeAddress(sc_dt::uint64 addr)
{
    DecodedAddress result;
    result.channel = (addr & masks["channel"]) >> shifts["channel"];
    //result.rank = (addr & masks["rank"]) >> shifts["rank"];
    //result.bankgroup = (addr & masks["bankgroup"]) >> shifts["bankgroup"];
    result.bank = (addr & masks["bank"]) >> shifts["bank"];
    result.bankgroup = result.bank %
                       Configuration::getInstance().memSpec.NumberOfBankGroups;
    result.rank = result.bank % Configuration::getInstance().memSpec.NumberOfRanks;
    result.row = (addr & masks["row"]) >> shifts["row"];
    result.column = (addr & masks["column"]) >> shifts["column"];
    result.bytes = (addr & masks["bytes"]) >> shifts["bytes"];
    return result;
}

sc_dt::uint64 xmlAddressDecoder::encodeAddress(DecodedAddress n)
{
    return n.channel << shifts["channel"] |
           n.rank << shifts["rank"] |
           n.bankgroup << shifts["bankgroup"] |
           n.row << shifts["row"] |
           n.bank << shifts["bank"] |
           n.column << shifts["column"] |
           n.bytes << shifts["bytes"];
}

bool xmlAddressDecoder::testConfigFile(std::string url)
{
    // Simple test if the root node has the correct name.
    // This is suitable for now, but can be extended in future.

    tinyxml2::XMLDocument doc;
    loadXML(url, doc);
    tinyxml2::XMLElement *addressMap = doc.RootElement();

    return (strcmp(addressMap->Name(), "addressmapping") == 0);
}

void xmlAddressDecoder::print()
{
    cout << headline << endl;
    cout << "Address Mapping:" << endl << endl;
    for (auto &pair : masks) {
        cout << std::setw(16) << pair.first << ": " << bitset<48>(pair.second) << endl;
    }
    cout << endl;
}
