<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>RISC-V on WSLIN</title><link>https://wslin.dev/tags/risc-v/</link><description>Recent content in RISC-V on WSLIN</description><generator>Hugo</generator><language>zh-tw</language><lastBuildDate>Thu, 23 Apr 2026 13:48:45 +0800</lastBuildDate><atom:link href="https://wslin.dev/tags/risc-v/index.xml" rel="self" type="application/rss+xml"/><item><title>【OS】兩個Process做Context Switch實現CPU Time Slicing機制</title><link>https://wslin.dev/blog/os-process-context-switch-cpu-time-slicing/</link><pubDate>Thu, 23 Apr 2026 13:48:45 +0800</pubDate><guid>https://wslin.dev/blog/os-process-context-switch-cpu-time-slicing/</guid><description>&lt;p&gt;經過了好幾個月的努力，終於成功的在RISC-V開發版上讓兩個Process 做context switch ，實現CPU time slicing 機制，雖然只是個小玩具，但絕對是學coding 這麼久以來最令人興奮的事情之一。&lt;/p&gt;&#10;&lt;h2 id="demo"&gt;&#10; Demo&#10; &lt;a class="heading-link" href="#demo"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;&#10;&#9;&#9;&#9;&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/L50YAd4V6M0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;&#10;&#9;&#9;&lt;/div&gt;&#10;&#10;&lt;h2 id="小小踩坑紀錄"&gt;&#10; 小小踩坑紀錄&#10; &lt;a class="heading-link" href="#%e5%b0%8f%e5%b0%8f%e8%b8%a9%e5%9d%91%e7%b4%80%e9%8c%84"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;同時也被一個bug困擾了快兩小時，症狀是在QEMU中都可以正常創建Process，但到了開發版上就完全沒有輸出。&#10;Debug超久才發現是QEMU預設會幫我們清零.bss（未初始化的全域變數），而開發版則不會，導致有些變數一開始就是髒的，果然真實硬體要注意的細節還是很多啊。&lt;/p&gt;</description></item><item><title>【RISC-V】Sv39 - 開啟MMU實現虛擬記憶體</title><link>https://wslin.dev/blog/sv39-risc-v-mmu/</link><pubDate>Thu, 23 Apr 2026 13:48:45 +0800</pubDate><guid>https://wslin.dev/blog/sv39-risc-v-mmu/</guid><description>&lt;p&gt;虛擬記憶體Virtual Memory絕對是現代OS最重要的核心機制，同時也是最複雜的系統之一，這篇文章就來記錄我對他的理解以及怎麼在OS實作。&lt;/p&gt;&#10;&lt;h2 id="why-virutual-memoryvm"&gt;&#10; Why Virutual Memory(VM)?&#10; &lt;a class="heading-link" href="#why-virutual-memoryvm"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;h3 id="傳統作法"&gt;&#10; 傳統作法&#10; &lt;a class="heading-link" href="#%e5%82%b3%e7%b5%b1%e4%bd%9c%e6%b3%95"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h3&gt;&#10;&lt;p&gt;首先我們要先了解傳統的記憶體管理像是分段Segment，有哪些缺點，以及VM如何改善的。&lt;/p&gt;&#10;&lt;p&gt;以分段為例，假設Process A的實體記憶體位址是&lt;code&gt;0x00001000-0x00002000&lt;/code&gt;，Process B是&lt;code&gt;0x00002000-0x00003000&lt;/code&gt;，可能會有以下問題:&lt;/p&gt;&#10;&lt;h4 id="載入時位址需relocation"&gt;&#10; 載入時位址需Relocation&#10; &lt;a class="heading-link" href="#%e8%bc%89%e5%85%a5%e6%99%82%e4%bd%8d%e5%9d%80%e9%9c%80relocation"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h4&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;一般編譯器編譯出來的可執行檔，記憶體位址預設都是從&lt;code&gt;0x00000000&lt;/code&gt;開始，如果此時OS分配該Process的起始位址到&lt;code&gt;0x00001000&lt;/code&gt;，loader就要負責處理這個Offset&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h4 id="擴充不易"&gt;&#10; 擴充不易&#10; &lt;a class="heading-link" href="#%e6%93%b4%e5%85%85%e4%b8%8d%e6%98%93"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h4&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;如果Process A記憶體不夠用了，想要擴充，就只能申請&lt;code&gt;0x00003000&lt;/code&gt;以後的位址使用，導致Process A內部要存取時必須處理位址不連續的狀況&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h4 id="權限管理不易"&gt;&#10; 權限管理不易&#10; &lt;a class="heading-link" href="#%e6%ac%8a%e9%99%90%e7%ae%a1%e7%90%86%e4%b8%8d%e6%98%93"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h4&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;如果某個Process意外存取另一個Process的位址空間應該要觸發Exception；或是某段位址空間理論上應該要W/R only，Segement都很難做到&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h4 id="外部碎片化"&gt;&#10; 外部碎片化&#10; &lt;a class="heading-link" href="#%e5%a4%96%e9%83%a8%e7%a2%8e%e7%89%87%e5%8c%96"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h4&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;當系統長時間運行後，可能不同Process間會有空閒的記憶體區塊，但卻因為太小不能放下新的連續空間的Process，導致整體看起來記憶體還夠，實際上卻放不了的狀況&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="virutual-memoryvm作法"&gt;&#10; Virutual Memory(VM)作法&#10; &lt;a class="heading-link" href="#virutual-memoryvm%e4%bd%9c%e6%b3%95"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h3&gt;&#10;&lt;p&gt;VM就是為了解決以上問題出現的，他的概念就是將記憶體切分成多個統一大小的Page，一般是4096 Bytes(4kb)當作基本分配記憶體的單位，並導入&lt;code&gt;邏輯/虛擬位址(Logical/Virtual Address，VA)&lt;/code&gt;轉&lt;code&gt;實體位址(Physical Address，PA)&lt;/code&gt;的概念，VA就是編譯後可執行檔從&lt;code&gt;0x00000000&lt;/code&gt;開始的位址，或是說整個Process內部實際使用的位址，PA則是真實物理記憶體的位址，也就是實際存放指令跟資料的地方。&lt;/p&gt;</description></item><item><title>【RISC-V】常用Register以及Caller/Callee Saved</title><link>https://wslin.dev/blog/risc-v-register-caller-callee-saved/</link><pubDate>Tue, 21 Apr 2026 13:48:45 +0800</pubDate><guid>https://wslin.dev/blog/risc-v-register-caller-callee-saved/</guid><description>&lt;p&gt;開發OS最重要的事情之一就是要熟悉使用的指令集，但我經常忘記RISC-V有哪些暫存器以及它們的功能等等，這篇文章就來記錄一下。&lt;/p&gt;&#10;&lt;h2 id="分類"&gt;&#10; 分類&#10; &lt;a class="heading-link" href="#%e5%88%86%e9%a1%9e"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;首先RISC-V每顆CPU核心都有幾類暫存器:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;x0-x31: 通用暫存器，最常計算用的&lt;/li&gt;&#10;&lt;li&gt;f0-f31: 浮點數運算使用，xv6沒有處理這個，編譯時就擋掉浮點數運算，我暫時也先不考慮支援這個，等到哪天真的碰到計算浮點數的Exception再說&lt;/li&gt;&#10;&lt;li&gt;csr (Control and Status Registers): 各種控制CPU行為的暫存器，非常重要&lt;/li&gt;&#10;&lt;li&gt;pc (Program Counter): 下一行指令的Address&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="x0-x31"&gt;&#10; x0-x31&#10; &lt;a class="heading-link" href="#x0-x31"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: left"&gt;Register&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: left"&gt;ABI 名稱&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: left"&gt;Caller/Callee Saved&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: left"&gt;用途&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x0&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;zero&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;恆為 0&lt;/strong&gt;。寫入會被丟棄，讀取永遠得到 0。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x1&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;ra&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;Caller&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;返回地址 (Return Address)&lt;/strong&gt;。執行 &lt;code&gt;call&lt;/code&gt; 時自動更新。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x2&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;sp&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;Callee&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;堆疊指標 (Stack Pointer)&lt;/strong&gt;。必須保持 16-byte 對齊。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x3&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;gp&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;全域指標 (Global Pointer)&lt;/strong&gt;。指向靜態全域變數區。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x4&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;tp&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;執行緒指標 (Thread Pointer)&lt;/strong&gt;。OS 常拿來存 CPU HartID 或 PCB。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x5 - x7&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;t0 - t2&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;Caller&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;臨時暫存器 (Temporary) 0 - 2。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x8&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;s0 / fp&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;Callee&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;Saved 0 / 幀指標 (Frame Pointer)&lt;/strong&gt;。用於 Trace 呼叫堆疊。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x9&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;s1&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;Callee&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;Saved 1。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x10 - x11&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;a0 - a1&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;Caller&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;函式參數 0 - 1 / 回傳值&lt;/strong&gt;。Syscall 結果也存於此。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x12 - x17&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;a2 - a7&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;Caller&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;函式參數 2 - 7。&lt;code&gt;a7&lt;/code&gt; 通常用於傳遞 Syscall ID。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x18 - x27&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;s2 - s11&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;Callee&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;Saved 2 - 11。&lt;code&gt;swtch.S&lt;/code&gt; 必須手動保存這組暫存器。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;x28 - x31&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;t3 - t6&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;Caller&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;臨時暫存器 3 - 6。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;註:&lt;/p&gt;</description></item><item><title>【RISC-V】指令、Regitster、Address長度小筆記</title><link>https://wslin.dev/blog/risc-v-regitster-address/</link><pubDate>Tue, 21 Apr 2026 13:48:45 +0800</pubDate><guid>https://wslin.dev/blog/risc-v-regitster-address/</guid><description>&lt;p&gt;每次都會忘記RISC-V各種東西的長度，因此要記錄下來。&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;指令: 32 bits&lt;/li&gt;&#10;&lt;li&gt;Regitster: 64 bits&lt;/li&gt;&#10;&lt;li&gt;Address: 64 bits (但SV39只用了39 bits 剩下的保留)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;因此如果要Load uint32到暫存器Lower的地方，就會需要lui (Load Upper Immediate) + addi指令&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-riscv" data-lang="riscv"&gt;lui a0, 0x12345 # 載入高 20 位元，a0 = 0x12345000&#10;addi a0, a0, 0x678 # 加上低 12 位元，a0 = 0x12345678&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;而如果是uint64則可以用Pseudo Instruction li&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-riscv" data-lang="riscv"&gt;li t0, 0x1234567812345678&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果不用li就會比較麻煩，需要再加上slli (Shift Left Logical Immediate)&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code class="language-riscv" data-lang="riscv"&gt;# 步驟 1：處理最高 32 位元的前半段 (0x12345)&#10;lui t0, 0x12345 # t0 = 0x0000000012345000&#10;addi t0, t0, 0x678 # t0 = 0x0000000012345678&#10;&#10;# 步驟 2：將這段數據推到最左邊 (左移 32 位)&#10;slli t0, t0, 32 # t0 = 0x1234567800000000&#10;&#10;# 步驟 3：開始蓋下半層大樓 (0x12345)&#10;# 注意：這裡通常會用另一個暫存器來湊，或者直接在 t0 上疊加&#10;lui t1, 0x12345 # t1 = 0x0000000012345000&#10;addi t1, t1, 0x678 # t1 = 0x0000000012345678&#10;&#10;# 步驟 4：將兩者合體&#10;or t0, t0, t1 # t0 = 0x1234567812345678&#10;&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>【OS】OpenSBI 開啟Timer Interrupt</title><link>https://wslin.dev/blog/os-opensbi-timer-interrupt/</link><pubDate>Mon, 20 Apr 2026 13:48:45 +0800</pubDate><guid>https://wslin.dev/blog/os-opensbi-timer-interrupt/</guid><description>&lt;p&gt;最近在實作RISC-V OS的Trap時，剛好要做Timer Interrupt，這在QEMU中非常簡單，只要follow xv6的做法就可以實現，但在Milkv開發版上就不太一樣。&lt;/p&gt;&#10;&lt;p&gt;因為我的Milkv開發版上不是裸機執行OS，而是透過OpenSBI來載入並降級到S-Mode，且又因為RISC-V的Timer Interrupt 是只能在M-Mode設置，因此做起來就比較麻煩，需要在S-Mode下透過ecall 委託M-Mode的OpenSBI來完成。&lt;/p&gt;&#10;&lt;h2 id="xv6在qemu上的做法"&gt;&#10; xv6在QEMU上的做法&#10; &lt;a class="heading-link" href="#xv6%e5%9c%a8qemu%e4%b8%8a%e7%9a%84%e5%81%9a%e6%b3%95"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;先來快速go through xv6是怎麼做Timer Interrupt的，首先需要先還在M-Mode時就先設置menvcfg (Machine Environment Configuration) 這個csr暫存器的bit 63來允許 S-mode 使用 stimecmp (Sstc or Supervisor-mode Timer Compare extension)。 (&lt;a href="https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/start.c" class="external-link" target="_blank" rel="noopener"&gt;xv6-start.c line:59&lt;/a&gt;)&lt;/p&gt;&#10;&lt;p&gt;然後再設置mcounteren的bit 1，允許 S-mode 讀 time 計數器，沒設的話在執行&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;asm&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;volatile&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;csrr %0, time&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;=r&amp;#34;&lt;/span&gt; (x));&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;時就會觸發 illegal instruction。&lt;/p&gt;&#10;&lt;p&gt;以上兩個都開啟後，就可以在S-Mode中設置stimecmp來開啟timer的計時功能了，時間到就會Trap，然後跳到我們寫好的Trap Handler處理。&lt;/p&gt;&#10;&lt;h2 id="milkv-duo-s-opensbi-上的做法"&gt;&#10; Milkv Duo S (OpenSBI) 上的做法&#10; &lt;a class="heading-link" href="#milkv-duo-s-opensbi-%e4%b8%8a%e7%9a%84%e5%81%9a%e6%b3%95"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;然而因為我目前的Milkv Duo S是跑在OpenSBI下的，所以OS一進來就是S-Mode，沒辦法碰到M-Mode的暫存器，加上他的CPU C906不支援Sstc，不能直接在S-Mode設timer。&lt;/p&gt;&#10;&lt;p&gt;但好在OpenSBI有開啟前面兩點，並提供直接設置Timer計數的功能，因此只要ecall呼叫一下OpenSBI請他開始計數即可。&lt;/p&gt;</description></item></channel></rss>