2012年7月14日土曜日

FPGA FMトランスミッタ: クロック信号供給方法の見直し

プリエンファシスモジュールに供給するクロックを下げる
前回の記事で、プリエンファシスモジュールを使用すると最大動作周波数が140MHzから110MHzに低下してしまう事象が見られました。システムクロックは77.824MHzですから問題とはなりませんが、サンプリング周波数152kHzに対して速すぎるシステムクロックは、消費電力や動作周波数の面で無駄なので落としてみてはどうかとアドバイスを受けて試してみることにしました。
結果、プリエンファシスモジュールのクロック周波数をシステムクロックの8分の1に変更したところ最大動作周波数が低下しなくなりました。


クロック信号供給方法の見直し
受け売りですが・・・同期回路のクロックはIC内部のすべてフリップフロップに数10psオーダで同時に供給する必要があります。このため、FPGAにはクロックを供給するための特別な配線リソースが組み込まれています。このリソースをクロック信号に使わせるため、FPGA設計ツールにクロック信号であることを明確に伝える配慮が必要だそうです。

改善点
・ASRCのマスタクロックを codec_if.v モジュール内部のDCMで生成していたが、DCMをトップ階層に変更した。
・外部クロック信号をいったん clock_gen というモジュールに引き込んだうえでFPGA全体のクロック信号にアサインしていたが、DCMのCLK0ピンから出力される77.824MHzをクロックとして使用するよう変更した。ついでに clock_gen の名前は、reset_gen と変更した。

top.v

module top(
  asrc_mclk_o, asrc_bck_o, asrc_lrck_o, asrc_data_i,
  spdif_nonaudio_i, spdif_error_i,
  codec_datar_i, codec_sck_o, codec_mck_o, codec_lrck_o, codec_datas_o,
  clk_i, resetsw_i, n_reset_o, n_sw_i
  );

  output asrc_mclk_o;
  output asrc_bck_o;
  output asrc_lrck_o;
  input asrc_data_i;
  input spdif_nonaudio_i;
  input spdif_error_i;
  input codec_datar_i;
  output codec_sck_o;
  output codec_mck_o;
  output codec_lrck_o;
  output codec_datas_o;
  input clk_i;
  input resetsw_i;
  output n_reset_o;
  input [3:0] n_sw_i;

  wire [23:0] if_l_o;
  wire [23:0] if_r_o;
  wire [23:0] l15k_l_o;
  wire [23:0] l15k_r_o;
  wire [23:0] pe_l_o;
  wire [23:0] pe_r_o;

  wire en;
  wire clk;
  wire clk_9M7;
  wire reset;
  wire if_sel;

  // ASRC/CODEC Interface
  codec_if codec_if(
    .asrc_bck_o(asrc_bck_o),
    .asrc_lrck_o(asrc_lrck_o),
    .asrc_data_i(asrc_data_i),
    .spdif_nonaudio_i(spdif_nonaudio_i),
    .spdif_error_i(spdif_error_i),
    .codec_datar_i(codec_datar_i),
    .codec_sck_o(codec_sck_o),
    .codec_mck_o(codec_mck_o),
    .codec_lrck_o(codec_lrck_o),
    .codec_datas_o(codec_datas_o),
    .if_l_o(if_l_o),
    .if_r_o(if_r_o),
    .if_mon0_i(pe_l_o),
    .if_mon1_i(pe_r_o),
    .en(en),
    .clk(clk),
    .reset(reset),
    .if_sel(n_sw_i[3])
    );

  // Reset Generator
  reset_gen reset_gen(
    .clk(clk),
    .resetsw_i(resetsw_i),
    .reset(reset),
    .n_reset_o(n_reset_o)
    );

  // DCM_SP: 77.824MHz *3/8 => 29.184MHz
  pll78to29 pll78to29 (
    .CLKIN_IN(clk_i),
    .CLKFX_OUT(asrc_mclk_o), 
//    .CLKIN_IBUFG_OUT(CLKIN_IBUFG_OUT), 
    .CLK0_OUT(clk)
    );

  // DCM_SP: 77.824MHz *1/8 => 9.728MHz
  pll78to10 pll78to10 (
    .CLKIN_IN(clk), 
    .CLKDV_OUT(clk_9M7) 
//    .CLK0_OUT(CLK0_OUT)
    );

  // 15kHz FIR LPF
  lpf15k lpf15k(
    .l15k_l_i(if_l_o),
    .l15k_r_i(if_r_o),
    .l15k_l_o(l15k_l_o),
    .l15k_r_o(l15k_r_o),
    .clk(clk),
    .reset(reset),
    .l15k_thru(~n_sw_i[2])
    );

  // Pre-emphasis
  pre_emph pre_emph(
    .pe_l_i(l15k_l_o),
    .pe_r_i(l15k_r_o),
    .pe_l_o(pe_l_o),
    .pe_r_o(pe_r_o),
    .clk(clk_9M7),
    .reset(reset),
    .pe_thru(~n_sw_i[1])
    );

endmodule

DCM_SP (pll78to29) 設定画面1

DCM_SP (pll78to29) 設定画面2

DCM_SP (pll78to29) 設定画面3

DCM_SP (pll78to10) 設定画面1

DCM_SP (pll78to10) 設定画面2

codec_if.v
module codec_if(
  asrc_bck_o, asrc_lrck_o, asrc_data_i,
  spdif_nonaudio_i, spdif_error_i, codec_datar_i, codec_sck_o, codec_mck_o,
  codec_lrck_o, codec_datas_o, if_l_o, if_r_o, if_mon0_i, if_mon1_i,
  en, clk, reset, if_sel
  );

  output asrc_bck_o;  // 64fs
  output asrc_lrck_o;  // fs
  input asrc_data_i;
  input spdif_nonaudio_i;
  input spdif_error_i;
  input codec_datar_i;
  output codec_sck_o;  // 64fs
  output codec_mck_o;  // 64fs
  output codec_lrck_o;  // fs
  output codec_datas_o;
  output [23:0] if_l_o;
  output [23:0] if_r_o;
  input [23:0] if_mon0_i;
  input [23:0] if_mon1_i;
  output en;
  input clk;  // 512fs
  input reset;
  input if_sel;  // 0: ASRC, 1: CODEC

  reg [8:0] div_count;
  wire [5:0] bit_count;
  wire read_latch;
  wire write_latch;
  wire en;

  // Receive Buffer
  reg [23:0] sreg0;  // ASRC L
  reg [23:0] sreg1;  // ASRC R
  reg [23:0] sreg2; // CODEC L
  reg [23:0] sreg3; // CODEC R

  // Send Buffer
  reg [23:0] sreg4;  // Ch0 /L
  reg [23:0] sreg5;  // Ch1 /R

  reg [23:0] if_l_o;
  reg [23:0] if_r_o;
  reg codec_datas_o;
  wire spdif_valid;
  wire bck;


///////////////////////////////////////////////
//  timing generator
///////////////////////////////////////////////

  always @(posedge clk or posedge reset)
    begin
      if (reset)
        div_count <= 0;

      else if (div_count == 511)
        div_count <= 0;
        
      else
        div_count <= div_count + 1;
    end

  assign en = (div_count == 511);
  assign read_latch = (div_count[2:0] == 3);
  assign write_latch = (div_count[2:0] == 0);
  assign bit_count = div_count[8:3];
  assign bck = div_count[2];  // 64fs
  assign asrc_lrck_o = ~div_count[8];  // fs
  assign codec_lrck_o = ~div_count[8];  // fs
  assign asrc_bck_o = bck;
  assign codec_sck_o = bck;
  assign codec_mck_o = bck;

///////////////////////////////////////////////
//  ASRC, CODEC I/F (BITCLOCK_FREQUENCY = 64fs)
// Left-Justified, MSB-First
///////////////////////////////////////////////

  // Serial to Parallel
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        begin
          sreg0 <= 0;
          sreg1 <= 0;
          sreg2 <= 0;
          sreg3 <= 0;
        end

      else if (read_latch)
        begin
          // Left Channel
          if (24 > bit_count)
            begin
              sreg0[23 - bit_count] <= asrc_data_i;
              sreg2[23 - bit_count] <= codec_datar_i;
            end

          // Right Channel
          else if ((bit_count > 31) & (bit_count < (24 + 32)))
            begin
              sreg1[23 - bit_count + 32] <= asrc_data_i;
              sreg3[23 - bit_count + 32] <= codec_datar_i;
            end
        end
    end

  // S/PDIF_valid
  assign spdif_valid = ~(spdif_error_i | spdif_nonaudio_i);

  // Mux
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        begin
          if_l_o <= 0;
          if_r_o <= 0;
        end

      else if (en)
        begin
          // Input Select: CODEC
          if (if_sel)
            begin
              if_l_o <= sreg2;
              if_r_o <= sreg3;
            end

          // Input Select: ASRC
          else
            begin
              if (spdif_valid)
                begin
                  if_l_o <= sreg0;
                  if_r_o <= sreg1;
                end
              else
                begin
                  if_l_o <= 0;
                  if_r_o <= 0;
                end
            end
        end
    end


  // Parallel to Serial
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        begin
          sreg4 <= 0;
          sreg5 <= 0;
        end

      // Send Buffer
      else if (en)
        begin
          sreg4 <= if_mon0_i;
          sreg5 <= if_mon1_i;
        end
    end

  
  always @(posedge clk or posedge reset)
    begin
      if (reset)
          codec_datas_o <= 0;

      else if (write_latch)
        begin
          // Left Channel
          if (24 > bit_count)
            codec_datas_o <= sreg4[23 - bit_count];

          // Right Channel
          else if ((bit_count > 31) & (bit_count < (24 + 32)))
            codec_datas_o <= sreg5[23 - bit_count +32];

          else
            codec_datas_o <= 0;
        end
    end

endmodule


reset_gen.v
module reset_gen(clk, resetsw_i, reset, n_reset_o);

  input clk;
  input resetsw_i;
  output reset;
  output n_reset_o;
  
  // reset signal
  reg [19:0] reset_count;
  reg reset;

  always @(posedge clk or negedge resetsw_i)
    begin
      if (~resetsw_i)
        begin
          reset_count <= 0;
          reset <= 1;
        end

      else if (reset_count == 20'hFFFFF)
        reset <= 0;
        
      else
        reset_count <= reset_count + 1;
    end

  assign n_reset_o = ~reset;

endmodule

0 件のコメント:

コメントを投稿