diff --git a/src/simulator/src/circuit.ts b/src/simulator/src/circuit.ts index 3704cdec..41150810 100644 --- a/src/simulator/src/circuit.ts +++ b/src/simulator/src/circuit.ts @@ -273,6 +273,8 @@ export function changeCircuitName(name: string, id = globalScope.id) { circuit_list.value[index].name = name } +type State = 'normal' | 'error' + /** * Class representing a Scope * @class @@ -305,6 +307,8 @@ export default class Scope { Splitter?: any[]; SubCircuit?: any[]; Clock?: any[]; + states: { NORMAL: State; ERROR: State; }; + currentState: State; constructor(name = 'localScope', id = undefined) { this.restrictedCircuitElementsUsed = [] this.id = id || Math.floor(Math.random() * 100000000000 + 1) @@ -340,6 +344,15 @@ export default class Scope { title_y: 13, titleEnabled: true, } + + // Defining all the possible states of simulator + this.states = { + NORMAL: 'normal', + ERROR: 'error', + }; + + // Setting current state of simulator + this.currentState = this.states.NORMAL; } isVisible() { diff --git a/src/simulator/src/data/undo.js b/src/simulator/src/data/undo.js index 67f22005..b3e913a6 100644 --- a/src/simulator/src/data/undo.js +++ b/src/simulator/src/data/undo.js @@ -8,7 +8,7 @@ import { layoutModeGet } from '../layoutMode' import Scope, { scopeList } from '../circuit' import { loadScope } from './load' import { updateRestrictedElementsInScope } from '../restrictedElementDiv' -import { forceResetNodesSet } from '../engine' +import { errorDetectedSet, forceResetNodesSet } from '../engine'; /** * Function called to generate a prompt to save an image * @param {Scope=} - the circuit in which we want to call undo @@ -38,6 +38,9 @@ export default function undo(scope = globalScope) { tempScope.name = scope.name tempScope.testbenchData = scope.testbenchData scopeList[scope.id] = tempScope + if (globalScope.currentState === globalScope.states.ERROR) { + errorDetectedSet(false); + } globalScope = tempScope globalScope.ox = backupOx globalScope.oy = backupOy diff --git a/src/simulator/src/engine.js b/src/simulator/src/engine.js index 5227eb76..9cfd05ad 100644 --- a/src/simulator/src/engine.js +++ b/src/simulator/src/engine.js @@ -14,6 +14,7 @@ import { resetup } from './setup' import { verilogModeGet } from './Verilog2CV' import { renderOrder, updateOrder } from './metadata' import ContentionPendingData from './contention'; +import { hidetabs, visibletabs } from './listeners'; /** * Core of the simulation and rendering algorithm. @@ -401,53 +402,65 @@ export function updateSelectionsAndPane(scope = globalScope) { * @category engine */ export function play(scope = globalScope, resetNodes = false) { - if (errorDetected) return // Don't simulate until error is fixed - if (loading === true) return // Don't simulate until loaded - - simulationArea.simulationQueue.reset() - plotArea.setExecutionTime() // Waveform thing - resetNodeHighlights(scope); - // Reset Nodes if required - if (resetNodes || forceResetNodes) { - scope.reset() - simulationArea.simulationQueue.reset() - forceResetNodesSet(false) - } + try { + if (errorDetected) return; // Don't simulate until error is fixed + if (loading === true) return; // Don't simulate until loaded + + simulationArea.simulationQueue.reset(); + plotArea.setExecutionTime(); // Waveform thing + resetNodeHighlights(scope); + // Reset Nodes if required + if (resetNodes || forceResetNodes) { + scope.reset(); + simulationArea.simulationQueue.reset(); + forceResetNodesSet(false); + } - // To store set of Nodes that have shown contention but kept temporarily + // To store set of Nodes that have shown contention but kept temporarily simulationArea.contentionPending = new ContentionPendingData(); - // add inputs to the simulation queue - scope.addInputs() - // to check if we have infinite loop in circuit - let stepCount = 0 - let elem - while (!simulationArea.simulationQueue.isEmpty()) { - if (errorDetected) { - simulationArea.simulationQueue.reset() - return - } - elem = simulationArea.simulationQueue.pop() + // add inputs to the simulation queue + scope.addInputs(); + // to check if we have infinite loop in circuit + let stepCount = 0; + let elem; + while (!simulationArea.simulationQueue.isEmpty()) { + if (errorDetected) { + simulationArea.simulationQueue.reset(); + return; + } + elem = simulationArea.simulationQueue.pop(); - elem.resolve() + elem.resolve(); - stepCount++ - if (stepCount > 1000000) { - // Cyclic or infinite Circuit Detection - showError( - 'Simulation Stack limit exceeded: maybe due to cyclic paths or contention' - ) - forceResetNodesSet(true) - } - } - // Check for Contentions - if (simulationArea.contentionPending.size() > 0) { - for (const [ourNode, theirNode] of simulationArea.contentionPending.nodes()) { - ourNode.highlighted = true; - theirNode.highlighted = true; + stepCount++; + if (stepCount > 1000000) { // Cyclic or infinite Circuit Detection + showError('Simulation Stack limit exceeded: maybe due to cyclic paths or contention'); + forceResetNodesSet(true); + } } + // Check for Contentions + if (simulationArea.contentionPending.size() > 0) { + for (const [ourNode, theirNode] of simulationArea.contentionPending.nodes()) { + ourNode.highlighted = true; + theirNode.highlighted = true; + } - forceResetNodesSet(true); + forceResetNodesSet(true); showError('Contention Error: One or more bus contentions in the circuit (check highlighted nodes)'); + } + + // change the state of simulator to normal + if (globalScope.currentState === globalScope.states.ERROR) { + globalScope.currentState = globalScope.states.NORMAL; + } + visibletabs(); + } catch (error) { + // change the state of simulator to error + if (globalScope.currentState === globalScope.states.NORMAL) { + globalScope.currentState = globalScope.states.ERROR; + hidetabs(); + } + showError('The simulator is in an error state. Now, you can only delete or undo components until the simulator returns to the normal state'); } } diff --git a/src/simulator/src/listeners.js b/src/simulator/src/listeners.js index 20f2c4dc..16f06f1b 100644 --- a/src/simulator/src/listeners.js +++ b/src/simulator/src/listeners.js @@ -41,6 +41,38 @@ import { setupTimingListeners } from './plotArea' var unit = 10 var listenToSimulator = true +function setPanelVisibility(visibility, display) { + if (window.screen.width < 1000) { + $('#touchMenu').toggle(visibility === 'visible'); + $('#smallNavbarMenu-btn').toggle(visibility === 'visible'); + } + else { + const classes = ['draggable-panel', 'nav-dropdown', 'logixButton']; + classes.forEach((className) => { + $(`.${className}`).each((index, element) => { + if (className === 'logixButton') { + $(element).toggle(visibility === 'visible'); + $(element).parent().toggle(visibility === 'visible'); + } else { + $(element).css('visibility', visibility); + } + }); + }); + } + var contextMenuItems = $("#contextMenu li:nth-child(1), #contextMenu li:nth-child(2), #contextMenu li:nth-child(3), #contextMenu li:nth-child(6), #contextMenu li:nth-child(7), #contextMenu li:nth-child(8)"); + contextMenuItems.css("display", display); +} + +// Function to hide tabs when simulator goes into error state +export function hidetabs() { + setPanelVisibility('hidden', 'none'); +} + +// Function to visible tabs when simulator returns back to normal state +export function visibletabs() { + setPanelVisibility('visible', 'block'); +} + export default function startListeners() { $(document).on('keyup', (e) => { if (e.key === 'Escape') exitFullView() diff --git a/src/simulator/src/ux.js b/src/simulator/src/ux.js index c2e26fac..80748e01 100644 --- a/src/simulator/src/ux.js +++ b/src/simulator/src/ux.js @@ -7,7 +7,8 @@ import { layoutModeGet } from './layoutMode' import { scheduleUpdate, wireToBeCheckedSet, - updateCanvasSet + updateCanvasSet, + errorDetectedSet } from './engine' import { simulationArea } from './simulationArea' import logixFunction from './data' @@ -289,6 +290,10 @@ export function deleteSelected() { updateCanvasSet(true) scheduleUpdate() updateRestrictedElementsInScope() + + if (globalScope.currentState === globalScope.states.ERROR) { + errorDetectedSet(false); + } } /**