FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

EmacsのVerilogモードで、AutoConnectビット幅を制御する(基本編)

Emacsには標準でVerliog-modeというものが付いており、Verilog-HDLのいろんな部分を自動生成してくれる。

僕が最も気に入っている機能は、サブモジュールを自動的にインスタンス化してくれる機能だ。

   pulsar2_top
     #(/*AUTOINSTPARAM*/)
   pulsar2_top_0
     (/*AUTOINST*/

このように、AUTOINST, AUTOINSTPARAMという文字を記述しておき、C-c C-aとすると、

   pulsar2_top
     #(/*AUTOINSTPARAM*/)
   pulsar2_top_0
     (/*AUTOINST*/
      // Outputs
      .IF_MARADDR          (IF_MARADDR[31:0]),
      .IF_MARBURST         (IF_MARBURST[1:0]),
      .IF_MARCACHE         (IF_MARCACHE[3:0]),
      .IF_MARID                (IF_MARID[axi_id_width-1:0]),
      .IF_MARLEN           (IF_MARLEN[7:0]),
      .IF_MARLOCK          (IF_MARLOCK),
      .IF_MARPROT          (IF_MARPROT[2:0]),
      .IF_MARQOS           (IF_MARQOS[3:0]),
      .IF_MARSIZE          (IF_MARSIZE[2:0]),
      .IF_MARVALID         (IF_MARVALID),
      .IF_MRREADY          (IF_MRREADY),
      .RN_BUSY             (RN_BUSY),
      .RN_CTRL_0           (RN_CTRL_0[`INST_CTRL_B]),
      .RN_CTRL_1           (RN_CTRL_1[`INST_CTRL_B]),
      .RN_CTRL_2           (RN_CTRL_2[`INST_CTRL_B]),
      .RN_CTRL_3           (RN_CTRL_3[`INST_CTRL_B]),
...

このように、自動的にポートを生成してくれる。このポート線は、デフォルトだとポート信号名と同じ名前になっているのだが、正規表現を記述することで、任意の信号線名に切り替えることが可能だ。 これは、AUTO_TEMPLATE識別子によって可能だ。例えば、

    /* mips_ctrl AUTO_TEMPLATE "\([0-9]+\)$" (
     .IF_INST_DEC (id0_inst_dec_w_@[]),
     .\(IF_INST_CTRL\) (id0_inst_ctrl_w_@[]),
     .\(IF_INST_TYPE\) (id0_inst_type_w_@[]),
     .\(IF_DST_ADDR\)  (id0_dst_addr_w_@[]),
     .\(IF_R1_ADDR\)   (id0_r1_addr_w_@[]),
     .\(IF_R2_ADDR\)   (id0_r2_addr_w_@[]),
     .\(IF_IMM\)       (id0_imm_w_@[]),
     )*/
    mips_ctrl
      mips_ctrl_0
        (/*AUTOINST*/
         // Outputs
         .IF_INST_TYPE                  (id0_inst_type_w_0[`INST_TYPE_W-1:0]), // Templated
         .IF_INST_CTRL                  (id0_inst_ctrl_w_0[`INST_CTRL_W-1:0]), // Templated
         .IF_DST_ADDR                   (id0_dst_addr_w_0[`REGADDR_W-1:0]), // Templated
         .IF_R1_ADDR                    (id0_r1_addr_w_0[`REGADDR_W-1:0]), // Templated
         .IF_R2_ADDR                    (id0_r2_addr_w_0[`REGADDR_W-1:0]), // Templated
         .IF_IMM                        (id0_imm_w_0[`WORD_B]),  // Templated
         // Inputs
         .IF_INST_DEC                   (id0_inst_dec_w_0[`INST_MAX-1:0])); // Templated

このように書くことにより、IF_INST_CTRLに接続されている信号線名がid0_inst_ctrl_w_0に変換されている。正規表現を記述することができ、@は、インスタンスの最後に識別されている名前に相当する。 ここでは、

"\([0-9]+\)$"

に相当し、mips_ctrl_0 の "0"の部分に相当する。

さて、これを使えば、ポートに接続される信号をさまざまに制御可能だが、例えば、3つのインスタンスから入ってくる信号を並べてポートに挿入するためにはどうしたらいいんだろう。 つまり、やりたいことは、XilinxのCrossbarインスタンスに対して、

module (
   .A ({signal2[2:0], signal1[2:0], signal0[2:0]}),
   .B ({en2, en1, en0})
);

というような接続を正規表現で記載したい。ここで考えなければいけないのは、ビット幅をどのように記述するか、ということだ。

現在のビット幅を、3分割する

Verilog-Modeでは、自分のポートのビット幅は、vl-widthで取得できる。ただし、問題なのはこいつが、文字列として生成されていることだ。だから、このビット幅を3分割したければ、(string-to-integer)を利用しなければならない。

(string-to-number vl-width)

ビット幅が3であれば、(分割後は1ビットになるので、)ビット幅を表示しない。

これも結局、lispのif文で記載している。

(if (not (= 3 (string-to-number vl-width))) (concat \\"[\\" (...

この結果、長いが、1ポートを記述する正規表現は以下で生成することになる。

{axi_dw_\1@"(if (not (= 3 (string-to-number vl-width))) (concat \\"[\\" (concat (number-to-string (- (/ (string-to-number vl-width) 3) 1)) \\":0]\\")))"}

Verilog-modeの性質上、1行に書かなければならないため面倒だが、実際には、以下のようになる。

(if (not (= 3 (string-to-number vl-width))) 
    (concat \\"[\\" 
            (concat (number-to-string (- (/ (string-to-number vl-width) 3) 1)) 
                    \\":0]\\"))
)

このようにすると、

module (
  .axi_dw_awdata ({axi_dw_awdata[31:0}),
  .axi_dw_awready ({axi_dw_awready}),
...
);

というものが生成されるため、これを3ポート文並べる。長い!

    .s_axi_\(.*\) ({axi_dw_\1@"(if (not (= 3 (string-to-number vl-width))) (concat \\"[\\" (concat (number-to-string (- (/ (string-to-number vl-width) 3) 1)) \\":0]\\")))", axi_dr_\1@"(if (not (= 3 (string-to-number vl-width))) (concat \\"[\\" (concat (number-to-string (- (/ (string-to-number vl-width) 3) 1)) \\":0]\\")))", axi_if_\1@"(if (not (= 3 (string-to-number vl-width))) (concat \\"[\\" (concat (number-to-string (- (/ (string-to-number vl-width) 3) 1)) \\":0]\\")))"}),

これで、以下のようなポートが生成できるようになった。

   pulsar1_connect
     #(/*AUTOINSTPARAM*/)
   pulsar1_connect_0
     (/*AUTOINST*/
      // Outputs
      .s_axi_awready            ({axi_dw_awready, axi_dr_awready, axi_if_awready}), // Templated
      .s_axi_wready         ({axi_dw_wready, axi_dr_wready, axi_if_wready}), // Templated
      .s_axi_bid            ({axi_dw_bid[3:0], axi_dr_bid[3:0], axi_if_bid[3:0]}), // Templated
      .s_axi_bresp          ({axi_dw_bresp[1:0], axi_dr_bresp[1:0], axi_if_bresp[1:0]}), // Templated
      .s_axi_bvalid         ({axi_dw_bvalid, axi_dr_bvalid, axi_if_bvalid}), // Templated
      .s_axi_arready            ({axi_dw_arready, axi_dr_arready, axi_if_arready}), // Templated
      .s_axi_rid            ({axi_dw_rid[3:0], axi_dr_rid[3:0], axi_if_rid[3:0]}), // Templated
      .s_axi_rdata          ({axi_dw_rdata[127:0], axi_dr_rdata[127:0], axi_if_rdata[127:0]}), // Templated
      .s_axi_rresp          ({axi_dw_rresp[1:0], axi_dr_rresp[1:0], axi_if_rresp[1:0]}), // Templated
      .s_axi_rlast          ({axi_dw_rlast, axi_dr_rlast, axi_if_rlast}), // Templated
      .s_axi_rvalid         ({axi_dw_rvalid, axi_dr_rvalid, axi_if_rvalid}), // Templated

3つのポートを接続して、並べている。それぞれ、AXIのDW,DR,IFのポートを接続して、Crossbarに突っ込んだ形だ。