// (C) 1992-2016 Altera Corporation. All rights reserved.                         
// Your use of Altera Corporation's design tools, logic functions and other       
// software and tools, and its AMPP partner logic functions, and any output       
// files any of the foregoing (including device programming or simulation         
// files), and any associated documentation or information are expressly subject  
// to the terms and conditions of the Altera Program License Subscription         
// Agreement, Altera MegaCore Function License Agreement, or other applicable     
// license agreement, including, without limitation, that your use is for the     
// sole purpose of programming logic devices manufactured by Altera and sold by   
// Altera or its authorized distributors.  Please refer to the applicable         
// agreement for further details.                                                 
    


//
// Helper module to handle bursting large sequential blocks of memory to or 
// from global memory.
//

/*****************************************************************************/
// Burst read master:
//   Keeps a local buffer populated with data from a sequential block of
//   global memory.  The block of global memory is specified by a base address
//   and size.
/*****************************************************************************/
module lsu_burst_read_master (
	clk,
	reset,
  o_active, //Debugging signal

	// control inputs and outputs
	control_fixed_location,
	control_read_base,
	control_read_length,
	control_go,
	control_done,
	control_early_done,
	
	// user logic inputs and outputs
	user_read_buffer,
	user_buffer_data,
	user_data_available,
	
	// master inputs and outputs
	master_address,
	master_read,
	master_byteenable,
	master_readdata,
	master_readdatavalid,
	master_burstcount,
	master_waitrequest
);
   /*************
   * Parameters *
   *************/
	parameter DATAWIDTH = 32;
	parameter MAXBURSTCOUNT = 4;
	parameter BURSTCOUNTWIDTH = 3;
	parameter BYTEENABLEWIDTH = 4;
	parameter ADDRESSWIDTH = 32;
	parameter FIFODEPTH = 32;
	parameter FIFODEPTH_LOG2 = 5;
	parameter FIFOUSEMEMORY = 1;  // set to 0 to use LEs instead
	parameter READTHRESHOLD = FIFODEPTH - MAXBURSTCOUNT - 4;
	
   /********
   * Ports *
   ********/
	input clk;
	input reset;

  output o_active;

	// control inputs and outputs
	input control_fixed_location;
	input [ADDRESSWIDTH-1:0] control_read_base;
	input [ADDRESSWIDTH-1:0] control_read_length;
	input control_go;
	output wire control_done;
	output wire control_early_done;  // don't use this unless you know what you are doing, it's going to fire when the last read is posted, not when the last data returns!
	
	// user logic inputs and outputs
	input user_read_buffer;
	output wire [DATAWIDTH-1:0] user_buffer_data;
	output wire user_data_available;
	
	// master inputs and outputs
	input master_waitrequest;
	input master_readdatavalid;
   input [DATAWIDTH-1:0] master_readdata;
	output wire [ADDRESSWIDTH-1:0] master_address;
	output wire master_read;
	output wire [BYTEENABLEWIDTH-1:0] master_byteenable;
	output wire [BURSTCOUNTWIDTH-1:0] master_burstcount;
	
   /***************
   * Architecture *
   ***************/
	// internal control signals
	reg control_fixed_location_d1;
	wire fifo_empty;
	reg [ADDRESSWIDTH-1:0] address;
	reg [ADDRESSWIDTH-1:0] length;
	reg [FIFODEPTH_LOG2-1:0] reads_pending;
	wire increment_address;
	wire [BURSTCOUNTWIDTH-1:0] burst_count;
	wire [BURSTCOUNTWIDTH-1:0] first_short_burst_count;
	wire first_short_burst_enable;
	wire [BURSTCOUNTWIDTH-1:0] final_short_burst_count;
	wire final_short_burst_enable;
	wire [BURSTCOUNTWIDTH-1:0] burst_boundary_word_address;
	wire too_many_reads_pending;
	wire [FIFODEPTH_LOG2-1:0] fifo_used;

	// registering the control_fixed_location bit
	always @ (posedge clk or posedge reset)
	begin
		if (reset == 1)
		begin
			control_fixed_location_d1 <= 0;
		end
		else
		begin
			if (control_go == 1)
			begin
				control_fixed_location_d1 <= control_fixed_location;
			end
		end
	end

	// master address logic 
	always @ (posedge clk or posedge reset)
	begin
		if (reset == 1)
		begin
			address <= 0;
		end
		else
		begin
			if(control_go == 1)
			begin
				address <= control_read_base;
			end
			else if((increment_address == 1) & (control_fixed_location_d1 == 0))
			begin
				address <= address + (burst_count * BYTEENABLEWIDTH);  // always performing word size accesses, increment by the burst count presented
			end
		end
	end

	// master length logic
	always @ (posedge clk or posedge reset)
	begin
		if (reset == 1)
		begin
			length <= 0;
		end
		else
		begin
			if(control_go == 1)
			begin
				length <= control_read_length;
			end
			else if(increment_address == 1)
			begin
				length <= length - (burst_count * BYTEENABLEWIDTH);  // always performing word size accesses, decrement by the burst count presented
			end
		end
	end	

	// controlled signals going to the master/control ports
	assign master_address = address;
	assign master_byteenable = -1;  // all ones, always performing word size accesses
	assign master_burstcount = burst_count;
	assign control_done = (length == 0) & (reads_pending == 0);  // need to make sure that the reads have returned before firing the done bit
	assign control_early_done = (length == 0);  // advanced feature, you should use 'control_done' if you need all the reads to return first
	assign master_read = (too_many_reads_pending == 0) & (length != 0);
	assign burst_boundary_word_address = ((address / BYTEENABLEWIDTH) & (MAXBURSTCOUNT - 1));
	assign first_short_burst_enable = (burst_boundary_word_address != 0);
	assign final_short_burst_enable = (length < (MAXBURSTCOUNT * BYTEENABLEWIDTH));
	
	assign first_short_burst_count = ((burst_boundary_word_address & 1'b1) == 1'b1)? 1 :  // if the burst boundary isn't a multiple of 2 then must post a burst of 1 to get to a multiple of 2 for the next burst
									  (((MAXBURSTCOUNT - burst_boundary_word_address) < (length / BYTEENABLEWIDTH))?
									  (MAXBURSTCOUNT - burst_boundary_word_address) : (length / BYTEENABLEWIDTH));
	assign final_short_burst_count = (length / BYTEENABLEWIDTH);
	
	assign burst_count = (first_short_burst_enable == 1)? first_short_burst_count :  // this will get the transfer back on a burst boundary, 
						 (final_short_burst_enable == 1)? final_short_burst_count : MAXBURSTCOUNT;
	assign increment_address = (too_many_reads_pending == 0) & (master_waitrequest == 0) & (length != 0);
	assign too_many_reads_pending = (reads_pending + fifo_used) >= READTHRESHOLD;  // make sure there are fewer reads posted than room in the FIFO

	// tracking FIFO
	always @ (posedge clk or posedge reset)
	begin
		if (reset == 1)
		begin
			reads_pending <= 0;
		end
		else
		begin
			if(increment_address == 1)
			begin
				if(master_readdatavalid == 0)
				begin
					reads_pending <= reads_pending + burst_count;
				end
				else
				begin
					reads_pending <= reads_pending + burst_count - 1;  // a burst read was posted, but a word returned
				end			
			end
			else
			begin
				if(master_readdatavalid == 0)
				begin
					reads_pending <= reads_pending;  // burst read was not posted and no read returned
				end
				else
				begin
					reads_pending <= reads_pending - 1;  // burst read was not posted but a word returned
				end				
			end
		end
	end

  assign o_active = |reads_pending;

	// read data feeding user logic	
	assign user_data_available = !fifo_empty;
	scfifo the_master_to_user_fifo (
		.aclr (reset),
		.clock (clk),
		.data (master_readdata),
		.empty (fifo_empty),
		.q (user_buffer_data),
		.rdreq (user_read_buffer),
		.usedw (fifo_used),
		.wrreq (master_readdatavalid),
      .almost_empty(),
      .almost_full(),
      .full(),
      .sclr()
	);
	defparam the_master_to_user_fifo.lpm_width = DATAWIDTH;
	defparam the_master_to_user_fifo.lpm_widthu = FIFODEPTH_LOG2;
	defparam the_master_to_user_fifo.lpm_numwords = FIFODEPTH;
	defparam the_master_to_user_fifo.lpm_showahead = "ON";
	defparam the_master_to_user_fifo.use_eab = (FIFOUSEMEMORY == 1)? "ON" : "OFF";
	defparam the_master_to_user_fifo.add_ram_output_register = "OFF";
	defparam the_master_to_user_fifo.underflow_checking = "OFF";
	defparam the_master_to_user_fifo.overflow_checking = "OFF";

  initial
    if ( READTHRESHOLD > FIFODEPTH ||
         READTHRESHOLD > FIFODEPTH - 4 ||
         READTHRESHOLD < 1 )
     $error("Invalid FIFODEPTH and MAXBURSTCOUNT comination.  Produced READTHRESHOLD = %d\n",READTHRESHOLD);

endmodule
