/*******************************************************************************
* 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.plexers;
import java.awt.Color;
import java.awt.Graphics;
import com.bfh.logisim.designrulecheck.CorrectLabel;
import com.cburch.logisim.LogisimVersion;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.BitWidth;
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.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.tools.key.JoinedConfigurator;
import com.cburch.logisim.util.GraphicsUtil;
public class Multiplexer extends InstanceFactory {
static void drawSelectCircle(Graphics g, Bounds bds, Location loc) {
int locDelta = Math.max(bds.getHeight(), bds.getWidth()) <= 50 ? 8 : 6;
Location circLoc;
if (bds.getHeight() >= bds.getWidth()) { // vertically oriented
if (loc.getY() < bds.getY() + bds.getHeight() / 2) { // at top
circLoc = loc.translate(0, locDelta);
} else { // at bottom
circLoc = loc.translate(0, -locDelta);
}
} else {
if (loc.getX() < bds.getX() + bds.getWidth() / 2) { // at left
circLoc = loc.translate(locDelta, 0);
} else { // at right
circLoc = loc.translate(-locDelta, 0);
}
}
g.setColor(Color.LIGHT_GRAY);
g.fillOval(circLoc.getX() - 3, circLoc.getY() - 3, 6, 6);
}
public Multiplexer() {
super("Multiplexer", Strings.getter("multiplexerComponent"));
setAttributes(new Attribute[] { StdAttr.FACING,
Plexers.ATTR_SELECT_LOC, Plexers.ATTR_SELECT, StdAttr.WIDTH,
Plexers.ATTR_DISABLED, Plexers.ATTR_ENABLE }, new Object[] {
Direction.EAST, Plexers.SELECT_BOTTOM_LEFT,
Plexers.DEFAULT_SELECT, BitWidth.ONE, Plexers.DISABLED_ZERO,
Plexers.DEFAULT_ENABLE });
setKeyConfigurator(JoinedConfigurator.create(new BitWidthConfigurator(
Plexers.ATTR_SELECT, 1, 5, 0), new BitWidthConfigurator(
StdAttr.WIDTH)));
setIconName("multiplexer.gif");
setFacingAttribute(StdAttr.FACING);
}
@Override
protected void configureNewInstance(Instance instance) {
instance.addAttributeListener();
updatePorts(instance);
}
@Override
public boolean contains(Location loc, AttributeSet attrs) {
Direction facing = attrs.getValue(StdAttr.FACING);
return Plexers.contains(loc, getOffsetBounds(attrs), facing);
}
@Override
public Object getDefaultAttributeValue(Attribute<?> attr, LogisimVersion ver) {
if (attr == Plexers.ATTR_ENABLE) {
int newer = ver.compareTo(LogisimVersion.get(2, 6, 4));
return Boolean.valueOf(newer >= 0);
} else {
return super.getDefaultAttributeValue(attr, ver);
}
}
@Override
public String getHDLName(AttributeSet attrs) {
StringBuffer CompleteName = new StringBuffer();
CompleteName.append(CorrectLabel.getCorrectLabel(this.getName()));
if (attrs.getValue(StdAttr.WIDTH).getWidth() > 1)
CompleteName.append("_bus");
CompleteName.append("_"
+ Integer.toString(1 << attrs.getValue(Plexers.ATTR_SELECT)
.getWidth()));
return CompleteName.toString();
}
@Override
public Bounds getOffsetBounds(AttributeSet attrs) {
Direction dir = attrs.getValue(StdAttr.FACING);
BitWidth select = attrs.getValue(Plexers.ATTR_SELECT);
int inputs = 1 << select.getWidth();
if (inputs == 2) {
return Bounds.create(-30, -25, 30, 50).rotate(Direction.EAST, dir,
0, 0);
} else {
int offs = -(inputs / 2) * 10 - 10;
int length = inputs * 10 + 20;
return Bounds.create(-40, offs, 40, length).rotate(Direction.EAST,
dir, 0, 0);
}
}
@Override
public boolean HasThreeStateDrivers(AttributeSet attrs) {
return (attrs.getValue(Plexers.ATTR_DISABLED) == Plexers.DISABLED_FLOATING);
}
@Override
public boolean HDLSupportedComponent(String HDLIdentifier,
AttributeSet attrs) {
if (MyHDLGenerator == null)
MyHDLGenerator = new MultiplexerHDLGeneratorFactory();
return MyHDLGenerator.HDLTargetSupported(HDLIdentifier, attrs);
}
@Override
protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) {
if (attr == StdAttr.FACING || attr == Plexers.ATTR_SELECT_LOC
|| attr == Plexers.ATTR_SELECT) {
instance.recomputeBounds();
updatePorts(instance);
} else if (attr == StdAttr.WIDTH || attr == Plexers.ATTR_ENABLE) {
updatePorts(instance);
} else if (attr == Plexers.ATTR_DISABLED) {
instance.fireInvalidated();
}
}
@Override
public void paintGhost(InstancePainter painter) {
Direction facing = painter.getAttributeValue(StdAttr.FACING);
BitWidth select = painter.getAttributeValue(Plexers.ATTR_SELECT);
Bounds bds = painter.getBounds();
if (select.getWidth() == 1) {
if (facing == Direction.EAST || facing == Direction.WEST) {
Plexers.drawTrapezoid(
painter.getGraphics(),
Bounds.create(bds.getX(), bds.getY() + 5,
bds.getWidth(), bds.getHeight() - 10), facing,
10);
} else {
Plexers.drawTrapezoid(
painter.getGraphics(),
Bounds.create(bds.getX() + 5, bds.getY(),
bds.getWidth() - 10, bds.getHeight()), facing,
10);
}
} else {
Plexers.drawTrapezoid(painter.getGraphics(), bds, facing, 20);
}
}
@Override
public void paintInstance(InstancePainter painter) {
Graphics g = painter.getGraphics();
Bounds bds = painter.getBounds();
Direction facing = painter.getAttributeValue(StdAttr.FACING);
BitWidth select = painter.getAttributeValue(Plexers.ATTR_SELECT);
boolean enable = painter.getAttributeValue(Plexers.ATTR_ENABLE)
.booleanValue();
int inputs = 1 << select.getWidth();
// draw stubs for select/enable inputs that aren't on instance boundary
GraphicsUtil.switchToWidth(g, 3);
boolean vertical = facing != Direction.NORTH
&& facing != Direction.SOUTH;
Object selectLoc = painter.getAttributeValue(Plexers.ATTR_SELECT_LOC);
int selMult = selectLoc == Plexers.SELECT_BOTTOM_LEFT ? 1 : -1;
int dx = vertical ? 0 : -selMult;
int dy = vertical ? selMult : 0;
if (inputs == 2) { // draw select wire
Location pt = painter.getInstance().getPortLocation(inputs);
if (painter.getShowState()) {
g.setColor(painter.getPortValue(inputs).getColor());
}
g.drawLine(pt.getX() - 2 * dx, pt.getY() - 2 * dy, pt.getX(),
pt.getY());
}
if (enable) {
Location en = painter.getInstance().getPortLocation(inputs + 1);
if (painter.getShowState()) {
g.setColor(painter.getPortValue(inputs + 1).getColor());
}
int len = inputs == 2 ? 6 : 4;
g.drawLine(en.getX() - len * dx, en.getY() - len * dy, en.getX(),
en.getY());
}
GraphicsUtil.switchToWidth(g, 1);
// draw a circle indicating where the select input is located
Multiplexer.drawSelectCircle(g, bds, painter.getInstance()
.getPortLocation(inputs));
// draw a 0 indicating where the numbering starts for inputs
int x0;
int y0;
int halign;
if (facing == Direction.WEST) {
x0 = bds.getX() + bds.getWidth() - 3;
y0 = bds.getY() + 15 + (inputs == 2 ? 5 : 0);
halign = GraphicsUtil.H_RIGHT;
} else if (facing == Direction.NORTH) {
x0 = bds.getX() + 10 + (inputs == 2 ? 5 : 0);
y0 = bds.getY() + bds.getHeight() - 2;
halign = GraphicsUtil.H_CENTER;
} else if (facing == Direction.SOUTH) {
x0 = bds.getX() + 10 + (inputs == 2 ? 5 : 0);
y0 = bds.getY() + 12;
halign = GraphicsUtil.H_CENTER;
} else {
x0 = bds.getX() + 3;
y0 = bds.getY() + 15 + (inputs == 2 ? 5 : 0);
halign = GraphicsUtil.H_LEFT;
}
g.setColor(Color.GRAY);
GraphicsUtil.drawText(g, "0", x0, y0, halign, GraphicsUtil.V_BASELINE);
// draw the trapezoid, "MUX" string, the individual ports
g.setColor(Color.BLACK);
if (inputs == 2) {
if (facing == Direction.EAST || facing == Direction.WEST) {
Plexers.drawTrapezoid(
g,
Bounds.create(bds.getX(), bds.getY() + 5,
bds.getWidth(), bds.getHeight() - 10), facing,
10);
} else {
Plexers.drawTrapezoid(
g,
Bounds.create(bds.getX() + 5, bds.getY(),
bds.getWidth() - 10, bds.getHeight()), facing,
10);
}
} else {
Plexers.drawTrapezoid(g, bds, facing, 20);
}
GraphicsUtil.drawCenteredText(g, "MUX",
bds.getX() + bds.getWidth() / 2, bds.getY() + bds.getHeight()
/ 2);
painter.drawPorts();
}
@Override
public void propagate(InstanceState state) {
BitWidth data = state.getAttributeValue(StdAttr.WIDTH);
BitWidth select = state.getAttributeValue(Plexers.ATTR_SELECT);
boolean enable = state.getAttributeValue(Plexers.ATTR_ENABLE)
.booleanValue();
int inputs = 1 << select.getWidth();
Value en = enable ? state.getPortValue(inputs + 1) : Value.TRUE;
Value out;
if (en == Value.FALSE) {
Object opt = state.getAttributeValue(Plexers.ATTR_DISABLED);
Value base = opt == Plexers.DISABLED_ZERO ? Value.FALSE
: Value.UNKNOWN;
out = Value.repeat(base, data.getWidth());
} else if (en == Value.ERROR && state.isPortConnected(inputs + 1)) {
out = Value.createError(data);
} else {
Value sel = state.getPortValue(inputs);
if (sel.isFullyDefined()) {
out = state.getPortValue(sel.toIntValue());
} else if (sel.isErrorValue()) {
out = Value.createError(data);
} else {
out = Value.createUnknown(data);
}
}
state.setPort(inputs + (enable ? 2 : 1), out, Plexers.DELAY);
}
private void updatePorts(Instance instance) {
Direction dir = instance.getAttributeValue(StdAttr.FACING);
Object selectLoc = instance.getAttributeValue(Plexers.ATTR_SELECT_LOC);
BitWidth data = instance.getAttributeValue(StdAttr.WIDTH);
BitWidth select = instance.getAttributeValue(Plexers.ATTR_SELECT);
boolean enable = instance.getAttributeValue(Plexers.ATTR_ENABLE)
.booleanValue();
int selMult = selectLoc == Plexers.SELECT_BOTTOM_LEFT ? 1 : -1;
int inputs = 1 << select.getWidth();
Port[] ps = new Port[inputs + (enable ? 3 : 2)];
Location sel;
if (inputs == 2) {
Location end0;
Location end1;
if (dir == Direction.WEST) {
end0 = Location.create(30, -10);
end1 = Location.create(30, 10);
sel = Location.create(20, selMult * 20);
} else if (dir == Direction.NORTH) {
end0 = Location.create(-10, 30);
end1 = Location.create(10, 30);
sel = Location.create(selMult * -20, 20);
} else if (dir == Direction.SOUTH) {
end0 = Location.create(-10, -30);
end1 = Location.create(10, -30);
sel = Location.create(selMult * -20, -20);
} else {
end0 = Location.create(-30, -10);
end1 = Location.create(-30, 10);
sel = Location.create(-20, selMult * 20);
}
ps[0] = new Port(end0.getX(), end0.getY(), Port.INPUT,
data.getWidth());
ps[1] = new Port(end1.getX(), end1.getY(), Port.INPUT,
data.getWidth());
} else {
int dx = -(inputs / 2) * 10;
int ddx = 10;
int dy = -(inputs / 2) * 10;
int ddy = 10;
if (dir == Direction.WEST) {
dx = 40;
ddx = 0;
sel = Location.create(20, selMult * (dy + 10 * inputs));
} else if (dir == Direction.NORTH) {
dy = 40;
ddy = 0;
sel = Location.create(selMult * dx, 20);
} else if (dir == Direction.SOUTH) {
dy = -40;
ddy = 0;
sel = Location.create(selMult * dx, -20);
} else {
dx = -40;
ddx = 0;
sel = Location.create(-20, selMult * (dy + 10 * inputs));
}
for (int i = 0; i < inputs; i++) {
ps[i] = new Port(dx, dy, Port.INPUT, data.getWidth());
dx += ddx;
dy += ddy;
}
}
Location en = sel.translate(dir, 10);
ps[inputs] = new Port(sel.getX(), sel.getY(), Port.INPUT,
select.getWidth());
if (enable) {
ps[inputs + 1] = new Port(en.getX(), en.getY(), Port.INPUT,
BitWidth.ONE);
}
ps[ps.length - 1] = new Port(0, 0, Port.OUTPUT, data.getWidth());
for (int i = 0; i < inputs; i++) {
ps[i].setToolTip(Strings.getter("multiplexerInTip", "" + i));
}
ps[inputs].setToolTip(Strings.getter("multiplexerSelectTip"));
if (enable) {
ps[inputs + 1].setToolTip(Strings.getter("multiplexerEnableTip"));
}
ps[ps.length - 1].setToolTip(Strings.getter("multiplexerOutTip"));
instance.setPorts(ps);
}
}