hr.fer.zemris.vhdllab.service.extractor.testbench.TestbenchMetadataExtractor.java Source code

Java tutorial

Introduction

Here is the source code for hr.fer.zemris.vhdllab.service.extractor.testbench.TestbenchMetadataExtractor.java

Source

/*******************************************************************************
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package hr.fer.zemris.vhdllab.service.extractor.testbench;

import hr.fer.zemris.vhdllab.applets.editor.newtb.enums.TimeScale;
import hr.fer.zemris.vhdllab.applets.editor.newtb.exceptions.UniformTestbenchParserException;
import hr.fer.zemris.vhdllab.applets.editor.newtb.model.Testbench;
import hr.fer.zemris.vhdllab.applets.editor.newtb.model.TestbenchParser;
import hr.fer.zemris.vhdllab.applets.editor.newtb.model.signals.Signal;
import hr.fer.zemris.vhdllab.applets.editor.newtb.model.signals.SignalChange;
import hr.fer.zemris.vhdllab.entity.File;
import hr.fer.zemris.vhdllab.service.ci.CircuitInterface;
import hr.fer.zemris.vhdllab.service.ci.Port;
import hr.fer.zemris.vhdllab.service.exception.CircuitInterfaceExtractionException;
import hr.fer.zemris.vhdllab.service.exception.DependencyExtractionException;
import hr.fer.zemris.vhdllab.service.exception.VhdlGenerationException;
import hr.fer.zemris.vhdllab.service.extractor.AbstractMetadataExtractor;
import hr.fer.zemris.vhdllab.service.result.Result;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.NotImplementedException;

public class TestbenchMetadataExtractor extends AbstractMetadataExtractor {

    @Override
    public CircuitInterface extractCircuitInterface(File file) throws CircuitInterfaceExtractionException {
        return new CircuitInterface(file.getName());
    }

    @Override
    protected CircuitInterface doExtractCircuitInterface(String data) throws CircuitInterfaceExtractionException {
        throw new NotImplementedException();
    }

    @Override
    protected Set<String> doExtractDependencies(String data) throws DependencyExtractionException {
        Testbench tb;
        try {
            tb = TestbenchParser.parseXml(data);
        } catch (UniformTestbenchParserException e) {
            throw new DependencyExtractionException(e);
        }
        return Collections.singleton(tb.getSourceName());
    }

    @Override
    public Result generateVhdl(File file) throws VhdlGenerationException {
        Testbench tbInfo;
        try {
            tbInfo = TestbenchParser.parseXml(file.getData());
        } catch (UniformTestbenchParserException e) {
            throw new VhdlGenerationException(e);
        }

        String name = tbInfo.getSourceName();
        File source = fileDao.findByName(file.getProject().getId(), name);

        CircuitInterface ci = metadataExtractor.extractCircuitInterface(source);
        String vhdl = null;
        try {
            vhdl = generirajVHDL(file.getName(), name, ci, tbInfo);
        } catch (Exception e) {
            throw new VhdlGenerationException(e);
        }
        return new Result(vhdl);
    }

    @Override
    protected Result doGenerateVhdl(String data) throws VhdlGenerationException {
        throw new NotImplementedException();
    }

    /**
     * Create a testbench VHDL code from circuit interface and testbench.
     * 
     * @param testBenchFileName
     *            name of the created testbench for particular component
     * @param testedFileName
     *            name of the component for which we created testbench
     * @param ci
     *            a circuit interface
     * @param tbInfo
     *            information about testbench from inducement
     * @return a string representing testbench VHDL.
     * 
     * @throws NullPointerException
     *             if <code>ci</code> or <code>testedFileName</code> or
     *             <code>testBnechFileName</code> or <code>tbInfo</code> is
     *             <code>null</code>.
     * 
     */
    private String generirajVHDL(String testBenchFileName, String testedFileName, CircuitInterface ci,
            Testbench tbInfo) {
        if (testedFileName == null)
            throw new NullPointerException("Tested filename can not be null.");
        if (testBenchFileName == null)
            throw new NullPointerException("TestBench filename can not be null.");
        if (tbInfo == null)
            throw new NullPointerException("TestBench filename can not be null.");
        long TestBenchLength = tbInfo.getTestBenchLength();
        int bufferSize = 0;
        if (tbInfo.getSimulationLength() != 0)
            TestBenchLength = tbInfo.getSimulationLength();
        for (Signal s : tbInfo.getSignals()) {
            bufferSize += s.getSignalChangeList(0, TestBenchLength).size() * 12;
        }
        StringBuilder vhdl = new StringBuilder(bufferSize);
        vhdl.append("library IEEE;\n").append("use IEEE.STD_LOGIC_1164.ALL;\n").append("\n").append("entity ")
                .append(testBenchFileName).append(" is \n").append("end ").append(testBenchFileName).append(";\n")
                .append("\n").append("architecture structural of ").append(testBenchFileName).append(" is \n");
        populateComponent(ci, vhdl);
        vhdl.append("end component;\n").append("\n");
        populateSignals(ci, vhdl);
        vhdl.append("begin\n");
        populatePortMap(ci, vhdl);
        vhdl.append("\n").append("\tprocess").append("\n").append("\tbegin").append("\n");
        populateProcess(ci, tbInfo, vhdl);
        vhdl.append("\n").append("\tend process;\n").append("end structural;");
        return vhdl.toString();
    }

    /**
     * Populate a process in testbench VHDL.
     * 
     * @param ci
     *            a circuit interface from where to draw information.
     * @param tbInfo
     *            information about testbench from inducement.
     * @param vhdl
     *            a string builder where to store testbench VHDL.
     */
    private void populateProcess(CircuitInterface ci, Testbench tbInfo, StringBuilder vhdl) {
        List<ChangesInMoment> list = createProcessList(ci, tbInfo);
        tbInfo.getTimeScale();
        long time = -1;
        for (ChangesInMoment slot : list) {
            if (time != -1) {
                vhdl.append("\t\twait for ")
                        .append(slot.getTime() / TimeScale.getMultiplier(tbInfo.getTimeScale())
                                - time / TimeScale.getMultiplier(tbInfo.getTimeScale()))
                        .append(" ").append(tbInfo.getTimeScale().toString()).append(";\n\n");
            }
            for (ChangeWithSignalName change : slot.getList()) {
                String state = change.getChange().getSignalValue();
                vhdl.append("\t\t").append(getTestbenchSignal(change.getSignalName())).append(" <= ");
                if (ci.getPort(change.getSignalName()).isScalar())
                    vhdl.append("\'").append(state).append("\';\n");
                else
                    vhdl.append("\"").append(state).append("\";\n");
            }
            time = slot.getTime();
        }
        vhdl.append("\t\twait for ")
                .append(tbInfo.getPeriodLength() / TimeScale.getMultiplier(tbInfo.getTimeScale())).append(" ")
                .append(tbInfo.getTimeScale().toString()).append(";\n");
        vhdl.append("\t\twait;\n");

    }

    /**
     * Create a process list. This method will create a list of objects
     * ChangesInMoment from where method populateProcess(CircuitInterface,
     * Testbench, StringBuilder) will draw information regarding signal changes
     * written in Testbench.
     * 
     * @param ci
     *            a circuit interface from where to draw information.
     * @param tbInfo
     *            information about testbench from inducement.
     */
    private List<ChangesInMoment> createProcessList(CircuitInterface ci, Testbench tbInfo) {
        ChangesInMoment tmp = new ChangesInMoment(0);
        Map<ChangesInMoment, ChangesInMoment> table = new HashMap<ChangesInMoment, ChangesInMoment>();
        for (Signal s : tbInfo.getSignals()) {
            Port port = ci.getPort(s.getName());
            if (port == null || !port.isIN())
                continue;
            long TestBenchLength = tbInfo.getTestBenchLength();
            if (tbInfo.getSimulationLength() != 0)
                TestBenchLength = tbInfo.getSimulationLength();
            for (SignalChange i : s.getSignalChangeList(0, TestBenchLength)) {
                tmp.setTime(i.getTime());
                ChangesInMoment list = table.get(tmp);
                if (list == null) {
                    list = new ChangesInMoment(tmp.getTime());
                    table.put(list, list);
                }
                ChangeWithSignalName imp = new ChangeWithSignalName(s.getName(), i);
                list.addChangeWithSignalName(imp);
            }
        }
        Collection<ChangesInMoment> c = table.values();
        List<ChangesInMoment> l = new ArrayList<ChangesInMoment>(c);
        Collections.sort(l);
        return l;

    }

    /**
     * This class contains information about each signal change in the
     * particular time moment. It contains time of change and list of
     * ChangeWithSignalName structure.
     * 
     * @author Ivan Kmetovic
     * 
     */
    private static class ChangesInMoment implements Comparable<ChangesInMoment> {

        private long time;
        private List<ChangeWithSignalName> list = new ArrayList<ChangeWithSignalName>();
        private List<ChangeWithSignalName> list_ro = Collections.unmodifiableList(list);

        /**
         * Construct an instance of this class using a time. A time must be a
         * non-negative number.
         * 
         * @param time
         */
        public ChangesInMoment(long time) {
            setTime(time);
        }

        /**
         * Getter method for time of signal change.
         * 
         * @return a time of signal change.
         */
        public long getTime() {
            return time;
        }

        /**
         * Setter method for a time of signal change.
         * 
         * @param time
         *            a time of signal change.
         * @throws IllegalArgumentException
         *             if <code>time</code> is null.
         */
        public void setTime(long time) {
            if (time < 0)
                throw new IllegalArgumentException("Time can not be negative.");
            this.time = time;
        }

        /**
         * Getter method for a read-only list of ChangeWithSignalName.
         * 
         * @return a read-only list of ChangeWithSignalName.
         */
        public List<ChangeWithSignalName> getList() {
            return list_ro;
        }

        /**
         * Append an ChangeWithSignalName to the end of this list.
         * 
         * @param i
         *            an ChangeWithSignalName to add.
         * @return <code>true</code> if <code>i</code> has been added;
         *         <code>false</code> otherwise.
         * @throws NullPointerException
         *             if <code>i</code> is <code>null</code>.
         */
        public boolean addChangeWithSignalName(ChangeWithSignalName i) {
            if (i == null)
                throw new NullPointerException("ChangeWithSignalName can not be null.");
            if (getList().contains(i))
                return false;
            list.add(i);
            return true;
        }

        /**
         * Compares this object ChangesInMoment with the specified object for
         * order. These two objects are compared by time.
         * 
         * @param other
         *            <code>ChangesInMoment</code> to be compared.
         * @return the value 0 if the argument object ChangesInMoment is equal
         *         to this object; a value less than 0 if this object has time
         *         less than the object argument; and a value greater than 0 if
         *         this object has time greater than the object argument.
         * @throws ClassCastException
         *             if the specified object's type prevents it from being
         *             compared to this ChangesInMoment.
         */
        public int compareTo(ChangesInMoment other) {
            long res = this.time - other.getTime();
            if (res < 0)
                return -1;
            else if (res > 0)
                return 1;
            else
                return 0;
        }

        /**
         * Compares this object ChangesInMoment to the specified object. Returns
         * <code>true</code> if and only if the specified object is also a
         * <code>ChangesInMoment</code> and if times are the same.
         * 
         * @param o
         *            the object to compare this <code>ChangesInMoment</code>
         *            against.
         * @return <code>true</code> if <code>ChangesInMoment</code> are equal;
         *         <code>false</code> otherwise.
         */
        @Override
        public boolean equals(Object o) {
            if (!(o instanceof ChangesInMoment))
                return false;
            ChangesInMoment other = (ChangesInMoment) o;
            return this.time == other.getTime();
        }

        /**
         * Returns a hash code value for this <code>ChangesInMoment</code>
         * instance. The hash code of <code>ChangesInMoment</code> instance is
         * hash code of time.
         * <p>
         * This ensures that <code>s1.equals(s2)</code> implies that
         * <code>s1.hashCode() == s2.hashCode()</code> for any two
         * ChangesInMoment objects, <code>s1</code> and <code>s2</code>, as
         * required by the general contract of <code>Object.hashCode</code>.
         * 
         * @return a hash code value for this <code>ChangesInMoment</code>
         *         instance.
         * @see java.lang.String#hashCode()
         * @see java.util.List#hashCode()
         */
        @Override
        public int hashCode() {
            return Long.valueOf(time).hashCode();
        }

    }

    /**
     * This class contains information regarding a signal change in a certain
     * time. It contains of signal name and an class that describes signal
     * change.
     * 
     * @author Ivan Kmetovic
     * 
     */
    private static class ChangeWithSignalName {

        private String signalName;
        private SignalChange change;

        /**
         * Construct an instance of this class using a signal name and an signal
         * change to explain an ChangeWithSignalName. A signal name must have
         * the following format:
         * <ul>
         * <li>it must contain only alpha (only letters of english alphabet),
         * numeric (digits 0 to 9) or underscore (_) characters
         * <li>it must not start with a non-alpha character
         * <li>it must not end with an underscore character
         * <li>it must not contain an underscore character after an underscore
         * character
         * <li>it must not be a reserved word (check at
         * hr.fer.zemris.vhdllab.utilities.NotValidVHDLNames.txt)
         * </ul>
         * 
         * @param name
         *            a name of a signal.
         * @param change
         *            information about signal change
         * @throws NullPointerException
         *             if <code>name</code> or an <code>impulse</code> is
         *             <code>null</code>.
         * @throws IllegalArgumentException
         *             if <code>name</code> is not of correct format.
         */
        public ChangeWithSignalName(String name, SignalChange change) {
            if (change == null)
                throw new NullPointerException("Change can not be null.");
            if (name == null)
                throw new NullPointerException("Signal name can not be null.");
            this.signalName = name;
            this.change = change;
        }

        /**
         * Getter method for an SignalChange.
         * 
         * @return a description of signal change.
         */
        public SignalChange getChange() {
            return change;
        }

        /**
         * Getter method for a name of a signal.
         * 
         * @return a name of a signal.
         */
        public String getSignalName() {
            return signalName;
        }

        /**
         * Compares this ChangeWithSignalName to the specified object. Returns
         * <code>true</code> if and only if the specified object is also a
         * <code>ChangeWithSignalName</code> and if signal names and described
         * signal changes are the same.
         * 
         * @param o
         *            the object to compare this
         *            <code>ChangeWithSignalName</code> against.
         * @return <code>true</code> if <code>ChangeWithSignalName</code> are
         *         equal; <code>false</code> otherwise.
         */
        @Override
        public boolean equals(Object o) {
            if (o == null)
                return false;
            if (!(o instanceof ChangeWithSignalName))
                return false;
            ChangeWithSignalName other = (ChangeWithSignalName) o;
            return other.getSignalName().equalsIgnoreCase(this.signalName) && other.getChange().equals(this.change);
        }

    }

    /**
     * Populate a port map in testbench VHDL.
     * 
     * @param ci
     *            a circuit interface from where to draw information.
     * @param vhdl
     *            a string builder where to store testbench VHDL.
     */
    private void populatePortMap(CircuitInterface ci, StringBuilder vhdl) {
        vhdl.append("uut: ").append(ci.getName()).append(" port map ( ");
        for (Port p : ci.getPorts()) {
            vhdl.append(getTestbenchSignal(p.getName())).append(", ");
        }
        vhdl.deleteCharAt(vhdl.length() - 2); // deleting last ','
        vhdl.append(");\n");
    }

    /**
     * Populate a signal list in testbench VHDL.
     * 
     * @param ci
     *            a circuit interface from where to draw information.
     * @param vhdl
     *            a string builder where to store testbench VHDL.
     */
    private void populateSignals(CircuitInterface ci, StringBuilder vhdl) {
        for (Port p : ci.getPorts()) {
            vhdl.append("\tsignal ").append(getTestbenchSignal(p.getName())).append(" : ").append(p.getTypeName());
            if (p.isVector())
                vhdl.append("(").append(p.getFrom()).append(" ").append(p.getDirectionName()).append(" ")
                        .append(p.getTo()).append(")");
            vhdl.append(";\n");
        }
    }

    /**
     * Create a testbench signal name from a given signal name. Testbench signal
     * name will have the following format: <blockquote> tb_'signalName'
     * </blockquote>
     * 
     * @param name
     *            a name of a signal.
     * @return a testbench signal name.
     */
    private String getTestbenchSignal(String name) {
        return "tb_" + name;
    }

    /**
     * Populate a component in testbench VHDL.
     * 
     * @param ci
     *            a circuit interface from where to draw information.
     * @param vhdl
     *            a string builder where to store testbench VHDL.
     */
    private void populateComponent(CircuitInterface ci, StringBuilder vhdl) {
        vhdl.append("component ").append(ci.getName()).append(" is port (\n");
        for (Port p : ci.getPorts()) {
            vhdl.append("\t").append(p.getName()).append(" : ").append(p.getDirection().toString()).append(" ")
                    .append(p.getTypeName());
            if (p.isVector())
                vhdl.append("(").append(p.getFrom()).append(" ").append(p.getDirectionName()).append(" ")
                        .append(p.getTo()).append(")");
            vhdl.append(";\n");
        }
        vhdl.deleteCharAt(vhdl.length() - 2) // deleting last ';'
                .append(");\n");
    }

}