From 7c1d966793e3e5fc27db18d1597db1ea6b595780 Mon Sep 17 00:00:00 2001 From: Tom Clarke Date: Fri, 13 Nov 2020 02:07:47 +0000 Subject: [PATCH] fix simulation error --- src/Renderer/UI/SimulationView.fs | 8 +- src/Simulator/DependencyMerger.fs | 2 +- src/Simulator/Runner.fs | 117 ++++++++++++++++++++++++++---- src/Simulator/SimulatorTypes.fs | 7 ++ 4 files changed, 113 insertions(+), 21 deletions(-) diff --git a/src/Renderer/UI/SimulationView.fs b/src/Renderer/UI/SimulationView.fs index 19a5cd3c5..447aa22f9 100644 --- a/src/Renderer/UI/SimulationView.fs +++ b/src/Renderer/UI/SimulationView.fs @@ -134,8 +134,8 @@ let private viewSimulationInputs let newBit = match bit with | Zero -> One | One -> Zero - feedSimulationInput simulationGraph - (ComponentId inputId) [newBit] + (fst <| feedSimulationInput simulationGraph + (ComponentId inputId) [newBit]) |> SetSimulationGraph |> dispatch ) ] [ str <| bitToString bit ] @@ -156,8 +156,8 @@ let private viewSimulationInputs // Close simulation notifications. CloseSimulationNotification |> dispatch // Feed input. - feedSimulationInput simulationGraph - (ComponentId inputId) bits + (fst <| feedSimulationInput simulationGraph + (ComponentId inputId) bits) |> SetSimulationGraph |> dispatch )) ] diff --git a/src/Simulator/DependencyMerger.fs b/src/Simulator/DependencyMerger.fs index 6c653c924..78605c490 100644 --- a/src/Simulator/DependencyMerger.fs +++ b/src/Simulator/DependencyMerger.fs @@ -283,7 +283,7 @@ let private makeCustomReducer graphInputs |> List.find (fun (_, ComponentLabel inpLabel, _) -> inpLabel = inputLabel) - feedSimulationInput graph inputId wireData + fst <| feedSimulationInput graph inputId wireData ) let outputs = extractOutputValuesAsMap graph graphOutputs outputLabels diff --git a/src/Simulator/Runner.fs b/src/Simulator/Runner.fs index 41fb8a9b4..e99028fe3 100644 --- a/src/Simulator/Runner.fs +++ b/src/Simulator/Runner.fs @@ -12,7 +12,7 @@ open Helpers open SimulatorTypes open SynchronousUtils -let mutable simTrace = None +let mutable simTrace = None //Some ["adder40";"4bitbusmux20";"dff430"] // During simulation, a Component Reducer function will produce the output only // when all of the expected inputs have a value. Once this happens, it will @@ -45,16 +45,23 @@ let traceReduction action (comp:SimulationComponent) (reducerInput:ReducerInput) action (shortPSComp comp) reducerOutput.Outputs reducerInput.Inputs reducerOutput.NewState | _ -> () + +let makeProp comp outputMap = + Map.toList outputMap + |> List.map (fun (oNumb,wireDat) -> comp.Id,oNumb) + |> Set + /// Take the Input, and feed it to the Component with the specified Id. /// This function should be used to feed combinational logic inputs, not to /// trigger a clock tick. /// If the Component is then ready to produce an output, propagate this output /// by recursively feeding it as an input to the connected Components. let rec private feedInput + (withStats: bool) (graph : SimulationGraph) (compId : ComponentId) (input : InputPortNumber * WireData) - : SimulationGraph = + : SimulationGraph * Set = // Extract component. let comp = match graph.TryFind compId with | None -> failwithf "what? Could not find component %A in simulationStep" compId @@ -87,7 +94,7 @@ let rec private feedInput traceReduction "comb-feedin" comp reducerInput reducerOutput match reducerOutput.Outputs with - | None -> graph // Keep on waiting for more inputs. + | None -> graph, Set.empty // Keep on waiting for more inputs. | Some outputMap -> // Received enough inputs and produced an output. @@ -96,36 +103,104 @@ let rec private feedInput let diffedOutputMap = diffReducerInputsOrOutputs outputMap oldOutputMap // Propagate each output produced. - let graph = feedReducerOutput comp graph diffedOutputMap + let graph, compset = feedReducerOutput withStats comp graph diffedOutputMap // Update the CustomSimulationGraph and return the new simulation graph. let comp = { comp with CustomSimulationGraph = reducerOutput.NewCustomSimulationGraph } - graph.Add (comp.Id, comp) + graph.Add (comp.Id, comp), if withStats then Set.union (makeProp comp diffedOutputMap) compset else Set.empty /// Propagate each output produced by a simulation component to all the /// components connected to its output ports. /// Return the updated simulationGraph. and private feedReducerOutput + (withStats: bool) (comp : SimulationComponent) (graph : SimulationGraph) (outputMap : Map) - : SimulationGraph = - (graph, outputMap) ||> Map.fold (fun graph outPortNumber wireData -> + : SimulationGraph * Set = + ((graph,Set.empty), outputMap) ||> Map.fold (fun (graph,compset) outPortNumber wireData -> match comp.Outputs.TryFind outPortNumber with - | None when comp.Type = IOLabel -> graph // special case, these components can generate output that is connected to nothing! + | None when comp.Type = IOLabel -> graph, Set.empty // special case, these components can generate output that is connected to nothing! | None -> failwithf "what? Reducer produced inexistent output portNumber %A in component %A" outPortNumber comp | Some targets -> let lookup (cid,pNum) = graph.[cid].Label,pNum //printfn "\tComb outputs fed to -> %A" (List.map lookup targets) // Trigger simulation step with the newly produced input in // every target. - (graph, targets) ||> List.fold (fun graph (nextCompId, nextPortNumber) -> - feedInput graph nextCompId (nextPortNumber, wireData) + ((graph,compset), targets) ||> List.fold (fun (graph,compset) (nextCompId, nextPortNumber) -> + let graph, compset' = feedInput withStats graph nextCompId (nextPortNumber, wireData) + graph, if withStats then Set.union compset' compset else Set.empty ) ) + +let clockedComps (graph:SimulationGraph) = + graph + |> Map.toArray + |> Array.filter (fun (_,comp) -> couldBeSynchronousComponent comp.Type) + |> Array.sortBy (fun (cid,comp) -> match comp.Type with Custom _ -> 1 | _ -> 0) + +let calculateStateChanges (graph : SimulationGraph) : SimulationGraph * OutputChange list = + let clockedCompsBeforeTick = clockedComps graph + // For each clocked component, feed the clock tick together with the inputs + // snapshotted just before the clock tick. + ((graph,[]), clockedCompsBeforeTick) ||> Array.fold (fun (graph,changes) (compId,comp) -> + let reducerInput = { + Inputs = comp.Inputs + CustomSimulationGraph = comp.CustomSimulationGraph + IsClockTick = Yes comp.State + } + // comp>Reducer recursively calls feedClockTick (and hence calculateStateChanges) if comp is a custom component + // in that case the recursive call does all the result of internal state change + // subgraph update for the Tick - propagateStateChanges will not do that + // The change in the subgraph is Ok here because it does not affect the evaluation of any other components at this + // level until the output changes are propagated. + // Note that the subgraphs may still need later changes as a result of the delayed processing of higher-level + // output changes. This change propagation must go through the whole design including subsheets again! + let reducerTickOutput = comp.Reducer reducerInput + traceReduction (sprintf "clockTick %A" reducerInput.IsClockTick) comp reducerInput reducerTickOutput + + match reducerTickOutput.Outputs with + | None -> failwithf "what? A clocked component should ALWAYS produce outputs after a clock tick: %A" comp + | Some outputMap -> + // Note that updating the CustomSimulationGraph is necessary since + // we may be dealing with custom clocked components, which means + // the feedClockTick operaion changes the graph of that custom + // component. + let comp = { comp with CustomSimulationGraph = reducerTickOutput.NewCustomSimulationGraph + State = reducerTickOutput.NewState } + let change = {CComp = comp; COutputs = outputMap} + Map.add comp.Id comp graph, (change :: changes) + // Feed the newly produced outputs into the combinational logic. + ) + +let propagateStateChanges (graph : SimulationGraph) (changes: OutputChange list) : SimulationGraph * Set = + // For each output change recorded in changes, update graph as follows + // Update the inputs driven by the changed wires + // Recursively propagate changes through graph + // record outputs changed and do not propagate chnages that have already touched as result of propagation + ((graph,Set.empty), changes) ||> List.fold (fun (graph,compset) (change) -> + let comp = change.CComp + + let outputMap = + change.COutputs + |> Map.filter (fun outPNum wData -> not <| Set.contains (comp.Id,outPNum) compset) + if simTrace <> None then + printfn "|prop|----> %A (%A)" comp.Label outputMap + // Note that here we update component inputs in the graph as we propagate changes. + // component inputs in comp are not uptodate, but this does not matter, they are not used + let graph, compset' = feedReducerOutput true comp graph outputMap + graph, Set.union compset compset' + ) + +let feedClockTick (graph : SimulationGraph) : SimulationGraph = + calculateStateChanges graph + ||> propagateStateChanges + |> fst + +(* /// Send one global clock tick to all clocked components, and return the updated /// simulationGraph. -let feedClockTick (graph : SimulationGraph) : SimulationGraph = +let feedClockTickOld (graph : SimulationGraph) : SimulationGraph = // Take a snapshot of each clocked component with its inputs just before the // clock tick. let clockedCompsBeforeTick = @@ -141,8 +216,18 @@ let feedClockTick (graph : SimulationGraph) : SimulationGraph = CustomSimulationGraph = comp.CustomSimulationGraph IsClockTick = Yes comp.State } + let reducerTickOutput = comp.Reducer reducerInput + let comp = match graph.TryFind comp.Id with + | None -> failwith "what? Impossible case in feedClockTick" + | Some comp -> comp + let reducerInput = { + Inputs = comp.Inputs + CustomSimulationGraph = comp.CustomSimulationGraph + IsClockTick = No + } + let reducerOutput = comp.Reducer reducerInput - traceReduction (sprintf "clockTick %A" reducerInput.IsClockTick) comp reducerInput reducerOutput + match reducerOutput.Outputs with | None -> failwithf "what? A clocked component should ALWAYS produce outputs after a clock tick: %A" comp | Some outputMap -> @@ -170,12 +255,12 @@ let feedClockTick (graph : SimulationGraph) : SimulationGraph = let graph = graph.Add (comp.Id, comp) // Feed the newly produced outputs into the combinational logic. feedReducerOutput comp graph outputMap - ) + ) *) /// Feed zero to a simulation input. /// This function is supposed to be used with Components of type Input. let feedSimulationInput graph inputId wireData = - feedInput graph inputId (InputPortNumber 0, wireData) + feedInput false graph inputId (InputPortNumber 0, wireData) /// Feed in constant outputs so that they propagated to things connected to them /// This function is supposed to be used with Components of type Constant @@ -194,7 +279,7 @@ let rec feedSimulationConstants (graph:SimulationGraph) = let feedConstant graph (comp:SimulationComponent) = match comp.Type with | Constant _ -> - feedReducerOutput comp graph (Map.ofList [OutputPortNumber 0, getWireData comp]) + fst <| feedReducerOutput false comp graph (Map.ofList [OutputPortNumber 0, getWireData comp]) | Custom cComp -> Option.map feedSimulationConstants comp.CustomSimulationGraph |> (fun graphOpt -> {comp with CustomSimulationGraph = graphOpt}) @@ -216,7 +301,7 @@ let InitialiseGraphWithZeros // Feed zero to all simulation inputs. (graph, inputIds) ||> List.fold (fun graph (inputId, _, width) -> let data = List.replicate width Zero - feedSimulationInput graph inputId data + fst <| feedSimulationInput graph inputId data ) |> feedSimulationConstants diff --git a/src/Simulator/SimulatorTypes.fs b/src/Simulator/SimulatorTypes.fs index 717a010b5..aea0eee8b 100644 --- a/src/Simulator/SimulatorTypes.fs +++ b/src/Simulator/SimulatorTypes.fs @@ -31,6 +31,7 @@ type IsClockTick = | No | Yes of SimulationComponentState // Pass the state only for clock ticks. + /// Like Component but with additional dynamic info used by simulator /// Clocked components have state data. /// All components have optional data on inputs that propagates @@ -93,6 +94,12 @@ and ReducerOutput = { NewState: SimulationComponentState // Will be saved only after clock ticks. } +/// contains info needed to propagate wire value changes through a simulation. +and OutputChange = { + CComp: SimulationComponent + COutputs: Map + } + /// For every IO node, keep track of its Id, Label and wire width. /// - Id: to feed values into the simulationGraph. /// - Label: to display a nice form to the user.