2012年7月8日日曜日

FPGA FMトランスミッタ: プリエンファシス

プリエンファシスの構成

前回の記事を再掲します。

プリエンファシスのブロック図

ここで、fs=152kHzのときのパラメータは次の通りです。

  k: 0.75 (2進 0.11)
  b: 0.04541 (2進  0.00001011101)
  G: 20.875 (2進 10100.111)

ブロック図によると、処理の過程で k倍とか 1+b倍とかの乗算処理を多用しています。前回は、乗算器を節約するため乗算処理をシフト演算と加算に分解していました。今回は、先のローパスフィルタを参考にして、1個の乗算器を使い入力信号を適宜切り替えています。乗算器は前回と同じハードマクロをインスタンシエーションしていますのでレイテンシの3を考慮して、タイミング関係を作図したうえでコードを作成しました。※state512は1クロック位置がずれています。わかりやすさ優先であえて直していません

タイミング関係

特性実測値

周波数特性(入力ローパスフィルタ含む)

スペクトル(5kHz、プリエンファシスON)
※赤い波形はバッグクラウンドノイズ

スペクトル(5kHz、プリエンファシスOFF)

pre_emphコード

一見しただけでは、何をやっているかさっぱりわからないシロモノになってしまいました。タイミング関係図と合わせて見て下さい。また、計算のダイナミックレンジの取り方を配慮できていないので、ビット幅が不足しているところと過剰なところが混在していると思います。出力レジスタのサチュレーション処理はローパスフィルタのコードを使わせて頂いています。

module pre_emph(
  pe_l_i, pe_r_i, pe_l_o, pe_r_o,
  clk, en, reset, pe_thru
  );

  input [23:0] pe_l_i;
  input [23:0] pe_r_i;
  output [23:0] pe_l_o;
  output [23:0] pe_r_o;
  input clk;
  input en;
  input reset;
  input pe_thru;

  reg [8:0] state512;
  reg [35:0] input_reg;
  reg [17:0] mul_in1;
  reg [17:0] mul_in2;
  wire [35:0] mul_out;
  reg [35:0] reg0;
  reg [35:0] reg1;
  reg [17:0] reg2_l;
  reg [17:0] reg2_r;
  reg [17:0] reg2_l2;
  reg [17:0] reg2_r2;
  reg [35:0] delay_buff;
  reg [29:0] output_reg_l;
  reg [29:0] output_reg_r;
  reg [23:0] pe_l_o;
  reg [23:0] pe_r_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 latch_out = (state512 == 17);

  // Pre Emphasis
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        begin
          input_reg <= 0;
          mul_in1 <= 0;
          mul_in2 <= 0;
          reg0 <= 0;
          reg1 <= 0;
          delay_buff <= 0;
          output_reg_l <= 0;
          output_reg_r <= 0;
        end

      else
        begin
          case (state512)
            0:  begin 
                input_reg <= {pe_l_i[23:6], pe_r_i[23:6]};  // Vin(L/R)
              end

            1:  begin 
                mul_in1 <= input_reg[35:18];  // Vin(L)
                mul_in2 <= 18'b01_1000_0000_0000_0000;  // k=0.75 (0.11)
              end

            2:  begin 
                mul_in1 <= input_reg[17:0];  // Vin(R)
                mul_in2 <= 18'b01_1000_0000_0000_0000;  // k=0.75 (0.11)
              end

            3:  begin 
                mul_in1 <= delay_buff[35:18];  // delay_buf(L)
                mul_in2 <= 18'b00_1000_0000_0000_0000;  // k=1-0.75=0.25 (0.01)
              end

            4:  begin 
                mul_in1 <= delay_buff[17:0];  // delay_buf(R)
                mul_in2 <= 18'b00_1000_0000_0000_0000;  // k=1-0.75=0.25 (0.01)
              end

            5:  begin 
                mul_in1 <= 0;
                mul_in2 <= 0;
                reg0[35:18] <= mul_out[34:17];
              end

            6:  begin 
                reg0[17:0] <= mul_out[34:17];
              end

            7:  begin 
                mul_in1 <= input_reg[35:18];  // Vin(L)
                mul_in2 <= 18'b01_0000_1011_1010_0000;  // 1+b=1.04541 (01.00001011101)
                reg1[35:18] <= mul_out[34:17];
              end

            8:  begin 
                mul_in1 <= input_reg[17:0];  // Vin(R)
                mul_in2 <= 18'b01_0000_1011_1010_0000;  // 1+b=1.04541 (01.00001011101)
                reg1[17:0] <= mul_out[34:17];
              end

            9:  begin 
                reg2_l <= reg0[35:18] + reg1[35:18];
                reg2_r <= reg0[17:0] + reg1[17:0];
              end

            10:begin 
                reg2_l2 <= ~reg2_l + 1;
                reg2_r2 <= ~reg2_r + 1;
              end

            11:begin 
                mul_in1 <= mul_out[34:17] + {reg2_l2[17], reg2_l2[17:1]};  // preemph(L)
                mul_in2 <= 18'b01_0100_1110_0000_0000;  // G=20.875 (010100.111)
                delay_buff <= {reg2_l, reg2_r};
              end

            12:begin 
                mul_in1 <= mul_out[34:17] + {reg2_r2[17], reg2_r2[17:1]};  // preemph(R)
                mul_in2 <= 18'b01_0100_1110_0000_0000;  // G=20.875 (010100.111)
              end

            15:begin 
                output_reg_l <= mul_out[34:5];  // Vout(L)
              end

            16:begin 
                output_reg_r <= mul_out[34:5];  // Vout(R)
              end

            default:  begin
                mul_in1 <= 0;
                mul_in2 <= 0;
              end

          endcase
        end
    end

  //  output data register, saturation
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        begin
          pe_l_o <= 0;
          pe_r_o <= 0;
        end
      else if (pe_thru)
        begin
          pe_l_o <= pe_l_i;
          pe_r_o <= pe_r_i;
        end
      else if (latch_out)
        begin
          // left channel
          if (~output_reg_l[29] && !(output_reg_l[28:23] == 6'b000000))
            pe_l_o <= 8388607;    // 24-bit positive max
          else if (output_reg_l[29] && !(output_reg_l[28:23] == 6'b111111))
            pe_l_o <= -8388608;    // 24-bit negative max
          else
            pe_l_o <= {output_reg_l[29], output_reg_l[22:0]};
          // right channel
          if (~output_reg_r[29] && !(output_reg_r[28:23] == 6'b000000))
            pe_r_o <= 8388607;    // 24-bit positive max
          else if (output_reg_r[29] && !(output_reg_r[28:23] == 6'b111111))
            pe_r_o <= -8388608;    // 24-bit negative max
          else
            pe_r_o <= {output_reg_r[29], output_reg_r[22:0]};
        end
    end

  mul_p U1 (
    .clk(clk),
    .sclr(reset),
    .a(mul_in1),
    .b(mul_in2),
    .p(mul_out)
    );

endmodule

速度の低下
プリエンファシスのモジュールの有無で最大動作周波数が下がってしまいました。できるだけ処理を細切れにしたつもりでしたが時間的に厳しいところもあるようです。

プリエンファシスあり

プリエンファシスなし

0 件のコメント:

コメントを投稿