module axi_dmac_regmap #(
  parameter ID = 0,
  parameter DISABLE_DEBUG_REGISTERS = 0,
  parameter BYTES_PER_BEAT_WIDTH_DEST = 1,
  parameter BYTES_PER_BEAT_WIDTH_SRC = 1,
  parameter BYTES_PER_BURST_WIDTH = 7,
  parameter DMA_TYPE_DEST = 0,
  parameter DMA_TYPE_SRC = 2,
  parameter DMA_AXI_ADDR_WIDTH = 32,
  parameter DMA_LENGTH_WIDTH = 24,
  parameter DMA_LENGTH_ALIGN = 3,
  parameter DMA_CYCLIC = 0,
  parameter HAS_DEST_ADDR = 1,
  parameter HAS_SRC_ADDR = 1,
  parameter DMA_2D_TRANSFER = 0,
  parameter SYNC_TRANSFER_START = 0
) (
  // Slave AXI interface
  input s_axi_aclk,
  input s_axi_aresetn,

  input s_axi_awvalid,
  output s_axi_awready,
  input [10:0] s_axi_awaddr,
  input [2:0] s_axi_awprot,

  input s_axi_wvalid,
  output s_axi_wready,
  input [31:0] s_axi_wdata,
  input [3:0] s_axi_wstrb,

  output s_axi_bvalid,
  input s_axi_bready,
  output [1:0] s_axi_bresp,

  input s_axi_arvalid,
  output s_axi_arready,
  input [10:0] s_axi_araddr,
  input [2:0] s_axi_arprot,

  output s_axi_rvalid,
  input s_axi_rready,
  output [1:0] s_axi_rresp,
  output [31:0] s_axi_rdata,

  // Interrupt
  output reg irq,

  // Control interface
  output reg ctrl_enable = 1'b0,
  output reg ctrl_pause = 1'b0,

  // DMA request interface
  output request_valid,
  input request_ready,
  output [DMA_AXI_ADDR_WIDTH-1:BYTES_PER_BEAT_WIDTH_DEST] request_dest_address,
  output [DMA_AXI_ADDR_WIDTH-1:BYTES_PER_BEAT_WIDTH_SRC] request_src_address,
  output [DMA_LENGTH_WIDTH-1:0] request_x_length,
  output [DMA_LENGTH_WIDTH-1:0] request_y_length,
  output [DMA_LENGTH_WIDTH-1:0] request_dest_stride,
  output [DMA_LENGTH_WIDTH-1:0] request_src_stride,
  output request_sync_transfer_start,
  output request_last,

  // DMA response interface
  input response_eot,
  input [BYTES_PER_BURST_WIDTH-1:0] response_measured_burst_length,
  input response_partial,
  input response_valid,
  output response_ready,

  // Debug interface
  input [DMA_AXI_ADDR_WIDTH-1:0] dbg_src_addr,
  input [DMA_AXI_ADDR_WIDTH-1:0] dbg_dest_addr,
  input [11:0] dbg_status,
  input [31:0] dbg_ids0,
  input [31:0] dbg_ids1
);

localparam PCORE_VERSION = 'h00040361;

// Register interface signals
reg [31:0] up_rdata = 32'h00;
reg up_wack = 1'b0;
reg up_rack = 1'b0;

wire up_wreq;
wire up_rreq;
wire [31:0] up_wdata;
wire [8:0] up_waddr;
wire [8:0] up_raddr;
wire [31:0] up_rdata_request;

// Scratch register
reg [31:0] up_scratch = 32'h00;

// Start and end of transfer
wire up_eot; // Asserted for one cycle when a transfer has been completed
wire up_sot; // Asserted for one cycle when a transfer has been queued

// Interupt handling
reg [1:0] up_irq_mask = 2'h3;
reg [1:0] up_irq_source = 2'h0;

wire [1:0] up_irq_pending;
wire [1:0] up_irq_trigger;
wire [1:0] up_irq_source_clear;

// IRQ handling
assign up_irq_pending = ~up_irq_mask & up_irq_source;
assign up_irq_trigger  = {up_eot, up_sot};
assign up_irq_source_clear = (up_wreq == 1'b1 && up_waddr == 9'h021) ? up_wdata[1:0] : 2'b00;

always @(posedge s_axi_aclk) begin
  if (s_axi_aresetn == 1'b0) begin
    irq <= 1'b0;
  end else begin
    irq <= |up_irq_pending;
  end
end

always @(posedge s_axi_aclk) begin
  if (s_axi_aresetn == 1'b0) begin
    up_irq_source <= 2'b00;
  end else begin
    up_irq_source <= up_irq_trigger | (up_irq_source & ~up_irq_source_clear);
  end
end

// Register Interface

always @(posedge s_axi_aclk) begin
  if (s_axi_aresetn == 1'b0) begin
    ctrl_enable <= 1'b0;
    ctrl_pause <= 1'b0;
    up_irq_mask <= 2'b11;
    up_scratch <= 32'h00;
    up_wack <= 1'b0;
  end else begin
    up_wack <= up_wreq;

    if (up_wreq == 1'b1) begin
      case (up_waddr)
      9'h002: up_scratch <= up_wdata;
      9'h020: up_irq_mask <= up_wdata[1:0];
      9'h100: {ctrl_pause, ctrl_enable} <= up_wdata[1:0];
      endcase
    end
  end
end

always @(posedge s_axi_aclk) begin
  if (s_axi_aresetn == 1'b0) begin
    up_rack <= 'd0;
  end else begin
    up_rack <= up_rreq;
  end
end

always @(posedge s_axi_aclk) begin
  if (up_rreq == 1'b1) begin
    case (up_raddr)
    9'h000: up_rdata <= PCORE_VERSION;
    9'h001: up_rdata <= ID;
    9'h002: up_rdata <= up_scratch;
    9'h003: up_rdata <= 32'h444d4143; // "DMAC"
    9'h004: up_rdata <= {16'b0,
                         2'b0,DMA_TYPE_SRC[1:0],BYTES_PER_BEAT_WIDTH_SRC[3:0],
                         2'b0,DMA_TYPE_DEST[1:0],BYTES_PER_BEAT_WIDTH_DEST[3:0]};
    9'h020: up_rdata <= up_irq_mask;
    9'h021: up_rdata <= up_irq_pending;
    9'h022: up_rdata <= up_irq_source;
    9'h100: up_rdata <= {ctrl_pause, ctrl_enable};
    9'h10d: up_rdata <= DISABLE_DEBUG_REGISTERS ? 32'h00 : dbg_dest_addr;
    9'h10e: up_rdata <= DISABLE_DEBUG_REGISTERS ? 32'h00 : dbg_src_addr;
    9'h10f: up_rdata <= DISABLE_DEBUG_REGISTERS ? 32'h00 : dbg_status;
    9'h110: up_rdata <= DISABLE_DEBUG_REGISTERS ? 32'h00 : dbg_ids0;
    9'h111: up_rdata <= DISABLE_DEBUG_REGISTERS ? 32'h00 : dbg_ids1;
    default: up_rdata <= up_rdata_request;
    endcase
  end
end

axi_dmac_regmap_request #(
  .DISABLE_DEBUG_REGISTERS(DISABLE_DEBUG_REGISTERS),
  .BYTES_PER_BEAT_WIDTH_DEST(BYTES_PER_BEAT_WIDTH_DEST),
  .BYTES_PER_BEAT_WIDTH_SRC(BYTES_PER_BEAT_WIDTH_SRC),
  .BYTES_PER_BURST_WIDTH(BYTES_PER_BURST_WIDTH),
  .DMA_AXI_ADDR_WIDTH(DMA_AXI_ADDR_WIDTH),
  .DMA_LENGTH_WIDTH(DMA_LENGTH_WIDTH),
  .DMA_LENGTH_ALIGN(DMA_LENGTH_ALIGN),
  .DMA_CYCLIC(DMA_CYCLIC),
  .HAS_DEST_ADDR(HAS_DEST_ADDR),
  .HAS_SRC_ADDR(HAS_SRC_ADDR),
  .DMA_2D_TRANSFER(DMA_2D_TRANSFER),
  .SYNC_TRANSFER_START(SYNC_TRANSFER_START)
) i_regmap_request (
  .clk(s_axi_aclk),
  .reset(~s_axi_aresetn),

  .up_sot(up_sot),
  .up_eot(up_eot),

  .up_wreq(up_wreq),
  .up_rreq(up_rreq),
  .up_waddr(up_waddr),
  .up_wdata(up_wdata),
  .up_raddr(up_raddr),
  .up_rdata(up_rdata_request),

  .ctrl_enable(ctrl_enable),

  .request_valid(request_valid),
  .request_ready(request_ready),
  .request_dest_address(request_dest_address),
  .request_src_address(request_src_address),
  .request_x_length(request_x_length),
  .request_y_length(request_y_length),
  .request_dest_stride(request_dest_stride),
  .request_src_stride(request_src_stride),
  .request_sync_transfer_start(request_sync_transfer_start),
  .request_last(request_last),

  .response_eot(response_eot),
  .response_measured_burst_length(response_measured_burst_length),
  .response_partial(response_partial),
  .response_valid(response_valid),
  .response_ready(response_ready)
);

up_axi #(
  .AXI_ADDRESS_WIDTH (11)
) i_up_axi (
  .up_rstn(s_axi_aresetn),
  .up_clk(s_axi_aclk),
  .up_axi_awvalid(s_axi_awvalid),
  .up_axi_awaddr(s_axi_awaddr),
  .up_axi_awready(s_axi_awready),
  .up_axi_wvalid(s_axi_wvalid),
  .up_axi_wdata(s_axi_wdata),
  .up_axi_wstrb(s_axi_wstrb),
  .up_axi_wready(s_axi_wready),
  .up_axi_bvalid(s_axi_bvalid),
  .up_axi_bresp(s_axi_bresp),
  .up_axi_bready(s_axi_bready),
  .up_axi_arvalid(s_axi_arvalid),
  .up_axi_araddr(s_axi_araddr),
  .up_axi_arready(s_axi_arready),
  .up_axi_rvalid(s_axi_rvalid),
  .up_axi_rresp(s_axi_rresp),
  .up_axi_rdata(s_axi_rdata),
  .up_axi_rready(s_axi_rready),
  .up_wreq(up_wreq),
  .up_waddr(up_waddr),
  .up_wdata(up_wdata),
  .up_wack(up_wack),
  .up_rreq(up_rreq),
  .up_raddr(up_raddr),
  .up_rdata(up_rdata),
  .up_rack(up_rack)
);

endmodule