2012年7月23日月曜日

FPGA FMトランスミッタ: オーディオ信号発生器

内部変調用のオーディオ信号発生器は、DDSで作ります。このDDSは、オプション指定で所要リソースがかわります。24ビット出力にして、余った乗算器を無駄遣いさせる事にしました。

2012.7.23補足
DDSのクロックは152kHzで動作させるべきでしょうが、何も考えずに9.728MHzで動作させていたことに気がつきました。結果的には、サンプルレート変換を行っている形ですが、ナイキスト周波数未満で使う分には問題ないように思っています。


オーディオ信号発生器の仕様
チューナなどのセパレーション調整に使えそうな信号を想定しています。

内部変調の動作モード

内部変調レベル

内部変調周波数

実測値
コーデックのアナログ出力で測定してみました。

0dBFS 100Hz

0dBFS 1kHz

af_osc.v

module af_osc(
  af_l_o, af_r_o, clk, reset, af_set
  );

  output [23:0] af_l_o;
  output [23:0] af_r_o;
  input clk;  // 64fs
  input reset;
  input [6:0] af_set;

  reg [23:0] af_l_o;
  reg [23:0] af_r_o;
  reg [5:0] state64;
  wire [23:0] sine;
  wire [23:0] pinc_in;
  wire [23:0] afosc_out;
  wire [1:0] OSC_MODE;
  wire [1:0] LEVEL_SETTING;
  wire [2:0] FREQ_SETTING;
  wire latch_out;

  // State counter
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        state64 <= 0;
      else if (state64 == 63)
        state64 <= 0;
      else
        state64 <= state64 + 1;
    end

  assign latch_out = (state64 == 0);
  assign OSC_MODE = af_set[6:5];
  assign LEVEL_SETTING = af_set[4:3];
  assign FREQ_SETTING = af_set[2:0];
  assign pinc_in = dds_setting(FREQ_SETTING);
  assign afosc_out = level_set(sine, LEVEL_SETTING);

  // output level setting
  function [23:0] level_set;
    input [23:0] audio_input;
    input [1:0] setting;
    case (setting)
      0: level_set = 0;
      1: level_set = {{4{audio_input[23]}}, audio_input[23:4]};
      2: level_set = {audio_input[23], audio_input[23:1]};
      3: level_set = audio_input;
      default: level_set = 0;
    endcase
  endfunction

  // dds_setting
  function [23:0] dds_setting;
    input [2:0] addr;
    case (addr)
      0: dds_setting = 24'h000056;
      1: dds_setting = 24'h0000AC;
      2: dds_setting = 24'h0002B2;
      3: dds_setting = 24'h0006BD;
      4: dds_setting = 24'h0021AF;
      5: dds_setting = 24'h003287;
      6: dds_setting = 24'h00435E;
      7: dds_setting = 24'h00650D;
      default: dds_setting = 24'h0006BD;
    endcase
  endfunction

  //  output data register
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        begin
          af_l_o <= 0;
          af_r_o <= 0;
        end
      else if (latch_out)
        begin
          case (OSC_MODE)
            0:  begin 
                af_l_o <= afosc_out;
                af_r_o <= afosc_out;
              end

            1:  begin 
                af_l_o <= afosc_out;
                af_r_o <= 0;
              end

            2:  begin 
                af_l_o <= 0;
                af_r_o <= afosc_out;
              end
  
            3:  begin 
                af_l_o <= afosc_out;
                af_r_o <= ~afosc_out + 1;
              end
  
            default:  begin
                af_l_o <= afosc_out;
                af_r_o <= afosc_out;
              end
          endcase
        end
    end

  // dds
  af_osc_dds af_osc_dds (
    .clk(clk),
    .pinc_in(pinc_in), // input [23 : 0] pinc_in
    .sine(sine)); // output [23 : 0] sine

endmodule

DDS Compiler設定画面1

DDS Compiler設定画面2

DDS Compiler設定画面3

DDS Compiler設定画面4

DDS Compiler IP Symbol

2012年7月21日土曜日

FPGA FMトランスミッタ: フィルタのインパルス応答をみる

デジタルフィルタのデバッグを行うには、インパルス応答を確認するのがわかりやすい。ということで、まずは正常動作する15kHzローパスフィルタで試してみました。

テストベンチ
ISEが自動生成してくれるテストベンチに少し手を入れたものです。
動作としては、動作スタート後100nsでリセット解除し、入力256サンプル分までLチャンネルを 0 とし、その後に1サンプルのみ24ビット正の最大値 7FFFFF(インパルス)としています。
青字部分はデータをファイルとして書き出すための記述で、ISimでこのシミュレーションを実行するとファイル DATAOUT.txt ができます。$fdisplayには、フォーマットに関する引数を指定でき、 %d: 10進表記、%h: 16進表記、%o: 8進表記、%b: 2進表記です。詳しくは、参考書を見て下さい。

module lpf15k_tb;

  parameter CYCLE = 12*512;
  parameter STROBE = 12*512*0.8;
  integer dataout;

  // Inputs
  reg [23:0] l15k_l_i;
  reg [23:0] l15k_r_i;
  reg clk;
  reg reset;
  reg l15k_thru;

  // Outputs
  wire [23:0] l15k_l_o;
  wire [23:0] l15k_r_o;

  // Instantiate the Unit Under Test (UUT)
  lpf15k uut (
    .l15k_l_i(l15k_l_i), 
    .l15k_r_i(l15k_r_i), 
    .l15k_l_o(l15k_l_o), 
    .l15k_r_o(l15k_r_o), 
    .clk(clk), 
    .reset(reset), 
    .l15k_thru(l15k_thru)
  );

  initial
    begin
      dataout = $fopen("DATAOUT.txt");
      $fdisplay (dataout, "time, l15k_l_i, l15k_l_o");

      // Initialize Inputs
      l15k_l_i = 0;
      l15k_r_i = 0;
      clk = 0;
      reset = 1;
      l15k_thru = 0;

      #100;
      reset = 0;

      #(CYCLE*256);
      l15k_l_i = 24'h7FFFFF;

      #(CYCLE);
      l15k_l_i = 0;

      #(CYCLE*256);
      l15k_l_i = 24'h800000;
    end

  always #6 //77.824MHz
    begin
      clk = ~clk;
    end

  always
    begin
      #STROBE
      $fdisplay (dataout, "%0d,%h,%h", $time, l15k_l_i, l15k_l_o);
      #(CYCLE-STROBE); 
    end
    
endmodule

シミュレーション結果
シミュレーションにより作られたファイルの内容を示します。

time   l15k_l_i   l15k_l_o
4915  000000  000000
11059  000000  000000
17203  000000  000000
23347  000000  000000
29491  000000  000000

(中略)

1565491  000000  000000
1571635  000000  000000
1577779  7fffff  000000
1583923  000000  000000
1590067  000000  00000f
1596211  000000  00000f
1602355  000000  000000
1608499  000000  fffff0
1614643  000000  ffffe0
(以下略)

FIRフィルタでは、インパルス応答はフィルタ係数がそのまま出てきますから両者を比較して一致していれば正しく設計できているということになります。グラフは、フィルタ係数データとインパルス応答(正規化しています)を重ね合わせたもので一致していることがわかります。



参考資料
・鳥海佳孝 田原追仁治 横溝憲治(2002) 実用HDLサンプル記述集 CQ出版社

2012年7月15日日曜日

FPGA FMトランスミッタ: ステレオ変調器

ステレオ変調器の構成
ここでは、入力切替器とマトリクス回路と38kHzサブキャリアとの平衡変調器、19kHzステレオパイロット信号付加を行います。


マトリクス回路周りの信号処理の詳細を前回の記事から再掲します。



サブチャンネルの処理
サブチャンネルは、入力L/Rの差信号と38kHzサブキャリアの乗算で生成します。ここでサブキャリアの波形を考えます。サンプリング周波数152kHzのとき38kHzの正弦波は、わずか4点で表現することになります。下表を見るとサブキャリアがとる値としては0, 1, -1しかありませんから乗算といいながら、L-R信号をオンオフするか極性反転だけで済むことになります。



これを見て、スイッチング方式のステレオ変調そのものだなと感じました。


入力レベルと変調度の関係
アナログ入力端子があるので入力レベル -6dBFS で変調度100%を基本とすることにしました。

(1)ステレオモード
L/Rチャンネルが-6dBFSのとき、Lチャンネル45%・Rチャンネル45%・ステレオパイロット信号10%で合計100%変調。

(2)モノラルモード
Lチャンネルが-6dBFSのとき100%変調。



ステレオパイロット信号の発生
ステレオパイロット信号は、38kHzサブキャリアの半分の周波数で同位相です。振幅は、ステレオモードで入力信号0dBFSのときメインチャンネルの18分の1とします。ただし、処理の過程でシフト演算により振幅を1/16にすることを考慮して、下表では22ビットフルスケールの16/18としました。




特性実測値
まず、コーデックのアナログ出力でのコンポジット信号の波形です。Lチャンネルのみ1kHz 0dBFSでステレオパイロット信号は断にしています。
教科書によると、波形の交点が0基線(図では上にずらしています)と一致し一直線上にあるのでメインチャンネルとサブチャンネルで位相差がなく(基部にうねりがあるので)レベル差だけがある状態とのこと。


コンポジット信号波形(1kHz 0dBFS Lチャンネル)

2012.08.10追記 波形の うねり は、コーデックの周波数特性が原因でした。下図のとおり、コンポジット信号の最高周波数53kHz付近(0.35 fs)では、なんと-3dBも下がっています。これではコンポジット信号を扱うことができません。

CS4270の周波数特性


パイロット信号のレベルを確認するためスペクトルを確認しました。
(1)は、1kHz 0dBFS L/Rチャンネル同相です。メインチャンネル-12.53dB、パイロット-37.9dBで25.4dBのレベル差があります。メインチャンネル180%変調相当レベル、パイロット10%のレベル差は25.1dBなので概ね妥当です。L/Rチャンネル同相のためサブキャリアはありません。

(1)コンポジット信号(1kHz 0dBFS L/Rチャンネル)

(2)は、1kHz 0dBFS Lチャンネルのみです。メインチャンネル-18.55dB、パイロット -37.9dB、サブキャリアのうち37kHzは-24.51dB、39kHzは-25.4dBでした。サブキャリアの37/39kHzでレベル差0.9dBもあります。
(3)(4)は、周波数を1kHz/125Hzと切替え、さらにFFTのサンプル数を変えてみたところです。低域側/高域側のレベルは、(3)1kHz: -24.51/-25.4dBで+0.89dB、125Hz: -25.28dB/-24.56dBで-0.72dB。(4)1kHz: -25.93dB/-24.73dBで-1.2dB、125Hz: -24.7dB/-25.95dBで+1.25dB。
測定系の誤差もあるようですが、よくわからなくなってきました。普通に考えればアナログ周りの周波数特性差でしょうか。
(2)コンポジット信号(1kHz 0dBFS Lチャンネル)


(3)サブキャリア(赤1kHz/黒125Hzの比較 FFT:65536)

(4)サブキャリア(赤1kHz/黒125Hzの比較 FFT:8192)

(5)は、モノラルモード1kHz 0dBFSで200%変調相当レベルで-11.6dBでした。(1)のメインチャンネルの180%変調相当レベルより0.93dB高く、概ね設計通りであることが確認できました。

(5)モノラルモード(1kHz 0dBFS Lチャンネル)


mpx.v
module mpx(
  mpx_l0_i, mpx_r0_i, mpx_l1_i, mpx_r1_i, mpx_o,
  clk, reset, mpx_set, mpx_pilot_o
  );

  input [23:0] mpx_l0_i;
  input [23:0] mpx_r0_i;
  input [23:0] mpx_l1_i;
  input [23:0] mpx_r1_i;
  output [23:0] mpx_o;
  input clk;  // 64fs
  input reset;
  input [4:0] mpx_set;
  output [23:0] mpx_pilot_o;

  reg [8:0] state512;
  wire [5:0] state64;
  wire [1:0] sub38k_addr;
  wire [2:0] pilot19k_addr;
  wire [21:0] pilot19k;
  wire latch_out;
  wire INPUT_SEL;
  wire MPX_MODE;
  wire MAIN_CHANNEL_CONT;
  wire SUB_CHANNEL_CONT;
  wire PILOT_CONT;
  
  reg [23:0] input_reg_l;
  reg [23:0] input_reg_r;
  reg [24:0] main_channel;
  reg [24:0] sub_channel;
  reg [24:0] diff;
  reg [25:0] stereo_composite;
  reg [25:0] stereo_composite2;
  reg [23:0] mpx_o;
  reg [23:0] mpx_pilot_o;

  // State counter
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        state512 <= 0;
      else if (state512 == 511)
        state512 <= 0;
      else
        state512 <= state512 + 1;
    end

  assign state64 = state512[5:0];
  assign sub38k_addr = state512[7:6];
  assign pilot19k_addr = state512[8:6];
  assign pilot19k = pilot19k_coe(pilot19k_addr);
  assign latch_out = (state64 == 9);
  assign INPUT_SEL = mpx_set[4];  // Selector 0:IN0, 1: IN1
  assign MPX_MODE = mpx_set[3];  // MPX mode  0: Stereo, 1: Mono
  assign MAIN_CHANNEL_CONT = mpx_set[2];  // Main Channel 0: On, 1: Off
  assign SUB_CHANNEL_CONT = mpx_set[1];  // Sub Channel 0: On, 1: Off
  assign PILOT_CONT = mpx_set[0];  // Stereo Pilot 0: On, 1: Off

  // Stereo MPX
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        begin
          input_reg_l <= 0;
          input_reg_r <= 0;
          main_channel <= 0;
          sub_channel <= 0;
          diff <= 0;
          stereo_composite <= 0;
          stereo_composite2 <= 0;
          diff <= 0;
        end

      else
        begin
          case (state64)
            0:  begin 
                input_reg_l <= (INPUT_SEL)? mpx_l1_i : mpx_l0_i;
                input_reg_r <= (INPUT_SEL)? mpx_r1_i : mpx_r0_i;
              end

            1:  begin 
                // L + R
                main_channel <= (MAIN_CHANNEL_CONT) ? 0
                          : {input_reg_l[23], input_reg_l}
                            + {input_reg_r[23], input_reg_r};
                // L - R
                diff <= (SUB_CHANNEL_CONT) ? 0
                      : {input_reg_l[23], input_reg_l}
                      + ~{input_reg_r[23], input_reg_r} + 1;  
              end

            2:  begin 
                case (sub38k_addr)
                  1:  sub_channel <= diff;
                  3:  sub_channel <= ~diff + 1;
                  default:  sub_channel <= 0;
                endcase
              end

            3:  begin 
                // MAIN + SUB
                stereo_composite <= {main_channel[24], main_channel}
                              + {sub_channel[24], sub_channel};
              end

            4:  begin 
                // MAIN + SUB + PILOT
                if (~PILOT_CONT)
                  stereo_composite <= stereo_composite
                              + {{5{pilot19k[21]}}, pilot19k[21:1]};
              end

            5:  begin 
                // stereo_composite2 = stereo_composite * (115/128) -> (-0.93dB)
                stereo_composite2 <= {stereo_composite[25], stereo_composite[25:1]}
                              + {{2{stereo_composite[25]}}, stereo_composite[25:2]};
              end

            6:  begin 
                stereo_composite2 <= stereo_composite2
                              + {{3{stereo_composite[25]}}, stereo_composite[25:3]};
              end

            7:  begin 
                stereo_composite2 <= stereo_composite2
                              + {{6{stereo_composite[25]}}, stereo_composite[25:6]};
              end

            8:  begin 
                stereo_composite2 <= stereo_composite2
                              + {{7{stereo_composite[25]}}, stereo_composite[25:7]};
              end

            default:  begin
                stereo_composite2 <= 0;
              end

          endcase
        end
    end

  //  output data register
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        begin
          mpx_o <= 0;
          mpx_pilot_o <= 0;
        end
      else if (latch_out)
        begin
          mpx_o <= (MPX_MODE) ? input_reg_l
                : stereo_composite2[25:1];
          mpx_pilot_o <= {pilot19k, 2'b0};
        end
    end

  // stereo pilot 19kHz coef
  function [21:0] pilot19k_coe;
    input [2:0] addr;

    case (addr)
      0: pilot19k_coe = 0;
      1: pilot19k_coe = 22'h141CFD;
      2: pilot19k_coe = 22'h1C71C6;
      3: pilot19k_coe = 22'h141CFD;
      4: pilot19k_coe = 0;
      5: pilot19k_coe = 22'h2BE303;
      6: pilot19k_coe = 22'h238E3A;
      7: pilot19k_coe = 22'h2BE303;
      default: pilot19k_coe = 0;
    endcase

  endfunction

endmodule

ステートカウンタ state64 に従って、処理を進めるように書いていますが、FPGAなのにこういうマイコン的な書き方でいいのか?と疑問を感じています。まあ動いているので結果オーライでしょうか。

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