| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <meta http-equiv="x-ua-compatible" content="ie=edge"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| |
| <title>gem5</title> |
| |
| <!-- SITE FAVICON --> |
| <link rel="shortcut icon" type="image/gif" href="/assets/img/gem5ColorVert.gif"/> |
| |
| <link rel="canonical" href="http://localhost:4000/cache-declarations/"> |
| <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,700,800,600' rel='stylesheet' type='text/css'> |
| <link href='https://fonts.googleapis.com/css?family=Muli:400,300' rel='stylesheet' type='text/css'> |
| |
| <!-- FAVICON --> |
| <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"> |
| |
| <!-- BOOTSTRAP --> |
| <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"> |
| |
| <!-- CUSTOM CSS --> |
| <link rel="stylesheet" href="/css/main.css"> |
| </head> |
| |
| |
| <body> |
| <nav class="navbar navbar-expand-md navbar-light bg-light"> |
| <a class="navbar-brand" href="/"> |
| <img src="/assets/img/gem5ColorLong.gif" alt="gem5" height=45px> |
| </a> |
| <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation"> |
| <span class="navbar-toggler-icon"></span> |
| </button> |
| <div class="collapse navbar-collapse" id="navbarNavDropdown"> |
| <ul class="navbar-nav ml-auto"> |
| <li class="nav-item "> |
| <a class="nav-link" href="/">Home</a> |
| </li> |
| |
| <li class="nav-item dropdown "> |
| <a class="nav-link dropdown-toggle" href="/about" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> |
| About |
| </a> |
| <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink"> |
| <a class="dropdown-item" href="/about">About</a> |
| <a class="dropdown-item" href="/publications">Publications</a> |
| <a class="dropdown-item" href="/governance">Governance</a> |
| </div> |
| </li> |
| |
| <li class="nav-item dropdown active"> |
| <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> |
| Documentation |
| </a> |
| <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink"> |
| <!-- Pull navigation from _data/documentation.yml --> |
| |
| <a class="dropdown-item" href="/introduction">Introduction</a> |
| |
| <a class="dropdown-item" href="/building">Getting Started</a> |
| |
| <a class="dropdown-item" href="/environment">Modifying/Extending</a> |
| |
| <a class="dropdown-item" href="/MSIintro">Modeling Cache Coherence with Ruby</a> |
| |
| </div> |
| </li> |
| |
| <li class="nav-item "> |
| <a class="nav-link" href="/contributing">Contributing</a> |
| </li> |
| |
| <li class="nav-item "> |
| <a class="nav-link" href="/blog">Blog</a> |
| </li> |
| |
| <li class="nav-item "> |
| <a class="nav-link" href="/search">Search</a> |
| </li> |
| </ul> |
| </div> |
| </nav> |
| |
| <main> |
| <div class="sidenav-top"> |
| <a href="/"><img src="/assets/img/gem5ColorLong.gif" height="80"></a> |
| <div class="search"> |
| <form action="/search" method="get"> |
| <!-- <label for="search-box"><i class="fa fa-search"></i></label> --> |
| <input type="text" name="query"> |
| <button type="submit" name="submit"><i class="fa fa-search"></i></button> |
| </form> |
| </div> |
| </div> |
| <div class="sidenav"> |
| <!-- Pull navigation from _data/documentation.yml --> |
| |
| <a class="item" href="/introduction" role="button" aria-expanded="false" aria-controls="collapseExample"> |
| Introduction |
| </a> |
| <div class="collapse " id="introduction"> |
| |
| </div> |
| |
| <a class="item" data-toggle="collapse" href="#pt1" role="button" aria-expanded="false" aria-controls="collapseExample"> |
| Getting Started |
| </a> |
| <div class="collapse " id="pt1"> |
| |
| <a class="subitem " href="/building">Building gem5</a> |
| |
| <a class="subitem " href="/simple_config">Creating a simple configuration script</a> |
| |
| <a class="subitem " href="/cache_config">Adding cache to configuration script</a> |
| |
| <a class="subitem " href="/gem5_stats">Understanding gem5 statistics and output</a> |
| |
| <a class="subitem " href="/example_configs">Using the default configuration scripts</a> |
| |
| </div> |
| |
| <a class="item" data-toggle="collapse" href="#pt2" role="button" aria-expanded="false" aria-controls="collapseExample"> |
| Modifying/Extending |
| </a> |
| <div class="collapse " id="pt2"> |
| |
| <a class="subitem " href="/environment">Setting up your development environment</a> |
| |
| <a class="subitem " href="/helloobject">Creating a very simple SimObject</a> |
| |
| <a class="subitem " href="/debugging">Debugging gem5</a> |
| |
| <a class="subitem " href="/events">Event-driven programming</a> |
| |
| <a class="subitem " href="/parameters">Adding parameters to SimObjects and more events</a> |
| |
| <a class="subitem " href="/memoryobject">Creating SimObjects in the memory system</a> |
| |
| <a class="subitem " href="/simplecache">Creating a simple cache object</a> |
| |
| </div> |
| |
| <a class="item" data-toggle="collapse" href="#pt3" role="button" aria-expanded="false" aria-controls="collapseExample"> |
| Modeling Cache Coherence with Ruby |
| </a> |
| <div class="collapse show" id="pt3"> |
| |
| <a class="subitem " href="/MSIintro">Introduction to Ruby</a> |
| |
| <a class="subitem " href="/cache-intro">MSI example cache protocol</a> |
| |
| <a class="subitem active" href="/cache-declarations">Declaring a state machine</a> |
| |
| <a class="subitem " href="/cache-in-ports">In port code blocks</a> |
| |
| <a class="subitem " href="/cache-actions">Action code blocks</a> |
| |
| <a class="subitem " href="/cache-transitions">Transition code blocks</a> |
| |
| <a class="subitem " href="/directory">MSI Directory implementation</a> |
| |
| <a class="subitem " href="/MSIbuilding">Compiling a SLICC protocol</a> |
| |
| <a class="subitem " href="/configuration">Configuring a simple Ruby system</a> |
| |
| <a class="subitem " href="/running">Running the simple Ruby system</a> |
| |
| <a class="subitem " href="/MSIdebugging">Debugging SLICC Protocols</a> |
| |
| <a class="subitem " href="/simple-MI_example">Configuring for a standard protocol</a> |
| |
| </div> |
| |
| </div> |
| |
| <div class="container" id="doc-container"> |
| <div class="edit"><a href="https://github.com/gem5/new-website/tree/master/_pages/documentation/part3/cache-declarations.md">Edit this page</a></div> |
| <dl> |
| <dt>authors</dt> |
| <dd>Jason Lowe-Power</dd> |
| </dl> |
| |
| <h1 id="declaring-a-state-machine">Declaring a state machine</h1> |
| |
| <p>Let’s start on our first state machine file! First, we will create the |
| L1 cache controller for our MSI protocol.</p> |
| |
| <p>Create a file called <code class="highlighter-rouge">MSI-cache.sm</code> and the following code declares the |
| state machine.</p> |
| |
| <p>``` {.sourceCode .c++} |
| machine(MachineType:L1Cache, “MSI cache”) |
| : <parameters> |
| { |
| <All state="" machine="" code=""> |
| }</All></parameters></p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| The first thing you'll notice about the state machine code is that is |
| looks very C++-like. The state machine file is like creating a C++ |
| object in a header file, if you included all of the code there as well. |
| When in doubt, C++ syntax with *probably* work in SLICC. However, there |
| are many cases where C++ syntax is incorrect syntax for SLICC as well as |
| cases where SLICC extends the syntax. |
| |
| With `MachineType:L1Cache`, we are naming this state machine `L1Cache`. |
| SLICC will generate many different objects for us from the state machine |
| using that name. For instance, once this file is compiled, there will be |
| a new SimObject: `L1Cache_Controller` that is the cache controller. Also |
| included in this declaration is a description of this state machine: |
| "MSI cache". |
| |
| There are many cases in SLICC where you must include a description to go |
| along with the variable. The reason for this is that SLICC was |
| originally designed to just describe, not implement, coherence |
| protocols. Today, these extra descriptions serve two purposes. First, |
| they act as comments on what the author intended each variable, or |
| state, or event, to be used for. Second, many of them are still exported |
| into HTML when building the HTML tables for the SLICC protocol. Thus, |
| while browsing the HTML table, you can see the more detailed comments |
| from the author of the protocol. It is important to be clear with these |
| descriptions since coherence protocols can get quite complicated. |
| |
| State machine parameters |
| ------------------------ |
| |
| Proceeding the `machine()` declaration is a colon, after which all of |
| the parameters to the state machine are declared. These parameters are |
| directly exported to the SimObject that is generated by the state |
| machine. |
| |
| For our MSI L1 cache, we have the following parameters: |
| |
| ``` {.sourceCode .c++} |
| machine(MachineType:L1Cache, "MSI cache") |
| : Sequencer *sequencer; |
| CacheMemory *cacheMemory; |
| bool send_evictions; |
| |
| <Message buffer declarations> |
| |
| { |
| |
| } |
| </code></pre></div></div> |
| |
| <p>First, we have a <code class="highlighter-rouge">Sequencer</code>. This is a special class that is |
| implemented in Ruby to interface with the rest of gem5. The Sequencer is |
| a gem5 <code class="highlighter-rouge">MemObject</code> with a slave port so it can accept memory requests |
| from other objects. The sequencer accepts requests from a CPU (or other |
| master port) and converts the gem5 the packet into a <code class="highlighter-rouge">RubyRequest</code>. |
| Finally, the <code class="highlighter-rouge">RubyRequest</code> is pushed onto the <code class="highlighter-rouge">mandatoryQueue</code> of the |
| state machine. We will revisit the <code class="highlighter-rouge">mandatoryQueue</code> in |
| in port section <MSI-in-ports-section>.</p> |
| |
| <p>Next, there is a <code class="highlighter-rouge">CacheMemory</code> object. This is what holds the cache data |
| (i.e., cache entries). The exact implementation, size, etc. is |
| configurable at runtime.</p> |
| |
| <p>Finally, we can specify any other parameters we would like, similar to a |
| general <code class="highlighter-rouge">SimObject</code>. In this case, we have a boolean variable |
| <code class="highlighter-rouge">send_evictions</code>. This is used for out-of-order core models to notify |
| the load-store queue if an address is evicted after a load to squash a |
| load if it is speculative.</p> |
| |
| <p>Next, also in the parameter block (i.e., before the first open bracket), |
| we need to declare all of the message buffers that this state machine |
| will use. Message buffers are the interface between the state machine |
| and the Ruby network. Messages are sent and received via the message |
| buffers. Thus, for each virtual channel in our protocol we need a |
| separate message buffer.</p> |
| |
| <p>The MSI protocol needs three different virtual networks. Virtual |
| networks are needed to prevent deadlock (e.g., it is bad if a response |
| gets stuck behind a stalled request). In this protocol, the highest |
| priority is responses (virtual network 2), followed by forwarded |
| requests (virtual network 1), then requests have the lowest priority |
| (virtual network 0). See Sorin et al. for details on why these three |
| virtual networks are needed.</p> |
| |
| <p>The following code declares all of the needed message buffers.</p> |
| |
| <dl> |
| <dt>``` {.sourceCode .c++}</dt> |
| <dt>machine(MachineType:L1Cache, “MSI cache”)</dt> |
| <dd>Sequencer *sequencer; |
| CacheMemory *cacheMemory; |
| bool send_evictions; |
| |
| <p>MessageBuffer * requestToDir, network=”To”, virtual_network=”0”, vnet_type=”request”; |
| MessageBuffer * responseToDirOrSibling, network=”To”, virtual_network=”2”, vnet_type=”response”;</p> |
| |
| <p>MessageBuffer * forwardFromDir, network=”From”, virtual_network=”1”, vnet_type=”forward”; |
| MessageBuffer * responseFromDirOrSibling, network=”From”, virtual_network=”2”, vnet_type=”response”;</p> |
| |
| <p>MessageBuffer * mandatoryQueue;</p> |
| </dd> |
| </dl> |
| |
| <p>{</p> |
| |
| <p>}</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| We have five different message buffers: two "To", two "From", and one |
| special message buffer. The "To" message buffers are similar to slave |
| ports in gem5. These are the message buffers that this controller uses |
| to send messages to other controllers in the system. The "From" message |
| buffers are like slave ports. This controller receives messages on |
| "From" buffers from other controllers in the system. |
| |
| We have two different "To" buffers, one for low priority requests, and |
| one for high priority responses. The priority for the networks are not |
| inherent. The priority is based on the order that other controllers look |
| at the message buffers. It is a good idea to number the virtual networks |
| so that higher numbers mean higher priority, but the virtual network |
| number is ignored by Ruby except that messages on network 2 can only go |
| to other message buffers on network 2 (i.e., messages can't jump from |
| one network to another). |
| |
| Similarly, there is two different ways this cache can receive messages, |
| either as a forwarded request from the directory (e.g., another cache |
| requests a writable block and we have a readable copy) or as a response |
| to a request this controller made. The response is higher priority than |
| the forwarded requests. |
| |
| Finally, there is a special message buffer, the `mandatoryQueue`. This |
| message buffer is used by the `Sequencer` to convert gem5 packets into |
| Ruby requests. Unlike the other message buffers, `mandatoryQueue` does |
| not connect to the Ruby network. Note: the name of this message buffer |
| is hard-coded and must be exactly "mandatoryQueue". |
| |
| As previously mentioned, this parameter block is converted into the |
| SimObject description file. Any parameters you put in this block will be |
| SimObject parameters that are accessible from the Python configuration |
| files. If you look at the generated file L1Cache\_Controller.py, it will |
| look very familiar. Note: This is a generated file and you should never |
| modify generated files directly! |
| |
| ``` {.sourceCode .python} |
| from m5.params import * |
| from m5.SimObject import SimObject |
| from Controller import RubyController |
| |
| class L1Cache_Controller(RubyController): |
| type = 'L1Cache_Controller' |
| cxx_header = 'mem/protocol/L1Cache_Controller.hh' |
| sequencer = Param.RubySequencer("") |
| cacheMemory = Param.RubyCache("") |
| send_evictions = Param.Bool("") |
| requestToDir = Param.MessageBuffer("") |
| responseToDirOrSibling = Param.MessageBuffer("") |
| forwardFromDir = Param.MessageBuffer("") |
| responseFromDirOrSibling = Param.MessageBuffer("") |
| mandatoryQueue = Param.MessageBuffer("") |
| </code></pre></div></div> |
| |
| <h2 id="state-declarations">State declarations</h2> |
| |
| <p>The next part of the state machine is the state declaration. Here, we |
| are going to declare all of the stable and transient states for the |
| state machine. We will follow the naming convention in Sorin et al. For |
| instance, the transient state “IM_AD” corresponds to moving from |
| Invalid to Modified waiting on acks and data. These states come directly |
| from the left column of Table 8.3 in Sorin et al.</p> |
| |
| <p>``` {.sourceCode .c++} |
| state_declaration(State, desc=”Cache states”) { |
| I, AccessPermission:Invalid, |
| desc=”Not present/Invalid”;</p> |
| |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// States moving out of I |
| IS_D, AccessPermission:Invalid, |
| desc="Invalid, moving to S, waiting for data"; |
| IM_AD, AccessPermission:Invalid, |
| desc="Invalid, moving to M, waiting for acks and data"; |
| IM_A, AccessPermission:Busy, |
| desc="Invalid, moving to M, waiting for acks"; |
| |
| S, AccessPermission:Read_Only, |
| desc="Shared. Read-only, other caches may have the block"; |
| |
| // States moving out of S |
| SM_AD, AccessPermission:Read_Only, |
| desc="Shared, moving to M, waiting for acks and 'data'"; |
| SM_A, AccessPermission:Read_Only, |
| desc="Shared, moving to M, waiting for acks"; |
| |
| M, AccessPermission:Read_Write, |
| desc="Modified. Read & write permissions. Owner of block"; |
| |
| // States moving to Invalid |
| MI_A, AccessPermission:Busy, |
| desc="Was modified, moving to I, waiting for put ack"; |
| SI_A, AccessPermission:Busy, |
| desc="Was shared, moving to I, waiting for put ack"; |
| II_A, AccessPermission:Invalid, |
| desc="Sent valid data before receiving put ack. "Waiting for put ack."; } ``` |
| </code></pre></div></div> |
| |
| <p>Each state has an associated access permission: “Invalid”, “NotPresent”, |
| “Busy”, “Read_Only”, or “Read_Write”. The access permission is used |
| for <em>functional</em> accesses to the cache. Functional accesses are |
| debug-like accesses when the simulator wants to read or update the data |
| immediately. One example of this is reading in files in SE mode which |
| are directly loaded into memory.</p> |
| |
| <p>For functional accesses all caches are checked to see if they have a |
| corresponding block with matching address. For functional reads, <em>all</em> |
| of the blocks with a matching address that have read-only or read-write |
| permission are accessed (they should all have the same data). For |
| functional writes, all blocks are updated with new data if they have |
| busy, read-only, or read-write permission.</p> |
| |
| <h2 id="event-declarations">Event declarations</h2> |
| |
| <p>Next, we need to declare all of the events that are triggered by |
| incoming messages for this cache controller. These events come directly |
| from the first row in Table 8.3 in Sorin et al.</p> |
| |
| <p>``` {.sourceCode .c++} |
| enumeration(Event, desc=”Cache events”) { |
| // From the processor/sequencer/mandatory queue |
| Load, desc=”Load from processor”; |
| Store, desc=”Store from processor”;</p> |
| |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Internal event (only triggered from processor requests) |
| Replacement, desc="Triggered when block is chosen as victim"; |
| |
| // Forwarded request from other cache via dir on the forward network |
| FwdGetS, desc="Directory sent us a request to satisfy GetS. We must have the block in M to respond to this."; |
| FwdGetM, desc="Directory sent us a request to satisfy GetM. We must have the block in M to respond to this."; |
| Inv, desc="Invalidate from the directory."; |
| PutAck, desc="Response from directory after we issue a put. This must be on the fwd network to avoid deadlock."; |
| |
| // Responses from directory |
| DataDirNoAcks, desc="Data from directory (acks = 0)"; |
| DataDirAcks, desc="Data from directory (acks > 0)"; |
| |
| // Responses from other caches |
| DataOwner, desc="Data from owner"; |
| InvAck, desc="Invalidation ack from other cache after Inv"; |
| |
| // Special event to simplify implementation |
| LastInvAck, desc="Triggered after the last ack is received"; } ``` |
| </code></pre></div></div> |
| |
| <h2 id="user-defined-structures">User-defined structures</h2> |
| |
| <p>Next, we need to define some structures that we will use in other places |
| in this controller. The first one we will define is <code class="highlighter-rouge">Entry</code>. This is the |
| structure that is stored in the <code class="highlighter-rouge">CacheMemory</code>. It only needs to contain |
| data and a state, but it may contain any other data you want. Note: The |
| state that this structure is storing is the <code class="highlighter-rouge">State</code> type that was |
| defined above, not a hardcoded state type.</p> |
| |
| <p>You can find the abstract version of this class (<code class="highlighter-rouge">AbstractCacheEntry</code>) |
| in <code class="highlighter-rouge">src/mem/ruby/slicc_interface/AbstractCacheEntry.hh</code>. If you want to |
| use any of the member functions of <code class="highlighter-rouge">AbstractCacheEntry</code>, you need to |
| declare them here (this isn’t used in this protocol).</p> |
| |
| <p>``` {.sourceCode .c++} |
| structure(Entry, desc=”Cache entry”, interface=”AbstractCacheEntry”) { |
| State CacheState, desc=”cache state”; |
| DataBlock DataBlk, desc=”Data in the block”; |
| }</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| Another structure we will need is a TBE. TBE is the "transaction buffer |
| entry". This stores information needed during transient states. This is |
| *like* an MSHR. It functions as an MSHR in this protocol, but the entry |
| is also allocated for other uses. In this protocol, it will store the |
| state (usually needed), data (also usually needed), and the number of |
| acks that this block is currently waiting for. The `AcksOutstanding` is |
| used for the transitions where other controllers send acks instead of |
| the data. |
| |
| ``` {.sourceCode .c++} |
| structure(TBE, desc="Entry for transient requests") { |
| State TBEState, desc="State of block"; |
| DataBlock DataBlk, desc="Data for the block. Needed for MI_A"; |
| int AcksOutstanding, default=0, desc="Number of acks left to receive."; |
| } |
| </code></pre></div></div> |
| |
| <p>Next, we need a place to store all of the TBEs. This is an externally |
| defined class; it is defined in C++ outside of SLICC. Therefore, we need |
| to declare that we are going to use it, and also declare any of the |
| functions that we will call on it. You can find the code for the |
| <code class="highlighter-rouge">TBETable</code> in src/mem/ruby/structures/TBETable.hh. It is templatized on |
| the TBE structure defined above, which gets a little confusing, as we |
| will see.</p> |
| |
| <p>``` {.sourceCode .c++} |
| structure(TBETable, external=”yes”) { |
| TBE lookup(Addr); |
| void allocate(Addr); |
| void deallocate(Addr); |
| bool isPresent(Addr); |
| }</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| The `external="yes"` tells SLICC to not look for the definition of this |
| structure. This is similar to declaring a variable `extern` in C/C++. |
| |
| Other declarations and definitions required |
| ------------------------------------------- |
| |
| Finally, we are going to go through some boilerplate of declaring |
| variables, declaring functions in `AbstractController` that we will use |
| in this controller, and defining abstract functions in |
| `AbstractController`. |
| |
| First, we need to have a variable that stores a TBE table. We have to do |
| this in SLICC because it is not until this time that we know the true |
| type of the TBE table since the TBE type was defined above. This is some |
| particularly tricky (or nasty) code to get SLICC to generate the right |
| C++ code. The difficulty is that we want templatize `TBETable` based on |
| the `TBE` type above. The key is that SLICC mangles the names of all |
| types declared in the machine with the machine's name. For instance, |
| `TBE` is actually L1Cache\_TBE in C++. |
| |
| We also want to pass a parameter to the constructor of the `TBETable`. |
| This is a parameter that is actually part of the `AbstractController`, |
| thus we need to use the C++ name for the variable since it doesn't have |
| a SLICC name. |
| |
| ``` {.sourceCode .c++} |
| TBETable TBEs, template="<L1Cache_TBE>", constructor="m_number_of_TBEs"; |
| </code></pre></div></div> |
| |
| <p>If you can understand the above code, then you are an official SLICC |
| ninja!</p> |
| |
| <p>Next, any functions that are part of AbstractController need to be |
| declared, if we are going to use them in the rest of the file. In this |
| case, we are only going to use <code class="highlighter-rouge">clockEdge()</code></p> |
| |
| <p>``` {.sourceCode .c++} |
| Tick clockEdge();</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| There are a few other functions we're going to use in actions. These |
| functions are used in actions to set and unset implicit variables |
| available in action code-blocks. Action code blocks will be explained in |
| detail in the action section \<MSI-actions-section\>. These may be |
| needed when a transition has many actions. |
| |
| ``` {.sourceCode .c++} |
| void set_cache_entry(AbstractCacheEntry a); |
| void unset_cache_entry(); |
| void set_tbe(TBE b); |
| void unset_tbe(); |
| </code></pre></div></div> |
| |
| <p>Another useful function is <code class="highlighter-rouge">mapAddressToMachine</code>. This allows us to |
| change the address mappings for banked directories or caches at runtime |
| so we don’t have to hardcode them in the SLICC file.</p> |
| |
| <p>``` {.sourceCode .c++} |
| MachineID mapAddressToMachine(Addr addr, MachineType mtype);</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| Finally, you can also add any functions you may want to use in the file |
| and implement them here. For instance, it is convenient to access cache |
| blocks by address with a single function. Again, in this function there |
| is some SLICC trickery. We need to access "by pointer" since the cache |
| block is something that we need to be mutable later ("by reference" |
| would have been a better name). The cast is also necessary since we |
| defined a specific `Entry` type in the file, but the `CacheMemory` holds |
| the abstract type. |
| |
| ``` {.sourceCode .c++} |
| // Convenience function to look up the cache entry. |
| // Needs a pointer so it will be a reference and can be updated in actions |
| Entry getCacheEntry(Addr address), return_by_pointer="yes" { |
| return static_cast(Entry, "pointer", cacheMemory.lookup(address)); |
| } |
| </code></pre></div></div> |
| |
| <p>The next set of boilerplate code rarely changes between different |
| protocols. There’s a set of functions that are pure-virtual in |
| <code class="highlighter-rouge">AbstractController</code> that we must implement.</p> |
| |
| <dl> |
| <dt><code class="highlighter-rouge">getState</code></dt> |
| <dd>Given a TBE, cache entry, and address return the state of the block. |
| This is called on the block to decide which transition to execute |
| when an event is triggered. Usually, you return the state in the TBE |
| or cache entry, whichever is valid.</dd> |
| <dt><code class="highlighter-rouge">setState</code></dt> |
| <dd>Given a TBE, cache entry, and address make sure the state is set |
| correctly on the block. This is called at the end of the transition |
| to set the final state on the block.</dd> |
| <dt><code class="highlighter-rouge">getAccessPermission</code></dt> |
| <dd>Get the access permission of a block. This is used during functional |
| access to decide whether or not to functionally access the block. It |
| is similar to <code class="highlighter-rouge">getState</code>, get the information from the TBE if valid, |
| cache entry, if valid, or the block is not present.</dd> |
| <dt><code class="highlighter-rouge">setAccessPermission</code></dt> |
| <dd>Like <code class="highlighter-rouge">getAccessPermission</code>, but sets the permission.</dd> |
| <dt><code class="highlighter-rouge">functionalRead</code></dt> |
| <dd>Functionally read the data. It is possible the TBE has more |
| up-to-date information, so check that first. Note: testAndRead/Write |
| defined in src/mem/ruby/slicc_interface/Util.hh</dd> |
| <dt><code class="highlighter-rouge">functionalWrite</code></dt> |
| <dd>Functionally write the data. Similarly, you may need to update the |
| data in both the TBE and the cache entry.</dd> |
| </dl> |
| |
| <p>``` {.sourceCode .c++} |
| State getState(TBE tbe, Entry cache_entry, Addr addr) { |
| // The TBE state will override the state in cache memory, if valid |
| if (is_valid(tbe)) { return tbe.TBEState; } |
| // Next, if the cache entry is valid, it holds the state |
| else if (is_valid(cache_entry)) { return cache_entry.CacheState; } |
| // If the block isn’t present, then it’s state must be I. |
| else { return State:I; } |
| }</p> |
| |
| <p>void setState(TBE tbe, Entry cache_entry, Addr addr, State state) { |
| if (is_valid(tbe)) { tbe.TBEState := state; } |
| if (is_valid(cache_entry)) { cache_entry.CacheState := state; } |
| }</p> |
| |
| <p>AccessPermission getAccessPermission(Addr addr) { |
| TBE tbe := TBEs[addr]; |
| if(is_valid(tbe)) { |
| return L1Cache_State_to_permission(tbe.TBEState); |
| }</p> |
| |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Entry cache_entry := getCacheEntry(addr); |
| if(is_valid(cache_entry)) { |
| return L1Cache_State_to_permission(cache_entry.CacheState); |
| } |
| |
| return AccessPermission:NotPresent; } |
| </code></pre></div></div> |
| |
| <p>void setAccessPermission(Entry cache_entry, Addr addr, State state) { |
| if (is_valid(cache_entry)) { |
| cache_entry.changePermission(L1Cache_State_to_permission(state)); |
| } |
| }</p> |
| |
| <p>void functionalRead(Addr addr, Packet *pkt) { |
| TBE tbe := TBEs[addr]; |
| if(is_valid(tbe)) { |
| testAndRead(addr, tbe.DataBlk, pkt); |
| } else { |
| testAndRead(addr, getCacheEntry(addr).DataBlk, pkt); |
| } |
| }</p> |
| |
| <p>int functionalWrite(Addr addr, Packet *pkt) { |
| int num_functional_writes := 0;</p> |
| |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>TBE tbe := TBEs[addr]; |
| if(is_valid(tbe)) { |
| num_functional_writes := num_functional_writes + |
| testAndWrite(addr, tbe.DataBlk, pkt); |
| return num_functional_writes; |
| } |
| |
| num_functional_writes := num_functional_writes + |
| testAndWrite(addr, getCacheEntry(addr).DataBlk, pkt); |
| return num_functional_writes; } ``` |
| </code></pre></div></div> |
| |
| <br> |
| |
| <!-- RETRIVE PREVIOUS PAGE LINK --> |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| <!-- RETRIEVE NEXT PAGE LINK --> |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| <div class="navbuttons"> |
| |
| <a href="/cache-intro"><button type="button" class="btn btn-outline-primary">PREVIOUS</button></a> |
| |
| |
| |
| <a href="/cache-in-ports"><button type="button" class="btn btn-outline-primary">NEXT</button></a> |
| |
| </div> |
| </div> |
| |
| </main> |
| |
| |
| <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script> |
| <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script> |
| |
| <script> |
| // When the user scrolls down 20px from the top of the document, show the button |
| window.onscroll = function() {scrollFunction()}; |
| |
| function scrollFunction() { |
| if (document.body.scrollTop > 100 || document.documentElement.scrollTop > 20) { |
| document.getElementById("myBtn").style.display = "block"; |
| } else { |
| document.getElementById("myBtn").style.display = "none"; |
| } |
| } |
| |
| // When the user clicks on the button, scroll to the top of the document |
| function topFunction() { |
| document.body.scrollTop = 0; |
| document.documentElement.scrollTop = 0; |
| } |
| </script> |
| |
| </body> |
| |
| |
| </html> |