From 2deaba945ce9cff27c454e8791dc44fb398924a7 Mon Sep 17 00:00:00 2001 From: lucasng32 <60285534+lucasng32@users.noreply.github.com> Date: Sat, 9 Sep 2023 16:33:48 +0800 Subject: [PATCH] Step sim UI (backwards and refresh) (#398) * Added refresh popup dialog * Added restart sim if steps are overwritten by new data * Added refresh popup * Added backwards button, check for sim restart --- src/Renderer/DrawBlock/PopupHelpers.fs | 21 +++ src/Renderer/Simulator/Fast/FastReduce.fs | 10 +- src/Renderer/Simulator/Fast/FastRun.fs | 60 +++++-- src/Renderer/UI/SimulationView.fs | 187 ++++++++++++---------- 4 files changed, 181 insertions(+), 97 deletions(-) diff --git a/src/Renderer/DrawBlock/PopupHelpers.fs b/src/Renderer/DrawBlock/PopupHelpers.fs index 47f6fd0b2..3d01ac798 100644 --- a/src/Renderer/DrawBlock/PopupHelpers.fs +++ b/src/Renderer/DrawBlock/PopupHelpers.fs @@ -575,6 +575,27 @@ let dialogPopup title body buttonText buttonAction isDisabled extraStyle dispatc ] dynamicClosablePopup title body foot extraStyle dispatch +// Used for refresh popup +let dialogPopupRefresh title body extraStyle dispatch = + let foot = + fun (model: Model) -> + let dialogData = model.PopupDialogData + Level.level [ Level.Level.Props [ Style [ Width "100%" ] ] ] [ + Level.left [] [] + Level.right [] [ + Level.item [] [ + Button.button [ + Button.Color IsLight + Button.OnClick (fun _ -> + dispatch ClosePopup + dispatch FinishUICmd) + ] [ str "Cancel" ] + ] + ] + ] + + dynamicClosablePopup title body foot extraStyle dispatch + /// Popup with an input textbox and two buttons. /// The text is reflected in Model.PopupDialogText. let dialogVerilogPopup title body saveUpdateText noErrors showingExtraInfo saveButtonAction moreInfoButton isDisabled extraStyle dispatch = diff --git a/src/Renderer/Simulator/Fast/FastReduce.fs b/src/Renderer/Simulator/Fast/FastReduce.fs index d6bacd9dc..88d155dbf 100644 --- a/src/Renderer/Simulator/Fast/FastReduce.fs +++ b/src/Renderer/Simulator/Fast/FastReduce.fs @@ -254,11 +254,15 @@ let fastReduce (maxArraySize: int) (numStep: int) (isClockedReduction: bool) (co /// get last cycle data from output i for component let inline insOldUInt32 i = checkInputPortNumber i - comp.GetInputUInt32 (simStepOld) (InputPortNumber i) + match numStep with + | 0 -> 0u + | _ -> comp.GetInputUInt32 (simStepOld) (InputPortNumber i) let inline insOldBigInt i = checkInputPortNumber i - comp.GetInputBigInt (simStepOld) (InputPortNumber i) + match numStep with + | 0 -> 0I + | _ -> comp.GetInputBigInt (simStepOld) (InputPortNumber i) /// Write current step output data for output port pn let inline putUInt32 pn fd = comp.PutOutputUInt32 (simStep) (OutputPortNumber pn) fd @@ -328,7 +332,7 @@ let fastReduce (maxArraySize: int) (numStep: int) (isClockedReduction: bool) (co | Constant(width, cVal), true -> putBigInt 0 <| (convertInt64ToBigInt width cVal) | Output width, false -> let bits = insUInt32 0 - //printfn "In output bits=%A, ins = %A" bits comp.InputLinks + // printfn "In output bits=%A, ins = %A" bits comp.InputLinks checkWidth width (comp.InputWidth 0) putUInt32 0 bits | Output width, true -> diff --git a/src/Renderer/Simulator/Fast/FastRun.fs b/src/Renderer/Simulator/Fast/FastRun.fs index 092bd3e03..a0c8ab114 100644 --- a/src/Renderer/Simulator/Fast/FastRun.fs +++ b/src/Renderer/Simulator/Fast/FastRun.fs @@ -509,24 +509,54 @@ let buildFastSimulationFData /// sets up default no-change input values for the next step let private propagateInputsFromLastStep (step: int) (fastSim: FastSimulation) = - if step > 0 then - fastSim.FGlobalInputComps - |> Array.iter (fun fc -> - let vec = fc.Outputs[0] - if vec.Width > 32 then - vec.BigIntStep[step] <- vec.BigIntStep[step - 1] - else - vec.UInt32Step[step] <- vec.UInt32Step[step - 1]) + let stepsim = + if step = 0 then + fastSim.MaxArraySize + else + step + fastSim.FGlobalInputComps + |> Array.iter (fun fc -> + let vec = fc.Outputs[0] + if vec.Width > 32 then + vec.BigIntStep[step] <- vec.BigIntStep[stepsim - 1] + else + vec.UInt32Step[step] <- vec.UInt32Step[stepsim - 1]) + + +let private setInputstoDefault (fastSim: FastSimulation) = + fastSim.FGlobalInputComps + |> Array.iter (fun fc -> + match fc.FType with + | Input1(w, defaultVal) -> + match defaultVal with + | Some defaultVal -> + let vec = fc.Outputs[0] + if vec.Width > 32 then + vec.BigIntStep[0] <- bigint defaultVal + else + vec.UInt32Step[0] <- uint32 defaultVal + | None -> () + | _ -> () + ) /// advance the simulation one step let private stepSimulation (fs: FastSimulation) = let index = (fs.ClockTick + 1) % fs.MaxArraySize // index of circular array + propagateInputsFromLastStep index fs - Array.iter (fastReduce fs.MaxArraySize index true) fs.FClockedComps - Array.iter (fastReduce fs.MaxArraySize index false) fs.FOrderedComps + Array.iter (fastReduce fs.MaxArraySize (fs.ClockTick + 1) true) fs.FClockedComps + Array.iter (fastReduce fs.MaxArraySize (fs.ClockTick + 1) false) fs.FOrderedComps fs.ClockTick <- fs.ClockTick + 1 +/// set simulation data for clock tick 0 when regenerating data +let private restartSimulation (fs: FastSimulation) = + setInputstoDefault fs + Array.iter (fastReduce fs.MaxArraySize 0 true) fs.FClockedComps + Array.iter (fastReduce fs.MaxArraySize 0 false) fs.FOrderedComps + + fs.ClockTick <- 0 + /// sets the mutable simulation data for a given input at a given time step let private setSimulationInput (cid: ComponentId) (fd: FastData) (step: int) (fs: FastSimulation) = match Map.tryFind (cid, []) fs.FComps, fd.Width with @@ -635,12 +665,18 @@ let extractStatefulComponents (step: int) (fastSim: FastSimulation) = let runFastSimulation (timeOut: float option) (lastStepNeeded: int) (fs: FastSimulation) : float option = if fs.MaxArraySize = 0 then failwithf "ERROR: can't run a fast simulation with 0 length arrays!" - //printfn $"running sim steps={numberOfSteps}, arraySize = {fs.MaxArraySize}, maxstepnum={fs.MaxStepNum}" + // printfn $"running sim clocktick={fs.ClockTick}, arraySize = {fs.MaxArraySize}, laststepneeded={lastStepNeeded}" let simStartTime = getTimeMs () let stepsToDo = lastStepNeeded - fs.ClockTick if stepsToDo <= 0 then - None // do nothing + if (fs.ClockTick - lastStepNeeded) < 550 then + None + else + restartSimulation fs + while fs.ClockTick < lastStepNeeded do + stepSimulation fs + None else let startTick = fs.ClockTick let mutable time = simStartTime diff --git a/src/Renderer/UI/SimulationView.fs b/src/Renderer/UI/SimulationView.fs index c2000f657..3d6c33872 100644 --- a/src/Renderer/UI/SimulationView.fs +++ b/src/Renderer/UI/SimulationView.fs @@ -90,8 +90,8 @@ let setFastSimInputsToDefault (fs:FastSimulation) = | _, None -> cid, convertIntToFastData w 0u) |> List.iter (fun (cid, wire) -> FastRun.changeInput cid (FSInterface.IData wire) 0 fs) -let InputDefaultsEqualInputs fs (model:Model) = - let tick = fs.ClockTick +let InputDefaultsEqualInputs fs (model:Model) (clocktick : int)= + let tick = clocktick fs.FComps |> Map.filter (fun cid fc -> fc.AccessPath = [] && match fc.FType with | Input1 _ -> true | _ -> false) |> Map.map (fun fid fc -> @@ -111,47 +111,8 @@ let InputDefaultsEqualInputs fs (model:Model) = |> Map.values |> Seq.forall id -let InputDefaultsEqualInputsRefresh fs (model:Model) (startsimulation: bool) = - let tick = fs.ClockTick - fs.FComps - |> Map.filter (fun cid fc -> fc.AccessPath = [] && match fc.FType with | Input1 _ -> true | _ -> false) - |> Map.map (fun fid fc -> - let cid = fst fid - if Map.containsKey cid (Optic.get SheetT.symbols_ model.Sheet) then - let typ = (Optic.get (SheetT.symbolOf_ cid) model.Sheet).Component.Type - let currdefault = match typ with - | Input1(_, Some d) -> d - | _ -> 0 - let outputarray = - if fc.OutputWidth 0 > 32 then - Array.map (fun x -> convertBigIntToInt32 x) fc.Outputs[0].BigIntStep - else - Array.map (fun x -> int x) fc.Outputs[0].UInt32Step - let slicedArray = Array.sub outputarray 0 (tick % fs.MaxArraySize) - let areAllElementsSame arr = - match arr with - | [||] -> - if startsimulation then - match typ with - | Input1(width, Some d)-> - if width > 32 then - fc.Outputs[0].BigIntStep[fs.MaxArraySize-1] <- bigint d - else - fc.Outputs[0].UInt32Step[fs.MaxArraySize-1] <- uint32 d - | _ -> () - true - | [|elem|] -> - elem = outputarray[fs.MaxArraySize-1] - | _ -> - let first = arr[0] - Array.forall (fun elem -> elem = first) arr - areAllElementsSame slicedArray - else - true) - |> Map.values - |> Seq.forall id -let setInputDefaultsFromInputs fs (dispatch: Msg -> Unit) = +let setInputDefaultsFromInputs fs (dispatch: Msg -> Unit) (clocktick: int)= let setInputDefault (newDefault: int) (sym: SymbolT.Symbol) = let comp = sym.Component let comp' = @@ -161,7 +122,7 @@ let setInputDefaultsFromInputs fs (dispatch: Msg -> Unit) = | x -> x {comp with Type = ct} {sym with Component = comp'} - let tick = fs.ClockTick + let tick = clocktick fs.FComps |> Map.filter (fun cid fc -> fc.AccessPath = [] && match fc.FType with | Input1 _ -> true | _ -> false) |> Map.map (fun fid fc -> @@ -185,7 +146,7 @@ let setInputDefaultsFromInputs fs (dispatch: Msg -> Unit) = type SimCache = { Name: string ClockTickRefresh: int - PrevInputEqualsDefaults: bool + RestartSim: bool StoredState: LoadedComponent list StoredResult: Result } @@ -195,7 +156,7 @@ type SimCache = { let simCacheInit () = { Name = ""; ClockTickRefresh = 0 - PrevInputEqualsDefaults = true + RestartSim = false StoredState = [] StoredResult = Ok { FastSim = @@ -713,19 +674,23 @@ let viewSimulationError let private simulationClockChangePopup (simData: SimulationData) (dispatch: Msg -> Unit) (model':Model) = let dialog = model'.PopupDialogData let step = simData.ClockTickNumber + let restartsimrequired (lastStepNeeded: int) = (simData.FastSim.ClockTick - lastStepNeeded) > 550 div [] [ h6 [] [str $"This simulation contains {simData.FastSim.FComps.Count} components"] (match dialog.Int with - | Some n when n > step -> - Text.p [ - Modifiers [Modifier.TextWeight TextWeight.Bold] - ] [str "Goto Tick:"] - | _ -> Text.p [ - Modifiers [ - Modifier.TextWeight TextWeight.Bold - Modifier.TextColor IsDanger] - ] [str $"The clock tick must be > {step}"]) + | Some n when restartsimrequired n -> + Text.p + [Modifiers [ + Modifier.TextWeight TextWeight.Bold + Modifier.TextColor IsDanger] + ] + [str $"To generate data for time step {n}, + the hardware will be resimulated using default inputs. "] + | _ -> + Text.p [Modifiers [ + Modifier.TextWeight TextWeight.Bold]] + [str $"Go to Tick:"]) br [] Input.number [ Input.Props [AutoFocus true;Style [Width "100px"]] @@ -850,13 +815,32 @@ let private viewSimulationData (step: int) (simData : SimulationData) model disp | false -> div [] [] | true -> div [] [ + Button.button [ + Button.Color IsSuccess + Button.Disabled (simData.ClockTickNumber = 0) + Button.OnClick (fun _ -> + if SimulationRunner.simTrace <> None then + printfn "*********************Incrementing clock from simulator button******************************" + printfn "-------------------------------------------------------------------------------------------" + //let graph = feedClockTick simData.Graph + printfn "clock %d "simData.ClockTickNumber + FastRun.runFastSimulation None (simData.ClockTickNumber-1) simData.FastSim |> ignore + dispatch <| SetSimulationGraph(simData.Graph, simData.FastSim) + if SimulationRunner.simTrace <> None then + printfn "-------------------------------------------------------------------------------------------" + printfn "*******************************************************************************************" + IncrementSimulationClockTick -1 |> dispatch + ) + ] [ str "◀" ] + str " " + str " " Button.button [ Button.Color IsSuccess Button.OnClick (fun _ -> let isDisabled (model': Model) = let dialogData = model'.PopupDialogData match dialogData.Int with - | Some n -> n <= step + | Some n -> n < 0 | None -> true dialogPopup "Advance Simulation" @@ -866,7 +850,7 @@ let private viewSimulationData (step: int) (simData : SimulationData) model disp isDisabled [] dispatch) - ] [ str "Goto" ] + ] [ str <| sprintf "Clock Tick %d" simData.ClockTickNumber ] str " " str " " Button.button [ @@ -883,7 +867,7 @@ let private viewSimulationData (step: int) (simData : SimulationData) model disp printfn "*******************************************************************************************" IncrementSimulationClockTick 1 |> dispatch ) - ] [ str <| sprintf "Clock Tick %d" simData.ClockTickNumber ] + ] [ str "▶" ] ] let maybeStatefulComponents() = let stateful = @@ -916,7 +900,7 @@ let private viewSimulationData (step: int) (simData : SimulationData) model disp ] [str txt] ] div [] [ splittedLine maybeBaseSelector maybeClockTickBtn - + Heading.h6 [ Heading.Props [ Style [ Float FloatOptions.Right ] ]] [ str <| sprintf "Simulated up to clock tick %d" simData.FastSim.ClockTick] Heading.h5 [ Heading.Props [ Style [ MarginTop "15px" ] ] ] [ str "Inputs" ] viewSimulationInputs simData.NumberBase @@ -959,7 +943,6 @@ let viewSimulation canvasState model dispatch = tryGetSimData canvasState model |> function | Ok simData -> - InputDefaultsEqualInputsRefresh simData.FastSim model true |> ignore Ok simData | Error simError -> setSimErrorFeedback simError model dispatch @@ -1008,19 +991,19 @@ let viewSimulation canvasState model dispatch = [ str buttonText ] ] | Some sim -> - match sim with - | Error simError -> () - | Ok simData -> - if not (InputDefaultsEqualInputsRefresh simData.FastSim model false) then - simCache <- {simCache with PrevInputEqualsDefaults = false} - let canvasStatechange = hasCanvasChanged canvasState simCache model let body = match sim with | Error simError -> viewSimulationError canvasState simError model StepSim dispatch | Ok simData -> + if simCache.RestartSim then + let clock = simData.ClockTickNumber + startSimulation() + simCache <- {simCache with RestartSim = false} + simCache <- {simCache with ClockTickRefresh = clock} if (simData.ClockTickNumber = 0 && not (simCache.ClockTickRefresh = 0)) then IncrementSimulationClockTick simCache.ClockTickRefresh |> dispatch FastRun.runFastSimulation None simCache.ClockTickRefresh simData.FastSim |> ignore + simCache <- {simCache with ClockTickRefresh = 0} viewSimulationData simData.ClockTickNumber simData model dispatch let setDefaultButton = match sim with @@ -1029,8 +1012,8 @@ let viewSimulation canvasState model dispatch = Button.button [ Button.Color IsInfo; - Button.Disabled (InputDefaultsEqualInputs simData.FastSim model) - Button.OnClick (fun _ -> setInputDefaultsFromInputs simData.FastSim dispatch) ; + Button.Disabled (InputDefaultsEqualInputs simData.FastSim model simData.ClockTickNumber) + Button.OnClick (fun _ -> setInputDefaultsFromInputs simData.FastSim dispatch simData.ClockTickNumber) ; Button.Props [Style [Display DisplayOptions.Inline; Float FloatOptions.Right ]] ] [ str "Save current input values as default" ] @@ -1039,9 +1022,42 @@ let viewSimulation canvasState model dispatch = | Ok _, _ -> IsSuccess, "Refresh" | Error _, _ -> IsWarning, "See Problems" + let confirmrefresh (model:Model) dispatch simData = + fun (model:Model) -> + div [] + [ + div [Style [Height "60px"; Display DisplayOptions.Block; MarginBottom "5px"]] [ + h6 [Style [Width "80%"; Float FloatOptions.Left;]] [str $"Refresh the simulation using current values of the inputs and the latest design? + The current values will be used as default for future simulations."] + Button.button [ + Button.Color IsSuccess + Button.OnClick (fun _ -> + printfn "did a refresh with ok" + setInputDefaultsFromInputs simData.FastSim dispatch simData.ClockTickNumber + simCache <- {simCache with RestartSim = true} + ClosePopup |> dispatch + ) + Button.Props [Style [Display DisplayOptions.Inline; Float FloatOptions.Right; MarginTop "10px";]] + ] [ str "Ok" ]] + hr [Style [Width "100%"; Float FloatOptions.Left;]] + div [Style [Height "50px"; Display DisplayOptions.Block;]] [ + h6 [Style [Width "80%"; Float FloatOptions.Left ]] [str $"Refresh the simulation using default values of inputs, current values will be lost."] + Button.button [ + Button.Color IsInfo + Button.OnClick (fun _ -> + let clock = simData.ClockTickNumber + startSimulation() + simCache <- {simCache with ClockTickRefresh = clock} + ClosePopup |> dispatch + ) + Button.Props [Style [Display DisplayOptions.Inline; Float FloatOptions.Right ]] + ] [ str "Reset" ]] + ] + let refreshButton = - match canvasStatechange, simCache.PrevInputEqualsDefaults, (sim), (simRes) with - | true, true, (Ok simData), _ -> + match canvasStatechange, sim with + | true, Ok simData -> + if InputDefaultsEqualInputs simData.FastSim model simData.ClockTickNumber then Button.button [ Button.Color buttonColor; @@ -1052,31 +1068,39 @@ let viewSimulation canvasState model dispatch = Button.Props [Style [Display DisplayOptions.Inline; Float FloatOptions.None; MarginLeft "5px" ]] ] [str buttonText] - | true, true, (Error _), _ -> - //refresh simulation after error is fixed + else + // add pop up Button.button [ Button.Color buttonColor; Button.OnClick (fun _ -> - let clock = simCache.ClockTickRefresh - startSimulation() - simCache <- {simCache with ClockTickRefresh = clock}) - Button.Props [Style [Display DisplayOptions.Inline; Float FloatOptions.None; MarginLeft "5px"]] + match simRes with + | Ok _, _ -> + dialogPopupRefresh + "Refresh" + (confirmrefresh model dispatch simData) + [] + dispatch + | Error _, _ -> + let clock = simData.ClockTickNumber + startSimulation() + simCache <- {simCache with ClockTickRefresh = clock}) + Button.Props [Style [Display DisplayOptions.Inline; Float FloatOptions.None; MarginLeft "5px" ]] ] [str buttonText] - | true, false, _, (Error _, _) -> - //view simulation error after canvas change (after manual change of inputs) + | true, Error _ -> Button.button [ Button.Color buttonColor; Button.OnClick (fun _ -> - let PrevInputEqualsDefaults_save = simCache.PrevInputEqualsDefaults + let clock = simCache.ClockTickRefresh startSimulation() - simCache <- {simCache with PrevInputEqualsDefaults = PrevInputEqualsDefaults_save}) + simCache <- {simCache with ClockTickRefresh = clock} + ) Button.Props [Style [Display DisplayOptions.Inline; Float FloatOptions.None; MarginLeft "5px"]] ] [str buttonText] - | _, _, _ , _ -> div [Style [Display DisplayOptions.Inline; Float FloatOptions.None; MarginLeft "5px"; Height "100%"]] [ str " "] + | false, _ -> div [Style [Display DisplayOptions.Inline; Float FloatOptions.None; MarginLeft "5px"; Height "100%"]] [ str " "] div [Style [Height "100%"]] [ div [Style [Height "40px"]] [ Button.button @@ -1093,8 +1117,7 @@ let viewSimulation canvasState model dispatch = br []; div [Style [Display DisplayOptions.Block;]] [] str "The simulation uses the diagram as it was at the moment of - pressing the \"Start simulation\" or \"Refresh\" button using default input values. - \"Refresh\" is not enabled after any manual change of input values in previous clock ticks. " + pressing the \"Start simulation\" or \"Refresh\" button using default input values." hr [] body ]