Example #1
0
ncycles_t RowModel::Write( NVMainRequest *request, NVMDataBlock& /*oldData*/ ) 
{
    NVMAddress address = request->address;

    /*
     *  The default life map is an stl map< uint64_t, uint64_t >. 
     *  You may map row and col to this map_key however you want.
     *  It is up to you to ensure there are no collisions here.
     */
    uint64_t row;
    ncycles_t rv = 0;

    /*
     *  For our simple row model, we just set the key equal to the row.
     */
    address.GetTranslatedAddress( &row, NULL, NULL, NULL, NULL, NULL );
    
    /*
     *  If using the default life map, we can call the DecrementLife
     *  function which will check if the map_key already exists. If so,
     *  the life value is decremented (write count incremented). Otherwise 
     *  the map_key is inserted with a write count of 1.
     */
    if( !DecrementLife( row ) )
        rv = -1;

    return rv;
}
Example #2
0
ncycles_t WordModel::Write( NVMainRequest *request, NVMDataBlock& /*oldData*/ ) 
{
    NVMAddress address = request->address;

    /*
     *  The default life map is an stl map< uint64_t, uint64_t >. 
     *  You may map row and col to this map_key however you want.
     *  It is up to you to ensure there are no collisions here.
     */
    uint64_t row;
    uint64_t col;
    ncycles_t rv = 0;

    /*
     *  For our simple row model, we just set the key equal to the row.
     */
    address.GetTranslatedAddress( &row, &col, NULL, NULL, NULL, NULL );

    /*
     *  If using the default life map, we can call the DecrementLife
     *  function which will check if the map_key already exists. If so,
     *  the life value is decremented (write count incremented). Otherwise 
     *  the map_key is inserted with a write count of 1.
     */
    uint64_t wordkey;
    uint64_t rowSize;
    uint64_t wordSize;
    uint64_t partitionCount;

    wordSize = p->BusWidth;
    wordSize *= p->tBURST * p->RATE;
    wordSize /= 8;

    rowSize = p->COLS * wordSize;

    /*
     *  Think of each row being partitioned into 64-bit divisions (or
     *  whatever the Bus Width is). Each row has rowSize / wordSize
     *  paritions. For the key we will use:
     *
     *  row * number of partitions + partition in this row
     */
    partitionCount = rowSize / wordSize;

    wordkey = row * partitionCount + col;

    if( !DecrementLife( wordkey ) )
        rv = -1;

    return rv;
}
Example #3
0
void MQMigrator::Translate( uint64_t address, uint64_t *row, uint64_t *col, uint64_t *bank,
                          uint64_t *rank, uint64_t *channel, uint64_t *subarray )
{
    /* Use the default -- We will only change the channel if needed. */
    AddressTranslator::Translate( address, row, col, bank, rank, channel, subarray );
	/**
    std::cout << "\nMQMigrator::Translate address " << std::hex << address << " To:\n" \
                                       "old channel " << *channel << "\n" \
                                       "rank " << *channel << "\n" \
                                       "bank " << *channel << "\n" \
                                       "row " << *channel << "\n" \
                                       "col " << *channel << "\n" \
                                       "subarray " << *channel << "\n" \
                                                   << std::endl; 
   	*/ 

	/* This should be a unique key for this address. */
    NVMAddress keyAddress;
    keyAddress.SetTranslatedAddress( *row, *col, *bank, *rank, *channel, *subarray );
    keyAddress.SetPhysicalAddress( address );
    uint64_t key = GetAddressKey( keyAddress );
	ncounter_t r_channel = *channel;
	if( access_times.count( r_channel)==0)
	{
		access_times[ r_channel]= 1;
	}   
	else
		access_times[r_channel]++;

    /* Check if the page was migrated and migration is complete. */
    if( migrationMap.count( key ) != 0 )
    {
        if( migrationState[key] == MQ_MIGRATION_DONE )
        {
            *channel = migrationMap[key];
  			
//			std::cout << "\n------new channel " << std::hex << *channel <<std::endl;

            migratedAccesses++;
        }
    }
	ncounter_t m_channel = *channel;
	if( migrate_access_times.count( m_channel)==0)
	{
		migrate_access_times[ m_channel]= 1;
	}   
	else
		migrate_access_times[m_channel]++;
}
Example #4
0
uint64_t MultiQueueMigrator::GetRealChannel( MQMigrator *at, NVMAddress address )
{
	if(at->IsMigrated(address))
	{
		return at->GetNewChannel(address);		
	}
	
	return address.GetChannel();
}
Example #5
0
void MQMigrator::StartMigration( NVMAddress& promotee, NVMAddress& demotee )
{
    /* 
     *  The address being demoted is assumed to be in the "fast" memory and
     *  the address being promoted in the slow memory, therefore we define
     *  the promotion channel as the demotion address' value and similarly
     *  for demotion channel.
     */
	//std::cout<<"start migration"<<std::endl;
    uint64_t demoChannel, promoChannel;
    demoChannel = promotee.GetChannel( );
    promoChannel = demotee.GetChannel( );

    /* Get unique keys for each page to migrate. */
    uint64_t promokey = GetAddressKey( promotee );
    uint64_t demokey = GetAddressKey( demotee );

    /* Ensure we are not already migrating a page. */
    assert( migrating == false );

    /*
     *  Set the new channel decodings immediately, but mark the migration
     *  as being in progress.
     */
    migrationMap[promokey] = promoChannel;
    migrationMap[demokey] = demoChannel;
	//std::cout<<"key promo:"<<promokey<<" state is:"<<MQ_MIGRATION_READING<<std::endl;
	//std::cout<<"key demo:"<<demokey<<" state is:"<<MQ_MIGRATION_READING<<std::endl;
    migrationState[promokey] = MQ_MIGRATION_READING;
    migrationState[demokey] = MQ_MIGRATION_READING;

    /*
     *  Only one migration is allowed at a time; These values hold the
     *  key values for each migration page and marks a migration in
     *  progress.
     */
    migrating = true;
    inputPage = promokey;
    outputPage = demokey;
}
Example #6
0
/*
 *  Calculates a unique key for each possible unit of memory that can be
 *  migrated. In this case, we are migrating single rows of a bank.
 */
uint64_t MQMigrator::GetAddressKey( NVMAddress& address )
{
    uint64_t row, bank, rank, subarray, channel;
    address.GetTranslatedAddress( &row, NULL, &bank, &rank, &channel, &subarray );

    /* 
     * We will migrate entire memory pages, therefore only the column is
     * irrelevant.
     */
    return (row * numBanks * numRanks * numSubarrays * numChannels 
            + bank * numRanks * numSubarrays * numChannels
            + rank * numSubarrays * numChannels
            + subarray * numChannels
            + channel);
}
Example #7
0
uint64_t MultiQueueMigrator::GetPageNumber( MQMigrator *at, NVMAddress address )
{
	uint64_t pageNum = at->GetAddressKey(address);

	if(at->IsMigrated(address))
	{
		uint64_t channel;
		address.GetTranslatedAddress(NULL, NULL, NULL, NULL, &channel, NULL);
		
		uint64_t newChannel = at->GetNewChannel(address);
		pageNum = pageNum - channel + newChannel;
	}
	
	return pageNum;
}
void Migrator::Translate( uint64_t address, uint64_t *row, uint64_t *col, uint64_t *bank,
                          uint64_t *rank, uint64_t *channel, uint64_t *subarray )
{
    /* Use the default -- We will only change the channel if needed. */
    AddressTranslator::Translate( address, row, col, bank, rank, channel, subarray );

    
    /* This should be a unique key for this address. */
    NVMAddress keyAddress;
    keyAddress.SetTranslatedAddress( *row, *col, *bank, *rank, *channel, *subarray );
    keyAddress.SetPhysicalAddress( address );
    uint64_t key = GetAddressKey( keyAddress );

    /* Check if the page was migrated and migration is complete. */
    if( migrationMap.count( key ) != 0 )
    {
        if( migrationState[key] == MIGRATION_DONE )
        {
            *channel = migrationMap[key];

            migratedAccesses++;
        }
    }
}
Example #9
0
/*
 *  This trace is printed from nvmain.cpp. The format is:
 *
 *  CYCLE OP ADDRESS DATA THREADID
 */
bool NVMainTraceReader::GetNextAccess( TraceLine *nextAccess )
{
    /* If there is no trace file, we can't do anything. */
    if( traceFile == "" )
    {
        std::cerr << "No trace file specified!" << std::endl;
        return false;
    }

    /* If the trace file is not open, open it if possible. */
    if( !trace.is_open( ) )
    {
        trace.open( traceFile.c_str( ) );
        if( !trace.is_open( ) )
        {
            std::cerr << "Could not open trace file: " << traceFile << "!" << std::endl;
            return false;
        }
    }

    std::string fullLine;

    /* We will read in a full line and fill in these values */
    unsigned int cycle = 0;
    OpType operation = READ;
    uint64_t address;
    NVMDataBlock dataBlock;
    unsigned int threadId = 0;
    
    /* There are no more lines in the trace... Send back a "dummy" line */
    getline( trace, fullLine );
    if( trace.eof( ) )
    {
        NVMAddress nAddress;
        nAddress.SetPhysicalAddress( 0xDEADC0DEDEADBEEFULL );
        nextAccess->SetLine( nAddress, NOP, 0, dataBlock, 0 );
        std::cout << "NVMainTraceReader: Reached EOF!" << std::endl;
        return false;
    }
    
    std::istringstream lineStream( fullLine );
    std::string field;
    unsigned char fieldId = 0;
    
    /*
     *  Again, the format is : CYCLE OP ADDRESS DATA THREADID
     *  So the field ids are :   0    1    2      3      4
     */
    while( getline( lineStream, field, ' ' ) )
    {
        if( field != "" )
        {
            if( fieldId == 0 )
                cycle = atoi( field.c_str( ) );
            else if( fieldId == 1 )
            {
                if( field == "R" )
                    operation = READ;
                else if( field == "W" )
                    operation = WRITE;
                else
                    std::cout << "Warning: Unknown operation `" 
                        << field << "'" << std::endl;
            }
            else if( fieldId == 2 )
            {
                std::stringstream fmat;

                fmat << std::hex << field;
                fmat >> address;
            }
            else if( fieldId == 3 )
            {
                int byte;
                int start, end;

                /* Assumes 64-byte memory words.... */
                // TODO: Drop assumption and use field.length()/2 bytes
                assert(sizeof(uint64_t)*8 == 64);
                assert(field.length() == 128); // 1 char per 4 bits

                dataBlock.SetSize( 64 );

                uint64_t *rawData = reinterpret_cast<uint64_t*>(dataBlock.rawData);
                memset(rawData, 0, 64);
                
                for( byte = 0; byte < 8; byte++ )
                {
                    std::stringstream fmat;

                    end = (int)field.length( ) - 16*byte;
                    start = (int)field.length( ) - 16*byte - 16;

                    fmat << std::hex << field.substr( start, end - start );
                    fmat >> rawData[byte];
                }
            }
/*
 * Parse the trace file and find the next access to main memory. May read
 * multiple lines before a memory access is returned.
 */
bool RubyTraceReader::GetNextAccess( TraceLine *nextAccess )
{
    /* If trace file is not specified, we can't know what to do. */
    if( traceFile == "" )
    {
        std::cerr << "No trace file specified!" << std::endl;
        return false;
    }

    /* If trace file is not opened, we can't read from it. */
    if( !trace.is_open( ) )
    {
        trace.open( traceFile.c_str() );
        if( !trace.is_open( ) )
        {
            std::cerr << "Could not open trace file: " << traceFile << "!" << std::endl;
            return false;
        }
    }
    
    /* 
     * Read the next few lines from the file, looking for transactions that end
     * and do not end at one of the caches. Once the first one is found, return it.
     */
    std::string fullLine;

    /* We will break at errors / finishing points in the loop. */
    while( 1 )
    {
        NVMDataBlock dataBlock;
        NVMDataBlock oldDataBlock;
        unsigned int threadId;

        threadId = 0;

        /* 
         * Read a full line from the trace, ensuring we are not at the end of 
         * the file 
         */
        if( trace.eof( ) )
        {
            NVMAddress nAddress;
            nAddress.SetPhysicalAddress( 0xDEADC0DEDEADBEEFULL );
            nextAccess->SetLine( nAddress, NOP, 0, dataBlock, oldDataBlock, 0 );
            return false;
        }
        getline( trace, fullLine );

        /*
         * Insert the full ine into a string stream. We will use the string stream
         * to separate the fields in the trace files into useful data we need.
         */
        std::istringstream lineStream( fullLine );
        std::string field;
        unsigned char fieldId;
        
        /*
         * Interesting fields are the cycles, the unit issuing the trace command,
         * the command being executed on the memory, the address of the memory 
         * operation, and the operation- such as load/store/fetch
         */
        std::string cycle, unit, command, address, memory, operation;
        uint64_t decAddress;
        unsigned int currentCycle = 0;
        unsigned int cycles = 0;
        OpType memOp;
        
        /*
         * In a Ruby Trace, most of the fields are not necessary for main 
         * memory purposes.
         * We will increment the field ID and use it as a reference to 
         * determine what we are interested in. In this case, the format is 
         * as follows:
         *
         * 207 1 -1 Seq Done > [0x7ba4ce80, line 0x7ba4ce80] 206 cycles NULL IFETCH No
         *
         *  0  1  2  3    4  5      6        7        8       9    10    11    12   13
         *
         * Here we are interested in fields 3, 4, 6, 11, and 12. Field 3 is 
         * the unit * generating the memory request. Field 4 is the unit's 
         * command. Field 6 is the address. Field 11 is the memory region where
         * the result ends. Field 12 is the memory operation.
         */
        fieldId = 0;
        while( getline( lineStream, field, ' ' ) )
        {
            if( field != "" )
            {
                if( fieldId == 0 )
                    currentCycle = atoi( field.c_str( ) );
                else if( fieldId == 3 )
                    unit = field;
                else if( fieldId == 4 )
                    command = field;
                else if( fieldId == 6 )
                    address = field.substr( 1, field.length( ) - 2 );
                else if( fieldId == 9 )
                    cycles = atoi( field.c_str( ) );
                else if( fieldId == 11 )
                    memory = field;
                else if( fieldId == 12 )
                    operation = field;
                
                fieldId++;
            }
        }
        
        /*
         * If the unit generating the result is "Seq," it is the GEMS sequencer
         * stepping through the instructions. We want to find sequencer 
         * executing the "Done" command. 
         * If the memory is "NULL," this is main memory. Other possibilites are
         * "L1Cache" or "L2Cache" for example.
         *
         * If it is a main memory request, we need to convert to either a read 
         * or write.
         * Ruby uses LD for load, IFETCH for instruction fetch, and ST for store. 
         * Both LD and IFETCH will be mapped to a read command for the simulator. 
         * Stores are mapped to write commands.
         */
        if( unit == "Seq" )
        {
            if( command == "Done" && memory == "NULL" ) 
            {
                std::stringstream fmat;

                fmat << std::hex << address;
                fmat >> decAddress;

                if( operation == "IFETCH" || operation == "LD" )
                    memOp = READ;
                else if( operation == "ST" || operation == "ATOMIC" )
                    memOp = WRITE;
                else
                {
                    memOp = NOP;
                    std::cout << "RubyTraceReader: Unknown memory operation! " 
                        << operation << std::endl;
                }

                NVMAddress nAddress;
                nAddress.SetPhysicalAddress( decAddress );

                nextAccess->SetLine( nAddress, memOp, currentCycle - cycles, 
                                     dataBlock, oldDataBlock, threadId );
                break;
            }
        }
    }
Example #11
0
ncycles_t ByteModel::Write( NVMainRequest *request, NVMDataBlock& oldData ) 
{
    NVMDataBlock& newData = request->data;
    NVMAddress address = request->address;

    /*
     *  The default life map is an stl map< uint64_t, uint64_t >. 
     *  You may map row and col to this map_key however you want.
     *  It is up to you to ensure there are no collisions here.
     */
    uint64_t row;
    uint64_t col;
    ncycles_t rv = 0;

    /*
     *  For our simple row model, we just set the key equal to the row.
     */
    address.GetTranslatedAddress( &row, &col, NULL, NULL, NULL, NULL );
    
    /*
     *  If using the default life map, we can call the DecrementLife
     *  function which will check if the map_key already exists. If so,
     *  the life value is decremented (write count incremented). Otherwise 
     *  the map_key is inserted with a write count of 1.
     */
    uint64_t wordkey;
    uint64_t rowSize;
    uint64_t wordSize;
    uint64_t partitionCount;

    /* 
     * wordSize is the size of a word written to memory, usually a cacheline. 
     * This size is in bytes 
     */
    wordSize = p->BusWidth;
    wordSize *= p->tBURST * p->RATE;
    wordSize /= 8;

    /* Size of a row in bytes */
    rowSize = p->COLS * wordSize;

    /* Check each byte to see if it was modified */
    for( int i = (int)wordSize - 1; i >= 0; --i )
    {
        uint8_t oldByte, newByte;

        oldByte = oldData.GetByte( i );
        newByte = newData.GetByte( i );

        if( oldByte == newByte ) 
            continue;

        /*
         *  Think of each row being partitioned into 8-bit divisions. Each 
         *  row has rowSize / 8 paritions. For the key we will use:
         *
         *  row * number of partitions + partition in this row
         */
        partitionCount = ( rowSize / 8 );

        wordkey = row * partitionCount + col * wordSize + i;
      
        if( !DecrementLife( wordkey ) )
            rv = -1;  
    }

    return rv;
}
Example #12
0
void CommonMigrator::ChooseVictim( Migrator *at, NVMAddress& /*promotee*/, NVMAddress& victim )
{
    /*
     *  Since this is no method called after every module in the system is 
     *  initialized, we check here to see if we have queried the memory system
     *  about the information we need.
     */
    if( !queriedMemory )
    {
        /*
         *  Our naive replacement policy will simply circle through all the pages
         *  in the fast memory. In order to count the pages we need to count the
         *  number of rows in the fast memory channel. We do this by creating a
         *  dummy request which would route to the fast memory channel. From this
         *  we can grab it's config pointer and calculate the page count.
         */
        NVMainRequest queryRequest;
		//set query request's channel to promotionChannel
        queryRequest.address.SetTranslatedAddress( 0, 0, 0, 0, promotionChannel, 0 );
        queryRequest.address.SetPhysicalAddress( 0 );
        queryRequest.type = READ;
        queryRequest.owner = this;

        NVMObject *curObject = NULL;
		//search all children of parent , only if find the child node that can cast to SubArray safely , 
		//and assign it to curObject(find Subarray Object ) 
        FindModuleChildType( &queryRequest, SubArray, curObject, parent->GetTrampoline( ) );

        SubArray *promotionChannelSubarray = NULL;
        promotionChannelSubarray = dynamic_cast<SubArray *>( curObject );

        assert( promotionChannelSubarray != NULL );
        Params *p = promotionChannelSubarray->GetParams( );
        promotionChannelParams = p;

        totalPromotionPages = p->RANKS * p->BANKS * p->ROWS;
        currentPromotionPage = totalPromotionPages;

        if( p->COLS != numCols )
        {
            std::cout << "Warning: Page size of fast and slow memory differs." << std::endl;
        }

        queriedMemory = true;
    }
    /*
     *  From the current promotion page, simply craft some translated address together
     *  as the victim address.
     */
    uint64_t victimRank, victimBank, victimRow, victimSubarray, subarrayCount;
    ncounter_t promoPage = currentPromotionPage;

    victimRank = promoPage % promotionChannelParams->RANKS;
    promoPage >>= NVM::mlog2( promotionChannelParams->RANKS );

    victimBank = promoPage % promotionChannelParams->BANKS;
    promoPage >>= NVM::mlog2( promotionChannelParams->BANKS );

    subarrayCount = promotionChannelParams->ROWS / promotionChannelParams->MATHeight;
    victimSubarray = promoPage % subarrayCount;
    promoPage >>= NVM::mlog2( subarrayCount );

    victimRow = promoPage;

    victim.SetTranslatedAddress( victimRow, 0, victimBank, victimRank, promotionChannel, victimSubarray );
    uint64_t victimAddress = at->ReverseTranslate( victimRow, 0, victimBank, victimRank, promotionChannel, victimSubarray );
    victim.SetPhysicalAddress( victimAddress );
	if( currentPromotionPage>0)
	    currentPromotionPage = (currentPromotionPage - 1) % totalPromotionPages;
	else
		currentPromotionPage = ( currentPromotionPage + totalPromotionPages)% totalPromotionPages;
}