/* This file is part of JOP, the Java Optimized Processor see <http://www.jopdesign.com/> Copyright (C) 2008, 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.wcet.uppaal.translator; import com.jopdesign.common.MethodInfo; import com.jopdesign.common.misc.MiscUtils; import com.jopdesign.common.processormodel.JOPConfig.CacheImplementation; import com.jopdesign.wcet.WCETTool; import com.jopdesign.wcet.jop.BlockCache; import com.jopdesign.wcet.jop.MethodCache; import com.jopdesign.wcet.jop.VarBlockCache; import com.jopdesign.wcet.uppaal.UppAalConfig; import com.jopdesign.wcet.uppaal.UppAalConfig.UppaalCacheApproximation; import com.jopdesign.wcet.uppaal.model.DuplicateKeyException; import com.jopdesign.wcet.uppaal.model.NTASystem; import com.jopdesign.wcet.uppaal.model.Template; import com.jopdesign.wcet.uppaal.model.XmlSerializationException; import com.jopdesign.wcet.uppaal.translator.cache.CacheSimBuilder; import com.jopdesign.wcet.uppaal.translator.cache.FIFOCacheBuilder; import com.jopdesign.wcet.uppaal.translator.cache.FIFOVarBlockCacheBuilder; import com.jopdesign.wcet.uppaal.translator.cache.LRUCacheBuilder; import com.jopdesign.wcet.uppaal.translator.cache.LRUVarBlockCacheBuilder; import com.jopdesign.wcet.uppaal.translator.cache.StaticCacheBuilder; import org.w3c.dom.Document; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import java.util.Vector; /** * Builder for the system modeling the java program. * Support the following features / modeling elements * <ul> <li/> global, elapsed time * <li/> per-process clocks * <li/> method synchronization channels * <li/> call stacks * </ul> */ public class SystemBuilder { public static final String CLOCK = "t"; public static final String BB_CLOCK = "t_local"; public static String bbClock(int process) { return BB_CLOCK+"_"+process; } public static final String INVOKE_CHAN = "invoke"; public static final String RETURN_CHAN = "ret"; public static final String MAX_CALL_STACK_DEPTH = "max_csd"; public static final String NUM_METHODS = "num_methods"; public static final String ACTIVE_METHOD = "active_method"; public static final String CURRENT_CALL_STACK_DEPTH = "cur_csd"; private NTASystem system; public NTASystem getNTASystem() { return system; } private HashMap<Template,Integer> templates = new HashMap<Template,Integer>(); private HashMap<Template,Integer> priorities = new HashMap<Template,Integer>(); private HashMap<MethodInfo,Integer> methodId = new HashMap<MethodInfo, Integer>(); private CacheSimBuilder cacheSim; private WCETTool project; private UppAalConfig config; private int numMethods; private int maxCallStackDepth; private Vector<String> progressMeasures = new Vector<String>(); /** * Create a new top-level UPPAAL System builder * @param config configuration * @param p uplink to Project * @param maxCallStackDepth the maximal call stack depth * @param methods the methods of the program */ public SystemBuilder(UppAalConfig config, WCETTool p, int maxCallStackDepth, List<MethodInfo> methods) { this.config = config; this.project = p; this.system = new NTASystem(p.getProjectName()); this.numMethods = methods.size(); for(int i = 0; i < numMethods; i++) { methodId.put(methods.get(i), i); } this.cacheSim = getCacheSimulation(); this.maxCallStackDepth = maxCallStackDepth; system.appendDeclaration("clock " + CLOCK +";"); system.appendDeclaration("const int " + MAX_CALL_STACK_DEPTH + " = "+maxCallStackDepth+";"); system.appendDeclaration("const int " + NUM_METHODS + " = "+numMethods+";"); cacheSim.appendDeclarations(system,NUM_METHODS); } private CacheSimBuilder getCacheSimulation() { MethodCache cache = project.getWCETProcessorModel().getMethodCache(); UppaalCacheApproximation cacheApprox = config.getCacheApproximation(); CacheSimBuilder sim; if(cache.getName() == CacheImplementation.NO_METHOD_CACHE) { sim = new StaticCacheBuilder(false); } else if(cacheApprox == UppaalCacheApproximation.ALWAYS_MISS) { sim = new StaticCacheBuilder(true); } else { switch(cache.getName()) { case LRU_CACHE: sim = new LRUCacheBuilder((BlockCache)cache); break; case FIFO_CACHE: sim = new FIFOCacheBuilder((BlockCache)cache, config.emptyInitialCache); break; case LRU_VARBLOCK_CACHE: sim = new LRUVarBlockCacheBuilder(project, (VarBlockCache)cache, this.methodId.keySet()); break; case FIFO_VARBLOCK_CACHE: sim = new FIFOVarBlockCacheBuilder(project, (VarBlockCache)cache, this.methodId.keySet(), config.emptyInitialCache); break; default: throw new AssertionError("Unsupport cache implementation: "+cache.getName()); } } return sim; } public WCETTool getProject() { return project; } public CacheSimBuilder getCacheSim() { return this.cacheSim; } public int getMethodId(MethodInfo implementedMethod) { return this.methodId.get(implementedMethod); } public String accessCache(MethodInfo m) { return "access_cache("+getMethodId(m)+")"; } public String addProcessClock(int proc) { String cl = bbClock(proc); system.appendDeclaration(String.format("clock %s; ", cl)); return cl; } /* Method channel support * ---------------------- */ public void addMethodSynchChannels(List<MethodInfo> mis, Map<MethodInfo,Integer> mids) { for(MethodInfo mi : mis) { system.appendDeclaration("chan "+methodChannel(mids.get(mi))+";"); } } public static String methodChannel(int i) { return "invoke_"+i; } /* call stack support * ----------------- */ public void addCallStack(MethodInfo rootMethod, int numCallSites) { int rootId = this.getMethodId(rootMethod); List<String> initArray = new LinkedList<String>(); initArray.add(""+rootId); for(int i = 1; i < maxCallStackDepth; i++) { initArray.add(""+rootId); } system.appendDeclaration(String.format("int[0,%d] callStack[%s] = %s;", numCallSites, MAX_CALL_STACK_DEPTH, constArray(initArray).toString())); system.appendDeclaration( "void pushCallStack(int id) {\n" + " int i;\n" + " for(i = "+MAX_CALL_STACK_DEPTH+"-1;i > 0; --i) {\n"+ " callStack[i] = callStack[i-1];\n"+ " }\n"+ " callStack[0] = id;\n" + "}"); system.appendDeclaration( "bool matchCallStack(int id) {\n" + " return (callStack[0] == id);\n" + "}\n"); system.appendDeclaration( "void popCallStack() {\n" + " int i;\n" + " for(i = 0; i < "+MAX_CALL_STACK_DEPTH+"-1;i++) {\n"+ " callStack[i] = callStack[i+1];\n"+ " }\n"+ "}"); } public String pushCallStack(String id) { return String.format("pushCallStack(%s)",id); } public String matchCallStack(String id) { return String.format("callStack[0] == %s",id); } public String popCallStack() { return "popCallStack()"; } /** Build the final system */ public void buildSystem() { StringBuilder sys = new StringBuilder(); /* Create processes */ for(Entry<Template,Integer> tmpl : templates.entrySet()) { sys.append(String.format("M%s = %s() ;\n", tmpl.getValue(), tmpl.getKey().getId())); } /* Instantiate processes */ sys.append("system "); /* bucket sort templates by priority */ TreeMap<Integer, List<Template>> templatesByPrio = MiscUtils.partialSort(templates.keySet(), new MiscUtils.F1<Template, Integer>() { public Integer apply(Template t) { return priorities.get(t); } } ); /* create system declaration strings */ List<String> systemEntry = new LinkedList<String>(); for(List<Template> entry : templatesByPrio.values()) { List<String> proclist = new LinkedList<String>(); for(Template t : entry) { proclist.add("M"+templates.get(t)); } systemEntry.add(MiscUtils.joinStrings(proclist, ", ")); } sys.append(MiscUtils.joinStrings(systemEntry, " < ")); sys.append(";\n"); if(this.progressMeasures.size() > 0) { sys.append("progress { "); for(String pm : progressMeasures) { sys.append(" ");sys.append(pm);sys.append(";\n"); } sys.append("}"); } this.system.setSystem(sys.toString()); } /** add a template with the given id and priority */ public void addTemplate(int procid, int priority, Template templ) throws DuplicateKeyException { this.templates.put(templ,procid); this.priorities.put(templ,priority); this.system.addTemplate(templ); } public Document toXML() throws XmlSerializationException { return this.system.toXML(); } public static StringBuilder constArray(List<?> elems) { StringBuilder sb = new StringBuilder("{ "); boolean first = true; for(Object o : elems) { if(first) first = false; else sb.append(", "); sb.append(o); } sb.append(" }"); return sb; } public UppAalConfig getConfig() { return this.config; } public void addProgressMeasure(String progressMeasure) { this.progressMeasures .add(progressMeasure); } public void declareVariable(String ty, String var, String init) { this.system.appendDeclaration(ty+" "+var+" := "+init+";"); } } /* -- Using global-local clock * com.jopdesign.build.MethodInfo@cdbc83"wcet.StartLift.measure()V" * wcet: 9797 * complex: 98 * searchT: 4.58302 * solverTmax: 0.265957 * -- Using per method-local clock * com.jopdesign.build.MethodInfo@cdbc83"wcet.StartLift.measure()V" * wcet: 9797 * complex: 98 * searchT: 7.969566 * solverTmax: 0.491255 */