1 package net.mograsim.plugin.views;
3 import static net.mograsim.logic.model.preferences.RenderPreferences.DRAG_BUTTON;
4 import static net.mograsim.logic.model.preferences.RenderPreferences.ZOOM_BUTTON;
5 import static net.mograsim.plugin.preferences.PluginPreferences.SIMULATION_SPEED_PRECISION;
7 import java.math.BigDecimal;
8 import java.math.MathContext;
9 import java.util.HashSet;
10 import java.util.Optional;
12 import java.util.function.Consumer;
14 import org.eclipse.core.runtime.SafeRunner;
15 import org.eclipse.debug.ui.DebugUITools;
16 import org.eclipse.debug.ui.contexts.IDebugContextManager;
17 import org.eclipse.debug.ui.contexts.IDebugContextService;
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.layout.FillLayout;
20 import org.eclipse.swt.layout.GridData;
21 import org.eclipse.swt.layout.GridLayout;
22 import org.eclipse.swt.widgets.Button;
23 import org.eclipse.swt.widgets.Composite;
24 import org.eclipse.swt.widgets.Control;
25 import org.eclipse.swt.widgets.Label;
26 import org.eclipse.swt.widgets.Scale;
27 import org.eclipse.ui.PlatformUI;
28 import org.eclipse.ui.part.ViewPart;
30 import net.haspamelodica.swt.helper.input.DoubleInput;
31 import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput;
32 import net.mograsim.logic.core.LogicObserver;
33 import net.mograsim.logic.core.components.CoreClock;
34 import net.mograsim.logic.model.LogicUICanvas;
35 import net.mograsim.logic.model.modeladapter.CoreModelParameters;
36 import net.mograsim.logic.model.preferences.RenderPreferences;
37 import net.mograsim.machine.Machine;
38 import net.mograsim.machine.Memory.MemoryCellModifiedListener;
39 import net.mograsim.machine.mi.AssignableMicroInstructionMemory;
40 import net.mograsim.plugin.MograsimActivator;
41 import net.mograsim.plugin.launch.MachineDebugContextListener;
42 import net.mograsim.plugin.launch.MachineDebugTarget;
43 import net.mograsim.plugin.tables.DisplaySettings;
44 import net.mograsim.plugin.tables.mi.ActiveInstructionPreviewContentProvider;
45 import net.mograsim.plugin.tables.mi.InstructionTable;
46 import net.mograsim.plugin.util.OverlappingFillLayout;
48 public class SimulationView extends ViewPart
50 private static final int SIM_SPEED_SCALE_STEPS = 50;
51 private static final double SIM_SPEED_SCALE_STEP_FACTOR = 1.32;
52 private static final double SIM_SPEED_SCALE_STEP_FACTOR_LOG = Math.log(SIM_SPEED_SCALE_STEP_FACTOR);
54 private final Set<Control> controlsToDisableWhenNoMachinePresent;
55 private Button sbseButton;
56 private Scale simSpeedScale;
57 private DoubleInput simSpeedInput;
58 private Label simSpeedDescription;
59 private Composite contextDependentControlsParent;
60 private Composite canvasParent;
61 private InstructionTable instPreview;
62 private ActiveInstructionPreviewContentProvider contentProvider;
63 private Label noRunningMachineLabel;
65 private MachineDebugTarget debugTarget;
66 private LogicUICanvas canvas;
68 private final MemoryCellModifiedListener memCellListener;
69 private final LogicObserver clockObserver;
70 private final MachineDebugContextListener debugContextListener;
71 private final Consumer<Double> executionSpeedListener;
73 public SimulationView()
75 controlsToDisableWhenNoMachinePresent = new HashSet<>();
76 memCellListener = a -> instPreview.refresh();
77 // TODO use Step Over instead
78 // TODO this should not be managed by the Simulation View
81 if (((CoreClock) o).isOn())
82 SafeRunner.run(() -> debugTarget.suspend());
84 debugContextListener = new MachineDebugContextListener()
87 public void machineDebugContextChanged(Optional<MachineDebugTarget> oldTarget, Optional<MachineDebugTarget> newTarget)
89 SimulationView.this.debugContextChanged(newTarget);
92 executionSpeedListener = this::speedFactorChanged;
96 public void createPartControl(Composite parent)
99 parent.setLayout(new GridLayout());
101 addSimulationControlWidgets(parent);
103 Composite contextDependentControlsParentParent = new Composite(parent, SWT.NONE);
104 contextDependentControlsParentParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
105 contextDependentControlsParentParent.setLayout(new OverlappingFillLayout());
107 noRunningMachineLabel = new Label(contextDependentControlsParentParent, SWT.NONE);
108 noRunningMachineLabel.setText("No machine running && selected in the Debug view...");
110 contextDependentControlsParent = new Composite(contextDependentControlsParentParent, SWT.NONE);
111 GridLayout contexDependentControlsLayout = new GridLayout();
112 contexDependentControlsLayout.marginWidth = 0;
113 contexDependentControlsLayout.marginHeight = 0;
114 contextDependentControlsParent.setLayout(contexDependentControlsLayout);
116 canvasParent = new Composite(contextDependentControlsParent, SWT.NONE);
117 canvasParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
118 canvasParent.setLayout(new FillLayout());
120 addInstructionPreviewControlWidgets(contextDependentControlsParent);
122 IDebugContextManager debugCManager = DebugUITools.getDebugContextManager();
123 IDebugContextService contextService = debugCManager.getContextService(PlatformUI.getWorkbench().getActiveWorkbenchWindow());
124 contextService.addDebugContextListener(debugContextListener);
125 debugContextListener.debugContextChanged(contextService.getActiveContext());
128 private void addSimulationControlWidgets(Composite parent)
130 Composite c = new Composite(parent, SWT.NONE);
131 c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
132 c.setLayout(new GridLayout(8, false));
134 sbseButton = new Button(c, SWT.CHECK);
135 controlsToDisableWhenNoMachinePresent.add(sbseButton);
137 sbseButton.setText("Step by step execution");
138 sbseButton.addListener(SWT.Selection, e ->
140 CoreClock cl = debugTarget.getMachine().getClock();
141 if (sbseButton.getSelection())
142 cl.registerObserver(clockObserver);
144 cl.deregisterObserver(clockObserver);
146 sbseButton.setSelection(true);
148 Label simSpeedLabel = new Label(c, SWT.NONE);
149 controlsToDisableWhenNoMachinePresent.add(simSpeedLabel);
150 simSpeedLabel.setText("Simulation Speed: ");
152 simSpeedScale = new Scale(c, SWT.NONE);
153 controlsToDisableWhenNoMachinePresent.add(simSpeedScale);
154 simSpeedScale.setMinimum(0);
155 simSpeedScale.setMaximum(SIM_SPEED_SCALE_STEPS);
156 simSpeedScale.setIncrement(1);
157 simSpeedScale.setSelection(0);
158 simSpeedScale.addListener(SWT.Selection, e ->
160 double speed = Math.pow(SIM_SPEED_SCALE_STEP_FACTOR, simSpeedScale.getSelection() - SIM_SPEED_SCALE_STEPS);
161 debugTarget.setExecutionSpeed(speed);
164 simSpeedInput = new DoubleInput(c, SWT.NONE);
165 controlsToDisableWhenNoMachinePresent.add(simSpeedInput);
166 // TODO add a listener
167 simSpeedInput.setPrecision(MograsimActivator.instance().getPluginPrefs().getInt(SIMULATION_SPEED_PRECISION));
168 simSpeedInput.addChangeListener(speed ->
171 debugTarget.setExecutionSpeed(speed);
173 debugTarget.setExecutionSpeed(Math.pow(10, -simSpeedInput.getPrecision()));
176 simSpeedDescription = new Label(c, SWT.NONE);
177 simSpeedDescription.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
182 private String describeSimSpeed(double speed)
184 CoreModelParameters coreModelParameters = debugTarget.getMachine().getCoreModelParameters();
185 // TODO hardcoding this seems not optimal
186 int ticksPerSecond = 1000000;
188 double simulTicksPerRealSecond = speed * ticksPerSecond;
190 // TODO internationalize
191 StringBuilder sb = new StringBuilder();
192 sb.append("Per second: ");
193 sb.append(formatNSignificantDigits(4, simulTicksPerRealSecond / coreModelParameters.wireTravelTime));
194 sb.append(" wire travel times; ");
195 sb.append(formatNSignificantDigits(4, simulTicksPerRealSecond / coreModelParameters.gateProcessTime));
196 sb.append(" gate process times; ");
197 sb.append(formatNSignificantDigits(4, simulTicksPerRealSecond / debugTarget.getMachine().getClock().getDelta() / 2));
198 sb.append(" clock cycles");
199 return sb.toString();
202 private static String formatNSignificantDigits(int digits, double d)
204 return new BigDecimal(d, new MathContext(digits)).toPlainString();
207 private void speedFactorChanged(double speed)
209 simSpeedInput.setValue(speed);
210 int closestScalePos = (int) Math.round(Math.log(speed) / SIM_SPEED_SCALE_STEP_FACTOR_LOG + SIM_SPEED_SCALE_STEPS);
211 simSpeedScale.setSelection(Math.min(Math.max(closestScalePos, 0), SIM_SPEED_SCALE_STEPS));
212 simSpeedDescription.setText(describeSimSpeed(speed));
215 private void addInstructionPreviewControlWidgets(Composite parent)
217 instPreview = new InstructionTable(parent, new DisplaySettings(), getSite().getWorkbenchWindow().getWorkbench().getThemeManager(),
219 instPreview.getTableViewer().getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
220 contentProvider = new ActiveInstructionPreviewContentProvider(instPreview.getTableViewer());
221 instPreview.setContentProvider(contentProvider);
224 private void debugContextChanged(Optional<MachineDebugTarget> newTarget)
226 // call binToDebugTarget even if we didn't find a selected MachineDebugTarget
227 bindToDebugTarget(newTarget.orElse(null));
230 private void bindToDebugTarget(MachineDebugTarget debugTarget)
232 deregisterMachineDependentListeners();
233 this.debugTarget = debugTarget;
235 if (canvasParent == null)
236 // createPartControls has not been called yet
244 offX = canvas.getOffX();
245 offY = canvas.getOffY();
246 zoom = canvas.getZoom();
255 if (debugTarget != null)
257 noRunningMachineLabel.setVisible(false);
258 contextDependentControlsParent.setVisible(true);
259 controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(true));
261 Machine machine = debugTarget.getMachine();
263 RenderPreferences renderPrefs = MograsimActivator.instance().getRenderPrefs();
264 canvas = new LogicUICanvas(canvasParent, SWT.NONE, machine.getModel(), renderPrefs);
265 canvas.addListener(SWT.MouseDown, e -> canvas.setFocus());
266 ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(canvas);
267 // TODO add a listener
268 userInput.buttonDrag = renderPrefs.getInt(DRAG_BUTTON);
269 // TODO add a listener
270 userInput.buttonZoom = renderPrefs.getInt(ZOOM_BUTTON);
271 userInput.enableUserInput();
274 canvas.moveTo(offX, offY, zoom);
275 canvas.commitTransform();
278 AssignableMicroInstructionMemory mIMemory = machine.getMicroInstructionMemory();
279 instPreview.bindMicroInstructionMemory(mIMemory);
280 mIMemory.registerCellModifiedListener(memCellListener);
282 canvasParent.layout();
285 contentProvider.setMachine(machine);
288 machine.getClock().registerObserver(clockObserver);
290 // initialize executer
291 debugTarget.addExecutionSpeedListener(executionSpeedListener);
292 speedFactorChanged(debugTarget.getExecutionSpeed());
295 noRunningMachineLabel.setVisible(true);
296 contextDependentControlsParent.setVisible(false);
297 controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(false));
298 contentProvider.setMachine(null);
302 private void deregisterMachineDependentListeners()
304 if (debugTarget != null)
306 debugTarget.removeExecutionSpeedListener(executionSpeedListener);
307 debugTarget.getMachine().getMicroInstructionMemory().deregisterCellModifiedListener(memCellListener);
308 debugTarget.getMachine().getClock().deregisterObserver(clockObserver);
309 if (sbseButton != null && !sbseButton.isDisposed())
310 sbseButton.setSelection(true);
315 public void setFocus()
317 if (canvas != null && !canvas.isDisposed())
322 public void dispose()
324 deregisterMachineDependentListeners();
325 contentProvider.setMachine(null);
326 DebugUITools.getDebugContextManager().removeDebugContextListener(debugContextListener);