設計一個 RISC-V CPU,第 1 部分:軟件工程師如何學習硬件設計

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我在數字邏輯設計方面並沒有經驗。也就是說,直到最近我才決定嘗試設計自己的 CPU,並在 FPGA 上運行!如果你也是一名軟件工程師,並對硬件設計有着模糊的興趣,那麼我希望這一系列關於我所學到的知識的文章能夠對你有所幫助,並讓你感到有趣。本系列文章的第一部分中,將回答以下問題:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"什麼是數字邏輯設計?"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何開始,我應該使用什麼工具?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我將在以後的系列文章中詳細討論我的 CPU 設計和 RISC-V 架構,並將回答以下問題:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數字邏輯設計與軟件設計有什麼本質區別?"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數字邏輯設計和軟件設計有什麼相似之處?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以在"},{"type":"link","attrs":{"href":"https:\/\/github.com\/lochsh\/riscy-boi\/tree\/47e94dc6e9665f73c871add002c34d1516fd5106","title":null,"type":null},"content":[{"type":"text","text":"這裏"}]},{"type":"text","text":"看到我寫這篇文章時的 CPU 的代碼,或者在"},{"type":"link","attrs":{"href":"https:\/\/github.com\/lochsh\/riscy-boi","title":null,"type":null},"content":[{"type":"text","text":"這裏"}]},{"type":"text","text":"查看最新的版本。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"什麼是數字邏輯設計?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數字邏輯設計就是設計一個邏輯電路,對二進制數值進行運算。基本元件是邏輯門:例如,與門一樣,有兩個輸入和一個輸出。它的輸出爲 1 或 iff,兩個輸入均爲 1。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們所設計的同步電路,一般都是利用觸發器來儲存狀態,使電路運行與共時鐘同步。觸發器由邏輯門組成。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"模擬電路設計包括構成邏輯門的電子元件,例如晶體管和二極管。這種抽象通常是用於直接處理來自模擬傳感器的信號的應用,例如無線電接收器。在設計 CPU 時,這種抽象水平是行不通的:現代的 CPU 有幾十億個晶體管!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"相反,我們使用的工具可以將數字邏輯設計轉化爲不同的有用格式:FPGA 的配置(見下文);模擬;晶片佈局。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"FPGA 是什麼,爲什麼要用 FPGA?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上文中我們指出,不管我們是創建自定義 ASIC 芯片還是配置 FPGA,都可以使用相同的數字邏輯設計工具。現場可編程門陣列(Field-Programmable Gate Array,FPGA)是一種集成集成電路,其中包含了可編程邏輯塊陣列。你可以把它想象成一個大型的邏輯門陣列,可以通過多種方式連接起來。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"定製一款芯片動輒需要幾百萬美元,當然,一旦芯片被生產出來,就無法對它進行更改。所以 FPGA 通常用於下列情況:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於缺乏資金,無法負擔製作定製 ASIC 的費用(例如,如果你只是像我這樣的黑客,而不是 ARM 或英特爾)。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無法負擔製作定製 ASIC 的費用,因爲產量太低,不值得一次性支付高昂的費用 (例如,如果你正在使用定製的數據採集硬件生產少量的 MRI 機器)。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要靈活性。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"缺點是什麼?那就是 FPGA 的單芯片成本要高得多,並且由於它能夠以非常靈活的方式將邏輯塊連接在一起,因此速度通常要慢得多。與此相反,定製的設計可以減少晶體管的數量,而無需考慮靈活性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在我看來,比較 ASIC 的定製設計過程和 FPGA 的設計過程是很有幫助的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"邏輯設計:就像做 FPGA 一樣,ASIC 的邏輯設計也是用硬件描述語言來完成的。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"驗證:FPGA 設計可能會被驗證,但是可以期待 ASIC!設計的過程更嚴格了。畢竟,設計一旦製造出來就不能更改!驗證通常包括設計部分的正式驗證。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"合成:這將創建一個網表,一個邏輯塊及其連接的列表。連接被稱爲網,而塊被稱爲單元。對於 FPGA 和 ASIC 來說,單元是特定於廠商的。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"佈局佈線(Placement and routing,P&R):對於 FPGA 來說,它涉及到將網表中描述的邏輯塊映射到 FPGA 中的實際塊。由此產生的二進制通常稱爲比特流。對於 ASIC 來說,這涉及到決定在晶片上何處放置單元,以及如何將它們連接起來。這兩種應用通常都要使用自動優化工具。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"我需要什麼工具?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"硬件描述語言:我使用的是 "},{"type":"link","attrs":{"href":"https:\/\/github.com\/nmigen\/nmigen","title":null,"type":null},"content":[{"type":"text","text":"nMigen"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可能聽說過 Verilog 或 VHDL:這兩種流行的硬件描述語言(hardware description language,HDL)。這裏我所說的“流行”,是指廣泛使用,而非廣受歡迎。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我不會假裝對這些工具很瞭解。我只知道那些比我更聰明的人,有着豐富的邏輯設計經驗,卻對這些工具恨之入骨。由於 Verilog 和其他類似工具存在的問題,人們嘗試着開發出更有用、更友好的替代方法。nMigen 就是在 Python 中創建一 門領域專用語言的項目。用它自己的話就是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然用 Verilog 和 VHDL 進行硬件設計比輸入原理圖的速度要快,但是由於一些原因,硬件設計還是很枯燥,而且效率也不高。對目前邏輯設計中佔有重要地位的同步電路而言,事件驅動模型引入了不必要的問題,並引入了人工編碼。逆直覺的算術規則導致了更陡峭的學習曲線,併爲設計上的微小缺陷提供了溫牀。最後,通過“generate”語句來支持邏輯過程生成(元編程)非常有限,並且限制了代碼的通用、重用和組織方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"針對這些問題,我們開發了 nMigen FHDL,該庫取代了事件驅動範例,它採用了組合語句和同步語句的概念,並採用了算術規則,使整型始終像數學整型一樣,最重要的是允許 Python 程序構建所設計的邏輯。這一點使硬件設計人員能夠充分利用 Python 語言的豐富內容:面向對象編程、函數參數、生成器、操作符重載、庫等,構建組織良好、可重用的優雅設計。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假如你和我一樣,從未使用過 Verilog,那麼這些對你來說不僅僅是抽象的含義。但是聽起來確實很有前景,而且我可以證明,在沒有 Verilog 障礙的情況下,從邏輯設計開始就非常簡單。如果你對 Python 非常熟悉,我將推薦它!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我能想到的唯一缺點是,nMigen 仍然處於開發階段,特別是文檔還不完整。但你可以通過 chat.freenode.net 的 #nmigen 頻道找到有用的社區。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"用於檢查模擬的波形顯示器:我使用的是 "},{"type":"link","attrs":{"href":"http:\/\/gtkwave.sourceforge.net\/","title":null,"type":null},"content":[{"type":"text","text":"GTKWave"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"nMigen 提供了模擬工具。我將它用於用 "},{"type":"codeinline","content":[{"type":"text","text":"pytest"}]},{"type":"text","text":" 編寫的測試。爲了幫助調試,我記錄了這些測試中的信號,並在波形顯示器中觀察它們。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/9b\/9bf35b44830ec86b8f35c1d9fe106be2.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"FPGA 開發板:我使用的是 myStorm BlackIce II"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你不必使用 FPGA 開發板來創建自己的 CPU。在模擬中,你可以做任何事情。對於我來說,工作中使用板子的樂趣就是能閃爍 LED,看着自己的設計運行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然,如果你要創建的東西比我的最基本的 CPU 更有用,那麼你可能需要一些硬件來運行它,而這並非“可選”選項!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"開始使用 nMigen"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 nMigen 系統中,我並沒有立刻嘗試設計一個 CPU,而是首先製作一個算術邏輯單元(Arithmetic Logic Unit ,ALU)。 在我見過的所有 CPU 設計中, ALU 是一個關鍵部件:它執行算術運算。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲什麼要從這裏開始呢?我知道我的 CPU 需要一個 ALU;我知道我能做一個簡單的 ALU;我知道當開始一個新的項目時,做事情的感覺是一種重要的動力!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我的設計看起來像這樣:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"\"\"\"Arithmetic Logic Unit\"\"\"import enum\n\nimport nmigen as nm\nclass ALUOp(enum.IntEnum):\n\n\"\"\"Operations for the ALU\"\"\"\n ADD = 0\n SUB = 1\n \n \nclass ALU(nm.Elaboratable):\n\"\"\"\n Arithmetic Logic Unit\n\n * op (in): the opcode\n * a (in): the first operand\n * b (in): the second operand\n\n * o (out): the output\n \"\"\"\n\ndef __init__(self, width):\n\"\"\"\n Initialiser\n\n Args:\n width (int): data width\n \"\"\"\n self.op = nm.Signal()\n self.a = nm.Signal(width)\n self.b = nm.Signal(width)\n self.o = nm.Signal(width)\n\ndef elaborate(self, _):\n m = nm.Module()\n\nwith m.Switch(self.op):\nwith m.Case(ALUOp.ADD):\n m.d.comb += self.o.eq(self.a + self.b)\nwith m.Case(ALUOp.SUB):\n m.d.comb += self.o.eq(self.a - self.b)\nreturn m"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如你所看到的,我們已經創建了大量的 nMigen "},{"type":"codeinline","content":[{"type":"text","text":"Signal"}]},{"type":"text","text":" 實例,以很好地表示定義 ALU 接口的信號!但這個複雜的方法是什麼呢?這個 "},{"type":"codeinline","content":[{"type":"text","text":"elaborate"}]},{"type":"text","text":" 方法又是什麼呢?我的理解是,“elaboration”是合成網表的第一步的名稱(見上文)。在上面的 nMigen 代碼中,我們的想法是,已經創建了一些可闡述的結構(通過繼承 "},{"type":"codeinline","content":[{"type":"text","text":"nm.Elaboratable"}]},{"type":"text","text":"),也就是用來描述想要合成的數字邏輯的東西。這個 "},{"type":"codeinline","content":[{"type":"text","text":"elaborate"}]},{"type":"text","text":" 方法描述了數字邏輯。它必須返回一個 "},{"type":"codeinline","content":[{"type":"text","text":"nMigen"}]},{"type":"text","text":" 模塊。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面讓我們進一步瞭解一下 "},{"type":"codeinline","content":[{"type":"text","text":"elaborate"}]},{"type":"text","text":" 的方法的內容。"},{"type":"codeinline","content":[{"type":"text","text":"Switch"}]},{"type":"text","text":" 將創造某種形式的合成設計決策邏輯。但什麼是 "},{"type":"codeinline","content":[{"type":"text","text":"m.d.comb"}]},{"type":"text","text":" 呢? nMigen 提出了同步("},{"type":"codeinline","content":[{"type":"text","text":"m.d.sync"}]},{"type":"text","text":")和組合("},{"type":"codeinline","content":[{"type":"text","text":"m.d.comb"}]},{"type":"text","text":")控制域的概念。來自 nMigen "},{"type":"link","attrs":{"href":"https:\/\/nmigen.info\/nmigen\/latest\/lang.html#lang-domains","title":null,"type":null},"content":[{"type":"text","text":"文檔"}]},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"控制域是指在相同條件下改變其值的一組命名信號。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有的設計都有一個預定義的組合域,其中包含所有的信號,當用來計算這些信號的任何值發生變化時,這些信號也隨之發生變化。名稱 comb 是爲組合域保留的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一種設計還可以有任意數量的用戶定義的同步域,也稱爲時鐘域,其中包含的信號在域的時鐘信號出現特定邊緣時會發生變化,或者,對於具有異步復位功能的域,域的復位信號會發生變化。大多數模塊只使用一個同步域。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在組合域和同步域中,信號的賦值的行爲各不相同。總的來說,同步域中的信號包含了設計的狀態,而組合域中的信號並不能形成反饋迴路或維持狀態。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面以移位寄存器爲例,說明要設計的邏輯。假定移位寄存器有 8 位,每個時鐘週期,該位值都會有一個移位(最左邊的值來自輸入信號)。這必然是同步的:不能通過簡單地將位連接在一起來創建這個功能,而在 nMigen 中,將位分配到組合域中將代表此功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我將在這個系列博客的下一部分詳細討論我的 CPU 設計。現在的情況是,我試圖在每個週期中只停用一個指令,而不使用流水線——這很不尋常,但是我希望這樣做可以簡化 CPU 的各個方面。其結果是,大多數邏輯是組合的,而非同步的,因爲我幾乎沒有在時鐘週期之間維持這種狀態。現在,我的寄存器文件設計有問題,爲了解決這個問題,我可能需要重新考慮我的“無流水線”想法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"編寫測試"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於 Python 測試,我喜歡使用 "},{"type":"codeinline","content":[{"type":"text","text":"pytest"}]},{"type":"text","text":",當然你也可以使用任何能吸引你的框架。以下是我在上面測試的 ALU 代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"\"\"\"ALU tests\"\"\"\nimport nmigen.sim\nimport pytest\n\nfrom riscy_boi import alu\n\[email protected]( \n\"op, a, b, o\", [ \n(alu.ALUOp.ADD, 1, 1, 2), \n(alu.ALUOp.ADD, 1, 2, 3), \n(alu.ALUOp.ADD, 2, 1, 3),\n(alu.ALUOp.ADD, 258, 203, 461), \n(alu.ALUOp.ADD, 5, 0, 5), \n(alu.ALUOp.ADD, 0, 5, 5), \n(alu.ALUOp.ADD, 2**32 - 1, 1, 0), \n(alu.ALUOp.SUB, 1, 1, 0), \n(alu.ALUOp.SUB, 4942, 0, 4942), \n(alu.ALUOp.SUB, 1, 2, 2**32 - 1)])\ndef test_alu(comb_sim, op, a, b, o): \nalu_inst = alu.ALU(32)\n\ndef testbench():\nyield alu_inst.op.eq(op)\nyield alu_inst.a.eq(a)\nyield alu_inst.b.eq(b)\nyield nmigen.sim.Settle()\nassert (yield alu_inst.o) == o\n\n comb_sim(alu_inst, testbench)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以及我的 "},{"type":"codeinline","content":[{"type":"text","text":"conftest.py"}]},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"\"\"\"Test configuration\"\"\"\nimport os\nimport shutil\n\nimport nmigen.sim\nimport pytest\n\nVCD_TOP_DIR = os.path.join(\n os.path.dirname(os.path.realpath(__file__)),\n\"tests\",\n\"vcd\")\n\ndef vcd_path(node):\n directory = os.path.join(VCD_TOP_DIR, node.fspath.basename.split(\".\")[0])\n os.makedirs(directory, exist_ok=True)\nreturn os.path.join(directory, node.name + \".vcd\")\n\[email protected](scope=\"session\", autouse=True)\ndef clear_vcd_directory():\n shutil.rmtree(VCD_TOP_DIR, ignore_errors=True)\n\[email protected]\ndef comb_sim(request):\n\ndef run(fragment, process):\n sim = nmigen.sim.Simulator(fragment)\n sim.add_process(process)\nwith sim.write_vcd(vcd_path(request.node)):\n sim.run_until(100e-6)\n\nreturn run\n\[email protected]\ndef sync_sim(request):\n\ndef run(fragment, process):\n sim = nmigen.sim.Simulator(fragment)\n sim.add_sync_process(process)\n sim.add_clock(1 \/ 10e6)\nwith sim.write_vcd(vcd_path(request.node)):\n sim.run()\n\nreturn run"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每次測試都會生成一個 "},{"type":"codeinline","content":[{"type":"text","text":"vcd"}]},{"type":"text","text":" 文件,我可以通過 GTKWave 等波形顯示器來查看,以便調試。你會注意到,組合模擬固定運行的時間段是任意小的,而同步模擬功能運行的時間段是確定的時鐘週期數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個信號產生於一個測試函數,它將從模擬器請求它的當前值。對於組合邏輯,我們生成 n"},{"type":"codeinline","content":[{"type":"text","text":"nmigen.sim.Settle()"}]},{"type":"text","text":" ,要求完成模擬。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於同步邏輯,還可以開始新的時鐘週期,而不需要參數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"設計一個 CPU"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在熟悉了 nMigen 之後,我開始嘗試繪製一個框圖來顯示我的 CPU。在本系列博客的下一部分中,我將對這個問題進行更詳細的討論,但我將簡單地說,我先繪製出一個指令所需要的邏輯,然後繪製出另一個指令的邏輯,然後找到如何將它們結合起來的方法。這裏有第一個混亂的草圖:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/0c\/0c4a2c23132e9fc4b72f0eaa45da33b5.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在弄清楚不同元件的接口要求是什麼時,這個框圖步驟非常有價值,但是在開始使用 nMigen 和在這個過程中學習數字邏輯設計之前,我不想這麼做。修改後的框圖如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/bc\/bcc615fc6210155159b2413ba9ad9dcf.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請關注本系列博客的下一部分,我將深入研究 RISC-V 和 CPU 設計。我想用第三部分來重新設計我的設計,使其適用於我要實現的全部指令集(RV32I)上工作😃"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者介紹:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"lochsh,住在英國牛津的軟件工程師,供職於 Perspectum Diagnostics,爲醫學圖像診斷工具編寫 C++。曾在 CMR Surgical 供職,在哪裏爲下一代手術機器人編寫裸機嵌入式 C。對 Rust 很感興趣,已經寫過很多 Python,願意嘗試更多的函數式編程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"原文鏈接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https:\/\/mcla.ug\/blog\/risc-v-cpu-part-1.html"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章