`resetall
`timescale 1ns / 1ps
`default_nettype none

// Outputs 4 IQ samples in parallel for connection to DAC interface

// start_pulse - Kicks off output for a single pulse, expected to be a single clock wide
// start_table_irq - This is used to indicate to software that playback from the top of the sequence table has begin
// half_table_irq - This is used to indicate to software that playback from the mid point of the sequence table has begin
//                  The mid point is defined as (n_total_chips >> 1)
// These interrupts are used in combination to enable software to continually update the sequence table in a ping pong 
// fashion while the system is operating

/*
* ADDR=0x0000 Control Register (reg_ctrl)  
* Currently unused
*/

/*
* ADDR=0x0004 Start Frequency Register (reg_start_freq)  
* Sets the value of the lowest frequency chip, final chip frequincies are calculated using this value, 
* the delta frequency, and the sequence number from the ram
* Bit 31:0: Frequency
*/

/*
* ADDR=0x0008 Chip Description 0 (reg_chips_0)  
* Bit 15:0: Number of total chips in the sequence table
*/

/*
* ADDR=0x000C Chip Description 1 (reg_chips_1)  
* Bit 15:0: Number of DAC samples per chip divided by 4 (due to parallel output of 4 samples)
* Bit 31:16: Number of Chips per Pulse
*/

/*
* ADDR=0x0010 Delta Frequency Register (reg_start_freq)  
* Sets the delta frequency value between chips, final chip frequincies are calculated using this value, 
* the delta frequency, and the sequence number from the ram
* Bit 31:0: Delta Frequency
*/

/*
* ADDR=0x0014 Currently Unused
*/

/*
* ADDR=0x0018 Sequence Memory Write Address
* Sets the write address of the sequence memory
* Bit 15:0: BRAM Write Address 
*/

/*
* ADDR=0x001C Sequence Memory Write Data
* Write a value into the sequence BRAM, and automatically increments the address.  
* Repeated writes to this registers store values in sequential address in the BRAM
* Bit 15:0: BRAM Write Data 
*/

/*
* ADDR=0x0020 Start Phase Memory Write Address
* Sets the write address of the sequence memory
* Bit 15:0: BRAM Write Address 
*/

/*
* ADDR=0x0024 Start Phase Memory Write Data
* Write a value into the sequence BRAM, and automatically increments the address.  
* Repeated writes to this registers store values in sequential address in the BRAM
* Bit 15:0: BRAM Write Data 
*/

module gen_ofdm # (
    parameter integer AXI_ADDR_WIDTH	= 32,
    parameter integer AXI_DATA_WIDTH	= 32	
)
(
    input  wire        clk,
    input  wire        reset,
    axi4l_intf.slave   ctrl_if,

	input  wire 	   start_pulse,

    output wire         start_table_irq,
    output wire         half_table_irq,
    output wire [127:0] iq_out,
	output wire         iq_out_valid
);

// ------------------------------
// BRAM for holding sequence
// ------------------------------
wire [15:0] read_addr;
wire [15:0] read_sequence;
reg bram_we;
reg [15:0] bram_waddr;
reg [15:0] bram_wdata;

ofdm_sequence_ram sequence_ram (
  .clka(ctrl_if.clk), 
  .ena(1'b1),
  .wea(bram_we),    
  .addra(bram_waddr),
  .dina(bram_wdata), 
  .clkb(clk),
  .enb(1'b1),
  .addrb(read_addr),
  .doutb(read_sequence) 
);

wire [15:0] read_phase;
reg phase_bram_we;
reg [15:0] phase_bram_waddr;
reg [15:0] phase_bram_wdata;

ofdm_sequence_ram phase_ram (
  .clka(ctrl_if.clk), 
  .ena(1'b1),
  .wea(phase_bram_we),    
  .addra(phase_bram_waddr),
  .dina(phase_bram_wdata), 
  .clkb(clk),
  .enb(1'b1),
  .addrb(read_addr),
  .doutb(read_phase) 
);

// ------------------------------
// AXIL Decode
// ------------------------------
wire [AXI_ADDR_WIDTH-1 : 0]		raddr;
wire [AXI_ADDR_WIDTH-1 : 0]		waddr;
wire 						    rden;
wire 						    wren;
wire [AXI_DATA_WIDTH-1 : 0]		wdata;
reg [AXI_DATA_WIDTH-1 : 0]		rdata;


axil_slave 
# (
	.DATA_WIDTH(AXI_DATA_WIDTH),
	.ADDR_WIDTH(AXI_ADDR_WIDTH)
) axil_slave_i 
(
	// AXIL Slave
	.S_AXI_ACLK(ctrl_if.clk),
	.S_AXI_ARESETN(ctrl_if.resetn),
	.S_AXI_AWADDR(ctrl_if.awaddr),
	.S_AXI_AWPROT(ctrl_if.awprot),
	.S_AXI_AWVALID(ctrl_if.awvalid),
	.S_AXI_AWREADY(ctrl_if.awready),
	.S_AXI_WDATA(ctrl_if.wdata),
	.S_AXI_WSTRB(ctrl_if.wstrb),
	.S_AXI_WVALID(ctrl_if.wvalid),
	.S_AXI_WREADY(ctrl_if.wready),
	.S_AXI_BRESP(ctrl_if.bresp),
	.S_AXI_BVALID(ctrl_if.bvalid),
	.S_AXI_BREADY(ctrl_if.bready),
	.S_AXI_ARADDR(ctrl_if.araddr),
	.S_AXI_ARPROT(ctrl_if.arprot),
	.S_AXI_ARVALID(ctrl_if.arvalid),
	.S_AXI_ARREADY(ctrl_if.arready),
	.S_AXI_RDATA(ctrl_if.rdata),
	.S_AXI_RRESP(ctrl_if.rresp),
	.S_AXI_RVALID(ctrl_if.rvalid),
	.S_AXI_RREADY(ctrl_if.rready),	
	
	.raddr(raddr),
	.waddr(waddr),
	.wren(wren),
    .rden(rden),
	.wdata(wdata),
	.rdata(rdata)
);

// ------------------------------
// Config Registers
// ------------------------------
reg [31:0]  reg_ctrl;
reg [31:0]  reg_start_freq;
reg [31:0]  reg_delta_freq;
reg [31:0]  reg_delta_phase;
reg [31:0]  reg_chips_0;
reg [31:0]  reg_chips_1;

always @ (posedge ctrl_if.clk) begin
    if (~ctrl_if.resetn) begin
        reg_ctrl <= 0;
        reg_start_freq <= 0;
		reg_delta_freq <= 0;
        reg_chips_0 <= 0;
        reg_chips_1 <= 0;
		reg_delta_phase <= 0;
		bram_we <= 1'b0;
		bram_waddr <= 0;
		bram_wdata <= 0;
		phase_bram_we <= 1'b0;
		phase_bram_waddr <= 0;
		phase_bram_wdata <= 0;		
    end else begin

		bram_we <= 1'b0;
		if (bram_we) begin
			bram_waddr <= bram_waddr + 1;
		end

		phase_bram_we <= 1'b0;
		if (phase_bram_we) begin
			phase_bram_waddr <= phase_bram_waddr + 1;
		end		

        if (wren) begin
            if (waddr[11:0] == 'h000) begin
                reg_ctrl <= wdata;
            end
            if (waddr[11:0] == 'h004) begin
                reg_start_freq <= wdata;
            end
            if (waddr[11:0] == 'h008) begin
                reg_chips_0 <= wdata;
            end 
            if (waddr[11:0] == 'h00C) begin
                reg_chips_1 <= wdata;
            end      
            if (waddr[11:0] == 'h010) begin
                reg_delta_freq <= wdata;
            end    
            if (waddr[11:0] == 'h014) begin
                reg_delta_phase <= wdata;
            end				
			if (waddr[11:0] == 'h018) begin
                bram_waddr <= wdata;
            end				  
            if (waddr[11:0] == 'h01C) begin
				bram_we <= 1'b1;
                bram_wdata <= wdata;
            end				
			if (waddr[11:0] == 'h020) begin
                phase_bram_waddr <= wdata;
            end				  
            if (waddr[11:0] == 'h024) begin
				phase_bram_we <= 1'b1;
                phase_bram_wdata <= wdata;
            end													                       
        end
    end
end

wire [15:0] n_total_chips = reg_chips_0[15:0];

wire [15:0] n_samp_per_chip = reg_chips_1[15:0];
wire [15:0] n_chip_per_pulse = reg_chips_1[31:16];

// ------------------------------
// Bla
// ------------------------------
reg [24:0] pulse_cnt;
reg [15:0] chip_cnt;
reg [15:0] chip_ind;
reg [15:0] chip_per_pulse_cnt;
reg pulse_active;
wire set_phase;
reg start_irq;
reg half_irq;

assign read_addr = chip_ind;
assign start_table_irq = start_irq;
assign half_table_irq = half_irq;

always @ (posedge clk) begin
	if (reset) begin
		chip_cnt <= 0;
		chip_ind <= 0;
        chip_per_pulse_cnt <= 0;
		pulse_active <= 1'b0;
	end else begin
		if (start_pulse) begin
			chip_cnt <= 0;
            chip_per_pulse_cnt <= 0;
			pulse_active <= 1'b1;
		end

		if (pulse_active) begin
			chip_cnt <= chip_cnt + 1;
			if (chip_cnt == n_samp_per_chip - 1) begin
				chip_cnt <= 0;
				chip_ind <= chip_ind + 1;
                chip_per_pulse_cnt <= chip_per_pulse_cnt + 1;
				if (chip_per_pulse_cnt == n_chip_per_pulse - 1) begin
					chip_per_pulse_cnt <= 0;
					pulse_active <= 1'b0;
                    if (chip_ind == n_total_chips-1) begin
                        chip_ind <= 0;
                    end
				end
			end
		end

        start_irq <= 1'b0;
        if (pulse_active) begin
            if (chip_cnt == 0) begin
                if (chip_ind == 0) begin
                    start_irq <= 1'b1;
                end
            end
        end

        half_irq <= 1'b0;
        if (pulse_active) begin
            if (chip_cnt == 0) begin
                if (chip_ind == (n_total_chips >> 1)) begin
                    half_irq <= 1'b1;
                end
            end
        end        


	end
end

wire [47:0] mult_out;
wire [31:0] chip_delta_freq;
ofdm_freq_mult freq_mult (
  .CLK(clk), 
  .A(reg_delta_freq),  
  .B(read_sequence),
  .P(mult_out)
);
assign chip_delta_freq = mult_out[31:0];

reg [31:0] chip_freq;
reg [15:0] read_phase_q;
reg [15:0] read_phase_q2;
always @ (posedge clk) begin
	chip_freq <= chip_delta_freq + reg_start_freq;
    read_phase_q <= read_phase;
    read_phase_q2 <= read_phase_q;
end

assign set_phase = (chip_cnt == 0) ? pulse_active : 1'b0;

wire pulse_active_delayed;
delay_shift_register # (
    .DELAY_CYCLES(4)
) delay_valid (
  .clk(clk), 
  .reset(reset),
  .data_in(pulse_active),    
  .data_out(pulse_active_delayed)
);

wire set_phase_delayed;
delay_shift_register # (
    .DELAY_CYCLES(4)
) delay_set_phase (
  .clk(clk), 
  .reset(reset),
  .data_in(set_phase),    
  .data_out(set_phase_delayed)
);


gen_sine gen_sine_i (
    .clk(clk),
    .reset(reset),  
    .set_phase(set_phase_delayed),  
	.valid(pulse_active_delayed),
    .phase({read_phase_q2, 16'h0000}),
    .frequency(chip_freq),
    .iq_out(iq_out),
	.iq_out_valid(iq_out_valid)
);

endmodule
`resetall

