//------------------------------------------------------------------------------
// task statements for user-defined testbench
// Please use these tasks to implement test cases
//------------------------------------------------------------------------------
// - nclk()
//    Wait for next clock edge
// - mem_write(addr, size, data)
//    Write data to location 'addr' on DRAM, size in byte (1 to 32)
// - mem_read(addr, size, data)
//    Read data from location 'addr' on DRAM, size in byte (1 to 32)
// - slave_write_MEMORYTYPE_NAME_ID(data, addr)
//    Write data to location 'addr' on DRAM. size equals to data width of I/F
// - slave_read_MEMORYTYPE_NAME_ID(data, addr)
//    Read data from location 'addr' on DRAM. size equals to data width of I/F
//------------------------------------------------------------------------------

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

`define DUMP_VCD
`define MEM_READ_LATENCY 8
`define MEM_WRITE_LATENCY 4

module test_top;
`ifdef DUMP_VCD
  initial begin
    $dumpfile("uut.vcd");
    $dumpvars(0, uut);
  end
`endif
  test uut ();
endmodule

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_BUS = {{ clock_hperiod_bus }};
  parameter SINGLE_CLOCK = {% if single_clock %}1{% else %}0{% endif %};

  // Memory Access Latency (in User logic Clock)
  parameter READ_LATENCY = `MEM_READ_LATENCY;
  parameter WRITE_LATENCY = `MEM_WRITE_LATENCY;

  // Bus Type
  parameter BUS_TYPE = "axi";
  parameter IGNORE_PROTOCOL_ERROR = {% if ignore_protocol_error %}1{% else %}0{% endif %};

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

{% for master in masterlist | sort(attribute='name') %}  
  parameter integer C_{{ master.name }}_AXI_DATA_WIDTH        = {{ master.datawidth }};
  parameter integer C_{{ master.name }}_AXI_ADDR_WIDTH        = {{ ext_addrwidth }};
  parameter integer C_{{ master.name }}_AXI_THREAD_ID_WIDTH   = 1;
  parameter integer C_{{ master.name }}_AXI_AWUSER_WIDTH      = 1;
  parameter integer C_{{ master.name }}_AXI_ARUSER_WIDTH      = 1;
  parameter integer C_{{ master.name }}_AXI_WUSER_WIDTH       = 1;
  parameter integer C_{{ master.name }}_AXI_RUSER_WIDTH       = 1;
  parameter integer C_{{ master.name }}_AXI_BUSER_WIDTH       = 1;
  parameter integer C_{{ master.name }}_AXI_SUPPORTS_WRITE    = 1;
  parameter integer C_{{ master.name }}_AXI_SUPPORTS_READ     = 1;
{% endfor %}
{% for slave in slavelist | sort(attribute='name') %}
  parameter integer C_{{ slave.name }}_AXI_DATA_WIDTH = {{ slave.datawidth }};
  parameter integer C_{{ slave.name }}_AXI_ADDR_WIDTH = {{ slave.addrwidth }}; 
  parameter integer C_{{ slave.name }}_AXI_ID_WIDTH = 1;
  parameter integer C_{{ slave.name }}_AXI_AWUSER_WIDTH = 1;
  parameter integer C_{{ slave.name }}_AXI_ARUSER_WIDTH = 1;
  parameter integer C_{{ slave.name }}_AXI_WUSER_WIDTH = 1;
  parameter integer C_{{ slave.name }}_AXI_RUSER_WIDTH = 1;
  parameter integer C_{{ slave.name }}_AXI_BUSER_WIDTH = 1;
  parameter integer C_{{ slave.name }}_AXI_SUPPORTS_WRITE = 1;
  parameter integer C_{{ slave.name }}_AXI_SUPPORTS_READ = 1;
{% endfor %}

  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 %}

  //---------------------------------------------------------------------------
  // Simulation Clock and Reset
  //---------------------------------------------------------------------------
  wire sim_clk;
  wire sim_resetn;
  assign sim_clk = UCLK;
  assign sim_resetn = URESETN;

  //------------------------------------------------------------------------------
  // AXI interface
  //------------------------------------------------------------------------------
{% for master in masterlist | sort(attribute='name') %}
  // Clock and Reset
  reg {{ master.name }}_AXI_ACLK;
  reg {{ master.name }}_AXI_ARESETN;
  // Master Interface Write Address
  wire [C_{{ master.name }}_AXI_THREAD_ID_WIDTH-1:0] {{ master.name }}_AXI_AWID;
  wire [C_{{ master.name }}_AXI_ADDR_WIDTH-1:0]      {{ master.name }}_AXI_AWADDR;
  wire [8-1:0]                       {{ master.name }}_AXI_AWLEN;
  wire [3-1:0]                       {{ master.name }}_AXI_AWSIZE;
  wire [2-1:0]                       {{ master.name }}_AXI_AWBURST;
  wire                               {{ master.name }}_AXI_AWLOCK;
  wire [4-1:0]                       {{ master.name }}_AXI_AWCACHE;
  wire [3-1:0]                       {{ master.name }}_AXI_AWPROT;
  wire [4-1:0]                       {{ master.name }}_AXI_AWQOS;
  wire [C_{{ master.name }}_AXI_AWUSER_WIDTH-1:0]    {{ master.name }}_AXI_AWUSER;
  wire                               {{ master.name }}_AXI_AWVALID;
  wire                               {{ master.name }}_AXI_AWREADY;
  
  // Master Interface Write Data
  wire [C_{{ master.name }}_AXI_DATA_WIDTH-1:0]      {{ master.name }}_AXI_WDATA;
  wire [C_{{ master.name }}_AXI_DATA_WIDTH/8-1:0]    {{ master.name }}_AXI_WSTRB;
  wire                               {{ master.name }}_AXI_WLAST;
  wire [C_{{ master.name }}_AXI_WUSER_WIDTH-1:0]     {{ master.name }}_AXI_WUSER;
  wire                               {{ master.name }}_AXI_WVALID;
  wire                               {{ master.name }}_AXI_WREADY;
  
  // Master Interface Write Response
  wire [C_{{ master.name }}_AXI_THREAD_ID_WIDTH-1:0] {{ master.name }}_AXI_BID;
  wire [2-1:0]                       {{ master.name }}_AXI_BRESP;
  wire [C_{{ master.name }}_AXI_BUSER_WIDTH-1:0]     {{ master.name }}_AXI_BUSER;
  wire                               {{ master.name }}_AXI_BVALID;
  wire                               {{ master.name }}_AXI_BREADY;
  
  // Master Interface Read Address
  wire [C_{{ master.name }}_AXI_THREAD_ID_WIDTH-1:0] {{ master.name }}_AXI_ARID;
  wire [C_{{ master.name }}_AXI_ADDR_WIDTH-1:0]      {{ master.name }}_AXI_ARADDR;
  wire [8-1:0]                       {{ master.name }}_AXI_ARLEN;
  wire [3-1:0]                       {{ master.name }}_AXI_ARSIZE;
  wire [2-1:0]                       {{ master.name }}_AXI_ARBURST;
  wire [2-1:0]                       {{ master.name }}_AXI_ARLOCK;
  wire [4-1:0]                       {{ master.name }}_AXI_ARCACHE;
  wire [3-1:0]                       {{ master.name }}_AXI_ARPROT;
  wire [4-1:0]                       {{ master.name }}_AXI_ARQOS;
  wire [C_{{ master.name }}_AXI_ARUSER_WIDTH-1:0]    {{ master.name }}_AXI_ARUSER;
  wire                               {{ master.name }}_AXI_ARVALID;
  wire                               {{ master.name }}_AXI_ARREADY;
  
  // Master Interface Read Data 
  wire [C_{{ master.name }}_AXI_THREAD_ID_WIDTH-1:0] {{ master.name }}_AXI_RID;
  wire [C_{{ master.name }}_AXI_DATA_WIDTH-1:0]      {{ master.name }}_AXI_RDATA;
  wire [2-1:0]                       {{ master.name }}_AXI_RRESP;
  wire                               {{ master.name }}_AXI_RLAST;
  wire [C_{{ master.name }}_AXI_RUSER_WIDTH-1:0]     {{ master.name }}_AXI_RUSER;
  wire                               {{ master.name }}_AXI_RVALID;
  wire                               {{ master.name }}_AXI_RREADY;
{% endfor %}

{% for slave in slavelist | sort(attribute='name') %}
  // Clock and Reset
  reg {{ slave.name }}_AXI_ACLK;
  reg {{ slave.name }}_AXI_ARESETN;

  // Slave Interface Write Address
  wire [(C_{{ slave.name }}_AXI_ID_WIDTH-1):0] {{ slave.name }}_AXI_AWID;
  wire [(C_{{ slave.name }}_AXI_ADDR_WIDTH-1):0] {{ slave.name }}_AXI_AWADDR;
  wire [7:0] {{ slave.name }}_AXI_AWLEN;
  wire [2:0] {{ slave.name }}_AXI_AWSIZE;
  wire [1:0] {{ slave.name }}_AXI_AWBURST;
  wire [1:0] {{ slave.name }}_AXI_AWLOCK;
  wire [3:0] {{ slave.name }}_AXI_AWCACHE;
  wire [2:0] {{ slave.name }}_AXI_AWPROT;
  wire [3:0] {{ slave.name }}_AXI_AWREGION;
  wire [3:0] {{ slave.name }}_AXI_AWQOS;
  wire [C_{{ slave.name }}_AXI_AWUSER_WIDTH-1:0] {{ slave.name }}_AXI_AWUSER;
  wire {{ slave.name }}_AXI_AWVALID;
  wire {{ slave.name }}_AXI_AWREADY;

  // Slave Interface Write Data
  wire [C_{{ slave.name }}_AXI_ID_WIDTH-1:0] {{ slave.name }}_AXI_WID;
  wire [(C_{{ slave.name }}_AXI_DATA_WIDTH-1):0] {{ slave.name }}_AXI_WDATA;
  wire [((C_{{ slave.name }}_AXI_DATA_WIDTH/8) -1):0] {{ slave.name }}_AXI_WSTRB;
  wire {{ slave.name }}_AXI_WLAST;
  wire [(C_{{ slave.name }}_AXI_WUSER_WIDTH-1):0] {{ slave.name }}_AXI_WUSER;
  wire {{ slave.name }}_AXI_WVALID;
  wire {{ slave.name }}_AXI_WREADY;

  // Slave Interface Write Response
  wire [(C_{{ slave.name }}_AXI_ID_WIDTH-1):0] {{ slave.name }}_AXI_BID;
  wire [1:0] {{ slave.name }}_AXI_BRESP;
  wire [(C_{{ slave.name }}_AXI_BUSER_WIDTH-1):0] {{ slave.name }}_AXI_BUSER;
  wire {{ slave.name }}_AXI_BVALID;
  wire {{ slave.name }}_AXI_BREADY;

  // Slave Interface Read Address
  wire [(C_{{ slave.name }}_AXI_ID_WIDTH-1):0] {{ slave.name }}_AXI_ARID;
  wire [(C_{{ slave.name }}_AXI_ADDR_WIDTH-1):0] {{ slave.name }}_AXI_ARADDR;
  wire [7:0] {{ slave.name }}_AXI_ARLEN;
  wire [2:0] {{ slave.name }}_AXI_ARSIZE;
  wire [1:0] {{ slave.name }}_AXI_ARBURST;
  wire [1:0] {{ slave.name }}_AXI_ARLOCK;
  wire [3:0] {{ slave.name }}_AXI_ARCACHE;
  wire [2:0] {{ slave.name }}_AXI_ARPROT;
  wire [3:0] {{ slave.name }}_AXI_ARREGION;
  wire [3:0] {{ slave.name }}_AXI_ARQOS;
  wire [(C_{{ slave.name }}_AXI_ARUSER_WIDTH-1):0] {{ slave.name }}_AXI_ARUSER;
  wire {{ slave.name }}_AXI_ARVALID;
  wire {{ slave.name }}_AXI_ARREADY;

  // Slave Interface Read Data
  wire [(C_{{ slave.name }}_AXI_ID_WIDTH-1):0] {{ slave.name }}_AXI_RID;
  wire [(C_{{ slave.name }}_AXI_DATA_WIDTH-1):0] {{ slave.name }}_AXI_RDATA;
  wire [1:0] {{ slave.name }}_AXI_RRESP;
  wire {{ slave.name }}_AXI_RLAST;
  wire [(C_{{ slave.name }}_AXI_RUSER_WIDTH-1):0] {{ slave.name }}_AXI_RUSER;
  wire {{ slave.name }}_AXI_RVALID;
  wire {{ slave.name }}_AXI_RREADY;
{% endfor %}


  {{ ipname }}
  inst_uut
  (
{% for master in masterlist | sort(attribute='name') %}
   .{{ master.name }}_AXI_ACLK({{ master.name }}_AXI_ACLK),
   .{{ master.name }}_AXI_ARESETN({{ master.name }}_AXI_ARESETN),

{%- if not master.lite %}
   .{{ master.name }}_AXI_AWID({{ master.name }}_AXI_AWID),
{%- endif %}
   .{{ master.name }}_AXI_AWADDR({{ master.name }}_AXI_AWADDR),
{%- if not master.lite %}
   .{{ master.name }}_AXI_AWLEN({{ master.name }}_AXI_AWLEN),
   .{{ master.name }}_AXI_AWSIZE({{ master.name }}_AXI_AWSIZE),
   .{{ master.name }}_AXI_AWBURST({{ master.name }}_AXI_AWBURST),
   .{{ master.name }}_AXI_AWLOCK({{ master.name }}_AXI_AWLOCK),
   .{{ master.name }}_AXI_AWCACHE({{ master.name }}_AXI_AWCACHE),
{%- endif %}
   .{{ master.name }}_AXI_AWPROT({{ master.name }}_AXI_AWPROT),
{%- if not master.lite %}
   .{{ master.name }}_AXI_AWQOS({{ master.name }}_AXI_AWQOS),
   .{{ master.name }}_AXI_AWUSER({{ master.name }}_AXI_AWUSER),
{%- endif %}
   .{{ master.name }}_AXI_AWVALID({{ master.name }}_AXI_AWVALID),
   .{{ master.name }}_AXI_AWREADY({{ master.name }}_AXI_AWREADY),
   
   .{{ master.name }}_AXI_WDATA({{ master.name }}_AXI_WDATA),
   .{{ master.name }}_AXI_WSTRB({{ master.name }}_AXI_WSTRB),
{%- if not master.lite %}
   .{{ master.name }}_AXI_WLAST({{ master.name }}_AXI_WLAST),
   .{{ master.name }}_AXI_WUSER({{ master.name }}_AXI_WUSER),
{%- endif %}
   .{{ master.name }}_AXI_WVALID({{ master.name }}_AXI_WVALID),
   .{{ master.name }}_AXI_WREADY({{ master.name }}_AXI_WREADY),
   
{%- if not master.lite %}
   .{{ master.name }}_AXI_BID({{ master.name }}_AXI_BID),
{%- endif %}
   .{{ master.name }}_AXI_BRESP({{ master.name }}_AXI_BRESP),
{%- if not master.lite %}
   .{{ master.name }}_AXI_BUSER({{ master.name }}_AXI_BUSER),
{%- endif %}
   .{{ master.name }}_AXI_BVALID({{ master.name }}_AXI_BVALID),
   .{{ master.name }}_AXI_BREADY({{ master.name }}_AXI_BREADY),
   
{%- if not master.lite %}
   .{{ master.name }}_AXI_ARID({{ master.name }}_AXI_ARID),
{%- endif %}
   .{{ master.name }}_AXI_ARADDR({{ master.name }}_AXI_ARADDR),
{%- if not master.lite %}
   .{{ master.name }}_AXI_ARLEN({{ master.name }}_AXI_ARLEN),
   .{{ master.name }}_AXI_ARSIZE({{ master.name }}_AXI_ARSIZE),
   .{{ master.name }}_AXI_ARBURST({{ master.name }}_AXI_ARBURST),
   .{{ master.name }}_AXI_ARLOCK({{ master.name }}_AXI_ARLOCK),
   .{{ master.name }}_AXI_ARCACHE({{ master.name }}_AXI_ARCACHE),
{%- endif %}
   .{{ master.name }}_AXI_ARPROT({{ master.name }}_AXI_ARPROT),
{%- if not master.lite %}
   .{{ master.name }}_AXI_ARQOS({{ master.name }}_AXI_ARQOS),
   .{{ master.name }}_AXI_ARUSER({{ master.name }}_AXI_ARUSER),
{%- endif %}
   .{{ master.name }}_AXI_ARVALID({{ master.name }}_AXI_ARVALID),
   .{{ master.name }}_AXI_ARREADY({{ master.name }}_AXI_ARREADY),
   
{%- if not master.lite %}
   .{{ master.name }}_AXI_RID({{ master.name }}_AXI_RID),
{%- endif %}
   .{{ master.name }}_AXI_RDATA({{ master.name }}_AXI_RDATA),
   .{{ master.name }}_AXI_RRESP({{ master.name }}_AXI_RRESP),
{%- if not master.lite %}
   .{{ master.name }}_AXI_RLAST({{ master.name }}_AXI_RLAST),
   .{{ master.name }}_AXI_RUSER({{ master.name }}_AXI_RUSER),
{%- endif %}
   .{{ master.name }}_AXI_RVALID({{ master.name }}_AXI_RVALID),
   .{{ master.name }}_AXI_RREADY({{ master.name }}_AXI_RREADY),
{% endfor %}

{% for slave in slavelist | sort(attribute='name') %}
   .{{ slave.name }}_AXI_ACLK({{ slave.name }}_AXI_ACLK),
   .{{ slave.name }}_AXI_ARESETN({{ slave.name }}_AXI_ARESETN),

{%- if not slave.lite %}
   .{{ slave.name }}_AXI_AWID({{ slave.name }}_AXI_AWID),
{%- endif %}
   .{{ slave.name }}_AXI_AWADDR({{ slave.name }}_AXI_AWADDR),
{%- if not slave.lite %}
   .{{ slave.name }}_AXI_AWLEN({{ slave.name }}_AXI_AWLEN),
   .{{ slave.name }}_AXI_AWSIZE({{ slave.name }}_AXI_AWSIZE),
   .{{ slave.name }}_AXI_AWBURST({{ slave.name }}_AXI_AWBURST),
   .{{ slave.name }}_AXI_AWLOCK({{ slave.name }}_AXI_AWLOCK),
   .{{ slave.name }}_AXI_AWCACHE({{ slave.name }}_AXI_AWCACHE),
{%- endif %}
   .{{ slave.name }}_AXI_AWPROT({{ slave.name }}_AXI_AWPROT),
{%- if not slave.lite %}
   .{{ slave.name }}_AXI_AWREGION({{ slave.name }}_AXI_AWREGION),
   .{{ slave.name }}_AXI_AWQOS({{ slave.name }}_AXI_AWQOS),
   .{{ slave.name }}_AXI_AWUSER({{ slave.name }}_AXI_AWUSER),
{%- endif %}
   .{{ slave.name }}_AXI_AWVALID({{ slave.name }}_AXI_AWVALID),
   .{{ slave.name }}_AXI_AWREADY({{ slave.name }}_AXI_AWREADY),
   
{%- if not slave.lite %}
   .{{ slave.name }}_AXI_WID({{ slave.name }}_AXI_WID),
{%- endif %}
   .{{ slave.name }}_AXI_WDATA({{ slave.name }}_AXI_WDATA),
   .{{ slave.name }}_AXI_WSTRB({{ slave.name }}_AXI_WSTRB),
{%- if not slave.lite %}
   .{{ slave.name }}_AXI_WLAST({{ slave.name }}_AXI_WLAST),
   .{{ slave.name }}_AXI_WUSER({{ slave.name }}_AXI_WUSER),
{%- endif %}
   .{{ slave.name }}_AXI_WVALID({{ slave.name }}_AXI_WVALID),
   .{{ slave.name }}_AXI_WREADY({{ slave.name }}_AXI_WREADY),
   
{%- if not slave.lite %}
   .{{ slave.name }}_AXI_BID({{ slave.name }}_AXI_BID),
{%- endif %}
   .{{ slave.name }}_AXI_BRESP({{ slave.name }}_AXI_BRESP),
{%- if not slave.lite %}
   .{{ slave.name }}_AXI_BUSER({{ slave.name }}_AXI_BUSER),
{%- endif %}
   .{{ slave.name }}_AXI_BVALID({{ slave.name }}_AXI_BVALID),
   .{{ slave.name }}_AXI_BREADY({{ slave.name }}_AXI_BREADY),
   
{%- if not slave.lite %}
   .{{ slave.name }}_AXI_ARID({{ slave.name }}_AXI_ARID),
{%- endif %}
   .{{ slave.name }}_AXI_ARADDR({{ slave.name }}_AXI_ARADDR),
{%- if not slave.lite %}
   .{{ slave.name }}_AXI_ARLEN({{ slave.name }}_AXI_ARLEN),
   .{{ slave.name }}_AXI_ARSIZE({{ slave.name }}_AXI_ARSIZE),
   .{{ slave.name }}_AXI_ARBURST({{ slave.name }}_AXI_ARBURST),
   .{{ slave.name }}_AXI_ARLOCK({{ slave.name }}_AXI_ARLOCK),
   .{{ slave.name }}_AXI_ARCACHE({{ slave.name }}_AXI_ARCACHE),
{%- endif %}
   .{{ slave.name }}_AXI_ARPROT({{ slave.name }}_AXI_ARPROT),
{%- if not slave.lite %}
   .{{ slave.name }}_AXI_ARREGION({{ slave.name }}_AXI_ARREGION),
   .{{ slave.name }}_AXI_ARQOS({{ slave.name }}_AXI_ARQOS),
   .{{ slave.name }}_AXI_ARUSER({{ slave.name }}_AXI_ARUSER),
{%- endif %}
   .{{ slave.name }}_AXI_ARVALID({{ slave.name }}_AXI_ARVALID),
   .{{ slave.name }}_AXI_ARREADY({{ slave.name }}_AXI_ARREADY),
   
{%- if not slave.lite %}
   .{{ slave.name }}_AXI_RID({{ slave.name }}_AXI_RID),
{%- endif %}
   .{{ slave.name }}_AXI_RDATA({{ slave.name }}_AXI_RDATA),
   .{{ slave.name }}_AXI_RRESP({{ slave.name }}_AXI_RRESP),
{%- if not slave.lite %}
   .{{ slave.name }}_AXI_RLAST({{ slave.name }}_AXI_RLAST),
   .{{ slave.name }}_AXI_RUSER({{ slave.name }}_AXI_RUSER),
{%- endif %}
   .{{ slave.name }}_AXI_RVALID({{ slave.name }}_AXI_RVALID),
   .{{ slave.name }}_AXI_RREADY({{ slave.name }}_AXI_RREADY),
{% endfor %}

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

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

{% for master in masterlist | sort(attribute='name') %}
{%- if master.lite %}
  assign {{ master.name }}_AXI_AWID = 0;
  assign {{ master.name }}_AXI_AWLEN = 0;
  assign {{ master.name }}_AXI_AWSIZE = {{ log2(int(master.datawidth / 8)) }};
  assign {{ master.name }}_AXI_AWBURST = 2'b01;
  assign {{ master.name }}_AXI_AWLOCK = 1'b0;
  assign {{ master.name }}_AXI_AWCACHE = 4'b0011;
  assign {{ master.name }}_AXI_AWQOS = 4'b0;
  assign {{ master.name }}_AXI_AWUSER = 0;

  assign {{ master.name }}_AXI_WLAST = 1'b1;
  assign {{ master.name }}_AXI_WUSER = 0;

  assign {{ master.name }}_AXI_ARID = 0;
  assign {{ master.name }}_AXI_ARLEN = 0;
  assign {{ master.name }}_AXI_ARSIZE = {{ log2(int(master.datawidth / 8)) }};
  assign {{ master.name }}_AXI_ARBURST = 2'b01;
  assign {{ master.name }}_AXI_ARLOCK = 1'b0;
  assign {{ master.name }}_AXI_ARCACHE = 4'b0011;
  assign {{ master.name }}_AXI_ARQOS = 4'b0;
  assign {{ master.name }}_AXI_ARUSER = 0;
{%- endif %}
{% endfor %}

  dram_stub #
  (
{% for master in masterlist | sort(attribute='name') %}
   .C_{{ master.name }}_AXI_THREAD_ID_WIDTH(C_{{ master.name }}_AXI_THREAD_ID_WIDTH),
   .C_{{ master.name }}_AXI_DATA_WIDTH(C_{{ master.name }}_AXI_DATA_WIDTH),
   .C_{{ master.name }}_AXI_ADDR_WIDTH(C_{{ master.name }}_AXI_ADDR_WIDTH),
   .C_{{ master.name }}_AXI_AWUSER_WIDTH(C_{{ master.name }}_AXI_AWUSER_WIDTH),
   .C_{{ master.name }}_AXI_ARUSER_WIDTH(C_{{ master.name }}_AXI_ARUSER_WIDTH),
   .C_{{ master.name }}_AXI_WUSER_WIDTH(C_{{ master.name }}_AXI_WUSER_WIDTH),
   .C_{{ master.name }}_AXI_RUSER_WIDTH(C_{{ master.name }}_AXI_RUSER_WIDTH),
   .C_{{ master.name }}_AXI_BUSER_WIDTH(C_{{ master.name }}_AXI_BUSER_WIDTH),
{% endfor %}
   .MEMIMG(MEMIMG),
   .SIM_ADDR_WIDTH(SIM_ADDR_WIDTH),
   .READ_LATENCY(READ_LATENCY),
   .WRITE_LATENCY(WRITE_LATENCY),
   .IGNORE_PROTOCOL_ERROR(IGNORE_PROTOCOL_ERROR)
   )
  inst_dram_stub
  (
{% for master in masterlist | sort(attribute='name') %}
   .{{ master.name }}_AXI_ACLK({{ master.name }}_AXI_ACLK),
   .{{ master.name }}_AXI_ARESETN({{ master.name }}_AXI_ARESETN),

   .{{ master.name }}_AXI_AWID({{ master.name }}_AXI_AWID),
   .{{ master.name }}_AXI_AWADDR({{ master.name }}_AXI_AWADDR),
   .{{ master.name }}_AXI_AWLEN({{ master.name }}_AXI_AWLEN),
   .{{ master.name }}_AXI_AWSIZE({{ master.name }}_AXI_AWSIZE),
   .{{ master.name }}_AXI_AWBURST({{ master.name }}_AXI_AWBURST),
   .{{ master.name }}_AXI_AWLOCK({{ master.name }}_AXI_AWLOCK),
   .{{ master.name }}_AXI_AWCACHE({{ master.name }}_AXI_AWCACHE),
   .{{ master.name }}_AXI_AWPROT({{ master.name }}_AXI_AWPROT),
   .{{ master.name }}_AXI_AWQOS({{ master.name }}_AXI_AWQOS),
   .{{ master.name }}_AXI_AWUSER({{ master.name }}_AXI_AWUSER),
   .{{ master.name }}_AXI_AWVALID({{ master.name }}_AXI_AWVALID),
   .{{ master.name }}_AXI_AWREADY({{ master.name }}_AXI_AWREADY),
   
   .{{ master.name }}_AXI_WDATA({{ master.name }}_AXI_WDATA),
   .{{ master.name }}_AXI_WSTRB({{ master.name }}_AXI_WSTRB),
   .{{ master.name }}_AXI_WLAST({{ master.name }}_AXI_WLAST),
   .{{ master.name }}_AXI_WUSER({{ master.name }}_AXI_WUSER),
   .{{ master.name }}_AXI_WVALID({{ master.name }}_AXI_WVALID),
   .{{ master.name }}_AXI_WREADY({{ master.name }}_AXI_WREADY),
   
   .{{ master.name }}_AXI_BID({{ master.name }}_AXI_BID),
   .{{ master.name }}_AXI_BRESP({{ master.name }}_AXI_BRESP),
   .{{ master.name }}_AXI_BUSER({{ master.name }}_AXI_BUSER),
   .{{ master.name }}_AXI_BVALID({{ master.name }}_AXI_BVALID),
   .{{ master.name }}_AXI_BREADY({{ master.name }}_AXI_BREADY),
   
   .{{ master.name }}_AXI_ARID({{ master.name }}_AXI_ARID),
   .{{ master.name }}_AXI_ARADDR({{ master.name }}_AXI_ARADDR),
   .{{ master.name }}_AXI_ARLEN({{ master.name }}_AXI_ARLEN),
   .{{ master.name }}_AXI_ARSIZE({{ master.name }}_AXI_ARSIZE),
   .{{ master.name }}_AXI_ARBURST({{ master.name }}_AXI_ARBURST),
   .{{ master.name }}_AXI_ARLOCK({{ master.name }}_AXI_ARLOCK),
   .{{ master.name }}_AXI_ARCACHE({{ master.name }}_AXI_ARCACHE),
   .{{ master.name }}_AXI_ARPROT({{ master.name }}_AXI_ARPROT),
   .{{ master.name }}_AXI_ARQOS({{ master.name }}_AXI_ARQOS),
   .{{ master.name }}_AXI_ARUSER({{ master.name }}_AXI_ARUSER),
   .{{ master.name }}_AXI_ARVALID({{ master.name }}_AXI_ARVALID),
   .{{ master.name }}_AXI_ARREADY({{ master.name }}_AXI_ARREADY),
   
   .{{ master.name }}_AXI_RID({{ master.name }}_AXI_RID),
   .{{ master.name }}_AXI_RDATA({{ master.name }}_AXI_RDATA),
   .{{ master.name }}_AXI_RRESP({{ master.name }}_AXI_RRESP),
   .{{ master.name }}_AXI_RLAST({{ master.name }}_AXI_RLAST),
   .{{ master.name }}_AXI_RUSER({{ master.name }}_AXI_RUSER),
   .{{ master.name }}_AXI_RVALID({{ master.name }}_AXI_RVALID),
   .{{ master.name }}_AXI_RREADY({{ master.name }}_AXI_RREADY),
{% endfor %}

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

{% for slave in slavelist | sort(attribute='name') %}
  reg {{ slave.name }}_ext_write_enq;
  reg [C_{{ slave.name }}_AXI_DATA_WIDTH-1:0] {{ slave.name }}_ext_write_data;
  wire {{ slave.name }}_ext_write_almost_full;
  reg {{ slave.name }}_ext_read_deq;
  wire [C_{{ slave.name }}_AXI_DATA_WIDTH-1:0] {{ slave.name }}_ext_read_data;
  wire {{ slave.name }}_ext_read_empty;

  reg [C_{{ slave.name }}_AXI_ADDR_WIDTH-1:0] {{ slave.name }}_ext_addr;
  reg {{ slave.name }}_ext_read_enable;
  reg {{ slave.name }}_ext_write_enable;
  reg [8:0] {{ slave.name }}_ext_word_size;
  wire {{ slave.name }}_ext_done;

  initial begin
    {{ slave.name }}_ext_write_enq = 0;
    {{ slave.name }}_ext_write_data = 0;
    {{ slave.name }}_ext_read_deq = 0;
    {{ slave.name }}_ext_addr = 0;
    {{ slave.name }}_ext_read_enable = 0;
    {{ slave.name }}_ext_write_enable = 0;
    {{ slave.name }}_ext_word_size = 0;
  end
{% endfor %}

{% for slave in slavelist | sort(attribute='name') %}
  axi_master_fifo #
  (
   .C_M_AXI_THREAD_ID_WIDTH(C_{{ slave.name }}_AXI_ID_WIDTH),
   .C_M_AXI_ADDR_WIDTH(C_{{ slave.name }}_AXI_ADDR_WIDTH),
   .C_M_AXI_DATA_WIDTH(C_{{ slave.name }}_AXI_DATA_WIDTH),
   .C_M_AXI_AWUSER_WIDTH(C_{{ slave.name }}_AXI_AWUSER_WIDTH),
   .C_M_AXI_ARUSER_WIDTH(C_{{ slave.name }}_AXI_ARUSER_WIDTH),
   .C_M_AXI_WUSER_WIDTH(C_{{ slave.name }}_AXI_WUSER_WIDTH),
   .C_M_AXI_RUSER_WIDTH(C_{{ slave.name }}_AXI_RUSER_WIDTH),
   .C_M_AXI_BUSER_WIDTH(C_{{ slave.name }}_AXI_BUSER_WIDTH),
   .C_M_AXI_SUPPORTS_WRITE(C_{{ slave.name }}_AXI_SUPPORTS_WRITE),
   .C_M_AXI_SUPPORTS_READ(C_{{ slave.name }}_AXI_SUPPORTS_READ),
   .C_M_AXI_TARGET(0)
   )
  inst_axi_master_fifo_{{ slave.name }}
  (
   .ACLK({{ slave.name }}_AXI_ACLK), // AXI clock
   .ARESETN({{ slave.name }}_AXI_ARESETN), // AXI reset

   .user_write_enq({{ slave.name }}_ext_write_enq),
   .user_write_data({{ slave.name }}_ext_write_data),
   .user_write_almost_full({{ slave.name }}_ext_write_almost_full),
   .user_read_deq({{ slave.name }}_ext_read_deq),
   .user_read_data({{ slave.name }}_ext_read_data),
   .user_read_empty({{ slave.name }}_ext_read_empty),

   .user_addr({{ slave.name }}_ext_addr),
   .user_read_enable({{ slave.name }}_ext_read_enable),
   .user_write_enable({{ slave.name }}_ext_write_enable),
   .user_word_size({{ slave.name }}_ext_word_size),
   .user_done({{ slave.name }}_ext_done),

   .M_AXI_AWID({{ slave.name }}_AXI_AWID),
   .M_AXI_AWADDR({{ slave.name }}_AXI_AWADDR),
   .M_AXI_AWLEN({{ slave.name }}_AXI_AWLEN),
   .M_AXI_AWSIZE({{ slave.name }}_AXI_AWSIZE),
   .M_AXI_AWBURST({{ slave.name }}_AXI_AWBURST),
   .M_AXI_AWLOCK({{ slave.name }}_AXI_AWLOCK[0]),
   .M_AXI_AWCACHE({{ slave.name }}_AXI_AWCACHE),
   .M_AXI_AWPROT({{ slave.name }}_AXI_AWPROT),
   .M_AXI_AWQOS({{ slave.name }}_AXI_AWQOS),
   .M_AXI_AWUSER({{ slave.name }}_AXI_AWUSER),
   .M_AXI_AWVALID({{ slave.name }}_AXI_AWVALID),
   .M_AXI_AWREADY({{ slave.name }}_AXI_AWREADY),

   .M_AXI_WDATA({{ slave.name }}_AXI_WDATA),
   .M_AXI_WSTRB({{ slave.name }}_AXI_WSTRB),
   .M_AXI_WLAST({{ slave.name }}_AXI_WLAST),
   .M_AXI_WUSER({{ slave.name }}_AXI_WUSER),
   .M_AXI_WVALID({{ slave.name }}_AXI_WVALID),
   .M_AXI_WREADY({{ slave.name }}_AXI_WREADY),

   .M_AXI_BID({{ slave.name }}_AXI_BID),
   .M_AXI_BRESP({{ slave.name }}_AXI_BRESP),
   .M_AXI_BUSER({{ slave.name }}_AXI_BUSER),
   .M_AXI_BVALID({{ slave.name }}_AXI_BVALID),
   .M_AXI_BREADY({{ slave.name }}_AXI_BREADY),

   .M_AXI_ARID({{ slave.name }}_AXI_ARID),
   .M_AXI_ARADDR({{ slave.name }}_AXI_ARADDR),
   .M_AXI_ARLEN({{ slave.name }}_AXI_ARLEN),
   .M_AXI_ARSIZE({{ slave.name }}_AXI_ARSIZE),
   .M_AXI_ARBURST({{ slave.name }}_AXI_ARBURST),
   .M_AXI_ARLOCK({{ slave.name }}_AXI_ARLOCK),
   .M_AXI_ARCACHE({{ slave.name }}_AXI_ARCACHE),
   .M_AXI_ARPROT({{ slave.name }}_AXI_ARPROT),
   .M_AXI_ARQOS({{ slave.name }}_AXI_ARQOS),
   .M_AXI_ARUSER({{ slave.name }}_AXI_ARUSER),
   .M_AXI_ARVALID({{ slave.name }}_AXI_ARVALID),
   .M_AXI_ARREADY({{ slave.name }}_AXI_ARREADY),

   .M_AXI_RID({{ slave.name }}_AXI_RID),
   .M_AXI_RDATA({{ slave.name }}_AXI_RDATA),
   .M_AXI_RRESP({{ slave.name }}_AXI_RRESP),
   .M_AXI_RLAST({{ slave.name }}_AXI_RLAST),
   .M_AXI_RUSER({{ slave.name }}_AXI_RUSER),
   .M_AXI_RVALID({{ slave.name }}_AXI_RVALID),
   .M_AXI_RREADY({{ slave.name }}_AXI_RREADY)
  );
  assign {{ slave.name }}_AXI_AWREGION = 0;
  assign {{ slave.name }}_AXI_AWLOCK[1] = 1'b0;
{% endfor %}

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

{% for master in masterlist | sort(attribute='name') %}
  initial begin
    {{ master.name }}_AXI_ACLK = 0;
    #HPERIOD_CLK_BUS;
    forever #HPERIOD_CLK_BUS {{ master.name }}_AXI_ACLK = ~{{ master.name }}_AXI_ACLK;
  end
{% endfor %}

{% for slave in slavelist | sort(attribute='name') %}
  initial begin
    {{ slave.name }}_AXI_ACLK = 0;
    #HPERIOD_CLK_BUS;
    forever #HPERIOD_CLK_BUS {{ slave.name }}_AXI_ACLK = ~{{ slave.name }}_AXI_ACLK;
  end
{% endfor %}

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

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

    URESETN = 1;

{% for master in masterlist | sort(attribute='name') %}
    {{ master.name }}_AXI_ARESETN = 1;
{% endfor %}
{% for slave in slavelist | sort(attribute='name') %}
    {{ slave.name }}_AXI_ARESETN = 1;
{% endfor %}
    
    #100;

    URESETN = 0;

{% for master in masterlist | sort(attribute='name') %}
    {{ master.name }}_AXI_ARESETN = 0;
{% endfor %}
{% for slave in slavelist | sort(attribute='name') %}
    {{ slave.name }}_AXI_ARESETN = 0;
{% endfor %}

    #100;

    URESETN = 1;

{% for master in masterlist | sort(attribute='name') %}
    {{ master.name }}_AXI_ARESETN = 1;
{% endfor %}
{% for slave in slavelist | sort(attribute='name') %}
    {{ slave.name }}_AXI_ARESETN = 1;
{% endfor %}

    #100;

    nclk();

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

  //----------------------------------------------------------------------------
  // DRAM read/write task
  //----------------------------------------------------------------------------
  task mem_write;
    input [SIM_ADDR_WIDTH-1:0] addr;
    input [5:0] size;
    input [255:0] data;
    integer i;
    begin
      for(i=0; i<size; i=i+1) begin
        inst_dram_stub.memory[addr + i] = (data >> (8 * i)) & 8'hFF;
      end
    end
  endtask

  task mem_read;
    input [SIM_ADDR_WIDTH-1:0] addr;
    input [5:0] size;
    output [255:0] data;
    integer i;
    begin
      data = 256'h0;
      for(i=0; i<size; i=i+1) begin
        data = data | ((inst_dram_stub.memory[addr + i] & 8'hFF) << (i * 8));
      end
    end
  endtask

  //----------------------------------------------------------------------------
  // iochannel/ioregister read/write task
  //----------------------------------------------------------------------------
{% for slave in slavelist | sort(attribute='name') %}
  task slave_write_{{ slave.name }};
    input [C_{{ slave.name }}_AXI_DATA_WIDTH-1:0] data;
    input [C_{{ slave.name }}_AXI_ADDR_WIDTH-1:0] addr;
    begin
      nclk();
      wait(!{{ slave.name }}_ext_write_almost_full);
      #1;
      {{ slave.name }}_ext_write_enable = 1;
      {{ slave.name }}_ext_addr = addr;
      {{ slave.name }}_ext_word_size = 1;
      {{ slave.name }}_ext_write_enq = 1;
      {{ slave.name }}_ext_write_data = data;
      nclk();
      {{ slave.name }}_ext_write_enq = 0;
      nclk();
      wait({{ slave.name }}_ext_done);
      #1;
      {{ slave.name }}_ext_write_enable = 0;
      nclk();
    end
  endtask

  task slave_read_{{ slave.name }};
    output [C_{{ slave.name }}_AXI_DATA_WIDTH-1:0] data;
    input [C_{{ slave.name }}_AXI_ADDR_WIDTH-1:0] addr;
    begin
      nclk();
      {{ slave.name }}_ext_read_enable = 1;
      {{ slave.name }}_ext_addr = addr;
      {{ slave.name }}_ext_word_size = 1;
      nclk();
      nclk();
      wait({{ slave.name }}_ext_done);
      #1;
      {{ slave.name }}_ext_read_enable = 0;
      nclk();
      wait(!{{ slave.name }}_ext_read_empty);
      #1;
      data = {{ slave.name }}_ext_read_data;
      {{ slave.name }}_ext_read_deq = 1;
      nclk();
      {{ slave.name }}_ext_read_deq = 0;
      nclk();
    end
  endtask
{% endfor %}

  //----------------------------------------------------------------------------
  // 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 dram_stub #
  (
{% for master in masterlist | sort(attribute='name') %}   
   parameter integer C_{{ master.name }}_AXI_THREAD_ID_WIDTH       = 1,
   parameter integer C_{{ master.name }}_AXI_ADDR_WIDTH            = 32,
   parameter integer C_{{ master.name }}_AXI_DATA_WIDTH            = 32,
   parameter integer C_{{ master.name }}_AXI_AWUSER_WIDTH          = 1,
   parameter integer C_{{ master.name }}_AXI_ARUSER_WIDTH          = 1,
   parameter integer C_{{ master.name }}_AXI_WUSER_WIDTH           = 1,
   parameter integer C_{{ master.name }}_AXI_RUSER_WIDTH           = 1,
   parameter integer C_{{ master.name }}_AXI_BUSER_WIDTH           = 1,
{% endfor %}
   parameter MEMIMG = "{{ memimg }}",
   parameter SIM_ADDR_WIDTH = {{ simaddrwidth }},
   parameter READ_LATENCY = 32,
   parameter WRITE_LATENCY = 32,
   parameter IGNORE_PROTOCOL_ERROR = 0
   )
  (
{% for master in masterlist | sort(attribute='name') %}   
   // Clock and Reset
   input  wire                               {{ master.name }}_AXI_ACLK,
   input  wire                               {{ master.name }}_AXI_ARESETN,

   // Master Interface Write Address
   input  wire [C_{{ master.name }}_AXI_THREAD_ID_WIDTH-1:0] {{ master.name }}_AXI_AWID,
   input  wire [C_{{ master.name }}_AXI_ADDR_WIDTH-1:0]      {{ master.name }}_AXI_AWADDR,
   input  wire [8-1:0]                       {{ master.name }}_AXI_AWLEN,
   input  wire [3-1:0]                       {{ master.name }}_AXI_AWSIZE,
   input  wire [2-1:0]                       {{ master.name }}_AXI_AWBURST,
   input  wire                               {{ master.name }}_AXI_AWLOCK,
   input  wire [4-1:0]                       {{ master.name }}_AXI_AWCACHE,
   input  wire [3-1:0]                       {{ master.name }}_AXI_AWPROT,
   input  wire [4-1:0]                       {{ master.name }}_AXI_AWQOS,
   input  wire [C_{{ master.name }}_AXI_AWUSER_WIDTH-1:0]    {{ master.name }}_AXI_AWUSER,
   input  wire                               {{ master.name }}_AXI_AWVALID,
   output reg                                {{ master.name }}_AXI_AWREADY,
   
   // Master Interface Write Data
   input  wire [C_{{ master.name }}_AXI_DATA_WIDTH-1:0]      {{ master.name }}_AXI_WDATA,
   input  wire [C_{{ master.name }}_AXI_DATA_WIDTH/8-1:0]    {{ master.name }}_AXI_WSTRB,
   input  wire                               {{ master.name }}_AXI_WLAST,
   input  wire [C_{{ master.name }}_AXI_WUSER_WIDTH-1:0]     {{ master.name }}_AXI_WUSER,
   input  wire                               {{ master.name }}_AXI_WVALID,
   output reg                                {{ master.name }}_AXI_WREADY,
   
   // Master Interface Write Response
   output wire [C_{{ master.name }}_AXI_THREAD_ID_WIDTH-1:0] {{ master.name }}_AXI_BID,
   output wire [2-1:0]                       {{ master.name }}_AXI_BRESP,
   output wire [C_{{ master.name }}_AXI_BUSER_WIDTH-1:0]     {{ master.name }}_AXI_BUSER,
   output reg                                {{ master.name }}_AXI_BVALID,
   input  wire                               {{ master.name }}_AXI_BREADY,
   
   // Master Interface Read Address
   input  wire [C_{{ master.name }}_AXI_THREAD_ID_WIDTH-1:0] {{ master.name }}_AXI_ARID,
   input  wire [C_{{ master.name }}_AXI_ADDR_WIDTH-1:0]      {{ master.name }}_AXI_ARADDR,
   input  wire [8-1:0]                       {{ master.name }}_AXI_ARLEN,
   input  wire [3-1:0]                       {{ master.name }}_AXI_ARSIZE,
   input  wire [2-1:0]                       {{ master.name }}_AXI_ARBURST,
   input  wire [2-1:0]                       {{ master.name }}_AXI_ARLOCK,
   input  wire [4-1:0]                       {{ master.name }}_AXI_ARCACHE,
   input  wire [3-1:0]                       {{ master.name }}_AXI_ARPROT,
   input  wire [4-1:0]                       {{ master.name }}_AXI_ARQOS,
   input  wire [C_{{ master.name }}_AXI_ARUSER_WIDTH-1:0]    {{ master.name }}_AXI_ARUSER,
   input  wire                               {{ master.name }}_AXI_ARVALID,
   output reg                                {{ master.name }}_AXI_ARREADY,
   
   // Master Interface Read Data 
   output wire [C_{{ master.name }}_AXI_THREAD_ID_WIDTH-1:0] {{ master.name }}_AXI_RID,
   output reg  [C_{{ master.name }}_AXI_DATA_WIDTH-1:0]      {{ master.name }}_AXI_RDATA,
   output wire [2-1:0]                       {{ master.name }}_AXI_RRESP,
   output reg                                {{ master.name }}_AXI_RLAST,
   output wire [C_{{ master.name }}_AXI_RUSER_WIDTH-1:0]     {{ master.name }}_AXI_RUSER,
   output reg                                {{ master.name }}_AXI_RVALID,
   input  wire                               {{ master.name }}_AXI_RREADY,
{% endfor %}

   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

{% for master in masterlist | sort(attribute='name') %}   
  task mem_write_{{ master.name }};
    input [SIM_ADDR_WIDTH-1:0] addr;
    input [SIM_ADDR_WIDTH-1:0] size;
    input [C_{{ master.name }}_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_{{ master.name }};
    input [SIM_ADDR_WIDTH-1:0] addr;
    input [SIM_ADDR_WIDTH-1:0] size;
    output [C_{{ master.name }}_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
{% endfor %}

  //------------------------------------------------------------------------------
  // Timing Model
  //------------------------------------------------------------------------------
{% for master in masterlist | sort(attribute='name') %}   
  reg {{ master.name }}_AXI_write_mode;
  reg [C_{{ master.name }}_AXI_ADDR_WIDTH-1:0] d_{{ master.name }}_AXI_AWADDR;
  reg [8-1:0] d_{{ master.name }}_AXI_AWLEN;

  reg {{ master.name }}_AXI_read_mode;
  reg [C_{{ master.name }}_AXI_ADDR_WIDTH-1:0] d_{{ master.name }}_AXI_ARADDR;
  reg [8-1:0] d_{{ master.name }}_AXI_ARLEN;

  reg [31:0] {{ master.name }}_stall_count;

  reg {{ master.name }}_reset_done;
  initial begin
    {{ master.name }}_reset_done = 0;
    #1;
    wait(!{{ master.name }}_AXI_ARESETN);
    wait({{ master.name }}_AXI_ARESETN);
    #1;
    {{ master.name }}_reset_done = 1;
  end
  
  always @(negedge {{ master.name }}_AXI_ACLK) begin
    if(!{{ master.name }}_AXI_ARESETN) begin
      {{ master.name }}_AXI_write_mode <= 0;
      {{ master.name }}_AXI_read_mode <= 0;
      {{ master.name }}_stall_count <= 0;
    end else begin
      {{ master.name }}_AXI_AWREADY = 0;
      {{ master.name }}_AXI_WREADY = 0;
      {{ master.name }}_AXI_BVALID = 0;
      
      {{ master.name }}_AXI_ARREADY = 0;
      {{ master.name }}_AXI_RVALID = 0;
      {{ master.name }}_AXI_RLAST = 0;

      if({{ master.name }}_reset_done) begin
        if( !(({{ master.name }}_AXI_AWVALID === 1'b0) || ({{ master.name }}_AXI_AWVALID === 1'b1)) ) begin
          $display("Error: Illegal write operation: {{ master.name }}_AXI_AWVALID = %b, AWVALID should be 1'b1 or 1'b0.", {{ master.name }}_AXI_AWVALID);
          if(!IGNORE_PROTOCOL_ERROR) $finish;
        end
        if( !(({{ master.name }}_AXI_WVALID === 1'b0) || ({{ master.name }}_AXI_WVALID === 1'b1)) ) begin
          $display("Error: Illegal write operation: {{ master.name }}_AXI_WVALID = %b, WVALID should be 1'b1 or 1'b0.", {{ master.name }}_AXI_WVALID);
          if(!IGNORE_PROTOCOL_ERROR) $finish;
        end
        if( !(({{ master.name }}_AXI_ARVALID === 1'b0) || ({{ master.name }}_AXI_ARVALID === 1'b1)) ) begin
          $display("Error: Illegal read operation: {{ master.name }}_AXI_ARVALID = %b, ARVALID should be 1'b1 or 1'b0.", {{ master.name }}_AXI_ARVALID);
          if(!IGNORE_PROTOCOL_ERROR) $finish;
        end
        if( !(({{ master.name }}_AXI_RREADY === 1'b0) || ({{ master.name }}_AXI_RREADY === 1'b1)) ) begin
          $display("Error: Illegal read operation: {{ master.name }}_AXI_RREADY = %b, RREADY should be 1'b1 or 1'b0.", {{ master.name }}_AXI_RREADY);
          if(!IGNORE_PROTOCOL_ERROR) $finish;
        end
      end

      if(!{{ master.name }}_AXI_write_mode && {{ master.name }}_AXI_AWVALID) begin
        {{ master.name }}_stall_count <= {{ master.name }}_stall_count + 1;
        if({{ master.name }}_stall_count == WRITE_LATENCY) begin
          {{ master.name }}_stall_count <= 0;
          {{ master.name }}_AXI_write_mode <= 1;
          {{ master.name }}_AXI_AWREADY = 1;
          d_{{ master.name }}_AXI_AWADDR = {{ master.name }}_AXI_AWADDR;
          d_{{ master.name }}_AXI_AWLEN = {{ master.name }}_AXI_AWLEN;
        end
      end

      if(!{{ master.name }}_AXI_read_mode && {{ master.name }}_AXI_ARVALID) begin
        {{ master.name }}_stall_count <= {{ master.name }}_stall_count + 1;
        if({{ master.name }}_stall_count == READ_LATENCY) begin
          {{ master.name }}_stall_count <= 0;
          {{ master.name }}_AXI_read_mode <= 1;
          {{ master.name }}_AXI_ARREADY = 1;
          d_{{ master.name }}_AXI_ARADDR = {{ master.name }}_AXI_ARADDR;
          d_{{ master.name }}_AXI_ARLEN = {{ master.name }}_AXI_ARLEN;
        end
      end
      
      if({{ master.name }}_AXI_write_mode) begin
        {{ master.name }}_AXI_WREADY = 1;
        if({{ master.name }}_AXI_WVALID) begin
          mem_write_{{ master.name }}(d_{{ master.name }}_AXI_AWADDR, C_{{ master.name }}_AXI_DATA_WIDTH/8, {{ master.name }}_AXI_WDATA);
          if(d_{{ master.name }}_AXI_AWLEN == 0) begin // actual burst length -1
            if({{ master.name }}_AXI_WLAST !== 1'b1) begin
              $display("Error: Illegal write operation: {{ master.name }}_AXI_WLAST = %b, WLAST should be 1'b1.", {{ master.name }}_AXI_WLAST);
              if(!IGNORE_PROTOCOL_ERROR) $finish;
            end
            {{ master.name }}_AXI_write_mode <= 0;
            {{ master.name }}_AXI_BVALID = 1;
          end else begin
            if({{ master.name }}_AXI_WLAST !== 1'b0) begin
              $display("Error: Illegal write operation: {{ master.name }}_AXI_WLAST = %b, WLAST should be 1'b0.", {{ master.name }}_AXI_WLAST);
              if(!IGNORE_PROTOCOL_ERROR) $finish;
            end
          end
          d_{{ master.name }}_AXI_AWADDR = d_{{ master.name }}_AXI_AWADDR + (C_{{ master.name }}_AXI_DATA_WIDTH / 8);
          d_{{ master.name }}_AXI_AWLEN = d_{{ master.name }}_AXI_AWLEN - 1;
        end
      end

      if({{ master.name }}_AXI_read_mode) begin
        mem_read_{{ master.name }}(d_{{ master.name }}_AXI_ARADDR, C_{{ master.name }}_AXI_DATA_WIDTH/8, {{ master.name }}_AXI_RDATA);
        {{ master.name }}_AXI_RVALID = 1;
        if(d_{{ master.name }}_AXI_ARLEN == 0) begin // actual burst length -1
          {{ master.name }}_AXI_RLAST = 1;
          if({{ master.name }}_AXI_RREADY) begin
            {{ master.name }}_AXI_read_mode <= 0;
          end
        end
        if({{ master.name }}_AXI_RREADY) begin
          d_{{ master.name }}_AXI_ARADDR = d_{{ master.name }}_AXI_ARADDR + (C_{{ master.name }}_AXI_DATA_WIDTH / 8);
          d_{{ master.name }}_AXI_ARLEN = d_{{ master.name }}_AXI_ARLEN - 1;
        end
      end

    end
  end
{% endfor %}

endmodule

