diff --git a/src/main/dig/combinatorial/Demultiplexer.dig b/src/main/dig/combinatorial/Demultiplexer.dig index 61d1f389d..14b8e3deb 100644 --- a/src/main/dig/combinatorial/Demultiplexer.dig +++ b/src/main/dig/combinatorial/Demultiplexer.dig @@ -1,311 +1,311 @@ - 1 - - - In - - - Label - A_0 - - - - 0 - - - In - - - Label - A_1 - - - - 0 - - - Not - - - rotation - - - - - 3 - - - Not - - - rotation - - - - - 3 - - - And - - - Inputs - 3 - - - - 0 - - - In - - - Label - D - - - - 0 - - - And - - - Inputs - 3 - - - - 0 - - - And - - - Inputs - 3 - - - - 0 - - - And - - - Inputs - 3 - - - - 0 - - - Out - - - Label - Y_0 - - - - 0 - - - Out - - - Label - Y_1 - - - - 0 - - - Out - - - Label - Y_2 - - - - 0 - - - Out - - - Label - Y_3 - - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 1 + + + In + + + Label + A_1 + + + + 0 + + + In + + + Label + A_0 + + + + 0 + + + Not + + + rotation + + + + + 3 + + + Not + + + rotation + + + + + 3 + + + And + + + Inputs + 3 + + + + 0 + + + In + + + Label + D + + + + 0 + + + And + + + Inputs + 3 + + + + 0 + + + And + + + Inputs + 3 + + + + 0 + + + And + + + Inputs + 3 + + + + 0 + + + Out + + + Label + Y_0 + + + + 0 + + + Out + + + Label + Y_1 + + + + 0 + + + Out + + + Label + Y_2 + + + + 0 + + + Out + + + Label + Y_3 + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/dig/combinatorial/FullAdder.dig b/src/main/dig/combinatorial/FullAdder.dig index 3d249f1d4..800ca3254 100644 --- a/src/main/dig/combinatorial/FullAdder.dig +++ b/src/main/dig/combinatorial/FullAdder.dig @@ -76,10 +76,10 @@ Label - C_i + S_i - + 0 @@ -87,10 +87,10 @@ Label - S_i + C_i - + 0 diff --git a/src/main/java/de/neemann/digital/analyse/AnalyseException.java b/src/main/java/de/neemann/digital/analyse/AnalyseException.java new file mode 100644 index 000000000..4970ca7ca --- /dev/null +++ b/src/main/java/de/neemann/digital/analyse/AnalyseException.java @@ -0,0 +1,17 @@ +package de.neemann.digital.analyse; + +/** + * Exception thrown if there problems analysing the circuit + * + * @author hneemann + */ +public class AnalyseException extends Exception { + /** + * Creates a new instance + * + * @param message the message + */ + public AnalyseException(String message) { + super(message); + } +} diff --git a/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java b/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java new file mode 100644 index 000000000..54e4c5853 --- /dev/null +++ b/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java @@ -0,0 +1,138 @@ +package de.neemann.digital.analyse; + +import de.neemann.digital.core.Model; +import de.neemann.digital.core.NodeException; +import de.neemann.digital.lang.Lang; + +import java.util.ArrayList; + +/** + * Analyses a given model. + * Calculates the truth table which generated by the given model + * + * @author hneemann + */ +public class ModelAnalyser { + + private final Model model; + private final ArrayList inputs; + private final ArrayList outputs; + private final int rows; + private final int cols; + private final boolean[][] data; + + + /** + * Creates a new instance + * + * @param model the model + * @throws AnalyseException AnalyseException + */ + public ModelAnalyser(Model model) throws AnalyseException { + this.model = model; + inputs = checkBinary(model.getInputs()); + if (inputs.size() == 0) + throw new AnalyseException(Lang.get("err_analyseNoInputs")); + outputs = checkBinary(model.getOutputs()); + if (outputs.size() == 0) + throw new AnalyseException(Lang.get("err_analyseNoOutputs")); + rows = 1 << inputs.size(); + cols = inputs.size() + outputs.size(); + data = new boolean[rows][cols]; + } + + private ArrayList checkBinary(ArrayList list) throws AnalyseException { + for (Model.Signal s : list) + if (s.getValue().getBits() != 1) + throw new AnalyseException(Lang.get("err_analyseValue_N_IsNotBinary", s.getName())); + return list; + } + + /** + * Analyses the circuit + * + * @return this for call chaining + * @throws NodeException NodeException + */ + public ModelAnalyser analyse() throws NodeException { + model.init(); + for (int row = 0; row < rows; row++) { + for (int i = 0; i < inputs.size(); i++) { + int pos = inputs.size() - 1 - i; + boolean bool = (row & (1 << pos)) != 0; + inputs.get(i).getValue().setBool(bool); + data[row][i] = bool; + } + model.doStep(); + for (int i = 0; i < outputs.size(); i++) { + data[row][inputs.size() + i] = outputs.get(i).getValue().getBool(); + } + } + return this; + } + + /** + * @return returns the models inputs + */ + public ArrayList getInputs() { + return inputs; + } + + /** + * @return returns the models outputs + */ + public ArrayList getOutputs() { + return outputs; + } + + /** + * @return the truth table + */ + public boolean[][] getData() { + return data; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Model.Signal s : inputs) + sb.append(s.getName()).append("\t"); + for (Model.Signal s : outputs) + sb.append(s.getName()).append("\t"); + sb.append('\n'); + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + if (data[row][col]) + sb.append("1\t"); + else + sb.append("0\t"); + } + sb.append("\n"); + } + return sb.toString(); + } + + /** + * @return the number of rows + */ + public int getRows() { + return rows; + } + + /** + * @return the number of columns + */ + public int getCols() { + return cols; + } + + /** + * @return returns one value of the truth table + */ + public int getValue(int rowIndex, int columnIndex) { + if (data[rowIndex][columnIndex]) + return 1; + else + return 0; + } +} diff --git a/src/main/java/de/neemann/digital/analyse/ModelAnalyzerTableModel.java b/src/main/java/de/neemann/digital/analyse/ModelAnalyzerTableModel.java new file mode 100644 index 000000000..536083cc1 --- /dev/null +++ b/src/main/java/de/neemann/digital/analyse/ModelAnalyzerTableModel.java @@ -0,0 +1,60 @@ +package de.neemann.digital.analyse; + +import javax.swing.event.TableModelListener; +import javax.swing.table.TableModel; + +/** + * @author hneemann + */ +public class ModelAnalyzerTableModel implements TableModel { + private final ModelAnalyser analyzer; + + public ModelAnalyzerTableModel(ModelAnalyser analyzer) { + this.analyzer = analyzer; + } + + @Override + public int getRowCount() { + return analyzer.getRows(); + } + + @Override + public int getColumnCount() { + return analyzer.getCols(); + } + + @Override + public String getColumnName(int columnIndex) { + if (columnIndex < analyzer.getInputs().size()) + return analyzer.getInputs().get(columnIndex).getName(); + else + return analyzer.getOutputs().get(columnIndex - analyzer.getInputs().size()).getName(); + } + + @Override + public Class getColumnClass(int columnIndex) { + return Integer.class; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return false; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + return analyzer.getValue(rowIndex, columnIndex); + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + } + + @Override + public void addTableModelListener(TableModelListener l) { + } + + @Override + public void removeTableModelListener(TableModelListener l) { + } +} diff --git a/src/main/java/de/neemann/digital/analyse/package-info.java b/src/main/java/de/neemann/digital/analyse/package-info.java new file mode 100644 index 000000000..e00b063c5 --- /dev/null +++ b/src/main/java/de/neemann/digital/analyse/package-info.java @@ -0,0 +1,6 @@ +/** + * Classes used to analyse the actual circuit + * + * @author hneemann + */ +package de.neemann.digital.analyse; diff --git a/src/main/java/de/neemann/digital/core/Model.java b/src/main/java/de/neemann/digital/core/Model.java index 747724020..7946ee691 100644 --- a/src/main/java/de/neemann/digital/core/Model.java +++ b/src/main/java/de/neemann/digital/core/Model.java @@ -48,6 +48,8 @@ public class Model { private final ArrayList breaks; private final ArrayList resets; private final ArrayList signals; + private final ArrayList inputs; + private final ArrayList outputs; private final ArrayList roms; private final ArrayList nodes; @@ -65,6 +67,8 @@ public class Model { this.breaks = new ArrayList<>(); this.resets = new ArrayList<>(); this.signals = new ArrayList<>(); + this.outputs = new ArrayList<>(); + this.inputs = new ArrayList<>(); this.roms = new ArrayList<>(); this.nodes = new ArrayList<>(); this.nodesToUpdateAct = new ArrayList<>(); @@ -100,8 +104,7 @@ public class Model { /** * Needs to be called after all nodes are added. - * If not called it es called automatically. - * Calles init(true); + * Calls init(true); * * @throws NodeException NodeException */ @@ -373,10 +376,56 @@ public class Model { * @param value the signals value */ public void addSignal(String name, ObservableValue value) { - if (name != null && name.length() > 0 && value != null) + if (isSignal(name, value)) signals.add(new Signal(name, value)); } + private static boolean isSignal(String name, ObservableValue value) { + return name != null && name.length() > 0 && value != null; + } + + /** + * registers a input to the model + * + * @param name the signals name + * @param value the signals value + */ + public void addInput(String name, ObservableValue value) { + if (isSignal(name, value)) { + Signal signal = new Signal(name, value); + signals.add(signal); + inputs.add(signal); + } + } + + /** + * @return the models inputs + */ + public ArrayList getInputs() { + return inputs; + } + + /** + * registers a output to the model + * + * @param name the signals name + * @param value the signals value + */ + public void addOutput(String name, ObservableValue value) { + if (isSignal(name, value)) { + Signal signal = new Signal(name, value); + signals.add(signal); + outputs.add(signal); + } + } + + /** + * @return the models outputs + */ + public ArrayList getOutputs() { + return outputs; + } + /** * @return all registered Signals */ @@ -431,7 +480,10 @@ public class Model { * @param value the signals value */ public Signal(String name, ObservableValue value) { - this.name = name; + if (name.length() > 2 && name.charAt(0) == '$' && name.charAt(name.length() - 1) == '$') + this.name = name.substring(1, name.length() - 1); + else + this.name = name; this.value = value; } diff --git a/src/main/java/de/neemann/digital/core/io/In.java b/src/main/java/de/neemann/digital/core/io/In.java index 40e0cc52d..14de670eb 100644 --- a/src/main/java/de/neemann/digital/core/io/In.java +++ b/src/main/java/de/neemann/digital/core/io/In.java @@ -59,6 +59,6 @@ public class In implements Element { @Override public void registerNodes(Model model) { - model.addSignal(label, output); + model.addInput(label, output); } } diff --git a/src/main/java/de/neemann/digital/core/io/Out.java b/src/main/java/de/neemann/digital/core/io/Out.java index 3309d99f0..a2ca82613 100644 --- a/src/main/java/de/neemann/digital/core/io/Out.java +++ b/src/main/java/de/neemann/digital/core/io/Out.java @@ -104,6 +104,6 @@ public class Out implements Element { @Override public void registerNodes(Model model) { - model.addSignal(label, value); + model.addOutput(label, value); } } diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index acc8c3e91..fa947980d 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -1,5 +1,7 @@ package de.neemann.digital.gui; +import de.neemann.digital.analyse.AnalyseException; +import de.neemann.digital.analyse.ModelAnalyser; import de.neemann.digital.core.*; import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.Key; @@ -320,6 +322,8 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E run.add(speedTest.createJMenuItem()); doStep.setEnabled(false); + bar.add(createAanalyseMenu()); + JToolBar toolBar = new JToolBar(); toolBar.add(newFile.createJButtonNoText()); toolBar.add(open.createJButtonNoText()); @@ -348,6 +352,26 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E setLocationRelativeTo(parent); } + private JMenu createAanalyseMenu() { + JMenu analyse = new JMenu(Lang.get("menu_analyse")); + analyse.add(new ToolTipAction(Lang.get("menu_analyse")) { + @Override + public void actionPerformed(ActionEvent e) { + try { + Model model = new ModelDescription(circuitComponent.getCircuit(), library).createModel(); + new TableDialog(Main.this, new ModelAnalyser(model).analyse()).setVisible(true); + elementState.activate(); + } catch (PinException | NodeException | AnalyseException e1) { + showErrorAndStopModel(Lang.get("msg_annalyseErr"), e1); + } + } + } + .setToolTip(Lang.get("menu_analyse_tt")) + .createJMenuItem()); + + return analyse; + } + private void orderMeasurements() { try { Model m = new ModelDescription(circuitComponent.getCircuit(), library).createModel(); diff --git a/src/main/java/de/neemann/digital/gui/components/TableDialog.java b/src/main/java/de/neemann/digital/gui/components/TableDialog.java new file mode 100644 index 000000000..449ac6d84 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/TableDialog.java @@ -0,0 +1,30 @@ +package de.neemann.digital.gui.components; + +import de.neemann.digital.analyse.ModelAnalyser; +import de.neemann.digital.analyse.ModelAnalyzerTableModel; +import de.neemann.digital.lang.Lang; + +import javax.swing.*; +import java.awt.*; + +/** + * Dialog to visualise a truth table. + * + * @author hneemann + */ +public class TableDialog extends JDialog { + /** + * Creates a new instance + * + * @param owner the owner of this dialog + * @param analyzer the truth table + */ + public TableDialog(Frame owner, ModelAnalyser analyzer) { + super(owner, Lang.get("win_table")); + JTable table = new JTable(new ModelAnalyzerTableModel(analyzer)); + table.setPreferredScrollableViewportSize(table.getPreferredSize()); + getContentPane().add(new JScrollPane(table)); + pack(); + setLocationRelativeTo(owner); + } +} diff --git a/src/main/resources/lang/lang_de.properties b/src/main/resources/lang/lang_de.properties index 7ed92a9ba..e6e4e10f4 100644 --- a/src/main/resources/lang/lang_de.properties +++ b/src/main/resources/lang/lang_de.properties @@ -168,6 +168,9 @@ err_notAllOutputsSameBits=Es haben nicht alle Ausg\u00E4nge die gleiche Bitbreit err_notAllOutputsSupportHighZ=Wenn mehrere Ausg\u00E4nge verbunden sind, m\u00FCssen alle Ausg\u00E4nge Tri-State Ausg\u00E4nge sein err_breakTimeOut=Nach {0} Zyklen ist kein Break aufgetreten err_labelNotConnectedToNet_N=Ein Tunnel {0} ist nicht verbunden! +err_analyseNoInputs=Die Schaltung hat keine benannten Eing\u00E4nge +err_analyseNoOutputs=Die Schaltung hat keine benannten Ausg\u00E4nge +err_analyseValue_N_IsNotBinary=Der Wert {0} hat mehr als ein Bit. attr_dialogTitle=Eigenschaften msg_errorEditingValue=Fehler bei der Eingabe eines Wertes @@ -178,10 +181,11 @@ msg_errorWritingFile=Fehler beim Schreiben einer Datei msg_errorReadingFile=Fehler beim Lesen einer Datei msg_errorCalculatingStep=Fehler beim Berechnen eines Schrittes msg_missingShape_N=Es fehlt ein Diagramm f\u00FCr {0} -msg_fastRunError=Ein Fehler beim Schnellen Lauf +msg_fastRunError=Ein Fehler beim schnellen Lauf msg_errorReadingListing_N0=Fehler beim Laden des Listings {0} msg_clockError=Fehler bei der Berechnung einer Takt\u00E4nderung msg_frequency_N=Die maximale Frequenz ber\u00E4gt {0}Hz. +msg_annalyseErr=Fehler bei der Analyse der Schaltung stat_clocks={0} Halbzyklen @@ -244,6 +248,8 @@ menu_editRunAttributes=Simulationseinstellungen menu_editRunAttributes_tt=Einstellungen f\u00FCr den Start der Simulation menu_saveData=Daten speichern menu_saveData_tt=Speichert die Daten als CSV Datei +menu_analyse=Analyse +menu_analyse_tt=Analyse der aktuellen Schaltung menu_about=\u00DCber Digital @@ -255,3 +261,4 @@ win_measures_microstep=Messwerte im Einzelgattermodus win_measures_fullstep=Messwerte im Vollschrittmodus win_listing=Listing +win_table=Tabelle \ No newline at end of file diff --git a/src/main/resources/lang/lang_en.properties b/src/main/resources/lang/lang_en.properties index 4cbb5cf37..b605fd2c9 100644 --- a/src/main/resources/lang/lang_en.properties +++ b/src/main/resources/lang/lang_en.properties @@ -228,6 +228,8 @@ menu_editRunAttributes=Simulation Settings menu_editRunAttributes_tt=Settings used to start the simulation menu_saveData=Save Data menu_saveData_tt=Save data as CSV file +menu_analyse=Analyse +menu_analyse_tt=Analyses the actual circuit win_saveChanges=Save Changes? win_confirmExit=Confirm Exit! @@ -252,3 +254,8 @@ elem_Text_tt=Shows a text in the circuit key_Default_tt=Is set if the model is started err_labelNotConnectedToNet_N=A tunnel {0} is not connected! key_Width_tt=With of symbol if this circuit is used in an element ins an other circuit. +err_analyseNoInputs=The circuit has no Inputs +err_analyseNoOutputs=The circuit has no outputs +err_analyseValue_N_IsNotBinary=The value {0} has more the one bit. +msg_annalyseErr=Error analysing the circuit +win_table=Table diff --git a/src/test/java/de/neemann/digital/analyse/ModelAnalyserTest.java b/src/test/java/de/neemann/digital/analyse/ModelAnalyserTest.java new file mode 100644 index 000000000..5952f4694 --- /dev/null +++ b/src/test/java/de/neemann/digital/analyse/ModelAnalyserTest.java @@ -0,0 +1,31 @@ +package de.neemann.digital.analyse; + +import de.neemann.digital.core.Model; +import de.neemann.digital.integration.ToBreakRunner; +import junit.framework.TestCase; + +/** + * @author hneemann + */ +public class ModelAnalyserTest extends TestCase { + + public void testAnalyzer() throws Exception { + Model model = new ToBreakRunner("dig/analyzeTest.dig").getModel(); + ModelAnalyser ma = new ModelAnalyser(model).analyse(); + + assertEquals(4, ma.getRows()); + assertEquals(3, ma.getCols()); + + // circuit is XOr: + assertEquals(0, ma.getValue(0, 2)); + assertEquals(1, ma.getValue(1, 2)); + assertEquals(1, ma.getValue(2, 2)); + assertEquals(0, ma.getValue(3, 2)); + + assertEquals("A\tB\tY\t\n" + + "0\t0\t0\t\n" + + "0\t1\t1\t\n" + + "1\t0\t1\t\n" + + "1\t1\t0\t\n", ma.toString()); + } +} \ No newline at end of file