diff --git a/src/main/java/de/neemann/digital/hdl/model2/HDLCircuit.java b/src/main/java/de/neemann/digital/hdl/model2/HDLCircuit.java index 87b2b06e0..c0e72a04e 100644 --- a/src/main/java/de/neemann/digital/hdl/model2/HDLCircuit.java +++ b/src/main/java/de/neemann/digital/hdl/model2/HDLCircuit.java @@ -43,6 +43,7 @@ public class HDLCircuit implements Iterable, HDLModel.BitProvider, Prin private final ArrayList outputs; private final ArrayList inputs; private final ArrayList listOfNets; + private final String description; private ArrayList ports; private NetList netList; private ArrayList nodes; @@ -89,6 +90,7 @@ public class HDLCircuit implements Iterable, HDLModel.BitProvider, Prin nets = new HashMap<>(); listOfNets = new ArrayList<>(); netList = new NetList(circuit); + description = circuit.getAttributes().get(Keys.DESCRIPTION); ArrayList clocks = new ArrayList<>(); @@ -100,7 +102,8 @@ public class HDLCircuit implements Iterable, HDLModel.BitProvider, Prin getNetOfPin(v.getPins().get(0)), HDLPort.Direction.OUT, // from inside the node this is an output because it defines a value v.getElementAttributes().getBits()) - .setPinNumber(v.getElementAttributes().get(Keys.PINNUMBER)); + .setPinNumber(v.getElementAttributes().get(Keys.PINNUMBER)) + .setDescription(v.getElementAttributes().get(Keys.DESCRIPTION)); addInput(port); if (v.equalsDescription(Clock.DESCRIPTION)) clocks.add(new ClockInfo(port, v.getElementAttributes().get(Keys.FREQUENCY))); @@ -110,7 +113,8 @@ public class HDLCircuit implements Iterable, HDLModel.BitProvider, Prin getNetOfPin(v.getPins().get(0)), HDLPort.Direction.IN, // from inside the node this is an input because it reads the value to output v.getElementAttributes().getBits()) - .setPinNumber(v.getElementAttributes().get(Keys.PINNUMBER))); + .setPinNumber(v.getElementAttributes().get(Keys.PINNUMBER)) + .setDescription(v.getElementAttributes().get(Keys.DESCRIPTION))); else if (v.equalsDescription(Splitter.DESCRIPTION)) handleSplitter(c.createNode(v, this)); else if (isRealElement(v)) @@ -460,6 +464,20 @@ public class HDLCircuit implements Iterable, HDLModel.BitProvider, Prin return hdlEntityName; } + /** + * @return the description of this circuit + */ + public String getDescription() { + return description; + } + + /** + * @return true if the circuit has a description + */ + public boolean hasDescription() { + return description != null && description.trim().length() > 0; + } + /** * Integrates a clock node. * diff --git a/src/main/java/de/neemann/digital/hdl/model2/HDLPort.java b/src/main/java/de/neemann/digital/hdl/model2/HDLPort.java index 6b943a752..ac3bba033 100644 --- a/src/main/java/de/neemann/digital/hdl/model2/HDLPort.java +++ b/src/main/java/de/neemann/digital/hdl/model2/HDLPort.java @@ -15,6 +15,8 @@ import java.io.IOException; */ public class HDLPort implements Printable, HasName { + private String description; + /** * The ports direction */ @@ -71,6 +73,31 @@ public class HDLPort implements Printable, HasName { return this; } + /** + * Sets the description of this port. + * + * @param description the description + * @return this for chained calls + */ + public HDLPort setDescription(String description) { + this.description = description; + return this; + } + + /** + * @return the ports description. + */ + public String getDescription() { + return description; + } + + /** + * @return true if this port has a description + */ + public boolean hasDescription() { + return description != null && description.trim().length() > 0; + } + /** * @return the net of this port */ diff --git a/src/main/java/de/neemann/digital/hdl/printer/CodePrinter.java b/src/main/java/de/neemann/digital/hdl/printer/CodePrinter.java index bbba0038a..b929125c5 100644 --- a/src/main/java/de/neemann/digital/hdl/printer/CodePrinter.java +++ b/src/main/java/de/neemann/digital/hdl/printer/CodePrinter.java @@ -18,6 +18,7 @@ public class CodePrinter implements Closeable { private File file; private int ident = 0; private boolean newLine = true; + private int pos; /** * Creates a new instance @@ -139,7 +140,7 @@ public class CodePrinter implements Closeable { */ public CodePrinter print(char c) throws IOException { if (newLine && c != '\n') { - int pos = ident * indentWidth; + pos = ident * indentWidth; for (int i = 0; i < pos; i++) out.write(' '); newLine = false; @@ -148,6 +149,8 @@ public class CodePrinter implements Closeable { out.write(c); else out.write(("" + c).getBytes(CHARSET)); + pos++; + if (c == '\n') { newLine = true; eolIsWritten(); @@ -191,4 +194,38 @@ public class CodePrinter implements Closeable { public File getFile() { return file; } + + + /** + * Pronts a comment to the target file + * + * @param singleLineComment the string which opens a single line comment (-- in VHDL) + * @param comment the comment to print + * @return this for chained calls + * @throws IOException IOException + */ + public CodePrinter printComment(String singleLineComment, String comment) throws IOException { + if (comment == null || comment.length() == 0) + return this; + + + int startPos = pos; + if (newLine) + startPos = ident * indentWidth; + + print(singleLineComment); + for (int i = 0; i < comment.length(); i++) { + char c = comment.charAt(i); + print(c); + if (c == '\n') { + int spaceCount = startPos - ident * indentWidth; + for (int j = 0; j < spaceCount; j++) + print(' '); + print(singleLineComment); + } + } + println(); + + return this; + } } diff --git a/src/main/java/de/neemann/digital/hdl/vhdl2/Separator.java b/src/main/java/de/neemann/digital/hdl/vhdl2/Separator.java index 653ed18cf..8ebec4e01 100644 --- a/src/main/java/de/neemann/digital/hdl/vhdl2/Separator.java +++ b/src/main/java/de/neemann/digital/hdl/vhdl2/Separator.java @@ -14,34 +14,88 @@ import java.io.IOException; */ public class Separator { private final String sep; + private final CodePrinter out; + private final String finalizerSeparator; private boolean first = true; + private LineFinalizer lineFinalizer; /** * Creates a new instance * + * @param out the print stream * @param sep The separator */ - public Separator(String sep) { + public Separator(CodePrinter out, String sep) { this.sep = sep; + this.out = out; + if (sep.length() > 0 && sep.charAt(sep.length() - 1) == '\n') + finalizerSeparator = sep.substring(0, sep.length() - 1); + else + finalizerSeparator = sep; } /** * Inserts the separator * - * @param out the print stream * @throws IOException IOException */ - public void check(CodePrinter out) throws IOException { + public void check() throws IOException { if (first) first = false; - else - out.print(getSeperator()); + else { + if (lineFinalizer == null) + printSeparator(out); + else { + out.print(finalizerSeparator); + lineFinalizer.finalizeLine(out); + lineFinalizer = null; + } + } } /** - * @return the separator + * prints the separator + * + * @param out the print stream + * @throws IOException IOException */ - public String getSeperator() { - return sep; + public void printSeparator(CodePrinter out) throws IOException { + out.print(sep); + } + + /** + * Sets the line finalizer + * This finalizer is only used once at the next line ending. + * + * @param lineFinalizer thi file finalizer + */ + public void setLineFinalizer(LineFinalizer lineFinalizer) { + this.lineFinalizer = lineFinalizer; + } + + /** + * Closes this Separator. + * If there is a pending line separator, it's printed. + * + * @throws IOException IOException + */ + public void close() throws IOException { + if (lineFinalizer != null) + lineFinalizer.finalizeLine(out); + } + + /** + * If there is a finalizer, this finalizer method is called + * instead of calling printSeparator. + * A finerlizer is used only one time. + */ + public interface LineFinalizer { + /** + * Prints the line ending + * + * @param out the stream to print to + * @throws IOException IOException + */ + void finalizeLine(CodePrinter out) throws IOException; } } diff --git a/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLCreator.java b/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLCreator.java index f68d0b23a..1f35be97f 100644 --- a/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLCreator.java +++ b/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLCreator.java @@ -27,7 +27,7 @@ public class VHDLCreator { /** * Creates a new instance * - * @param out the output stream + * @param out the output stream */ VHDLCreator(CodePrinter out) { this.out = out; @@ -104,6 +104,9 @@ public class VHDLCreator { .println("USE ieee.numeric_std.all;") .println(); + if (circuit.hasDescription()) + out.printComment("-- ", circuit.getDescription()); + out.print("entity ").print(circuit.getHdlEntityName()).println(" is").inc(); writePorts(out, circuit); out.dec(); @@ -152,16 +155,19 @@ public class VHDLCreator { public static void writePorts(CodePrinter out, HDLCircuit circuit) throws IOException { out.println("port (").inc(); - Separator sep = new Separator(";\n"); + Separator sep = new Separator(out, ";\n"); for (HDLPort i : circuit.getInputs()) { - sep.check(out); + sep.check(); out.print(i.getName()).print(": in ").print(getType(i.getBits())); + if (i.hasDescription()) sep.setLineFinalizer(ou -> ou.printComment(" -- ", i.getDescription())); } for (HDLPort o : circuit.getOutputs()) { - sep.check(out); + sep.check(); out.print(o.getName()).print(": out ").print(getType(o.getBits())); + if (o.hasDescription()) sep.setLineFinalizer(ou -> ou.printComment(" -- ", o.getDescription())); } + sep.close(); out.println(");").dec(); } @@ -204,16 +210,16 @@ public class VHDLCreator { if (!(node instanceof HDLNodeCustom)) library.getEntity(node).writeGenericMap(out, node); out.println("port map (").inc(); - Separator sep = new Separator(",\n"); + Separator sep = new Separator(out, ",\n"); for (HDLPort i : node.getInputs()) if (i.getNet() != null) { - sep.check(out); + sep.check(); out.print(i.getName()).print(" => ").print(i.getNet().getName()); } for (HDLPort o : node.getOutputs()) if (o.getNet() != null) { - sep.check(out); + sep.check(); out.print(o.getName()).print(" => ").print(o.getNet().getName()); } out.println(");").dec().dec(); diff --git a/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLTestBenchCreator.java b/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLTestBenchCreator.java index c8d8bc3fc..0b465fac5 100644 --- a/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLTestBenchCreator.java +++ b/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLTestBenchCreator.java @@ -116,13 +116,13 @@ public class VHDLTestBenchCreator { out.dec().println("begin").inc(); out.println("main_0 : main port map (").inc(); - Separator comma = new Separator(",\n"); + Separator comma = new Separator(out, ",\n"); for (HDLPort p : main.getInputs()) { - comma.check(out); + comma.check(); out.print(p.getName() + " => " + p.getName()); } for (HDLPort p : main.getOutputs()) { - comma.check(out); + comma.check(); out.print(p.getName() + " => " + p.getName()); } out.println(" );").dec(); @@ -225,10 +225,10 @@ public class VHDLTestBenchCreator { private LineListenerVHDL(CodePrinter out, ArrayList dataOrder) { this.out = out; this.dataOrder = dataOrder; - lineSep = new Separator("") { + lineSep = new Separator(out, "") { @Override - public String getSeperator() { - return ", -- i=" + (line++) + "\n"; + public void printSeparator(CodePrinter out) throws IOException { + out.print(", -- i=").print((line++)).print("\n"); } }; } @@ -241,12 +241,12 @@ public class VHDLTestBenchCreator { if (v.getType() == Value.Type.CLOCK) containsClock = true; if (containsClock) { - lineSep.check(out); + lineSep.check(); writeValues(values, true, 0); - lineSep.check(out); + lineSep.check(); writeValues(values, true, 1); } - lineSep.check(out); + lineSep.check(); writeValues(values, false, 0); } catch (IOException e) { throw new RuntimeException(e); @@ -255,9 +255,9 @@ public class VHDLTestBenchCreator { private void writeValues(Value[] values, boolean isClock, int clock) throws IOException { out.print("("); - Separator sep = new Separator(", "); + Separator sep = new Separator(out, ", "); for (int i = 0; i < values.length; i++) { - sep.check(out); + sep.check(); Value val = values[i]; int bits = dataOrder.get(i).getBits(); switch (val.getType()) { diff --git a/src/main/java/de/neemann/digital/hdl/vhdl2/entities/VHDLTemplate.java b/src/main/java/de/neemann/digital/hdl/vhdl2/entities/VHDLTemplate.java index 36f194ab6..768da82dc 100644 --- a/src/main/java/de/neemann/digital/hdl/vhdl2/entities/VHDLTemplate.java +++ b/src/main/java/de/neemann/digital/hdl/vhdl2/entities/VHDLTemplate.java @@ -74,9 +74,9 @@ public class VHDLTemplate implements VHDLEntity { final Entity e = getEntity(node); if (!e.getGenerics().isEmpty()) { out.println("generic map (").inc(); - Separator semic = new Separator(",\n"); + Separator semic = new Separator(out, ",\n"); for (VHDLTemplateFunctions.Generic gen : e.getGenerics()) { - semic.check(out); + semic.check(); final Object value = node.getElementAttributes().hgsMapGet(gen.getName()); out.print(gen.getName()).print(" => ").print(gen.format(value)); } diff --git a/src/test/java/de/neemann/digital/hdl/printer/CodePrinterStrTest.java b/src/test/java/de/neemann/digital/hdl/printer/CodePrinterStrTest.java index b96c3bdb4..69ff55677 100644 --- a/src/test/java/de/neemann/digital/hdl/printer/CodePrinterStrTest.java +++ b/src/test/java/de/neemann/digital/hdl/printer/CodePrinterStrTest.java @@ -23,6 +23,20 @@ public class CodePrinterStrTest extends TestCase { .dec() .println("test") .toString()); + + assertEquals("test;\n" + + " -- Hello\n" + + " -- World\n" + + " -- Long text\n" + + "test",new CodePrinterStr() + .println("test;") + .printComment(" -- ","Hello\nWorld\nLong text").print("test").toString()); + assertEquals("test; -- Hello\n" + + " -- World\n" + + " -- Long text\n" + + "test",new CodePrinterStr() + .print("test;") + .printComment(" -- ","Hello\nWorld\nLong text").print("test").toString()); } } diff --git a/src/test/java/de/neemann/digital/hdl/vhdl2/DescriptionTest.java b/src/test/java/de/neemann/digital/hdl/vhdl2/DescriptionTest.java new file mode 100644 index 000000000..a2b7599d2 --- /dev/null +++ b/src/test/java/de/neemann/digital/hdl/vhdl2/DescriptionTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018 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.hdl.vhdl2; + +import de.neemann.digital.core.NodeException; +import de.neemann.digital.draw.elements.PinException; +import de.neemann.digital.draw.library.ElementNotFoundException; +import de.neemann.digital.hdl.hgs.HGSEvalException; +import de.neemann.digital.hdl.model2.HDLCircuit; +import de.neemann.digital.hdl.model2.HDLException; +import de.neemann.digital.hdl.model2.HDLModel; +import de.neemann.digital.hdl.printer.CodePrinterStr; +import de.neemann.digital.integration.ToBreakRunner; +import junit.framework.TestCase; + +import java.io.IOException; + +public class DescriptionTest extends TestCase { + + public void testDescription() throws PinException, NodeException, ElementNotFoundException, IOException, HDLException, HGSEvalException { + ToBreakRunner br = new ToBreakRunner("dig/hdl/model2/naming.dig"); + HDLCircuit circuit = new HDLCircuit( + br.getCircuit(), + "main" + , new HDLModel(br.getLibrary()), + null) + .mergeConstants() + .mergeExpressions() + .nameNets(); + CodePrinterStr out = new CodePrinterStr(); + new VHDLCreator(out).printHDLCircuit(circuit); + + assertEquals("\n" + + "LIBRARY ieee;\n" + + "USE ieee.std_logic_1164.all;\n" + + "USE ieee.numeric_std.all;\n" + + "\n" + + "-- Simple test circuit\n" + + "-- used to test comments.\n"+ + "entity main is\n" + + " port (\n" + + " S0: in std_logic; -- First input\n" + + " -- This is a far longer text.\n" + + " S1: in std_logic; -- Second input\n" + + " S2: out std_logic; -- first output\n" + + " S3: out std_logic -- second output\n" + + " -- also with a longer text\n" + + " );\n" + + "end main;\n" + + "\n" + + "architecture Behavioral of main is\n" + + " signal s4: std_logic;\n" + + "begin\n" + + " s4 <= NOT (S0 OR S1);\n" + + " S2 <= (S0 XOR s4);\n" + + " S3 <= (s4 XOR S1);\n" + + "end Behavioral;\n", out.toString()); + } +} diff --git a/src/test/java/de/neemann/digital/hdl/vhdl2/SeparatorTest.java b/src/test/java/de/neemann/digital/hdl/vhdl2/SeparatorTest.java new file mode 100644 index 000000000..befd072a2 --- /dev/null +++ b/src/test/java/de/neemann/digital/hdl/vhdl2/SeparatorTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017 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.hdl.vhdl2; + +import de.neemann.digital.hdl.printer.CodePrinterStr; +import junit.framework.TestCase; + +import java.io.IOException; + +public class SeparatorTest extends TestCase { + + public void testSeparator() throws IOException { + CodePrinterStr out = new CodePrinterStr(); + + out.println("open (").inc(); + Separator sep = new Separator(out, ",\n"); + for (int i = 0; i < 4; i++) { + sep.check(); + out.print("item").print(i); + } + out.println(")").dec(); + out.print("close"); + + assertEquals("open (\n" + + " item0,\n" + + " item1,\n" + + " item2,\n" + + " item3)\n" + + "close", out.toString()); + } + + public void testSeparator2() throws IOException { + CodePrinterStr out = new CodePrinterStr(); + + out.println("open (").inc(); + Separator sep = new Separator(out, ",\n"); + for (int i = 0; i < 4; i++) { + sep.check(); + out.print("item").print(i); + String ic = "item" + i + " comment"; + sep.setLineFinalizer(o -> o.printComment(" \\\\ ", ic)); + } + sep.close(); + out.println(")").dec(); + out.print("close"); + + assertEquals("open (\n" + + " item0, \\\\ item0 comment\n" + + " item1, \\\\ item1 comment\n" + + " item2, \\\\ item2 comment\n" + + " item3 \\\\ item3 comment\n" + + " )\n" + + "close", out.toString()); + } + + public void testSeparator3() throws IOException { + CodePrinterStr out = new CodePrinterStr(); + + out.println("open (").inc(); + Separator sep = new Separator(out, ","); + for (int i = 0; i < 4; i++) { + sep.check(); + out.print("item").print(i); + if ((i&1)==0) { + String ic = "item" + i + " comment"; + sep.setLineFinalizer(o -> o.printComment(" \\\\ ", ic)); + } + } + sep.close(); + out.println(")").dec(); + out.print("close"); + + assertEquals("open (\n" + + " item0, \\\\ item0 comment\n" + + " item1,item2, \\\\ item2 comment\n" + + " item3)\n" + + "close", out.toString()); + } + +} \ No newline at end of file diff --git a/src/test/resources/dig/hdl/model2/naming.dig b/src/test/resources/dig/hdl/model2/naming.dig index 5657c36f5..8806f206a 100644 --- a/src/test/resources/dig/hdl/model2/naming.dig +++ b/src/test/resources/dig/hdl/model2/naming.dig @@ -1,11 +1,21 @@ 1 - + + + Description + Simple test circuit +used to test comments. + + Out + + Description + first output + Label S2 @@ -20,6 +30,11 @@ In + + Description + First input +This is a far longer text. + Label S0 @@ -30,6 +45,10 @@ In + + Description + Second input + Label S1 @@ -55,6 +74,11 @@ Out + + Description + second output +also with a longer text + Label S3