FPGAでTD4（４bitCPU）を作ってみた

CPUの創りかた自体は発売されたとき買って読んだんだけどCPU作らずにずっときてしまったので、正月休みを利用してつくってみた。
<a  href="https://www.amazon.co.jp/gp/product/4839909865/ref=as_li_tf_il?ie=UTF8&camp=247&creative=1211&creativeASIN=4839909865&linkCode=as2&tag=oskimura-22"><img border="0" src="http://ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=4839909865&Format=_SL110_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=oskimura-22" ></a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=oskimura-22&l=as2&o=9&a=4839909865" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />


FPGAやverilog自体に慣れるために
<a  href="https://www.amazon.co.jp/gp/product/478983137X/ref=as_li_tf_il?ie=UTF8&camp=247&creative=1211&creativeASIN=478983137X&linkCode=as2&tag=oskimura-22"><img border="0" src="http://ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=478983137X&Format=_SL110_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=oskimura-22" ></a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=oskimura-22&l=as2&o=9&a=478983137X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />


ココらへんをよんでた。
最初はまったくわからなかったが、回路図を表現しているとわかってから結構、読み書きできるようになった。
あと、verilogは回路図に近い表記だけじゃなくて、わりと振る舞いや手続き言語っぽくかけるところもあるので、マルチプレクサなんかはすごく楽にかける。

そもそも自分は組み込みプログラミングの知識があったのピンがどうこうの話は慣れたらとく苦労はしなかった。（設定など面倒くさいが・・・）

あと、テストベンチの書き方がよくわからなかったがある程度わかるようになってからはモジュール毎に書くようになった。
今回さくせいしたCPUもモジュール毎にテストベンチを書いてある。

最初はicarus verilogとGTKWave上でテストベンチでシミュレーション上で動かして、動いたのを確認してから実機上（DE0)でも動かしてみた。
DE0は50MHZだったので動きを追うには分周する必要があったけど分周したらちゃんと動いた。

組んでみて一番苦労したのがデコーダーで、全命令に対して書くので面倒くさかった。テストベンチもふくめて。

TD4はかなり命令の少ないCPUでかつverilogなので、まともな規模で配線とかだと相当面倒くさいことが目に浮かぶ。
あと最初はリセットを付けてなかったので初期化されずCPUがうごかなかった。

verilogは結構わかるようになったんだけど、それでもalwaysやfunctionの使いわけがよくわからなかったりはまだしているのでもっと勉強したい。

あとはもうちょっとまともなCPUつくったり、NESをFPGAでつくってみたいなと思った。

リポジトリはここです。
https://github.com/oskimura/TD4


## 解説

### ALU
TD4のALUは足し算しかない。シンプルなALU。

````
module alu(ain, bin, c, out);
    input wire [3:0] ain,bin;
    output c;
    output wire [3:0] out;

    assign {c,out} = ain + bin;
endmodule
````

### カウンター

PC(プログラムカウンター)、クロックカウンターにジャンプ命令ようにロードを追加

````
module counter(reset, in,ld,clk,out);
    input reset;
    input [3:0] in;
    input ld;
    input clk;
    output [3:0] out;

    reg [3:0] cnt=4'b0000;
    always @(posedge clk or negedge reset) begin
        if (!reset) begin 
            cnt<= #1 4'b0000;
        end
        else if(ld==0) begin
            cnt<= #1 in;
        end 
        else begin
            cnt <= #1 cnt+1;
        end
    end
    assign out = cnt;

endmodule
````

#### レジスタ

リセットや入力がない限り同じ値を出力し続ける 

````
module register(reset,in,ld,clk,out);
  input reset;
  input [3:0] in;
  input ld;
  input clk;
  output [3:0] out;

  reg [3:0] mem=4'b000;
  always @(posedge clk or negedge reset) begin
    if(!reset) begin
       mem =  #1 4'b0000;
    end else if(!ld)begin
      mem =  #1 in;
    end
  end 
  assign out = mem;

endmodule
````

#### ロム
本にのっているラーメンタイマーのプログラムが実装されたROM。
1Hzで16秒カウンタを5回繰り返す

`````
module rom(addr,out);
    input [3:0] addr;
    output reg [7:0] out;
   
    always @(addr) begin
        case (addr) 
            4'b0000: out=8'b10110111; // out 5

//1           
            4'b0001: out=8'b00000001; // add a 1
            //2
            4'b0010: out=8'b11100001; // jnc 1

//3
            4'b0011: out=8'b00000001; // add a 1
            //4
            4'b0100: out=8'b11100011; // jnc 3

//5
            4'b0101: out=8'b10110110; // out 6
            //6
            4'b0110: out=8'b00000001; // add a 1
            //7
            4'b0111: out=8'b11100110; // jnc 6

//8
            4'b1000: out=8'b00000001; // add a 1
            //9
            4'b1001: out=8'b11101000; // jnc 8

            //10
            4'b1010: out=8'b10110000; // out 0
            //11
            4'b1011: out=8'b10110100; // out 4
            //12
            4'b1100: out=8'b00000001; // add a 1
            //13
            4'b1101: out=8'b11101010; // jnc 10

//14
            4'b1110: out=8'b10111000; // out 8
            //15
            4'b1111: out=8'b11111111; // jmp 15
        endcase
    end
 
endmodule
````

### デコーダー
アセンブラの命令をひたすら回路出力に変換していく

`````verilog
module decorder(op,c,sel,ld);
    input [3:0] op;
    input c;
    output [1:0] sel;
    output [3:0] ld;

    reg [3:0] load;
    reg [1:0] select;

    always @(op or c) begin      
        case (op) 
            // ADD,A,Im
            4'b0000 : load<=4'b1110; 
            // MOV A,B
            4'b0001: load=4'b1110; 
            // IN A
            4'b0010: load=4'b1110;
            // MOV A,Im
            4'b0011: load=4'b1110;
            
            // MOV B,A
            4'b0100: load=4'b1101; 
            // MOV B,Im
            4'b0101: load=4'b1101; 
            // IN B
            4'b0110: load=4'b1101; 
            // MOV B,Im
            4'b0111: load=4'b1101; 

            // OUT B
            4'b1001: load=4'b1011; 
            // OUT Im
            4'b1011: load=4'b1011; 

            // JMC
            4'b1110:  if(c==0) begin 
                        // JMC(C=0) 
                            load=4'b0111; 
                        end 
                        else begin
                        // JMC(C=1) 
                            load=4'b1111; 
                        end
            // JMP
            4'b1111: load=4'b0111;
        endcase
    end

    always @(op) begin      
        case (op) 
            // ADD,A,Im
            4'b0000 : select<=2'b00;
            // MOV A,B
            4'b0001: select=2'b01;
            // IN A
            4'b0010:  select=2'b10;
            // MOV A,Im
            4'b0011: select=2'b11;
            
            // MOV B,A
            4'b0100:  select=2'b00;
            // MOV B,Im
            4'b0101:  select=2'b01;
            // IN B
            4'b0110:  select=2'b10;
            // MOV B,Im
            4'b0111: select=2'b11;

            // OUT B
            4'b1001: select=2'b10;
            // OUT Im
            4'b1011: select=2'b11;

            // JMC
            4'b1110:  if(c==0) begin 
                            // JMC(C=0) 
                            select=2'b11;
                        end 
                        else begin
                            // JMC(C=1) 
                            select=2'bxx;
                        end
            // JMP
            4'b1111: select=2'b11;
    endcase end
    
    assign ld=load;
    assign sel=select;
endmodule
````

#### TD4
これまでのモジュールを繋いだもの。キャリーレジスタは忘れていたので後から追加。

````
module td4(reset,clk,inp,outp);
    input reset;
    input clk;
    input [3:0] inp;
    output [3:0] outp;

    wire [3:0] ch0,ch1,ch2,ch3;
    wire [3:0] addr;

    wire [3:0] a;

    wire [7:0] memdata;

    wire [3:0] alu_out;
    wire [3:0] ld;

    reg cflag;
    wire cflga_r;

    wire [1:0] sel;

    assign outp = ch2;
    wire [3:0] op,im;

    mem mem_u(.addr(addr),.out(memdata));
    assign ch3=4'b0000;

    register areg(.reset(reset),.in(alu_out),.ld(ld[0]),.clk(clk),.out(ch0));
    register breg(.reset(reset),.in(alu_out),.ld(ld[1]),.clk(clk),.out(ch1));
    register creg(.reset(reset),.in(alu_out),.ld(ld[2]),.clk(clk),.out(outp));
    counter pc(.reset(reset),.in(alu_out),.ld(ld[3]),.clk(clk),.out(addr));

    assign op = memdata[7:4];
    assign im = memdata[3:0];
    dataselector dataselector_u(.sel(sel),.c0(ch0),.c1(ch1),.c2(inp),.c3(ch3),.y(a));
   
    alu alu_u(.ain(a), .bin(im), .c(cflag_r), .out(alu_out));

    always @(posedge clk  or reset) begin
        if (!reset) begin
            cflag = 1'b0;
        end
        else begin   
            cflag = cflag_r;
        end
    end

    decorder decorder_u(.op(op),.c(cflag),.sel(sel),.ld(ld));


endmodule
````