使用 Verilog 設計 CPU0 處理器 (簡單設計版)

Verilog

基本語法

型態

全域變數

基本元件

多樣的寫法

指定

assign

always

initial

運算式

分枝

迴圈

模組

函數

Task

陣列

輸出入

觀察

真值表

測試程式

訊息顯示

注意事項

模擬程序

硬體工程

程式範例

Xor

Xor3

全加器

加法器

加減器

快速加法器

乘法器

ALU

閂鎖器

脈衝偵測

計數器

多工器

暫存器群

記憶體

延遲問題

浮點數

狀態機

程式計數器

CPU0-Mini

CPU0

pipeline

工具

QuartusII

Icarus

Veritek

訊息

相關網站

參考文獻

最新修改

簡體版

English

專案下載:cpu0.zip

輸入指令檔:cpu0s.hex

00 1F 00 24 // 0000       LD   R1, K1
00 2F 00 1C // 0004       LD   R2, K0
00 3F 00 20 // 0008       LD   R3, SUM
08 40 00 0A // 000C    LDI  R4, 10
13 22 10 00 // 0010 LOOP: ADD  R2, R2, R1
13 33 20 00 // 0014       ADD  R3, R3, R2
10 24 00 00 // 0018       CMP  R2, R4
21 FF FF F0 // 001C       JNE  LOOP
2C 00 00 00 // 0020       RET
00 00 00 00 // 0024 K0:   WORD 0
00 00 00 01 // 0028 K1:   WORD 1
00 00 00 00 // 002C SUM:  WORD 0

Verilog 程式:cpu0s.v

// 參考文獻:http://ccckmit.wikidot.com/ocs:cpu0
module cpu0(input clock, reset, output reg [2:0] tick, 
             output reg [31:0] ir, pc, mar, mdr, inout [31:0] dbus, 
             output reg m_en, m_rw, output reg [1:0] m_w1);
    reg signed [31:0] R [0:15];
    reg [7:0] op;
    reg [3:0] ra, rb, rc;
    reg [11:0] cx5;
    reg signed [11:0] cx12;
    reg signed [15:0] cx16;
    reg signed [23:0] cx24;
    reg signed [31:0] scx12, scx16, scx24, A, B, C, ipc; // ipc:instruction PC

    // 暫存器簡稱
    `define PC   R[15]   // 程式計數器
    `define LR   R[14]   // 連結暫存器
    `define SP   R[13]   // 堆疊暫存器
    `define SW   R[12]   // 狀態暫存器
    // 狀態暫存器旗標位元
    `define N    `SW[31] // 負號旗標
    `define Z    `SW[30] // 零旗標
    `define C    `SW[29] // 進位旗標
    `define V    `SW[28] // 溢位旗標
    `define I    `SW[7]  // 硬體中斷許可
    `define T    `SW[6]  // 軟體中斷許可
    `define M    `SW[0]  // 模式位元
    // 載入儲存指令
    `define LD   8'h00 // 載入word;         LD Ra, [Rb+Cx];        Ra<=[Rb+ Cx]
    `define ST   8'h01 // 儲存word;         ST Ra, [Rb+ Cx];     Ra=>[Rb+ Cx]
    `define LDB  8'h02 // 載入byte;         LDB Ra, [Rb+ Cx];     Ra<=(byte)[Rb+ Cx]
    `define STB  8'h03 // 儲存byte;         STB Ra, [Rb+ Cx];    Ra=>(byte)[Rb+ Cx]
    `define LDR  8'h04 // LD的暫存器版;     LDR Ra, [Rb+Rc];    Ra<=(byte)[Rb+ Rc] 注意:這個有錯,(byte) 要拿掉
    `define STR  8'h05 // ST的暫存器版;        STR Ra, [Rb+Rc];    Ra=>[Rb+ Rc]
    `define LBR  8'h06 // LDB的暫存器版;    LBR Ra, [Rb+Rc];    Ra<=(byte)[Rb+ Rc]
    `define SBR  8'h07 // STB的暫存器版;    SBR Ra, [Rb+Rc];    Ra=>(byte)[Rb+ Rc]
    `define LDI  8'h08 // 立即載入;            LDI Ra, Rb+Cx;        Ra<=Rb + Cx
    // 運算指令
    `define CMP  8'h10 // 比較;                CMP Ra, Rb;         SW=(Ra >=< Rb)
    `define MOV  8'h12 // 移動;                MOV Ra, Rb;         Ra<=Rb
    `define ADD  8'h13 // 加法;                ADD Ra, Rb, Rc;     Ra<=Rb+Rc
    `define SUB  8'h14 // 減法;                SUB Ra, Rb, Rc;     Ra<=Rb-Rc
    `define MUL     8'h15 // 乘法;             MUL Ra, Rb, Rc;     Ra<=Rb*Rc
     `define DIV  8'h16 // 除法;             DIV Ra, Rb, Rc;     Ra<=Rb/Rc
    `define AND  8'h18 // 位元 AND;            AND Ra, Rb, Rc;     Ra<=Rb and Rc
    `define OR   8'h19 // 位元 OR;            OR Ra, Rb, Rc;         Ra<=Rb or Rc
    `define XOR  8'h1A // 位元 XOR;            XOR Ra, Rb, Rc;     Ra<=Rb xor Rc
    `define ROL  8'h1C // 向左旋轉;            ROL Ra, Rb, Cx;     Ra<=Rb rol Cx // 刪除
    `define ROR  8'h1D // 向右旋轉;            ROR Ra, Rb, Cx;     Ra<=Rb ror Cx // 刪除
    `define SHL  8'h1E // 向左移位;            SHL Ra, Rb, Cx;     Ra<=Rb << Cx
    `define SHR  8'h1F // 向右移位;            SHR Ra, Rb, Cx;     Ra<=Rb >> Cx
    // 跳躍指令
    `define JEQ  8'h20    // 跳躍 (相等);        JEQ Cx;             if SW(=) PC  PC+Cx       
    `define JNE  8'h21    // 跳躍 (不相等);    JNE Cx;             if SW(!=) PC  PC+Cx       
    `define JLT  8'h22    // 跳躍 ( < );        JLT Cx;             if SW(<) PC  PC+Cx       
    `define JGT  8'h23    // 跳躍 ( > );        JGT Cx;             if SW(>) PC  PC+Cx       
    `define JLE  8'h24    // 跳躍 ( <= );        JLE Cx;             if SW(<=) PC  PC+Cx       
    `define JGE  8'h25    // 跳躍 ( >= );        JGE Cx;             if SW(>=) PC  PC+Cx       
    `define JMP  8'h26    // 跳躍 (無條件);    JMP Cx;             PC <= PC+Cx       
    `define SWI  8'h2A    // 軟體中斷;        SWI Cx;             LR <= PC; PC <= Cx; INT<=1
    `define CALL 8'h2B    // 跳到副程式;        CALL Cx;             LR PC; PC<=PC+Cx       
    `define RET  8'h2C    // 返回;            RET;                 PC <= LR
    `define IRET 8'h2D    // 中斷返回;        IRET;                 PC <= LR; INT<=0
    // 堆疊指令    
    `define PUSH  8'h30    // 推入word;        PUSH Ra;             SP-=4;[SP]<=Ra;
    `define POP   8'h31    // 彈出 word;        POP Ra;             Ra = [SP];     SP+=4;
    `define PUSHB 8'h32    // 推入 byte;        PUSHB Ra;             SP--;[SP]<=Ra; (byte)
    `define POPB  8'h33    // 彈出 byte;        POPB Ra;             Ra<=[SP]; SP++;(byte)
    // 記憶體讀取寫入的寬度代碼
    `define INT32 2'b11
    `define INT24 2'b10
    `define INT16 2'b01
    `define BYTE  2'b00

    task memReadStart(input [31:0] addr, input w1); begin // 讀取記憶體 Word
       mar = addr;     // read(m[addr])
       m_rw = 1;     // 讀取模式:read 
       m_en = 1;     // 啟動讀取
       m_w1 = w1;
    end    endtask

    task memReadEnd(output [31:0] data); begin // 讀取記憶體完成,取得資料
       mdr = dbus; // 取得記憶體傳回的 dbus = m[addr]
       data = mdr; // 傳回資料
       m_en = 0; // 讀取完畢
    end    endtask

    task memWriteStart(input [31:0] addr, input [31:0] data, input w1); begin // addr:寫入位址, data:寫入資料
       mar = addr;    // write(m[addr], data)
       mdr = data;
       m_rw = 0;    // 寫入模式:write
       m_en = 1;     // 啟動寫入
       m_w1  = w1;
    end    endtask

    task memWriteEnd; begin // 寫入記憶體完成
       m_en = 0; // 寫入完畢
    end endtask

    task regSet(input [3:0] i, input [31:0] data); begin
        if (i!=0) R[i] = data;
    end endtask

    always @(posedge clock) begin
        if (reset) begin
            `PC = 0;
            tick = 0;
            R[0] = 0;
            `LR = -1;
          end
        else begin
            tick = tick+1;
            m_en = 0;
            case (tick)    
                // 指令擷取階段 Tick 1..3 : memory.read(m[PC])
                1:    begin  // Tick 1:將 PC 丟到位址匯流排上
                    memReadStart(`PC, `INT32);
                    ipc  = `PC;
                    `PC = `PC+4;
                end
                2:    begin  // Tick 2:ir = m[PC]
                    memReadEnd(ir); // IR = dbus = m[PC]
                    {op,ra,rb,rc,cx12} = ir;
                    cx24 = ir[23:0];
                    cx16 = ir[15:0];
                    cx5  = ir[4:0];
                end
                3:    begin  // Tick 3:取出暫存器 R[ra], R[rb], R[rc] 內容
                    scx12 = cx12; // 取出 cx12 並轉為 32 位元有號數
                    scx16 = cx16; // 取出 cx16 並轉為 32 位元有號數
                    scx24 = cx24; // 取出 cx24 並轉為 32 位元有號數
                    A = R[ra];
                    B = R[rb];
                    C = R[rc];
                end
                // 指令執行階段 Tick 4..6 : execute(IR)
                4:    begin 
                    case (op) // 解讀 OP: Tick 4
                         // 載入儲存指令
                        `LD:  memReadStart(B+scx16, `INT32);
                        `ST:  memWriteStart(B+scx16, A, `INT32); // 寫入 A=R[ra] 到 B+cx 位址中
                        `LDB: memReadStart(B+scx16, `BYTE);
                        `STB: memWriteStart(B+scx16, A, `BYTE); // 寫入 A=R[ra] 到 B+cx 位址中
                        `LDR: memReadStart(B+C, `INT32);
                        `STR: memWriteStart(B+C, A, `INT32);
                        `LBR: memReadStart(B+C, `BYTE);
                        `SBR: memWriteStart(B+C, A, `BYTE);
                        `LDI: R[ra] = B+scx16;
                         // 運算指令
                        `CMP: begin `N = (A-B < 0);`Z = (A-B == 0); end
                        `MOV: regSet(ra, B); // R[ra] = B;
                        `ADD: regSet(ra, B+C);
                        `SUB: regSet(ra, B-C);
                        `MUL: regSet(ra, B*C);
                        `DIV: regSet(ra, B/C);
                        `AND: regSet(ra, B&C);
                        `OR:  regSet(ra, B|C);
                        `XOR: regSet(ra, B^C);
                        `SHL: regSet(ra, B<<cx5);
                        `SHR: regSet(ra, B>>cx5);
                        // 跳躍指令
                        `JMP: `PC = `PC+scx24;
                        `JEQ: if (`Z) `PC=`PC+scx24;
                        `JNE: if (!`Z) `PC=`PC+scx24;
                        `JLT: if (`N && !`Z) `PC=`PC+scx24;
                        `JGT: if (!`N && `Z) `PC=`PC+scx24;
                        `JLE: if (`N || `Z) `PC=`PC+scx24;
                        `JGE: if (!`N || `Z) `PC=`PC+scx24;
                        `SWI: begin `LR = `PC; `PC = scx24; `I = 1'b1;  end
                        `CALL:begin `LR = `PC; `PC = `PC + scx24;  end
                        `RET: begin `PC = `LR;  end
                        `IRET:begin `PC = `LR;`I = 1'b0; end
                        // 堆疊指令    
                        `PUSH:begin `SP = `SP-4; memWriteStart(`SP, A, `INT32); end
                        `POP: begin memReadStart(`SP, `INT32); `SP = `SP + 4; end
                        `PUSHB:begin `SP = `SP-1; memWriteStart(`SP, A, `BYTE); end
                        `POPB:begin memReadStart(`SP, `BYTE); `SP = `SP+1; end
                    endcase
                end
                5:    begin // 解讀 OP: Tick 5
                    case (op)
                        `LD, `LDB, `LDR, `LBR: memReadEnd(R[ra]); // 讀取記憶體完成
                        `ST, `STB, `STR, `SBR: memWriteEnd(); // 寫入記憶體完成
                    endcase
                end                
                6:    begin // 解讀 OP: Tick 6; 列印
                    $display("%4dns %8x : %8x R[%02d]=%-d", $stime, ipc, ir, ra, R[ra]);
                    if (op==`RET && `PC < 0) begin
                        $display("RET to PC < 0, finished!");
                        $finish;
                    end
                    tick = 0;
                end
            endcase
        end
        pc = `PC;
    end
endmodule

module memory0(input clock, reset, en, rw, input [1:0] m_w1, 
                input [31:0] abus, dbus_in, output [31:0] dbus_out);
reg [7:0] m [0:130];
reg [31:0] data;

integer i;
initial begin
    $readmemh("cpu0s.hex", m);
    for (i=0; i < 128; i=i+4) begin
        if (m[i] !== 8'hx)
            $display("%8x: %8x", i, {m[i], m[i+1], m[i+2], m[i+3]});
    end
end

    always @(clock or abus or en or rw or dbus_in) 
    begin
        if (abus >=0 && abus <= 127) begin
            if (en == 1 && rw == 0) begin // r_w==0:write
                data = dbus_in;
                case (m_w1)
                    `BYTE:  {m[abus]} = dbus_in[7:0];
                    `INT16: {m[abus], m[abus+1] } = dbus_in[15:0];
                    `INT24: {m[abus], m[abus+1], m[abus+2]} = dbus_in[24:0];
                    `INT32: {m[abus], m[abus+1], m[abus+2], m[abus+3]} = dbus_in;
                endcase
            end
            else if (en == 1 && rw == 1) // r_w==1:read
                data = {m[abus], m[abus+1], m[abus+2], m[abus+3]};
            else
                data = 32'hZZZZZZZZ;
        end else
            data = 32'hZZZZZZZZ;
    end
    assign dbus_out = data;
endmodule

module main;
reg clock, reset;
wire [2:0] tick;
wire [31:0] pc, ir, mar, mdr, dbus;
wire m_en, m_rw;
wire [1:0] m_w1;

cpu0 cpu(.clock(clock), .reset(reset), .pc(pc), .tick(tick), .ir(ir),
.mar(mar), .mdr(mdr), .dbus(dbus), .m_en(m_en), .m_rw(m_rw), .m_w1(m_w1));

memory0 mem(.clock(clock), .reset(reset), .en(m_en), .rw(m_rw), .m_w1(m_w1), 
.abus(mar), .dbus_in(mdr), .dbus_out(dbus));

initial
begin
  clock = 0;
  reset = 1;
  #20 reset = 0;
  #10000 $finish;
end

always #10 clock=clock+1;

endmodule

執行結果

D:\oc\cpu0>iverilog cpu0s.v -o cpu0s

D:\oc\cpu0>vvp cpu0s
WARNING: cpu0s.v:206: $readmemh(cpu0s.hex): Not enough words in the file for the
 requested range [0:130].
00000000: 001f0024
00000004: 002f001c
00000008: 003f0020
0000000c: 0840000a
00000010: 13221000
00000014: 13332000
00000018: 10240000
0000001c: 21fffff0
00000020: 2c000000
00000024: 00000000
00000028: 00000001
0000002c: 00000000
 130ns 00000000 : 001f0024 R[01]=1
 250ns 00000004 : 002f001c R[02]=0
 370ns 00000008 : 003f0020 R[03]=0
 490ns 0000000c : 0840000a R[04]=10
 610ns 00000010 : 13221000 R[02]=1
 730ns 00000014 : 13332000 R[03]=1
 850ns 00000018 : 10240000 R[02]=1
 970ns 0000001c : 21fffff0 R[15]=16
1090ns 00000010 : 13221000 R[02]=2
1210ns 00000014 : 13332000 R[03]=3
1330ns 00000018 : 10240000 R[02]=2
1450ns 0000001c : 21fffff0 R[15]=16
1570ns 00000010 : 13221000 R[02]=3
1690ns 00000014 : 13332000 R[03]=6
1810ns 00000018 : 10240000 R[02]=3
1930ns 0000001c : 21fffff0 R[15]=16
2050ns 00000010 : 13221000 R[02]=4
2170ns 00000014 : 13332000 R[03]=10
2290ns 00000018 : 10240000 R[02]=4
2410ns 0000001c : 21fffff0 R[15]=16
2530ns 00000010 : 13221000 R[02]=5
2650ns 00000014 : 13332000 R[03]=15
2770ns 00000018 : 10240000 R[02]=5
2890ns 0000001c : 21fffff0 R[15]=16
3010ns 00000010 : 13221000 R[02]=6
3130ns 00000014 : 13332000 R[03]=21
3250ns 00000018 : 10240000 R[02]=6
3370ns 0000001c : 21fffff0 R[15]=16
3490ns 00000010 : 13221000 R[02]=7
3610ns 00000014 : 13332000 R[03]=28
3730ns 00000018 : 10240000 R[02]=7
3850ns 0000001c : 21fffff0 R[15]=16
3970ns 00000010 : 13221000 R[02]=8
4090ns 00000014 : 13332000 R[03]=36
4210ns 00000018 : 10240000 R[02]=8
4330ns 0000001c : 21fffff0 R[15]=16
4450ns 00000010 : 13221000 R[02]=9
4570ns 00000014 : 13332000 R[03]=45
4690ns 00000018 : 10240000 R[02]=9
4810ns 0000001c : 21fffff0 R[15]=16
4930ns 00000010 : 13221000 R[02]=10
5050ns 00000014 : 13332000 R[03]=55
5170ns 00000018 : 10240000 R[02]=10
5290ns 0000001c : 21fffff0 R[15]=32
5410ns 00000020 : 2c000000 R[00]=0
RET to PC < 0, finished!

Facebook

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License