--- /dev/null
+package net.mograsim.logic.core.components.memory;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+import net.mograsim.logic.core.types.Bit;
+import net.mograsim.logic.core.types.BitVector;
+
+public class WordAddressableMemory
+{
+ private final int cellWidth;
+ private final long minimalAddress, maximalAddress;
+ private final int pageSize = 64;
+
+ private HashMap<Long, Page> pages;
+
+ public WordAddressableMemory(int cellWidth, long minimalAddress, long maximalAddress)
+ {
+ super();
+ this.cellWidth = cellWidth;
+ this.minimalAddress = minimalAddress;
+ this.maximalAddress = maximalAddress;
+ this.pages = new HashMap<>();
+ }
+
+ public void setCell(long address, BitVector b)
+ {
+ if (address < minimalAddress || address > maximalAddress)
+ throw new IndexOutOfBoundsException(String.format("Memory address out of bounds! Minimum: %d Maimum: %d Actual: %d",
+ minimalAddress, maximalAddress, address));
+ long page = address / pageSize;
+ int offset = (int) (address % pageSize);
+ Page p = pages.get(Long.valueOf(page));
+ if (p == null)
+ pages.put(page, p = new Page());
+ p.setCell(offset, b);
+ }
+
+ public BitVector getCell(long address)
+ {
+ long page = address / pageSize;
+ int offset = (int) (address % pageSize);
+ Page p = pages.get(Long.valueOf(page));
+ if (p == null)
+ return BitVector.of(Bit.U, cellWidth);
+ return p.getCell(offset);
+ }
+
+ private class Page
+ {
+ private BitVector[] memory;
+
+ public Page()
+ {
+ memory = new BitVector[pageSize];
+ }
+
+ public BitVector getCell(int index)
+ {
+ BitVector b = memory[index];
+ if (b == null)
+ return BitVector.of(Bit.U, cellWidth);
+ return memory[index];
+ }
+
+ public void setCell(int index, BitVector bits)
+ {
+ if (bits.length() != cellWidth)
+ throw new IllegalArgumentException(String.format(
+ "BitVector to be saved in memory cell has unexpected length. Expected: %d Actual: %d", cellWidth, bits.length()));
+ memory[index] = bits;
+ }
+
+ @Override
+ public String toString()
+ {
+ return Arrays.deepToString(memory);
+ }
+ }
+}
--- /dev/null
+package net.mograsim.logic.core.components.memory;
+
+import java.util.List;
+
+import net.mograsim.logic.core.components.BasicComponent;
+import net.mograsim.logic.core.timeline.Timeline;
+import net.mograsim.logic.core.types.Bit;
+import net.mograsim.logic.core.types.BitVector;
+import net.mograsim.logic.core.wires.Wire.ReadEnd;
+import net.mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+/**
+ * A memory component that only allows access to words of a specific length
+ */
+public class WordAddressableMemoryComponent extends BasicComponent
+{
+ private final WordAddressableMemory memory;
+ private final static Bit read = Bit.ONE;
+
+ private ReadWriteEnd data;
+ private ReadEnd rWBit, address;
+
+ /**
+ * @param data The bits of this ReadEnd are the value that is written to/read from memory; The bit width of this wire is the width of
+ * a memory word
+ * @param rWBit The value of the 0th bit dictates the mode: 0: Write, 1: Read
+ * @param address The bits of this ReadEnd address the memory cell to read/write
+ */
+ public WordAddressableMemoryComponent(Timeline timeline, int processTime, long minimalAddress, long maximalAddress, ReadWriteEnd data,
+ ReadEnd rWBit, ReadEnd address)
+ {
+ super(timeline, processTime);
+ this.data = data;
+ this.rWBit = rWBit;
+ this.address = address;
+ data.registerObserver(this);
+ rWBit.registerObserver(this);
+ address.registerObserver(this);
+
+ memory = new WordAddressableMemory(data.length(), minimalAddress, maximalAddress);
+ }
+
+ @Override
+ protected void compute()
+ {
+ if (!address.hasNumericValue())
+ {
+ if (read.equals(rWBit.getValue()))
+ data.feedSignals(BitVector.of(Bit.U, data.length()));
+ else
+ data.clearSignals();
+ return;
+ }
+ long addressed = address.getUnsignedValue();
+ if (read.equals(rWBit.getValue()))
+ data.feedSignals(memory.getCell(addressed));
+ else
+ {
+ data.clearSignals();
+ System.out.println(memory);
+ memory.setCell(addressed, data.getValues());
+ }
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ return List.of(data, rWBit, address);
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of(data);
+ }
+
+}
\ No newline at end of file
import static org.junit.jupiter.api.Assertions.assertEquals;\r
import static org.junit.jupiter.api.Assertions.fail;\r
\r
+import java.math.BigInteger;\r
+import java.util.Random;\r
import java.util.function.LongConsumer;\r
\r
+import org.junit.Before;\r
import org.junit.jupiter.api.Test;\r
\r
import net.mograsim.logic.core.components.Connector;\r
import net.mograsim.logic.core.components.gates.NotGate;\r
import net.mograsim.logic.core.components.gates.OrGate;\r
import net.mograsim.logic.core.components.gates.XorGate;\r
+import net.mograsim.logic.core.components.memory.WordAddressableMemoryComponent;\r
import net.mograsim.logic.core.timeline.Timeline;\r
import net.mograsim.logic.core.types.Bit;\r
import net.mograsim.logic.core.types.BitVector;\r
{\r
private Timeline t = new Timeline(11);\r
\r
+ @Before\r
+ void resetTimeline()\r
+ {\r
+ t.reset();\r
+ }\r
+\r
@Test\r
void circuitExampleTest()\r
{\r
@Test\r
void splitterTest()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 3, 1), b = new Wire(t, 2, 1), c = new Wire(t, 3, 1), in = new Wire(t, 8, 1);\r
in.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);\r
new Splitter(t, in.createReadOnlyEnd(), a.createReadWriteEnd(), b.createReadWriteEnd(), c.createReadWriteEnd());\r
@Test\r
void mergerTest()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 3, 1), b = new Wire(t, 2, 1), c = new Wire(t, 3, 1), out = new Wire(t, 8, 1);\r
a.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO);\r
b.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO);\r
@Test\r
void fusionTest1()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 3, 1), b = new Wire(t, 2, 1), c = new Wire(t, 3, 1), out = new Wire(t, 8, 1);\r
Wire.fuse(a, out, 0, 0, a.length);\r
Wire.fuse(b, out, 0, a.length, b.length);\r
@Test\r
void fusionTest2()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 3, 1), b = new Wire(t, 3, 1);\r
Wire.fuse(a, b);\r
ReadWriteEnd rw = a.createReadWriteEnd();\r
@Test\r
void fusionTest3()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 3, 1), b = new Wire(t, 3, 1);\r
a.createReadWriteEnd().feedSignals(Bit.Z, Bit.U, Bit.X);\r
t.executeAll();\r
@Test\r
void muxTest()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 4, 3), b = new Wire(t, 4, 6), c = new Wire(t, 4, 4), select = new Wire(t, 2, 5), out = new Wire(t, 4, 1);\r
ReadWriteEnd selectIn = select.createReadWriteEnd();\r
\r
@Test\r
void demuxTest()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 4, 3), b = new Wire(t, 4, 6), c = new Wire(t, 4, 4), select = new Wire(t, 2, 5), in = new Wire(t, 4, 1);\r
ReadWriteEnd selectIn = select.createReadWriteEnd();\r
\r
@Test\r
void andTest()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 4, 1), b = new Wire(t, 4, 3), c = new Wire(t, 4, 1);\r
new AndGate(t, 1, c.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd());\r
a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO);\r
@Test\r
void orTest()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 4, 1), b = new Wire(t, 4, 3), c = new Wire(t, 4, 1);\r
new OrGate(t, 1, c.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd());\r
a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO);\r
@Test\r
void nandTest()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 4, 1), b = new Wire(t, 4, 3), c = new Wire(t, 4, 1), d = new Wire(t, 4, 1);\r
new NandGate(t, 1, d.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd());\r
a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO);\r
@Test\r
void norTest()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 4, 1), b = new Wire(t, 4, 3), c = new Wire(t, 4, 1), d = new Wire(t, 4, 1);\r
new NorGate(t, 1, d.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd());\r
a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO);\r
@Test\r
void xorTest()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 3, 1), b = new Wire(t, 3, 2), c = new Wire(t, 3, 1), d = new Wire(t, 3, 1);\r
new XorGate(t, 1, d.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd());\r
a.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE);\r
@Test\r
void notTest()\r
{\r
- t.reset();\r
Wire a = new Wire(t, 3, 1), b = new Wire(t, 3, 2);\r
new NotGate(t, 1, a.createReadOnlyEnd(), b.createReadWriteEnd());\r
a.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE);\r
@Test\r
void rsLatchCircuitTest()\r
{\r
- t.reset();\r
Wire r = new Wire(t, 1, 1), s = new Wire(t, 1, 1), t1 = new Wire(t, 1, 15), t2 = new Wire(t, 1, 1), q = new Wire(t, 1, 1),\r
nq = new Wire(t, 1, 1);\r
\r
@Test\r
void numericValueTest()\r
{\r
- t.reset();\r
-\r
Wire a = new Wire(t, 4, 1);\r
a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ONE, Bit.ONE);\r
\r
@Test\r
void multipleInputs()\r
{\r
- t.reset();\r
Wire w = new Wire(t, 2, 1);\r
ReadWriteEnd wI1 = w.createReadWriteEnd(), wI2 = w.createReadWriteEnd();\r
wI1.feedSignals(Bit.ONE, Bit.Z);\r
{\r
// Nur ein Experiment, was über mehrere 'passive' Bausteine hinweg passieren würde\r
\r
- t.reset();\r
-\r
Wire a = new Wire(t, 1, 2);\r
Wire b = new Wire(t, 1, 2);\r
Wire c = new Wire(t, 1, 2);\r
test2.assertAfterSimulationIs(Bit.ONE);\r
}\r
\r
+ @Test\r
+ public void wordAddressableMemoryLargeTest()\r
+ {\r
+ Wire rW = new Wire(t, 1, 2);\r
+ Wire data = new Wire(t, 16, 2);\r
+ Wire address = new Wire(t, 64, 2);\r
+ ReadWriteEnd rWI = rW.createReadWriteEnd();\r
+ ReadWriteEnd dataI = data.createReadWriteEnd();\r
+ ReadWriteEnd addressI = address.createReadWriteEnd();\r
+\r
+ WordAddressableMemoryComponent memory = new WordAddressableMemoryComponent(t, 4, 4096L, Long.MAX_VALUE, data.createReadWriteEnd(),\r
+ rW.createReadOnlyEnd(), address.createReadOnlyEnd());\r
+\r
+ Random r = new Random();\r
+ for (long j = 1; j > 0; j *= 2)\r
+ {\r
+ for (int i = 0; i < 50; i++)\r
+ {\r
+ String sAddress = String.format("%64s", BigInteger.valueOf(4096 + i + j).toString(2)).replace(' ', '0');\r
+ sAddress = new StringBuilder(sAddress).reverse().toString();\r
+ BitVector bAddress = BitVector.parse(sAddress);\r
+ addressI.feedSignals(bAddress);\r
+ t.executeAll();\r
+ String random = BigInteger.valueOf(Math.abs(r.nextInt())).toString(5);\r
+ random = random.substring(Integer.max(0, random.length() - 16));\r
+ random = String.format("%16s", random).replace(' ', '0');\r
+ random = random.replace('2', 'X').replace('3', 'Z').replace('4', 'U');\r
+ BitVector vector = BitVector.parse(random);\r
+ dataI.feedSignals(vector);\r
+ rWI.feedSignals(Bit.ZERO);\r
+ t.executeAll();\r
+ rWI.feedSignals(Bit.ONE);\r
+ t.executeAll();\r
+ dataI.clearSignals();\r
+ t.executeAll();\r
+\r
+ assertBitArrayEquals(dataI.getValues(), vector.getBits());\r
+ }\r
+ }\r
+ }\r
+\r
private static void assertBitArrayEquals(BitVector actual, Bit... expected)\r
{\r
assertArrayEquals(expected, actual.getBits());\r
import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
import net.mograsim.logic.model.editor.Editor;
import net.mograsim.logic.model.editor.states.EditorState;
+import net.mograsim.logic.model.editor.util.PrioritySet;
import net.mograsim.logic.model.model.ViewModelModifiable;
import net.mograsim.logic.model.model.components.GUIComponent;
import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
private final Editor editor;
private boolean initialized = false;
- private CornerHandle cornerHandle;
-
public HandleManager(Editor editor)
{
this.editor = editor;
handlePerInterfacePin = new HashMap<>();
pointHandlesPerWire = new HashMap<>();
handlePerComp = new HashMap<>();
- handles = new HashSet<>();
+ handles = new PrioritySet<>((a, b) -> Integer.compare(a.getPriority(), b.getPriority()));
wirePointHandles = new HashSet<>();
handlePerWire = new HashMap<>();
comps.forEach(c -> registerComponent(c));
model.getWiresByName().values().forEach(w -> registerWire(w));
- addHandle(cornerHandle = new CornerHandle(editor.toBeEdited));
+ addHandle(new CornerHandle(editor.toBeEdited));
}
}
public void click(Point clicked, int stateMask)
{
EditorState entryState = editor.stateManager.getState();
-
- // TODO: As soon as wires connected to a component being removed also are removed, change priority
- if (!cornerHandle.click(clicked.x, clicked.y, stateMask, entryState))
- if (!click(handlePerPin.values(), clicked, entryState, stateMask))
- if (!click(handlePerInterfacePin.values(), clicked, entryState, stateMask))
- if (!click(getWirePointHandles(), clicked, entryState, stateMask))
- if (!click(getWireHandles(), clicked, entryState, stateMask))
- if (!click(handlePerComp.values(), clicked, entryState, stateMask))
- entryState.clickedEmpty(clicked, stateMask);
+ // TODO: As soon as wires connected to a component being removed also are removed, change priority)
+ if (!click(handles, clicked, entryState, stateMask))
+ entryState.clickedEmpty(clicked, stateMask);
entryState.clicked(clicked, stateMask);
}
--- /dev/null
+package net.mograsim.logic.model.editor.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Similar to a SortedSet, except it is allowed for multiple elements to have the same priority (<code>c.compare(e1, e2) == 0</code> is
+ * allowed to be true for two different elements e1 and e2). However, to elements are not allowed to be equal according to
+ * <code>Object.equals(Object o)</code>.
+ *
+ * @author Fabian Stemmler
+ *
+ * @param <T> the type of elements in this list
+ */
+public class PrioritySet<T> implements Set<T>
+{
+ private ArrayList<T> list;
+ private Comparator<T> c;
+
+ public PrioritySet(Comparator<T> c)
+ {
+ setComparator(c);
+ list = new ArrayList<>();
+ }
+
+ @SuppressWarnings("unchecked")
+ public PrioritySet()
+ {
+ this((e1, e2) -> ((Comparable<T>) e1).compareTo(e2));
+ }
+
+ public void setComparator(Comparator<T> c)
+ {
+ this.c = c;
+ }
+
+ //@formatter:off
+ @Override
+ public int size() { return list.size(); }
+ @Override
+ public boolean isEmpty() { return list.isEmpty(); }
+ @Override
+ public boolean contains(Object o) { return list.isEmpty(); }
+ @Override
+ public Iterator<T> iterator() { return list.iterator(); }
+ @Override
+ public Object[] toArray() { return list.toArray(); }
+ @Override
+ public <E> E[] toArray(E[] a) { return list.toArray(a); }
+ @Override
+ public boolean remove(Object o) { return list.remove(o); }
+ @Override
+ public boolean containsAll(Collection<?> c) { return list.containsAll(c); }
+ @Override
+ public boolean removeAll(Collection<?> c) { return list.removeAll(c); }
+ @Override
+ public boolean retainAll(Collection<?> c) { return list.retainAll(c); }
+ @Override
+ public void clear() { list.clear(); }
+ //@formatter:on
+
+ @Override
+ public boolean add(T e)
+ {
+ if (isEmpty())
+ {
+ list.add(e);
+ return true;
+ }
+ int index = Collections.binarySearch(list, e, c);
+ if (index < 0)
+ index = -index - 1;
+ if (index < size())
+ {
+ if (list.get(index).equals(e))
+ return false;
+ list.add(index, e);
+ } else
+ list.add(e);
+ return true;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends T> c)
+ {
+ return c.stream().map(e -> add(e)).reduce(false, (a, b) -> a || b);
+ }
+
+ @Override
+ public String toString()
+ {
+ return list.toString();
+ }
+}