Tejas/src/simulator/dram/CommandQueue.java

774 lines
23 KiB
Java

package dram;
import java.util.ArrayList;
//import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;
import config.MainMemoryConfig;
import config.MainMemoryConfig.QueuingStructure;
import config.MainMemoryConfig.RowBufferPolicy;
import config.MainMemoryConfig.SchedulingPolicy;
import dram.BankState.CurrentBankState;
import dram.MainMemoryBusPacket.BusPacketType;
import generic.GlobalClock;
import main.Main;
import misc.Error;
import net.Bus;
public class CommandQueue {
ArrayList<MainMemoryBusPacket> BusPacketQueue1D;
ArrayList<ArrayList<MainMemoryBusPacket>> perRankQueue;
ArrayList<ArrayList<ArrayList<MainMemoryBusPacket>>> queues;
//QueuingStructure queuingStructure; //in mainmemconfig
int numBankQueues;
int CMD_QUEUE_DEPTH;
BankState[][] bankStates;
int rowAccessCounters[][];
MainMemoryConfig mainMemoryConfig;
int nextRank;
int nextBank;
int nextRankPRE;
int nextBankPRE;
int refreshRank;
boolean refreshWaiting;
boolean sendAct;
ArrayList<ArrayList<Integer>> tFAWCountdown;
public CommandQueue(MainMemoryConfig mainMemoryConfig,BankState bankStates[][])
{
sendAct=true;
nextBank=0;
nextRank=0;
refreshWaiting=false;
refreshRank=0;
nextBankPRE=0;
nextRankPRE=0;
this.mainMemoryConfig=mainMemoryConfig;
rowAccessCounters=new int[mainMemoryConfig.numRanks][mainMemoryConfig.numBanks];
tFAWCountdown=new ArrayList<ArrayList<Integer>>(mainMemoryConfig.numRanks);
for (int i=0;i<mainMemoryConfig.numRanks;i++)
{
//init the empty vectors here so we don't seg fault later
tFAWCountdown.add(new ArrayList<Integer>());
}
this.bankStates=bankStates;
if (mainMemoryConfig.queuingStructure==QueuingStructure.PerRank)
{
numBankQueues = 1;
}
else if (mainMemoryConfig.queuingStructure==QueuingStructure.PerRankPerBank)
{
numBankQueues = mainMemoryConfig.numBanks;
}
else
{
Error.showErrorAndExit("== Error - Unknown queuing structure");
}
//BusPacketQueue1D = new ArrayList<MainMemoryBusPacket>();
this.CMD_QUEUE_DEPTH = 1024;
//perRankQueue = new ArrayList<ArrayList<MainMemoryBusPacket>>();
queues = new ArrayList<ArrayList<ArrayList<MainMemoryBusPacket>>>();
for (int rank=0; rank<mainMemoryConfig.numRanks; rank++)
{
//this loop will run only once for per-rank and NUM_BANKS times for per-rank-per-bank
perRankQueue = new ArrayList<ArrayList<MainMemoryBusPacket>>();
for (int bank=0; bank<numBankQueues; bank++)
{
BusPacketQueue1D = new ArrayList<MainMemoryBusPacket>();
//actualQueue = BusPacket1D();
//System.out.println(BusPacketQueue1D);
perRankQueue.add(BusPacketQueue1D);
}
queues.add(perRankQueue);
}
}
public void enqueue(MainMemoryBusPacket busPacket)
{
int rank = busPacket.rank;
int bank = busPacket.bank;
if (mainMemoryConfig.queuingStructure==QueuingStructure.PerRank)
{
queues.get(rank).get(0).add(busPacket);
//if(mainMemoryConfig.DEBUG_CMDQ)
// Test.outputLog.print("Enqueued to command queue for rank "+ rank + "\n");
if (queues.get(rank).get(0).size()>CMD_QUEUE_DEPTH)
{
Error.showErrorAndExit("== Error - Enqueued more than allowed in command queue");
}
}
else if (mainMemoryConfig.queuingStructure==QueuingStructure.PerRankPerBank)
{
queues.get(rank).get(bank).add(busPacket);
if (queues.get(rank).get(bank).size()>CMD_QUEUE_DEPTH)
{
Error.showErrorAndExit("== Error - Enqueued more than allowed in command queue");
}
}
else
{
Error.showErrorAndExit("== Error - Unknown queuing structure");
}
}
//checks if busPacket is allowed to be issued
boolean isIssuable(MainMemoryBusPacket busPacket,long currentClockCycle )
{
// long currentClockCycle=GlobalClock.getCurrentTime();
switch (busPacket.busPacketType)
{
case REFRESH:
break;
case ACTIVATE:
if ((bankStates[busPacket.rank][busPacket.bank].currentBankState == CurrentBankState.IDLE ||
bankStates[busPacket.rank][busPacket.bank].currentBankState == CurrentBankState.REFRESHING) &&
currentClockCycle >= bankStates[busPacket.rank][busPacket.bank].nextActivate &&
tFAWCountdown.get(busPacket.rank).size() < 4)
{
return true;
}
else
{
//busPacket.printPacketToFile();
//Main.debugPrinter.print("\n");
/*if(mainMemoryConfig.DEBUG_CMDQ)
{
if(currentClockCycle < 0 && busPacket.bank == 3)
{
Main.outputLog.print("Cannot issue Activate because \n");
Main.outputLog.print(String.valueOf(bankStates[busPacket.rank][busPacket.bank].currentBankState == CurrentBankState.IDLE) + " ");
Main.outputLog.print(String.valueOf(bankStates[busPacket.rank][busPacket.bank].currentBankState == CurrentBankState.REFRESHING) + " ");
Main.outputLog.print(String.valueOf(currentClockCycle >= bankStates[busPacket.rank][busPacket.bank].nextActivate) + " ");
Main.outputLog.print(String.valueOf(tFAWCountdown.get(busPacket.rank).size() < 4) + " \n");
Main.outputLog.print(bankStates[0][3].currentBankState.toString());
}
}*/
return false;
}
//break;
case WRITE:
case WRITE_P:
if (bankStates[busPacket.rank][busPacket.bank].currentBankState == CurrentBankState.ROW_ACTIVE &&
currentClockCycle >= bankStates[busPacket.rank][busPacket.bank].nextWrite &&
busPacket.row == bankStates[busPacket.rank][busPacket.bank].openRowAddress &&
rowAccessCounters[busPacket.rank][busPacket.bank] < MainMemoryConfig.TOTAL_ROW_ACCESSES)
{
return true;
}
else
{
//Main.debugPrinter.print("Cannot issue packet of type: \n");
//busPacket.printPacketToFile();
//Main.debugPrinter.print("\n");
return false;
}
//break;
case READ_P:
case READ:
if (bankStates[busPacket.rank][busPacket.bank].currentBankState == CurrentBankState.ROW_ACTIVE &&
currentClockCycle >= bankStates[busPacket.rank][busPacket.bank].nextRead &&
busPacket.row == bankStates[busPacket.rank][busPacket.bank].openRowAddress &&
rowAccessCounters[busPacket.rank][busPacket.bank] < MainMemoryConfig.TOTAL_ROW_ACCESSES)
{
return true;
}
else
{
//Main.debugPrinter.print("Cannot issue packet of type: \n");
//busPacket.printPacketToFile();
//Main.debugPrinter.print("\n");
return false;
}
//break;
case PRECHARGE:
if (bankStates[busPacket.rank][busPacket.bank].currentBankState == CurrentBankState.ROW_ACTIVE &&
currentClockCycle >= bankStates[busPacket.rank][busPacket.bank].nextPrecharge)
{
return true;
}
else
{
//Main.debugPrinter.print("Cannot issue packet of type: \n");
//busPacket.printPacketToFile();
//Main.debugPrinter.print("\n");
return false;
}
//break;
default:
Error.showErrorAndExit("== Error - Trying to issue a crazy bus packet type : ");
busPacket.printPacket();
}
return false;
}
//Removes the next item from the command queue based on the system's
//command scheduling policy
public MainMemoryBusPacket pop(long currentClockCycle)
{
MainMemoryBusPacket busPacket=null;
//this can be done here because pop() is called every clock cycle by the parent MemoryController
// figures out the sliding window requirement for tFAW
//
//deal with tFAW book-keeping
// each rank has it's own counter since the restriction is on a device level
for (int i=0;i<mainMemoryConfig.numRanks;i++)
{
//decrement all the counters we have going
for (int j=0;j<tFAWCountdown.get(i).size();j++)
{
tFAWCountdown.get(i).set(j,tFAWCountdown.get(i).get(j)-1);
}
//the head will always be the smallest counter, so check if it has reached 0
if (tFAWCountdown.get(i).size()>0 && tFAWCountdown.get(i).get(0)==0)
{
//tFAWCountdown[i].erase(tFAWCountdown[i].begin());
//Main.debugPrinter.print("\nFinally removed\n");
//Main.debugPrinter.print(i+" :rank\n");
//Main.debugPrinter.print("size: "+tFAWCountdown.get(i).size());
tFAWCountdown.get(i).remove(0);
}
}
/* Now we need to find a packet to issue. When the code picks a packet, it will set
busPacket = [some eligible packet]
First the code looks if any refreshes need to go
Then it looks for data packets
Otherwise, it starts looking for rows to close (in open page)
*/
if (mainMemoryConfig.rowBufferPolicy==RowBufferPolicy.ClosePage)
{
boolean sendingREF = false;
//if the memory controller set the flags signaling that we need to issue a refresh
if (refreshWaiting)
{
boolean foundActiveOrTooEarly = false;
//look for an open bank
for (int b=0;b<mainMemoryConfig.numBanks;b++)
{
ArrayList<MainMemoryBusPacket> queue = getCommandQueue(refreshRank,b);
//checks to make sure that all banks are idle
if (bankStates[refreshRank][b].currentBankState == CurrentBankState.ROW_ACTIVE)
{
foundActiveOrTooEarly = true;
//if the bank is open, make sure there is nothing else
// going there before we close it
for (int j=0;j<queue.size();j++)
{
MainMemoryBusPacket packet = queue.get(j);
if (packet.row == bankStates[refreshRank][b].openRowAddress &&
packet.bank == b)
{
if (packet.busPacketType != BusPacketType.ACTIVATE && isIssuable(packet,currentClockCycle))
{
busPacket = packet;
queue.remove(j);
sendingREF = true;
}
break;
}
}
break;
}
// NOTE: checks nextActivate time for each bank to make sure tRP is being
// satisfied. the next ACT and next REF can be issued at the same
// point in the future, so just use nextActivate field instead of
// creating a nextRefresh field
else if (bankStates[refreshRank][b].nextActivate > currentClockCycle)
{
foundActiveOrTooEarly = true;
break;
}
}
//if there are no open banks and timing has been met, send out the refresh
// reset flags and rank pointer
if (!foundActiveOrTooEarly && bankStates[refreshRank][0].currentBankState != CurrentBankState.POWER_DOWN)
{
busPacket = new MainMemoryBusPacket(1, 1, 0, refreshRank,0 ,BusPacketType.REFRESH);
refreshRank = -1;
refreshWaiting = false;
sendingREF = true;
}
} // refreshWaiting
//if we're not sending a REF, proceed as normal
if (!sendingREF)
{
boolean foundIssuable = false;
int startingRank = nextRank;
int startingBank = nextBank;
do
{
ArrayList<MainMemoryBusPacket> queue = getCommandQueue(nextRank, nextBank);
//make sure there is something in this queue first
// also make sure a rank isn't waiting for a refresh
// if a rank is waiting for a refresh, don't issue anything to it until the
// refresh logic above has sent one out (ie, letting banks close)
if (!(queue.size()==0) && !((nextRank == refreshRank) && refreshWaiting))
{
if (mainMemoryConfig.queuingStructure == QueuingStructure.PerRank)
{
//search from beginning to find first issuable bus packet
for (int i=0;i<queue.size();i++)
{
if (isIssuable(queue.get(i),currentClockCycle))
{
//check to make sure we aren't removing a read/write that is paired with an activate
if (i>0 && queue.get(i-1).busPacketType==BusPacketType.ACTIVATE &&
queue.get(i-1).physicalAddress == queue.get(i).physicalAddress)
continue;
busPacket = queue.get(i);
queue.remove(i);
foundIssuable = true;
break;
}
}
}
else
{
if (isIssuable(queue.get(0),currentClockCycle))
{
//no need to search because if the front can't be sent,
// then no chance something behind it can go instead
busPacket = queue.get(0);
queue.remove(0);
foundIssuable = true;
}
}
}
//if we found something, break out of do-while
if (foundIssuable) break;
//rank round robin
if (mainMemoryConfig.queuingStructure == QueuingStructure.PerRank)
{
nextRank = (nextRank + 1) % mainMemoryConfig.numRanks;
if (startingRank == nextRank)
{
break;
}
}
else
{
int temp[]=nextRankAndBank(nextRank, nextBank);
nextRank = temp[0];
nextBank = temp[1];
if (startingRank == nextRank && startingBank == nextBank)
{
break;
}
}
}
while (true);
//if we couldn't find anything to send, return false
if (!foundIssuable){
//Test.debugPrinter.print("Not foundIssuable !!!\n");
return null;}
}
}
else if (mainMemoryConfig.rowBufferPolicy==RowBufferPolicy.OpenPage)
{
boolean sendingREForPRE = false;
if (refreshWaiting)
{
boolean sendREF = true;
//make sure all banks idle and timing met for a REF
for (int b=0;b<mainMemoryConfig.numBanks;b++)
{
//if a bank is active we can't send a REF yet
if (bankStates[refreshRank][b].currentBankState == CurrentBankState.ROW_ACTIVE)
{
sendREF = false;
boolean closeRow = true;
//search for commands going to an open row
ArrayList<MainMemoryBusPacket> refreshQueue = getCommandQueue(refreshRank,b);
for (int j=0;j<refreshQueue.size();j++)
{
MainMemoryBusPacket packet = refreshQueue.get(j);
//if a command in the queue is going to the same row . . .
if (bankStates[refreshRank][b].openRowAddress == packet.row &&
b == packet.bank)
{
// . . . and is not an activate . . .
if (packet.busPacketType != BusPacketType.ACTIVATE)
{
closeRow = false;
// . . . and can be issued . . .
if (isIssuable(packet,currentClockCycle))
{
//send it out
busPacket = packet;
refreshQueue.remove(j);
sendingREForPRE = true;
}
break;
}
else //command is an activate
{
//if we've encountered another act, no other command will be of interest
break;
}
}
}
//if the bank is open and we are allowed to close it, then send a PRE
if (closeRow && currentClockCycle >= bankStates[refreshRank][b].nextPrecharge)
{
rowAccessCounters[refreshRank][b]=0;
busPacket = new MainMemoryBusPacket( 0, 0, b, refreshRank, 0, BusPacketType.PRECHARGE);
sendingREForPRE = true;
}
break;
}
// NOTE: the next ACT and next REF can be issued at the same
// point in the future, so just use nextActivate field instead of
// creating a nextRefresh field
else if (bankStates[refreshRank][b].nextActivate > currentClockCycle) //and this bank doesn't have an open row
{
sendREF = false;
break;
}
}
//if there are no open banks and timing has been met, send out the refresh
// reset flags and rank pointer
if (sendREF && bankStates[refreshRank][0].currentBankState != CurrentBankState.POWER_DOWN)
{
busPacket = new MainMemoryBusPacket(0, 0, 0, refreshRank, 0, BusPacketType.REFRESH);
refreshRank = -1;
refreshWaiting = false;
sendingREForPRE = true;
}
}
if (!sendingREForPRE)
{
int startingRank = nextRank;
int startingBank = nextBank;
boolean foundIssuable = false;
do // round robin over queues
{
ArrayList<MainMemoryBusPacket> queue = getCommandQueue(nextRank,nextBank);
//make sure there is something there first
if (!(queue.size()==0) && !((nextRank == refreshRank) && refreshWaiting))
{
//search from the beginning to find first issuable bus packet
for (int i=0;i<queue.size();i++)
{
MainMemoryBusPacket packet = queue.get(i);
if (isIssuable(packet,currentClockCycle))
{
//check for dependencies
boolean dependencyFound = false;
for (int j=0;j<i;j++)
{
MainMemoryBusPacket prevPacket = queue.get(j);
if (prevPacket.busPacketType != BusPacketType.ACTIVATE &&
prevPacket.bank == packet.bank &&
prevPacket.row == packet.row)
{
dependencyFound = true;
break;
}
}
if (dependencyFound) continue;
busPacket = packet;
//if the bus packet before is an activate, that is the act that was
// paired with the column access we are removing, so we have to remove
// that activate as well (check i>0 because if i==0 then theres nothing before it)
if (i>0 && queue.get(i-1).busPacketType == BusPacketType.ACTIVATE)
{
rowAccessCounters[(busPacket).rank][(busPacket).bank]++;
//Test.outputLog.print("incrementing row access counter for bank " + busPacket.bank + " value is "
// + rowAccessCounters[(busPacket).rank][(busPacket).bank] +"\n");
// i is being returned, but i-1 is being thrown away, so must delete it here
//changed by kushagra
queue.set(i-1,null);
// remove both i-1 (the activate) and i (we've saved the pointer in *busPacket)
//queue.remove(i-1); //added by kushagra
queue.remove(i);
queue.remove(i-1);
}
else // there's no activate before this packet
{
//or just remove the one bus packet
queue.remove(i);
}
foundIssuable = true;
break;
}
}
}
//if we found something, break out of do-while
if (foundIssuable) break;
//rank round robin
if (mainMemoryConfig.queuingStructure == QueuingStructure.PerRank)
{
nextRank = (nextRank + 1) % mainMemoryConfig.numRanks;
if (startingRank == nextRank)
{
break;
}
}
else
{
int temp[] = nextRankAndBank(nextRank, nextBank);
nextRank = temp[0];
nextBank = temp[1];
temp = null;
if (startingRank == nextRank && startingBank == nextBank)
{
break;
}
}
}
while (true);
//if nothing was issuable, see if we can issue a PRE to an open bank
// that has no other commands waiting
if (!foundIssuable)
{
//Test.outputLog.print("Searching for banks to close\n");
//search for banks to close
boolean sendingPRE = false;
startingRank = nextRankPRE;
startingBank = nextBankPRE;
do // round robin over all ranks and banks
{
ArrayList<MainMemoryBusPacket> queue = getCommandQueue(nextRankPRE, nextBankPRE);
boolean found = false;
//check if bank is open
if (bankStates[nextRankPRE][nextBankPRE].currentBankState == CurrentBankState.ROW_ACTIVE)
{
for (int i=0;i<queue.size();i++)
{
//if there is something going to that bank and row, then we don't want to send a PRE
if (queue.get(i).bank == nextBankPRE &&
queue.get(i).row == bankStates[nextRankPRE][nextBankPRE].openRowAddress)
{
found = true;
break;
}
}
//if nothing found going to that bank and row or too many accesses have happened, close it
if (!found || rowAccessCounters[nextRankPRE][nextBankPRE]==MainMemoryConfig.TOTAL_ROW_ACCESSES)
{
//Test.outputLog.print("Trying to issue precharge\n");
if (currentClockCycle >= bankStates[nextRankPRE][nextBankPRE].nextPrecharge)
{
//System.out.println("issuing precharge to open bank " + nextBankPRE + " next precharge " +
// bankStates[nextRankPRE][nextBankPRE].nextPrecharge + " at time " + currentClockCycle);
//Test.outputLog.print("issuing precharge to bank " + nextBankPRE);
sendingPRE = true;
rowAccessCounters[nextRankPRE][nextBankPRE] = 0;
busPacket = new MainMemoryBusPacket(0, 0, nextBankPRE, nextRankPRE, 0,BusPacketType.PRECHARGE );
break;
}
}
}
int temp[] = nextRankAndBank(nextRankPRE, nextBankPRE);
nextRankPRE = temp[0];
nextBankPRE = temp[1];
temp = null;
}
while (!(startingRank == nextRankPRE && startingBank == nextBankPRE));
//if no PREs could be sent, just return false
if (!sendingPRE) return null;
}
}
}
//sendAct is flag used for posted-cas
// posted-cas is enabled when AL>0
// when sendAct is true, when don't want to increment our indexes
// so we send the column access that is paid with this act
if (mainMemoryConfig.tAL>0 && sendAct)
{
sendAct = false;
}
else
{
sendAct = true;
int a[]=nextRankAndBank(nextRank, nextBank);
nextRank=a[0];
nextBank=a[1];
}
//if its an activate, add a tfaw counter
if (busPacket.busPacketType==BusPacketType.ACTIVATE)
{
tFAWCountdown.get((busPacket).rank).add(mainMemoryConfig.tFAW);
//Main.debugPrinter.print("\nInsert into FAW Rank: "+(busPacket).rank+"\n");
//Main.debugPrinter.print("size of rank FAW: "+tFAWCountdown.get(busPacket.rank).size()+"\n");
}
return busPacket;
}
int[] nextRankAndBank(int rank, int bank)
{
if (mainMemoryConfig.schedulingPolicy == SchedulingPolicy.RankThenBankRoundRobin)
{
rank++;
if (rank == mainMemoryConfig.numRanks)
{
rank = 0;
bank++;
if (bank == mainMemoryConfig.numBanks)
{
bank = 0;
}
}
int a[]={rank,bank};
return a;
}
//bank-then-rank round robin
else if (mainMemoryConfig.schedulingPolicy == SchedulingPolicy.BankThenRankRoundRobin)
{
bank++;
if (bank == mainMemoryConfig.numBanks)
{
bank = 0;
rank++;
if (rank == mainMemoryConfig.numRanks)
{
rank = 0;
}
}
int a[]={rank,bank};
return a;
}
else
{
Error.showErrorAndExit("== Error - Unknown scheduling policy");
return null;
}
}
ArrayList<MainMemoryBusPacket> getCommandQueue(int rank, int bank)
{
if (mainMemoryConfig.queuingStructure == QueuingStructure.PerRankPerBank)
{
return queues.get(rank).get(bank);
}
else if (mainMemoryConfig.queuingStructure == QueuingStructure.PerRank)
{
return queues.get(rank).get(0);
}
else
{
Error.showErrorAndExit("Unknown queue structure");
return null;
}
}
void needRefresh(int rank)
{
refreshWaiting = true;
refreshRank = rank;
}
public boolean canPop()
{
return(BusPacketQueue1D.size()>0);
}
public boolean hasRoomFor(int num, int rank, int bank)
{
return (CMD_QUEUE_DEPTH - getCommandQueue(rank, bank).size() >= num);
}
//this function for TEST
public void printTest()
{
if (mainMemoryConfig.queuingStructure== QueuingStructure.PerRank)
{
Main.outputLog.print("\n== Printing Per Rank Queue at Clock Cycle "+ GlobalClock.getCurrentTime() +"\n" );
for (int i=0;i< mainMemoryConfig.numRanks;i++)
{
Main.outputLog.print(" = Rank " + i + " size : " + queues.get(i).get(0).size() + "\n");
for (int j=0;j < queues.get(i).get(0).size();j++)
{
Main.outputLog.print(" "+ j + "]");
queues.get(i).get(0).get(j).printTest();
}
}
}
}
}