Compare commits
17 Commits
5710f10999
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| f2c96180e4 | |||
| d5aed3cdeb | |||
| 00eefba7b7 | |||
| 59b086ce1c | |||
| 452056f71c | |||
| 7c5a62d57a | |||
| 5a28a554a1 | |||
| e6c7638324 | |||
| 3c0f5d34bf | |||
| cc12636bcd | |||
| 5c2c13d83e | |||
| 281c589dc6 | |||
| b96c16918b | |||
| 23ff56ef82 | |||
| 0552a715db | |||
| 0cb9eb34d0 | |||
| 6a06a99529 |
32
README.md
Normal file
32
README.md
Normal 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
25
elm.json
Normal 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": {}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<script src="/static/js/elm.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<script src="static/js/elm.js"></script>
|
||||
<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>
|
||||
<body>
|
||||
<div id="elm"></div>
|
||||
|
||||
@@ -2,15 +2,21 @@ module CacheSim.AccessView exposing (..)
|
||||
import CacheSim.Cache exposing (..)
|
||||
import CacheSim.Hierarchy exposing (..)
|
||||
|
||||
type AccessPosition = Down Int | Up Int | Done
|
||||
type alias AccessView = (List (AccessEffect Cache), AccessPosition)
|
||||
type AccessPosition = Down Int | Up Int | Preview | End
|
||||
type alias AccessView =
|
||||
{ initialState : CacheHierarchy
|
||||
, blockAddr : BlockAddr
|
||||
, accessEffects : List (AccessEffect Cache)
|
||||
, position : AccessPosition
|
||||
}
|
||||
|
||||
accessPositionForward : Int -> AccessPosition -> AccessPosition
|
||||
accessPositionForward depth av =
|
||||
case av of
|
||||
Down n -> if n == depth - 1 then Up n else Down (n + 1)
|
||||
Up n -> if n == 0 then Done else Up (n - 1)
|
||||
Done -> Done
|
||||
Up n -> if n == 0 then Preview else Up (n - 1)
|
||||
Preview -> End
|
||||
End -> End
|
||||
|
||||
accessPositionBack : Int -> AccessPosition -> AccessPosition
|
||||
accessPositionBack depth av =
|
||||
@@ -18,36 +24,43 @@ accessPositionBack depth av =
|
||||
Down 0 -> Down 0
|
||||
Down n -> Down (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 av =
|
||||
case av of
|
||||
Done -> True
|
||||
End -> True
|
||||
_ -> False
|
||||
|
||||
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 (l, ap) = (l, accessPositionBack (List.length l) ap)
|
||||
accessViewBack av = { av | position = accessPositionBack (List.length av.accessEffects) av.position }
|
||||
|
||||
accessViewDone : AccessView -> Bool
|
||||
accessViewDone (_, ap) = accessPositionDone ap
|
||||
accessViewDone av = accessPositionDone av.position
|
||||
|
||||
effectiveCacheHierarchy : CacheHierarchy -> AccessView -> CacheHierarchy
|
||||
effectiveCacheHierarchy c (l, ap) =
|
||||
finalCacheHierarchy : AccessView -> CacheHierarchy
|
||||
finalCacheHierarchy av =
|
||||
List.map .output av.accessEffects ++ List.drop (List.length av.accessEffects) av.initialState
|
||||
|
||||
effectiveCacheHierarchy : AccessView -> CacheHierarchy
|
||||
effectiveCacheHierarchy av =
|
||||
let
|
||||
finalContents = List.map .output l
|
||||
unaccessed = List.drop (List.length l) c
|
||||
finalContents = List.map .output av.accessEffects
|
||||
unaccessed = List.drop (List.length av.accessEffects) av.initialState
|
||||
notDone =
|
||||
case ap of
|
||||
Done -> []
|
||||
Down _ -> List.take (List.length l) c
|
||||
Up n -> List.take n c
|
||||
case av.position of
|
||||
Preview -> []
|
||||
End -> []
|
||||
Down _ -> List.take (List.length av.accessEffects) av.initialState
|
||||
Up n -> List.take n av.initialState
|
||||
done =
|
||||
case ap of
|
||||
Done -> finalContents
|
||||
case av.position of
|
||||
Preview -> finalContents
|
||||
End -> finalContents
|
||||
Down _ -> []
|
||||
Up n -> List.drop n finalContents
|
||||
in
|
||||
|
||||
@@ -6,7 +6,7 @@ import CacheSim.AccessView exposing (..)
|
||||
type alias Model =
|
||||
{ rawHierarchy : RawCacheModelHierarchy
|
||||
, hierarchy : Maybe CacheHierarchy
|
||||
, accessView : Maybe AccessView
|
||||
, accessView : Maybe (List AccessView)
|
||||
, accessInput : String
|
||||
}
|
||||
type alias Flags = ()
|
||||
@@ -15,7 +15,8 @@ type Msg
|
||||
| CreateRawModel
|
||||
| DeleteRawModel Int
|
||||
| UseHierarchy (Maybe CacheModelHierarchy)
|
||||
| Access Int
|
||||
| Access (List Int)
|
||||
| ChangeAccessInput String
|
||||
| AccessViewCancel
|
||||
| AccessViewForward
|
||||
| AccessViewBack
|
||||
|
||||
@@ -16,7 +16,7 @@ updateChangeRawModel l f m =
|
||||
updateCreateRawModel : Model -> (Model, Cmd Msg)
|
||||
updateCreateRawModel m =
|
||||
let
|
||||
freshRawModel = { blockSize = "", setCount = "", setSize = "" }
|
||||
freshRawModel = { blockSize = "4", setCount = "4", setSize = "3" }
|
||||
newModel = { m | rawHierarchy = m.rawHierarchy ++ [ freshRawModel ] }
|
||||
cmd = Cmd.none
|
||||
in
|
||||
@@ -38,11 +38,32 @@ updateUseHierarchy cmh m =
|
||||
in
|
||||
(newModel, cmd)
|
||||
|
||||
updateAccess : Int -> Model -> (Model, Cmd Msg)
|
||||
updateAccess i m =
|
||||
updateAccess : List Int -> Model -> (Model, Cmd Msg)
|
||||
updateAccess li m =
|
||||
let
|
||||
accessResult = Maybe.andThen (Result.toMaybe << accessCacheHierarchy i) m.hierarchy
|
||||
newModel = { m | accessView = Maybe.map (\ar -> (ar, Down 0)) accessResult }
|
||||
process c xs =
|
||||
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
|
||||
in
|
||||
(newModel, cmd)
|
||||
@@ -50,13 +71,16 @@ updateAccess i m =
|
||||
updateAccessViewForward : Model -> (Model, Cmd Msg)
|
||||
updateAccessViewForward m =
|
||||
let
|
||||
afterStep = Maybe.map accessViewForward m.accessView
|
||||
replaceHierarchy avs h = List.map .output avs ++ List.drop (List.length avs) h
|
||||
(newHierarchy, newAccessView) =
|
||||
afterStep = Maybe.map (intMapUpdate 0 accessViewForward) m.accessView
|
||||
newAccessView =
|
||||
case afterStep of
|
||||
Just (avs, Done) -> (Maybe.map (replaceHierarchy avs) m.hierarchy, Nothing)
|
||||
as_ -> (m.hierarchy, as_)
|
||||
newModel = { m | accessView = newAccessView, hierarchy = newHierarchy }
|
||||
Just (result::xs) ->
|
||||
case (result.position, xs) of
|
||||
(End, []) -> Nothing
|
||||
(End, nxs) -> Just nxs
|
||||
_ -> Just (result::xs)
|
||||
as_ -> as_
|
||||
newModel = { m | accessView = newAccessView }
|
||||
cmd = Cmd.none
|
||||
in
|
||||
(newModel, cmd)
|
||||
@@ -64,7 +88,7 @@ updateAccessViewForward m =
|
||||
updateAccessViewBack : Model -> (Model, Cmd Msg)
|
||||
updateAccessViewBack m =
|
||||
let
|
||||
afterStep = Maybe.map accessViewBack m.accessView
|
||||
afterStep = Maybe.map (intMapUpdate 0 accessViewBack) m.accessView
|
||||
newModel = { m | accessView = afterStep }
|
||||
cmd = Cmd.none
|
||||
in
|
||||
|
||||
@@ -4,30 +4,72 @@ import CacheSim.Model exposing (..)
|
||||
import CacheSim.Cache exposing (..)
|
||||
import CacheSim.AccessView exposing (..)
|
||||
import CacheSim.Hierarchy exposing (..)
|
||||
import Html exposing (Html, input, text, div, label, span, h2, h3, table, tr, td)
|
||||
import Html.Attributes exposing (type_, class, value, for, classList, disabled, colspan)
|
||||
import Parser exposing ((|.))
|
||||
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)
|
||||
|
||||
optionalButton : Bool -> String -> Msg -> Html Msg
|
||||
optionalButton e s m =
|
||||
let
|
||||
events = if e then [ onClick m ] else [ disabled (not e) ]
|
||||
in
|
||||
input ([ type_ "button", value s ] ++ events) []
|
||||
-- Button components, powered by Bootstrap
|
||||
button : List (Attribute Msg) -> String -> Html Msg
|
||||
button attrs s = input ([ type_ "button", value s, class "btn"] ++ attrs) []
|
||||
|
||||
button : String -> Msg -> Html Msg
|
||||
button s m = input [ type_ "button", onClick m, value s] []
|
||||
basicButton : Msg -> String -> Html Msg
|
||||
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 = 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 s val f =
|
||||
div [ class "input-group" ]
|
||||
[ span [] [ text s ]
|
||||
, input [ value val, type_ "text", onInput 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
|
||||
@@ -36,15 +78,15 @@ viewRawCacheModel level rcm =
|
||||
updateSetSize s cm = { cm | setSize = s}
|
||||
wrapUpdate f s = ChangeRawModel level (f s)
|
||||
|
||||
deleteButton = button "Delete" (DeleteRawModel level)
|
||||
deleteButton = dangerButton "Delete" (DeleteRawModel level)
|
||||
|
||||
params = div [ class "cache-model-params" ]
|
||||
[ labeledInput "Block size" rcm.blockSize (wrapUpdate updateBlockSize)
|
||||
params = div []
|
||||
[ labeledInput "Block size (words)" rcm.blockSize (wrapUpdate updateBlockSize)
|
||||
, labeledInput "Set count" rcm.setCount (wrapUpdate updateSetCount)
|
||||
, labeledInput "Set size" rcm.setSize (wrapUpdate updateSetSize)
|
||||
]
|
||||
in
|
||||
div [ class "cache-model" ]
|
||||
panel
|
||||
[ h3 [] [ text <| "L" ++ String.fromInt (level + 1) ++ " Cache" ]
|
||||
, buttonWrapper [ deleteButton ]
|
||||
, params
|
||||
@@ -57,21 +99,19 @@ viewRawCacheModelHierarchy rcmh =
|
||||
<| List.indexedMap viewRawCacheModel rcmh
|
||||
translationResult = Result.andThen validateCacheModelHierarchy
|
||||
<| translateRawCacheModelHierarchy rcmh
|
||||
isValid =
|
||||
checkedResult =
|
||||
case translationResult of
|
||||
Ok _ -> True
|
||||
Err _ -> False
|
||||
Ok h -> if h == [] then Err "Please specify at least one cache level." else Ok h
|
||||
Err e -> Err e
|
||||
errorHtml =
|
||||
case translationResult of
|
||||
case checkedResult of
|
||||
Ok _ -> viewError True ""
|
||||
Err e -> viewError False e
|
||||
|
||||
newButton = button "Add level" CreateRawModel
|
||||
useButton = case translationResult of
|
||||
Ok cmh -> optionalButton True "Use hierarchy" (UseHierarchy <| Just cmh)
|
||||
Err _ -> optionalButton False "Use hierarchy" (UseHierarchy Nothing)
|
||||
newButton = infoButton "Add level" CreateRawModel
|
||||
useButton = resultButton checkedResult [ class "btn-info" ] "Start simulation" (UseHierarchy << Just)
|
||||
in
|
||||
div [ class "cache-model-hierarchy" ]
|
||||
div []
|
||||
[ h2 [] [ text "Cache hierarchy" ]
|
||||
, errorHtml
|
||||
, buttonWrapper [ newButton, useButton ]
|
||||
@@ -84,121 +124,164 @@ viewCache level (cm, cs) =
|
||||
slotLabels =
|
||||
List.indexedMap (\i _ -> td [] [ text <| String.fromInt i ])
|
||||
<| 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
|
||||
allSlotsLabel = List.repeat cm.setCount slotLabel
|
||||
|
||||
setLabels =
|
||||
List.indexedMap (\i _ -> td [ colspan cm.setSize ] [ text <| String.fromInt i ])
|
||||
<| List.repeat cm.setCount ()
|
||||
setLabel = [ td [ colspan <| cm.setSize * cm.setCount ] [ text "Set" ] ]
|
||||
setLabel = [ th [ colspan <| cm.setSize * cm.setCount ] [ text "Set" ] ]
|
||||
setRow set =
|
||||
let
|
||||
slotHtml s =
|
||||
case s of
|
||||
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
|
||||
List.map slotHtml set
|
||||
cacheRow = List.concat <| List.map setRow cs
|
||||
cacheTable =
|
||||
table []
|
||||
[ tr [ classList [("hidden", cm.setCount == 1)] ] setLabel
|
||||
, tr [ classList [("hidden", cm.setCount == 1)] ] setLabels
|
||||
, tr [ classList [("hidden", cm.setSize == 1)] ] allSlotsLabel
|
||||
, tr [ classList [("hidden", cm.setSize == 1)] ] allSlotLabels
|
||||
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
|
||||
, tr [] cacheRow
|
||||
]
|
||||
|
||||
in
|
||||
div [ class "cache" ]
|
||||
[ h3 [] [ text <| "L" ++ String.fromInt level ++ " Cache" ]
|
||||
panel
|
||||
[ h3 [] [ text <| "L" ++ String.fromInt (level + 1) ++ " Cache" ]
|
||||
, cacheTable
|
||||
]
|
||||
|
||||
viewCacheHierarchy : CacheHierarchy -> Html Msg
|
||||
viewCacheHierarchy ch =
|
||||
let
|
||||
levels = div [ class "cache-levels" ]
|
||||
levels = div []
|
||||
<| List.indexedMap viewCache ch
|
||||
in
|
||||
div [ class "cache-hierarchy" ] <|
|
||||
[ h2 [] [ text <| "Cache State" ]
|
||||
, levels
|
||||
]
|
||||
levels
|
||||
|
||||
viewAccessView : Model -> AccessView -> Html Msg
|
||||
viewAccessView m av =
|
||||
let
|
||||
currentCache = Maybe.withDefault [] m.hierarchy
|
||||
in
|
||||
div [ class "access-view" ]
|
||||
[ h2 [] [ text "Access Simulation" ]
|
||||
, buttonWrapper
|
||||
[ button "Forward" AccessViewForward
|
||||
, button "Back" AccessViewBack
|
||||
]
|
||||
, viewAccessLog av
|
||||
, viewCacheHierarchy <| effectiveCacheHierarchy currentCache av
|
||||
div []
|
||||
[ h2 [] [ text "Access Simulation" ]
|
||||
, p [] [ text ("Simulating access of address " ++ String.fromInt av.blockAddr) ]
|
||||
, buttonToolbar
|
||||
[ [ infoButton "Back" AccessViewBack, infoButton "Forward" AccessViewForward ]
|
||||
, [ dangerButton "Stop" AccessViewCancel ]
|
||||
]
|
||||
, h3 [] [ text "Access event log" ]
|
||||
, viewAccessLog av
|
||||
, h3 [] [ text "Current cache state" ]
|
||||
, viewCacheHierarchy <| effectiveCacheHierarchy av
|
||||
]
|
||||
|
||||
viewAccessLog : AccessView -> Html Msg
|
||||
viewAccessLog (aes, ap) =
|
||||
viewAccessLog av =
|
||||
let
|
||||
resultSpan r =
|
||||
case r of
|
||||
Hit -> span [ class "success" ] [ text "HIT" ]
|
||||
Miss -> span [ class "failure" ] [ text "MISS" ]
|
||||
span [ classList [ ("badge", True), ("badge-success", r == Hit), ("badge-danger", r == Miss) ] ]
|
||||
[ text <| if r == Hit then "Hit" else "Miss" ]
|
||||
downEvent n ae = div [ class "event" ]
|
||||
[ text <| "L" ++ String.fromInt (n + 1)
|
||||
[ text <| "L" ++ String.fromInt (n + 1) ++ " "
|
||||
, resultSpan ae.result
|
||||
]
|
||||
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 =
|
||||
case ap of
|
||||
Done -> []
|
||||
Down n -> List.indexedMap downEvent <| List.take (n + 1) aes
|
||||
Up n -> List.indexedMap downEvent aes ++
|
||||
(List.indexedMap upEvent <| List.drop n aes)
|
||||
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)
|
||||
in
|
||||
div [ class "access-log" ]
|
||||
[ h3 [] [ text "Simulation events" ]
|
||||
, div [ class "access-log-events" ] events
|
||||
]
|
||||
div [] events
|
||||
|
||||
viewAccessInput : Model -> Html Msg
|
||||
viewAccessInput m =
|
||||
let
|
||||
accessButton =
|
||||
case String.toInt m.accessInput of
|
||||
Just i -> optionalButton True "Access address" (Access i)
|
||||
Nothing -> optionalButton False "Access address" (Access -1)
|
||||
parser =
|
||||
Parser.sequence
|
||||
{ start = ""
|
||||
, 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
|
||||
div [ class "access-input" ]
|
||||
div []
|
||||
[ 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
|
||||
, accessButton
|
||||
, labeledInput "Access byte address" m.accessInput ChangeAccessInput
|
||||
, buttonWrapper [ accessButton, editHierarchyButton ]
|
||||
, errorHtml
|
||||
]
|
||||
|
||||
viewError : Bool -> String -> Html Msg
|
||||
viewError hide e = div [ classList [ ("hidden", hide), ("error", True) ] ] [ text e ]
|
||||
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"
|
||||
++ " 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 m =
|
||||
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 =
|
||||
case m.accessView of
|
||||
Nothing ->
|
||||
Maybe.withDefault []
|
||||
<| Maybe.map (List.singleton << viewCacheHierarchy) <| m.hierarchy
|
||||
Just _ -> []
|
||||
accessView = Maybe.withDefault [] <| Maybe.map (List.singleton << viewAccessView m) <| m.accessView
|
||||
accessInputView = [ viewAccessInput m ]
|
||||
accessView = Maybe.withDefault [] <| Maybe.map (List.singleton << viewAccessView m) <| Maybe.andThen (List.head) <| m.accessView
|
||||
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
|
||||
div [] <| [ rawView ] ++ cacheView ++ accessView ++ accessInputView
|
||||
div [ class "container" ]
|
||||
<| [ viewDescription] ++ rawView ++ accessInputView ++ remainingAccessView ++ accessView ++ cacheView
|
||||
|
||||
@@ -8,15 +8,11 @@ import CacheSim.View exposing (..)
|
||||
import Browser exposing (Document, document)
|
||||
import Html exposing (text)
|
||||
|
||||
testCacheModelHierarchy =
|
||||
[ { blockSize = "128", setCount = "8", setSize = "1" }
|
||||
]
|
||||
|
||||
init : Flags -> (Model, Cmd Msg)
|
||||
init f =
|
||||
let
|
||||
initialModel =
|
||||
{ rawHierarchy = testCacheModelHierarchy
|
||||
{ rawHierarchy = []
|
||||
, hierarchy = Nothing
|
||||
, accessView = Nothing
|
||||
, accessInput = ""
|
||||
@@ -39,6 +35,7 @@ update msg m =
|
||||
UseHierarchy cmh -> updateUseHierarchy cmh m
|
||||
Access i -> updateAccess i m
|
||||
ChangeAccessInput s -> ({ m | accessInput = s }, Cmd.none)
|
||||
AccessViewCancel -> updateAccessViewCancel m
|
||||
AccessViewForward -> updateAccessViewForward m
|
||||
AccessViewBack -> updateAccessViewBack m
|
||||
|
||||
|
||||
@@ -1,128 +1,7 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Raleway&display=swap');
|
||||
$primary-color: lighten(#26c176, 30%);
|
||||
|
||||
body {
|
||||
font-family: Raleway, serif;
|
||||
.btn-group {
|
||||
width: min-content;
|
||||
}
|
||||
|
||||
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;
|
||||
.btn:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user