Try   HackMD
tags: VHDL NCKU

CPU作業心得補充

  • 建立於此篇的概念上我仍然遇到的問題 112張娟鳴

檔案架構

  • main.s + Makefile: 可以更改指令,改完後make他即可(助教很貼心的給makefile了。
  • top_tb.v: 跑完測試拉波形的時候,自己宣告的register file會在Memory那邊,沒有跟sim放在一起;通常拉i_CPU, register file以及i_DM即可(假設你很順利能讀指令)
  • 當初沒注意到main.s裡面已經有答案了,我是使用main.logdebug的,可以兩個對照啊這樣就不用自己算值了
  • 四個.v檔,main0~main3以及golden_hex一定要跟工作專案是同個資料夾,最簡單的方法是直接把資料夾的全部複製過去,最後單獨把CPU.v和自己增加的module複製回來即可

CPU的I/O訊號

在還是XXXXX的時候

  • 前文也說了,是因為指令沒寫好,但不可能改那麼快,所以基本上是先將下面註解掉
initial begin #(`MAX*`CYCLE) $display("Simulation Failed"); $finish; end

他的意思是,當你時間超過他們規定的時間時,就要算失敗,但是助教給的時間非常充裕,所以問題是出在

  • instr_out沒有東西,沒有指令DECODE
  • 某幾個指令寫錯,讓他沒辦法跳到結束那一段程式

    在log檔裡,有一個叫做main_exit的block,你要跳到那邊
    以我來說,我沒辦法跳到是因為我的simm和register file沒有做signed,做了以後就跳到了

如果註解掉後就可以看到DM裡的結果,那問題是第二種;如果還是不行,就是第一種,或是如原文說的初始化不夠正確

開始測試

補個讓人霧煞煞的LW和SW

  • 當你要lw
    • 先將data_read改成1
    • data_addr給入需要的register_file位置

      注意:給值時候是給類似x[7]+4,只要data_addr顯示了這個的值(假設x[7]=3092,那data_addr總共就是3096)就是對的

    • data_out出現值,這時要將這個值分到rd

      data_out的值不等於data_addr(data_addr=3096,但data_out會是另外的值,這就要看原本在那裡存了什麼)

    • 總之,做的都是地址,地址!(我debug的時候忘記這件事情,自己陷入死迴圈)
  • 當你要sw
    • 將data_addr放入你要存的地址
    • 依據需要寫的地方更改data_write,1是可以寫入,0是不可以,所以大部分時間都要是0

      進階的部份:data_addr <= x[7] + 13和data_addr <= x[7] +16在DM的addr顯示的是同個東西,但對我們來說,我們要寫入的位置會變得不同,以sb為例
      __ __ __ __ | __ __ __ __
      16 15 14 13 | 16 15 14 13
      差別大概是這種感覺,要用imm的末兩位來判斷,可以直接參考裡面的文章寫,如果出來結果還是不對就調換順序(快速debug方式,概念則是這樣)

    • 依據要寫的地方來放置data_in,才能存入

      基本寫法和data_write差不多,只要注意在data_write是1的地方data_in要對就好,至於其他部份即使是前一時刻的值也沒什麼關係

lb: load byte (8 bit)
lh: load half word (16 bit),這兩個在你分配x[rd]要做好位置的分配與補0

  • 關於imm
    • 我是依照指令分了不同種類的imm,而且有的imm有reg signed有的沒有,有的甚至也不是32bit(32 bit和12bit的register是可以相加的)
    • 會特別需要注意signed extend的是I-type和S-type的,在加地址的時候,會有要加負號的情況,但如果他不是signed的,像-4就會變成+4092

      解決方法:
      1.如果是32bit:設成reg signed(或是用$signed個別補充)並在讀取imm的時候要自己sign extend他,讓他依據你讀入的最高位補
      2. 12 bit register: 直接設成reg signed(附帶一題:register file設成signed會比較好,不用自己轉),不用補東西
      他做加法時會自己幫你補剩下20位,好耶!

      Image Not Showing Possible Reasons
      • The image file may be corrupted
      • The server hosting the image is unavailable
      • The image path is incorrect
      • The image format is not supported
      Learn More →

    • 缺點是要設定很多register,優點是很直觀的直接把看到的數字分配塞進去

易寫錯或有爭議的指令

JAL

那個是故意的,你絕對不可以改x[0]

  • 比較建議的做法每個步驟最後面都加x[0] <= 0
  • 我是分FSM,並且一個變數一個always block,這樣我的變數x[]在不知道要做什麼,甚至是每個state後面,我都可以塞x[0] <= 0(模擬電腦不讓給x[0]的情況)

JALR

jalr本身是因為要跳到某個地方,必須先把現在位置存到暫存器,然後才能跳過去;存的時候要存的是現在位置+4,因為現在這裡已經做過了,我是要接下一步

問題指令:jalr $t0,$t0,0 原意

x[rd] = pc + 4; pc = imm + x[rs1];

出問題時的流程:

//pc=912,imm=400,$t0=200 $t0 = pc + 4; //$t0 = 916 pc = imm + $t0; //pc = 1316

實際上我們想要的:
(1)

//pc=912,imm=400,$t0=200 pc = imm + $t0; //pc = 600 $t0 = pc + 4; //$t0 = 916

這樣也能解決問題
(2)

//pc=912,imm=400,$t0=200 $t0 <= pc + 4; //$t0 = 916 pc <= imm + $t0; //pc = 600 //前提,兩個在同個clk一起做,pc不可以比較晚

(3)

//pc=912,imm=400,$t0=200 reg [32:0]temp; temp = $t0; $t0 = pc + 4; //$t0 = 916 pc = imm + temp; //pc = 600

也就是,會不會遇到要看程式架構:

  • unblocking(<=):如果rd和PC在同個state處理(或是PC比較早處理),因為他取的都是上個clk的值,所以沒有遇到這個問題,但這樣會有幾個複雜的指令(其他地方)要提前處理
  • blocking(=):有同學是另外開個變數去存rd的東西
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →

    (我就想放圖不要阻撓我)

結果

errr,去年是44個指令,但今年(112級)是54筆測資,今年多了mul系列,不知道明年會不會又多,郭
所以DM的參考指令不一定準,前面幾個差不多,大概在sw後面開始會不一樣,不一樣的話:

  • 看一下錯在哪個DM
  • 看那個DM輸出什麼值,然後你$t0(x[5])什麼時候是這個值
  • 差不多就在那邊(因為他每次都存在$t0,所以去那邊看就好)

或是可以數已經使用了幾個sw,但指令很多會數到亂掉Q

心得

我本身的程式架構

-------------------------- 助教給的input,output(有的output可以自己加reg,這個是從紅綠燈就知道的事情ㄛ) -------------------------- -------------------------- 自己的register 我用到了: reg [31:0] x [31:0]//register file reg [11:0]iimm,simm...//只需要12 bit的imm reg [31:0]jimm...//需要31 bit imm(因為中間一坨0) reg tmp, flag...//輔助用,依照自己需求加減 --------------------------- --------------------------- parameter implement 我做了兩組parameter宣告,一組是單純FSM,一組是opcode的type optype其實可有可無,有的話比較好閱讀,因為你可以記你是Rtype或是什麼, 分case處理時很容易閱讀,至少比binary好閱讀 缺點就浪費行數跟空間r --------------------------- --------------------------- always@(posedge clk)begin if(rst)begin 大多變數為0的初始化 end else begin 依據不同state不同狀態改變 end end 有n個,總之就是一個always block只放一個變數,有幾個我就寫幾個 一開始會先處理parameter變數變化//例如 FSM 和 opcode type 再來處理各種會連到外面的output//data_in,data_addr 各種指令會用到的變數的讀取//rs1,rs2,funct3,imm x[]和PC等計算//加減乘除會在x[]|beq,bne會在PC ---------------------------
  • 優點:
    很好debug(只要會用modelsim,到後期6小時把所有bug de完,errr雖然前面花很久),看波形圖哪塊出問題,直接去那塊變數看,不用整個架構從頭找到尾還找不到,現在很多IDE都有良好的編輯功能,可以把block縮起來,真的是良心
    確保變數不會被亂改,因為分成一個個變數寫,很容易知道自己漏寫什麼,比較不會有layout
  • 缺點:
    行數很高,檔案特別大,會覺得自己是不是笨蛋,怎麼寫那麼多還沒寫完,而且要一直寫重複的語句可能會很煩啦

我看過的程式架構

  • 把ALU和decoder獨立出來,R-type另外處理

    • 優點:主程式行數少,也很好懂
    • 缺點:自己要會接module,因為我不太會接所以對我來說很難,我很抱歉
  • 全部擠在一起

    • 優點:行數很少,檔案很小,狀態沒有那麼細,速度應該會比較快
    • 缺點:如果當初沒有寫註解的話自然很難debug,因為參考到了這種類型的,我又快看不懂(我就爛),還好有朋友給我其他份有註解的

如果不知道如何下手,或許可以

  • 先處令指令跟register file, PC的相加減關係
  • 先接線

本來就是分這兩部份啦,只是會忘記
然後modelsim有個小技巧是在波形圖左下角有時間,時間可以自己輸入和複製,這樣就不用一直重複拉波形圖了,可以直接跳到那邊,好耶!

可能用得上的debug法

  1. 先確定自己的instr_out是否有讀入東西,且是否是從PC讀的
  2. 接著使用log檔比對指令是否正確,可以看一下他PC位置是否正確(main.log大約在300行左右可以看到一開始他幫你執行什麼指令,main.s裡面沒辦法知道)
    modelsim內PC可以使用16進位顯示,和log檔相對應
  3. 在開始測試add,sub指令是否正確前,會有一長串得初始化以及stack的寫入,基本上其實你大部分程式時間都會在這邊
  4. 一開始顯示XXXXX的時候不要慌,如果指令有正常讀入,修正到後面答案會跑出來;也可能出現一度顯示答案後來又顯示不了的情況
  5. stack的寫入其實是他會比較你的x[10]和x[11],然後x[10]一定要比x[11]大,如果沒有比較大的話就要+4,然後再比一次,重複此動作直到x[10]>=x[11]
    在這裡,+4以後會跳回比較指令的第一行,PC顯示會是01000114之類的,前面的數字不用管,只要看後面是不是正確即可
  6. 第五個動作會做兩次,做完後才進入測試add那些指令
    這時,PC位置是ee>>ec>>118,不可以跑到f0,他會結束你的程式
  7. 進到這邊以後大部分PC不會有太大問題(頂多是jalr),等到所有指令順利跑到底後才會進入f0>>f4>>f8>>fc>>100
  8. 如果順利是這樣的話答案應該要能夠顯示了(儘管不一定對),再不能顯示應該會是SW問題
  9. 剩下的就是一般指令問題了

這樣de可能會比較踏實,當然,普通的一行一行對也可以,因為通常是小錯誤,修改完後可以多對很多,後面不會差太多