上一篇博客我們簡單介紹了Y86指令集體系,而這篇博客我們將介紹指令集體系的邏輯設計和硬件控制語言HCL,為后面去實現Y86打下基礎。
在硬件設計中,用電子電路來計算對位進行運算的函數,以及在各種存儲器元素中存儲位。大多數現代電路技術都是用信號上的高電壓或者低電壓來表示不同的位值。在當前的技術中心,邏輯 1 是用 1.0 伏特的高電壓表示,而邏輯 0 是用 0.0 伏特的低電壓表示。要實現一個數字系統需要三個組成部分:
①、計算對位進行邏輯操作的函數的組合邏輯
②、存儲位的存儲器元素
③、控制存儲器元素更新的時鍾信號
本篇博客我們就介紹這些不同的組成部分,以及用來描述不同處理器設計的控制邏輯——HCL(Hardware Control Language,硬件控制語言)
1、基本邏輯設計——邏輯門
邏輯門是數字電路的基本計算元素,它們產生的輸出,等於它們輸入位值的某個布爾函數。如下圖:
這三個邏輯門和我們前面講C語言運算符是對應的。但是要注意區別:
①、邏輯門只對單個位進行操作,而不是整個字。這很好理解,因為硬件識別的是高低電平。
②、邏輯門總是活動的,一旦一個門的輸入變化了,在很短的時間內,輸出就會相應的變化。
2、高級邏輯設計——組合電路
上面說的都是單個邏輯門,只能執行很簡單的計算,如果遇到稍微復雜點的,就不能計算了,這時候組合電路就出現了。
將很多邏輯門組合成一個網所構建的計算塊稱為組合電路。構建這個網有兩個限制:
①、兩個或多個邏輯門的輸出不能連在一起。否則它們可能會導致線上的信號矛盾,可能會導致一個不合法的電壓或電路故障。
②、這個網必須是無環的,也就是在網中不能有路徑經過一系列的門而形成一個回路,這樣的回路會導致該網絡計算的函數有歧義。
我們給出幾個非常有用的簡單組合電路例子:
1、== 表達式(單個位)
上圖我們用HCL來寫就是:eq = (a && b) || (!a && !b)。這個表達式的結果分析,只有a 和 b 都為 0 或者都為 1 的時候,輸出才為1。而這個對應於我們高級編程語言中的 a == b 表達式。
2、HCL表達式:(a && s) || (b && !s)
對於上面電路的結果,我們分析如果s為1,則結果為a,否則結果為b。
3、HCL表達式 和 C 語言邏輯表達式的區別
區別有以下三點:
①、邏輯門是持續輸出的,如果電路的輸入變化了,在一定的延遲之后,輸出也會相應的變化,而C表達式是在執行到的時候才會求值。
②、兩者操作的值不同,邏輯門只對值 0 和值 1 進行操作,而C 邏輯表達式允許參數是任意整數,0表示false,其它任意值都表示true。
③、C 邏輯表達式存在短路,比如對於a && b這個符號來說,C語言中的規定是如果前者為假,則后者不會再計算。而HCL當中沒有這種說法。
4、字級組合電路和 HCL 整數表達式
前面我們說的高級邏輯設計,也依然是針對1位進行操作的。那么如何才能真正操作多個位呢,比如平時常用的32位或者64位。那么我們就必須將多個位一起操作,比如下面的:
這個圖當中每個位相等的判斷都是上面所提到的兩個not門,兩個and門和一個or門組成的組合電路(第一個講的組合電路(a && b) || (!a && !b))。總共32個,它們並列的一起進行,最終再通過一個and門,就完成了判斷兩個32位的數字是否相等的操作。也就是表達式A == B,值得注意的是,這里的A和B都是32位的。C語言中可以假設為 int 型的。
還有一種字級多路復用器用HCL來描述就是:
它代表的意思就是如果s為1,則輸出A,否則輸出B,同樣,這里的A和B都是32位的。那么使用電路如何表達呢?其實就是第二個組合電路的32位版本。如下圖:
可以看到,s的not值是被復用的,否則的話,這里需要32個not門。值得一提的是,HCL中條件選擇表達式中的條件是不需要互斥的,只是按照優先順序依次選取,這與C語言中的switch是不同的。
最后一種HCL表達式,選擇表達式可以是任意的布爾表達式,可以有任意多的情況。
用邏輯門表示如下:
上述的表達式可以簡化,比如第二個表達式為!s1,而不用寫的完整!s1&&s0,因為另一種情況s1=0已經出現在第一個表達式中了。
5、存儲器和時鍾
上面我們介紹的都是組合電路,它們的作用是根據輸入來產生一個值。但是剛才也說過,組合電路是一直持續輸出的,因此它無法保持一個狀態不變。也就是說組合電路從本質上來講,不存儲任何信息。他們只是簡單的響應輸入信號,產生等於輸入的某個函數的輸出。但我們的計算機是需要存儲數據的,因此就需要能保存狀態的存儲設備。存儲設備是由一個時鍾控制,時鍾是一個周期型號,它控制着存儲設備什么時候更新設備里的值。
常用的存儲設備一般有兩種:
①、時鍾寄存器:簡稱寄存器,存儲單個位或字。時鍾信號控制寄存器加載輸入值。
②、隨機訪問存儲器:簡稱存儲器,存儲多個字,用地址來選擇該讀或者該寫哪個字。
時鍾寄存器的典型應用是程序計數器PC、條件碼寄存器以及程序狀態。它們都有明確的輸入,這意味着它們的值其實是某幾個值的一個函數,比如條件碼寄存器的輸入主要就是邏輯計算單元的值,因此條件碼寄存器的值就可以看做是邏輯計算單元的函數。
下圖表示寄存器的工作狀態。大多數時候,寄存器都保持在穩定狀態(用x表示),產生的輸出等於它的當前狀態,信號沿着寄存器前面的組合邏輯傳播,這時產生了一個新的寄存器輸入(用y表示),但是只要時鍾信號是低電位的,寄存器的輸出就保持不變。當時鍾變為高電位時,輸入信號就加載到寄存器中,稱為下一個狀態y,直到下一個時鍾上升沿,這個狀態就一直是寄存器的新輸出。
隨即訪問存儲器最典型的例子就是我們的寄存器文件(也就是8個程序寄存器)和隨即訪問存儲器(也就是我們常說的內存)。它們沒有明確的輸入值,因此不存在函數關系。不論是寄存器文件還是隨即訪問存儲器,都有讀和寫兩種操作,而對於時鍾寄存器來說,是無所謂讀和寫的,因為它只會根據輸入的變化改變輸出的值,是可以直接連接到電路上去的。
如下圖:寄存器文件一般有兩個讀端口(A和B)和一個寫端口(W)。每個端口都附帶一個地址來標識操作的是哪個寄存器,而對於寫端口,還有一個輸入數據,對於讀端口,則還有一個輸出數據。具體的圖示如下。
上面的寄存器文件,電路可以讀取兩個程序寄存器的值,同時更新第三個寄存器的狀態。每個端口都有一個地址輸入,表明該選擇是哪個程序寄存器,另外還有一個數據輸出或對應該程序寄存器的輸入值。可以看到在寄存器文件的寫端口處,有一個時鍾(clock)控制着寫的操作。當時鍾變化時,輸入數據的值將會更新到對應的寄存器當中。而對於讀數據,則類似於組合電路,根據輸入的地址值(src),寄存器文件會輸出相應的數據。
對於隨即訪問存儲器來說,與寄存器文件非常相似。不同的是,隨即訪問存儲器有一個地址輸入,一個寫的數據輸入以及一個讀的數據輸出。
讀操作:如果我們在輸入address上提供一個地址,並將write控制信號設置為0,那么經過一段延遲之后,存儲在那個地址上的值就會出現在輸出data上。如果地址超出了范圍,error信號會設置為1,否則就設置為0。
寫存儲器是由時鍾控制的:我們將address設置為期望的地址,將data in(數據輸入) 設置為期望的值,而 write設置為1,然后當我們控制時鍾時,只要地址是合法的,就會更新存儲器中指定的位置。對於讀操作來說,如果地址是不合法的,error信號會被設置為1.這個信號由組合邏輯產生,因為所需的邊界檢查純粹就是地址輸入的函數,不涉及任何保存狀態。