• Fuentes

 #449108  por overxfl0w13
 16 Jul 2014, 04:32
Como ya comenté estaba interesado en trabajar la ruta de datos del mips32 y en un par de días creo que he sacado algo decente. Voy a soltaros algunas nociones para entender cómo va el tema.


Lo que mínimamente he intentado simular es el funcionamiento del procesador mips r2000 de 32 bits implementando su ruta de datos de la siguiente imagen (la cpu implementada no está segmentada así que las barras intermedias del dibujo sobrarían -aunque de cierto modo se usan-) :
Imagen
-Imagen 1-

Siempre que se diseña un procesador hay que elegir el conjunto de instrucciones que va a poder ejecutar, ésto formará parte de la arquitectura del procesador -Risc en este caso- junto con el manejo de memoria virtual,mapa de direcciones... Las instrucciones que se han elegido para implementar la ruta son:

Imagen
-Imagen 2-

Del conjunto de instrucciones hay que diferenciar las tipo R y las tipo I (las tipo J no se han implementado, aumenta ligeramente la complejidad de la ruta y esto ya me ha comido bastante tiempo).

Imagen
-Imagen 3-

En las instrucciones con formato R la formación del opcode es de la siguiente forma:
·cop (Código de operación) -> 6 bits
·Rs (Registro [$0..$31]) -> 5 bits
·Rt (Registro [$0..$31]) -> 5 bits
·Rd (Registro [$0..$31]) -> 5 bits
·5 bits a 0 en nuestro caso.
·func (Código de función) -> 6 bits

En las tipo I:
·cop (Código de operación) -> 6 bits
·Rs (Registro [$0..$31]) -> 5 bits
·Rt (Registro [$0..$31]) -> 5 bits
·desp (Desplazamiento) -> 16 bits

Con ésto y tiempo ya podéis montaros vuestros opcodes, el recorrido de las instrucciones por la ruta de datos -Imagen 1- me llevaría páginas explicarlo en profundidad así que podéis verlo aquí: [ Debe registrarse para ver este enlace ]


El programa de ejemplo en el código es:

[ADDRESS] [INSTRUCT] [OPCODE]
[0x00400000] add $5,$5,$3 0x00A32808
[0x00400004] sub $4,$5,$3 0x00A32022
[0x00400008] or $6,$4,$5 0x00853025
[0x0040000C] sw $6,0($12) 0xAD860000
[0x00400010] lw $7,0($12) 0x8D870000
[0x00400014] lw $16,4($30) 0x8FD00004
[0x00400018] beq $16,$15,28 0x120F0006

Los datos de los registros no se pueden modificar por código porque la ruta de datos no permite instrucciones de asignación en registros, se deben precargar los registros con los datos iniciales del programa, en el ejemplo:
this.registers.setValue(13,5); // Escribir en registro 5 un 13
this.registers.setValue(13,3); // Escribir en registro 3 un 13
this.registers.setValue(0x00010000,12); // Escribir en registro 12 0x00010000 (dirección de memoria para guardar resultado)
this.registers.setValue(0x0000FFFC,30); // Escribir en registro 30 0x0000FFFC ( dirección de memoria -vacía inicialmente-)
this.registers.setValue(32,15); // Escribir en registro 15 un 32 (Este valor hace referencia al resultado de la operación, con el podremos o no evaluar el bucle del código)

En el código primero guardamos en el registro 5 (rd) el resultado de la suma entre el valor del registro 5 (rs) y el valor del registro 3 (rt) (add $5,$5,$3), el resultado será 26 (13+13).
Guardamos en el registro 4 (rd), el resultado de la resta entre el valor del registro 5 (rs) y el valor del registro 3 (rt) (sub $4,$5,$3), el resultado será 13 (26-13).
Guardamos en el registro 6 (rd), el resultado de la or entre el valor del registro 4 (rs) y el valor del registro 5 (rt) (or $6,$4,$5), el resultado será 31 (26|13).
Escribir en memoria el resultado de la or anterior en la dirección especificada por (0+$12) que es 0x00010000 (sw $6,0($12)).
Leemos de memoria el dato escrito antes en el registro 7 desde la dirección de memoria especificada por (0+$12) (lw $7,0($12)).
Leemos de memoria el dato escrito antes en el registro 16 desde la dirección de memoria especificada por (4+$30) (4+0x0000FFFC=0x00010000, la misma de antes) (lw $16,4($30)).
Finalmente comprobamos si los valores del registro 15 y del registro 16 coinciden ($15==$16) para saltar a la dirección calculada de la siguiente forma:
·PC = PC - (despl<<2)

El desplazamiento es el nº de instrucciones entre la instrucción actual y la instrucción a la que se quiere saltar:
1) inst1
2) inst2
3) inst3
4) inst4
5) j 4 // 4 = desplazamiento para saltar a la linea 1 PC = 5 - 4 = 1, se usa <<2 para multiplicar por 4 en el ejemplo anterior (word)

Como veréis esa forma de calcular el nuevo contador de programa solo permite saltos hacia atrás, podéis cambiar el signo y especificar cuando forméis los opcode el desplazamiento en complemento a 2 para permitir saltos hacia delante.

Siguiendo con el código de ejemplo, si $16 y $15 tienen el mismo valor finalizará la ejecución, ésto es lo que pasa con los datos actuales,
en la imagen se ve la salida de las 2 últimas instrucciones:
Imagen

Sin embargo si cambiamos el valor del registro $15 con la salida conocida 31 (this.registers.setValue(32,15);), se ejecutará el bucle infinito que se genera al lograrse la igualdad de $15 y $16 en el bucle.

Eso es todo, no me puedo alargar más con la explicación. Respecto al código simplemente he implementado todos los componentes de la ruta de datos (Memoria de instrucciones,Memoria de datos, Banco de registros,ALU,Multiplexores,Extensor de signo,Parser de instrucciones y Unidad de Control), almacenado todas las señales de control que generan las instrucciones implementadas (podéis verlas en la wiki si no recuerdo mal) y simulado el flujo de instrucciones a lo largo de la ruta. Os dejo el código:

[Package Componentes]

[CyclicMux2to1.java]
package Componentes;


public class CyclicMux2to1
{
    // Get mux execution output
    // Como se permite el reuso de un objeto multiplexor y cada uno tiene una señal de control diferente, hay que
    // pasarle al método su correspondiente señal.
    public static final int getOutput(int ent0,int ent1,byte cSignal){
        int output = cSignal==0 ? ent0 : ent1;
        return output;
    }
    
    /**public static void main(String args[]){
        System.out.println(CyclicMux2to1.getOutput(2,3,(byte)0));
    }**/
}
[InstructionMemory.java]
package Componentes;
import Configuracion.General;
import Configuracion.ControlSignals;
// Se intentará evitar pasar por los wrappers de java y usar clases que impliquen su uso //
// Un array de bytes con los opcodes del código a ejecutar, se posicionará un índice de la siguiente forma:
// El array de opcodes tendrá de longitud: dirección final - dirección inicial
// for(int i=OpCodes.instBaseAddr;i>=0;i--){ opCode = v[OpCodes.instBaseAddr-i]; }

// La unidad de control sacará a su salida la instrucción indicada por el PC si Linst está a 1 //
public class InstructionMemory
{
    private int[] memInstructionAddrs;
    private final int staticLength = General.instLastAddr - General.instBaseAddr;
    
    public InstructionMemory(){
        // Lanzar excepción si se supera el limite de staticLength (añadir este límite también) //
        this.memInstructionAddrs = new int[this.staticLength];
    }
    
    public void addInstruction(int inst){ this.memInstructionAddrs[(General.PC - General.instBaseAddr)] = inst;}
    
    public int getInstruction() throws Exception{ 
        if(ControlSignals.Linst==1) return this.memInstructionAddrs[(General.PC - General.instBaseAddr)];
        return -1;
    }
    
    public void resetMemory(){ for(int i=0,act=this.memInstructionAddrs[i];i<staticLength && act!=0;this.memInstructionAddrs[i++]=0,act=this.memInstructionAddrs[i]); }
    
    public void loadProgram(int[] program){ 
        int initialPC = General.PC;
        for(int e : program){ this.addInstruction(e); General.PC += 4;}
        General.PC = initialPC;
    }
    
    /**public static void main(String args[]){
    /*  try{
        InstructionMemory im = new InstructionMemory();
        ControlSignals.Linst = 1;
        im.addInstruction(0x13371337);
        General.PC += 4;
        im.addInstruction(0x13371338);
        General.PC += 4;
        General.PC = General.instBaseAddr;
        System.out.println(im.getInstruction());
        General.PC += 4;
        System.out.println(im.getInstruction());
    }catch(Exception e){}
    }**/
   
        
}
[Registers.java]
package Componentes;
import Configuracion.ControlSignals;
public class Registers
{
   // 32 registros de 4B 
   private int[] output; // se devuelven las 2 salidas en un array, la 1º salida es rs, la 2º es rt
   private int[] registers;
   
   public Registers(){
       this.registers = new int[32]; // 32 registros //
       this.output    = new int[2];
   }
   
   public int[] getOutput(int rd,int rs,int rt,int data) throws Exception{
       // Si la señal de control LReg está a 1, la 1º salida será el valor del registro v[rs] y la 2º salida será el valor del registro v[rt] //
       if(ControlSignals.LReg==1){ if(rs<32 && rt<32) {this.output[0] = registers[rs]; this.output[1] = registers[rt]; } else throw new Exception("Invalid Register"); }
       // Si no, si la señar EReg está a 1, las salidas serán 0 y se guardará en el registro rd el valor data //
       else if(ControlSignals.EReg==1){ if(rd<32){this.registers[rd] = data; } else throw new Exception("Invalid Register");}
       return this.output;
   }
   
   // DEBUGGING //
   public void setValue(int d,int i){ this.registers[i] = d;    }
   public int  getValue(int i)      { return this.registers[i]; }
   
   /**public static void main(String args[]){
       try{
           Registers regs = new Registers();
           ControlSignals.EReg = 1;
           for(int e : regs.getOutput(14,0,0,1337)) System.out.println(e);
           ControlSignals.EReg = 0;
           ControlSignals.LReg = 1;
           for(int e : regs.getOutput(0,14,15,9999)) System.out.println(e);
       }catch(Exception e){ e.printStackTrace(); }
    }**/
}
[RI.java]
package Componentes;

/** El RI se encarga de a partir de la instrucción separar rs,rt los 16 bits de inmediato, es la primera parte del proceso,
 *  posteriormente se ejecutará la unidad de control para asignar las señales de control y luego seguir el proceso
 */
public class RI
{
    // A partir de la instrucción, se devolverá un array con 6 valores [instruccion para la unidad de control,rs,rt,inm,tipoI,tipoR]
    // Los valores son de diferente tamaño <= sizeof(int) por tanto se unifican todos en un array de int para evitar tener que depender de genericidad y otras soluciones //
    public final static int[] getOutput(int inst){
        int[] output = new int[6];
        output[0] = inst;                                      // Instruction //
        output[1] =  (byte)((inst & 0x03E00000) >> 21);        // Rs //
        output[2] =  (byte)((inst & 0x001F0000) >> 16);        // Rt //
        output[3] = (short)((inst & 0x0000FFFF));              // inm //
        output[4] =  (byte)((inst & 0x001F0000) >> 16);        // tipoI -> Rt //
        output[5] =  (byte)((inst & 0x0000F800) >> 11);        // tipoR -> Rd // 
        return output;
    }
    
    
    /** public static void main(String args[]){
        // Instruction: add $5,$5,$3 //
        for(int e : RI.getOutput(0x00A32808)) System.out.println(e);
    }**/
}
[ALU.java]
package Componentes;
import Configuracion.OpCodes;
import Configuracion.ControlSignals;
import Configuracion.General;
public class ALU
{
    
    // Get alu execution output
    public final static int getOutput(int ent0,int ent1){ 
        int res = 0x00;
        
        if(ControlSignals.OpALU==OpCodes.opaAnd) res = ent0 & ent1;
        if(ControlSignals.OpALU==OpCodes.opaOr)  res = ent0 | ent1;
        if(ControlSignals.OpALU==OpCodes.opaAdd) res = ent0 + ent1;        
        if(ControlSignals.OpALU==OpCodes.opaSub) res = ent0 - ent1;
        if(res==0) General.flagZ = 1;
        else General.flagZ = 0;
        if(res%2==0) General.flagP = 1;
        else General.flagP = 0;
        return res;
    }
        
    
    /**public static void main(String args[]){
        ControlSignals.OpALU = 0x02;
        System.out.println(ALU.getOutput(2,2));
    }**/
}
[DataMemory.java]
package Componentes;
import Configuracion.General;
import Configuracion.ControlSignals;
// Se intentará evitar pasar por los wrappers de java y usar clases que impliquen su uso //
// Un array de bytes con los opcodes del código a ejecutar, se posicionará un índice de la siguiente forma:
// El array de opcodes tendrá de longitud: dirección final - dirección inicial
// for(int i=OpCodes.instBaseAddr;i>=0;i--){ opCode = v[OpCodes.instBaseAddr-i]; }

// La memoria de datos 
public class DataMemory
{
    private int[] memDataAddrs;
    public int pActData;
    private final int staticLength = General.dataLastAddr - General.dataBaseAddr;
    
    public DataMemory(){
        this.memDataAddrs = new int[this.staticLength];
        this.pActData     = 0;
    }
    
    public int getOutput(int data,int addr){
        // Si la señal de control LMem está a 1, coger el valor de la dirección addr y retornarla //
        if(ControlSignals.LMem==1){ System.out.println("Readed from memory : " + this.getWord(addr)); return this.getWord(addr); }
        // Si no, si EMem está a 1, guardar los datos en la dirección siguiente
        else if(ControlSignals.EMem==1){ this.addWord(data); System.out.println("Writted in memory : " + memDataAddrs[this.pActData-4]); }
        return 0x00;
    }
    
    public void addWord(int data){ this.memDataAddrs[this.pActData] = data; this.pActData += 4; }
 
    public void resetMemory(){ 
        for(int i=0,act=this.memDataAddrs[i];i<this.staticLength && act!=0;this.memDataAddrs[i++]=0,act=this.memDataAddrs[i]);
        this.pActData    = 0;
    }
    
    public int getWord(int addr){ return this.memDataAddrs[addr-General.dataBaseAddr]; }
    public int getHalf(int addr){ return (short)(this.getWord(addr) & 0x0000FFFF); }
    public int getByte(int addr){ return (byte)(this.getWord(addr) & 0x000000FF); }
    
    /**public static void main(String args[]){ 
        DataMemory im = new DataMemory();
        // Control signal de escritura a 1 //
        ControlSignals.LMem = 0;
        ControlSignals.EMem = 1;
        System.out.println(im.getOutput(1337,0x00010000));
        System.out.println(im.getOutput(1338,0x00010004));
        // Control signal de lectura a 1 //
        ControlSignals.LMem = 1;
        ControlSignals.EMem = 0;
        System.out.println(im.getOutput(999,0x00010000));
        System.out.println(im.getOutput(999,0x00010004));
    }**/
    
}
[ControlUnit.java]
package Componentes;
import Configuracion.ControlSignals;
import Configuracion.ControlSignalPerInstructionControl;
/** La unidad de control se encarga de parsear los opcodes y setear correctamente los flags de las señales
 *  de control en Configuracion.ControlSignals para usar luego en la simulación. 
 *  Debe, partiendo de los 4B del opcode (solo se tratarán instrucciones R y I) identificar el tipo de instrucción,
 *  coger los 6 bits de función y extraer las señales de control correspondientes, luego setearlas en ControlSignals
 */
public class ControlUnit
{
    public final static void setControlSignalsByInst(int instruction) throws Exception{
        // Extraer OpCode de la instrucción (6 MSB) //
        byte opCode  = (byte)Math.abs(((instruction & 0xFF000000)>>24));
        // Con el OpCode ver si es 0 es una operación aritmético-lógica (R) si no es de tipo I (memoria y condicional)
        // Extraer el opfCode correspondiente (6 LSB) si el opcode es 0 (R) //
        if(opCode==0){ 
            opCode = (byte)Math.abs((((instruction & 0x0000000F))));
            opCode &= 0x0F;
            opCode = (byte)Math.abs(opCode);
            // Usar los 4 LSB para identificar la función (problemas java para sacar 
        }
        else{
           // Como los 4LSB del byte CO del opcode son dependientes del registro rs, multiplexar partiendo de los 4MSB //
           opCode = (byte)Math.abs((((instruction & 0xF0000000)>>24)));
           opCode &= 0xF0;
           if(opCode==0x60) opCode = 0x2B; // SW
           if(opCode==-128) opCode = 0x23; // LW
           if(opCode==16) opCode = 0x06; // BEQ
        }
        // Con el opCode setteado obtener el array de señales de control //
        byte[] cSignals = ControlSignalPerInstructionControl.controlSignalsByFunctionCode(opCode);
        // Una vez se tienen las señales de control settear ControlSignals //
        ControlSignals.PCin = cSignals[0]; ControlSignals.Linst = cSignals[1]; ControlSignals.LReg   = cSignals[2];
        ControlSignals.EReg   = cSignals[3]; ControlSignals.OpALU  = cSignals[4]; ControlSignals.LMem   = cSignals[5];
        ControlSignals.EMem   = cSignals[6]; ControlSignals.MxPC   = cSignals[7]; ControlSignals.MxALU  = cSignals[8];
        ControlSignals.MxDst  = cSignals[9]; ControlSignals.MxER   = cSignals[10];      
    }
      
    /**public static void main(String args[]){
        ControlUnit.setControlSignalsByInst(0x00A32808); // Tipo R add $5,$5,$3
        System.out.println(ControlSignals.OpALU);
        //ControlUnit.setControlSignalsByInst(0x8C620004);
    }**/
      
}
[SignExtender.java]
package Componentes;


public class SignExtender
{
    public static final int signExtend(short valueToExtend){
        int res = 0x00000000;
        // Si el MSB es un 1, se extiende el 1 en los 16 bits restantes del int //
        if((valueToExtend&0x8000)==0x8000){ res = 0xFFFF0000 | valueToExtend; }
        else res |= valueToExtend;
        return res;
    }
    
    /**public static void main(String args[]){
        System.out.println((short)0xFFF1);
        System.out.println(SignExtender.signExtend((short)0x3FF1));
    }**/
}

[Package Configuración]

[ControlSignals.java]
package Configuracion;

public class ControlSignals
{
    public static byte MxPC  = 0x00;
    public static byte PCin  = 0x00;
    public static byte Linst = 0x00;
    public static byte LReg  = 0x00;
    public static byte EReg  = 0x00;
    public static byte MxDst = 0x00;
    public static byte MxALU = 0x00;
    public static byte OpALU = 0x00;
    public static byte MxER  = 0x00;
    public static byte LMem  = 0x00;
    public static byte EMem  = 0x00;    
}
[OPCodes.java]
package Configuracion;

// Será útil si se quiere dar soporte a traducción de órdenes (string) -> (op code) //
public class OpCodes
{
    // CPU Opcodes //
    public final static byte opfAdd = 0x08;
    public final static byte opfSub = 0x02;
    public final static byte opfAnd = 0x04;
    public final static byte opfOr  = 0x05;
    public final static byte opfLw  = 0x23;
    public final static byte opfSw  = 0x2B;
    public final static byte opfBeq = 0x06;
    
    // ALU Opcodes //
    public final static byte opaAnd = 0x00;
    public final static byte opaOr  = 0x01;
    public final static byte opaAdd = 0x02;
    public final static byte opaSub = 0x06;
    
}
[General.java]
package Configuracion;

public class General
{
    public static int PC                   = 0x00400000;
    public static int rs                   = 0x00000000;
    public static int rd                   = 0x00000000;
    public static int rt                   = 0x00000000;
    public static int actInstruction       = 0x00000000;
    public static byte flagZ               = 0x00;
    public static byte flagP               = 0x00;
    // Los tamaños son reducidos para evitar limitaciones de tamaño
    public final static int dataBaseAddr   = 0x00010000;
    public final static int dataLastAddr   = 0x0001FFFF;
    public final static int instBaseAddr   = 0x00400000;
    public final static int instLastAddr   = 0x004FFFFF;
    // De la 0x80000000 hasta 0xFFFFFFFF reservado para el operativo (2GB)
    //public final static int kernelBaseAddr = 0x80000000;
    //public final static int kernelLastAddr = 0xFFFFFFFF;
}
[ControlSignalPerInstructionControl.java]
package Configuracion;
import java.util.HashMap;
import java.util.Map;
import Configuracion.OpCodes;
public class ControlSignalPerInstructionControl
{
   // Array[0][j] = add (function opcode(byte) 00100000)
   // Array[0][j] = sub (function opcode(byte) 00100010)
   // Array[0][j] = and (function opcode(byte) 00100100)
   // Array[0][j] = or  (function opcode(byte) 00100101)
   // Array[0][j] = lw  (function opcode(byte) 00100011)
   // Array[0][j] = sw  (function opcode(byte) 00101011)
   // Array[0][j] = beq (function opcode(byte) 00000100)
   // Array[x][0] = PCin
   // Array[x][1] = LInst
   // Array[x][2] = LReg
   // Array[x][3] = EReg
   // Array[x][4] = opALU
   // Array[x][5] = LMem
   // Array[x][6] = EMem
   // Array[x][7] = MxPC
   // Array[x][8] = MxALU
   // Array[x][9] = MxDst
   // Array[x][10] = MxER

   private static final byte[][] controlTable  = {{0x01,0x01,0x01,0x01,0x02,0x00,0x00,0x00,0x00,0x01,0x00},
                                                  {0x01,0x01,0x01,0x01,0x06,0x00,0x00,0x00,0x00,0x01,0x00},
                                                  {0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x00},
                                                  {0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x01,0x00},
                                                  {0x01,0x01,0x01,0x01,0x02,0x01,0x00,0x00,0x01,0x00,0x01},
                                                  {0x01,0x01,0x01,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x00},
                                                  {0x01,0x01,0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00}};
                                                  
    
   public static final byte[] controlSignalsByFunctionCode(byte opFunction) throws Exception{
       System.out.println("\n\n---------------- -----------------");
       if(opFunction==OpCodes.opfAdd){System.out.println("\n[Instruction] ADD"); return ControlSignalPerInstructionControl.controlTable[0]; }     // add
       else if(opFunction==OpCodes.opfSub){ System.out.println("\n[Instruction] SUB"); return ControlSignalPerInstructionControl.controlTable[1]; }// sub
       else if(opFunction==OpCodes.opfAnd){ System.out.println("\n[Instruction] AND"); return ControlSignalPerInstructionControl.controlTable[2]; }// and
       else if(opFunction==OpCodes.opfOr){ System.out.println("\n[Instruction] OR"); return ControlSignalPerInstructionControl.controlTable[3]; }// or
       else if(opFunction==OpCodes.opfLw){ System.out.println("\n[Instruction] LW"); return ControlSignalPerInstructionControl.controlTable[4]; }// lw
       else if(opFunction==OpCodes.opfSw){ System.out.println("\n[Instruction] SW"); return ControlSignalPerInstructionControl.controlTable[5]; }// sw
       else if(opFunction==OpCodes.opfBeq){ System.out.println("\n[Instruction] BEQ"); return ControlSignalPerInstructionControl.controlTable[6];} // beq
       return null;
    }
    
    /**public static void main(String args[]){
       System.out.println(ControlSignalPerInstructionControl.controlSignalsByFunctionCode((byte)0x20)[4]);
    }**/
}
[Package Simulacion]

[CPUProcess.java]
package Simulacion;
import Configuracion.*;
import Componentes.*;

public class CPUProcess
{
    // Algunas clases son estáticas y no necesitan instanciación pero se quiere expresar que la CPU tiene los
    // siguientes elementos (relación tiene un)
    private CyclicMux2to1       cyclicMx;
    private ALU                 alu;
    private DataMemory          dataMemory;
    private InstructionMemory   instMemory;
    private Registers           registers;
    private ControlUnit         controlUnit;
    private SignExtender        signExtender;
    private RI                  instParser;
    
    public CPUProcess(){
        this.dataMemory        = new DataMemory();
        this.instMemory        = new InstructionMemory();
        this.registers         = new Registers();
        this.controlUnit       = new ControlUnit(); 
    }
    
    public final void __run__(){     
        /** Test Work **/
        // Setear los registros (no hay instrucción de asignación en registro con esta ruta de datos, se deben precargar)
        this.registers.setValue(13,5); // Valor,Registro //
        this.registers.setValue(13,3);
        this.registers.setValue(0x00010000,12); // Registro 12 addr en memoria para guardar el resultado //
        this.registers.setValue(0x0000FFFC,30); // Registro 13 addr en memoria para probar avance de punteros en lectura de memoria //
        this.registers.setValue(31,15);         // Guardar el resultado final conocido para comparar con el correcto (comparación del código de ejemplo) //
        //[0x00400000] add $5,$5,$3  ( 13 + 13 = 26 ) en $5 -> 0x00A32808
        //[0x00400004] sub $4,$5,$3  ( 26 - 13 = 13 ) en $4 -> 0x00A32022
        //[0x00400008] or $6,$4,$5   ( 13 | 26 = 31 ) en $6 -> 0x00853025
        //[0x0040000C] sw $6,0($12)  ( Guardar en memoria $6 ) -> 0xAD860000
        //[0x00400010] lw $7,0($12)  ( Cargar el valor guardado en $7 ) -> 0x8D870000
        //[0x00400014] lw $16,4($30) ( Cargar el valor guardado con desplazamiento desde posición inferior en un registro alto ) -> 0x8FD00004
        //[0x00400018] beq $16,$15,28 ( Si $15 == $16 saltar a 0x00400000 (PC + desp) (como siempre lo será, bucle infinito ) -> 0x120F0006
        // Program: {0x00A32808,0x00A32022,0x00853025,0xAD860000,0x8D870000,0x8FD00004,0x120F0006}
        // El desplazamiento (desp) se expresa en nº de lineas (de instrucciones)
        //and &6,&6,$zero ( poner a 0 $6 )     -> 0x00C03024 ($zero puede ser cualquier registro puesto a 0, no es como en mips reg[0] reservado, aquí el programador es libre)
        
        int program[] = {0x00A32808,0x00A32022,0x00853025,0xAD860000,0x8D870000,0x8FD00004,0x120F0006}; 
        // Cargar programa en memoria de instrucciones //
        this.instMemory.loadProgram(program);
        // Variables //
        int actInst = 0x00;
        int[] instParsed = new int[11];
        int rs,rt,rd = 0x00;
        int drs,drt  = 0x00; // Datos contenidos en registros rs y rt
        int k = 0x00;
        byte antEReg = 0x00;
        int data = 0x00;
        int[] registerOutput = new int[2];
        int aluOutput = 0x00;
        int memOut    = 0x00;
        int NPC       = 0x00;
        // Señales de control constantes //
        ControlSignals.Linst = 1;
        // Bucle principal //
        while(true){     
            /** Etapa LI (Lectura de instrucción **/
            // Leer la instrucción de la dirección de memoria PC // 
            try{
                actInst = this.instMemory.getInstruction();
            }catch(Exception e){ break; }
            // Avanzar PC dependiendo si viene de un condicional o si avanza normalmente (+4) //
           
            /** Etapa DI (Decodificación de la instrucción **/
            // Satear las señales de control de la correspondiente instrucción en ControlSignals//
            try{
                this.controlUnit.setControlSignalsByInst(actInst);
            }catch(Exception e){ break; }
            // Decodificar la instrucción //
            instParsed = this.instParser.getOutput(actInst);
            // Obtener rd del MxDst //
            rd = this.cyclicMx.getOutput(instParsed[4],instParsed[5],ControlSignals.MxDst);
            // Leer Rs y Rt del banco de registros (en operaciones load rt no hace falta leer pero se hace por acortar //
            // Para esto poner el bit LReg a 1 y el EReg a 0, guardar anterior valor de EReg por si luego hace falta escribir //
            rs = instParsed[1];
            rt = instParsed[2];
            // [instruccion para la unidad de control,rs,rt,inm,tipoI,tipoR] //
            antEReg = ControlSignals.EReg; ControlSignals.EReg = 0; 
            System.out.println("[Decode]");
            System.out.println("\tRT: " + rt + " RS: " + rs + " RD: " + rd);
            
            /** Añadir más excepciones sobre otros componentes que pueden ser mal usados **/
            try{
                registerOutput = this.registers.getOutput(rd,rs,rt,data);
            }catch(Exception e){ break; }
            // Se obtiene el valor extendido del extensor de signo //
            k = this.signExtender.signExtend((short)instParsed[3]);
            System.out.println("\tRS Value: " + registerOutput[0]);
            System.out.println("\tRt Value: " + registerOutput[1]);
            
            /** Etapa EX (Ejecución de la instrucción) **/
            // Multiplexar el valor procedente del extensor de signo y de rt con la señal MxALU y utilizar el valor como entrada 1 de la ALU //
            aluOutput = this.alu.getOutput(registerOutput[0],this.cyclicMx.getOutput(registerOutput[1],k,ControlSignals.MxALU));
            System.out.println("[Execution] Result: " + aluOutput);
            
            /** Etapa M (Interacción con memoria) **/
            /** Data = register[rt] **/
            /** Addr = resultado de la ALU **/
            memOut = this.dataMemory.getOutput(registerOutput[1],aluOutput);
            // Con el valor de MemOut y ALUOut se multiplexa y se cogen los datos a guardar en el registro //
            data = this.cyclicMx.getOutput(aluOutput,memOut,ControlSignals.MxER);
            
            /** Etapa ER (Escritura en registro) **/
            ControlSignals.EReg = antEReg; // Restaurar el registro de escritura
            // Si el flag EReg està activado, poner LReg a 0 y guardar en registro //
            if(ControlSignals.EReg==1){
                ControlSignals.LReg = 0;
                try{
                    this.registers.getOutput(rd,rs,rt,data);
                }catch(Exception e){ e.printStackTrace(); }
                ControlSignals.LReg = 1;
            }          
            // Para avanzar el pc, sumar PC+4 + el desplazamiento extendido desplazado 2 veces a izquierda (*(2^2)), se multiplexa ese valor con el (PC+4) avance normal //
            // En teoría lo de arriba funciona, pero paso de tocar más, se deja el salto hacia atrás y ya //
            NPC = General.PC - (k<<2);
            General.PC = this.cyclicMx.getOutput(General.PC+4,NPC,General.flagZ);
            
            System.out.println("[New PC] 0x00" + Integer.toHexString(General.PC));
        }
        
    }
    
    public static void main(String args[]){
        CPUProcess cpu = new CPUProcess();
        cpu.__run__();
    }
}
Con eso acabo, si tenéis alguna duda buscad por internet hay fuentes muy buenas, si aun así seguís escribid :).

Un saludo :)
 #449135  por Blau
 16 Jul 2014, 16:50
¡Enorme over!

Como te dije, Terminator cada vez está más cerca...
 #490362  por davinciomar
 10 Ene 2017, 23:15
excelente simulador tengo que probarlo! gracias