// ***************************************************************************
// ***************************************************************************
// Copyright 2014 - 2017 (c) Analog Devices, Inc. All rights reserved.
//
// In this HDL repository, there are many different and unique modules, consisting
// of various HDL (Verilog or VHDL) components. The individual modules are
// developed independently, and may be accompanied by separate and unique license
// terms.
//
// The user should read each of these license terms, and understand the
// freedoms and responsibilities that he or she has by using this source/core.
//
// This core is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.
//
// Redistribution and use of source or resulting binaries, with or without modification
// of this file, are permitted under one of the following two license terms:
//
//   1. The GNU General Public License version 2 as published by the
//      Free Software Foundation, which can be found in the top level directory
//      of this repository (LICENSE_GPL2), and also online at:
//      <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
//
// OR
//
//   2. An ADI specific BSD license, which can be found in the top level directory
//      of this repository (LICENSE_ADIBSD), and also on-line at:
//      https://github.com/analogdevicesinc/hdl/blob/master/LICENSE_ADIBSD
//      This will allow to generate bit files and not release the source code,
//      as long as it attaches to an ADI device.
//
// ***************************************************************************
// ***************************************************************************

`timescale 1ns/100ps

module dmac_src_mm_axi #(

  parameter ID_WIDTH = 3,
  parameter DMA_DATA_WIDTH = 64,
  parameter DMA_ADDR_WIDTH = 32,
  parameter BYTES_PER_BEAT_WIDTH = 3,
  parameter BEATS_PER_BURST_WIDTH = 4,
  parameter AXI_LENGTH_WIDTH = 8)(

  input                           m_axi_aclk,
  input                           m_axi_aresetn,

  input                           req_valid,
  output                          req_ready,
  input [DMA_ADDR_WIDTH-1:BYTES_PER_BEAT_WIDTH] req_address,
  input [BEATS_PER_BURST_WIDTH-1:0] req_last_burst_length,
  input [BYTES_PER_BEAT_WIDTH-1:0]  req_last_beat_bytes,

  input                           enable,
  output reg                      enabled = 1'b0,

  output                             bl_valid,
  input                              bl_ready,
  output [BEATS_PER_BURST_WIDTH-1:0] measured_last_burst_length,
/*
  output                          response_valid,
  input                           response_ready,
  output [1:0]                    response_resp,
*/

  input  [ID_WIDTH-1:0]         request_id,
  output [ID_WIDTH-1:0]         response_id,

  output [ID_WIDTH-1:0]         data_id,
  output [ID_WIDTH-1:0]         address_id,
  input                           address_eot,

  output                          fifo_valid,
  output [DMA_DATA_WIDTH-1:0]   fifo_data,
  output [BYTES_PER_BEAT_WIDTH-1:0] fifo_valid_bytes,
  output                          fifo_last,

  // Read address
  input                            m_axi_arready,
  output                           m_axi_arvalid,
  output [DMA_ADDR_WIDTH-1:0]      m_axi_araddr,
  output [AXI_LENGTH_WIDTH-1:0]    m_axi_arlen,
  output [ 2:0]                    m_axi_arsize,
  output [ 1:0]                    m_axi_arburst,
  output [ 2:0]                    m_axi_arprot,
  output [ 3:0]                    m_axi_arcache,

  // Read data and response
  input  [DMA_DATA_WIDTH-1:0]    m_axi_rdata,
  output                           m_axi_rready,
  input                            m_axi_rvalid,
  input                            m_axi_rlast,
  input  [ 1:0]                    m_axi_rresp
);

`include "inc_id.vh"

reg [ID_WIDTH-1:0] id = 'h00;

wire address_enabled;
wire req_ready_ag;
wire req_valid_ag;
wire bl_ready_ag;
wire bl_valid_ag;

assign data_id = id;
assign response_id = id;

assign measured_last_burst_length = req_last_burst_length;

reg [BYTES_PER_BEAT_WIDTH-1:0] last_beat_bytes;
reg [BYTES_PER_BEAT_WIDTH-1:0] last_beat_bytes_mem[0:2**ID_WIDTH-1];

assign fifo_valid_bytes = last_beat_bytes_mem[data_id];

always @(posedge m_axi_aclk) begin
  if (bl_ready_ag == 1'b1 && bl_valid_ag == 1'b1) begin
    last_beat_bytes <= req_last_beat_bytes;
  end
end


always @(posedge m_axi_aclk) begin
  last_beat_bytes_mem[address_id] <= address_eot ? last_beat_bytes :
                                                   {BYTES_PER_BEAT_WIDTH{1'b1}};
end

splitter #(
  .NUM_M(3)
) i_req_splitter (
  .clk(m_axi_aclk),
  .resetn(m_axi_aresetn),
  .s_valid(req_valid),
  .s_ready(req_ready),
  .m_valid({
    bl_valid,
    bl_valid_ag,
    req_valid_ag
  }),
  .m_ready({
    bl_ready,
    bl_ready_ag,
    req_ready_ag
  })
);

dmac_address_generator #(
  .ID_WIDTH(ID_WIDTH),
  .BEATS_PER_BURST_WIDTH(BEATS_PER_BURST_WIDTH),
  .BYTES_PER_BEAT_WIDTH(BYTES_PER_BEAT_WIDTH),
  .DMA_DATA_WIDTH(DMA_DATA_WIDTH),
  .LENGTH_WIDTH(AXI_LENGTH_WIDTH),
  .DMA_ADDR_WIDTH(DMA_ADDR_WIDTH)
) i_addr_gen (
  .clk(m_axi_aclk),
  .resetn(m_axi_aresetn),

  .enable(enable),
  .enabled(address_enabled),

  .request_id(request_id),
  .id(address_id),

  .req_valid(req_valid_ag),
  .req_ready(req_ready_ag),
  .req_address(req_address),

  .bl_valid(bl_valid_ag),
  .bl_ready(bl_ready_ag),
  .measured_last_burst_length(req_last_burst_length),

  .eot(address_eot),

  .addr_ready(m_axi_arready),
  .addr_valid(m_axi_arvalid),
  .addr(m_axi_araddr),
  .len(m_axi_arlen),
  .size(m_axi_arsize),
  .burst(m_axi_arburst),
  .prot(m_axi_arprot),
  .cache(m_axi_arcache)
);

assign fifo_valid = m_axi_rvalid;
assign fifo_data = m_axi_rdata;
assign fifo_last = m_axi_rlast;

/*
 * There is a requirement that data_id <= address_id (modulo 2**ID_WIDTH).  We
 * know that we will never receive data before we have requested it so there is
 * an implicit dependency between data_id and address_id and no need to
 * explicitly track it.
 */
always @(posedge m_axi_aclk) begin
  if (m_axi_aresetn == 1'b0) begin
    id <= 'h00;
  end else if (m_axi_rvalid == 1'b1 && m_axi_rlast == 1'b1) begin
    id <= inc_id(id);
  end
end

/*
 * We won't be receiving data before we've requested it and we won't request
 * data unless there is room in the store-and-forward memory.
 */
assign m_axi_rready = 1'b1;

/*
 * We need to complete all bursts for which an address has been put onto the
 * AXI-MM interface.
 */
always @(posedge m_axi_aclk) begin
  if (m_axi_aresetn == 1'b0) begin
    enabled <= 1'b0;
  end else if (address_enabled == 1'b1) begin
    enabled <= 1'b1;
  end else if (id == address_id) begin
    enabled <= 1'b0;
  end
end

/* TODO
`include "resp.vh"

assign response_valid = 1'b0;
assign response_resp = RESP_OKAY;

reg [1:0] rresp;

always @(posedge m_axi_aclk)
begin
  if (m_axi_rvalid && m_axi_rready) begin
    if (m_axi_rresp != 2'b0)
      rresp <= m_axi_rresp;
  end
end
*/

endmodule
