簡單的多週期MIPS I CPU設計(二)—— 指令執行階段

huanghongxun發表於2018-06-26

上一篇:綜述
下一篇:主存

通常我們將RISC CPU的指令的執行過程分為5個步驟,分別是取指令(IF)、解碼(ID)、執行(EX)、記憶體讀寫(MEM)、暫存器寫(WB)。我們要實現CPU,可以將5個步驟做成5個子模組分別設計,這樣可以大大降低我們的開發複雜度。

取指令(IF)

CPU在取指令階段(IF階段)時,先向一級指令快取要指令,要到指令後我們將程式計數器(PC)自增1(1表示移動一條指令的寬度,如果資料單位是32位,那麼就自增1,如果資料單位是8位1位元組,那麼就自增4)。這樣我們在下次取指令的時候就能取到下一條指令了。同時如果你實現了分支預測,那麼在這裡則需要做另外的處理。

那麼我們的取指令模組fetch_unit.v的埠如下:

埠種類 埠名 埠意義
input clk 時鐘訊號
input rst_n 復位訊號
input stall 阻塞訊號
input rw 讀寫狀態
input write 跳轉到的地址
output pc 程式計數器

當不阻塞時,寫狀態則將pc置為write,否則自增。很簡單的邏輯。

    always @(posedge clk, negedge rst_n)
        if (!rst_n)
            pc <= 0; // 復位
        else
            if (!stall) // 如果未被阻塞(和流水線有關)
                if (rw == `MEM_WRITE) // 寫狀態,二選一的資料選擇器
                    pc <= write; // 預置端
                else // 預設狀態
                    pc <= pc + 1; // 一個加法單元,其中一個運算元固定為1

解碼(ID)

注意到我們有這些指令對:add和addi,addu和addui,or和ori等,這些指令對的功能是一樣的,只是取運算元的方式不一樣,如果我們能用某種方式統一這些指令對,那麼我們在實現這些指令的執行將會變得更簡單,因為對於同一類的指令我們能做同一個操作。解碼器就是做這個事情的。我們要實現的解碼器將會把指令的資訊分成如下的幾條:

資訊代號資訊描述
rs左運算元暫存器代號
nop、lui、j、jal、mflo、mfhi不需要
rs_enable是否有左運算元
nop、lui、j、jal、mflo、mfhi指令該項為false
rt右運算元暫存器代號
對於所有有立即數(imm和shamt)和只需要讀取一個暫存器的指令都不需要
rt_enable是否有右運算元/右運算元是否從暫存器中提取
對於所有有立即數(imm和shamt)和只需要讀取一個暫存器的指令該項為false
rd結果存放的暫存器代號
對於所有有立即數(imm)的指令,和指令中的rt部分相等
imm32位立即數(如果指令有立即數imm,需要擴充套件至32位
算術運算(無論是有符號還是無符號)均為有符號擴充套件,邏輯與地址運算均為無符號擴充套件
alu_opALU的微指令碼
需要和ALU一致
addr跳轉指令要跳轉到的地址
對於j類指令,addr為立即數;對於b類指令,由傳入的pc和立即數運算得到
ex_src執行階段使用ALU、浮點運算單元、還是整數乘除單元
\makecell{mul、mulu、div、divu採用整數乘除單元;浮點運算採用FPU;\\算術邏輯地址運算使用ALU;否則交由外部裝置執行
b_ctrl控制右運算元使用立即數代替還是從暫存器中取數
有立即數的指令均為真,否則為假
mem_width讀寫記憶體的時候是位元組模式、半字模式還是字模式
lb、lbu、sb、sbu為位元組模式;lh、lhu、sh、shu為半字模式;lw、sw為字模式
mem_rw寫記憶體還是讀記憶體
l類指令為讀記憶體(低電平);s類指令為寫記憶體(高電平)
mem_enable指令是否要操作記憶體
比如lb, sb之類的指令需要操作記憶體
sign_extend記憶體操作時是否進行符號擴充套件
\makecell{lb、lh、lbu、sb、sh之類的指令不足一個字,而暫存器為1個字長\\u類指令需要零擴充套件,非u類指令為符號擴充套件
wb_srcWB階段寫入暫存器的資料從ALU拿還是從記憶體拿
運算類指令從ALU拿,l類指令從記憶體拿
wb_regWB階段是否需要將資料寫回到暫存器
跳轉、儲存、浮點運算、整數乘除都不需要將資料寫回
jump跳轉到的地址在暫存器中儲存還是從指令中的地址碼拿
jr和jalr需要,j和jal不需要
branch是否是跳轉指令
j類指令和b類指令為真

CPU在解碼階段(ID階段)時還需要根據解碼器得到的運算元暫存器的編號,從相應的暫存器中取出ALU所需要的運算元,因此我們也將暫存器歸到解碼階段中。

執行(EX)

執行階段(EX階段)是我們呼叫ALU進行真正的計算過程。由於乘法和除法的速度比較慢,如果1個週期能完成加法的計算,那麼乘法和除法就需要超過1個週期的時間,也就是說乘法器和除法器在多週期CPU裡是多週期的。同時,所有的浮點運算也都是多週期的,比如浮點加是4個週期(另外在浮點運算單元中同樣存在流水線,把浮點加法分成4個階段計算)。

同時對於syscall指令,我們也要在執行階段完成操作。

因此執行階段CPU一共會有以下執行模組,分別是ALU、整數的乘除運算單元、浮點運算單元(內部仍存在流水線,而且加減法和乘除法可以並行執行)和其他的一些處理電路。

ALU

ALU在執行階段將會按照給定的ALU的微指令,對兩個運算元進行運算(無論是加減運算、位運算、比較運算以及跳轉指令需要的比較運算),並得到結果輸出(輸出包括運算結果、是否跳轉等資訊)。對於記憶體操作的指令,我們也需要ALU計算出我們要讀取的記憶體的真實地址(因為指令規範中給定的是某個暫存器的偏移,或者當前指令的偏移)。

當解碼階段得到的ALU使能訊號為真時,ALU計算結果。目前我們要支援的運算如下:

代號描述代號描述代號描述代號描述
sll邏輯左移srl邏輯右移sra算術右移lu左移16位
mfhi載入乘除法的高32位mflo載入乘除法的低32位
add加法sub減法and邏輯與or邏輯或
xor異或nor同或slt算術比較sltu無符號算術比較
beq相等跳轉bne不相等跳轉bltz小於零跳轉bgez大於等於零跳轉
blez小於等於零跳轉bgtz大於零跳轉

ALU的埠如下:

埠種類 埠名 埠意義
input stall 阻塞訊號
input en 使能訊號
input op 微指令
input rs 運算元
input rt 運算元
output rd 運算結果
output branch 是否跳轉
output test_state 執行測試用指令結果

那麼實現一個ALU就是一個大的無條件always語句以及case語句了。
電路方面就是多路選擇器。

記憶體讀寫(MEM)

對於lblbulhlhulwsbsbushshusw等記憶體操作的指令,這個階段將被啟用。這個階段將會與一級的資料快取互動。

暫存器寫(WB)

這個階段(WB階段)有結果的指令將會把資料存到對應的暫存器裡。

上一篇:綜述
下一篇:主存

相關文章