I2S→左詰め16bit（BCK 32fs）への変換をFPGAでやる

# きっかけ
80年代の積分型の（NOS：ノンオーバーサンプリング）DACには、現代のDACと比較しても音質的な優位性があるらしく、
機会があったらこの時代のCDプレイヤーを入手して、DACに改造しようかな～と思っていました。
そんなタイミングで、
[PCM-501ESの驚愕音質とSPDIF化改造](http://innocent-key.com/wordpress/?page_id=11019)
のような記事を見つけて、どんな音か聞いてみたい気持ちが高まりました。

調べてみるとこの時代のDACに入力するデジタルオーディオフォーマットは、
16bit 左詰めフォーマット（BCK = 32fs）
つまり昔の16bit DAC 専用のI2S入力（に準じたもの）である必要があるらしく、

各種ブログの作例としては、
・16bit 左詰めフォーマット で出すDAIを使用する
ことが紹介されていることが多いのですが、このような出力を持つDAIは古いものに限られています。

ということで悩んでいたのですが、
「これFPGAでI2Sを直接I/F変換すればよいのでは…？」
と思ったので、久しぶりにFPGAを触るリハビリもかねてやってみることにしました。

# I2S(Inter IC Sound)について（作成中）
このタイトルでピンと来た方にわざわざ説明する必要はないと思いますが、
I2Sは以下の3つの信号線からなります（場合によりMCKが必要）。

* BCK
* LRCK
* DATA

## 目標の出力
上段の3つのCLK（I2S）
↓
下段の3つのCLK（16LJ～）
のように変換されていればよいです。
![16LJ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280257/a3df21cf-9812-c560-973b-f5569d04ff4f.png)

* LRCKの周波数は変わらず、L、RのDataがLRCKと同じタイミングで出る
* BCKの周波数を半分にする
* 上記に伴いLRCK１周期で元データの先頭16bit分のDATAを出力

# Source Code（参考）
Verilogソースコードを示します。
Fs = 44.1/48kHzで　USBDDCからI2S出力を行い、
これをCX890 / CX20017（積分型DAC）に入力させ、
それなりに安定動作[^except]しました。

```verilog

//I2Sを16LJに変換
module I2S_to_16LJ(bck, data, lrck, bck_out, data_out, lrck_out);

	input bck;
	input data;
	input lrck;
	output bck_out;
	output data_out;
	output lrck_out;

	reg data_out = 0; 			
	reg[4:0] data_counter = 0 ; //元のI2Sにおける何bitめのDATAか
	reg[31:0] data_fifo = 0; 	//元のI2S DATA (32bit) を 1sample分格納する

	//LRCK を 元々のBCK2周期分遅らせる
	wire lrck_32LJ;
	delay_1BCK I2S_to_32LJ (lrck, bck, lrck_32LJ);
	delay_1BCK delay (lrck_32LJ, bck, lrck_out);	
	
	//BCKを2分周
	//元のBCKに同期して、lrck_changedでリセットをかける
	reg lrck_changed;
	half_freq half_freq_ins (bck, lrck_changed, bck_out);
	
	//分周したBCKに同期させてDATA出力をする
	reg lrck_before = 0;
	always @ (posedge bck)
	begin
		//lrckの切り替わりを検出、読んでるbitのインデックスを0にする
		if(lrck_before != lrck_32LJ)
		begin
			data_counter <= 0;
			data_fifo[0] <= data;
			lrck_changed <= 1;
		end
		else
		begin
			data_counter <= data_counter + 1;
			data_fifo[data_counter + 1] <= data;
			lrck_changed <= 0;
		end
		lrck_before <= lrck_32LJ;
	end

	always @ (negedge bck_out)
	begin
		data_out <= data_fifo [data_counter / 2];
	end

endmodule


```

# 解説と、各モジュールの詳細説明
## I2S　→　32bit左詰め に変換
これは簡単です。I2S信号はDataがLRCKに対して1BCK分ずれていますので
この分LRCK側をずらしてあげればよいです。

```verilog

module delay_1BCK (lrck, bck, lrck_out);

	input lrck;
	input bck;
	output lrck_out;
	
	reg lrck_out;
	reg lrck_before;

	always @ (posedge bck)
	begin
		lrck_before <= lrck;
	end

	always @ (negedge bck)
	begin
		lrck_out <= lrck_before;
	end

endmodule

```

## BCKの2分周
単なる２分周回路だと、パワーOnタイミングによってはBCKの正負が反転する可能性があります。
（なので本当はパワーオンリセットが必要）
これを防ぐため、LRCKの立ち上がり／下がり（lrck_changed）に同期してリセットがかかるようにしています。

```verilog
module half_freq(bck, lrck_changed, bck_out);
	input bck;
	input lrck_changed;
	output bck_out;

	reg bck_out;

	always @ (negedge bck)
	begin
		if(lrck_changed == 1)
		begin
			bck_out <= 0; 
		end
		else
		begin
			bck_out <= ~bck_out;
		end
	end
	
endmodule
```

## Dataの乗せ換え
BCKの立ち上がりで元々のI2S信号をラッチし、レジスタdata_fifoに保持しておきます。
分周したBCKの立下りタイミングに同期して、data_fifo内のデータを1bitずつ出力します。
これはちょうど　data_counter / 2 ビット目　のデータになります。
これディスクリートのロジックでやろうと思うとちょっと大変そうですね。

## 2*LRCKが必要な場合
PCM-501ES　のように LRCKに同期し倍の周波数を持つ信号 = 2*LRCK が必要な場合には、
LRCKをBCK8個分遅らせた信号を作成し、これを元のLRCKとNXOR すればよいです。

```verilog

module make_WCK(bck, lrck, WCK);
    
    input bck;
    input lrck;
    output WCK;
    
    reg delayed_lrck = 0;
    reg[7:0] lrck_fifo;

    assign WCK = lrck ~^ delayed_lrck;

    always @ (posedge bck)
    begin
        lrck_fifo <= {lrck_fifo[6:0],lrck};
    end

    always @ (negedge bck)
    begin
        delayed_lrck <= lrck_fifo[7];
    end

endmodule
```

# やってみた
PCM-501ESで実験したときの様子を示します。
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3238303235372f39326531313366652d313634352d363166622d373733612d3238386132636533623362302e6a706567.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280257/f5a506d7-cc52-d2a5-156e-1b0ee0812347.jpeg)

急ぎでつくったので配線は超適当ですが、このヒョロヒョロのGND（黒線）でもそこそこまともな音が出ます。
ポテンシャルを感じます！

# おわりに
・USB-DDC（[こういうの](https://www.amazon.co.jp/ATSAM3U1C-XC2C64A-Amanero-IIS%E3%83%87%E3%82%B8%E3%82%BF%E3%83%AB%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%82%A4%E3%82%B9DAC%E3%83%87%E3%82%B3%E3%83%BC%E3%83%80%E3%83%9C%E3%83%BC%E3%83%89%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88DSD512-32%E3%83%93%E3%83%83%E3%83%88384K/dp/B07BQPQJ9V/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=USB+DDC+%E3%83%9C%E3%83%BC%E3%83%89&qid=1581425043&s=electronics&sr=1-1)）
・ちっちゃいFPGAボード（[こういうの](https://www.amazon.co.jp/gp/product/B0719H7RMB/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1)）
さえあれば簡単にできるので、興味があるけど手が出せてない人のお助けになると幸いです。
あとVerilogのコーディングに関しては限りなく素人なので詳しいかた突っ込んでいただけると助かります…。

# 参考文献
[PCM-501ESの驚愕音質とSPDIF化改造](http://innocent-key.com/wordpress/?page_id=11019)

[^except]: CM6631のように、1fsでBCK=128fsになる特殊なケースを除きます。またサンプリング周波数の遷移などイレギュラーな動作については確認していませんので、ハードウェアや耳の破損には責任もてませんのでご了承ください。