mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-17 08:55:05 -04:00
clock integration is mostly working
This commit is contained in:
parent
0b491154c1
commit
89a4b43cf9
@ -24,6 +24,8 @@ import de.neemann.digital.draw.model.InverterConfig;
|
|||||||
import de.neemann.digital.draw.model.Net;
|
import de.neemann.digital.draw.model.Net;
|
||||||
import de.neemann.digital.draw.model.NetList;
|
import de.neemann.digital.draw.model.NetList;
|
||||||
import de.neemann.digital.gui.components.data.DummyElement;
|
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.ExprNot;
|
||||||
import de.neemann.digital.hdl.model2.expression.ExprVar;
|
import de.neemann.digital.hdl.model2.expression.ExprVar;
|
||||||
import de.neemann.digital.hdl.printer.CodePrinter;
|
import de.neemann.digital.hdl.printer.CodePrinter;
|
||||||
@ -57,6 +59,20 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
|
|||||||
* @throws NodeException NodeException
|
* @throws NodeException NodeException
|
||||||
*/
|
*/
|
||||||
HDLCircuit(Circuit circuit, String elementName, HDLModel c) throws PinException, HDLException, 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;
|
this.elementName = elementName;
|
||||||
|
|
||||||
if (elementName.toLowerCase().endsWith(".dig"))
|
if (elementName.toLowerCase().endsWith(".dig"))
|
||||||
@ -71,20 +87,26 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
|
|||||||
nets = new HashMap<>();
|
nets = new HashMap<>();
|
||||||
listOfNets = new ArrayList<>();
|
listOfNets = new ArrayList<>();
|
||||||
netList = new NetList(circuit);
|
netList = new NetList(circuit);
|
||||||
|
|
||||||
|
ArrayList<ClockInfo> clocks = new ArrayList<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (VisualElement v : circuit.getElements()) {
|
for (VisualElement v : circuit.getElements()) {
|
||||||
if (v.equalsDescription(In.DESCRIPTION) || v.equalsDescription(Clock.DESCRIPTION))
|
if (v.equalsDescription(In.DESCRIPTION) || v.equalsDescription(Clock.DESCRIPTION)) {
|
||||||
addInput(new HDLPort(
|
final HDLPort port = new HDLPort(
|
||||||
v.getElementAttributes().getCleanLabel(),
|
v.getElementAttributes().getCleanLabel(),
|
||||||
getNetOfPin(v.getPins().get(0)),
|
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())
|
v.getElementAttributes().getBits())
|
||||||
.setPinNumber(v.getElementAttributes().get(Keys.PINNUMBER)));
|
.setPinNumber(v.getElementAttributes().get(Keys.PINNUMBER));
|
||||||
else if (v.equalsDescription(Out.DESCRIPTION))
|
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(
|
addOutput(new HDLPort(
|
||||||
v.getElementAttributes().getCleanLabel(),
|
v.getElementAttributes().getCleanLabel(),
|
||||||
getNetOfPin(v.getPins().get(0)),
|
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())
|
v.getElementAttributes().getBits())
|
||||||
.setPinNumber(v.getElementAttributes().get(Keys.PINNUMBER)));
|
.setPinNumber(v.getElementAttributes().get(Keys.PINNUMBER)));
|
||||||
else if (v.equalsDescription(Splitter.DESCRIPTION))
|
else if (v.equalsDescription(Splitter.DESCRIPTION))
|
||||||
@ -99,6 +121,9 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
|
|||||||
netList = null;
|
netList = null;
|
||||||
nets = null;
|
nets = null;
|
||||||
|
|
||||||
|
if (clockIntegrator != null && !clocks.isEmpty())
|
||||||
|
clockIntegrator.integrateClocks(this, clocks);
|
||||||
|
|
||||||
for (HDLNet n : listOfNets)
|
for (HDLNet n : listOfNets)
|
||||||
n.fixBits();
|
n.fixBits();
|
||||||
|
|
||||||
@ -147,10 +172,10 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
|
|||||||
HDLNodeSplitterOneToMany oneToMany = new HDLNodeSplitterOneToMany(node, outputSplit);
|
HDLNodeSplitterOneToMany oneToMany = new HDLNodeSplitterOneToMany(node, outputSplit);
|
||||||
|
|
||||||
manyToOne.getOutputs().clear();
|
manyToOne.getOutputs().clear();
|
||||||
manyToOne.addOutput(left);
|
manyToOne.addPort(left);
|
||||||
|
|
||||||
oneToMany.getInputs().clear();
|
oneToMany.getInputs().clear();
|
||||||
oneToMany.addInput(right);
|
oneToMany.addPort(right);
|
||||||
|
|
||||||
nodes.add(manyToOne);
|
nodes.add(manyToOne);
|
||||||
nodes.add(oneToMany);
|
nodes.add(oneToMany);
|
||||||
@ -164,8 +189,8 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
|
|||||||
HDLNet inNet = p.getNet();
|
HDLNet inNet = p.getNet();
|
||||||
inNet.remove(p);
|
inNet.remove(p);
|
||||||
|
|
||||||
n.addInput(new HDLPort(Not.DESCRIPTION.getInputDescription(attr).get(0).getName(), inNet, HDLPort.Direction.IN, p.getBits()));
|
n.addPort(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.getOutputDescriptions(attr).get(0).getName(), outNet, HDLPort.Direction.OUT, p.getBits()));
|
||||||
|
|
||||||
p.setNet(outNet);
|
p.setNet(outNet);
|
||||||
node.replaceNet(inNet, 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
|
* @return this for chained calls
|
||||||
*/
|
*/
|
||||||
@ -398,6 +423,27 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
|
|||||||
return hdlEntityName;
|
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
|
* The net naming algorithm
|
||||||
*/
|
*/
|
||||||
|
@ -20,6 +20,7 @@ import de.neemann.digital.draw.elements.PinException;
|
|||||||
import de.neemann.digital.draw.elements.VisualElement;
|
import de.neemann.digital.draw.elements.VisualElement;
|
||||||
import de.neemann.digital.draw.library.ElementLibrary;
|
import de.neemann.digital.draw.library.ElementLibrary;
|
||||||
import de.neemann.digital.draw.library.ElementNotFoundException;
|
import de.neemann.digital.draw.library.ElementNotFoundException;
|
||||||
|
import de.neemann.digital.hdl.model2.clock.HDLClockIntegrator;
|
||||||
import de.neemann.digital.hdl.model2.expression.*;
|
import de.neemann.digital.hdl.model2.expression.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -144,9 +145,9 @@ public class HDLModel implements Iterable<HDLCircuit> {
|
|||||||
for (Pin p : v.getPins()) {
|
for (Pin p : v.getPins()) {
|
||||||
HDLNet net = c.getNetOfPin(p);
|
HDLNet net = c.getNetOfPin(p);
|
||||||
if (p.getDirection().equals(PinDescription.Direction.input))
|
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
|
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;
|
return node;
|
||||||
}
|
}
|
||||||
@ -161,13 +162,14 @@ public class HDLModel implements Iterable<HDLCircuit> {
|
|||||||
* Analyses the given circuit
|
* Analyses the given circuit
|
||||||
*
|
*
|
||||||
* @param circuit the circuit
|
* @param circuit the circuit
|
||||||
|
* @param clockIntegrator the clock integrator. Meybe null
|
||||||
* @return this for chained calls
|
* @return this for chained calls
|
||||||
* @throws PinException PinException
|
* @throws PinException PinException
|
||||||
* @throws HDLException HDLException
|
* @throws HDLException HDLException
|
||||||
* @throws NodeException NodeException
|
* @throws NodeException NodeException
|
||||||
*/
|
*/
|
||||||
public HDLModel create(Circuit circuit) throws PinException, HDLException, NodeException {
|
public HDLModel create(Circuit circuit, HDLClockIntegrator clockIntegrator) throws PinException, HDLException, NodeException {
|
||||||
main = new HDLCircuit(circuit, "main", this);
|
main = new HDLCircuit(circuit, "main", this, clockIntegrator);
|
||||||
circuitMap.put(circuit, main);
|
circuitMap.put(circuit, main);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -234,7 +236,7 @@ public class HDLModel implements Iterable<HDLCircuit> {
|
|||||||
/**
|
/**
|
||||||
* The bit provider interface
|
* The bit provider interface
|
||||||
*/
|
*/
|
||||||
interface BitProvider {
|
public interface BitProvider {
|
||||||
/**
|
/**
|
||||||
* Returns the number of bits of the signal with the given name
|
* Returns the number of bits of the signal with the given name
|
||||||
*
|
*
|
||||||
|
@ -86,7 +86,7 @@ public class HDLNet implements Printable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void print(CodePrinter out) throws IOException {
|
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();
|
return output.getBits();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resets the output of this net
|
||||||
|
*/
|
||||||
|
public void resetOutput() {
|
||||||
|
output = null;
|
||||||
|
name = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renames this net.
|
* Renames this net.
|
||||||
*
|
*
|
||||||
@ -148,4 +156,5 @@ public class HDLNet implements Printable {
|
|||||||
public void rename(HDLModel.Renaming renaming) {
|
public void rename(HDLModel.Renaming renaming) {
|
||||||
name = renaming.checkName(name);
|
name = renaming.checkName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,18 @@ public class HDLNode {
|
|||||||
outputs = new ArrayList<>();
|
outputs = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addInput(HDLPort port) {
|
/**
|
||||||
inputs.add(port);
|
* Adds a port to this node
|
||||||
}
|
*
|
||||||
|
* @param port the port to add
|
||||||
void addOutput(HDLPort port) {
|
* @return this for chained calls
|
||||||
|
*/
|
||||||
|
public HDLNode addPort(HDLPort port) {
|
||||||
|
if (port.getDirection().equals(HDLPort.Direction.OUT))
|
||||||
outputs.add(port);
|
outputs.add(port);
|
||||||
|
else
|
||||||
|
inputs.add(port);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -111,12 +117,6 @@ public class HDLNode {
|
|||||||
} else
|
} else
|
||||||
out.print(", ");
|
out.print(", ");
|
||||||
p.print(out);
|
p.print(out);
|
||||||
if (p.getNet() == null)
|
|
||||||
out.print(" is not used");
|
|
||||||
else {
|
|
||||||
out.print(" is ");
|
|
||||||
p.getNet().print(out);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (first)
|
if (first)
|
||||||
out.print("(");
|
out.print("(");
|
||||||
|
@ -18,7 +18,7 @@ public class HDLNodeBuildIn extends HDLNode {
|
|||||||
* @param elementAttributes the attributes
|
* @param elementAttributes the attributes
|
||||||
* @param bitProvider the bit provider which provides the outputs bit width
|
* @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);
|
super(elementName, elementAttributes, bitProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,14 @@ public class HDLPort implements Printable {
|
|||||||
public enum Direction {
|
public enum Direction {
|
||||||
/**
|
/**
|
||||||
* input
|
* input
|
||||||
|
* Caution: a circuits output components port has type IN because it reads the value to
|
||||||
|
* output, seen from inside the node.
|
||||||
*/
|
*/
|
||||||
IN,
|
IN,
|
||||||
/**
|
/**
|
||||||
* output
|
* output
|
||||||
|
* Caution: a circuits input components port has type OUT because it defines a value,
|
||||||
|
* seen from inside the node.
|
||||||
*/
|
*/
|
||||||
OUT
|
OUT
|
||||||
}
|
}
|
||||||
@ -81,6 +85,7 @@ public class HDLPort implements Printable {
|
|||||||
if (this.net != null)
|
if (this.net != null)
|
||||||
this.net.remove(this);
|
this.net.remove(this);
|
||||||
this.net = net;
|
this.net = net;
|
||||||
|
if (net != null)
|
||||||
net.addPort(this);
|
net.addPort(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +134,15 @@ public class HDLPort implements Printable {
|
|||||||
@Override
|
@Override
|
||||||
public void print(CodePrinter out) throws IOException {
|
public void print(CodePrinter out) throws IOException {
|
||||||
out.print(name).print(":").print(bits);
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,14 +58,14 @@ class OperationMerger {
|
|||||||
|
|
||||||
circuit.removeNet(obsoleteNet);
|
circuit.removeNet(obsoleteNet);
|
||||||
|
|
||||||
node.addOutput(host.getOutput());
|
node.addPort(host.getOutput());
|
||||||
for (HDLPort i : host.getInputs())
|
for (HDLPort i : host.getInputs())
|
||||||
if (i.getNet() != obsoleteNet)
|
if (i.getNet() != obsoleteNet)
|
||||||
node.addInput(i);
|
node.addPort(i);
|
||||||
|
|
||||||
for (HDLPort i : include.getInputs())
|
for (HDLPort i : include.getInputs())
|
||||||
if (!node.hasInput(i))
|
if (!node.hasInput(i))
|
||||||
node.addInput(i);
|
node.addPort(i);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
@ -34,7 +34,7 @@ public class ExprVar implements Expression {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void print(CodePrinter out) throws IOException {
|
public void print(CodePrinter out) throws IOException {
|
||||||
net.print(out);
|
out.print(net.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -11,13 +11,18 @@ import de.neemann.digital.draw.elements.PinException;
|
|||||||
import de.neemann.digital.draw.library.ElementLibrary;
|
import de.neemann.digital.draw.library.ElementLibrary;
|
||||||
import de.neemann.digital.hdl.hgs.HGSEvalException;
|
import de.neemann.digital.hdl.hgs.HGSEvalException;
|
||||||
import de.neemann.digital.hdl.model2.HDLCircuit;
|
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.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.CodePrinter;
|
||||||
import de.neemann.digital.hdl.printer.CodePrinterStr;
|
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 de.neemann.digital.lang.Lang;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.Closeable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,6 +33,7 @@ public class VHDLGenerator implements Closeable {
|
|||||||
private final ElementLibrary library;
|
private final ElementLibrary library;
|
||||||
private final CodePrinter out;
|
private final CodePrinter out;
|
||||||
private ArrayList<File> testBenches;
|
private ArrayList<File> testBenches;
|
||||||
|
private boolean useClockIntegration = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new exporter
|
* Creates a new exporter
|
||||||
@ -58,8 +64,15 @@ public class VHDLGenerator implements Closeable {
|
|||||||
* @throws IOException IOException
|
* @throws IOException IOException
|
||||||
*/
|
*/
|
||||||
public VHDLGenerator export(Circuit circuit) throws 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 {
|
try {
|
||||||
HDLModel model = new HDLModel(library).create(circuit);
|
HDLModel model = new HDLModel(library).create(circuit, clockIntegrator);
|
||||||
for (HDLCircuit c : model)
|
for (HDLCircuit c : model)
|
||||||
c.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
|
c.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
|
||||||
|
|
||||||
@ -87,11 +100,15 @@ public class VHDLGenerator implements Closeable {
|
|||||||
new VHDLCreator(out).printHDLCircuit(model.getMain());
|
new VHDLCreator(out).printHDLCircuit(model.getMain());
|
||||||
|
|
||||||
File outFile = out.getFile();
|
File outFile = out.getFile();
|
||||||
if (outFile != null)
|
if (outFile != null) {
|
||||||
testBenches = new VHDLTestBenchCreator(circuit, model)
|
testBenches = new VHDLTestBenchCreator(circuit, model)
|
||||||
.write(outFile)
|
.write(outFile)
|
||||||
.getTestFileWritten();
|
.getTestFileWritten();
|
||||||
|
|
||||||
|
if (board != null)
|
||||||
|
board.writeFiles(outFile.getParentFile(), model);
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
} catch (PinException | NodeException | HDLException | HGSEvalException e) {
|
} catch (PinException | NodeException | HDLException | HGSEvalException e) {
|
||||||
throw new IOException(Lang.get("err_vhdlExporting"), e);
|
throw new IOException(Lang.get("err_vhdlExporting"), e);
|
||||||
@ -114,4 +131,15 @@ public class VHDLGenerator implements Closeable {
|
|||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the clock integration.
|
||||||
|
* Used only for the tests.
|
||||||
|
*
|
||||||
|
* @return this for chained calls
|
||||||
|
*/
|
||||||
|
public VHDLGenerator disableClockIntegration() {
|
||||||
|
useClockIntegration = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
150
src/main/java/de/neemann/digital/hdl/vhdl2/boards/Vivado.java
Normal file
150
src/main/java/de/neemann/digital/hdl/vhdl2/boards/Vivado.java
Normal 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>");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
@ -10,6 +10,8 @@ import de.neemann.digital.draw.elements.Circuit;
|
|||||||
import de.neemann.digital.draw.elements.PinException;
|
import de.neemann.digital.draw.elements.PinException;
|
||||||
import de.neemann.digital.draw.library.ElementLibrary;
|
import de.neemann.digital.draw.library.ElementLibrary;
|
||||||
import de.neemann.digital.draw.shapes.ShapeFactory;
|
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.hdl.printer.CodePrinterStr;
|
||||||
import de.neemann.digital.integration.Resources;
|
import de.neemann.digital.integration.Resources;
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
@ -19,55 +21,55 @@ import java.io.IOException;
|
|||||||
|
|
||||||
public class HDLCircuitTest extends TestCase {
|
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);
|
File file = new File(Resources.getRoot(), filename);
|
||||||
ElementLibrary library = new ElementLibrary();
|
ElementLibrary library = new ElementLibrary();
|
||||||
library.setRootFilePath(file.getParentFile());
|
library.setRootFilePath(file.getParentFile());
|
||||||
ShapeFactory shapeFactory = new ShapeFactory(library);
|
ShapeFactory shapeFactory = new ShapeFactory(library);
|
||||||
Circuit c = Circuit.loadCircuit(file, shapeFactory);
|
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 {
|
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());
|
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
|
||||||
|
|
||||||
CodePrinterStr cp = new CodePrinterStr();
|
CodePrinterStr cp = new CodePrinterStr();
|
||||||
hdl.print(cp);
|
hdl.print(cp);
|
||||||
assertEquals("circuit main\n" +
|
assertEquals("circuit main\n" +
|
||||||
" in(A:1, B:1, C:1)\n" +
|
" in(A:1 defines (A->3), B:1 defines (B->2), C:1 defines (C->2))\n" +
|
||||||
" out(X:1, Y:1, Z:1, Aident:1)\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:1, s0:1, Z_temp:1, s1:1)\n" +
|
" sig(Y_temp->2, s0->1, Z_temp->2, s1->1)\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
" node Const\n" +
|
" node Const\n" +
|
||||||
" in()\n" +
|
" in()\n" +
|
||||||
" out(out:1 is s1:1)\n" +
|
" out(out:1 defines (s1->1))\n" +
|
||||||
" s1:1 := 1:1\n" +
|
" s1->1 := 1:1\n" +
|
||||||
" node Not\n" +
|
" node Not\n" +
|
||||||
" in(in:1 is A:1)\n" +
|
" in(in:1 reads (A->3))\n" +
|
||||||
" out(out:1 is Z_temp:1)\n" +
|
" out(out:1 defines (Z_temp->2))\n" +
|
||||||
" Z_temp:1 := NOT A:1\n" +
|
" Z_temp->2 := NOT A\n" +
|
||||||
" node merged expression\n" +
|
" node merged expression\n" +
|
||||||
" in(In_1:1 is B:1, in:1 is C:1)\n" +
|
" in(In_1:1 reads (B->2), in:1 reads (C->2))\n" +
|
||||||
" out(out:1 is Y_temp:1)\n" +
|
" out(out:1 defines (Y_temp->2))\n" +
|
||||||
" Y_temp:1 := (B:1 OR NOT C:1)\n" +
|
" Y_temp->2 := (B OR NOT C)\n" +
|
||||||
" node merged expression\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" +
|
" 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 is s0:1)\n" +
|
" out(out:1 defines (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" +
|
" 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" +
|
" node D_FF\n" +
|
||||||
" in(D:1 is s0:1, C:1 is s1:1)\n" +
|
" in(D:1 reads (s0->1), C:1 reads (s1->1))\n" +
|
||||||
" out(Q:1 is X:1, ~Q:1 is not used)\n" +
|
" out(Q:1 defines (X->1), ~Q:1 is not used)\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
" Y:1 := Y_temp:1\n" +
|
" Y:1 reads (Y_temp->2) := Y_temp->2\n" +
|
||||||
" Z:1 := Z_temp:1\n" +
|
" Z:1 reads (Z_temp->2) := Z_temp->2\n" +
|
||||||
" Aident:1 := A:1\n" +
|
" Aident:1 reads (A->3) := A->3\n" +
|
||||||
"end circuit main\n", cp.toString());
|
"end circuit main\n", cp.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSimple2() throws IOException, PinException, HDLException, NodeException {
|
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());
|
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
|
||||||
|
|
||||||
CodePrinterStr cp = new CodePrinterStr();
|
CodePrinterStr cp = new CodePrinterStr();
|
||||||
@ -86,7 +88,7 @@ public class HDLCircuitTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testInputInvert() throws IOException, PinException, HDLException, NodeException {
|
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());
|
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
|
||||||
|
|
||||||
CodePrinterStr cp = new CodePrinterStr();
|
CodePrinterStr cp = new CodePrinterStr();
|
||||||
@ -105,7 +107,7 @@ public class HDLCircuitTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testInputInvert2() throws IOException, PinException, HDLException, NodeException {
|
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());
|
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
|
||||||
|
|
||||||
CodePrinterStr cp = new CodePrinterStr();
|
CodePrinterStr cp = new CodePrinterStr();
|
||||||
@ -124,7 +126,7 @@ public class HDLCircuitTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testSplitter() throws IOException, PinException, HDLException, NodeException {
|
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());
|
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
|
||||||
|
|
||||||
CodePrinterStr cp = new CodePrinterStr();
|
CodePrinterStr cp = new CodePrinterStr();
|
||||||
@ -148,7 +150,7 @@ public class HDLCircuitTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testSplitter2() throws IOException, PinException, HDLException, NodeException {
|
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());
|
hdl.mergeOperations().nameNets(new HDLCircuit.SimpleNetNaming());
|
||||||
|
|
||||||
CodePrinterStr cp = new CodePrinterStr();
|
CodePrinterStr cp = new CodePrinterStr();
|
||||||
@ -172,4 +174,25 @@ public class HDLCircuitTest extends TestCase {
|
|||||||
"end circuit main\n", cp.toString());
|
"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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -9,16 +9,12 @@ import de.neemann.digital.core.ExceptionWithOrigin;
|
|||||||
import de.neemann.digital.core.NodeException;
|
import de.neemann.digital.core.NodeException;
|
||||||
import de.neemann.digital.core.element.Keys;
|
import de.neemann.digital.core.element.Keys;
|
||||||
import de.neemann.digital.core.extern.ProcessStarter;
|
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.elements.PinException;
|
||||||
import de.neemann.digital.draw.library.ElementLibrary;
|
|
||||||
import de.neemann.digital.draw.library.ElementNotFoundException;
|
import de.neemann.digital.draw.library.ElementNotFoundException;
|
||||||
import de.neemann.digital.draw.shapes.ShapeFactory;
|
|
||||||
import de.neemann.digital.gui.Settings;
|
import de.neemann.digital.gui.Settings;
|
||||||
import de.neemann.digital.hdl.hgs.HGSEvalException;
|
import de.neemann.digital.hdl.hgs.HGSEvalException;
|
||||||
import de.neemann.digital.hdl.model.HDLException;
|
import de.neemann.digital.hdl.model.HDLException;
|
||||||
import de.neemann.digital.hdl.printer.CodePrinter;
|
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.FileScanner;
|
||||||
import de.neemann.digital.integration.Resources;
|
import de.neemann.digital.integration.Resources;
|
||||||
import de.neemann.digital.integration.ToBreakRunner;
|
import de.neemann.digital.integration.ToBreakRunner;
|
||||||
@ -63,7 +59,7 @@ public class VHDLGeneratorTest extends TestCase {
|
|||||||
File examples = new File(Resources.getRoot(), "/dig/hdl");
|
File examples = new File(Resources.getRoot(), "/dig/hdl");
|
||||||
try {
|
try {
|
||||||
int tested = new FileScanner(this::checkVHDLExport).noOutput().scan(examples);
|
int tested = new FileScanner(this::checkVHDLExport).noOutput().scan(examples);
|
||||||
assertEquals(35, tested);
|
assertEquals(36, tested);
|
||||||
} catch (FileScanner.SkipAllException e) {
|
} catch (FileScanner.SkipAllException e) {
|
||||||
// if ghdl is not installed its also ok
|
// if ghdl is not installed its also ok
|
||||||
}
|
}
|
||||||
@ -122,7 +118,7 @@ public class VHDLGeneratorTest extends TestCase {
|
|||||||
.replace('-', '_')+ ".vhdl");
|
.replace('-', '_')+ ".vhdl");
|
||||||
CodePrinter out = new CodePrinter(vhdlFile);
|
CodePrinter out = new CodePrinter(vhdlFile);
|
||||||
try (VHDLGenerator vhdl = new VHDLGenerator(br.getLibrary(), out)) {
|
try (VHDLGenerator vhdl = new VHDLGenerator(br.getLibrary(), out)) {
|
||||||
vhdl.export(br.getCircuit());
|
vhdl.disableClockIntegration().export(br.getCircuit());
|
||||||
ArrayList<File> testFiles = vhdl.getTestBenches();
|
ArrayList<File> testFiles = vhdl.getTestBenches();
|
||||||
out.close();
|
out.close();
|
||||||
runGHDL(vhdlFile, testFiles);
|
runGHDL(vhdlFile, testFiles);
|
||||||
|
69
src/test/resources/dig/hdl/model2/clock.dig
Normal file
69
src/test/resources/dig/hdl/model2/clock.dig
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user