/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: BusSample.java * * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * * Electric(tm) 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. * * Electric(tm) 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 Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.tool.simulation; import com.sun.electric.database.geometry.Poly; import com.sun.electric.database.geometry.PolyBase; import com.sun.electric.database.variable.TextDescriptor; import com.sun.electric.tool.user.waveform.Panel; import com.sun.electric.tool.user.waveform.WaveSignal; import com.sun.electric.tool.user.waveform.Panel.WaveSelection; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.font.GlyphVector; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.HashSet; import java.util.List; import java.util.TreeMap; /** * A bus of many Signal<S> is represented as a single * Signal<BusSample<S>>. */ public class BusSample<S extends Sample> implements Sample { private final Sample[] vals; public BusSample(Sample[] vals) { int numValids = 0; for(int i=0; i<vals.length; i++) if (vals[i] != null) numValids++; this.vals = new Sample[numValids]; int fill = 0; for(int i=0; i<vals.length; i++) if (vals[i] != null) this.vals[fill++] = vals[i]; } public int getWidth() { return vals.length; } public S getTrace(int i) { return (S)vals[i]; } public boolean equals(Object o) { if (o==null) return false; if (!(o instanceof BusSample<?>)) return false; BusSample<?> bo = (BusSample<?>)o; if (bo.vals.length != vals.length) return false; for(int i=0; i<vals.length; i++) if (!vals[i].equals(bo.vals[i])) return false; return true; } public int hashCode() { int ret = 0; for(int i=0; i<vals.length; i++) ret ^= vals[i].hashCode(); return ret; } public boolean isLogicX() { for(Sample s : vals) if (!s.isLogicX()) return false; return true; } public boolean isLogicZ() { for(Sample s : vals) if (!s.isLogicZ()) return false; return true; } public Sample lub(Sample s) { if (!(s instanceof BusSample<?>)) throw new RuntimeException("tried to call BusSample.lub("+s.getClass().getName()+")"); BusSample<?> ds = (BusSample<?>)s; if (ds.vals.length != vals.length) throw new RuntimeException("tried to call lub() on BusSamples of different width"); Sample[] ret = new Sample[vals.length]; for(int i=0; i<ret.length; i++) ret[i] = vals[i].lub(ds.vals[i]); return new BusSample<DigitalSample>(ret); } public Sample glb(Sample s) { if (!(s instanceof BusSample<?>)) throw new RuntimeException("tried to call BusSample.glb("+s.getClass().getName()+")"); BusSample<?> ds = (BusSample<?>)s; if (ds.vals.length != vals.length) throw new RuntimeException("tried to call glb() on BusSamples of different width"); Sample[] ret = new Sample[vals.length]; for(int i=0; i<ret.length; i++) ret[i] = vals[i].glb(ds.vals[i]); return new BusSample<DigitalSample>(ret); } public double getMinValue() { double min = Double.MAX_VALUE; for(int i=0; i<vals.length; i++) min = Math.min(min, vals[i].getMinValue()); return min; } public double getMaxValue() { double max = -Double.MAX_VALUE; for(int i=0; i<vals.length; i++) max = Math.max(max, vals[i].getMaxValue()); return max; } /** create a MutableSignal<BusSample<SS>> */ public static <SS extends Sample> Signal<BusSample<SS>> createSignal(SignalCollection sc, Stimuli sd, String signalName, String signalContext, int width) { /* final Unboxed<BusSample<SS>> unboxer = new Unboxed<BusSample<SS>>() { public int getSize() { return 1; } public DigitalSample deserialize(byte[] buf, int ofs) { return fromByteRepresentation(buf[ofs]); } public void serialize(DigitalSample v, byte[] buf, int ofs) { buf[ofs] = v.getByteRepresentation(); } public int compare(byte[] buf1, int ofs1, byte[] buf2, int ofs2) { return (buf1[ofs1]&0xff)-(buf2[ofs2]&0xff); } }; Signal<ScalarSample> ret = new BTreeSignal<ScalarSample>(an, signalName, signalContext, BTreeSignal.getTree(unboxer, latticeOp)) { public boolean isAnalog() { return true; } }; an.addSignal(ret); return ret; */ throw new RuntimeException("not implemented"); } /** create a Signal<BusSample<S>> from preexisting Signal<S>'s */ public static <SS extends Sample> Signal<BusSample<SS>> createSignal(SignalCollection sc, Stimuli sd, String signalName, String signalContext, boolean digital, final Signal<SS>[] subsignals) { return new Signal<BusSample<SS>>(sc, sd, signalName, signalContext, digital) { public boolean isEmpty() { for(Signal<SS> sig : subsignals) if (!sig.isEmpty()) return false; return true; } public Signal<?>[] getBusMembers() { return subsignals; } public Signal.View<RangeSample<BusSample<SS>>> getRasterView(final double t0, final double t1, final int numPixels) { final Signal.View<RangeSample<SS>>[] subviews = new Signal.View[subsignals.length]; for(int i=0; i<subviews.length; i++) subviews[i] = subsignals[i].getRasterView(t0, t1, numPixels); // the subviews' getRasterView() methods might have differing getNumEvents() values or different // getTime() values for a given index. Therefore, we must "collate" them. By using a sorted treemap // here we ensure that this takes only O(n log n) time. // INVARIANT: tm.get(t).contains(i) ==> (exists j such that subviews[i].getTime(j)==t) TreeMap<Double,HashSet<Integer>> tm = new TreeMap<Double,HashSet<Integer>>(); for (int i=0; i<subviews.length; i++) { Signal.View<RangeSample<SS>> view = subviews[i]; for(int j=0; j<view.getNumEvents(); j++) { Double t = new Double(view.getTime(j)); HashSet<Integer> hs = tm.get(t); if (hs == null) tm.put(t, hs = new HashSet<Integer>()); hs.add(new Integer(i)); } } // now we know, for each point in time, which signals changed at that point final double[] times = new double[tm.size()]; final RangeSample<BusSample<SS>>[] vals = new RangeSample[tm.size()]; int i = 0; int[] event = new int[subviews.length]; SS[] minvals = (SS[])new Sample[subviews.length]; SS[] maxvals = (SS[])new Sample[subviews.length]; for(Double t : tm.keySet()) { times[i] = t.doubleValue(); HashSet<Integer> hs = tm.get(t); for(int v : hs) { // this used to be an assertion, but since it is possible for two successive events // to have the same time, the assertion may fail if (subviews[v].getTime(event[v]) != t.doubleValue()) continue; RangeSample<SS> rs = subviews[v].getSample(event[v]); if (rs != null) { minvals[v] = rs.getMin(); maxvals[v] = rs.getMax(); } event[v]++; } vals[i] = new RangeSample<BusSample<SS>>( new BusSample<SS>(minvals), new BusSample<SS>(maxvals) ); i++; } return new Signal.View<RangeSample<BusSample<SS>>>() { public int getNumEvents() { return times.length; } public double getTime(int event) { return times[event]; } public RangeSample<BusSample<SS>> getSample(int event) { return vals[event]; } }; } public Signal.View<BusSample<SS>> getExactView() { final Signal.View<SS>[] subviews = new Signal.View[subsignals.length]; for(int i=0; i<subviews.length; i++) subviews[i] = subsignals[i].getExactView(); // the subviews' getRasterView() methods might have differing getNumEvents() values or different // getTime() values for a given index. Therefore, we must "collate" them. By using a sorted treemap // here we ensure that this takes only O(n log n) time. // INVARIANT: tm.get(t).contains(i) ==> (exists j such that subviews[i].getTime(j)==t) TreeMap<Double,HashSet<Integer>> tm = new TreeMap<Double,HashSet<Integer>>(); for (int i=0; i<subviews.length; i++) { Signal.View<SS> view = subviews[i]; for(int j=0; j<view.getNumEvents(); j++) { Double t = new Double(view.getTime(j)); HashSet<Integer> hs = tm.get(t); if (hs == null) tm.put(t, hs = new HashSet<Integer>()); hs.add(new Integer(i)); } } // now we know, for each point in time, which signals changed at that point final double[] times = new double[tm.size()]; final BusSample<SS>[] vals = new BusSample[tm.size()]; int i = 0; int[] event = new int[subviews.length]; SS[] sampleVals = (SS[])new Sample[subviews.length]; for(Double t : tm.keySet()) { times[i] = t.doubleValue(); HashSet<Integer> hs = tm.get(t); for(int v : hs) { assert subviews[v].getTime(event[v]) == t.doubleValue(); // sanity check SS rs = subviews[v].getSample(event[v]); if (rs != null) sampleVals[v] = rs; event[v]++; } vals[i] = new BusSample<SS>(sampleVals); i++; } return new Signal.View<BusSample<SS>>() { public int getNumEvents() { return times.length; } public double getTime(int event) { return times[event]; } public BusSample<SS> getSample(int event) { return vals[event]; } }; } public double getMinTime() { double min = Double.MAX_VALUE; for(Signal<SS> sig : subsignals) min = Math.min(min, sig.getMinTime()); return min; } public double getMaxTime() { double max = -Double.MAX_VALUE; for(Signal<SS> sig : subsignals) max = Math.max(max, sig.getMaxTime()); return max; } public double getMinValue() { double min = Double.MAX_VALUE; for(Signal<SS> sig : subsignals) min = Math.min(min, sig.getMinValue()); return min; } public double getMaxValue() { double max = -Double.MAX_VALUE; for(Signal<SS> sig : subsignals) max = Math.max(max, sig.getMaxValue()); return max; } public void plot(Panel panel, Graphics g, WaveSignal ws, Color light, List<PolyBase> forPs, Rectangle2D bounds, List<WaveSelection> selectedObjects, Signal<?> xAxisSignal) { Dimension sz = panel.getSize(); int hei = sz.height; // draw digital traces Signal<BusSample<DigitalSample>> ds = (Signal<BusSample<DigitalSample>>)ws.getSignal(); Signal.View<RangeSample<BusSample<DigitalSample> > > view = ds.getRasterView(panel.convertXScreenToData(0), panel.convertXScreenToData(sz.width), sz.width); int lastX = 0; int trueLen = (subsignals.length + 3) / 4 * 4; int [] values = new int[trueLen]; for(int i=0; i<view.getNumEvents(); i++) { double xValue = view.getTime(i); RangeSample<BusSample<DigitalSample> > rs = view.getSample(i); // XXX: shouldn't ignore the max! BusSample<DigitalSample> bs = rs.getMin(); int invert = trueLen - 1; for(int j=0; j<bs.getWidth(); j++) { switch (DigitalSample.getState(bs.getTrace(j)) & Stimuli.LOGIC) { case Stimuli.LOGIC_LOW: values[invert] = 0; break; case Stimuli.LOGIC_HIGH: values[invert] = 1; break; case Stimuli.LOGIC_X: values[invert] = 2; break; case Stimuli.LOGIC_Z: values[invert] = 3; break; default: values[invert] = 4; break; } invert--; } int x = panel.convertXDataToScreen(xValue); if (x >= panel.getVertAxisPos()) { if (x < panel.getVertAxisPos()+5) { // on the left edge: just draw the "<" if (panel.processALine(g, x, hei/2, x+5, hei-5, bounds, forPs, selectedObjects, ws, -1)) return; if (panel.processALine(g, x, hei/2, x+5, 5, bounds, forPs, selectedObjects, ws, -1)) return; } else { // bus change point: draw the "X" if (panel.processALine(g, x-5, 5, x+5, hei-5, bounds, forPs, selectedObjects, ws, -1)) return; if (panel.processALine(g, x+5, 5, x-5, hei-5, bounds, forPs, selectedObjects, ws, -1)) return; } if (lastX+5 < x-5) { // previous bus change point: draw horizontal bars to connect if (panel.processALine(g, lastX+5, 5, x-5, 5, bounds, forPs, selectedObjects, ws, -1)) return; if (panel.processALine(g, lastX+5, hei-5, x-5, hei-5, bounds, forPs, selectedObjects, ws, -1)) return; } String valString = "0x"; for(int j=0; j<trueLen; j++) { if (values[j] == 4) { valString = "?"; break; } if (values[j] == 3) { valString = "Z"; break; } if (values[j] == 2) { valString = "X"; break; } } if (valString.length() > 1) { for(int j=0; j<trueLen; j += 4) { int hexDigit = 0; if (values[j] == 1) hexDigit += 8; if (values[j+1] == 1) hexDigit += 4; if (values[j+2] == 1) hexDigit += 2; if (values[j+3] == 1) hexDigit += 1; if (hexDigit < 10) valString += (char)('0'+hexDigit); else valString += (char)('A'+hexDigit-10); } } if (g != null) { g.setFont(panel.getWaveWindow().getFont()); GlyphVector gv = panel.getWaveWindow().getFont().createGlyphVector(panel.getWaveWindow().getFontRenderContext(), valString); Rectangle2D glyphBounds = gv.getLogicalBounds(); int textHei = (int)glyphBounds.getHeight(); g.drawString(valString, x+2, hei/2+textHei/2); } if (forPs != null) { Point2D [] pts = new Point2D[1]; pts[0] = new Point2D.Double(x+2, hei/2); Poly poly = new Poly(pts); poly.setStyle(Poly.Type.TEXTLEFT); poly.setTextDescriptor(TextDescriptor.EMPTY.withAbsSize(8)); poly.setString(valString); forPs.add(poly); } } lastX = x; } // connect lines to the right int wid = sz.width; if (lastX+5 < wid) { if (panel.processALine(g, lastX+5, 5, wid, 5, bounds, forPs, selectedObjects, ws, -1)) return; if (panel.processALine(g, lastX+5, hei-5, wid, hei-5, bounds, forPs, selectedObjects, ws, -1)) return; } } }; } }