clock integration is mostly working

This commit is contained in:
hneemann 2018-03-25 11:57:06 +02:00
parent 0b491154c1
commit 89a4b43cf9
21 changed files with 843 additions and 73 deletions

View File

@ -24,6 +24,8 @@ import de.neemann.digital.draw.model.InverterConfig;
import de.neemann.digital.draw.model.Net;
import de.neemann.digital.draw.model.NetList;
import de.neemann.digital.gui.components.data.DummyElement;
import de.neemann.digital.hdl.model2.clock.ClockInfo;
import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator;
import de.neemann.digital.hdl.model2.expression.ExprNot;
import de.neemann.digital.hdl.model2.expression.ExprVar;
import de.neemann.digital.hdl.printer.CodePrinter;
@ -57,6 +59,20 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
* @throws NodeException NodeException
*/
HDLCircuit(Circuit circuit, String elementName, HDLModel c) throws PinException, HDLException, NodeException {
this(circuit, elementName, c, null);
}
/**
* Creates a new instance
*
* @param circuit the circuit
* @param elementName the name of the circuit
* @param c the context to create the circuits
* @throws PinException PinException
* @throws HDLException HDLException
* @throws NodeException NodeException
*/
HDLCircuit(Circuit circuit, String elementName, HDLModel c, HDLClockIntegrator clockIntegrator) throws PinException, HDLException, NodeException {
this.elementName = elementName;
if (elementName.toLowerCase().endsWith(".dig"))
@ -71,20 +87,26 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
nets = new HashMap<>();
listOfNets = new ArrayList<>();
netList = new NetList(circuit);
ArrayList<ClockInfo> clocks = new ArrayList<>();
try {
for (VisualElement v : circuit.getElements()) {
if (v.equalsDescription(In.DESCRIPTION) || v.equalsDescription(Clock.DESCRIPTION))
addInput(new HDLPort(
if (v.equalsDescription(In.DESCRIPTION) || v.equalsDescription(Clock.DESCRIPTION)) {
final HDLPort port = new HDLPort(
v.getElementAttributes().getCleanLabel(),
getNetOfPin(v.getPins().get(0)),
HDLPort.Direction.OUT,
HDLPort.Direction.OUT, // from inside the node this is an output because it defines a value
v.getElementAttributes().getBits())
.setPinNumber(v.getElementAttributes().get(Keys.PINNUMBER)));
else if (v.equalsDescription(Out.DESCRIPTION))
.setPinNumber(v.getElementAttributes().get(Keys.PINNUMBER));
addInput(port);
if (v.equalsDescription(Clock.DESCRIPTION))
clocks.add(new ClockInfo(port, v.getElementAttributes().get(Keys.FREQUENCY)));
} else if (v.equalsDescription(Out.DESCRIPTION))
addOutput(new HDLPort(
v.getElementAttributes().getCleanLabel(),
getNetOfPin(v.getPins().get(0)),
HDLPort.Direction.IN,
HDLPort.Direction.IN, // from inside the node this is an input because it reads the value to output
v.getElementAttributes().getBits())
.setPinNumber(v.getElementAttributes().get(Keys.PINNUMBER)));
else if (v.equalsDescription(Splitter.DESCRIPTION))
@ -99,6 +121,9 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
netList = null;
nets = null;
if (clockIntegrator != null && !clocks.isEmpty())
clockIntegrator.integrateClocks(this, clocks);
for (HDLNet n : listOfNets)
n.fixBits();
@ -147,10 +172,10 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
HDLNodeSplitterOneToMany oneToMany = new HDLNodeSplitterOneToMany(node, outputSplit);
manyToOne.getOutputs().clear();
manyToOne.addOutput(left);
manyToOne.addPort(left);
oneToMany.getInputs().clear();
oneToMany.addInput(right);
oneToMany.addPort(right);
nodes.add(manyToOne);
nodes.add(oneToMany);
@ -164,8 +189,8 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
HDLNet inNet = p.getNet();
inNet.remove(p);
n.addInput(new HDLPort(Not.DESCRIPTION.getInputDescription(attr).get(0).getName(), inNet, HDLPort.Direction.IN, p.getBits()));
n.addOutput(new HDLPort(Not.DESCRIPTION.getOutputDescriptions(attr).get(0).getName(), outNet, HDLPort.Direction.OUT, p.getBits()));
n.addPort(new HDLPort(Not.DESCRIPTION.getInputDescription(attr).get(0).getName(), inNet, HDLPort.Direction.IN, p.getBits()));
n.addPort(new HDLPort(Not.DESCRIPTION.getOutputDescriptions(attr).get(0).getName(), outNet, HDLPort.Direction.OUT, p.getBits()));
p.setNet(outNet);
node.replaceNet(inNet, outNet);
@ -269,7 +294,7 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
}
/**
* Merges logcal operations if possible
* Merges logical operations if possible
*
* @return this for chained calls
*/
@ -398,6 +423,27 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
return hdlEntityName;
}
/**
* Integrates a clock node.
*
* @param clock the clock port
* @param clockNode the new clock node
* @throws HDLException HDLException
*/
public void integrateClockNode(HDLPort clock, HDLNodeBuildIn clockNode) throws HDLException {
HDLNet outNet = clock.getNet();
HDLNet inNet = new HDLNet(null);
outNet.resetOutput();
clock.setNet(inNet);
listOfNets.add(inNet);
clockNode
.addPort(new HDLPort("cout", outNet, HDLPort.Direction.OUT, 1))
.addPort(new HDLPort("cin", inNet, HDLPort.Direction.IN, 1));
nodes.add(clockNode);
}
/**
* The net naming algorithm
*/

View File

@ -20,6 +20,7 @@ import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator;
import de.neemann.digital.hdl.model2.expression.*;
import java.util.ArrayList;
@ -144,9 +145,9 @@ public class HDLModel implements Iterable<HDLCircuit> {
for (Pin p : v.getPins()) {
HDLNet net = c.getNetOfPin(p);
if (p.getDirection().equals(PinDescription.Direction.input))
node.addInput(new HDLPort(p.getName(), net, HDLPort.Direction.IN, 0));
node.addPort(new HDLPort(p.getName(), net, HDLPort.Direction.IN, 0));
else
node.addOutput(new HDLPort(p.getName(), net, HDLPort.Direction.OUT, node.getBits(p.getName())));
node.addPort(new HDLPort(p.getName(), net, HDLPort.Direction.OUT, node.getBits(p.getName())));
}
return node;
}
@ -160,14 +161,15 @@ public class HDLModel implements Iterable<HDLCircuit> {
/**
* Analyses the given circuit
*
* @param circuit the circuit
* @param circuit the circuit
* @param clockIntegrator the clock integrator. Meybe null
* @return this for chained calls
* @throws PinException PinException
* @throws HDLException HDLException
* @throws NodeException NodeException
*/
public HDLModel create(Circuit circuit) throws PinException, HDLException, NodeException {
main = new HDLCircuit(circuit, "main", this);
public HDLModel create(Circuit circuit, HDLClockIntegrator clockIntegrator) throws PinException, HDLException, NodeException {
main = new HDLCircuit(circuit, "main", this, clockIntegrator);
circuitMap.put(circuit, main);
return this;
}
@ -234,7 +236,7 @@ public class HDLModel implements Iterable<HDLCircuit> {
/**
* The bit provider interface
*/
interface BitProvider {
public interface BitProvider {
/**
* Returns the number of bits of the signal with the given name
*

View File

@ -86,7 +86,7 @@ public class HDLNet implements Printable {
@Override
public void print(CodePrinter out) throws IOException {
out.print(name).print(":").print(output.getBits());
out.print(name).print("->").print(inputs.size());
}
/**
@ -140,6 +140,14 @@ public class HDLNet implements Printable {
return output.getBits();
}
/**
* resets the output of this net
*/
public void resetOutput() {
output = null;
name = null;
}
/**
* Renames this net.
*
@ -148,4 +156,5 @@ public class HDLNet implements Printable {
public void rename(HDLModel.Renaming renaming) {
name = renaming.checkName(name);
}
}

View File

@ -37,12 +37,18 @@ public class HDLNode {
outputs = new ArrayList<>();
}
void addInput(HDLPort port) {
inputs.add(port);
}
void addOutput(HDLPort port) {
outputs.add(port);
/**
* Adds a port to this node
*
* @param port the port to add
* @return this for chained calls
*/
public HDLNode addPort(HDLPort port) {
if (port.getDirection().equals(HDLPort.Direction.OUT))
outputs.add(port);
else
inputs.add(port);
return this;
}
@Override
@ -111,12 +117,6 @@ public class HDLNode {
} else
out.print(", ");
p.print(out);
if (p.getNet() == null)
out.print(" is not used");
else {
out.print(" is ");
p.getNet().print(out);
}
}
if (first)
out.print("(");

View File

@ -18,7 +18,7 @@ public class HDLNodeBuildIn extends HDLNode {
* @param elementAttributes the attributes
* @param bitProvider the bit provider which provides the outputs bit width
*/
HDLNodeBuildIn(String elementName, ElementAttributes elementAttributes, HDLModel.BitProvider bitProvider) {
public HDLNodeBuildIn(String elementName, ElementAttributes elementAttributes, HDLModel.BitProvider bitProvider) {
super(elementName, elementAttributes, bitProvider);
}
}

View File

@ -20,10 +20,14 @@ public class HDLPort implements Printable {
public enum Direction {
/**
* input
* Caution: a circuits output components port has type IN because it reads the value to
* output, seen from inside the node.
*/
IN,
/**
* output
* Caution: a circuits input components port has type OUT because it defines a value,
* seen from inside the node.
*/
OUT
}
@ -81,7 +85,8 @@ public class HDLPort implements Printable {
if (this.net != null)
this.net.remove(this);
this.net = net;
net.addPort(this);
if (net != null)
net.addPort(this);
}
/**
@ -129,6 +134,15 @@ public class HDLPort implements Printable {
@Override
public void print(CodePrinter out) throws IOException {
out.print(name).print(":").print(bits);
if (net != null) {
if (net.getOutput() == this)
out.print(" defines (");
else
out.print(" reads (");
net.print(out);
out.print(")");
} else
out.print(" is not used");
}
/**

View File

@ -58,14 +58,14 @@ class OperationMerger {
circuit.removeNet(obsoleteNet);
node.addOutput(host.getOutput());
node.addPort(host.getOutput());
for (HDLPort i : host.getInputs())
if (i.getNet() != obsoleteNet)
node.addInput(i);
node.addPort(i);
for (HDLPort i : include.getInputs())
if (!node.hasInput(i))
node.addInput(i);
node.addPort(i);
return node;
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2018 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.hdl.model2.clock;
import de.neemann.digital.hdl.model2.HDLPort;
/**
* A clock information bean.
*/
public class ClockInfo {
private HDLPort clockPort;
private int frequency;
/**
* Creates a new instance.
*
* @param clockPort the clock port
* @param frequency the clock frequency
*/
public ClockInfo(HDLPort clockPort, int frequency) {
this.clockPort = clockPort;
this.frequency = frequency;
}
/**
* @return the clock port
*/
public HDLPort getClockPort() {
return clockPort;
}
/**
* @return the clocks frequency
*/
public int getFrequency() {
return frequency;
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2017 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.hdl.model2.clock;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Key;
import de.neemann.digital.hdl.model2.*;
import de.neemann.digital.hdl.model2.HDLCircuit;
import java.util.ArrayList;
/**
* Creates a simple generic clock divider.
* Inserts a simple clock divider to match the frequency given in the model.
* Not suited for real world applications because a logical signal is used
* to clock the entities.
* You should use the FPGA-dependant clock resources to generate a clock signal.
*/
public class ClockIntegratorGeneric implements HDLClockIntegrator {
private static final Key<Integer> COUNTER_KEY = new Key<>("maxCounter", 0);
private double periodns;
/**
* Creates a new instance
*
* @param periodns the clock period in ns
*/
public ClockIntegratorGeneric(double periodns) {
this.periodns = periodns;
}
@Override
public void integrateClocks(HDLCircuit circuit, ArrayList<ClockInfo> clocks) throws HDLException {
for (ClockInfo ci : clocks) {
int freq = ci.getFrequency();
int counter = (int) (1000000000.0 / (periodns * 2 * freq));
if (counter >= 2) {
HDLNodeBuildIn node = new HDLNodeBuildIn("simpleClockDivider",
new ElementAttributes().set(COUNTER_KEY, counter),
name -> 1);
circuit.integrateClockNode(ci.getClockPort(), node);
}
}
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2017 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.hdl.model2.clock;
import de.neemann.digital.hdl.model2.HDLCircuit;
import de.neemann.digital.hdl.model2.HDLException;
import java.util.ArrayList;
/**
* Used to utilize the board specific clock resources
*/
public interface HDLClockIntegrator {
/**
* Modifies the circuit to integrate the clock sources
*
* @param circuit the circuit
* @param clocks the clock input ports
* @throws HDLException HDLException
*/
void integrateClocks(HDLCircuit circuit, ArrayList<ClockInfo> clocks) throws HDLException;
}

View File

@ -0,0 +1,10 @@
/*
* 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.
*/
/**
* Classes to be used to modify the clock source of a circuit
*/
package de.neemann.digital.hdl.model2.clock;

View File

@ -34,7 +34,7 @@ public class ExprVar implements Expression {
@Override
public void print(CodePrinter out) throws IOException {
net.print(out);
out.print(net.getName());
}
@Override

View File

@ -11,13 +11,18 @@ 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.HDLModel;
import de.neemann.digital.hdl.model2.HDLException;
import de.neemann.digital.hdl.model2.HDLModel;
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.BoardInterface;
import de.neemann.digital.hdl.vhdl2.boards.BoardProvider;
import de.neemann.digital.lang.Lang;
import java.io.*;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
/**
@ -28,6 +33,7 @@ public class VHDLGenerator implements Closeable {
private final ElementLibrary library;
private final CodePrinter out;
private ArrayList<File> testBenches;
private boolean useClockIntegration = true;
/**
* Creates a new exporter
@ -58,8 +64,15 @@ public class VHDLGenerator implements Closeable {
* @throws IOException IOException
*/
public VHDLGenerator export(Circuit circuit) throws IOException {
BoardInterface board = BoardProvider.getInstance().getBoard(circuit);
HDLClockIntegrator clockIntegrator = null;
if (board != null && useClockIntegration)
clockIntegrator = board.getClockIntegrator();
try {
HDLModel model = new HDLModel(library).create(circuit);
HDLModel model = new HDLModel(library).create(circuit, clockIntegrator);
for (HDLCircuit c : model)
c.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
@ -87,11 +100,15 @@ public class VHDLGenerator implements Closeable {
new VHDLCreator(out).printHDLCircuit(model.getMain());
File outFile = out.getFile();
if (outFile != null)
if (outFile != null) {
testBenches = new VHDLTestBenchCreator(circuit, model)
.write(outFile)
.getTestFileWritten();
if (board != null)
board.writeFiles(outFile.getParentFile(), model);
}
return this;
} catch (PinException | NodeException | HDLException | HGSEvalException e) {
throw new IOException(Lang.get("err_vhdlExporting"), e);
@ -114,4 +131,15 @@ public class VHDLGenerator implements Closeable {
public void close() throws IOException {
out.close();
}
/**
* Disables the clock integration.
* Used only for the tests.
*
* @return this for chained calls
*/
public VHDLGenerator disableClockIntegration() {
useClockIntegration = false;
return this;
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2017 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.hdl.vhdl2.boards;
import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator;
import de.neemann.digital.hdl.model2.HDLModel;
import java.io.File;
import java.io.IOException;
/**
* Interface to write the additional files
*/
public interface BoardInterface {
/**
* Writes additional files
*
* @param path the target path
* @param model the model
* @throws IOException IOException
*/
void writeFiles(File path, HDLModel model) throws IOException;
/**
* @return returns the board specific clock integrator
*/
HDLClockIntegrator getClockIntegrator();
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2017 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.hdl.vhdl2.boards;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.gui.components.data.DummyElement;
/**
* Provides additional information for a specific board
*/
public final class BoardProvider {
private static final class InstanceHolder {
static final BoardProvider INSTANCE = new BoardProvider();
}
/**
* @return the BoardProvider instance
*/
public static BoardProvider getInstance() {
return InstanceHolder.INSTANCE;
}
private BoardProvider() {
}
/**
* Returns a spscific board
*
* @param circuit the circuit
* @return the board or null
*/
public BoardInterface getBoard(Circuit circuit) {
String board = null;
for (VisualElement element : circuit.getElements()) {
if (element.equalsDescription(DummyElement.TEXTDESCRIPTION)) {
String text = element.getElementAttributes().get(Keys.DESCRIPTION).toLowerCase();
if (text.startsWith("board:")) {
board = text.substring(6).trim();
}
}
}
if (board == null)
return null;
if (board.equals("basys3"))
return new Vivado("LVCMOS33",
"W5",
10,
new ClockIntegratorARTIX7(10),
"xc7a35ticpg236-1L");
return null;
}
}

View File

@ -0,0 +1,198 @@
/*
* Copyright (c) 2017 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.hdl.vhdl2.boards;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Key;
import de.neemann.digital.hdl.model2.*;
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;
/**
* Implements clocking on Artix 7
*/
public class ClockIntegratorARTIX7 implements HDLClockIntegrator {
private static final int F_PFD_MIN_MHZ = 10;
private static final int F_PFD_MAX_MHZ = 450;
private static final int F_VCO_MIN_MHZ = 600;
private static final int F_VCO_MAX_MHZ = 1200;
private static final int MAX_CLOCK_DIVIDE = 128;
private final double clkInPeriod;
/**
* Creates a new instance
*
* @param clkInPeriod clock in period in ns
*/
ClockIntegratorARTIX7(double clkInPeriod) {
this.clkInPeriod = clkInPeriod;
}
@Override
public void integrateClocks(HDLCircuit model, ArrayList<ClockInfo> clocks) throws HDLException {
if (clocks.size() > 1)
throw new HDLException("up to now only a single clock is supported on ARTIX-7");
Params p = new Parameters(clocks.get(0).getFrequency(), clkInPeriod).getBest();
if (p == null)
new ClockIntegratorGeneric(clkInPeriod).integrateClocks(model, clocks);
else
insertMMCMClock(model, p, clocks.get(0).getClockPort());
}
private void insertMMCMClock(HDLCircuit model, Params p, HDLPort clock) throws HDLException {
ElementAttributes attr = new ElementAttributes()
.set(new Key<>("cascading", 0), p.isCascading())
.set(new Key<>("D_PARAM", 0), p.d)
.set(new Key<>("M_PARAM", 0), p.m)
.set(new Key<>("DIV_PARAM", 0), p.divider)
.set(new Key<>("DIV4_PARAM", 0), p.divider4)
.set(new Key<>("PERIOD_PARAM", 0.0), clkInPeriod);
model.integrateClockNode(clock, new HDLNodeBuildIn("MMCME2_BASE", attr, name -> 1));
}
static final class Parameters {
private final Params best;
Parameters(int frequency, double clkInPeriod) {
double fInMHz = 1000 / clkInPeriod;
double targetFreqInMHz = ((double) frequency) / 1000 / 1000;
int dMin = (int) Math.ceil(fInMHz / F_PFD_MAX_MHZ);
int dMax = (int) Math.floor(fInMHz / F_PFD_MIN_MHZ);
int mMin = (int) Math.ceil(F_VCO_MIN_MHZ / fInMHz * dMin);
int mMax = (int) Math.floor(F_VCO_MIN_MHZ / fInMHz * dMax);
int mIdeal = (int) Math.floor(dMin * F_VCO_MAX_MHZ / fInMHz);
Params best = null;
for (int m = mMin; m <= mMax; m++)
for (int d = dMin; d <= dMax; d++) {
double fVco = fInMHz * m / d;
double fpdf = fVco / m;
boolean valid = (F_VCO_MIN_MHZ <= fVco) && (fVco <= F_VCO_MAX_MHZ)
&& (F_PFD_MIN_MHZ <= fpdf) && (fpdf <= F_PFD_MAX_MHZ);
if (valid) {
int divider = (int) (fVco / targetFreqInMHz);
if (divider >= 1 && divider <= MAX_CLOCK_DIVIDE) {
double f = fVco / divider;
double error = (F_VCO_MAX_MHZ - fVco) + Math.abs(f - targetFreqInMHz) * 10 + (Math.abs(m - mIdeal) * 10);
if (best == null || best.error > error)
best = new Params(m, d, divider, f, error);
} else {
if (divider > MAX_CLOCK_DIVIDE && divider <= MAX_CLOCK_DIVIDE * MAX_CLOCK_DIVIDE) {
int divider4 = 0;
int divider6 = 0;
int bestErr = Integer.MAX_VALUE;
for (int d6 = 1; d6 <= MAX_CLOCK_DIVIDE; d6++)
for (int d4 = 1; d4 <= MAX_CLOCK_DIVIDE; d4++) {
int dd = d4 * d6;
int err = Math.abs(divider - dd);
if (err < bestErr) {
bestErr = err;
divider4 = d4;
divider6 = d6;
}
}
if (divider4 > 0 && divider6 > 0) {
double f = fVco / divider6 / divider4;
double error = (F_VCO_MAX_MHZ - fVco) + Math.abs(f - targetFreqInMHz) * 10 + (Math.abs(m - mIdeal) * 10);
if (best == null || best.error > error)
best = new Params(m, d, divider6, divider4, f, error);
}
}
}
}
}
this.best = best;
}
Params getBest() {
return best;
}
}
static final class Params {
private final int m;
private final int d;
private final int divider;
private final double error;
private final double f;
private final int divider4;
private Params(int m, int d, int divider, double f, double error) {
this.m = m;
this.d = d;
this.divider = divider;
this.divider4 = 0;
this.f = f;
this.error = error;
}
private Params(int m, int d, int divider, int divider4, double f, double error) {
this.m = m;
this.d = d;
this.divider = divider;
this.divider4 = divider4;
this.f = f;
this.error = error;
}
@Override
public String toString() {
return "Params{"
+ "m=" + m
+ ", d=" + d
+ ", divider=" + divider
+ ", div4=" + divider4
+ ", error=" + error
+ ", f=" + f
+ '}';
}
public int getD() {
return d;
}
public int getM() {
return m;
}
public int getDivider() {
return divider;
}
public boolean isCascading() {
return divider4 != 0;
}
public int getDivider4() {
return divider4;
}
}
}

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2017 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.hdl.vhdl2.boards;
import de.neemann.digital.analyse.SplitPinString;
import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator;
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.lang.Lang;
import java.io.*;
/**
* Creates the needed vivado files.
* Up to now only the constraints file containing the pin assignments is created
*/
public class Vivado implements BoardInterface {
private final String pinIoType;
private final String clockPin;
private final int periodns;
private final HDLClockIntegrator clockIntegrator;
private final String device;
/**
* Creates a new instance
*
* @param pinIoType the pin output type
* @param clockPin the pin the clock is connected to
* @param periodns the clock period in nano seconds
* @param clockIntegrator the clock integrator to use
* @param device the xilinx device code
*/
public Vivado(String pinIoType, String clockPin, int periodns, HDLClockIntegrator clockIntegrator, String device) {
this.pinIoType = pinIoType;
this.clockPin = clockPin;
this.periodns = periodns;
this.clockIntegrator = clockIntegrator;
this.device = device;
}
@Override
public void writeFiles(File path, HDLModel model) throws IOException {
String projectName = path.getName();
if (projectName.endsWith(".vhdl"))
projectName = projectName.substring(0, projectName.length() - 5);
File constraints = new File(path.getParentFile(), projectName.replace('.', '_') + "_constraints.xdc");
try (CodePrinter out = new CodePrinter(new FileOutputStream(constraints))) {
writeConstraints(out, model);
}
createVivadoProject(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());
if (p.getPinNumber().equals(clockPin))
out
.print("create_clock -add -name sys_clk_pin -period ")
.print(periodns)
.print(" -waveform {0 5} [get_ports ")
.print(p.getName())
.println("]");
} 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("set_property CFGBVS VCCO [current_design]");
out.println("set_property CONFIG_VOLTAGE 3.3 [current_design]");
}
private 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));
out.print("set_property PACKAGE_PIN ").print(pinNumber).print(" [get_ports ").print(name).println("]");
out.print("set_property IOSTANDARD ").print(pinIoType).print(" [get_ports ").print(name).println("]");
}
@Override
public HDLClockIntegrator getClockIntegrator() {
return clockIntegrator;
}
private void createVivadoProject(File path, String projectName, File vhdl, File constraints) throws IOException {
String projectDir = projectName + "_vivado";
File projectPath = new File(path, projectDir);
// don't overwrite existing projects!
if (!projectPath.exists()) {
if (projectPath.mkdirs()) {
File projectFile = new File(projectPath, projectName + ".xpr");
try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(projectFile), "utf-8"))) {
writeVivadoProject(w, projectFile, vhdl, constraints);
}
}
}
}
private void writeVivadoProject(BufferedWriter w, File project, File vhdl, File constraints) throws IOException {
w.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!-- Created by Digital -->\n"
+ "\n"
+ "<Project Version=\"7\" Minor=\"20\" Path=\"" + project.getPath() + "\">\n"
+ " <DefaultLaunch Dir=\"$PRUNDIR\"/>\n"
+ " <Configuration>\n"
+ " <Option Name=\"Part\" Val=\"" + device + "\"/>\n"
+ " </Configuration>\n"
+ " <FileSets Version=\"1\" Minor=\"31\">\n"
+ " <FileSet Name=\"sources_1\" Type=\"DesignSrcs\" RelSrcDir=\"$PSRCDIR/sources_1\">\n"
+ " <Filter Type=\"Srcs\"/>\n"
+ " <File Path=\"$PPRDIR/../" + vhdl.getName() + "\">\n"
+ " <FileInfo>\n"
+ " <Attr Name=\"UsedIn\" Val=\"synthesis\"/>\n"
+ " <Attr Name=\"UsedIn\" Val=\"simulation\"/>\n"
+ " </FileInfo>\n"
+ " </File>\n"
+ " <Config>\n"
+ " <Option Name=\"DesignMode\" Val=\"RTL\"/>\n"
+ " <Option Name=\"TopModule\" Val=\"main\"/>\n"
+ " <Option Name=\"TopAutoSet\" Val=\"TRUE\"/>\n"
+ " </Config>\n"
+ " </FileSet>\n"
+ " <FileSet Name=\"constrs_1\" Type=\"Constrs\" RelSrcDir=\"$PSRCDIR/constrs_1\">\n"
+ " <Filter Type=\"Constrs\"/>\n"
+ " <File Path=\"$PPRDIR/../" + constraints.getName() + "\">\n"
+ " <FileInfo>\n"
+ " <Attr Name=\"UsedIn\" Val=\"synthesis\"/>\n"
+ " <Attr Name=\"UsedIn\" Val=\"implementation\"/>\n"
+ " </FileInfo>\n"
+ " </File>\n"
+ " <Config>\n"
+ " <Option Name=\"ConstrsType\" Val=\"XDC\"/>\n"
+ " </Config>\n"
+ " </FileSet>\n"
+ " </FileSets>\n"
+ "</Project>");
}
}

View File

@ -0,0 +1,12 @@
/*
* Copyright (c) 2017 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
/**
* Support fpr different boards.
* The classes in this package are able to create the files neede
* to run a vhdl file on a board.
*/
package de.neemann.digital.hdl.vhdl2.boards;

View File

@ -10,6 +10,8 @@ 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.draw.shapes.ShapeFactory;
import de.neemann.digital.hdl.model2.clock.ClockIntegratorGeneric;
import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator;
import de.neemann.digital.hdl.printer.CodePrinterStr;
import de.neemann.digital.integration.Resources;
import junit.framework.TestCase;
@ -19,55 +21,55 @@ import java.io.IOException;
public class HDLCircuitTest extends TestCase {
HDLCircuit getCircuit(String filename) throws IOException, PinException, HDLException, NodeException {
HDLCircuit getCircuit(String filename, HDLClockIntegrator ci) throws IOException, PinException, HDLException, NodeException {
File file = new File(Resources.getRoot(), filename);
ElementLibrary library = new ElementLibrary();
library.setRootFilePath(file.getParentFile());
ShapeFactory shapeFactory = new ShapeFactory(library);
Circuit c = Circuit.loadCircuit(file, shapeFactory);
return new HDLCircuit(c, "main", new HDLModel(library));
return new HDLCircuit(c, "main", new HDLModel(library), ci);
}
public void testSimple() throws IOException, PinException, HDLException, NodeException {
HDLCircuit hdl = getCircuit("dig/hdl/model2/comb.dig");
HDLCircuit hdl = getCircuit("dig/hdl/model2/comb.dig", null);
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
CodePrinterStr cp = new CodePrinterStr();
hdl.print(cp);
assertEquals("circuit main\n" +
" in(A:1, B:1, C:1)\n" +
" out(X:1, Y:1, Z:1, Aident:1)\n" +
" sig(Y_temp:1, s0:1, Z_temp:1, s1:1)\n" +
" in(A:1 defines (A->3), B:1 defines (B->2), C:1 defines (C->2))\n" +
" out(X:1 reads (X->1), Y:1 reads (Y_temp->2), Z:1 reads (Z_temp->2), Aident:1 reads (A->3))\n" +
" sig(Y_temp->2, s0->1, Z_temp->2, s1->1)\n" +
"\n" +
" node Const\n" +
" in()\n" +
" out(out:1 is s1:1)\n" +
" s1:1 := 1:1\n" +
" out(out:1 defines (s1->1))\n" +
" s1->1 := 1:1\n" +
" node Not\n" +
" in(in:1 is A:1)\n" +
" out(out:1 is Z_temp:1)\n" +
" Z_temp:1 := NOT A:1\n" +
" in(in:1 reads (A->3))\n" +
" out(out:1 defines (Z_temp->2))\n" +
" Z_temp->2 := NOT A\n" +
" node merged expression\n" +
" in(In_1:1 is B:1, in:1 is C:1)\n" +
" out(out:1 is Y_temp:1)\n" +
" Y_temp:1 := (B:1 OR NOT C:1)\n" +
" in(In_1:1 reads (B->2), in:1 reads (C->2))\n" +
" out(out:1 defines (Y_temp->2))\n" +
" Y_temp->2 := (B OR NOT C)\n" +
" node merged expression\n" +
" in(In_5:1 is Y_temp:1, In_1:1 is A:1, In_2:1 is C:1, In_1:1 is Z_temp:1, In_1:1 is B:1)\n" +
" out(out:1 is s0:1)\n" +
" s0:1 := ((A:1 OR C:1) AND (Z_temp:1 OR C:1) AND 1:1 AND NOT (B:1 OR C:1) AND Y_temp:1)\n" +
" in(In_5:1 reads (Y_temp->2), In_1:1 reads (A->3), In_2:1 reads (C->2), In_1:1 reads (Z_temp->2), In_1:1 reads (B->2))\n" +
" out(out:1 defines (s0->1))\n" +
" s0->1 := ((A OR C) AND (Z_temp OR C) AND 1:1 AND NOT (B OR C) AND Y_temp)\n" +
" node D_FF\n" +
" in(D:1 is s0:1, C:1 is s1:1)\n" +
" out(Q:1 is X:1, ~Q:1 is not used)\n" +
" in(D:1 reads (s0->1), C:1 reads (s1->1))\n" +
" out(Q:1 defines (X->1), ~Q:1 is not used)\n" +
"\n" +
" Y:1 := Y_temp:1\n" +
" Z:1 := Z_temp:1\n" +
" Aident:1 := A:1\n" +
" Y:1 reads (Y_temp->2) := Y_temp->2\n" +
" Z:1 reads (Z_temp->2) := Z_temp->2\n" +
" Aident:1 reads (A->3) := A->3\n" +
"end circuit main\n", cp.toString());
}
public void testSimple2() throws IOException, PinException, HDLException, NodeException {
HDLCircuit hdl = getCircuit("dig/hdl/model2/comb2.dig");
HDLCircuit hdl = getCircuit("dig/hdl/model2/comb2.dig", null);
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
CodePrinterStr cp = new CodePrinterStr();
@ -86,7 +88,7 @@ public class HDLCircuitTest extends TestCase {
}
public void testInputInvert() throws IOException, PinException, HDLException, NodeException {
HDLCircuit hdl = getCircuit("dig/hdl/model2/inputInvert.dig");
HDLCircuit hdl = getCircuit("dig/hdl/model2/inputInvert.dig", null);
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
CodePrinterStr cp = new CodePrinterStr();
@ -105,7 +107,7 @@ public class HDLCircuitTest extends TestCase {
}
public void testInputInvert2() throws IOException, PinException, HDLException, NodeException {
HDLCircuit hdl = getCircuit("dig/hdl/model2/inputInvert2.dig");
HDLCircuit hdl = getCircuit("dig/hdl/model2/inputInvert2.dig", null);
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
CodePrinterStr cp = new CodePrinterStr();
@ -124,7 +126,7 @@ public class HDLCircuitTest extends TestCase {
}
public void testSplitter() throws IOException, PinException, HDLException, NodeException {
HDLCircuit hdl = getCircuit("dig/hdl/model2/splitter.dig");
HDLCircuit hdl = getCircuit("dig/hdl/model2/splitter.dig", null);
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
CodePrinterStr cp = new CodePrinterStr();
@ -148,7 +150,7 @@ public class HDLCircuitTest extends TestCase {
}
public void testSplitter2() throws IOException, PinException, HDLException, NodeException {
HDLCircuit hdl = getCircuit("dig/hdl/model2/splitter2.dig");
HDLCircuit hdl = getCircuit("dig/hdl/model2/splitter2.dig", null);
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
CodePrinterStr cp = new CodePrinterStr();
@ -172,4 +174,25 @@ public class HDLCircuitTest extends TestCase {
"end circuit main\n", cp.toString());
}
public void testClock() throws IOException, PinException, HDLException, NodeException {
HDLCircuit hdl = getCircuit("dig/hdl/model2/clock.dig", new ClockIntegratorGeneric(10));
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
CodePrinterStr cp = new CodePrinterStr();
hdl.print(cp);
assertEquals("circuit main\n" +
" in(A:1 defines (A->1), C:1 defines (C->1))\n" +
" out(X:1 reads (X->1))\n" +
" sig(s0->1)\n" +
"\n" +
" node simpleClockDivider\n" +
" in(cin:1 reads (C->1))\n" +
" out(cout:1 defines (s0->1))\n" +
" node D_FF\n" +
" in(D:1 reads (A->1), C:1 reads (s0->1))\n" +
" out(Q:1 defines (X->1), ~Q:1 is not used)\n" +
"\n" +
"end circuit main\n", cp.toString());
}
}

View File

@ -9,16 +9,12 @@ 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.Circuit;
import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.Settings;
import de.neemann.digital.hdl.hgs.HGSEvalException;
import de.neemann.digital.hdl.model.HDLException;
import de.neemann.digital.hdl.printer.CodePrinter;
import de.neemann.digital.hdl.printer.CodePrinterStr;
import de.neemann.digital.integration.FileScanner;
import de.neemann.digital.integration.Resources;
import de.neemann.digital.integration.ToBreakRunner;
@ -63,7 +59,7 @@ public class VHDLGeneratorTest extends TestCase {
File examples = new File(Resources.getRoot(), "/dig/hdl");
try {
int tested = new FileScanner(this::checkVHDLExport).noOutput().scan(examples);
assertEquals(35, tested);
assertEquals(36, tested);
} catch (FileScanner.SkipAllException e) {
// if ghdl is not installed its also ok
}
@ -122,7 +118,7 @@ public class VHDLGeneratorTest extends TestCase {
.replace('-', '_')+ ".vhdl");
CodePrinter out = new CodePrinter(vhdlFile);
try (VHDLGenerator vhdl = new VHDLGenerator(br.getLibrary(), out)) {
vhdl.export(br.getCircuit());
vhdl.disableClockIntegration().export(br.getCircuit());
ArrayList<File> testFiles = vhdl.getTestBenches();
out.close();
runGHDL(vhdlFile, testFiles);

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<circuit>
<version>1</version>
<attributes/>
<visualElements>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>X</string>
</entry>
<entry>
<string>Inputs</string>
<int>1</int>
</entry>
</elementAttributes>
<pos x="360" y="140"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>A</string>
</entry>
</elementAttributes>
<pos x="240" y="140"/>
</visualElement>
<visualElement>
<elementName>D_FF</elementName>
<elementAttributes/>
<pos x="280" y="140"/>
</visualElement>
<visualElement>
<elementName>Clock</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>C</string>
</entry>
</elementAttributes>
<pos x="240" y="180"/>
</visualElement>
</visualElements>
<wires>
<wire>
<p1 x="260" y="160"/>
<p2 x="280" y="160"/>
</wire>
<wire>
<p1 x="240" y="180"/>
<p2 x="260" y="180"/>
</wire>
<wire>
<p1 x="340" y="140"/>
<p2 x="360" y="140"/>
</wire>
<wire>
<p1 x="240" y="140"/>
<p2 x="280" y="140"/>
</wire>
<wire>
<p1 x="260" y="160"/>
<p2 x="260" y="180"/>
</wire>
</wires>
<measurementOrdering/>
</circuit>