FPGA 版 FM 音源 (68) -- FPGA 版 EG (12)

ステレオ 16 ビット・シリアル入力 DA 用送信モジュール「i2s_tx()」の Verilog ソースを下に示します。
"i2s_tx.v"

//
// i2s_tx.v : I2S Transmitter module
//            only support 16-bit data word 
//            / 32 clock per frame format (std. format)
//            ( I2S format not supported )
//
// 2017/01/19 Created by pcm1723
//

module i2s_tx(
// Input
  reset, // async reset
  m_clk, // master clock
  clk_128fs_en, // 128fs clock enable
  lrck,  // LR clock input
  da,    // 16-bit parallel audio data
// Output  
  sclk,  // serial clock
  sdat,  // serial data
  lrck_out, // LR clock output
  da_out    // latched data out for monitor
);
// Input port
  input         reset;
  input         m_clk;
  input         clk_128fs_en;
  input         lrck;
  input  [15:0] da;
// Output port
  output        sclk;
  output        sdat;
  output        lrck_out;
  output [15:0] da_out;
//
  reg    [15:0] da_out;
  
// wires
  wire          sclk_en;
  wire          load;
  
// regs
  reg    [15:0] sr;      // shift regeiser
  reg    [1:0]  cnt;     // 1/4 clock divider / bit counter
  reg           lrck_z1; // 1 clock delay of lrck

  assign sclk_en  = ( clk_128fs_en & (cnt == 2'b11) );
  assign sclk     = cnt[1];
  assign load     = ( lrck ^ lrck_z1 ); // parallel data load timing
  assign sdat     = sr[15];
  assign lrck_out = lrck_z1;
  
// SCLK generator
  always @(posedge m_clk or posedge reset)
    begin
      if (reset) // async reset
        begin
          cnt     <= 2'b00;
          lrck_z1 <= 1'b0;
        end
      else if (clk_128fs_en) // 128fs clock timing
        begin
          cnt <= (cnt + 1'b1); // count up
          if ( sclk_en ) // SCLK timing
            begin
              lrck_z1 <= lrck; // 1 clock delay
            end // if (cnt4 == ...
        end // if (reset) ...
    end // always @() ...
    
// serializer
  always @(posedge m_clk or posedge reset)
    begin
      if (reset) // async reset
        begin
          sr     <= 16'h0000;
          da_out <= 16'h0000;
        end
      else if ( sclk_en )
        begin
          if ( load ) // parallel load timing
            begin
              sr     <= da; // parallel load
              da_out <= da;
            end
          else // shift
            begin
              sr     <= { sr[14:0], 1'b0 };
              da_out <= da_out; // hold
            end // if (load) ...
        end // if (reset) ...
    end // always @() ...  
endmodule

ここで、I2S は「シリアル・データ入力を持つオーディオ用 DAC」とのインターフェースを行なう回路の「総称名」として使っており、データ・フォーマット自体は、いわゆる「標準フォーマット」のみをサポートしており、「I2S フォーマット」はサポートしていません。
このモジュールでは、

  • 非同期リセット (reset)
  • マスター・クロック (m_clk)
  • 128 fs クロック・イネーブル信号 (clk_128fs_en)
  • デューティー 50 %、fs レートの方形波クロック (lrck)
  • 16 ビット・パラレル、2 の補数のデータ入力 (da[15:0])

を入力とし、

  • DA 用シリアル・クロック (sclk)
  • DA 用シリアル・データ (sdat)
  • DA 用 LR クロック (lrck_out)
  • パラレル・ロードされた DA データのモニタ出力 (da_out[15:0])

を出力します。
モジュール内部で 128 fs クロックを 4 分周し、32 fs クロックを得て sclk として出力します。
内部にはビット・カウンタを持たず、(128 fs クロックで再サンプリングした) lrck が「H」側、「L」側ともに正確に 128 fs クロック 64 周期分、(sclk 16 周期分) であることを前提にしています。
シリアライズの動作としては、いわゆる「左詰め」、つまり 16 ビット・データの MSB (da[15]) が LRCK のエッジと接するような配置になりますが、L/R の期間がそれぞれ SCLK 16 周期分なので、16 ビット・データの LSB (da[0]) が LRCK のエッジと接する、つまり「右詰め」の状態でもあります。
da_out[15:0] は、内部のシフトレジスタにロードされた値を外部からモニタするための出力で、シフトレジスタへのデータ・ロードと同じタイミングで同じ値が da_out[15:0] レジスタにもロードされ、それ以降は次のロード・タイミングまで (シフトせず) 単にその値を保持し続けます。
シミュレーション結果を下に示します。 (図をクリックすると拡大します)

i2s_tx_modelsim.png