首先給出源代碼
頭文件的
#ifndef __FRFCFS_H__
#define __FRFCFS_H__
#include "src/MemoryController.h"
#include <deque>
namespace NVM {
class FRFCFS : public MemoryController
// FRFCFS是繼承自內存控制的一個子類
{
public:
FRFCFS( );
~FRFCFS( );
bool IssueCommand( NVMainRequest *req );
bool IsIssuable( NVMainRequest *request, FailReason *fail = NULL );
bool RequestComplete( NVMainRequest * request );
void SetConfig( Config *conf, bool createChildren = true );
void Cycle( ncycle_t steps );
void RegisterStats( );
void CalculateStats( );
private:
NVMTransactionQueue *memQueue;
/* Cached Configuration Variables*/
uint64_t queueSize;
/* Stats */
uint64_t measuredLatencies, measuredQueueLatencies, measuredTotalLatencies;
double averageLatency, averageQueueLatency, averageTotalLatency;
uint64_t mem_reads, mem_writes;
uint64_t rb_hits;
uint64_t rb_miss;
uint64_t starvation_precharges;
uint64_t write_pauses;
};
};
//注意這裏的變量有measuredLatencies, measuredQueueLatencies, measuredTotalLatencies;averageLatency, averageQueueLatency, averageTotalLatency;
#endif
和我們在helloworld裏面的就有點聯繫了
接下來我們一個一個看看怎麼實現的
首先是構造函數和解構函數
FRFCFS::FRFCFS( )
{
std::cout << "Created a First Ready First Come First Serve memory controller!"
<< std::endl;
queueSize = 32;
starvationThreshold = 4;
// 飢餓閾值是4
averageLatency = 0.0f;
averageQueueLatency = 0.0f;
averageTotalLatency = 0.0f;
measuredLatencies = 0;
measuredQueueLatencies = 0;
measuredTotalLatencies = 0;
mem_reads = 0;
mem_writes = 0;
rb_hits = 0;
rb_miss = 0;
write_pauses = 0;
starvation_precharges = 0;
//沒有飢餓刷新
psInterval = 0;
InitQueues( 1 );
//初始化個隊列,然後內存隊列初始化
//memQueue指向了transaction隊列的首地址,這個隊列的每一個元素都是transrequest
memQueue = &(transactionQueues[0]);
}
FRFCFS::~FRFCFS( )
{
std::cout << "FRFCFS memory controller destroyed. " << memQueue->size( )
<< " commands still in memory queue." << std::endl;
}
這裏這個InitQueues是這樣的:
void MemoryController::InitQueues( unsigned int numQueues )
{
if( transactionQueues != NULL )
delete [] transactionQueues;
transactionQueues = new NVMTransactionQueue[ numQueues ];
transactionQueueCount = numQueues;
for( unsigned int i = 0; i < numQueues; i++ )
transactionQueues[i].clear( );
}
setconfig
void FRFCFS::SetConfig( Config *conf, bool createChildren )
{
if( conf->KeyExists( "StarvationThreshold" ) )
{
starvationThreshold = static_cast<unsigned int>( conf->GetValue( "StarvationThreshold" ) );
}
if( conf->KeyExists( "QueueSize" ) )
{
queueSize = static_cast<unsigned int>( conf->GetValue( "QueueSize" ) );
}
MemoryController::SetConfig( conf, createChildren );
SetDebugName( "FRFCFS", conf );
}
添加狀態
void FRFCFS::RegisterStats( )
{
AddStat(mem_reads);
AddStat(mem_writes);
AddStat(rb_hits);
AddStat(rb_miss);
AddStat(starvation_precharges);
AddStat(averageLatency);
AddStat(averageQueueLatency);
AddStat(averageTotalLatency);
AddStat(measuredLatencies);
AddStat(measuredQueueLatencies);
AddStat(measuredTotalLatencies);
AddStat(write_pauses);
MemoryController::RegisterStats( );
}
Issuecommand請求命令(所有的調度決定都在這裏)
/*
* This method is called whenever a new transaction from the processor issued to
* this memory controller / channel. All scheduling decisions should be made here.
*/
bool FRFCFS::IssueCommand( NVMainRequest *req )
{
if( !IsIssuable( req ) )
{
return false;
}
//如果issue提出時已經不可提出issue了(滿了),返回false
req->arrivalCycle = GetEventQueue()->GetCurrentCycle();
// GetEventQueue()會返回一個指向EventQueue類的指針,然後調用它的方法獲得當前時間
/*
* Just push back the read/write. It's easier to inject dram commands than break it up
* here and attempt to remove them later.
*/
Enqueue( 0, req );
if( req->type == READ )
mem_reads++;
else
mem_writes++;
/*
* Return whether the request could be queued. Return false if the queue is full.
*/
return true;
}
這裏直接就把第0個queue從隊尾加上req
判斷request請求是否完成
bool FRFCFS::RequestComplete( NVMainRequest * request )
{
//如果是寫請求並且寫請求預充電了,就撤銷,直接加在隊頭
if( request->type == WRITE || request->type == WRITE_PRECHARGE )
{
/*
* Put cancelled requests at the head of the write queue
* like nothing ever happened.
*/
if( request->flags & NVMainRequest::FLAG_CANCELLED
|| request->flags & NVMainRequest::FLAG_PAUSED )
{
Prequeue( 0, request );
return true;
}
}
/* Only reads and writes are sent back to NVMain and checked for in the transaction queue. */
if( request->type == READ
|| request->type == READ_PRECHARGE
|| request->type == WRITE
|| request->type == WRITE_PRECHARGE )
{
request->status = MEM_REQUEST_COMPLETE;
request->completionCycle = GetEventQueue()->GetCurrentCycle();
/* Update the average latencies based on this request for READ/WRITE only. */
averageLatency = ((averageLatency * static_cast<double>(measuredLatencies))
+ static_cast<double>(request->completionCycle)
- static_cast<double>(request->issueCycle))
/ static_cast<double>(measuredLatencies+1);
measuredLatencies += 1;
averageQueueLatency = ((averageQueueLatency * static_cast<double>(measuredQueueLatencies))
+ static_cast<double>(request->issueCycle)
- static_cast<double>(request->arrivalCycle))
/ static_cast<double>(measuredQueueLatencies+1);
measuredQueueLatencies += 1;
averageTotalLatency = ((averageTotalLatency * static_cast<double>(measuredTotalLatencies))
+ static_cast<double>(request->completionCycle)
- static_cast<double>(request->arrivalCycle))
/ static_cast<double>(measuredTotalLatencies+1);
measuredTotalLatencies += 1;
}
return MemoryController::RequestComplete( request );
}
無論是讀還是寫,都要計算各種延遲
FRFCFS 的cycle
先看看memorycontroller的cycle裏面有什麼
void MemoryController::Cycle( ncycle_t /*steps*/ )
{
/*
* Recheck transaction queues for issuables. This may happen when two
* transactions can be issued in the same cycle, but we can't guarantee
* the transaction won't be blocked by the first wake up.
*/
bool scheduled = false;
ncycle_t nextWakeup = GetEventQueue( )->GetCurrentCycle( ) + 1;
/* Skip this if another transaction is scheduled this cycle. */
if( GetEventQueue( )->FindEvent( EventCycle, this, NULL, nextWakeup ) )
return;
for( ncounter_t queueIdx = 0; queueIdx < commandQueueCount; queueIdx++ )
{
if( EffectivelyEmpty( queueIdx )
&& TransactionAvailable( queueIdx ) )
{
GetEventQueue( )->InsertEvent( EventCycle, this, nextWakeup, NULL, transactionQueuePriority );
/* Only allow one request. */
scheduled = true;
break;
}
if( scheduled )
{
break;
}
}
}
先看看有沒有飢餓請求
void FRFCFS::Cycle( ncycle_t steps )
{
NVMainRequest *nextRequest = NULL;
/* Check for starved requests BEFORE row buffer hits. */
if( FindStarvedRequest( *memQueue, &nextRequest ) )
{
rb_miss++;
starvation_precharges++;
}
/* Check for row buffer hits. */
//如果要讀取rb
else if( FindRowBufferHit( *memQueue, &nextRequest) )
{
rb_hits++;
}
/* Check if the address is accessible through any other means. */
else if( FindCachedAddress( *memQueue, &nextRequest ) )
{
}
else if( FindWriteStalledRead( *memQueue, &nextRequest ) )
{
if( nextRequest != NULL )
write_pauses++;
}
/* Find the oldest request that can be issued. */
else if( FindOldestReadyRequest( *memQueue, &nextRequest ) )
{
rb_miss++;
}
/* Find requests to a bank that is closed. */
else if( FindClosedBankRequest( *memQueue, &nextRequest ) )
{
rb_miss++;
}
else
{
nextRequest = NULL;
}
/* Issue the commands for this transaction. */
if( nextRequest != NULL )
{
IssueMemoryCommands( nextRequest );
}
/* Issue any commands in the command queues. */
CycleCommandQueues( );
MemoryController::Cycle( steps );
}
void FRFCFS::CalculateStats( )
{
MemoryController::CalculateStats( );
}
所有代碼
#include "MemControl/FRFCFS/FRFCFS.h"
#include "src/EventQueue.h"
#include "include/NVMainRequest.h"
#ifndef TRACE
#ifdef GEM5
#include "SimInterface/Gem5Interface/Gem5Interface.h"
#include "base/statistics.hh"
#include "base/types.hh"
#include "sim/core.hh"
#include "sim/stat_control.hh"
#endif
#endif
#include <iostream>
#include <set>
#include <assert.h>
using namespace NVM;
FRFCFS::FRFCFS( )
{
std::cout << "Created a First Ready First Come First Serve memory controller!"
<< std::endl;
queueSize = 32;
starvationThreshold = 4;
averageLatency = 0.0f;
averageQueueLatency = 0.0f;
averageTotalLatency = 0.0f;
measuredLatencies = 0;
measuredQueueLatencies = 0;
measuredTotalLatencies = 0;
mem_reads = 0;
mem_writes = 0;
rb_hits = 0;
rb_miss = 0;
write_pauses = 0;
starvation_precharges = 0;
psInterval = 0;
InitQueues( 1 );
memQueue = &(transactionQueues[0]);
}
FRFCFS::~FRFCFS( )
{
std::cout << "FRFCFS memory controller destroyed. " << memQueue->size( )
<< " commands still in memory queue." << std::endl;
}
void FRFCFS::SetConfig( Config *conf, bool createChildren )
{
if( conf->KeyExists( "StarvationThreshold" ) )
{
starvationThreshold = static_cast<unsigned int>( conf->GetValue( "StarvationThreshold" ) );
}
if( conf->KeyExists( "QueueSize" ) )
{
queueSize = static_cast<unsigned int>( conf->GetValue( "QueueSize" ) );
}
MemoryController::SetConfig( conf, createChildren );
SetDebugName( "FRFCFS", conf );
}
void FRFCFS::RegisterStats( )
{
AddStat(mem_reads);
AddStat(mem_writes);
AddStat(rb_hits);
AddStat(rb_miss);
AddStat(starvation_precharges);
AddStat(averageLatency);
AddStat(averageQueueLatency);
AddStat(averageTotalLatency);
AddStat(measuredLatencies);
AddStat(measuredQueueLatencies);
AddStat(measuredTotalLatencies);
AddStat(write_pauses);
MemoryController::RegisterStats( );
}
bool FRFCFS::IsIssuable( NVMainRequest * /*request*/, FailReason * /*fail*/ )
{
bool rv = true;
/*
* Limit the number of commands in the queue. This will stall the caches/CPU.
* 前面提到過這裏的queuesize是32
*/
if( memQueue->size( ) >= queueSize )
{
rv = false;
}
return rv;
}
/*
* This method is called whenever a new transaction from the processor issued to
* this memory controller / channel. All scheduling decisions should be made here.
*/
bool FRFCFS::IssueCommand( NVMainRequest *req )
{
if( !IsIssuable( req ) )
{
return false;
}
req->arrivalCycle = GetEventQueue()->GetCurrentCycle();
/*
* Just push back the read/write. It's easier to inject dram commands than break it up
* here and attempt to remove them later.
*/
Enqueue( 0, req );
if( req->type == READ )
mem_reads++;
else
mem_writes++;
/*
* Return whether the request could be queued. Return false if the queue is full.
*/
return true;
}
bool FRFCFS::RequestComplete( NVMainRequest * request )
{
if( request->type == WRITE || request->type == WRITE_PRECHARGE )
{
/*
* Put cancelled requests at the head of the write queue
* like nothing ever happened.
*/
if( request->flags & NVMainRequest::FLAG_CANCELLED
|| request->flags & NVMainRequest::FLAG_PAUSED )
{
Prequeue( 0, request );
return true;
}
}
/* Only reads and writes are sent back to NVMain and checked for in the transaction queue. */
if( request->type == READ
|| request->type == READ_PRECHARGE
|| request->type == WRITE
|| request->type == WRITE_PRECHARGE )
{
request->status = MEM_REQUEST_COMPLETE;
request->completionCycle = GetEventQueue()->GetCurrentCycle();
/* Update the average latencies based on this request for READ/WRITE only. */
averageLatency = ((averageLatency * static_cast<double>(measuredLatencies))
+ static_cast<double>(request->completionCycle)
- static_cast<double>(request->issueCycle))
/ static_cast<double>(measuredLatencies+1);
measuredLatencies += 1;
averageQueueLatency = ((averageQueueLatency * static_cast<double>(measuredQueueLatencies))
+ static_cast<double>(request->issueCycle)
- static_cast<double>(request->arrivalCycle))
/ static_cast<double>(measuredQueueLatencies+1);
measuredQueueLatencies += 1;
averageTotalLatency = ((averageTotalLatency * static_cast<double>(measuredTotalLatencies))
+ static_cast<double>(request->completionCycle)
- static_cast<double>(request->arrivalCycle))
/ static_cast<double>(measuredTotalLatencies+1);
measuredTotalLatencies += 1;
}
return MemoryController::RequestComplete( request );
}
void FRFCFS::Cycle( ncycle_t steps )
{
NVMainRequest *nextRequest = NULL;
/* Check for starved requests BEFORE row buffer hits. */
if( FindStarvedRequest( *memQueue, &nextRequest ) )
{
rb_miss++;
starvation_precharges++;
}
/* Check for row buffer hits. */
else if( FindRowBufferHit( *memQueue, &nextRequest) )
{
rb_hits++;
}
/* Check if the address is accessible through any other means. */
else if( FindCachedAddress( *memQueue, &nextRequest ) )
{
}
else if( FindWriteStalledRead( *memQueue, &nextRequest ) )
{
if( nextRequest != NULL )
write_pauses++;
}
/* Find the oldest request that can be issued. */
else if( FindOldestReadyRequest( *memQueue, &nextRequest ) )
{
rb_miss++;
}
/* Find requests to a bank that is closed. */
else if( FindClosedBankRequest( *memQueue, &nextRequest ) )
{
rb_miss++;
}
else
{
nextRequest = NULL;
}
/* Issue the commands for this transaction. */
if( nextRequest != NULL )
{
IssueMemoryCommands( nextRequest );
}
/* Issue any commands in the command queues. */
CycleCommandQueues( );
MemoryController::Cycle( steps );
}
void FRFCFS::CalculateStats( )
{
MemoryController::CalculateStats( );
}