數字集成電路設計-7-一個簡單cpu的設計,實現,仿真與綜合

引言
經過對OpenRISC近一年的分析與研究,在此過程中我們瞭解了計算機體系結構設計相關的主要概念,重要的技術,和基本思想。我覺的,現在我們有必要練練手了。

本小節,我們將設計一個簡單的cpu,包括ISA的設計,模塊的劃分,RTL實現,編寫asm彙編程序,用modelsim進行仿真,以及用quartusII的綜合。

1,計算器與計算機
我認爲,在EDVAC計算機之前的計算機,都可認爲是計算器。

原因是,馮諾依曼對EDVAC分析時提出了二進制運算和加入存儲部件,而在這之前的計算機是沒有存儲功能的,比如我們要計算(1+2)&(3+4),如果是採用計算器,其運算步驟如下:

a,先用計算器算出1+2的結果3,然後人腦自己記住這個數。

b,再用計算器計算出3+4的結果7,人腦也自己記住這個數。

c,最後用計算器算出3&7的結果3。

如果採用計算機,其運算過程如下:

首先我們需要寫一段程序,假設程序放在程序存儲器的地址0x0處,數據1,2,3,4分別放在數據存儲器的55,56,57,58四個地址。

程序的執行過程如下:

a,將data_mem的0x55處的數據放到r1。

b,將data_mem的0x56處的數據放到r2。

c,執行add r2,r1,結果放在r2裏面。

d,將r2的內容寫入到data_mem的0x60這個地址。

e,將data_mem的0x57處的數據放到r3。

f,將data_mem的0x58處的數據放到r4。

g,執行add r4,r3,結果放在r4裏面。

h,將r4的內容寫入到data_mem的0x61這個地址。

i,將data_mem的0x60處的數據放到r5。

j,將data_mem的0x61處的數據放到r6。

k,執行and r6,r5,結果放在r6裏面。

l,將r6的內容寫入到data_mem的0x62這個地址,最終得到計算結果。


我們可以看出,如果用計算器計算,只需三步就可以完成,但是如果用計算機的話需要12步才能完成。那是不是用計算機的效率低呢?今天計算機的蓬勃發展使答案不言而喻。

原因就是隻要實現寫好程序,用計算機的整個計算過程不用人爲干預。

我想這正是計算機發展的根本原因之所在,就是計算機的出現是對人的很大解放。我們只要按照一定的方式寫好程序,然後交給計算機,計算機會自動完成任務,而我們的手就可以幹些其他的事情了!

2,架構設計
1>整體設計
通過上面的例子,我們可以體會到計算機的好處,下面我們就動手設計一個cpu,來完成1+2的計算。

關於計算機體系結構,我們之前說過的內容已經夠多了。這裏只說明以下幾點:

a,我們採用harvard結構,即,指令和數據的總線是獨立的。

b,流水線,我們暫時不採用流水設計,但是在最後,我給出了五級流水時的數據通路設計框架。

c,關於指令集,由於是學習目的,我們只實現基本的訪存指令,運算指令和分支指令。運算不支持乘除和浮點。關於具體的指令細節,請參考附錄。每條指令爲16-bit。

d,爲了對我們設計的cpu進行仿真和驗證,我們需要設計一個簡單的soc才行,這個soc只包含指令存儲器,cpu內核,數據存儲器。

e,core內總線爲8-bit。這就有一個問題,core外是8-bit,但是分支指令的目的地址爲11-bit,所以如果超過8-bit,就會有問題,暫時還沒解決。

下面是soc的整體架構圖:我們給他取個名字吧,就叫 tiny_soc,小cpu就簡單的稱她爲tiny_core。

2>模塊劃分
cpu core的結構如下:

整個cpu core由數據通路和控制通路和調試單元組成。

其中數據通路包括:

PC的產生模塊:genpc

運算模塊:alu,在alu的前面是對操作數多選一的一個mux。

寄存器堆:regfile模塊

還有棧:stack。

數據通路受控制通路模塊ctrl_path模塊的控制,控制通路負責指令的解碼,併產生對應的控制信號。

調試單元,由於只是學習目的,調試單元最簡化,只輸出當前的PC值和當前的指令內容兩個信息。

3,模塊劃分與接口定義
整體的架構設計完成後,我們就需要進一步的細化了,這時,需要定義具體的模塊名稱,模塊功能,一旦功能確定之後我們就可以確定具體的模塊接口信號了。

如果模塊功能過大,我們需要拆分成更小的模塊,這就是top-down的設計方法。關於設計方法學(top-down,bottom-up),很多資料裏都有介紹,這裏就不再贅述了。

一個完整的工程,和做理論研究不同,需要處理很多實現細節,下面,我們介紹一下,其中比較重要的部分:

1>genpc模塊
這裏面需要考慮三點:上電覆位PC默認值爲多少?正常指令執行時PC如何變化?遇到分支指令時PC如何變化?

關於上電默認值,我們可以通過一個define語句來設定,允許用戶後期修改方便。

關於正常指令的指令,PC是加1還是加2還是加4,這要看指令存儲器的訪問方式,我們的指令存儲器是每個地址放一條指令,每條指令是2個字節(16-bit),所以我們只要PC加1就可以了。

關於遇到分支指令,我們直接將控制通路經過解碼的跳轉地址賦給PC即可。

genpc模塊的C語言僞代碼如下:

genpc module pseudo code
 
if(rst)
{
    pc= boot_addr;
}
else
{
    if(branch)
    {
        pc = branch_target;
    }
    else
    {
        pc = pc +1;
    }
}


2>alu模塊
alu模塊,大家都很熟悉了,是執行單元部件,負責運算指令的運算工作。

這個模塊的輸入信號是有控制通路的解碼出來的操作數和操作碼,輸出信號就是運算結果。

需要說明的是,這個模塊可以完全是組合邏輯電路。

3>rf模塊
register file模塊,從物理上來說,就是一個block ram。

從邏輯上來說,這個模塊是對軟件程序員是透明的,寄存器堆和指令集是軟件和硬件的交互接口。

4>stack
stack(棧),是用來處理分支指令時,存放PC的值的,比如,我們在處理子程序調用時,需要先將當前的PC+1壓棧,等到遇到子程序返回指令時使用。

棧的特點是LIFO(last in first out),這一點與heap(堆)不同。

5>ctrl_path模塊
控制通路負責將genpc模塊產生的地址處的指令進行解碼,併產生對應的操作數,操作碼和控制型號。這部分信號比較多一點。

6>tiny_soc
爲了測試這個cpu內核,我們需要搭一個最小系統,包括指令只讀存儲器insn_rom模塊,裏面存放機器碼。

由於是harvard結構,所以還需要一個數據存儲器ram模塊,相當於內存。

當然,如果想外掛其他I/O外設,我們只需要定義其地址空間即可,需要說明的是I/O外設的地址空間不能與RAM重疊,各個I/O外設之間也不能重疊。

RAM和I/O外設之間可通過一個arbiter與cpu core實現數據交互。

當然,如果存放指令的地方不止一個,也需要一個指令arbiter。

4,RTL實現
在完成模塊劃分,接口定義,仔細分析考慮模塊間時序之後,如果沒有什麼問題,我們就可以進行編碼工作了。

編碼,需要注意的是編碼一定要規範,信號命名,代碼註釋什麼的,儘量要仔細。這裏直接給出RTL代碼(verilog HDL)

按照自上而下的順序依次給出:

1>tiny_soc頂層模塊:soc_top

/*
*
* file name    : soc_top.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
`timescale 1ns / 1ps
 
module soc_top
(
input clk,
input rst
);
 
 
wire          read_e;
wire          write_e;
wire [7:0]     port_addr;
wire [7:0]     core_in;
wire [7:0]     core_out;
wire [15:0] instruction;
wire [10:0] inst_addr;
 
wire [15:0] debug_insn;
wire [10:0] debug_pc;
 
insn_rom insn_rom
(
.clk (clk),
.rst (rst),
.address (inst_addr),
.instruction (instruction)
);
 
core core
(
.clk (clk),
.rst (rst),
 
.read_e (read_e),
.write_e (write_e),
.port_addr (port_addr),
.data_in (core_in),
.data_out (core_out),
.inst_addr (inst_addr),
.instruction (instruction),
.debug_pc (debug_pc),
.debug_insn (debug_insn)
);
 
ram ram
(
.clk (clk),
.rst (rst),
 
.wr (write_e),
.rd (read_e),
.addr (port_addr),
.din (core_out),
.dout(core_in)
);
 
 
    
endmodule


2>指令存儲器:insn_rom

/*
*
* file name    : insn_rom.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
 
 
module insn_rom
(
input clk,
input rst,
input [10:0] address,
output reg [15:0] instruction
);
 
//(* RAM_STYLE="BLOCK" *)
reg [15:0] rom [2047:0];
 
always @(posedge clk)
begin
    if(rst)
        begin
            rom[0] <= 16'h5801;//0:            jmp start
            rom[1] <= 16'h1101;//1:start     mov r1,1
            rom[2] <= 16'h1202;//2:            mov r2,2
            rom[3] <= 16'h3220;//3:            add r2,r1
            rom[4] <= 16'h2237;//4:            str r2,55
            rom[5] <= 16'h5806;//5:            jmp end
            rom[6] <= 16'h5806;//6:end        jmp end*/
        end
    else
        begin
            instruction <= rom[address];
        end
end
 
 
endmodule


3>數據存儲器:ram

*
*
* file name    : ram.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
 
module ram(
    input clk,
    input rst,
    
    input [7:0] din,
    input [7:0] addr,
    output reg [7:0] dout,
    input wr,
    input rd
    );
 
   (* RAM_STYLE="DISTRIBUTED" *)
   
    reg [7:0] ram [255:0];
 
   always @(posedge clk)
    begin
        if(rst)
            begin
                dout <= 8'b0;
                ram[0] = 0;
                ram[1] = 1;
                ram[2] = 2;
                ram[32] = 32;
                ram[64] = 64;
 
            end
        else
            begin
            if (wr)
                ram[addr] <= din;
            else if(rd)
                dout <= ram[addr];
        end
    end
 
endmodule


4>CPU核心:core

/*
*
* file name    : core.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
 
 
module core
(
    input clk,
    input rst,
    output [7:0] port_addr,
    output read_e,
    output write_e,
    input [7:0] data_in,
    output [7:0] data_out,
    output [10:0] inst_addr,
    input [15:0] instruction,
    
    output [10:0] debug_pc,//debug i/f
    output [15:0] debug_insn
);
 
wire z,c;
wire insel;
wire we;
wire [2:0] raa;
wire [2:0] rab;
wire [2:0] wa;
wire [2:0] opalu;
wire [2:0] sh;
wire selpc;
wire ldpc;
wire ldflag;
wire [10:0] ninst_addr;
wire selk;
wire [7:0] KTE;
wire [10:0] stack_addr;
wire wr_en, rd_en;
wire [7:0] imm;
wire selimm;
 
 
 
 
control_path control_path
(
.clk (clk),
.rst (rst),
.instruction (instruction),
.z (z),
.c (c),
.port_addr (port_addr),
.write_e (write_e),
.read_e (read_e),
.insel (insel),
.we (we),
.raa (raa),
.rab (rab),
.wa (wa),
.opalu (opalu),
.sh (sh),
.selpc (selpc),
.ldpc (ldpc),
.ldflag (ldflag),
.naddress (ninst_addr),
.selk (selk),
.KTE (KTE),
.stack_addr (stack_addr),
.wr_en (wr_en),
.rd_en (rd_en),
.imm (imm),
.selimm (selimm)
);
 
 
 
data_path data_path_i
(
.clk (clk),
.rst (rst),
.data_in (data_in),
.insel (insel),
.we (we),
.raa (raa),
.rab (rab),
.wa (wa),
.opalu (opalu),
.sh (sh),
.selpc (selpc),
.selk (selk),
.ldpc (ldpc),
.ldflag (ldflag),
.wr_en (wr_en),
.rd_en (rd_en),
.ninst_addr (ninst_addr),
.kte (KTE),
.imm (imm),
.selimm (selimm),
.data_out (data_out),
.inst_addr (inst_addr),
.stack_addr (stack_addr),
.z (z),
.c (c)
);
 
debug debug
(
.pc_in (inst_addr),
.insn_in (instruction),
 
.pc (debug_pc),
.insn (debug_insn)
);
 
 
endmodule


5>調試單元:debug

/*
*
* file name    : debug.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
 
 
module debug
(
input [10:0] pc_in,
input [15:0] insn_in,
 
output [10:0] pc,
output [15:0] insn
);
 
assign pc = pc_in;
assign insn = insn_in;
 
endmodule


6>控制通路:control_path

/*
*
* file name    : control_path.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
 
 
module control_path
(
    input clk,
    input rst,
    input [15:0] instruction,
    input z,
    input c,
    output reg [7:0] port_addr,
    output reg write_e,
    output reg read_e,
    output reg insel,
    output reg we,
    output reg [2:0] raa,
    output reg [2:0] rab,
    output reg [2:0] wa,
    output reg [2:0] opalu,
    output reg [2:0] sh,
    output reg selpc,
    output reg ldpc,
    output reg ldflag,
    output reg [10:0] naddress,
    output reg selk,
    output reg [7:0] KTE,
    input [10:0] stack_addr,
    output reg wr_en, rd_en,
    output reg [7:0] imm,
    output reg selimm
    );
 
 
parameter fetch=    5'd0;
parameter decode=    5'd1;
 
parameter ldi=        5'd2;
parameter ldm=        5'd3;
parameter stm=        5'd4;
parameter cmp=        5'd5;
parameter add=        5'd6;
parameter sub=        5'd7;
parameter andi=        5'd8;
parameter oor=        5'd9;
parameter xori=        5'd10;
parameter jmp=        5'd11;
parameter jpz=        5'd12;
parameter jnz=        5'd13;
parameter jpc=        5'd14;
parameter jnc=        5'd15;
parameter csr=        5'd16;
parameter ret=        5'd17;
 
parameter adi=        5'd18;
parameter csz=        5'd19;
parameter cnz=        5'd20;
parameter csc=        5'd21;
parameter cnc=        5'd22;
parameter sl0=        5'd23;
parameter sl1=        5'd24;
parameter sr0=        5'd25;
parameter sr1=        5'd26;
parameter rrl=        5'd27;
parameter rrr=        5'd28;
parameter noti=        5'd29;
 
parameter nop=        5'd30;
 
wire [4:0] opcode;
reg [4:0] state;
 
assign opcode=instruction[15:11];
 
always@(posedge clk or posedge rst)
begin
    if (rst)
        begin
            state<=decode;
        end
        
    else
        begin
            case (state)
                fetch: 
                    begin
                        state<=decode;
                    end
 
                decode: 
                    begin
                        if(opcode >=ldi && opcode <=nop)
                            state <= opcode;//state just is the opcode now
                        else
                            state <= nop;
                    end
                    
                default:
                    state<=fetch;
            endcase
        end
        
end    
 
 
always@(*)
begin
        port_addr<=0;
        write_e<=0;
        read_e<=0;
        insel<=0;
        we<=0;
        raa<=0;
        rab<=0;
        wa<=0;
        opalu<=4;
        sh<=4;
        selpc<=0;
        ldpc<=1;
        ldflag<=0;
        naddress<=0;
        selk<=0;
        KTE<=0;
        wr_en<=0;
        rd_en<=0;
        imm<=0;
        selimm<=0;
        
        case (state)
            fetch:     begin
                        ldpc<=0;
                    end
 
            decode: begin
                        ldpc<=0;
                        if (opcode==stm)
                            begin
                                raa<=instruction[10:8];
                                port_addr<=instruction[7:0];
                            end
                        else if (opcode==ldm)
                            begin
                                wa<=instruction[10:8];
                                port_addr<=instruction[7:0];
                            end
                        else if (opcode==ret)
                            begin
                                rd_en<=1;
                            end
                    end
                
            ldi:    begin
                        selk<=1;
                        KTE<=instruction[7:0];
                        we<=1;
                        wa<=instruction[10:8];
                    end
                    
            ldm:    begin
                        wa<=instruction[10:8];
                        we<=1;
                        read_e<=1;
                        port_addr<=instruction[7:0];
                    end
                    
            stm:    begin
                        raa<=instruction[10:8];
                        write_e<=1;
                        port_addr<=instruction[7:0];
                    end
                    
            cmp:    begin
                        ldflag<=1;
                        raa<=instruction[10:8];
                        rab<=instruction[7:5];
                        opalu<=6;
                    end
                    
            add:    begin
                        raa<=instruction[10:8];
                        rab<=instruction[7:5];
                        wa<=instruction[10:8];
                        insel<=1;
                        opalu<=5;
                        we<=1;
                    end
                    
            sub:    begin
                        raa<=instruction[10:8];
                        rab<=instruction[7:5];
                        wa<=instruction[10:8];
                        insel<=1;
                        opalu<=6;
                        we<=1;
                    end
                    
            andi:    begin
                        raa<=instruction[10:8];
                        rab<=instruction[7:5];
                        wa<=instruction[10:8];
                        insel<=1;
                        opalu<=1;
                        we<=1;
                    end
                    
            oor:    begin
                        raa<=instruction[10:8];
                        rab<=instruction[7:5];
                        wa<=instruction[10:8];
                        insel<=1;
                        opalu<=3;
                        we<=1;
                    end
                    
            xori:    begin
                        raa<=instruction[10:8];
                        rab<=instruction[7:5];
                        wa<=instruction[10:8];
                        insel<=1;
                        opalu<=2;
                        we<=1;
                    end
                    
            jmp:    begin
                        naddress<=instruction[10:0];
                        selpc<=1;
                        ldpc<=1;
                    end
                    
            jpz:        if (z)
                        begin
                            naddress<=instruction[10:0];
                            selpc<=1;
                            ldpc<=1;
                        end
                                        
            jnz:        if (!z)
                            begin
                                naddress<=instruction[10:0];
                                selpc<=1;
                                ldpc<=1;
                            end
                        
                    
            jpc:    if (c)
                            begin
                                naddress<=instruction[10:0];
                                selpc<=1;
                                ldpc<=1;
                            end
                        
                    
            jnc:    if (!c)
                            begin
                                naddress<=instruction[10:0];
                                selpc<=1;
                                ldpc<=1;
                            end
                            
            csr:    begin
                        naddress<=instruction[10:0];
                        selpc<=1;
                        ldpc<=1;
                        wr_en<=1;
                    end
                    
            ret:    begin
                        naddress<=stack_addr;
                        selpc<=1;
                        ldpc<=1;
                    end
                    
            adi:    begin
                        raa<=instruction[10:8];
                        wa<=instruction[10:8];
                        imm<=instruction[7:0];
                        selimm<=1;
                        insel<=1;
                        opalu<=5;
                        we<=1;
                    end    
                    
            csz:    if (z)
                        begin
                            naddress<=instruction[10:0];
                            selpc<=1;
                            ldpc<=1;
                            wr_en<=1;
                        end
                        
            cnz:    if (!z)
                        begin
                            naddress<=instruction[10:0];
                            selpc<=1;
                            ldpc<=1;
                            wr_en<=1;
                        end
                        
            csc:    if (c)
                        begin
                            naddress<=instruction[10:0];
                            selpc<=1;
                            ldpc<=1;
                            wr_en<=1;
                        end
                        
            cnc:    if (!c)
                        begin
                            naddress<=instruction[10:0];
                            selpc<=1;
                            ldpc<=1;
                            wr_en<=1;
                        end
            
            sl0:    begin    
                        raa<=instruction[10:8];
                        wa<=instruction[10:8];
                        insel<=1;
                        sh<=0;
                        we<=1;
                    end
                    
            sl1:    begin    
                        raa<=instruction[10:8];
                        wa<=instruction[10:8];
                        insel<=1;
                        sh<=5;
                        we<=1;
                    end
                    
            sr0:    begin    
                        raa<=instruction[10:8];
                        wa<=instruction[10:8];
                        insel<=1;
                        sh<=2;
                        we<=1;
                    end
                    
            sr1:    begin    
                        raa<=instruction[10:8];
                        wa<=instruction[10:8];
                        insel<=1;
                        sh<=6;
                        we<=1;
                    end    
 
            rrl:    begin    
                        raa<=instruction[10:8];
                        wa<=instruction[10:8];
                        insel<=1;
                        sh<=1;
                        we<=1;
                    end                        
                    
            rrr:    begin    
                        raa<=instruction[10:8];
                        wa<=instruction[10:8];
                        insel<=1;
                        sh<=3;
                        we<=1;
                    end
                    
            noti:    begin
                        raa<=instruction[10:8];
                        wa<=instruction[10:8];
                        insel<=1;
                        opalu<=0;
                        we<=1;
                    end
 
            nop:    begin
                        opalu<=4;
                    end    
        endcase
end
 
 
endmodule


7>數據通路:data_path

/*
*
* file name    : data_path.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
 
 
module data_path
(
    input clk,
    input rst,
    input [7:0] data_in,
    input insel,
    input we,
    input [2:0] raa,
    input [2:0] rab,
    input [2:0] wa,
    input [2:0] opalu,
    input [2:0] sh,
    input selpc,
    input selk,
    input ldpc,
    input ldflag,
    input wr_en, rd_en,
    input [10:0] ninst_addr,
    input [7:0] kte,
    input [7:0] imm,
    input selimm,
    output [7:0] data_out,
    output [10:0] inst_addr,
    output [10:0] stack_addr,
    output  z,c
);
 
wire [7:0] regmux, muximm;
wire [7:0] portA, portB;
 
wire [7:0] shiftout;
 
assign data_out=shiftout;
 
 
genpc genpc
(
.clk (clk),
.rst (rst),
 
.ldpc (ldpc),
.selpc (selpc),
.ninst_addr (ninst_addr),
 
.inst_addr (inst_addr)
);
 
 
alu_mux alu_mux
(
.selimm (selimm),
.imm (imm),
.portB (portB),
 
.muximm (muximm)
);
 
alu alu
(
.a (portA),
.b (muximm),
.opalu (opalu),
.ldflag (ldflag),
.zero (z),
.carry (c),
.sh (sh),
.dshift (shiftout)
);
 
 
stack stack
(
.clk (clk),
.rst (rst),
.wr_en (wr_en),
.rd_en (rd_en),
.din (inst_addr),
.dout (stack_addr)
);
 
 
regfile_mux regfile_mux
(
.insel (insel),
.selk (selk),
.shiftout (shiftout),
.kte (kte),
.data_in (data_in),
 
.regmux (regmux)
);
 
regfile regfile
(
.datain (regmux),
.clk (clk),
.we (we),
.wa (wa),
.raa (raa),
.rab (rab),
.porta (portA),
.portb (portB)
);
 
 
 
endmodule


8>程序計算器:genpc

/*
*
* file name    : genpc.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
`define boot_addr 0     //boot address after power on
 
module genpc
(
input clk,
input rst,
 
input ldpc,
input selpc,
input [10:0] ninst_addr,
 
output [10:0] inst_addr
);
 
reg [10:0] pc;
 
assign inst_addr=pc;
 
always@(posedge clk or posedge rst)
begin
    if (rst)
        pc <=`boot_addr;
    else
        if (ldpc)    
            if(selpc)
                pc<=ninst_addr;
            else
                pc<=pc+1;
end
 
 
endmodule
 
 


9>運算單元:alu ,alu_mux


/*
*
* file name    : alu_mux.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
 
 
module alu_mux
(
input selimm,
input [7:0] imm,
input [7:0] portB,
 
output [7:0] muximm
);
 
assign muximm = selimm? imm : portB;//result : imm if ldi insn,portb if ldm insn
 
 
endmodule


/*
*
* file name    : alu.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
 
 
module alu
(
input [7:0] a,
input [7:0] b,
input [2:0] opalu,
input ldflag,
output zero,
output carry,
input [2:0] sh,
output reg [7:0] dshift
);
 
reg [7:0] resu;
 
assign zero=ldflag?(resu==0):1'b0;
 
assign carry=ldflag?(a<b):1'b0;
 
always@(*)
    case (opalu)
        0: resu <= ~a;
        1: resu <= a & b;
        2: resu <= a ^ b;
        3: resu <= a | b;
        4: resu <= a;
        5: resu <= a + b;
        6: resu <= a - b;
        default: resu <= a + 1;
    endcase
    
    
always@*
        case (sh)
            0: dshift <= {resu[6:0], 1'b0};
            1: dshift <= {resu[6:0], resu[7]};
            2: dshift <= {1'b0, resu[7:1]};
            3: dshift <= {resu[0], resu[7:1]};
            4: dshift <= resu;
            5: dshift <= {resu[6:0], 1'b1};
            6: dshift <= {1'b1, resu[7:1]};
            default: dshift <= resu;
        endcase
 
endmodule


10>寄存器堆:regfile,regfile_mux

/*
*
* file name    : regfile_mux.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
 
 
module regfile_mux
(
input insel,
input selk,
input [7:0] shiftout,
input [7:0] kte,
input [7:0] data_in,
 
output [7:0] regmux
);
 
wire [7:0] muxkte;
 
assign regmux=insel? shiftout : muxkte;
assign muxkte=selk? kte : data_in;
 
 
endmodule


/*
*
* file name    : regfile.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
 
 
module regfile(
    input [7:0] datain,
    input clk, we,
    input [2:0] wa,
    input [2:0] raa,
    input [2:0] rab,
    output [7:0] porta,
    output [7:0] portb
    );
 
 
reg [7:0] mem [7:0];//r0 ~r255
 
always@(posedge clk)
begin
    mem[0]<=0;//r0 always is 0
        
    if(we)
        mem[wa]<=datain;
end     
    
assign porta=mem[raa];
assign portb=mem[rab];
 
 
endmodule


11>棧:stack

/*
*
* file name    : stack.v
* author    : Rill
* date        : 2013-08-11
*
*/
 
 
 
module stack(
    input clk,
     input rst,
    input wr_en,
    input rd_en,
    input [10:0] din,
    output [10:0] dout
    );
 
 
   (* RAM_STYLE="DISTRIBUTED" *)
reg [3:0] addr;
reg [10:0] ram [15:0];
 
assign dout = ram[addr] +1;
 
always@(posedge clk)
begin
    if (rst)
        addr<=0;
    else 
        begin 
            if (wr_en==0 && rd_en==1)  //leer
                if (addr>0)
                    addr<=addr-1;
            if (wr_en==1 && rd_en==0)  //guardar
                if (addr<15)
                    addr<=addr+1;
        end
end
        
always @(posedge clk)
begin
    if (wr_en)
        ram[addr] <= din;
end
 
endmodule


5,modelsim仿真
1>編寫testbench
要進行仿真,需要編寫對應的testbench,由於咱們這個cpu很簡單,所以測試激勵也很簡單,代碼如下:

/*
*
* file name    : tiny_soc_tb.v
* atthor    : Rill
* date        : 2013-08-11
*
*/
 
`timescale 1ns / 1ps
 
module tiny_soc_tb;
 
 
reg clk;
reg rst;
 
 
always #5 clk = ~clk;
 
initial
begin
    #0 
        clk = 0;
        rst = 0;
    #15 
        rst = 1;
    #10 
        rst = 0;
        
    #1000
        $stop;
end
 
soc_top soc_top
(
.clk (clk),
.rst (rst)
);
 
 
endmodule


2>編寫彙編代碼及手動彙編
當然還要編寫其彙編代碼,如下:

然後我們要手動彙編成機器碼,指令集都是自己定義的,所以是沒有現成的compiler,只能手動彙編了,還好手動彙編要比手動反彙編輕鬆多了(之前,我們手動反彙編過OpenRISC的啓動代碼)。

彙編完成後,我們將機器碼放到指令存儲器裏,如下,共七條指令。

3>仿真結果
完成上面的工作之後,我們就可以用仿真工具進行仿真了,下面是我用modelsim仿真的結果。

從波形可以清晰的看出七條指令的執行過程。在運算完成後拉高write_e信號並將1+2的運算結果3寫到了ram地址是55(0x37)的地方。

4>代碼仿真覆蓋率
由於這個測試彙編代碼很短,我們有必要了解一下這個仿真激勵運行過程中到底測試了多少代碼,下面是code coverage報告截圖。

需要說明的是上面的截圖並不完整,下面是完整的代碼覆蓋率報告。

Coverage Report Summary Data by file
 
File: E:/work/tiny_soc/bench/tiny_soc_tb.v
    Enabled Coverage        Active      Hits % Covered
    ----------------        ------      ---- ---------
    Stmts                       11        11     100.0
    Branches                     0         0     100.0
    Conditions                   0         0     100.0
    Fec Conditions               0         0     100.0
    Expressions                  0         0     100.0
    Fec Expressions              0         0     100.0
 
File: E:/work/tiny_soc/rtl/core/alu.v
    Enabled Coverage        Active      Hits % Covered
    ----------------        ------      ---- ---------
    Stmts                       20         7      35.0
    Branches                    16         3      18.7
    Conditions                   0         0     100.0
    Fec Conditions               0         0     100.0
    Expressions                  8         2      25.0
    Fec Expressions              8         2      25.0
 
File: E:/work/tiny_soc/rtl/core/alu_mux.v
    Enabled Coverage        Active      Hits % Covered
    ----------------        ------      ---- ---------
    Stmts                        1         1     100.0
    Branches                     2         1      50.0
    Conditions                   0         0     100.0
    Fec Conditions               0         0     100.0
    Expressions                  0         0     100.0
    Fec Expressions              0         0     100.0
 
File: E:/work/tiny_soc/rtl/core/control_path.v
    Enabled Coverage        Active      Hits % Covered
    ----------------        ------      ---- ---------
    Stmts                      160        48      30.0
    Branches                    60        18      30.0
    Conditions                   3         1      33.3
    Fec Conditions               4         2      50.0
    Expressions                  0         0     100.0
    Fec Expressions              0         0     100.0
 
File: E:/work/tiny_soc/rtl/core/genpc.v
    Enabled Coverage        Active      Hits % Covered
    ----------------        ------      ---- ---------
    Stmts                        4         4     100.0
    Branches                     6         6     100.0
    Conditions                   0         0     100.0
    Fec Conditions               0         0     100.0
    Expressions                  0         0     100.0
    Fec Expressions              0         0     100.0
 
File: E:/work/tiny_soc/rtl/core/regfile.v
    Enabled Coverage        Active      Hits % Covered
    ----------------        ------      ---- ---------
    Stmts                        4         4     100.0
    Branches                     2         2     100.0
    Conditions                   0         0     100.0
    Fec Conditions               0         0     100.0
    Expressions                  0         0     100.0
    Fec Expressions              0         0     100.0
 
File: E:/work/tiny_soc/rtl/core/regfile_mux.v
    Enabled Coverage        Active      Hits % Covered
    ----------------        ------      ---- ---------
    Stmts                        1         1     100.0
    Branches                     4         4     100.0
    Conditions                   0         0     100.0
    Fec Conditions               0         0     100.0
    Expressions                  0         0     100.0
    Fec Expressions              0         0     100.0
 
File: E:/work/tiny_soc/rtl/core/stack.v
    Enabled Coverage        Active      Hits % Covered
    ----------------        ------      ---- ---------
    Stmts                        7         4      57.1
    Branches                    12         5      41.6
    Conditions                   6         2      33.3
    Fec Conditions               8         2      25.0
    Expressions                  0         0     100.0
    Fec Expressions              0         0     100.0
 
File: E:/work/tiny_soc/rtl/insn_rom.v
    Enabled Coverage        Active      Hits % Covered
    ----------------        ------      ---- ---------
    Stmts                        9         9     100.0
    Branches                     2         2     100.0
    Conditions                   0         0     100.0
    Fec Conditions               0         0     100.0
    Expressions                  0         0     100.0
    Fec Expressions              0         0     100.0
 
File: E:/work/tiny_soc/rtl/ram.v
    Enabled Coverage        Active      Hits % Covered
    ----------------        ------      ---- ---------
    Stmts                        9         8      88.8
    Branches                     6         5      83.3
    Conditions                   0         0     100.0
    Fec Conditions               0         0     100.0
    Expressions                  0         0     100.0
    Fec Expressions              0         0     100.0


6,綜合
完成了仿真之後,我們就可以綜合一下,在FPGA上驗證了,下面是我用quartusII 12.0綜合的結果。

soc_top模塊:

core模塊:

data_path模塊:

7,改進及優化
咱們設計現在這個小cpu core的目的是爲了學習,在於瞭解cpu內部的運行機制,與軟件的交互過程,沒有考慮性能,功耗,面積等問題。

下面,我給出了採用五級流水的數據通路的模塊劃分和接口定義,如下所示,感興趣的兄弟可以試着實現她。

8,future work
1,pipeline,將core流水化

2,增加乘法,除法指令。咱們前面介紹過4-bit乘法器和除法器的設計:http://blog.csdn.net/rill_zhen/article/details/9700155

3,fix bug

4,設計tiny_core的assembler,這樣就不用手動彙編了。

9,小結
本小節,我們試着設計了一個簡單的cpu,並編寫了可綜合的外部基本模塊和不可綜合的測試激勵模塊,然後用quartusII進行了綜合,最後還給出了優化後的數據通路結構。

我想,通過本小節的內容,應該對cpu的工作機制和軟件與硬件的關係有了更深入的感覺了。

需要說明的是,我的水平有限,再加上時間很短(兩週),所以這裏面肯定還有很多問題,和需要改進的地方,如果感興趣,可以把代碼down下來,先用modelsim仿真一下,然後再寫個I/O controller,比如uart的控制器,掛到數據總線上,如果PC機能收到tiny_soc發出的“hello world”,那是多麼令人激動的一件事情啊。

附錄:tiny_core的指令集
說明:

rrr:代表寄存器。

aaa和bbb分別代表寄存器A和寄存器B,其中寄存器A是目的寄存器。

iiiiiiii:代表8-bit的立即數。

xxx:代表忽略。

ttt:11 bit的跳轉/分支地址。

此外,這個指令集其實很早就開始弄了,最早是去年上半年開始的,現在是最新版本。


————————————————
版權聲明:本文爲CSDN博主「Rill」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/rill_zhen/article/details/9924007

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章