CacheSim/src/CacheSim/View.elm

288 lines
11 KiB
Elm
Raw Normal View History

module CacheSim.View exposing (..)
import CacheSim.Raw exposing (..)
import CacheSim.Model exposing (..)
2019-05-28 21:01:35 -07:00
import CacheSim.Cache exposing (..)
import CacheSim.AccessView exposing (..)
2019-05-28 21:01:35 -07:00
import CacheSim.Hierarchy exposing (..)
2019-05-29 23:48:02 -07:00
import Parser exposing ((|.))
2019-05-29 18:37:31 -07:00
import Html exposing (Html, Attribute, input, text, div, label, span, h2, h3, table, tr, td, th, p, h1)
import Html.Attributes exposing (type_, class, value, for, classList, disabled, colspan, hidden)
import Html.Events exposing (onInput, onClick)
-- Button components, powered by Bootstrap
2019-05-31 23:37:32 -07:00
button : List (Attribute Msg) -> String -> Html Msg
button attrs s = input ([ type_ "button", value s, class "btn"] ++ attrs) []
2019-05-31 23:37:32 -07:00
basicButton : Msg -> String -> Html Msg
basicButton msg s = button [ onClick msg ] s
2019-05-31 23:37:32 -07:00
disabledButton : List (Attribute Msg) -> String -> Html Msg
disabledButton attrs = button (attrs ++ [ disabled True ])
dangerButton : String -> Msg -> Html Msg
2019-05-31 23:37:32 -07:00
dangerButton s m = button [ onClick m, class "btn-danger" ] s
2019-05-31 23:58:27 -07:00
infoButton : String -> Msg -> Html Msg
infoButton s m = button [ onClick m, class "btn-info" ] s
primaryButton : String -> Msg -> Html Msg
2019-05-31 23:37:32 -07:00
primaryButton s m = button [ onClick m, class "btn-primary" ] s
secondaryButton : String -> Msg -> Html Msg
2019-05-31 23:37:32 -07:00
secondaryButton s m = button [ onClick m, class "btn-secondary" ] s
2019-05-31 23:37:32 -07:00
maybeButton : Maybe a -> List (Attribute Msg) -> String -> (a -> Msg) -> Html Msg
maybeButton m attrs s f =
case m of
2019-05-31 23:37:32 -07:00
Just v -> button (attrs ++ [ onClick (f v) ]) s
_ -> disabledButton attrs s
2019-05-31 23:37:32 -07:00
resultButton : Result e a -> List (Attribute Msg) -> String -> (a -> Msg) -> Html Msg
resultButton = maybeButton << Result.toMaybe
-- Button wrapper (button group)
buttonWrapper : List (Html Msg) -> Html Msg
buttonWrapper = div [ classList [("btn-group", True), ("mb-3", True), ("mr-3", True) ] ]
buttonToolbar : List (List (Html Msg)) -> Html Msg
buttonToolbar ll = div [ class "btn-toolbar" ] <| List.map buttonWrapper ll
-- Input with a label
labeledInput : String -> String -> (String -> Msg) -> Html Msg
labeledInput s val f =
div [ class "input-group mb-3" ]
[ div [ class "input-group-prepend" ]
[ span [ class "input-group-text" ] [ text s ]
]
, input [ value val, type_ "text", class "form-control", onInput f ] []
]
-- Error view
viewError : Bool -> String -> Html Msg
viewError hide e = div
[ classList
[ ("alert", True)
, ("alert-danger", True)
]
, hidden hide
] [ text e ]
panel : List (Html Msg) -> Html Msg
panel = div [ classList [("card", True), ("p-3", True), ("mb-3", True) ] ]
viewRawCacheModel : Int -> RawCacheModel -> Html Msg
viewRawCacheModel level rcm =
let
updateBlockSize s cm = { cm | blockSize = s}
updateSetCount s cm = { cm | setCount = s}
updateSetSize s cm = { cm | setSize = s}
2019-05-28 19:37:22 -07:00
wrapUpdate f s = ChangeRawModel level (f s)
deleteButton = dangerButton "Delete" (DeleteRawModel level)
params = div []
2019-05-30 18:32:25 -07:00
[ labeledInput "Block size (words)" rcm.blockSize (wrapUpdate updateBlockSize)
, labeledInput "Set count" rcm.setCount (wrapUpdate updateSetCount)
, labeledInput "Set size" rcm.setSize (wrapUpdate updateSetSize)
]
in
panel
[ h3 [] [ text <| "L" ++ String.fromInt (level + 1) ++ " Cache" ]
, buttonWrapper [ deleteButton ]
, params
]
viewRawCacheModelHierarchy : RawCacheModelHierarchy -> Html Msg
viewRawCacheModelHierarchy rcmh =
let
models = div [ class "cache-model-levels" ]
2019-05-28 21:01:35 -07:00
<| List.indexedMap viewRawCacheModel rcmh
2019-05-28 20:08:04 -07:00
translationResult = Result.andThen validateCacheModelHierarchy
<| translateRawCacheModelHierarchy rcmh
2019-05-30 18:14:35 -07:00
checkedResult =
2019-05-28 20:08:04 -07:00
case translationResult of
2019-05-30 18:14:35 -07:00
Ok h -> if h == [] then Err "Please specify at least one cache level." else Ok h
Err e -> Err e
errorHtml =
case checkedResult of
2019-05-28 20:08:04 -07:00
Ok _ -> viewError True ""
Err e -> viewError False e
2019-05-31 23:58:27 -07:00
newButton = infoButton "Add level" CreateRawModel
useButton = resultButton checkedResult [ class "btn-info" ] "Start simulation" (UseHierarchy << Just)
in
div []
[ h2 [] [ text "Cache hierarchy" ]
2019-05-28 20:08:04 -07:00
, errorHtml
, buttonWrapper [ newButton, useButton ]
, models
]
2019-05-28 20:08:04 -07:00
2019-05-28 21:01:35 -07:00
viewCache : Int -> Cache -> Html Msg
viewCache level (cm, cs) =
let
slotLabels =
List.indexedMap (\i _ -> td [] [ text <| String.fromInt i ])
<| List.repeat cm.setSize ()
slotLabel = th [ colspan cm.setSize ] [ text "Slot" ]
2019-05-28 21:01:35 -07:00
allSlotLabels = List.concat <| List.repeat cm.setCount slotLabels
allSlotsLabel = List.repeat cm.setCount slotLabel
setLabels =
List.indexedMap (\i _ -> td [ colspan cm.setSize ] [ text <| String.fromInt i ])
<| List.repeat cm.setCount ()
setLabel = [ th [ colspan <| cm.setSize * cm.setCount ] [ text "Set" ] ]
2019-05-28 21:07:41 -07:00
setRow set =
let
slotHtml s =
case s of
Empty -> td [] [ text "" ]
2019-06-01 14:43:40 -07:00
Used l a -> td [] [ text <|
(String.fromInt (a * cm.blockSize * 4)) ++
"-" ++
(String.fromInt ((a + 1) * cm.blockSize * 4 - 1)) ]
2019-05-28 21:07:41 -07:00
in
List.map slotHtml set
cacheRow = List.concat <| List.map setRow cs
2019-05-28 21:01:35 -07:00
cacheTable =
table [ class "table" ]
[ tr [ hidden (cm.setCount == 1), class "table-info" ] setLabel
, tr [ hidden (cm.setCount == 1) ] setLabels
, tr [ hidden (cm.setSize == 1), class "table-info" ] allSlotsLabel
, tr [ hidden (cm.setSize == 1) ] allSlotLabels
2019-05-28 21:07:41 -07:00
, tr [] cacheRow
2019-05-28 21:01:35 -07:00
]
2019-05-28 21:07:41 -07:00
2019-05-28 21:01:35 -07:00
in
panel
[ h3 [] [ text <| "L" ++ String.fromInt (level + 1) ++ " Cache" ]
2019-05-28 21:01:35 -07:00
, cacheTable
]
viewCacheHierarchy : CacheHierarchy -> Html Msg
viewCacheHierarchy ch =
let
levels = div []
2019-05-28 21:01:35 -07:00
<| List.indexedMap viewCache ch
in
levels
2019-05-28 21:01:35 -07:00
viewAccessView : Model -> AccessView -> Html Msg
viewAccessView m av =
2019-05-31 23:37:32 -07:00
div []
[ h2 [] [ text "Access Simulation" ]
, p [] [ text ("Simulating access of address " ++ String.fromInt av.blockAddr) ]
, buttonToolbar
2019-05-31 23:58:27 -07:00
[ [ infoButton "Back" AccessViewBack, infoButton "Forward" AccessViewForward ]
, [ dangerButton "Stop" AccessViewCancel ]
2019-05-31 23:37:32 -07:00
]
, h3 [] [ text "Access event log" ]
, viewAccessLog av
, h3 [] [ text "Current cache state" ]
, viewCacheHierarchy <| effectiveCacheHierarchy av
]
2019-05-28 22:51:02 -07:00
viewAccessLog : AccessView -> Html Msg
2019-05-31 23:37:32 -07:00
viewAccessLog av =
2019-05-28 22:51:02 -07:00
let
resultSpan r =
2019-05-29 18:37:31 -07:00
span [ classList [ ("badge", True), ("badge-success", r == Hit), ("badge-danger", r == Miss) ] ]
[ text <| if r == Hit then "Hit" else "Miss" ]
2019-05-28 22:51:02 -07:00
downEvent n ae = div [ class "event" ]
[ text <| "L" ++ String.fromInt (n + 1) ++ " "
2019-05-28 22:51:02 -07:00
, resultSpan ae.result
]
upEvent n ae = div [ class "event" ]
2019-05-31 23:37:32 -07:00
[ text <| "Updated L" ++ String.fromInt (List.length av.accessEffects - n)
2019-05-28 22:51:02 -07:00
]
events =
2019-05-31 23:37:32 -07:00
case av.position of
Preview -> List.indexedMap downEvent av.accessEffects ++
(List.indexedMap upEvent av.accessEffects) ++
[ div [ class "event" ] [ text "Access complete. Viewing final cache state." ] ]
End -> []
Down n -> List.indexedMap downEvent <| List.take (n + 1) av.accessEffects
Up n -> List.indexedMap downEvent av.accessEffects ++
(List.indexedMap upEvent <| List.drop n av.accessEffects)
2019-05-28 22:51:02 -07:00
in
div [] events
2019-05-28 22:51:02 -07:00
2019-05-28 22:12:36 -07:00
viewAccessInput : Model -> Html Msg
viewAccessInput m =
let
2019-05-29 23:48:02 -07:00
parser =
2019-05-31 23:37:32 -07:00
Parser.sequence
{ start = ""
, end = ""
, separator = ","
, spaces = Parser.spaces
, item = Parser.int
, trailing = Parser.Optional
}
2019-05-30 18:14:35 -07:00
parseErrorToString _ = "Unable to parse input. Please enter a sequence of numbers separated by commas."
2019-05-29 23:48:02 -07:00
parseResult = Parser.run (parser |. Parser.end) m.accessInput
2019-05-30 18:14:35 -07:00
checkedResult =
2019-05-29 23:48:02 -07:00
case parseResult of
2019-05-30 18:14:35 -07:00
Ok is -> if is == [] then Err "Please enter at least one number." else Ok is
Err e -> Err <| parseErrorToString e
2019-05-31 23:58:27 -07:00
accessButton = resultButton checkedResult [ class "btn-info" ] "Access address" Access
2019-05-30 18:14:35 -07:00
errorHtml =
case checkedResult of
2019-05-29 23:48:02 -07:00
Ok _ -> viewError True ""
2019-05-30 18:14:35 -07:00
Err e -> viewError False e
editHierarchyButton = button [ onClick (UseHierarchy Nothing), class "btn-dark" ] "Edit hierarchy"
2019-05-28 22:12:36 -07:00
in
div []
2019-05-28 23:44:53 -07:00
[ h2 [] [ text "Run access simulation" ]
2019-05-30 18:33:40 -07:00
, labeledInput "Access byte address" m.accessInput ChangeAccessInput
, buttonWrapper [ accessButton, editHierarchyButton ]
2019-05-29 23:48:02 -07:00
, errorHtml
2019-05-28 22:12:36 -07:00
]
2019-05-28 21:01:35 -07:00
2019-05-29 18:37:31 -07:00
viewDescription : Html Msg
viewDescription =
div []
[ h1 [] [ text "Cache simulator" ]
, p []
[ text <| "This is a simulator for testing various cache configurations for educational purposes. It allows for the creation of"
++ " n-way associative caches, and their special cases (direct mapped and fully associative caches)."
]
, p []
[ text <| "To use the simulator, first create a fitting cache configuration by using the \"Add level\" button,"
++ " as well as the settings provided by each individual cache level. When the cache is correctly specified, and"
2019-05-31 23:37:32 -07:00
++ " no warnings appear, click \"Start simulation\" to load the specified hierarchy and begin simulating. To simulate,"
2019-05-29 18:37:31 -07:00
++ " type a block address into the \"Access address\" field, and click \"Access\". This will bring forward the simulation"
++ " view, which will allow you to step through the steps of accessing a cache."
]
]
2019-05-28 20:08:04 -07:00
viewBase : Model -> Html Msg
viewBase m =
2019-05-28 21:01:35 -07:00
let
rawView =
case m.hierarchy of
Nothing -> [ viewRawCacheModelHierarchy m.rawHierarchy ]
Just _ -> []
accessInputView =
case (m.hierarchy, m.accessView) of
(Just _, Nothing) -> [ viewAccessInput m ]
_ -> []
2019-05-28 23:44:53 -07:00
cacheView =
case m.accessView of
Nothing ->
Maybe.withDefault []
<| Maybe.map (List.singleton << viewCacheHierarchy) <| m.hierarchy
Just _ -> []
2019-05-29 23:48:02 -07:00
accessView = Maybe.withDefault [] <| Maybe.map (List.singleton << viewAccessView m) <| Maybe.andThen (List.head) <| m.accessView
2019-05-30 18:14:35 -07:00
remainingAccessView =
case Maybe.map (\l -> List.length l - 1) m.accessView of
Just n -> if n <= 0 then [] else
[ div [ class "alert", class "alert-info" ] [ text <|
"Simulating more than one access. " ++ (String.fromInt n) ++
" addresses in queue." ]
]
_ -> []
2019-05-28 21:01:35 -07:00
in
div [ class "container" ]
2019-05-30 18:14:35 -07:00
<| [ viewDescription] ++ rawView ++ accessInputView ++ remainingAccessView ++ accessView ++ cacheView