//-----------------------------------------------------------------
//                        SPDIF Transmitter
//                              V0.1
//                        Ultra-Embedded.com
//                          Copyright 2012
//
//                 Email: admin@ultra-embedded.com
//
//                         License: GPL
// If you would like a version with a more permissive license for
// use in closed source commercial applications please contact me
// for details.
//-----------------------------------------------------------------
//
// This file is open source HDL; you can redistribute it and/or 
// modify it under the terms of the GNU General Public License as 
// published by the Free Software Foundation; either version 2 of 
// the License, or (at your option) any later version.
//
// This file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public 
// License along with this file; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
//-----------------------------------------------------------------
// altera message_off 10762
// altera message_off 10240

module spdif

//-----------------------------------------------------------------
// Params
//-----------------------------------------------------------------
#(
    parameter CLK_RATE              = 50000000,
    parameter AUDIO_RATE            = 48000,

    // Generated params
    parameter WHOLE_CYCLES          = (CLK_RATE) / (AUDIO_RATE*128),
    parameter ERROR_BASE            = 10000,
    parameter [63:0] ERRORS_PER_BIT = ((CLK_RATE * ERROR_BASE) / (AUDIO_RATE*128)) - (WHOLE_CYCLES * ERROR_BASE)
)

//-----------------------------------------------------------------
// Ports
//-----------------------------------------------------------------
(
    input        clk_i,
    input        rst_i,
    input        half_rate,

    // Output
    output       spdif_o,

    // Audio interface (16-bit x 2 = RL)
    input [15:0] audio_r,
    input [15:0] audio_l,
    output       sample_req_o
);

reg         bit_clk_q;

// Clock pulse generator
always @ (posedge rst_i or posedge clk_i) begin
	reg [31:0]  count_q;
	reg [31:0]  error_q;
	reg ce;

	if (rst_i) begin
		count_q   <= 0;
		error_q   <= 0;
		bit_clk_q <= 1;
		ce        <= 0;
	end
	else
	begin
		if(count_q == WHOLE_CYCLES-1) begin
			if (error_q < (ERROR_BASE - ERRORS_PER_BIT)) begin
				error_q <= error_q + ERRORS_PER_BIT[31:0];
				count_q <= 0;
			end else begin
				error_q <= error_q + ERRORS_PER_BIT[31:0] - ERROR_BASE;
				count_q <= count_q + 1;
			end
		end else if(count_q == WHOLE_CYCLES) begin
			count_q <= 0;
		end else begin
			count_q <= count_q + 1;
		end

		bit_clk_q <= 0;
		if(!count_q) begin
			ce <= ~ce;
			if(~half_rate || ce) bit_clk_q <= 1;
		end
	end
end

//-----------------------------------------------------------------
// Core SPDIF
//-----------------------------------------------------------------

wire [31:0] sample_i = {audio_r, audio_l};

spdif_core
u_core
(
    .clk_i(clk_i),
    .rst_i(rst_i),

    .bit_out_en_i(bit_clk_q),

    .spdif_o(spdif_o),

    .sample_i(sample_i),
    .sample_req_o(sample_req_o)
);

endmodule

module spdif_core
(
    input           clk_i,
    input           rst_i,

    // SPDIF bit output enable
    // Single cycle pulse synchronous to clk_i which drives
    // the output bit rate.
    // For 44.1KHz, 44100×32×2×2 = 5,644,800Hz
    // For 48KHz,   48000×32×2×2 = 6,144,000Hz
    input           bit_out_en_i,

    // Output
    output          spdif_o,

    // Audio interface (16-bit x 2 = RL)
    input [31:0]    sample_i,
    output reg      sample_req_o
);

//-----------------------------------------------------------------
// Registers
//-----------------------------------------------------------------
reg [15:0]  audio_sample_q;
reg [8:0]   subframe_count_q;

reg         load_subframe_q;
reg [7:0]   preamble_q;
wire [31:0] subframe_w;

reg [5:0]   bit_count_q;
reg         bit_toggle_q;

reg         spdif_out_q;

reg [5:0]   parity_count_q;

//-----------------------------------------------------------------
// Subframe Counter
//-----------------------------------------------------------------
always @ (posedge rst_i or posedge clk_i )
begin
    if (rst_i == 1'b1)
        subframe_count_q    <= 9'd0;
    else if (load_subframe_q)
    begin
        // 192 frames (384 subframes) in an audio block
        if (subframe_count_q == 9'd383)
            subframe_count_q <= 9'd0;
        else
            subframe_count_q <= subframe_count_q + 9'd1;
    end
end

//-----------------------------------------------------------------
// Sample capture
//-----------------------------------------------------------------
reg [15:0] sample_buf_q;

always @ (posedge rst_i or posedge clk_i )
begin
   if (rst_i == 1'b1)
   begin
        audio_sample_q      <= 16'h0000;
        sample_buf_q        <= 16'h0000;
        sample_req_o        <= 1'b0;
   end
   else if (load_subframe_q)
   begin
        // Start of frame (first subframe)?
        if (subframe_count_q[0] == 1'b0)
        begin
            // Use left sample
            audio_sample_q <= sample_i[15:0];

            // Store right sample
            sample_buf_q <= sample_i[31:16];

            // Request next sample
            sample_req_o <= 1'b1;
        end
        else
        begin
            // Use right sample
            audio_sample_q <= sample_buf_q;

            sample_req_o <= 1'b0;
        end
   end
   else
        sample_req_o <= 1'b0;
end

// Timeslots 3 - 0 = Preamble
assign subframe_w[3:0] = 4'b0000;

// Timeslots 7 - 4 = 24-bit audio LSB
assign subframe_w[7:4] = 4'b0000;

// Timeslots 11 - 8 = 20-bit audio LSB
assign subframe_w[11:8] = 4'b0000;

// Timeslots 27 - 12 = 16-bit audio
assign subframe_w[27:12] = audio_sample_q;

// Timeslots 28 = Validity
assign subframe_w[28] = 1'b0; // Valid

// Timeslots 29 = User bit
assign subframe_w[29] = 1'b0;

// Timeslots 30 = Channel status bit
assign subframe_w[30] = 1'b0;

// Timeslots 31 = Even Parity bit (31:4)
assign subframe_w[31] = 1'b0;

//-----------------------------------------------------------------
// Preamble
//-----------------------------------------------------------------
localparam PREAMBLE_Z = 8'b00010111;
localparam PREAMBLE_Y = 8'b00100111;
localparam PREAMBLE_X = 8'b01000111;

reg [7:0] preamble_r;

always @ *
begin
    // Start of audio block?
    // Z(B) - Left channel
    if (subframe_count_q == 9'd0)
        preamble_r = PREAMBLE_Z; // Z(B)
    // Right Channel?
    else if (subframe_count_q[0] == 1'b1)
        preamble_r = PREAMBLE_Y; // Y(W)
    // Left Channel (but not start of block)?
    else
        preamble_r = PREAMBLE_X; // X(M)
end

always @ (posedge rst_i or posedge clk_i )
if (rst_i == 1'b1)
    preamble_q  <= 8'h00;
else if (load_subframe_q)
    preamble_q  <= preamble_r;

//-----------------------------------------------------------------
// Parity Counter
//-----------------------------------------------------------------
always @ (posedge rst_i or posedge clk_i )
begin
   if (rst_i == 1'b1)
   begin
        parity_count_q  <= 6'd0;
   end
   // Time to output a bit?
   else if (bit_out_en_i)
   begin
        // Preamble bits?
        if (bit_count_q < 6'd8)
        begin
            parity_count_q  <= 6'd0;
        end
        // Normal timeslots
        else if (bit_count_q < 6'd62)
        begin
            // On first pass through this timeslot, count number of high bits
            if (bit_count_q[0] == 0 && subframe_w[bit_count_q / 2] == 1'b1)
                parity_count_q <= parity_count_q + 6'd1;
        end
   end
end

//-----------------------------------------------------------------
// Bit Counter
//-----------------------------------------------------------------
always @ (posedge rst_i or posedge clk_i)
begin
    if (rst_i == 1'b1)
    begin
        bit_count_q     <= 6'b0;
        load_subframe_q <= 1'b1;
    end
    // Time to output a bit?
    else if (bit_out_en_i)
    begin
        // 32 timeslots (x2 for double frequency)
        if (bit_count_q == 6'd63)
        begin
            bit_count_q     <= 6'd0;
            load_subframe_q <= 1'b1;
        end
        else
        begin
            bit_count_q     <= bit_count_q + 6'd1;
            load_subframe_q <= 1'b0;
        end
    end
    else
        load_subframe_q <= 1'b0;
end

//-----------------------------------------------------------------
// Bit half toggle
//-----------------------------------------------------------------
always @ (posedge rst_i or posedge clk_i)
if (rst_i == 1'b1)
    bit_toggle_q    <= 1'b0;
// Time to output a bit?
else if (bit_out_en_i)
    bit_toggle_q <= ~bit_toggle_q;

//-----------------------------------------------------------------
// Output bit (BMC encoded)
//-----------------------------------------------------------------
reg bit_r;

always @ *
begin
    bit_r = spdif_out_q;

    // Time to output a bit?
    if (bit_out_en_i)
    begin
        // Preamble bits?
        if (bit_count_q < 6'd8)
        begin
            bit_r = preamble_q[bit_count_q[2:0]];
        end
        // Normal timeslots
        else if (bit_count_q < 6'd62)
        begin
            if (subframe_w[bit_count_q / 2] == 1'b0)
            begin
                if (bit_toggle_q == 1'b0)
                    bit_r = ~spdif_out_q;
                else
                    bit_r = spdif_out_q;
            end
            else
                bit_r = ~spdif_out_q;
        end
        // Parity timeslot
        else
        begin
            // Even number of high bits, make odd
            if (parity_count_q[0] == 1'b0)
            begin
                if (bit_toggle_q == 1'b0)
                    bit_r = ~spdif_out_q;
                else
                    bit_r = spdif_out_q;
            end
            else
                bit_r = ~spdif_out_q;
        end
    end
end

always @ (posedge rst_i or posedge clk_i )
if (rst_i == 1'b1)
    spdif_out_q <= 1'b0;
else
    spdif_out_q <= bit_r;

assign spdif_o  = spdif_out_q;

endmodule
