/******************************************************************************* * Copyright (c) 2015-2021 MINRES Technologies GmbH and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * MINRES Technologies GmbH - initial API and implementation *******************************************************************************/ package com.minres.scviewer.database.vcd; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayDeque; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.NavigableMap; import java.util.TreeMap; import java.util.Vector; import java.util.zip.GZIPInputStream; import com.google.common.collect.Iterables; import com.minres.scviewer.database.BitVector; import com.minres.scviewer.database.DoubleVal; import com.minres.scviewer.database.IEvent; import com.minres.scviewer.database.IWaveform; import com.minres.scviewer.database.IWaveformDb; import com.minres.scviewer.database.IWaveformDbLoader; import com.minres.scviewer.database.InputFormatException; import com.minres.scviewer.database.RelationType; /** * The Class VCDDb. */ public class VCDDbLoader implements IWaveformDbLoader, IVCDDatabaseBuilder { /** The Constant TIME_RES. */ private static final Long TIME_RES = 1000L; // ps /** The module stack. */ private ArrayDeque moduleStack; /** The signals. */ private List signals; /** The max time. */ private long maxTime; /** The pcs. */ protected PropertyChangeSupport pcs = new PropertyChangeSupport(this); private static boolean isGzipped(File f) { try (InputStream is = new FileInputStream(f)) { byte [] signature = new byte[2]; int nread = is.read( signature ); //read the gzip signature return nread == 2 && signature[ 0 ] == (byte) 0x1f && signature[ 1 ] == (byte) 0x8b; } catch (IOException e) { return false; } } /** * Can load. * * @param inputFile the input file * @return true, if successful */ @Override public boolean canLoad(File inputFile) { if(!inputFile.isDirectory() || inputFile.exists()) { String name = inputFile.getName(); if(!(name.endsWith(".vcd") || name.endsWith(".vcdz") || name.endsWith(".vcdgz") || name.endsWith(".vcd.gz")) ) return false; boolean gzipped = isGzipped(inputFile); try(InputStream stream = gzipped ? new GZIPInputStream(new FileInputStream(inputFile)) : new FileInputStream(inputFile)){ byte[] buffer = new byte[8]; if (stream.read(buffer, 0, buffer.length) == buffer.length) { return buffer[0]=='$'; } } catch (Exception e) { return false; } } return false; } /* (non-Javadoc) * @see com.minres.scviewer.database.ITrDb#load(java.io.File) */ @SuppressWarnings("unchecked") @Override public void load(IWaveformDb db, File file) throws InputFormatException { dispose(); this.maxTime=0; boolean res = false; try { signals = new Vector<>(); moduleStack= new ArrayDeque<>(); FileInputStream fis = new FileInputStream(file); res = new VCDFileParser(false).load(isGzipped(file)?new GZIPInputStream(fis):fis, this); moduleStack=null; } catch(IOException e) { moduleStack=null; throw new InputFormatException(e.toString()); } if(!res) throw new InputFormatException("Could not parse VCD file"); // calculate max time of this database for(IWaveform waveform:signals) { NavigableMap events =waveform.getEvents(); if(!events.isEmpty()) maxTime= Math.max(maxTime, events.lastKey()); } // extend signals to have a last value set at max time for(IWaveform s:signals){ if(s instanceof VCDSignal) { TreeMap events = (TreeMap) ((VCDSignal)s).getEvents(); if(events.size()>0 && events.lastKey())s).addSignalChange(maxTime, (BitVector) val); } else if(val instanceof DoubleVal) ((VCDSignal)s).addSignalChange(maxTime, (DoubleVal) val); } } } pcs.firePropertyChange(IWaveformDbLoader.LOADING_FINISHED, null, null); } public void dispose() { moduleStack=null; signals=null; } /* (non-Javadoc) * @see com.minres.scviewer.database.ITrDb#getMaxTime() */ @Override public Long getMaxTime() { return maxTime; } /* (non-Javadoc) * @see com.minres.scviewer.database.ITrDb#getAllWaves() */ @Override public Collection getAllWaves() { return signals; } /* (non-Javadoc) * @see com.minres.scviewer.database.vcd.ITraceBuilder#enterModule(java.lang.String) */ @Override public void enterModule(String tokenString) { if(moduleStack.isEmpty()) { if("SystemC".compareTo(tokenString)!=0) moduleStack.push(tokenString); } else moduleStack.push(moduleStack.peek()+"."+tokenString); } /* (non-Javadoc) * @see com.minres.scviewer.database.vcd.ITraceBuilder#exitModule() */ @Override public void exitModule() { if(!moduleStack.isEmpty()) moduleStack.pop(); } /* (non-Javadoc) * @see com.minres.scviewer.database.vcd.ITraceBuilder#newNet(java.lang.String, int, int) */ @SuppressWarnings("unchecked") @Override public Integer newNet(String name, int i, int width) { String netName = moduleStack.isEmpty()? name: moduleStack.peek()+"."+name; int id = signals.size(); if(width==0) { signals.add( i<0 ? new VCDSignal(id, netName, width) : new VCDSignal((VCDSignal)signals.get(i), id, netName)); } else if(width>0){ signals.add( i<0 ? new VCDSignal(id, netName, width) : new VCDSignal((VCDSignal)signals.get(i), id, netName)); } pcs.firePropertyChange(IWaveformDbLoader.SIGNAL_ADDED, null, Iterables.getLast(signals)); return id; } /* (non-Javadoc) * @see com.minres.scviewer.database.vcd.ITraceBuilder#getNetWidth(int) */ @Override public int getNetWidth(int intValue) { VCDSignal signal = (VCDSignal) signals.get(intValue); return signal.getRowCount(); } /* (non-Javadoc) * @see com.minres.scviewer.database.vcd.ITraceBuilder#appendTransition(int, long, com.minres.scviewer.database.vcd.BitVector) */ @SuppressWarnings("unchecked") @Override public void appendTransition(int signalId, long currentTime, BitVector value) { VCDSignal signal = (VCDSignal) signals.get(signalId); Long time = currentTime* TIME_RES; signal.addSignalChange(time, value); } /* (non-Javadoc) * @see com.minres.scviewer.database.vcd.ITraceBuilder#appendTransition(int, long, com.minres.scviewer.database.vcd.BitVector) */ @SuppressWarnings("unchecked") @Override public void appendTransition(int signalId, long currentTime, double value) { VCDSignal signal = (VCDSignal) signals.get(signalId); Long time = currentTime* TIME_RES; signal.addSignalChange(time, new DoubleVal(value)); } /* (non-Javadoc) * @see com.minres.scviewer.database.IWaveformDbLoader#getAllRelationTypes() */ @Override public Collection getAllRelationTypes(){ return Collections.emptyList(); } /** * Adds the property change listener. * * @param l the l */ @Override public void addPropertyChangeListener(PropertyChangeListener l) { pcs.addPropertyChangeListener(l); } /** * Removes the property change listener. * * @param l the l */ @Override public void removePropertyChangeListener(PropertyChangeListener l) { pcs.removePropertyChangeListener(l); } }