package dram; import java.util.ArrayList; import config.SystemConfig; 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.Core; import generic.Event; import generic.EventQueue; import generic.GlobalClock; import generic.RequestType; import main.ArchitecturalComponent; import main.Main; import memorysystem.AddressCarryingEvent; import memorysystem.Cache; import memorysystem.MainMemoryController; import misc.Error; public class MainMemoryDRAMControllerTest extends MainMemoryController{ private int numTransactions; MainMemoryConfig mainMemoryConfig; //TODO: need a way to store the actual requesting element //dirty workaround for now Cache parentCache; int refreshRank; Test parentTest; ArrayList pendingTransQueue; //MainMemoryBusPacket pendingTransQueue[]; //to keep track of packets that could not be added to command queue BankState bankStates[][]; CommandQueue commandQueue; Rank ranks[]; int refreshCount[]; public MainMemoryDRAMControllerTest(MainMemoryConfig mainMemoryConfig, Test parentTest) { super(); this.parentTest = parentTest; this.mainMemoryConfig = mainMemoryConfig; numTransactions = 0; refreshRank=0; ranks = new Rank[mainMemoryConfig.numRanks]; bankStates = new BankState[mainMemoryConfig.numRanks][mainMemoryConfig.numBanks]; //TODO is there a more elegant way :P for(int i=0; i < mainMemoryConfig.numRanks;i++) { for(int j=0; j < mainMemoryConfig.numBanks; j++) { bankStates[i][j] = new BankState(); } ranks[i] = new Rank(mainMemoryConfig,i,this); } pendingTransQueue=new ArrayList(); commandQueue = new CommandQueue(mainMemoryConfig,bankStates); //TODO: allocate and initialize arrays refreshCount=new int[mainMemoryConfig.numRanks]; for(int i=0;i perform address mapping and add it to command queue if(true) { //System.out.println("Hi handling a Cache Read!!"); //TODO: very dirty workaround this.parentCache = (Cache) e.getRequestingElement(); AddressCarryingEvent event = (AddressCarryingEvent) e; //maintain number of transactions waiting to be serviced numTransactions++; MainMemoryBusPacket b = AddressMapping(event.getAddress()); b.setBusPacketType(requestTypeToBusPacketType(event.getRequestType())); //for TIMING b.timeCreated = GlobalClock.getCurrentTime(); //pendingTransQueue.add(x); //MainMemoryBusPacket b=pendingTransQueue.get(0); Test.debugPrinter.print("Of bus packet type:"); b.printPacketToFile(); if(commandQueue.hasRoomFor(2,b.rank, b.bank)) { numTransactions--; //the transaction is no longer waiting in the controller //pendingTransQueue.remove(0); //create new ACTIVATE bus packet with the address we just decoded MainMemoryBusPacket ACTcommand = b.Clone(); //check cloning is ok ACTcommand.setBusPacketType(BusPacketType.ACTIVATE); //create read or write command and enqueue it MainMemoryBusPacket RWcommand = b.Clone(); //RWcommand.setBusPacketType(requestTypeToBusPacketType(event.getRequestType())); //System.out.println("Enqueuing commands for address " + event.getAddress()); //System.out.println("ACTcommand busPacketType "+ACTcommand.busPacketType); //System.out.println("RWcommand busPacketType "+RWcommand.busPacketType); commandQueue.enqueue(ACTcommand); commandQueue.enqueue(RWcommand); Test.debugPrinter.print("Enqueued ACT command bus packet to queue as follows:"); ACTcommand.printPacketToFile(); Test.debugPrinter.print("Enqueued RW command bus packet to queue as follows:"); RWcommand.printPacketToFile(); //TODO: do we need to keep transactions yet to receive data in a pending queue? } //TODO: add power calculations. here? else { //TODO: need to postpone this event, but by how much time?? Test.debugPrinter.print("No room in command queue!! For rank " + b.rank +"\n"); Test.debugPrinter.print("Adding to pending queue!"); pendingTransQueue.add(b); } } else { //TODO: see how to handle this //actually there is no need to handle this if the transq size is not limited } } //finally send the data to cpu else if (e.getRequestType() == RequestType.Rank_Response) { //System.out.println("Received rank response! Sending event"); //for TEST if(mainMemoryConfig.DEBUG_BUS) { Test.outputLog.print(" -- MC Receiving From Data Bus on Clock Cycle "+ GlobalClock.getCurrentTime() +" : "); //((RamBusAddressCarryingEvent) e).getBusPacket().printTest(); } //for TEST if (mainMemoryConfig.DEBUG_BUS) { Test.outputLog.print(" -- MC Issuing to CPU bus at Clock Cycle " + GlobalClock.getCurrentTime() +" : "); Test.outputLog.print("T [Data] [0x"+ String.format("%07X",((AddressCarryingEvent)e).getAddress()).toLowerCase() + "] [0]\n"); } //for TIMING TEST /*Test.timingLog.print("Id: " + ((RamBusAddressCarryingEvent) e).getBusPacket().testid); Test.timingLog.print(" Address : " + String.format("%08X",((RamBusAddressCarryingEvent) e).getBusPacket().physicalAddress)); Test.timingLog.print(" Created at : " + ((RamBusAddressCarryingEvent) e).getBusPacket().timeCreated); Test.timingLog.print(" Completed at : " + GlobalClock.getCurrentTime() + "\n"); */ Test.timingLog.print(Long.toString(((RamBusAddressCarryingEvent) e).getBusPacket().timeCreated)); Test.timingLog.print(" " + GlobalClock.getCurrentTime() + "\n"); AddressCarryingEvent event = new AddressCarryingEvent(eventQ, 0, this, this.parentCache, RequestType.Mem_Response, ((AddressCarryingEvent)e).getAddress()); //IMP //TODO: how to make processing element as cache???? //very dirty workaround right now //understand what this does //getComInterface().sendMessage(event); //TODO: what to do for a write? } } public void oneCycleOperation(){ Test.debugPrinter.print("\nhi! In one cycle operation for time " + GlobalClock.getCurrentTime() + "\n"); //MainMemoryBusPacket b = commandQueue.pop(); long currentTime = GlobalClock.getCurrentTime(); //Core parentTest = ArchitecturalComponent.getCores()[0]; //using core 0 queue similar to as in cache if (refreshCount[refreshRank]==0) { commandQueue.needRefresh(refreshRank); ranks[refreshRank].refreshWaiting = true; refreshCount[refreshRank] = (int)(mainMemoryConfig.RefreshPeriod/mainMemoryConfig.tCK); refreshRank++; if (refreshRank == mainMemoryConfig.numRanks) { refreshRank = 0; } } //TODO:need to implement power portion of refresh MainMemoryBusPacket b = null; b = commandQueue.pop(currentTime); //if(commandQueue.canPop()) if(b!=null) { //TODO: is there a more elegant way to do this? //MainMemoryBusPacket b = commandQueue.pop(currentTime); Test.debugPrinter.print("\n\nHi!! Popped a bus packet from queue successfully! Packet is \n"); b.printPacketToFile(); Test.debugPrinter.print("\n\n"); //added by harveenk //Popped a memory packet so let's add pending packets if we have space for 2 and if it is the correct queue //First check if we have any pending packets if(pendingTransQueue.size()>0 && commandQueue.hasRoomFor(2, b.rank, b.bank)) { int foundPacketIndex = -1; MainMemoryBusPacket pendingPacket = null; //find the first packet from the pending list that belongs to this particular queue for(int i = 0; i < pendingTransQueue.size(); i++) { pendingPacket = pendingTransQueue.get(i); if(pendingPacket.rank == b.rank && !( mainMemoryConfig.queuingStructure == QueuingStructure.PerRankPerBank && pendingPacket.bank != b.bank)) { foundPacketIndex = i; break; } } /*if(mainMemoryConfig.DEBUG_CMDQ && currentTime >= 34193) { Test.outputLog.print("Trying to enqueue pending packet\n"); pendingPacket.printTest(); Test.outputLog.print("\n"); }*/ if(foundPacketIndex != -1) //found a packet for this queue, so add! { numTransactions--; //the transaction is no longer waiting in the controller pendingTransQueue.remove(foundPacketIndex); //create new ACTIVATE bus packet with the address we just decoded MainMemoryBusPacket ACTcommand = pendingPacket.Clone(); //check cloning is ok ACTcommand.setBusPacketType(BusPacketType.ACTIVATE); //create read or write command and enqueue it MainMemoryBusPacket RWcommand = pendingPacket.Clone(); //RWcommand.setBusPacketType(requestTypeToBusPacketType(event.getRequestType())); Test.debugPrinter.print("\nGot room in queue! Adding a pending bus packet!!\n\n"); //System.out.println("Enqueuing commands for address " + pendingPacket.physicalAddress); //System.out.println("ACTcommand busPacketType "+ACTcommand.busPacketType); //System.out.println("RWcommand busPacketType "+RWcommand.busPacketType); commandQueue.enqueue(ACTcommand); commandQueue.enqueue(RWcommand); Test.debugPrinter.print("Enqueued ACT command bus packet to queue as follows:"); ACTcommand.printPacketToFile(); Test.debugPrinter.print("Enqueued RW command bus packet to queue as follows:"); RWcommand.printPacketToFile(); //TODO: do we need to keep transactions yet to receive data in a pending queue? } } int rank = b.rank; int bank = b.bank; if (b.busPacketType == BusPacketType.WRITE || b.busPacketType == BusPacketType.WRITE_P) { //if write, schedule the data packet MainMemoryBusPacket dataPacketToSend = b.Clone(); dataPacketToSend.setBusPacketType(BusPacketType.DATA); Test.debugPrinter.print("\n\n Received a write, scheduling event for data packet for address " + dataPacketToSend.physicalAddress + "\n\n"); RamBusAddressCarryingEvent event = new RamBusAddressCarryingEvent( parentTest.getEventQueue() , (currentTime + mainMemoryConfig.tWL), this, ranks[rank], RequestType.Main_Mem_Access, dataPacketToSend.physicalAddress, dataPacketToSend); event.getEventQ().addEvent(event); } //update state according to the popped bus packet switch(b.busPacketType) { case READ_P: case READ: if(b.busPacketType == BusPacketType.READ_P) { bankStates[rank][bank].nextActivate = Math.max(currentTime + mainMemoryConfig.ReadAutopreDelay, bankStates[rank][bank].nextActivate); bankStates[rank][bank].lastCommand = BusPacketType.READ_P; //create and send event state update event //sending to core 0 event queue currently //keeping requesting and processing element same StateUpdateEvent StUpdtEvent = new StateUpdateEvent(parentTest.getEventQueue(), (currentTime+mainMemoryConfig.ReadToPreDelay), this, this, RequestType.Mem_Cntrlr_State_Update, rank, bank); StUpdtEvent.getEventQ().addEvent(StUpdtEvent); } else if (b.busPacketType == BusPacketType.READ) { bankStates[rank][bank].nextPrecharge = Math.max(currentTime + mainMemoryConfig.ReadToPreDelay, bankStates[rank][bank].nextPrecharge); bankStates[rank][bank].lastCommand = BusPacketType.READ; } for (int i=0;i< mainMemoryConfig.numRanks;i++) { for (int j=0;j 8 bytes -> lower 3 bits of address irrelevant int ColBytesOffset = log2(mainMemoryConfig.tBL); //these are the bits we need to throw away because of "bursts". The column address is incremented internally on bursts //So for a burst length of 4, 8 bytes of data are transferred on each burst //Each consecutive 8 byte chunk comes for the "next" column //So we traverse 4 columns in 1 request. Thus the lower log2(4) bits become irrelevant for us. Throw them away //Finally we get 8 bytes * 4 = 32 bytes of data for a 64 bit data bus and BL = 4. //This is equal to a cache line //For clarity //Throw away bits to account for data bus size in bytes //and for burst length physicalAddress >>>= (DataBusBytesOffest + ColBytesOffset); //using >>> for unsigned right shift //System.out.println("Shifted address by " + (DataBusBytesOffest + ColBytesOffset) + " bits"); //By the same logic, need to remove the burst-related column bits from the column bit width to be decoded colEffectiveBits = colBits - ColBytesOffset; //implementing 1 scheme --- "scheme 2" //chan:row:col:bank:rank tempA = physicalAddress; physicalAddress = physicalAddress >>> rankBits; //always unsigned shifting tempB = physicalAddress << rankBits; //System.out.println("Shifted address by " + rankBits + " bits"); decodedRank = (int) (tempA ^ tempB); //System.out.println("decoded rank: " + Integer.toBinaryString(decodedRank)); tempA = physicalAddress; physicalAddress = physicalAddress >>> bankBits; tempB = physicalAddress << bankBits; //System.out.println("Shifted address by " + bankBits + " bits"); decodedBank = (int) (tempA ^ tempB); //System.out.println("decoded bank: " + Integer.toBinaryString(decodedBank)); tempA = physicalAddress; physicalAddress = physicalAddress >>> colEffectiveBits; tempB = physicalAddress << colEffectiveBits; //System.out.println("Shifted address by " + colEffectiveBits + " bits"); decodedCol = (int) (tempA ^ tempB); //System.out.println("decoded col: " + Integer.toBinaryString(decodedCol)); tempA = physicalAddress; physicalAddress = physicalAddress >>> rowBits; tempB = physicalAddress << rowBits; decodedRow = (int) (tempA ^ tempB); tempA = physicalAddress; physicalAddress = physicalAddress >>> channelBits; tempB = physicalAddress << channelBits; decodedChan = (int) (tempA ^ tempB); //TODO: channel not being taken into acount right now!! //if num ranks = 1, decoded rank will always be "0" MainMemoryBusPacket b = new MainMemoryBusPacket(decodedRow, decodedCol, decodedBank, decodedRank, address, null); return b; } public static int log2(int a) { return (int) (Math.log(a)/Math.log(2)); } public BusPacketType requestTypeToBusPacketType(RequestType requestType) { switch(requestType) { case Cache_Read: if(mainMemoryConfig.getRowBufferPolicy()==RowBufferPolicy.ClosePage) { return BusPacketType.READ_P; } else if(mainMemoryConfig.getRowBufferPolicy()==RowBufferPolicy.OpenPage) { return BusPacketType.READ; } else { Error.showErrorAndExit("Unkown row buffer policy"); return null; //needed to avoid compile error } //break; //not required because "unreachable" code case Cache_Write: if(mainMemoryConfig.getRowBufferPolicy()==RowBufferPolicy.ClosePage) { return BusPacketType.WRITE_P; } else if(mainMemoryConfig.getRowBufferPolicy()==RowBufferPolicy.OpenPage) { return BusPacketType.WRITE; } else { Error.showErrorAndExit("Unkown row buffer policy"); return null; //needed to avoid compile error } //break; default: Error.showErrorAndExit("Request type "+ requestType + "does not have a corresponding bus packet type"); return null; } } void printBankStateTest() { Test.outputLog.print("== Printing bank states (According to MC) at Clock Cycle " + GlobalClock.getCurrentTime() + "\n"); for (int i=0; i < mainMemoryConfig.numRanks; i++) { for (int j=0; j < mainMemoryConfig.numBanks; j++) { if (bankStates[i][j].currentBankState == CurrentBankState.ROW_ACTIVE) { Test.outputLog.print("[" + bankStates[i][j].openRowAddress + "] "); } else if (bankStates[i][j].currentBankState == CurrentBankState.IDLE) { Test.outputLog.print("[idle] "); } else if (bankStates[i][j].currentBankState == CurrentBankState.PRECHARGING) { Test.outputLog.print("[pre] "); } else if (bankStates[i][j].currentBankState == CurrentBankState.REFRESHING) { Test.outputLog.print("[ref] "); } else if (bankStates[i][j].currentBankState == CurrentBankState.POWER_DOWN) { Test.outputLog.print("[lowp] "); } } Test.outputLog.print("\n"); } } }