/** * **************************************************************************** * Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com). * <p> * This file is part of the Archimulator multicore architectural simulator. * <p> * Archimulator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * <p> * Archimulator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * <p> * You should have received a copy of the GNU General Public License * along with Archimulator. If not, see <http://www.gnu.org/licenses/>. * **************************************************************************** */ package archimulator.uncore.dram; import archimulator.common.SimulationEvent; import archimulator.uncore.MemoryHierarchy; import archimulator.util.math.Counter; import java.util.ArrayList; import java.util.List; /** * Basic memory controller. * * @author Min Cai */ public class BasicMemoryController extends MemoryController { /** * Bank status. */ private enum BankStatus { /** * Closed. */ CLOSED, /** * Precharged. */ PRECHARGED } /** * Bank. */ private class Bank { /** * Pending access. */ private class PendingAccess { private int row; private boolean contiguous; private Runnable onCompletedCallback; /** * Create a pending access on the specified row of the bank. * * @param row the row * @param contiguous a value indicating whether access is contiguous or not * @param onCompletedCallback the callback action performed when the access is completed */ private PendingAccess(int row, boolean contiguous, Runnable onCompletedCallback) { this.row = row; this.contiguous = contiguous; this.onCompletedCallback = onCompletedCallback; } /** * Complete the pending access. */ private void complete() { this.onCompletedCallback.run(); } } private BankStatus status; private int currentRow; private List<PendingAccess> pendingAccesses; /** * Create a bank. */ private Bank() { this.status = BankStatus.CLOSED; this.currentRow = -1; this.pendingAccesses = new ArrayList<>(); } /** * Act on a precharge. * * @param onCompletedCallback the callback action performed when the precharge is completed */ private void precharge(final Runnable onCompletedCallback) { getCycleAccurateEventQueue().schedule(this, () -> { status = BankStatus.PRECHARGED; onCompletedCallback.run(); }, getClosedLatency()); } /** * Refresh the bank. */ private void refresh() { if (this.pendingAccesses.size() > 0) { final PendingAccess pendingAccess = this.pendingAccesses.get(0); access(pendingAccess.row, pendingAccess.contiguous, () -> { pendingAccess.complete(); pendingAccesses.remove(0); refresh(); }); } } /** * Begin the access on the specified row of the bank. * * @param row the row * @param contiguous a value indicating whether the access is contiguous or not * @param onCompletedCallback the callback action performed when the access is completed */ private void beginAccess(int row, boolean contiguous, Runnable onCompletedCallback) { final PendingAccess pendingAccess = new PendingAccess(row, contiguous, onCompletedCallback); this.pendingAccesses.add(pendingAccess); if (this.pendingAccesses.size() == 1) { this.refresh(); } } /** * Access the specified row of the bank. * * @param row the row * @param contiguous a value indicating whether the access is contiguous or not * @param onCompletedCallback the callback action performed when the access is completed */ private void access(final int row, final boolean contiguous, final Runnable onCompletedCallback) { if (this.status == BankStatus.CLOSED) { this.precharge(() -> access(row, contiguous, onCompletedCallback)); } else { if (currentRow == row) { if (contiguous) { getCycleAccurateEventQueue().schedule(this, onCompletedCallback, getFromDramLatency()); } else { getCycleAccurateEventQueue().schedule(this, onCompletedCallback, getPrechargeLatency() + getFromDramLatency()); } } else { getCycleAccurateEventQueue().schedule(this, () -> { currentRow = row; precharge(() -> access(row, contiguous, onCompletedCallback)); }, getConflictLatency()); } } } } /** * Access event. */ protected abstract class AccessEvent extends SimulationEvent { private int address; private int bank; /** * Create an access event. * * @param address the address * @param bank the bank */ public AccessEvent(int address, int bank) { super(BasicMemoryController.this); this.address = address; this.bank = bank; } /** * Get the address. * * @return the address */ public int getAddress() { return address; } /** * Get the bank. * * @return the bank */ public int getBank() { return bank; } } /** * Begin access event. */ public class BeginAccessEvent extends AccessEvent { /** * Create a begin access event. * * @param address the address * @param bank the bank */ public BeginAccessEvent(int address, int bank) { super(address, bank); } @Override public String toString() { return String.format("BeginAccessEvent{address=0x%08x, bank=%d}", getAddress(), getBank()); } } /** * End access event. */ public class EndAccessEvent extends AccessEvent { /** * Create an end access event. * * @param address the address * @param bank the bank */ public EndAccessEvent(int address, int bank) { super(address, bank); } @Override public String toString() { return String.format("EndAccessEvent{address=0x%08x, bank=%d}", getAddress(), getBank()); } } private int rowBits; private List<Bank> banks; private int previousBank = 0; /** * Create a basic memory controller. * * @param memoryHierarchy the parent memory hierarchy */ public BasicMemoryController(MemoryHierarchy memoryHierarchy) { super(memoryHierarchy); this.banks = new ArrayList<>(); for (int i = 0; i < this.getNumBanks(); i++) { this.banks.add(new Bank()); } int rowSize = this.getRowSize(); this.rowBits = 0; while (rowSize > 0) { this.rowBits++; rowSize >>= 1; } } /** * Act on an access of the specified address. * * @param address the address * @param onCompletedCallback the callback action performed when the access is completed */ @Override protected void access(int address, final Runnable onCompletedCallback) { final Counter counterPending = new Counter(0); int offset = 0; int size = this.getLineSize(); while (size > 0) { size -= this.getBusWidth(); final int currentAddress = address + offset; this.getCycleAccurateEventQueue().schedule(this, () -> accessDram(currentAddress, () -> { counterPending.decrement(); if (counterPending.getValue() == 0) { onCompletedCallback.run(); } }), this.getToDramLatency()); counterPending.increment(); offset += this.getBusWidth(); } } /** * Access the specified address of the DRAM. * * @param address the address * @param onCompletedCallback the callback action performed when the access is completed */ private void accessDram(final int address, final Runnable onCompletedCallback) { final int targetRow = (address >> this.rowBits) / this.getNumBanks(); final int targetBank = (address >> this.rowBits) % this.getNumBanks(); final boolean contiguous = (targetBank == previousBank); getBlockingEventDispatcher().dispatch(new BeginAccessEvent(address, targetBank)); this.banks.get(targetBank).beginAccess(targetRow, contiguous, () -> { getBlockingEventDispatcher().dispatch(new EndAccessEvent(address, targetBank)); getCycleAccurateEventQueue().schedule(this, onCompletedCallback, getFromDramLatency()); }); previousBank = targetBank; } /** * Get the "to DRAM" latency. * * @return the "to DRAM" latency */ public int getToDramLatency() { return getExperiment().getConfig().getBasicMemoryControllerToDramLatency(); } /** * Get the "from DRAM" latency. * * @return the "from DRAM" latency */ public int getFromDramLatency() { return getExperiment().getConfig().getBasicMemoryControllerFromDramLatency(); } /** * Get the precharge latency. * * @return the precharge latency */ public int getPrechargeLatency() { return getExperiment().getConfig().getBasicMemoryControllerPrechargeLatency(); } /** * Get the closed latency. * * @return the closed latency */ public int getClosedLatency() { return getExperiment().getConfig().getBasicMemoryControllerClosedLatency(); } /** * Get the conflict latency. * * @return the conflict latency */ public int getConflictLatency() { return getExperiment().getConfig().getBasicMemoryControllerConflictLatency(); } /** * Get the bus width. * * @return the bus width */ public int getBusWidth() { return getExperiment().getConfig().getBasicMemoryControllerBusWidth(); } /** * Get the number of banks. * * @return the number of banks */ public int getNumBanks() { return getExperiment().getConfig().getBasicMemoryControllerNumBanks(); } /** * Get the size of a row. * * @return the size of a row */ public int getRowSize() { return getExperiment().getConfig().getBasicMemoryControllerRowSize(); } }