深入淺出計算機組成原理學習筆記:第十九講


一、引子

上一講,我們講解了時鍾信號是怎么實現的,以及怎么利用這個時鍾信號,來控制數據的讀寫,可以使得我們能把需要的數據“存儲”下來。那么,這一講,我們要讓計算機“自動”跑起來。

通過一個時鍾信號,我們可以實現計數器,這個會成為我們的PC寄存器。然后,我們還需要一個能夠幫我們在內存里面尋找指定數據地址的譯碼器,以及解析讀取到的機器指令的譯碼器。
這樣,我們就能把所有學習到的硬件組件串聯起來,變成一個CPU,實現我們在計算機指令的執行部分的運行步驟。

二、PC寄存器所需要的計數器

我們常說的PC寄存器,還有個名字叫程序計數器。下面我們就來看看,它為什么叫作程序計數器。

有了時鍾信號,我們可以提供定時的輸入;有了D型觸發器,我們可以在時鍾信號控制的時間點寫入數據。我們把這兩個功能組合起來,就可以實現一個自動的計數器了。

加法器的兩個輸入,一個始終設置成1,另外一個來自於一個D型觸發器A。我們把加法器的輸出結果,寫到這個D型觸發器A里面。於是,D型觸發器里面的數據就會

在固定的時鍾信號為1的時候更新一次。

 

這樣,我們就有了一個每過一個時鍾周期,就能固定自增1的自動計數器了。這個自動計數器,可以拿來當我們的PC寄存器。事實上,PC寄存器的這個PC,
英文就是Program Counter,也就是 程序計數器的意思。

每次自增之后,我們可以去對應的D型觸發器里面取值,這也是我們下一條需要運行指令的地址。前面第5講我們講過,
同一個程序的指令應該要順序地存放在內存里面。這里就和前面對應上了,順序地存放指令,就是為了讓我們通過程序計數器就能定時地不斷執行新指令。

加法計數、內存取值,乃至后面的命令執行,最終其實都是由我們一開始講的時鍾信號,來控制執行時間點和先后順序的,這也是我們需要時序電路最核心的原因。

在最簡單的情況下,我們需要讓每一條指令,從程序計數,到獲取指令、執行指令,都在一個時鍾周期內完成。如果PC寄存器自增地太快,程序就會出錯。
因為前一次的運算結果還沒有寫回到對應的寄存器里面的時候,后面一條指令已經開始讀取里面的數據來做下一次計算了。這個時候,如果我們的指令使用同樣的寄
存器,前一條指令的計算就會沒有效果,計算結果就錯了。

在這種設計下,我們需要在一個時鍾周期里,確保執行完一條最復雜的CPU指令,也就是耗時最長的一條CPU指令。這樣的CPU設計,我們稱之為 單指令周期處理器(Single Cycle Processor)。

很顯然,這樣的設計有點兒浪費。因為即便只調用一條非常簡單的指令,我們也需要等待整個時鍾周期的時間走完,才能執行下一條指令。在后面章節里我們會講到,通過流水線技術進行性能優化,可以減少需要等待的時間,這里我們暫且說到這里。

三、讀寫數據所需要的譯碼器

現在,我們的數據能夠存儲在D型觸發器里了。如果我們把很多個D型觸發器放在一起,就可以形成一塊很大的存儲空間,甚至可以當成一塊內存來用。像我現在手頭這台電腦,
有16G內存。那我們怎么才能知道,寫入和讀取的數據,是在這么大的內存的哪幾個比特呢?

於是,我們就需要有一個電路,來完成“尋址”的工作。這個“尋址”電路,就是我們接下來要講的譯碼器。

在現在實際使用的計算機里面,內存所使用的DRAM,並不是通過上面的D型觸發器來實現的,而是使用了一種CMOS芯片來實現的。不過,這並不影響我們從基礎原理方面來理解譯碼器。
在這里,我們還是可以把內存芯片,當成是很多個連在一起的D型觸發器來實現的。

如果把“尋址”這件事情退化到最簡單的情況,就是在兩個地址中,去選擇一個地址。這樣的電路,我們叫作 2-1選擇器。我把它的電路實現畫在了這里。

我們通過一個反相器、兩個與門和一個或門,就可以實現一個2-1選擇器。通過控制反相器的輸入是0還是1,能夠決定對應的輸出信號,是和地址A,還是地址B的輸入信號一致。

一個反向器只能有0和1這樣兩個狀態,所以我們只能從兩個地址中選擇一個。如果輸入的信號有三個不同的開關,我們就能從$2^3$,也就是8個地址中選擇一個了。這樣的電路,我們就叫 3-8譯碼。現代的計算機,如果CPU是64位的,就意味着我們的尋址空間也是$2^{64}$,那么我們就需要一個有64個開關的譯碼器。

 

所以說,其實譯碼器的本質,就是從輸入的多個位的信號中,根據一定的開關和電路組合,選擇出自己想要的信號。除了能夠進行“尋址”之外,我們還可以把對應的需要運行的指令碼,同樣通過譯碼器,找出我們

期望執行的指令,也就是在之前我們講到過的opcode,以及后面對應的操作數或者寄存器地址。只是,這樣的“譯碼器”,比起2-1選擇器和3-8譯碼器,要復雜的多。

四、建立數據通路,構造一個最簡單的CPU

1、怎么把這些零件組合起來

D觸發器、自動計數以及譯碼器,再加上一個我們之前說過的ALU,我們就湊齊了一個拼裝一個CPU必須要的零件了。下面,我們就來看一看,怎么把這些零件組合起來,才能實現指令執行和算術邏輯計算的CPU。

1. 首先,我們有一個自動計數器。這個自動計數器會隨着時鍾主頻不斷地自增,來作為我們的PC寄存器。
2. 在這個自動計數器的后面,我們連上一個譯碼器。譯碼器還要同時連着我們通過大量的D觸發器組成的內存。
3. 自動計數器會隨着時鍾主頻不斷自增,從譯碼器當中,找到對應的計數器所表示的內存地址,然后讀取出里面的CPU指令。
4. 讀取出來的CPU指令會通過我們的CPU時鍾的控制,寫入到一個由D觸發器組成的寄存器,也就是指令寄存器當中。
5. 在指令寄存器后面,我們可以再跟一個譯碼器。這個譯碼器不再是用來尋址的了,而是把我們拿到的指令,解析成opcode和對應的操作數。
6. 當我們拿到對應的opcode和操作數,對應的輸出線路就要連接ALU,開始進行各種算術和邏輯運算。對應的計算結果,則會再寫回到D觸發器組成的寄存器或者內存當中。

這樣的一個完整的通路,也就完成了我們的CPU的一條指令的執行過程。在這個過程中,你會發現這樣幾個有意思的問題。

2、第一個有意思的問題

是我們之前在第6講講過的程序跳轉所使用的條件碼寄存器。那時,講計算機的指令執行的時候,我們說高級語言中的if…else,其實是變成了一條cmp指令和一條jmp指令。
cmp指令是在進行對應的比較,比較的結果會更新到條件碼寄存器當中。jmp指令則是根據條件碼寄存器當中的標志位,來決定是否進行跳轉以及跳轉到什么地址。

不知道你當時看到這個知識點的時候,有沒有一些疑惑,為什么我們的if…else會變成這樣兩條指令,而不是設計成一個復雜的電路,變成一條指令?到這里,我們就可以解釋了。
這樣分成兩個指令實現,完全匹配好了我們在電路層面,“譯碼-執行-更新寄存器“這樣的步驟。

cmp指令的執行結果放到了條件碼寄存器里面,我們的條件跳轉指令也是在ALU層面執行的,而不是在控制器里面執行的。這樣的實現方式在電路層面非常直觀,我們不需要一個非常復雜的電路,
就能實現if…else的功能。

3、第二個有意思的問題

是關於我們在第17講里講到的指令周期、CPU周期和時鍾周期的差異。在上面的抽象的邏輯模型中,你很容易發現,我們執行一條指令,其實可以不放在一個時鍾周期里面,
可以直接拆分到多個時鍾周期。

我們可以在一個時鍾周期里面,去自增PC寄存器的值,也就是指令對應的內存地址。然后,我們要根據這個地址從D觸發器里面讀取指令,這個還是可以在剛才那個時鍾周期內。
但是對應的指令寫入到指令寄存器,我們可以放在一個新的時鍾周期里面。指令譯碼給到ALU之后的計算結果,要寫回到寄存器,又可以放到另一個新的時鍾周期。所以,執行一條計算機指令,

其實可以拆分到很多個時鍾周期,而不是必須使用單指令周期處理器的設計。

因為從內存里面讀取指令時間很長,所以如果使用單指令周期處理器,就意味着我們的指令都要去等待一些慢速的操作。這些不同指令執行速度的差異,也正是計算機指令有指令周期、
CPU周期和時鍾周期之分的原因。因此,現代我們優化CPU的性能時,用的CPU都不是單指令周期處理器,而是通過流水線、分支預測等技術,來實現在一個周期里同時執行多個指令。

五、總結延伸

好了,今天我們講完了,怎么通過連接不同功能的電路,實現出一個完整的CPU。

我們可以通過自動計數器的電路,來實現一個PC寄存器,不斷生成下一條要執行的計算機指令的內存地址。然后通過譯碼器,從內存里面讀出對應的指令,
寫入到D觸發器實現的指令寄存器中。再通過另外一個譯碼器,把它解析成我們需要執行的指令和操作數的地址。這些電路,組成了我們計算機五大組成部分里面的控制器。

我們把opcode和對應的操作數,發送給ALU進行計算,得到計算結果,再寫回到寄存器以及內存里面來,這個就是我們計算機五大組成部分里面的運算器。

我們的時鍾信號,則提供了協調這樣一條條指令的執行時間和先后順序的機制。同樣的,這也帶來了一個挑戰,那就是單指令周期處理器去執行一條指令的時間太長了。而這個挑戰,
也是我們接下來的幾講里要解答的問題。


免責聲明!

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



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