Compare commits

...

5 Commits

Author SHA1 Message Date
b96c16918b Only display certain views when they are useful. 2019-05-29 19:11:37 -07:00
23ff56ef82 Use sane defaults for cache hierarchies. 2019-05-29 19:11:25 -07:00
0552a715db Use relative links. 2019-05-29 19:11:04 -07:00
0cb9eb34d0 Add an info view. 2019-05-29 18:37:31 -07:00
6a06a99529 Use bootstrap styles.
Better than anything I can do in a short period of time!
2019-05-29 18:29:18 -07:00
5 changed files with 114 additions and 199 deletions

View File

@ -1,7 +1,8 @@
<html> <html>
<head> <head>
<script src="/static/js/elm.js"></script> <script src="static/js/elm.js"></script>
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="static/css/style.css">
</head> </head>
<body> <body>
<div id="elm"></div> <div id="elm"></div>

View File

@ -16,7 +16,7 @@ updateChangeRawModel l f m =
updateCreateRawModel : Model -> (Model, Cmd Msg) updateCreateRawModel : Model -> (Model, Cmd Msg)
updateCreateRawModel m = updateCreateRawModel m =
let let
freshRawModel = { blockSize = "", setCount = "", setSize = "" } freshRawModel = { blockSize = "4", setCount = "4", setSize = "3" }
newModel = { m | rawHierarchy = m.rawHierarchy ++ [ freshRawModel ] } newModel = { m | rawHierarchy = m.rawHierarchy ++ [ freshRawModel ] }
cmd = Cmd.none cmd = Cmd.none
in in

View File

@ -4,30 +4,68 @@ import CacheSim.Model exposing (..)
import CacheSim.Cache exposing (..) import CacheSim.Cache exposing (..)
import CacheSim.AccessView exposing (..) import CacheSim.AccessView exposing (..)
import CacheSim.Hierarchy exposing (..) import CacheSim.Hierarchy exposing (..)
import Html exposing (Html, input, text, div, label, span, h2, h3, table, tr, td) 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) import Html.Attributes exposing (type_, class, value, for, classList, disabled, colspan, hidden)
import Html.Events exposing (onInput, onClick) import Html.Events exposing (onInput, onClick)
optionalButton : Bool -> String -> Msg -> Html Msg -- Button components, powered by Bootstrap
optionalButton e s m = basicButton : List (Attribute Msg) -> String -> Html Msg
let basicButton attrs s = input ([ type_ "button", value s, class "btn", class "btn-info" ] ++ attrs) []
events = if e then [ onClick m ] else [ disabled (not e) ]
in disabledButton : String -> Html Msg
input ([ type_ "button", value s ] ++ events) [] disabledButton = basicButton [ disabled True ]
advancedButton : List (Attribute Msg) -> String -> Msg -> Html Msg
advancedButton attrs s m = basicButton (attrs ++ [ onClick m ]) s
button : String -> Msg -> Html Msg button : String -> Msg -> Html Msg
button s m = input [ type_ "button", onClick m, value s] [] button s m = basicButton [ onClick m ] s
dangerButton : String -> Msg -> Html Msg
dangerButton = advancedButton [ class "btn-danger" ]
primaryButton : String -> Msg -> Html Msg
primaryButton = advancedButton [ class "btn-primary" ]
secondaryButton : String -> Msg -> Html Msg
secondaryButton = advancedButton [ class "btn-secondary" ]
maybeButton : Maybe a -> String -> (a -> Msg) -> Html Msg
maybeButton m s f =
case m of
Just v -> button s (f v)
_ -> disabledButton s
resultButton : Result e a -> String -> (a -> Msg) -> Html Msg
resultButton = maybeButton << Result.toMaybe
-- Button wrapper (button group)
buttonWrapper : List (Html Msg) -> Html Msg buttonWrapper : List (Html Msg) -> Html Msg
buttonWrapper = div [ class "button-wrapper" ] buttonWrapper = div [ classList [("btn-group", True), ("mb-3", True) ] ]
-- Input with a label
labeledInput : String -> String -> (String -> Msg) -> Html Msg labeledInput : String -> String -> (String -> Msg) -> Html Msg
labeledInput s val f = labeledInput s val f =
div [ class "input-group" ] div [ class "input-group mb-3" ]
[ span [] [ text s ] [ div [ class "input-group-prepend" ]
, input [ value val, type_ "text", onInput f ] [] [ 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 : Int -> RawCacheModel -> Html Msg
viewRawCacheModel level rcm = viewRawCacheModel level rcm =
let let
@ -36,15 +74,15 @@ viewRawCacheModel level rcm =
updateSetSize s cm = { cm | setSize = s} updateSetSize s cm = { cm | setSize = s}
wrapUpdate f s = ChangeRawModel level (f s) wrapUpdate f s = ChangeRawModel level (f s)
deleteButton = button "Delete" (DeleteRawModel level) deleteButton = dangerButton "Delete" (DeleteRawModel level)
params = div [ class "cache-model-params" ] params = div []
[ labeledInput "Block size" rcm.blockSize (wrapUpdate updateBlockSize) [ labeledInput "Block size" rcm.blockSize (wrapUpdate updateBlockSize)
, labeledInput "Set count" rcm.setCount (wrapUpdate updateSetCount) , labeledInput "Set count" rcm.setCount (wrapUpdate updateSetCount)
, labeledInput "Set size" rcm.setSize (wrapUpdate updateSetSize) , labeledInput "Set size" rcm.setSize (wrapUpdate updateSetSize)
] ]
in in
div [ class "cache-model" ] panel
[ h3 [] [ text <| "L" ++ String.fromInt (level + 1) ++ " Cache" ] [ h3 [] [ text <| "L" ++ String.fromInt (level + 1) ++ " Cache" ]
, buttonWrapper [ deleteButton ] , buttonWrapper [ deleteButton ]
, params , params
@ -57,21 +95,15 @@ viewRawCacheModelHierarchy rcmh =
<| List.indexedMap viewRawCacheModel rcmh <| List.indexedMap viewRawCacheModel rcmh
translationResult = Result.andThen validateCacheModelHierarchy translationResult = Result.andThen validateCacheModelHierarchy
<| translateRawCacheModelHierarchy rcmh <| translateRawCacheModelHierarchy rcmh
isValid =
case translationResult of
Ok _ -> True
Err _ -> False
errorHtml = errorHtml =
case translationResult of case translationResult of
Ok _ -> viewError True "" Ok _ -> viewError True ""
Err e -> viewError False e Err e -> viewError False e
newButton = button "Add level" CreateRawModel newButton = button "Add level" CreateRawModel
useButton = case translationResult of useButton = resultButton translationResult "Use hierarchy" (UseHierarchy << Just)
Ok cmh -> optionalButton True "Use hierarchy" (UseHierarchy <| Just cmh)
Err _ -> optionalButton False "Use hierarchy" (UseHierarchy Nothing)
in in
div [ class "cache-model-hierarchy" ] div []
[ h2 [] [ text "Cache hierarchy" ] [ h2 [] [ text "Cache hierarchy" ]
, errorHtml , errorHtml
, buttonWrapper [ newButton, useButton ] , buttonWrapper [ newButton, useButton ]
@ -84,14 +116,14 @@ viewCache level (cm, cs) =
slotLabels = slotLabels =
List.indexedMap (\i _ -> td [] [ text <| String.fromInt i ]) List.indexedMap (\i _ -> td [] [ text <| String.fromInt i ])
<| List.repeat cm.setSize () <| List.repeat cm.setSize ()
slotLabel = td [ colspan cm.setSize ] [ text "Slot" ] slotLabel = th [ colspan cm.setSize ] [ text "Slot" ]
allSlotLabels = List.concat <| List.repeat cm.setCount slotLabels allSlotLabels = List.concat <| List.repeat cm.setCount slotLabels
allSlotsLabel = List.repeat cm.setCount slotLabel allSlotsLabel = List.repeat cm.setCount slotLabel
setLabels = setLabels =
List.indexedMap (\i _ -> td [ colspan cm.setSize ] [ text <| String.fromInt i ]) List.indexedMap (\i _ -> td [ colspan cm.setSize ] [ text <| String.fromInt i ])
<| List.repeat cm.setCount () <| List.repeat cm.setCount ()
setLabel = [ td [ colspan <| cm.setSize * cm.setCount ] [ text "Set" ] ] setLabel = [ th [ colspan <| cm.setSize * cm.setCount ] [ text "Set" ] ]
setRow set = setRow set =
let let
slotHtml s = slotHtml s =
@ -102,43 +134,42 @@ viewCache level (cm, cs) =
List.map slotHtml set List.map slotHtml set
cacheRow = List.concat <| List.map setRow cs cacheRow = List.concat <| List.map setRow cs
cacheTable = cacheTable =
table [] table [ class "table" ]
[ tr [ classList [("hidden", cm.setCount == 1)] ] setLabel [ tr [ hidden (cm.setCount == 1), class "table-info" ] setLabel
, tr [ classList [("hidden", cm.setCount == 1)] ] setLabels , tr [ hidden (cm.setCount == 1) ] setLabels
, tr [ classList [("hidden", cm.setSize == 1)] ] allSlotsLabel , tr [ hidden (cm.setSize == 1), class "table-info" ] allSlotsLabel
, tr [ classList [("hidden", cm.setSize == 1)] ] allSlotLabels , tr [ hidden (cm.setSize == 1) ] allSlotLabels
, tr [] cacheRow , tr [] cacheRow
] ]
in in
div [ class "cache" ] panel
[ h3 [] [ text <| "L" ++ String.fromInt level ++ " Cache" ] [ h3 [] [ text <| "L" ++ String.fromInt (level + 1) ++ " Cache" ]
, cacheTable , cacheTable
] ]
viewCacheHierarchy : CacheHierarchy -> Html Msg viewCacheHierarchy : CacheHierarchy -> Html Msg
viewCacheHierarchy ch = viewCacheHierarchy ch =
let let
levels = div [ class "cache-levels" ] levels = div []
<| List.indexedMap viewCache ch <| List.indexedMap viewCache ch
in in
div [ class "cache-hierarchy" ] <| levels
[ h2 [] [ text <| "Cache State" ]
, levels
]
viewAccessView : Model -> AccessView -> Html Msg viewAccessView : Model -> AccessView -> Html Msg
viewAccessView m av = viewAccessView m av =
let let
currentCache = Maybe.withDefault [] m.hierarchy currentCache = Maybe.withDefault [] m.hierarchy
in in
div [ class "access-view" ] div []
[ h2 [] [ text "Access Simulation" ] [ h2 [] [ text "Access Simulation" ]
, buttonWrapper , buttonWrapper
[ button "Forward" AccessViewForward [ primaryButton "Back" AccessViewBack
, button "Back" AccessViewBack , primaryButton "Forward" AccessViewForward
] ]
, h3 [] [ text "Access event log" ]
, viewAccessLog av , viewAccessLog av
, h3 [] [ text "Current cache state" ]
, viewCacheHierarchy <| effectiveCacheHierarchy currentCache av , viewCacheHierarchy <| effectiveCacheHierarchy currentCache av
] ]
@ -146,11 +177,10 @@ viewAccessLog : AccessView -> Html Msg
viewAccessLog (aes, ap) = viewAccessLog (aes, ap) =
let let
resultSpan r = resultSpan r =
case r of span [ classList [ ("badge", True), ("badge-success", r == Hit), ("badge-danger", r == Miss) ] ]
Hit -> span [ class "success" ] [ text "HIT" ] [ text <| if r == Hit then "Hit" else "Miss" ]
Miss -> span [ class "failure" ] [ text "MISS" ]
downEvent n ae = div [ class "event" ] downEvent n ae = div [ class "event" ]
[ text <| "L" ++ String.fromInt (n + 1) [ text <| "L" ++ String.fromInt (n + 1) ++ " "
, resultSpan ae.result , resultSpan ae.result
] ]
upEvent n ae = div [ class "event" ] upEvent n ae = div [ class "event" ]
@ -163,35 +193,48 @@ viewAccessLog (aes, ap) =
Up n -> List.indexedMap downEvent aes ++ Up n -> List.indexedMap downEvent aes ++
(List.indexedMap upEvent <| List.drop n aes) (List.indexedMap upEvent <| List.drop n aes)
in in
div [ class "access-log" ] div [] events
[ h3 [] [ text "Simulation events" ]
, div [ class "access-log-events" ] events
]
viewAccessInput : Model -> Html Msg viewAccessInput : Model -> Html Msg
viewAccessInput m = viewAccessInput m =
let let
accessButton = accessButton = maybeButton (String.toInt m.accessInput) "Access address" Access
case String.toInt m.accessInput of editHierarchyButton = button "Edit hierarchy" (UseHierarchy Nothing)
Just i -> optionalButton True "Access address" (Access i)
Nothing -> optionalButton False "Access address" (Access -1)
in in
div [ class "access-input" ] div []
[ h2 [] [ text "Run access simulation" ] [ h2 [] [ text "Run access simulation" ]
, div [ class "note" ]
[ text "Please make sure to click \"Use Hierarchy\" to load a hierarchy to simulate."
]
, labeledInput "Access address" m.accessInput ChangeAccessInput , labeledInput "Access address" m.accessInput ChangeAccessInput
, accessButton , buttonWrapper [ accessButton, editHierarchyButton ]
] ]
viewError : Bool -> String -> Html Msg viewDescription : Html Msg
viewError hide e = div [ classList [ ("hidden", hide), ("error", True) ] ] [ text e ] 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"
++ " no warnings appear, click \"Use hierarchy\" to load the specified hierarchy and begin simulating. To simulate,"
++ " 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."
]
]
viewBase : Model -> Html Msg viewBase : Model -> Html Msg
viewBase m = viewBase m =
let let
rawView = viewRawCacheModelHierarchy m.rawHierarchy rawView =
case m.hierarchy of
Nothing -> [ viewRawCacheModelHierarchy m.rawHierarchy ]
Just _ -> []
accessInputView =
case (m.hierarchy, m.accessView) of
(Just _, Nothing) -> [ viewAccessInput m ]
_ -> []
cacheView = cacheView =
case m.accessView of case m.accessView of
Nothing -> Nothing ->
@ -199,6 +242,6 @@ viewBase m =
<| Maybe.map (List.singleton << viewCacheHierarchy) <| m.hierarchy <| Maybe.map (List.singleton << viewCacheHierarchy) <| m.hierarchy
Just _ -> [] Just _ -> []
accessView = Maybe.withDefault [] <| Maybe.map (List.singleton << viewAccessView m) <| m.accessView accessView = Maybe.withDefault [] <| Maybe.map (List.singleton << viewAccessView m) <| m.accessView
accessInputView = [ viewAccessInput m ]
in in
div [] <| [ rawView ] ++ cacheView ++ accessView ++ accessInputView div [ class "container" ]
<| [ viewDescription] ++ rawView ++ accessInputView ++ accessView ++ cacheView

View File

@ -8,15 +8,11 @@ import CacheSim.View exposing (..)
import Browser exposing (Document, document) import Browser exposing (Document, document)
import Html exposing (text) import Html exposing (text)
testCacheModelHierarchy =
[ { blockSize = "128", setCount = "8", setSize = "1" }
]
init : Flags -> (Model, Cmd Msg) init : Flags -> (Model, Cmd Msg)
init f = init f =
let let
initialModel = initialModel =
{ rawHierarchy = testCacheModelHierarchy { rawHierarchy = []
, hierarchy = Nothing , hierarchy = Nothing
, accessView = Nothing , accessView = Nothing
, accessInput = "" , accessInput = ""

View File

@ -1,128 +1,3 @@
@import url('https://fonts.googleapis.com/css?family=Raleway&display=swap'); .btn-group {
$primary-color: lighten(#26c176, 30%); width: min-content;
body {
font-family: Raleway, serif;
}
h1, h2, h3, h4, h5, h6 {
margin-bottom: 5px;
margin-top: 5px;
}
.hidden {
display: none;
}
.input-group {
width: 100%;
display: flex;
align-items: center;
margin-bottom: 5px;
span {
margin-right: 10px;
}
input {
flex-grow: 1;
}
}
input[type="text"] {
padding: 5px;
border-radius: 5px;
border: none;
background-color: #efefef;
&:active, &:focus {
background-color: $primary-color;
transition: .25s;
}
}
.error {
box-sizing: border-box;
padding: 5px;
border-radius: 5px;
margin-bottom: 10px;
border: 2px solid tomato;
color: tomato;
}
.note {
box-sizing: border-box;
padding: 5px;
border-radius: 5px;
margin-bottom: 10px;
}
span.success {
margin-left: 10px;
color: green;
}
span.failure {
margin-left: 10px;
color: tomato;
}
button, input[type="button"] {
box-sizing: border-box;
border: none;
margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
padding: 5px;
border-radius: 5px;
font-size: 14px;
&:hover, &:focus {
background-color: $primary-color;
transition: .25s;
&:disabled {
background-color: lightgrey;
}
}
}
.cache-model-levels {
display: flex;
flex-wrap: wrap;
}
.cache-model {
max-width: 600px;
padding: 10px;
border-radius: 5px;
border: 2px solid lightgrey;
margin: 5px;
}
.cache, .cache-model {
padding: 10px;
border-radius: 5px;
border: 2px solid lightgrey;
margin: 5px;
}
.cache-model-hierarchy, .cache-hierarchy, .access-input {
margin-bottom: 10px;
}
table {
width: 100%;
}
tr {
&:nth-child(1), &:nth-child(3) {
background-color: $primary-color;
}
}
td {
text-align: center;
padding: 3px;
} }