/*
  Legal Notice: (C)2009 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 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.
*/
/*

  Author:  JCJB
  Date:  08/13/2010

  Version 1.1

  This block is responsible determine the appropriate burst count based on the
  master length register as well as the buffer watermark and eop/early termination
  conditions.

  Within this block is a burst counter which is used to control when the next burst
  is started.  This down counter is loaded with whatever burst count is presented
  to the fabric and counts down when waitrequest is deasserted.  When it reaches 1
  it can either start another burst or reach 0.  When the counter reaches 0 this is
  considered the idle state which can occur if there is not enough data buffered to
  start another burst.

  During write bursts the address and burst count must be held for all the beats.
  This block will register the address and burst count to keep these signals
  held constant to the fabric.  This block will not begin a burst until enough
  data has been buffered to start the burst so it will assert the stall signal
  to keep the write master from advancing to the next word (just like waitrequest)
  and will filter the write signal accordingly. 


  Revision History:

  1.0 Initial version
  
  1.1 Added sw_stop and stopped so that the write master will not be stopped in the middle of a burst write transaction

*/


// synthesis translate_off
`timescale 1ns / 1ps
// synthesis translate_on

// turn off superfluous verilog processor warnings 
// altera message_level Level1 
// altera message_off 10034 10035 10036 10037 10230 10240 10030 


module write_burst_control (
  clk,
  reset,
  sw_reset,
  sw_stop,

  length,
  eop_enabled,
  eop,
  early_termination,
  address_in,
  write_in,
  max_burst_count,
  write_fifo_used,
  waitrequest,
  short_first_access_enable,
  short_last_access_enable,
  short_first_and_last_access_enable,

  address_out,
  write_out,
  burst_count,
  stall,
  reset_taken,
  stopped
);

  parameter BURST_ENABLE = 1;  // set to 0 to hardwire the address and write signals straight out
  parameter BURST_COUNT_WIDTH = 3;
  parameter WORD_SIZE = 4;
  parameter WORD_SIZE_LOG2 = 2;
  parameter ADDRESS_WIDTH = 32;
  parameter LENGTH_WIDTH = 32;
  parameter WRITE_FIFO_USED_WIDTH = 5;
  parameter BURST_WRAPPING_SUPPORT = 1;  // set 1 for on, set 0 for off.  This parameter can't be enabled when the master supports programmable bursting.
  localparam BURST_OFFSET_WIDTH = (BURST_COUNT_WIDTH == 1)? 1: (BURST_COUNT_WIDTH-1);

  input clk;
  input reset;
  input sw_reset;
  input sw_stop;

  input [LENGTH_WIDTH-1:0] length;
  input eop_enabled;
  input eop;
  input early_termination;
  input [ADDRESS_WIDTH-1:0] address_in;
  input write_in;
  input [BURST_COUNT_WIDTH-1:0] max_burst_count;  // will be either a hardcoded input or programmable
  input [WRITE_FIFO_USED_WIDTH:0] write_fifo_used;  // using the fifo full MSB as well
  input waitrequest;  // this needs to be the waitrequest from the fabric and not the byte enable generator since partial transfers count as burst beats
  input short_first_access_enable;
  input short_last_access_enable;
  input short_first_and_last_access_enable;

  output wire [ADDRESS_WIDTH-1:0] address_out;
  output wire write_out;
  output wire [BURST_COUNT_WIDTH-1:0] burst_count;
  output wire stall;  // need to issue a stall if there isn't enough data buffered to start a burst
  output wire reset_taken;  // if a reset occurs in the middle of a burst larger than 1 then the write master needs to know that the burst hasn't completed yet
  output wire stopped;  // if a stop occurs in the middle of a burst larger than 1 then the write master needs to know that the burst hasn't completed yet


  reg [ADDRESS_WIDTH-1:0] address_d1;
  reg [BURST_COUNT_WIDTH-1:0] burst_counter;  // interal statemachine register
  wire idle_state;
  wire decrement_burst_counter;
  wire ready_during_idle_state;  // when there is enough data buffered to start up the burst counter state machine again
  wire ready_for_quick_burst;    // when there is enough data bufferred to start another burst immediately 
  wire burst_begin_from_idle_state;
  wire burst_begin_quickly;      // start another burst immediately after the previous burst completes
  wire burst_begin;
  wire burst_of_one_enable;      // asserted when partial word accesses are occuring or the last early termination word is being written out
  wire [BURST_COUNT_WIDTH-1:0] short_length_burst;
  wire [BURST_COUNT_WIDTH-1:0] short_packet_burst;
  wire short_length_burst_enable;
  wire short_early_termination_burst_enable;
  wire short_packet_burst_enable;
  wire [3:0] mux_select;
  reg [BURST_COUNT_WIDTH-1:0] internal_burst_count;
  reg [BURST_COUNT_WIDTH-1:0] internal_burst_count_d1;
  reg packet_complete;
  wire [BURST_OFFSET_WIDTH-1:0] burst_offset;



  always @ (posedge clk or posedge reset)
  begin
    if (reset)
    begin
      packet_complete <= 0;
    end
    else
    begin
      if ((packet_complete == 1) & (write_fifo_used == 0))
      begin
        packet_complete <= 0;
      end
      else if (eop == 1)
      begin
        packet_complete <= 1;
      end
    end
  end


  always @ (posedge clk or posedge reset)
  begin
    if (reset)
    begin
      address_d1 <= 0;
    end
    else if (burst_begin == 1)
    begin
      address_d1 <= (burst_begin_quickly == 1)? (address_in + WORD_SIZE) : address_in;
    end
  end


  always @ (posedge clk or posedge reset)
  begin
    if (reset)
    begin
      burst_counter <= 0;
    end
    else
    if ((burst_begin == 1) & (sw_reset == 0) & (sw_stop == 0))  // for reset and stop we need to let the burst complete so the fabric doesn't lock up
    begin
      burst_counter <= internal_burst_count;
    end
    else if (decrement_burst_counter == 1)
    begin
      burst_counter <= burst_counter - 1'b1;
    end
  end


  always @ (posedge clk or posedge reset)
  begin
    if (reset)
    begin
      internal_burst_count_d1 <= 0;
    end
    else if (burst_begin == 1)
    begin
      internal_burst_count_d1 <= internal_burst_count;
    end
  end


  // state machine status and control
  assign idle_state = (burst_counter == 0);  // any time idle_state is set then there is no burst underway
  assign decrement_burst_counter = (idle_state == 0) & (waitrequest == 0);

  // control for all the various cases that a burst of one beat needs to be posted
  assign burst_offset = address_in[BURST_OFFSET_WIDTH+WORD_SIZE_LOG2-1:WORD_SIZE_LOG2];
  assign burst_of_one_enable = (short_first_access_enable == 1) | (short_last_access_enable == 1) | (short_first_and_last_access_enable == 1) | (early_termination == 1) |
                               ((BURST_WRAPPING_SUPPORT == 1) & (idle_state == 1) & (burst_offset != 0)) |  // need to make sure bursts start on burst boundaries
                               ((BURST_WRAPPING_SUPPORT == 1) & (idle_state == 0) & (burst_offset != (max_burst_count - 1)));  // need to make sure bursts start on burst boundaries
  assign short_length_burst_enable = ((length >> WORD_SIZE_LOG2) < max_burst_count) & (eop_enabled == 0) & (burst_of_one_enable == 0);
  assign short_early_termination_burst_enable = ((length >> WORD_SIZE_LOG2) < max_burst_count) & (eop_enabled == 1) & (burst_of_one_enable == 0);  // trim back the burst count regardless if there is enough data buffered for a full burst
  assign short_packet_burst_enable = (short_early_termination_burst_enable == 0) & (eop_enabled == 1) & (packet_complete == 1) & (write_fifo_used < max_burst_count) & (burst_of_one_enable == 0);

  // various burst amounts that are not the max burst count or 1 that feed the internal_burst_count mux.  short_length_burst is used when short_length_burst_enable or short_early_termination_burst_enable is asserted.
  assign short_length_burst = (length >> WORD_SIZE_LOG2) & {(BURST_COUNT_WIDTH-1){1'b1}};
  assign short_packet_burst = (write_fifo_used & {(BURST_COUNT_WIDTH-1){1'b1}});

  // since the write master may not have enough data buffered in the FIFO to start a burst the FIFO fill level must be checked before starting another burst
  assign ready_during_idle_state = (burst_of_one_enable == 1) |  // burst of one is only enabled when there is data in the write fifo so write_fifo_used doesn't need to be checked in this case
                                   ((write_fifo_used >= short_length_burst) & (short_length_burst_enable == 1)) |
                                   ((write_fifo_used >= short_length_burst) & (short_early_termination_burst_enable == 1)) |
                                   ((write_fifo_used >= short_packet_burst) & (short_packet_burst_enable == 1)) |
                                    (write_fifo_used >= max_burst_count);

  // same as ready_during_idle_state only we need to make sure there is more data in the fifo than the burst being posted (since the FIFO is in the middle of being popped)
  assign ready_for_quick_burst = (length >= (max_burst_count << WORD_SIZE_LOG2)) & (burst_of_one_enable == 0) &  // address and length lags by one clock cycle so this will let the state machine catch up
                                 (  ((write_fifo_used > short_length_burst) & (short_length_burst_enable == 1)) |
                                    ((write_fifo_used > short_length_burst) & (short_early_termination_burst_enable == 1)) |
                                    ((write_fifo_used > short_packet_burst) & (short_packet_burst_enable == 1)) |
                                     (write_fifo_used > max_burst_count)  );


  // burst begin signals used to start up the burst counter state machine
  assign burst_begin_from_idle_state = (write_in == 1) & (idle_state == 1) & (ready_during_idle_state == 1);   // start the state machine up again
  assign burst_begin_quickly = (write_in == 1) & (burst_counter == 1) & (waitrequest == 0) & (ready_for_quick_burst == 1); // enough data is buffered to start another burst immediately after the current burst
  assign burst_begin = (burst_begin_quickly == 1) | (burst_begin_from_idle_state == 1);

  assign mux_select = {short_packet_burst_enable, short_early_termination_burst_enable, short_length_burst_enable, burst_of_one_enable};

  // one-hot mux that selects the appropriate burst count to present to the fabric
  always @ (short_length_burst or short_packet_burst or max_burst_count or mux_select)
  begin
    case (mux_select)
      4'b0001 : internal_burst_count = 1;
      4'b0010 : internal_burst_count = short_length_burst;
      4'b0100 : internal_burst_count = short_length_burst;
      4'b1000 : internal_burst_count = short_packet_burst;
      default : internal_burst_count = max_burst_count;
    endcase
  end


generate
  if (BURST_ENABLE == 1)
  begin
    // outputs that need to be held constant throughout the entire burst transaction
    assign address_out = address_d1;
    assign burst_count = internal_burst_count_d1;
    assign write_out = (idle_state == 0);
    assign stall = (idle_state == 1);
    assign reset_taken = (sw_reset == 1) & (idle_state == 1);  // for bursts of 1 the write master logic will handle the correct reset timing
	assign stopped = (sw_stop == 1) & (idle_state == 1);       // for bursts of 1 the write master logic will handle the correct stop timing
  end
  else
  begin
    assign address_out = address_in;
    assign burst_count = 1;  // this will be stubbed at the top level
    assign write_out = write_in;
    assign stall = 0;
    assign reset_taken = sw_reset;
	assign stopped = sw_stop;
  end
endgenerate

endmodule
