如何自制CPU?(2)

如何自制CPU?(2)

觀眾老爺好!今天繼續講如何自制CPU?前一期把簡單的CPU模型用程序語言構建而成,但很多功能沒有實現,觀眾老爺是不是等不及了,當然觀眾老爺還是拿出飲料茶水瓜子隨小編一起走進……哦!不僅僅是編程的世界,而是虛擬與現實完美結合的數字世界!觀眾老爺是不是在嘀咕,你個誇大其詞的小編,有何勇氣大放厥詞!當然小編也沒那麼大能力,任何成熟的商業軟件、商業硬件都不是一人之功,需要很多的工程師,物理學家,數學家等人員合作才能完成!不過呢小編希望看過小編文章的人都有進入此行列的機會!

觀眾老爺還記得上一期的內容吧?CPU的整個運作流程是取指令,譯碼,執行,指令計數,在開始之前我們把昨天的模型複製過來(小編是不是有湊字數嫌疑!),可能今天的內容會跟上篇會略有差異。(我們在設計細節時需要一些本來不存在的數據結構,在編程時會用到就建立,這不是一個實際變量可能只是個虛構的中間數據。當然這是小編的個人見解,如有反駁和不同意見的不要罵小編,這種情況很少遇到而已,但絕對是存在的。)

在開始之前,我們定義一些特殊的數據:

//這裡設計的是16位數據總線和16位地址總線的CPU

//CPU運行時的判斷指令

#define flase 00b //定義flase為0

#define true 01b //定義true為1

#define enable 01b //定義高電平為1

#define disable 00b //定義低電平為0

//定義寄存器

#define NULL 0000b //無寄存器操作

#define dateremOne 0001b //數據寄存器1

#define dateremtwo 0010b //數據寄存器2

#define adressrem 0011b //地址寄存器

#define ESP 0100b //指針寄存器,當然為了簡便這裡不詳細去實現了

//譯碼後的判斷指令定義

#define nop 0000b //定義NOP指令

#define mov 0001b //定義mov指令

#define add 0010b //定義加法指令

#define sub 0011b //定義減法指令

#define mul 0100b //定義乘法指令

#define div 0101b //定義除法指令

#define jmp 0110b //定義跳轉指令

#define push 0111b //定義壓棧指令

#define pop 1000b //定義彈棧指令

////還可以多加幾個但是這裡就不加了,這幾個最基本的彙編指令夠用了,後面如果想再實現其它功能時再加,這裡進行定義是為了我們更好的用編程語言描述,不然一堆二進制碼我們是沒法讀的,也不方便進行流程說明,後面具體涉及到數字電路時咱們就必須用二進制說明了,用1代表高電平,用0代表低電平(也就是開關狀態)。

//CPU從最小結構到最大機構數據建模

//指令碼也是16位,前四位為操作碼,中間四位為寄存器選擇符,最後八位為地址碼或者實數

Struct DeCode { //指令譯碼器

Input wire CodeLine[3:0];//輸入指令位數

Input wire Rem[3:0];//寄存器操作碼

Input wire Address[7:0];//地址碼或者實數,這個地址碼是偏移地址

Output wire OutRem[3:0];//寄存器操作數輸出

Output wire OutCodeLine[3:0];//輸出指令

Output wire OutAddress[7:0];//地址和實數輸出

}

Struct CodeRem{ //指令寄存器

Input wire indate[15:0];//寄存器輸入

Output wire outdate[15:0];//寄存器輸出

}

Struct CPUControl { //控制單元

Input wire ControlLine[15:0];//讀取操作碼

Output wire outCodeLine[15:0];//指令輸出

Inout wire Address[15:0];//地址輸入輸出端口

BIT PCAddress[16]; //指令計數器中保存著指令地址值

CodeRem CodeRemMod;

DeCode DeCodeMod;

//CPUControl (){

//指令取碼、譯碼、控制的實現,放到後面去實現

//}

}

Struct CPUDateRem{ //數據寄存器

Inout wire Indateone[15:0];

Inout wire Indatetwo[15:0];

Bit [] dateOne[16];//這裡可以用16位的寄存器,觀眾老爺可以擴展

Bit [] dateTwo[16];//這裡可以用16位的寄存器,觀眾老爺可以擴展

Output wire outdateone[15:0];//輸出也可以只有一個

Output wire outdatetwo[15:0];

}

Struct CPUMath {//運算寄存器

Input wire inDateone[15:0];

Input wire inDatetwo[15:0];

Output wire outDate[15:0];

//CPUMath () {

//算術運算和邏輯處理

//}

}

Struct CPU{

//CPU有很多引腳我們這裡只用到幾個就行

Input wire rst;//復位引腳

Input wire Vcc;//電源引腳

Input wire Enable;//使能引腳,只有此引腳輸入高電平時,CPU才能正常運行

Output wire Gnd;//接地引腳,接地很重要,防止芯片內部電壓過高擊穿電子器件當然這個引腳不進行數據傳輸只是保證芯片正常工作用的

Input wire clk;//時鐘引腳

Inout wire Adress[15:0];//地址選取端口

Input wire Code[15:0];//指令讀取端口

Input wire Date[15:0];//數據輸入端口,也可以把數據輸入輸出端口合併

Output wire Outdate[15:0];//數據輸出端口

CPUControl CPUControlMod;//控制單元

CPUDateRem CPUDateRemMod;//數據寄存器單元

CPUMath CPUMathMod;//運算單元

Bit [] CodeLine[4]; //操作碼存放寄存器

Bit [] Rem[4];//寄存器選擇符寄存器

//首先進行導線連接,這些部分本來是在各個模塊當中進行,但這裡單獨拿出來進行連接這樣有助於我們理解CPU的實際線路走向和工作流程,到具體實現CPU數字電路時不會頭暈,其實這幾期比較枯燥,小編儘量把枯燥的東西講詳細點,後面小編會畫一下具體的流程圖

//首先通過讀取指令引線到控制單元,控制單元的指令碼要送入譯碼器用 "=>" 代表把引線連接起來當然不同的硬件編程語言可能表述方式不一樣,電路跟水路有點像是有流向的!而且數字電路二級管是嚴格的流向控制的,反向有高阻狀態

CPU () {

////////取指令階段

//CPU地址選址

Address => CPUControlMod.Address;//地址傳輸到控制單元

//在CPU初始化時程序的地址已經被BIOS改變成程序入口點

CPUControlMod.PCAddress = CPUControlMod.Address;//把程序地址保存到指令計數器當中,當然這個計數器是簡化的,還可以擴展

Code => CPUControlMod.ControlLine; //讀取指令後數據流向控制單元,

CPUControlMod.ControlLine => CPUControlMod.outCodeLine => CPUControlMod.CodeRem.indate;//指令流向指令寄存器這裡不直接連輸出口,是因為咱們這個CPU還可以擴展功能,現在實現的是基本的模塊

CPUControlMod.CodeRem.indate => CPUControlMod.CodeRem.outdate;//指令從寄存器中輸出咱們為了方便把寄存過程省略了

////////指令譯碼階段

CPUControlMod.CodeRem.outdate[3:0] => CPUControlMod.DeCode.CodeLine;//這裡開始指令操作碼與地址碼分開

CPUControlMod.CodeRem.outdate[7:4] => CPUControlMod.DeCode.Rem;//寄存器選擇碼

CPUControlMod.DeCode.Rem => CPUControlMod.DeCode.OutRem; //寄存器操作碼輸出

CPUControlMod.CodeRem.outdate[15:8] => CPUControlMod.DeCode.Address;//得到偏移地址

CPUControlMod.DeCode.CodeLine => CPUControlMod.DeCode.OutCodeLine;//把操作碼傳輸出指令寄存器

CPUControlMod.DeCode.addressDate = 0x00h || CPUControlMod.DeCode.Address;//把地址擴展這個可以放到算術邏輯單元當中計算,這裡為了方便直接計算了

CPUControlMod.DeCode.addressDate => CPUControlMod.DeCode.OutAddress ;//把偏移地址地址值傳輸

//其實在寄存器當中也是可以實現部分邏輯指令,但這裡小編儘量簡化,咱們只是要了解CPU結構,但太複雜的話後面也不利於我們電路講解,而且小編是真的全憑腦袋回憶和原理來實現的,太費神的話小編也吃不消。

//控制單元連線完畢

//接下來是整個CPU控制和處理流程,這裡的連線比較錯綜複雜不能用直連方式表達

CodeLine = CPUControlMod.DeCode.OutCodeLine;//CPU與操作碼有關

Rem = CPUControlMod.DeCode.OutRem;//寄存器選擇符

//////執行指令階段

if (Enable == enable){

If(rst == deable){

If(clk == enable){

Switch(CodeLine){

Case nop:

//空操作,什麼都不做

Break;

Case mov:

//數據傳輸操作

Switch (Rem){

Case NULL:

//將當前的數據直接傳輸給當前地址,這裡不存在這種操作,所以為空操作

Break;

Case dateremOne:

//將數據傳輸給數據寄存器1中

CPUControlMod.DeCode.OutAddress => CPUDateRemMod. Indateone;

CPUDateRemMod.dateOne = CPUDateRemMod. Indateone;

Break;

Case dateremtwo:

//將數據傳輸給數據寄存器2中

CPUControlMod.DeCode.OutAddress => CPUDateRemMod. Indatetwo;

CPUDateRemMod.dateTwo = CPUDateRemMod. Indatetwo;

Break;

Case adressrem:

//將地址或者數據傳到地址寄存器當中,這裡不實現了

Break;

Case ESP:

//這裡不存在這種操作,當然觀眾老爺可以自己試著去填加這種實現

Break;

}

Break;

Case add:

//加法操作

Switch (Rem){

Case NULL:

//空操作

Break;

Case dateremOne:

//用數據寄存器1的數據與實數或地址相加結果保存到數據寄存器1中

//將譯碼器當中的數據傳輸給運算單元

CPUControlMod.DeCode.OutAddress => CPUMathMod.inDatetwo;

//將數據寄存器1中的數據傳送到運算單元中

CPUDateRemMod.dateOne => CPUDateRemMod.outdateone;

CPUDateRemMod.outdateone => CPUMathMod.inDateone;

//進行計算,其實中間最好有個過程但這裡就不實現了

CPUMathMod.outDate = CPUMathMod.inDateone + CPUMathMod.inDatetwo;

//運算結果保存到數據存器1中

CPUMathMod.outDate => CPUDateRemMod.Indateone;

CPUDateRemMod.dateOne = CPUDateRemMod.Indateone;

Break;

Case dateremtwo:

//用數據寄存器2的數據與實數或地址相加結果保存到數據寄存器2中

//將譯碼器當中的數據傳輸給運算單元

CPUControlMod.DeCode.OutAddress => CPUMathMod.inDatetwo;

//將數據寄存器2中的數據傳送到運算單元中

CPUDateRemMod.dateTwo => CPUDateRemMod.outdatetwo;

CPUDateRemMod.outdatetwo => CPUMathMod.inDateone;

//進行計算

CPUMathMod.outDate = CPUMathMod.inDateone + CPUMathMod.inDatetwo;

//運算結構保存到數據存器2中

CPUMathMod.outDate => CPUDateRemMod.Indatetwo;

CPUDateRemMod.dateTwo = CPUDateRemMod.Indatetwo;

Break;

Case adressrem:

//這裡不實現了

Break;

Case ESP:

//這裡不存在這種操作

Break;

}

Break;

Case sub:

//減法操作

Switch (Rem){

Case NULL:

//空操作

Break;

Case dateremOne:

//用數據寄存器1的數據與實數或地址相加結果保存到數據寄存器1中

//將譯碼器當中的數據傳輸給運算單元

CPUControlMod.DeCode.OutAddress => CPUMathMod.inDatetwo;

//將數據寄存器1中的數據傳送到運算單元中

CPUDateRemMod.dateOne => CPUDateRemMod.outdateone;

CPUDateRemMod.outdateone => CPUMathMod.inDateone;

CPUMathMod.outDate = CPUMathMod.inDateone - CPUMathMod.inDatetwo;

//運算結果保存到數據存器1中

CPUMathMod.outDate => CPUDateRemMod.Indateone;

CPUDateRemMod.dateOne = CPUDateRemMod.Indateone;

Break;

Case dateremtwo:

//用數據寄存器2的數據與實數或地址相加結果保存到數據寄存器2中

//將譯碼器當中的數據傳輸給運算單元

CPUControlMod.DeCode.OutAddress => CPUMathMod.inDatetwo;

//將數據寄存器2中的數據傳送到運算單元中

CPUDateRemMod.dateTwo => CPUDateRemMod.outdatetwo;

CPUDateRemMod.outdatetwo => CPUMathMod.inDateone;

CPUMathMod.outDate = CPUMathMod.inDateone - CPUMathMod.inDatetwo;

//運算結構保存到數據存器2中

CPUMathMod.outDate => CPUDateRemMod.Indatetwo;

CPUDateRemMod.dateTwo = CPUDateRemMod.Indatetwo;

Break;

Case adressrem:

//這裡不實現了

Break;

Case ESP:

//這裡不存在這種操作

Break;

}

Break;

Case mul:

//乘法操作

Switch (Rem){

Case NULL:

//空操作

Break;

Case dateremOne:

//用數據寄存器1的數據與實數或地址相加結果保存到數據寄存器1中

//將譯碼器當中的數據傳輸給運算單元

CPUControlMod.DeCode.OutAddress => CPUMathMod.inDatetwo;

//將數據寄存器1中的數據傳送到運算單元中

CPUDateRemMod.dateOne => CPUDateRemMod.outdateone;

CPUDateRemMod.outdateone => CPUMathMod.inDateone;

CPUMathMod.outDate = CPUMathMod.inDateone* CPUMathMod.inDatetwo;

//運算結果保存到數據存器1中

CPUMathMod.outDate => CPUDateRemMod.Indateone;

CPUDateRemMod.dateOne = CPUDateRemMod.Indateone;

Break;

Case dateremtwo:

//用數據寄存器2的數據與實數或地址相加結果保存到數據寄存器2中

//將譯碼器當中的數據傳輸給運算單元

CPUControlMod.DeCode.OutAddress => CPUMathMod.inDatetwo;

//將數據寄存器2中的數據傳送到運算單元中

CPUDateRemMod.dateTwo => CPUDateRemMod.outdatetwo;

CPUDateRemMod.outdatetwo => CPUMathMod.inDateone;

CPUMathMod.outDate = CPUMathMod.inDateone* CPUMathMod.inDatetwo;

//運算結構保存到數據存器2中

CPUMathMod.outDate => CPUDateRemMod.Indatetwo;

CPUDateRemMod.dateTwo = CPUDateRemMod.Indatetwo;

Break;

Case adressrem:

//這裡不實現了

Break;

Case ESP:

//這裡不存在這種操作

Break;

}

Break;

Case div:

//除法操作

Switch (Rem){

Case NULL:

//空操作

Break;

Case dateremOne:

//用數據寄存器1的數據與實數或地址相加結果保存到數據寄存器1中

//將譯碼器當中的數據傳輸給運算單元

CPUControlMod.DeCode.OutAddress => CPUMathMod.inDatetwo;

//將數據寄存器1中的數據傳送到運算單元中

CPUDateRemMod.dateOne => CPUDateRemMod.outdateone;

CPUDateRemMod.outdateone => CPUMathMod.inDateone;

CPUMathMod.outDate = CPUMathMod.inDateone / CPUMathMod.inDatetwo;

//運算結果保存到數據存器1中

CPUMathMod.outDate => CPUDateRemMod.Indateone;

CPUDateRemMod.dateOne = CPUDateRemMod.Indateone;

Break;

Case dateremtwo:

//用數據寄存器2的數據與實數或地址相加結果保存到數據寄存器2中

//將譯碼器當中的數據傳輸給運算單元

CPUControlMod.DeCode.OutAddress => CPUMathMod.inDatetwo;

//將數據寄存器2中的數據傳送到運算單元中

CPUDateRemMod.dateTwo => CPUDateRemMod.outdatetwo;

CPUDateRemMod.outdatetwo => CPUMathMod.inDateone;

CPUMathMod.outDate = CPUMathMod.inDateone / CPUMathMod.inDatetwo;

//運算結構保存到數據存器2中

CPUMathMod.outDate => CPUDateRemMod.Indatetwo;

CPUDateRemMod.dateTwo = CPUDateRemMod.Indatetwo;

Break;

Case adressrem:

//這裡不實現了

Break;

Case ESP:

//這裡不存在這種操作

Break;

}

Break;

CPUControlMod.PCAddress = CPUControlMod.PCAddress+1;//程序每讀取一個指令後這個地址值自動增加一,指向讀取下一條指令的地址,但有些操作比較特殊就不能進行地址增加

Case jmp:

//跳轉操作其實就是地址選取操作

CPUControlMod.PCAddress = CPUControlMod.PCAddress + CPUControlMod.DeCode.OutAddress;

CPUControlMod.PCAddress =>CPUControlMod.Address;

CPUControlMod.Address => Adress;

Break;

Case push:

//壓棧操作,就是把數據和地址值進行保存,這裡不實現

Break;

Case pop:

//彈棧操作,就是把數據和地址值進行保存,這裡不實現把地址恢復

Break;

}

}

}

}

觀眾老爺到了是不是對CPU的具體實現有個瞭解了呢,當然小編也有很多細節沒有模擬,但是整個CPU的原理基本是實現了,具體的硬件實現下期再進行講解,觀眾老爺記得點贊收藏關注啊,您的支持就是我成長的動力!觀眾老爺下期再會!

如何自制CPU?(2)

相關推薦

推薦中...