使用logisim搭建單週期CPU與新增指令
阿新 • • 發佈:2020-11-27
# 使用logisim搭建單週期CPU與新增指令
## 搭建
### 總設計
借用高老闆的圖,我們只需要分別做出PC、NPC、IM、RF、EXT、ALU、DM、Controller模組即可,再按圖連線,最後進行控制訊號的處理,一個CPU就差不多搭完了。目前支援的指令集為{addu、subu、ori、lw、sw、beq、jal、jr、nop、lui、sb、lb、sh、lh}
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114114561.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTUxOTMw,size_16,color_FFFFFF,t_70#pic_center)
下面分模組逐個分析
### PC
本質上就是一個32位的暫存器,這裡採用的是非同步復位,所以直接把reset訊號連在clear口。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2020112711421427.png#pic_center)
### NPC
由於我的CPU支援beq、jal、jr,所以NPCOp有2位,如下表所示
| NPCOp | 功能 |
| :---: | :------------------: |
| 00 | 計算順序地址(PC+4) |
| 01 | 計算beq地址 |
| 10 | 計算jal地址 |
| 11 | 計算jr地址 |
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114252628.png#pic_center)
其中PC4是用來把PC+4輸出至RF以完成jal指令
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114313844.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTUxOTMw,size_16,color_FFFFFF,t_70#pic_center)
### IM
這個就更簡單了,直接一個ROM搞定,注意把PC的2~6位引出作為IM的地址。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114405272.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTUxOTMw,size_16,color_FFFFFF,t_70#pic_center)
### RF
這個比較耗時間,聽說用vscode開啟.circ檔案就可以寫程式碼去搭建這玩意,開啟後發現3k行程式碼,直接反手關掉vscode繼續手動連線。連完應該差不多這個樣子。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114423896.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTUxOTMw,size_16,color_FFFFFF,t_70#pic_center)
外部看起來是這樣子的,RD1輸出A1對應暫存器的值,RD2輸出A2對應暫存器的值,當寫使能訊號WE3有效時,將在時鐘上升沿把WD3寫入A3對應的暫存器。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114438256.png#pic_center)
### EXT
將imm16擴充套件後輸出。由於我的lui是使用的EXT載入到高位(好像說其實應該用ALU實現?),因此我的EXT有3個功能分別是無符號擴充套件、符號擴充套件、左移16位。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114451728.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTUxOTMw,size_16,color_FFFFFF,t_70#pic_center)
### ALU
根據ALUOp進行不同的運算即可,這裡加法和減法用的一個加法器(A-B=A+~B+1),但是好像這樣不太好擴充套件(?)
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114510507.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTUxOTMw,size_16,color_FFFFFF,t_70#pic_center)
### DM
本來是一個非常簡單的RAM,但是由於做了lb、sb、lh、sh,就得對DM前後加上組合邏輯以保證不改變其他位的資料。這裡用兩個控制訊號,SSel控制store時的位寬,LSel控制Load時的位寬。(其實用一個控制訊號就可以,當時做的時候傻了一下用了兩個)
使用一個譯碼器,選擇被替換的那一段,如sb時,A的0~1位為01,那麼就會將DM中的對應地址的32位資料中的8~15位替換成WD的低8位,再存入DM,這樣就保證了僅讀入一個位元組而對其他位不改變,即實現了sb。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114528367.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTUxOTMw,size_16,color_FFFFFF,t_70#pic_center)
### Controller
使用了最簡樸的方法,搭建時可以先用小手把opcode和funct點成要新增的指令,然後再連接出該指令。如果opcode是000000,那麼再與funct得出的訊號並起來,即得到該指令,如圖中的addu和subu。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/202011271145575.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTUxOTMw,size_16,color_FFFFFF,t_70#pic_center)
得到指令後,再根據列的真值表,把在真值表中是1的連上
如對於NPCOp[1:0],只有beq時為01,只有jal時為10,只有jr時為11,也就是NPCOp[0]僅在beq和jr時取1,NPCOp[1]僅在jal和jr時取1,所以把他們連線起來即可,如下圖。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114615212.png#pic_center)
對於每一個控制訊號都如此連線,即可完成CPU的搭建。
## 新增指令
### 注意事項
- 在改變與門或者或門的輸入資料個數時,建議從奇數個到奇數個,否則可能出現這樣的情況:如我有一個5input的或門,如下
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114630930.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTUxOTMw,size_16,color_FFFFFF,t_70#pic_center)
現在要新增第6個input,如果直接改成6input,那麼如圖所示
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114645404.png#pic_center)
發現中間的空掉了
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114658778.png#pic_center)
而如果遵循奇數個改奇數個的原則就不會出錯,如將number of input改成7就如下圖所示,中間的連上了。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114709644.png#pic_center)
### eg:新增addiu
#### 首先分析資料通路
判斷是否需要增加新的通路以實現該指令,可以看出其需要的功能我的CPU都有了,因此直接修改控制訊號即可
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114722838.png#pic_center)
#### 確定控制訊號
對於NPCOp,這不是一個跳轉指令,因此NPCOp取00
對於RFWr,要回寫到R[rt],因此RFWr為1
對於EXTOp,要進行符號擴充套件,所以取01
對於ALUOp,加法,所以取00
對於DMWr,不用寫入DM,所以取0
對於WRSel,由於寫入的是R[rt],所以取01
對於WDSel,由於寫入的資料來自ALU的計算結果,所以取00
對於BSel,由於參與ALU計算的第二個數來自EXT,所以取1
對於SSel和LSel,由於不涉及半字或位元組,都取00
#### 新增指令訊號
首先將opcode點成該指令
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114735674.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTUxOTMw,size_16,color_FFFFFF,t_70#pic_center)
然後再連接出addiu
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114747992.png#pic_center)
這時候由於opcode即addiu指令,應該只有addiu是亮的。
#### 修改控制訊號
得到addiu訊號後,僅需要在addiu控制訊號為1的對應位置連線即可,如addiu只有RFWr、EXTOp[0]、WRSel[0]、BSel為1,所以只需要將他們的或門新增一根線與addiu訊號連線。連線完成後,應檢查一遍此時的控制訊號是否與之前分析的一樣。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114924744.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ1NTUxOTMw,size_16,color_FFFFFF,t_70#pic_center)
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201127114930634.png#pic_center)
可以看到與之前分析的完全一樣,至此addiu的新增