FPGA SDR(21)PCM5102A I2S DAC


2019/03/23 追記:LRCKの極性を修正しました。
2018/07/14 追記:音量調節機能を追加しました。

 

ebayでPCM5102A I2S DACモジュールを購入しました。400円ほどです。

抵抗だけのR-2R DACはオシロを使った動作確認専用にして、音声出力はI2S DACを使うことにします。オペアンプ4580DDでヘッドフォンをドライブします。ステレオジャックですが左右同じ信号を出力しています。左右各32ビットもあるので電子ボリュームの機能を追加できます。

 

I2S DACのサンプリング周波数を48kHzにしたかったんですが、ALTPLLで48kHz x 1536 = 73.728MHzを生成できなかったので 73.75MHz / 1536 = 48.015kHzにしました。PCM5102Aは問題なく動作しています。

 

`timescale 1ns/1ns


module MyI2S
 #(
	parameter VOLUME_WIDTH = 4,
	parameter IN_WIDTH = 10,
	parameter OUT_WIDTH = 32
 )
(
	input wire clk, // 73.728M
	input wire reset_n,
	
	input wire [VOLUME_WIDTH-1:0] volume,
	input wire signed [IN_WIDTH-1:0] in_left,
	input wire signed [IN_WIDTH-1:0] in_right,
	input wire in_valid,

	output reg SCK,
	output reg BCK,
	output reg LRCK,
	output reg DATA
);


	localparam LRCK_CLK = 1536;
	localparam LRCK_HALF_CLK = LRCK_CLK >> 1;
	localparam BCK_CLK = 24;
	localparam BCK_HALF_CLK = BCK_CLK >> 1;


	reg [10:0] lrck_cnt; // fs = 48k = clk / 1536
	reg [5:0] bck_cnt; // fs * 64 = clk / 24
	
	reg signed [OUT_WIDTH-1:0] out_left;
	reg signed [OUT_WIDTH-1:0] out_right;


	always @(posedge clk) begin
		if (~reset_n) begin
			lrck_cnt <= 0;
			
			LRCK <= 1'b1;
		end
		else begin
			if (in_valid) begin
				lrck_cnt <= 0;
				
				LRCK <= 1'b1;
			end
			else begin
				lrck_cnt <= lrck_cnt + 1;
				
				if (lrck_cnt == LRCK_HALF_CLK-1) begin
					LRCK <= 1'b0;
				end
			end
		end
	end

	always @(posedge clk) begin
		if (~reset_n) begin
			bck_cnt <= 0;

			BCK <= 1'b0;

			DATA <= 1'b0;
			out_left <= 0;
			out_right <= 0;
		end
		else begin
			if (in_valid) begin
				bck_cnt <= 0;

				BCK <= 1'b0;

				DATA <= (lrck_cnt < LRCK_HALF_CLK) ? out_left[OUT_WIDTH-1] : out_right[OUT_WIDTH-1];
				out_left <= in_left <<< (OUT_WIDTH - IN_WIDTH - volume);
				out_right <= in_right <<< (OUT_WIDTH - IN_WIDTH - volume);
			end
			else begin
				if (bck_cnt == BCK_CLK-1) begin
					bck_cnt <= 0;
					
					BCK <= 1'b0;
					
					DATA <= (lrck_cnt < LRCK_HALF_CLK) ? out_left[OUT_WIDTH-1] : out_right[OUT_WIDTH-1];
					if (lrck_cnt < LRCK_HALF_CLK) out_left <= out_left <<< 1;
					else out_right <= out_right <<< 1;
				end
				else begin
					bck_cnt <= bck_cnt + 1;
					
					if (bck_cnt == BCK_HALF_CLK-1) begin
						BCK <= 1'b1;
					end
				end
			end
		end
	end


endmodule