首先给出源代码
头文件的
#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( );
}