package com.jopdesign.timing.jop; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.Vector; import java.util.Map.Entry; import com.jopdesign.timing.ConsoleTable; import com.jopdesign.timing.InstructionInfo; import com.jopdesign.timing.ConsoleTable.Alignment; import com.jopdesign.timing.ConsoleTable.TableRow; import com.jopdesign.timing.jop.MicrocodeAnalysis.MicrocodeVerificationException; import com.jopdesign.tools.JopInstr; public class SingleCoreTiming extends JOPTimingTable { /** * Create a new timing table, reading the generated assembler file. * @param file the assembler file * @return the calculated timing table * @throws IOException if reading the assembler file failed */ public static SingleCoreTiming getTimingTable(File file) throws IOException { MicropathTable mpt = MicropathTable.getTimingTableFromAsmFile(file); SingleCoreTiming tt = new SingleCoreTiming(mpt); return tt; } public SingleCoreTiming(MicropathTable mpt) { super(mpt); timingTable = new TreeMap<Integer,Vector<MicropathTiming>>(); for(int i = 0; i < 256; i++) { if(mpt.hasTiming(i)) { try { this.timingTable.put(i, calculateTiming(i)); } catch(MicrocodeVerificationException ex) { this.analysisErrors.put(i, ex); } } } calculateHiddenCycles(); } private TreeMap<Integer, Vector<MicropathTiming>> timingTable; @Override public boolean hasTimingInfo(int opcode) { return this.timingTable.containsKey(opcode); } private long minCyclesHiddenOnInvoke = 0; private long minCyclesHiddenOnReturn = 0; @Override protected long getCycles(int opcode, boolean isHit, int words) { Vector<MicropathTiming> timing = this.getTiming(opcode); if(hasBytecodeLoad(timing) && ! JopInstr.isInJava(opcode) && ! InstructionInfo.isReturnOpcode(opcode) && ! InstructionInfo.isInvokeOpcode(opcode) && ! InstructionInfo.isAsyncHandlerOpcode(opcode)) { throw new AssertionError("Instruction " + JopInstr.OPCODE_NAMES[opcode] + " accesses the method cache, " + "but table JopInstr believes it is NOT implemented in Java. Check this " + "inconsistency manually!"); } else if(!hasBytecodeLoad(timing) && JopInstr.isInJava(opcode)) { throw new AssertionError("Instruction " + JopInstr.OPCODE_NAMES[opcode] + " does not access the method cache, " + "but table JopInstr believes it IS implemented in Java. Check this " + "inconsistency manually!"); } if(words < 0) { if(hasBytecodeLoad(timing)) { throw new AssertionError("Cannot calculate WCET of instruction accessing method cache"+ "without information on the size of the method"); } } return this.getCycles(timing, this.methodCacheAccessCycles(isHit, words)); } public Vector<MicropathTiming> getTiming(int opcode) { if(analysisErrors.containsKey(opcode)) { throw new AssertionError("Failed to analyse microcode timing: "+opcode); } if(! micropathTable.hasMicrocodeImpl(opcode)) { return this.timingTable.get(MicrocodeAnalysis.JOPSYS_NOIM); } else { return this.timingTable.get(opcode); } } private long getCycles(Vector<MicropathTiming> timing, long bytecodeDelay) { long maxCycles = 0; for(MicropathTiming mtiming : timing) { maxCycles = Math.max(maxCycles, mtiming.getCycles(readWaitStates, writeWaitStates, bytecodeDelay)); } return maxCycles; } /** * Method load time on invoke or return if there is a cache miss. * * @see ms thesis p 232 */ public long methodCacheAccessCycles(boolean hit, int words) { int b = -1; int c = readWaitStates > 0 ? (readWaitStates-1) : 0; if (hit) { b = 4; } else { b = 6 + (words+1) * (2+c); } return b; } public long methodCacheHiddenAccessCycles(boolean onInvoke) { return (onInvoke ? minCyclesHiddenOnInvoke : minCyclesHiddenOnReturn); } public long methodCacheHiddenAccessCycles(int opcode) { long hiddenAccess = Long.MAX_VALUE; for(MicropathTiming path : this.timingTable.get(opcode)) { if(! path.hasBytecodeLoad()) continue; hiddenAccess = Math.min(hiddenAccess,path.getHiddenBytecodeLoadCycles()); } return hiddenAccess; } public long javaImplBcDispatchCycles(int opcode, boolean isHit, int mWords) { if(this.hasTimingInfo(opcode)) { if(! hasBytecodeLoad(this.timingTable.get(opcode))) { throw new AssertionError(""+ JopInstr.OPCODE_NAMES[opcode]+ " is not a java implemented bytecode"); } return this.getCycles(opcode, isHit, mWords); } else { return this.getCycles(MicrocodeAnalysis.JOPSYS_NOIM, isHit, mWords); } } private void calculateHiddenCycles() { Set<Integer> returnOpcodes = new HashSet<Integer>(); long rhidden = Integer.MAX_VALUE; for(int rop : InstructionInfo.RETURN_OPCODES) { if(! hasTimingInfo(rop)) { System.err.println("No timing info for return opcode: "+rop); continue; } rhidden = Math.min(rhidden, this.methodCacheHiddenAccessCycles(rop)); returnOpcodes.add(rop); } this.minCyclesHiddenOnReturn = rhidden; /* now check all other opcodes with a bytecode load */ long ihidden = Integer.MAX_VALUE; for(int iop = 0; iop < 256; iop++) { if(returnOpcodes.contains(iop)) continue; if(! hasTimingInfo(iop)) continue; ihidden = Math.min(ihidden, methodCacheHiddenAccessCycles(iop)); } this.minCyclesHiddenOnInvoke = ihidden; } private Vector<MicropathTiming> calculateTiming(int opcode) throws MicrocodeVerificationException { Vector<MicropathTiming> timings = new Vector<MicropathTiming>(); for(MicrocodePath p : micropathTable.getMicroPaths(opcode)) { MicropathTiming mt = new MicropathTiming(p); timings.add(mt); } return timings; } public boolean hasBytecodeLoad(int i) { return hasBytecodeLoad(this.getTiming(i)); } /** * Print the microcode timing table from jvmgen.asm * @param argv */ public static void main(String argv[]) { String head = "JOP Timing Table on " + new Date(); System.out.println(head); System.out.println(ConsoleTable.getSepLine('=',head.length())); System.out.println(); System.out.println(" Loading " + MicrocodeAnalysis.DEFAULT_ASM_FILE); System.out.println(" Before generating the timing table do not forget to run e.g."); System.out.println(" > make gen_mem -e ASM_SRC=jvm JVM_TYPE=USB\n"); SingleCoreTiming tt = null; try { tt = SingleCoreTiming.getTimingTable(MicrocodeAnalysis.DEFAULT_ASM_FILE); } catch (IOException e) { e.printStackTrace(); System.exit(1); } // build table ConsoleTable table = dumpTimingTable(tt); System.out.println(table.render()); /* for now, check if we agree with WCETInstruction */ checkWithWCETInstruction(tt); } private static ConsoleTable dumpTimingTable(SingleCoreTiming tt) { ConsoleTable table = new ConsoleTable(); table.addColumn("opcode", Alignment.ALIGN_RIGHT) .addColumn("name", Alignment.ALIGN_LEFT) .addColumn("timing path", Alignment.ALIGN_LEFT) .addColumn("(1,2,H)",Alignment.ALIGN_RIGHT) .addColumn("(1,2,32)",Alignment.ALIGN_RIGHT) .addColumn("(3,5,H)",Alignment.ALIGN_RIGHT) .addColumn("(3,5,32)",Alignment.ALIGN_RIGHT); for(int i = 0; i < 256; i++) { int opcode = i; if(JopInstr.isReserved(opcode)) continue; TableRow row = table.addRow(); row.addCell(opcode) .addCell(JopInstr.OPCODE_NAMES[i]); if(tt.getAnalysisError(i) != null) { row.addCell("... FAILED: "+tt.getAnalysisError(i).getMessage()+" ...",5,Alignment.ALIGN_LEFT); } else if (! tt.isImplemented(i)) { row.addCell("... no microcode implementation ...",5,Alignment.ALIGN_LEFT); } else { String timingPath = MicropathTiming.toString(tt.getTiming(opcode)); tt.configureWaitStates(1, 2); long exampleTiming1 = tt.getCycles(opcode, true, 0); long exampleTiming2 = tt.getCycles(opcode, false, 32); tt.configureWaitStates(3, 5); long exampleTiming3 = tt.getCycles(opcode, true, 0); long exampleTiming4 = tt.getCycles(opcode, false, 32); row.addCell(timingPath) .addCell(exampleTiming1) .addCell(exampleTiming2) .addCell(exampleTiming3) .addCell(exampleTiming4); } } table.addLegendTop(" (x,y,z) ~ (read delay, write delay, bytecode access)"); table.addLegendTop(" z = H ... cache hit, n ... load n words into cache"); table.addLegendTop(" infeasible branches: "+Arrays.toString(MicrocodeAnalysis.INFEASIBLE_BRANCHES)); table.addLegendBottom(" [expr] denotes max(0,expr)"); table.addLegendBottom(" r = read delay, w = write delay, b = bytecode load delay"); table.addLegendBottom(String.format(" hidden cycles on invoke (including JavaImplBCs) and return: %d / %d", tt.minCyclesHiddenOnInvoke,tt.minCyclesHiddenOnReturn)); return table; } private static void checkWithWCETInstruction(SingleCoreTiming tt) { // demo config (DE2/70) tt.configureWaitStates(3,5); WCETInstruction wcetInst = new WCETInstruction(3,5); // test set boolean[] testHit = { true, true, false, false, false }; int[] testLoad = { 1, 128, 1, 11, 57 }; Map<Integer,String> failures = new TreeMap<Integer,String>(); outer: for(int i = 0; i < 256; i++) { long wiTime0 = wcetInst.getCycles(i, false, 0); if(JopInstr.isReserved(i)) { if(wiTime0 >= 0) { failures.put(i,"WCETInstruction has timing: "+wiTime0+", but JopInstr says this is a RESERVED INSTRUCTION"); } continue; } if(! tt.isImplemented(i)) { if(wiTime0 >= 0) { failures.put(i,"WCETInstruction has timing: "+wiTime0+", but there is NO MICROCODE IMPLEMENTATION, "); } continue; } if(tt.getAnalysisError(i) != null) { if(wiTime0 >= 0) { failures.put(i, "WCETInstruction has timing: "+wiTime0+", but we FAILED TO ANALYSE the microcode"); } continue; } String tString = MicropathTiming.toString(tt.getTiming(i)); if(wiTime0 < 0) { boolean javaImplemented = false; for(MicropathTiming mt : tt.getTiming(i)) { if(mt.hasBytecodeLoad()) javaImplemented = true; } if(! javaImplemented) { failures.put(i,"WCETInstruction HAS NO TIMING information, but we have: "+tString); } continue; } for(int j = 0; j < testHit.length; j++) { long wiT = wcetInst.getCycles(i, ! testHit[j], testLoad[j]); long wiMA = tt.getCycles(i, testHit[j],testLoad[j]); if(wiT != wiMA) { failures.put(i,"WCETInstruction has DIFFERENT TIMING INFO "+ "(hit = " + testHit[j] + ", load = " + testLoad[j] + "): " + "microcodeanalysis := " + wiMA + " /= " + wiT + " =: wcetinstruction" + " ("+ tt.getTiming(i) + ")"); continue outer; } } } for(Entry<Integer, String> fail : failures.entrySet()) { int opc = fail.getKey(); System.err.println("["+opc+"] " + JopInstr.OPCODE_NAMES[opc]+" : "+fail.getValue()); } } }