P3-單周期CPU(Logisim實現)


僅憑閱讀本文,您不可能系統地學會如何搭建單周期CPU。即使這樣,您的收獲也可能有以下幾點:了解用Logisim搭建CPU時的一種並不優秀的實現方法,以及這種方法是如何進一步優化的;了解課上測試的坑在哪里(比如復位,比如一些nb的現成部件),了解課上測試的形式,讓准備更有針對性。

upd:16進制碼導入電路時文件頭為v2.0 raw,還有就是不要用未定義的指令。

二戰已過

作為一個剛接觸培養工程能力課程的萌新,不能缺少重來一次的勇氣(第一版十個小時造完了,一堆bug加沒法很好地拓展),只有搭完了第一遍,才能真正體會到會遇到哪些問題,才會體會到高老板課件中的精髓,才能在第二版中更好地布線,命名以及合理使用tunnel。

坑點和技巧總結在后面,前面一部分搭建過程純屬個人深夜胡言亂語,請無視。

先看完那幾頁要求!

當時還沒學懂理論,先搭建了一下各個部件。GRF不用說了,肯定是用P0搭好的,CPU可以重新來過,但是GRF必須復用。注意GRF要有Reset,總Reset按下的時候PC、DM、GRF都要回到原始狀態(助教在討論區說了)。IFU(取指令單元)我拆成了PC與IM兩個子電路。注意PC有Reset,IM用的是ROM,這些只要仔細讀過要求應該不會出問題。32bitALU也沒什么特別的,只是考慮到為了方便以后擴展(比如加運算指令),我的ALUOp用了3位,也就是說ALU支持8種運算,目前只有無符號加減以及或運算。對於DM(我也不知道叫啥,反正是內存),注意其輸入地址為5位(32bit*32要求),注意有reset,注意MemWrite的作用是為1的時候寫入數據,是數據從外面進入內存,而不是從內存寫出去!至此,我們上課講的基礎元件已經搭好了。但是我推薦大家搭建一個萬能分線器,它的頂層長這樣:

 

 

 功能大家可以望文生義一下,有了他之后的頂層布線大家亦可以想象,會十分簡潔,需要的信號只需要一條線就能引出來。

下面從數據通路開始連接,只靠課本或者自己老師(我的理論課老師不是老板)的課件對於7條指令的CPU也不會麻煩,但指令多了之后工程化方法會很好地讓我們加指令與加元件。我是先從addu開始連的通路,具體連法課本或課件已經講得很清楚了,不再詳述。然后連lw和sw,隨后又補上lui指令。此處lui指令我沒有放入ALU,而是單獨在頂層里面加了一個16位邏輯左移移位器,然后把這個結果和ALU的結果做一個多路選擇,選擇信號叫做AorS_sel,當然,這個選擇出來的結果最后還是免不了與從內存中讀出來的數據做另一個多路選擇,信號叫做MemtoReg.在加這些I型指令的時候,務必仔細閱讀MIPS手冊,看看到底是0擴展還是符號擴展,需不需要左移兩位之類的。最后我加上了beq,為了使得線不亂,我在IFU那里的算PC+4+offset處用了tunnel進行簡化布線。

數據通路不難畫,控制器需要想想,並且好好查表,造表。我把控制器分成了兩部分,一個只看OpCode的部分,另一個是需要綜合第一部分給出的ALUOp信號以及Func信號去生成ALUcontrol信號(位數自己看着來)。兩部分的結構都是與或門陣列,第一部分是OpCode六位以及其取反的戰場,第二部分是ALUOp及其取反以及Func及其取反的戰場。為了連線,我們需要先查mips手冊,連接與門陣列,然后再造表連接或門陣列。關於造表的順序,下面簡單說一下。先搬過來教程中的

 

 

 

我開始是個鐵憨憨,橫着填的表,這是一種麻煩的填表順序,因為每填一行,需要把所有指令的數據通路都想一遍,想得腦殼疼。第二次搭建的時候我考慮豎着填表,每填一列只需要把該指令的通路走一遍,思考量從O(n^2)降低到O(n),還不容易錯。表中數據的確定是由自己的設計決定的,不能與教程中給出的數據苟同。另外,對於R型指令,所有的R型指令在與門陣列中合成一個與門就行,叫if R,因為R型指令最終干什么,還是取決於Func,所以在第一部分控制器中所有R型指令統統歸為一類即可。對於if R的結果,考慮到R型指令必然寫入Regfile,所以是要和RegWrite連接的。至於第一部分中的ALUOp,值的情況取決於ALU的設計以及自己的規定,一種規則是000為加法,001為減法,010為或,011為比較,100為取決於Func字段。第一部分控制器的大體思路已經講完了,下面考慮第二部分控制器的搭建思路。我第一次搭建第二部分的時候,由於考慮不周+沒看高老板課件,出了嚴重的漏洞,至於哪里錯了待會兒再討論。一種正確(應該正確吧,等課上就知道了)的方案是仍然用與或門陣列。與或門陣列沒看懂?對於R型指令,考慮一個例子:比如addu,它的Func是100001,需要在ALUOp為100時才會生效,所以,我們要把這對應的9條線(1取原信號,0取取反之后的信號)都連到addu的與門上才行。如圖:

 

 

這樣的話,如果ALUOp信號不是100的話,就說明不是R型指令,addu自然不能生效。相信有了這個例子,能更好地理解與或門中的與部分。由於學藝不精,我最開始不會搭這里,在室友提醒下才發現和第一部分控制器如出一轍(腦子笨不會融會貫通啊)有了這個例子,相信其他情況也容易類比實現。但是講真的,第二部分控制器我設計的不好,用到一個常量0來輔助,要是加指令的話,我需要去掉0,並且思考如何擴展使得信號不會亂。這里是個雷,不知道周四能不能炸死自己。兩部分都結束之后,控制器也就搭完了。控制信號我統統用tunnel連到各處的,這樣感覺更加可讀。

就這樣,CPU就搭完了,它出生時是長這樣的:

 

 太難看,但是如果按照我寫的思路搭的話,第二遍就是這樣:

 

 增加了信號的名字,並刻意加大骨干元件大小之后,看起來終於像一個CPU了

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

以下並非胡言亂語。

剛剛發現弱測結果終於出來了,WA了第三個點。看了討論區,發現自己的MemAddr取錯了(圖中32 to 5封裝起來的內部錯了)。為了保證4的倍數,類似於PC部分的取址,應該取2-6位而不是0-4位!!!討論區萬能!!!這其實也是我的一個知識盲點:現代計算機按字節編址,如果寫sw $s1,20($s2)的話,是存在第五個字里(32位機器)。而DM中一個地址是一個字,所以要除以4,即取2-6位!!

關於測試:http://shell-storm.org/online/Online-Assembler-and-Disassembler/     反匯編轉化網站,選擇mips,bigendian,然后disassemble就行了。

更新:對於beq指令,在寫測試程序的時候,判斷相等一定要是兩個寄存器里的數據判斷是否相等,不能判斷寄存器和立即數是否相等,這樣會導致測試程序在電路上跑不出正確結果,但MARS上無所謂,MARS比較聰明,幫我們善后了。

更新:關於加指令時的完備思路:(這個思路可能僅適用於我畫的CPU,因為我的控制器部分設計得非常糟糕,導致ALU控制器的功能並不純凈!但大家的CPU可能沒有我這個特點)先跑一遍數據通路,看看是不是需要加mux和其他的元件(一般得加),在需要增加mux的地方在表中做一個標記,表示需要增加新的控制信號,然后看控制器,如果是I,J型指令,就先把這條指令的名字加到主控制器的與邏輯中,如果是R型指令,就把這條指令的名字加入到ALU控制器的與邏輯中。然后看看需不需要加新的控制信號,再之后連接控制信號或邏輯(注意,以前的控制信號也可能因為多了一條指令而又多了一種生效的情況),最后,把控制信號加到對應的mux上,寫一個簡單的程序驗證一下,看看相應的寄存器以及內存中的值改變得對不對。

更新:如何將PC值直接置為0x00003000:考慮p0中所用的寄存器同步復位的方法。這里把圖搬過來加深一下印象:

 

 把0改成0x00003000就好了(課下測試部分關於PC的復位,用同步復位或者異步復位似乎都可以通過,也就是說,測試電路似乎保證了clear一直保持到時鍾上升沿到來)

結果就相當於押到了一半的題。

結果就死在了異步復位到0x3000,彰顯了菜雞本色

那么,究竟怎么異步復位到0x3000呢?

 

 

要實現異步復位,一個必要條件是必須把reset接到寄存器上,否則,做不到異步。 

考慮上面那個電路:在按下復位之后,寄存器被清零,0=0,所以MUX選擇了0x3000輸出,當下一個時鍾到來之后,寄存器的值不再為0,這時候便是輸出0x3004了,之后的過程就是在這個基礎上不斷加4,也就是實現了異步復位。這個方法不知道大家有沒有在斐波那契那個問題中使用過,我真的是下了場之后才想到自己之前用這個電路做過斐波那契數列那個題,於是心中各種不甘心。。。

安利思考題的網站:https://wenku.baidu.com/view/f5db168fcc7931b764ce1545.html 可以參考,但最好還是自己想+查資料

 更新:深夜總結:ALU選擇信號3位還是不太夠(可能是我的實現方式不好),所以考慮重新交一個4位選擇信號的ALU,整個電路需要改動的地方挺多的,改之前先留一個保命版本確保自己能參加課上測試。萬能分線器為了支持跳轉指令,需要進行一定的增加。這些坑必須在測試之前解決掉,不然到時候改到懷疑人生。

帶符號運算:按照指令集的意思,是溢出的話輸出溢出標志,並且不寫回數據。所以考慮加一個overflow,在ALU中,增加新輸出溢出判斷,溢出判斷只有在真溢出時才為1。這個東西取完反之后和寄存器寫使能做與運算作為“新”寫使能(溢出時不寫回去)

B開頭的各種轉移指令:類似於beq,需要增加多路器,控制信號可以考慮取反后再用一下

 更新:當時在考場上發現最后提交的CPU中有一處明顯錯誤,是調整與門的接口數量時導致的,有一根線沒連上,不過還好我考試之前一直在想這個事情,上了考場先把那個錯改過來了。這里提醒大家,當邏輯門的輸入數量為偶數時,門最中間是沒有輸入口的,為奇數時最中間才是輸入口。所以在盲改輸入個數之后一定拖動着看看!

 11月13號更新:剛剛回顧了P3,發現這里的總結仍然有遺漏的地方,下面進行一些補充。

1.關於評測機的顯然和實際不符的報錯:討論區中有人問過為什么評測機爆出來的自己出錯部分的代碼的行為反匯編之后正確的,但評測機卻要求一個八竿子打不着的行為,和這條指令完全對不上。事實上,這是因為評測機報錯報的是我們的電路在這條指令處出錯,這條指令,是我們的電路目前執行的指令,報錯有兩種原因吧,第一種,是這條指令執行的時候我們有值賦值錯誤(這個大家應該容易發現),第二種,就是我們當前根本不應該執行這條指令,但卻執行了它,即可能是跳轉發生了錯誤,這時候我們就要仔細的檢查一下自己的跳轉指令有沒有出問題。

2.關於添加指令的一些問題:首先,課下別自己添加太多指令,因為課上需要加一些奇怪的組合指令,這些在指令集里找不到,如果我們課下加了太多指令,課上的修改空間就會變得十分緊張;其次,加指令的時候別拘泥於理論課上的一些內容,比如理論課上加跳轉指令j時,是多加了一個多路選擇器,即用兩個多路選擇器選擇三個信號,這樣短期收益的確不錯,並且我甚至傾向於推薦在課上連接電路的時候用這個方法來節省時間,但是到了P4,我再用這種多加多路選擇器時發現加指令變得比較困難了:需要定義更多的導線,而導線的命名很大程度上影響了我們出bug的幾率(我P4課下導線定義了60多行,費盡千辛萬苦改導線,cpu勉強能用,但是我還是選擇等P3重測完了之后重新再寫一遍)其實我們完全可以把多路選擇器選擇信號位數增加,這樣更符合實際的cpu的構造。

課上測試(11月14日重測版)

第一題,仍然是加一條跳轉指令,為了避免和7號測試的jal重復,這次讓加bal指令,看指令集容易理解,數據通路在jal的基礎上幾乎不用修改。推薦課下除了要求的指令之外,把jal,jr,jalr加進去,這樣除非遇到一些奇怪的組合指令,否則就基本上只是加多路選擇器和選擇信號的工作量了。因為第一次復位踩坑掛掉,這次吸取了經驗很快做掉了。

第二題,取2的對數指令,結果向下取整。指令集中仍然是以循環的方式給出的,但是也同樣和第一次求前導1個數一樣,可以利用BitFinder做出來:最高位1的所在位數(從零開始)就是取2的對數並且向下取整的結果。說實話重測有第一次的經驗真的可以快很多很多,不會傻乎乎去搭建狀態機了(加狀態機目測也不對,可能就不是單周期能跑完的指令了),也沒有考試快結束時才知道要用BitFinder的令人窒息的操作了。

第三題,添加lbu指令,這個也是指令集里面的,對着指令集的說明去好好想想,不難做出來。最后快倆小時一直提交不上去,不知道最后有沒有對,我的疑問在大小端存儲上,場上不清楚取一個字節是從左往右還是從右往左取,不過這並不是問題,這種提交不過,那另一種肯定能過了(只要沒別的毛病)。

以后,我可以吸取進度最靠前的室友的經驗了(但願他能挺到最后)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM