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. *

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).

*/ 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; itotalVerticalPixels)? 1 : totalVerticalPixels/numberOfBlocks; int blockPixelWidth = 40; Dimension blockDimension = new Dimension(blockPixelWidth, blockPixelHeight); blocks = new JTextField[numberOfBlocks]; for (int i=0; i