diff --git a/distribution/ReleaseNotes.txt b/distribution/ReleaseNotes.txt index 1d237f60f..08511b148 100644 --- a/distribution/ReleaseNotes.txt +++ b/distribution/ReleaseNotes.txt @@ -4,6 +4,8 @@ Release Notes HEAD, planned as v0.22 - Improved the RAM/ROM data loader. Now binary files and Intel HEX files are supported. +- A dual-port multi-byte memory has been added that allows single bytes to + be overwritten. v0.21, released on 10. Dec 2018 - Added a simple SVG importer to define custom shapes. diff --git a/src/main/java/de/neemann/digital/core/memory/RAMDualPort.java b/src/main/java/de/neemann/digital/core/memory/RAMDualPort.java index 3cc7f388c..890db056d 100644 --- a/src/main/java/de/neemann/digital/core/memory/RAMDualPort.java +++ b/src/main/java/de/neemann/digital/core/memory/RAMDualPort.java @@ -52,7 +52,7 @@ public class RAMDualPort extends Node implements Element, RAMInterface { /** * Creates a new instance * - * @param attr the elemets attributes + * @param attr the elements attributes */ public RAMDualPort(ElementAttributes attr) { super(true); @@ -167,11 +167,15 @@ public class RAMDualPort extends Node implements Element, RAMInterface { addr = (int) addrIn.getValue(); if (str) - memory.setData(addr, data); + writeDataToMemory(addr, data); lastClk = clk; } + void writeDataToMemory(int addr, long data) { + memory.setData(addr, data); + } + @Override public void writeOutputs() throws NodeException { if (ld) { diff --git a/src/main/java/de/neemann/digital/core/memory/RAMDualPortMasked.java b/src/main/java/de/neemann/digital/core/memory/RAMDualPortMasked.java new file mode 100644 index 000000000..66cc175f2 --- /dev/null +++ b/src/main/java/de/neemann/digital/core/memory/RAMDualPortMasked.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.core.memory; + +import de.neemann.digital.core.NodeException; +import de.neemann.digital.core.ObservableValue; +import de.neemann.digital.core.ObservableValues; +import de.neemann.digital.core.element.ElementAttributes; +import de.neemann.digital.core.element.ElementTypeDescription; +import de.neemann.digital.core.element.Keys; + +import static de.neemann.digital.core.element.PinInfo.input; + +/** + * A memory which allows to overwrite single bytes. + */ +public class RAMDualPortMasked extends RAMDualPort { + + private static final long[] MASK_TABLE = new long[256]; + + static { + for (int i = 0; i < 256; i++) { + long m = 0; + long bits = 0xff; + for (int b = 0; b < 8; b++) { + if ((i & (1 << b)) != 0) + m = m | bits; + bits = bits << 8; + } + MASK_TABLE[i] = m; + } + } + + /** + * The RAMs {@link ElementTypeDescription} + */ + public static final ElementTypeDescription DESCRIPTION = new ElementTypeDescription(RAMDualPortMasked.class, + input("A"), + input("Din"), + input("str"), + input("C").setClock(), + input("ld"), + input("mask")) + .addAttribute(Keys.ROTATE) + .addAttribute(Keys.BITS) + .addAttribute(Keys.ADDR_BITS) + .addAttribute(Keys.IS_PROGRAM_MEMORY) + .addAttribute(Keys.LABEL); + + private final int maskBits; + private ObservableValue maskVal; + + /** + * Creates a new instance + * + * @param attr the elements attributes + */ + public RAMDualPortMasked(ElementAttributes attr) { + super(attr); + maskBits = Math.min(8, (getDataBits() - 1) / 8 + 1); + } + + @Override + public void setInputs(ObservableValues inputs) throws NodeException { + super.setInputs(inputs); + maskVal = inputs.get(5).checkBits(maskBits, this).addObserverToValue(this); + } + + @Override + void writeDataToMemory(int addr, long data) { + DataField memory = getMemory(); + long old = memory.getDataWord(addr); + + long mask = MASK_TABLE[(int) maskVal.getValue()]; + data = data & mask; + old = old & ~mask; + + memory.setData(addr, data | old); + } +} diff --git a/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java b/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java index 7ec474908..83089b9a3 100644 --- a/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java +++ b/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java @@ -170,6 +170,7 @@ public class ElementLibrary implements Iterable .add(new LibraryNode(Lang.get("lib_memory")) .add(new LibraryNode(Lang.get("lib_ram")) .add(RAMDualPort.DESCRIPTION) + .add(RAMDualPortMasked.DESCRIPTION) .add(RAMSinglePort.DESCRIPTION) .add(RAMSinglePortSel.DESCRIPTION) .add(RegisterFile.DESCRIPTION) diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index bbed43250..c75b17695 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -478,6 +478,24 @@ Ist diese Leitung high, wird der Ausgang aktiviert, und die Daten liegen dort an. Ist diese Leitung high, wird das Datenwort gespeichert, wenn der Takt ansteigt. + RAM, getrennte Ports, Byte-Zugriff + RAM + Ein RAM Modul mit getrennten Daten-Anschlüssen für Lesen und Schreiben. + Es gibt einen Eingang für das Beschreiben und einen Ausgang für das Auslesen der gespeicherten Daten. + Zusätzlich wird das Überschreigen einzelner Bytes ermöglicht. Auf diese Weise kann z.B. in einem 32 + Bit Speicher ein einzelnes Byte überschrieben werden. + + Die Adresse, an der gelesen bzw. geschrieben wird. + Der Takt. Eine steigende Flanke aktiviert das Speichern. + Die Daten, die gespeichert werden sollen. + Ausgabe der gespeicherten Daten. + Ist diese Leitung high, wird der Ausgang aktiviert, und die Daten liegen dort an. + Ist diese Leitung high, wird das Datenwort gespeichert, wenn der Takt ansteigt. + Setzt die Schreibmaske. In dieser Maske steht jewels ein Bit für ein + Byte im Speicher, welches überschrieben werden kann. Soll also das zweite Byte im Speicherwort überschrieben werden, + muss auch das zweite Bit in der Maske gesetzt sein. + + EEPROM, getrennte Ports EEPROM Ein EEPROM Modul mit getrennten Daten-Anschlüssen für Lesen und Schreiben. diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 2861d0b40..e808e1774 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -484,6 +484,21 @@ If this input is high the output is activated and the data is visible at the output. If this input is high and when the clock becomes high, the the data is stored. + RAM, separated ports, byte access + RAM + A RAM module with separate inputs for storing and output for reading the stored data. + In addition, individual bytes can be overwritten. In this way, for example, a single byte can be overwritten in a 32-bit memory. + + The address to read from or write to. + Clock input + The data to be stored in the RAM. + The data output pin + If this input is high the output is activated and the data is visible at the output. + If this input is high and when the clock becomes high, the the data is stored. + Sets the write mask. In this mask there is one bit for each byte in the memory, + which can be overwritten. If the second byte in the memory word is to be overwritten, the second bit in + the mask must also be set. + EEPROM, separated Ports EEPROM A EEPROM module with separate inputs for storing and output for reading the stored data. diff --git a/src/test/java/de/neemann/digital/integration/TestExamples.java b/src/test/java/de/neemann/digital/integration/TestExamples.java index ef3ecc20e..d91721068 100644 --- a/src/test/java/de/neemann/digital/integration/TestExamples.java +++ b/src/test/java/de/neemann/digital/integration/TestExamples.java @@ -43,8 +43,8 @@ public class TestExamples extends TestCase { */ public void testTestExamples() throws Exception { File examples = new File(Resources.getRoot(), "/dig/test"); - assertEquals(146, new FileScanner(this::check).scan(examples)); - assertEquals(138, testCasesInFiles); + assertEquals(147, new FileScanner(this::check).scan(examples)); + assertEquals(139, testCasesInFiles); } /** diff --git a/src/test/resources/dig/test/RAM/maskedRAM.dig b/src/test/resources/dig/test/RAM/maskedRAM.dig new file mode 100644 index 000000000..25387643e --- /dev/null +++ b/src/test/resources/dig/test/RAM/maskedRAM.dig @@ -0,0 +1,184 @@ + + + 1 + + + + RAMDualPortMasked + + + AddrBits + 8 + + + Bits + 32 + + + + + + Out + + + Label + D + + + Bits + 32 + + + + + + In + + + Label + A + + + Bits + 8 + + + + + + In + + + Label + Din + + + Bits + 32 + + + + + + In + + + Label + str + + + + + + Clock + + + Label + C + + + + + + In + + + Label + ld + + + + + + In + + + Label + mask + + + Bits + 4 + + + + + + Testcase + + + Testdata + + C A Din str ld mask D + +# no write at all, mask is zero +C 0 0xffffffff 1 0 0b0000 x +0 0 0 0 1 0b0000 0 + +# write bytes + +C 0 0xffffffff 1 0 0b0001 x +0 0 0 0 1 0b0000 0xff + +C 0 0xffffff00 1 0 0b0010 x +0 0 0 0 1 0b0000 0xffff + +C 0 0xffff0000 1 0 0b0100 x +0 0 0 0 1 0b0000 0xffffff + +C 0 0xff000000 1 0 0b1000 x +0 0 0 0 1 0b0000 0xffffffff + +# write 16 bit words + +C 1 0xffff 1 0 0b0011 x +0 1 0 0 1 0b0000 0xffff + +C 1 0xffff0000 1 0 0b1100 x +0 1 0 0 1 0b0000 0xffffffff + +C 1 0xaaaa00 1 0 0b0110 x +0 1 0 0 1 0b0000 0xffaaaaff + +# write 32 bit words + +C 2 0xffffffff 1 0 0b1111 x +0 2 0 0 1 0b0000 0xffffffff + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/test/ram.dig b/src/test/resources/dig/test/RAM/ram.dig similarity index 100% rename from src/test/resources/dig/test/ram.dig rename to src/test/resources/dig/test/RAM/ram.dig