TD4をSFLで書いてみた。

CPUの創りかた」のTD4を、SFL(Wikipedia)で記述してみた。
組み込まれたエンジニアデザインウェーブPDP-11互換CPUを作る連載(pdf)を読んでると、SFLを勉強したくなるですよ。

良かったところ

  • cygwinを使った実行環境をhttp://www.ip-arch.jp/からダウンロードすれば、すぐにSFLを試せる。サンプルもたくさんついてくる。
  • verilogと比べて、クロックとリセット書かなくていいので、記述量が少なくなって楽です。

verilogとの違いが気になる。

  • 左項に連結やビット指定が使えない。
  • verilogみたく、don't care を掛けない。

verilogのコードをベースにしたので、SFLっぽくないです。
コードがSFL準拠でない。sfl2vlでしか試していない。正式なSFLだとmoduleの中で+とか使えないはず。


LEDちかちか と ラーメンタイマが動作することはシミュレーションまで動作確認しました。
テストベンチについては、後で書く予定
せっかくSFLで書いたので、パイプライン化するのがいいかも。

/*
 SFL version of TD4 (Toriaezu Dousa surudakeno 4bit CPU)

 (C)Copyright Kohei Sekine 2009.
 URL: http://d.hatena.ne.jp/eggman/
 */

declare td4 {
    input  pin<4>;
    input  min<8>;
    output pout<4>;
    output madr<4>;
}

module td4 {
    input  pin<4>;      // input port
    input  min<8>;      // rom data

    output pout<4>;     // output port
    output madr<4>;     // rom address

    reg_wr a<4>;        // A register
    reg_wr b<4>;        // B register
    reg_wr p<4>;        // output port register
    reg_wr pc<4>;       // program counter;
    reg_wr c;           // carry flag

    sel op<8>;          // <7:4>:opcode, <3:0>:operand
    sel data_sel<2>;    // ALU input data select
    sel sel_out<4>;     // ALU input data
    sel alu_out<5>;     // ALU output
    sel load<4>;        // load select
    sel n_sel_a, n_sel_b, n_sel_p, n_sel_j;     // alias of load select

    // wire
    op = min;           // read op from ROM
    madr = pc;          // output address of ROM
    pout = p;           // assgin output port

    n_sel_a = load<3>;  // select A register
    n_sel_b = load<2>;  // select B register
    n_sel_p = load<1>;  // select output port register 
    n_sel_j = load<0>;  // select jump

    // decode
    any{
        op<7:4>==0x0 : par{ data_sel = 0b00; load = 0b0111; }   //ADD A,Im
        op<7:4>==0x1 : par{ data_sel = 0b01; load = 0b0111; }   //MOV A,B
        op<7:4>==0x2 : par{ data_sel = 0b10; load = 0b0111; }   //IN  A
        op<7:4>==0x3 : par{ data_sel = 0b11; load = 0b0111; }   //MOV A,Im
        op<7:4>==0x4 : par{ data_sel = 0b00; load = 0b1011; }   //MOV B,A
        op<7:4>==0x5 : par{ data_sel = 0b01; load = 0b1011; }   //ADD B,Im
        op<7:4>==0x6 : par{ data_sel = 0b10; load = 0b1011; }   //IN  B
        op<7:4>==0x7 : par{ data_sel = 0b11; load = 0b1011; }   //MOV B,Im
        op<7:4>==0x9 : par{ data_sel = 0b01; load = 0b1101; }   //OUT B
        op<7:4>==0xb : par{ data_sel = 0b11; load = 0b1101; }   //OUT Im
        op<7:4>==0xe : any{                                     //JNC Im
            c==0b0 :   par{ data_sel = 0b11; load = 0b1110; }       // jump
            c==0b1 :                         load = 0b1111;         // don't jump
                       }
        op<7:4>==0xf : par{ data_sel = 0b11; load = 0b1110; }   //JMP Im
    }

    // data selecter for ALU input
    any{
        data_sel==0b00 : sel_out = a;           // select A register
        data_sel==0b01 : sel_out = b;           // select B register
        data_sel==0b10 : sel_out = pin;         // select input port
        data_sel==0b11 : sel_out = 0x0;         // select Im
    }

    // ALU
    alu_out = (0b0||sel_out) + (0b0||op<3:0>);

    // set register
    any{
        n_sel_a==0b0 : a := alu_out<3:0>;       // set A register
        n_sel_b==0b0 : b := alu_out<3:0>;       // set B register
        n_sel_p==0b0 : p := alu_out<3:0>;       // set output port register
    }
    any{
        n_sel_j==0b0 : pc := alu_out<3:0>;      // jump
        n_sel_j==0b1 : pc := pc + 0b0001;       // increment PC
    }
    c := alu_out<4>;                            // set carry flag
}

参考:MAX II マイクロキットで「CPUの創りかた」 - Sim's blogverilog版TD4をベースにしました。