org.pshdl.model.validation.builtin.BuiltInValidator.java Source code

Java tutorial

Introduction

Here is the source code for org.pshdl.model.validation.builtin.BuiltInValidator.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.validation.builtin;

import static org.pshdl.model.extensions.FullNameExtension.fullNameOf;
import static org.pshdl.model.utils.HDLQuery.isEqualTo;
import static org.pshdl.model.validation.builtin.ErrorCode.ARRAY_ASSIGNMENT_NOT_SAME_DIMENSIONS;
import static org.pshdl.model.validation.builtin.ErrorCode.ARRAY_DIMENSIONS_NOT_CONSTANT;
import static org.pshdl.model.validation.builtin.ErrorCode.ARRAY_INDEX_NEGATIVE;
import static org.pshdl.model.validation.builtin.ErrorCode.ARRAY_INDEX_NO_RANGE;
import static org.pshdl.model.validation.builtin.ErrorCode.ARRAY_INDEX_OUT_OF_BOUNDS;
import static org.pshdl.model.validation.builtin.ErrorCode.ARRAY_INDEX_POSSIBLY_NEGATIVE;
import static org.pshdl.model.validation.builtin.ErrorCode.ARRAY_INDEX_POSSIBLY_OUT_OF_BOUNDS;
import static org.pshdl.model.validation.builtin.ErrorCode.ARRAY_REFERENCE_NOT_SAME_DIMENSIONS;
import static org.pshdl.model.validation.builtin.ErrorCode.ARRAY_REFERENCE_TOO_FEW_DIMENSIONS_IN_EXPRESSION;
import static org.pshdl.model.validation.builtin.ErrorCode.ARRAY_REFERENCE_TOO_MANY_DIMENSIONS;
import static org.pshdl.model.validation.builtin.ErrorCode.ARRAY_WRITE_MULTI_DIMENSION;
import static org.pshdl.model.validation.builtin.ErrorCode.ASSIGNMENT_CLIPPING_WILL_OCCUR;
import static org.pshdl.model.validation.builtin.ErrorCode.ASSIGNMENT_ENUM_NOT_WRITABLE;
import static org.pshdl.model.validation.builtin.ErrorCode.ASSIGNMENT_NOT_ENUM;
import static org.pshdl.model.validation.builtin.ErrorCode.ASSIGNMENT_NOT_PRIMITIVE;
import static org.pshdl.model.validation.builtin.ErrorCode.ASSIGNMENT_NOT_SUPPORTED;
import static org.pshdl.model.validation.builtin.ErrorCode.BIT_ACCESS_NEGATIVE;
import static org.pshdl.model.validation.builtin.ErrorCode.BIT_ACCESS_NOT_POSSIBLE;
import static org.pshdl.model.validation.builtin.ErrorCode.BIT_ACCESS_NOT_POSSIBLE_ON_TYPE;
import static org.pshdl.model.validation.builtin.ErrorCode.BIT_ACCESS_OUT_OF_BOUNDS;
import static org.pshdl.model.validation.builtin.ErrorCode.BIT_ACCESS_POSSIBLY_NEGATIVE;
import static org.pshdl.model.validation.builtin.ErrorCode.BIT_ACCESS_POSSIBLY_OUT_OF_BOUNDS;
import static org.pshdl.model.validation.builtin.ErrorCode.BOOL_NEGATE_NUMERIC_NOT_SUPPORTED;
import static org.pshdl.model.validation.builtin.ErrorCode.CLOCK_NOT_BIT;
import static org.pshdl.model.validation.builtin.ErrorCode.CLOCK_UNKNOWN_WIDTH;
import static org.pshdl.model.validation.builtin.ErrorCode.COMBINED_ASSIGNMENT_NOT_ALLOWED;
import static org.pshdl.model.validation.builtin.ErrorCode.CONCAT_TYPE_NOT_ALLOWED;
import static org.pshdl.model.validation.builtin.ErrorCode.CONSTANT_DEFAULT_VALUE_NOT_CONSTANT;
import static org.pshdl.model.validation.builtin.ErrorCode.CONSTANT_NEED_DEFAULTVALUE;
import static org.pshdl.model.validation.builtin.ErrorCode.CONSTANT_PORT_CANT_REGISTER;
import static org.pshdl.model.validation.builtin.ErrorCode.CONSTANT_WIDTH_MISMATCH;
import static org.pshdl.model.validation.builtin.ErrorCode.DIRECTION_NOT_ALLOWED_IN_SCOPE;
import static org.pshdl.model.validation.builtin.ErrorCode.EQUALITY_ALWAYS_FALSE;
import static org.pshdl.model.validation.builtin.ErrorCode.EQUALITY_ALWAYS_TRUE;
import static org.pshdl.model.validation.builtin.ErrorCode.FOR_LOOP_RANGE_NOT_CONSTANT;
import static org.pshdl.model.validation.builtin.ErrorCode.GLOBAL_CANT_REGISTER;
import static org.pshdl.model.validation.builtin.ErrorCode.GLOBAL_NOT_CONSTANT;
import static org.pshdl.model.validation.builtin.ErrorCode.GLOBAL_VAR_SAME_NAME;
import static org.pshdl.model.validation.builtin.ErrorCode.IN_PORT_CANT_REGISTER;
import static org.pshdl.model.validation.builtin.ErrorCode.MULTI_PROCESS_WRITE;
import static org.pshdl.model.validation.builtin.ErrorCode.NO_SUCH_FUNCTION;
import static org.pshdl.model.validation.builtin.ErrorCode.ONLY_ONE_CLOCK_ANNOTATION_ALLOWED;
import static org.pshdl.model.validation.builtin.ErrorCode.ONLY_ONE_RESET_ANNOTATION_ALLOWED;
import static org.pshdl.model.validation.builtin.ErrorCode.PARAMETER_NOT_FOUND;
import static org.pshdl.model.validation.builtin.ErrorCode.RANGE_NOT_DOWN;
import static org.pshdl.model.validation.builtin.ErrorCode.RANGE_NOT_UP;
import static org.pshdl.model.validation.builtin.ErrorCode.RANGE_OVERLAP;
import static org.pshdl.model.validation.builtin.ErrorCode.REGISTER_UNKNOWN_ARGUMENT;
import static org.pshdl.model.validation.builtin.ErrorCode.REGISTER_UNKNOWN_ARGUMENT_VALUE;
import static org.pshdl.model.validation.builtin.ErrorCode.RESET_NOT_BIT;
import static org.pshdl.model.validation.builtin.ErrorCode.RESET_UNKNOWN_WIDTH;
import static org.pshdl.model.validation.builtin.ErrorCode.SWITCH_CASE_NEEDS_CONSTANT_WIDTH;
import static org.pshdl.model.validation.builtin.ErrorCode.SWITCH_CASE_NEEDS_WIDTH;
import static org.pshdl.model.validation.builtin.ErrorCode.SWITCH_LABEL_DUPLICATE;
import static org.pshdl.model.validation.builtin.ErrorCode.SWITCH_LABEL_NOT_CONSTANT;
import static org.pshdl.model.validation.builtin.ErrorCode.SWITCH_LABEL_WRONG_ENUM;
import static org.pshdl.model.validation.builtin.ErrorCode.SWITCH_MULTIPLE_DEFAULT;
import static org.pshdl.model.validation.builtin.ErrorCode.SWITCH_NO_DEFAULT;
import static org.pshdl.model.validation.builtin.ErrorCode.TYPE_INVALID_PRIMITIVE;
import static org.pshdl.model.validation.builtin.ErrorCode.TYPE_SAME_NAME;
import static org.pshdl.model.validation.builtin.ErrorCode.UNKNOWN_RANGE;
import static org.pshdl.model.validation.builtin.ErrorCode.UNRESOLVED_ENUM;
import static org.pshdl.model.validation.builtin.ErrorCode.UNRESOLVED_FRAGMENT;
import static org.pshdl.model.validation.builtin.ErrorCode.UNRESOLVED_FUNCTION;
import static org.pshdl.model.validation.builtin.ErrorCode.UNRESOLVED_INTERFACE;
import static org.pshdl.model.validation.builtin.ErrorCode.UNRESOLVED_TYPE;
import static org.pshdl.model.validation.builtin.ErrorCode.UNRESOLVED_VARIABLE;
import static org.pshdl.model.validation.builtin.ErrorCode.UNSUPPORTED_TYPE_FOR_OP;
import static org.pshdl.model.validation.builtin.ErrorCode.VARIABLE_KEYWORD_NAME;
import static org.pshdl.model.validation.builtin.ErrorCode.VARIABLE_NAME_NOT_RECOMMENDED;
import static org.pshdl.model.validation.builtin.ErrorCode.VARIABLE_SAME_NAME;
import static org.pshdl.model.validation.builtin.ErrorCode.VARIABLE_SAME_NAME_DIFFERENT_CASE;
import static org.pshdl.model.validation.builtin.ErrorCode.VARIABLE_SCOPE_SAME_NAME;
import static org.pshdl.model.validation.builtin.ErrorCode.VARIABLE_SCOPE_SAME_NAME_DIFFERENT_CASE;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.pshdl.model.HDLAnnotation;
import org.pshdl.model.HDLArgument;
import org.pshdl.model.HDLArithOp;
import org.pshdl.model.HDLArrayInit;
import org.pshdl.model.HDLAssignment;
import org.pshdl.model.HDLBitOp;
import org.pshdl.model.HDLBlock;
import org.pshdl.model.HDLClass;
import org.pshdl.model.HDLConcat;
import org.pshdl.model.HDLDirectGeneration;
import org.pshdl.model.HDLEnum;
import org.pshdl.model.HDLEnumRef;
import org.pshdl.model.HDLEqualityOp;
import org.pshdl.model.HDLExpression;
import org.pshdl.model.HDLForLoop;
import org.pshdl.model.HDLFunction;
import org.pshdl.model.HDLFunctionCall;
import org.pshdl.model.HDLInlineFunction;
import org.pshdl.model.HDLInstantiation;
import org.pshdl.model.HDLInterface;
import org.pshdl.model.HDLInterfaceInstantiation;
import org.pshdl.model.HDLInterfaceRef;
import org.pshdl.model.HDLManip;
import org.pshdl.model.HDLObject;
import org.pshdl.model.HDLObject.GenericMeta;
import org.pshdl.model.HDLOpExpression;
import org.pshdl.model.HDLPackage;
import org.pshdl.model.HDLPrimitive;
import org.pshdl.model.HDLPrimitive.HDLPrimitiveType;
import org.pshdl.model.HDLRange;
import org.pshdl.model.HDLReference;
import org.pshdl.model.HDLRegisterConfig;
import org.pshdl.model.HDLResolvedRef;
import org.pshdl.model.HDLShiftOp;
import org.pshdl.model.HDLSubstituteFunction;
import org.pshdl.model.HDLSwitchCaseStatement;
import org.pshdl.model.HDLSwitchStatement;
import org.pshdl.model.HDLType;
import org.pshdl.model.HDLUnit;
import org.pshdl.model.HDLUnresolvedFragment;
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.RangeExtension;
import org.pshdl.model.extensions.TypeExtension;
import org.pshdl.model.simulation.RangeTool;
import org.pshdl.model.types.builtIn.HDLAnnotations;
import org.pshdl.model.types.builtIn.HDLBuiltInAnnotationProvider.HDLBuiltInAnnotations;
import org.pshdl.model.types.builtIn.HDLGenerators;
import org.pshdl.model.types.builtIn.HDLPrimitives;
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.MetaAccess;
import org.pshdl.model.utils.services.HDLTypeInferenceInfo;
import org.pshdl.model.utils.services.IHDLValidator;
import org.pshdl.model.validation.HDLValidator.HDLAdvise;
import org.pshdl.model.validation.Problem;
import org.pshdl.model.validation.RWValidation;

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

public class BuiltInValidator implements IHDLValidator {

    public static final GenericMeta<Range<BigInteger>> ARRAY_RANGE = new GenericMeta<Range<BigInteger>>(
            "arrayRange", true);
    public static final GenericMeta<Range<BigInteger>> ACCESS_RANGE = new GenericMeta<Range<BigInteger>>(
            "accessRange", true);
    public static String[] PSHDL_KEYWORDS = new String[] { "bit", "out", "string", "switch", "include", "process",
            "for", "function", "import", "else", "extends", "native", "package", "testbench", "int", "if", "in",
            "default", "enum", "const", "module", "inline", "generate", "bool", "simulation", "uint", "case",
            "inout", "substitute", "param", "register", "interface" };
    public final static Set<String> keywordSet;
    static {
        keywordSet = Sets.newLinkedHashSet();
        for (final String keyword : PSHDL_KEYWORDS) {
            keywordSet.add(keyword);
        }
    }

    public static enum IntegerMeta implements MetaAccess<Integer> {
        READ_COUNT, WRITE_COUNT, ACCESS;

        @Override
        public boolean inherit() {
            return true;
        }
    }

    @Override
    public boolean check(HDLPackage pkg, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        // TODO find a way to distinguish between context dependent problems and
        // others
        try {
            checkUnresolved(pkg, problems);
            checkFunctionCalls(pkg, problems, hContext);
            pkg = Insulin.inlineFunctions(pkg);
            RWValidation.checkVariableUsage(pkg, problems);
            pkg = Insulin.setParameterOnInstance(pkg);
            checkVariableNaming(pkg, problems);
            checkClockAndResetAnnotation(pkg, problems);
            checkConstantBoundaries(pkg, problems, hContext);
            checkParameterInstance(pkg, problems, hContext);
            checkArrayBoundaries(pkg, problems, hContext);
            checkConstantEquals(pkg, problems, hContext);
            checkBitAccess(pkg, problems, hContext);
            // TODO Validate value ranges, check for 0 divide
            checkRangeDirections(pkg, problems, hContext);
            // TODO Check for POW only power of 2
            checkCombinedAssignment(pkg, problems, hContext);
            checkAnnotations(pkg, problems, hContext);
            checkType(pkg, problems, hContext);
            checkProcessWrite(pkg, problems, hContext);
            checkGenerators(pkg, problems, hContext);
            checkConstantPackageDeclarations(pkg, problems, hContext);
            checkLiteralConcat(pkg, problems);
            checkDuplicateType(pkg, problems);
            // TODO Validate bitWidth mismatch
            checkBitWidthMismatch(pkg, problems, hContext);
            checkAssignments(pkg, problems, hContext);
            checkDirectionSubScopes(pkg, problems, hContext);
            // TODO Multi-bit Write only for Constants
            // TODO check for signals named clk or rst and warn about the
            // collision
            // TODO check for valid parameter
            checkSwitchStatements(pkg, problems, hContext);
            // TODO Type checking!
            // TODO Check for combinatorial loop.
            // TODO Check for multiple assignment in same Scope
            // TODO No processes in Module
            // TODO no I/O variables in block
            // TODO warn for name collision in generators
            // TODO Out port array size need to be constant
            // TODO Check for bit access on left assignment side when right is
            // matching
            // TODO no Registers in Testbench/Global gen
            checkRegisters(pkg, problems, hContext);

        } catch (final Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    private void checkDirectionSubScopes(HDLPackage pkg, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLUnit[] units = pkg.getAllObjectsOf(HDLUnit.class, true);
        for (final HDLUnit hdlUnit : units) {
            final Collection<HDLVariableDeclaration> allHVDs = HDLQuery.select(HDLVariableDeclaration.class)
                    .from(hdlUnit).where(HDLObject.fContainer).isNotEqualTo(hdlUnit).getAll();
            for (final HDLVariableDeclaration hvd : allHVDs) {
                switch (hvd.getDirection()) {
                case CONSTANT:
                case HIDDEN:
                case INTERNAL:
                    break;
                case IN:
                case INOUT:
                case OUT:
                case PARAMETER:
                    problems.add(new Problem(DIRECTION_NOT_ALLOWED_IN_SCOPE, hvd));
                    break;
                }
            }
        }
    }

    private void checkParameterInstance(HDLPackage pkg, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLInterfaceInstantiation[] instances = pkg.getAllObjectsOf(HDLInterfaceInstantiation.class, true);
        for (final HDLInterfaceInstantiation hii : instances) {
            final Optional<HDLInterface> hIf = hii.resolveHIf();
            if (!hIf.isPresent()) {
                continue;
            }
            final HDLInterface hif = hIf.get();
            final Collection<HDLVariableDeclaration> params = HDLQuery.select(HDLVariableDeclaration.class)
                    .from(hif).where(HDLVariableDeclaration.fDirection).isEqualTo(HDLDirection.PARAMETER).getAll();
            final Map<String, HDLVariable> paramNames = Maps.newLinkedHashMap();
            for (final HDLVariableDeclaration hvd : params) {
                for (final HDLVariable var : hvd.getVariables()) {
                    paramNames.put(var.getMeta(HDLInterfaceInstantiation.ORIG_NAME), var);
                }
            }
            final ArrayList<HDLArgument> arguments = hii.getArguments();
            for (final HDLArgument hdlArgument : arguments) {
                if (!paramNames.containsKey(hdlArgument.getName())) {
                    problems.add(new Problem(PARAMETER_NOT_FOUND, hdlArgument, hif));
                } else {
                    final HDLVariable var = paramNames.get(hdlArgument.getName());
                    checkAss(hdlArgument, var, hdlArgument.getExpression(), problems,
                            getContext(hContext, hdlArgument));
                }
            }
        }
    }

    private static void checkBitWidthMismatch(HDLPackage pkg, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        for (final HDLOpExpression ope : pkg.getAllObjectsOf(HDLOpExpression.class, true)) {
            final HDLExpression left = ope.getLeft();
            final Optional<? extends HDLType> leftType = TypeExtension.typeOf(left);
            if (!leftType.isPresent()) {
                continue;
            }
            final HDLExpression right = ope.getRight();
            final Optional<? extends HDLType> rightType = TypeExtension.typeOf(right);
            if (!rightType.isPresent()) {
                continue;
            }
            final HDLType rType = rightType.get();
            if (rType.getClassType() != HDLClass.HDLPrimitive) {
                continue;
            }
            final HDLPrimitive rpType = (HDLPrimitive) rType;
            if ((rpType.getType() == HDLPrimitiveType.STRING) || (rpType.getType() == HDLPrimitiveType.BOOL)) {
                continue;
            }
            final HDLType lType = leftType.get();
            if (lType.getClassType() != HDLClass.HDLPrimitive) {
                continue;
            }
            final HDLPrimitive lpType = (HDLPrimitive) lType;
            if ((lpType.getType() == HDLPrimitiveType.STRING) || (lpType.getType() == HDLPrimitiveType.BOOL)) {
                continue;
            }
            final HDLEvaluationContext lcontext = getContext(hContext, left);
            final Integer lw = HDLPrimitives.getWidth(lType, lcontext);
            if (lw == null) {
                continue;
            }
            final HDLEvaluationContext rcontext = getContext(hContext, right);
            final Integer rw = HDLPrimitives.getWidth(lType, rcontext);
            if (rw == null) {
                continue;
            }
            final Optional<BigInteger> lValue = ConstantEvaluate.valueOf(left, lcontext);
            if (lValue.isPresent()) {
                final BigInteger val = lValue.get();
                if (rw < val.bitLength()) {
                    problems.add(new Problem(CONSTANT_WIDTH_MISMATCH, left));
                }
            }
            final Optional<BigInteger> rValue = ConstantEvaluate.valueOf(right, rcontext);
            if (rValue.isPresent()) {
                final BigInteger val = rValue.get();
                if (lw < val.bitLength()) {
                    problems.add(new Problem(CONSTANT_WIDTH_MISMATCH, right));
                }
            }
        }
    }

    private static void checkDuplicateType(HDLPackage pkg, Set<Problem> problems) {
        checkType(problems, pkg.getAllObjectsOf(HDLUnit.class, true));
        checkType(problems, pkg.getAllObjectsOf(HDLInterface.class, true));
        checkType(problems, pkg.getAllObjectsOf(HDLEnum.class, true));
        // for (final HDLObject hif : pkg.getAllObjectsOf(HDLFunction.class,
        // true)) {
        // final HDLQualifiedName fqn = fullNameOf(hif);
        // final HDLLibrary library = hif.getLibrary();
        // final Optional<Iterable<HDLFunction>> resolve =
        // library.resolveFunction(Collections.<String> emptyList(), fqn);
        // if (resolve.isPresent()) {
        // final Iterable<HDLFunction> type = resolve.get();
        // if (type.getID() != hif.getID()) {
        // problems.add(new Problem(FUNCTION_SAME_NAME, hif, type));
        // }
        // }
        // }
        final HDLVariableDeclaration[] hvds = pkg.getAllObjectsOf(HDLVariableDeclaration.class, false);
        for (final HDLVariableDeclaration hvd : hvds) {
            for (final HDLVariable hif : hvd.getVariables()) {

                final HDLQualifiedName fqn = fullNameOf(hif);
                final HDLLibrary library = hif.getLibrary();
                final Optional<HDLVariable> resolve = library.resolveVariable(Collections.<String>emptyList(), fqn);
                if (resolve.isPresent()) {
                    final HDLVariable type = resolve.get();
                    if (type.getID() != hif.getID()) {
                        problems.add(new Problem(GLOBAL_VAR_SAME_NAME, hif, hif));
                    }
                }
            }
        }
    }

    public static void checkType(Set<Problem> problems, HDLObject[] ifs) {
        for (final HDLObject hif : ifs) {
            final HDLQualifiedName fqn = fullNameOf(hif);
            final HDLLibrary library = hif.getLibrary();
            if (library != null) {
                final Optional<? extends HDLType> resolve = library.resolve(Collections.<String>emptyList(), fqn);
                if (resolve.isPresent()) {
                    final HDLType type = resolve.get();
                    if (type.getID() != hif.getID()) {
                        problems.add(new Problem(TYPE_SAME_NAME, hif, type));
                    }
                }
            }
        }
    }

    /**
     * Check that constants declared on the global scope are indeed constant and
     * not registers
     *
     */
    private static void checkConstantPackageDeclarations(HDLPackage pkg, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLVariableDeclaration[] hvds = pkg.getAllObjectsOf(HDLVariableDeclaration.class, false);
        for (final HDLVariableDeclaration hvd : hvds) {
            if (hvd.getRegister() != null) {
                problems.add(new Problem(GLOBAL_CANT_REGISTER, hvd));
            }
            if (hvd.getDirection() != HDLDirection.CONSTANT) {
                problems.add(new Problem(GLOBAL_NOT_CONSTANT, hvd));
            }
            for (final HDLVariable var : hvd.getVariables()) {
                if (var.getDefaultValue() == null) {
                    problems.add(new Problem(GLOBAL_NOT_CONSTANT, var));
                } else {
                    final Optional<BigInteger> valueOf = ConstantEvaluate.valueOf(var.getDefaultValue());
                    if (!valueOf.isPresent()) {
                        problems.add(new Problem(GLOBAL_NOT_CONSTANT, var));
                    }
                }
            }
        }
    }

    /**
     * Check that registers are neither constants nor in ports
     */
    private static void checkRegisters(HDLPackage pkg, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLVariableDeclaration[] hvds = pkg.getAllObjectsOf(HDLVariableDeclaration.class, true);
        for (final HDLVariableDeclaration hvd : hvds) {
            final HDLRegisterConfig reg = hvd.getRegister();
            if (reg != null) {
                final Iterable<HDLArgument> meta = reg.getMeta(HDLRegisterConfig.ORIGINAL_ARGS);
                for (final HDLArgument hdlArgument : meta) {
                    if (!HDLRegisterConfig.VALID_PARAMS.contains(hdlArgument.getName())) {
                        problems.add(new Problem(REGISTER_UNKNOWN_ARGUMENT, hdlArgument));
                    }
                }
                if ((reg.getUnresolvedClockType() != null)
                        && !(reg.getUnresolvedClockType() instanceof HDLEnumRef)) {
                    problems.add(new Problem(REGISTER_UNKNOWN_ARGUMENT_VALUE, reg.getUnresolvedClockType(),
                            "Edge.RISING and Edge.FALLING"));
                }
                if ((reg.getUnresolvedSyncType() != null) && !(reg.getUnresolvedSyncType() instanceof HDLEnumRef)) {
                    problems.add(new Problem(REGISTER_UNKNOWN_ARGUMENT_VALUE, reg.getUnresolvedSyncType(),
                            "Sync.SYNC and Sync.ASYNC"));
                }
                if ((reg.getUnresolvedResetType() != null)
                        && !(reg.getUnresolvedResetType() instanceof HDLEnumRef)) {
                    problems.add(new Problem(REGISTER_UNKNOWN_ARGUMENT_VALUE, reg.getUnresolvedResetType(),
                            "Active.HIGH and Active.LOW"));
                }
                final HDLExpression clk = reg.getClk();
                if (clk != null) {
                    final HDLExpression width = TypeExtension.getWidth(clk);
                    if (width == null) {
                        problems.add(new Problem(CLOCK_UNKNOWN_WIDTH, clk));
                    } else {
                        final Optional<BigInteger> clkWidth = ConstantEvaluate.valueOf(width);
                        if (!clkWidth.isPresent()) {
                            problems.add(new Problem(CLOCK_UNKNOWN_WIDTH, clk));
                        } else {
                            if (!clkWidth.get().equals(BigInteger.ONE)) {
                                problems.add(new Problem(CLOCK_NOT_BIT, clk));
                            }
                        }
                    }
                }
                final HDLExpression rst = reg.getRst();
                if (rst != null) {
                    final HDLExpression width = TypeExtension.getWidth(rst);
                    if (width == null) {
                        problems.add(new Problem(RESET_UNKNOWN_WIDTH, rst));
                    } else {
                        final Optional<BigInteger> rstWidth = ConstantEvaluate.valueOf(width);
                        if (!rstWidth.isPresent()) {
                            problems.add(new Problem(RESET_UNKNOWN_WIDTH, rst));
                        } else {
                            if (!rstWidth.get().equals(BigInteger.ONE)) {
                                problems.add(new Problem(RESET_NOT_BIT, rst));
                            }
                        }
                    }
                }
                switch (hvd.getDirection()) {
                case CONSTANT:
                case PARAMETER:
                    problems.add(new Problem(CONSTANT_PORT_CANT_REGISTER, hvd));
                    break;
                case IN:
                    problems.add(new Problem(IN_PORT_CANT_REGISTER, hvd));
                    break;
                default:
                }
            }
        }
    }

    /**
     * Check that the from range is of the right correction. That is from>to for
     * variables and to>from for loops
     */
    private static void checkRangeDirections(HDLPackage pkg, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLRange[] ranges = pkg.getAllObjectsOf(HDLRange.class, true);
        for (final HDLRange hdlRange : ranges) {
            // If it is a single range, there is no direction :)
            final HDLExpression from = hdlRange.getFrom();
            if (from == null) {
                continue;
            }
            if (skipExp(hdlRange)) {
                continue;
            }
            final HDLEvaluationContext context = getContext(hContext, hdlRange);
            // For loop ranges are up to
            final Optional<Range<BigInteger>> fromRangeOf = RangeExtension.rangeOf(from, context);
            if (!fromRangeOf.isPresent()) {
                problems.add(new Problem(UNKNOWN_RANGE, from));
                continue;
            }
            final HDLExpression to = hdlRange.getTo();
            final Optional<Range<BigInteger>> toRangeOf = RangeExtension.rangeOf(to, context);
            if (!toRangeOf.isPresent()) {
                problems.add(new Problem(UNKNOWN_RANGE, to));
                continue;
            }
            if (fromRangeOf.get().isConnected(toRangeOf.get()) && !fromRangeOf.get().equals(toRangeOf.get())) {
                problems.add(new Problem(RANGE_OVERLAP, hdlRange));
                continue;
            }
            if (hdlRange.getContainer() instanceof HDLForLoop) {
                if (fromRangeOf.get().upperEndpoint().compareTo(toRangeOf.get().lowerEndpoint()) > 0) {
                    problems.add(new Problem(RANGE_NOT_UP, hdlRange));
                }
            } else if (toRangeOf.get().lowerEndpoint().compareTo(fromRangeOf.get().upperEndpoint()) > 0) {
                problems.add(new Problem(RANGE_NOT_DOWN, hdlRange));
            }
        }
    }

    private static void checkBitAccess(HDLPackage pkg, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLVariableRef[] refs = pkg.getAllObjectsOf(HDLVariableRef.class, true);
        for (final HDLVariableRef ref : refs) {
            if (skipExp(ref)) {
                continue;
            }
            if (ref.getBits().size() != 0) {
                final Optional<HDLVariable> resolveVar = ref.resolveVar();
                if (resolveVar.isPresent()) {
                    final Optional<? extends HDLType> varType = TypeExtension.typeOf(resolveVar.get());
                    if (!varType.isPresent()) {
                        // Don't know type, do nothing then..
                    } else if (!(varType.get() instanceof HDLPrimitive)) {
                        problems.add(new Problem(BIT_ACCESS_NOT_POSSIBLE_ON_TYPE, ref, varType.get()));
                    } else {
                        final HDLPrimitive primitive = (HDLPrimitive) varType.get();
                        switch (primitive.getType()) {
                        case BITVECTOR:
                        case INT:
                        case UINT:
                            break;
                        default:
                            problems.add(new Problem(BIT_ACCESS_NOT_POSSIBLE, ref, varType.get()));
                            continue;
                        }
                        final Optional<Range<BigInteger>> bitSizeRangeOpt = RangeExtension
                                .rangeOf(primitive.getWidth(), null);
                        if (!bitSizeRangeOpt.isPresent()) {
                            continue;
                        }
                        final Range<BigInteger> availableRange = bitSizeRangeOpt.get();
                        for (final HDLRange bit : ref.getBits()) {
                            final Optional<Range<BigInteger>> accessRangeFrom = RangeExtension.rangeOf(bit);
                            if (!accessRangeFrom.isPresent()) {
                                continue;
                            }
                            checkAccessBoundaries(accessRangeFrom.get(), availableRange, problems, bit, ref, true);
                        }
                    }
                }
            }
        }
    }

    private static void checkUnresolved(HDLPackage pkg, Set<Problem> problems) {
        final HDLUnresolvedFragment[] fragments = pkg.getAllObjectsOf(HDLUnresolvedFragment.class, true);
        for (final HDLUnresolvedFragment fragment : fragments) {
            final IHDLObject container = fragment.getContainer();
            if ((container instanceof HDLUnresolvedFragment)) {
                final HDLUnresolvedFragment contFrag = (HDLUnresolvedFragment) container;
                if (contFrag.getSub() == fragment) {
                    continue;
                }
            }
            problems.add(new Problem(UNRESOLVED_FRAGMENT, fragment));
        }
        final HDLResolvedRef[] refs = pkg.getAllObjectsOf(HDLResolvedRef.class, true);
        for (final HDLResolvedRef ref : refs) {
            final Optional<HDLVariable> resolveVar = ref.resolveVar();
            if (!resolveVar.isPresent()) {
                problems.add(new Problem(UNRESOLVED_VARIABLE, ref));
            }
            if (ref instanceof HDLEnumRef) {
                final HDLEnumRef enumRef = (HDLEnumRef) ref;
                final Optional<HDLEnum> resolveHEnum = enumRef.resolveHEnum();
                if (!resolveHEnum.isPresent()) {
                    problems.add(new Problem(UNRESOLVED_ENUM, ref));
                }
            }
            if (ref instanceof HDLInterfaceRef) {
                final HDLInterfaceRef hir = (HDLInterfaceRef) ref;
                final Optional<HDLVariable> hIf = hir.resolveHIf();
                if (!hIf.isPresent()) {
                    problems.add(new Problem(UNRESOLVED_VARIABLE, ref));
                }
            }
        }
        final HDLFunctionCall[] functionCalls = pkg.getAllObjectsOf(HDLFunctionCall.class, true);
        for (final HDLFunctionCall call : functionCalls) {
            final Optional<HDLFunction> function = call.resolveFunction();
            if (!function.isPresent()) {
                problems.add(new Problem(UNRESOLVED_FUNCTION, call));
            }
        }
        final HDLVariableDeclaration[] hvds = pkg.getAllObjectsOf(HDLVariableDeclaration.class, true);
        for (final HDLVariableDeclaration hvd : hvds) {
            final Optional<? extends HDLType> type = hvd.resolveType();
            if (!type.isPresent()) {
                problems.add(new Problem(UNRESOLVED_TYPE, hvd));
            }
        }
        final HDLInterfaceInstantiation[] hiis = pkg.getAllObjectsOf(HDLInterfaceInstantiation.class, true);
        for (final HDLInterfaceInstantiation hii : hiis) {
            final Optional<HDLInterface> type = hii.resolveHIf();
            if (!type.isPresent()) {
                problems.add(new Problem(UNRESOLVED_INTERFACE, hii));
            }
        }
    }

    private static void checkAssignments(HDLPackage pkg, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLAssignment[] asss = pkg.getAllObjectsOf(HDLAssignment.class, true);
        for (final HDLAssignment ass : asss) {
            final HDLEvaluationContext context = getContext(hContext, ass);
            checkAss(ass, ass.getLeft(), ass.getRight(), problems, context);
        }

        final HDLVariable[] vars = pkg.getAllObjectsOf(HDLVariable.class, true);
        for (final HDLVariable hdlVariable : vars)
            if (hdlVariable.getDefaultValue() != null) {
                final HDLEvaluationContext context = getContext(hContext, hdlVariable);
                checkAss(hdlVariable, hdlVariable, hdlVariable.getDefaultValue(), problems, context);
            }
    }

    private static void checkAss(IHDLObject obj, IHDLObject leftRef, HDLExpression rightExp, Set<Problem> problems,
            HDLEvaluationContext context) {
        final Optional<? extends HDLType> lType = TypeExtension.typeOf(leftRef);
        final Optional<? extends HDLType> rType = TypeExtension.typeOf(rightExp);
        if ((!lType.isPresent()) || (!rType.isPresent()))
            return;
        switch (lType.get().getClassType()) {
        case HDLEnum:
            if (rType.get().getClassType() != HDLClass.HDLEnum) {
                problems.add(new Problem(ASSIGNMENT_NOT_ENUM, obj));
            }
            break;
        case HDLPrimitive:
            if (rType.get().getClassType() != HDLClass.HDLPrimitive) {
                problems.add(new Problem(ASSIGNMENT_NOT_PRIMITIVE, obj));
            } else {
                final HDLPrimitive left = (HDLPrimitive) lType.get();
                final HDLPrimitive right = (HDLPrimitive) rType.get();
                if ((right.getType() == HDLPrimitiveType.STRING) && (left.getType() != HDLPrimitiveType.STRING)) {
                    problems.add(new Problem(ASSIGNMENT_NOT_SUPPORTED, obj,
                            "Strings can only be assigned to other strings"));
                } else {
                    switch (left.getType()) {
                    case BIT:
                        if (right.getType() != HDLPrimitiveType.BIT)
                            if (right.getWidth() != null) {
                                final Optional<BigInteger> w = ConstantEvaluate.valueOf(right.getWidth(), context);
                                if (w.isPresent() && !w.get().equals(BigInteger.ONE)) {
                                    problems.add(new Problem(ASSIGNMENT_CLIPPING_WILL_OCCUR, rightExp, obj));
                                }
                            }
                        break;
                    case BITVECTOR:
                        break;
                    case BOOL:
                        // String is also invalid, but handled above
                        break;
                    case INT:
                    case INTEGER:
                    case NATURAL:
                    case UINT:
                        if (!right.isNumber()) {
                            problems.add(new Problem(ASSIGNMENT_NOT_SUPPORTED, obj,
                                    "The assigned value needs to be numeric (uint<?>/int<?>)"));
                        }
                        break;
                    case STRING:
                        if (right.getType() != HDLPrimitiveType.STRING) {
                            problems.add(new Problem(ASSIGNMENT_NOT_SUPPORTED, obj,
                                    "Strings can only be assigned to other strings"));
                        }
                        break;
                    }
                }
            }
            break;
        case HDLInterface:
        default:
            problems.add(new Problem(ASSIGNMENT_NOT_SUPPORTED, obj));
        }
    }

    private static void checkLiteralConcat(HDLPackage pkg, Set<Problem> problems) {
        final HDLConcat[] concats = pkg.getAllObjectsOf(HDLConcat.class, true);
        for (final HDLConcat hdlConcat : concats) {
            final ArrayList<HDLExpression> cats = hdlConcat.getCats();
            for (final HDLExpression exp : cats) {
                final Optional<? extends HDLType> type = TypeExtension.typeOf(exp);
                if (type.isPresent())
                    if (type.get() instanceof HDLPrimitive) {
                        final HDLPrimitive prim = (HDLPrimitive) type.get();
                        switch (prim.getType()) {
                        case BIT:
                        case BITVECTOR:
                        case INT:
                        case UINT:
                            break;
                        case BOOL:
                        case INTEGER:
                        case NATURAL:
                        case STRING:
                            problems.add(new Problem(CONCAT_TYPE_NOT_ALLOWED, exp, prim));
                            break;
                        }
                    } else {
                        problems.add(new Problem(CONCAT_TYPE_NOT_ALLOWED, exp, type.get()));
                    }
            }
        }
    }

    private static void checkSwitchStatements(HDLPackage pkg, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLSwitchStatement[] switches = pkg.getAllObjectsOf(HDLSwitchStatement.class, true);
        for (final HDLSwitchStatement switchStatement : switches) {
            boolean defaultFound = false;
            final ArrayList<HDLSwitchCaseStatement> cases = switchStatement.getCases();
            final Set<BigInteger> values = Sets.newLinkedHashSet();
            final Set<HDLQualifiedName> enums = Sets.newLinkedHashSet();
            final Optional<? extends HDLType> type = TypeExtension.typeOf(switchStatement.getCaseExp());
            if (!type.isPresent()) {
                continue;
            }
            if (type.get() instanceof HDLPrimitive) {
                final HDLPrimitive primitive = (HDLPrimitive) type.get();
                if (primitive.getWidth() == null) {
                    problems.add(new Problem(SWITCH_CASE_NEEDS_WIDTH, switchStatement.getCaseExp()));
                }
                final Optional<BigInteger> width = ConstantEvaluate.valueOf(primitive.getWidth(), null);
                if (!width.isPresent() && ((primitive.getType() == HDLPrimitiveType.INT)
                        || (primitive.getType() == HDLPrimitiveType.UINT)
                        || (primitive.getType() == HDLPrimitiveType.BITVECTOR))) {
                    problems.add(new Problem(SWITCH_CASE_NEEDS_CONSTANT_WIDTH, switchStatement.getCaseExp()));
                }
            }
            final boolean isEnum = type.get() instanceof HDLEnum;
            for (final HDLSwitchCaseStatement caseStatement : cases) {
                final HDLExpression label = caseStatement.getLabel();
                if (label == null) {
                    if (defaultFound) {
                        problems.add(new Problem(SWITCH_MULTIPLE_DEFAULT, caseStatement));
                    }
                    defaultFound = true;
                } else if (!isEnum) {
                    final Optional<BigInteger> constant = ConstantEvaluate.valueOf(label, null);
                    if (!constant.isPresent()) {
                        problems.add(new Problem(SWITCH_LABEL_NOT_CONSTANT, caseStatement));
                    } else if (!values.add(constant.get())) {
                        problems.add(new Problem(SWITCH_LABEL_DUPLICATE, caseStatement));
                    }
                } else {
                    final Optional<? extends HDLType> labelType = TypeExtension.typeOf(label);
                    if (labelType.isPresent() && !type.get().equals(labelType.get())) {
                        problems.add(new Problem(SWITCH_LABEL_WRONG_ENUM, caseStatement));
                    }
                    if ((label instanceof HDLEnumRef) && !enums.add(((HDLEnumRef) label).getVarRefName())) {
                        problems.add(new Problem(SWITCH_LABEL_DUPLICATE, caseStatement));
                    }
                }
            }
            if (!defaultFound) {
                problems.add(new Problem(SWITCH_NO_DEFAULT, switchStatement));
            }
        }
    }

    private static void checkVariableNaming(HDLPackage pkg, Set<Problem> problems) {
        final HDLVariable[] vars = pkg.getAllObjectsOf(HDLVariable.class, true);
        final Map<String, HDLVariable> nameMap = Maps.newLinkedHashMap();
        for (final HDLVariable hdlVariable : vars) {
            final HDLQualifiedName fullName = fullNameOf(hdlVariable);
            final String lastSegment = fullName.getLastSegment();
            if (keywordSet.contains(lastSegment)) {
                problems.add(new Problem(VARIABLE_KEYWORD_NAME, hdlVariable));
            }
            if (lastSegment.charAt(0) == '$') {
                problems.add(new Problem(VARIABLE_NAME_NOT_RECOMMENDED, hdlVariable));
            }
            final HDLVariable put = nameMap.put(fullName.toString().toLowerCase(), hdlVariable);
            if (put != null) {
                final HDLQualifiedName otherName = fullNameOf(put);
                if (otherName.equals(fullName)) {
                    problems.add(new Problem(VARIABLE_SAME_NAME, hdlVariable, put));
                } else {
                    problems.add(new Problem(VARIABLE_SAME_NAME_DIFFERENT_CASE, hdlVariable, put));
                }
            }
        }
        for (final Entry<String, HDLVariable> entry : nameMap.entrySet()) {
            final HDLVariable var = entry.getValue();
            final HDLQualifiedName fullName = fullNameOf(var);
            final String lastSegment = fullName.getLastSegment();
            final HDLQualifiedName type = fullName.getTypePart();
            final HDLQualifiedName localPart = fullName.getLocalPart().skipLast(1);
            for (int i = 0; i < localPart.length; i++) {
                final HDLQualifiedName scoped = localPart.skipLast(i + 1).append(lastSegment);
                final HDLQualifiedName newName = type.append(scoped);
                final HDLVariable otherVar = nameMap.get(newName.toString().toLowerCase());
                if (otherVar != null) {
                    final HDLQualifiedName otherName = fullNameOf(otherVar);
                    if (otherName.equals(newName)) {
                        problems.add(new Problem(VARIABLE_SCOPE_SAME_NAME, var, otherVar));
                    } else {
                        problems.add(new Problem(VARIABLE_SCOPE_SAME_NAME_DIFFERENT_CASE, var, otherVar));
                    }
                }
            }
        }
    }

    private static void checkGenerators(HDLPackage unit, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLDirectGeneration[] generators = unit.getAllObjectsOf(HDLDirectGeneration.class, true);
        for (final HDLDirectGeneration hdg : generators) {
            if (HDLGenerators.validate(hdg, problems, getContext(hContext, hdg))) {
                if (hdg.getHIf() == null) {
                    problems.add(new Problem(ErrorCode.GENERATOR_ERROR, hdg));
                }
            }
        }
    }

    /**
     * Checks whether called functions exists, whether they have the correct
     * number of arguments etc..
     */
    private static void checkFunctionCalls(HDLPackage unit, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLFunctionCall[] calls = unit.getAllObjectsOf(HDLFunctionCall.class, true);
        for (final HDLFunctionCall call : calls) {
            final Optional<HDLFunction> function = call.resolveFunction();
            if (!function.isPresent()) {
                problems.add(new Problem(NO_SUCH_FUNCTION, call));
            }
        }
    }

    private static void checkProcessWrite(HDLPackage unit, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLVariable[] vars = unit.getAllObjectsOf(HDLVariable.class, true);
        for (final HDLVariable var : vars)
            if (var.hasMeta(RWValidation.BLOCK_META_CLASH)) {
                problems.add(new Problem(MULTI_PROCESS_WRITE, var));
            }
    }

    private static void checkType(HDLPackage unit, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLVariableDeclaration[] hvds = unit.getAllObjectsOf(HDLVariableDeclaration.class, true);
        for (final HDLVariableDeclaration hvd : hvds) {
            final Optional<? extends HDLType> type = hvd.resolveType();
            if (type.isPresent()) {
                final HDLType hdlType = type.get();
                if (hdlType instanceof HDLPrimitive) {
                    final HDLPrimitive primType = (HDLPrimitive) hdlType;
                    switch (primType.getType()) {
                    case BIT:
                    case INTEGER:
                    case NATURAL:
                        break;
                    case STRING:
                        if (primType.getWidth() != null) {
                            problems.add(new Problem(TYPE_INVALID_PRIMITIVE, hvd, "Strings can not have a width"));
                        }
                        break;
                    case BOOL:
                        if (primType.getWidth() != null) {
                            problems.add(new Problem(TYPE_INVALID_PRIMITIVE, hvd, "Booleans can not have a width"));
                        }
                        break;
                    case BITVECTOR:
                    case INT:
                    case UINT:
                        final Optional<Range<BigInteger>> rangeOpt = RangeExtension.rangeOf(primType.getWidth());
                        if (rangeOpt.isPresent()) {
                            final Range<BigInteger> range = rangeOpt.get();
                            if (!range.hasLowerBound()) {
                                problems.add(new Problem(ErrorCode.TYPE_NEGATIVE_WIDTH, hvd));
                            } else {
                                final BigInteger le = range.lowerEndpoint();
                                if (le.compareTo(BigInteger.ZERO) < 0) {
                                    if (range.hasUpperBound()
                                            && (range.upperEndpoint().compareTo(BigInteger.ZERO) < 0)) {
                                        problems.add(new Problem(ErrorCode.TYPE_NEGATIVE_WIDTH, hvd));
                                    } else {
                                        problems.add(new Problem(ErrorCode.TYPE_POSSIBLY_NEGATIVE_WIDTH, hvd));
                                    }
                                } else if (le.equals(BigInteger.ZERO) && range.hasUpperBound()
                                        && range.upperEndpoint().equals(BigInteger.ZERO)) {
                                    problems.add(new Problem(ErrorCode.TYPE_ZERO_WIDTH, hvd));
                                } else if (le.equals(BigInteger.ZERO)) {
                                    problems.add(new Problem(ErrorCode.TYPE_POSSIBLY_ZERO_WIDTH, hvd));
                                }
                            }
                        }
                        break;
                    }
                }
            }
        }

        final HDLOpExpression[] ops = unit.getAllObjectsOf(HDLOpExpression.class, true);
        for (final HDLOpExpression ope : ops) {
            if (skipExp(ope)) {
                continue;
            }
            checkOpExpression(problems, ope, ope);
        }
        final HDLManip[] manips = unit.getAllObjectsOf(HDLManip.class, true);
        for (final HDLManip manip : manips) {
            final Optional<? extends HDLType> targetType = TypeExtension.typeOf(manip.getTarget());
            if (targetType.isPresent()) {
                final HDLType tt = targetType.get();
                switch (manip.getType()) {
                case ARITH_NEG:
                    if (tt instanceof HDLPrimitive) {
                        final HDLPrimitive primitive = (HDLPrimitive) tt;
                        if (!primitive.isNumber()) {
                            problems.add(new Problem(UNSUPPORTED_TYPE_FOR_OP, manip,
                                    "Can not use arithmetic negate on a non-number"));
                        }
                    } else {
                        problems.add(new Problem(UNSUPPORTED_TYPE_FOR_OP, manip,
                                "Can not use arithmetic negate on a non-number"));
                    }
                    break;
                case BIT_NEG:
                    if (manip.getTarget().getClassType() == HDLClass.HDLLiteral) {
                        problems.add(new Problem(UNSUPPORTED_TYPE_FOR_OP, manip,
                                "Can not use binary negate on literals as they have no width"));
                    }
                    if (tt instanceof HDLPrimitive) {
                        final HDLPrimitive primitive = (HDLPrimitive) tt;
                        if (!primitive.isBits()) {
                            problems.add(new Problem(UNSUPPORTED_TYPE_FOR_OP, manip,
                                    "Can not use binary negate on a non-bits"));
                        }
                    } else {
                        problems.add(new Problem(UNSUPPORTED_TYPE_FOR_OP, manip,
                                "Can not use binary negate on a non-bits"));
                    }
                    break;
                case LOGIC_NEG:
                    if (tt instanceof HDLPrimitive) {
                        final HDLPrimitive primitive = (HDLPrimitive) tt;
                        if ((primitive.getType() != HDLPrimitiveType.BOOL)
                                && (primitive.getType() != HDLPrimitiveType.BIT)) {
                            problems.add(new Problem(BOOL_NEGATE_NUMERIC_NOT_SUPPORTED, manip,
                                    "Can not use logic negate on a non boolean/bit"));
                        }
                    } else {
                        problems.add(new Problem(UNSUPPORTED_TYPE_FOR_OP, manip,
                                "Can not use logic negate on a non boolean"));
                    }
                    break;
                case CAST:
                    final HDLType castTo = manip.getCastTo();
                    if (castTo instanceof HDLInterface) {
                        if (!(tt instanceof HDLInterface)) {
                            problems.add(new Problem(UNSUPPORTED_TYPE_FOR_OP, manip,
                                    "Can not cast from interface to non interface type:" + castTo));
                        }
                    }
                    if (castTo instanceof HDLEnum) {
                        problems.add(
                                new Problem(UNSUPPORTED_TYPE_FOR_OP, manip, "Enums can not be casted to anything"));
                    }
                    if (castTo instanceof HDLPrimitive) {
                        if (!(tt instanceof HDLPrimitive)) {
                            problems.add(new Problem(UNSUPPORTED_TYPE_FOR_OP, manip,
                                    "Can not cast from primitve to non primitive type:" + castTo));
                        }
                    }
                    break;
                }
            }
        }
    }

    private static void checkOpExpression(Set<Problem> problems, HDLOpExpression ope, IHDLObject node) {
        HDLTypeInferenceInfo info = null;
        final HDLPrimitives instance = HDLPrimitives.getInstance();
        switch (ope.getClassType()) {
        case HDLArithOp:
            info = instance.getArithOpType((HDLArithOp) ope);
            break;
        case HDLBitOp:
            info = instance.getBitOpType((HDLBitOp) ope);
            break;
        case HDLShiftOp:
            info = instance.getShiftOpType((HDLShiftOp) ope);
            break;
        case HDLEqualityOp:
            info = instance.getEqualityOpType((HDLEqualityOp) ope);
            break;
        default:
            throw new IllegalArgumentException("Did not expect class:" + ope.getClassType());
        }
        if (info == null)
            throw new IllegalArgumentException("Info should not be null");
        if (info.error != null) {
            problems.add(new Problem(UNSUPPORTED_TYPE_FOR_OP, node, info.error));
        }
    }

    private static void checkAnnotations(HDLPackage unit, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLAnnotation[] annos = unit.getAllObjectsOf(HDLAnnotation.class, true);
        for (final HDLAnnotation hdlAnnotation : annos) {
            final Problem[] p = HDLAnnotations.validate(hdlAnnotation);
            for (final Problem problem : p) {
                problems.add(problem);
            }
        }
    }

    private static void checkCombinedAssignment(HDLPackage unit, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final Collection<HDLAssignment> all = HDLQuery.select(HDLAssignment.class).from(unit)
                .where(HDLAssignment.fType).isNotEqualTo(HDLAssignment.HDLAssignmentType.ASSGN).getAll();
        for (final HDLAssignment ass : all) {
            final HDLReference ref = ass.getLeft();
            if (ref instanceof HDLUnresolvedFragment)
                return;
            final HDLOpExpression opExpression = Insulin.toOpExpression(ass);
            if (opExpression != null) {
                final IHDLObject freeze = opExpression.copyDeepFrozen(ass.getContainer());
                checkOpExpression(problems, (HDLOpExpression) freeze, ass);
                final Optional<HDLVariable> var = ((HDLResolvedRef) ref).resolveVar();
                if ((var.isPresent()) && (var.get().getRegisterConfig() == null)) {
                    final HDLBlock container = ass.getContainer(HDLBlock.class);
                    if ((container != null) && container.getProcess()) {
                        // If the assignment is happening within a process,
                        // chances
                        // are that the dev is trying something legal
                        continue;
                    }
                    problems.add(new Problem(COMBINED_ASSIGNMENT_NOT_ALLOWED, ass));
                }
            }
        }
    }

    private static void checkConstantEquals(HDLPackage unit, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLEqualityOp[] equalities = unit.getAllObjectsOf(HDLEqualityOp.class, true);
        for (final HDLEqualityOp op : equalities) {
            if (skipExp(op)) {
                continue;
            }
            final Optional<BigInteger> res = ConstantEvaluate.valueOf(op, getContext(hContext, op));
            if (res.isPresent())
                if (res.get().equals(BigInteger.ONE)) {
                    problems.add(new Problem(EQUALITY_ALWAYS_TRUE, op));
                } else {
                    problems.add(new Problem(EQUALITY_ALWAYS_FALSE, op));
                }
        }
    }

    public static boolean skipExp(IHDLObject op) {
        return (op.getContainer(HDLInlineFunction.class) != null)
                || (op.getContainer(HDLSubstituteFunction.class) != null);
    }

    private static void checkConstantBoundaries(HDLPackage unit, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        // XXX Check Array Dimensions
        final Collection<HDLVariableDeclaration> constants = HDLQuery.select(HDLVariableDeclaration.class)//
                .from(unit).where(HDLVariableDeclaration.fDirection)//
                .matches(isEqualTo(HDLDirection.CONSTANT))//
                .or(isEqualTo(HDLDirection.PARAMETER)) //
                .getAll();
        for (final HDLVariableDeclaration hvd : constants) {
            for (final HDLVariable var : hvd.getVariables()) {
                final HDLExpression def = var.getDefaultValue();
                if (var.getDefaultValue() == null) {
                    problems.add(new Problem(CONSTANT_NEED_DEFAULTVALUE, var));
                } else {
                    HDLEvaluationContext lContext = getContext(hContext, var);
                    if (lContext != null) {
                        lContext = lContext.withEnumAndBool(true, true);
                    }
                    if (def instanceof HDLArrayInit) {
                        checkConstantsArrayInit(problems, (HDLArrayInit) def, lContext);
                    } else {
                        assumeConstant(problems, CONSTANT_DEFAULT_VALUE_NOT_CONSTANT, null, def, lContext);
                    }
                }
            }
        }
        final HDLForLoop[] forLoops = unit.getAllObjectsOf(HDLForLoop.class, true);
        for (final HDLForLoop hdlForLoop : forLoops) {
            for (final HDLRange r : hdlForLoop.getRange()) {
                final Optional<BigInteger> evalTo = ConstantEvaluate.valueOf(r.getTo(), getContext(hContext, r));
                if (!evalTo.isPresent()) {
                    problems.add(new Problem(FOR_LOOP_RANGE_NOT_CONSTANT, r.getTo(), r, null));
                }
                if (r.getFrom() != null) {
                    final Optional<BigInteger> evalFrom = ConstantEvaluate.valueOf(r.getFrom(),
                            getContext(hContext, r));
                    if (!evalFrom.isPresent()) {
                        problems.add(new Problem(FOR_LOOP_RANGE_NOT_CONSTANT, r.getFrom(), r, null));
                    }
                }
            }
        }
    }

    public static void assumeConstant(Set<Problem> problems, IErrorCode code, final HDLExpression contextNode,
            final HDLExpression exp, HDLEvaluationContext lContext) {
        final Optional<BigInteger> constant = ConstantEvaluate.valueOf(exp, lContext);
        if (!constant.isPresent()) {
            final Optional<? extends HDLType> typeOf = TypeExtension.typeOf(exp);
            if (typeOf.isPresent()) {
                if (typeOf.get() instanceof HDLPrimitive) {
                    final HDLPrimitive prim = (HDLPrimitive) typeOf.get();
                    if (prim.isNumber()) {
                        problems.add(new Problem(code, exp, contextNode, null));
                    }
                }
            } else {
                if (!(exp instanceof HDLEnumRef)) {
                    problems.add(new Problem(code, exp, contextNode, null));
                }
            }
        }
    }

    private static void checkConstantsArrayInit(Set<Problem> problems, HDLArrayInit arrayInit,
            HDLEvaluationContext context) {
        for (final HDLExpression exp : arrayInit.getExp()) {
            final Optional<BigInteger> valueOf = ConstantEvaluate.valueOf(exp);
            if (!valueOf.isPresent())
                if (exp instanceof HDLArrayInit) {
                    final HDLArrayInit hai = (HDLArrayInit) exp;
                    checkConstantsArrayInit(problems, hai, context);
                } else {
                    assumeConstant(problems, CONSTANT_DEFAULT_VALUE_NOT_CONSTANT, exp, arrayInit, context);
                }
        }
    }

    private static void checkArrayBoundaries(HDLPackage unit, Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext) {
        final HDLVariable[] vars = unit.getAllObjectsOf(HDLVariable.class, true);
        for (final HDLVariable var : vars) {
            final HDLDirection dir = var.getDirection();
            if ((dir == HDLDirection.IN) || (dir == HDLDirection.INOUT) || (dir == HDLDirection.OUT)) {
                for (final HDLExpression dim : var.getDimensions()) {
                    final Optional<BigInteger> valueOf = ConstantEvaluate.valueOf(dim);
                    if (!valueOf.isPresent()) {
                        problems.add(new Problem(ARRAY_DIMENSIONS_NOT_CONSTANT, dim));
                    }
                }
            }
        }
        final HDLVariableRef[] refs = unit.getAllObjectsOf(HDLVariableRef.class, true);
        for (final HDLVariableRef ref : refs) {
            if (skipExp(ref)) {
                continue;
            }
            final Optional<HDLVariable> resolveVar = ref.resolveVar();
            if (resolveVar.isPresent()) {
                final ArrayList<HDLExpression> dimensions = resolveVar.get().getDimensions();
                compareBoundaries(problems, hContext, ref, dimensions, ref.getArray());
                if (ref instanceof HDLInterfaceRef) {
                    final HDLInterfaceRef hir = (HDLInterfaceRef) ref;
                    final Optional<HDLVariable> var = hir.resolveHIf();
                    if (var.isPresent()) {
                        compareBoundaries(problems, hContext, ref, var.get().getDimensions(), hir.getIfArray());
                    }
                }
            }
        }
    }

    /**
     * Compares whether the actual access array fits within the declared array
     * dimensions
     *
     * @param problems
     * @param hContext
     * @param ref
     *            the subject upon which the error will be declared
     * @param dimensions
     *            the declared dimensions of the variable
     * @param array
     *            the accessed dimensions of the variable
     */
    private static void compareBoundaries(Set<Problem> problems,
            Map<HDLQualifiedName, HDLEvaluationContext> hContext, HDLVariableRef ref,
            ArrayList<HDLExpression> dimensions, ArrayList<HDLExpression> array) {
        if (dimensions.size() < array.size()) {
            problems.add(new Problem(ARRAY_REFERENCE_TOO_MANY_DIMENSIONS, ref));
            return;
        } else if ((dimensions.size() > array.size())) {
            // Check whether dimensions have been left out. This is only ok when
            // it is an assignment and the other dimension is the same
            final IHDLObject container = ref.getContainer();
            if (container instanceof HDLAssignment) {
                final HDLAssignment ass = (HDLAssignment) container;
                final HDLReference left = ass.getLeft();
                if (left instanceof HDLUnresolvedFragment)
                    return;
                if (left.getClassType() == HDLClass.HDLEnumRef) {
                    problems.add(new Problem(ASSIGNMENT_ENUM_NOT_WRITABLE, left));
                } else {
                    final HDLVariableRef varRef = (HDLVariableRef) left;
                    final Optional<HDLVariable> var = varRef.resolveVar();
                    if (var.isPresent()) {
                        final HDLEvaluationContext context = getContext(hContext, ass);
                        final ArrayList<HDLExpression> targetDim = var.get().getDimensions();
                        for (int i = 0; i < varRef.getArray().size(); i++) {
                            if (targetDim.size() == 0) {
                                problems.add(new Problem(ARRAY_REFERENCE_TOO_MANY_DIMENSIONS, varRef));
                                return;
                            }
                            targetDim.remove(0);
                        }
                        if (left != ref) {
                            validateArrayAssignment(problems, context, ref, ass, left, targetDim);
                        } else {
                            final HDLClass classType = ass.getRight().getClassType();
                            if ((classType != HDLClass.HDLVariableRef) && (classType != HDLClass.HDLInterfaceRef)
                                    && (classType != HDLClass.HDLArrayInit)) {
                                problems.add(new Problem(ARRAY_WRITE_MULTI_DIMENSION, ass));
                            }
                        }
                    }
                }
            } else if (container instanceof HDLVariable) {
                final HDLVariable var = (HDLVariable) container;
                final HDLEvaluationContext context = getContext(hContext, var);
                validateArrayAssignment(problems, context, ref, var, var, var.getDimensions());
            } else {
                problems.add(new Problem(ARRAY_REFERENCE_TOO_FEW_DIMENSIONS_IN_EXPRESSION, ref));
            }
        }
        int dim = 0;
        for (final HDLExpression arr : array) {
            final HDLEvaluationContext context = getContext(hContext, arr);
            final Optional<Range<BigInteger>> accessRangeRaw = RangeExtension.rangeOf(arr, context);
            if (!accessRangeRaw.isPresent()) {
                problems.add(new Problem(ARRAY_INDEX_NO_RANGE, arr));
                break;
            }
            final Optional<Range<BigInteger>> arrayRangeRaw = RangeExtension.rangeOf(dimensions.get(dim), context);
            if (!arrayRangeRaw.isPresent()) {
                problems.add(new Problem(ARRAY_INDEX_NO_RANGE, dimensions.get(dim)));
                break;
            }
            final Range<BigInteger> accessRange = accessRangeRaw.get();
            final Range<BigInteger> arrayRange = arrayRangeRaw.get();
            checkAccessBoundaries(accessRange, arrayRange, problems, arr, ref, false);
            dim++;
        }
    }

    /**
     *
     * @param accessRange
     *            the range in which the array/bits can be acccessed
     * @param indexRange
     *            the range of the size that array size/ width of the type can
     *            be in
     * @param problems
     *            problems will be added here
     * @param arr
     *            the accessing {@link HDLExpression}
     * @param ref
     *            the reference that is accessed
     * @param bit
     *            when true bit access errors will be reported
     */
    private static void checkAccessBoundaries(Range<BigInteger> accessRange, Range<BigInteger> declaredRange,
            Set<Problem> problems, IHDLObject arr, HDLVariableRef ref, boolean bit) {
        // Reduce the declaredRange to the index limits
        Range<BigInteger> indexRange;
        if (declaredRange.hasUpperBound()) {
            final BigInteger upperEndpoint = declaredRange.upperEndpoint();
            final BigInteger subtract = upperEndpoint.subtract(BigInteger.ONE);
            if (subtract.compareTo(BigInteger.ZERO) < 0)
                // Maybe generate a warning here?
                return;
            indexRange = RangeTool.createRange(BigInteger.ZERO, subtract);
        } else {
            indexRange = Range.atLeast(BigInteger.ZERO);
        }
        final String info = "Expected value range:" + indexRange;
        // Check if highest idx is negative (Definitely a problem)
        if (accessRange.hasUpperBound() && (accessRange.upperEndpoint().signum() < 0)) {
            problems.add(new Problem(bit ? BIT_ACCESS_NEGATIVE : ARRAY_INDEX_NEGATIVE, arr, ref, info)
                    .addMeta(ACCESS_RANGE, accessRange).addMeta(ARRAY_RANGE, indexRange));
            // Check if lowest idx is negative (Might be a problem)
        } else if (accessRange.hasLowerBound() && (accessRange.lowerEndpoint().signum() < 0)) {
            problems.add(
                    new Problem(bit ? BIT_ACCESS_POSSIBLY_NEGATIVE : ARRAY_INDEX_POSSIBLY_NEGATIVE, arr, ref, info)
                            .addMeta(ACCESS_RANGE, accessRange).addMeta(ARRAY_RANGE, indexRange));
        }
        // Check whether the index and the access have at least something in
        // common (index 0..5 access 7..9)
        if (!indexRange.isConnected(accessRange)) {
            problems.add(new Problem(bit ? BIT_ACCESS_OUT_OF_BOUNDS : ARRAY_INDEX_OUT_OF_BOUNDS, arr, ref, info)
                    .addMeta(ACCESS_RANGE, accessRange).addMeta(ARRAY_RANGE, indexRange));
        } else if (accessRange.hasUpperBound() && indexRange.hasUpperBound()
                && (accessRange.upperEndpoint().compareTo(indexRange.upperEndpoint()) > 0)) {
            problems.add(new Problem(bit ? BIT_ACCESS_POSSIBLY_OUT_OF_BOUNDS : ARRAY_INDEX_POSSIBLY_OUT_OF_BOUNDS,
                    arr, ref, info).addMeta(ACCESS_RANGE, accessRange).addMeta(ARRAY_RANGE, indexRange));
        }
    }

    private static void validateArrayAssignment(Set<Problem> problems, HDLEvaluationContext context,
            HDLVariableRef ref, IHDLObject ass, IHDLObject left, ArrayList<HDLExpression> targetDim) {
        final Optional<HDLVariable> right = ref.resolveVar();
        if (!right.isPresent())
            return;
        final ArrayList<HDLExpression> sourceDim = right.get().getDimensions();
        for (int i = 0; i < ref.getArray().size(); i++) {
            if (sourceDim.size() == 0) {
                problems.add(new Problem(ARRAY_REFERENCE_TOO_MANY_DIMENSIONS, ref));
                return;
            }
            sourceDim.remove(0);
        }
        if (targetDim.size() != sourceDim.size()) {
            problems.add(new Problem(ARRAY_REFERENCE_NOT_SAME_DIMENSIONS, ass));
        } else {
            final HDLDirection dir = right.get().getDirection();
            if ((dir == HDLDirection.IN) || (dir == HDLDirection.INOUT) || (dir == HDLDirection.OUT)) {
                context = null;
            }
            for (int i = 0; i < targetDim.size(); i++) {
                final HDLExpression source = sourceDim.get(i);
                final Optional<BigInteger> s = ConstantEvaluate.valueOf(source, context);
                if (!s.isPresent()) {
                    problems.add(new Problem(ARRAY_DIMENSIONS_NOT_CONSTANT, right.get()));
                }
                final HDLExpression target = targetDim.get(i);
                final Optional<BigInteger> t = ConstantEvaluate.valueOf(target, context);
                if (!t.isPresent()) {
                    problems.add(new Problem(ARRAY_DIMENSIONS_NOT_CONSTANT, left));
                }
                if ((t.isPresent()) && (s.isPresent()))
                    if (!s.get().equals(t.get())) {
                        problems.add(new Problem(ARRAY_ASSIGNMENT_NOT_SAME_DIMENSIONS, ass));
                    }
            }
        }
    }

    private static HDLEvaluationContext getContext(Map<HDLQualifiedName, HDLEvaluationContext> hContext,
            IHDLObject var) {
        final HDLUnit container = var.getContainer(HDLUnit.class);
        if (container == null)
            return null;
        if (var.getClassType() == HDLClass.HDLInterfaceRef) {
            final HDLInterfaceRef hir = (HDLInterfaceRef) var;
            final HDLInterfaceInstantiation hii = HDLQuery.select(HDLInterfaceInstantiation.class).from(container)
                    .where(HDLInstantiation.fVar).lastSegmentIs(hir.getHIfRefName().getLastSegment()).getFirst();
            if (hii != null) {
                final Optional<HDLInterface> resolveHIf = hii.resolveHIf();
                if (resolveHIf.isPresent()) {
                    final HDLInterface hdlInterface = resolveHIf.get();
                    final HDLUnit unit = container.getLibrary().getUnit(hdlInterface.asRef());
                    HDLEvaluationContext defaultContext;
                    if (unit != null) {
                        defaultContext = HDLEvaluationContext.createDefault(unit);
                    } else {
                        defaultContext = HDLEvaluationContext.createDefault(hdlInterface);
                    }
                    return hii.getContext(defaultContext);
                }
            }
        }
        final HDLEvaluationContext hdlEvaluationContext = hContext.get(fullNameOf(container));
        return hdlEvaluationContext;
    }

    private static void checkClockAndResetAnnotation(HDLPackage pkg, Set<Problem> problems) {
        final ArrayList<HDLUnit> units = pkg.getUnits();
        for (final HDLUnit unit : units) {
            final Collection<HDLAnnotation> clocks = HDLQuery.select(HDLAnnotation.class).from(unit)
                    .where(HDLAnnotation.fName).isEqualTo(HDLBuiltInAnnotations.clock.toString()).getAll();
            if (clocks.size() > 1) {
                for (final HDLAnnotation anno : clocks) {
                    problems.add(new Problem(ONLY_ONE_CLOCK_ANNOTATION_ALLOWED, anno));
                }
            }
            final Collection<HDLAnnotation> resets = HDLQuery.select(HDLAnnotation.class).from(unit)
                    .where(HDLAnnotation.fName).isEqualTo(HDLBuiltInAnnotations.reset.toString()).getAll();
            if (resets.size() > 1) {
                for (final HDLAnnotation anno : resets) {
                    problems.add(new Problem(ONLY_ONE_RESET_ANNOTATION_ALLOWED, anno));
                }
            }
        }
    }

    @Override
    public Class<?> getErrorClass() {
        return ErrorCode.class;
    }

    @Override
    public HDLAdvise advise(Problem problem) {
        return BuiltInAdvisor.advise(problem);
    }

    @Override
    public String getName() {
        return "PSHDL Validator";
    }

}