diff --git a/src/main/java/de/neemann/digital/builder/tt2/TT2Exporter.java b/src/main/java/de/neemann/digital/builder/tt2/TT2Exporter.java new file mode 100644 index 000000000..429a73242 --- /dev/null +++ b/src/main/java/de/neemann/digital/builder/tt2/TT2Exporter.java @@ -0,0 +1,353 @@ +package de.neemann.digital.builder.tt2; + +import de.neemann.digital.analyse.expression.Expression; +import de.neemann.digital.analyse.expression.Not; +import de.neemann.digital.analyse.expression.Operation; +import de.neemann.digital.analyse.expression.Variable; +import de.neemann.digital.builder.*; +import de.neemann.digital.builder.jedec.FuseMapFillerException; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.*; + +/** + * Exporter for the tt2 format needed by the ATF15xx fitters from ATMEL. + * Created by hneemann on 03.03.17. + */ +public class TT2Exporter implements ExpressionExporter { + private BuilderCollector builder; + private PinMap pinMap; + private int clockPin; + private String name; + private String device; + private OutputStreamWriter writer; + private HashMap varIndexMap; + private HashMap outIndexMap; + private TreeMap termMap; + private ArrayList inputs; + private ArrayList outputs; + + /** + * Creates a new instance + */ + public TT2Exporter() { + builder = new BuilderCollector(); + pinMap = new PinMap(); + device = "f1502plcc44"; + name = "unknown"; + clockPin = 43; + } + + @Override + public BuilderInterface getBuilder() { + return builder; + } + + /** + * Sets the pin connected to the clock of the ff + * + * @param clockPin the pin number + * @return this for chained calls + */ + public TT2Exporter setClockPin(int clockPin) { + this.clockPin = clockPin; + return this; + } + + /** + * Sets the project name used + * + * @param name the project name + * @return this for chained calls + */ + public TT2Exporter setName(String name) { + this.name = name; + return this; + } + + /** + * Sets the device name used + * + * @param device the device name + * @return this for chained calls + */ + public TT2Exporter setDevice(String device) { + this.device = device; + return this; + } + + @Override + public PinMap getPinMapping() { + return pinMap; + } + + @Override + public void writeTo(OutputStream out) throws FuseMapFillerException, IOException, PinMapException { + writeTo(new OutputStreamWriter(out, "ISO-8859-1")); + } + + private void writeTo(OutputStreamWriter writer) throws IOException, FuseMapFillerException, PinMapException { + createProductTerms(); + + this.writer = writer; + line("#$ TOOL CUPL"); + line("# Berkeley PLA format generated using Digital"); + line("#$ TITLE " + name); + line("#$ MODULE " + name); + line("#$ JEDECFILE " + name); + line("#$ DEVICE " + device); + line("#$ PINS " + getPins()); + line(".i " + inputs.size()); + line(".o " + outputs.size()); + line(".type f"); + line(".ilb " + strList(inputs)); + line(".ob " + strList(outputs)); + line(".phase " + getPhase()); + + line(".p " + termMap.size()); + for (Map.Entry e : termMap.entrySet()) + line(e.getKey() + " " + e.getValue()); + + line(".e"); + writer.close(); + } + + private void createProductTerms() throws FuseMapFillerException { + + inputs = builder.getInputs(); + varIndexMap = new HashMap<>(); + int i = 0; + for (String name : inputs) { + varIndexMap.put(name, i); + i++; + } + + ProdInput clkIn = null; + if (!builder.getRegistered().isEmpty()) { + int clk = inputs.size(); + inputs.add("CLK"); + varIndexMap.put("CLK", i); + i++; + for (String reg : builder.getRegistered().keySet()) { + inputs.add(reg + ".Q"); + varIndexMap.put(reg, i); + i++; + } + + clkIn = new ProdInput(inputs.size()); + clkIn.set(clk, 1); + } + + ArrayList clkInList = new ArrayList(); + outputs = new ArrayList<>(); + outIndexMap = new HashMap<>(); + i = 0; + for (String name : builder.getOutputs()) { + if (builder.getRegistered().containsKey(name)) { + outIndexMap.put(name + ".REG", i++); + outputs.add(name + ".REG"); + outIndexMap.put(name + ".AR", i++); + outputs.add(name + ".AR"); + clkInList.add(i); + outIndexMap.put(name + ".C", i++); + outputs.add(name + ".C"); + } else { + outIndexMap.put(name, i++); + outputs.add(name); + } + } + + termMap = new TreeMap<>(); + if (!builder.getRegistered().isEmpty()) { // connect clock and ar + StateSet clk = new StateSet(outputs.size()); + for (int c : clkInList) + clk.set(c, 1); + termMap.put(clkIn, clk); + + // AR + termMap.put(new ProdInput(inputs.size()), new StateSet(outputs.size())); + } + + + for (Map.Entry e : builder.getCombinatorial().entrySet()) + addExpression(e.getKey(), e.getValue()); + + for (Map.Entry e : builder.getRegistered().entrySet()) + addExpression(e.getKey() + ".REG", e.getValue()); + } + + private void addExpression(String name, Expression expression) throws FuseMapFillerException { + if (expression instanceof Operation.Or) { + Operation.Or or = (Operation.Or) expression; + for (Expression e : or.getExpressions()) + addProdFor(name, e); + } else + addProdFor(name, expression); + } + + private void addProdFor(String name, Expression e) throws FuseMapFillerException { + ProdInput pt = new ProdInput(getInputCount()); + if (e instanceof Operation.And) { + Operation.And and = (Operation.And) e; + for (Expression z : and.getExpressions()) + pt.add(z); + } else + pt.add(e); + + StateSet o = termMap.computeIfAbsent(pt, k -> new StateSet(getOutputCount())); + o.set(getOutNum(name)); + } + + private String strList(ArrayList pins) { + StringBuilder sb = new StringBuilder(); + for (String p : pins) { + if (sb.length() > 0) + sb.append(" "); + sb.append(p); + } + return sb.toString(); + } + + private void line(String s) throws IOException { + writer.write(s); + writer.write("\r\n"); + } + + private int getInputCount() { + return varIndexMap.size(); + } + + private int getVarNum(String identifier) throws FuseMapFillerException { + final Integer i = varIndexMap.get(identifier); + if (i == null) + throw new FuseMapFillerException("ident " + identifier + " not found!"); + return i; + } + + private int getOutputCount() { + return outIndexMap.size(); + } + + private int getOutNum(String identifier) { + return outIndexMap.get(identifier); + } + + private String getPhase() { + StringBuilder sb = new StringBuilder(getOutputCount()); + for (int i = 0; i < getOutputCount(); i++) + sb.append("1"); + return sb.toString(); + } + + private String getPins() throws PinMapException { + StringBuilder sb = new StringBuilder(); + int numPins = builder.getInputs().size() + builder.getOutputs().size(); + if (!builder.getRegistered().isEmpty()) + numPins++; + + sb.append(numPins); + + + for (String i : builder.getInputs()) { + int p = pinMap.getInputFor(i); + sb.append(" ").append(i).append("+:").append(p); + } + + if (!builder.getRegistered().isEmpty()) + sb.append(" CLK+:").append(clockPin); + + for (String o : builder.getOutputs()) { + int p = pinMap.getInputFor(o); + sb.append(" ").append(o).append("+:").append(p); + } + + + return sb.toString(); + } + + private static class StateSet implements Comparable { + private final int[] state; + + private StateSet(int outputCount) { + state = new int[outputCount]; + } + + void setAllToUnused() { + Arrays.fill(state, 2); + } + + private void set(int i) { + set(i, 1); + } + + void set(int i, int value) { + state[i] = value; + } + + @Override + public int compareTo(StateSet stateSet) { + for (int i = 0; i < state.length; i++) { + int c = Integer.compare(state[i], stateSet.state[i]); + if (c != 0) + return c; + } + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + StateSet stateSet = (StateSet) o; + + return Arrays.equals(state, stateSet.state); + } + + @Override + public int hashCode() { + return Arrays.hashCode(state); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(state.length); + for (int i : state) + switch (i) { + case 0: + sb.append("0"); + break; + case 1: + sb.append("1"); + break; + default: + sb.append("-"); + } + return sb.toString(); + } + } + + private final class ProdInput extends StateSet { + private ProdInput(int inputCount) { + super(inputCount); + setAllToUnused(); + } + + public void add(Expression z) throws FuseMapFillerException { + if (z instanceof Not) + add(((Not) z).getExpression(), true); + else + add(z, false); + } + + private void add(Expression var, boolean invers) throws FuseMapFillerException { + if (var instanceof Variable) { + set(getVarNum(((Variable) var).getIdentifier()), invers ? 0 : 1); + } else + throw new FuseMapFillerException("invalid Expression"); + } + } + +} diff --git a/src/main/java/de/neemann/digital/builder/tt2/package-info.java b/src/main/java/de/neemann/digital/builder/tt2/package-info.java new file mode 100644 index 000000000..e1e760d88 --- /dev/null +++ b/src/main/java/de/neemann/digital/builder/tt2/package-info.java @@ -0,0 +1,8 @@ +/** + * Generator for the OpenABLE Truth Table (tt) format. + * Used to create the input files for the ATF15xx Fitters provided by ATMEL. + * These fitters then are used to create the needed JEDEC files. + *

+ * Created by hneemann on 03.03.17. + */ +package de.neemann.digital.builder.tt2; diff --git a/src/test/java/de/neemann/digital/builder/tt2/TT2ExporterTest.java b/src/test/java/de/neemann/digital/builder/tt2/TT2ExporterTest.java new file mode 100644 index 000000000..a54eaf495 --- /dev/null +++ b/src/test/java/de/neemann/digital/builder/tt2/TT2ExporterTest.java @@ -0,0 +1,102 @@ +package de.neemann.digital.builder.tt2; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; + +import static de.neemann.digital.analyse.expression.Not.not; +import static de.neemann.digital.analyse.expression.Operation.and; +import static de.neemann.digital.analyse.expression.Operation.or; +import static de.neemann.digital.analyse.expression.Variable.v; + +/** + * Created by hneemann on 03.03.17. + */ +public class TT2ExporterTest extends TestCase { + + public void testCombinatorial() throws Exception { + TT2Exporter tt2 = new TT2Exporter(); + tt2.getPinMapping().setAvailBidirectional(4,5,6,8,20,21); + tt2.getBuilder().addCombinatorial("Y", and(v("A"), v("B"))); + tt2.getBuilder().addCombinatorial("X", or(v("A1"), v("B1"))); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + tt2.writeTo(baos); + + assertEquals("#$ TOOL CUPL\r\n" + + "# Berkeley PLA format generated using Digital\r\n" + + "#$ TITLE unknown\r\n" + + "#$ MODULE unknown\r\n" + + "#$ JEDECFILE unknown\r\n" + + "#$ DEVICE f1502plcc44\r\n" + + "#$ PINS 6 A+:4 A1+:5 B+:6 B1+:8 Y+:20 X+:21\r\n" + + ".i 4\r\n" + + ".o 2\r\n" + + ".type f\r\n" + + ".ilb A A1 B B1\r\n" + + ".ob Y X\r\n" + + ".phase 11\r\n" + + ".p 3\r\n" + + "1-1- 10\r\n" + + "-1-- 01\r\n" + + "---1 01\r\n" + + ".e\r\n",baos.toString()); + } + + public void testSequential() throws Exception { + TT2Exporter tt2 = new TT2Exporter(); + tt2.getPinMapping().setAvailBidirectional(4,5,6,8,20,21); + tt2.getBuilder().addSequential("Yn", and(v("A"), not(v("Yn")))); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + tt2.writeTo(baos); + + assertEquals("#$ TOOL CUPL\r\n" + + "# Berkeley PLA format generated using Digital\r\n" + + "#$ TITLE unknown\r\n" + + "#$ MODULE unknown\r\n" + + "#$ JEDECFILE unknown\r\n" + + "#$ DEVICE f1502plcc44\r\n" + + "#$ PINS 3 A+:4 CLK+:43 Yn+:5\r\n" + + ".i 3\r\n" + + ".o 3\r\n" + + ".type f\r\n" + + ".ilb A CLK Yn.Q\r\n" + + ".ob Yn.REG Yn.AR Yn.C\r\n" + + ".phase 111\r\n" + + ".p 3\r\n" + + "1-0 100\r\n" + + "-1- 001\r\n" + + "--- 000\r\n" + + ".e\r\n",baos.toString()); + } + + public void testSequential2() throws Exception { + TT2Exporter tt2 = new TT2Exporter(); + tt2.getPinMapping().setAvailBidirectional(4,5,6,8,20,21); + tt2.getBuilder().addSequential("Yn", and(v("A"), not(v("Yn")))); + tt2.getBuilder().addSequential("Xn", or(v("B"), not(v("Xn")))); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + tt2.writeTo(baos); + + assertEquals("#$ TOOL CUPL\r\n" + + "# Berkeley PLA format generated using Digital\r\n" + + "#$ TITLE unknown\r\n" + + "#$ MODULE unknown\r\n" + + "#$ JEDECFILE unknown\r\n" + + "#$ DEVICE f1502plcc44\r\n" + + "#$ PINS 5 A+:4 B+:5 CLK+:43 Yn+:6 Xn+:8\r\n" + + ".i 5\r\n" + + ".o 6\r\n" + + ".type f\r\n" + + ".ilb A B CLK Xn.Q Yn.Q\r\n" + + ".ob Yn.REG Yn.AR Yn.C Xn.REG Xn.AR Xn.C\r\n" + + ".phase 111111\r\n" + + ".p 5\r\n" + + "1---0 100000\r\n" + + "-1--- 000100\r\n" + + "--1-- 001001\r\n" + + "---0- 000100\r\n" + + "----- 000000\r\n" + + ".e\r\n",baos.toString()); + } + +} \ No newline at end of file