/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.logisim.std.memory;
import java.awt.Color;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import com.cburch.logisim.circuit.CircuitState;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeEvent;
import com.cburch.logisim.data.AttributeListener;
import com.cburch.logisim.data.AttributeOption;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.AttributeSets;
import com.cburch.logisim.data.Attributes;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.gui.hex.HexFrame;
import com.cburch.logisim.instance.Instance;
import com.cburch.logisim.instance.InstanceData;
import com.cburch.logisim.instance.InstanceLogger;
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.proj.Project;
public class Ram extends Mem {
static final AttributeOption BUS_COMBINED
= new AttributeOption("combined", Strings.getter("ramBusSynchCombined"));
static final AttributeOption BUS_ASYNCH
= new AttributeOption("asynch", Strings.getter("ramBusAsynchCombined"));
static final AttributeOption BUS_SEPARATE
= new AttributeOption("separate", Strings.getter("ramBusSeparate"));
static final Attribute<AttributeOption> ATTR_BUS = Attributes.forOption("bus",
Strings.getter("ramBusAttr"),
new AttributeOption[] { BUS_COMBINED, BUS_ASYNCH, BUS_SEPARATE });
private static Attribute<?>[] ATTRIBUTES = {
Mem.ADDR_ATTR, Mem.DATA_ATTR, ATTR_BUS
};
private static Object[] DEFAULTS = {
BitWidth.create(8), BitWidth.create(8), BUS_COMBINED
};
private static final int OE = MEM_INPUTS + 0;
private static final int CLR = MEM_INPUTS + 1;
private static final int CLK = MEM_INPUTS + 2;
private static final int WE = MEM_INPUTS + 3;
private static final int DIN = MEM_INPUTS + 4;
private static Object[][] logOptions = new Object[9][];
public Ram() {
super("RAM", Strings.getter("ramComponent"), 3);
setIconName("ram.gif");
setInstanceLogger(Logger.class);
}
@Override
protected void configureNewInstance(Instance instance) {
super.configureNewInstance(instance);
instance.addAttributeListener();
}
@Override
protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) {
super.instanceAttributeChanged(instance, attr);
configurePorts(instance);
}
@Override
void configurePorts(Instance instance) {
Object bus = instance.getAttributeValue(ATTR_BUS);
if (bus == null) bus = BUS_COMBINED;
boolean asynch = bus == null ? false : bus.equals(BUS_ASYNCH);
boolean separate = bus == null ? false : bus.equals(BUS_SEPARATE);
int portCount = MEM_INPUTS;
if (asynch) portCount += 2;
else if (separate) portCount += 5;
else portCount += 3;
Port[] ps = new Port[portCount];
configureStandardPorts(instance, ps);
ps[OE] = new Port(-50, 40, Port.INPUT, 1);
ps[OE].setToolTip(Strings.getter("ramOETip"));
ps[CLR] = new Port(-30, 40, Port.INPUT, 1);
ps[CLR].setToolTip(Strings.getter("ramClrTip"));
if (!asynch) {
ps[CLK] = new Port(-70, 40, Port.INPUT, 1);
ps[CLK].setToolTip(Strings.getter("ramClkTip"));
}
if (separate) {
ps[WE] = new Port(-110, 40, Port.INPUT, 1);
ps[WE].setToolTip(Strings.getter("ramWETip"));
ps[DIN] = new Port(-140, 20, Port.INPUT, DATA_ATTR);
ps[DIN].setToolTip(Strings.getter("ramInTip"));
} else {
ps[DATA].setToolTip(Strings.getter("ramBusTip"));
}
instance.setPorts(ps);
}
@Override
public AttributeSet createAttributeSet() {
return AttributeSets.fixedSet(ATTRIBUTES, DEFAULTS);
}
@Override
MemState getState(InstanceState state) {
BitWidth addrBits = state.getAttributeValue(ADDR_ATTR);
BitWidth dataBits = state.getAttributeValue(DATA_ATTR);
RamState myState = (RamState) state.getData();
if (myState == null) {
MemContents contents = MemContents.create(addrBits.getWidth(), dataBits.getWidth());
Instance instance = state.getInstance();
myState = new RamState(instance, contents, new MemListener(instance));
state.setData(myState);
} else {
myState.setRam(state.getInstance());
}
return myState;
}
@Override
MemState getState(Instance instance, CircuitState state) {
BitWidth addrBits = instance.getAttributeValue(ADDR_ATTR);
BitWidth dataBits = instance.getAttributeValue(DATA_ATTR);
RamState myState = (RamState) instance.getData(state);
if (myState == null) {
MemContents contents = MemContents.create(addrBits.getWidth(), dataBits.getWidth());
myState = new RamState(instance, contents, new MemListener(instance));
instance.setData(state, myState);
} else {
myState.setRam(instance);
}
return myState;
}
@Override
HexFrame getHexFrame(Project proj, Instance instance, CircuitState circState) {
RamState state = (RamState) getState(instance, circState);
return state.getHexFrame(proj);
}
@Override
public void propagate(InstanceState state) {
RamState myState = (RamState) getState(state);
BitWidth dataBits = state.getAttributeValue(DATA_ATTR);
Object busVal = state.getAttributeValue(ATTR_BUS);
boolean asynch = busVal == null ? false : busVal.equals(BUS_ASYNCH);
boolean separate = busVal == null ? false : busVal.equals(BUS_SEPARATE);
Value addrValue = state.getPort(ADDR);
boolean chipSelect = state.getPort(CS) != Value.FALSE;
boolean triggered = asynch || myState.setClock(state.getPort(CLK), StdAttr.TRIG_RISING);
boolean outputEnabled = state.getPort(OE) != Value.FALSE;
boolean shouldClear = state.getPort(CLR) == Value.TRUE;
if (shouldClear) {
myState.getContents().clear();
}
if (!chipSelect) {
myState.setCurrent(-1);
state.setPort(DATA, Value.createUnknown(dataBits), DELAY);
return;
}
int addr = addrValue.toIntValue();
if (!addrValue.isFullyDefined() || addr < 0)
return;
if (addr != myState.getCurrent()) {
myState.setCurrent(addr);
myState.scrollToShow(addr);
}
if (!shouldClear && triggered) {
boolean shouldStore;
if (separate) {
shouldStore = state.getPort(WE) != Value.FALSE;
} else {
shouldStore = !outputEnabled;
}
if (shouldStore) {
Value dataValue = state.getPort(separate ? DIN : DATA);
myState.getContents().set(addr, dataValue.toIntValue());
}
}
if (outputEnabled) {
int val = myState.getContents().get(addr);
state.setPort(DATA, Value.createKnown(dataBits, val), DELAY);
} else {
state.setPort(DATA, Value.createUnknown(dataBits), DELAY);
}
}
@Override
public void paintInstance(InstancePainter painter) {
super.paintInstance(painter);
Object busVal = painter.getAttributeValue(ATTR_BUS);
boolean asynch = busVal == null ? false : busVal.equals(BUS_ASYNCH);
boolean separate = busVal == null ? false : busVal.equals(BUS_SEPARATE);
if (!asynch) painter.drawClock(CLK, Direction.NORTH);
painter.drawPort(OE, Strings.get("ramOELabel"), Direction.SOUTH);
painter.drawPort(CLR, Strings.get("ramClrLabel"), Direction.SOUTH);
if (separate) {
painter.drawPort(WE, Strings.get("ramWELabel"), Direction.SOUTH);
painter.getGraphics().setColor(Color.BLACK);
painter.drawPort(DIN, Strings.get("ramDataLabel"), Direction.EAST);
}
}
private static class RamState extends MemState
implements InstanceData, AttributeListener {
private Instance parent;
private MemListener listener;
private HexFrame hexFrame = null;
private ClockState clockState;
RamState(Instance parent, MemContents contents, MemListener listener) {
super(contents);
this.parent = parent;
this.listener = listener;
this.clockState = new ClockState();
if (parent != null) parent.getAttributeSet().addAttributeListener(this);
contents.addHexModelListener(listener);
}
void setRam(Instance value) {
if (parent == value) return;
if (parent != null) parent.getAttributeSet().removeAttributeListener(this);
parent = value;
if (value != null) value.getAttributeSet().addAttributeListener(this);
}
@Override
public RamState clone() {
RamState ret = (RamState) super.clone();
ret.parent = null;
ret.clockState = this.clockState.clone();
ret.getContents().addHexModelListener(listener);
return ret;
}
// Retrieves a HexFrame for editing within a separate window
public HexFrame getHexFrame(Project proj) {
if (hexFrame == null) {
hexFrame = new HexFrame(proj, getContents());
hexFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
hexFrame = null;
}
});
}
return hexFrame;
}
//
// methods for accessing the write-enable data
//
public boolean setClock(Value newClock, Object trigger) {
return clockState.updateClock(newClock, trigger);
}
public void attributeListChanged(AttributeEvent e) { }
public void attributeValueChanged(AttributeEvent e) {
AttributeSet attrs = e.getSource();
BitWidth addrBits = attrs.getValue(Mem.ADDR_ATTR);
BitWidth dataBits = attrs.getValue(Mem.DATA_ATTR);
getContents().setDimensions(addrBits.getWidth(), dataBits.getWidth());
}
}
public static class Logger extends InstanceLogger {
@Override
public Object[] getLogOptions(InstanceState state) {
int addrBits = state.getAttributeValue(ADDR_ATTR).getWidth();
if (addrBits >= logOptions.length) addrBits = logOptions.length - 1;
synchronized(logOptions) {
Object[] ret = logOptions[addrBits];
if (ret == null) {
ret = new Object[1 << addrBits];
logOptions[addrBits] = ret;
for (int i = 0; i < ret.length; i++) {
ret[i] = Integer.valueOf(i);
}
}
return ret;
}
}
@Override
public String getLogName(InstanceState state, Object option) {
if (option instanceof Integer) {
String disp = Strings.get("ramComponent");
Location loc = state.getInstance().getLocation();
return disp + loc + "[" + option + "]";
} else {
return null;
}
}
@Override
public Value getLogValue(InstanceState state, Object option) {
if (option instanceof Integer) {
MemState s = (MemState) state.getData();
int addr = ((Integer) option).intValue();
return Value.createKnown(BitWidth.create(s.getDataBits()),
s.getContents().get(addr));
} else {
return Value.NIL;
}
}
}
}