org.pshdl.model.simulation.HDLSimulator.java Source code

Java tutorial

Introduction

Here is the source code for org.pshdl.model.simulation.HDLSimulator.java

Source

/*******************************************************************************
 * PSHDL is a library and (trans-)compiler for PSHDL input. It generates
 *     output suitable for implementation or simulation of it.
 *
 *     Copyright (C) 2013 Karsten Becker (feedback (at) pshdl (dot) org)
 *
 *     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/>.
 *
 *     This License does not grant permission to use the trade names, trademarks,
 *     service marks, or product names of the Licensor, except as required for
 *     reasonable and customary use in describing the origin of the Work.
 *
 * Contributors:
 *     Karsten Becker - initial API and implementation
 ******************************************************************************/
package org.pshdl.model.simulation;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedSet;
import java.util.concurrent.atomic.AtomicInteger;

import org.pshdl.model.HDLAnnotation;
import org.pshdl.model.HDLArrayInit;
import org.pshdl.model.HDLAssignment;
import org.pshdl.model.HDLAssignment.HDLAssignmentType;
import org.pshdl.model.HDLBlock;
import org.pshdl.model.HDLDeclaration;
import org.pshdl.model.HDLEqualityOp;
import org.pshdl.model.HDLEqualityOp.HDLEqualityOpType;
import org.pshdl.model.HDLExpression;
import org.pshdl.model.HDLForLoop;
import org.pshdl.model.HDLFunction;
import org.pshdl.model.HDLFunctionCall;
import org.pshdl.model.HDLIfStatement;
import org.pshdl.model.HDLIfStatement.TreeSide;
import org.pshdl.model.HDLInterfaceInstantiation;
import org.pshdl.model.HDLLiteral;
import org.pshdl.model.HDLManip;
import org.pshdl.model.HDLManip.HDLManipType;
import org.pshdl.model.HDLPackage;
import org.pshdl.model.HDLPrimitive;
import org.pshdl.model.HDLRange;
import org.pshdl.model.HDLReference;
import org.pshdl.model.HDLResolvedRef;
import org.pshdl.model.HDLStatement;
import org.pshdl.model.HDLSwitchCaseStatement;
import org.pshdl.model.HDLSwitchStatement;
import org.pshdl.model.HDLTernary;
import org.pshdl.model.HDLType;
import org.pshdl.model.HDLUnit;
import org.pshdl.model.HDLVariable;
import org.pshdl.model.HDLVariableDeclaration;
import org.pshdl.model.HDLVariableDeclaration.HDLDirection;
import org.pshdl.model.HDLVariableRef;
import org.pshdl.model.IHDLObject;
import org.pshdl.model.evaluation.ConstantEvaluate;
import org.pshdl.model.evaluation.HDLEvaluationContext;
import org.pshdl.model.extensions.FullNameExtension;
import org.pshdl.model.extensions.RangeExtension;
import org.pshdl.model.extensions.TypeExtension;
import org.pshdl.model.simulation.RangeTool.RangeVal;
import org.pshdl.model.utils.HDLLibrary;
import org.pshdl.model.utils.HDLQualifiedName;
import org.pshdl.model.utils.HDLQuery;
import org.pshdl.model.utils.Insulin;
import org.pshdl.model.utils.ModificationSet;
import org.pshdl.model.utils.Refactoring;

import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Range;

public class HDLSimulator {

    public static HDLUnit createSimulationModel(HDLUnit unit, HDLEvaluationContext context, String src,
            char separator) {
        if ((unit.getSimulation() != null) && unit.getSimulation())
            return createTestbenchModel(unit, context, src, separator);
        return createRegularSimModel(unit, context, src, separator);
    }

    private static HDLUnit createRegularSimModel(HDLUnit unit, HDLEvaluationContext context, String src,
            char separator) {
        HDLUnit insulin = Insulin.transform(unit, src);
        final HDLPackage pkg = flattenAll(context, insulin, separator);
        insulin = getSingleUnit(pkg);
        insulin = convertArrayInits(context, insulin);
        insulin = unrollForLoops(context, insulin);
        insulin = createBitRanges(context, insulin);
        insulin = literalBitRanges(context, insulin);
        insulin = convertBooleanLiterals(context, insulin);
        insulin = convertTernary(context, insulin);
        insulin = removeDoubleAssignments(context, insulin);
        insulin = removeDeadLeaves(context, insulin);
        insulin.validateAllFields(insulin.getContainer(), true);
        return insulin;
    }

    public static final HDLAnnotation TB_VAR = new HDLAnnotation().setName("@TestbenchVar");
    public static final HDLAnnotation TB_UNIT = new HDLAnnotation().setName("@Testbench");

    /**
     * General outline:
     *
     * <ul>
     * <li>Create a global variable $time
     * <li>Foreach process create 2 variables: process_state, process_time
     * <li>Split process into state machine
     * </ul>
     *
     * Execution then by:
     * <ul>
     * <li>Run all processes methods until $process_time > $time or
     * $process_state < 0 (is blocked)
     * <li>Run logic
     * </ul>
     *
     */
    private static HDLUnit createTestbenchModel(HDLUnit unit, HDLEvaluationContext context, String src,
            char separator) {
        HDLUnit insulin = Insulin.transform(unit, src);
        insulin = addTimeVar(insulin);
        insulin = rewriteProcesses(insulin);
        insulin = annotateVariableDeclarations(insulin);
        insulin = insulin.addAnnotations(TB_UNIT).copyDeepFrozen(insulin.getContainer());
        insulin.validateAllFields(insulin.getContainer(), true);
        return createRegularSimModel(insulin, context, src, separator);
    }

    private static HDLUnit annotateVariableDeclarations(HDLUnit insulin) {
        final ModificationSet ms = new ModificationSet();
        final HDLVariableDeclaration[] hvds = insulin.getAllObjectsOf(HDLVariableDeclaration.class, true);
        for (final HDLVariableDeclaration hdlVariableDeclaration : hvds) {
            if (hdlVariableDeclaration.getAnnotation(TB_VAR.getName()) == null) {
                ms.replace(hdlVariableDeclaration, hdlVariableDeclaration.addAnnotations(TB_VAR));
            }
        }
        return ms.apply(insulin);
    }

    private static HDLUnit rewriteProcesses(HDLUnit insulin) {
        final ModificationSet ms = new ModificationSet();
        final Collection<HDLBlock> processes = HDLQuery.select(HDLBlock.class).from(insulin)
                .where(HDLBlock.fProcess).isEqualTo(Boolean.TRUE).getAll();
        for (final HDLBlock process : processes) {
            final HDLQualifiedName fqn = FullNameExtension.fullNameOf(process);
            HDLVariableDeclaration processNextTimeDecl = new HDLVariableDeclaration()
                    .setType(HDLPrimitive.getUint().setWidth(HDLLiteral.get(64)));
            final HDLVariable processNexTimeVar = new HDLVariable()
                    .setName("$process_time_next_" + fqn.getLastSegment()).setDefaultValue(HDLLiteral.get(0));
            processNextTimeDecl = processNextTimeDecl.addVariables(processNexTimeVar);
            HDLVariableDeclaration processStateDecl = new HDLVariableDeclaration()
                    .setType(HDLPrimitive.getInteger());
            final HDLVariable processStateVar = new HDLVariable().setName("$process_state_" + fqn.getLastSegment())
                    .setDefaultValue(HDLLiteral.get(0));
            processStateDecl = processStateDecl.addVariables(processStateVar);
            ms.addTo(insulin, HDLUnit.fInits, processNextTimeDecl, processStateDecl);
            final Map<Integer, List<HDLStatement>> cases = Maps.newLinkedHashMap();
            int currentCase = 0;
            cases.put(currentCase, Lists.<HDLStatement>newArrayList());
            for (final HDLStatement stmnt : process.getStatements()) {
                currentCase = processStatement(cases, stmnt, currentCase, processStateVar, processNexTimeVar, ms);
            }
            cases.get(currentCase)
                    .add(new HDLAssignment().setLeft(processStateVar.asHDLRef()).setRight(HDLLiteral.get(0)));
            final HDLSwitchStatement processSwitch = new HDLSwitchStatement()
                    .setCaseExp(processStateVar.asHDLRef());
            final List<HDLSwitchCaseStatement> caseList = Lists.newArrayList();
            for (final Entry<Integer, List<HDLStatement>> e : cases.entrySet()) {
                final HDLSwitchCaseStatement caseStatement = new HDLSwitchCaseStatement()
                        .setLabel(HDLLiteral.get(e.getKey())).setDos(e.getValue());
                caseList.add(caseStatement);
            }
            final HDLSwitchStatement finalSwitch = processSwitch.setCases(caseList);
            ms.replace(process, new HDLBlock().setProcess(true).addStatements(finalSwitch));
        }
        return ms.apply(insulin);
    }

    private static int processStatement(Map<Integer, List<HDLStatement>> cases, HDLStatement stmnt, int currentCase,
            HDLVariable processStateVar, HDLVariable processNexTimeVar, ModificationSet ms) {
        final List<HDLStatement> currentCaseList = cases.get(currentCase);
        switch (stmnt.getClassType()) {
        case HDLAssignment:
            currentCaseList.add(stmnt);
            break;
        case HDLFunctionCall:
            final HDLFunctionCall hfc = (HDLFunctionCall) stmnt;
            final Optional<HDLFunction> func = hfc.resolveFunction();
            if (func.isPresent()) {
                final HDLQualifiedName fqn = FullNameExtension.fullNameOf(func.get());
                final ArrayList<HDLExpression> params = hfc.getParams();
                if (fqn.equals(HDLQualifiedName.create("pshdl", "waitFor"))) {
                    final HDLExpression amount = params.get(0);
                    currentCase = waitFor(cases, currentCase, processStateVar, processNexTimeVar, amount);
                }
                if (fqn.equals(HDLQualifiedName.create("pshdl", "waitUntil"))) {
                    final int waitCase = currentCase = newCase(cases, currentCase);
                    insertJump(currentCaseList, processStateVar, waitCase);
                    final List<HDLStatement> list = cases.get(waitCase);
                    currentCase = newCase(cases, currentCase);
                    final HDLTernary tern = new HDLTernary().setIfExpr(params.get(0))//
                            .setThenExpr(toInteger(currentCase))//
                            .setElseExpr(toInteger(-(waitCase + 1)));
                    list.add(new HDLAssignment().setLeft(processStateVar.asHDLRef()).setRight(tern));
                }
                if (fqn.equals(HDLQualifiedName.create("pshdl", "wait"))) {
                    // Create a new case so that the goto 0 case has some place
                    // to go
                    currentCase = newCase(cases, currentCase);
                    insertJump(currentCaseList, processStateVar, Integer.MAX_VALUE);
                }
                if (fqn.equals(HDLQualifiedName.create("pshdl", "pulse"))) {
                    final HDLReference var = (HDLReference) params.get(0);
                    final HDLExpression amount = params.get(1);
                    final HDLAssignment setZero = new HDLAssignment().setLeft(var).setRight(HDLLiteral.get(0));
                    processStatement(cases, setZero, currentCase, processStateVar, processNexTimeVar, ms);
                    currentCase = waitFor(cases, currentCase, processStateVar, processNexTimeVar, amount);
                    final HDLAssignment setOne = new HDLAssignment().setLeft(var).setRight(HDLLiteral.get(1));
                    processStatement(cases, setOne, currentCase, processStateVar, processNexTimeVar, ms);
                    currentCase = waitFor(cases, currentCase, processStateVar, processNexTimeVar, amount);
                }

            }
            break;
        case HDLIfStatement: {
            final HDLIfStatement ifStmnt = (HDLIfStatement) stmnt;
            final int thenCase = currentCase = newCase(cases, currentCase);
            for (final HDLStatement hdlStatement : ifStmnt.getThenDo()) {
                currentCase = processStatement(cases, hdlStatement, currentCase, processStateVar, processNexTimeVar,
                        ms);
            }
            final int thenExitCase = currentCase;
            final int elseCase = currentCase = newCase(cases, currentCase);
            for (final HDLStatement hdlStatement : ifStmnt.getElseDo()) {
                currentCase = processStatement(cases, hdlStatement, currentCase, processStateVar, processNexTimeVar,
                        ms);
            }
            final int elseExitCase = currentCase;
            final int targetCase = currentCase = newCase(cases, currentCase);
            final HDLTernary tern = new HDLTernary().setIfExpr(ifStmnt.getIfExp())//
                    .setThenExpr(HDLLiteral.get(thenCase)) //
                    .setElseExpr(HDLLiteral.get(elseCase));
            final HDLAssignment switchJump = new HDLAssignment().setLeft(processStateVar.asHDLRef()).setRight(tern);
            currentCaseList.add(switchJump);
            insertJump(cases.get(thenExitCase), processStateVar, targetCase);
            insertJump(cases.get(elseExitCase), processStateVar, targetCase);
            break;
        }
        case HDLForLoop: {
            final HDLForLoop loop = (HDLForLoop) stmnt;
            final String fqn = FullNameExtension.fullNameOf(loop.getParam()).toString('_');
            final HDLVariable loopVar = loop.getParam().setName(fqn);
            final HDLRange range = loop.getRange().get(0);
            new HDLAssignment().setLeft(loopVar.asHDLRef()).setRight(range.getFrom());
            int condCase;
            if (currentCaseList.isEmpty()) {
                condCase = currentCase;
            } else {
                condCase = currentCase = newCase(cases, currentCase);
                insertJump(currentCaseList, processStateVar, condCase);
            }
            HDLVariableDeclaration idxDecl = new HDLVariableDeclaration().setType(HDLPrimitive.getNatural());
            final HDLVariable idxVar = new HDLVariable().setName(fqn);
            idxDecl = idxDecl.addVariables(idxVar);
            ms.addTo(loop.getContainer(HDLUnit.class), HDLUnit.fInits, idxDecl);
            final HDLForLoop newLoop = Refactoring.renameVariable(loop.getParam(), fqn, loop);
            final int execCase = currentCase = newCase(cases, currentCase);
            for (final HDLStatement doStatement : newLoop.getDos()) {
                currentCase = processStatement(cases, doStatement, currentCase, processStateVar, processNexTimeVar,
                        ms);
            }
            final List<HDLStatement> caseList = cases.get(currentCase);
            caseList.add(new HDLAssignment().setLeft(idxVar.asHDLRef()).setType(HDLAssignmentType.ADD_ASSGN)
                    .setRight(HDLLiteral.get(1)));
            insertJump(caseList, processStateVar, condCase);
            final int exitCase = currentCase = newCase(cases, currentCase);
            final HDLTernary tern = new HDLTernary()
                    .setIfExpr(new HDLEqualityOp().setLeft(loopVar.asHDLRef()).setType(HDLEqualityOpType.GREATER_EQ)
                            .setRight(range.getTo()))//
                    .setThenExpr(HDLLiteral.get(execCase)) //
                    .setElseExpr(HDLLiteral.get(exitCase));
            final HDLAssignment switchJump = new HDLAssignment().setLeft(processStateVar.asHDLRef()).setRight(tern);
            cases.get(condCase).add(switchJump);
            break;
        }
        case HDLBlock: {
            final HDLBlock block = (HDLBlock) stmnt;
            for (final HDLStatement sub : block.getStatements()) {
                processStatement(cases, sub, currentCase, processStateVar, processNexTimeVar, ms);
            }
            break;
        }
        case HDLSwitchStatement:
            throw new IllegalArgumentException("Switch cases are currently not supported in Testbench processes");
        default:
            break;
        }
        return currentCase;
    }

    public static HDLManip toInteger(int target) {
        return new HDLManip().setTarget(HDLLiteral.get(target)).setType(HDLManipType.CAST)
                .setCastTo(HDLPrimitive.getInteger());
    }

    public static int waitFor(Map<Integer, List<HDLStatement>> cases, int currentCase, HDLVariable processStateVar,
            HDLVariable processNexTimeVar, HDLExpression amount) {
        final List<HDLStatement> currentCaseList = cases.get(currentCase);
        currentCaseList.add(new HDLAssignment().setLeft(processNexTimeVar.asHDLRef())
                .setType(HDLAssignmentType.ADD_ASSGN).setRight(amount));
        final int timeCase = currentCase = newCase(cases, currentCase);
        insertJump(currentCaseList, processStateVar, timeCase);
        return currentCase;
    }

    public static void insertJump(final List<HDLStatement> caseList, HDLVariable processStateVar,
            final long targetCase) {
        caseList.add(new HDLAssignment().setLeft(processStateVar.asHDLRef()).setRight(HDLLiteral.get(targetCase)));
    }

    public static int newCase(Map<Integer, List<HDLStatement>> cases, int currentCase) {
        final int targetCase = currentCase + 1;
        cases.put(targetCase, Lists.<HDLStatement>newArrayList());
        return targetCase;
    }

    private static HDLUnit addTimeVar(HDLUnit insulin) {
        final ModificationSet ms = new ModificationSet();
        HDLVariableDeclaration hvd = new HDLVariableDeclaration()
                .setType(HDLPrimitive.getUint().setWidth(HDLLiteral.get(64)));
        hvd = hvd.addVariables(new HDLVariable().setName("$time").setDefaultValue(HDLLiteral.get(0)));
        ms.addTo(insulin, HDLUnit.fInits, hvd);
        // TODO Unify time base
        return ms.apply(insulin);
    }

    private static HDLUnit convertBooleanLiterals(HDLEvaluationContext context, HDLUnit insulin) {
        final ModificationSet ms = new ModificationSet();
        final HDLLiteral[] literals = insulin.getAllObjectsOf(HDLLiteral.class, true);
        for (final HDLLiteral hdlLiteral : literals) {
            final String val = hdlLiteral.getVal();
            if (HDLLiteral.TRUE.equals(val)) {
                ms.replace(hdlLiteral, HDLLiteral.get(1));
            }
            if (HDLLiteral.FALSE.equals(val)) {
                ms.replace(hdlLiteral, HDLLiteral.get(0));
            }
        }
        return ms.apply(insulin);
    }

    private static HDLUnit getSingleUnit(final HDLPackage pkg) {
        final ArrayList<HDLUnit> units = pkg.getUnits();
        if (units.size() != 1)
            throw new IllegalArgumentException("Something went wrong, found more or less than 1 HDLUnit");
        return units.get(0);
    }

    private static HDLUnit removeDeadLeaves(HDLEvaluationContext context, HDLUnit insulin) {
        final HDLIfStatement[] ifs = insulin.getAllObjectsOf(HDLIfStatement.class, true);
        final ModificationSet ms = new ModificationSet();
        for (final HDLIfStatement hif : ifs) {
            final HDLExpression ifExp = hif.getIfExp();
            final Optional<BigInteger> valueOf = ConstantEvaluate.valueOf(ifExp, context);
            if (valueOf.isPresent()) {
                if (valueOf.get().equals(BigInteger.ZERO)) {
                    final ArrayList<HDLStatement> elseDo = hif.getElseDo();
                    ms.replace(hif, elseDo.toArray(new HDLStatement[elseDo.size()]));
                } else {
                    final ArrayList<HDLStatement> thenDo = hif.getThenDo();
                    ms.replace(hif, thenDo.toArray(new HDLStatement[thenDo.size()]));
                }
            }
        }
        return ms.apply(insulin);
    }

    private static HDLUnit convertArrayInits(HDLEvaluationContext context, HDLUnit insulin) {
        final ModificationSet ms = new ModificationSet();
        final HDLArrayInit[] inits = insulin.getAllObjectsOf(HDLArrayInit.class, true);
        for (final HDLArrayInit arrayInit : inits) {
            final IHDLObject container = arrayInit.getContainer();
            switch (container.getClassType()) {
            case HDLArrayInit:
                break;
            case HDLAssignment:
                final HDLReference left = ((HDLAssignment) container).getLeft();
                if (!(left instanceof HDLVariableRef))
                    throw new IllegalArgumentException("Unsupported assignment target for ArrayInit:" + left);
                final HDLVariableRef varRef = (HDLVariableRef) left;
                addInit(varRef, arrayInit, container, ms);
                ms.remove(container);
                break;
            case HDLVariable:
                final HDLVariable var = (HDLVariable) container;
                addInit(var.asHDLRef(), arrayInit, var.getContainer(), ms);
                // ms.replace(var, var.setDefaultValue(null));
                break;
            case HDLRegisterConfig:
                // resetValues are taken care of later
                break;
            default:
                throw new IllegalArgumentException("Unsupported container for ArrayInit:" + container);
            }
        }
        return ms.apply(insulin);
    }

    private static void addInit(HDLVariableRef varRef, HDLArrayInit arrayInit, IHDLObject container,
            ModificationSet ms) {
        final ArrayList<HDLExpression> exp = arrayInit.getExp();
        for (int i = 0; i < exp.size(); i++) {
            final HDLExpression subExp = exp.get(i);
            final HDLVariableRef addArray = varRef.addArray(HDLLiteral.get(i));
            if (subExp instanceof HDLArrayInit) {
                final HDLArrayInit subArray = (HDLArrayInit) subExp;
                addInit(addArray, subArray, container, ms);
            } else {
                final HDLAssignment ass = new HDLAssignment().setLeft(addArray).setRight(subExp);
                ms.insertAfter(container, ass);
            }
        }
    }

    private static HDLPackage flattenAll(HDLEvaluationContext context, HDLUnit insulin, char separator) {
        HDLInterfaceInstantiation[] hii = null;
        HDLPackage res = new HDLPackage();
        int count = 0;
        while ((hii = insulin.getAllObjectsOf(HDLInterfaceInstantiation.class, true)).length > 0) {
            if (count > 50)
                throw new IllegalArgumentException(
                        "It appears that the number of inlined units is exceptionally large. Maybe a recursive inclusion?");
            count++;
            final HDLLibrary library = insulin.getLibrary();
            final HDLInterfaceInstantiation hi = hii[0];
            final HDLQualifiedName asRef = hi.resolveHIf().get().asRef();
            HDLUnit unit = library.getUnit(asRef);
            if (unit == null)
                throw new IllegalArgumentException("Can not find unit for interface:" + asRef);
            unit = Insulin.transform(unit, library.getSrc(asRef));
            final HDLEvaluationContext hiContext = hi.getContext(HDLEvaluationContext.createDefault(unit));
            final HDLPackage subUnit = flattenAll(hiContext, unit, separator);
            for (final HDLDeclaration decl : subUnit.getDeclarations()) {
                res = res.addDeclarations(decl);
            }
            final HDLPackage iuRes = Refactoring.inlineUnit(insulin, hi, getSingleUnit(subUnit), separator);
            for (final HDLDeclaration decl : iuRes.getDeclarations()) {
                res = res.addDeclarations(decl);
            }
            insulin = getSingleUnit(iuRes);
        }
        res = res.addUnits(insulin);
        return res;
    }

    private static class InitTuple {
        public final HDLReference ref;
        public final IHDLObject container;

        public InitTuple(HDLReference ref, IHDLObject container) {
            super();
            this.ref = ref;
            this.container = container;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = (prime * result) + ((container == null) ? 0 : container.hashCode());
            result = (prime * result) + ((ref == null) ? 0 : ref.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            final InitTuple other = (InitTuple) obj;
            if (container != other.container)
                return false;
            if (ref == null) {
                if (other.ref != null)
                    return false;
            } else if (!ref.equals(other.ref))
                return false;
            return true;
        }

    }

    private static HDLUnit removeDoubleAssignments(HDLEvaluationContext context, HDLUnit insulin) {
        final ModificationSet ms = new ModificationSet();
        final HDLAssignment[] asss = insulin.getAllObjectsOf(HDLAssignment.class, true);
        final Map<InitTuple, HDLAssignment> scopeWrites = Maps.newLinkedHashMap();
        for (final HDLAssignment hdlAssignment : asss) {
            final IHDLObject container = hdlAssignment.getContainer();
            final InitTuple it = new InitTuple(hdlAssignment.getLeft(), container);
            final HDLAssignment otherAss = scopeWrites.get(it);
            if (otherAss != null) {
                if (container instanceof HDLIfStatement) {
                    final HDLIfStatement hdlIfStatement = (HDLIfStatement) container;
                    final TreeSide oSide = hdlIfStatement.treeSide(otherAss);
                    final TreeSide tSide = hdlIfStatement.treeSide(hdlAssignment);
                    if (oSide == tSide) {
                        ms.remove(otherAss);
                    }
                } else {
                    ms.remove(otherAss);
                }
            }
            scopeWrites.put(it, hdlAssignment);
        }
        return ms.apply(insulin);
    }

    private static AtomicInteger tempID = new AtomicInteger();

    private static HDLUnit convertTernary(HDLEvaluationContext context, HDLUnit insulin) {
        final ModificationSet ms = new ModificationSet();
        final HDLTernary[] ternaries = insulin.getAllObjectsOf(HDLTernary.class, true);
        for (final HDLTernary ternary : ternaries) {
            final Optional<? extends HDLType> typeOf = TypeExtension.typeOf(ternary);
            final String name = "$tmp_" + tempID.getAndIncrement();
            final HDLVariable var = new HDLVariable().setName(name);
            final HDLVariableDeclaration hvd = new HDLVariableDeclaration().setType(typeOf.get()).addVariables(var);
            final HDLAssignment newAss = new HDLAssignment().setLeft(var.asHDLRef());
            final HDLIfStatement ifStatement = new HDLIfStatement()//
                    .setIfExp(ternary.getIfExpr())//
                    .addThenDo(newAss.setRight(ternary.getThenExpr()))//
                    .addElseDo(newAss.setRight(ternary.getElseExpr()));
            ms.replace(ternary, var.asHDLRef());
            ms.insertBefore(ternary.getContainer(HDLStatement.class), hvd, ifStatement);
        }
        return ms.apply(insulin);
    }

    private static HDLUnit literalBitRanges(HDLEvaluationContext context, HDLUnit insulin) {
        final ModificationSet ms = new ModificationSet();
        final HDLRange[] ranges = insulin.getAllObjectsOf(HDLRange.class, true);
        for (final HDLRange hdlRange : ranges) {
            final Optional<BigInteger> toBig = ConstantEvaluate.valueOf(hdlRange.getTo(), context);
            if (!toBig.isPresent())
                throw new IllegalArgumentException("Given the context it should always be non null");
            final HDLExpression from = hdlRange.getFrom();
            if (from != null) {
                final Optional<BigInteger> fromBig = ConstantEvaluate.valueOf(from, context);
                if (!fromBig.isPresent())
                    throw new IllegalArgumentException("Given the context it should always be non null");
                ms.replace(hdlRange,
                        hdlRange.setFrom(HDLLiteral.get(fromBig.get())).setTo(HDLLiteral.get(toBig.get())));
            } else {
                ms.replace(hdlRange, hdlRange.setTo(HDLLiteral.get(toBig.get())));
            }
        }
        return ms.apply(insulin);
    }

    private static HDLUnit createBitRanges(HDLEvaluationContext context, HDLUnit insulin) {
        final HDLVariableRef[] refs = insulin.getAllObjectsOf(HDLVariableRef.class, true);
        final Map<HDLQualifiedName, List<RangeVal>> ranges = new LinkedHashMap<HDLQualifiedName, List<RangeVal>>();
        final Map<HDLQualifiedName, Range<BigInteger>> fullRanges = new LinkedHashMap<HDLQualifiedName, Range<BigInteger>>();
        for (final HDLVariableRef ref : refs)
            if (ref.getContainer() instanceof HDLAssignment) {
                final HDLAssignment ass = (HDLAssignment) ref.getContainer();
                if (ass.getLeft() == ref) {
                    final Optional<HDLVariable> resolved = ref.resolveVar();
                    if (!resolved.isPresent())
                        throw new IllegalArgumentException("Can not resolve:" + ref.getVarRefName());
                    final HDLVariable resolveVar = resolved.get();
                    if (resolveVar.getDirection() != HDLDirection.IN) {
                        final HDLQualifiedName varRefName = ref.getVarRefName();
                        if (ref.getBits().size() > 0) {
                            List<RangeVal> set = ranges.get(varRefName);
                            if (set == null) {
                                set = new LinkedList<RangeVal>();
                                ranges.put(varRefName, set);
                            }
                            for (final HDLRange r : ref.getBits()) {
                                final Optional<Range<BigInteger>> determineRange = RangeExtension.rangeOf(r,
                                        context);
                                if (!determineRange.isPresent())
                                    throw new IllegalArgumentException("Can not determine Range for:" + r);
                                set.add(new RangeVal(determineRange.get().lowerEndpoint(), 1));
                                set.add(new RangeVal(determineRange.get().upperEndpoint(), -1));
                            }
                        } else {
                            final HDLExpression width = TypeExtension.getWidth(resolveVar);
                            if (width != null) {
                                final Optional<BigInteger> bWidth = ConstantEvaluate.valueOf(width, context);
                                if (!bWidth.isPresent())
                                    throw new IllegalArgumentException("Given the context this should be constant");
                                fullRanges.put(varRefName, RangeTool.createRange(BigInteger.ZERO,
                                        bWidth.get().subtract(BigInteger.ONE)));
                            }
                        }
                    }
                }
            }
        final ModificationSet ms = new ModificationSet();
        final Map<HDLQualifiedName, SortedSet<Range<BigInteger>>> splitRanges = new LinkedHashMap<HDLQualifiedName, SortedSet<Range<BigInteger>>>();
        for (final Map.Entry<HDLQualifiedName, List<RangeVal>> entry : ranges.entrySet()) {
            final List<RangeVal> value = entry.getValue();
            final HDLQualifiedName varName = entry.getKey();
            if (fullRanges.containsKey(varName)) {
                final Range<BigInteger> fullWidth = fullRanges.get(varName);
                value.add(new RangeVal(fullWidth.lowerEndpoint(), 1));
                value.add(new RangeVal(fullWidth.upperEndpoint(), -1));
            }
            final SortedSet<Range<BigInteger>> split = RangeTool.split(value);
            splitRanges.put(varName, split);
        }
        // Change bit access to broken down ranges
        for (final HDLVariableRef ref : refs) {
            final SortedSet<Range<BigInteger>> list = splitRanges.get(ref.getVarRefName());
            if (list != null) {
                final ArrayList<HDLRange> newRanges = new ArrayList<HDLRange>();
                if (!ref.getBits().isEmpty()) {
                    for (final HDLRange bit : ref.getBits())
                        if (bit.getFrom() != null) { // Singular ranges don't do
                            // anything
                            final Optional<Range<BigInteger>> range = RangeExtension.rangeOf(bit, context);
                            if (!range.isPresent())
                                throw new IllegalArgumentException("Can not determine Range of:" + bit);
                            for (final Range<BigInteger> newRange : list)
                                if (range.get().isConnected(newRange)) {
                                    newRanges.add(0, createRange(newRange));
                                }
                        } else {
                            newRanges.add(0, bit);
                        }
                } else {
                    for (final Range<BigInteger> vRange : list) {
                        newRanges.add(0, createRange(vRange));
                    }
                }
                if (newRanges.size() != 0) {
                    ms.replace(ref, ref.setBits(newRanges));
                }
            }
        }
        final HDLUnit apply = ms.apply(insulin);
        return Insulin.handleMultiBitAccess(apply, context);
    }

    private static HDLRange createRange(Range<BigInteger> newRange) {
        if (newRange.lowerEndpoint().equals(newRange.upperEndpoint()))
            return new HDLRange().setTo(HDLLiteral.get(newRange.upperEndpoint()));
        return new HDLRange().setTo(HDLLiteral.get(newRange.lowerEndpoint()))
                .setFrom(HDLLiteral.get(newRange.upperEndpoint()));
    }

    /**
     * Unrolls a loop and creates the statements with an accordingly replaced
     * loop parameter
     *
     * @param context
     * @param unit
     * @return
     */
    private static HDLUnit unrollForLoops(HDLEvaluationContext context, HDLUnit unit) {
        HDLUnit newUnit = unit;
        int count = 0;
        do {
            if (count > 50)
                throw new IllegalArgumentException("Something went wrong while unrolling the loops");
            count++;
            unit = newUnit;
            final HDLForLoop[] loops = unit.getAllObjectsOf(HDLForLoop.class, true);
            final ModificationSet ms = new ModificationSet();
            for (final HDLForLoop loop : loops) {
                final HDLVariable param = loop.getParam();
                final Optional<Range<BigInteger>> r = RangeExtension.rangeOf(loop.getRange().get(0), context);
                final List<HDLStatement> newStmnts = new ArrayList<HDLStatement>();
                for (final HDLStatement stmnt : loop.getDos()) {
                    final Collection<HDLVariableRef> refs = HDLQuery.select(HDLVariableRef.class).from(stmnt)
                            .where(HDLResolvedRef.fVar).lastSegmentIs(param.getName()).getAll();
                    if (refs.size() == 0) {
                        newStmnts.add(stmnt);
                    } else {
                        BigInteger counter = r.get().lowerEndpoint();
                        do {
                            final ModificationSet stmntMs = new ModificationSet();
                            for (final HDLVariableRef ref : refs) {
                                stmntMs.replace(ref, HDLLiteral.get(counter));
                            }
                            newStmnts.add(stmntMs.apply(stmnt));
                            counter = counter.add(BigInteger.ONE);
                        } while (counter.compareTo(r.get().upperEndpoint()) <= 0);
                    }
                }
                ms.replace(loop, newStmnts.toArray(new HDLStatement[0]));
            }
            newUnit = ms.apply(unit);
        } while (newUnit != unit);
        return newUnit;
    }

    public static void resetTempIDs() {
        tempID.set(0);
    }
}