RISC-V from Scratch: Building a Virtual Machine
<p>I’ve always wanted to learn RISC-V. A few days ago, I finally got my hands dirty with it now.</p>
<p>This post will guide you through the process of building a simple RISC-V VM from the ground up, using Rust as our implementation language.</p>
<h2 id="Understanding-the-Core-Concepts"><a href="#Understanding-the-Core-Concepts" class="headerlink" title="Understanding the Core Concepts"></a>Understanding the Core Concepts</h2>
<p>Before writing any code, I need to grasp the fundamentals of RISC-V.</p>
<ul>
<li><strong>RISC vs. CISC</strong>: RISC (Reduced Instruction Set Computing) architectures use a small, highly optimized set of instructions. This is in contrast to CISC (Complex Instruction Set Computing), which has a large number of complex instructions. RISC-V’s simplicity makes it ideal for building a VM.</li>
<li><strong>Modular Architecture</strong>: RISC-V has a base instruction set (RV32I for 32-bit systems) and optional extensions like M (for multiplication) or F (for floating-point). We’ll focus on the RV32I base to keep things simple.</li>
<li><strong>The Three Pillars</strong>: At its core, a CPU (and thus our VM) consists of three main components:
<ul>
<li><strong>Registers</strong>: A small set of high-speed memory locations used for calculations. RISC-V has 32 general-purpose registers (<code>x0</code>-<code>x31</code>).</li>
<li><strong>Memory</strong>: A much larger space for storing program code and data.</li>
<li><strong>Program Counter (PC)</strong>: A special register that holds the memory address of the next instruction to be executed.</li>
</ul>
</li>
</ul>
<p>We can get all the details of RISC-V instructions from <a target="_blank" rel="noopener" href="https://riscv.atlassian.net/wiki/spaces/HOME/pages/16154769/RISC-V+Technical+Specifications">RISC-V Technical Specifications</a>.</p>
<h2 id="The-VM’s-Core-Logic"><a href="#The-VM’s-Core-Logic" class="headerlink" title="The VM’s Core Logic"></a>The VM’s Core Logic</h2>
<p>Our VM is essentially a program that emulates a real CPU’s behavior. The core of our VM is the <strong>instruction loop</strong>, which follows a simple <strong>fetch-decode-execute</strong> cycle.</p>
<ol>
<li><strong>Fetch</strong>: Read the 32-bit instruction from the memory address pointed to by the PC.</li>
<li><strong>Decode</strong>: Parse the instruction’s binary code to determine its type and what operation to perform.</li>
<li><strong>Execute</strong>: Perform the operation (e.g., an addition) and update the relevant registers or memory.</li>
</ol>
<p>Here’s a simplified Rust code snippet to illustrate the <code>VM</code> structure and the <code>run</code> loop:</p>
<pre class="line-numbers language-rust" data-language="rust"><code class="language-rust"><span class="token rust"><span class="token struct rust"><span class="token keyword rust">pub</span> <span class="token keyword struct rust">struct</span> </span><span class="token struct rust"><span class="entity name struct rust">VM</span> </span><span class="token struct rust"><span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token variable other member rust">x_registers</span><span class="token punctuation separator rust">:</span> [<span class="token keyword rust">u32</span>; 32],
<span class="token variable other member rust">pc</span><span class="token punctuation separator rust">:</span> <span class="token keyword rust">u32</span>,
<span class="token variable other member rust">memory</span><span class="token punctuation separator rust">:</span> <span class="token generic rust">Vec<span class="token punctuation definition generic begin rust"><</span><span class="token keyword rust">u8</span><span class="token punctuation definition generic end rust">></span></span>,
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span></span>
<span class="token impl rust"><span class="token keyword impl rust">impl</span> </span><span class="token impl rust"><span class="entity name impl rust">VM</span> </span><span class="token impl rust"><span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token function rust"><span class="token function rust"><span class="token keyword rust">pub</span> <span class="token keyword function rust">fn</span> </span><span class="token function rust">run</span></span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters begin rust">(</span><span class="token operator rust">&</span><span class="token keyword rust">mut</span> <span class="token variable parameter rust">self</span></span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters end rust">)</span></span></span></span><span class="token function rust"> </span><span class="token function rust"><span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">loop</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> 1. Fetch the instruction
</span> <span class="token keyword rust">let</span> instruction <span class="token operator rust">=</span> <span class="token variable language rust">self</span>.<span class="token function rust">fetch_instruction</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> 2. Decode
</span> <span class="token keyword rust">let</span> decoded_instruction <span class="token operator rust">=</span> <span class="token variable language rust">self</span>.<span class="token function rust">decode</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>instruction</span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> 3. Execute
</span> <span class="token variable language rust">self</span>.<span class="token function rust">execute_instruction</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>decoded_instruction</span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> 4. Increment the PC
</span> <span class="token variable language rust">self</span>.pc <span class="token operator rust">+</span><span class="token operator rust">=</span> <span class="token number integer decimal rust">4</span><span class="token punctuation terminator rust">;</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span></span>
</span></code></pre>
<p>The fetch instruction turns out to be very simple, we just load 4 bytes in little-endian format into a u32 integer:</p>
<pre class="line-numbers language-rust" data-language="rust"><code class="language-rust"><span class="token rust"> <span class="token comment documentation rust"><span class="token punctuation definition comment rust">///</span> Fetch 32-bit instruction from memory at current PC
</span> <span class="token function rust"><span class="token function rust"><span class="token keyword function rust">fn</span> </span><span class="token function rust">fetch_instruction</span></span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters begin rust">(</span><span class="token operator rust">&</span><span class="token variable parameter rust">self</span></span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters end rust">)</span></span></span></span><span class="token function rust"> <span class="token function return-type rust"><span class="token punctuation separator rust">-></span> <span class="token generic rust">Option<span class="token punctuation definition generic begin rust"><</span><span class="token keyword rust">u32</span><span class="token punctuation definition generic end rust">></span></span></span> </span><span class="token function rust"><span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">let</span> pc <span class="token operator rust">=</span> <span class="token variable language rust">self</span>.pc <span class="token operator rust">as</span> <span class="token keyword rust">usize</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">if</span> pc <span class="token operator rust">+</span> <span class="token number integer decimal rust">4</span> <span class="token operator rust">></span> <span class="token variable language rust">self</span>.memory.<span class="token function rust">len</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">return</span> <span class="token class-name rust">None</span><span class="token punctuation terminator rust">;</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> RISC-V uses little-endian byte order
</span> <span class="token keyword rust">let</span> instruction <span class="token operator rust">=</span> <span class="token keyword rust">u32</span><span class="token path rust"><span class="token punctuation accessor rust">::</span></span>from_le_bytes<span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token group rust"><span class="token punctuation section group begin rust">[</span>
<span class="token variable language rust">self</span>.memory<span class="token group rust"><span class="token punctuation section group begin rust">[</span>pc<span class="token punctuation section group end rust">]</span></span><span class="token punctuation separator rust">,</span>
<span class="token variable language rust">self</span>.memory<span class="token group rust"><span class="token punctuation section group begin rust">[</span>pc <span class="token operator rust">+</span> <span class="token number integer decimal rust">1</span><span class="token punctuation section group end rust">]</span></span><span class="token punctuation separator rust">,</span>
<span class="token variable language rust">self</span>.memory<span class="token group rust"><span class="token punctuation section group begin rust">[</span>pc <span class="token operator rust">+</span> <span class="token number integer decimal rust">2</span><span class="token punctuation section group end rust">]</span></span><span class="token punctuation separator rust">,</span>
<span class="token variable language rust">self</span>.memory<span class="token group rust"><span class="token punctuation section group begin rust">[</span>pc <span class="token operator rust">+</span> <span class="token number integer decimal rust">3</span><span class="token punctuation section group end rust">]</span></span><span class="token punctuation separator rust">,</span>
<span class="token punctuation section group end rust">]</span></span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
<span class="token class-name rust">Some</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>instruction</span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span></span>
</span></code></pre>
<p>Then we need to decode the integer into a RISC-V instruction. Here’s how we decode <code>IType</code> and <code>RType</code> instructions. The specifications for these two types are:
<img src="http://catcoding.me/images/ob_pasted-image-20251117091305.png" alt="" /></p>
<pre class="line-numbers language-rust" data-language="rust"><code class="language-rust"><span class="token rust"><span class="token comment documentation rust"><span class="token punctuation definition comment rust">///</span> Decode 32-bit instruction into structured format
</span><span class="token function rust"><span class="token function rust"><span class="token keyword function rust">fn</span> </span><span class="token function rust">decode</span></span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters begin rust">(</span><span class="token operator rust">&</span><span class="token variable parameter rust">self</span>, <span class="token variable parameter rust">code</span><span class="token punctuation separator rust">:</span> <span class="token keyword rust">u32</span></span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters end rust">)</span></span></span></span><span class="token function rust"> <span class="token function return-type rust"><span class="token punctuation separator rust">-></span> <span class="token generic rust">Option<span class="token punctuation definition generic begin rust"><</span>Instruction<span class="token punctuation definition generic end rust">></span></span></span> </span><span class="token function rust"><span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">let</span> opcode <span class="token operator rust">=</span> code <span class="token operator rust">&</span> <span class="token number integer hexadecimal rust">0x7f</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">match</span> opcode <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token number integer hexadecimal rust">0x13</span> <span class="token operator rust">=></span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> I-type instruction (ADDI, etc.)
</span> <span class="token keyword rust">let</span> rd <span class="token operator rust">=</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>code <span class="token operator rust">></span><span class="token operator rust">></span> <span class="token number integer decimal rust">7</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">&</span> <span class="token number integer hexadecimal rust">0x1f</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">as</span> <span class="token keyword rust">usize</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> rs1 <span class="token operator rust">=</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>code <span class="token operator rust">></span><span class="token operator rust">></span> <span class="token number integer decimal rust">15</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">&</span> <span class="token number integer hexadecimal rust">0x1f</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">as</span> <span class="token keyword rust">usize</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> funct3 <span class="token operator rust">=</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span>code <span class="token operator rust">></span><span class="token operator rust">></span> <span class="token number integer decimal rust">12</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">&</span> <span class="token number integer hexadecimal rust">0x7</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> imm <span class="token operator rust">=</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span>code <span class="token operator rust">as</span> <span class="token keyword rust">i32</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">></span><span class="token operator rust">></span> <span class="token number integer decimal rust">20</span><span class="token punctuation terminator rust">;</span> <span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> Sign-extended
</span>
<span class="token class-name rust">Some</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token path rust">Instruction<span class="token punctuation accessor rust">::</span></span>IType <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
rd<span class="token punctuation separator rust">,</span>
rs1<span class="token punctuation separator rust">,</span>
imm<span class="token punctuation separator rust">,</span>
funct3<span class="token punctuation separator rust">,</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
<span class="token number integer hexadecimal rust">0x33</span> <span class="token operator rust">=></span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> R-type instruction (ADD, SUB, etc.)
</span> <span class="token keyword rust">let</span> rd <span class="token operator rust">=</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>code <span class="token operator rust">></span><span class="token operator rust">></span> <span class="token number integer decimal rust">7</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">&</span> <span class="token number integer hexadecimal rust">0x1f</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">as</span> <span class="token keyword rust">usize</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> rs1 <span class="token operator rust">=</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>code <span class="token operator rust">></span><span class="token operator rust">></span> <span class="token number integer decimal rust">15</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">&</span> <span class="token number integer hexadecimal rust">0x1f</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">as</span> <span class="token keyword rust">usize</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> rs2 <span class="token operator rust">=</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>code <span class="token operator rust">></span><span class="token operator rust">></span> <span class="token number integer decimal rust">20</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">&</span> <span class="token number integer hexadecimal rust">0x1f</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">as</span> <span class="token keyword rust">usize</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> funct3 <span class="token operator rust">=</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span>code <span class="token operator rust">></span><span class="token operator rust">></span> <span class="token number integer decimal rust">12</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">&</span> <span class="token number integer hexadecimal rust">0x7</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> funct7 <span class="token operator rust">=</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span>code <span class="token operator rust">></span><span class="token operator rust">></span> <span class="token number integer decimal rust">25</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">&</span> <span class="token number integer hexadecimal rust">0x7f</span><span class="token punctuation terminator rust">;</span>
<span class="token class-name rust">Some</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token path rust">Instruction<span class="token punctuation accessor rust">::</span></span>RType <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
rd<span class="token punctuation separator rust">,</span>
rs1<span class="token punctuation separator rust">,</span>
rs2<span class="token punctuation separator rust">,</span>
funct3<span class="token punctuation separator rust">,</span>
funct7<span class="token punctuation separator rust">,</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
<span class="token operator rust">_</span> <span class="token operator rust">=></span> <span class="token class-name rust">None</span><span class="token punctuation separator rust">,</span> <span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> Unsupported opcode
</span> </span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span></span>
</span></code></pre>
<p>Then we want to execute the instruction, just following the specification. For demonstration purposes, we return the execution debug string as a result:</p>
<pre class="line-numbers language-rust" data-language="rust"><code class="language-rust"><span class="token rust"><span class="token comment documentation rust"><span class="token punctuation definition comment rust">///</span> Execute decoded instruction
</span><span class="token function rust"><span class="token function rust"><span class="token keyword function rust">fn</span> </span><span class="token function rust">execute</span></span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters begin rust">(</span><span class="token operator rust">&</span><span class="token keyword rust">mut</span> <span class="token variable parameter rust">self</span>, <span class="token variable parameter rust">instruction_type</span><span class="token punctuation separator rust">:</span> Instruction</span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters end rust">)</span></span></span></span><span class="token function rust"> <span class="token function return-type rust"><span class="token punctuation separator rust">-></span> <span class="token generic rust">Result<span class="token punctuation definition generic begin rust"><</span>String, String<span class="token punctuation definition generic end rust">></span></span></span> </span><span class="token function rust"><span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">match</span> instruction_type <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token path rust">Instruction<span class="token punctuation accessor rust">::</span></span>IType <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
rd<span class="token punctuation separator rust">,</span>
rs1<span class="token punctuation separator rust">,</span>
imm<span class="token punctuation separator rust">,</span>
funct3<span class="token punctuation separator rust">,</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span> <span class="token operator rust">=></span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">match</span> funct3 <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token number integer hexadecimal rust">0x0</span> <span class="token operator rust">=></span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> ADDI - Add immediate
</span> <span class="token variable language rust">self</span>.<span class="token function rust">write_register</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>rd<span class="token punctuation separator rust">,</span> <span class="token variable language rust">self</span>.x_registers<span class="token group rust"><span class="token punctuation section group begin rust">[</span>rs1<span class="token punctuation section group end rust">]</span></span> <span class="token operator rust">+</span> imm <span class="token operator rust">as</span> <span class="token keyword rust">u32</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
<span class="token class-name rust">Ok</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="support macro rust">format!</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span></span><span class="token group rust">
<span class="token string double rust"><span class="token punctuation definition string begin rust">"</span>ADDI x<span class="token constant placeholder rust">{}</span>, x<span class="token constant placeholder rust">{}</span>, <span class="token constant placeholder rust">{}</span> -> x<span class="token constant placeholder rust">{}</span> = <span class="token constant placeholder rust">{}</span><span class="token punctuation definition string end rust">"</span></span></span><span class="token group rust"><span class="token punctuation separator rust">,</span>
rd<span class="token punctuation separator rust">,</span> rs1<span class="token punctuation separator rust">,</span> imm<span class="token punctuation separator rust">,</span> rd<span class="token punctuation separator rust">,</span> <span class="token variable language rust">self</span>.x_registers<span class="token group rust"><span class="token punctuation section group begin rust">[</span>rd<span class="token punctuation section group end rust">]</span></span>
<span class="token punctuation section group end rust">)</span></span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
<span class="token operator rust">_</span> <span class="token operator rust">=></span> <span class="token class-name rust">Err</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="support macro rust">format!</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span></span><span class="token group rust"><span class="token string double rust"><span class="token punctuation definition string begin rust">"</span>Unsupported I-type funct3: <span class="token constant placeholder rust">{:#x}</span><span class="token punctuation definition string end rust">"</span></span></span><span class="token group rust"><span class="token punctuation separator rust">,</span> funct3<span class="token punctuation section group end rust">)</span></span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation separator rust">,</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
<span class="token path rust">Instruction<span class="token punctuation accessor rust">::</span></span>RType <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
rd<span class="token punctuation separator rust">,</span>
rs1<span class="token punctuation separator rust">,</span>
rs2<span class="token punctuation separator rust">,</span>
funct3<span class="token punctuation separator rust">,</span>
funct7<span class="token punctuation separator rust">,</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span> <span class="token operator rust">=></span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">match</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span>funct3<span class="token punctuation separator rust">,</span> funct7</span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token number integer hexadecimal rust">0x0</span><span class="token punctuation separator rust">,</span> <span class="token number integer hexadecimal rust">0x00</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">=></span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> ADD - Add registers
</span> <span class="token keyword rust">let</span> result <span class="token operator rust">=</span> <span class="token variable language rust">self</span>.x_registers<span class="token group rust"><span class="token punctuation section group begin rust">[</span>rs1<span class="token punctuation section group end rust">]</span></span> <span class="token operator rust">+</span> <span class="token variable language rust">self</span>.x_registers<span class="token group rust"><span class="token punctuation section group begin rust">[</span>rs2<span class="token punctuation section group end rust">]</span></span><span class="token punctuation terminator rust">;</span>
<span class="token variable language rust">self</span>.<span class="token function rust">write_register</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>rd<span class="token punctuation separator rust">,</span> result</span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
<span class="token class-name rust">Ok</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="support macro rust">format!</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span></span><span class="token group rust">
<span class="token string double rust"><span class="token punctuation definition string begin rust">"</span>ADD x<span class="token constant placeholder rust">{}</span>, x<span class="token constant placeholder rust">{}</span>, x<span class="token constant placeholder rust">{}</span> -> x<span class="token constant placeholder rust">{}</span> = <span class="token constant placeholder rust">{}</span><span class="token punctuation definition string end rust">"</span></span></span><span class="token group rust"><span class="token punctuation separator rust">,</span>
rd<span class="token punctuation separator rust">,</span> rs1<span class="token punctuation separator rust">,</span> rs2<span class="token punctuation separator rust">,</span> rd<span class="token punctuation separator rust">,</span> <span class="token variable language rust">self</span>.x_registers<span class="token group rust"><span class="token punctuation section group begin rust">[</span>rd<span class="token punctuation section group end rust">]</span></span>
<span class="token punctuation section group end rust">)</span></span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
<span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token number integer hexadecimal rust">0x0</span><span class="token punctuation separator rust">,</span> <span class="token number integer hexadecimal rust">0x20</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">=></span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> SUB - Subtract registers
</span> <span class="token keyword rust">let</span> result <span class="token operator rust">=</span> <span class="token variable language rust">self</span>.x_registers<span class="token group rust"><span class="token punctuation section group begin rust">[</span>rs1<span class="token punctuation section group end rust">]</span></span> <span class="token operator rust">-</span> <span class="token variable language rust">self</span>.x_registers<span class="token group rust"><span class="token punctuation section group begin rust">[</span>rs2<span class="token punctuation section group end rust">]</span></span><span class="token punctuation terminator rust">;</span>
<span class="token variable language rust">self</span>.<span class="token function rust">write_register</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>rd<span class="token punctuation separator rust">,</span> result</span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
<span class="token class-name rust">Ok</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="support macro rust">format!</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span></span><span class="token group rust">
<span class="token string double rust"><span class="token punctuation definition string begin rust">"</span>SUB x<span class="token constant placeholder rust">{}</span>, x<span class="token constant placeholder rust">{}</span>, x<span class="token constant placeholder rust">{}</span> -> x<span class="token constant placeholder rust">{}</span> = <span class="token constant placeholder rust">{}</span><span class="token punctuation definition string end rust">"</span></span></span><span class="token group rust"><span class="token punctuation separator rust">,</span>
rd<span class="token punctuation separator rust">,</span> rs1<span class="token punctuation separator rust">,</span> rs2<span class="token punctuation separator rust">,</span> rd<span class="token punctuation separator rust">,</span> <span class="token variable language rust">self</span>.x_registers<span class="token group rust"><span class="token punctuation section group begin rust">[</span>rd<span class="token punctuation section group end rust">]</span></span>
<span class="token punctuation section group end rust">)</span></span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
<span class="token operator rust">_</span> <span class="token operator rust">=></span> <span class="token class-name rust">Err</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="support macro rust">format!</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span></span><span class="token group rust">
<span class="token string double rust"><span class="token punctuation definition string begin rust">"</span>Unsupported R-type instruction: funct3=<span class="token constant placeholder rust">{:#x}</span>, funct7=<span class="token constant placeholder rust">{:#x}</span><span class="token punctuation definition string end rust">"</span></span></span><span class="token group rust"><span class="token punctuation separator rust">,</span>
funct3<span class="token punctuation separator rust">,</span> funct7
<span class="token punctuation section group end rust">)</span></span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation separator rust">,</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span></span>
</span></code></pre>
<p>The simplest VM code is available at: <a target="_blank" rel="noopener" href="https://github.com/chenyukang/ss/tree/main/risc_vm_v0">riscv-vm-v0</a></p>
<h2 id="From-Rust-to-RISC-V-binary"><a href="#From-Rust-to-RISC-V-binary" class="headerlink" title="From Rust to RISC-V binary"></a>From Rust to RISC-V binary</h2>
<p>Now we need to write more complex assembly code for testing our VM, but we don’t want to write assembly code by hand.</p>
<p>To test our VM, we will write Rust code then use <strong>cross-compile</strong> toolchains to compile it into RISC-V executable files.</p>
<ol>
<li><strong>Prepare the Environment</strong>: Install the <code>riscv32imac-unknown-none-elf</code> target toolchain. This is a bare-metal target, meaning it doesn’t rely on any operating system.</li>
</ol>
<pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shell bash"><span class="token function-call shell"><span class="token variable function shell">rustup</span></span><span class="token function-call arguments shell"> target add riscv32imac-unknown-none-elf</span>
</span></code></pre>
<p>Next, you’ll need a RISC-V linker. You can get this from the official RISC-V GNU toolchain.</p>
<pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shell bash"><span class="token comment number-sign shell"><span class="token punctuation definition comment begin shell">#</span></span><span class="token comment number-sign shell"> On Linux or macOS</span><span class="token comment number-sign shell">
</span><span class="token function-call shell"><span class="token variable function shell">sudo</span></span><span class="token function-call arguments shell"> apt-get install gcc-riscv64-unknown-elf</span>
<span class="token comment number-sign shell"><span class="token punctuation definition comment begin shell">#</span></span><span class="token comment number-sign shell"> Alternatively, on macOS</span><span class="token comment number-sign shell">
</span><span class="token function-call shell"><span class="token variable function shell">brew</span></span><span class="token function-call arguments shell"> install riscv-gnu-toolchain</span>
</span></code></pre>
<p><strong>Note:</strong> The <code>gcc-riscv64-unknown-elf</code> package includes both 32-bit and 64-bit tools.</p>
<ol start="2">
<li><strong>Write “Bare-Metal” Rust</strong>: Our Rust program must be written for a “bare-metal” environment, meaning you cannot use the standard library and must provide your own entry point and panic handler.</li>
</ol>
<pre class="line-numbers language-rust" data-language="rust"><code class="language-rust"><span class="token rust"><span class="token annotation rust"><span class="token punctuation definition annotation rust">#</span><span class="token punctuation section group begin rust">[</span><span class="token variable annotation rust">unsafe</span><span class="token annotation parameters rust"><span class="token group rust"><span class="token punctuation section group begin rust">(</span></span></span><span class="token annotation parameters rust"><span class="token group rust">no_mangle</span></span><span class="token annotation parameters rust"><span class="token group rust"><span class="token punctuation section group end rust">)</span></span></span><span class="token punctuation section group end rust">]</span></span>
<span class="token keyword rust">pub</span> <span class="token keyword rust">extern</span> <span class="token string double rust"><span class="token punctuation definition string begin rust">"</span>C<span class="token punctuation definition string end rust">"</span></span> <span class="token function rust"><span class="token function rust"><span class="token keyword function rust">fn</span> </span><span class="token function rust">_start</span></span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters begin rust">(</span></span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters end rust">)</span></span></span></span><span class="token function rust"> </span><span class="token function rust"><span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">let</span> <span class="token keyword rust">mut</span> sum <span class="token operator rust">=</span> <span class="token number integer decimal rust">0</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">for</span> i <span class="token operator rust">in</span> <span class="token number integer decimal rust">1</span><span class="token operator rust">..</span><span class="token operator rust">=</span><span class="token number integer decimal rust">10</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
sum <span class="token operator rust">+</span><span class="token operator rust">=</span> i<span class="token punctuation terminator rust">;</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> Store the result (which should be 55) in a known memory location.
</span> <span class="token keyword rust">let</span> result_ptr <span class="token operator rust">=</span> <span class="token number integer hexadecimal rust">0x1000</span> <span class="token operator rust">as</span> <span class="token operator rust">*</span><span class="token keyword rust">mut</span> <span class="token keyword rust">u32</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">unsafe</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token operator rust">*</span>result_ptr <span class="token operator rust">=</span> sum<span class="token punctuation terminator rust">;</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span></span>
<span class="token annotation rust"><span class="token punctuation definition annotation rust">#</span><span class="token punctuation section group begin rust">[</span><span class="token variable annotation rust">panic_handler</span><span class="token punctuation section group end rust">]</span></span>
<span class="token function rust"><span class="token function rust"><span class="token keyword function rust">fn</span> </span><span class="token function rust">panic</span></span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters begin rust">(</span><span class="token variable parameter rust">_info</span><span class="token punctuation separator rust">:</span> <span class="token operator rust">&</span>PanicInfo</span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters end rust">)</span></span></span></span><span class="token function rust"> <span class="token function return-type rust"><span class="token punctuation separator rust">-></span> </span></span><span class="token operator rust">!</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">loop</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span></span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span></code></pre>
<ol start="3">
<li><strong>Cross-Compile</strong>: Use <code>cargo</code> with the specific target and a linker script to build the executable. We need to add options for Cargo in <code>.cargo/config.toml</code></li>
</ol>
<pre class="line-numbers language-toml" data-language="toml"><code class="language-toml">[target.riscv32imac-unknown-none-elf]
rustflags = ["-C", "link-arg=-Tlink.ld"]
</code></pre>
<p>The content for <code>link.ld</code> is as follows. It tells the linker the layout of the binary file generated. Notice that we specify the entry point at address <code>0x80</code>:</p>
<pre class="line-numbers language-plain" data-language="plain"><code class="language-plain">OUTPUT_ARCH(riscv)
ENTRY(_start)
SECTIONS {
. = 0x80;
.text : {
*(.text.boot)
*(.text)
}
.rodata : {
*(.rodata)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}
</code></pre>
<p>Then we can build the program to a binary:</p>
<pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">cargo build --release --target riscv32imac-unknown-none-elf
</code></pre>
<ol start="4">
<li><strong>Disassemble and check the binary code</strong>: We can use the tool <code>riscv64-unknown-elf-objdump</code> to double-check the generated binary file:</li>
</ol>
<pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">riscv64-unknown-elf-objdump -d ./demo/target/riscv32imac-unknown-none-elf/release/demo
./demo/target/riscv32imac-unknown-none-elf/release/demo: file format elf32-littleriscv
Disassembly of section .text._start:
00000080 <_start>:
80: 4501 li a0,0
82: 4605 li a2,1
84: 45ad li a1,11
86: 4729 li a4,10
88: 00e61763 bne a2,a4,96 <_start+0x16>
8c: 46a9 li a3,10
8e: 9532 add a0,a0,a2
90: 00e61863 bne a2,a4,a0 <_start+0x20>
94: a809 j a6 <_start+0x26>
96: 00160693 addi a3,a2,1
9a: 9532 add a0,a0,a2
9c: 00e60563 beq a2,a4,a6 <_start+0x26>
a0: 8636 mv a2,a3
a2: feb6e3e3 bltu a3,a1,88 <_start+0x8>
a6: 6585 lui a1,0x1
a8: c188 sw a0,0(a1)
aa: 8082 ret
</code></pre>
<p>The complete cross-compile Rust code is available at: <a target="_blank" rel="noopener" href="https://github.com/chenyukang/ss/tree/main/riscv_vm/demo">riscv-demo</a></p>
<h2 id="Using-the-VM-to-Execute-Binary"><a href="#Using-the-VM-to-Execute-Binary" class="headerlink" title="Using the VM to Execute Binary"></a>Using the VM to Execute Binary</h2>
<p>The first problem is how do we parse the executable file? It turns out there is a crate called <code>elf</code> that can help us parse the header of an ELF file. We extract the interested parts from the header and record the <code>base_mem</code> so that we can convert <code>virtual address</code> to <code>physical address</code>. Of course, we also load the code into memory:</p>
<pre class="line-numbers language-rust" data-language="rust"><code class="language-rust"><span class="token rust"><span class="token function rust"><span class="token function rust"><span class="token keyword rust">pub</span> <span class="token keyword function rust">fn</span> </span><span class="token function rust">new_from_elf</span></span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters begin rust">(</span><span class="token variable parameter rust">elf_data</span><span class="token punctuation separator rust">:</span> <span class="token operator rust">&</span>[<span class="token keyword rust">u8</span>]</span><span class="token function rust"><span class="token function parameters rust"><span class="token punctuation section parameters end rust">)</span></span></span></span><span class="token function rust"> <span class="token function return-type rust"><span class="token punctuation separator rust">-></span> <span class="token keyword rust">Self</span></span> </span><span class="token function rust"><span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">let</span> <span class="token keyword rust">mut</span> memory <span class="token operator rust">=</span> <span class="support macro rust">vec!</span><span class="token group rust"><span class="token punctuation section group begin rust">[</span><span class="token number integer decimal rust">0</span><span class="token keyword numeric rust">u8</span><span class="token punctuation separator rust">;</span> <span class="token constant rust">MEM_SIZE</span><span class="token punctuation section group end rust">]</span></span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> elf <span class="token operator rust">=</span> <span class="token path rust">ElfBytes<span class="token punctuation accessor rust">::</span></span><span class="token generic rust"><span class="token punctuation definition generic begin rust"><</span><span class="token path rust">elf<span class="token punctuation accessor rust">::</span></span><span class="token path rust">endian<span class="token punctuation accessor rust">::</span></span>AnyEndian<span class="token punctuation definition generic end rust">></span></span><span class="token path rust"><span class="token punctuation accessor rust">::</span></span>minimal_parse<span class="token group rust"><span class="token punctuation section group begin rust">(</span>elf_data</span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span>
.<span class="token function rust">expect</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token string double rust"><span class="token punctuation definition string begin rust">"</span>Failed to parse ELF file<span class="token punctuation definition string end rust">"</span></span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> Get the program entry point
</span> <span class="token keyword rust">let</span> entry_point <span class="token operator rust">=</span> elf.ehdr.e_entry <span class="token operator rust">as</span> <span class="token keyword rust">u32</span><span class="token punctuation terminator rust">;</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> Iterate through program headers, load PT_LOAD type segments
</span> <span class="token keyword rust">for</span> segment <span class="token operator rust">in</span> elf.<span class="token function rust">segments</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span>.<span class="token function rust">expect</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token string double rust"><span class="token punctuation definition string begin rust">"</span>Failed to get segments<span class="token punctuation definition string end rust">"</span></span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">if</span> segment.p_type <span class="token operator rust">=</span><span class="token operator rust">=</span> <span class="token constant rust">PT_LOAD</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">let</span> virt_addr <span class="token operator rust">=</span> segment.p_vaddr <span class="token operator rust">as</span> <span class="token keyword rust">usize</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> file_size <span class="token operator rust">=</span> segment.p_filesz <span class="token operator rust">as</span> <span class="token keyword rust">usize</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> mem_size <span class="token operator rust">=</span> segment.p_memsz <span class="token operator rust">as</span> <span class="token keyword rust">usize</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> file_offset <span class="token operator rust">=</span> segment.p_offset <span class="token operator rust">as</span> <span class="token keyword rust">usize</span><span class="token punctuation terminator rust">;</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> Address translation: virtual address -> physical address
</span> <span class="token keyword rust">let</span> phys_addr <span class="token operator rust">=</span> virt_addr <span class="token operator rust">-</span> entry_point <span class="token operator rust">as</span> <span class="token keyword rust">usize</span><span class="token punctuation terminator rust">;</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> Check memory boundaries
</span> <span class="token keyword rust">if</span> phys_addr <span class="token operator rust">+</span> mem_size <span class="token operator rust">></span> <span class="token constant rust">MEM_SIZE</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="support macro rust">panic!</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>
<span class="token string double rust"><span class="token punctuation definition string begin rust">"</span>Segment is too large for the allocated memory. vaddr: {:#x}, mem_size: {:#x}<span class="token punctuation definition string end rust">"</span></span><span class="token punctuation separator rust">,</span>
virt_addr<span class="token punctuation separator rust">,</span> mem_size
</span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> Copy data from ELF file to memory
</span> <span class="token keyword rust">if</span> file_size <span class="token operator rust">></span> <span class="token number integer decimal rust">0</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">let</span> segment_data <span class="token operator rust">=</span> <span class="token operator rust">&</span>elf_data<span class="token group rust"><span class="token punctuation section group begin rust">[</span>file_offset<span class="token operator rust">..</span>file_offset <span class="token operator rust">+</span> file_size<span class="token punctuation section group end rust">]</span></span><span class="token punctuation terminator rust">;</span>
memory<span class="token group rust"><span class="token punctuation section group begin rust">[</span>phys_addr<span class="token operator rust">..</span>phys_addr <span class="token operator rust">+</span> file_size<span class="token punctuation section group end rust">]</span></span>.<span class="token function rust">copy_from_slice</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>segment_data</span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
<span class="token keyword rust">let</span> <span class="token keyword rust">mut</span> vm <span class="token operator rust">=</span> <span class="token constant rust">VM</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
x_registers<span class="token punctuation separator rust">:</span> <span class="token group rust"><span class="token punctuation section group begin rust">[</span><span class="token number integer decimal rust">0</span><span class="token punctuation separator rust">;</span> <span class="token number integer decimal rust">32</span><span class="token punctuation section group end rust">]</span></span><span class="token punctuation separator rust">,</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> Set directly to entry_point to match the linker script
</span> pc<span class="token punctuation separator rust">:</span> entry_point<span class="token punctuation separator rust">,</span>
memory<span class="token punctuation separator rust">,</span>
mem_base<span class="token punctuation separator rust">:</span> entry_point<span class="token punctuation separator rust">,</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span><span class="token punctuation terminator rust">;</span>
vm.x_registers<span class="token group rust"><span class="token punctuation section group begin rust">[</span><span class="token number integer decimal rust">0</span><span class="token punctuation section group end rust">]</span></span> <span class="token operator rust">=</span> <span class="token number integer decimal rust">0</span><span class="token punctuation terminator rust">;</span>
vm
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span></span>
</span></code></pre>
<p>What’s left is that we need to extend our VM to support all the instruction formats used in this binary file, including <code>li</code>, <code>bne</code>, <code>beq</code>, etc.</p>
<p>There are 16-bit compressed instructions, so we can’t always increment the PC by 4; sometimes we only need to increment it by 2 for shorter ones.</p>
<p>Another interesting thing is that some of them are conditional jump instructions, so we need to get the return <code>new_pc</code> from the execution of the instruction.</p>
<p>So now we need to update the core logic of fetch and execution of instructions:</p>
<pre class="line-numbers language-rust" data-language="rust"><code class="language-rust"><span class="token rust"><span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> Check the lowest 2 bits to determine instruction length
</span><span class="token keyword rust">if</span> first_half <span class="token operator rust">&</span> <span class="token number integer hexadecimal rust">0x3</span> <span class="token operator rust">!</span><span class="token operator rust">=</span> <span class="token number integer hexadecimal rust">0x3</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> 16-bit compressed instruction
</span> pc_increment <span class="token operator rust">=</span> <span class="token number integer decimal rust">2</span><span class="token punctuation terminator rust">;</span>
new_pc <span class="token operator rust">=</span> <span class="token variable language rust">self</span>.<span class="token function rust">execute_compressed_instruction</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>first_half</span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span> <span class="token keyword rust">else</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token comment double-slash rust"><span class="token punctuation definition comment rust">//</span> 32-bit instruction
</span> pc_increment <span class="token operator rust">=</span> <span class="token number integer decimal rust">4</span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">if</span> physical_pc.<span class="token function rust">saturating_add</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token number integer decimal rust">3</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust">></span><span class="token operator rust">=</span> <span class="token variable language rust">self</span>.memory.<span class="token function rust">len</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">break</span><span class="token punctuation terminator rust">;</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
<span class="token keyword rust">let</span> second_half <span class="token operator rust">=</span> <span class="token keyword rust">u16</span><span class="token path rust"><span class="token punctuation accessor rust">::</span></span>from_le_bytes<span class="token group rust"><span class="token punctuation section group begin rust">(</span><span class="token group rust"><span class="token punctuation section group begin rust">[</span>
<span class="token variable language rust">self</span>.memory<span class="token group rust"><span class="token punctuation section group begin rust">[</span>physical_pc <span class="token operator rust">+</span> <span class="token number integer decimal rust">2</span><span class="token punctuation section group end rust">]</span></span><span class="token punctuation separator rust">,</span>
<span class="token variable language rust">self</span>.memory<span class="token group rust"><span class="token punctuation section group begin rust">[</span>physical_pc <span class="token operator rust">+</span> <span class="token number integer decimal rust">3</span><span class="token punctuation section group end rust">]</span></span><span class="token punctuation separator rust">,</span>
<span class="token punctuation section group end rust">]</span></span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">let</span> instruction <span class="token operator rust">=</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span>second_half <span class="token operator rust">as</span> <span class="token keyword rust">u32</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span> <span class="token operator rust"><</span><span class="token operator rust"><</span> <span class="token number integer decimal rust">16</span> <span class="token operator rust">|</span> <span class="token group rust"><span class="token punctuation section group begin rust">(</span>first_half <span class="token operator rust">as</span> <span class="token keyword rust">u32</span></span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
<span class="token keyword rust">if</span> instruction <span class="token operator rust">=</span><span class="token operator rust">=</span> <span class="token number integer decimal rust">0</span> <span class="token block rust"><span class="token punctuation section block begin rust">{</span>
<span class="token keyword rust">break</span><span class="token punctuation terminator rust">;</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
new_pc <span class="token operator rust">=</span> <span class="token variable language rust">self</span>.<span class="token function rust">execute_instruction</span><span class="token group rust"><span class="token punctuation section group begin rust">(</span>instruction</span><span class="token group rust"><span class="token punctuation section group end rust">)</span></span><span class="token punctuation terminator rust">;</span>
</span><span class="token block rust"><span class="token punctuation section block end rust">}</span></span>
</span></code></pre>
<p>The complete new VM which can run compiled RISC-V binary files is available at: <a target="_blank" rel="noopener" href="https://github.com/chenyukang/ss/tree/main/riscv_vm">riscv-vm</a></p>
<h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2>
<ul>
<li><a target="_blank" rel="noopener" href="https://riscv.org/technical/specifications/">RISC-V Technical Specifications</a></li>
</ul>