FPGA 版 FM 音源 (66) -- FPGA 版 EG (10)
S/PDIF 送信モジュール「spdif_tx()」の Verilog ソースを下に示します。
"spdif_tx.v"
/*****************************************************/ /* S/PDIF TX module (L/R mux'ed 16/24 bit para. data */ /* */ /* 2017/01/15 Added da_ext[] */ /* 2013/11/30 Created by pcm1723 */ /*****************************************************/ module spdif_tx(reset, m_clk, clk_128fs_en, lrck, cs_fs48k, da, da_ext, clk_64fs, sosf, sof, sob, tx_out ); // Input port input reset; // async reset input m_clk; // master clock input clk_128fs_en; // clock enable for 128fs clock input lrck; // LRCK (50 % duty) input cs_fs48k; // ch status fs bit ('0' for 44.1 kHz / '1' for 48 kHz) input [15:0] da; // L/R mux'ed MS 16 bit parallel data input [ 7:0] da_ext; // L/R mux'ed LS 8 bit parallel data // Output port output clk_64fs; // 64fs clock output (square wave) output sosf; // Start of SubFrame output sof; // Start of Frame output sob; // Start of channel status Block output tx_out; // encoded TX output // reg clk_64fs; localparam [7:0] BLK_LEN = 192; // Channel Status block length localparam [7:0] CS_COPY_BIT = 2; // Channel Status Copy bit (CS2) localparam [7:0] CS_FS1_BIT = 25; // Channel Status fs bit 1 (CS25) localparam DEBUG = 0; wire [30:0] sr_para_in; // 31-bit shift register parallel data input wire [3:0] mask_para_in; // 1st edge mask shift register parallel data input wire B_pre; wire W_pre; wire M_pre; wire ch_status; wire Lch; wire Rch; reg [7:0] blk_cnt; // Channel Status bit counter in the Block (0..191) reg parity; // parity calculation register reg [30:0] sr; // 31-bit shift register for the SubFrame data but parity // {sr, sr_out} forms 32-bit shift register reg sr_out; // parity of prev SubFrame loaded at parallel load time, // else, serial output of 32-bit shift register reg [3:0] edge1_mask; // biphase encoding 1st edge mask reg [1:0] lrck_dly; // 64fs clk delay element(s) for LRCK reg sr_out_dly; // 128fs clk delay for biphase encoding reg [4:0] bit_cnt; // bit position counter in the SubFrame (0..31) assign Lch = lrck_dly[0]; // L channel indication assign Rch = (~lrck_dly[0]); // R channel indication // preamble indication assign B_pre = ( Lch & (sob) ); // B preamble (L_ch & Start_of_Block) assign W_pre = ( Rch ); // W preamble (R_ch) assign M_pre = ( Lch & (~sob) ); // M preamble (L_ch & not Start_of_Block) // channel status assign ch_status = ( (CS_COPY_BIT == blk_cnt) // Copy bit | ((CS_FS1_BIT == blk_cnt) & cs_fs48k) // fs bit1 ); assign tx_out = sr_out_dly; // 31-bit shift register parallel data input at Start of SubFrame assign sr_para_in = { ch_status, // b30 (C bit) 1'b0, // b29 (U bit) 1'b0, // b28 (V bit) da, // b27..b12 (AUDIO data MS 16 bit) da_ext, // b11..b4 (AUDIO data LS 8 bit) // preamble M_pre, // b3 (omit 2nd edge if not 'M') ~M_pre, // b2 (omit 2nd edge if 'M') 1'b1, // b1 1'b0 // b0 (omit 2nd edge always) }; // biphase encoding first edge mask shift register parallel data input assign mask_para_in = { ~B_pre, // b3 (omit 1st edge if 'B') B_pre, // b2 (omit 1st edge if not 'B') 1'b0, // b1 (omit 1st edge always) 1'b1 // b0 }; // Start of Frame (rising edge of LRCK) assign sof = ((~lrck_dly[1]) & (lrck_dly[0])); // Start of SubFrame (both edge of LRCK) assign sosf = ( (lrck_dly[1]) ^ (lrck_dly[0])); // Start of Block assign sob = ( 8'h00 == blk_cnt); // 64fs clk divider always @(posedge reset or posedge m_clk) begin if (reset) clk_64fs <= 1'b0; else if (clk_128fs_en) clk_64fs <= (~clk_64fs); end // always // LRCK delay(s) always @(posedge reset or posedge m_clk) begin if (reset) lrck_dly <= 2'b00; else if (clk_128fs_en & clk_64fs) lrck_dly <= {lrck_dly[0], lrck}; end // always // parity calculation always @(posedge reset or posedge m_clk) begin if (reset) parity <= 1'b0; else if (clk_128fs_en & clk_64fs) parity <= ( (sosf) ? (1'b0) : (parity ^ sr[0]) ); end // always // Channel Status bit number counter in the Block always @(posedge reset or posedge m_clk) begin if (reset) blk_cnt <= (BLK_LEN - 1'b1); else if (clk_128fs_en & clk_64fs & sosf & (~sof)) begin if ((BLK_LEN - 1'b1) == blk_cnt) // max count blk_cnt <= 8'h00; // reset to '0' else // increment counter blk_cnt <= (blk_cnt + 1'b1); end // if (clk_128fs_en & clk_64fs & sof) ... end // always // bit counter in the SubFrame (debug purpose only, not used in timing generation) always @(posedge reset or posedge m_clk) begin if (DEBUG) begin if (reset) bit_cnt <= 5'd31; else if (clk_128fs_en & clk_64fs) bit_cnt <= ( (sosf) ? (5'd31) : (bit_cnt + 1'b1) ); end // if (DEBUG) end // always // 32 bit shift register / biphase 1st edge mask always @(posedge reset or posedge m_clk) begin if (reset) begin {sr, sr_out} <= 31'h00_00_00_00; edge1_mask <= 4'b0000; end else if (clk_128fs_en & clk_64fs) begin // parallel input at Start of SubFrame, else shift {sr, sr_out} <= ( (sosf) ? {sr_para_in, parity} : {1'b0, sr} ); edge1_mask <= ( (sosf) ? (mask_para_in) : {1'b1, edge1_mask[3:1]} ); end else // hold begin {sr, sr_out} <= {sr, sr_out}; edge1_mask <= edge1_mask; end end // always // biphase encoder always @(posedge reset or posedge m_clk) begin if (reset) sr_out_dly <= 1'b0; else if (clk_128fs_en) begin if (clk_64fs) // 1st edge sr_out_dly <= (edge1_mask[0]) ^ sr_out_dly; else // 2nd edge sr_out_dly <= (sr_out) ^ sr_out_dly; end // if (clk_128fs_en) ... end // always endmodule
このモジュールでは、
- 非同期リセット (reset)
- マスター・クロック (m_clk)
- 128 fs クロック・イネーブル信号 (clk_128fs_en) (6.144 MHz)
- LR クロック (lrck) (デューティー 50 % の 48 kHz 方形波)
- チャネル・ステータス中の fs = 48 kHz を示すビット (cs_fs48k)
- 16 ビット・パラレルのオーディオ・データ (da[15:0]) (L/R マルチプレクス)
- 8 ビット・パラレルのオーディオ・データ拡張部 (da_ext[7:0]) (L/R マルチプレクス)
を入力とし、
- 64 fs クロック (clk_64fs) (デューティー 50 % の 3.072 MHz 方形波)
- サブフレーム開始信号 (sosf)
- フレーム開始信号 (sof)
- ブロック開始信号 (sob)
- SPDIF 送信出力 (tx_out)
を出力します。
モジュール内部ではフレーム数カウントは行なっていますが、フレーム/サブフレームのビット・カウントは行なっておらず、LR クロックからタイミングを作り出しています。 したがって、lrck 信号は、
- マスター・クロック (m_clk) に同期し、
- 位相が安定していて、128 fs クロックや 64 fs クロックでの再サンプリングに対してジッタが出ない。
- 50 % デューティーで H 期間 / L 期間ともに、(再サンプリング後に) 128 fs クロックでびったり 64 周期分の長さ
であることが求められます。
チャネル・ステータスについては、
- 内容は左右チャネル同一で、
- コピー許可ビット (b2)
- サンプリング周波数識別ビット (b25)
のみサポートしています。
チャネル・ステータス b25 には cs_fs48k 入力の値をそのまま反映させているので、cs_fs48k = 0 で fs = 44.1 kHz、cs_fs48k = 1 で fs = 48 kHz を意味することになります。
SPDIF では BMC (Biphase Mark Code) と呼ばれる方式で出力が変調されます。
(普通の) データの変調では、前のビットとの境界では必ず出力信号が反転し、データが「0」の場合にはそのまま、データが「1」の場合には当該ビット・タイムの中央で出力信号が反転します。
前ビットとの境界のエッジを「ファースト・エッジ」、当該ビット・タイムの中央での反転を「セカンド・エッジ」と表現すると、普通のデータに対しては、
- ファースト・エッジでは必ず反転
- データが「1」の場合のみセカンド・エッジで反転
する規則となっています。
フレーム/サブフレームの先頭には、フレーム同期のための同期パターンが存在し、「プリアンブル」と呼ばれています。
プリアンブルでは、前述のコーディング規則が破られていて、受信側で同期を取るための手がかりとなっています。
上の図のようにプリアンブルには「B」、「M」、「W」の 3 種類があって、
- B — ブロック開始および左チャネル (フレーム開始)
- M — 左チャネル (ブロック開始フレーム以外)
- W — 右チャネル (サブフレーム開始)
という役割になっています。
いずれも、ファースト・エッジでの反転がないパターンが含まれていて、通常データにはない 1.5 T 幅のパルスを含んでいます。
サブフレーム・データのシフト・レジスタおよびバイフェース・マーク・エンコーディング部のブロック図を下に示します。
「P」はパリティ計算回路で、ひとつ前のサブフレームに対する結果です。
「B」はプリアンブル「B」を出力するタイミングを表す信号、「M」はプリアンブル「M」を出力するタイミングを表す信号です。
「C」はチャネル・ステータス・ビットです。
サブフレームのデータをパラレル・ロードする 31 ビットのシフトレジスタでセカンド・エッジを制御しています。
それに加え、プリアンブル部のファースト・エッジを制御する 4 ビットのシフトレジスタを設けています。
プリアンブル部以外は常にファースト・エッジが存在するので、シフトレジスタ長は 4 ビットとし、シリアル・シフト入力を「1」に吊っておいてプリアンブル部以外では常に「1」が出力されるようにしています。
ネガティブ・エッジで D-FF をトリガすることを許せば、128 fs クロックではなく 64 fs クロックで BMC 符号化回路を構成することも可能ですが、FPGA 回路では好ましくないので、普通にポジティブ・エッジのみ有効な 128 fs クロックを使う回路としています。
ブロック開始部分付近のシミュレーション結果を下に示します。 (図をクリックすると拡大します)