//
// LpGBT dual port memory control
//
// v1.0 jb/may/2020
//
//
//***************************************************************************
//
//  LpGBT_DPM_control   
//                [ 11:  0] DP_write_address
//                [ 19: 12] word_count
//                [ 21: 20] operation (0-fill_DP_w0, 
//                                     1-whole downlink data operation, 
//                                     2-EC, 
//                                     3-IC)
//                     [22] play_out_flag
//                [ 27: 23] play_count (0-always)
//                  
//                [ 59: 28] word1 {EC[1:0]}, IC[1:0], data[31:0]}
//                [ 99: 64] word2
//                [135:100] word3
//                [171:136] word4 
//                [207:172] word5 
//                [243:208] word6 
//                [279:244] word7 
//
//***************************************************************************
//

		  
module LpGBT_DPM
 (  
  //module state machine inputs
  input             clk,
  input             rst,
  input             op_start,
  input wire[279:0] LpGBT_DPM_control,
		  
  //Ec & Ic data fields
  output   reg[1:0] downlinkEcData,
  output   reg[1:0] downlinkIcData, 
			 
  //downlink user data       
  output  reg[31:0] downlinkUserData
 );
		
//variables			
reg        op_starts, op_start_reg, rst_reg, rst_flg; 
reg        op_in_progress, wr_op_done;
reg        play;
reg  [11:0]wraddress, rdaddress;
reg   [7:0]word_count;
reg [252:0]data_reg;
reg        wr_en, wr_addr_inc;
wire [35:0]downlinkUserData_dp;
reg   [1:0]op_type;

//main body
always @(posedge clk)	
 begin	
//operation starts
        op_start_reg <= op_start;
        op_starts    <= ~op_start_reg & op_start;
//reset flag
        rst_reg <= rst;
        rst_flg <= ~rst_reg & rst;
 end

//operation in progress flag 
always @(posedge clk)	
 begin	
  if         (rst_flg) op_in_progress <= 1'b0;
  else
  if       (op_starts) op_in_progress <= 1'b1;
  else if (wr_op_done) op_in_progress <= 1'b0;
 end

//write address register
always @(posedge clk)	
 begin	
  if          (rst_flg) wraddress <= 12'h000;
  else
  if        (op_starts) wraddress <= LpGBT_DPM_control[11:0];
  else if (wr_addr_inc) wraddress <= wraddress + 12'h001;
 end

//data register, play flag
always @(posedge clk)	
 begin	
  if(rst_flg)
   begin  
    data_reg <= {252{1'b0}};
    play     <= 1'b0;
   end
  else
   begin
    if                           (op_starts) data_reg <= LpGBT_DPM_control[279:28];
    else if ((wr_addr_inc)&(op_type==2'b01)) data_reg <= {{36{1'b0}}, data_reg[251:36]}; //all signals operation
    else if ((wr_addr_inc)&(op_type==2'b10)) data_reg <= {{ 2{1'b1}}, data_reg[251: 2]}; //EC operation
    else if ((wr_addr_inc)&(op_type==2'b11)) data_reg <= {{ 2{1'b1}}, data_reg[251: 2]}; //IC operation
	
	if (op_starts)  play <= LpGBT_DPM_control[22];	
	
	if (op_start)op_type <= LpGBT_DPM_control[21:20];
	end
 end

//write enable and write address increment flag 
always @(posedge clk)	
 begin	
  if             (rst_flg) wr_en <= 1'b0;
  else
  if           (op_starts) wr_en <= 1'b1;
  else if (op_in_progress) wr_en <= ~wr_en;
  else                     wr_en <= 1'b0;
  
                     wr_addr_inc <= wr_en;
 end

//operation done flag
always @(posedge clk)	
 begin	
  if      (rst_flg) 
   begin
    wr_op_done <= 1'b0;
    word_count <= 8'h00;
   end
  else
   begin
    if             (op_starts) word_count <= LpGBT_DPM_control[19:12];
	 else if           (wr_en) word_count <= word_count - 8'h01;
	 else if (~op_in_progress) word_count <= 8'h00;

    if             (op_starts) wr_op_done <= 1'b0;
	else if(word_count==8'h00) wr_op_done <= 1'b1;
   end
 end


//
//Dual port memory for LpGBT downlink data
//
wire [35:0] data_mux;

    assign data_mux = (op_type==2'b01) ? {                  data_reg[35:0]} :  //all downlink signals operation
                      (op_type==2'b10) ? {       data_reg[1:0], {34{1'b0}}} :  //EC  operation
                      (op_type==2'b11) ? {2'b11, data_reg[1:0], {32{1'b0}}} :  //IC  operation
					                                           36'h000000000;  //DP fill with zeros
LpGBT_DP m1 
	(
		.data      (data_mux),          //  ram_input.datain
		.wraddress (wraddress),         //           .wraddress
		.rdaddress (rdaddress),         //           .rdaddress
		.wren      (wr_en),             //           .wren
		.wrclock   (clk),               //           .wrclock
		.rdclock   (clk),               //           .rdclock
		.q         (downlinkUserData_dp)// ram_output.dataout
	);


//read address register
reg [4:0]play_count;
reg      read_op;
reg      always_play;
always @(posedge clk)	
 begin	
  if      (rst_flg) 
   begin
    read_op     <= 1'b0;
    play_count  <= 5'b00000;
    rdaddress   <= 12'h000;
	always_play <= 1'b0;
   end
  else
   begin
//read address register   
   if (read_op) rdaddress <= rdaddress + 12'h001;
   else         rdaddress <= 12'h000;
   
//always play flag
  if (op_starts & LpGBT_DPM_control[22]) always_play <=  (LpGBT_DPM_control[27:23]==5'b00000);

//play count   
   if          (wr_op_done & op_in_progress )  play_count <= LpGBT_DPM_control[27:23]; 
   else if   (read_op & (rdaddress==12'hfff))  play_count <= play_count - 5'b0001;
   else if                         (~read_op)  play_count <= 5'b00000;
   
//play operation 
   if      (op_starts | rst_flg)                               read_op <= 1'b0;
   else if (wr_op_done & op_in_progress & play)                read_op <= 1'b1;
   else if (~always_play & read_op & (play_count==5'b00000))   read_op <= 1'b0;
   end
 end
	
// 
//Downlink data and fields
//
always @(posedge clk)	
begin	
 if (rst_flg | (read_op==1'b0))
  begin
   downlinkEcData   <= 2'b00;
   downlinkIcData   <= 2'b11;
   downlinkUserData <= 32'haaaaaaaa;
  end
 else 
  begin
   downlinkEcData   <= downlinkUserData_dp[35:34];
   downlinkIcData   <= downlinkUserData_dp[33:32];
   downlinkUserData <= downlinkUserData_dp[31: 0];
  end
end	

endmodule				