/******************************************************************************* * 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.Font; import java.awt.Graphics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.cburch.logisim.data.Attribute; import com.cburch.logisim.data.AttributeOption; import com.cburch.logisim.data.AttributeSet; import com.cburch.logisim.data.Attributes; import com.cburch.logisim.data.BitWidth; import com.cburch.logisim.data.Bounds; import com.cburch.logisim.data.Value; import com.cburch.logisim.instance.Instance; import com.cburch.logisim.instance.InstanceFactory; import com.cburch.logisim.instance.InstancePainter; import com.cburch.logisim.instance.InstanceState; import com.cburch.logisim.instance.Port; import com.cburch.logisim.instance.StdAttr; import com.cburch.logisim.tools.key.BitWidthConfigurator; import com.cburch.logisim.util.GraphicsUtil; import com.cburch.logisim.util.StringUtil; public class Counter extends InstanceFactory { public static int SymbolWidth(int NrOfBits) { return 150 + ((NrOfBits - 8) / 5) * 10; } final static Logger logger = LoggerFactory.getLogger(Counter.class); static final AttributeOption ON_GOAL_WRAP = new AttributeOption("wrap", "wrap", Strings.getter("counterGoalWrap")); static final AttributeOption ON_GOAL_STAY = new AttributeOption("stay", "stay", Strings.getter("counterGoalStay")); static final AttributeOption ON_GOAL_CONT = new AttributeOption("continue", "continue", Strings.getter("counterGoalContinue")); static final AttributeOption ON_GOAL_LOAD = new AttributeOption("load", "load", Strings.getter("counterGoalLoad")); static final Attribute<Integer> ATTR_MAX = Attributes.forHexInteger("max", Strings.getter("counterMaxAttr")); static final Attribute<AttributeOption> ATTR_ON_GOAL = Attributes .forOption("ongoal", Strings.getter("counterGoalAttr"), new AttributeOption[] { ON_GOAL_WRAP, ON_GOAL_STAY, ON_GOAL_CONT, ON_GOAL_LOAD }); static final int DELAY = 8; static final int OUT = 0; static final int IN = 1; public static final int CK = 2; static final int CLR = 3; static final int LD = 4; static final int UD = 5; static final int EN = 6; static final int CARRY = 7; public Counter() { super("Counter", Strings.getter("counterComponent")); setOffsetBounds(Bounds.create(0, 0, 30, 40)); setIconName("counter.gif"); setInstancePoker(CounterPoker.class); setInstanceLogger(RegisterLogger.class); setKeyConfigurator(new BitWidthConfigurator(StdAttr.WIDTH)); } @Override protected void configureNewInstance(Instance instance) { configurePorts(instance); instance.addAttributeListener(); } private void configurePorts(Instance instance) { Bounds bds = instance.getBounds(); BitWidth widthVal = instance.getAttributeValue(StdAttr.WIDTH); int width = widthVal == null ? 8 : widthVal.getWidth(); Port[] ps = new Port[8]; if (width == 1) { ps[OUT] = new Port(SymbolWidth(width) + 40, 120, Port.OUTPUT, StdAttr.WIDTH); ps[IN] = new Port(0, 120, Port.INPUT, StdAttr.WIDTH); } else { ps[OUT] = new Port(SymbolWidth(width) + 40, 110, Port.OUTPUT, StdAttr.WIDTH); ps[IN] = new Port(0, 110, Port.INPUT, StdAttr.WIDTH); } ps[CK] = new Port(0, 80, Port.INPUT, 1); ps[CLR] = new Port(0, 20, Port.INPUT, 1); ps[LD] = new Port(0, 30, Port.INPUT, 1); ps[UD] = new Port(0, 50, Port.INPUT, 1); ps[EN] = new Port(0, 70, Port.INPUT, 1); ps[CARRY] = new Port(40 + SymbolWidth(width), 50, Port.OUTPUT, 1); ps[OUT].setToolTip(Strings.getter("counterQTip")); ps[IN].setToolTip(Strings.getter("counterDataTip")); ps[CK].setToolTip(Strings.getter("counterClockTip")); ps[CLR].setToolTip(Strings.getter("counterResetTip")); ps[LD].setToolTip(Strings.getter("counterLoadTip")); ps[UD].setToolTip(Strings.getter("counterUpDownTip")); ps[EN].setToolTip(Strings.getter("counterEnableTip")); ps[CARRY].setToolTip(Strings.getter("counterCarryTip")); instance.setPorts(ps); instance.setTextField(StdAttr.LABEL, StdAttr.LABEL_FONT, bds.getX() + bds.getWidth() / 2, bds.getY() - 3, GraphicsUtil.H_CENTER, GraphicsUtil.V_BASELINE); } @Override public AttributeSet createAttributeSet() { return new CounterAttributes(); } private void DrawControl(InstancePainter painter, int xpos, int ypos) { Graphics g = painter.getGraphics(); GraphicsUtil.switchToWidth(g, 2); BitWidth widthVal = painter.getAttributeValue(StdAttr.WIDTH); int width = widthVal == null ? 8 : widthVal.getWidth(); g.drawLine(xpos + 20, ypos, xpos + 20 + SymbolWidth(width), ypos); g.drawLine(xpos + 20, ypos, xpos + 20, ypos + 100); g.drawLine(xpos + 20 + SymbolWidth(width), ypos, xpos + 20 + SymbolWidth(width), ypos + 100); g.drawLine(xpos + 20, ypos + 100, xpos + 30, ypos + 100); g.drawLine(xpos + 10 + SymbolWidth(width), ypos + 100, xpos + 20 + SymbolWidth(width), ypos + 100); g.drawLine(xpos + 30, ypos + 100, xpos + 30, ypos + 110); g.drawLine(xpos + 10 + SymbolWidth(width), ypos + 100, xpos + 10 + SymbolWidth(width), ypos + 110); /* Draw clock entry symbols */ painter.drawClockSymbol(xpos + 20, ypos + 80); painter.drawClockSymbol(xpos + 20, ypos + 90); /* Draw Label */ int max = painter.getAttributeValue(ATTR_MAX).intValue(); boolean IsCTRm = (max == painter.getAttributeValue(StdAttr.WIDTH) .getMask()); Object onGoal = painter.getAttributeValue(ATTR_ON_GOAL); IsCTRm |= onGoal == ON_GOAL_CONT; String Label = (IsCTRm) ? "CTR" + Integer.toString(painter.getAttributeValue(StdAttr.WIDTH) .getWidth()) : "CTR DIV0x" + Integer.toHexString(max); GraphicsUtil.drawCenteredText(g, Label, xpos + (SymbolWidth(width) / 2) + 20, ypos + 5); GraphicsUtil.switchToWidth(g, 2); /* Draw Reset Input */ g.drawLine(xpos, ypos + 20, xpos + 20, ypos + 20); GraphicsUtil.drawText(g, "R", xpos + 30, ypos + 20, GraphicsUtil.H_LEFT, GraphicsUtil.V_CENTER); painter.drawPort(CLR); /* Draw Load Input */ g.drawLine(xpos, ypos + 30, xpos + 20, ypos + 30); g.drawLine(xpos + 5, ypos + 40, xpos + 12, ypos + 40); g.drawLine(xpos + 5, ypos + 30, xpos + 5, ypos + 40); g.drawOval(xpos + 12, ypos + 36, 8, 8); g.fillOval(xpos + 2, ypos + 27, 6, 6); painter.drawPort(LD); GraphicsUtil.drawText(g, "M2 [count]", xpos + 30, ypos + 40, GraphicsUtil.H_LEFT, GraphicsUtil.V_CENTER); GraphicsUtil.drawText(g, "M1 [load]", xpos + 30, ypos + 30, GraphicsUtil.H_LEFT, GraphicsUtil.V_CENTER); /* Draw UpDn input */ g.drawLine(xpos, ypos + 50, xpos + 20, ypos + 50); g.drawLine(xpos + 5, ypos + 60, xpos + 12, ypos + 60); g.drawLine(xpos + 5, ypos + 50, xpos + 5, ypos + 60); g.drawOval(xpos + 12, ypos + 56, 8, 8); g.fillOval(xpos + 2, ypos + 47, 6, 6); GraphicsUtil.drawText(g, "M3 [up]", xpos + 30, ypos + 50, GraphicsUtil.H_LEFT, GraphicsUtil.V_CENTER); GraphicsUtil.drawText(g, "M4 [down]", xpos + 30, ypos + 60, GraphicsUtil.H_LEFT, GraphicsUtil.V_CENTER); painter.drawPort(UD); /* Draw Enable Port */ g.drawLine(xpos, ypos + 70, xpos + 20, ypos + 70); GraphicsUtil.drawText(g, "G5", xpos + 30, ypos + 70, GraphicsUtil.H_LEFT, GraphicsUtil.V_CENTER); painter.drawPort(EN); /* Draw Clock */ boolean inverted = painter.getAttributeValue(StdAttr.EDGE_TRIGGER) .equals(StdAttr.TRIG_FALLING); int xend = (inverted) ? xpos + 12 : xpos + 20; g.drawLine(xpos, ypos + 80, xend, ypos + 80); g.drawLine(xpos + 5, ypos + 90, xend, ypos + 90); g.drawLine(xpos + 5, ypos + 80, xpos + 5, ypos + 90); g.fillOval(xpos + 2, ypos + 77, 6, 6); if (inverted) { g.drawOval(xend, ypos + 76, 8, 8); g.drawOval(xend, ypos + 86, 8, 8); } GraphicsUtil.drawText(g, "2,3,5+/C6", xpos + 30, ypos + 80, GraphicsUtil.H_LEFT, GraphicsUtil.V_CENTER); GraphicsUtil.drawText(g, "2,4,5-", xpos + 30, ypos + 90, GraphicsUtil.H_LEFT, GraphicsUtil.V_CENTER); painter.drawPort(CK); /* Draw Carry */ g.drawLine(xpos + 20 + SymbolWidth(width), ypos + 50, xpos + 40 + SymbolWidth(width), ypos + 50); g.drawLine(xpos + 20 + SymbolWidth(width), ypos + 60, xpos + 35 + SymbolWidth(width), ypos + 60); g.drawLine(xpos + 35 + SymbolWidth(width), ypos + 50, xpos + 35 + SymbolWidth(width), ypos + 60); g.fillOval(xpos + 32 + SymbolWidth(width), ypos + 47, 6, 6); String MaxVal = "3CT=0x" + Integer.toHexString( painter.getAttributeValue(ATTR_MAX).intValue()) .toUpperCase(); GraphicsUtil.drawText(g, MaxVal, xpos + 17 + SymbolWidth(width), ypos + 50, GraphicsUtil.H_RIGHT, GraphicsUtil.V_CENTER); GraphicsUtil.drawText(g, "4CT=0", xpos + 17 + SymbolWidth(width), ypos + 60, GraphicsUtil.H_RIGHT, GraphicsUtil.V_CENTER); painter.drawPort(CARRY); GraphicsUtil.switchToWidth(g, 1); /* Draw counter Value */ if (painter.getShowState()) { int len = (width + 3) / 4; int xcenter = SymbolWidth(width) - 25; RegisterData state = (RegisterData) painter.getData(); int val = state == null ? 0 : state.value; String Value = StringUtil.toHexString(width, val).toUpperCase(); g.setColor(Color.LIGHT_GRAY); g.fillRect(xpos + xcenter - len * 4, ypos + 22, len * 8, 16); g.setColor(Color.BLACK); GraphicsUtil.drawText(g, Value, xpos + xcenter - len * 4 + 1, ypos + 30, GraphicsUtil.H_LEFT, GraphicsUtil.V_CENTER); } } private void DrawDataBlock(InstancePainter painter, int xpos, int ypos, int BitNr, int NrOfBits) { int RealYpos = ypos + BitNr * 20; boolean first = BitNr == 0; boolean last = BitNr == (NrOfBits - 1); Graphics g = painter.getGraphics(); Font font = g.getFont(); g.setFont(font.deriveFont(7.0f)); GraphicsUtil.switchToWidth(g, 2); g.drawRect(xpos + 20, RealYpos, SymbolWidth(NrOfBits), 20); /* Input Line */ if (NrOfBits > 1) { g.drawLine(xpos + 10, RealYpos + 10, xpos + 20, RealYpos + 10); g.drawLine(xpos + 5, RealYpos + 5, xpos + 10, RealYpos + 10); } else { g.drawLine(xpos, RealYpos + 10, xpos + 20, RealYpos + 10); } /* Ouput Line */ if (NrOfBits > 1) { g.drawLine(xpos + 20 + SymbolWidth(NrOfBits), RealYpos + 10, xpos + 30 + SymbolWidth(NrOfBits), RealYpos + 10); g.drawLine(xpos + 30 + SymbolWidth(NrOfBits), RealYpos + 10, xpos + 35 + SymbolWidth(NrOfBits), RealYpos + 5); } else { g.drawLine(xpos + 20 + SymbolWidth(NrOfBits), RealYpos + 10, xpos + 40 + SymbolWidth(NrOfBits), RealYpos + 10); } g.setColor(Color.BLACK); if (NrOfBits > 1) { GraphicsUtil.drawText(g, Integer.toString(BitNr), xpos + 30 + SymbolWidth(NrOfBits), RealYpos + 8, GraphicsUtil.H_RIGHT, GraphicsUtil.V_BASELINE); GraphicsUtil.drawText(g, Integer.toString(BitNr), xpos + 10, RealYpos + 8, GraphicsUtil.H_LEFT, GraphicsUtil.V_BASELINE); } g.setFont(font); GraphicsUtil.drawText(g, "1,6D", xpos + 21, RealYpos + 10, GraphicsUtil.H_LEFT, GraphicsUtil.V_CENTER); int LineWidth = (NrOfBits == 1) ? 2 : 5; GraphicsUtil.switchToWidth(g, LineWidth); if (first) { painter.drawPort(IN); painter.drawPort(OUT); if (NrOfBits > 1) { g.drawLine(xpos, RealYpos, xpos + 5, RealYpos + 5); g.drawLine(xpos + 35 + SymbolWidth(NrOfBits), RealYpos + 5, xpos + 40 + SymbolWidth(NrOfBits), RealYpos); g.drawLine(xpos + 5, RealYpos + 5, xpos + 5, RealYpos + 20); g.drawLine(xpos + 35 + SymbolWidth(NrOfBits), RealYpos + 5, xpos + 35 + SymbolWidth(NrOfBits), RealYpos + 20); } } else if (last) { g.drawLine(xpos + 5, RealYpos, xpos + 5, RealYpos + 5); g.drawLine(xpos + 35 + SymbolWidth(NrOfBits), RealYpos, xpos + 35 + SymbolWidth(NrOfBits), RealYpos + 5); } else { g.drawLine(xpos + 5, RealYpos, xpos + 5, RealYpos + 20); g.drawLine(xpos + 35 + SymbolWidth(NrOfBits), RealYpos, xpos + 35 + SymbolWidth(NrOfBits), RealYpos + 20); } GraphicsUtil.switchToWidth(g, 1); if (painter.getShowState()) { /* Here we draw the bit value */ RegisterData state = (RegisterData) painter.getData(); int val = state == null ? 0 : state.value; BitWidth widthVal = painter.getAttributeValue(StdAttr.WIDTH); int width = widthVal == null ? 8 : widthVal.getWidth(); int xcenter = (SymbolWidth(width) / 2) + 10; int value = ((1 << BitNr) & val) != 0 ? 1 : 0; g.setColor(Color.LIGHT_GRAY); g.fillRect(xpos + xcenter + 16, RealYpos + 4, 8, 16); g.setColor(Color.BLACK); GraphicsUtil.drawText(g, Integer.toString(value), xpos + xcenter + 20, RealYpos + 10, GraphicsUtil.H_CENTER, GraphicsUtil.V_CENTER); } } @Override public String getHDLName(AttributeSet attrs) { return "LogisimCounter"; } @Override public Bounds getOffsetBounds(AttributeSet attrs) { BitWidth widthVal = attrs.getValue(StdAttr.WIDTH); int width = widthVal == null ? 8 : widthVal.getWidth(); return Bounds.create(0, 0, SymbolWidth(width) + 40, 110 + 20 * width); } @Override public boolean HDLSupportedComponent(String HDLIdentifier, AttributeSet attrs) { if (MyHDLGenerator == null) MyHDLGenerator = new CounterHDLGeneratorFactory(); return MyHDLGenerator.HDLTargetSupported(HDLIdentifier, attrs); } @Override protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) { if (attr == StdAttr.WIDTH) { instance.recomputeBounds(); configurePorts(instance); } } @Override public void paintInstance(InstancePainter painter) { int Xpos = painter.getLocation().getX(); int Ypos = painter.getLocation().getY(); painter.drawLabel(); DrawControl(painter, Xpos, Ypos); BitWidth widthVal = painter.getAttributeValue(StdAttr.WIDTH); int width = widthVal == null ? 8 : widthVal.getWidth(); for (int bit = 0; bit < width; bit++) { DrawDataBlock(painter, Xpos, Ypos + 110, bit, width); } } @Override public void propagate(InstanceState state) { RegisterData data = (RegisterData) state.getData(); if (data == null) { data = new RegisterData(); state.setData(data); } BitWidth dataWidth = state.getAttributeValue(StdAttr.WIDTH); Object triggerType = state.getAttributeValue(StdAttr.EDGE_TRIGGER); int max = state.getAttributeValue(ATTR_MAX).intValue(); Value clock = state.getPortValue(CK); boolean triggered = data.updateClock(clock, triggerType); Value newValue; boolean carry; if (state.getPortValue(CLR) == Value.TRUE) { newValue = Value.createKnown(dataWidth, 0); carry = false; } else { boolean ld = state.getPortValue(LD) == Value.TRUE; boolean en = state.getPortValue(EN) != Value.FALSE; boolean UpCount = state.getPortValue(UD) != Value.FALSE; int oldVal = data.value; int newVal; if (!triggered) { newVal = oldVal; } else if (ld) { Value in = state.getPortValue(IN); newVal = in.isFullyDefined() ? in.toIntValue() : 0; if (newVal > max) newVal &= max; } else if (en) { int goal = (UpCount) ? max : 0; if (oldVal == goal) { Object onGoal = state.getAttributeValue(ATTR_ON_GOAL); if (onGoal == ON_GOAL_WRAP) { newVal = (UpCount) ? 0 : max; } else if (onGoal == ON_GOAL_STAY) { newVal = oldVal; } else if (onGoal == ON_GOAL_LOAD) { Value in = state.getPortValue(IN); newVal = in.isFullyDefined() ? in.toIntValue() : 0; if (newVal > max) newVal &= max; } else if (onGoal == ON_GOAL_CONT) { newVal = (UpCount) ? oldVal + 1 : oldVal - 1; } else { logger.error("Invalid goal attribute {}", onGoal); newVal = ld ? max : 0; } } else { newVal = (UpCount) ? oldVal + 1 : oldVal - 1; } } else { newVal = oldVal; } newValue = Value.createKnown(dataWidth, newVal); newVal = newValue.toIntValue(); carry = newVal == (UpCount ? max : 0); /* * I would want this if I were worried about the carry signal * outrunning the clock. But the component's delay should be enough * to take care of it. if (carry) { if (triggerType == * StdAttr.TRIG_FALLING) { carry = clock == Value.TRUE; } else { * carry = clock == Value.FALSE; } } */ } data.value = newValue.toIntValue(); state.setPort(OUT, newValue, DELAY); state.setPort(CARRY, carry ? Value.TRUE : Value.FALSE, DELAY); } }