/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2009, Benedikt Huber (benedikt.huber@gmail.com)
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
(at your option) 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. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.timing.jop;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import com.jopdesign.timing.jop.MicrocodeAnalysis.MicrocodeVerificationException;
import com.jopdesign.timing.jop.MicrocodePath.PathEntry;
import com.jopdesign.tools.Instruction;
/** Timing information for one microcode path of the form<br/>
* {@code sum(exprs) + max(0, b - hidden) } <br/>
*
* Modelsim reports (25.11.09, r=1, w=2):
* stgs: stgs | nop | wait (3) = wait(2+r) , mit read@wait[0]
* stps: stps | nop | wait (4) = wait(2+w) , mit write@wait[0]
* stgf: stgf | nop | wait (7) = wait(5+ 2r), mit read@wait[0] und read@wait[4]
* stpf: stpf | nop | wait (8) = wait(5+r+w), mit read@wait[0] und write@wait[4]
*/
public class MicropathTiming {
/** Timing expression: {@code Math.max(0, C + x r + y w - hidden)} */
public static class TimingExpression {
private int constCycles;
private int reads;
private int writes;
private int hidden;
public static TimingExpression constTime(int constCycles) {
return new TimingExpression(constCycles,0,0);
}
public static TimingExpression read(int hidden)
throws MicrocodeVerificationException {
return new TimingExpression(0,1,0,hidden);
}
public static TimingExpression write(int hidden)
throws MicrocodeVerificationException {
return new TimingExpression(0,0,1,hidden);
}
public TimingExpression(int constCycles, int reads, int writes) {
this.constCycles = constCycles;
this.reads = reads;
this.writes = writes;
this.hidden = 0;
}
public TimingExpression(int constCycles, int reads, int writes, int hidden)
throws MicrocodeVerificationException {
this(constCycles,reads,writes);
if(hidden < 0) {
throw new MicrocodeVerificationException("Too few wait states ?");
}
this.hidden = hidden;
}
public int eval(int r, int w) {
int cycles = constCycles + reads * r + writes * w - hidden;
return (cycles > 0 ? cycles : 0);
}
public String toString() {
Vector<String> sb = new Vector<String>();
// System.out.println(String.format("consts: %d, r: %d, w: %d, hidden: %d",constCycles,reads,writes,hidden));
int consts = this.constCycles - hidden;
if(reads > 0) sb.add(product(reads,"r"));
if(writes > 0) sb.add(product(writes,"w"));
if(sb.isEmpty())
{
if(consts <= 0) throw new AssertionError("0 cost instruction");
return ""+consts;
}
StringBuffer s = MicropathTiming.concat(" + ",sb);
if(consts < 0) {
String expr = String.format("[%s - %d]",s,-consts);
return expr;
} else if(consts > 0) {
return String.format("%d + %s",consts,s);
} else {
return s.toString();
}
}
private static String product(int count, String name) {
if(count == 0) return "";
else if(count == 1) return name;
else if(count == -1) return "-"+name;
else return count + " " + name;
}
}
private List<TimingExpression> timing = new Vector<TimingExpression>();
private int bcAccessHidden = -1;
/** Calculate timing info for the given path */
public MicropathTiming(MicrocodePath p) throws MicrocodeVerificationException {
computeTiming(p);
compress();
}
public boolean hasBytecodeLoad() {
return this.bcAccessHidden > 0;
}
public long getHiddenBytecodeLoadCycles() {
return this.bcAccessHidden;
}
private void computeTiming(MicrocodePath p) throws MicrocodeVerificationException {
int start = -1;
int constantCycles = 0;
boolean wasWait = false;
int accessKind = -1;
Integer accessAddress = null;
for(PathEntry l : p.getPath()) {
Instruction i = l.getInstruction();
switch(i.opcode) {
case MicrocodeConstants.STMWA:
accessAddress = l.getTOS(); /* Deal with OP addresses here */
break;
case MicrocodeConstants.STMRA:
case MicrocodeConstants.STMRAC:
case MicrocodeConstants.STMRAF:
accessAddress = l.getTOS();
/* fallthrough */
case MicrocodeConstants.STMWD:
case MicrocodeConstants.STMWDF:
case MicrocodeConstants.STBCRD:
case MicrocodeConstants.STGF:
case MicrocodeConstants.STPF:
case MicrocodeConstants.STALD:
case MicrocodeConstants.STCP:
case MicrocodeConstants.STAST:
case MicrocodeConstants.STGS:
case MicrocodeConstants.STPS:
if(accessKind >= 0) {
throw new MicrocodeVerificationException("No wait after memory access (unsafe) !");
}
start = constantCycles;
accessKind = i.opcode;
wasWait = false;
break;
case MicrocodeConstants.WAIT:
if(wasWait && accessKind > 0) { // unhandled wait
int passed = constantCycles - start;
/* We currently have some possibly redundant wait s in null_pointer: */
// if(accessKind < 0) {
// throw new MicrocodeVerificationException("Wait without memory access (or 3 waits in a row) ?: "+p.getPath());
// }
switch(accessKind) {
case MicrocodeConstants.STMRA:
if(accessAddress != null) {
throw new MicrocodeVerificationException("Timing for IO access not yet implemented[r]: "+accessAddress);
}
timing.add(TimingExpression.read(passed - 2));
break;
case MicrocodeConstants.STMWD:
if(accessAddress != null && accessAddress < 0) {
// no wait states
} else {
timing.add(TimingExpression.write(passed - 2));
}
break;
case MicrocodeConstants.STBCRD:
if(bcAccessHidden >= 0) {
throw new MicrocodeVerificationException("More than one bytecode read in a microcode sequence");
}
bcAccessHidden = passed - 2;
break;
case MicrocodeConstants.STGS:
/* wait(2+r) */
timing.add(new TimingExpression(2,1,0,passed-1));
break;
case MicrocodeConstants.STPS:
/* wait(2+w) */
timing.add(new TimingExpression(2,0,1,passed-1));
break;
case MicrocodeConstants.STMRAC:
timing.add(TimingExpression.read(passed - 2));
break;
case MicrocodeConstants.STMRAF:
timing.add(TimingExpression.read(passed - 2));
break;
case MicrocodeConstants.STMWDF:
/* wait(2+w) */
timing.add(new TimingExpression(2,0,1,passed-1));
break;
case MicrocodeConstants.STGF:
/* wait(5+2r) */
// TODO: MS don't know if this is ok after
// changes by WP and changes back by MS?
timing.add(new TimingExpression(5,2,0,passed-1));
break;
case MicrocodeConstants.STPF:
/* wait (6+r+w) */
timing.add(new TimingExpression(6,1,1,passed-1));
break;
case MicrocodeConstants.STALD:
/* wait (TODO) */
timing.add(new TimingExpression(1,3,0,passed - 3));
break;
case MicrocodeConstants.STAST:
/* wait (TODO) */
timing.add(new TimingExpression(4,2,1,passed - 4));
break;
case MicrocodeConstants.STCP:
throw new MicrocodeVerificationException("stcp not yet supported: "+accessKind);
default:
throw new MicrocodeVerificationException("Unsupport kind of memory access: "+accessKind);
}
accessKind = start = -1;
} else {
wasWait = true;
}
break;
default: break;
}
constantCycles++;
}
timing.add(TimingExpression.constTime(constantCycles));
}
/* compact all timing expressions without hidden cycles */
private void compress() {
int constCycles = 0, reads = 0, writes = 0;
ArrayList<TimingExpression> timingNew = new ArrayList<TimingExpression>();
for(TimingExpression te : this.timing) {
if(te.hidden == 0) {
constCycles += te.constCycles;
reads += te.reads;
writes += te.writes;
} else {
timingNew.add(te);
}
}
if(writes > 0 || reads > 0 | constCycles > 0) {
timingNew.add(0,new TimingExpression(constCycles,reads,writes));
}
timing = timingNew;
}
public int getCycles(int readDelay, int writeDelay, long bytecodeDelay) {
int r = 0;
for(TimingExpression te : timing) {
r += te.eval(readDelay, writeDelay);
}
if(bcAccessHidden > 0) r += Math.max(0, bytecodeDelay - bcAccessHidden);
return r;
}
/* Abbrev. form: [expr] denotes max(0,expr) */
public String toString() {
Vector<String> timingExprs = new Vector<String>();
// FIXME: Fold constant factors
for(TimingExpression x : timing) {
timingExprs.add(x.toString());
}
StringBuffer s = concat(" + ",timingExprs);
if(this.hasBytecodeLoad()) {
s.append(" + [b - "+bcAccessHidden+"]");
}
return s.toString();
}
/** String representation of several path timing informations.<br/>
* {@code [expr]} denotes {@code max(0,expr)}
* @param timings
* @return
*/
public static String toString(Vector<MicropathTiming> timings) {
Vector<String> strs = new Vector<String>();
for(MicropathTiming timing : timings) {
strs.add(timing.toString());
}
return concat(" <|> ",strs).toString();
}
/* helper, FIXME: move to util lib */
static StringBuffer concat(String sep, List<? extends Object> list) {
boolean first = true;
StringBuffer buf = new StringBuffer();
for(Object s : list) {
String str = s.toString();
if(str.length() == 0) continue;
if(first) { buf.append(str); first = false; }
else { buf.append(sep); buf.append(str); }
}
return buf;
}
}