2011年10月3日 星期一

J1 Forth CPU 研究之三:系統重設和 program counter

這是一系列文章中的第三篇,這系列的文章是要解讀 J1 CPU 的設計以及在其上的 Forth 語言的使用法。

這篇要談的是 j1 CPU 的 Verilog 程式中和系統重設以及 program counter 有關的部份,請見以下檔案:

src/hardware/verilog/j1.v

在 j1.v 中,系統重設的訊號是 sys_rst_i,因此搜尋所有 sys_rst_i 出現的地方。
以下是其中一處:

  always @(posedge sys_clk_i)
  begin
    if (sys_rst_i) begin
      pc <= 0;
      dsp <= 0;
      st0 <= 0;
      rsp <= 0;
    end else begin
      dsp <= _dsp;
      pc <= _pc;
      st0 <= _st0;
      rsp <= _rsp;
    end
  end

說明如下:
  1. sys_clk_i:系統的 clock 訊號
  2. always @(poseedge sys_clk_i):在系統的 clock 訊號的上升邊緣總是要執行底下 begin 至 end 的事。
  3. sys_rst_i:系統的重設訊號
  4. pc:program counter
  5. dsp: 資料堆疊指標
  6. st0: 資料堆疊頂端的暫存器
  7. rsp:返回堆疊的指標
因此,這段程式是說,在系統的 clock 訊號的上升邊緣如果重設訊號為真,就把 pc, dsp, st0, rsp 都設為零。否則, pc, dsp, st0 和 rsp 會被設為 _pc, _dsp, _st0, _rsp。其中 _pc, _dsp, _st0, _rsp 這幾個值會在別處計算。
因此我們知道,這個 CPU 一被重設,就會從位址零開始執行。而且,所有的堆疊都被清空。

以下是另一處:

  always @*
  begin
    if (sys_rst_i)
      _pc = pc;
    else
      if ((insn[15:13] == 3'b000) |
          ((insn[15:13] == 3'b001) & (|st0 == 0)) |
          (insn[15:13] == 3'b010))
        _pc = insn[12:0];
      else if (is_alu & insn[12])
        _pc = rst0[15:1];                          // 注意推上返回堆疊的副程式返回位址是 8 bit 的 byte 位址,而 program counter 使用的是 16 bit 的 word 位址,因此用 rts0[15:1],不使用 rts0[15:0]。
      else
        _pc = pc_plus_1;
  end

為了瞭解以上程式必須瞭解 J1 機器碼的編碼,請見下圖或是 J1: a small Forth CPU Core for FPGAs

程式說明如下:
  1. always @*:總是要做以下 begin 至 end 間的事。
  2. insn:放的是目前要執行的機器碼。
  3. 如果系統重設訊號 sys_rst_i 為真,則 _pc = pc, 不做任何改變。
  4. 如果重設訊號為假,則檢查目前要執行的機器碼,
    1. 如果第 13 到第 15 位元為二進制的 000, 或是 001 或是 010,分別是 j1 CPU  的 jump, conditaional jump 及 call 指令,則 program counter 被設為機器碼中 0 到 12 位元的值。
    2. 如果機器碼是 alu (13到15位元為二進制的 011),而且 12 位元為 1,則這是一個把返回堆疊頂的資料放到 program counter 的指令,就是副程式返回,就是 Forth 的 NEXT 指令啦。
    3. 如果以上皆非,那麼 program counter 等於 program counter 加一,處理下一個機器碼。
程式中的 3'b000 是 Verilog 的語法,一個 3bit 的二進制數字 000。3'b001 及 3'b010 依此類推。


程式中 |st0 == 0 的 | 會把 st0 中的 bits 都 or 起來,因此,這句判斷堆疊最上面的資料是否為零。這被用在 conditional jump:如果堆疊最上面的資料為零就要 jump。


由於 jump, conditional jump 及 call 後的位址只有 0 到 12 共 13 個位元,我們知道 j1 的程式最大只有 8K。不是 8K bytes,因為 J1 是 16 位元的 CPU,每個機器碼長度為 16 bits。

這樣子,我們就瞭解了 j1 在系統重設時的行為,以及它是如何實現 jump, conditional jump, call 及 ret (NEXT) 的。 由於 j1.v 只有 200 行程式,我們已經看懂了它的 30/200 之一的程式。

不過,在看上面的程式時要注意 Verilog 的 <= 和 = 的差異,這部份比較隱晦,請閱讀相關書籍。

沒有留言:

張貼留言