/******************************************************************************* * This file is part of logisim-evolution. * * logisim-evolution 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. * * logisim-evolution 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 logisim-evolution. If not, see <http://www.gnu.org/licenses/>. * * Original code by Carl Burch (http://www.cburch.com), 2011. * Subsequent modifications by : * + Haute École Spécialisée Bernoise * http://www.bfh.ch * + Haute École du paysage, d'ingénierie et d'architecture de Genève * http://hepia.hesge.ch/ * + Haute École d'Ingénierie et de Gestion du Canton de Vaud * http://www.heig-vd.ch/ * The project is currently maintained by : * + REDS Institute - HEIG-VD * Yverdon-les-Bains, Switzerland * http://reds.heig-vd.ch *******************************************************************************/ package com.cburch.logisim.std.memory; import java.awt.Color; import java.awt.Graphics; import java.awt.event.MouseEvent; import com.cburch.logisim.data.Attribute; import com.cburch.logisim.data.AttributeOption; import com.cburch.logisim.data.AttributeSet; import com.cburch.logisim.data.Bounds; import com.cburch.logisim.data.Direction; import com.cburch.logisim.data.Location; import com.cburch.logisim.data.Value; import com.cburch.logisim.instance.Instance; import com.cburch.logisim.instance.InstanceData; import com.cburch.logisim.instance.InstanceFactory; import com.cburch.logisim.instance.InstanceLogger; import com.cburch.logisim.instance.InstancePainter; import com.cburch.logisim.instance.InstancePoker; import com.cburch.logisim.instance.InstanceState; import com.cburch.logisim.instance.Port; import com.cburch.logisim.instance.StdAttr; import com.cburch.logisim.util.GraphicsUtil; import com.cburch.logisim.util.StringGetter; abstract class AbstractFlipFlop extends InstanceFactory { public static class Logger extends InstanceLogger { @Override public String getLogName(InstanceState state, Object option) { String ret = state.getAttributeValue(StdAttr.LABEL); return ret != null && !ret.equals("") ? ret : null; } @Override public Value getLogValue(InstanceState state, Object option) { StateData s = (StateData) state.getData(); return s == null ? Value.FALSE : s.curValue; } } public static class Poker extends InstancePoker { boolean isPressed = true; private boolean isInside(InstanceState state, MouseEvent e) { Location loc = state.getInstance().getLocation(); int dx = e.getX() - (loc.getX() + 20); int dy = e.getY() - (loc.getY() + 30); int d2 = dx * dx + dy * dy; return d2 < 8 * 8; } @Override public void mousePressed(InstanceState state, MouseEvent e) { isPressed = isInside(state, e); } @Override public void mouseReleased(InstanceState state, MouseEvent e) { if (isPressed && isInside(state, e)) { StateData myState = (StateData) state.getData(); if (myState == null) return; myState.curValue = myState.curValue.not(); state.fireInvalidated(); } isPressed = false; } } private static class StateData extends ClockState implements InstanceData { Value curValue = Value.FALSE; } private static final int STD_PORTS = 5; private Attribute<AttributeOption> triggerAttribute; protected AbstractFlipFlop(String name, String iconName, StringGetter desc, int numInputs, boolean allowLevelTriggers) { super(name, desc); setIconName(iconName); triggerAttribute = allowLevelTriggers ? StdAttr.TRIGGER : StdAttr.EDGE_TRIGGER; setAttributes(new Attribute[] { triggerAttribute, StdAttr.LABEL, StdAttr.LABEL_FONT }, new Object[] { StdAttr.TRIG_RISING, "", StdAttr.DEFAULT_LABEL_FONT }); setOffsetBounds(Bounds.create(-10, 0, 60, 60)); setInstancePoker(Poker.class); setInstanceLogger(Logger.class); Port[] ps = new Port[numInputs + STD_PORTS]; if (numInputs == 1) { ps[0] = new Port(-10, 10, Port.INPUT, 1); ps[1] = new Port(-10, 50, Port.INPUT, 1); } else if (numInputs == 2) { ps[0] = new Port(-10, 10, Port.INPUT, 1); ps[1] = new Port(-10, 30, Port.INPUT, 1); ps[2] = new Port(-10, 50, Port.INPUT, 1); } else { throw new RuntimeException("flip-flop input > 2"); } ps[numInputs + 1] = new Port(50, 10, Port.OUTPUT, 1); ps[numInputs + 2] = new Port(50, 50, Port.OUTPUT, 1); ps[numInputs + 3] = new Port(20, 60, Port.INPUT, 1); ps[numInputs + 4] = new Port(20, 0, Port.INPUT, 1); ps[numInputs].setToolTip(Strings.getter("flipFlopClockTip")); ps[numInputs + 1].setToolTip(Strings.getter("flipFlopQTip")); ps[numInputs + 2].setToolTip(Strings.getter("flipFlopNotQTip")); ps[numInputs + 3].setToolTip(Strings.getter("flipFlopResetTip")); ps[numInputs + 4].setToolTip(Strings.getter("flipFlopPresetTip")); setPorts(ps); } protected abstract Value computeValue(Value[] inputs, Value curValue); // // concrete methods not intended to be overridden // @Override protected void configureNewInstance(Instance instance) { Bounds bds = instance.getBounds(); instance.setTextField(StdAttr.LABEL, StdAttr.LABEL_FONT, bds.getX() + bds.getWidth() / 2, bds.getY() - 3, GraphicsUtil.H_CENTER, GraphicsUtil.V_BASELINE); } @Override public String getHDLName(AttributeSet attrs) { StringBuffer CompleteName = new StringBuffer(); String[] Parts = this.getName().split(" "); CompleteName.append(Parts[0].replace("-", "_").toUpperCase()); CompleteName.append("_"); if (attrs.containsAttribute(StdAttr.EDGE_TRIGGER)) { CompleteName.append("FlipFlop".toUpperCase()); } else { if (attrs.containsAttribute(StdAttr.TRIGGER)) { if ((attrs.getValue(StdAttr.TRIGGER) == StdAttr.TRIG_FALLING) || (attrs.getValue(StdAttr.TRIGGER) == StdAttr.TRIG_RISING)) { CompleteName.append("FlipFlop".toUpperCase()); } else { CompleteName.append("Latch".toUpperCase()); } } else { CompleteName.append("FlipFlop".toUpperCase()); } } return CompleteName.toString(); } // // abstract methods intended to be implemented in subclasses // protected abstract String getInputName(int index); @Override public void paintInstance(InstancePainter painter) { Graphics g = painter.getGraphics(); painter.drawLabel(); Location loc = painter.getLocation(); int x = loc.getX(); int y = loc.getY(); GraphicsUtil.switchToWidth(g, 2); g.drawRect(x, y, 40, 60); if (painter.getShowState()) { StateData myState = (StateData) painter.getData(); if (myState != null) { g.setColor(myState.curValue.getColor()); g.fillOval(x + 13, y + 23, 14, 14); g.setColor(Color.WHITE); GraphicsUtil.drawCenteredText(g, myState.curValue.toDisplayString(), x + 21, y + 29); g.setColor(Color.BLACK); } } int n = getPorts().size() - STD_PORTS; g.setColor(Color.GRAY); painter.drawPort(n + 3, "R", Direction.SOUTH); painter.drawPort(n + 4, "S", Direction.NORTH); g.setColor(Color.BLACK); for (int i = 0; i < n; i++) { g.fillRect(x - 10, y + 9 + i * 20, 10, 3); painter.drawPort(i); GraphicsUtil.drawCenteredText(g, getInputName(i), x + 8, y + 10 + i * 20); } Object Trigger = painter.getAttributeValue(triggerAttribute); if (Trigger.equals(StdAttr.TRIG_RISING) || Trigger.equals(StdAttr.TRIG_FALLING)) { painter.drawClockSymbol(x, y + 50); } else { GraphicsUtil.drawCenteredText(g, "E", x + 8, y + 50); } if (Trigger.equals(StdAttr.TRIG_RISING) || Trigger.equals(StdAttr.TRIG_HIGH)) { g.fillRect(x - 10, y + 49, 10, 3); } else { GraphicsUtil.switchToWidth(g, 2); g.drawOval(x - 10, y + 45, 10, 10); GraphicsUtil.switchToWidth(g, 1); } painter.drawPort(n); g.fillRect(x + 40, y + 9, 10, 3); GraphicsUtil.drawCenteredText(g, "Q", x + 31, y + 10); painter.drawPort(n + 1); GraphicsUtil.switchToWidth(g, 2); g.drawOval(x + 40, y + 45, 10, 10); GraphicsUtil.switchToWidth(g, 1); painter.drawPort(n + 2); } @Override public void propagate(InstanceState state) { // boolean changed = false; StateData data = (StateData) state.getData(); if (data == null) { // changed = true; data = new StateData(); state.setData(data); } int n = getPorts().size() - STD_PORTS; Object triggerType = state.getAttributeValue(triggerAttribute); boolean triggered = data .updateClock(state.getPortValue(n), triggerType); if (state.getPortValue(n + 3) == Value.TRUE) { // clear requested // changed |= data.curValue != Value.FALSE; data.curValue = Value.FALSE; } else if (state.getPortValue(n + 4) == Value.TRUE) { // preset // requested // changed |= data.curValue != Value.TRUE; data.curValue = Value.TRUE; } else if (triggered/* && state.getPortValue(n + 5) != Value.FALSE */) { // Clock has triggered and flip-flop is enabled: Update the state Value[] inputs = new Value[n]; for (int i = 0; i < n; i++) { inputs[i] = state.getPortValue(i); } Value newVal = computeValue(inputs, data.curValue); if (newVal == Value.TRUE || newVal == Value.FALSE) { // changed |= data.curValue != newVal; data.curValue = newVal; } } state.setPort(n + 1, data.curValue, Memory.DELAY); state.setPort(n + 2, data.curValue.not(), Memory.DELAY); } }