`timescale 1ns/100ps
`include "{{ hdlname }}"

module test;
  parameter SIM_CYCLE = 1000 * 1000 * 1000;
  parameter MEMIMG = "{{ memimg }}";
  parameter SIM_ADDR_WIDTH = {{ simaddrwidth }};

  // Clock Period (Half)
  parameter HPERIOD_CLK_ULOGIC = {{ clock_hperiod_userlogic }};
  parameter HPERIOD_CLK_AXI = {{ clock_hperiod_axi }};
  parameter SINGLE_CLOCK = {% if single_clock %}1{% else %}0{% endif %};

  // Memory Access Latency (in User logic Clock)
  parameter READ_LATENCY = 32;
  parameter WRITE_LATENCY = 32;

{% for param in def_top_parameters %}
  {{ param }}
{%- endfor %}

  parameter integer C_M_AXI_ADDR_WIDTH            = {{ ext_addrwidth }};
  parameter integer C_M_AXI_DATA_WIDTH            = {{ ext_datawidth }};
  parameter integer C_M_AXI_THREAD_ID_WIDTH       = 1;
  parameter integer C_M_AXI_AWUSER_WIDTH          = 1;
  parameter integer C_M_AXI_ARUSER_WIDTH          = 1;
  parameter integer C_M_AXI_WUSER_WIDTH           = 1;
  parameter integer C_M_AXI_RUSER_WIDTH           = 1;
  parameter integer C_M_AXI_BUSER_WIDTH           = 1;
 
  parameter integer C_M_AXI_SUPPORTS_WRITE        = 1;
  parameter integer C_M_AXI_SUPPORTS_READ         = 1;

  parameter C_M_AXI_TARGET = 'h00000000;
  
  //------------------------------------------------------------------------------
  // User logic Clock and Reset
  //------------------------------------------------------------------------------
  reg UCLK;
  reg URESETN;

  //---------------------------------------------------------------------------
  // User-defined I/O ports in Top-level User logic (wire)
  //---------------------------------------------------------------------------
{%- for ioport in def_top_ioports | sort() %}
  {{ ioport }}
{%- endfor %}

  //------------------------------------------------------------------------------
  // AXI interface
  //------------------------------------------------------------------------------
  // Clock and Reset
  reg M_AXI_ACLK;
  reg M_AXI_ARESETN;

  // Master Interface Write Address
  wire [C_M_AXI_THREAD_ID_WIDTH-1:0] M_AXI_AWID;
  wire [C_M_AXI_ADDR_WIDTH-1:0]      M_AXI_AWADDR;
  wire [8-1:0]                       M_AXI_AWLEN;
  wire [3-1:0]                       M_AXI_AWSIZE;
  wire [2-1:0]                       M_AXI_AWBURST;
  wire                               M_AXI_AWLOCK;
  wire [4-1:0]                       M_AXI_AWCACHE;
  wire [3-1:0]                       M_AXI_AWPROT;
  wire [4-1:0]                       M_AXI_AWQOS;
  wire [C_M_AXI_AWUSER_WIDTH-1:0]    M_AXI_AWUSER;
  wire                               M_AXI_AWVALID;
  wire                               M_AXI_AWREADY;
  
  // Master Interface Write Data
  wire [C_M_AXI_DATA_WIDTH-1:0]      M_AXI_WDATA;
  wire [C_M_AXI_DATA_WIDTH/8-1:0]    M_AXI_WSTRB;
  wire                               M_AXI_WLAST;
  wire [C_M_AXI_WUSER_WIDTH-1:0]     M_AXI_WUSER;
  wire                               M_AXI_WVALID;
  wire                               M_AXI_WREADY;
  
  // Master Interface Write Response
  wire [C_M_AXI_THREAD_ID_WIDTH-1:0] M_AXI_BID;
  wire [2-1:0]                       M_AXI_BRESP;
  wire [C_M_AXI_BUSER_WIDTH-1:0]     M_AXI_BUSER;
  wire                               M_AXI_BVALID;
  wire                               M_AXI_BREADY;
  
  // Master Interface Read Address
  wire [C_M_AXI_THREAD_ID_WIDTH-1:0] M_AXI_ARID;
  wire [C_M_AXI_ADDR_WIDTH-1:0]      M_AXI_ARADDR;
  wire [8-1:0]                       M_AXI_ARLEN;
  wire [3-1:0]                       M_AXI_ARSIZE;
  wire [2-1:0]                       M_AXI_ARBURST;
  wire [2-1:0]                       M_AXI_ARLOCK;
  wire [4-1:0]                       M_AXI_ARCACHE;
  wire [3-1:0]                       M_AXI_ARPROT;
  wire [4-1:0]                       M_AXI_ARQOS;
  wire [C_M_AXI_ARUSER_WIDTH-1:0]    M_AXI_ARUSER;
  wire                               M_AXI_ARVALID;
  wire                               M_AXI_ARREADY;
  
  // Master Interface Read Data 
  wire [C_M_AXI_THREAD_ID_WIDTH-1:0] M_AXI_RID;
  wire [C_M_AXI_DATA_WIDTH-1:0]      M_AXI_RDATA;
  wire [2-1:0]                       M_AXI_RRESP;
  wire                               M_AXI_RLAST;
  wire [C_M_AXI_RUSER_WIDTH-1:0]     M_AXI_RUSER;
  wire                               M_AXI_RVALID;
  wire                               M_AXI_RREADY;

{%- for domain in domains | sort(attribute='name') %}
{%- for space in domain.spaces | sort() %}
  wire [63:0] {{ domain.name }}_{{ space }}_cycle_idle;
  wire [63:0] {{ domain.name }}_{{ space }}_cycle_hit;
  wire [63:0] {{ domain.name }}_{{ space }}_cycle_miss;
  wire [63:0] {{ domain.name }}_{{ space }}_cycle_conflict;
  wire [63:0] {{ domain.name }}_{{ space }}_cycle_wait;
  wire [63:0] {{ domain.name }}_{{ space }}_num_miss;
{%- endfor %}
{%- endfor %}
  wire reset_performance_count;

{%- for domain in domains | sort(attribute='name') %}
{%- for outchannel in domain.outchannels | sort(attribute='name') %}
  wire [{{ outchannel.datawidth -1 }}:0] {{ outchannel.name }}_outchannel_data_out;
  wire {{ outchannel.name }}_outchannel_enq_out;
  wire {{ outchannel.name }}_outchannel_ready_in;
{%- endfor %}
{%- for inchannel in domain.inchannels | sort(attribute='name') %}
  wire {{ inchannel.name }}_inchannel_clk;
  wire {{ inchannel.name }}_inchannel_rst;
  wire [{{ inchannel.datawidth -1 }}:0] {{ inchannel.name }}_inchannel_data_in;
  wire {{ inchannel.name }}_inchannel_enq_in;
  wire {{ inchannel.name }}_inchannel_ready_out;
{%- endfor %}
{%- endfor %}

  syrup_{{ userlogic_name.lower() }}
  inst_uut
  (
   .M_AXI_ACLK(M_AXI_ACLK),
   .M_AXI_ARESETN(M_AXI_ARESETN),

   .M_AXI_AWID(M_AXI_AWID),
   .M_AXI_AWADDR(M_AXI_AWADDR),
   .M_AXI_AWLEN(M_AXI_AWLEN),
   .M_AXI_AWSIZE(M_AXI_AWSIZE),
   .M_AXI_AWBURST(M_AXI_AWBURST),
   .M_AXI_AWLOCK(M_AXI_AWLOCK),
   .M_AXI_AWCACHE(M_AXI_AWCACHE),
   .M_AXI_AWPROT(M_AXI_AWPROT),
   .M_AXI_AWQOS(M_AXI_AWQOS),
   .M_AXI_AWUSER(M_AXI_AWUSER),
   .M_AXI_AWVALID(M_AXI_AWVALID),
   .M_AXI_AWREADY(M_AXI_AWREADY),
   
   .M_AXI_WDATA(M_AXI_WDATA),
   .M_AXI_WSTRB(M_AXI_WSTRB),
   .M_AXI_WLAST(M_AXI_WLAST),
   .M_AXI_WUSER(M_AXI_WUSER),
   .M_AXI_WVALID(M_AXI_WVALID),
   .M_AXI_WREADY(M_AXI_WREADY),
   
   .M_AXI_BID(M_AXI_BID),
   .M_AXI_BRESP(M_AXI_BRESP),
   .M_AXI_BUSER(M_AXI_BUSER),
   .M_AXI_BVALID(M_AXI_BVALID),
   .M_AXI_BREADY(M_AXI_BREADY),
   
   .M_AXI_ARID(M_AXI_ARID),
   .M_AXI_ARADDR(M_AXI_ARADDR),
   .M_AXI_ARLEN(M_AXI_ARLEN),
   .M_AXI_ARSIZE(M_AXI_ARSIZE),
   .M_AXI_ARBURST(M_AXI_ARBURST),
   .M_AXI_ARLOCK(M_AXI_ARLOCK),
   .M_AXI_ARCACHE(M_AXI_ARCACHE),
   .M_AXI_ARPROT(M_AXI_ARPROT),
   .M_AXI_ARQOS(M_AXI_ARQOS),
   .M_AXI_ARUSER(M_AXI_ARUSER),
   .M_AXI_ARVALID(M_AXI_ARVALID),
   .M_AXI_ARREADY(M_AXI_ARREADY),
   
   .M_AXI_RID(M_AXI_RID),
   .M_AXI_RDATA(M_AXI_RDATA),
   .M_AXI_RRESP(M_AXI_RRESP),
   .M_AXI_RLAST(M_AXI_RLAST),
   .M_AXI_RUSER(M_AXI_RUSER),
   .M_AXI_RVALID(M_AXI_RVALID),
   .M_AXI_RREADY(M_AXI_RREADY),

{%- for domain in domains | sort(attribute='name') %}
{%- for space in domain.spaces | sort() %}
   .{{ domain.name }}_{{ space }}_cycle_idle({{ domain.name }}_{{ space }}_cycle_idle),
   .{{ domain.name }}_{{ space }}_cycle_hit({{ domain.name }}_{{ space }}_cycle_hit),
   .{{ domain.name }}_{{ space }}_cycle_miss({{ domain.name }}_{{ space }}_cycle_miss),
   .{{ domain.name }}_{{ space }}_cycle_conflict({{ domain.name }}_{{ space }}_cycle_conflict),
   .{{ domain.name }}_{{ space }}_cycle_wait({{ domain.name }}_{{ space }}_cycle_wait),
   .{{ domain.name }}_{{ space }}_num_miss({{ domain.name }}_{{ space }}_num_miss),
{%- endfor %}
{%- endfor %}

   .reset_performance_count(reset_performance_count),

{%- for domain in domains | sort(attribute='name') %}
{%- for outchannel in domain.outchannels | sort(attribute='name') %}
   .{{ outchannel.name }}_outchannel_data_out({{ outchannel.name }}_outchannel_data_out),
   .{{ outchannel.name }}_outchannel_enq_out({{ outchannel.name }}_outchannel_enq_out),
   .{{ outchannel.name }}_outchannel_ready_in({{ outchannel.name }}_outchannel_ready_in),
{%- endfor %}
{%- for inchannel in domain.inchannels | sort(attribute='name') %}
   .{{ inchannel.name }}_inchannel_clk({{ inchannel.name }}_inchannel_clk),
   .{{ inchannel.name }}_inchannel_rst({{ inchannel.name }}_inchannel_rst),
   .{{ inchannel.name }}_inchannel_data_in({{ inchannel.name }}_inchannel_data_in),
   .{{ inchannel.name }}_inchannel_enq_in({{ inchannel.name }}_inchannel_enq_in),
   .{{ inchannel.name }}_inchannel_ready_out({{ inchannel.name }}_inchannel_ready_out),
{%- endfor %}
{%- endfor %}

{%- for ioport in name_top_ioports | sort() %}
   .{{ ioport }}({{ ioport }}),
{%- endfor %}

   .UCLK(UCLK),
   .URESETN(URESETN)
   ); 

  axi_dram_stub #
  (
   .C_M_AXI_THREAD_ID_WIDTH(C_M_AXI_THREAD_ID_WIDTH),
   .C_M_AXI_ADDR_WIDTH(C_M_AXI_ADDR_WIDTH),
   .C_M_AXI_DATA_WIDTH(C_M_AXI_DATA_WIDTH),
   .C_M_AXI_AWUSER_WIDTH(C_M_AXI_AWUSER_WIDTH),
   .C_M_AXI_ARUSER_WIDTH(C_M_AXI_ARUSER_WIDTH),
   .C_M_AXI_WUSER_WIDTH(C_M_AXI_WUSER_WIDTH),
   .C_M_AXI_RUSER_WIDTH(C_M_AXI_RUSER_WIDTH),
   .C_M_AXI_BUSER_WIDTH(C_M_AXI_BUSER_WIDTH),

   .MEMIMG(MEMIMG),
   .SIM_ADDR_WIDTH(SIM_ADDR_WIDTH),
   .READ_LATENCY(READ_LATENCY),
   .WRITE_LATENCY(WRITE_LATENCY)
   )
  inst_axi_dram_stub
  (
   .M_AXI_ACLK(M_AXI_ACLK),
   .M_AXI_ARESETN(M_AXI_ARESETN),

   .M_AXI_AWID(M_AXI_AWID),
   .M_AXI_AWADDR(M_AXI_AWADDR),
   .M_AXI_AWLEN(M_AXI_AWLEN),
   .M_AXI_AWSIZE(M_AXI_AWSIZE),
   .M_AXI_AWBURST(M_AXI_AWBURST),
   .M_AXI_AWLOCK(M_AXI_AWLOCK),
   .M_AXI_AWCACHE(M_AXI_AWCACHE),
   .M_AXI_AWPROT(M_AXI_AWPROT),
   .M_AXI_AWQOS(M_AXI_AWQOS),
   .M_AXI_AWUSER(M_AXI_AWUSER),
   .M_AXI_AWVALID(M_AXI_AWVALID),
   .M_AXI_AWREADY(M_AXI_AWREADY),
   
   .M_AXI_WDATA(M_AXI_WDATA),
   .M_AXI_WSTRB(M_AXI_WSTRB),
   .M_AXI_WLAST(M_AXI_WLAST),
   .M_AXI_WUSER(M_AXI_WUSER),
   .M_AXI_WVALID(M_AXI_WVALID),
   .M_AXI_WREADY(M_AXI_WREADY),
   
   .M_AXI_BID(M_AXI_BID),
   .M_AXI_BRESP(M_AXI_BRESP),
   .M_AXI_BUSER(M_AXI_BUSER),
   .M_AXI_BVALID(M_AXI_BVALID),
   .M_AXI_BREADY(M_AXI_BREADY),
   
   .M_AXI_ARID(M_AXI_ARID),
   .M_AXI_ARADDR(M_AXI_ARADDR),
   .M_AXI_ARLEN(M_AXI_ARLEN),
   .M_AXI_ARSIZE(M_AXI_ARSIZE),
   .M_AXI_ARBURST(M_AXI_ARBURST),
   .M_AXI_ARLOCK(M_AXI_ARLOCK),
   .M_AXI_ARCACHE(M_AXI_ARCACHE),
   .M_AXI_ARPROT(M_AXI_ARPROT),
   .M_AXI_ARQOS(M_AXI_ARQOS),
   .M_AXI_ARUSER(M_AXI_ARUSER),
   .M_AXI_ARVALID(M_AXI_ARVALID),
   .M_AXI_ARREADY(M_AXI_ARREADY),
   
   .M_AXI_RID(M_AXI_RID),
   .M_AXI_RDATA(M_AXI_RDATA),
   .M_AXI_RRESP(M_AXI_RRESP),
   .M_AXI_RLAST(M_AXI_RLAST),
   .M_AXI_RUSER(M_AXI_RUSER),
   .M_AXI_RVALID(M_AXI_RVALID),
   .M_AXI_RREADY(M_AXI_RREADY),

   .UCLK(UCLK),
   .URESETN(URESETN)
   );

  initial begin
    UCLK = 0;
    forever #HPERIOD_CLK_ULOGIC UCLK = ~UCLK;
  end

  initial begin
    M_AXI_ACLK = 0;
    forever #HPERIOD_CLK_AXI M_AXI_ACLK = ~M_AXI_ACLK;
  end

  task nclk;
    begin
      wait(~UCLK);
      wait(UCLK);
      #1;
    end
  endtask

  integer i;
  
  initial begin
    if(SINGLE_CLOCK && (HPERIOD_CLK_ULOGIC != HPERIOD_CLK_AXI)) begin
      $display("ERROR: All clock periods should be same in single clock mode");
      $finish;
    end

    URESETN = 1;
    M_AXI_ARESETN = 1;
    
    #100;

    URESETN = 0;
    M_AXI_ARESETN = 0;

    #100;

    URESETN = 1;
    M_AXI_ARESETN = 1;

    #100;

    nclk();

    for(i=0; i<SIM_CYCLE; i=i+1) begin
      nclk();
    end
    
    $display("[syrup] time:%d simulation time out. cycle:%d", $stime, i);
    $finish;
  end

  initial begin
    $dumpfile("uut.vcd");
    $dumpvars(0, inst_uut, inst_axi_dram_stub);
  end

  //----------------------------------------------------------------------------
  // Setting of User-defined I/O ports
  //----------------------------------------------------------------------------
  // Please add signal or module definitions here for simulation
{% if usertestcode != '' %}
{{ usertestcode }}
{% endif %}
  
endmodule


//------------------------------------------------------------------------------
// DRAM Stub with AXI interface
//------------------------------------------------------------------------------
module axi_dram_stub #
  (
   parameter integer C_M_AXI_THREAD_ID_WIDTH       = 1,
   parameter integer C_M_AXI_ADDR_WIDTH            = 32,
   parameter integer C_M_AXI_DATA_WIDTH            = 32,
   parameter integer C_M_AXI_AWUSER_WIDTH          = 1,
   parameter integer C_M_AXI_ARUSER_WIDTH          = 1,
   parameter integer C_M_AXI_WUSER_WIDTH           = 1,
   parameter integer C_M_AXI_RUSER_WIDTH           = 1,
   parameter integer C_M_AXI_BUSER_WIDTH           = 1,

   parameter MEMIMG = "{{ memimg }}",
   parameter SIM_ADDR_WIDTH = {{ simaddrwidth }},
   parameter READ_LATENCY = 32,
   parameter WRITE_LATENCY = 32
   )
  (
   // Clock and Reset
   input  wire                               M_AXI_ACLK,
   input  wire                               M_AXI_ARESETN,

   // Master Interface Write Address
   input  wire [C_M_AXI_THREAD_ID_WIDTH-1:0] M_AXI_AWID,
   input  wire [C_M_AXI_ADDR_WIDTH-1:0]      M_AXI_AWADDR,
   input  wire [8-1:0]                       M_AXI_AWLEN,
   input  wire [3-1:0]                       M_AXI_AWSIZE,
   input  wire [2-1:0]                       M_AXI_AWBURST,
   input  wire                               M_AXI_AWLOCK,
   input  wire [4-1:0]                       M_AXI_AWCACHE,
   input  wire [3-1:0]                       M_AXI_AWPROT,
   input  wire [4-1:0]                       M_AXI_AWQOS,
   input  wire [C_M_AXI_AWUSER_WIDTH-1:0]    M_AXI_AWUSER,
   input  wire                               M_AXI_AWVALID,
   output reg                                M_AXI_AWREADY,
   
   // Master Interface Write Data
   input  wire [C_M_AXI_DATA_WIDTH-1:0]      M_AXI_WDATA,
   input  wire [C_M_AXI_DATA_WIDTH/8-1:0]    M_AXI_WSTRB,
   input  wire                               M_AXI_WLAST,
   input  wire [C_M_AXI_WUSER_WIDTH-1:0]     M_AXI_WUSER,
   input  wire                               M_AXI_WVALID,
   output reg                                M_AXI_WREADY,
   
   // Master Interface Write Response
   output wire [C_M_AXI_THREAD_ID_WIDTH-1:0] M_AXI_BID,
   output wire [2-1:0]                       M_AXI_BRESP,
   output wire [C_M_AXI_BUSER_WIDTH-1:0]     M_AXI_BUSER,
   output reg                                M_AXI_BVALID,
   input  wire                               M_AXI_BREADY,
   
   // Master Interface Read Address
   input  wire [C_M_AXI_THREAD_ID_WIDTH-1:0] M_AXI_ARID,
   input  wire [C_M_AXI_ADDR_WIDTH-1:0]      M_AXI_ARADDR,
   input  wire [8-1:0]                       M_AXI_ARLEN,
   input  wire [3-1:0]                       M_AXI_ARSIZE,
   input  wire [2-1:0]                       M_AXI_ARBURST,
   input  wire [2-1:0]                       M_AXI_ARLOCK,
   input  wire [4-1:0]                       M_AXI_ARCACHE,
   input  wire [3-1:0]                       M_AXI_ARPROT,
   input  wire [4-1:0]                       M_AXI_ARQOS,
   input  wire [C_M_AXI_ARUSER_WIDTH-1:0]    M_AXI_ARUSER,
   input  wire                               M_AXI_ARVALID,
   output reg                                M_AXI_ARREADY,
   
   // Master Interface Read Data 
   output wire [C_M_AXI_THREAD_ID_WIDTH-1:0] M_AXI_RID,
   output reg  [C_M_AXI_DATA_WIDTH-1:0]      M_AXI_RDATA,
   output wire [2-1:0]                       M_AXI_RRESP,
   output reg                                M_AXI_RLAST,
   output wire [C_M_AXI_RUSER_WIDTH-1:0]     M_AXI_RUSER,
   output reg                                M_AXI_RVALID,
   input  wire                               M_AXI_RREADY,

   input UCLK, // User logic (Unused)
   input URESETN // User logic (Unused)
   );

  //------------------------------------------------------------------------------
  // Memory Field
  //------------------------------------------------------------------------------
  localparam MEMORY_LEN = (2 ** SIM_ADDR_WIDTH);
  reg [7:0] memory [0:MEMORY_LEN-1];

  integer i;
  integer val;
  integer __fp, __c;

  initial begin
    if(MEMIMG == "None") begin
      val = 0;
      for(i=0; i<MEMORY_LEN; i=i+1) begin
        memory[i] = val >> (8 * (i % 4));
        if((i % 4) == 3) val = val + 1;
      end
    end else begin
{%- if binfile %}
      __fp = $fopen(MEMIMG, "rb");
      __c = $fread(memory, __fp);
{%- else %}
      $readmemh(MEMIMG, memory);
{%- endif %}
      $display("read memory image file %s", MEMIMG);
    end
  end

  task mem_write_M;
    input [SIM_ADDR_WIDTH-1:0] addr;
    input [SIM_ADDR_WIDTH-1:0] size;
    input [C_M_AXI_DATA_WIDTH-1:0] data;
    integer pos;
    begin
      for(pos=0; pos < size; pos=pos+1) begin
        memory[addr+pos] = (data >> (8*pos)) & 'hFF;
      end
    end
  endtask
  
  task mem_read_M;
    input [SIM_ADDR_WIDTH-1:0] addr;
    input [SIM_ADDR_WIDTH-1:0] size;
    output [C_M_AXI_DATA_WIDTH-1:0] data;
    integer pos;
    begin
      data = 0;
      for(pos=0; pos < size; pos=pos+1) begin
        data = data | memory[addr+pos] << (8*pos);
      end
    end
  endtask

  //------------------------------------------------------------------------------
  // Dummy Controller
  //------------------------------------------------------------------------------
  reg                               M_AXI_write_mode;
  reg [C_M_AXI_ADDR_WIDTH-1:0]      d_M_AXI_AWADDR;
  reg [8-1:0]                       d_M_AXI_AWLEN;

  reg                               M_AXI_read_mode;
  reg [C_M_AXI_ADDR_WIDTH-1:0]      d_M_AXI_ARADDR;
  reg [8-1:0]                       d_M_AXI_ARLEN;

  wire [SIM_ADDR_WIDTH-1:0] M_AXI_read_addr;
  wire [SIM_ADDR_WIDTH-1:0] M_AXI_write_addr;
  assign M_AXI_read_addr = d_M_AXI_ARADDR;
  assign M_AXI_write_addr = d_M_AXI_AWADDR;

  reg [31:0] M_stall_count;
  
  always @(posedge M_AXI_ACLK) begin
    if(!M_AXI_ARESETN) begin
      M_AXI_write_mode <= 0;
      M_AXI_read_mode <= 0;
      M_stall_count <= 0;
    end else begin
      M_AXI_AWREADY <= 0;
      M_AXI_WREADY <= 0;
      M_AXI_BVALID <= 0;
      
      M_AXI_ARREADY <= 0;
      M_AXI_RVALID <= 0;
      M_AXI_RLAST <= 0;
      
      if(!M_AXI_write_mode && M_AXI_AWVALID) begin
        M_stall_count <= M_stall_count + 1;
        if(M_stall_count == WRITE_LATENCY) begin
          M_stall_count <= 0;
          M_AXI_AWREADY <= 1;
          M_AXI_write_mode <= 1;
          d_M_AXI_AWADDR <= M_AXI_AWADDR;
          d_M_AXI_AWLEN <= M_AXI_AWLEN;
        end
      end

      if(!M_AXI_read_mode && M_AXI_ARVALID) begin
        M_stall_count <= M_stall_count + 1;
        if(M_stall_count == READ_LATENCY) begin
          M_stall_count <= 0;
          M_AXI_ARREADY <= 1;
          M_AXI_read_mode <= 1;
          d_M_AXI_ARADDR <= M_AXI_ARADDR;
          d_M_AXI_ARLEN <= M_AXI_ARLEN;
        end
      end
      
      if(M_AXI_write_mode) begin
        if(M_AXI_WVALID) begin
          M_AXI_WREADY <= 1;
          if(M_AXI_WREADY != 0) begin
            //memory[ M_AXI_write_addr ] <= M_AXI_WDATA;
            mem_write_M(M_AXI_write_addr, C_M_AXI_DATA_WIDTH/8, M_AXI_WDATA);
            d_M_AXI_AWADDR <= d_M_AXI_AWADDR + (C_M_AXI_DATA_WIDTH / 8);
            d_M_AXI_AWLEN <= d_M_AXI_AWLEN - 1;
            if(d_M_AXI_AWLEN == 0 || M_AXI_WLAST) begin // actual burst length -1
              M_AXI_BVALID <= 1;
              M_AXI_write_mode <= 0;
            end
          end
        end
      end

      if(M_AXI_read_mode) begin
        if(M_AXI_RREADY) begin
          M_AXI_RVALID <= 1;
          //M_AXI_RDATA <= memory[ M_AXI_read_addr ];
          mem_read_M(M_AXI_read_addr, C_M_AXI_DATA_WIDTH/8, M_AXI_RDATA);
          d_M_AXI_ARADDR <= d_M_AXI_ARADDR + (C_M_AXI_DATA_WIDTH / 8);
          d_M_AXI_ARLEN <= d_M_AXI_ARLEN - 1;
          if(d_M_AXI_ARLEN == 0) begin // actual burst length -1
            M_AXI_RLAST <= 1;
            M_AXI_read_mode <= 0;
          end
        end
      end

    end
  end

endmodule


