diff --git a/.gitignore b/.gitignore index 0dde712a4..9dd4b26aa 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,6 @@ buildNumber.properties /Digital.sh /upload.sh /copyToPluginExample.sh + +#netbeans project files +nbactions.xml \ No newline at end of file diff --git a/README.md b/README.md index 134c29a16..4227845bd 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,16 @@ These are the main features of Digital: See this [example](https://github.com/hneemann/digitalCustomComponents) for details. - Simple remote TCP interface which e.g. allows an [assembler IDE](https://github.com/hneemann/Assembler) to control the simulator. -- Components can be described using VHDL. The open source VHDL simulator [ghdl](http://ghdl.free.fr/) is required to +- Components can be described using VHDL. The open source VHDL simulator [ghdl](http://ghdl.free.fr/) is required to simulate a VHDL defined component. The ghdl source code is also available at [GitHub](https://github.com/ghdl/ghdl). -- Export to VHDL: A circuit can be exported to VHDL. There is also support for the +- Export to VHDL: A circuit can be exported to VHDL. There is also support for the [BASYS3 Board](https://reference.digilentinc.com/reference/programmable-logic/basys-3/start). See the documentation for details. The examples folder contains a variant of the example CPU, which runs on a BASYS3 board. -- Direct export of JEDEC files which you can flash to a [GAL16v8](https://www.microchip.com/wwwproducts/en/ATF16V8C) +- Components can be described using Verilog. The open source Verilog simulator [Icarus Verilog](http://iverilog.icarus.com/) is required to + simulate a Verilog defined component. The Icarus Verilog source code is also available at [GitHub](https://github.com/steveicarus/iverilog). +- Exporting a circuit to Verilog is also possible. Special thanks to Ivan de Jesus Deras Tabora, who has + implemented the Verilog code generator and all the necessary Verilog templates! +- Direct export of JEDEC files which you can flash to a [GAL16v8](https://www.microchip.com/wwwproducts/en/ATF16V8C) or a [GAL22v10](https://www.microchip.com/wwwproducts/en/ATF22V10C). These chips are somewhat outdated (introduced in 1985!) but sufficient for beginners exercises, easy to understand and well documented. Also the [ATF150x](https://www.microchip.com/design-centers/programmable-logic/spld-cpld/cpld-atf15xx-family) chips are diff --git a/distribution/ReleaseNotes.txt b/distribution/ReleaseNotes.txt index b1bae2c5f..2cf7bdd3e 100644 --- a/distribution/ReleaseNotes.txt +++ b/distribution/ReleaseNotes.txt @@ -4,6 +4,8 @@ HEAD, planned as v0.19 - Added a tabbed pane to the attributes dialog to make it more beginner friendly. - Added support for asynchronous sequential circuits such as the Muller-pipeline. Take a look at the new asynchronous examples for illustration. +- Added export to Verilog. Special thanks to Ivan de Jesus Deras Tabora, who has + implemented the Verilog code generator and all the necessary Verilog templates! - All examples are translated to english. - A "test all" function has been added to start all tests in all circuits in the current folder. diff --git a/src/main/java/de/neemann/digital/core/element/Keys.java b/src/main/java/de/neemann/digital/core/element/Keys.java index 19e9db783..7be89f0c4 100644 --- a/src/main/java/de/neemann/digital/core/element/Keys.java +++ b/src/main/java/de/neemann/digital/core/element/Keys.java @@ -618,6 +618,12 @@ private Keys() { public static final Key SETTINGS_GHDL_PATH = new Key.KeyFile("ghdlPath", new File("ghdl")).setSecondary(); + /** + * Path to iverilog installation directory + */ + public static final Key SETTINGS_IVERILOG_PATH + = new Key.KeyFile("iverilogPath", new File("iverilog")).setSecondary(); + /** * Avoid component tooltips in the main panel */ diff --git a/src/main/java/de/neemann/digital/core/extern/Application.java b/src/main/java/de/neemann/digital/core/extern/Application.java index ad03ebb34..87837ed04 100644 --- a/src/main/java/de/neemann/digital/core/extern/Application.java +++ b/src/main/java/de/neemann/digital/core/extern/Application.java @@ -26,7 +26,11 @@ enum Type { /** * ghdl vhdl interpreter */ - GHDL + GHDL, + /** + * Icarus verilog interpreter + */ + IVERILOG } /** @@ -41,6 +45,8 @@ static Application create(Type type) { return new ApplicationGeneric(); case GHDL: return new ApplicationGHDL(); + case IVERILOG: + return new ApplicationIVerilog(); default: return null; } diff --git a/src/main/java/de/neemann/digital/core/extern/ApplicationIVerilog.java b/src/main/java/de/neemann/digital/core/extern/ApplicationIVerilog.java new file mode 100644 index 000000000..2a4de5bf3 --- /dev/null +++ b/src/main/java/de/neemann/digital/core/extern/ApplicationIVerilog.java @@ -0,0 +1,177 @@ +/* + * 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.core.extern; + +import de.neemann.digital.core.element.Keys; +import de.neemann.digital.core.extern.handler.ProcessInterface; +import de.neemann.digital.core.extern.handler.StdIOInterface; +import de.neemann.digital.gui.Settings; +import de.neemann.digital.lang.Lang; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.LinkedList; + +/** + * Abstraction of the iverilog Application. + * See http://iverilog.icarus.com/ + */ +public class ApplicationIVerilog extends ApplicationVerilogStdIO { + private String iverilogFolder; + private String iverilog; + private String vvp; + private final boolean hasIverilog; + + /** + * Initialize a new instance + */ + public ApplicationIVerilog() { + iverilogFolder = ""; + hasIverilog = findIVerilog(); + } + + @Override + public ProcessInterface start(String label, String code, PortDefinition inputs, PortDefinition outputs) throws IOException { + File file = null; + + if (!hasIverilog) { + throw new IOException(Lang.get("err_iverilogNotInstalled")); + } + + try { + String ivlModuleDir = iverilogFolder + File.separator + "lib" + File.separator + "ivl"; + + file = createVerilogFile(label, code, inputs, outputs); + String testOutputName = label + ".out"; + String m1 = ProcessStarter.start(file.getParentFile(), iverilog, "-tvvp", "-o" + testOutputName, file.getName()); + ProcessBuilder pb = new ProcessBuilder(vvp, "-M", ivlModuleDir, testOutputName).redirectErrorStream(true).directory(file.getParentFile()); + return new IVerilogProcessInterface(pb.start(), file.getParentFile()); + } catch (IOException e) { + if (file != null) + ProcessStarter.removeFolder(file.getParentFile()); + if (iverilogNotFound(e)) + throw new IOException(Lang.get("err_iverilogNotInstalled")); + else + throw e; + } + } + + private boolean iverilogNotFound(Throwable e) { + while (e != null) { + if (e instanceof ProcessStarter.CouldNotStartProcessException) + return true; + e = e.getCause(); + } + return false; + } + + @Override + public boolean checkSupported() { + return true; + } + + @Override + public String checkCode(String label, String code, PortDefinition inputs, PortDefinition outputs) throws IOException { + File file = null; + + if (!hasIverilog) { + throw new IOException(Lang.get("err_iverilogNotInstalled")); + } + try { + file = createVerilogFile(label, code, inputs, outputs); + String testOutputName = label + ".out"; + + return ProcessStarter.start(file.getParentFile(), iverilog, "-tvvp", "-o" + testOutputName, file.getName()); + } catch (IOException e) { + if (iverilogNotFound(e)) + throw new IOException(Lang.get("err_iverilogNotInstalled")); + else + throw e; + } finally { + if (file != null) + ProcessStarter.removeFolder(file.getParentFile()); + } + } + + private boolean findIVerilog() { + Path ivp = null; + File ivDir = Settings.getInstance().get(Keys.SETTINGS_IVERILOG_PATH); + + if (ivDir != null) { + Path p = Paths.get(ivDir.getAbsolutePath()); + + if (Files.isExecutable(p)) { + ivp = p; + if (Files.isSymbolicLink(p)) { + try { + ivp = Files.readSymbolicLink(ivp); + } catch (IOException ex) { + return false; + } + } + } + } + + if (ivp == null) { + // Let's try to find iverilog in the system path + String[] strPaths = System.getenv("PATH").split(File.pathSeparator); + + for (String sp : strPaths) { + Path p = Paths.get(sp, "iverilog"); + + if (Files.isExecutable(p)) { + ivp = p; + if (Files.isSymbolicLink(p)) { + try { + ivp = Files.readSymbolicLink(ivp); + } catch (IOException ex) { + return false; + } + } + break; + } + } + } + + if (ivp != null) { + iverilogFolder = ivp.getParent().getParent().toString(); + iverilog = ivp.getParent().resolve("iverilog").toString(); + vvp = ivp.getParent().resolve("vvp").toString(); + + return true; + } else { + return false; + } + } + + private static final class IVerilogProcessInterface extends StdIOInterface { + private final File folder; + + private IVerilogProcessInterface(Process process, File folder) { + super(process); + this.folder = folder; + } + + @Override + public String getConsoleOutNoWarn(LinkedList consoleOut) { + StringBuilder sb = new StringBuilder(); + for (String s : consoleOut) { + if (!s.contains(": warning:") && !s.contains(": :")) + sb.append(s).append("\n"); + } + return sb.toString(); + } + + @Override + public void close() throws IOException { + super.close(); + ProcessStarter.removeFolder(folder); + } + } +} diff --git a/src/main/java/de/neemann/digital/core/extern/ApplicationVerilogStdIO.java b/src/main/java/de/neemann/digital/core/extern/ApplicationVerilogStdIO.java new file mode 100644 index 000000000..734b028f0 --- /dev/null +++ b/src/main/java/de/neemann/digital/core/extern/ApplicationVerilogStdIO.java @@ -0,0 +1,188 @@ +/* + * 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.core.extern; + +import de.neemann.digital.core.element.ElementAttributes; +import de.neemann.digital.core.element.Keys; +import de.neemann.digital.core.extern.VerilogTokenizer.Token; +import de.neemann.digital.hdl.hgs.Context; +import de.neemann.digital.hdl.hgs.HGSEvalException; +import de.neemann.digital.hdl.hgs.Parser; +import de.neemann.digital.hdl.hgs.Statement; + +import java.io.*; +import java.nio.file.Files; +import java.util.NoSuchElementException; + + +/** + * Base class of applications which are able to interpret Verilog-Code. + * The generated verilog code is able to operate with the {@link de.neemann.digital.core.extern.handler.StdIOInterface}. + */ +public abstract class ApplicationVerilogStdIO implements Application { + private Token currToken; + + private static final Statement TEMPLATE = + Parser.createFromJarStatic("verilog/VerilogStdIOTemplate.vtpl"); + + /** + * Creates a verilog file in a temp directory. + * + * @param label the name of the verilog code + * @param code the verilog code + * @param inputs the inputs + * @param outputs the outputs + * @return the verilog file + * @throws IOException IOException + */ + public File createVerilogFile(String label, String code, PortDefinition inputs, PortDefinition outputs) throws IOException { + File dir = Files.createTempDirectory("digital_verilog_").toFile(); + + File file = new File(dir, label + ".v"); + + try (Writer w = new FileWriter(file)) { + w.write(createVerilog(label, code, inputs, outputs)); + } catch (HGSEvalException e) { + throw new IOException("error evaluating the template", e); + } + + return file; + } + + /** + * Creates the verilog code + * + * @param label the name of the verilog module + * @param code the verilog code + * @param inputs the inputs + * @param outputs the outputs + * @return the verilog code + * @throws HGSEvalException HGSEvalException + */ + public String createVerilog(String label, String code, PortDefinition inputs, PortDefinition outputs) throws HGSEvalException { + Context context = new Context() + .declareVar("moduleName", label) + .declareVar("code", code) + .declareVar("inputs", inputs) + .declareVar("outputs", outputs); + + TEMPLATE.execute(context); + return context.toString(); + } + + private void match(Token tkExpect, String tkText, VerilogTokenizer st) throws ParseException, IOException, VerilogTokenizer.TokenizerException { + if (currToken != tkExpect) { + throw new ParseException("unexpected '" + tkText + "'"); + } + currToken = st.nextToken(); + } + + @Override + public boolean ensureConsistency(ElementAttributes attributes) { + String code = attributes.get(Keys.EXTERNAL_CODE); + VerilogTokenizer st = new VerilogTokenizer(new StringReader(code)); + + try { + currToken = st.nextToken(); + + match(Token.MODULE, "keyword 'module'", st); + String label = st.value(); + match(Token.IDENT, "identifier", st); + match(Token.OPENPAR, "'('", st); + + PortDefinition in = new PortDefinition(""); + PortDefinition out = new PortDefinition(""); + scanPorts(st, in, out); + + if (currToken == Token.SEMICOLON) { + if (!st.lookEndModule()) { + return false; + } + } + + if (in.size() > 0 && out.size() > 0) { + attributes.set(Keys.LABEL, label); + attributes.set(Keys.EXTERNAL_INPUTS, in.toString()); + attributes.set(Keys.EXTERNAL_OUTPUTS, out.toString()); + return true; + } else + return false; + + } catch (NoSuchElementException | ParseException | VerilogTokenizer.TokenizerException | IOException e) { + return false; + } + } + + private void scanPorts(VerilogTokenizer st, PortDefinition in, PortDefinition out) throws ParseException, IOException, VerilogTokenizer.TokenizerException { + boolean keepScanning = true; + + do { + scanPort(st, in, out); + switch (currToken) { + case CLOSEPAR: + currToken = st.nextToken(); + keepScanning = false; + break; + case COMMA: + currToken = st.nextToken(); + break; + default: + throw new ParseException("unexpected '" + st.value() + "'"); + } + } while (keepScanning); + } + + private void scanPort(VerilogTokenizer st, PortDefinition in, PortDefinition out) throws ParseException, IOException, VerilogTokenizer.TokenizerException { + boolean isInput; + + switch (currToken) { + case INPUT: + isInput = true; + currToken = st.nextToken(); + if (currToken == Token.WIRE) { + currToken = st.nextToken(); + } + break; + case OUTPUT: + isInput = false; + currToken = st.nextToken(); + if (currToken == Token.WIRE + || currToken == Token.REG) { + currToken = st.nextToken(); + } + break; + default: + throw new ParseException("unexpected '" + st.value() + "'"); + } + + int bits = 1; + if (currToken == Token.OPENBRACKET) { + match(Token.OPENBRACKET, "", st); + String rangeStart = st.value(); + match(Token.NUMBER, "a number", st); + match(Token.COLON, "':'", st); + String rangeEnd = st.value(); + match(Token.NUMBER, "a number", st); + match(Token.CLOSEBRACKET, "']'", st); + bits = (Integer.parseInt(rangeStart) - Integer.parseInt(rangeEnd)) + 1; + } + String name = st.value(); + match(Token.IDENT, "identifier", st); + + if (isInput) { + in.addPort(name, bits); + } else { + out.addPort(name, bits); + } + } + + private static final class ParseException extends Exception { + private ParseException(String message) { + super(message); + } + } + +} diff --git a/src/main/java/de/neemann/digital/core/extern/VerilogTokenizer.java b/src/main/java/de/neemann/digital/core/extern/VerilogTokenizer.java new file mode 100644 index 000000000..cecf98b89 --- /dev/null +++ b/src/main/java/de/neemann/digital/core/extern/VerilogTokenizer.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2016 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.extern; + +import de.neemann.digital.core.ExceptionWithOrigin; + +import java.io.IOException; +import java.io.Reader; + + +/** + * Simple tokenizer to tokenize boolean expressions. + */ +public class VerilogTokenizer { + + enum Token {UNKNOWN, MODULE, INPUT, OUTPUT, INOUT, REG, WIRE, ENDMODULE, + EOF, NUMBER, IDENT, OPENPAR, CLOSEPAR, OPENBRACKET, CLOSEBRACKET, + SEMICOLON, COLON, COMMA}; + + private final Reader in; + private String value; + private boolean isUnreadChar = false; + private int unreadChar; + + /** + * Creates a new instance + * + * @param in the reader + */ + public VerilogTokenizer(Reader in) { + this.in = in; + } + + /** + * Look for the end of the module + * @return true if the end of the module was found, false otherwise. + * @throws IOException IOException + */ + public boolean lookEndModule() throws IOException { + while (true) { + int ch = readChar(); + + if (ch == 'e') { + StringBuilder sb = new StringBuilder(); + while (Character.isLetter(ch)) { + sb.append((char) ch); + ch = readChar(); + } + if (sb.toString().equals("endmodule")) + return true; + } else if (ch == -1) { + break; + } + } + return false; + } + + /** + * Reads the next token + * + * @return the token + * @throws IOException IOException + * @throws TokenizerException TokenizerException + */ + public Token nextToken() throws IOException, TokenizerException { + while (true) { + int ch = readChar(); + + if (ch == '/') { + int cc = readChar(); + switch (cc) { + case '*': + boolean hasAsterisk = false; + + while (true) { + cc = readChar(); + if (cc == -1) + throw new TokenizerException("unexpected EOF"); + if (cc == '/' && hasAsterisk) + break; + + hasAsterisk = (ch == '*'); + } + break; + case '/': + while (cc != '\n' && cc != -1) { + cc = readChar(); + } + + continue; + default: + unreadChar(cc); + break; + } + } + + value = "" + (char) ch; + + switch (ch) { + case ' ': + case '\t': + case '\r': + case '\n': + continue; + case -1: return Token.EOF; + case '(': return Token.OPENPAR; + case ')': return Token.CLOSEPAR; + case '[': return Token.OPENBRACKET; + case ']': return Token.CLOSEBRACKET; + case ';': return Token.SEMICOLON; + case ':': return Token.COLON; + case ',': return Token.COMMA; + case '`': + while (ch != '\n' && ch != -1) { + ch = readChar(); + } + break; + default: + if (isNumberChar(ch)) { + StringBuilder sb = new StringBuilder(); + + while (isNumberChar(ch)) { + sb.append((char) ch); + ch = readChar(); + } + unreadChar(ch); + value = sb.toString(); + return Token.NUMBER; + } else if (isIdentChar(ch)) { + StringBuilder sb = new StringBuilder(); + + while (isIdentChar(ch) || isNumberChar(ch) || ch == '$') { + sb.append((char) ch); + ch = readChar(); + } + unreadChar(ch); + value = sb.toString(); + + return lookUpKeyword(value); + } else { + throw new TokenizerException("unexpected '" + (char) ch + "'"); + } + } + } + } + + private Token lookUpKeyword(String str) { + switch (str) { + case "module": return Token.MODULE; + case "input": return Token.INPUT; + case "output": return Token.OUTPUT; + case "inout": return Token.INOUT; + case "reg": return Token.REG; + case "wire": return Token.WIRE; + case "endmodule": return Token.ENDMODULE; + default: + return Token.IDENT; + } + } + + + /** + * @return the value of the last parsed token + */ + public String value() { + return value; + } + + private int readChar() throws IOException { + if (isUnreadChar) { + isUnreadChar = false; + return unreadChar; + } else + return in.read(); + } + + private void unreadChar(int c) { + unreadChar = c; + isUnreadChar = true; + } + + private boolean isIdentChar(int c) { + return (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c == '_'); + } + + private boolean isNumberChar(int c) { + return (c >= '0' && c <= '9'); + } + + /** + * The tokenizer exception + */ + public static final class TokenizerException extends ExceptionWithOrigin { + private TokenizerException(String message) { + super(message); + } + } +} diff --git a/src/main/java/de/neemann/digital/core/extern/handler/StdIOInterface.java b/src/main/java/de/neemann/digital/core/extern/handler/StdIOInterface.java index 948283ddd..299cc2f25 100644 --- a/src/main/java/de/neemann/digital/core/extern/handler/StdIOInterface.java +++ b/src/main/java/de/neemann/digital/core/extern/handler/StdIOInterface.java @@ -171,6 +171,7 @@ public void readValues(ObservableValues values) throws IOException { char c = line.charAt(pos); switch (c) { case 'Z': + case 'z': highZ |= mask; break; case 'H': @@ -179,6 +180,7 @@ public void readValues(ObservableValues values) throws IOException { break; case 'W': case 'X': + case 'x': case 'U': case 'L': case '0': diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index 6f79f4e2e..8691ca0fc 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -76,6 +76,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import static de.neemann.digital.draw.shapes.GenericShape.SIZE; +import de.neemann.digital.hdl.verilog2.VerilogGenerator; import static javax.swing.JOptionPane.showInputDialog; /** @@ -520,6 +521,7 @@ public void actionPerformed(ActionEvent e) { export.add(new ExportZipAction(this).createJMenuItem()); export.add(createVHDLExportAction().createJMenuItem()); + export.add(createVerilogExportAction().createJMenuItem()); JMenu file = new JMenu(Lang.get("menu_file")); menuBar.add(file); @@ -575,6 +577,41 @@ public void actionPerformed(ActionEvent actionEvent) { }.setToolTip(Lang.get("menu_exportVHDL_tt")); } + private ToolTipAction createVerilogExportAction() { + return new ToolTipAction(Lang.get("menu_exportVerilog")) { + @Override + public void actionPerformed(ActionEvent actionEvent) { + + // check model for errors + try { + new ModelCreator(circuitComponent.getCircuit(), library).createModel(false); + } catch (PinException | NodeException | ElementNotFoundException e) { + showErrorWithoutARunningModel(Lang.get("msg_modelHasErrors"), e); + return; + } + + JFileChooser fc = new MyFileChooser(); + if (filename != null) + fc.setSelectedFile(SaveAsHelper.checkSuffix(filename, "v")); + + ElementAttributes settings = Settings.getInstance().getAttributes(); + File exportDir = settings.getFile("exportDirectory"); + if (exportDir != null) + fc.setCurrentDirectory(exportDir); + + fc.addChoosableFileFilter(new FileNameExtensionFilter("Verilog", "v")); + new SaveAsHelper(Main.this, fc, "v").checkOverwrite( + file -> { + settings.setFile("exportDirectory", file.getParentFile()); + try (VerilogGenerator vlog = new VerilogGenerator(library, new CodePrinter(file))) { + vlog.export(circuitComponent.getCircuit()); + } + } + ); + } + }.setToolTip(Lang.get("menu_exportVerilog_tt")); + } + /** * @return the file name base */ diff --git a/src/main/java/de/neemann/digital/gui/Settings.java b/src/main/java/de/neemann/digital/gui/Settings.java index 7c67e2bcc..f8afc8bf5 100644 --- a/src/main/java/de/neemann/digital/gui/Settings.java +++ b/src/main/java/de/neemann/digital/gui/Settings.java @@ -56,6 +56,7 @@ private Settings() { intList.add(Keys.SETTINGS_ATF1502_FITTER); intList.add(Keys.SETTINGS_ATMISP); intList.add(Keys.SETTINGS_GHDL_PATH); + intList.add(Keys.SETTINGS_IVERILOG_PATH); intList.add(Keys.SETTINGS_FONT_SCALING); intList.add(Keys.SETTINGS_MAC_MOUSE); diff --git a/src/main/java/de/neemann/digital/gui/components/EditorFactory.java b/src/main/java/de/neemann/digital/gui/components/EditorFactory.java index c552917bd..febe4bdc9 100644 --- a/src/main/java/de/neemann/digital/gui/components/EditorFactory.java +++ b/src/main/java/de/neemann/digital/gui/components/EditorFactory.java @@ -642,7 +642,7 @@ public void actionPerformed(ActionEvent actionEvent) { try { String message = app.checkCode(label, code, ins, outs); - if (message != null) + if (message != null && !message.isEmpty()) new ErrorMessage(Lang.get("msg_checkResult") + "\n\n" + message).show(getAttributeDialog()); } catch (IOException e) { new ErrorMessage(Lang.get("msg_checkResult")).addCause(e).show(getAttributeDialog()); diff --git a/src/main/java/de/neemann/digital/hdl/boards/BoardClockInfo.java b/src/main/java/de/neemann/digital/hdl/boards/BoardClockInfo.java new file mode 100644 index 000000000..8b8102d5b --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/boards/BoardClockInfo.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Ivan Deras + * 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.boards; + +/** + * Board clock pin information + */ +public class BoardClockInfo { + private final String pinNumber; // The pin number + private final double clkPeriod; // Clock period in nanoseconds + + /** + * Initialize a new instance + * + * @param pinNumber The pin number + * @param clkPeriod The clock period in nanoseconds + */ + public BoardClockInfo(String pinNumber, double clkPeriod) { + this.pinNumber = pinNumber; + this.clkPeriod = clkPeriod; + } + + /** + * Returns the pin number + * + * @return the pin number + */ + public String getPinNumber() { + return pinNumber; + } + + /** + * Returns the clock period + * + * @return the clock period + */ + public double getClkPeriod() { + return clkPeriod; + } +} diff --git a/src/main/java/de/neemann/digital/hdl/vhdl2/boards/BoardInterface.java b/src/main/java/de/neemann/digital/hdl/boards/BoardInterface.java similarity index 94% rename from src/main/java/de/neemann/digital/hdl/vhdl2/boards/BoardInterface.java rename to src/main/java/de/neemann/digital/hdl/boards/BoardInterface.java index f78868ce0..0f64020ec 100644 --- a/src/main/java/de/neemann/digital/hdl/vhdl2/boards/BoardInterface.java +++ b/src/main/java/de/neemann/digital/hdl/boards/BoardInterface.java @@ -3,7 +3,7 @@ * 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.boards; +package de.neemann.digital.hdl.boards; import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator; import de.neemann.digital.hdl.model2.HDLModel; diff --git a/src/main/java/de/neemann/digital/hdl/vhdl2/boards/BoardProvider.java b/src/main/java/de/neemann/digital/hdl/boards/BoardProvider.java similarity index 72% rename from src/main/java/de/neemann/digital/hdl/vhdl2/boards/BoardProvider.java rename to src/main/java/de/neemann/digital/hdl/boards/BoardProvider.java index 8ca683a6e..e6ba91994 100644 --- a/src/main/java/de/neemann/digital/hdl/vhdl2/boards/BoardProvider.java +++ b/src/main/java/de/neemann/digital/hdl/boards/BoardProvider.java @@ -3,7 +3,7 @@ * 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.boards; +package de.neemann.digital.hdl.boards; import de.neemann.digital.core.element.Keys; import de.neemann.digital.draw.elements.Circuit; @@ -30,7 +30,7 @@ private BoardProvider() { } /** - * Returns a spscific board + * Returns a specific board * * @param circuit the circuit * @return the board or null @@ -49,13 +49,20 @@ public BoardInterface getBoard(Circuit circuit) { if (board == null) return null; - if (board.equals("basys3")) - return new Vivado("LVCMOS33", - "W5", - 10, - new ClockIntegratorARTIX7(10), - "xc7a35ticpg236-1L"); + switch (board) { + case "basys3": + return new Vivado("LVCMOS33", + "W5", + 10, + new ClockIntegratorARTIX7(10), + "xc7a35ticpg236-1L"); + case "mimasv1": + return new MimasV1Board(); + case "mimasv2": + return new MimasV2Board(); + default: + return null; + } - return null; } } diff --git a/src/main/java/de/neemann/digital/hdl/vhdl2/boards/ClockIntegratorARTIX7.java b/src/main/java/de/neemann/digital/hdl/boards/ClockIntegratorARTIX7.java similarity index 99% rename from src/main/java/de/neemann/digital/hdl/vhdl2/boards/ClockIntegratorARTIX7.java rename to src/main/java/de/neemann/digital/hdl/boards/ClockIntegratorARTIX7.java index 98669cac5..2bd06c911 100644 --- a/src/main/java/de/neemann/digital/hdl/vhdl2/boards/ClockIntegratorARTIX7.java +++ b/src/main/java/de/neemann/digital/hdl/boards/ClockIntegratorARTIX7.java @@ -3,7 +3,7 @@ * 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.boards; +package de.neemann.digital.hdl.boards; import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.Key; diff --git a/src/main/java/de/neemann/digital/hdl/boards/ClockIntegratorSpartan6.java b/src/main/java/de/neemann/digital/hdl/boards/ClockIntegratorSpartan6.java new file mode 100644 index 000000000..efa161fed --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/boards/ClockIntegratorSpartan6.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018 Ivan Deras + * 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.boards; + +import de.neemann.digital.core.element.ElementAttributes; +import de.neemann.digital.core.element.Key; +import de.neemann.digital.hdl.model2.HDLCircuit; +import de.neemann.digital.hdl.model2.HDLException; +import de.neemann.digital.hdl.model2.HDLNodeBuildIn; +import de.neemann.digital.hdl.model2.HDLPort; +import de.neemann.digital.hdl.model2.clock.ClockInfo; +import de.neemann.digital.hdl.model2.clock.ClockIntegratorGeneric; +import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator; +import java.util.ArrayList; + +/** + * + * @author ideras + */ +public class ClockIntegratorSpartan6 implements HDLClockIntegrator { + private static final int MIN_FREQ = 5; // Minimum frequency in MHz + private static final int MAX_FREQ = 333; // Maximum frequency in MHz + private final BoardClockInfo[] boardClkInfo; + + /** + * Initialize a new instance + * + * @param boardClkInfo the board clock pins information + */ + public ClockIntegratorSpartan6(BoardClockInfo[] boardClkInfo) { + this.boardClkInfo = boardClkInfo; + } + + @Override + public void integrateClocks(HDLCircuit circuit, ArrayList clocks) throws HDLException { + if (clocks.size() > 1) + throw new HDLException("up to now only a single clock is supported on Spartan-6"); + + BoardClockInfo boardClockInfo = null; + ClockInfo clkInfo = clocks.get(0); + + for (BoardClockInfo bci : boardClkInfo) { + if (bci.getPinNumber().equals(clkInfo.getClockPort().getPinNumber())) { + boardClockInfo = bci; + break; + } + } + if (boardClockInfo == null) { + throw new HDLException("Cannot find clock pin '" + clkInfo.getClockPort().getPinNumber() + "'"); + } + + int freq = clkInfo.getFrequency(); + Params p = getParameters(freq, boardClockInfo.getClkPeriod()); + + if (p == null) { + new ClockIntegratorGeneric(boardClockInfo.getClkPeriod()).integrateClocks(circuit, clocks); + } else { + insertDCM(circuit, p, clkInfo.getClockPort(), boardClockInfo.getClkPeriod()); + } + } + + private void insertDCM(HDLCircuit circuit, Params p, HDLPort clock, double clkInPeriod) throws HDLException { + ElementAttributes attr = new ElementAttributes() + .set(new Key<>("CLKFX_DIVIDE", 0), p.d) + .set(new Key<>("CLKFX_MULTIPLY", 0), p.m) + .set(new Key<>("CLKIN_PERIOD", 0.0), clkInPeriod); + + circuit.integrateClockNode(clock, new HDLNodeBuildIn("DCM_SP", attr, name -> 1)); + } + + Params getParameters(int targetFreq, double clkInPeriod) { + double fInMHz = 1000 / clkInPeriod; + double targetFreqInMHz = ((double) targetFreq) / 1000 / 1000; + Params p = null; + + if (!(targetFreqInMHz >= MIN_FREQ && targetFreqInMHz <= MAX_FREQ)) { + return null; + } + + for (int m=2; m<32; m++) { + for (int d=1; d<=32; d++) { + double fGen = fInMHz * (double) m / (double) d; + double error = Math.abs(fGen - targetFreqInMHz); + + if (p == null || p.error > error) { + p = new Params(m, d, fGen, error); + } + } + } + + if (p.error < 0.01) { + return p; + } else { + return null; + } + } + + static final class Params { + private final int m; + private final int d; + private final double error; + private final double f; + + private Params(int m, int d, double f, double error) { + this.m = m; + this.d = d; + this.f = f; + this.error = error; + } + + @Override + public String toString() { + return "Params{" + + "m=" + m + + ", d=" + d + + ", error=" + error + + ", f=" + f + + '}'; + } + + } +} diff --git a/src/main/java/de/neemann/digital/hdl/boards/ISE.java b/src/main/java/de/neemann/digital/hdl/boards/ISE.java new file mode 100644 index 000000000..98332bafd --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/boards/ISE.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2018 Ivan Deras. Adapted from the Vivado exporter. + * 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.boards; + +import de.neemann.digital.analyse.SplitPinString; +import de.neemann.digital.hdl.model2.HDLModel; +import de.neemann.digital.hdl.model2.HDLPort; +import de.neemann.digital.hdl.printer.CodePrinter; + +import java.io.*; + +/** + * Creates the needed ISE files. + * Up to now only the constraints files containing the pin assignments and project file is created + */ +public abstract class ISE implements BoardInterface { + private static final String ISE_PROJECT_TPLT = + "\n" + + "\n" + + "
\n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n"; + + @Override + public void writeFiles(File path, HDLModel model) throws IOException { + String projectName = path.getName(); + if (projectName.endsWith(".v")) + projectName = projectName.substring(0, projectName.length() - 2); + else if (projectName.endsWith(".vhdl")) + projectName = projectName.substring(0, projectName.length() - 5); + File constraints = new File(path.getParentFile(), projectName.replace('.', '_') + "_constraints.ucf"); + try (CodePrinter out = new CodePrinter(new FileOutputStream(constraints))) { + writeConstraints(out, model); + } + createISEProject(path.getParentFile(), projectName, path, constraints); + } + + private void writeConstraints(CodePrinter out, HDLModel model) throws IOException { + for (HDLPort p : model.getMain().getPorts()) { + if (p.getBits() == 1) { + writePin(out, p.getName(), p.getPinNumber()); + } else { + SplitPinString pins = SplitPinString.create(p.getPinNumber()); + for (int i = 0; i < p.getBits(); i++) { + writePin(out, p.getName() + "[" + i + "]", pins.getPin(i)); + out.println(); + } + } + + out.println(); + } + } + + /** + * Write the pin information to a Xilinx UCF (User Constraints File) + * + * @param out the code printer + * @param name the signal name + * @param pinNumber the pin name + * @throws IOException IOException + */ + abstract void writePin(CodePrinter out, String name, String pinNumber) throws IOException; + + /** + * Returns the FPGA board information (Family, Code and Package in that order) + * + * @return The board information + */ + abstract BoardInformation getBoardInfo(); + + private void createISEProject(File path, String projectName, File srcFile, File constraints) throws IOException { + String projectDir = projectName + "_ise"; + File projectPath = new File(path, projectDir); + // don't overwrite existing projects! + if (!projectPath.exists()) { + if (projectPath.mkdirs()) { + File projectFile = new File(projectPath, projectName + ".xise"); + try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(projectFile), "utf-8"))) { + writeISEProject(w, projectFile, srcFile, constraints); + } + } + } + } + + private void writeISEProject(BufferedWriter w, File project, File srcFile, File constraints) throws IOException { + BoardInformation bi = getBoardInfo(); + + w.write(String.format(ISE_PROJECT_TPLT, "../" + srcFile.getName(), + "../" + constraints.getName(), bi.getFamily(), bi.getCode(), bi.getPkg())); + } + + static class BoardInformation { + private final String family; + private final String code; + private final String pkg; + + BoardInformation(String family, String code, String pkg) { + this.family = family; + this.code = code; + this.pkg = pkg; + } + + public String getFamily() { + return family; + } + + public String getCode() { + return code; + } + + public String getPkg() { + return pkg; + } + } +} diff --git a/src/main/java/de/neemann/digital/hdl/boards/MimasV1Board.java b/src/main/java/de/neemann/digital/hdl/boards/MimasV1Board.java new file mode 100644 index 000000000..c974fb2e5 --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/boards/MimasV1Board.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 Ivan Deras + * 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.boards; + +import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator; +import de.neemann.digital.hdl.printer.CodePrinter; +import de.neemann.digital.lang.Lang; +import java.io.IOException; + +/** + * Mimas Board Version 1 + */ +public class MimasV1Board extends ISE { + private static final BoardInformation BOARD_INFO = new BoardInformation("Spartan6", "xc6slx9", "tqg144"); + private static final BoardClockInfo[] BOARD_CLOCKPINS = { + new BoardClockInfo("P126", 10) + }; + /** + * Initialize a new instance + */ + public MimasV1Board() { + } + + @Override + void writePin(CodePrinter out, String name, String pinNumber) throws IOException { + if (pinNumber == null || pinNumber.length() == 0) + throw new IOException(Lang.get("err_vhdlPin_N_hasNoNumber", name)); + + String line = String.format("NET \"%s\" LOC = \"%s\" | IOSTANDARD = LVCMOS33", name, pinNumber); + + switch (pinNumber) { + case "P126": + line += String.format(";\nTIMESPEC TS_CLK = PERIOD \"%s\" 100 MHz HIGH 50%%;\n", name); + break; + /* Switch pins */ + case "P124": + case "P123": + case "P121": + case "P120": + line += " | DRIVE = 8 | SLEW = FAST | PULLUP"; + break; + default: + line += " | DRIVE = 8 | SLEW = FAST"; + } + + line += ";"; + + out.print(line); + } + + @Override + BoardInformation getBoardInfo() { + return BOARD_INFO; + } + + @Override + public HDLClockIntegrator getClockIntegrator() { + return new ClockIntegratorSpartan6(BOARD_CLOCKPINS); + } +} diff --git a/src/main/java/de/neemann/digital/hdl/boards/MimasV2Board.java b/src/main/java/de/neemann/digital/hdl/boards/MimasV2Board.java new file mode 100644 index 000000000..84e135c0b --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/boards/MimasV2Board.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018 Ivan Deras + * 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.boards; + +import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator; +import de.neemann.digital.hdl.printer.CodePrinter; +import de.neemann.digital.lang.Lang; +import java.io.IOException; + +/** + * Mimas Board Version 2 + */ +public class MimasV2Board extends ISE { + private static final BoardInformation BOARD_INFO = new BoardInformation("Spartan6", "xc6slx9", "csg324"); + private static final BoardClockInfo[] BOARD_CLOCKPINS = { + new BoardClockInfo("V10", 10), + new BoardClockInfo("D9", 83.33) + }; + /** + * Initialize a new instance + */ + public MimasV2Board() { + } + + @Override + void writePin(CodePrinter out, String name, String pinNumber) throws IOException { + if (pinNumber == null || pinNumber.length() == 0) + throw new IOException(Lang.get("err_vhdlPin_N_hasNoNumber", name)); + + String line = String.format("NET \"%s\" LOC = \"%s\" | IOSTANDARD = LVCMOS33", name, pinNumber); + + switch (pinNumber) { + case "V10": + line += " | PERIOD = 100MHz"; + break; + case "D9": + line += " | PERIOD = 12MHz"; + break; + case "C17": + case "C18": + case "D17": + case "D18": + case "E18": + case "E16": + case "F18": + case "F17": + case "M18": + case "L18": + case "M16": + case "L17": + case "K17": + case "K18": + line += " | DRIVE = 8 | SLEW = FAST | PULLUP"; + break; + default: + line += " | DRIVE = 8 | SLEW = FAST"; + } + + line += ";"; + + out.print(line); + } + + @Override + BoardInformation getBoardInfo() { + return BOARD_INFO; + } + + @Override + public HDLClockIntegrator getClockIntegrator() { + return new ClockIntegratorSpartan6(BOARD_CLOCKPINS); + } +} diff --git a/src/main/java/de/neemann/digital/hdl/vhdl2/boards/Vivado.java b/src/main/java/de/neemann/digital/hdl/boards/Vivado.java similarity index 99% rename from src/main/java/de/neemann/digital/hdl/vhdl2/boards/Vivado.java rename to src/main/java/de/neemann/digital/hdl/boards/Vivado.java index 953d57c45..e193ed0f9 100644 --- a/src/main/java/de/neemann/digital/hdl/vhdl2/boards/Vivado.java +++ b/src/main/java/de/neemann/digital/hdl/boards/Vivado.java @@ -3,7 +3,7 @@ * 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.boards; +package de.neemann.digital.hdl.boards; import de.neemann.digital.analyse.SplitPinString; import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator; diff --git a/src/main/java/de/neemann/digital/hdl/vhdl2/boards/package-info.java b/src/main/java/de/neemann/digital/hdl/boards/package-info.java similarity index 75% rename from src/main/java/de/neemann/digital/hdl/vhdl2/boards/package-info.java rename to src/main/java/de/neemann/digital/hdl/boards/package-info.java index 79912d2aa..469d3b563 100644 --- a/src/main/java/de/neemann/digital/hdl/vhdl2/boards/package-info.java +++ b/src/main/java/de/neemann/digital/hdl/boards/package-info.java @@ -7,6 +7,6 @@ /** * Support for different boards. * The classes in this package are able to create the files needed - * to run a vhdl file on a board. + * to run a vhdl or verilog file on a board. */ -package de.neemann.digital.hdl.vhdl2.boards; +package de.neemann.digital.hdl.boards; diff --git a/src/main/java/de/neemann/digital/hdl/hgs/Parser.java b/src/main/java/de/neemann/digital/hdl/hgs/Parser.java index da7e7fc9c..7332a5732 100644 --- a/src/main/java/de/neemann/digital/hdl/hgs/Parser.java +++ b/src/main/java/de/neemann/digital/hdl/hgs/Parser.java @@ -33,7 +33,7 @@ public static Statement createFromJar(String path) throws IOException, ParserExc if (in == null) throw new FileNotFoundException("file not found: " + path); try (Reader r = new InputStreamReader(in, "utf-8")) { - Parser p = new Parser(r); + Parser p = new Parser(r, path); return p.parse(); } } @@ -63,16 +63,17 @@ public static Statement createFromJarStatic(String path) { * @param code the code to parse */ public Parser(String code) { - this(new StringReader(code)); + this(new StringReader(code), ""); } /** * Creates a new instance * * @param reader the reader to parse + * @param srcFile the source file name if any */ - public Parser(Reader reader) { - tok = new Tokenizer(reader); + public Parser(Reader reader, String srcFile) { + tok = new Tokenizer(reader, srcFile); staticContext = new Context(); } @@ -327,7 +328,7 @@ private ParserException newUnexpectedToken(Tokenizer.Token token) { } private ParserException newParserException(String s) { - return new ParserException(s + " (" + tok.getLine() + ")"); + return new ParserException(s + " (" + tok.getSrcFile() + ":" + tok.getLine() + ")"); } /** diff --git a/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java b/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java index 83ec71789..568dfd8f7 100644 --- a/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java +++ b/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java @@ -40,6 +40,7 @@ enum Token { private StringBuilder builder; private boolean isUnreadChar = false; private int unreadChar; + private String srcFile; private int line = 1; /** @@ -47,8 +48,9 @@ enum Token { * * @param in the reader */ - Tokenizer(Reader in) { + Tokenizer(Reader in, String srcFile) { this.in = in; + this.srcFile = srcFile; token = Token.UNKNOWN; isToken = false; builder = new StringBuilder(); @@ -73,6 +75,15 @@ public void consume() { isToken = false; } + /** + * Returns the source file name if any + * + * @return the source file name + */ + public String getSrcFile() { + return srcFile; + } + /** * Peeks the next token. * The token is kept in the stream, so next() or peek() will return this token again! diff --git a/src/main/java/de/neemann/digital/hdl/verilog2/VerilogCreator.java b/src/main/java/de/neemann/digital/hdl/verilog2/VerilogCreator.java new file mode 100644 index 000000000..08f5fe847 --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/verilog2/VerilogCreator.java @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2018 Ivan Deras. + * 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.verilog2; + +import de.neemann.digital.hdl.vhdl2.*; +import de.neemann.digital.core.Bits; +import de.neemann.digital.core.wiring.Splitter; +import de.neemann.digital.hdl.hgs.HGSEvalException; +import de.neemann.digital.hdl.model2.*; +import de.neemann.digital.hdl.model2.expression.*; +import de.neemann.digital.hdl.printer.CodePrinter; +import de.neemann.digital.hdl.verilog2.lib.VerilogElement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.HashSet; + +/** + * Create the verilog output + */ +public class VerilogCreator { + private static final Logger LOGGER = LoggerFactory.getLogger(VerilogCreator.class); + private final CodePrinter out; + private final VerilogLibrary library; + private final HashSet customPrinted; + + /** + * Creates a new instance + * + * @param out the output stream + */ + VerilogCreator(CodePrinter out) { + this.out = out; + library = new VerilogLibrary(); + customPrinted = new HashSet<>(); + } + + /** + * Returns the verilog bit range + * + * @param bits the number of bits + * @return the bit range + */ + public static String getRange(int bits) { + if (bits == 1) + return ""; + else + return "[" + (bits - 1) + ":0]"; + } + + /** + * Returns the verilog type for a signal + * + * @param dir the signal type (input or output) + * @param bits the number of bits + * @return the verilog signal type + */ + public static String getType(HDLPort.Direction dir, int bits) { + String result = (dir == HDLPort.Direction.IN)? "input" : "output"; + + if (bits > 1) { + result += " [" + (bits - 1) + ":0]"; + } + + return result; + } + + /** + * Creates a verilog value + * + * @param con the constant + * @return the value as vhdl code + */ + public static String value(ExprConstant con) { + return value(con.getValue(), con.getBits()); + } + + /** + * Creates a verilog value + * + * @param val the value + * @param bits the bit number + * @return the value as vhdl code + */ + public static String value(long val, int bits) { + String s = Long.toBinaryString(val & Bits.mask(bits)); + + return (bits + "'b" + s); + } + + private void printNodeBuiltIn(HDLNodeBuildIn node) throws HDLException, IOException, HGSEvalException { + VerilogElement elem = library.getVerilogElement(node); + String hdlEntityName = elem.print(out, node); + node.setHdlEntityName(hdlEntityName); + } + + private void printNodeCustom(HDLNodeCustom node) throws HDLException, IOException, HGSEvalException { + if (!customPrinted.contains(node.getElementName())) { + printHDLCircuit(node.getCircuit(), node.getHdlEntityName()); + customPrinted.add(node.getElementName()); + } + } + + /** + * Prints the given circuit to the output. + * Also all needed entities are printed. + * + * @param circuit the circuit to print + * @param moduleName the module name + * @throws IOException IOException + * @throws HDLException HDLException + * @throws HGSEvalException HGSEvalException + */ + public void printHDLCircuit(HDLCircuit circuit, String moduleName) throws IOException, HDLException, HGSEvalException { + // at first print all used entities to maintain the correct order + for (HDLNode node : circuit) + if (node instanceof HDLNodeCustom) + printNodeCustom((HDLNodeCustom) node); + else if (node instanceof HDLNodeBuildIn) + printNodeBuiltIn((HDLNodeBuildIn) node); + + LOGGER.info("export " + moduleName); + + out.println(); + if (circuit.hasDescription()) + out.printComment("// ", circuit.getDescription()); + + out.print("module ").print(moduleName).println(" (").inc(); + writePorts(out, circuit); + out.dec(); + out.println().println(");"); + + out.inc(); + for (HDLNet net : circuit.getNets()) { + if (net.needsVariable()) { + String range = ""; + + if (net.getBits() > 1) { + range += " [" + (net.getBits() - 1) + ":0]"; + } + out.print("wire").print(range).print(" ").print(net.getName()).println(";"); + } + } + + int num = 0; + for (HDLNode node : circuit) + if (node instanceof HDLNodeAssignment) + printExpression((HDLNodeAssignment) node); + else if (node instanceof HDLNodeBuildIn) + printModuleInstantiation((HDLNodeBuildIn) node, num++); + else if (node instanceof HDLNodeSplitterOneToMany) + printOneToMany((HDLNodeSplitterOneToMany) node); + else if (node instanceof HDLNodeSplitterManyToOne) + printManyToOne((HDLNodeSplitterManyToOne) node); + else + throw new HDLException("Not yet implemented: " + node.getClass().getSimpleName()); + + for (HDLPort p : circuit.getOutputs()) { + final HDLNet net = p.getNet(); + if (net.needsVariable() || net.isInput()) + out.print("assign ").print(p.getName()).print(" = ").print(p.getNet().getName()).println(";"); + } + + out.dec().println("endmodule"); + } + + /** + * Writes the ports of the given circuit + * + * @param out the stream to write to + * @param circuit the circuit + * @throws IOException IOException + */ + public static void writePorts(CodePrinter out, HDLCircuit circuit) throws IOException { + Separator sep = new Separator(out, ",\n"); + + for (HDLPort i : circuit.getInputs()) { + sep.check(); + out.print(getType(HDLPort.Direction.IN, i.getBits())).print(" ").print(i.getName()); + if (i.hasDescription()) sep.setLineFinalizer(ou -> ou.printComment(" // ", i.getDescription())); + } + for (HDLPort o : circuit.getOutputs()) { + sep.check(); + out.print(getType(HDLPort.Direction.OUT, o.getBits())).print(" ").print(o.getName()); + if (o.hasDescription()) sep.setLineFinalizer(ou -> ou.printComment(" // ", o.getDescription())); + } + sep.close(); + } + + private void printManyToOne(HDLNodeSplitterManyToOne node) throws IOException, HDLException { + String target = node.getTargetSignal(); + + for (HDLNodeSplitterManyToOne.SplitterAssignment in : node) { + out.print("assign ").print(target).print("["); + if (in.getLsb() == in.getMsb()) + out.print(in.getLsb()); + else + out.print(in.getMsb()).print(":").print(in.getLsb()); + out.print("] = "); + printExpression(in.getExpression()); + out.println(";"); + } + } + + /** + * After ReplaceOneToMany optimization there are no such nodes in the model! + */ + private void printOneToMany(HDLNodeSplitterOneToMany node) throws IOException { + String source = node.getSourceSignal(); + Splitter.Ports is = node.getOutputSplit(); + int i = 0; + for (HDLPort outPort : node.getOutputs()) { + Splitter.Port sp = is.getPort(i++); + if (outPort.getNet() != null) { + out.print(outPort.getNet().getName()).print(" <= ").print(source).print("("); + if (outPort.getBits() == 1) + out.print(sp.getPos()); + else + out.print(sp.getPos() + sp.getBits() - 1).print(" downto ").print(sp.getPos()); + out.println(");"); + } + } + } + + private void printModuleInstantiation(HDLNodeBuildIn node, int num) throws IOException, HDLException { + String entityName = node.getHdlEntityName(); + + final String label = node.getElementAttributes().getCleanLabel(); + if (label != null && label.length() > 0) + out.print("// ").println(label.replace('\n', ' ')); + + out.print(entityName).print(" "); + + if (!(node instanceof HDLNodeCustom)) { + library.getVerilogElement(node).writeGenericMap(out, node); + } + + // entityName can have an space at the end if the identifier is escaped + String instanceName = entityName.trim() + "_i" + num; + + out.print(instanceName + " ") + .println("("); + + out.inc(); + Separator sep = new Separator(out, ",\n"); + for (HDLNodeBuildIn.InputAssignment i : node) { + sep.check(); + out.print(".").print(i.getTargetName()).print("( "); + printExpression(i.getExpression()); + out.print(" )"); + } + + for (HDLPort o : node.getOutputs()) + if (o.getNet() != null) { + sep.check(); + out.print(".").print(o.getName()).print("( ").print(o.getNet().getName()).print(" )"); + } + out.dec(); + out.println().println(");"); + } + + private void printExpression(HDLNodeAssignment node) throws IOException, HDLException { + out.print("assign ").print(node.getTargetNet().getName()).print(" = "); + printExpression(node.getExpression()); + out.println(";"); + } + + private void printExpression(Expression expression) throws IOException, HDLException { + if (expression instanceof ExprVar) + out.print(((ExprVar) expression).getNet().getName()); + else if (expression instanceof ExprVarRange) { + final ExprVarRange evr = (ExprVarRange) expression; + out.print(evr.getNet().getName()).print("["); + if (evr.getMsb() == evr.getLsb()) + out.print(evr.getMsb()); + else + out.print(evr.getMsb()).print(":").print(evr.getLsb()); + out.print("]"); + } else if (expression instanceof ExprConstant) { + final ExprConstant constant = (ExprConstant) expression; + out.print(value(constant)); + } else if (expression instanceof ExprNot) { + out.print("~ "); + printExpression(((ExprNot) expression).getExpression()); + } else if (expression instanceof ExprOperate) { + out.print("("); + boolean first = true; + final ExprOperate operate = (ExprOperate) expression; + + String op; + switch (operate.getOperation()) { + case OR: + op = " | "; + break; + case AND: + op = " & "; + break; + case XOR: + op = " ^ "; + break; + default: + throw new HDLException("unknown operation " + operate.getOperation()); + } + for (Expression exp : operate.getOperands()) { + if (first) + first = false; + else + out.print(op); + printExpression(exp); + } + out.print(")"); + } else + throw new HDLException("expression type " + expression.getClass().getSimpleName() + " unknown"); + } +} diff --git a/src/main/java/de/neemann/digital/hdl/verilog2/VerilogGenerator.java b/src/main/java/de/neemann/digital/hdl/verilog2/VerilogGenerator.java new file mode 100644 index 000000000..fb675cc99 --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/verilog2/VerilogGenerator.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2018 Ivan Deras. + * 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.verilog2; + +import de.neemann.digital.core.NodeException; +import de.neemann.digital.core.element.Keys; +import de.neemann.digital.draw.elements.Circuit; +import de.neemann.digital.draw.elements.PinException; +import de.neemann.digital.draw.library.ElementLibrary; +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.model2.HDLNet; +import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator; +import de.neemann.digital.hdl.printer.CodePrinter; +import de.neemann.digital.hdl.boards.BoardInterface; +import de.neemann.digital.hdl.boards.BoardProvider; +import de.neemann.digital.lang.Lang; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +/** + * Used to create the verilog output + */ +public class VerilogGenerator implements Closeable { + + private final ElementLibrary library; + private final CodePrinter out; + private ArrayList testBenches; + private boolean useClockIntegration = true; + + /** + * Creates a new exporter + * + * @param library the library + * @param out the output stream + */ + public VerilogGenerator(ElementLibrary library, CodePrinter out) { + this.library = library; + this.out = out; + } + + /** + * Exports the given circuit + * + * @param circuit the circuit to export + * @return this for chained calls + * @throws IOException IOException + */ + public VerilogGenerator export(Circuit circuit) throws IOException { + try { + + if (!circuit.getAttributes().get(Keys.ROMMANAGER).isEmpty()) + throw new HDLException(Lang.get("err_centralDefinedRomsAreNotSupported")); + + BoardInterface board = BoardProvider.getInstance().getBoard(circuit); + + HDLClockIntegrator clockIntegrator = null; + if (board != null && useClockIntegration) + clockIntegrator = board.getClockIntegrator(); + + HDLModel model = new HDLModel(library).create(circuit, clockIntegrator); + for (HDLCircuit hdlCircuit : model) + hdlCircuit.applyDefaultOptimizations(); + + HDLModel.Renaming vrename = new VerilogRenaming(); + model.renameLabels(vrename); + + for (HDLCircuit hdlCircuit : model) + checkForUniqueNetNames(hdlCircuit); + + out.println("/*"); + out.println(" * Generated by Digital. Don't modify this file!"); + out.println(" * Any changes will be lost if this file is regenerated."); + out.println(" */"); + + String fileName = out.getFile() != null? out.getFile().getName() : circuit.getOrigin().getName(); + String[] tokens = fileName.split("(?=(\\.[^\\.]+)$)"); // The power of regex :) + + String topModuleName = vrename.checkName(tokens[0]); + + new VerilogCreator(out).printHDLCircuit(model.getMain(), topModuleName); + + File outFile = out.getFile(); + if (outFile != null) { + testBenches = new VerilogTestBenchCreator(circuit, model, topModuleName) + .write(outFile) + .getTestFileWritten(); + + if (board != null) + board.writeFiles(outFile, model); + } + + return this; + } catch (PinException | NodeException | HDLException | HGSEvalException e) { + throw new IOException(Lang.get("err_vhdlExporting"), e); + } + } + + private void checkForUniqueNetNames(HDLCircuit hdlCircuit) throws HDLException { + ArrayList nets = hdlCircuit.getNets(); + // try to resolve duplicate names + for (HDLNet n : nets) + if (n.isUserNamed()) + for (HDLNet nn : nets) + if (n.getName().equalsIgnoreCase(nn.getName()) && n != nn) { + String newName = "s_" + n.getName(); + int i = 1; + while (exits(newName, nets)) + newName = "s_" + n.getName() + (i++); + n.setName(newName); + } + + // throw an exception if there is still a duplicate name + for (int i = 0; i < nets.size(); i++) { + final HDLNet n1 = nets.get(i); + for (int j = i + 1; j < nets.size(); j++) { + final HDLNet n2 = nets.get(j); + if (n1.getName().equalsIgnoreCase(n2.getName())) + throw new HDLException( + Lang.get("err_namesAreNotUnique_N", n1.getName() + "==" + n2.getName()), + hdlCircuit.getOrigin()); + } + } + } + + private boolean exits(String newName, ArrayList nets) { + for (HDLNet n : nets) + if (n.getName().equalsIgnoreCase(newName)) + return true; + return false; + } + + /** + * @return the test bench files, maybe null + */ + public ArrayList getTestBenches() { + return testBenches; + } + + @Override + public String toString() { + return out.toString(); + } + + @Override + public void close() throws IOException { + out.close(); + } + + /** + * Disables the clock integration. + * Used only for the tests. + * + * @return this for chained calls + */ + public VerilogGenerator disableClockIntegration() { + useClockIntegration = false; + return this; + } +} diff --git a/src/main/java/de/neemann/digital/hdl/verilog2/VerilogLibrary.java b/src/main/java/de/neemann/digital/hdl/verilog2/VerilogLibrary.java new file mode 100644 index 000000000..d5d2a4b33 --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/verilog2/VerilogLibrary.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 Ivan Deras + * 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.verilog2; + +import de.neemann.digital.core.element.ElementTypeDescription; +import de.neemann.digital.hdl.model2.HDLException; +import de.neemann.digital.hdl.model2.HDLNode; +import de.neemann.digital.hdl.verilog2.lib.VerilogElement; +import de.neemann.digital.hdl.verilog2.lib.VerilogTemplate; +import de.neemann.digital.lang.Lang; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author ideras + */ +public class VerilogLibrary { + private static final Logger LOGGER = LoggerFactory.getLogger(VerilogLibrary.class); + + private final HashMap map; + private final ArrayList nodeList = new ArrayList<>(); + + /** + * Creates a new instance + * + */ + public VerilogLibrary() { + map = new HashMap<>(); + } + + private void put(ElementTypeDescription description, VerilogElement velem) { + map.put(description.getName(), velem); + } + + /** + * Returns the associated verilog element for a given node + * + * @param node the HDL node + * @return the associated verilog element. + * @throws HDLException HDLException + */ + public VerilogElement getVerilogElement(HDLNode node) throws HDLException { + String elementName = node.getElementName(); + VerilogElement e = map.get(elementName); + if (e == null) { + try { + e = new VerilogTemplate(elementName); + map.put(elementName, e); + } catch (IOException ex) { + ex.printStackTrace(); + LOGGER.info("could not load '" + VerilogTemplate.neededFileName(elementName) + "'"); + } + } + + if (e == null) + throw new HDLException(Lang.get("err_verilogNoElement_N", elementName)); + + return e; + } + + /** + * Returns the verilog name of the given node + * + * @param node the node + * @return the name + */ + public String getName(HDLNode node) { + if (!nodeList.contains(node)) { + nodeList.add(node); + node.setHdlEntityName(node.getElementName()); + + } + return node.getHdlEntityName(); + } +} diff --git a/src/main/java/de/neemann/digital/hdl/verilog2/VerilogRenaming.java b/src/main/java/de/neemann/digital/hdl/verilog2/VerilogRenaming.java new file mode 100644 index 000000000..3c7a6377d --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/verilog2/VerilogRenaming.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018 Helmut Neemann, Ivan Deras. + * 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.verilog2; + +import de.neemann.digital.hdl.model2.HDLModel; +import java.util.Arrays; + +import java.util.HashSet; + +/** + * Renames the labels to valid Verilog names. + */ +public class VerilogRenaming implements HDLModel.Renaming { + + private static final HashSet KEYWORDS = new HashSet<>(Arrays.asList("always", + "and", "assign", "automatic", "begin", "buf", "bufif0", "bufif1", "case", "casex", + "casez", "cell", "cmos", "config", "deassign", "default", "defparam", "design", + "disable", "edge", "else", "end", "endcase", "endconfig", "endfunction", "endgenerate", + "endmodule", "endprimitive", "endspecify", "endtable", "endtask", "event", "for", + "force", "forever", "fork", "function", "generate", "genvar", "highz0", "highz1", + "if", "ifnone", "incdir", "include", "initial", "inout", "input", "instance", "integer", + "join", "large", "liblist", "library", "localparam", "macromodule", "medium", "module", + "nand", "negedge", "nmos", "nor", "noshowcancelledno", "not", "notif0", "notif1", + "or", "output", "parameter", "pmos", "posedge", "primitive", "pull0", "pull1", + "pulldown", "pullup", "pulsestyle_oneventglitch", "pulsestyle_ondetectglitch", + "remos", "real", "realtime", "reg", "release", "repeat", "rnmos", "rpmos", "rtran", + "rtranif0", "rtranif1", "scalared", "showcancelled", "signed", "small", "specify", + "specparam", "strong0", "strong1", "supply0", "supply1", "table", "task", "time", + "tran", "tranif0", "tranif1", "tri", "tri0", "tri1", "triand", "trior", "trireg", + "unsigned", "use", "vectored", "wait", "wand", "weak0", "weak1", "while", "wire", + "wor", "xnor", "xor")); + + @Override + public String checkName(String name) { + if (isKeyword(name) || !isFirstCharValid(name)) + // Escaped identifier, the space is part of the identifier. + return "\\" + name + " "; + else { + return cleanName(name); + } + } + + private boolean isFirstCharValid(String name) { + char c = name.charAt(0); + + return ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c == '_')); + } + + private boolean isKeyword(String str) { + return KEYWORDS.contains(str.toLowerCase()); + } + + private String cleanName(String name) { + StringBuilder sb = new StringBuilder(); + boolean needScaping = false; + + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || (c == '_') || (c == '$')) + sb.append(c); + else { + switch (c) { + case '/': + case '!': + case '~': + case '\u00AC': + sb.append("not"); + break; + default: + sb.append(c); + needScaping = true; + } + } + } + + if (needScaping) { + sb.insert(0, "\\"); // Escaped identifier + sb.append(" "); // The space is part of an escaped identifier + } + + return sb.toString(); + } + + +} diff --git a/src/main/java/de/neemann/digital/hdl/verilog2/VerilogTestBenchCreator.java b/src/main/java/de/neemann/digital/hdl/verilog2/VerilogTestBenchCreator.java new file mode 100644 index 000000000..f24719189 --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/verilog2/VerilogTestBenchCreator.java @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2018 Ivan Deras + * 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.verilog2; + +import de.neemann.digital.hdl.vhdl2.*; +import de.neemann.digital.core.element.ElementAttributes; +import de.neemann.digital.data.Value; +import de.neemann.digital.draw.elements.Circuit; +import de.neemann.digital.draw.elements.VisualElement; +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.model2.HDLPort; +import de.neemann.digital.hdl.printer.CodePrinter; +import de.neemann.digital.hdl.printer.CodePrinterStr; +import de.neemann.digital.lang.Lang; +import de.neemann.digital.testing.TestCaseDescription; +import de.neemann.digital.testing.TestCaseElement; +import de.neemann.digital.testing.TestingDataException; +import de.neemann.digital.testing.parser.Context; +import de.neemann.digital.testing.parser.LineListener; +import de.neemann.digital.testing.parser.ParserException; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +import static de.neemann.digital.testing.TestCaseElement.TESTDATA; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Creates a test bench for a model. + * The needed test date is taken from the test cases in the circuit + */ +public class VerilogTestBenchCreator { + private final ArrayList testCases; + private final HDLCircuit main; + private final String topModuleName; + private final HDLModel.Renaming renaming; + private final ArrayList testFileWritten; + + /** + * Creates a new instance + * + * @param circuit the circuit + * @param model the model + * @param topModuleName the name of the module under test + */ + public VerilogTestBenchCreator(Circuit circuit, HDLModel model, String topModuleName) { + this.main = model.getMain(); + this.topModuleName = topModuleName; + testCases = new ArrayList<>(); + for (VisualElement ve : circuit.getElements()) + if (ve.equalsDescription(TestCaseElement.TESTCASEDESCRIPTION)) + testCases.add(ve.getElementAttributes()); + testFileWritten = new ArrayList<>(); + renaming = model.getRenaming(); + } + + /** + * Writes the test benches + * + * @param file the original verilog file + * @return this for chained calls + * @throws IOException IOException + * @throws HDLException HDLException + */ + public VerilogTestBenchCreator write(File file) throws IOException, HDLException { + String filename = file.getName(); + int p = filename.indexOf('.'); + if (p > 0) + filename = filename.substring(0, p); + + for (ElementAttributes tc : testCases) { + String testName = tc.getCleanLabel(); + if (testName.length() > 0) + testName = filename + "_" + testName + "_tb"; + else + testName = filename + "_tb"; + + //testName = HDLPort.getHDLName(testName); + + File f = new File(file.getParentFile(), testName + ".v"); + testFileWritten.add(f); + try (CodePrinter out = new CodePrinter(f)) { + try { + writeTestBench(out, topModuleName, testName, tc); + } catch (RuntimeException e) { + throw new HDLException(Lang.get("err_vhdlErrorWritingTestBench"), e); + } catch (TestingDataException | ParserException ex) { + Logger.getLogger(VerilogTestBenchCreator.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + return this; + } + + /** + * @return returns the files which are written + */ + public ArrayList getTestFileWritten() { + return testFileWritten; + } + + private void writeTestBench(CodePrinter out, String moduleName, String testName, ElementAttributes tc) throws IOException, HDLException, TestingDataException, ParserException { + out.print("// A testbench for ").println(testName); + out.println("`timescale 1us/1ns").println(); + out.print("module ").print(testName).println(";"); + + // Write local port declaration + out.inc(); + + for (HDLPort p : main.getPorts()) { + out.print(" ").print(getSignalDeclarationCode(p)).println(";"); + } + + out.println(); + out.print(moduleName).print(" ").print(moduleName).print("0 (").println(); + out.inc(); + + Separator comma = new Separator(out, ",\n"); + + for (HDLPort p : main.getPorts()) { + comma.check(); + out.print(".").print(p.getName()).print("(").print(p.getName()).print(")"); + } + out.dec().println().print(");").println().println(); + + TestCaseDescription testdata = tc.get(TESTDATA); + + ArrayList dataOrder = new ArrayList<>(); + ArrayList inputsInOrder = new ArrayList<>(); + ArrayList outputsInOrder = new ArrayList<>(); + for (String name : testdata.getNames()) { + String saveName = renaming.checkName(name); + boolean found = false; + for (HDLPort p : main.getPorts()) { + if (p.getName().equals(saveName)) { + dataOrder.add(p); + + if (p.getDirection() == HDLPort.Direction.OUT) { + inputsInOrder.add(p); + } else { + outputsInOrder.add(p); + } + + found = true; + break; + } + } + if (!found) + throw new TestingDataException(Lang.get("err_testSignal_N_notFound", name)); + } + + int rowBits = 0; + for (HDLPort p : dataOrder) { + rowBits += p.getBits(); + } + + CodePrinterStr outTmp = new CodePrinterStr(); + + outTmp.inc(); + LineListener parent = new LineListenerVerilog(outTmp, dataOrder, rowBits); + testdata.getLines().emitLines(parent, new Context()); + int lineCount = ((LineListenerVerilog) parent).getLineCount(); + + String patternRange1 = rowBits == 1? "" : String.format("[%d:0] ", rowBits - 1); + String patternRange2 = lineCount == 1? "" : String.format("[0:%d]", lineCount - 1); + + out.inc(); + out.print("reg ").print(patternRange1).print("patterns").print(patternRange2).println(";"); + + String loopVar = "i"; + int lv = 0; + while (loopVarExists(loopVar, main.getPorts())) + loopVar = "i" + (lv++); + + out.print("integer ").print(loopVar).println(";"); + + out.println().println("initial begin"); + out.println(outTmp.toString()); + + out.inc(); + out.print(String.format("for (%1$s = 0; %1$s < %2$d; %1$s = %1$s + 1)\n", loopVar, lineCount)); + out.println("begin").inc(); + + int rangeStart = rowBits - 1; + for (HDLPort p : inputsInOrder) { + int rangeEnd = rangeStart - p.getBits() + 1; + String rangeStr = (rangeStart != rangeEnd)? ("[" + rangeStart + ":" + rangeEnd + "]") : ("[" + rangeStart + "]"); + + out.print(p.getName()).print(" = patterns[").print(loopVar).print("]").print(rangeStr).println(";"); + rangeStart -= p.getBits(); + } + out.println("#10;"); + + for (HDLPort p : outputsInOrder) { + String dontCareValue = (p.getBits()) + "'hx"; + int rangeEnd = rangeStart - p.getBits() + 1; + String rangeStr = (rangeStart != rangeEnd)? ("[" + rangeStart + ":" + rangeEnd + "]") : ("[" + rangeStart + "]"); + + out.print("if (patterns[").print(loopVar).print("]").print(rangeStr).print(" !== ").print(dontCareValue).println(")") + .println("begin"); + out.inc(); + out.print("if (").print(p.getName()).print(" !== patterns[").print(loopVar).print("]").print(rangeStr).println(")") + .println("begin"); + out.inc(); + out.print("$display(\"%d:") + .print(p.getName()).print(": (assertion error). Expected %h, found %h\", ") + .print(loopVar).print(", ").print("patterns[").print(loopVar).print("]").print(rangeStr).print(", ") + .print(p.getName()).print(");") + .println(); + out.println("$finish;"); + out.dec().println("end"); + out.dec().println("end"); + + rangeStart -= p.getBits(); + } + out.dec(); + out.println("end"); + + out.println().println("$display(\"All tests passed.\");"); + + out.dec().println("end"); + out.println("endmodule"); + } + + private boolean loopVarExists(String loopVar, ArrayList ports) { + for (HDLPort p : ports) + if (p.getName().equals(loopVar)) + return true; + return false; + } + + private String getSignalDeclarationCode(HDLPort p) throws HDLException { + String declCode; + + switch (p.getDirection()) { + case IN: declCode = "wire "; break; + case OUT: declCode = "reg "; break; + default: + declCode = "/* Invalid port */"; + } + + if (p.getBits() > 1) + declCode += "[" + Integer.toString(p.getBits() - 1) + ":0] "; + + declCode += p.getName(); + + return declCode; + } + + private static final class LineListenerVerilog implements LineListener { + private final CodePrinter out; + private final ArrayList dataOrder; + private final int rowBits; + private int rowIndex; + + private LineListenerVerilog(CodePrinter out, ArrayList dataOrder, int rowBits) { + this.out = out; + this.dataOrder = dataOrder; + this.rowBits = rowBits; + rowIndex = 0; + } + + @Override + public void add(Value[] values) { + try { + boolean containsClock = false; + for (Value v : values) + if (v.getType() == Value.Type.CLOCK) + containsClock = true; + if (containsClock) { + writeValues(values, true, 0); + writeValues(values, true, 1); + } + writeValues(values, false, 0); + } catch (IOException | HDLException e) { + throw new RuntimeException(e); + } + } + + /** + * Returns the number of lines emitted + * + * @return the number of lines + */ + public int getLineCount() { + return rowIndex; + } + + private void writeValues(Value[] values, boolean isClock, int clock) throws IOException, HDLException { + out.print("patterns[").print(rowIndex).print("] = ").print(rowBits).print("'b"); + + for (int i = 0; i < values.length; i++) { + HDLPort p = dataOrder.get(i); + + if (p.getDirection() == HDLPort.Direction.OUT) { + if (values[i].getType() == Value.Type.CLOCK) { + out.print(clock); + } else { + out.print(toBinaryString(values[i], p.getBits())); + } + out.print("_"); + } + } + + Separator sep = new Separator(out, "_"); + + for (int i = 0; i < values.length; i++) { + HDLPort p = dataOrder.get(i); + + if (p.getDirection() == HDLPort.Direction.IN) { + sep.check(); + + if (isClock) { + out.print(toBinaryString(0, Value.Type.DONTCARE, p.getBits())); + } else { + out.print(toBinaryString(values[i], p.getBits())); + } + } + } + + out.println(";"); + + rowIndex++; + } + + private String toBinaryString(Value v, int bits) { + return toBinaryString(v.getValue(), v.getType(), bits); + } + + private String toBinaryString(long val, Value.Type type, int bits) { + String binStr = ""; + char fillCh = '0'; + + switch (type) { + case DONTCARE: + fillCh = 'x'; + break; + case HIGHZ: + fillCh = 'z'; + break; + default: + long mask = (bits < 64)? ((1L << bits)-1) : 0xffffffffffffffffL; + binStr = Long.toBinaryString(val & mask); + } + + StringBuilder sb = new StringBuilder(); + if (binStr.length() < bits) { + int diff = bits - binStr.length(); + + for (int i = 0; i < diff; i++) { + sb.append(fillCh); + } + } + sb.append(binStr); + + return sb.toString(); + } + } +} diff --git a/src/main/java/de/neemann/digital/hdl/verilog2/lib/VerilogElement.java b/src/main/java/de/neemann/digital/hdl/verilog2/lib/VerilogElement.java new file mode 100644 index 000000000..b76faac18 --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/verilog2/lib/VerilogElement.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Ivan Deras + * 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.verilog2.lib; + +import de.neemann.digital.hdl.hgs.HGSEvalException; +import de.neemann.digital.hdl.model2.HDLNode; +import de.neemann.digital.hdl.printer.CodePrinter; +import java.io.IOException; + +/** + * Responsible of generating the Verilog code. + * + * @author ideras + */ +public interface VerilogElement { + /** + * Prints the entity to the printer if not allrady written. + * + * @param out the output to print the code to + * @param node the node to print + * @return the verilog name of the node + * @throws HGSEvalException HGSEvalException + * @throws IOException IOException + */ + String print(CodePrinter out, HDLNode node) throws HGSEvalException, IOException; + + /** + * Write the generic map of this node + * + * @param out the output to write to + * @param node the node + * @throws IOException IOException + */ + void writeGenericMap(CodePrinter out, HDLNode node) throws IOException; +} diff --git a/src/main/java/de/neemann/digital/hdl/verilog2/lib/VerilogTemplate.java b/src/main/java/de/neemann/digital/hdl/verilog2/lib/VerilogTemplate.java new file mode 100644 index 000000000..051a2e1c9 --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/verilog2/lib/VerilogTemplate.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2018 Ivan Deras + * 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.verilog2.lib; + +import de.neemann.digital.hdl.hgs.Context; +import de.neemann.digital.hdl.hgs.HGSEvalException; +import de.neemann.digital.hdl.hgs.Parser; +import de.neemann.digital.hdl.hgs.ParserException; +import de.neemann.digital.hdl.hgs.Statement; +import de.neemann.digital.hdl.hgs.Value; +import de.neemann.digital.hdl.model2.HDLException; +import de.neemann.digital.hdl.model2.HDLNode; +import de.neemann.digital.hdl.printer.CodePrinter; +import de.neemann.digital.hdl.vhdl2.Separator; +import de.neemann.digital.lang.Lang; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * + * @author ideras + */ +public class VerilogTemplate implements VerilogElement { + + private final static String MODULE_PREFIX = "DIG_"; + + private final String moduleBaseName; + private final Statement statements; + private HashMap modules; + + /** + * Creates a new instance + * + * @param elementName the element name + * @throws IOException IOException + * @throws HDLException HDLException + */ + public VerilogTemplate(String elementName) throws IOException, HDLException { + super(); + this.moduleBaseName = MODULE_PREFIX + elementName; + modules = new HashMap<>(); + + try { + statements = parseFile(moduleBaseName); + } catch (ParserException ex) { + throw new HDLException(ex.getMessage()); + } + + if (statements == null) { + throw new HDLException("Invalid verilog template file. Template is empty."); + } + } + + private Statement parseFile(String moduleName) throws IOException, ParserException { + Statement stmt; + String fileName = createFileName(moduleName); + + InputStream inputStream = getClass().getClassLoader().getResourceAsStream(fileName); + if (inputStream == null) { + throw new IOException("file not present: " + fileName); + } + + try (InputStreamReader in = new InputStreamReader(inputStream)) { + Parser parser = new Parser(in, fileName); + + stmt = parser.parse(); + } + + return stmt; + } + + private static String createFileName(String name) { + return "verilog/" + name + ".v"; + } + + /** + * Creates the name of the file used to load the vhdl file for the given + * element + * + * @param elementName the element name + * @return the filename + */ + public static String neededFileName(String elementName) { + return createFileName(MODULE_PREFIX + elementName); + } + + @Override + public String print(CodePrinter out, HDLNode node) throws HGSEvalException, IOException { + Module m = getModule(node); + + if (!m.isWritten) { + out.println(m.code); + m.setWritten(); + } + + return m.name; + } + + @Override + public void writeGenericMap(CodePrinter out, HDLNode node) throws IOException { + try { + Module m = getModule(node); + + List generics = m.getGenerics(); + + if (generics == null || generics.isEmpty()) { + return; + } + + out.println("#("); + out.inc(); + + Separator comma = new Separator(out, ",\n"); + + for (Object objv : generics) { + String keyName; + + keyName = Value.toString(objv); + + Object keyVal = node.getElementAttributes().hgsMapGet(keyName); + String kvs; + if (keyVal instanceof Boolean) { + kvs = ((Boolean) keyVal) ? "1" : "0"; + } else { + kvs = keyVal.toString(); + } + comma.check(); + out.print(".").print(keyName).print("(").print(kvs).print(")"); + } + + out.dec(); + out.println().println(")"); + } catch (HGSEvalException ex) { + throw new IOException("error evaluating the template " + createFileName(moduleBaseName), ex); + } + } + + private Module getModule(HDLNode node) throws HGSEvalException { + Module genModule = new Module(node, moduleBaseName); + + Module e = modules.get(genModule.name); + if (e == null) { + modules.put(genModule.name, genModule); + return genModule; + } else { + if (!(genModule.code.equals(e.code))) + throw new HGSEvalException(Lang.get("err_ifExternalComponentIsUsedTwiceCodeMustBeIdentical_N", genModule.name)); + else + return e; + } + } + + private final class Module { + private final String code; + private String name; + private final List generics; + private boolean isWritten = false; + + private Module(HDLNode node, String name) throws HGSEvalException { + this.name = name; + final Context ctx = createRuntimeContext(node); + + statements.execute(ctx); + code = ctx.toString(); + + if (ctx.contains("moduleName")) { + this.name = Value.toString(ctx.getVar("moduleName")); + } + + generics = (List) ctx.getVar("generics"); + } + + private Context createRuntimeContext(HDLNode node) throws HGSEvalException { + Context ctx = new Context(); + + ctx.declareVar("moduleName", name); + ctx.declareVar("generics", new ArrayList<>()); + ctx.declareVar("elem", node.getElementAttributes()); + ctx.declareVar("inp", node.getInputs()); + ctx.declareVar("outp", node.getOutputs()); + + return ctx; + } + + private String getCode() { + return code; + } + + private boolean isWritten() { + return isWritten; + } + + private void setWritten() { + isWritten = true; + } + + private String getName() { + return name; + } + + private List getGenerics() { + return generics; + } + } +} diff --git a/src/main/java/de/neemann/digital/hdl/verilog2/lib/package-info.java b/src/main/java/de/neemann/digital/hdl/verilog2/lib/package-info.java new file mode 100644 index 000000000..8c86d7339 --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/verilog2/lib/package-info.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2018 Ivan Deras + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ + +/** + * The elements used to create the verilog output + */ +package de.neemann.digital.hdl.verilog2.lib; diff --git a/src/main/java/de/neemann/digital/hdl/verilog2/package-info.java b/src/main/java/de/neemann/digital/hdl/verilog2/package-info.java new file mode 100644 index 000000000..70c4252b0 --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/verilog2/package-info.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2018 Ivan Deras. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ + +/** + * The verilog exporter + */ +package de.neemann.digital.hdl.verilog2; diff --git a/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLGenerator.java b/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLGenerator.java index 5c814f194..0bc775d9f 100644 --- a/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLGenerator.java +++ b/src/main/java/de/neemann/digital/hdl/vhdl2/VHDLGenerator.java @@ -17,8 +17,8 @@ import de.neemann.digital.hdl.model2.HDLNet; import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator; import de.neemann.digital.hdl.printer.CodePrinter; -import de.neemann.digital.hdl.vhdl2.boards.BoardInterface; -import de.neemann.digital.hdl.vhdl2.boards.BoardProvider; +import de.neemann.digital.hdl.boards.BoardInterface; +import de.neemann.digital.hdl.boards.BoardProvider; import de.neemann.digital.lang.Lang; import java.io.Closeable; diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index e29965312..b0caa935e 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -851,6 +851,7 @@ Sind evtl. die Namen der Variablen nicht eindeutig? Die Pinnummer {0} ist keine Zahl! Fehler beim Export zu VHDL. Kein VHDL Code für {0} verfügbar! + Kein Verilog Code für {0} verfügbar! Der Pin {0} hat keine Nummer! Fehler beim erzeugen des Testfalls! Werte vom Typ {0} sind nicht erlaubt! @@ -898,12 +899,16 @@ Sind evtl. die Namen der Variablen nicht eindeutig? Rückgabewert der Anwendung war nicht 0 sondern {0}: {1} Externer Code kann nur exportiert werden, wenn es sich um VHDL handelt! + Externer Code kann nur exportiert werden, wenn es sich um Verilog handelt! Wird eine externes Element mehrfach verwendet, muss der Code identisch sein! Betrifft {0} Konnte nicht auf stdOut schreiben: {0} Der VHDL Simulator ghdl scheint nicht installiert zu sein. Installieren Sie ghdl (http://ghdl.free.fr/) und versuchen Sie es erneut. Sollte es immer noch Probleme geben, überprüfen Sie den Pfad zur ausfühbaren ghdl-Datei in den Digital-Einstellungen. + + Der Verilog Simulator Icarus scheint nicht installiert zu sein. Installieren Sie Icarus (http://iverilog.icarus.com/) und versuchen Sie es erneut. Sollte es immer noch Probleme geben, überprüfen Sie den Pfad zur ausfühbaren iverilog-Datei in den Digital-Einstellungen. + Fehler bei der Analyse der Schaltung: {0} Jedes ROM braucht eine eindeutige Bezeichnung um exportiert zu werden! Der Zähler benötigt mindestens zwei Bits. @@ -1111,6 +1116,7 @@ Sind evtl. die Namen der Variablen nicht eindeutig?
Gibt an, welche Anwendung gestartet werden soll, um den Code auszuführen. Generisch GHDL + IVerilog Eingänge Die Eingänge des externen Prozesses. Es handelt sich um eine kommaseparierte Liste mit Signalnamen. Bei jedem Signalnamen kann, mit einem Doppelpunkt getrennt, eine Bitanzahl angegeben werden. @@ -1124,6 +1130,9 @@ Sind evtl. die Namen der Variablen nicht eindeutig? GHDL Pfad der ausführbaren ghdl-Datei. Nur wichtig, wenn ghdl zur Interpretation von VHDL-Code verwendet werden soll. + IVerilog + Pfad zum Icarus-Verilog-Installationsordner. Nur notwendig, wenn Sie iverilog + verwenden möchten, um mit Verilog definierte Komponenten zu simulieren. Maximalwert Wird hier eine Null eingetragen, wird der maximal mögliche Wert verwendet (Alle Bits sind Eins). @@ -1351,6 +1360,8 @@ Sind evtl. die Namen der Variablen nicht eindeutig? Erweitert die Schaltung um eine Spannungsversorung. Export zu VHDL Exportiert die Schaltung zu VHDL + Export zu Verilog + Exportiert die Schaltung zu Verilog KV-Tafel Zeigt eine KV-Tafel der Tabelle an. Dokumentation diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 4b9eae790..80a5fa678 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -847,6 +847,7 @@ The pin nummer {0} is not a integer! Error during export to VHDL. No VHDL code for {0} available! + No Verilog code for {0} available! Pin {0} has no number! Error creating a test bench! Values of typ {0} are not allowed! @@ -893,12 +894,16 @@ Application exit status was not 0 but {0}: {1} External code can only be exported if it is VHDL! + External code can only be exported if it is Verilog! If an external component is used multiple times, the code must be identical! Effects: {0} Could not write to stdOut: {0} The VHDL simulator ghdl does not seem to be installed. Install ghdl (http://ghdl.free.fr/) and try again. If there are still problems, check the path to the ghdl executable in the Digital settings. + + The Verilog simulator Icarus does not seem to be installed. Install ghdl (http://iverilog.icarus.com/) and try again. If there are still problems, check the path to the iverilog executable in the Digital settings. + Error analysing the circuit: {0} Every ROM needs a unique label to be exported! The counter needs at least two bits. @@ -1103,6 +1108,7 @@ Defines which application to use. Generic GHDL + IVerilog Inputs The inputs of the external process. It is a comma-separated list of signal names. For each signal name, with a colon separated, a number of bits @@ -1116,6 +1122,9 @@ GHDL Path to the executable ghdl file. Only necessary if you want to use ghdl to simulate components defined with vhdl. + IVerilog + Path to the Icarus verilog installation folder. Only necessary if you want to use iverilog to simulate + components defined with verilog. Maximum Value If a zero is entered, the maximum possible value is used (all bits are one). @@ -1344,6 +1353,8 @@ Adds a power supply to the circuit. Export to VHDL Exports the circuit to VHDL + Export to Verilog + Exports the circuit to Verilog Documentation Open {0} Error opening a PDF file! diff --git a/src/main/resources/verilog/DIG_Add.v b/src/main/resources/verilog/DIG_Add.v new file mode 100644 index 000000000..cc9d2895f --- /dev/null +++ b/src/main/resources/verilog/DIG_Add.v @@ -0,0 +1,22 @@ +module +#( + parameter Bits = 1 +) +( + input [(Bits-1):0] a, + input [(Bits-1):0] b, + input c_i, + output [(Bits - 1):0] s, + output c_o +); + wire [Bits:0] temp; + + assign temp = a + b + c_i; + assign s = temp [(Bits-1):0]; + assign c_o = temp[Bits]; +endmodule + diff --git a/src/main/resources/verilog/DIG_BitExtender.v b/src/main/resources/verilog/DIG_BitExtender.v new file mode 100644 index 000000000..1e9e3b7c3 --- /dev/null +++ b/src/main/resources/verilog/DIG_BitExtender.v @@ -0,0 +1,33 @@ + 1) { + generics[0] := "inputBits"; + generics[1] := "outputBits"; + + moduleName = "DIG_BitExtender"; +-?> +module DIG_BitExtender #( + parameter inputBits = 2, + parameter outputBits = 4 +) +( + input [(inputBits-1):0] in, + output [(outputBits - 1):0] out +); + assign out = {{(outputBits - inputBits){in[inputBits - 1]}}, in}; +endmodule + +module DIG_BitExtenderSingle #( + parameter outputBits = 2 +) +( + input in, + output [(outputBits - 1):0] out +); + assign out = {outputBits{in}}; +endmodule + + diff --git a/src/main/resources/verilog/DIG_BitSelector.v b/src/main/resources/verilog/DIG_BitSelector.v new file mode 100644 index 000000000..f6f35926a --- /dev/null +++ b/src/main/resources/verilog/DIG_BitSelector.v @@ -0,0 +1,13 @@ + +module ( + input in, + input sel, + output out +); + assign out = in[sel]; +endmodule diff --git a/src/main/resources/verilog/DIG_Comparator.v b/src/main/resources/verilog/DIG_Comparator.v new file mode 100644 index 000000000..8b3d31552 --- /dev/null +++ b/src/main/resources/verilog/DIG_Comparator.v @@ -0,0 +1,28 @@ + +module #( + parameter Bits = 1 +) +( + input [(Bits -1):0] a, + input [(Bits -1):0] b, + output \> , + output \= , + output \< +); + + assign \> = $signed(a) > $signed(b); + assign \= = $signed(a) == $signed(b); + assign \< = $signed(a) < $signed(b); + + assign \> = a > b; + assign \= = a == b; + assign \< = a < b; + +endmodule diff --git a/src/main/resources/verilog/DIG_Counter.v b/src/main/resources/verilog/DIG_Counter.v new file mode 100644 index 000000000..4786dc46a --- /dev/null +++ b/src/main/resources/verilog/DIG_Counter.v @@ -0,0 +1,41 @@ + 1) { + generics[0] := "Bits"; + moduleName = format("%s_Nbit", moduleName); + bitRange := "[(Bits-1):0] "; + } + else { + bitRange := ""; + moduleName = format("%s_1bit", moduleName); + } +?> +module 1) { ?> +#( + parameter Bits = 2 +) +( + output out, + output ovf, + input C, + input en, + input clr +); + reg count; + + always @ (posedge C) begin + if (clr) + count <= 'h0; + else if (en) + count <= count + 1'b1; + end + + assign out = count; + assign ovf = en? &count : 1'b0; + + initial begin + count = 'h0; + end +endmodule diff --git a/src/main/resources/verilog/DIG_CounterPreset.v b/src/main/resources/verilog/DIG_CounterPreset.v new file mode 100644 index 000000000..03a0ad5a9 --- /dev/null +++ b/src/main/resources/verilog/DIG_CounterPreset.v @@ -0,0 +1,56 @@ + +module DIG_CounterPreset #( + parameter Bits = 2, + parameter maxValue = 4 +) +( + input C, + input en, + input clr, + input dir, + input [(Bits-1):0] in, + input ld, + output [(Bits-1):0] out, + output ovf +); + + reg [(Bits-1):0] count = 'h0; + + function [(Bits-1):0] maxVal (input [(Bits-1):0] maxv); + if (maxv == 0) + maxVal = (1 << Bits) - 1; + else + maxVal = maxv; + endfunction + + assign out = count; + assign ovf = ((count == maxVal(maxValue) & dir == 1'b0) + | (count == 'b0 & dir == 1'b1))? en : 1'b0; + + always @ (posedge C) begin + if (clr == 1'b1) + count <= 'h0; + else if (ld == 1'b1) + count <= in; + else if (en == 1'b1) begin + if (dir == 1'b0) begin + if (count == maxVal(maxValue)) + count <= 'h0; + else + count <= count + 1'b1; + end + else begin + if (count == 'h0) + count <= maxVal(maxValue); + else + count <= count - 1; + end + end + end +endmodule diff --git a/src/main/resources/verilog/DIG_DCM_SP.v b/src/main/resources/verilog/DIG_DCM_SP.v new file mode 100644 index 000000000..6f121c75e --- /dev/null +++ b/src/main/resources/verilog/DIG_DCM_SP.v @@ -0,0 +1,58 @@ + +module DIG_DCM_SP #( + parameter integer CLKFX_DIVIDE = 1, + parameter integer CLKFX_MULTIPLY = 2, + parameter real CLKIN_PERIOD = 10.0 +) +( + input cin, + output cout +); + // DCM_SP: Digital Clock Manager + // Spartan-6 + // Xilinx HDL Libraries Guide, version 14.1 + + DCM_SP #( + .CLKDV_DIVIDE(2.0), // CLKDV divide value + // (1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,9,10,11,12,13,14,15,16). + .CLKFX_DIVIDE(CLKFX_DIVIDE), // Divide value on CLKFX outputs - D - (1-32) + .CLKFX_MULTIPLY(CLKFX_MULTIPLY), // Multiply value on CLKFX outputs - M - (2-32) + .CLKIN_DIVIDE_BY_2("FALSE"), // CLKIN divide by two (TRUE/FALSE) + .CLKIN_PERIOD(CLKIN_PERIOD), // Input clock period specified in nS + .CLKOUT_PHASE_SHIFT("NONE"), //Output phase shift (NONE, FIXED, VARIABLE) + .CLK_FEEDBACK("1X"), // Feedback source (NONE, 1X, 2X) + .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SYSTEM_SYNCHRNOUS or SOURCE_SYNCHRONOUS + .DFS_FREQUENCY_MODE("LOW"), // Unsupported - Do not change value + .DLL_FREQUENCY_MODE("LOW"), // Unsupported - Do not change value + .DSS_MODE("NONE"), // Unsupported - Do not change value + .DUTY_CYCLE_CORRECTION("TRUE"), // Unsupported - Do not change value + .FACTORY_JF(16'hc080), // Unsupported - Do not change value + .PHASE_SHIFT(0), // Amount of fixed phase shift (-255 to 255) + .STARTUP_WAIT("FALSE") // Delay config DONE until DCM_SP LOCKED (TRUE/FALSE) + ) + DCM_SP_inst ( + .CLK0(), // 1-bit output: 0 degree clock output + .CLK180(), // 1-bit output: 180 degree clock output + .CLK270(), // 1-bit output: 270 degree clock output + .CLK2X(), // 1-bit output: 2X clock frequency clock output + .CLK2X180(), // 1-bit output: 2X clock frequency, 180 degree clock output + .CLK90(), // 1-bit output: 90 degree clock output + .CLKDV(), // 1-bit output: Divided clock output + .CLKFX(cout), // 1-bit output: Digital Frequency Synthesizer output (DFS) + .CLKFX180(), // 1-bit output: 180 degree CLKFX output + .LOCKED(), // 1-bit output: DCM_SP Lock Output + .PSDONE(), // 1-bit output: Phase shift done output + .STATUS(), // 8-bit output: DCM_SP status output + .CLKFB(), // 1-bit input: Clock feedback input + .CLKIN(cin), // 1-bit input: Clock input + .DSSEN(1'b0), // 1-bit input: Unsupported, specify to GND. + .PSCLK(1'b0), // 1-bit input: Phase shift clock input + .PSEN(1'b0), // 1-bit input: Phase shift enable + .PSINCDEC(), // 1-bit input: Phase shift increment/decrement input + .RST(1'b0) // 1-bit input: Active high reset input + ); +endmodule \ No newline at end of file diff --git a/src/main/resources/verilog/DIG_D_FF.v b/src/main/resources/verilog/DIG_D_FF.v new file mode 100644 index 000000000..773089146 --- /dev/null +++ b/src/main/resources/verilog/DIG_D_FF.v @@ -0,0 +1,38 @@ + 1) { + generics[0] := "Bits"; + generics[1] := "Default"; + moduleName = format("%s_Nbit", moduleName); + bitRange := "[(Bits-1):0] "; + } + else { + generics[0] := "Default"; + moduleName = format("%s_1bit", moduleName); + bitRange := ""; + } +?>module +#( 1) { ?> + parameter Bits = 2, + parameter Default = 0 +) +( + input D, + input C, + output Q, + output \~Q +); + reg state; + + assign Q = state; + assign \~Q = ~state; + + always @ (posedge C) begin + state <= D; + end + + initial begin + state = Default; + end +endmodule diff --git a/src/main/resources/verilog/DIG_D_FF_AS.v b/src/main/resources/verilog/DIG_D_FF_AS.v new file mode 100644 index 000000000..220f33d2d --- /dev/null +++ b/src/main/resources/verilog/DIG_D_FF_AS.v @@ -0,0 +1,49 @@ + 1) { + generics[0] := "Bits"; + generics[1] := "Default"; + moduleName = format("%s_Nbit", moduleName); + bitRange := "[(Bits-1):0] "; + setExpr := "{Bits{1'b1}}"; + } + else { + generics[0] := "Default"; + moduleName = format("%s_1bit", moduleName); + bitRange := ""; + setExpr := "1'b1"; + } +?> +module +#( 1) { ?> + parameter Bits = 2, + + parameter Default = 0 +) +( + input Set, + input D, + input C, + input Clr, + output Q, + output \~Q +); + reg state; + + assign Q = state; + assign \~Q = ~state; + + always @ (posedge C or posedge Clr or posedge Set) + begin + if (Set) + state <= ; + else if (Clr) + state <= 'h0; + else + state <= D; + end + + initial begin + state = Default; + end +endmodule \ No newline at end of file diff --git a/src/main/resources/verilog/DIG_Decoder.v b/src/main/resources/verilog/DIG_Decoder.v new file mode 100644 index 000000000..fa71b7403 --- /dev/null +++ b/src/main/resources/verilog/DIG_Decoder.v @@ -0,0 +1,15 @@ + +module ( + + output out_, + + input sel +); + + assign out_ = (sel == )? 1'b1 : 1'b0; + +endmodule diff --git a/src/main/resources/verilog/DIG_Demultiplexer.v b/src/main/resources/verilog/DIG_Demultiplexer.v new file mode 100644 index 000000000..c27ffb736 --- /dev/null +++ b/src/main/resources/verilog/DIG_Demultiplexer.v @@ -0,0 +1,27 @@ + +module + 1) { + generics[0] := "Bits"; ?> #( + parameter Bits = 2 +) + +( + + output out_, + + input sel, + input in +); + + assign out_ = (sel == )? in : ; + +endmodule diff --git a/src/main/resources/verilog/DIG_Driver.v b/src/main/resources/verilog/DIG_Driver.v new file mode 100644 index 000000000..470f2e29b --- /dev/null +++ b/src/main/resources/verilog/DIG_Driver.v @@ -0,0 +1,25 @@ + +module + 1) { ?>#( + parameter Bits = 2 +) + +( + input in, + input sel, + output out +); + assign out = (sel == 1'b1)? in : ; +endmodule \ No newline at end of file diff --git a/src/main/resources/verilog/DIG_DriverInvSel.v b/src/main/resources/verilog/DIG_DriverInvSel.v new file mode 100644 index 000000000..3a6cdf9a9 --- /dev/null +++ b/src/main/resources/verilog/DIG_DriverInvSel.v @@ -0,0 +1,25 @@ + +module + 1) { ?>#( + parameter Bits = 2 +) + +( + input in, + input sel, + output out +); + assign out = (sel == 1'b0)? in : ; +endmodule \ No newline at end of file diff --git a/src/main/resources/verilog/DIG_External.v b/src/main/resources/verilog/DIG_External.v new file mode 100644 index 000000000..c1a7fded8 --- /dev/null +++ b/src/main/resources/verilog/DIG_External.v @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/src/main/resources/verilog/DIG_JK_FF.v b/src/main/resources/verilog/DIG_JK_FF.v new file mode 100644 index 000000000..80afa00cb --- /dev/null +++ b/src/main/resources/verilog/DIG_JK_FF.v @@ -0,0 +1,32 @@ +module +#( + parameter Default = 1'b0 +) +( + input J, + input C, + input K, + output Q, + output \~Q +); + reg state; + + assign Q = state; + assign \~Q = ~state; + + always @ (posedge C) begin + if (~J & K) + state <= 1'b0; + else if (J & ~K) + state <= 1'b1; + else if (J & K) + state <= ~state; + end + + initial begin + state = Default; + end +endmodule diff --git a/src/main/resources/verilog/DIG_JK_FF_AS.v b/src/main/resources/verilog/DIG_JK_FF_AS.v new file mode 100644 index 000000000..862550b12 --- /dev/null +++ b/src/main/resources/verilog/DIG_JK_FF_AS.v @@ -0,0 +1,38 @@ +module +#( + parameter Default = 1'b0 +) +( + input Set, + input J, + input C, + input K, + input Clr, + output Q, + output \~Q +); + reg state; + + assign Q = state; + assign \~Q = ~state; + + always @ (posedge C or posedge Clr or posedge Set) begin + if (Set) + state <= 1'b1; + else if (Clr) + state <= 1'b0; + else if (~J & K) + state <= 1'b0; + else if (J & ~K) + state <= 1'b1; + else if (J & K) + state <= ~state; + end + + initial begin + state = Default; + end +endmodule diff --git a/src/main/resources/verilog/DIG_Mul.v b/src/main/resources/verilog/DIG_Mul.v new file mode 100644 index 000000000..647bbe2bd --- /dev/null +++ b/src/main/resources/verilog/DIG_Mul.v @@ -0,0 +1,13 @@ + +module DIG_Mul #( + parameter Bits = 1 +) +( + input [(Bits-1):0] a, + input [(Bits-1):0] b, + output [(Bits*2-1):0] mul +); + assign mul = a * b; +endmodule diff --git a/src/main/resources/verilog/DIG_Multiplexer.v b/src/main/resources/verilog/DIG_Multiplexer.v new file mode 100644 index 000000000..62105bea5 --- /dev/null +++ b/src/main/resources/verilog/DIG_Multiplexer.v @@ -0,0 +1,36 @@ + +module + 1) { ?> #( + parameter Bits = 2 +) + +( + input sel, + + input in_, + + output reg out +); + always @ (*) begin + case (sel) + + 'h: out = in_; + + default: + out = 'h0; + endcase + end +endmodule diff --git a/src/main/resources/verilog/DIG_PriorityEncoder.v b/src/main/resources/verilog/DIG_PriorityEncoder.v new file mode 100644 index 000000000..d049eb3de --- /dev/null +++ b/src/main/resources/verilog/DIG_PriorityEncoder.v @@ -0,0 +1,27 @@ + +module ( + + input in, + + output reg num, + output any +); + always @ (*) begin + 0; n--) { -?> + if (in == 1'b1) + num = ; + else + + num = ; + end + + assign any = ; +endmodule diff --git a/src/main/resources/verilog/DIG_RAMDualAccess.v b/src/main/resources/verilog/DIG_RAMDualAccess.v new file mode 100644 index 000000000..8b8a37b80 --- /dev/null +++ b/src/main/resources/verilog/DIG_RAMDualAccess.v @@ -0,0 +1,33 @@ +module +#( + parameter Bits = 8, + parameter AddrBits = 4 +) +( + input C, // Clock signal + input ld, + input [(AddrBits-1):0] \1A , + input [(AddrBits-1):0] \2A , + input [(Bits-1):0] \1Din , + input str, + output [(Bits-1):0] \1D , + output [(Bits-1):0] \2D +); + // CAUTION: uses distributed RAM + reg [(Bits-1):0] memory [0:((1 << AddrBits)-1)]; + + assign \1D = ld? memory[\1A ] : 'hz; + assign \2D = memory[\2A ]; + + always @ (posedge C) begin + if (str) + memory[\1A ] <= \1Din ; + end + +endmodule + diff --git a/src/main/resources/verilog/DIG_RAMDualPort.v b/src/main/resources/verilog/DIG_RAMDualPort.v new file mode 100644 index 000000000..4cf3ece66 --- /dev/null +++ b/src/main/resources/verilog/DIG_RAMDualPort.v @@ -0,0 +1,27 @@ +module +#( + parameter Bits = 8, + parameter AddrBits = 4 +) +( + input [(AddrBits-1):0] A, + input [(Bits-1):0] Din, + input str, + input C, + input ld, + output [(Bits-1):0] D +); + reg [(Bits-1):0] memory[0:((1 << AddrBits) - 1)]; + + assign D = ld? memory[A] : 'hz; + + always @ (posedge C) begin + if (str) + memory[A] <= Din; + end +endmodule diff --git a/src/main/resources/verilog/DIG_ROM.v b/src/main/resources/verilog/DIG_ROM.v new file mode 100644 index 000000000..986a5e0f8 --- /dev/null +++ b/src/main/resources/verilog/DIG_ROM.v @@ -0,0 +1,33 @@ +module ( + input A, + input sel, + output reg D +); + reg my_rom [0:]; + + always @ (*) begin + if (~sel) + D = 'hz; + else if (A > ) + D = 'h0; + else + D = my_rom[A]; + end + + initial begin + my_rom[] = ; + end +endmodule diff --git a/src/main/resources/verilog/DIG_Register.v b/src/main/resources/verilog/DIG_Register.v new file mode 100644 index 000000000..ceeb52784 --- /dev/null +++ b/src/main/resources/verilog/DIG_Register.v @@ -0,0 +1,32 @@ + +module + 1) {?> #( + parameter Bits = 1 +) + +( + input C, + input en, + input D, + output Q +); + + reg state = 'h0; + + assign Q = state; + + always @ (posedge C) begin + if (en) + state <= D; + end +endmodule \ No newline at end of file diff --git a/src/main/resources/verilog/DIG_RegisterFile.v b/src/main/resources/verilog/DIG_RegisterFile.v new file mode 100644 index 000000000..31dae2320 --- /dev/null +++ b/src/main/resources/verilog/DIG_RegisterFile.v @@ -0,0 +1,31 @@ +module +#( + parameter Bits = 8, + parameter AddrBits = 4 +) +( + input [(Bits-1):0] Din, + input we, + input [(AddrBits-1):0] Rw, + input C, + input [(AddrBits-1):0] Ra, + input [(AddrBits-1):0] Rb, + output [(Bits-1):0] Da, + output [(Bits-1):0] Db +); + + reg [(Bits-1):0] memory[0:((1 << AddrBits)-1)]; + + assign Da = memory[Ra]; + assign Db = memory[Rb]; + + always @ (posedge C) begin + if (we) + memory[Rw] <= Din; + end +endmodule diff --git a/src/main/resources/verilog/DIG_Reset.v b/src/main/resources/verilog/DIG_Reset.v new file mode 100644 index 000000000..1df35fd84 --- /dev/null +++ b/src/main/resources/verilog/DIG_Reset.v @@ -0,0 +1,13 @@ + +module +#( + parameter invertOutput = 0 +) +( + output Reset +); + // ToDo: how to deal with the reset pin? + assign Reset = invertOutput; +endmodule \ No newline at end of file diff --git a/src/main/resources/verilog/DIG_Sub.v b/src/main/resources/verilog/DIG_Sub.v new file mode 100644 index 000000000..3420f8115 --- /dev/null +++ b/src/main/resources/verilog/DIG_Sub.v @@ -0,0 +1,19 @@ + +module DIG_Sub #( + parameter Bits = 2 +) +( + input [(Bits-1):0] a, + input [(Bits-1):0] b, + input c_i, + output [(Bits-1):0] s, + output c_o +); + wire [Bits:0] temp; + + assign temp = a - b - c_i; + assign s = temp[(Bits-1):0]; + assign c_o = temp[Bits]; +endmodule diff --git a/src/main/resources/verilog/DIG_simpleClockDivider.v b/src/main/resources/verilog/DIG_simpleClockDivider.v new file mode 100644 index 000000000..3df25d07f --- /dev/null +++ b/src/main/resources/verilog/DIG_simpleClockDivider.v @@ -0,0 +1,31 @@ + +module DIG_simpleClockDivider +#( + parameter maxCounter = 1 +) +( + input cin, + output cout +); + /* + * Don't use a logic signal as clock source in a real world application! + * Use the on chip clock resources instead! + */ + reg [31:0] counter; + reg state; + + assign cout = state; + + always @ (posedge cin) begin + if (counter == maxCounter) begin + counter <= 0; + state <= ~state; + end + else begin + counter <= counter + 1; + end + end + +endmodule diff --git a/src/main/resources/verilog/VerilogStdIOTemplate.vtpl b/src/main/resources/verilog/VerilogStdIOTemplate.vtpl new file mode 100644 index 000000000..cf98c2036 --- /dev/null +++ b/src/main/resources/verilog/VerilogStdIOTemplate.vtpl @@ -0,0 +1,120 @@ + + 1) + return "[" + (bits - 1) + ":0] "; + else + return ""; + } + + func getBitIndex(pos, bits) { + if (bits = 1) + return pos; + else + return (pos+bits-1) + ":" + pos; + } + + func getSliceExpr(signal, pos, bits) { + if (bits = 1) + return signal + "[" + pos + "]"; + else { + result := "{"; + for (i := (pos+bits-1); i >= pos; i=i-1) { + result = result + signal + "[" + i + "]"; + if (i > pos) + result = result + ", "; + } + result = result + "}"; + + return result; + } + } + + inBits := bitSum(inputs); + outBits := bitSum(outputs); + + if (inBits > 1) + inRange := "[0:" + (inBits - 1) + "] "; + else + inRange := ""; + + if (outBits > 1) + outRange := "[0:" + (outBits - 1) + "] "; + else + outRange := ""; +?> +module stdIOInterface; + reg mainIn; + wire mainOut; + 1) { + for (i:=0; i 1) { + for (i:=0; i + integer res, exit; + + ( + 1) { + for (i:=0; i 1) { + outCount := sizeOf(outputs); + for (i:=0; i + ); + + initial begin + exit = 0; + while (exit == 0) + begin + res = $fscanf('h8000_0000, "%b", mainIn); + #1; + + if (res == 1) begin + $display("Digital:%b", mainOut); + $fflush('h8000_0001); + end + else + exit = 1; + end + end +endmodule \ No newline at end of file diff --git a/src/main/resources/vhdl2/DIG_DCM_SP.tem b/src/main/resources/vhdl2/DIG_DCM_SP.tem new file mode 100644 index 000000000..acb51f842 --- /dev/null +++ b/src/main/resources/vhdl2/DIG_DCM_SP.tem @@ -0,0 +1,53 @@ +LIBRARY ieee; +USE ieee.std_logic_1164.all; + +Library UNISIM; +use UNISIM.vcomponents.all; + +entity DIG_DCM_SP is + generic ( + CLKFX_DIVIDE : integer; + CLKFX_MULTIPLY : integer; + CLKIN_PERIOD: real); + port ( + cin: in std_logic; + cout: out std_logic ); +end DIG_DCM_SP; + +architecture DIG_DCM_SP_arch of DIG_DCM_SP is + +begin + + -- DCM_SP: Digital Clock Manager + -- Spartan-6 + -- Xilinx HDL Libraries Guide, version 14.1 + + DCM_SP_int : DCM_SP + generic map ( + CLKDV_DIVIDE => 2.0, -- CLKDV divide value + -- (1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,9,10,11,12,13,14,15,16). + CLKFX_DIVIDE => CLKFX_DIVIDE, -- Divide value on CLKFX outputs - D - (1-32) + CLKFX_MULTIPLY => CLKFX_MULTIPLY, -- Multiply value on CLKFX outputs - M - (2-32) + CLKIN_DIVIDE_BY_2 => FALSE, -- CLKIN divide by two (TRUE/FALSE) + CLKIN_PERIOD => CLKIN_PERIOD, -- Input clock period specified in nS + CLKOUT_PHASE_SHIFT => "NONE", --Output phase shift (NONE, FIXED, VARIABLE) + CLK_FEEDBACK => "1X", -- Feedback source (NONE, 1X, 2X) + DESKEW_ADJUST => "SYSTEM_SYNCHRONOUS", -- SYSTEM_SYNCHRNOUS or SOURCE_SYNCHRONOUS + DFS_FREQUENCY_MODE => "LOW", -- Unsupported - Do not change value + DLL_FREQUENCY_MODE => "LOW", -- Unsupported - Do not change value + DSS_MODE => "NONE", -- Unsupported - Do not change value + DUTY_CYCLE_CORRECTION => TRUE, -- Unsupported - Do not change value + FACTORY_JF => X"c080", -- Unsupported - Do not change value + PHASE_SHIFT => 0, -- Amount of fixed phase shift (-255 to 255) + STARTUP_WAIT => FALSE -- Delay config DONE until DCM_SP LOCKED (TRUE/FALSE) + ) + port map ( + CLKFX => cout, -- 1-bit output: Digital Frequency Synthesizer output (DFS) + CLKIN => cin, -- 1-bit input: Clock input + DSSEN => '0', -- 1-bit input: Unsupported, specify to GND. + PSCLK => '0', -- 1-bit input: Phase shift clock input + PSEN => '0', -- 1-bit input: Phase shift enable + RST => '0' -- 1-bit input: Active high reset input + ); + +end DIG_DCM_SP_arch; \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/hdl/boards/ISEProjectGeneratorTest.java b/src/test/java/de/neemann/digital/hdl/boards/ISEProjectGeneratorTest.java new file mode 100644 index 000000000..e2cca461b --- /dev/null +++ b/src/test/java/de/neemann/digital/hdl/boards/ISEProjectGeneratorTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2018 Ivan Deras. + * 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.boards; + +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.model2.HDLException; +import de.neemann.digital.hdl.model2.HDLModel; +import de.neemann.digital.integration.ToBreakRunner; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import junit.framework.TestCase; + +public class ISEProjectGeneratorTest extends TestCase { + + public void testISEProjectExport() throws IOException, PinException, NodeException, ElementNotFoundException, HDLException { + HDLModel m = createModel("dig/hdl/model2/clock_mimasv1.dig"); + MimasV1Board b = new MimasV1Board(); + File dir = Files.createTempDirectory("digital_verilog_" + getTime() + "_").toFile(); + File file = new File(dir, "clock_mimasv1.v"); + + System.out.println(dir.getAbsolutePath()); + b.writeFiles(file, m); + File iseProjectFile = new File(dir, "clock_mimasv1_ise" + File.separator + "clock_mimasv1.xise"); + String output = readAllFile(iseProjectFile); + + assertEquals( "\n" + + "\n" + + "
\n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n", output); + } + + private String readAllFile(File f) throws FileNotFoundException, IOException { + BufferedReader r = new BufferedReader(new FileReader(f)); + StringBuilder sb = new StringBuilder(); + String str; + + while ((str = r.readLine()) != null) { + sb.append(str).append("\n"); + } + + return sb.toString(); + } + + HDLModel createModel(String filePath) throws IOException, PinException, NodeException, ElementNotFoundException, HDLException { + ToBreakRunner br = new ToBreakRunner(filePath); + + HDLModel m = new HDLModel(br.getLibrary()); + + return m.create(br.getCircuit(), null); + } + + private String getTime() { + DateFormat f = new SimpleDateFormat("YY-MM-dd_HH-mm_ss"); + return f.format(new Date()); + } +} diff --git a/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java b/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java index f6cbcd1bf..1ed2fe587 100644 --- a/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java +++ b/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java @@ -524,7 +524,7 @@ public void testTrim() throws IOException, ParserException, HGSEvalException { // checks the available VHDL templates public void testVHDLTemplates() throws Exception { final File path = new File(Resources.getRoot(), "../../main/resources/vhdl2"); - int n = new FileScanner(f -> new Parser(new FileReader(f)).parse()).setSuffix(".tem").scan(path); + int n = new FileScanner(f -> new Parser(new FileReader(f), f.getName()).parse()).setSuffix(".tem").scan(path); assertTrue(n > 10); } diff --git a/src/test/java/de/neemann/digital/hdl/verilog2/DescriptionTest.java b/src/test/java/de/neemann/digital/hdl/verilog2/DescriptionTest.java new file mode 100644 index 000000000..0e5b7102e --- /dev/null +++ b/src/test/java/de/neemann/digital/hdl/verilog2/DescriptionTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Ivan Deras. + * 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.verilog2; + +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) + .applyDefaultOptimizations(); + CodePrinterStr out = new CodePrinterStr(); + new VerilogCreator(out).printHDLCircuit(circuit, "naming"); + + assertEquals( "\n" + + "// Simple test circuit\n" + + "// used to test comments.\n" + + "module naming (\n" + + " input S0, // First input\n" + + " // This is a far longer text.\n" + + " input S1, // Second input\n" + + " output S2, // first output\n" + + " output S3 // second output\n" + + " // also with a longer text\n" + + "\n" + + ");\n" + + " wire s4;\n" + + " assign s4 = ~ (S0 | S1);\n" + + " assign S2 = (S0 ^ s4);\n" + + " assign S3 = (s4 ^ S1);\n" + + "endmodule\n", out.toString()); + } +} diff --git a/src/test/java/de/neemann/digital/hdl/verilog2/VerilogGeneratorTest.java b/src/test/java/de/neemann/digital/hdl/verilog2/VerilogGeneratorTest.java new file mode 100644 index 000000000..58bcac51f --- /dev/null +++ b/src/test/java/de/neemann/digital/hdl/verilog2/VerilogGeneratorTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2018 Ivan Deras. + * 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.verilog2; + +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.printer.CodePrinterStr; +import de.neemann.digital.integration.ToBreakRunner; +import junit.framework.TestCase; + +import java.io.IOException; + +public class VerilogGeneratorTest extends TestCase { + + public void testComb() throws PinException, NodeException, ElementNotFoundException, IOException { + ToBreakRunner br = new ToBreakRunner("dig/hdl/model2/comb.dig"); + CodePrinterStr out = new CodePrinterStr(); + VerilogGenerator gen = new VerilogGenerator(br.getLibrary(), out).export(br.getCircuit()); + + assertEquals( "/*\n" + + " * Generated by Digital. Don't modify this file!\n" + + " * Any changes will be lost if this file is regenerated.\n" + + " */\n" + + "module DIG_D_FF_1bit\n" + + "#(\n" + + " parameter Default = 0\n" + + ")\n" + + "(\n" + + " input D,\n" + + " input C,\n" + + " output Q,\n" + + " output \\~Q\n" + + ");\n" + + " reg state;\n" + + "\n" + + " assign Q = state;\n" + + " assign \\~Q = ~state;\n" + + "\n" + + " always @ (posedge C) begin\n" + + " state <= D;\n" + + " end\n" + + "\n" + + " initial begin\n" + + " state = Default;\n" + + " end\n" + + "endmodule\n" + + "\n" + + "\n" + + "module comb (\n" + + " input A,\n" + + " input B,\n" + + " input C,\n" + + " output X,\n" + + " output Y,\n" + + " output Z,\n" + + " output Aident\n" + + ");\n" + + " wire Y_temp;\n" + + " wire s0;\n" + + " wire Z_temp;\n" + + " assign Y_temp = (B | ~ C);\n" + + " assign Z_temp = ~ A;\n" + + " assign s0 = ((A | C) & (Z_temp | C) & 1'b1 & ~ (B | C) & Y_temp);\n" + + " DIG_D_FF_1bit #(\n" + + " .Default(0)\n" + + " )\n" + + " DIG_D_FF_1bit_i0 (\n" + + " .D( s0 ),\n" + + " .C( 1'b1 ),\n" + + " .Q( X )\n" + + " );\n" + + " assign Y = Y_temp;\n" + + " assign Z = Z_temp;\n" + + " assign Aident = A;\n" + + "endmodule\n", out.toString()); + } + + public void testSplitter3() throws PinException, NodeException, ElementNotFoundException, IOException { + ToBreakRunner br = new ToBreakRunner("dig/hdl/model2/splitter3.dig"); + CodePrinterStr out = new CodePrinterStr(); + VerilogGenerator gen = new VerilogGenerator(br.getLibrary(), out).export(br.getCircuit()); + + assertEquals( "/*\n" + + " * Generated by Digital. Don't modify this file!\n" + + " * Any changes will be lost if this file is regenerated.\n" + + " */\n" + + "\n" + + "module splitter3 (\n" + + " input [3:0] A,\n" + + " input [3:0] B,\n" + + " output [3:0] S\n" + + ");\n" + + " assign S[1:0] = (A[1:0] & B[1:0]);\n" + + " assign S[3:2] = (A[3:2] | B[3:2]);\n" + + "endmodule\n", out.toString()); + } + + public void testSplitter2() throws PinException, NodeException, ElementNotFoundException, IOException { + ToBreakRunner br = new ToBreakRunner("dig/hdl/model2/splitter2.dig"); + CodePrinterStr out = new CodePrinterStr(); + VerilogGenerator gen = new VerilogGenerator(br.getLibrary(), out).export(br.getCircuit()); + + assertEquals( "/*\n" + + " * Generated by Digital. Don't modify this file!\n" + + " * Any changes will be lost if this file is regenerated.\n" + + " */\n" + + "\n" + + "module splitter2 (\n" + + " input [1:0] A,\n" + + " input [1:0] B,\n" + + " output X,\n" + + " output [2:0] Y\n" + + ");\n" + + " wire [3:0] s0;\n" + + " assign s0[1:0] = A;\n" + + " assign s0[3:2] = B;\n" + + " assign X = s0[0];\n" + + " assign Y = s0[3:1];\n" + + "endmodule\n", out.toString()); + } + + public void testSplitter2I() throws PinException, NodeException, ElementNotFoundException, IOException { + ToBreakRunner br = new ToBreakRunner("dig/hdl/splitter2.dig"); + CodePrinterStr out = new CodePrinterStr(); + VerilogGenerator gen = new VerilogGenerator(br.getLibrary(), out).export(br.getCircuit()); + + assertEquals( "/*\n" + + " * Generated by Digital. Don't modify this file!\n" + + " * Any changes will be lost if this file is regenerated.\n" + + " */\n" + + "\n" + + "module splitter2 (\n" + + " input [15:0] inst,\n" + + " output [15:0] \\9SD \n" + + ");\n" + + " wire s0;\n" + + " assign s0 = inst[8];\n" + + " assign \\9SD [7:0] = inst[7:0];\n" + + " assign \\9SD [8] = s0;\n" + + " assign \\9SD [9] = s0;\n" + + " assign \\9SD [10] = s0;\n" + + " assign \\9SD [11] = s0;\n" + + " assign \\9SD [12] = s0;\n" + + " assign \\9SD [13] = s0;\n" + + " assign \\9SD [14] = s0;\n" + + " assign \\9SD [15] = s0;\n" + + "endmodule\n", out.toString()); + } + + +} \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/hdl/verilog2/VerilogRenamingTest.java b/src/test/java/de/neemann/digital/hdl/verilog2/VerilogRenamingTest.java new file mode 100644 index 000000000..958c6fd1c --- /dev/null +++ b/src/test/java/de/neemann/digital/hdl/verilog2/VerilogRenamingTest.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Ivan Deras. + * 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.verilog2; + +import junit.framework.TestCase; + +public class VerilogRenamingTest extends TestCase { + + public void testCheckName() { + VerilogRenaming r = new VerilogRenaming(); + assertEquals("a", r.checkName("a")); + assertEquals("\\0a ", r.checkName("0a")); + assertEquals("\\input ", r.checkName("input")); + assertEquals("\\a&u(in ", r.checkName("a&u(in")); + assertEquals("\\a&ü(in ", r.checkName("a&ü(in")); + assertEquals("\\a\"o\"o ", r.checkName("a\"o\"o")); + assertEquals("\\\"o\" ", r.checkName("\"o\"")); + assertEquals("_o_", r.checkName("_o_")); + assertEquals("\\/Q ", r.checkName("/Q")); + assertEquals("\\!Q ", r.checkName("!Q")); + assertEquals("\\~Q ", r.checkName("~Q")); + assertEquals("\\ab ", r.checkName("a>b")); + assertEquals("\\a=b ", r.checkName("a=b")); + } +} \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/hdl/verilog2/VerilogSimulatorTest.java b/src/test/java/de/neemann/digital/hdl/verilog2/VerilogSimulatorTest.java new file mode 100644 index 000000000..ddabb4de8 --- /dev/null +++ b/src/test/java/de/neemann/digital/hdl/verilog2/VerilogSimulatorTest.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2018 Ivan Deras. + * 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.verilog2; + +import de.neemann.digital.core.ExceptionWithOrigin; +import de.neemann.digital.core.NodeException; +import de.neemann.digital.core.element.Keys; +import de.neemann.digital.core.extern.ProcessStarter; +import de.neemann.digital.draw.elements.PinException; +import de.neemann.digital.draw.library.ElementNotFoundException; +import de.neemann.digital.gui.Settings; +import de.neemann.digital.hdl.model2.HDLException; +import de.neemann.digital.hdl.printer.CodePrinter; +import de.neemann.digital.integration.FileScanner; +import de.neemann.digital.integration.Resources; +import de.neemann.digital.integration.ToBreakRunner; +import junit.framework.TestCase; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + +import java.nio.file.Path; +import java.nio.file.Paths; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static de.neemann.digital.integration.TestExamples.check; + +public class VerilogSimulatorTest extends TestCase { + private static final Logger LOGGER = LoggerFactory.getLogger(VerilogSimulatorTest.class); + private static String IVERILOG = System.getProperty("iverilog", ""); + private static String IVERILOG_DIR; + private static String VVP; + private static final boolean foundIVerilog = findIVerilogDir(); + private int testBenches; + + public void testInSimulator() throws Exception { + File examples = new File(Resources.getRoot(), "/dig/test/vhdl"); + try { + int tested = new FileScanner(this::checkVerilogExport).noOutput().scan(examples); + assertEquals(32, tested); + assertEquals(tested+2, testBenches); + } catch (FileScanner.SkipAllException e) { + // if iverilog is not installed its also ok + } + } + + public void testInSimulator2() throws Exception { + File examples = new File(Resources.getRoot(), "/dig/hdl"); + try { + int tested = new FileScanner(this::checkVerilogExport).noOutput().scan(examples); + assertEquals(47, tested); + } catch (FileScanner.SkipAllException e) { + // if iverilog is not installed its also ok + } + } + + public void testDistributedInSimulator() throws Exception { + File examples = new File(Resources.getRoot(), "../../main/dig/vhdl"); + try { + int tested = new FileScanner(this::checkVerilogExport).noOutput().scan(examples); + assertEquals(1, tested); + assertEquals(1, testBenches); + } catch (FileScanner.SkipAllException e) { + // if iverilog is not installed its also ok + } + } + + public void testProcessorInSimulator() throws Exception { + File file = new File(Resources.getRoot(), "../../main/dig/processor/VHDLExample.dig"); + try { + checkVerilogExport(file); + } catch (FileScanner.SkipAllException e) { + // if iverilog is not installed its also ok + } catch (Exception e) { + System.out.println(ExceptionWithOrigin.getOriginOf(e)); + throw e; + } + } + + public void testMultiplierInSimulator() throws Exception { + File file = new File(Resources.getRoot(), "../../main/dig/combinatorial/Multiply8Bit.dig"); + try { + checkVerilogExport(file); + } catch (FileScanner.SkipAllException e) { + // if iverilog is not installed its also ok + } catch (Exception e) { + System.out.println(ExceptionWithOrigin.getOriginOf(e)); + throw e; + } + } + + public void testIVERILOGInSimulator() throws Exception { + if (foundIVerilog) { + Settings.getInstance().getAttributes().set(Keys.SETTINGS_GHDL_PATH, new File(IVERILOG)); + + File source = new File(Resources.getRoot(), "dig/external/verilog"); + + int tested = new FileScanner(f -> { + checkVerilogExport(f); + // check simulation in Digital + check(f); + }).scan(source); + assertEquals(4, tested); + } + } + + + private void checkVerilogExport(File file) throws PinException, NodeException, ElementNotFoundException, IOException, FileScanner.SkipAllException, HDLException { + ToBreakRunner br = new ToBreakRunner(file); + File dir = Files.createTempDirectory("digital_verilog_" + getTime() + "_").toFile(); + try { + File srcFile = new File(dir, file.getName() + .replace('.', '_') + .replace('-', '_')+ ".v"); + CodePrinter out = new CodePrinter(srcFile); + try (VerilogGenerator gen = new VerilogGenerator(br.getLibrary(), out)) { + gen.disableClockIntegration().export(br.getCircuit()); + ArrayList testFiles = gen.getTestBenches(); + out.close(); + runIVerilog(srcFile, testFiles); + } + ProcessStarter.removeFolder(dir); + } finally { + br.close(); + } + } + + private void runIVerilog(File sourceFile, ArrayList testFileWritten) throws IOException, FileScanner.SkipAllException, HDLException { + String ivlModuleDir = IVERILOG_DIR + File.separator + "lib" + File.separator + "ivl"; + for (File testbench : testFileWritten) { + String name = testbench.getName(); + String module = name.substring(0, name.length() - 2); + String testOutputName = module + ".out"; + + checkWarn(testbench, startProcess(sourceFile.getParentFile(), IVERILOG, "-tvvp", "-o" + testOutputName, sourceFile.getName(), name)); + + String result = startProcess(sourceFile.getParentFile(), VVP, "-M", ivlModuleDir, testOutputName); + if (result.contains("(assertion error)")) + throw new HDLException("test bench " + name + " failed:\n" + result); + checkWarn(testbench, result); + testBenches++; + } + } + + private void checkWarn(File file, String result) { + if (result.contains("warning")) { + System.out.println(file); + System.out.println(result); + } + } + + private String startProcess(File dir, String... args) throws IOException, FileScanner.SkipAllException { + try { + return ProcessStarter.start(dir, args); + } catch (ProcessStarter.CouldNotStartProcessException e) { + throw new FileScanner.SkipAllException("iverilog (https://github.com/steveicarus/iverilog) is not installed! Add iverilog binary to the system path or set system property 'iverilog' to iverilog binary"); + } + } + + private String getTime() { + DateFormat f = new SimpleDateFormat("YY-MM-dd_HH-mm_ss"); + return f.format(new Date()); + } + + private static boolean findIVerilogDir() { + Path ivp = null; + + if (!IVERILOG.isEmpty()) { + Path p = Paths.get(IVERILOG); + + if (Files.isExecutable(p)) { + ivp = p; + if (Files.isSymbolicLink(p)) { + try { + ivp = Files.readSymbolicLink(ivp); + } catch (IOException ex) { + LOGGER.info("I/O Exception: " + ex.getMessage()); + return false; + } + } + } + } + + if (ivp == null) { + // Let's try to find iverilog in the system path + String[] strPaths = System.getenv("PATH").split(File.pathSeparator); + + for (String sp : strPaths) { + Path p = Paths.get(sp, "iverilog"); + + if (Files.isExecutable(p)) { + ivp = p; + if (Files.isSymbolicLink(p)) { + try { + ivp = Files.readSymbolicLink(ivp); + } catch (IOException ex) { + LOGGER.info("I/O Exception: " + ex.getMessage()); + return false; + } + } + break; + } + } + } + + if (ivp != null) { + IVERILOG_DIR = ivp.getParent().getParent().toString(); + IVERILOG = ivp.getParent().resolve("iverilog").toString(); + VVP = ivp.getParent().resolve("vvp").toString(); + + return true; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/hdl/vhdl2/ClockTest.java b/src/test/java/de/neemann/digital/hdl/vhdl2/ClockTest.java index 82d5015bf..ff32bb019 100644 --- a/src/test/java/de/neemann/digital/hdl/vhdl2/ClockTest.java +++ b/src/test/java/de/neemann/digital/hdl/vhdl2/ClockTest.java @@ -16,7 +16,7 @@ import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator; import de.neemann.digital.hdl.printer.CodePrinter; import de.neemann.digital.hdl.printer.CodePrinterStr; -import de.neemann.digital.hdl.vhdl2.boards.ClockIntegratorARTIX7; +import de.neemann.digital.hdl.boards.ClockIntegratorARTIX7; import de.neemann.digital.integration.ToBreakRunner; import junit.framework.TestCase; diff --git a/src/test/java/de/neemann/digital/hdl/vhdl2/VHDLSimulatorTest.java b/src/test/java/de/neemann/digital/hdl/vhdl2/VHDLSimulatorTest.java index a570bd2d0..fe799f209 100644 --- a/src/test/java/de/neemann/digital/hdl/vhdl2/VHDLSimulatorTest.java +++ b/src/test/java/de/neemann/digital/hdl/vhdl2/VHDLSimulatorTest.java @@ -58,7 +58,7 @@ public void testInSimulator2() throws Exception { File examples = new File(Resources.getRoot(), "/dig/hdl"); try { int tested = new FileScanner(this::checkVHDLExport).noOutput().scan(examples); - assertEquals(46, tested); + assertEquals(47, tested); } catch (FileScanner.SkipAllException e) { // if ghdl is not installed its also ok } diff --git a/src/test/java/de/neemann/digital/lang/TestLang.java b/src/test/java/de/neemann/digital/lang/TestLang.java index 0896007e2..4cd7e8d37 100644 --- a/src/test/java/de/neemann/digital/lang/TestLang.java +++ b/src/test/java/de/neemann/digital/lang/TestLang.java @@ -93,7 +93,7 @@ private void parseTree(File file, HashSet keys) throws IOException { try { if (f.getName().endsWith(".java")) checkSourceFile(f, keys, PATTERN); - if (f.getName().endsWith(".tem")) + if (f.getName().endsWith(".tem") || f.getName().endsWith(".v")) checkSourceFile(f, keys, TEM_PATTERN); } catch (AssertionFailedError e) { throw new AssertionFailedError(e.getMessage() + " in file " + f); diff --git a/src/test/resources/dig/external/verilog/verilog.dig b/src/test/resources/dig/external/verilog/verilog.dig new file mode 100644 index 000000000..39bacd618 --- /dev/null +++ b/src/test/resources/dig/external/verilog/verilog.dig @@ -0,0 +1,194 @@ + + + 1 + + + + External + + + applicationType + IVERILOG + + + Label + add + + + externalParameters + /home/hneemann/temp/Digital/go/go + + + processType + GHDL + + + externalInputs + a:4,b:4,c_i + + + externalOutputs + s:4,c_o + + + Code + module add +( + input [3:0] a, + input [3:0] b, + input c_i, + output [3:0] s, + output c_o +); + wire [4:0] temp; + + assign temp = a + b + c_i; + assign s = temp [3:0]; + assign c_o = temp[4]; +endmodule + + + + + + In + + + Label + A + + + Bits + 4 + + + InDefault + + + + + + + In + + + Label + B + + + Bits + 4 + + + InDefault + + + + + + + Out + + + Label + S + + + Bits + 4 + + + + + + In + + + Label + C_i + + + InDefault + + + + + + + Out + + + Label + C_o + + + + + + Testcase + + + Testdata + + A B C_i C_o S + +loop(a,16) +loop(b,16) + (a) (b) 0 ((a+b)>>4) (a+b) +end loop +end loop + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/external/verilog/verilog2.dig b/src/test/resources/dig/external/verilog/verilog2.dig new file mode 100644 index 000000000..67114f70b --- /dev/null +++ b/src/test/resources/dig/external/verilog/verilog2.dig @@ -0,0 +1,343 @@ + + + 1 + + + + In + + + Label + A + + + Bits + 4 + + + InDefault + + + + + + + In + + + Label + B + + + Bits + 4 + + + InDefault + + + + + + + Out + + + Label + S + + + Bits + 4 + + + + + + In + + + Label + C_i + + + InDefault + + + + + + + Out + + + Label + C_o + + + + + + Testcase + + + Testdata + + A B C_i C_o S + +loop(a,16) +loop(b,16) + (a) (b) 0 ((a+b)>>4) (a+b) +end loop +end loop + + + + + + + + Splitter + + + Input Splitting + 4 + + + Output Splitting + 2,2 + + + + + + Splitter + + + Input Splitting + 4 + + + Output Splitting + 2,2 + + + + + + Splitter + + + Input Splitting + 2,2 + + + Output Splitting + 4 + + + + + + External + + + applicationType + IVERILOG + + + Label + add + + + externalParameters + /home/hneemann/temp/Digital/go/go + + + processType + GHDL + + + externalInputs + c_i,a:2,b:2 + + + externalOutputs + s:2,c_o + + + Code + module add +( + input [1:0] a, + input [1:0] b, + input c_i, + output [1:0] s, + output c_o +); + wire [2:0] temp; + + assign temp = a + b + c_i; + assign s = temp [1:0]; + assign c_o = temp[2]; +endmodule + + + + + + External + + + applicationType + IVERILOG + + + Label + add + + + externalParameters + /home/hneemann/temp/Digital/go/go + + + processType + GHDL + + + externalInputs + c_i,a:2,b:2 + + + externalOutputs + s:2,c_o + + + Code + module add +( + input [1:0] a, + input [1:0] b, + input c_i, + output [1:0] s, + output c_o +); + wire [2:0] temp; + + assign temp = a + b + c_i; + assign s = temp [1:0]; + assign c_o = temp[2]; +endmodule + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/external/verilog/verilog3-inc.dig b/src/test/resources/dig/external/verilog/verilog3-inc.dig new file mode 100644 index 000000000..533d6d407 --- /dev/null +++ b/src/test/resources/dig/external/verilog/verilog3-inc.dig @@ -0,0 +1,174 @@ + + + 1 + + + + External + + + applicationType + IVERILOG + + + Label + add + + + externalParameters + /home/hneemann/temp/Digital/go/go + + + processType + GHDL + + + externalInputs + c_i,a:2,b:2 + + + externalOutputs + s:2,c_o + + + Code + module add +( + input [1:0] a, + input [1:0] b, + input c_i, + output [1:0] s, + output c_o +); + wire [2:0] temp; + + assign temp = a + b + c_i; + assign s = temp [1:0]; + assign c_o = temp[2]; +endmodule + + + + + + In + + + Label + C_i + + + InDefault + + + + + + + In + + + Label + A + + + Bits + 2 + + + InDefault + + + + + + + In + + + Label + B + + + Bits + 2 + + + InDefault + + + + + + + Out + + + Label + S + + + Bits + 2 + + + + + + Out + + + Label + C_o + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/external/verilog/verilog3.dig b/src/test/resources/dig/external/verilog/verilog3.dig new file mode 100644 index 000000000..7252b49ba --- /dev/null +++ b/src/test/resources/dig/external/verilog/verilog3.dig @@ -0,0 +1,259 @@ + + + 1 + + + + In + + + Label + A + + + Bits + 4 + + + InDefault + + + + + + + In + + + Label + B + + + Bits + 4 + + + InDefault + + + + + + + Out + + + Label + S + + + Bits + 4 + + + + + + In + + + Label + C_i + + + InDefault + + + + + + + Out + + + Label + C_o + + + + + + Testcase + + + Testdata + + A B C_i C_o S + +loop(a,16) +loop(b,16) + (a) (b) 0 ((a+b)>>4) (a+b) +end loop +end loop + + + + + + + + Splitter + + + Input Splitting + 4 + + + Output Splitting + 2,2 + + + + + + Splitter + + + Input Splitting + 4 + + + Output Splitting + 2,2 + + + + + + Splitter + + + Input Splitting + 2,2 + + + Output Splitting + 4 + + + + + + verilog3-inc.dig + + + + + verilog3-inc.dig + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dig/hdl/model2/clock_mimasv1.dig b/src/test/resources/dig/hdl/model2/clock_mimasv1.dig new file mode 100644 index 000000000..2b3478c3a --- /dev/null +++ b/src/test/resources/dig/hdl/model2/clock_mimasv1.dig @@ -0,0 +1,95 @@ + + + 1 + + + + D_FF + + + + + In + + + Label + A + + + pinNumber + P124 + + + + + + Text + + + Description + Board: MimasV1 + + + + + + Clock + + + runRealTime + true + + + Label + C + + + Frequency + 1000000 + + + pinNumber + P126 + + + + + + Out + + + Label + X + + + pinNumber + P119 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/docu/static_de.xml b/src/test/resources/docu/static_de.xml index 07377e97e..a4c256d36 100644 --- a/src/test/resources/docu/static_de.xml +++ b/src/test/resources/docu/static_de.xml @@ -363,46 +363,48 @@ ist. - + - Eine Schaltung kann zu VHDL exportiert werden. Dabei wird eine Datei erzeugt, welche die komplette - Beschreibung der Schaltung enthält. Der erzeugte VHDL code wurde mit + Eine Schaltung kann zu VHDL oder Verilog exportiert werden. Dabei wird eine Datei erzeugt, + welche die komplette Beschreibung der Schaltung enthält. Der erzeugte VHDL Code wurde mit Xilinx Vivado und dem Open Source VHDL Simulator ghdl getestet. + Der Verilog Code mit dem Verilog Simulator Icarus Verilog. - Wenn eine Schaltung Testfälle enthält, wird anhand der Testdaten eine VHDL-Test-Bench erzeugt. - Diese kann verwendet werden, um die korrekte Funktion der Schaltung in einer VHDL Simulation zu + Wenn eine Schaltung Testfälle enthält, wird anhand der Testdaten eine Test-Bench erzeugt. + Diese kann verwendet werden, um die korrekte Funktion der Schaltung in einer HDL-Simulation zu überprüfen. - Für spezielle Boards können zusätzliche Dateien erzeugt werden. Zur Zeit wird nur das + Für spezielle Boards können zusätzliche Dateien erzeugt werden. Zur Zeit werden nur das BASYS3 - Board unterstützt. + Board und die Boards Mimas + und Mimas V2 unterstützt. Dabei wird eine Constraints-Datei erzeugt, welche die Zuordnung der Pins beinhaltet. Die Bezeichnung der - Pins kann dem BASYS3 Datenblatt entnommen werden und ist als Pinnummer bei den Ein- und Ausgängen + Pins kann den entsprechenden Datenblättern entnommen werden und ist als Pinnummer bei den Ein- und Ausgängen einzutragen. - Wenn die Taktfrequenz niedrig ist, wird ein Frequenzteiler in den VHDL Code integriert, + Beim BASYS3 Board wird, wenn die Taktfrequenz niedrig ist, ein Frequenzteiler in den HDL Code integriert, um den Boardtakt entsprechend zu teilen. Wenn die in der Schaltung gewählte Taktfrequenz über 37kHz liegt, wird die MMCM Einheit des Artix-7 zur Takterzeugung verwendet. - Dies stellt sicher, dass die für die Taktverteilung vorgesehenen FPGA-Resourcen auch tatsächlich verwendet werden. Der enthaltene Beispiel-Prozessor läuft z.B. mit 20MHz, und wenn auf den Multiplizierer in der ALU verzichtet werden kann, sind auch 30MHz möglich. - Soll eine Schaltung auf einem BASYS3 Board betrieben werden, kann in Vivado ein neues Projekt angelegt werden. - Dabei ist die erzeugte VHDL Datei und die Constraints-Datei dem Projekt hinzuzufügen. Wurde das - Projekt erstellt, kann der Bitstream erzeugt und mit dem Hardware-Manager in ein BASYS3 Board übertragen werden. + Auch bei den Mimas-Boards wird der Spartan 6 DCM für die Takterzeugung verwendet. - Um neben der VHDL Datei auch die erforderliche Constraints-Datei erzeugen zu lassen, muss die Schaltung - ein Textfeld mit dem Text "Board: BASYS3" enthalten. + Soll eine Schaltung auf einem BASYS3 Board betrieben werden, wird eine Vivado Projektdatei angelegt, + die direkt mit Vivado geöffnet werden kann. Es läßt sich dann der Bitstream erzeugen und mit dem + Hardware-Manager kann dieser in ein BASYS3 Board übertragen werden. + + + Um neben der HDL Datei auch die erforderliche Constraints-Datei erzeugen zu lassen, muss die Schaltung + ein Textfeld mit dem Text "Board: BASYS3", "Board: MimasV1" oder "Board: MimasV2" enthalten. diff --git a/src/test/resources/docu/static_en.xml b/src/test/resources/docu/static_en.xml index 0b46be20a..a1c1ec557 100644 --- a/src/test/resources/docu/static_en.xml +++ b/src/test/resources/docu/static_en.xml @@ -338,33 +338,39 @@ On Linux systems, the fitters can also be executed by Digital if wine is installed. - + - A circuit can be exported to VHDL. A file is generated which contains the complete description of the - circuit. The generated VHDL code was tested with + A circuit can be exported to VHDL or Verilog. A file is generated which contains the complete description + of the circuit. The generated VHDL code was tested with Xilinx Vivado and the open source VHDL simulator ghdl. + The Verilog code is tested with the Verilog simulator Icarus Verilog. - If a circuit contains test cases, the test data is used to generate a VHDL test bench. This can be used - to check the correct function of the circuit in a VHDL simulation. + If a circuit contains test cases, the test data is used to generate a HDL test bench. This can be used + to check the correct function of the circuit in a HDL simulation. Additional files which are needed by special boards can be created. At present only the BASYS3 - board is supported. + board and the Mimas boards Mimas + and Mimas V2 + are supported. A constraints file is created, which contains the assignment of the pins. The description of the pins can - be found in the BASYS3 data sheet, and must be entered as a pin number for the inputs and outputs. + be found in the boards data sheet, and must be entered as a pin number for the inputs and outputs. - If the circuit clock frequency is low, a frequency divider is integrated into the VHDL code to divide - the board clock accordingly. + At a BASYS3 board, if the circuit clock frequency is low, a frequency divider is integrated into the HDL + code to divide the board clock accordingly. If the clock frequency selected in the circuit exceeds 37kHz, the MMCM unit of the Artix-7 is used for clock generation. This ensures that the FPGA resources provided for the clock distribution are used. This allows the included example processor to run at 20MHz, and if you can do without the multiplier, 30HMz is also possible. + + Also at the Mimas-Boards the Spartan 6 DCM is utilized for the clock generation. + If a circuit is to run on a BASYS3 board, a new project can be created in Vivado. The generated VHDL file and the constraints file must be added to the project. @@ -372,8 +378,8 @@ to program a BASYS3 board. - To create the required constraints file, the circuit must contain a text field with the text "Board: - BASYS3". + To create the required constraints file, the circuit must contain a text field with the text + "Board: BASYS3", "Board: MimasV1" or "Board: MimasV2".