//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:       James C. Ahlstrom
// 
// Create Date:    12:31:22 10/23/2006 
// Design Name:    Digital up converter
// Module Name:    duc1 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////

// Data from the MCU has address in_addr, data byte in_data.
// in_addr:
//    0: in_data is the control byte
//    1: commands - check in_data to see what to do
//    2: in_data is the phase data
//    3: in_data is audio data: two 16-bit samples (I and Q), maximum amplitude +/- 22190
// For in_addr == 1 (command), in_data is as follows:
//    0: reset the FPGA
//    1: start of phase data; zero the phase byte address
//    2: start of audio data; zero the audio byte address

// CIC 1 has interpolation R = 25, order M = 5, delay N = 1.
// CIC 1 gain = (N*R)^M / R = 390625
// CIC 2 has interpolation R = 75, order M = 2, delay N = 1.
// CIC 2 gain = (N*R)^M / R = 75
// Reduce CIC 1 to CIC 2 by 17 bits; gain = 2^-17
// Reduce CIC 2 to Cordic by 4 bits; gain = 2^-4
// The net CIC gain is 13.97
// The maximum Cordic amplitude in 20 bits is 310000.
// So the maximum 16-bit sample amplitude from the MCU is 22190.
// For I/Q data, each channel is 22190/sqrt(2) = 15691.  Or limit
// sqrt(I*I + Q*Q) to 22190.

// ****** MAXIMUM 16-bit signed sample amplitude is 22190 (not 2^15) ******

module duc1(
	output wire signed [13:0] dac_data,	// parallel data to DAC
	output dac_clock,			// clock for DAC
	input clk,					// master clock
	input [7:0] in_data,		// 8-bit port from MCU
	input [1:0] in_addr,		// address bus from MCU
	input in_strobe,			// write strobe from MCU
	output wire red_led,		// red LED on front panel - assert when reading audio data
	input wire keying,			// do not produce output unless asserted
	output wire green_led,		// assert when programmed
	output wire pin_41);		// test pin output

	// Variables for MCU data input
	reg [31:0] delta;					// CORDIC tuning word - 32 bits
	reg [3:0] strobe_timer;				// timer for in_strobe
	reg [2:0] phase_address;			// address for in_strobe data at in_addr == 2
	reg [1:0] audio_address;			// collect audio bytes into a 4-byte word
	
	// Variables for first CIC filter; 36-bit input; 24-bit output
	parameter interp_cic_1 = 5'd24;	// interpolation factor for first CIC MINUS 1
	reg [4:0] count_cic_1;			// counter for first CIC
	wire signed [35:0] sign_ext_real;		// input for real sample
	wire signed [35:0] sign_ext_imag;		// input for imaginary sample
	reg signed [35:0] x0, x1, x2, x3, x4, x5, dx0, dx1, dx2, dx3, dx4;		// variables for first CIC comb, real
	reg signed [35:0] y1, y2, y3, y4, y5;	// variables for first CIC integrator, real
	reg signed [35:0] q0, q1, q2, q3, q4, q5, dq0, dq1, dq2, dq3, dq4;		// variables for first CIC comb, imag
	reg signed [35:0] s1, s2, s3, s4, s5;	// variables for first CIC integrator, imag
	wire signed [23:0] wire_y5, wire_s5;   // reduced bits for first CIC output
	
	// Variables for second CIC filter; 24-bit input; 20-bit output
	parameter interp_cic_2 = 7'd74;	// interpolation factor for second CIC MINUS 1
	reg [6:0] count_cic_2;			// counter for second CIC
	reg signed [23:0] xx1, xx2, dxx0, dxx1, yy1, yy2;		// variables for second CIC, real
	reg signed [23:0] qq1, qq2, dqq0, dqq1, ss1, ss2;		// variables for second CIC, imag
	wire signed [19:0] wire_yy2, wire_ss2;   // reduced bits for second CIC output

	// Variables for memory access
	parameter MEM_SIZE = 13'd4608;		// memory size in 32-bit samples - REDEFINED BELOW
	reg start_reading;					// is enough data is available in memory to start reading?
	reg [12:0] write_addr;				// write address for MCU data
	reg [12:0] read_addr;				// read memory address
	reg [12:0] mem_count;				// count of data in memory in 32-bit samples
	reg [31:0] audio_write;				// 4-byte audio data word from MCU; write it to memory
	reg valid_audio_write;				// Is there valid data in audio_write?
	wire [31:0] audio_read;				// audio data read from memory
	reg write_enable;
	wire mem_clock;
	
	assign mem_clock = count_cic_2[2];	// derived memory clock is master clock / 8
	
	audio_memory am(audio_read, audio_write, write_addr, read_addr, write_enable, mem_clock);
	
	assign red_led = start_reading;
	assign pin_41 = 0;
	assign green_led = 1;
	
	// Reduce bits from first to second CIC
	// Output of first CIC filter: divide by 2**17, reduce 36 bits to 24
	assign wire_y5 = {{5{y5[35]}}, y5[35:17]};
	assign wire_s5 = {{5{s5[35]}}, s5[35:17]};
	
	// Reduce bits from second CIC to CORDIC
	// Output of second CIC filter: divide by 2**4, reduce 24 bits to 20
	assign wire_yy2 = yy2[23:4];
	assign wire_ss2 = ss2[23:4];
	
	// Create DAC clock
	assign dac_clock = ~clk;
		
	// Translate second CIC output to output frequency using CORDIC	
	cordic1 cordic(dac_data, clk, delta, wire_yy2, wire_ss2);
	

	always @(posedge clk)
	begin
		////// Read in data from the MCU.
		// There may be ringing on in_strobe from the MCU.  Wait for strobe_timer
		// before accepting the data.
		if (in_strobe)
			begin
				if (strobe_timer == 10)
					// ringing is over; accept data
					begin
						strobe_timer <= strobe_timer + 1'd1;
						case (in_addr)				// look at address to see where to put data
						0: ;
						1:	begin					// commands - check in_data
							case (in_data)
								0: ;						// FPGA reset
								1: phase_address <= 0;		// reset address for phase data
								2:
									begin
										audio_address <= 0;		// reset address for audio data
										valid_audio_write <= 0;
									end
								default: ;
							endcase
							end
						2: begin
							case (phase_address)	// read in two phase words, 4 bytes each
								0: delta[7:0] <= in_data;	// first four byte tuning word
								1: delta[15:8] <= in_data;
								2: delta[23:16] <= in_data;
								3: delta[31:24] <= in_data;
								// The next four bytes are the secondary tuning word; not used.
								default: ;
							endcase
							phase_address <= phase_address + 1'd1;
							end
						3:	begin
							case (audio_address)	// read in bytes, collect into 4-byte word
								0: audio_write[7:0] <= in_data;	// first four byte tuning word
								1: audio_write[15:8] <= in_data;
								2: audio_write[23:16] <= in_data;
								3:
									begin
										audio_write[31:24] <= in_data;
										valid_audio_write <= 1;
									end
							endcase
							audio_address <= audio_address + 1'd1;
							end
						endcase
					end
				else if (strobe_timer == 15)	// maximum value
					;
				else
					strobe_timer <= strobe_timer + 1'd1;
			end
		else
			begin
				strobe_timer <= 0;	// zero the timer
			end

		////// Second CIC filter/interpolator
		// (y5, s5) -> comb -> (xx2, qq2) -> interpolate -> integrate -> (yy2, ss2)
		case (count_cic_2)
		0:	begin
				count_cic_2 <= count_cic_2 + 1'd1;
				xx2 <= 0;	// stuff a zero sample
				qq2 <= 0;
				if (valid_audio_write)
					// Use count_cic_2 as a clock to write audio data from the MCU to memory
					begin
						valid_audio_write <= 0;
						write_enable <= 1;
					end
			end
		8:	begin
				count_cic_2 <= count_cic_2 + 1'd1;
				xx2 <= 0;	// stuff a zero sample
				qq2 <= 0;
				if (write_enable)
					begin
						write_enable <= 0;
						if (write_addr == MEM_SIZE - 1)
							write_addr <= 0;
						else
							write_addr <= write_addr + 1'd1;
					end
			end
		16:	begin
				count_cic_2 <= count_cic_2 + 1'd1;
				xx2 <= 0;	// stuff a zero sample
				qq2 <= 0;
				////// First CIC filter/interpolator
				// (x0, q0) -> comb -> (x5, q5) -> interpolate -> integrate -> (y5, s5)
				case (count_cic_1)
				// Read audio data from memory, or provide a zero sample
				4:	begin
						count_cic_1 <= count_cic_1 + 1'd1;
						x5 <= 0;	// stuff a zero
						q5 <= 0;
						if (! keying)	// Zero memory sample unless we are keyed
							begin
								start_reading <= 0;
								read_addr <= 0;
								write_addr <= 0;
								x0 <= 0;
								q0 <= 0;
							end
						else if (read_addr == write_addr)	// Is there anything in memory?
							begin		// Memory is empty
								start_reading <= 0;
								x0 <= 0;
								q0 <= 0;
							end
						else if (start_reading || mem_count > MEM_SIZE / 2)
							begin	// There is enough data in memory to start reading it.
								start_reading <= 1;
								// Sign extend 16-bit datum from memory to first CIC word size
								x0 <= {{(35 - 15){audio_read[15]}}, audio_read[15:0]};
								q0 <= {{(35 - 15){audio_read[31]}}, audio_read[31:16]};
							end
						else	// we are writing but not reading memory; provide a zero sample
							begin
								x0 <= 0;
								q0 <= 0;
							end
					end
				interp_cic_1:	// Process the sample (x0, q0) to get (x5, q5)
					begin
						count_cic_1 <= 0;
						if (start_reading)			// Increment memory read address
							begin
								if (read_addr == MEM_SIZE - 1)
									read_addr <= 0;
								else
									read_addr <= read_addr + 1'd1;
							end
						// Process the data from memory
						// Comb for real data
						x1 <= x0 - dx0;
						x2 <= x1 - dx1;			
						x3 <= x2 - dx2;				
						x4 <= x3 - dx3;				
						x5 <= x4 - dx4;			
						dx0 <= x0;
						dx1 <= x1;
						dx2 <= x2;
						dx3 <= x3;
						dx4 <= x4;
						// Comb for imaginary data
						q1 <= q0 - dq0;
						q2 <= q1 - dq1;
						q3 <= q2 - dq2;
						q4 <= q3 - dq3;
						q5 <= q4 - dq4;
						dq0 <= q0;
						dq1 <= q1;
						dq2 <= q2;
						dq3 <= q3;
						dq4 <= q4;
			
					end
				default:
					begin
						count_cic_1 <= count_cic_1 + 1'd1;
						x5 <= 0;	// stuff a zero
						q5 <= 0;
					end
				endcase
				// Integrate the sample (x5, q5) to get the output (y5, s5)
				// Integrator for real data; input is x5
				y1 <= y1 + x5;
				y2 <= y2 + y1;
				y3 <= y3 + y2;
				y4 <= y4 + y3;
				y5 <= y5 + y4;
				// Integrator for imaginary data; input is q5
				s1 <= s1 + q5;
				s2 <= s2 + s1;
				s3 <= s3 + s2;
				s4 <= s4 + s3;
				s5 <= s5 + s4;
				end
		interp_cic_2:
			begin
				count_cic_2 <= 0;
				// Calculate memory used while not writing or reading data
				if (write_addr >= read_addr)
					mem_count <= write_addr - read_addr;
				else
					mem_count <= MEM_SIZE - read_addr + write_addr;
				// Read from CIC 1.
				// Second CIC comb: input is (y5, s5), output is (xx2, qq2)	
				// Comb for real data
				xx1 <= wire_y5 - dxx0;
				xx2 <= xx1 - dxx1;				
				dxx0 <= wire_y5;
				dxx1 <= xx1;
				// Comb for imaginary data
				qq1 <= wire_s5 - dqq0;
				qq2 <= qq1 - dqq1;
				dqq0 <= wire_s5;
				dqq1 <= qq1;
			end
		default:
			begin
				count_cic_2 <= count_cic_2 + 1'd1;
				xx2 <= 0;	// stuff a zero sample
				qq2 <= 0;
			end
		endcase
		// Second CIC integrator
		// Integrator for real data xx2
		yy1 <= yy1 + xx2;
		yy2 <= yy2 + yy1;
		// Integrator for imaginary data qq2
		ss1 <= ss1 + qq2;
		ss2 <= ss2 + ss1;
	end

endmodule

// Memory is MEM_SIZE samples == 36 * 4096 bits == 18,432 bytes == 4608 samples.
// 4608 samples / 48000 samples/sec == 96 milliseconds.
module audio_memory(
	output reg[31:0] q,
	input wire[31:0] data,
	input wire[12:0] write_addr,
	input wire[12:0] read_addr,
	input wire write_enable,
	input wire clock);
	
	parameter MEM_SIZE = 13'd4608;	// memory size in 32-bit samples - REDEFINED ABOVE
	reg[31:0] mem [MEM_SIZE-1:0];
	
	always@(posedge clock)
		begin
			if (write_enable)
				mem[write_addr] <= data;
			q <= mem[read_addr];
		end
endmodule