<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://kaustav-goswami.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://kaustav-goswami.github.io/" rel="alternate" type="text/html" /><updated>2026-06-26T23:23:57+00:00</updated><id>https://kaustav-goswami.github.io/feed.xml</id><title type="html">Kaustav Goswami</title><subtitle>Write an awesome description for your new site here. You can edit this line in _config.yml. It will appear in your document head meta (for Google search results) and in your feed.xml site description.</subtitle><author><name>Kaustav Goswami</name></author><entry><title type="html">CXL-ClusterSim: Simulating CXL systems using gem5 and SST</title><link href="https://kaustav-goswami.github.io/simulation/gem5-sst-cxl/" rel="alternate" type="text/html" title="CXL-ClusterSim: Simulating CXL systems using gem5 and SST" /><published>2026-02-03T00:00:00+00:00</published><updated>2026-02-03T00:00:00+00:00</updated><id>https://kaustav-goswami.github.io/simulation/gem5-sst-cxl</id><content type="html" xml:base="https://kaustav-goswami.github.io/simulation/gem5-sst-cxl/"><![CDATA[<p>Simulating multiple gem5 instances isn’t feasible.
This documents how to use the composable memory simulation platform in a gem5, SST and gem5 + SST setup.
The setup can be used in gem5 to fast-forward full-system simulation and then used in SST to simulate a multi-node system.</p>

<p>See the paper for more details <a href="https://arch.cs.ucdavis.edu/simulation/memory/cxl/2026/05/26/cxl-clustersim.html">CXL-ClusterSim: Modeling CXL-based Disaggregated Memory Cluster for Pooling and Sharing using gem5 and SST</a></p>

<p>TL;DR See <a href="#shared-memory">Shared Memory Simulation in CXL 3.0</a> for simulating full-system shared memory workloads.
TL;DR See <a href="https://github.com/darchr/cxl-clustersim">CXL-ClusterSim</a> to start using the code ASAP.</p>

<p>We started this project in 2023 when there were limited options in simulating multiple hosts to evaluate 
Now there are several tools that allows the simulation of CXL.
This project simply helps scale existing gem5 implementation of CXL hosts using SST.</p>

<p>Alternately, you can also use pure-gem5 to do multi-host simulations.
The <code class="language-plaintext highlighter-rouge">fs.py</code> script already supports bringing up two different gem5 instances on the same gem5 thread.
The only downside (documented in the <code class="language-plaintext highlighter-rouge">fs.py</code> script) is that the simulation is slow (its super slow!).
KVM doesn’t work in this case as both the gem5 instances have the exact memory ranges.</p>

<p>Currently we use classic caches in gem5.
This can be extended to support directories.</p>

<h1 id="roadmap">Roadmap</h1>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Memory pooling from a remote memory node set up in SST.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />NUMA support for ARM and RISCV nodes at the OS-level.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Bandwidth validation.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Can scale up to 32 gem5 nodes pooling memory from the remote memory.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" /><code class="language-plaintext highlighter-rouge">--numa-preferred</code> calls.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />CXL 3.0-like memory sharing.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Workloads for memory sharing: <a href="https://github.com/darchr/shared-gapbs">Shared-GAPBS</a></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Sharing memory with an example.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Implement Back Invalidates (BISnp) with <code class="language-plaintext highlighter-rouge">clflush</code> on the hosts (WIP on the <code class="language-plaintext highlighter-rouge">dev</code> branch)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Use <code class="language-plaintext highlighter-rouge">OneShot</code> API in SST to improve the performance of the simulation (WIP)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Pooling resources must be added to gem5-resources</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Create a directory controller in SST to mimic CXL.cache’s directory controller.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Switch to RUBY caches in gem5 to create local directories.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Implement BIConflicts</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Implement a CXL cache-coherence paper.</li>
</ul>

<h2 id="d-tours">D-Tours</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Simple graph example for shared memory: <a href="https://github.com/kaustav-goswami/disaggregated-shared-graphs">simple-graphs</a></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Extend the memory allocation of <a href="https://github.com/darchr/shared-gapbs">Shared-GAPBS</a> to be a generic shared memory allocator for more workloads: <a href="https://github.com/kaustav-goswami/disagg-shmem-allocator">disagg-shmem-alloc</a></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Reimplement memcached with shared memory: <a href="https://github.com/kaustav-goswami/shared-memcached">memcached</a>, <a href="https://github.com/kaustav-goswami/YCSB">YCSB</a></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Reimplement TPC-C and/or TPC-H with shared memory</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Reimplement some KV-cache for more shared memory workloads.</li>
</ul>

<h1 id="source-code-changes">Source Code Changes</h1>

<p>The code is mainly confined in the <code class="language-plaintext highlighter-rouge">disaggregated_memory</code> directory.
The directory is divided into four subdirectories, similar to the structure of the gem5’s standard library:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">boards</code>: The disaggregated memory boards are inherited from the stdlib’s boards. Users can pass two memory ranges. The first one is to model the local memory and the second one is to model a remote memory. The remote memory may or may not be in gem5, as these boards can be used directly with SST. These ranges are exposed as NUMA and zNUMA nodes to the operating system. Currently the following boards are supported:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">ArmComposableMemoryBoard</code> implemented in <code class="language-plaintext highlighter-rouge">arm_main_board.py</code>. Used for pooling out of the box.</li>
      <li><code class="language-plaintext highlighter-rouge">RiscvComposableMemoryBoard</code> implemented in <code class="language-plaintext highlighter-rouge">riscv_main_board.py</code>. Used for pooling out of the box.</li>
      <li><code class="language-plaintext highlighter-rouge">X86ComposableMemoryBoard</code> implemented in <code class="language-plaintext highlighter-rouge">x86_main_board.py</code>. Used for pooling with fake NUMA nodes for pooling.</li>
      <li><code class="language-plaintext highlighter-rouge">X86SharedMemoryBoard</code> implemented in <code class="language-plaintext highlighter-rouge">x86_shared_board.py</code>. Uses DAX ranges (E820 type=12) enable memory sharing across hosts.</li>
      <li><code class="language-plaintext highlighter-rouge">X86AlternateSharedMemoryBoard</code> removes the annoying 3 GiB limit on X86 systems. There is an additional kernel memory used as a hack.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">memories</code>: This directory contains <code class="language-plaintext highlighter-rouge">ExternalRemoteMemory</code> inherited from ExternalMemory. Users can use both gem5 and SST to model this remote memory. <code class="language-plaintext highlighter-rouge">ExternalRemoteMemory</code> is a gem5 stdlib component, that can be plugged into any gem5-side stdlib config script. <em>Note</em> that any other AbstractMemorySystem can be plugged in to simulate DAX ranges (cannot share).</li>
  <li><code class="language-plaintext highlighter-rouge">cachehierarchies</code>: gem5’s stdlib cachehierarchies were modified to handle more than one outgoing connection from the LLC. Currently the following cachehierarchies are supported:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">ClassicPrivateL1PrivateL2DMCache</code>: A 2-level private classic cache hierarchy</li>
      <li><code class="language-plaintext highlighter-rouge">ClassicPrivateL1PrivateL2SharedL3DMCache</code>: A 3-level classic cache hierarchy that has a shared LLC.</li>
      <li><em>Note</em> ruby caches only work with the RiscvComposableMemoryBoard.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">configs</code>: Top-level gem5 scripts that can be used to take checkpoints or run SST simulations.</li>
  <li><code class="language-plaintext highlighter-rouge">joblists</code>: A folder that has some sample config scripts as json files.</li>
  <li><code class="language-plaintext highlighter-rouge">unified_run.py</code>: The end-user python script that starts the entire simulation.</li>
</ul>

<p>Instructions on how to use this platform can be found in the following sections.</p>

<h2 id="workflow">Workflow</h2>

<p>In short, we use this setup to fast-forward simulations using gem5 to reach the ROI and take a checkpoint. We then end the simulation and start is again in SST while loading the checkpoint.</p>

<p>SST does not allow untimed memory accesses at runtime as different gem5 nodes might be reciding on different processes. Therefore, we split this simulation into two phases. The following diagram shows the workflow of the platform.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>G t0 : starting simulation in gem5 (atomic/kvm)
E |
M |     t1 : simulation reached the start of ROI
5 |_____|____________________________________________________________ time -&gt;
         |                                                  |
S        t2 : we start the simulation in SST (timing)       |
S                                                           |
T                                       end of simulation : t3
</code></pre></div></div>
<p>The first phase is entirely in gem5. This is represented by time t0 and t1. The objective here is to reach the ROI asap take a checkpoint.</p>

<p>The second phase starts by loading the checkpoint back into the system but
using an SST-side script. The system remains identical except for the External
Memory, which now sends requests and receives responses to and from SST’s
memory.</p>

<p>This can be scaled into N differnt gem5 nodes. Checkpoints need to be taken for
each of these nodes in their respective first phases.</p>

<p>See the paper link here for a better visualization.</p>

<h1 id="using-the-infrastructure">Using the infrastructure</h1>

<p>The setup of this infrastructure is fairly complicated.
The main reason being two different softwares tied together.
SST in theory can run gem5 in KVM creating just one simple run command however, it doesn’t support that at the moment.
Therefore we came up with a simple python interface that allows users to quickly simulate multiple gem5 instances with or without sharing remote memory in SST.</p>

<p>To understand the required paramters of the infrastructure, you can:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python disaggregated_memory/unified_run.py <span class="nt">-h</span>
</code></pre></div></div>
<p>Please don’t get overwhelmed by the number of parameters the script accepts!
To simplify the usability of the infrastructure, we let the user specify a JSON joblist.
The configuration of a system is best written as a python script.
However, since there are multiple systems now, we need a better and more structured way of denoting systems without code repetations.
This can be further argued and I’d love to hear other ways of improving this.</p>

<p>Here are the required parameters to start a simulation:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">--clock</span><span class="o">=[</span>str]       <span class="c"># Specify the clock frequency in Hz. All the hosts are</span>
                    <span class="c"># clocked at the same frequency. You can have variable</span>
                    <span class="c"># frequency, however this feature is not implemented.</span>
<span class="nt">--count</span><span class="o">=[</span>int]       <span class="c"># Specify the number of hosts in the system. The number of</span>
                    <span class="c"># SST MPI processes is automatically count + 1, where the</span>
                    <span class="c"># final process is for the remote memory.</span>
<span class="nt">--exp-name</span><span class="o">=[</span>str]    <span class="c"># Specify the name of the output directory. All stats,</span>
                    <span class="c"># checkpoints and output files will be stored here.</span>
<span class="nt">--systemd</span><span class="o">=[</span>bool]    <span class="c"># Optionally specify if the simulation needs to boot up</span>
                    <span class="c"># the OS using systemd. This is helpful if the user wants</span>
                    <span class="c"># to fast forward without kvm.</span>
<span class="nt">--joblist</span><span class="o">=[</span>str]     <span class="c"># Specify the path to a JSON file that describes a host.</span>
</code></pre></div></div>

<p>There are other features to create the same system multiple times without creating a joblist.
These features are not guaranteed to be supported at a later version.</p>

<p><strong>I don’t know a better way to describe how to define multiple node specifications using a single script. I ended up using JSON as the most structured way to do the same. Shoot me an email if you have better ways to do this.</strong></p>

<p>To describe a system, here is a simple joblist explaining each of the keys:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"&lt;node-id&gt;"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"comment"</span><span class="p">:</span><span class="w"> </span><span class="s2">"For better book-keeping of each host, the JSON allows commenting as a key-value."</span><span class="p">,</span><span class="w">
            </span><span class="nl">"experiment"</span><span class="p">:</span><span class="w"> </span><span class="s2">"The output directory of this host will be created using this string."</span><span class="p">,</span><span class="w">
            </span><span class="nl">"checkpoint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"[optional] Specify the path to already load a checkpoint for this host."</span><span class="p">,</span><span class="w">
            </span><span class="nl">"max-tics"</span><span class="p">:</span><span class="w"> </span><span class="s2">"[optional] Specify an absolute maximum number of cycles to simulate across all the nodes. Enforced by SST."</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"cpu"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"ff-core"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Specify the CPU to use for fast-forwarding [atomic/kvm]"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"roi-core"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Specify a timing-based CPU for the ROI [timing/o3]"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"isa"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Specify the ISA to simulate (can be mixed too!) [arm/riscv/x86]"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"count"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Specify the maximum number of CPU cores to simulate."</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"cache"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Specify the cache hierarchy [l1l2/l1l2l3]"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"l1i-size"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Specify the amount of L1I cache in KiB/MiB"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"l1d-size"</span><span class="p">:</span><span class="w"> </span><span class="s2">"32KiB"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"l2-size"</span><span class="p">:</span><span class="w"> </span><span class="s2">"512KiB"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"l3-size"</span><span class="p">:</span><span class="w"> </span><span class="s2">"8MiB"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"l3-assoc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"16"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"local-memory"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ddr4"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"size"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2GiB"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"remote-memory"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"size"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
            </span><span class="nl">"shared"</span><span class="p">:</span><span class="w"> </span><span class="s2">"false"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"latency"</span><span class="p">:</span><span class="w"> </span><span class="s2">"170ns"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0x280000000"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0x300000000"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"workitem"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"cmd"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"echo </span><span class="se">\"</span><span class="s2">exp: starting STREAM remotely!</span><span class="se">\"\n</span><span class="s2">"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"numastat;"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"numactl --membind=1 -- /home/ubuntu/simple-vectorizable-benchmarks/stream/stream.hw.m5 8388608;"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"numastat;"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"m5 --addr=0x10010000 exit;"</span><span class="p">],</span><span class="w">
            </span><span class="nl">"disk"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/kaustavg/disk-images/arm/arm64-hpc-2204-numa-kvm.img-20240304"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"kernel"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/kaustavg/vmlinux-5.4.49-NUMA.arm64"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"bootloader"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/home/kaustavg/kernel/arm/bootloader/arm64-bootloader"</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>See the following sections on how to start the simulation.</p>

<h2 id="building">Building</h2>

<p>You need to build the infrastructure first! Follow the following steps if you’re building the infrastructure on a Linux based system:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone git@github.com:darchr/cxl-clustersim
<span class="nb">cd </span>cxl-clustersim
scons build/ALL/gem5.opt <span class="nt">-j16</span>
scons build/ALL/libgem5_opt.so <span class="nt">-j16</span> <span class="nt">--without-tcmalloc</span> <span class="nt">--duplicate-sources</span>

<span class="c"># Start building SST now</span>
<span class="nb">cd </span>ext/sst
<span class="nb">export </span><span class="nv">SST_CORE_HOME</span><span class="o">=</span><span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>
wget https://github.com/sstsimulator/sst-core/releases/download/v14.0.0_Final/sstcore-14.0.0.tar.gz
<span class="nb">tar </span>xf sstcore-14.0.0.tar.gz
<span class="nb">cd </span>sstcore-14.0.0
./configure <span class="nt">--prefix</span><span class="o">=</span><span class="nv">$SST_CORE_HOME</span> <span class="nt">--with-python</span><span class="o">=</span>/usr/bin/python3-config
make all <span class="nt">-j128</span>
make <span class="nb">install
cd</span> ..
wget https://github.com/sstsimulator/sst-elements/releases/download/v14.0.0_Final/sstelements-14.0.0.tar.gz
<span class="nb">tar </span>xf sstelements-14.0.0.tar.gz
<span class="nb">cd </span>sst-elements-library-14.0.0
./configure <span class="nt">--prefix</span><span class="o">=</span><span class="nv">$SST_CORE_HOME</span> <span class="nt">--with-python</span><span class="o">=</span>/usr/bin/python3-config <span class="se">\</span>
                                        <span class="nt">--with-sst-core</span><span class="o">=</span><span class="nv">$SST_CORE_HOME</span>
make all <span class="nt">-j128</span>
make <span class="nb">install
cd</span> ..
<span class="c"># To avoid doing this everytime when compiling the gem5 shared object, put this lines in .bashrc</span>
<span class="nb">export </span><span class="nv">PKG_CONFIG_PATH</span><span class="o">=</span><span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/lib/pkgconfig

<span class="c"># There are mac makefiles in case you want to test it with MacOS.</span>
<span class="nb">mv </span>Makefile.linux Makefile
make clean <span class="p">;</span> make <span class="nv">ARCH</span><span class="o">=</span>ALL <span class="nt">-j4</span>

<span class="c"># go back to the top gem5 directory</span>
<span class="nb">cd</span> ../..
</code></pre></div></div>

<p>The instructions for the <code class="language-plaintext highlighter-rouge">dev</code> branch may be different. Please see the local READMEs to keep up with the same.</p>

<h2 id="sample-example">Sample Example</h2>

<p>There are sample joblist in the repository that is used to generate the results of the paper.
To simulate STREAM in the remote memory with 170ns of remote memory latency across 4 nodes, you can:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python disaggregated_memory/unified_run.py <span class="nt">--clock</span><span class="o">=</span>4GHz <span class="se">\</span>
                <span class="nt">--count</span><span class="o">=</span>4 <span class="se">\</span>
                <span class="nt">--exp-name</span><span class="o">=</span>sample_STREAM_example_with_4_nodes <span class="se">\</span>
                <span class="nt">--joblist</span><span class="o">=</span>disaggregated_memory/joblist/case-study-1/170ns/joblist_stream_remote_4_nodes.json <span class="se">\</span>
                <span class="nt">--systemd</span><span class="o">=</span>True
</code></pre></div></div>
<p>This will start the fast-forward simulation in gem5.
Then it’ll create checkpoints in <code class="language-plaintext highlighter-rouge">sample_STREAM_example_with_4_nodes/exp_name_*/</code> directories.
The simulation will then automatically switch to SST.
The final statistics will then be written to <code class="language-plaintext highlighter-rouge">sample_STREAM_example_with_4_nodes</code> directory.</p>

<h2 id="gem5-resources">Gem5 resources</h2>

<p>TODO.
This section is under-contruction.</p>

<p>The setup requires specialized kernel and disk images.
Instructions on how to compile the disk image, kernel, bootloader etc. can be found at <code class="language-plaintext highlighter-rouge">https://github.com/kaustav-goswami/gem5-resources</code> in the branch <code class="language-plaintext highlighter-rouge">disaggregated</code></p>

<h2 id="changing-the-infrastructure-to-implement-a-research-idea">Changing the infrastructure to implement a research idea</h2>

<p>Changing gem5 is fairly complicated in my opinion.
There is a structured way of changing the infrastrcuture.
This section explains most of the necessary changes.</p>

<h3 id="varying-cxl-latency">Varying CXL latency</h3>

<p>The host to device latency is a configurable parameter per host.
On a joblist file, changing the [“remote-memory”][“latency”] in unit time (ps, ns, us, s) changes the CXL latency.
Improving the device memory controller or QoS needs to be investigated on the SST-side.</p>

<h3 id="adding-new-host-side-logic">Adding new host-side logic</h3>

<p>Since hosts are implemented in gem5, the user only needs to add new SimObjects for timing, atomic and functional requests and responses.
The LLC <code class="language-plaintext highlighter-rouge">mem_side_ports</code> (from a classic membus for this current version) needs to be connected to the new SimObject’s <code class="language-plaintext highlighter-rouge">cpu_side_port</code> and the SimObject’s <code class="language-plaintext highlighter-rouge">mem_side_port</code> needs to be connected to ExternalMemory’s <code class="language-plaintext highlighter-rouge">cpu_side_ports</code> aka <code class="language-plaintext highlighter-rouge">port</code>.</p>

<h3 id="adding-new-device-side-logic">Adding new device-side logic</h3>

<p>Device-side logic needs to be implemented in SST.
The <code class="language-plaintext highlighter-rouge">translator.h</code> translates gem5 packets to SST standardmem requests.
Device-side logic can be added at the gem5 subcomponent, that has access to the host’s memory range.
In the case where a user wants to add new logic for shared memory across multiple hosts, a new SST component might be needed that accepts SST::StandardMem::Request after the gem5 subcomponent, byt before SST’s memHierarchy.
See <a href="https://sst-simulator.org/sst-docs/docs/elements/memHierarchy/stdmem">https://sst-simulator.org/sst-docs/docs/elements/memHierarchy/stdmem</a> on how to get started.</p>

<p>The CXL BISnp is implemented in SST by extending the timingDRAM to send BISnp as an <code class="language-plaintext highlighter-rouge">InvNotify*</code> object from the StandardMem interface.
The <code class="language-plaintext highlighter-rouge">translator.h</code> and <code class="language-plaintext highlighter-rouge">sst_responder_subcomponent.cc</code> is modified accordingly to accept incoming <code class="language-plaintext highlighter-rouge">InvNotify</code> requests back to gem5.</p>

<h1 id="shared-memory">Shared Memory</h1>

<p>CXL 3.0 allows simulating device-side shared memory.
We implement memory sharing via DAX devices in CXL-ClusterSim.
We demonstrate an example using GAPBS, a popular graph benchmark suite.</p>

<p>Prepare a new joblist where physical address ranges are shared across multiple hosts.</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">    </span><span class="err">..</span><span class="w">
    </span><span class="nl">"remote-memory"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"size"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
        </span><span class="nl">"shared"</span><span class="p">:</span><span class="w"> </span><span class="s2">"true"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"latency"</span><span class="p">:</span><span class="w"> </span><span class="s2">"170ns"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0x500000000"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0x900000000"</span><span class="w">
    </span><span class="p">}</span><span class="w">
    </span><span class="err">..</span><span class="w">
</span></code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">start</code> and <code class="language-plaintext highlighter-rouge">end</code> addresses of multiple hosts needs to be the same.
There can be multiple shared memory ranges.
SST automatically figures out the memory ranges.</p>

<p>In gem5, we make sure that the same range is mapped to the same <code class="language-plaintext highlighter-rouge">AddrRange</code>.
The <code class="language-plaintext highlighter-rouge">ExternalMemory</code> class takes care of the rest.
There exists an additional XBar after the membus, which is a proxy for adding additional lookup latencies in gem5.
The CXL specification’s SAT, GDT, GFD tables etc. are not implemented.
Instead additional latencies is added at the crossbar.</p>

<h2 id="shared-memory-workloads">Shared Memory Workloads</h2>

<p>We do not have a lot of shared memory workloads as of today.
Disaggregated shared memory workloads is an active research topic (e.g. <a href="https://www.usenix.org/conference/osdi25/presentation/huang-yibo">Tigon</a>).
We use the same foundation laid by <a href="https://github.com/cxl-micron-reskit/famfs">FAMFS</a> of using <code class="language-plaintext highlighter-rouge">/dev/dax</code> to enable physical address range sharing across hosts.</p>

<p>The shared memory programming model is another research topic.
A simple single writer multiple reader for shared CXL memory is the easiest way to create such workloads.
Such workloads are memory-decoupled. 
For instance, GAPBS, a popular graph processing benchmark, can be extended to share a graph across multiple hosts , where each host executes a different graph kernel (i.e. BFS) on the same graph.
Similarly, memcached can be reconfigured to store both keys (yes, its slow) and the values on the shared memory such that multiple hosts can querry the same database.
ML workloads can also share the weights across hosts running inference on the same model and these weights are usually not updated.
I am working on a practical way to easily identify and create memory-decoupled workloads from existing workloads.</p>

<p>On the other hand, compute-decoupled workloads, where parts of the workload are distributed across hosts is a difficult performance problem to solve.
Disaggregated memory does not use RPC to find a variable on the remote memory as there is no compute logic on the remote memory node.
It uses LD/ST instructions directly to save time (1000x).
The infrastrcuture currently supports software-based coherence with <code class="language-plaintext highlighter-rouge">clwb</code> and <code class="language-plaintext highlighter-rouge">clflush</code> instructions, enabling the exploration of multiple writer multiple reader workloads without any consistency guarantees.</p>

<p>Relevant resources include:</p>
<ol>
  <li><a href="https://github.com/kaustav-goswami/shared-memcached">shared-memcached</a></li>
  <li><a href="https://github.com/kaustav-goswami/YCSB">YCSB</a></li>
  <li><a href="https://github.com/kaustav-goswami/gem5-resources">gem5 resources</a>; branch: disaggregated</li>
</ol>

<h2 id="building-resources">Building resources</h2>

<p>For simplicity, we demonstrate a single writer host and multiple reader hosts programming model where a host allocates a graph in the shared memory and the rest of the hosts run a graph kernel from GAPBS.
We used a special version of <a href="https://github.com/darchr/shared-gapbs">GAPBS</a> that enables graph sharing between weighted and unweighted graphs.</p>

<p>Prepare the workload by building the gem5 resources:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone git@github.com:kaustav-goswami/gem5-resources
<span class="nb">cd </span>gem5-resources
git checkout disaggregated
<span class="nb">cd </span>src
<span class="nb">cd </span>shared-gapbs
./build-x86.sh 24.04
<span class="c"># artifacts will be created in the disk-images folder</span>

<span class="nb">cd</span> ..
<span class="nb">cd </span>kernels
<span class="c"># see README.md on how to build the kernel</span>
git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
<span class="nb">cd </span>linux
git checkout v6.9.9 <span class="c"># maybe</span>
<span class="nb">cp</span> ../linux-configs/config.x86.6.9.9 .config
make <span class="nt">-j</span><span class="sb">`</span><span class="nb">nproc</span><span class="sb">`</span>

</code></pre></div></div>

<p>Use these resources on your simulation script.</p>

<h2 id="running-examples">Running examples</h2>

<p>To run this example from the repository:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 disaggregated_memory/unified_run.py <span class="nt">--count</span><span class="o">=</span>8 <span class="nt">--exp-name</span><span class="o">=</span>gapbs-shared-170ns <span class="nt">--joblist</span> disaggregated_memory/joblist/shared-gapbs/base-170ns.json
python3 disaggregated_memory/unified_run.py <span class="nt">--count</span><span class="o">=</span>8 <span class="nt">--exp-name</span><span class="o">=</span>gapbs-shared-250ns <span class="nt">--joblist</span> disaggregated_memory/joblist/shared-gapbs/base-250ns.json
</code></pre></div></div>

<h2 id="software-enforced-back-invalidate-snoops-bisnp">Software-Enforced Back-Invalidate Snoops (BISnp)</h2>

<p>The infrastructure currently supports BISnp.
On a write seen by the shared memory, a BISnp via <code class="language-plaintext highlighter-rouge">InvNotify</code> request is sent to all the gem5s connected to the same port in SST.
The gem5-bridge translates the <code class="language-plaintext highlighter-rouge">InvNotify</code> to <code class="language-plaintext highlighter-rouge">TimingSnoop</code> packet and forces the caches to invalidate the cache line, if cached.
This is enabled via a new SST subcomponent that extends timingDRAM.</p>

<p>On a side note, I have been testing the infrastructure (and gem5 in general) with <code class="language-plaintext highlighter-rouge">clwb</code> and <code class="language-plaintext highlighter-rouge">clflush</code> instructions.
From my understanding, these instructions are implemented in classic caches in gem5.
However, these instructions are not sent to the downstream cache/memory immediately (see src/mem/cache/Cache.py params <code class="language-plaintext highlighter-rouge">clusivity</code> and <code class="language-plaintext highlighter-rouge">writeback_clean</code>).</p>

<h3 id="known-issues">Known Issues</h3>

<p>The infrastructure does require the caches to be warmed up a bit before the ROI starts and ends.</p>

<h2 id="building-with-bisnp">Building with BISnp</h2>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://github.com/sstsimulator/sst-core/releases/download/v15.0.0_Final/sstcore-15.0.0.tar.gz
<span class="nb">tar </span>xzf sstcore-15.0.0.tar.gz
<span class="nb">cd </span>sstcore-15.0.0
./configure <span class="nt">--prefix</span><span class="o">=</span><span class="nv">$SST_CORE_HOME</span> <span class="nt">--with-python</span><span class="o">=</span>/usr/bin/python3-config <span class="se">\</span>
            <span class="nt">--disable-mpi</span> <span class="c"># optional, used when MPI is not available.</span>
make all <span class="nt">-j</span><span class="si">$(</span><span class="nb">nproc</span><span class="si">)</span>
make <span class="nb">install
export </span><span class="nv">PATH</span><span class="o">=</span><span class="nv">$SST_CORE_HOME</span>/bin:<span class="nv">$PATH</span>

https://github.com/kaustav-goswami/sst-elements.git
<span class="c"># make sure to use the correct branch</span>
<span class="nb">cd </span>sst-elements
checkout cxl-bi
./autogen.sh
<span class="nb">cd</span> ..

<span class="nb">cd </span>sst-elements
./configure <span class="nt">--prefix</span><span class="o">=</span><span class="nv">$SST_CORE_HOME</span> <span class="nt">--with-python</span><span class="o">=</span>/usr/bin/python3-config <span class="se">\</span>
            <span class="nt">--with-sst-core</span><span class="o">=</span><span class="nv">$SST_CORE_HOME</span>
make all <span class="nt">-j</span><span class="si">$(</span><span class="nb">nproc</span><span class="si">)</span>
make <span class="nb">install

echo</span> <span class="s2">"export PKG_CONFIG_PATH=</span><span class="nv">$PKG_CONFIG_PATH</span><span class="s2">:</span><span class="nv">$SST_CORE_HOME</span><span class="s2">/lib/pkgconfig/"</span> <span class="o">&gt;&gt;</span> ~/.bashrc
scons build/RISCV/libgem5_opt.so <span class="nt">-j</span> <span class="si">$(</span><span class="nb">nproc</span><span class="si">)</span> <span class="nt">--without-tcmalloc</span> <span class="nt">--duplicate-sources</span>
</code></pre></div></div>]]></content><author><name>Kaustav Goswami</name></author><category term="Simulation" /><category term="CXL" /><category term="gem5" /><category term="SST" /><category term="simulation" /><summary type="html"><![CDATA[Simulating multiple gem5 instances isn’t feasible. This documents how to use the composable memory simulation platform in a gem5, SST and gem5 + SST setup. The setup can be used in gem5 to fast-forward full-system simulation and then used in SST to simulate a multi-node system.]]></summary></entry><entry><title type="html">HammerSim: Simulating RowHammer in gem5</title><link href="https://kaustav-goswami.github.io/simulation/hammersim/" rel="alternate" type="text/html" title="HammerSim: Simulating RowHammer in gem5" /><published>2026-02-03T00:00:00+00:00</published><updated>2026-02-03T00:00:00+00:00</updated><id>https://kaustav-goswami.github.io/simulation/hammersim</id><content type="html" xml:base="https://kaustav-goswami.github.io/simulation/hammersim/"><![CDATA[<p>This file contains information on how to get started with the RowHammer module.
gem5 does not support RowHammer/data corruption by default but this repository adds several modifications to enable probabilistic modeling of RowHammer within gem5 in very fine-grained resolution, at the capacitor level.</p>

<p>TL;DR If you want to understand the code structure and data corruption, see <a href="#understanding-the-code-structure">understanding HammerSim</a>.</p>

<p>TL;DR If you want to add a new mitigation, see <a href="#adding-a-new-rowhammer-mitigation">adding new mitigation</a>.</p>

<p>TL;DR If you’re interested to start using the infrastructure ASAP, see <a href="#using-hammersim">using HammerSim</a></p>

<h1 id="changes-and-simulation-parameters">Changes and Simulation Parameters</h1>

<p>TL;DR Changes to the gem5’s source is confined to the following files:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">src/mem/DRAMInterface.py</code></li>
  <li><code class="language-plaintext highlighter-rouge">src/mem/mem_ctrl.cc</code></li>
  <li><code class="language-plaintext highlighter-rouge">src/mem/mem_ctrl.hh</code></li>
  <li><code class="language-plaintext highlighter-rouge">src/mem/packet.hh</code></li>
  <li><code class="language-plaintext highlighter-rouge">src/mem/dram_interface.hh</code></li>
  <li><code class="language-plaintext highlighter-rouge">src/mem/dram_interface.cc</code></li>
  <li><code class="language-plaintext highlighter-rouge">src/mem/mem_interface.hh</code></li>
  <li><code class="language-plaintext highlighter-rouge">src/mem/mem_interface.cc</code></li>
  <li><code class="language-plaintext highlighter-rouge">src/mem/SConscript</code></li>
</ul>

<p>Most of the RowHammer parameters are defined in <code class="language-plaintext highlighter-rouge">src/mem/DRAMInterface.py</code>.
In the class <code class="language-plaintext highlighter-rouge">DRAMInterface</code>, we have defined the following parameters:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">device_file</code> - Absolute path to the device map file. The “device map” file
refers to a list of all weak cells in the DRAM device. Currently we only
only flip bits at the column level. The resolution of a bit flip can be
further tuned to be at the capacitor level. This file is a <code class="language-plaintext highlighter-rouge">.json</code> file with
the following format:
    <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"rank_number"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"bank_number"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"row_number"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"(int)list_of_all_weak_columns"</span><span class="p">],</span><span class="w">
      </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div>    </div>
    <p>For getting started, you can use the map included in the repository under <code class="language-plaintext highlighter-rouge">prob-005.json.zip</code>.
This map is statistically generated using VARIUS
(S. Sarangi et al.) (see the abstract/writeup for details). You can also
generate this map from the hardware using a RowHammer software like TRRespass
(P. Frigo et al.) or Blacksmith (P. Jattke et al.).</p>
  </li>
  <li><code class="language-plaintext highlighter-rouge">rowhammer_threshold</code> - This is the number of activates requires to trigger a
single bitflip in a victim row. This number is taken from previous research
(Y. Kim et al., J. S. Kim et al.) which states that the minimum activates
required for DDR3 DRAM DIMMs is 139,000 and DDR4 DRAM DIMMs is 50,000. LPDDR
numbers are even lower (~8,000 – 16,000).</li>
  <li><code class="language-plaintext highlighter-rouge">counter_table_length</code> - This is a Target Row Refresh (TRR) specific
parameter. TRR is the mitigation mechanism present in all modern day DDR4
DRAM DIMMs. Most of these TRR parameters are either reverse-engineered via
previously mentioned RowHammer softwares or are taken from other reverse-
engineering papers including but not limited to (H. Hassan et al.).
<code class="language-plaintext highlighter-rouge">counter_table_length</code> is the total size of the main TRR table. TRR samples
frequently activated rows. This table keeps a track of these rows.</li>
  <li><code class="language-plaintext highlighter-rouge">trr_variant</code> - [0 – 4]. We have implemented a version of the 2 TRR variants
out of the three major DRAM vendors (Samsung, SK Hynix and MICRON) based on
previous reverse-engineering techniques and also our own observations. This
is not a 1:1 implementation of the actual TRR as it is proprietary, however
we have tested for similar bitflips in same rows against real hardware.
Following are the four different <code class="language-plaintext highlighter-rouge">trr_variants</code>:
    <ul>
      <li>0: No TRR</li>
      <li>1: A counter table-based TRR mechanism, which works on a per-bank basis.</li>
      <li>2: A sampler-based TRR mechanism, which maintains a global refreshing
   scheme.</li>
      <li>3: Partially implemented another sampler-based TRR mechanism, which is not
   verified.</li>
      <li>4: PARA (Y. Kim et al.), one of the first RH mitigation mechanism, which
   issues activates to rows with a probability P. This is hard-coded to
   PARA-001 in the source.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">companion_table_length</code> - Inserting a row into the companion table is tricky IMO.
Therefore, I have used another small table, similar to the work called ProHIT (M. Son et al.).
A row is initially inserted into the companion table first.
Then, it is promoted to the counter table.
This is specific to the TRR variant, which uses counter tables.</li>
  <li><code class="language-plaintext highlighter-rouge">companion_threshold</code> - This is minimum number of activates required to make
an entry into the companion table. Understandably, the threshold for the
companion table is much lower than the actual TRR table (1024).</li>
  <li><code class="language-plaintext highlighter-rouge">trr_stat_dump</code> - This is a boolean value ot dump all the actions of the TRR
mechanism. One can set this to true to do a post-runtime analysis of
RowHammer and TRR.</li>
  <li><code class="language-plaintext highlighter-rouge">rh_stat_dump</code> - Similar to <code class="language-plaintext highlighter-rouge">trr_stat_dump</code>, you can also dump the stats of
the RowHammer triggers. This is helpful for post-runtime analysis.</li>
  <li><code class="language-plaintext highlighter-rouge">single_sided_prob</code> - The number of bitflips observed with a single-sided
RowHammer attack is much lower than a double-sided rowhammer attack. We saw
that this drop is 1e7 times less probable than a double-sided RowHammer
attack.</li>
  <li><code class="language-plaintext highlighter-rouge">half_double_prob</code> - Half-double (Google) is even more rare than a single
sided RowHammer attack. We could not reproduce this with our experimental
hardware setup. Therefore, we took this number from the Half-Double report.
We kept this probability at 1/1e9.</li>
</ul>

<h1 id="understanding-the-code-structure">Understanding the code structure</h1>

<p>Change to gem5 in HammerSim is quite invasive, meaning that dram_interface was directly modified without adding new SimObjects.
The justification is that this is a separate fork of gem5, that models hardware equivalent of a DRAM DIMM within the simulator.
Since RowHammer is baked into the DIMM directly, we decided to make changes directly into the dram_interface.</p>

<p>RowHammer is implemented using counters called <code class="language-plaintext highlighter-rouge">rhTriggers[row][4]</code> per row per bank.
Each of these counters count the likelyhood of the neighboring rows getting triggered.
Whenever there is an ACTIVATE for a row <em>r</em>, the <code class="language-plaintext highlighter-rouge">rhTriggers[row]</code> increment by 1.
ACTIVATE is implemented in the method</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span>
<span class="n">DRAMInterface</span><span class="o">::</span><span class="n">activateBank</span><span class="p">(</span><span class="n">Rank</span><span class="o">&amp;</span> <span class="n">rank_ref</span><span class="p">,</span> <span class="n">Bank</span><span class="o">&amp;</span> <span class="n">bank_ref</span><span class="p">,</span>
                       <span class="n">Tick</span> <span class="n">act_tick</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">row</span><span class="p">)</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">rhTriggers</code> keeps a track of rows <em>r - 2</em>, <em>r - 1</em>, <em>r + 1</em> and <em>r + 2</em> using the four counter indices.
Since ACTIVATING the row and then PRECHARGING the same row nullifies the likelyhood of a bit flip, the rhTriggers of rows <em>r - 2</em>, <em>r - 1</em>, <em>r + 1</em> and <em>r + 2</em> are updated with respect to row <em>r</em>.</p>

<p>Whenever <code class="language-plaintext highlighter-rouge">rhTrigger[row][index]</code> crosses the <code class="language-plaintext highlighter-rouge">rowhammer_threshold</code> (defined via python), there is a non-zero probability of a bitflip on the same row <code class="language-plaintext highlighter-rouge">row</code>.
There are four probability distributions to make this decision:</p>
<ol>
  <li>Whether the given capacitor is weak (See RowHammer PUF [], FP-Rowhammer [] and FP-Hammer []). Prior research have shown that not every bit has the same probability of flipping. There are strong and weak cells. This information in HammerSim is captured via a variation map, provided as the <code class="language-plaintext highlighter-rouge">device_map</code>.</li>
  <li>The uniform probability of selecting a weak cell from a given set of weak cells.</li>
  <li>The uniform probability of selecting a double-sided or a single-sided bitflip.</li>
  <li>The uniform probability of causing a half-double bit flip.</li>
</ol>

<p>RowHammer is checked in the method:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span>
<span class="n">DRAMInterface</span><span class="o">::</span><span class="n">checkRowHammer</span><span class="p">(</span><span class="n">Bank</span><span class="o">&amp;</span> <span class="n">bank_ref</span><span class="p">,</span> <span class="n">MemPacket</span><span class="o">*</span> <span class="n">mem_pkt</span><span class="p">)</span>
<span class="p">{</span>
  <span class="c1">// each of the four conditions of having a bitflip is checked here.</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This method <code class="language-plaintext highlighter-rouge">checkRowHammer()</code> is called after finishing a burst access to the DRAM:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="n">Tick</span><span class="p">,</span> <span class="n">Tick</span><span class="o">&gt;</span>
<span class="n">DRAMInterface</span><span class="o">::</span><span class="n">doBurstAccess</span><span class="p">(</span><span class="n">MemPacket</span><span class="o">*</span> <span class="n">mem_pkt</span><span class="p">,</span> <span class="n">Tick</span> <span class="n">next_burst_at</span><span class="p">,</span>
                             <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">MemPacketQueue</span><span class="o">&gt;&amp;</span> <span class="n">queue</span><span class="p">)</span>
</code></pre></div></div>
<p>This makes sure to cause RowHammer bitflips if triggered in the current ACTIVATE.</p>

<p>To make changes on how RowHammer behaves, start with <code class="language-plaintext highlighter-rouge">checkRowHammer()</code> method.
This method automatically calls <code class="language-plaintext highlighter-rouge">doMemoryCorruption()</code> with the aggressor row address, and, victim locations.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span>
<span class="n">DRAMInterface</span><span class="o">::</span><span class="n">doMemoryCorruption</span><span class="p">(</span><span class="n">MemPacket</span><span class="o">*</span> <span class="n">mem_pkt</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">bank</span><span class="p">,</span>
                <span class="kt">uint32_t</span> <span class="n">victim_row</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">col</span><span class="p">,</span> <span class="kt">int</span> <span class="n">distance</span><span class="p">)</span> <span class="p">{</span>
</code></pre></div></div>
<p>The correct victim location (victim row, column, and capacitor) is computed each time when a new bit is flipped, which makes data corruption an expensive feature.
Currently, we only support DRAM address mapping with <code class="language-plaintext highlighter-rouge">ro</code> as the high bits and <code class="language-plaintext highlighter-rouge">co</code> as the low order bits.
See how different mapping in gem5 work: <a href="https://gem5-review.googlesource.com/c/public/gem5/+/51614/2/src/python/gem5/components/memory/ReadMe_MultiChannel_Memory.md">DRAM mappings in gem5</a>.
We’ll support other memory mappings soon with multi-channeled memory.</p>

<p>If you’re using <code class="language-plaintext highlighter-rouge">rowhammer-test</code>, should be able to see your aggressor rows and victim rows correctly.</p>

<h2 id="data-structures-for-writing-a-new-rowhammer-mitigation">Data structures for writing a new RowHammer Mitigation</h2>

<p>HammerSim implements TRR using <code class="language-plaintext highlighter-rouge">trr_tables[TABLE_LENGTH][4 PARAMETERS]</code> to keep track of highly activated rows per bank.
Each entry stores the rank, bank, row and a count of activates.
Some TRR implementations are per rank.
There are a lot of data structures that can be reused for keeping track of frequent aggressors.
See <code class="language-plaintext highlighter-rouge">companion_table</code> for a multi-table TRR/mitigation method.</p>

<p>In addition, there are <code class="language-plaintext highlighter-rouge">flagged_entries</code> to make sure the same capacitor cannot flip twice.
Aggressors are deterministically tracked using <code class="language-plaintext highlighter-rouge">aggressor_rows</code>.</p>

<h2 id="adding-a-new-rowhammer-mitigation">Adding a new RowHammer Mitigation</h2>

<p>Adding a new mitigation mechanism has to be done in the <code class="language-plaintext highlighter-rouge">dram_interface.cc</code> file.
This is done in:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// the sampler/counter mechanism is defined here.</span>
<span class="kt">void</span>
<span class="n">DRAMInterface</span><span class="o">::</span><span class="n">activateBank</span><span class="p">(</span><span class="n">Rank</span><span class="o">&amp;</span> <span class="n">rank_ref</span><span class="p">,</span> <span class="n">Bank</span><span class="o">&amp;</span> <span class="n">bank_ref</span><span class="p">,</span>
                       <span class="n">Tick</span> <span class="n">act_tick</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">row</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">...</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">trrVariant</span><span class="p">)</span> <span class="p">{</span>
        <span class="p">...</span>
        <span class="k">case</span> <span class="n">N</span><span class="p">:</span> <span class="p">{</span>
            <span class="c1">// write a new mitigation mechanism here.</span>
        <span class="p">}</span>
        <span class="p">...</span>
    <span class="p">}</span>
    <span class="p">...</span>
<span class="p">}</span>

<span class="c1">// the inhibitor mechanism is implemented here. this is because the inhibitor</span>
<span class="c1">// mechanism is triggers when the DRAM device is locked for refreshing.</span>
<span class="kt">void</span>
<span class="n">DRAMInterface</span><span class="o">::</span><span class="n">Rank</span><span class="o">::</span><span class="n">processRefreshEvent</span><span class="p">()</span> <span class="p">{</span>
    <span class="p">...</span>
    <span class="k">switch</span><span class="p">(</span><span class="n">dram</span><span class="p">.</span><span class="n">trrVariant</span><span class="p">)</span> <span class="p">{</span>
        <span class="p">...</span>
        <span class="k">case</span> <span class="n">N</span><span class="p">:</span> <span class="p">{</span>
            <span class="c1">// write the inhibitor mechanism here to keep DRAM timing</span>
            <span class="c1">// consistent.</span>
        <span class="p">}</span>
        <span class="p">...</span>
    <span class="p">}</span>
    <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>RowHammer bitflips are checked in the following function:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span>
<span class="n">DRAMInterface</span><span class="o">::</span><span class="n">checkRowHammer</span><span class="p">(</span><span class="n">Bank</span><span class="o">&amp;</span> <span class="n">bank_ref</span><span class="p">,</span> <span class="n">MemPacket</span><span class="o">*</span> <span class="n">mem_pkt</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="tutorial-mitigation-twice-tables">Tutorial mitigation: TWICE tables</h2>

<p>TWICE (E Lee et al.) is a good mitigation mechanism, easy to understand as a beginner to HammerSim.
In this tutorial, we show how to add TWICE in HammerSim.</p>

<p>TWICE mathematically calculates the theoretical maximum number of aggressors possible during one tREFW.
This is given by:
\(N = \frac{tREFW}{RowHammer Threshold \times tRC}\)</p>

<p>For a simple DDR4 DIMM, $N = 25$
This can be further pruned via:</p>

<p>To implement tracking of these many aggressors per bank, we first define the trrTableLength in the python class for the DIMM.</p>
<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">TwiceDIMM</span><span class="p">():</span>
    <span class="n">trr_table_length</span> <span class="o">=</span> <span class="n">N</span>
    <span class="c1"># We'll define a unique trr_variant number for this mitigation
</span>    <span class="n">trr_variant</span> <span class="o">=</span> <span class="mi">10</span>
</code></pre></div></div>
<p>We’ll use the above trr_variant to write our C++ changes.
For this, we’ll directly use <code class="language-plaintext highlighter-rouge">trrTable</code> that tracks rows with activate count.</p>

<h2 id="plating-with-error-correcting-codes-ecc">Plating with error correcting codes (ECC)</h2>

<p>TODO</p>

<p>HammerSim models a <strong>functional (not timing)</strong> ECC to detect and correct RowHammered bitflips.
We implement SECDED within a simplified interface.
ECC bits are computed and stored for each DRAM write.
Data is corrected if corrupt when read.
Since this is expensive to simulate in gem5 for full-system configureations, we selectively keep track of data that is corrupted.
If the user wants to simulate ECC, then at every data corruption, we track the original data for the ECC bits calculation.
At the time of reading the same data, we compute and use the ECC bits to correct up to 1 bit and detect up to 2 bits error.</p>

<h1 id="using-hammersim">Using HammerSim</h1>

<p>There are pre-defined config scripts, that can be directly used with HammerSim.
There are located in <code class="language-plaintext highlighter-rouge">configs/dram/rowhammer</code> directory.
There are both traffic generators and also full system scripts.
Note that the disk image path need to be replaced.</p>

<h2 id="synthetic-traffic-via-gem5s-traffic-generators">Synthetic Traffic via gem5’s Traffic Generators</h2>

<h2 id="full-system-simulation">Full-System Simulation</h2>

<h3 id="creating-full-system-rowhammer-workload">Creating Full-System RowHammer Workload</h3>

<p>Testing was done using <a href="https://github.com/google/rowhammer-test">Google’s rowhammer-test</a>.
We ran one iteration of the hammering run for both single-sided and double-sided versions of RowHammer.
<code class="language-plaintext highlighter-rouge">m5 exit</code> is dropped before starting the workload and <code class="language-plaintext highlighter-rouge">m5 exit</code> is dropped at the end of the iteration (with or without bitflips).</p>

<p>To create the disk image, use:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/kaustav-goswami/gem5-resources.git
<span class="nb">cd </span>gem5-resources
git checkout rowhammer
<span class="nb">cd </span>src/rowhammer-fs
./build-x86.sh 22.04
</code></pre></div></div>

<p>To build the same kernel that we used for the full system simulation:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://www.kernel.org/pub/linux/kernel/v5.x/linux-5.4.49.tar.xz
<span class="nb">tar </span>xvf linux-5.4.49.tar.xz
<span class="nb">cd </span>linux-5.4.49.tar.xz
menu config                                      <span class="c"># Use the default build</span>
make <span class="nt">-j32</span>
</code></pre></div></div>
<p>Alternately, you can also build the same kernel from <code class="language-plaintext highlighter-rouge">gem5-resources</code> repository.</p>

<p>Use custom resource in gem5 to plug these artifacts.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">board</span><span class="p">.</span><span class="n">set_kernel_disk_workload</span><span class="p">(</span>
    <span class="c1"># The x86 linux kernel will be automatically downloaded to the if not
</span>    <span class="c1"># already present.
</span>    <span class="n">kernel</span><span class="o">=</span><span class="n">CustomResource</span><span class="p">(</span>
        <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span>
            <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">expanduser</span><span class="p">(</span><span class="s">"~"</span><span class="p">),</span> <span class="s">".cache/gem5/x86-linux-kernel-5.4.49"</span>
        <span class="p">)</span>
    <span class="p">),</span>
    <span class="c1"># The x86 ubuntu image will be automatically downloaded to the if not
</span>    <span class="c1"># already present.
</span>    <span class="n">disk_image</span><span class="o">=</span><span class="n">CustomDiskImageResource</span><span class="p">(</span>
        <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">getcwd</span><span class="p">(),</span>
          <span class="s">"gem5-resources/src/rowhammer-fs/x86-disk-image-22-04/x86-ubuntu"</span><span class="p">),</span>
        <span class="n">root_partition</span><span class="o">=</span><span class="s">"1"</span>
    <span class="p">),</span>
    <span class="n">readfile_contents</span><span class="o">=</span><span class="s">" "</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">command</span><span class="p">),</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Scripts to run <code class="language-plaintext highlighter-rouge">rowhammer-test</code> is located in <code class="language-plaintext highlighter-rouge">configs/dram/rowhammer/FSConfigs/rowhammer-test/x86-rowhammer-with-kvm.py</code> (and there is a no cache version).</p>

<h1 id="more-information">More Information</h1>

<p>More on HammerSim can be found here: <a href="https://arch.cs.ucdavis.edu/memory/simulation/security/2023/03/20/yarch-hammersim.html">https://arch.cs.ucdavis.edu/memory/simulation/security/2023/03/20/yarch-hammersim.html</a></p>]]></content><author><name>Kaustav Goswami</name></author><category term="Simulation" /><category term="rowhammer" /><category term="gem5" /><summary type="html"><![CDATA[This file contains information on how to get started with the RowHammer module. gem5 does not support RowHammer/data corruption by default but this repository adds several modifications to enable probabilistic modeling of RowHammer within gem5 in very fine-grained resolution, at the capacitor level.]]></summary></entry></feed>