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

module waveform_gen #
(
    parameter CTRL_REG_ADDR = 32'h00000000,
    parameter NUM_SAMPLES_REG_ADDR = 32'h00000004,
    parameter START_SAMPLE_REG_ADDR = 32'h00000008,
    
    parameter integer AXI_ADDR_WIDTH	= 32,
    parameter integer AXI_DATA_WIDTH	= 32    
)
(
    input  wire       clk,

    // AXI4L Config Interface
    axi4l_intf.slave ctrl_if,
    
    input  wire        start_of_pulse,
    
    input  wire [14:0] dac0_wf_bram_addr,
    input  wire        dac0_wf_bram_clk,
    input  wire [31:0] dac0_wf_bram_din,
    output wire [31:0] dac0_wf_bram_dout,
    input  wire        dac0_wf_bram_en,
    input  wire        dac0_wf_bram_rst,
    input  wire [3:0]  dac0_wf_bram_we,   
 
    input  wire [14:0] dac1_wf_bram_addr,
    input  wire        dac1_wf_bram_clk,
    input  wire [31:0] dac1_wf_bram_din,
    output wire [31:0] dac1_wf_bram_dout,
    input  wire        dac1_wf_bram_en,
    input  wire        dac1_wf_bram_rst,
    input  wire [3:0]  dac1_wf_bram_we, 
 
    input  wire [14:0] dac2_wf_bram_addr,
    input  wire        dac2_wf_bram_clk,
    input  wire [31:0] dac2_wf_bram_din,
    output wire [31:0] dac2_wf_bram_dout,
    input  wire        dac2_wf_bram_en,
    input  wire        dac2_wf_bram_rst,
    input  wire [3:0]  dac2_wf_bram_we, 
 
    input  wire [14:0] dac3_wf_bram_addr,
    input  wire        dac3_wf_bram_clk,
    input  wire [31:0] dac3_wf_bram_din,
    output wire [31:0] dac3_wf_bram_dout,
    input  wire        dac3_wf_bram_en,
    input  wire        dac3_wf_bram_rst,
    input  wire [3:0]  dac3_wf_bram_we,  


    output wire [511:0] jesd_tx
);



reg [15:0] dac0_bram_addr;
wire [127:0] dac0_bram_dout;
reg [15:0] dac1_bram_addr;
wire [127:0] dac1_bram_dout;
reg [15:0] dac2_bram_addr;
wire [127:0] dac2_bram_dout;
reg [15:0] dac3_bram_addr;
wire [127:0] dac3_bram_dout;            


// ------------------------------
// 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
// ------------------------------
wire reset;
reg [31:0]  reg_ctrl;
reg [15:0]  reg_num_samples;
reg [27:0]  reg_start_sample;

always @ (posedge ctrl_if.clk) begin
    if (~ctrl_if.resetn) begin
        reg_ctrl <= 0;
    end else if (wren && waddr[11:0] == CTRL_REG_ADDR) begin
        reg_ctrl <= wdata[15:0];
    end
end

always @ (posedge ctrl_if.clk) begin
    if (~ctrl_if.resetn) begin
        reg_num_samples <= 0;
    end else if (wren && waddr[11:0] == NUM_SAMPLES_REG_ADDR) begin
        reg_num_samples <= wdata[15:0];
    end
end

always @ (posedge ctrl_if.clk) begin
    if (~ctrl_if.resetn) begin
        reg_start_sample <= 0;
    end else if (wren && waddr[11:0] == START_SAMPLE_REG_ADDR) begin
        reg_start_sample <= wdata;
    end
end

always @ (posedge ctrl_if.clk) begin
    if (rden) begin
        if ( raddr[11:0] == CTRL_REG_ADDR ) 
            rdata <= reg_ctrl;       
        if ( raddr[11:0] == NUM_SAMPLES_REG_ADDR ) 
            rdata <= reg_num_samples;   
        if ( raddr[11:0] == START_SAMPLE_REG_ADDR ) 
            rdata <= reg_start_sample;   
    end      
end

assign reset = reg_ctrl[0];

// ------------------------------
// Sample gating
// ------------------------------
reg [16:0] sample_cnt;
reg pulse_active;
reg pulse_active_q;
reg pulse_active_q2;


// Delay event pulse by start sample
reg [27:0] start_sample_cnt;
reg [27:0] start_sample_this_pulse;
reg delay_active;
reg delay_active_q;
reg delay_active_fed;


reg [255:0] jesd_out_reg;
reg [255:0] all_brams_out;

always @ (posedge clk) begin
    if (reset == 1'b1) begin
        start_sample_cnt <= 0;
        delay_active <= 0;
    end else begin

        delay_active_q <= delay_active;
        delay_active_fed <= ~delay_active && delay_active_q;

        if (start_of_pulse) begin
            start_sample_cnt <= reg_start_sample;
            start_sample_this_pulse <= reg_start_sample;
            delay_active <= 1;
        end

        if (delay_active) begin
            start_sample_cnt <= start_sample_cnt - 1;
            if (start_sample_cnt == 0) begin
                delay_active <= 0;
            end
        end

    end
end

always @ (posedge clk) begin
    if (reset == 1'b1) begin
        sample_cnt <= 0;
        pulse_active <= 0;
        pulse_active_q <= 0;
        pulse_active_q2 <= 0;
        
    end else begin
       
        pulse_active_q <= pulse_active;
        pulse_active_q2 <= pulse_active_q;

        if (delay_active_fed) begin
            sample_cnt <= 0;
            pulse_active <= 1;
        end;
            
        if (pulse_active) begin
            sample_cnt <= sample_cnt + 1;
            if (sample_cnt == reg_num_samples-1) begin
                sample_cnt <= 0;
                pulse_active <= 0;
            end
        end

        if (pulse_active_q2) begin
            jesd_out_reg <= all_brams_out;
        end else begin
            jesd_out_reg <= 0;            
        end

    end
end

always @ (posedge clk) begin
    if (pulse_active) begin
        dac0_bram_addr <= dac0_bram_addr + 1;
    end else begin
        dac0_bram_addr <= 0;
    end
end


//assign all_brams_out[16*0+15 + 384 : 16*0 + 384]   = dac3_bram_dout[16*6+15:16*6];  
//assign all_brams_out[16*1+15 + 384 : 16*1 + 384]   = dac3_bram_dout[16*4+15:16*4];    
//assign all_brams_out[16*2+15 + 384 : 16*2 + 384]   = dac3_bram_dout[16*2+15:16*2];    
//assign all_brams_out[16*3+15 + 384 : 16*3 + 384]   = dac3_bram_dout[16*0+15:16*0];    
//assign all_brams_out[16*4+15 + 384 : 16*4 + 384]   = dac3_bram_dout[16*7+15:16*7];    
//assign all_brams_out[16*5+15 + 384 : 16*5 + 384]   = dac3_bram_dout[16*5+15:16*5];    
//assign all_brams_out[16*6+15 + 384 : 16*6 + 384]   = dac3_bram_dout[16*3+15:16*3];    
//assign all_brams_out[16*7+15 + 384 : 16*7 + 384]   = dac3_bram_dout[16*1+15:16*1];    
  
//assign all_brams_out[16*0+15 + 256 : 16*0 + 256]   = dac2_bram_dout[16*6+15:16*6];  
//assign all_brams_out[16*1+15 + 256 : 16*1 + 256]   = dac2_bram_dout[16*4+15:16*4];    
//assign all_brams_out[16*2+15 + 256 : 16*2 + 256]   = dac2_bram_dout[16*2+15:16*2];    
//assign all_brams_out[16*3+15 + 256 : 16*3 + 256]   = dac2_bram_dout[16*0+15:16*0];    
//assign all_brams_out[16*4+15 + 256 : 16*4 + 256]   = dac2_bram_dout[16*7+15:16*7];    
//assign all_brams_out[16*5+15 + 256 : 16*5 + 256]   = dac2_bram_dout[16*5+15:16*5];    
//assign all_brams_out[16*6+15 + 256 : 16*6 + 256]   = dac2_bram_dout[16*3+15:16*3];    
//assign all_brams_out[16*7+15 + 256 : 16*7 + 256]   = dac2_bram_dout[16*1+15:16*1];    
  
assign all_brams_out[16*0+15 + 128 : 16*0 + 128]   = dac1_bram_dout[16*6+15:16*6];  
assign all_brams_out[16*1+15 + 128 : 16*1 + 128]   = dac1_bram_dout[16*4+15:16*4];    
assign all_brams_out[16*2+15 + 128 : 16*2 + 128]   = dac1_bram_dout[16*2+15:16*2];    
assign all_brams_out[16*3+15 + 128 : 16*3 + 128]   = dac1_bram_dout[16*0+15:16*0];    
assign all_brams_out[16*4+15 + 128 : 16*4 + 128]   = dac1_bram_dout[16*7+15:16*7];    
assign all_brams_out[16*5+15 + 128 : 16*5 + 128]   = dac1_bram_dout[16*5+15:16*5];    
assign all_brams_out[16*6+15 + 128 : 16*6 + 128]   = dac1_bram_dout[16*3+15:16*3];    
assign all_brams_out[16*7+15 + 128 : 16*7 + 128]   = dac1_bram_dout[16*1+15:16*1];     
  
assign all_brams_out[16*0+15 +   0 : 16*0 +   0]   = dac0_bram_dout[16*6+15:16*6];  
assign all_brams_out[16*1+15 +   0 : 16*1 +   0]   = dac0_bram_dout[16*4+15:16*4];    
assign all_brams_out[16*2+15 +   0 : 16*2 +   0]   = dac0_bram_dout[16*2+15:16*2];    
assign all_brams_out[16*3+15 +   0 : 16*3 +   0]   = dac0_bram_dout[16*0+15:16*0];    
assign all_brams_out[16*4+15 +   0 : 16*4 +   0]   = dac0_bram_dout[16*7+15:16*7];    
assign all_brams_out[16*5+15 +   0 : 16*5 +   0]   = dac0_bram_dout[16*5+15:16*5];    
assign all_brams_out[16*6+15 +   0 : 16*6 +   0]   = dac0_bram_dout[16*3+15:16*3];    
assign all_brams_out[16*7+15 +   0 : 16*7 +   0]   = dac0_bram_dout[16*1+15:16*1];    

assign dac1_bram_addr = dac0_bram_addr;
//assign dac2_bram_addr = dac0_bram_addr;
//assign dac3_bram_addr = dac0_bram_addr;


// dac2 and dac3 are used for LOs, so just need a constant 1 + j0 output.  The NCO
// inside the AD9081 will be used to turn this into a tone

assign jesd_tx[511:384] = {16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h7FFF, 16'h7FFF, 16'h7FFF, 16'h7FFF};    
assign jesd_tx[383:256] = {16'h0000, 16'h0000, 16'h0000, 16'h0000, 16'h7FFF, 16'h7FFF, 16'h7FFF, 16'h7FFF};   
assign jesd_tx[255:0] = jesd_out_reg;

wf_memory dac0_wf_mem (
  .clka(dac0_wf_bram_clk),    
  .ena(dac0_wf_bram_en),      
  .wea(dac0_wf_bram_we),      
  .addra(dac0_wf_bram_addr[14:2]),
  .dina(dac0_wf_bram_din),    
  .douta(dac0_wf_bram_dout),  
  
  .clkb(clk),
  .enb(1'b1),      
  .web(1'b0),      
  .addrb(dac0_bram_addr),
  .dinb(0),   
  .doutb(dac0_bram_dout)
);    

wf_memory dac1_wf_mem (
  .clka(dac1_wf_bram_clk),    
  .ena(dac1_wf_bram_en),      
  .wea(dac1_wf_bram_we),      
  .addra(dac1_wf_bram_addr[14:2]),
  .dina(dac1_wf_bram_din),    
  .douta(dac1_wf_bram_dout),  
  
  .clkb(clk),
  .enb(1'b1),      
  .web(1'b0),      
  .addrb(dac1_bram_addr),
  .dinb(0),   
  .doutb(dac1_bram_dout)
);   

//wf_memory dac2_wf_mem (
//  .clka(dac2_wf_bram_clk),    
//  .ena(dac2_wf_bram_en),      
//  .wea(dac2_wf_bram_we),      
//  .addra(dac2_wf_bram_addr[14:2]),
//  .dina(dac2_wf_bram_din),    
//  .douta(dac2_wf_bram_dout),  
  
//  .clkb(clk),
//  .enb(1'b1),      
//  .web(1'b0),      
//  .addrb(dac2_bram_addr),
//  .dinb(0),   
//  .doutb(dac2_bram_dout)
//);      

//wf_memory dac3_wf_mem (
//  .clka(dac3_wf_bram_clk),    
//  .ena(dac3_wf_bram_en),      
//  .wea(dac3_wf_bram_we),      
//  .addra(dac3_wf_bram_addr[14:2]),
//  .dina(dac3_wf_bram_din),    
//  .douta(dac3_wf_bram_dout),  
  
//  .clkb(clk),
//  .enb(1'b1),      
//  .web(1'b0),      
//  .addrb(dac3_bram_addr),
//  .dinb(0),   
//  .doutb(dac3_bram_dout)
//);            


endmodule


`resetall
