16位CPU怎么做,DIY大神給你支支招兒
狀態機基本上與系統所有的組件都連接到一起了,因為上面所說的所有動作的執行,都需要狀態機的控制,狀態機其實就是由一部分觸發器構成的記憶電路和另外一部 分組合邏輯構成的次態譯碼電路構成,還有根據當前狀態和輸入進行譯碼的部分用于控制各個部件,下面是教科書上的典型FSM結構:
本文引用地址:http://www.czjhyjcfj.com/article/201903/398604.htm
而我們用的狀態機狀態轉移圖如下:

因為這個處理器設計的很簡單,所以沒有出現很多狀態,當處理器經歷完以上的狀態之后,處理器就執行完了一條指令。
有的CISC的處理器用微指令進行控制,作用和狀態機相近,這種結構出現在一些比較古老的處理器上,因為那個時候的設計工具和方法沒有現在的先進,所以往往 改動硬件是困難的和高成本的,所以用微指令的話,做好了硬件的結構,要是需要改動只要修改微指令就好了,而現在的電子技術很發達,設計工具也很完備,所以 就有很多直接通過硬連線實現的處理器。
好馬配好鞍,有了處理器,我們就得給它配上一個好的程序,下面我們就用自己設計的處理器進行求和,從1加到100,因為我們沒有設計編譯器,也沒有設計匯編器,所以程序只能用機器碼寫出,示例程序如下:
我們不妨先寫出程序的匯編代碼:
mov [ADDR],r0;r0 = 0
mov r1,100
lop:add r2,r1
sub r1,1
cmp r1,0
jz ext
mov r4,4
jmp r4(lop)
ext:mov [ADDR],r2
jmp $
先將內存中存放數據的地址清零,這樣才能存放等下送來的結果,然后將r1寄存器存入循環次數(也就是求和的上限)。然后再將r1的值加到r2中來,r2其實就是放求和的寄存器,最后我們會將r2中的值送到內存中的某個地址存放的。
然 后將r1減去1,看看是否為0?如果為0則說明求和結束了,如果不是0則說明還要繼續,結束后程序就跳到ext部分將結果存放到內存中某個地址(例子中給 的是49152也就是二進制的1100000000000000b),最后jmp $是為了讓程序停在這一行,防止程序跑飛(跑飛的程序危害很大!有可能吧數據當代碼或者把代碼當數據!)
轉換成VerilogHDL語言如下:
module memory
(
input [15:0] addr,
inout [15:0] data,
input rw
);
reg [15:0] data_ram[0:16'b1111_1111_1111_1111];
integer i;
initial begin
for (i = 0; i <= 16'b1111_1111_1111_1111; i = i + 1)
data_ram[i] = $random();
data_ram[0] = 16'b1000000100000000; //mov [ADDR],r0;r0 = 0
data_ram[1] = 16'b1100000000000000; //ADDR
data_ram[2] = 16'b1000000010001000; //mov r1,100
data_ram[3] = 100; //100
//data_ram[2] = 16'b1110011001000000;
data_ram[4] = 16'b0010000100010001; //lop:add r2,r1
data_ram[5] = 16'b1110000011001000; //sub r1,1
data_ram[6] = 16'b0000000000000001; //1
data_ram[7] = 16'b1110000000001000; //cmp r1,0
data_ram[8] = 16'b0000000000000000; //0
data_ram[9] = 16'b1110011010000000; //jz ext
data_ram[10] = 16'b0000000000000011; //+3 offset(ext)
data_ram[11] = 16'b1000000010100000;//mov r4,4
data_ram[12] = 16'b0000000000000100;
data_ram[13] = 16'b0110011001100000;//jmp r4(lop)
data_ram[14] = 16'b1000000100000010;//ext:mov [ADDR],r2
data_ram[15] = 16'b1100000000000000;//ADDR
data_ram[16] = 16'b1110011001000000;//jmp $
data_ram[17] = 16'b1111111111111110;//-2 offset($)
/*data_ram[0] = 16'b1000000010000000; //mov r0,imm
data_ram[1] = 16'b0011111111111111; //imm
data_ram[2] = 16'b0000000001111000; //mov r7,r0
data_ram[3] = 16'b1000000010011000; //mov r3,0
data_ram[4] = 16'b0000000000000000;
data_ram[5] = 16'b1000000010100000; //mov r4,code of jmp r5
data_ram[6] = 16'b0110011001101000; //jmp r5
data_ram[7] = 16'b0000000101011100; //mov [r3],r4
data_ram[8] = 16'b1000000011110000; //mov r6,[0]
data_ram[9] = 16'b0000000000000000; //[0]
data_ram[10]= 16'b1000000100000110; //mov [255],r6
data_ram[11]= 16'b0000000011111111;
data_ram[12]= 16'b0110011001011000; //jmp r3
*/
end
always @ (addr or rw or data)
if (rw)
data_ram[addr] = data;
assign data = rw ? 16'hzzzz : data_ram[addr];
endmodule
設計中CPU外圍還需要一個內存設備(Memory),我用HDL對其建模,初始化的時候每個內存地址上對應的數據都初始化為隨機的,然后只有從0開始的一系列地址被初始化為我寫的代碼,機器碼對應的匯編指令在注釋中已經給出。
然后是結果,結果應該是r2從0變化到5050(1+2+3+......+100=5050)
而r1則從100變化到0,變化到0后程序將進入死循環,停止在jmp $那一條。這是仿真開始的時候:

大家可以看到初始化后,d0~d7都變成了0,這是r0~r7寄存器的Q端,而state_current和state_next則是狀態機的現態和狀態機 的次態,cpu的各個部件都通過這個狀態機受到控制。狀態名出現的順序和上面的FSM Viewer的連線順序是一樣的。
而且大家可以看到,d2從0變化到了0x64也就是十進制100,說明已經執行了第一次加法了。
再來看看仿真結束:

這時候d1變化到了0而d2變化到了0x13ba(十進制的5050),說明程序已經在我們設計的處理器里面運行并且成功的得出了結果!
最后給出一些我用到的指令(跟x86的很像):
add dst,src 將src和dst相加并且送到dst寄存器中
mov [addr],src 將src的值送到以addr位地址的內存單元
sub dst,src 將dst減去src并且送到dst中去
cmp dst,src 將dst減去src 然后不送到dst中 只改變標志位
jz dst 當zf=1時(即上次的算術操作結果為0)則跳轉到dst中去
最后再提一下:
我是用synplify綜合的電路,然后用debussy+modelsim仿真的。
評論