ARC_Simulator.java
/* $Id: ARC_Simulator.java,v 1.3 2003/11/24 19:46:24 vickery Exp $
*
* Created on: Nov 19, 2003
* Author: C. Vickery
*
* Copyright (c) 2003, Queens College of the City University
* of New York. All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the
* following conditions are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* * Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* * Neither the name of Queens College of CUNY
* nor the names of its contributors may be used to
* endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $Log: ARC_Simulator.java,v $
* Revision 1.3 2003/11/24 19:46:24 vickery
* Added "oxydize" function (bad pun) so unimplemented op3 messages
* can show missing code value in octal.
*
* Revision 1.2 2003/11/24 18:52:35 vickery
* Updated javadocs for ALU_Operations.
* Corrected documentation error in javadocs for ARC_Simulator.
*
* Revision 1.1 2003/11/21 19:05:14 vickery
* Changed name of main class to ARC_Simulator.
* Added class UnimplementedOpCodeException.
*
* Revision 1.2 2003/11/21 06:11:29 vickery
* Added Javadoc version tags.
*
* Revision 1.1 2003/11/21 06:05:39 vickery
* Initial version of a simulator for the ARC CPU and memory.
*
*/
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.StringTokenizer;
import javax.swing.JFrame;
// Class Simulator
// ------------------------------------------------------------------
/**
* <p>A simulator for the ARC Processor in the Murdocca and Heuring
* text, <i>Principles of Computer Architecture</i>. This version
* of the simulator operates from the command line, and has no
* graphical user interface.</p>
*
* <p>Use the assembler available with the Murdocca and Heuring
* program to assemble programs to be run. In the editor for that
* assembler, click on File->Save (Ctrl-S), which will save a
* ".bin" file, which can be loaded into this simulator for
* execution.</p>
*
* <p>The .bin files produced by the text's assembler and
* recognized by this assembler are simple text files with each
* line giving the contents of one 4 byte word of simulated memory,
* specified as two hexadecimal strings, representing an address
* and a data value. This program assumes execution starts at the
* first word of memory that does not contain 0x00000000.</p>
*
* <p>This simulator is invoked by the command line using the
* following form:</p>
* <pre>
* java ARC_Simulator [options] file ...
* </pre>
*
* <p><a name="params">The program recognizes the following command
* line options:</a></p>
* <pre>
* -m size Size is the size of the simulated memory
* in bytes. The default value is 1 MB. This
* value should be made smaller if Java gives an
* out of memory error.
*
* -n num Num is the maximum number of nop instructions
* the simulator should execute before stopping.
* The default number is ten.
* </pre>
*
* <p>Any number of file names may be listed after the options;
* each will be loaded into a fresh copy of simulated memory and
* executed.</p>
*
* <p>The program displays a cycle-by-cycle description of what it
* is doing as it runs. It will stop either when the nop limit has
* been reached or a halt instruction is encountered. Nop
* instructions are all zeros, and will be found in all
* uninitialized words of memory. Halt is a special instruction
* that the textbook's assembler recognizes in addition to the ones
* listed in the book. That assembler also recognizes bne (branch
* not equal) and add (add but don't change condition code bits),
* which this simulator processes also.</p>
*
* @author C. Vickery
* @version $Revision: 1.3 $
*/
public class ARC_Simulator extends JFrame
implements Register_Names, ALU_Operations
{
/** Memory size, in bytes. */
static int memSize = 1 << 20;
/** Maximum number of nops before simulation stops. */
static int maxNops = 10;
/** Number of nops encountered so far. */
static int numNops = 0;
/** Number of instructions executed so far. */
static int numInstructions = 0;
/** Number of unimplemented opcodes */
static int numUnimplemented = 0;
/** Number of clock cycles so far. */
static int numClocks = 0;
/** Should memory write operations be logged? */
static boolean logWrites = false;
/** Should memory read operations be logged? */
static boolean logReads = true;
/** Should register updates be logged? */
static boolean logRegs = true;
// Constructor
// ----------------------------------------------------------------
/**
* Creates a Graphical User Interface for interacting with the
* simulator. Or will when the code for it has been written!
*
* @param title String to display in the title bar, which
* should be the name of the file being executed.
*
*/
ARC_Simulator( String title )
{
super( title );
}
// oxydize()
// ----------------------------------------------------------------
/**
* Generates the n-digit octal representation of an int.
*/
static String oxydize( int val, int numDigits )
{
StringBuffer sb = new StringBuffer( Long.toOctalString( val ) );
while (sb.length() < numDigits )
{
sb.insert(0, '0' );
}
if ( sb.length() > numDigits )
{
sb.delete( 0, (sb.length() - numDigits) );
}
for (int i = 0; i < numDigits; i++ )
{
char c = Character.toUpperCase( sb.charAt( i ) );
sb.setCharAt( i, c );
}
return new String( sb );
}
// hexadize()
// ----------------------------------------------------------------
/**
* Generates the n-digit hexadecimal representation of an int.
*/
static String hexadize( int val, int numDigits )
{
StringBuffer sb = new StringBuffer( Long.toHexString( val ) );
while (sb.length() < numDigits )
{
sb.insert(0, '0' );
}
if ( sb.length() > numDigits )
{
sb.delete( 0, (sb.length() - numDigits) );
}
for (int i = 0; i < numDigits; i++ )
{
char c = Character.toUpperCase( sb.charAt( i ) );
sb.setCharAt( i, c );
}
return new String( sb );
}
// log()
// --------------------------------------------------------------
/**
* <p>Displays messages about the simulation process without
* appending a newline so more information can be appended
* to the same line.</p>
*
* <p>Right now it writes to the console. A future version
* would display these messages in the gui.</p>
*/
static void log( String msg )
{
System.out.print( msg );
}
// lognl()
// --------------------------------------------------------------
/**
* <p>Displays messages about the simulation process, including
* a newline at the end.</p>
*
* <p>Right now it writes to the console. A future version
* would display these messages in the gui.</p>
*/
static void lognl( String msg )
{
System.out.println( msg );
}
// main()
// ----------------------------------------------------------------
/**
* <p>Processes command line arguments, loads files into simulated
* memory, and manages the simulation(s).</p>
*
* @param args The command line arguments, as specified in the
* <a href="#params">description of this class</a>.
*/
public static void main(String[] args)
{
for (int f = 0; f < args.length; f++ )
{
// Read a program into memory
logWrites = false; // Makes program loading silent.
try
{
BufferedReader br = new BufferedReader(
new FileReader( args[ f ]) );
String line = null;
System.out.println( "\nProcessing file: " + args[ f ] );
Memory.clear( memSize );
while ( (line = br.readLine()) != null )
{
StringTokenizer st = new StringTokenizer( line );
String addr = st.nextToken();
String cont = st.nextToken();
long address = Long.parseLong( addr, 16 );
long data = Long.parseLong( cont, 16 );
Memory.writeWord( (int)(address & 0x7FFFFFFF),
(int)(data & 0x0FFFFFFFF) );
}
}
catch (NumberFormatException e)
{
System.err.println( "Content error while processing " +
args[f] + ": " + e);
continue;
}
catch (FileNotFoundException e)
{
System.err.println( "File not found: " + args[f] );
continue;
}
catch (IOException e)
{
System.err.println(e);
continue;
}
// Memory is initialized; assume first non-zero location is
// first instruction.
int initPC = Memory.findFirst();
if ( initPC < 0 )
{
lognl( "Empty file" );
continue;
}
log( "Program loaded." );
lognl( " First non-zero location is " +
hexadize( initPC, 6 ) );
Registers.setValue( pc, initPC );
lognl( "First instruction found at " +
hexadize( Registers.getValue(pc), 6) );
// The fetch-execute Cycle
// ----------------------------------------------------------
/*
* Execute until a halt instruction or the limit on nop
* instructions is reached.
*/
logWrites = true;
numClocks = 0;
numInstructions = 0;
numUnimplemented = 0;
numNops = 0;
while ( numNops < maxNops )
{
/*
* Fetch
*/
Registers.setValue( ir,
Memory.readWord( Registers.getValue(pc) ));
log( " " );
log( " PC: " + hexadize( Registers.getValue(pc), 6 ) +
" IR: " + hexadize( Registers.getValue(ir), 8 ) );
/*
* Decode
*/
IR.decode();
/*
* Execute
*/
if ( Registers.getValue( ir ) == 0x91d02000 )
{
lognl( " halt" );
break;
}
numInstructions++;
try
{
switch ( IR.op )
{
case 0:
doBranch();
break;
case 1:
// Execute call instructions.
Registers.setValue( 15, Registers.getValue( pc ) );
Registers.setValue( pc,
Registers.getValue( pc ) + IR.disp30 << 2 );
break;
case 2:
doArithmetic();
break;
case 3:
doMemory();
break;
default:
throw new RuntimeException( "Invalid op field: " +
IR.op );
}
}
catch ( UnimplementedOpcodeException uoe )
{
lognl( uoe.getMessage() );
numUnimplemented++;
Registers.setValue( pc,
ALU.do_op( Registers.getValue(pc), 0, INCPC ));
}
}
// Execution complete. Log statistics and go on to next
// program.
lognl( "\nExecution summary for file " + args[ f ] + ":" );
lognl( " Instructions executed: " + numInstructions );
lognl( " Invalid op codes: " + numUnimplemented );
lognl( " Nops: " + numNops );
lognl( " Clock cycles: " + numClocks );
}
}
// doBranch()
// ----------------------------------------------------------------
/**
* Process branch, sethi, and nop instructions.
*
* @throws UnimplementedOpcodeException if the op2 field is not
* recognized or if op2 is branch and the cond field is
* not recognized.
*/
static void doBranch() throws UnimplementedOpcodeException
{
switch ( IR.op2 )
{
case 0:
lognl( " nop");
numNops++;
break;
case 2:
boolean doBranch = false;
switch ( IR.cond )
{
case 000:
lognl( " be" );
doBranch = ALU.Z_bit;
break;
case 005:
lognl( " bcs" );
doBranch = ALU.C_bit;
break;
case 006:
lognl( " bneg" );
doBranch = ALU.N_bit;
break;
case 007:
lognl( " bvs" );
doBranch = ALU.V_bit;
break;
case 010:
lognl( " ba" );
doBranch = true;
return;
case 011: // Extended opcode
lognl( " bne" );
doBranch = ! ALU.Z_bit;
break;
default:
throw new UnimplementedOpcodeException(
" Unimplemented Branch cond: " + IR.cond );
}
if ( doBranch )
{
// Calculate branch target address, and branch
Registers.setValue( t0,
ALU.do_op( Registers.getValue( pc ), IR.disp22, ADD ) );
Registers.setValue( pc, Registers.getValue( t0 ) );
return;
}
break;
case 4:
lognl( " sethi");
Registers.setValue( IR.rd,
ALU.do_op( IR.disp22, 0, LSHIFT10 ) );
break;
default:
throw new UnimplementedOpcodeException(
" Unimplemented Branch/sethi op2: " + IR.op2 );
}
// nop, sethi, and failed branch update the pc.
Registers.setValue( pc,
ALU.do_op( Registers.getValue(pc), 0, INCPC ));
}
// doArithmetic()
// ----------------------------------------------------------------
/**
* Process arithmetic instructions.
*
* @throws UnimplementedOpcodeException if the op3 field is not
* recognized.
*/
static void doArithmetic() throws UnimplementedOpcodeException
{
switch ( IR.op3 )
{
case 000: // add - Extended opcode
lognl( " add" );
Registers.setValue( IR.rd, ALU.do_op(
Registers.getValue( IR.rs1 ), (IR.i_bit == 1) ?
IR.simm13 : Registers.getValue( IR.rs2 ), ADD ) );
break;
case 020: // addcc
lognl( " addcc" );
Registers.setValue( IR.rd, ALU.do_op(
Registers.getValue( IR.rs1 ), (IR.i_bit == 1) ?
IR.simm13 : Registers.getValue( IR.rs2 ), ADDCC ) );
break;
case 021: // andcc
lognl( " andcc" );
Registers.setValue( IR.rd, ALU.do_op(
Registers.getValue( IR.rs1 ), (IR.i_bit == 1) ?
IR.simm13 : Registers.getValue( IR.rs2 ), ANDCC ) );
break;
case 022: // orcc
lognl( " orcc" );
Registers.setValue( IR.rd, ALU.do_op(
Registers.getValue( IR.rs1 ), (IR.i_bit == 1) ?
IR.simm13 : Registers.getValue( IR.rs2 ), ORCC ) );
break;
case 026: // orncc
lognl( " orncc" );
Registers.setValue( IR.rd, ALU.do_op(
Registers.getValue( IR.rs1 ), (IR.i_bit == 1) ?
IR.simm13 : Registers.getValue( IR.rs2 ), NORCC ) );
break;
case 046: // srl
lognl( " srl" );
Registers.setValue( IR.rd, ALU.do_op(
Registers.getValue( IR.rs1 ), (IR.i_bit == 1) ?
IR.simm13 : Registers.getValue( IR.rs2 ), SRL ) );
break;
case 070: // jmpl
lognl( " jmpl" );
Registers.setValue( IR.rd, Registers.getValue( pc ) );
Registers.setValue( IR.pc,
ALU.do_op( Registers.getValue( IR.rs1 ), (IR.i_bit == 1) ?
IR.simm13 : Registers.getValue( IR.rs2 ), ADD ) );
return; // Don't update PC
default:
throw new UnimplementedOpcodeException(
" Unimplemented Arithmetic op3: " + oxydize( IR.op3, 3) );
}
// All arithmetic instructions except jmpl update the pc.
Registers.setValue( pc,
ALU.do_op( Registers.getValue(pc), 0, INCPC ));
}
// doMemory()
// ----------------------------------------------------------------
/**
* Process ld/st instructions.
*
* @throws UnimplementedOpcodeException if the op3 field is not
* recognized.
*/
static void doMemory() throws UnimplementedOpcodeException
{
switch ( IR.op3 )
{
case 000:
lognl( " ld" );
Registers.setValue( t0,
ALU.do_op( Registers.getValue( IR.rs1 ),
(IR.i_bit == 1) ? IR.simm13 : Registers.getValue( IR.rs2 ),
ADD ));
Registers.setValue( IR.rd,
Memory.readWord( Registers.getValue( t0 ) ) );
break;
case 004:
lognl( " st" );
Registers.setValue( t0,
ALU.do_op( Registers.getValue( IR.rs1 ),
(IR.i_bit == 1) ? IR.simm13 : Registers.getValue( IR.rs2 ),
ADD ));
Memory.writeWord( Registers.getValue( t0 ),
Registers.getValue( IR.rd ));
break;
default:
throw new UnimplementedOpcodeException(
" Unimplemented Memory op3: " + IR.op3 );
}
// Both memory operations update the pc.
Registers.setValue( pc,
ALU.do_op( Registers.getValue(pc), 0, INCPC ));
}
}