Compare commits

...

8 Commits

7 changed files with 152 additions and 84 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
```

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,41 +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
finalCacheHierarchy : CacheHierarchy -> AccessView -> CacheHierarchy finalCacheHierarchy : AccessView -> CacheHierarchy
finalCacheHierarchy ch (l, ap) = finalCacheHierarchy av =
List.map .output l ++ List.drop (List.length l) ch List.map .output av.accessEffects ++ List.drop (List.length av.accessEffects) av.initialState
effectiveCacheHierarchy : AccessView -> CacheHierarchy
effectiveCacheHierarchy : CacheHierarchy -> AccessView -> CacheHierarchy effectiveCacheHierarchy av =
effectiveCacheHierarchy c (l, ap) =
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

@@ -17,5 +17,6 @@ type Msg
| UseHierarchy (Maybe CacheModelHierarchy) | UseHierarchy (Maybe CacheModelHierarchy)
| Access (List Int) | Access (List Int)
| ChangeAccessInput String | ChangeAccessInput String
| AccessViewCancel
| AccessViewForward | AccessViewForward
| AccessViewBack | AccessViewBack

View File

@@ -45,8 +45,13 @@ updateAccess li m =
case xs of case xs of
[] -> Ok [] [] -> Ok []
(i::t) -> (i::t) ->
case accessCacheHierarchy i c of case accessCacheHierarchy (i // 4) c of
Ok av -> Result.map ((::) (av, Down 0)) <| process (finalCacheHierarchy c (av, Done)) t 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 Err s -> Err s
accessResult = Maybe.andThen (\h -> Result.toMaybe <| process h li) m.hierarchy accessResult = Maybe.andThen (\h -> Result.toMaybe <| process h li) m.hierarchy
@@ -55,16 +60,27 @@ updateAccess li m =
in in
(newModel, cmd) (newModel, cmd)
updateAccessViewCancel : Model -> (Model, Cmd Msg)
updateAccessViewCancel m =
let
newModel = { m | accessView = Nothing }
cmd = Cmd.none
in
(newModel, cmd)
updateAccessViewForward : Model -> (Model, Cmd Msg) updateAccessViewForward : Model -> (Model, Cmd Msg)
updateAccessViewForward m = updateAccessViewForward m =
let let
afterStep = Maybe.map (intMapUpdate 0 accessViewForward) m.accessView afterStep = Maybe.map (intMapUpdate 0 accessViewForward) m.accessView
(newHierarchy, newAccessView) = newAccessView =
case afterStep of case afterStep of
Just ((avs, Done)::[]) -> (Maybe.map (\h -> finalCacheHierarchy h (avs, Done)) m.hierarchy, Nothing) Just (result::xs) ->
Just ((avs, Done)::xs) -> (Maybe.map (\h -> finalCacheHierarchy h (avs, Done)) m.hierarchy, Just xs) case (result.position, xs) of
as_ -> (m.hierarchy, as_) (End, []) -> Nothing
newModel = { m | accessView = newAccessView, hierarchy = newHierarchy } (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)

View File

@@ -10,39 +10,42 @@ import Html.Attributes exposing (type_, class, value, for, classList, disabled,
import Html.Events exposing (onInput, onClick) import Html.Events exposing (onInput, onClick)
-- Button components, powered by Bootstrap -- Button components, powered by Bootstrap
basicButton : List (Attribute Msg) -> String -> Html Msg button : List (Attribute Msg) -> String -> Html Msg
basicButton attrs s = input ([ type_ "button", value s, class "btn", class "btn-info" ] ++ attrs) [] button attrs s = input ([ type_ "button", value s, class "btn"] ++ attrs) []
disabledButton : String -> Html Msg basicButton : Msg -> String -> Html Msg
disabledButton = basicButton [ disabled True ] basicButton msg s = button [ onClick msg ] s
advancedButton : List (Attribute Msg) -> String -> Msg -> Html Msg disabledButton : List (Attribute Msg) -> String -> Html Msg
advancedButton attrs s m = basicButton (attrs ++ [ onClick m ]) s disabledButton attrs = button (attrs ++ [ disabled True ])
button : String -> Msg -> Html Msg
button s m = basicButton [ onClick m ] s
dangerButton : String -> Msg -> Html Msg dangerButton : String -> Msg -> Html Msg
dangerButton = advancedButton [ class "btn-danger" ] 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 : String -> Msg -> Html Msg
primaryButton = advancedButton [ class "btn-primary" ] primaryButton s m = button [ onClick m, class "btn-primary" ] s
secondaryButton : String -> Msg -> Html Msg secondaryButton : String -> Msg -> Html Msg
secondaryButton = advancedButton [ class "btn-secondary" ] secondaryButton s m = button [ onClick m, class "btn-secondary" ] s
maybeButton : Maybe a -> String -> (a -> Msg) -> Html Msg maybeButton : Maybe a -> List (Attribute Msg) -> String -> (a -> Msg) -> Html Msg
maybeButton m s f = maybeButton m attrs s f =
case m of case m of
Just v -> button s (f v) Just v -> button (attrs ++ [ onClick (f v) ]) s
_ -> disabledButton s _ -> disabledButton attrs s
resultButton : Result e a -> String -> (a -> Msg) -> Html Msg resultButton : Result e a -> List (Attribute Msg) -> String -> (a -> Msg) -> Html Msg
resultButton = maybeButton << Result.toMaybe resultButton = maybeButton << Result.toMaybe
-- Button wrapper (button group) -- Button wrapper (button group)
buttonWrapper : List (Html Msg) -> Html Msg buttonWrapper : List (Html Msg) -> Html Msg
buttonWrapper = div [ classList [("btn-group", True), ("mb-3", True) ] ] 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 -- Input with a label
labeledInput : String -> String -> (String -> Msg) -> Html Msg labeledInput : String -> String -> (String -> Msg) -> Html Msg
@@ -105,8 +108,8 @@ viewRawCacheModelHierarchy rcmh =
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 = resultButton checkedResult "Use hierarchy" (UseHierarchy << Just) useButton = resultButton checkedResult [ class "btn-info" ] "Start simulation" (UseHierarchy << Just)
in in
div [] div []
[ h2 [] [ text "Cache hierarchy" ] [ h2 [] [ text "Cache hierarchy" ]
@@ -134,7 +137,10 @@ viewCache level (cm, cs) =
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
@@ -163,23 +169,21 @@ viewCacheHierarchy ch =
viewAccessView : Model -> AccessView -> Html Msg viewAccessView : Model -> AccessView -> Html Msg
viewAccessView m av = viewAccessView m av =
let
currentCache = Maybe.withDefault [] m.hierarchy
in
div [] div []
[ h2 [] [ text "Access Simulation" ] [ h2 [] [ text "Access Simulation" ]
, buttonWrapper , p [] [ text ("Simulating access of address " ++ String.fromInt av.blockAddr) ]
[ primaryButton "Back" AccessViewBack , buttonToolbar
, primaryButton "Forward" AccessViewForward [ [ infoButton "Back" AccessViewBack, infoButton "Forward" AccessViewForward ]
, [ dangerButton "Stop" AccessViewCancel ]
] ]
, h3 [] [ text "Access event log" ] , h3 [] [ text "Access event log" ]
, viewAccessLog av , viewAccessLog av
, h3 [] [ text "Current cache state" ] , 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 =
span [ classList [ ("badge", True), ("badge-success", r == Hit), ("badge-danger", r == Miss) ] ] span [ classList [ ("badge", True), ("badge-success", r == Hit), ("badge-danger", r == Miss) ] ]
@@ -189,14 +193,17 @@ viewAccessLog (aes, ap) =
, 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 [] events div [] events
@@ -204,7 +211,6 @@ viewAccessInput : Model -> Html Msg
viewAccessInput m = viewAccessInput m =
let let
parser = parser =
Parser.map (List.map (\i -> i // 4)) <|
Parser.sequence Parser.sequence
{ start = "" { start = ""
, end = "" , end = ""
@@ -219,12 +225,12 @@ viewAccessInput m =
case parseResult of case parseResult of
Ok is -> if is == [] then Err "Please enter at least one number." else Ok is Ok is -> if is == [] then Err "Please enter at least one number." else Ok is
Err e -> Err <| parseErrorToString e Err e -> Err <| parseErrorToString e
accessButton = resultButton checkedResult "Access address" Access accessButton = resultButton checkedResult [ class "btn-info" ] "Access address" Access
errorHtml = errorHtml =
case checkedResult of case checkedResult of
Ok _ -> viewError True "" Ok _ -> viewError True ""
Err e -> viewError False e Err e -> viewError False e
editHierarchyButton = button "Edit hierarchy" (UseHierarchy Nothing) editHierarchyButton = button [ onClick (UseHierarchy Nothing), class "btn-dark" ] "Edit hierarchy"
in in
div [] div []
[ h2 [] [ text "Run access simulation" ] [ h2 [] [ text "Run access simulation" ]
@@ -244,7 +250,7 @@ viewDescription =
, p [] , p []
[ text <| "To use the simulator, first create a fitting cache configuration by using the \"Add level\" button," [ 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" ++ " 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," ++ " 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" ++ " 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." ++ " view, which will allow you to step through the steps of accessing a cache."
] ]

View File

@@ -35,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,3 +1,7 @@
.btn-group { .btn-group {
width: min-content; width: min-content;
} }
.btn:focus {
box-shadow: none;
}