/******************************************************************************* * 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.applets.editor.newtb.model; import hr.fer.zemris.vhdllab.applets.editor.newtb.enums.TimeScale; import hr.fer.zemris.vhdllab.applets.editor.newtb.exceptions.UniformTestbenchException; import hr.fer.zemris.vhdllab.applets.editor.newtb.listeners.SignalChangeListener; import hr.fer.zemris.vhdllab.applets.editor.newtb.listeners.TestbenchListener; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.signals.EditableSignal; import hr.fer.zemris.vhdllab.applets.editor.newtb.model.signals.ScalarSignal; 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.applets.editor.newtb.model.signals.VectorSignal; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * * @author Davor Jurisic * */ public abstract class Testbench implements Iterable<Signal>, SignalChangeListener, TestbenchModel { protected String sourceName; protected long testBenchLength; protected TimeScale timeScale; protected Map<String, Signal> signalMap; protected long simulationLength; private List<TestbenchListener> listeners = null; private Set<Signal> suspendedChangedSignals = null; private boolean signalChangedEventsSuspended; public Testbench(String sourceName, long testBenchLength, TimeScale timeScale) throws UniformTestbenchException { if(sourceName == null || sourceName.length() == 0) throw new UniformTestbenchException("SourceName ne smije biti null ili duljine 0."); if(testBenchLength <= 0) throw new UniformTestbenchException("testBenchLength mora biti veci ili jednak 1."); if(timeScale == null) throw new UniformTestbenchException("timeScale ne smije biti null."); this.sourceName = sourceName; this.testBenchLength = testBenchLength; this.timeScale = timeScale; this.simulationLength = 0; // changed to LinkedHashMap by Miro to preserve signal ordering this.signalMap = new LinkedHashMap<String, Signal>(); this.listeners = new ArrayList<TestbenchListener>(); this.suspendedChangedSignals = new HashSet<Signal>(); this.signalChangedEventsSuspended = false; } public abstract long getPeriodLength(); public List<Signal> getSignals() { return new ArrayList<Signal>(this.signalMap.values()); } public Signal getSignal(String signalName) throws UniformTestbenchException { if(signalName == null) { throw new UniformTestbenchException("signalName ne smije biti null."); } return this.signalMap.get(signalName); } public void addSignal(EditableSignal signal) throws UniformTestbenchException { if(signal == null) { throw new UniformTestbenchException("signal ne smije biti null."); } if(!this.signalMap.containsKey(signal.getName())) { this.signalMap.put(signal.getName(), signal); signal.setSignalChangeListener(this); } } public long getTestBenchLength() { return testBenchLength; } public void setTestBenchLength(long testBenchLength) throws UniformTestbenchException { if(testBenchLength <= 0) throw new UniformTestbenchException("testBenchLength mora biti veci ili jednak 1."); long oldTestBenchLength = this.testBenchLength; this.testBenchLength = testBenchLength; if(oldTestBenchLength != this.testBenchLength) { this.fireTestBenchLengthChangedEvent(); } } public TimeScale getTimeScale() { return timeScale; } public long getSimulationLength() { return simulationLength; } public void setSimulationLength(long simulationLength) throws UniformTestbenchException { if(simulationLength < 0) throw new UniformTestbenchException("simulationLength mora biti veci ili jednak 0."); long oldSimulationLength = this.simulationLength; this.simulationLength = simulationLength; if(oldSimulationLength != this.simulationLength) { this.fireSimulationLengthChangedEvent(); } } public String getSourceName() { return sourceName; } protected Element getSignalsElement(Document xmldoc) { Element signalsElement = xmldoc.createElementNS(null, "Signals"); Element sElement = null; for(Signal s : this) { if(s.getClass() == VectorSignal.class || s.getClass() == ScalarSignal.class) { sElement = xmldoc.createElementNS(null, "Signal"); sElement.setAttribute("name", s.getName()); if(s.getDimension() == 1) { sElement.setAttribute("type", "scalar"); } else { sElement.setAttribute("type", "vector"); sElement.setAttribute("dimension", String.valueOf(s.getDimension())); sElement.setAttribute("direction", ((VectorSignal)s).getDirection().toString()); } StringBuilder sb = new StringBuilder(); for(SignalChange sc : s) { sb.append("("); sb.append(String.valueOf(sc.getTime() / TimeScale.getMultiplier(this.timeScale))); sb.append(","); sb.append(sc.getSignalValue()); sb.append(")"); } sElement.appendChild(xmldoc.createTextNode(sb.toString())); signalsElement.appendChild(sElement); } } return signalsElement; } protected Element getTestBenchInfoElement(Document xmldoc) { Element testBenchInfoElement = xmldoc.createElementNS(null, "TestBenchInfo"); Element sourceNameElement = xmldoc.createElementNS(null, "SourceName"); Element simulationLengthElement = xmldoc.createElementNS(null, "SimulationLength"); testBenchInfoElement.appendChild(sourceNameElement); testBenchInfoElement.appendChild(simulationLengthElement); sourceNameElement.appendChild(xmldoc.createTextNode(this.sourceName)); simulationLengthElement.appendChild(xmldoc.createTextNode(String.valueOf(this.simulationLength / TimeScale.getMultiplier(this.timeScale)))); return testBenchInfoElement; } private void increaseTestbenchLength(long newLength) { try { if(newLength > this.testBenchLength) { this.setTestBenchLength(newLength); } } catch (UniformTestbenchException e) { e.printStackTrace(); } } protected abstract Element getInitTimingInfoElement(Document xmldoc); public abstract String toXml(); @Override public void suspendSignalChangedEvents() { this.signalChangedEventsSuspended = true; } @Override public void resumeSignalChangedEvents() { this.signalChangedEventsSuspended = false; for(Signal s : this.suspendedChangedSignals) { this.signalChanged(s); } this.suspendedChangedSignals.clear(); } @Override public void signalChanged(Signal signal) { if(this.signalChangedEventsSuspended) { this.suspendedChangedSignals.add(signal); } else { if(this.signalMap.get(signal.getName()) != null) { long signalLength = this.signalMap.get(signal.getName()).getSignalLength(); if(signalLength > this.testBenchLength) { this.increaseTestbenchLength(signalLength + this.getPeriodLength()); } } this.fireSignalChangedEvent(signal.getName()); } } @Override public boolean signalChangedEventsSuspended() { return this.signalChangedEventsSuspended; } @Override public Iterator<Signal> iterator() { return this.signalMap.values().iterator(); } protected void fireSignalChangedEvent(String signalName) { for(TestbenchListener l : this.listeners) { l.signalChanged(signalName); } } protected void fireTestBenchLengthChangedEvent() { for(TestbenchListener l : this.listeners) { l.testBenchLengthChanged(); } } protected void fireSimulationLengthChangedEvent() { for(TestbenchListener l : this.listeners) { l.simulationLengthChanged(); } } @Override public void addTestbenchListener(TestbenchListener l) { this.listeners.add(l); } @Override public Testbench getTestbench() { return this; } @Override public void removeTestbenchListener(TestbenchListener l) { this.listeners.remove(l); } }