0da1c5dcca
First commit of the 4.5 version (latest version available)
904 lines
41 KiB
Java
904 lines
41 KiB
Java
package mars.tools;
|
|
import javax.swing.*;
|
|
import javax.swing.border.*;
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.util.*;
|
|
import mars.util.*;
|
|
import mars.tools.*;
|
|
import mars.mips.hardware.*;
|
|
|
|
/*
|
|
Copyright (c) 2003-2011, Pete Sanderson and Kenneth Vollmar
|
|
|
|
Developed by Pete Sanderson (psanderson@otterbein.edu)
|
|
and Kenneth Vollmar (kenvollmar@missouristate.edu)
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
a copy of this software and associated documentation files (the
|
|
"Software"), to deal in the Software without restriction, including
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
permit persons to whom the Software is furnished to do so, subject
|
|
to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
|
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
(MIT license, http://www.opensource.org/licenses/mit-license.html)
|
|
*/
|
|
|
|
/**
|
|
* A data cache simulator. It can be run either as a stand-alone Java application having
|
|
* access to the mars package, or through MARS as an item in its Tools menu. It makes
|
|
* maximum use of methods inherited from its abstract superclass AbstractMarsToolOrApp.
|
|
* Pete Sanderson, v 1.0: 16-18 October 2006, v 1.1: 7 November 2006. v 1.2: 23 December 2010.
|
|
* <p>Version 1.2 fixes a bug in the hit/miss animator under full or N-way set associative. It was
|
|
* animating the block of initial access (first block of set). Now it animates the block
|
|
* of final access (where address found or stored). Also added log display to GUI (previously System.out).</p>
|
|
*/
|
|
public class CacheSimulator extends AbstractMarsToolAndApplication {
|
|
private static boolean debug = false; // controls display of debugging info
|
|
private static String version = "Version 1.2";
|
|
private static String heading = "Simulate and illustrate data cache performance";
|
|
// Major GUI components
|
|
private JComboBox cacheBlockSizeSelector, cacheBlockCountSelector,
|
|
cachePlacementSelector, cacheReplacementSelector,
|
|
cacheSetSizeSelector;
|
|
private JTextField memoryAccessCountDisplay, cacheHitCountDisplay, cacheMissCountDisplay,
|
|
replacementPolicyDisplay,cachableAddressesDisplay,
|
|
cacheSizeDisplay;
|
|
private JProgressBar cacheHitRateDisplay;
|
|
private Animation animations;
|
|
|
|
private JPanel logPanel;
|
|
private JScrollPane logScroll;
|
|
private JTextArea logText;
|
|
private JCheckBox logShow;
|
|
|
|
// Some GUI settings
|
|
private EmptyBorder emptyBorder = new EmptyBorder(4,4,4,4);
|
|
private Font countFonts = new Font("Times", Font.BOLD,12);
|
|
private Color backgroundColor = Color.WHITE;
|
|
|
|
// Values for Combo Boxes
|
|
private int[] cacheBlockSizeChoicesInt, cacheBlockCountChoicesInt;
|
|
private String[] cacheBlockSizeChoices = {"1","2","4","8","16","32","64","128","256","512","1024","2048"};
|
|
private String[] cacheBlockCountChoices = {"1","2","4","8","16","32","64","128","256","512","1024","2048"};
|
|
private String[] placementPolicyChoices = {"Direct Mapping", "Fully Associative", "N-way Set Associative" };
|
|
private final int DIRECT = 0, FULL = 1, SET = 2; // NOTE: these have to match placementPolicyChoices order!
|
|
private String[] replacementPolicyChoices = {"LRU","Random"};
|
|
private final int LRU = 0, RANDOM = 1; // NOTE: these have to match replacementPolicyChoices order!
|
|
private String[] cacheSetSizeChoices; // will change dynamically based on the other selections
|
|
private int defaultCacheBlockSizeIndex = 2;
|
|
private int defaultCacheBlockCountIndex = 3;
|
|
private int defaultPlacementPolicyIndex = DIRECT;
|
|
private int defaultReplacementPolicyIndex = LRU;
|
|
private int defaultCacheSetSizeIndex = 0;
|
|
|
|
// Cache-related data structures
|
|
private AbstractCache theCache;
|
|
private int memoryAccessCount, cacheHitCount, cacheMissCount;
|
|
private double cacheHitRate;
|
|
|
|
// RNG used for random replacement policy. For testing, set seed for reproducible stream
|
|
private Random randu = new Random(0);
|
|
|
|
/**
|
|
* Simple constructor, likely used to run a stand-alone cache simulator.
|
|
* @param title String containing title for title bar
|
|
* @param heading String containing text for heading shown in upper part of window.
|
|
*/
|
|
public CacheSimulator(String title, String heading) {
|
|
super(title,heading);
|
|
}
|
|
|
|
/**
|
|
* Simple constructor, likely used by the MARS Tools menu mechanism
|
|
*/
|
|
public CacheSimulator() {
|
|
super ("Data Cache Simulation Tool, "+version, heading);
|
|
}
|
|
|
|
|
|
/**
|
|
* Main provided for pure stand-alone use. Recommended stand-alone use is to write a
|
|
* driver program that instantiates a CacheSimulator object then invokes its go() method.
|
|
* "stand-alone" means it is not invoked from the MARS Tools menu. "Pure" means there
|
|
* is no driver program to invoke the Cache Simulator.
|
|
*/
|
|
public static void main(String[] args) {
|
|
new CacheSimulator("Data Cache Simulator stand-alone, "+version,heading).go();
|
|
}
|
|
|
|
|
|
/**
|
|
* Required MarsTool method to return Tool name.
|
|
* @return Tool name. MARS will display this in menu item.
|
|
*/
|
|
public String getName() {
|
|
return "Data Cache Simulator";
|
|
}
|
|
|
|
|
|
/**
|
|
* Method that constructs the main cache simulator display area. It is organized vertically
|
|
* into three major components: the cache configuration which an be modified
|
|
* using combo boxes, the cache performance which is updated as the
|
|
* attached MIPS program executes, and the runtime log which is optionally used
|
|
* to display log of each cache access.
|
|
* @return the GUI component containing these three areas
|
|
*/
|
|
protected JComponent buildMainDisplayArea() {
|
|
// OVERALL STRUCTURE OF MAIN UI (CENTER)
|
|
Box results = Box.createVerticalBox();
|
|
results.add(buildOrganizationArea());
|
|
results.add(buildPerformanceArea());
|
|
results.add(buildLogArea());
|
|
return results;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
private JComponent buildLogArea() {
|
|
logPanel = new JPanel();
|
|
TitledBorder ltb =new TitledBorder("Runtime Log");
|
|
ltb.setTitleJustification(TitledBorder.CENTER);
|
|
logPanel.setBorder(ltb);
|
|
logShow = new JCheckBox("Enabled",debug);
|
|
logShow.addItemListener(
|
|
new ItemListener() {
|
|
public void itemStateChanged(ItemEvent e) {
|
|
debug = e.getStateChange() == ItemEvent.SELECTED;
|
|
resetLogDisplay();
|
|
logText.setEnabled(debug);
|
|
logText.setBackground( debug ? Color.WHITE : logPanel.getBackground() );
|
|
}
|
|
});
|
|
logPanel.add(logShow);
|
|
logText = new JTextArea(5,70);
|
|
logText.setEnabled(debug);
|
|
logText.setBackground( debug ? Color.WHITE : logPanel.getBackground() );
|
|
logText.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
|
logText.setToolTipText("Displays cache activity log if enabled");
|
|
logScroll = new JScrollPane(logText,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
|
logPanel.add(logScroll);
|
|
return logPanel;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
private JComponent buildOrganizationArea() {
|
|
JPanel organization = new JPanel(new GridLayout(3,2));
|
|
TitledBorder otb =new TitledBorder("Cache Organization");
|
|
otb.setTitleJustification(TitledBorder.CENTER);
|
|
organization.setBorder(otb);
|
|
cachePlacementSelector = new JComboBox(placementPolicyChoices);
|
|
cachePlacementSelector.setEditable(false);
|
|
cachePlacementSelector.setBackground(backgroundColor);
|
|
cachePlacementSelector.setSelectedIndex(defaultPlacementPolicyIndex);
|
|
cachePlacementSelector.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
updateCacheSetSizeSelector();
|
|
reset();
|
|
}
|
|
});
|
|
|
|
cacheReplacementSelector = new JComboBox(replacementPolicyChoices);
|
|
cacheReplacementSelector.setEditable(false);
|
|
cacheReplacementSelector.setBackground(backgroundColor);
|
|
cacheReplacementSelector.setSelectedIndex(defaultReplacementPolicyIndex);
|
|
|
|
cacheBlockSizeSelector = new JComboBox(cacheBlockSizeChoices);
|
|
cacheBlockSizeSelector.setEditable(false);
|
|
cacheBlockSizeSelector.setBackground(backgroundColor);
|
|
cacheBlockSizeSelector.setSelectedIndex(defaultCacheBlockSizeIndex);
|
|
cacheBlockSizeSelector.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
updateCacheSizeDisplay();
|
|
reset();
|
|
}
|
|
});
|
|
cacheBlockCountSelector = new JComboBox(cacheBlockCountChoices);
|
|
cacheBlockCountSelector.setEditable(false);
|
|
cacheBlockCountSelector.setBackground(backgroundColor);
|
|
cacheBlockCountSelector.setSelectedIndex(defaultCacheBlockCountIndex);
|
|
cacheBlockCountSelector.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
updateCacheSetSizeSelector();
|
|
theCache = createNewCache();
|
|
resetCounts();
|
|
updateDisplay();
|
|
updateCacheSizeDisplay();
|
|
animations.fillAnimationBoxWithCacheBlocks();
|
|
}
|
|
});
|
|
|
|
cacheSetSizeSelector = new JComboBox(cacheSetSizeChoices);
|
|
cacheSetSizeSelector.setEditable(false);
|
|
cacheSetSizeSelector.setBackground(backgroundColor);
|
|
cacheSetSizeSelector.setSelectedIndex(defaultCacheSetSizeIndex);
|
|
cacheSetSizeSelector.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
reset();
|
|
}
|
|
});
|
|
|
|
// ALL COMPONENTS FOR "CACHE ORGANIZATION" SECTION
|
|
JPanel placementPolicyRow = getPanelWithBorderLayout();
|
|
placementPolicyRow.setBorder(emptyBorder);
|
|
placementPolicyRow.add(new JLabel("Placement Policy "),BorderLayout.WEST);
|
|
placementPolicyRow.add(cachePlacementSelector,BorderLayout.EAST);
|
|
|
|
JPanel replacementPolicyRow = getPanelWithBorderLayout();
|
|
replacementPolicyRow.setBorder(emptyBorder);
|
|
replacementPolicyRow.add(new JLabel("Block Replacement Policy "),BorderLayout.WEST);
|
|
/* replacementPolicyDisplay = new JTextField("N/A",6);
|
|
replacementPolicyDisplay.setEditable(false);
|
|
replacementPolicyDisplay.setBackground(backgroundColor);
|
|
replacementPolicyDisplay.setHorizontalAlignment(JTextField.RIGHT); */
|
|
replacementPolicyRow.add(cacheReplacementSelector, BorderLayout.EAST);
|
|
|
|
JPanel cacheSetSizeRow = getPanelWithBorderLayout();
|
|
cacheSetSizeRow.setBorder(emptyBorder);
|
|
cacheSetSizeRow.add(new JLabel("Set size (blocks) "),BorderLayout.WEST);
|
|
cacheSetSizeRow.add(cacheSetSizeSelector,BorderLayout.EAST);
|
|
|
|
// Cachable address range "selection" removed for now...
|
|
/*
|
|
JPanel cachableAddressesRow = getPanelWithBorderLayout();
|
|
cachableAddressesRow.setBorder(emptyBorder);
|
|
cachableAddressesRow.add(new JLabel("Cachable Addresses "),BorderLayout.WEST);
|
|
cachableAddressesDisplay = new JTextField("all data segment");
|
|
cachableAddressesDisplay.setEditable(false);
|
|
cachableAddressesDisplay.setBackground(backgroundColor);
|
|
cachableAddressesDisplay.setHorizontalAlignment(JTextField.RIGHT);
|
|
cachableAddressesRow.add(cachableAddressesDisplay, BorderLayout.EAST);
|
|
*/
|
|
JPanel cacheNumberBlocksRow = getPanelWithBorderLayout();
|
|
cacheNumberBlocksRow.setBorder(emptyBorder);
|
|
cacheNumberBlocksRow.add(new JLabel("Number of blocks "), BorderLayout.WEST);
|
|
cacheNumberBlocksRow.add(cacheBlockCountSelector, BorderLayout.EAST);
|
|
|
|
JPanel cacheBlockSizeRow = getPanelWithBorderLayout();
|
|
cacheBlockSizeRow.setBorder(emptyBorder);
|
|
cacheBlockSizeRow.add(new JLabel("Cache block size (words) "), BorderLayout.WEST);
|
|
cacheBlockSizeRow.add(cacheBlockSizeSelector, BorderLayout.EAST);
|
|
|
|
JPanel cacheTotalSizeRow = getPanelWithBorderLayout();
|
|
cacheTotalSizeRow.setBorder(emptyBorder);
|
|
cacheTotalSizeRow.add(new JLabel("Cache size (bytes) "), BorderLayout.WEST);
|
|
cacheSizeDisplay = new JTextField(8);
|
|
cacheSizeDisplay.setHorizontalAlignment(JTextField.RIGHT);
|
|
cacheSizeDisplay.setEditable(false);
|
|
cacheSizeDisplay.setBackground(backgroundColor);
|
|
cacheSizeDisplay.setFont(countFonts);
|
|
cacheTotalSizeRow.add(cacheSizeDisplay, BorderLayout.EAST);
|
|
updateCacheSizeDisplay();
|
|
|
|
// Lay 'em out in the grid...
|
|
organization.add(placementPolicyRow);
|
|
organization.add(cacheNumberBlocksRow);
|
|
organization.add(replacementPolicyRow);
|
|
organization.add(cacheBlockSizeRow);
|
|
//organization.add(cachableAddressesRow);
|
|
organization.add(cacheSetSizeRow);
|
|
organization.add(cacheTotalSizeRow);
|
|
return organization;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
private JComponent buildPerformanceArea() {
|
|
JPanel performance = new JPanel(new GridLayout(1,2));
|
|
TitledBorder ptb =new TitledBorder("Cache Performance");
|
|
ptb.setTitleJustification(TitledBorder.CENTER);
|
|
performance.setBorder(ptb);
|
|
JPanel memoryAccessCountRow = getPanelWithBorderLayout();
|
|
memoryAccessCountRow.setBorder(emptyBorder);
|
|
memoryAccessCountRow.add(new JLabel("Memory Access Count "), BorderLayout.WEST);
|
|
memoryAccessCountDisplay = new JTextField(10);
|
|
memoryAccessCountDisplay.setHorizontalAlignment(JTextField.RIGHT);
|
|
memoryAccessCountDisplay.setEditable(false);
|
|
memoryAccessCountDisplay.setBackground(backgroundColor);
|
|
memoryAccessCountDisplay.setFont(countFonts);
|
|
memoryAccessCountRow.add(memoryAccessCountDisplay, BorderLayout.EAST);
|
|
|
|
JPanel cacheHitCountRow = getPanelWithBorderLayout();
|
|
cacheHitCountRow.setBorder(emptyBorder);
|
|
cacheHitCountRow.add(new JLabel("Cache Hit Count "), BorderLayout.WEST);
|
|
cacheHitCountDisplay = new JTextField(10);
|
|
cacheHitCountDisplay.setHorizontalAlignment(JTextField.RIGHT);
|
|
cacheHitCountDisplay.setEditable(false);
|
|
cacheHitCountDisplay.setBackground(backgroundColor);
|
|
cacheHitCountDisplay.setFont(countFonts);
|
|
cacheHitCountRow.add(cacheHitCountDisplay, BorderLayout.EAST);
|
|
|
|
JPanel cacheMissCountRow = getPanelWithBorderLayout();
|
|
cacheMissCountRow.setBorder(emptyBorder);
|
|
cacheMissCountRow.add(new JLabel("Cache Miss Count "), BorderLayout.WEST);
|
|
cacheMissCountDisplay = new JTextField(10);
|
|
cacheMissCountDisplay.setHorizontalAlignment(JTextField.RIGHT);
|
|
cacheMissCountDisplay.setEditable(false);
|
|
cacheMissCountDisplay.setBackground(backgroundColor);
|
|
cacheMissCountDisplay.setFont(countFonts);
|
|
cacheMissCountRow.add(cacheMissCountDisplay, BorderLayout.EAST);
|
|
|
|
JPanel cacheHitRateRow = getPanelWithBorderLayout();
|
|
cacheHitRateRow.setBorder(emptyBorder);
|
|
cacheHitRateRow.add(new JLabel("Cache Hit Rate "), BorderLayout.WEST);
|
|
cacheHitRateDisplay = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100);
|
|
cacheHitRateDisplay.setStringPainted(true);
|
|
cacheHitRateDisplay.setForeground(Color.BLUE);
|
|
cacheHitRateDisplay.setBackground(backgroundColor);
|
|
cacheHitRateDisplay.setFont(countFonts);
|
|
cacheHitRateRow.add(cacheHitRateDisplay, BorderLayout.EAST);
|
|
|
|
resetCounts();
|
|
updateDisplay();
|
|
|
|
// Vertically align these 4 measures in a grid, then add to left column of main grid.
|
|
JPanel performanceMeasures = new JPanel(new GridLayout(4,1));
|
|
performanceMeasures.add(memoryAccessCountRow);
|
|
performanceMeasures.add(cacheHitCountRow);
|
|
performanceMeasures.add(cacheMissCountRow);
|
|
performanceMeasures.add(cacheHitRateRow);
|
|
performance.add(performanceMeasures);
|
|
|
|
// LET'S TRY SOME ANIMATION ON THE RIGHT SIDE...
|
|
animations = new Animation();
|
|
animations.fillAnimationBoxWithCacheBlocks();
|
|
JPanel animationsPanel = new JPanel(new GridLayout(1,2));
|
|
Box animationsLabel = Box.createVerticalBox();
|
|
JPanel tableTitle1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
JPanel tableTitle2 = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
tableTitle1.add(new JLabel("Cache Block Table"));
|
|
tableTitle2.add(new JLabel("(block 0 at top)"));
|
|
animationsLabel.add(tableTitle1);
|
|
animationsLabel.add(tableTitle2);
|
|
Dimension colorKeyBoxSize = new Dimension(8,8);
|
|
|
|
JPanel emptyKey = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
JPanel emptyBox = new JPanel();
|
|
emptyBox.setSize(colorKeyBoxSize);
|
|
emptyBox.setBackground(animations.defaultColor);
|
|
emptyBox.setBorder(BorderFactory.createLineBorder(Color.BLACK));
|
|
emptyKey.add(emptyBox);
|
|
emptyKey.add(new JLabel(" = empty"));
|
|
|
|
JPanel missBox = new JPanel();
|
|
JPanel missKey = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
missBox.setSize(colorKeyBoxSize);
|
|
missBox.setBackground(animations.missColor);
|
|
missBox.setBorder(BorderFactory.createLineBorder(Color.BLACK));
|
|
missKey.add(missBox);
|
|
missKey.add(new JLabel(" = miss"));
|
|
|
|
JPanel hitKey = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
JPanel hitBox = new JPanel();
|
|
hitBox.setSize(colorKeyBoxSize);
|
|
hitBox.setBackground(animations.hitColor);
|
|
hitBox.setBorder(BorderFactory.createLineBorder(Color.BLACK));
|
|
hitKey.add(hitBox);
|
|
hitKey.add(new JLabel(" = hit"));
|
|
|
|
animationsLabel.add(emptyKey);
|
|
animationsLabel.add(hitKey);
|
|
animationsLabel.add(missKey);
|
|
animationsLabel.add(Box.createVerticalGlue());
|
|
animationsPanel.add(animationsLabel);
|
|
animationsPanel.add(animations.getAnimationBox());
|
|
|
|
performance.add(animationsPanel);
|
|
return performance;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// Rest of the protected methods. These override do-nothing methods inherited from
|
|
// the abstract superclass.
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Apply caching policies and update display when connected MIPS program accesses (data) memory.
|
|
* @param memory the attached memory
|
|
* @param accessNotice information provided by memory in MemoryAccessNotice object
|
|
*/
|
|
protected void processMIPSUpdate(Observable memory, AccessNotice accessNotice) {
|
|
MemoryAccessNotice notice = (MemoryAccessNotice) accessNotice;
|
|
memoryAccessCount++;
|
|
CacheAccessResult cacheAccessResult = theCache.isItAHitThenReadOnMiss(notice.getAddress());
|
|
if (cacheAccessResult.isHit()) {
|
|
cacheHitCount++;
|
|
animations.showHit(cacheAccessResult.getBlock());
|
|
}
|
|
else {
|
|
cacheMissCount++;
|
|
animations.showMiss(cacheAccessResult.getBlock());
|
|
}
|
|
cacheHitRate = cacheHitCount / (double)memoryAccessCount;
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize all JComboBox choice structures not already initialized at declaration.
|
|
* Also creates initial default cache object. Overrides inherited method that does nothing.
|
|
*/
|
|
protected void initializePreGUI() {
|
|
cacheBlockSizeChoicesInt = new int[cacheBlockSizeChoices.length];
|
|
for (int i=0; i<cacheBlockSizeChoices.length; i++) {
|
|
try {
|
|
cacheBlockSizeChoicesInt[i] = Integer.parseInt(cacheBlockSizeChoices[i]);
|
|
}
|
|
catch (NumberFormatException nfe) {
|
|
cacheBlockSizeChoicesInt[i] = 1;
|
|
}
|
|
}
|
|
cacheBlockCountChoicesInt = new int[cacheBlockCountChoices.length];
|
|
for (int i=0; i<cacheBlockCountChoices.length; i++) {
|
|
try {
|
|
cacheBlockCountChoicesInt[i] = Integer.parseInt(cacheBlockCountChoices[i]);
|
|
}
|
|
catch (NumberFormatException nfe) {
|
|
cacheBlockCountChoicesInt[i] = 1;
|
|
}
|
|
}
|
|
cacheSetSizeChoices = determineSetSizeChoices(defaultCacheBlockCountIndex,defaultPlacementPolicyIndex);
|
|
}
|
|
|
|
|
|
/**
|
|
* The only post-GUI initialization is to create the initial cache object based on the default settings
|
|
* of the various combo boxes. Overrides inherited method that does nothing.
|
|
*/
|
|
|
|
protected void initializePostGUI() {
|
|
theCache = createNewCache();
|
|
}
|
|
|
|
|
|
/**
|
|
* Method to reset cache, counters and display when the Reset button selected.
|
|
* Overrides inherited method that does nothing.
|
|
*/
|
|
protected void reset() {
|
|
theCache = createNewCache();
|
|
resetCounts();
|
|
updateDisplay();
|
|
animations.reset();
|
|
resetLogDisplay();
|
|
}
|
|
|
|
/**
|
|
* Updates display immediately after each update (AccessNotice) is processed, after
|
|
* cache configuration changes as needed, and after each execution step when Mars
|
|
* is running in timed mode. Overrides inherited method that does nothing.
|
|
*/
|
|
protected void updateDisplay() {
|
|
updateMemoryAccessCountDisplay();
|
|
updateCacheHitCountDisplay();
|
|
updateCacheMissCountDisplay();
|
|
updateCacheHitRateDisplay();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// Private methods defined to support the above.
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Will determine range of choices for "set size in blocks", which is determined both by
|
|
// the number of blocks in the cache and by placement policy.
|
|
private String[] determineSetSizeChoices(int cacheBlockCountIndex, int placementPolicyIndex) {
|
|
String[] choices;
|
|
int firstBlockCountIndex = 0;
|
|
int lastBlockCountIndex = cacheBlockCountIndex;
|
|
switch (placementPolicyIndex) {
|
|
case DIRECT : choices = new String[1];
|
|
choices[0] = cacheBlockCountChoices[firstBlockCountIndex]; // set size fixed at 1
|
|
break;
|
|
case SET : choices = new String[lastBlockCountIndex-firstBlockCountIndex+1];
|
|
for (int i=0; i<choices.length; i++) {
|
|
choices[i] = cacheBlockCountChoices[firstBlockCountIndex+i];
|
|
}
|
|
break;
|
|
case FULL : // 1 set total, so set size fixed at current number of blocks
|
|
default : choices = new String[1];
|
|
choices[0] = cacheBlockCountChoices[lastBlockCountIndex];
|
|
}
|
|
return choices;
|
|
}
|
|
|
|
// Update the Set Size combo box selection in response to other selections..
|
|
private void updateCacheSetSizeSelector() {
|
|
cacheSetSizeSelector.setModel(
|
|
new DefaultComboBoxModel(determineSetSizeChoices(
|
|
cacheBlockCountSelector.getSelectedIndex(),
|
|
cachePlacementSelector.getSelectedIndex()
|
|
)));
|
|
}
|
|
|
|
// create and return a new cache object based on current specs
|
|
private AbstractCache createNewCache() {
|
|
AbstractCache theNewCache = null;
|
|
int setSize = 1;
|
|
try {
|
|
setSize = Integer.parseInt((String)cacheSetSizeSelector.getSelectedItem());
|
|
}
|
|
catch (NumberFormatException nfe) { // if this happens its my fault!
|
|
}
|
|
theNewCache = new AnyCache(
|
|
cacheBlockCountChoicesInt[cacheBlockCountSelector.getSelectedIndex()],
|
|
cacheBlockSizeChoicesInt[cacheBlockSizeSelector.getSelectedIndex()],
|
|
setSize);
|
|
return theNewCache;
|
|
}
|
|
|
|
private void resetCounts() {
|
|
memoryAccessCount = 0;
|
|
cacheHitCount = 0;
|
|
cacheMissCount = 0;
|
|
cacheHitRate = 0.0;
|
|
}
|
|
|
|
|
|
private void updateMemoryAccessCountDisplay() {
|
|
memoryAccessCountDisplay.setText(new Integer(memoryAccessCount).toString());
|
|
}
|
|
|
|
private void updateCacheHitCountDisplay() {
|
|
cacheHitCountDisplay.setText(new Integer(cacheHitCount).toString());
|
|
}
|
|
|
|
private void updateCacheMissCountDisplay() {
|
|
cacheMissCountDisplay.setText(new Integer(cacheMissCount).toString());
|
|
}
|
|
|
|
private void updateCacheHitRateDisplay() {
|
|
cacheHitRateDisplay.setValue((int) Math.round(cacheHitRate*100));
|
|
}
|
|
|
|
private void updateCacheSizeDisplay() {
|
|
int cacheSize = cacheBlockSizeChoicesInt[cacheBlockSizeSelector.getSelectedIndex()] *
|
|
cacheBlockCountChoicesInt[cacheBlockCountSelector.getSelectedIndex()] *
|
|
Memory.WORD_LENGTH_BYTES;
|
|
cacheSizeDisplay.setText(Integer.toString(cacheSize));
|
|
}
|
|
|
|
private JPanel getPanelWithBorderLayout() {
|
|
return new JPanel(new BorderLayout(2,2));
|
|
}
|
|
|
|
private void resetLogDisplay() {
|
|
logText.setText("");
|
|
}
|
|
|
|
private void writeLog(String text) {
|
|
logText.append(text);
|
|
logText.setCaretPosition(logText.getDocument().getLength());
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// Specialized inner classes for cache modeling and animation.
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Represents a block in the cache. Since we are only simulating
|
|
// cache performance, there's no need to actually store memory contents.
|
|
private class CacheBlock {
|
|
private boolean valid;
|
|
private int tag;
|
|
private int sizeInWords;
|
|
private int mostRecentAccessTime;
|
|
public CacheBlock(int sizeInWords) {
|
|
this.valid = false;
|
|
this.tag = 0;
|
|
this.sizeInWords = sizeInWords;
|
|
this.mostRecentAccessTime = -1;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Represents the outcome of a cache access. There are two parts:
|
|
// whether it was a hit or not, and in which block is the value stored.
|
|
// In the case of a hit, the block associated with address. In the case of
|
|
// a miss, the block where new association is made. DPS 23-Dec-2010
|
|
private class CacheAccessResult {
|
|
private boolean hitOrMiss;
|
|
private int blockNumber;
|
|
public CacheAccessResult(boolean hitOrMiss, int blockNumber) {
|
|
this.hitOrMiss = hitOrMiss;
|
|
this.blockNumber = blockNumber;
|
|
}
|
|
public boolean isHit() {
|
|
return hitOrMiss;
|
|
}
|
|
public int getBlock() {
|
|
return blockNumber;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Abstract Cache class. Subclasses will implement specific policies.
|
|
private abstract class AbstractCache {
|
|
private int numberOfBlocks, blockSizeInWords, setSizeInBlocks, numberOfSets;
|
|
protected CacheBlock[] blocks;
|
|
|
|
protected AbstractCache(int numberOfBlocks, int blockSizeInWords, int setSizeInBlocks) {
|
|
this.numberOfBlocks = numberOfBlocks;
|
|
this.blockSizeInWords = blockSizeInWords;
|
|
this.setSizeInBlocks = setSizeInBlocks;
|
|
this.numberOfSets = numberOfBlocks/setSizeInBlocks;
|
|
this.blocks = new CacheBlock[numberOfBlocks];
|
|
this.reset();
|
|
}
|
|
|
|
public int getNumberOfBlocks() {
|
|
return this.numberOfBlocks;
|
|
}
|
|
|
|
public int getNumberOfSets() {
|
|
return this.numberOfSets;
|
|
}
|
|
|
|
public int getSetSizeInBlocks() {
|
|
return this.setSizeInBlocks;
|
|
}
|
|
|
|
public int getBlockSizeInWords() {
|
|
return this.blockSizeInWords;
|
|
}
|
|
|
|
public int getCacheSizeInWords() {
|
|
return this.numberOfBlocks * this.blockSizeInWords;
|
|
}
|
|
|
|
public int getCacheSizeInBytes() {
|
|
return this.numberOfBlocks * this.blockSizeInWords * Memory.WORD_LENGTH_BYTES;
|
|
}
|
|
|
|
|
|
// This will work regardless of placement.
|
|
// For direct map, #sets==#blocks
|
|
// For full assoc, #sets==1 so anything % #sets == 0
|
|
// For n-way assoc, it extracts the set bits in address.
|
|
public int getSetNumber(int address) {
|
|
return address / Memory.WORD_LENGTH_BYTES / this.blockSizeInWords % this.numberOfSets;
|
|
}
|
|
|
|
// This will work regardless of placement policy (direct map, n-way or full assoc)
|
|
public int getTag(int address) {
|
|
return address / Memory.WORD_LENGTH_BYTES / this.blockSizeInWords / this.numberOfSets;
|
|
}
|
|
|
|
// This will work regardless of placement policy (direct map, n-way or full assoc)
|
|
// Returns absolute block offset into the cache.
|
|
public int getFirstBlockToSearch(int address) {
|
|
return this.getSetNumber(address) * this.setSizeInBlocks;
|
|
}
|
|
|
|
// This will work regardless of placement policy (direct map, n-way or full assoc)
|
|
// Returns absolute block offset into the cache.
|
|
public int getLastBlockToSearch(int address) {
|
|
return this.getFirstBlockToSearch(address) + this.setSizeInBlocks - 1;
|
|
}
|
|
|
|
/* Reset the cache contents. */
|
|
public void reset() {
|
|
for (int i=0; i<numberOfBlocks; i++) {
|
|
this.blocks[i] = new CacheBlock(blockSizeInWords);
|
|
}
|
|
System.gc(); // scoop 'em up now
|
|
}
|
|
|
|
// Subclass must implement this according to its policies
|
|
public abstract CacheAccessResult isItAHitThenReadOnMiss(int address);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Implements any of the well-known cache organizations. Physical memory
|
|
// address is partitioned depending on organization:
|
|
// Direct Mapping: [ tag | block | word | byte ]
|
|
// Fully Associative: [ tag | word | byte ]
|
|
// Set Associative: [ tag | set | word | byte ]
|
|
//
|
|
// Bit lengths of each part are determined as follows:
|
|
// Direct Mapping:
|
|
// byte = log2 of #bytes in a word (typically 4)
|
|
// word = log2 of #words in a block
|
|
// block = log2 of #blocks in the cache
|
|
// tag = #bytes in address - (byte+word+block)
|
|
// Fully Associative:
|
|
// byte = log2 of #bytes in a word (typically 4)
|
|
// word = log2 of #words in a block
|
|
// tag = #bytes in address - (byte+word)
|
|
// Set Associative:
|
|
// byte = log2 of #bytes in a word (typically 4)
|
|
// word = log2 of #words in a block
|
|
// set = log2 of #sets in the cache
|
|
// tag = #bytes in address - (byte+word+set)
|
|
//
|
|
// Direct Mapping (1 way set associative):
|
|
// The block value for a given address identifies its block index into the cache.
|
|
// That's why its called "direct mapped." This is the only cache block it can
|
|
// occupy. If that cache block is empty or if it is occupied by a different tag,
|
|
// this is a MISS. If that cache block is occupied by the same tag, this is a HIT.
|
|
// There is no replacement policy: upon a cache miss of an occupied block, the old
|
|
// block is written out (unless write-through) and the new one read in.
|
|
// Those actions are not simulated here.
|
|
//
|
|
// Fully Associative:
|
|
// There is one set, and very tag has to be searched before determining hit or miss.
|
|
// If tag is matched, it is a hit. If tag is not matched and there is at least one
|
|
// empty block, it is a miss and the new tag will occupy it. If tag is not matched
|
|
// and every block is occupied, it is a miss and one of the occupied blocks will be
|
|
// selected for removal and the new tag will replace it.
|
|
//
|
|
// n-way Set Associative:
|
|
// Each set consists of n blocks, and the number of sets in the cache is total number
|
|
// of blocks divided by n. The set bits in the address will identify which set to
|
|
// search, and every tag in that set has to be searched before determining hit or miss.
|
|
// If tag is matched, it is a hit. If tag is not matched and there is at least one
|
|
// empty block, it is a miss and the new tag will occupy it. If tag is not matched
|
|
// and every block is occupied, it is a miss and one of the occupied blocks will be
|
|
// selected for removal and the new tag will replace it.
|
|
//
|
|
private class AnyCache extends AbstractCache {
|
|
public AnyCache(int numberOfBlocks, int blockSizeInWords, int setSizeInBlocks) {
|
|
super(numberOfBlocks, blockSizeInWords, setSizeInBlocks);
|
|
}
|
|
|
|
private final int SET_FULL=0, HIT=1, MISS=2;
|
|
|
|
// This method works for any of the placement policies:
|
|
// direct mapped, full associative or n-way set associative.
|
|
public CacheAccessResult isItAHitThenReadOnMiss(int address) {
|
|
int result = SET_FULL;
|
|
int firstBlock = getFirstBlockToSearch(address);
|
|
int lastBlock = getLastBlockToSearch(address);
|
|
if (debug) //System.out.print
|
|
writeLog("("+memoryAccessCount+") address: "+Binary.intToHexString(address)+" (tag "+Binary.intToHexString(getTag(address))+") "+" block range: "+firstBlock+"-"+lastBlock+"\n");
|
|
CacheBlock block;
|
|
int blockNumber = 0;
|
|
// Will do a sequential instead of associative search!
|
|
for (blockNumber = firstBlock; blockNumber <= lastBlock; blockNumber++) {
|
|
block = blocks[blockNumber];
|
|
if (debug) //System.out.print
|
|
writeLog(" trying block "+blockNumber+ ((block.valid)? " tag "+Binary.intToHexString(block.tag) : " empty"));
|
|
if (block.valid && block.tag==getTag(address)) {// it's a hit!
|
|
if (debug) //System.out.print
|
|
writeLog(" -- HIT\n");
|
|
result = HIT;
|
|
block.mostRecentAccessTime = memoryAccessCount;
|
|
break;
|
|
}
|
|
if (!block.valid) {// it's a miss but I got it now because it is empty!
|
|
if (debug) //System.out.print
|
|
writeLog(" -- MISS\n");
|
|
result = MISS;
|
|
block.valid = true;
|
|
block.tag = getTag(address);
|
|
block.mostRecentAccessTime = memoryAccessCount;
|
|
break;
|
|
}
|
|
if (debug) //System.out.print
|
|
writeLog(" -- OCCUPIED\n");
|
|
}
|
|
if (result == SET_FULL) {
|
|
//select one to replace and replace it...
|
|
if (debug) //System.out.print
|
|
writeLog(" MISS due to FULL SET");
|
|
int blockToReplace = selectBlockToReplace(firstBlock,lastBlock);
|
|
block = blocks[blockToReplace];
|
|
block.tag = getTag(address);
|
|
block.mostRecentAccessTime = memoryAccessCount;
|
|
blockNumber = blockToReplace;
|
|
}
|
|
return new CacheAccessResult(result==HIT, blockNumber);
|
|
}
|
|
|
|
// call this if all blocks in the set are full. If the set contains more than one block,
|
|
// It will pick on to replace based on selected replacement policy.
|
|
private int selectBlockToReplace(int first, int last) {
|
|
int replaceBlock = first;
|
|
if (first!=last) {
|
|
switch (cacheReplacementSelector.getSelectedIndex()) {
|
|
case RANDOM :
|
|
replaceBlock = first + randu.nextInt(last-first+1);
|
|
if (debug) //System.out.print
|
|
writeLog(" -- Random replace block "+replaceBlock+"\n");
|
|
break;
|
|
case LRU :
|
|
default :
|
|
int leastRecentAccessTime = memoryAccessCount; // all of them have to be less than this
|
|
for (int block = first; block <= last; block++) {
|
|
if (blocks[block].mostRecentAccessTime < leastRecentAccessTime) {
|
|
leastRecentAccessTime = blocks[block].mostRecentAccessTime;
|
|
replaceBlock = block;
|
|
}
|
|
}
|
|
if (debug) //System.out.print
|
|
writeLog(" -- LRU replace block "+replaceBlock+"; unused since ("+leastRecentAccessTime+")\n");
|
|
break;
|
|
}
|
|
}
|
|
return replaceBlock;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// Class to display animated cache
|
|
//
|
|
private class Animation {
|
|
|
|
private Box animation;
|
|
private JTextField[] blocks;
|
|
public final Color hitColor = Color.GREEN;
|
|
public final Color missColor = Color.RED;
|
|
public final Color defaultColor = Color.WHITE;
|
|
|
|
public Animation() {
|
|
animation = Box.createVerticalBox();
|
|
}
|
|
|
|
private Box getAnimationBox() {
|
|
return animation;
|
|
}
|
|
|
|
public int getNumberOfBlocks() {
|
|
return (blocks==null)? 0 : blocks.length;
|
|
}
|
|
|
|
public void showHit(int blockNum) {
|
|
blocks[blockNum].setBackground(hitColor);
|
|
}
|
|
|
|
public void showMiss(int blockNum) {
|
|
blocks[blockNum].setBackground(missColor);
|
|
}
|
|
|
|
public void reset() {
|
|
for (int i=0; i<blocks.length; i++){
|
|
blocks[i].setBackground(defaultColor);
|
|
}
|
|
}
|
|
|
|
// initialize animation of cache blocks
|
|
private void fillAnimationBoxWithCacheBlocks() {
|
|
animation.setVisible(false);
|
|
animation.removeAll();
|
|
int numberOfBlocks = cacheBlockCountChoicesInt[cacheBlockCountSelector.getSelectedIndex()];
|
|
int totalVerticalPixels = 128;
|
|
int blockPixelHeight = (numberOfBlocks>totalVerticalPixels)? 1 : totalVerticalPixels/numberOfBlocks;
|
|
int blockPixelWidth = 40;
|
|
Dimension blockDimension = new Dimension(blockPixelWidth, blockPixelHeight);
|
|
blocks = new JTextField[numberOfBlocks];
|
|
for (int i=0; i<numberOfBlocks; i++){
|
|
blocks[i] = new JTextField();
|
|
blocks[i].setEditable(false);
|
|
blocks[i].setBackground(defaultColor);
|
|
blocks[i].setSize(blockDimension);
|
|
blocks[i].setPreferredSize(blockDimension);
|
|
animation.add(blocks[i]);
|
|
}
|
|
animation.repaint();
|
|
animation.setVisible(true);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
} |