Compare commits

...

17 Commits

Author SHA1 Message Date
f2c96180e4 Add cache range view 2019-06-01 14:43:40 -07:00
d5aed3cdeb Emphasize the SFTP in the README 2019-06-01 14:37:59 -07:00
00eefba7b7 Change public_html to code 2019-06-01 14:31:45 -07:00
59b086ce1c Add README 2019-06-01 14:30:15 -07:00
452056f71c Add some other minor visual fixes 2019-05-31 23:58:27 -07:00
7c5a62d57a Add some visual changes to improve usability 2019-05-31 23:54:11 -07:00
5a28a554a1 Change buttons to correct value 2019-05-31 23:37:32 -07:00
e6c7638324 Display access numbers and reset cache state between accesses. 2019-05-31 23:06:03 -07:00
3c0f5d34bf Make address type explicit. 2019-05-30 18:33:40 -07:00
cc12636bcd Convert byte address to words. 2019-05-30 18:32:25 -07:00
5c2c13d83e Disallow empty lists. 2019-05-30 18:14:35 -07:00
281c589dc6 Allow accessing several numbers. 2019-05-29 23:48:02 -07:00
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
9 changed files with 306 additions and 251 deletions

32
README.md Normal file
View File

@@ -0,0 +1,32 @@
# CacheSim
CacheSim is an Elm-based website to simulate various cache hierarchy configurations. It supports
fully associative, direct mapped, and n-way set associative caches.
## Building and Hosting
CacheSim is entirely a front-end project. As long as the HTML, CSS, and generated JavaScript
are hosted somewhere, it will work.
CacheSim is built using Elm and Sass. As such, you will need the compilers for the two
languages. On Arch Linux, this means `sassc` for Sass, and `elm-platform-bin` (from AUR).
On other platforms, you can use npm:
```
npm install -g elm sass
```
Now, to build the project, you can use the following commands:
```
mkdir -p static/js
mkdir -p static/css
sassc static/scss/style.scss static/css/style.css
elm make src/Main.elm --output=static/js/elm.js --optimize
```
Replace `sassc` with `sass` in the third command if you installed Sass from npm.
Now, the `index.html` file and everything in the `static` folder can be safely copied
into any directory exposed by a web server. I use my university `public_html` folder.
Below are the _SFTP commands_ I use to upload a fresh copy of the project:
```
put index.html public_html/ECE472/index.html
put -r static public_html/ECE472/static
```

25
elm.json Normal file
View File

@@ -0,0 +1,25 @@
{
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"dependencies": {
"direct": {
"elm/browser": "1.0.1",
"elm/core": "1.0.2",
"elm/html": "1.0.0",
"elm/parser": "1.1.0"
},
"indirect": {
"elm/json": "1.1.3",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.2"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}

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

@@ -2,15 +2,21 @@ module CacheSim.AccessView exposing (..)
import CacheSim.Cache exposing (..) import CacheSim.Cache exposing (..)
import CacheSim.Hierarchy exposing (..) import CacheSim.Hierarchy exposing (..)
type AccessPosition = Down Int | Up Int | Done type AccessPosition = Down Int | Up Int | Preview | End
type alias AccessView = (List (AccessEffect Cache), AccessPosition) type alias AccessView =
{ initialState : CacheHierarchy
, blockAddr : BlockAddr
, accessEffects : List (AccessEffect Cache)
, position : AccessPosition
}
accessPositionForward : Int -> AccessPosition -> AccessPosition accessPositionForward : Int -> AccessPosition -> AccessPosition
accessPositionForward depth av = accessPositionForward depth av =
case av of case av of
Down n -> if n == depth - 1 then Up n else Down (n + 1) Down n -> if n == depth - 1 then Up n else Down (n + 1)
Up n -> if n == 0 then Done else Up (n - 1) Up n -> if n == 0 then Preview else Up (n - 1)
Done -> Done Preview -> End
End -> End
accessPositionBack : Int -> AccessPosition -> AccessPosition accessPositionBack : Int -> AccessPosition -> AccessPosition
accessPositionBack depth av = accessPositionBack depth av =
@@ -18,36 +24,43 @@ accessPositionBack depth av =
Down 0 -> Down 0 Down 0 -> Down 0
Down n -> Down (n-1) Down n -> Down (n-1)
Up n -> if n == (depth - 1) then Down n else Up (n + 1) Up n -> if n == (depth - 1) then Down n else Up (n + 1)
Done -> Up 0 Preview -> Up 0
End -> Preview
accessPositionDone : AccessPosition -> Bool accessPositionDone : AccessPosition -> Bool
accessPositionDone av = accessPositionDone av =
case av of case av of
Done -> True End -> True
_ -> False _ -> False
accessViewForward : AccessView -> AccessView accessViewForward : AccessView -> AccessView
accessViewForward (l, ap) = (l, accessPositionForward (List.length l) ap) accessViewForward av = { av | position = accessPositionForward (List.length av.accessEffects) av.position }
accessViewBack : AccessView -> AccessView accessViewBack : AccessView -> AccessView
accessViewBack (l, ap) = (l, accessPositionBack (List.length l) ap) accessViewBack av = { av | position = accessPositionBack (List.length av.accessEffects) av.position }
accessViewDone : AccessView -> Bool accessViewDone : AccessView -> Bool
accessViewDone (_, ap) = accessPositionDone ap accessViewDone av = accessPositionDone av.position
effectiveCacheHierarchy : CacheHierarchy -> AccessView -> CacheHierarchy finalCacheHierarchy : AccessView -> CacheHierarchy
effectiveCacheHierarchy c (l, ap) = finalCacheHierarchy av =
List.map .output av.accessEffects ++ List.drop (List.length av.accessEffects) av.initialState
effectiveCacheHierarchy : AccessView -> CacheHierarchy
effectiveCacheHierarchy av =
let let
finalContents = List.map .output l finalContents = List.map .output av.accessEffects
unaccessed = List.drop (List.length l) c unaccessed = List.drop (List.length av.accessEffects) av.initialState
notDone = notDone =
case ap of case av.position of
Done -> [] Preview -> []
Down _ -> List.take (List.length l) c End -> []
Up n -> List.take n c Down _ -> List.take (List.length av.accessEffects) av.initialState
Up n -> List.take n av.initialState
done = done =
case ap of case av.position of
Done -> finalContents Preview -> finalContents
End -> finalContents
Down _ -> [] Down _ -> []
Up n -> List.drop n finalContents Up n -> List.drop n finalContents
in in

View File

@@ -6,7 +6,7 @@ import CacheSim.AccessView exposing (..)
type alias Model = type alias Model =
{ rawHierarchy : RawCacheModelHierarchy { rawHierarchy : RawCacheModelHierarchy
, hierarchy : Maybe CacheHierarchy , hierarchy : Maybe CacheHierarchy
, accessView : Maybe AccessView , accessView : Maybe (List AccessView)
, accessInput : String , accessInput : String
} }
type alias Flags = () type alias Flags = ()
@@ -15,7 +15,8 @@ type Msg
| CreateRawModel | CreateRawModel
| DeleteRawModel Int | DeleteRawModel Int
| UseHierarchy (Maybe CacheModelHierarchy) | UseHierarchy (Maybe CacheModelHierarchy)
| Access Int | Access (List Int)
| ChangeAccessInput String | ChangeAccessInput String
| AccessViewCancel
| AccessViewForward | AccessViewForward
| AccessViewBack | AccessViewBack

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
@@ -38,11 +38,32 @@ updateUseHierarchy cmh m =
in in
(newModel, cmd) (newModel, cmd)
updateAccess : Int -> Model -> (Model, Cmd Msg) updateAccess : List Int -> Model -> (Model, Cmd Msg)
updateAccess i m = updateAccess li m =
let let
accessResult = Maybe.andThen (Result.toMaybe << accessCacheHierarchy i) m.hierarchy process c xs =
newModel = { m | accessView = Maybe.map (\ar -> (ar, Down 0)) accessResult } case xs of
[] -> Ok []
(i::t) ->
case accessCacheHierarchy (i // 4) c of
Ok av ->
let
newView = { blockAddr = i, accessEffects = av, position = Down 0, initialState = c }
in
Result.map ((::) newView)
<| process (finalCacheHierarchy newView) t
Err s -> Err s
accessResult = Maybe.andThen (\h -> Result.toMaybe <| process h li) m.hierarchy
newModel = { m | accessView = accessResult }
cmd = Cmd.none
in
(newModel, cmd)
updateAccessViewCancel : Model -> (Model, Cmd Msg)
updateAccessViewCancel m =
let
newModel = { m | accessView = Nothing }
cmd = Cmd.none cmd = Cmd.none
in in
(newModel, cmd) (newModel, cmd)
@@ -50,13 +71,16 @@ updateAccess i m =
updateAccessViewForward : Model -> (Model, Cmd Msg) updateAccessViewForward : Model -> (Model, Cmd Msg)
updateAccessViewForward m = updateAccessViewForward m =
let let
afterStep = Maybe.map accessViewForward m.accessView afterStep = Maybe.map (intMapUpdate 0 accessViewForward) m.accessView
replaceHierarchy avs h = List.map .output avs ++ List.drop (List.length avs) h newAccessView =
(newHierarchy, newAccessView) =
case afterStep of case afterStep of
Just (avs, Done) -> (Maybe.map (replaceHierarchy avs) m.hierarchy, Nothing) Just (result::xs) ->
as_ -> (m.hierarchy, as_) case (result.position, xs) of
newModel = { m | accessView = newAccessView, hierarchy = newHierarchy } (End, []) -> Nothing
(End, nxs) -> Just nxs
_ -> Just (result::xs)
as_ -> as_
newModel = { m | accessView = newAccessView }
cmd = Cmd.none cmd = Cmd.none
in in
(newModel, cmd) (newModel, cmd)
@@ -64,7 +88,7 @@ updateAccessViewForward m =
updateAccessViewBack : Model -> (Model, Cmd Msg) updateAccessViewBack : Model -> (Model, Cmd Msg)
updateAccessViewBack m = updateAccessViewBack m =
let let
afterStep = Maybe.map accessViewBack m.accessView afterStep = Maybe.map (intMapUpdate 0 accessViewBack) m.accessView
newModel = { m | accessView = afterStep } newModel = { m | accessView = afterStep }
cmd = Cmd.none cmd = Cmd.none
in in

View File

@@ -4,30 +4,72 @@ 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 Parser exposing ((|.))
import Html.Attributes exposing (type_, class, value, for, classList, disabled, colspan) 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) import Html.Events exposing (onInput, onClick)
optionalButton : Bool -> String -> Msg -> Html Msg -- Button components, powered by Bootstrap
optionalButton e s m = button : List (Attribute Msg) -> String -> Html Msg
let button attrs s = input ([ type_ "button", value s, class "btn"] ++ attrs) []
events = if e then [ onClick m ] else [ disabled (not e) ]
in
input ([ type_ "button", value s ] ++ events) []
button : String -> Msg -> Html Msg basicButton : Msg -> String -> Html Msg
button s m = input [ type_ "button", onClick m, value s] [] basicButton msg s = button [ onClick msg ] s
disabledButton : List (Attribute Msg) -> String -> Html Msg
disabledButton attrs = button (attrs ++ [ disabled True ])
dangerButton : String -> Msg -> Html Msg
dangerButton s m = button [ onClick m, class "btn-danger" ] s
infoButton : String -> Msg -> Html Msg
infoButton s m = button [ onClick m, class "btn-info" ] s
primaryButton : String -> Msg -> Html Msg
primaryButton s m = button [ onClick m, class "btn-primary" ] s
secondaryButton : String -> Msg -> Html Msg
secondaryButton s m = button [ onClick m, class "btn-secondary" ] s
maybeButton : Maybe a -> List (Attribute Msg) -> String -> (a -> Msg) -> Html Msg
maybeButton m attrs s f =
case m of
Just v -> button (attrs ++ [ onClick (f v) ]) s
_ -> disabledButton attrs s
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 : List (Html Msg) -> Html Msg
buttonWrapper = div [ class "button-wrapper" ] 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 : 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 +78,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 (words)" 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 +99,19 @@ viewRawCacheModelHierarchy rcmh =
<| List.indexedMap viewRawCacheModel rcmh <| List.indexedMap viewRawCacheModel rcmh
translationResult = Result.andThen validateCacheModelHierarchy translationResult = Result.andThen validateCacheModelHierarchy
<| translateRawCacheModelHierarchy rcmh <| translateRawCacheModelHierarchy rcmh
isValid = checkedResult =
case translationResult of case translationResult of
Ok _ -> True Ok h -> if h == [] then Err "Please specify at least one cache level." else Ok h
Err _ -> False Err e -> Err e
errorHtml = errorHtml =
case translationResult of case checkedResult of
Ok _ -> viewError True "" Ok _ -> viewError True ""
Err e -> viewError False e Err e -> viewError False e
newButton = button "Add level" CreateRawModel newButton = infoButton "Add level" CreateRawModel
useButton = case translationResult of useButton = resultButton checkedResult [ class "btn-info" ] "Start simulation" (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,121 +124,164 @@ 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 =
case s of case s of
Empty -> td [] [ text "" ] Empty -> td [] [ text "" ]
Used l a -> td [] [ text <| String.fromInt a ] Used l a -> td [] [ text <|
(String.fromInt (a * cm.blockSize * 4)) ++
"-" ++
(String.fromInt ((a + 1) * cm.blockSize * 4 - 1)) ]
in in
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 div []
currentCache = Maybe.withDefault [] m.hierarchy [ h2 [] [ text "Access Simulation" ]
in , p [] [ text ("Simulating access of address " ++ String.fromInt av.blockAddr) ]
div [ class "access-view" ] , buttonToolbar
[ h2 [] [ text "Access Simulation" ] [ [ infoButton "Back" AccessViewBack, infoButton "Forward" AccessViewForward ]
, buttonWrapper , [ dangerButton "Stop" AccessViewCancel ]
[ button "Forward" AccessViewForward ]
, button "Back" AccessViewBack , h3 [] [ text "Access event log" ]
] , viewAccessLog av
, viewAccessLog av , h3 [] [ text "Current cache state" ]
, viewCacheHierarchy <| effectiveCacheHierarchy currentCache av , viewCacheHierarchy <| effectiveCacheHierarchy av
] ]
viewAccessLog : AccessView -> Html Msg viewAccessLog : AccessView -> Html Msg
viewAccessLog (aes, ap) = viewAccessLog av =
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" ]
[ text <| "Updated L" ++ String.fromInt (List.length aes - n) [ text <| "Updated L" ++ String.fromInt (List.length av.accessEffects - n)
] ]
events = events =
case ap of case av.position of
Done -> [] Preview -> List.indexedMap downEvent av.accessEffects ++
Down n -> List.indexedMap downEvent <| List.take (n + 1) aes (List.indexedMap upEvent av.accessEffects) ++
Up n -> List.indexedMap downEvent aes ++ [ div [ class "event" ] [ text "Access complete. Viewing final cache state." ] ]
(List.indexedMap upEvent <| List.drop n aes) 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)
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 = parser =
case String.toInt m.accessInput of Parser.sequence
Just i -> optionalButton True "Access address" (Access i) { start = ""
Nothing -> optionalButton False "Access address" (Access -1) , end = ""
, separator = ","
, spaces = Parser.spaces
, item = Parser.int
, trailing = Parser.Optional
}
parseErrorToString _ = "Unable to parse input. Please enter a sequence of numbers separated by commas."
parseResult = Parser.run (parser |. Parser.end) m.accessInput
checkedResult =
case parseResult of
Ok is -> if is == [] then Err "Please enter at least one number." else Ok is
Err e -> Err <| parseErrorToString e
accessButton = resultButton checkedResult [ class "btn-info" ] "Access address" Access
errorHtml =
case checkedResult of
Ok _ -> viewError True ""
Err e -> viewError False e
editHierarchyButton = button [ onClick (UseHierarchy Nothing), class "btn-dark" ] "Edit hierarchy"
in in
div [ class "access-input" ] div []
[ h2 [] [ text "Run access simulation" ] [ h2 [] [ text "Run access simulation" ]
, div [ class "note" ] , labeledInput "Access byte address" m.accessInput ChangeAccessInput
[ text "Please make sure to click \"Use Hierarchy\" to load a hierarchy to simulate." , buttonWrapper [ accessButton, editHierarchyButton ]
] , errorHtml
, labeledInput "Access address" m.accessInput ChangeAccessInput
, accessButton
] ]
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 \"Start simulation\" 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 ->
Maybe.withDefault [] Maybe.withDefault []
<| 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) <| Maybe.andThen (List.head) <| m.accessView
accessInputView = [ viewAccessInput m ] 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." ]
]
_ -> []
in in
div [] <| [ rawView ] ++ cacheView ++ accessView ++ accessInputView div [ class "container" ]
<| [ viewDescription] ++ rawView ++ accessInputView ++ remainingAccessView ++ 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 = ""
@@ -39,6 +35,7 @@ update msg m =
UseHierarchy cmh -> updateUseHierarchy cmh m UseHierarchy cmh -> updateUseHierarchy cmh m
Access i -> updateAccess i m Access i -> updateAccess i m
ChangeAccessInput s -> ({ m | accessInput = s }, Cmd.none) ChangeAccessInput s -> ({ m | accessInput = s }, Cmd.none)
AccessViewCancel -> updateAccessViewCancel m
AccessViewForward -> updateAccessViewForward m AccessViewForward -> updateAccessViewForward m
AccessViewBack -> updateAccessViewBack m AccessViewBack -> updateAccessViewBack m

View File

@@ -1,128 +1,7 @@
@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 { .btn:focus {
margin-bottom: 5px; box-shadow: none;
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;
} }