ELEC60015-HLP-CW/Tests/SimulatorTests.fs

458 lines
19 KiB
Forth

module SimulatorTests
open CommonTypes
open SimulatorTypes
open CanvasStates
open CanvasStatesWithBuses
open Simulator
/// Tuple with: (diagramName, state, loadedComponents, inputs).
type private SimulatorTestCaseInput = string * CanvasState * LoadedComponent list * (ComponentId * WireData) list
type private SimulatorTestCaseOutput = Result<(SimulationIO * WireData) list, SimulationError>
type private SimulatorTestCase = string * SimulatorTestCaseInput * SimulatorTestCaseOutput
let private makeError msg deps comps conns =
Error {
Msg = msg
InDependency = deps
ComponentsAffected = comps |> List.map ComponentId
ConnectionsAffected = conns |> List.map ConnectionId
}
/// Given a list of N generic elements, associate each element with a bit and
/// return 2^N lists with all the possible bit combinations.
/// A bit is simply a bus with width 1.
let makeAllBitCombinations (lst : 'a list) : (('a * WireData) list) list =
let rec allCombinations lst result stack =
match lst with
| [] -> List.rev stack :: result
| el :: lst' ->
let result = allCombinations lst' result ((el,[Zero]) :: stack)
allCombinations lst' result ((el,[One]) :: stack)
List.rev <| allCombinations lst [] []
/// Auto generate all the testcases for a CanvasState (i.e. a full thruth
/// table). The thruth table considers ALL inputs to be single bit inputs,
/// please do not use this function if not all inputs are like that.
let private createAllTestCases
(title : string)
(diagramName : string)
(state : CanvasState)
(dependencies : LoadedComponent list)
(inputLabels : ComponentId list)
(expectedResults : ((SimulationIO * WireData) list) list)
: SimulatorTestCase list =
let allInputCombinations = makeAllBitCombinations inputLabels
assert(List.length allInputCombinations = List.length expectedResults)
(allInputCombinations, expectedResults)
||> List.map2 (fun inputs outputs ->
sprintf "%s: %A" title inputs,
(diagramName, state, dependencies, inputs),
Ok outputs
)
// The input to a test case is formed by:
// - a CanvasState,
// - a list of loaded dependencies,
// - a list of input values that will be applied to the simulation graph.
// The dependency list and the inputs do not matter since the test has to fail
// in the earlier checks.
let private testCasesSimulatorPortError : SimulatorTestCase list = [
"Unconnected input node",
("", state1, [], []),
makeError
"All ports must have at least one connection."
None
["input-node0"]
[]
"Two unconnected input nodes",
("", state2, [], []),
makeError
"All ports must have at least one connection."
None
["input-node0"]
[]
"Two inputs and one output",
("", state5, [], []),
makeError
"A wire must have precisely one driving component, but 2 were found. If you want to merge wires together use a MergeWires component."
None
["output-node0"]
[]
"Two inputs, one And, one output, with extra connection input to output",
("", state7, [], []),
makeError
"A wire must have precisely one driving component, but 2 were found. If you want to merge wires together use a MergeWires component."
None
["output"]
[]
"Two inputs, one And, one output, with extra connections inputs to and",
("", state8, [], []),
makeError
"A wire must have precisely one driving component, but 2 were found. If you want to merge wires together use a MergeWires component."
None
["and"]
[]
"Mux2 with only two connected ports",
("", state9, [], []),
makeError
"All ports must have at least one connection."
None
["mux"]
[]
]
let private testCasesSimulatorDuplicatIOError : SimulatorTestCase list = [
"Simple circuit with duplicated output label",
("", state14, [], []),
makeError
"Two Output components cannot have the same label: output-duplicate-label."
None
["output-node0"; "output-node1"]
[]
"Simple And circuit with duplicated input label",
("", state15, [], []),
makeError
"Two Input components cannot have the same label: input-duplicate-label."
None
["top-input"; "bottom-input"]
[]
]
let private testCasesSimulatorCycleError : SimulatorTestCase list = [
"Complex diagram with three Ands and two cycles",
("", state10, [], []),
makeError
"Cycle detected in combinatorial logic."
None
["and2"; "and0"]
["conn5"; "conn4"]
"Complex diagram with three Ands and one long cycle",
("", state11, [], []),
makeError
"Cycle detected in combinatorial logic."
None
["and1"; "and2"; "and0"]
["conn1"; "conn5"; "conn3"]
]
// In the next tests, we have no dependencies.
// The inputs are set since the simulation graph should be fine.
let private testCasesSimulatorOkNoDependencies : SimulatorTestCase list =
createAllTestCases
"Simple circuit with one input and one output"
"main" state3 [] [ComponentId "input-node0"]
[
[(ComponentId "output-node0", ComponentLabel "output-node0-label", 1), [Zero]]
[(ComponentId "output-node0", ComponentLabel "output-node0-label", 1), [One]]
]
@
createAllTestCases
"Simple circuit with one input connected to two outputs"
"main" state4 [] [ComponentId "input-node0"]
[
[
(ComponentId "output-node0", ComponentLabel "output-node0-label", 1), [Zero]
(ComponentId "output-node1", ComponentLabel "output-node1-label", 1), [Zero]
]
[
(ComponentId "output-node0", ComponentLabel "output-node0-label", 1), [One]
(ComponentId "output-node1", ComponentLabel "output-node1-label", 1), [One]
]
]
@
createAllTestCases
"Two inputs; one And; one output"
"main" state6 [] [ComponentId "top-input"; ComponentId "bottom-input"]
[
[(ComponentId "output", ComponentLabel "output-node0-label", 1), [Zero]]
[(ComponentId "output", ComponentLabel "output-node0-label", 1), [Zero]]
[(ComponentId "output", ComponentLabel "output-node0-label", 1), [Zero]]
[(ComponentId "output", ComponentLabel "output-node0-label", 1), [One]]
]
@
createAllTestCases
"Weird diagram with a series of and gates"
"main" state12 [] [ComponentId "input"]
[
[(ComponentId "output", ComponentLabel "output", 1), [Zero]]
[(ComponentId "output", ComponentLabel "output", 1), [One]]
]
@
createAllTestCases
"One bit adder (Zero, Zero)"
"main" state13 [] [
ComponentId "2953603d-44e4-5c1f-3fb1-698f7863b6b5"
ComponentId "170e69f4-b3d7-d9e0-9f1d-6a564ba62062"
]
[
[
(ComponentId "9aaf18a9-b3ac-bf51-1ed3-625baa1ff6a9", ComponentLabel "Sum", 1), [Zero]
(ComponentId "94da6dd7-a263-a3ec-ec76-bfa07b0b0f34", ComponentLabel "Carry", 1), [Zero]
]
[
(ComponentId "9aaf18a9-b3ac-bf51-1ed3-625baa1ff6a9", ComponentLabel "Sum", 1), [One]
(ComponentId "94da6dd7-a263-a3ec-ec76-bfa07b0b0f34", ComponentLabel "Carry", 1), [Zero]
]
[
(ComponentId "9aaf18a9-b3ac-bf51-1ed3-625baa1ff6a9", ComponentLabel "Sum", 1), [One]
(ComponentId "94da6dd7-a263-a3ec-ec76-bfa07b0b0f34", ComponentLabel "Carry", 1), [Zero]
]
[
(ComponentId "9aaf18a9-b3ac-bf51-1ed3-625baa1ff6a9", ComponentLabel "Sum", 1), [Zero]
(ComponentId "94da6dd7-a263-a3ec-ec76-bfa07b0b0f34", ComponentLabel "Carry", 1), [One]
]
]
let testCasesSimulatorDependencyError : SimulatorTestCase list =
createAllTestCases
"Broken unused dependency." // Since the dependency is unused the test should pass.
"main" state3 [state1Dependency] [ComponentId "input-node0"]
[
[(ComponentId "output-node0", ComponentLabel "output-node0-label", 1), [Zero]]
[(ComponentId "output-node0", ComponentLabel "output-node0-label", 1), [One]]
]
@
[
// Broken dependencies.
"Input connected to broken depdendency.",
("main", state19, [state1Dependency], []),
makeError
"All ports must have at least one connection."
(Some "broken-one-input") [] []
// Dependency cycle.
"Component using itself.",
(state16Dependency.Name, state20, [state16Dependency], []),
makeError
(sprintf "Found a cycle in dependencies: \"%s\" --> \"%s\"." state16Dependency.Name state16Dependency.Name)
None [] []
"Long cycle starting at root.",
(state23Dependency.Name, state23, [state21Dependency; state22Dependency], []),
makeError
(sprintf "Found a cycle in dependencies: \"%s\" --> \"%s\" --> \"%s\" --> \"%s\"." state23Dependency.Name state21Dependency.Name state22Dependency.Name state23Dependency.Name)
None [] []
"Long cycle.",
("main", state24, [state21Dependency; state22Dependency; state23Dependency], []),
makeError
(sprintf "Found a cycle in dependencies: \"%s\" --> \"%s\" --> \"%s\" --> \"%s\"." state23Dependency.Name state21Dependency.Name state22Dependency.Name state23Dependency.Name)
None [] []
// Missing dependencies.
"2 bit full adder missing dependencies",
("2-bit-adder", twoBitAdderState, [], []),
makeError
"Could not resolve dependency: \"full-adder\". Make sure a dependency with such name exists in the current project."
(Some "2-bit-adder") [] []
"2 bit full adder missing half adder dependency",
("2-bit-adder", twoBitAdderState, [fullAdderDependency], []),
makeError
"Could not resolve dependency: \"half-adder\". Make sure a dependency with such name exists in the current project."
(Some "full-adder") [] []
"2 bit full adder missing full adder dependency",
("2-bit-adder", twoBitAdderState, [halfAdderDependency], []),
makeError
"Could not resolve dependency: \"full-adder\". Make sure a dependency with such name exists in the current project."
(Some "2-bit-adder") [] []
]
// Outputs fot the 2bit adder test.
let private zero = [
(ComponentId "dbb1f55a-edf3-bde2-4c69-43a02560e17d", ComponentLabel "Sum1", 1), [Zero]
(ComponentId "8f5bded5-f46d-722d-6108-03dda4236c01", ComponentLabel "Sum0", 1), [Zero]
(ComponentId "7d948312-376d-1d4b-cf02-90872026be16", ComponentLabel "Cout", 1), [Zero]
]
let private one = [
(ComponentId "dbb1f55a-edf3-bde2-4c69-43a02560e17d", ComponentLabel "Sum1", 1), [Zero]
(ComponentId "8f5bded5-f46d-722d-6108-03dda4236c01", ComponentLabel "Sum0", 1), [One]
(ComponentId "7d948312-376d-1d4b-cf02-90872026be16", ComponentLabel "Cout", 1), [Zero]
]
let private two = [
(ComponentId "dbb1f55a-edf3-bde2-4c69-43a02560e17d", ComponentLabel "Sum1", 1), [One]
(ComponentId "8f5bded5-f46d-722d-6108-03dda4236c01", ComponentLabel "Sum0", 1), [Zero]
(ComponentId "7d948312-376d-1d4b-cf02-90872026be16", ComponentLabel "Cout", 1), [Zero]
]
let private three = [
(ComponentId "dbb1f55a-edf3-bde2-4c69-43a02560e17d", ComponentLabel "Sum1", 1), [One]
(ComponentId "8f5bded5-f46d-722d-6108-03dda4236c01", ComponentLabel "Sum0", 1), [One]
(ComponentId "7d948312-376d-1d4b-cf02-90872026be16", ComponentLabel "Cout", 1), [Zero]
]
let private four = [
(ComponentId "dbb1f55a-edf3-bde2-4c69-43a02560e17d", ComponentLabel "Sum1", 1), [Zero]
(ComponentId "8f5bded5-f46d-722d-6108-03dda4236c01", ComponentLabel "Sum0", 1), [Zero]
(ComponentId "7d948312-376d-1d4b-cf02-90872026be16", ComponentLabel "Cout", 1), [One]
]
let private five = [
(ComponentId "dbb1f55a-edf3-bde2-4c69-43a02560e17d", ComponentLabel "Sum1", 1), [Zero]
(ComponentId "8f5bded5-f46d-722d-6108-03dda4236c01", ComponentLabel "Sum0", 1), [One]
(ComponentId "7d948312-376d-1d4b-cf02-90872026be16", ComponentLabel "Cout", 1), [One]
]
let private six = [
(ComponentId "dbb1f55a-edf3-bde2-4c69-43a02560e17d", ComponentLabel "Sum1", 1), [One]
(ComponentId "8f5bded5-f46d-722d-6108-03dda4236c01", ComponentLabel "Sum0", 1), [Zero]
(ComponentId "7d948312-376d-1d4b-cf02-90872026be16", ComponentLabel "Cout", 1), [One]
]
let private seven = [
(ComponentId "dbb1f55a-edf3-bde2-4c69-43a02560e17d", ComponentLabel "Sum1", 1), [One]
(ComponentId "8f5bded5-f46d-722d-6108-03dda4236c01", ComponentLabel "Sum0", 1), [One]
(ComponentId "7d948312-376d-1d4b-cf02-90872026be16", ComponentLabel "Cout", 1), [One]
]
let testCasesSimulatorOkWithDependencies : SimulatorTestCase list =
createAllTestCases
"Simple input-output dependency" "main"
state16 [state3Dependency] [ComponentId "outer-input-node0"]
[
[(ComponentId "outer-output-node0", ComponentLabel "outer-output-node0-label", 1), [Zero]]
[(ComponentId "outer-output-node0", ComponentLabel "outer-output-node0-label", 1), [One]]
]
@
createAllTestCases
"Nested input-output dependency" "main"
state17 [state16Dependency; state3Dependency] [ComponentId "outer-outer-input-node0"]
[
[(ComponentId "outer-outer-output-node0", ComponentLabel "outer-outer-output-node0-label", 1), [Zero]]
[(ComponentId "outer-outer-output-node0", ComponentLabel "outer-outer-output-node0-label", 1), [One]]
]
@
createAllTestCases
"Doubly nested input-output dependency" "main"
state18 [state17Dependency; state16Dependency; state3Dependency] [ComponentId "outer-outer-outer-input-node0"]
[
[(ComponentId "outer-outer-outer-output-node0", ComponentLabel "outer-outer-outer-output-node0-label", 1), [Zero]]
[(ComponentId "outer-outer-outer-output-node0", ComponentLabel "outer-outer-outer-output-node0-label", 1), [One]]
]
@
createAllTestCases
"2 bit adder" "2-bit-adder"
twoBitAdderState [fullAdderDependency; halfAdderDependency] [
ComponentId "78795182-35c4-1c50-2190-6fc944a2adea" // Cin
ComponentId "a63fe5a2-9f4d-e70f-131b-ed35d3f3a9e1" // B1
ComponentId "69a6ad2a-af19-369f-0483-0e09e6841da3" // B0
ComponentId "82a03f0b-ae31-b487-ed1b-335e235adeb7" // A1
ComponentId "86372781-c2f4-09f2-406f-f385ee7a47a9" // A0
]
[
zero;one;two;three;
one;two;three;four;
two;three;four;five;
three;four;five;six;
one;two;three;four;
two;three;four;five;
three;four;five;six;
four;five;six;seven
]
let private testCasesSimulatorBusesError : SimulatorTestCase list = [
"Two inputs make a bus2, then Push input a to bus, then try to split into 2 single bits (fail)",
("main", stateBus11, [], []),
makeError "Wrong wire width. Target port expects a 1 bit(s) signal, but source port produces a 2 bit(s) signal." None [] ["conn"]
"A 4 bit input connected to a 3 bit output",
("main", stateBus14, [], []),
makeError "Wrong wire width. Target port expects a 3 bit(s) signal, but source port produces a 4 bit(s) signal." None [] ["conn"]
"A 3 bit input connected to a 4 bit output",
("main", stateBus15, [], []),
makeError "Wrong wire width. Target port expects a 4 bit(s) signal, but source port produces a 3 bit(s) signal." None [] ["conn"]
]
let private testCasesSimulatorOkWithBuses : SimulatorTestCase list =
createAllTestCases
"Two inputs, packed into a bus, unpacked into two outputs" "main"
stateBus10 [] [
ComponentId "a91be585-2d3b-d872-be0f-b416c8eb03d2" // a
ComponentId "9985ebc6-1cd5-8863-1341-1d543d236d38" // b
]
(makeAllBitCombinations [
(ComponentId "8a9392fc-493b-7e96-72ec-b6f5f11ded8a", ComponentLabel "a-out", 1)
(ComponentId "dfcf6cff-fbac-e54f-7a9d-7059d17e3a0b", ComponentLabel "b-out", 1)
])
@
createAllTestCases
"Four inputs, packed into a bus, unpacked into four outputs" "main"
stateBus12 [] [
ComponentId "76de964a-124b-5c16-6de1-6158626344ac" // a
ComponentId "a91be585-2d3b-d872-be0f-b416c8eb03d2" // b
ComponentId "9985ebc6-1cd5-8863-1341-1d543d236d38" // c
ComponentId "9824ceb8-e999-8e48-9a56-7a4349e495b1" // d
]
(makeAllBitCombinations [
(ComponentId "59b45f9c-192c-98ce-da25-a94db45a5790", ComponentLabel "a-out", 1)
(ComponentId "8a9392fc-493b-7e96-72ec-b6f5f11ded8a", ComponentLabel "b-out", 1)
(ComponentId "dfcf6cff-fbac-e54f-7a9d-7059d17e3a0b", ComponentLabel "c-out", 1)
(ComponentId "214620f0-51f6-59fe-1558-ed47fd2c680a", ComponentLabel "d-out", 1)
])
@
[
"A 4 bit input connected to a four bit output (1)",
("main", stateBus13, [], [
ComponentId "9bcba47e-deae-0b3f-2079-a1b124526b00", [One; One; One; One]
]),
[
(ComponentId "ad2ef0c3-537e-9d2e-0064-ac6b952e4b97", ComponentLabel "b", 4), [One; One; One; One]
] |> Ok
"A 4 bit input connected to a four bit output (2)",
("main", stateBus13, [], [
ComponentId "9bcba47e-deae-0b3f-2079-a1b124526b00", [Zero; One; Zero; One]
]),
[
(ComponentId "ad2ef0c3-537e-9d2e-0064-ac6b952e4b97", ComponentLabel "b", 4), [Zero; One; Zero; One]
] |> Ok
"A 2 bit input split into 2 single bit outputs (1)",
("main", stateBus16, [], [
ComponentId "c6f000db-310f-d8ad-ff5e-938d7c2aaa7c", [One; Zero]
]),
[
(ComponentId "60e2df66-bb8c-53f1-832d-e154c30cf9dd", ComponentLabel "b", 1), [One]
(ComponentId "85e19389-c087-8b30-6c0a-02f7cc753695", ComponentLabel "c", 1), [Zero]
] |> Ok
"A 2 bit input split into 2 single bit outputs (2)",
("main", stateBus16, [], [
ComponentId "c6f000db-310f-d8ad-ff5e-938d7c2aaa7c", [Zero; One]
]),
[
(ComponentId "60e2df66-bb8c-53f1-832d-e154c30cf9dd", ComponentLabel "b", 1), [Zero]
(ComponentId "85e19389-c087-8b30-6c0a-02f7cc753695", ComponentLabel "c", 1), [One]
] |> Ok
"3 bit input merged with 4 bit input, then split in the same way",
("main", stateBus17, [], [
ComponentId "6bcdc74a-9d71-3304-537d-1a17f02924eb", [One; Zero; One] // 3 bits.
ComponentId "97c4b56d-4f8c-2b00-fb61-a08cdd01dd76", [Zero; One; Zero; One] // 4 bits.
]),
[
(ComponentId "1a6e1bb4-cfe0-77f9-a207-f409168ef210", ComponentLabel "out3", 3), [One; Zero; One]
(ComponentId "d2676492-2302-24d9-52eb-6e69e7971339", ComponentLabel "out4", 4), [Zero; One; Zero; One]
] |> Ok
]
let testCasesSimulator =
testCasesSimulatorPortError @
testCasesSimulatorCycleError @
testCasesSimulatorDuplicatIOError @
testCasesSimulatorOkNoDependencies @
testCasesSimulatorDependencyError @
testCasesSimulatorOkWithDependencies @
testCasesSimulatorBusesError @
testCasesSimulatorOkWithBuses