/*******************************************************************************
* openDLX - A DLX/MIPS processor simulator.
* Copyright (C) 2013 The openDLX project, University of Augsburg, Germany
* Project URL: <https://sourceforge.net/projects/opendlx>
* Development branch: <https://github.com/smetzlaff/openDLX>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program, see <LICENSE>. If not, see
* <http://www.gnu.org/licenses/>.
******************************************************************************/
package openDLX;
import openDLX.datatypes.*;
import openDLX.exception.ExecuteStageException;
import openDLX.exception.PipelineException;
import openDLX.util.DLXTrapHandler;
import openDLX.util.PrintHandler;
import org.apache.log4j.Logger;
public class ALU
{
private static Logger logger = Logger.getLogger("EXECUTE/ALU");
private PrintHandler print_handler=null;
private DLXTrapHandler trap_handler=null;
public ALU()
{
print_handler = PrintHandler.getInstance();
trap_handler = DLXTrapHandler.getInstance();
}
/* calculates result of ALU operation. If a 32bit result is calculated, it is available to both 32bit outputs.
* For a 64bit result, the result is split into a lower and upper 32bit results.
*/
public uint32[] doOperation(ALUFunction operation, uint32 A, uint32 B) throws PipelineException
{
uint32 resultLO = new uint32(0);
uint32 resultHI = new uint32(0);
switch(operation)
{
case ADD:
// TODO missing trap on overflow
case ADDU:
resultLO.setValue(A.getValue() + B.getValue());
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " + " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case AND:
resultLO.setValue(A.getValue() & B.getValue());
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " & " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case BA:
resultLO.setValue((A.getValue()&0xF0000000)|((int)B.getValue()<<2));
logger.debug("(" + B.getValue() + "(" + B.getValueAsHexString() + ")" + " << 2) + " + (A.getValue()&0xF0000000) + "(" + Integer.toHexString(A.getValue()&0xF0000000) + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case BR:
short tgt_offset = (short)B.getValue();
resultLO.setValue(A.getValue()+(int)(tgt_offset << 2));
logger.debug("(" + B.getValue() + "(" + B.getValueAsHexString() + ")" + " << 2) + " + A.getValue() + "(" + A.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case DIV:
{
if(B.getValue() == 0)
{
throw new ExecuteStageException("Division by zero.");
}
// takes usually multiple cycles (3 according to the isa)
int q = A.getValue() / B.getValue();
int r = A.getValue() % B.getValue();
resultLO.setValue(q);
resultHI.setValue(r);
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " / " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = LO (q): " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")" + " HI (r): " + resultHI.getValue() + "(" + resultHI.getValueAsHexString() + ")");
break;
}
case DIVU:
{
if(B.getValue() == 0)
{
throw new ExecuteStageException("Division by zero.");
}
// takes usually multiple cycles (3 according to the isa)
// chop of sign bit
int q = (A.getValue()&0x7FFFFFFF) / (B.getValue()&0x7FFFFFFF);
int r = (A.getValue()&0x7FFFFFFF) % (B.getValue()&0x7FFFFFFF);
resultLO.setValue(q);
resultHI.setValue(r);
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " / " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = LO (q): " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")" + " HI (r): " + resultHI.getValue() + "(" + resultHI.getValueAsHexString() + ")");
break;
}
case LUI:
resultLO.setValue(((int)B.getValue() << 16));
logger.debug(B.getValue() + "(" + B.getValueAsHexString() + ")" + " << " + 16 + "(0x10)" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case MULT:
{
// takes usually multiple cycles (3 according to the isa)
long mult = (long)A.getValue() * (long)B.getValue();
resultLO.setValue((int)(mult & 0xFFFFFFFF));
resultHI.setValue((int)((mult >>> 32) & 0xFFFFFFFF));
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " * " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + mult + " HI: " + resultHI.getValue() + "(" + resultHI.getValueAsHexString() + ")" + " LO: " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
break;
}
case MULTU:
{
// takes usually multiple cycles (3 according to the isa)
// chop of sign bit
long mult = (long)(A.getValue()&0x7FFFFFFF) * ((long)B.getValue()&0x7FFFFFFF);
resultLO.setValue((int)(mult & 0xFFFFFFFF));
resultHI.setValue((int)((mult >>> 32) & 0xFFFFFFFF));
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " * " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + mult + " HI: " + resultHI.getValue() + "(" + resultHI.getValueAsHexString() + ")" + " LO: " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
break;
}
case OR:
resultLO.setValue(A.getValue() | B.getValue());
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " | " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case NOR:
resultLO.setValue(~(A.getValue() | B.getValue()));
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " NOR " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SLL:
{
short s = (short) (B.getValue() & 0x1F);
resultLO.setValue(A.getValue() << s);
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " << " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
}
case SLLV:
{
short s = (short) (A.getValue() & 0x1F);
resultLO.setValue(B.getValue() << s);
logger.debug(B.getValue() + "(" + B.getValueAsHexString() + ")" + " << " + A.getValue() + "(" + A.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
}
case SEQ:
// NOTICE: this ALU function is only needed for the DLX ISA
if(A.getValue() == B.getValue())
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SEQ " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SEQU:
// NOTICE: this ALU function is only needed for the DLX ISA
// chop of sign bit
if((A.getValue()&0x7FFFFFFF) == (B.getValue()&0x7FFFFFFF))
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SEQU " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SNE:
// NOTICE: this ALU function is only needed for the DLX ISA
if(A.getValue() != B.getValue())
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SNE " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SNEU:
// NOTICE: this ALU function is only needed for the DLX ISA
// chop of sign bit
if((A.getValue()&0x7FFFFFFF) != (B.getValue()&0x7FFFFFFF))
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SNEU " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SGE:
// NOTICE: this ALU function is only needed for the DLX ISA
if(A.getValue() >= B.getValue())
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SGE " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SGEU:
// NOTICE: this ALU function is only needed for the DLX ISA
// chop of sign bit
if((A.getValue()&0x7FFFFFFF) >= (B.getValue()&0x7FFFFFFF))
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SGEU " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SGT:
// NOTICE: this ALU function is only needed for the DLX ISA
if(A.getValue() > B.getValue())
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SGT " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SGTU:
// NOTICE: this ALU function is only needed for the DLX ISA
// chop of sign bit
if((A.getValue()&0x7FFFFFFF) > (B.getValue()&0x7FFFFFFF))
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SGT " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SLE:
// NOTICE: this ALU function is only needed for the DLX ISA
if(A.getValue() <= B.getValue())
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SLE " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SLEU:
// NOTICE: this ALU function is only needed for the DLX ISA
// chop of sign bit
if((A.getValue()&0x7FFFFFFF) <= (B.getValue()&0x7FFFFFFF))
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SLEU " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SLT:
if(A.getValue() < B.getValue())
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SLT " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SLTU:
// chop of sign bit
if((A.getValue()&0x7FFFFFFF) < (B.getValue()&0x7FFFFFFF))
{
resultLO.setValue(1);
}
else
{
resultLO.setValue(0);
}
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " SLT " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case SRL:
{
short s = (short) (B.getValue() & 0x1F);
resultLO.setValue(A.getValue() >>> s);
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " >>> " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
}
case SRA:
{
short s = (short) (B.getValue() & 0x1F);
resultLO.setValue(A.getValue() >> s);
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " >> " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
}
case SRLV:
{
short s = (short) (A.getValue() & 0x1F);
resultLO.setValue(B.getValue() >>> s);
logger.debug(B.getValue() + "(" + B.getValueAsHexString() + ")" + " >>> " + A.getValue() + "(" + A.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
}
case SRAV:
{
short s = (short) (A.getValue() & 0x1F);
resultLO.setValue(B.getValue() >> s);
logger.debug(B.getValue() + "(" + B.getValueAsHexString() + ")" + " >> " + A.getValue() + "(" + A.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
}
case SUB:
// TODO missing trap on overflow
case SUBU:
resultLO.setValue(A.getValue() - B.getValue());
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " - " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case XOR:
resultLO.setValue(A.getValue() ^ B.getValue());
logger.debug(A.getValue() + "(" + A.getValueAsHexString() + ")" + " XOR " + B.getValue() + "(" + B.getValueAsHexString() + ")" + " = " + resultLO.getValue() + "(" + resultLO.getValueAsHexString() + ")");
// duplicate results
resultHI = resultLO;
break;
case NOP:
resultLO.setValue(0);
// duplicate results
resultHI = resultLO;
break;
case SYSCALL:
// NOTICE: this ALU function is only needed for the MIPS ISA
doSyscall(A.getValue(),B.getValue());
resultLO.setValue(0);
// duplicate results
resultHI = resultLO;
break;
case TRAP:
// NOTICE: this ALU function is only needed for the DLX ISA
uint32 trapResult = doDLXTrap(B.getValue(),A.getValue());
resultLO.setValue(trapResult);
// duplicate results
resultHI = resultLO;
break;
case TEQ:
if(A.getValue() == B.getValue())
{
doTrap();
}
resultLO.setValue(0);
// duplicate results
resultHI = resultLO;
break;
case TGE:
if(A.getValue() >= B.getValue())
{
doTrap();
}
resultLO.setValue(0);
// duplicate results
resultHI = resultLO;
break;
case TGEU:
// chop of sign bit
if((A.getValue()&0x7FFFFFFF) >= (B.getValue()&0x7FFFFFFF))
{
doTrap();
}
resultLO.setValue(0);
// duplicate results
resultHI = resultLO;
break;
case TLT:
if(A.getValue() < B.getValue())
{
doTrap();
}
resultLO.setValue(0);
// duplicate results
resultHI = resultLO;
break;
case TLTU:
// chop of sign bit
if((A.getValue()&0x7FFFFFFF) < (B.getValue()&0x7FFFFFFF))
{
doTrap();
}
resultLO.setValue(0);
// duplicate results
resultHI = resultLO;
break;
case TNE:
if(A.getValue() != B.getValue())
{
doTrap();
}
resultLO.setValue(0);
// duplicate results
resultHI = resultLO;
break;
default:
throw new ExecuteStageException("Unknown ALU operation");
}
uint32[] results = new uint32[2];
results[0] = resultLO;
results[1] = resultHI;
return results;
}
private uint32 doDLXTrap(int trap_id, int parameter) throws PipelineException
{
uint32 return_result = new uint32(trap_id);
switch(trap_id)
{
case PipelineConstants.DLX_TRAP_STOP:
{
logger.info("Catched trap 0: stopping pipeline in write back");
break;
}
case PipelineConstants.DLX_TRAP_OPEN:
{
logger.debug("TRAP " + trap_id + ": parameter: " + parameter);
trap_handler.open(parameter);
break;
}
case PipelineConstants.DLX_TRAP_CLOSE:
{
logger.debug("TRAP " + trap_id + ": parameter: " + parameter);
trap_handler.close(parameter);
break;
}
case PipelineConstants.DLX_TRAP_READ:
{
uint32 trap_result = trap_handler.read(parameter);
return_result.setValue(trap_result);
break;
}
case PipelineConstants.DLX_TRAP_WRITE:
{
logger.debug("TRAP " + trap_id + ": parameter: " + parameter);
trap_handler.write(parameter);
break;
}
case PipelineConstants.DLX_TRAP_PRINTF:
{
trap_handler.printf(parameter);
break;
}
default:
logger.warn("Unknown Trap: " + trap_id + " parameter: " + parameter);
}
return return_result;
}
private void doSyscall(int syscall_id, int value)
{
switch(syscall_id)
{
case PipelineConstants.SYSCALL_PUTCHAR:
{
print_handler.putChar(value);
break;
}
default:
logger.warn("Unknown Syscall: " + syscall_id + " value: " + value);
}
}
private void doTrap()
{
logger.warn("Catched Trap ... don't know what to do maybe divition by zero occured. Stopping simulation.");
System.exit(1);
}
}