- Added automated testing for the verilog exporter.

- Added missing elements (Reset and Demultiplexer) to the verilog exporter.
- Added missing functionality in the splitter verilog exporter element.
- Some small BUG fixes.
This commit is contained in:
Ivan de Jesus Deras 2018-03-12 12:46:41 -06:00
parent f5805a1717
commit f8f5d6ae07
16 changed files with 373 additions and 41 deletions

View File

@ -537,5 +537,10 @@ public final class Keys {
*/
public static final Key.KeyFile SETTINGS_GHDL_PATH
= new Key.KeyFile("ghdlPath", new File("ghdl"));
/**
* Path to iverilog
*/
public static final Key.KeyFile SETTINGS_IVERILOG_PATH
= new Key.KeyFile("iverilogPath", new File("iverilog"));
}

View File

@ -63,4 +63,13 @@ public class ModuleList {
public boolean isEmpty() {
return moduleNameList.isEmpty();
}
/**
* Returns the number of registered modules
*
* @return the number of registered modules
*/
public int size() {
return moduleCodeList.size();
}
}

View File

@ -94,6 +94,15 @@ public class VerilogCodeBuilder {
}
}
/**
* Return the HDL model associated with the builder
*
* @return the HDL model
*/
public HDLModel getModel() {
return model;
}
/**
* Return the HDL node associated with the signal.
*
@ -169,19 +178,12 @@ public class VerilogCodeBuilder {
* @param codeIr the code IR
*/
public void setCodeIrForSignal(String signalName, VIRNode codeIr) {
if (signalName.equals("S25")) {
System.out.println("S25");
}
if (codeIrMap.containsKey(signalName)) {
// The signal has been registered already, this can happen when
// there is a loop between components
VIRNode n = getSignalCodeIr(signalName);
if (n instanceof VEmptyStatement) {
VEmptyStatement dstmt = (VEmptyStatement) n;
} else if (n instanceof VDelegatedExpr) {
if (n instanceof VDelegatedExpr) {
VDelegatedExpr dexpr = (VDelegatedExpr) n;
statements.remove(dexpr.getStatement());
@ -205,7 +207,7 @@ public class VerilogCodeBuilder {
} else {
throw new RuntimeException("BUG in the machine: Invalid code IR node '" + codeIr.getClass().toString() + "'");
}
} else {
} else if (!(n instanceof VEmptyStatement)) {
throw new RuntimeException("BUG in the machine: Called twice to setCodeIR");
}
}

View File

@ -74,6 +74,17 @@ public class VerilogGenerator implements Closeable {
vlibrary = new VerilogLibrary(library);
}
/**
* If called the integration of clock dividers and so on is omitted.
* Mainly used for tests.
*
* @return this for chained calls
*/
public VerilogGenerator omitClockDividers() {
this.omitClockDividers = true;
return this;
}
/**
* Writes the file to the given stream
*
@ -91,7 +102,7 @@ public class VerilogGenerator implements Closeable {
BoardInterface board = BoardProvider.getInstance().getBoard(circuit);
ModelList modelList = new ModelList(library);
File f = circuit.getOrigin();
File f = out.getFile();
String moduleName = getBaseName(f.getName());
HDLModel model = new HDLModel(circuit, library, modelList).setName(moduleName);
ModuleList moduleSet = new ModuleList();
@ -120,7 +131,7 @@ public class VerilogGenerator implements Closeable {
out.print(m).println();
}
//nodesWritten += vhdlLibrary.finish(out);
nodesWritten += moduleSet.size();
File outFile = out.getFile();
if (board != null && outFile != null)
@ -370,6 +381,13 @@ public class VerilogGenerator implements Closeable {
return baseName;
}
/**
* @return the test bench creator
*/
public VerilogTestBenchCreator getTestBenches() {
return testBenches;
}
@Override
public void close() throws IOException {
out.close();

View File

@ -24,6 +24,7 @@ import de.neemann.digital.core.io.VDD;
import de.neemann.digital.core.memory.Register;
import de.neemann.digital.core.wiring.BitSelector;
import de.neemann.digital.core.wiring.Decoder;
import de.neemann.digital.core.wiring.Demultiplexer;
import de.neemann.digital.core.wiring.Driver;
import de.neemann.digital.core.wiring.DriverInvSel;
import de.neemann.digital.core.wiring.Multiplexer;
@ -42,6 +43,7 @@ import de.neemann.digital.hdl.verilog.lib.ComparatorVerilog;
import de.neemann.digital.hdl.verilog.lib.ConstVerilog;
import de.neemann.digital.hdl.verilog.lib.CustomElemVerilog;
import de.neemann.digital.hdl.verilog.lib.DecoderVerilog;
import de.neemann.digital.hdl.verilog.lib.DemultiplexerVerilog;
import de.neemann.digital.hdl.verilog.lib.DriverVerilog;
import de.neemann.digital.hdl.verilog.lib.MulVerilog;
import de.neemann.digital.hdl.verilog.lib.MultiplexerVerilog;
@ -93,6 +95,7 @@ public class VerilogLibrary {
put(Ground.DESCRIPTION, new ConstVerilog(Ground.DESCRIPTION));
put(PriorityEncoder.DESCRIPTION, new PriorityEncoderVerilog());
put(Multiplexer.DESCRIPTION, new MultiplexerVerilog());
put(Demultiplexer.DESCRIPTION, new DemultiplexerVerilog());
put(BitExtender.DESCRIPTION, new BitExtenderVerilog());
put(BitSelector.DESCRIPTION, new BitSelectorVerilog());
put(Add.DESCRIPTION, new AddVerilog(Add.DESCRIPTION, VOperator.ADD));

View File

@ -220,7 +220,7 @@ public class VerilogTestBenchCreator {
out.inc();
out.print("$display(\"")
.print(line).print(": ")
.print(p.getName()).print(": Assert failed. Expected %h, found %h\", ")
.print(p.getName()).print(": (assertion error). Expected %h, found %h\", ")
.print(valStr).print(", ").print(p.getName()).print(");").println();
out.println("$finish;");
out.dec().println("end");

View File

@ -41,9 +41,6 @@ public class CustomElemVerilog extends VerilogElement {
VIRNode irnode = vcBuilder.getSignalCodeIr(p.getSignal());
VExpr inExpr = irnode.resolveToExpr(vcBuilder);
if (p.getSignal().getName().equals("S25")) {
System.out.println("buildCodeIr");
}
signalMappings.add(new VInstanceMapping(p.getName(), inExpr));
}

View File

@ -0,0 +1,69 @@
/*
* 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.verilog.lib;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.core.wiring.Demultiplexer;
import de.neemann.digital.hdl.model.HDLException;
import de.neemann.digital.hdl.model.HDLNode;
import de.neemann.digital.hdl.model.Port;
import de.neemann.digital.hdl.model.Signal;
import de.neemann.digital.hdl.verilog.VerilogCodeBuilder;
import de.neemann.digital.hdl.verilog.ir.VIRNode;
import de.neemann.digital.hdl.verilog.ir.VOperator;
import de.neemann.digital.hdl.verilog.ir.VSignalDecl;
import de.neemann.digital.hdl.verilog.ir.expr.VBinaryExpr;
import de.neemann.digital.hdl.verilog.ir.expr.VConditionalExpr;
import de.neemann.digital.hdl.verilog.ir.expr.VConstExpr;
import de.neemann.digital.hdl.verilog.ir.expr.VExpr;
import de.neemann.digital.hdl.verilog.ir.stmt.VAssignStatement;
import de.neemann.digital.hdl.verilog.ir.stmt.VStatement;
import de.neemann.digital.hdl.verilog.ir.stmt.VStatementPlace;
import java.util.ArrayList;
/**
* Demultiplexer verilog element
*
* @author ideras
*/
public class DemultiplexerVerilog extends VerilogElement {
/**
* Initialize a new instance
*/
public DemultiplexerVerilog() {
super(Demultiplexer.DESCRIPTION);
}
@Override
public void buildCodeIr(VerilogCodeBuilder vcBuilder, HDLNode node) throws HDLException {
int dataBits = node.get(Keys.BITS);
int selBits = node.get(Keys.SELECTOR_BITS);
Signal selSignal = node.getPorts().getInputs().get(0).getSignal();
Signal inSignal = node.getPorts().getInputs().get(1).getSignal();
VIRNode selNode = vcBuilder.getSignalCodeIr(selSignal);
VIRNode inNode = vcBuilder.getSignalCodeIr(inSignal);
VExpr selExpr = selNode.resolveToExpr(vcBuilder);
VExpr inExpr = inNode.resolveToExpr(vcBuilder);
ArrayList<Port> outputPorts = node.getPorts().getOutputs();
for (int i = 0; i < outputPorts.size(); i++) {
Signal outSignal = outputPorts.get(i).getSignal();
VStatementPlace outPlace = new VStatementPlace(outSignal);
VStatement stmt = new VAssignStatement(outPlace,
new VConditionalExpr(
new VBinaryExpr(selExpr, new VConstExpr(selBits, i), VOperator.EQ),
inExpr,
new VConstExpr(dataBits, 0))
);
vcBuilder.registerSignalDecl(outSignal, VSignalDecl.Type.WIRE);
vcBuilder.setCodeIrForSignal(outSignal, stmt);
}
}
}

View File

@ -32,10 +32,36 @@ public class SplitterVerilog extends VerilogElement {
@Override
public void buildCodeIr(VerilogCodeBuilder vcBuilder, HDLNode node) throws HDLException {
if (node.getPorts().getInputs().size() == 1) {
VExpr inExpr;
if (node.getPorts().getInputs().size() != 1) {
ArrayList<VExpr> exprList = new ArrayList<>();
int totalBits = 0;
for (Port p : node.getPorts().getInputs()) {
VIRNode inCodeIrNode = vcBuilder.getSignalCodeIr(p.getSignal());
exprList.add(0, inCodeIrNode.resolveToExpr(vcBuilder));
totalBits += p.getBits();
}
Signal s = vcBuilder.getModel().createSignal().setBits(totalBits);
inExpr = new VConcatExpr(exprList).setSignal(s);
} else {
Signal inSignal = node.getPorts().getInputs().get(0).getSignal();
VIRNode inCodeIr = vcBuilder.getSignalCodeIr(inSignal);
VExpr inExpr = inCodeIr.resolveToExpr(vcBuilder);
inExpr = inCodeIr.resolveToExpr(vcBuilder);
}
if (node.getPorts().getOutputs().size() == 1
&& node.getPorts().getOutputs().get(0).getBits() == inExpr.getSignal().getBits()) {
Port outPort = node.getPorts().getOutputs().get(0);
Signal outSignal = outPort.getSignal();
if (outSignal != null) {
inExpr.setSignal(outSignal);
vcBuilder.setCodeIrForSignal(outSignal, inExpr);
}
} else {
inExpr = inExpr.resolveToIdExpr(vcBuilder);
for (Port p : node.getPorts().getOutputs()) {
@ -61,23 +87,6 @@ public class SplitterVerilog extends VerilogElement {
expr.setSignal(p.getSignal());
vcBuilder.setCodeIrForSignal(p.getSignal(), expr);
}
} else { // Assumes one output signal
Port outPort = node.getPorts().getOutputs().get(0);
Signal outSignal = outPort.getSignal();
ArrayList<VExpr> exprList = new ArrayList<>();
for (Port p : node.getPorts().getInputs()) {
VIRNode inCodeIrNode = vcBuilder.getSignalCodeIr(p.getSignal());
VExpr inExpr = inCodeIrNode.resolveToExpr(vcBuilder);
exprList.add(0, inExpr);
}
VExpr expr = new VConcatExpr(exprList);
expr.setSignal(outSignal);
vcBuilder.setCodeIrForSignal(outSignal, expr);
}
}
}

View File

@ -161,7 +161,14 @@ public class VerilogFileTemplate extends VerilogElement {
// ensures the usage of the correct default value
key = findKey(keyName);
}
genericMappings.add(new VGenericMapping(keyName, node.get(key).toString()));
Object keyVal = node.get(key);
String kvs;
if (keyVal instanceof Boolean) {
kvs = ((Boolean) keyVal)? "1" : "0";
} else {
kvs = keyVal.toString();
}
genericMappings.add(new VGenericMapping(keyName, kvs));
}
}

View File

@ -1103,6 +1103,9 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
<string name="key_ghdlPath">GHDL</string>
<string name="key_ghdlPath_tt">Pfad der ausführbaren ghdl-Datei. Nur wichtig, wenn ghdl zur Interpretation von
VHDL-Code verwendet werden soll.</string>
<string name="key_iverilogPath">GHDL</string>
<string name="key_iverilogPath_tt">Pfad der ausführbaren iverilog-Datei. Nur wichtig, wenn ghdl zur Interpretation von
Verilog-Code verwendet werden soll.</string>
<string name="mod_insertWire">Leitung eingefügt.</string>
<string name="mod_insertCopied">Aus Zwischenablage eingefügt.</string>

View File

@ -1093,6 +1093,9 @@
<string name="key_ghdlPath">GHDL</string>
<string name="key_ghdlPath_tt">Path to the executable ghdl file. Only necessary if you want to use ghdl to simulate
components defined with vhdl.</string>
<string name="key_iverilogPath">iverilog</string>
<string name="key_iverilogPath_tt">Path to the executable iverilog file. Only necessary if you want to use iverilog to simulate
components defined with verilog.</string>
<string name="mod_insertWire">Inserted wire.</string>
<string name="mod_insertCopied">Insert from clipboard.</string>

View File

@ -12,8 +12,8 @@ generics[1] = "AddrBits";
input PORT_C, // Clock signal
input PORT_ld,
input [(AddrBits-1):0] PORT_1A,
input [(Bits-1):0] PORT_1D_in,
input [(AddrBits-1):0] PORT_2A,
input [(Bits-1):0] PORT_1Din,
input PORT_str,
output [(Bits-1):0] PORT_1D,
output [(Bits-1):0] PORT_2D
@ -25,7 +25,7 @@ generics[1] = "AddrBits";
always @ (posedge PORT_C) begin
if (PORT_str)
memory[PORT_1A] <= PORT_1D_in;
memory[PORT_1A] <= PORT_1Din;
end
endmodule

View File

@ -10,7 +10,7 @@ generics[1] = "AddrBits";
)
(
input [(AddrBits-1):0] PORT_A,
input [(Bits-1):0] PORT_D_in,
input [(Bits-1):0] PORT_Din,
input PORT_str,
input PORT_C,
input PORT_ld,
@ -22,6 +22,6 @@ generics[1] = "AddrBits";
always @ (posedge PORT_C) begin
if (PORT_str)
memory[PORT_A] <= PORT_D_in;
memory[PORT_A] <= PORT_Din;
end
endmodule

View File

@ -0,0 +1,13 @@
<?
generics[0] = "invertOutput";
?>
module <?= elem.name ?>
#(
parameter invertOutput = 0
)
(
output PORT_Reset
);
// ToDo: how to deal with the reset pin?
assign PORT_Reset = invertOutput;
endmodule

View File

@ -0,0 +1,194 @@
/*
* 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.verilog;
import de.neemann.digital.core.ExceptionWithOrigin;
import de.neemann.digital.core.NodeException;
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.hdl.model.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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
/**
* Test verilog files in icarus verilog simulator
*/
public class TestInSimulator extends TestCase {
private static final Logger LOGGER = LoggerFactory.getLogger(TestInSimulator.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(27, tested);
assertEquals(tested, 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(28, 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;
}
}
private void checkVerilogExport(File file) throws PinException, NodeException, ElementNotFoundException, IOException, FileScanner.SkipAllException, HDLException {
ToBreakRunner br = new ToBreakRunner(file);
File dir = Files.createTempDirectory("digital_vhdl_" + getTime() + "_").toFile();
try {
File sourceFile = new File(dir, file.getName().replace('.', '_') + ".v");
CodePrinter out = new CodePrinter(sourceFile);
try (VerilogGenerator vgen = new VerilogGenerator(br.getLibrary(), out)) {
vgen.omitClockDividers().export(br.getCircuit());
VerilogTestBenchCreator tb = vgen.getTestBenches();
out.close();
runIVerilog(sourceFile, tb.getTestFileWritten());
}
} finally {
br.close();
ProcessStarter.removeFolder(dir);
}
}
private void runIVerilog(File sourceFile, ArrayList<File> 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;
}
}
}