diff --git a/src/finn/qnn-data/verilog/myadd/myadd_myadd.v b/src/finn/qnn-data/verilog/myadd/myadd_myadd.v
new file mode 100644
index 0000000000000000000000000000000000000000..0b18b5c300c4dc62d2f0105a896d19980a4f7dca
--- /dev/null
+++ b/src/finn/qnn-data/verilog/myadd/myadd_myadd.v
@@ -0,0 +1,118 @@
+// ==============================================================
+// RTL generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
+// Version: 2019.1
+// Copyright (C) 1986-2019 Xilinx, Inc. All Rights Reserved.
+//
+// ===========================================================
+
+`timescale 1 ns / 1 ps
+
+(* CORE_GENERATION_INFO="myadd_myadd,hls_ip_2019_1,{HLS_INPUT_TYPE=cxx,HLS_INPUT_FLOAT=0,HLS_INPUT_FIXED=1,HLS_INPUT_PART=xc7z020-clg400-1,HLS_INPUT_CLOCK=5.000000,HLS_INPUT_ARCH=others,HLS_SYN_CLOCK=3.552000,HLS_SYN_LAT=0,HLS_SYN_TPT=none,HLS_SYN_MEM=0,HLS_SYN_DSP=0,HLS_SYN_FF=144,HLS_SYN_LUT=271,HLS_VERSION=2019_1}" *)
+
+module myadd_myadd (
+        s_axi_control_AWVALID,
+        s_axi_control_AWREADY,
+        s_axi_control_AWADDR,
+        s_axi_control_WVALID,
+        s_axi_control_WREADY,
+        s_axi_control_WDATA,
+        s_axi_control_WSTRB,
+        s_axi_control_ARVALID,
+        s_axi_control_ARREADY,
+        s_axi_control_ARADDR,
+        s_axi_control_RVALID,
+        s_axi_control_RREADY,
+        s_axi_control_RDATA,
+        s_axi_control_RRESP,
+        s_axi_control_BVALID,
+        s_axi_control_BREADY,
+        s_axi_control_BRESP,
+        ap_clk,
+        ap_rst_n,
+        interrupt
+);
+
+parameter    C_S_AXI_CONTROL_DATA_WIDTH = 32;
+parameter    C_S_AXI_CONTROL_ADDR_WIDTH = 6;
+parameter    C_S_AXI_DATA_WIDTH = 32;
+
+parameter C_S_AXI_CONTROL_WSTRB_WIDTH = (32 / 8);
+parameter C_S_AXI_WSTRB_WIDTH = (32 / 8);
+
+input   s_axi_control_AWVALID;
+output   s_axi_control_AWREADY;
+input  [C_S_AXI_CONTROL_ADDR_WIDTH - 1:0] s_axi_control_AWADDR;
+input   s_axi_control_WVALID;
+output   s_axi_control_WREADY;
+input  [C_S_AXI_CONTROL_DATA_WIDTH - 1:0] s_axi_control_WDATA;
+input  [C_S_AXI_CONTROL_WSTRB_WIDTH - 1:0] s_axi_control_WSTRB;
+input   s_axi_control_ARVALID;
+output   s_axi_control_ARREADY;
+input  [C_S_AXI_CONTROL_ADDR_WIDTH - 1:0] s_axi_control_ARADDR;
+output   s_axi_control_RVALID;
+input   s_axi_control_RREADY;
+output  [C_S_AXI_CONTROL_DATA_WIDTH - 1:0] s_axi_control_RDATA;
+output  [1:0] s_axi_control_RRESP;
+output   s_axi_control_BVALID;
+input   s_axi_control_BREADY;
+output  [1:0] s_axi_control_BRESP;
+input   ap_clk;
+input   ap_rst_n;
+output   interrupt;
+
+wire    ap_start;
+wire    ap_done;
+wire    ap_idle;
+wire    ap_ready;
+wire   [31:0] a_V;
+wire   [31:0] b_V;
+wire   [31:0] ap_return;
+ reg    ap_rst_n_inv;
+
+myadd_myadd_control_s_axi #(
+    .C_S_AXI_ADDR_WIDTH( C_S_AXI_CONTROL_ADDR_WIDTH ),
+    .C_S_AXI_DATA_WIDTH( C_S_AXI_CONTROL_DATA_WIDTH ))
+myadd_control_s_axi_U(
+    .AWVALID(s_axi_control_AWVALID),
+    .AWREADY(s_axi_control_AWREADY),
+    .AWADDR(s_axi_control_AWADDR),
+    .WVALID(s_axi_control_WVALID),
+    .WREADY(s_axi_control_WREADY),
+    .WDATA(s_axi_control_WDATA),
+    .WSTRB(s_axi_control_WSTRB),
+    .ARVALID(s_axi_control_ARVALID),
+    .ARREADY(s_axi_control_ARREADY),
+    .ARADDR(s_axi_control_ARADDR),
+    .RVALID(s_axi_control_RVALID),
+    .RREADY(s_axi_control_RREADY),
+    .RDATA(s_axi_control_RDATA),
+    .RRESP(s_axi_control_RRESP),
+    .BVALID(s_axi_control_BVALID),
+    .BREADY(s_axi_control_BREADY),
+    .BRESP(s_axi_control_BRESP),
+    .ACLK(ap_clk),
+    .ARESET(ap_rst_n_inv),
+    .ACLK_EN(1'b1),
+    .ap_start(ap_start),
+    .interrupt(interrupt),
+    .ap_ready(ap_ready),
+    .ap_done(ap_done),
+    .ap_idle(ap_idle),
+    .ap_return(ap_return),
+    .a_V(a_V),
+    .b_V(b_V)
+);
+
+assign ap_done = ap_start;
+
+assign ap_idle = 1'b1;
+
+assign ap_ready = ap_start;
+
+assign ap_return = (b_V + a_V);
+
+always @ (*) begin
+    ap_rst_n_inv = ~ap_rst_n;
+end
+
+endmodule //myadd_myadd
diff --git a/src/finn/qnn-data/verilog/myadd/myadd_myadd_control_s_axi.v b/src/finn/qnn-data/verilog/myadd/myadd_myadd_control_s_axi.v
new file mode 100644
index 0000000000000000000000000000000000000000..40260ca930b5dc251f9d095420ae9ad0b77d4c93
--- /dev/null
+++ b/src/finn/qnn-data/verilog/myadd/myadd_myadd_control_s_axi.v
@@ -0,0 +1,357 @@
+// ==============================================================
+// Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC v2019.1 (64-bit)
+// Copyright 1986-2019 Xilinx, Inc. All Rights Reserved.
+// ==============================================================
+`timescale 1ns/1ps
+module myadd_myadd_control_s_axi
+#(parameter
+    C_S_AXI_ADDR_WIDTH = 6,
+    C_S_AXI_DATA_WIDTH = 32
+)(
+    input  wire                          ACLK,
+    input  wire                          ARESET,
+    input  wire                          ACLK_EN,
+    input  wire [C_S_AXI_ADDR_WIDTH-1:0] AWADDR,
+    input  wire                          AWVALID,
+    output wire                          AWREADY,
+    input  wire [C_S_AXI_DATA_WIDTH-1:0] WDATA,
+    input  wire [C_S_AXI_DATA_WIDTH/8-1:0] WSTRB,
+    input  wire                          WVALID,
+    output wire                          WREADY,
+    output wire [1:0]                    BRESP,
+    output wire                          BVALID,
+    input  wire                          BREADY,
+    input  wire [C_S_AXI_ADDR_WIDTH-1:0] ARADDR,
+    input  wire                          ARVALID,
+    output wire                          ARREADY,
+    output wire [C_S_AXI_DATA_WIDTH-1:0] RDATA,
+    output wire [1:0]                    RRESP,
+    output wire                          RVALID,
+    input  wire                          RREADY,
+    output wire                          interrupt,
+    output wire                          ap_start,
+    input  wire                          ap_done,
+    input  wire                          ap_ready,
+    input  wire                          ap_idle,
+    input  wire [31:0]                   ap_return,
+    output wire [31:0]                   a_V,
+    output wire [31:0]                   b_V
+);
+//------------------------Address Info-------------------
+// 0x00 : Control signals
+//        bit 0  - ap_start (Read/Write/SC)
+//        bit 1  - ap_done (Read/COR)
+//        bit 2  - ap_idle (Read)
+//        bit 3  - ap_ready (Read)
+//        bit 7  - auto_restart (Read/Write)
+//        others - reserved
+// 0x04 : Global Interrupt Enable Register
+//        bit 0  - Global Interrupt Enable (Read/Write)
+//        others - reserved
+// 0x08 : IP Interrupt Enable Register (Read/Write)
+//        bit 0  - Channel 0 (ap_done)
+//        others - reserved
+// 0x0c : IP Interrupt Status Register (Read/TOW)
+//        bit 0  - Channel 0 (ap_done)
+//        others - reserved
+// 0x10 : Data signal of ap_return
+//        bit 31~0 - ap_return[31:0] (Read)
+// 0x18 : Data signal of a_V
+//        bit 31~0 - a_V[31:0] (Read/Write)
+// 0x1c : reserved
+// 0x20 : Data signal of b_V
+//        bit 31~0 - b_V[31:0] (Read/Write)
+// 0x24 : reserved
+// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)
+
+//------------------------Parameter----------------------
+localparam
+    ADDR_AP_CTRL     = 6'h00,
+    ADDR_GIE         = 6'h04,
+    ADDR_IER         = 6'h08,
+    ADDR_ISR         = 6'h0c,
+    ADDR_AP_RETURN_0 = 6'h10,
+    ADDR_A_V_DATA_0  = 6'h18,
+    ADDR_A_V_CTRL    = 6'h1c,
+    ADDR_B_V_DATA_0  = 6'h20,
+    ADDR_B_V_CTRL    = 6'h24,
+    WRIDLE           = 2'd0,
+    WRDATA           = 2'd1,
+    WRRESP           = 2'd2,
+    WRRESET          = 2'd3,
+    RDIDLE           = 2'd0,
+    RDDATA           = 2'd1,
+    RDRESET          = 2'd2,
+    ADDR_BITS         = 6;
+
+//------------------------Local signal-------------------
+    reg  [1:0]                    wstate = WRRESET;
+    reg  [1:0]                    wnext;
+    reg  [ADDR_BITS-1:0]          waddr;
+    wire [31:0]                   wmask;
+    wire                          aw_hs;
+    wire                          w_hs;
+    reg  [1:0]                    rstate = RDRESET;
+    reg  [1:0]                    rnext;
+    reg  [31:0]                   rdata;
+    wire                          ar_hs;
+    wire [ADDR_BITS-1:0]          raddr;
+    // internal registers
+    reg                           int_ap_idle;
+    reg                           int_ap_ready;
+    reg                           int_ap_done = 1'b0;
+    reg                           int_ap_start = 1'b0;
+    reg                           int_auto_restart = 1'b0;
+    reg                           int_gie = 1'b0;
+    reg                           int_ier = 1'b0;
+    reg                           int_isr = 1'b0;
+    reg  [31:0]                   int_ap_return;
+    reg  [31:0]                   int_a_V = 'b0;
+    reg  [31:0]                   int_b_V = 'b0;
+
+//------------------------Instantiation------------------
+
+//------------------------AXI write fsm------------------
+assign AWREADY = (wstate == WRIDLE);
+assign WREADY  = (wstate == WRDATA);
+assign BRESP   = 2'b00;  // OKAY
+assign BVALID  = (wstate == WRRESP);
+assign wmask   = { {8{WSTRB[3]}}, {8{WSTRB[2]}}, {8{WSTRB[1]}}, {8{WSTRB[0]}} };
+assign aw_hs   = AWVALID & AWREADY;
+assign w_hs    = WVALID & WREADY;
+
+// wstate
+always @(posedge ACLK) begin
+    if (ARESET)
+        wstate <= WRRESET;
+    else if (ACLK_EN)
+        wstate <= wnext;
+end
+
+// wnext
+always @(*) begin
+    case (wstate)
+        WRIDLE:
+            if (AWVALID)
+                wnext = WRDATA;
+            else
+                wnext = WRIDLE;
+        WRDATA:
+            if (WVALID)
+                wnext = WRRESP;
+            else
+                wnext = WRDATA;
+        WRRESP:
+            if (BREADY)
+                wnext = WRIDLE;
+            else
+                wnext = WRRESP;
+        default:
+            wnext = WRIDLE;
+    endcase
+end
+
+// waddr
+always @(posedge ACLK) begin
+    if (ACLK_EN) begin
+        if (aw_hs)
+            waddr <= AWADDR[ADDR_BITS-1:0];
+    end
+end
+
+//------------------------AXI read fsm-------------------
+assign ARREADY = (rstate == RDIDLE);
+assign RDATA   = rdata;
+assign RRESP   = 2'b00;  // OKAY
+assign RVALID  = (rstate == RDDATA);
+assign ar_hs   = ARVALID & ARREADY;
+assign raddr   = ARADDR[ADDR_BITS-1:0];
+
+// rstate
+always @(posedge ACLK) begin
+    if (ARESET)
+        rstate <= RDRESET;
+    else if (ACLK_EN)
+        rstate <= rnext;
+end
+
+// rnext
+always @(*) begin
+    case (rstate)
+        RDIDLE:
+            if (ARVALID)
+                rnext = RDDATA;
+            else
+                rnext = RDIDLE;
+        RDDATA:
+            if (RREADY & RVALID)
+                rnext = RDIDLE;
+            else
+                rnext = RDDATA;
+        default:
+            rnext = RDIDLE;
+    endcase
+end
+
+// rdata
+always @(posedge ACLK) begin
+    if (ACLK_EN) begin
+        if (ar_hs) begin
+            rdata <= 1'b0;
+            case (raddr)
+                ADDR_AP_CTRL: begin
+                    rdata[0] <= int_ap_start;
+                    rdata[1] <= int_ap_done;
+                    rdata[2] <= int_ap_idle;
+                    rdata[3] <= int_ap_ready;
+                    rdata[7] <= int_auto_restart;
+                end
+                ADDR_GIE: begin
+                    rdata <= int_gie;
+                end
+                ADDR_IER: begin
+                    rdata <= int_ier;
+                end
+                ADDR_ISR: begin
+                    rdata <= int_isr;
+                end
+                ADDR_AP_RETURN_0: begin
+                    rdata <= int_ap_return[31:0];
+                end
+                ADDR_A_V_DATA_0: begin
+                    rdata <= int_a_V[31:0];
+                end
+                ADDR_B_V_DATA_0: begin
+                    rdata <= int_b_V[31:0];
+                end
+            endcase
+        end
+    end
+end
+
+
+//------------------------Register logic-----------------
+assign interrupt = int_gie & (|int_isr);
+assign ap_start  = int_ap_start;
+assign a_V       = int_a_V;
+assign b_V       = int_b_V;
+// int_ap_start
+always @(posedge ACLK) begin
+    if (ARESET)
+        int_ap_start <= 1'b0;
+    else if (ACLK_EN) begin
+        if (w_hs && waddr == ADDR_AP_CTRL && WSTRB[0] && WDATA[0])
+            int_ap_start <= 1'b1;
+        else if (ap_done & int_auto_restart)
+            int_ap_start <= 1'b1; // auto restart
+        else
+            int_ap_start <= 1'b0; // self clear
+    end
+end
+
+// int_ap_done
+always @(posedge ACLK) begin
+    if (ARESET)
+        int_ap_done <= 1'b0;
+    else if (ACLK_EN) begin
+        if (ap_done)
+            int_ap_done <= 1'b1;
+        else if (ar_hs && raddr == ADDR_AP_CTRL)
+            int_ap_done <= 1'b0; // clear on read
+    end
+end
+
+// int_ap_idle
+always @(posedge ACLK) begin
+    if (ARESET)
+        int_ap_idle <= 1'b0;
+    else if (ACLK_EN) begin
+            int_ap_idle <= ap_idle;
+    end
+end
+
+// int_ap_ready
+always @(posedge ACLK) begin
+    if (ARESET)
+        int_ap_ready <= 1'b0;
+    else if (ACLK_EN) begin
+            int_ap_ready <= ap_ready;
+    end
+end
+
+// int_auto_restart
+always @(posedge ACLK) begin
+    if (ARESET)
+        int_auto_restart <= 1'b0;
+    else if (ACLK_EN) begin
+        if (w_hs && waddr == ADDR_AP_CTRL && WSTRB[0])
+            int_auto_restart <=  WDATA[7];
+    end
+end
+
+// int_gie
+always @(posedge ACLK) begin
+    if (ARESET)
+        int_gie <= 1'b0;
+    else if (ACLK_EN) begin
+        if (w_hs && waddr == ADDR_GIE && WSTRB[0])
+            int_gie <= WDATA[0];
+    end
+end
+
+// int_ier
+always @(posedge ACLK) begin
+    if (ARESET)
+        int_ier <= 1'b0;
+    else if (ACLK_EN) begin
+        if (w_hs && waddr == ADDR_IER && WSTRB[0])
+            int_ier <= WDATA[0];
+    end
+end
+
+// int_isr
+always @(posedge ACLK) begin
+    if (ARESET)
+        int_isr <= 1'b0;
+    else if (ACLK_EN) begin
+        if (int_ier & ap_done)
+            int_isr <= 1'b1;
+        else if (w_hs && waddr == ADDR_ISR && WSTRB[0])
+            int_isr <= int_isr ^ WDATA[0]; // toggle on write
+    end
+end
+
+// int_ap_return
+always @(posedge ACLK) begin
+    if (ARESET)
+        int_ap_return <= 0;
+    else if (ACLK_EN) begin
+        if (ap_done)
+            int_ap_return <= ap_return;
+    end
+end
+
+// int_a_V[31:0]
+always @(posedge ACLK) begin
+    if (ARESET)
+        int_a_V[31:0] <= 0;
+    else if (ACLK_EN) begin
+        if (w_hs && waddr == ADDR_A_V_DATA_0)
+            int_a_V[31:0] <= (WDATA[31:0] & wmask) | (int_a_V[31:0] & ~wmask);
+    end
+end
+
+// int_b_V[31:0]
+always @(posedge ACLK) begin
+    if (ARESET)
+        int_b_V[31:0] <= 0;
+    else if (ACLK_EN) begin
+        if (w_hs && waddr == ADDR_B_V_DATA_0)
+            int_b_V[31:0] <= (WDATA[31:0] & wmask) | (int_b_V[31:0] & ~wmask);
+    end
+end
+
+
+//------------------------Memory logic-------------------
+
+endmodule
diff --git a/src/finn/transformation/fpgadataflow/set_fifo_depths.py b/src/finn/transformation/fpgadataflow/set_fifo_depths.py
index 713148d7fcdfea4411554b6d3b817a14b33a53c6..30fa8b089ae6bdfc3249b2a725e1b97c2ba9c1f0 100644
--- a/src/finn/transformation/fpgadataflow/set_fifo_depths.py
+++ b/src/finn/transformation/fpgadataflow/set_fifo_depths.py
@@ -39,11 +39,8 @@ from finn.transformation.fpgadataflow.create_stitched_ip import CreateStitchedIP
 from finn.transformation.fpgadataflow.insert_dwc import InsertDWC
 from finn.transformation.fpgadataflow.insert_fifo import InsertFIFO
 from finn.transformation.general import GiveUniqueNodeNames, GiveReadableTensorNames
-from finn.core.rtlsim_exec import (
-    _reset_rtlsim,
-    _toggle_clk,
-)
 from finn.util.fpgadataflow import pyverilate_stitched_ip, is_fpgadataflow_node
+from finn.util.pyverilator import reset_rtlsim, toggle_clk
 
 
 def reset_implementation(node):
@@ -298,8 +295,8 @@ class InsertAndSetFIFODepths(Transformation):
         # prepare pyverilator model
         sim = pyverilate_stitched_ip(model)
 
-        _reset_rtlsim(sim)
-        _toggle_clk(sim)
+        reset_rtlsim(sim)
+        toggle_clk(sim)
 
         # set all input valids to 0 and output readies to 1
         # set input data to some constant
@@ -309,7 +306,7 @@ class InsertAndSetFIFODepths(Transformation):
 
         output_detected = False
         while ncycles > 0:
-            _toggle_clk(sim)
+            toggle_clk(sim)
             # set/unset valids
             if ncycles % ncycles_per_input == 0:
                 set_signal(sim, "tvalid", 1)
diff --git a/src/finn/util/pyverilator.py b/src/finn/util/pyverilator.py
new file mode 100644
index 0000000000000000000000000000000000000000..41b72db9504ebeb8d10c5d838977da93b27f94ed
--- /dev/null
+++ b/src/finn/util/pyverilator.py
@@ -0,0 +1,124 @@
+# Copyright (c) 2020, Xilinx
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+#   list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+#   this list of conditions and the following disclaimer in the documentation
+#   and/or other materials provided with the distribution.
+#
+# * Neither the name of FINN nor the names of its
+#   contributors may be used to endorse or promote products derived from
+#   this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+def reset_rtlsim(sim, rst_name="ap_rst_n", active_low=True):
+    """Sets reset input in pyverilator to zero, toggles the clock and set it
+    back to one"""
+    sim.io[rst_name] = 0 if active_low else 1
+    toggle_clk(sim)
+    toggle_clk(sim)
+    sim.io[rst_name] = 1 if active_low else 0
+    toggle_clk(sim)
+    toggle_clk(sim)
+
+
+def toggle_clk(sim, clk_name="ap_clk"):
+    """Toggles the clock input in pyverilator once."""
+    sim.io[clk_name] = 0
+    sim.eval()
+    sim.io[clk_name] = 1
+    sim.eval()
+
+
+def wait_for_handshake(sim, ifname, basename="s_axi_control_", dataname="DATA"):
+    """Wait for handshake (READY and VALID high at the same time) on given
+    interface on PyVerilator sim object.
+
+    Arguments:
+    - sim : PyVerilator sim object
+    - ifname : name for decoupled interface to wait for handshake on
+    - basename : prefix for decoupled interface name
+    - dataname : interface data sig name, will be return value if it exists
+
+    Returns: value of interface data signal during handshake (if given by dataname),
+    None otherwise (e.g. if there is no data signal associated with interface)
+    """
+    ret = None
+    while 1:
+        hs = (
+            sim.io[basename + ifname + "READY"] == 1
+            and sim.io[basename + ifname + "VALID"] == 1
+        )
+        if basename + ifname + dataname in sim.io:
+            ret = sim.io[basename + ifname + dataname]
+        toggle_clk(sim)
+        if hs:
+            break
+    return ret
+
+
+def axilite_write(sim, addr, val, basename="s_axi_control_", wstrb=0xF):
+    """Write val to addr on AXI lite interface given by basename.
+
+    Arguments:
+    - sim : PyVerilator sim object
+    - addr : address for write
+    - val : value to be written at addr
+    - basename : prefix for AXI lite interface name
+    - wstrb : write strobe value to do partial writes, see AXI protocol reference
+    """
+    sim.io[basename + "WSTRB"] = wstrb
+    sim.io[basename + "AWADDR"] = addr
+    sim.io[basename + "AWVALID"] = 1
+    wait_for_handshake(sim, "AW", basename=basename)
+    # write request done
+    sim.io[basename + "AWVALID"] = 0
+    # write data
+    sim.io[basename + "WDATA"] = val
+    sim.io[basename + "WVALID"] = 1
+    wait_for_handshake(sim, "W", basename=basename)
+    # write data OK
+    sim.io[basename + "WVALID"] = 0
+    # wait for write response
+    sim.io[basename + "BREADY"] = 1
+    wait_for_handshake(sim, "B", basename=basename)
+    # write response OK
+    sim.io[basename + "BREADY"] = 0
+
+
+def axilite_read(sim, addr, basename="s_axi_control_"):
+    """Read val from addr on AXI lite interface given by basename.
+
+    Arguments:
+    - sim : PyVerilator sim object
+    - addr : address for read
+    - basename : prefix for AXI lite interface name
+
+    Returns: read value from AXI lite interface at given addr
+    """
+    sim.io[basename + "ARADDR"] = addr
+    sim.io[basename + "ARVALID"] = 1
+    wait_for_handshake(sim, "AR", basename=basename)
+    # read request OK
+    sim.io[basename + "ARVALID"] = 0
+    # wait for read response
+    sim.io[basename + "RREADY"] = 1
+    ret_data = wait_for_handshake(sim, "R", basename=basename)
+    sim.io[basename + "RREADY"] = 0
+    return ret_data
diff --git a/tests/util/test_pyverilator.py b/tests/util/test_pyverilator.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d837a924d92ca9a834557c21756d5f490146908
--- /dev/null
+++ b/tests/util/test_pyverilator.py
@@ -0,0 +1,96 @@
+# Copyright (c) 2020, Xilinx
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+#   list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+#   this list of conditions and the following disclaimer in the documentation
+#   and/or other materials provided with the distribution.
+#
+# * Neither the name of FINN nor the names of its
+#   contributors may be used to endorse or promote products derived from
+#   this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import pkg_resources as pk
+from pyverilator import PyVerilator
+from finn.util.pyverilator import axilite_read, axilite_write, reset_rtlsim
+
+
+def test_pyverilator_axilite():
+    example_root = pk.resource_filename("finn.qnn-data", "verilog/myadd")
+    # load example verilog: takes two 32-bit integers as AXI lite mem mapped
+    # registers, adds them together and return result
+    sim = PyVerilator.build(
+        "myadd_myadd.v", verilog_path=[example_root], top_module_name="myadd_myadd",
+    )
+    ifname = "s_axi_control_"
+    expected_signals = [
+        "AWVALID",
+        "AWREADY",
+        "AWADDR",
+        "WVALID",
+        "WREADY",
+        "WDATA",
+        "WSTRB",
+        "ARVALID",
+        "ARREADY",
+        "ARADDR",
+        "RVALID",
+        "RREADY",
+        "RDATA",
+        "RRESP",
+        "BVALID",
+        "BREADY",
+        "BRESP",
+    ]
+    for signal_name in expected_signals:
+        assert ifname + signal_name in sim.io
+    reset_rtlsim(sim)
+    # initial values
+    sim.io[ifname + "WVALID"] = 0
+    sim.io[ifname + "AWVALID"] = 0
+    sim.io[ifname + "ARVALID"] = 0
+    sim.io[ifname + "BREADY"] = 0
+    sim.io[ifname + "RREADY"] = 0
+    # write + verify first parameter in AXI lite memory mapped regs
+    val_a = 3
+    addr_a = 0x18
+    axilite_write(sim, addr_a, val_a)
+    ret_data = axilite_read(sim, addr_a)
+    assert ret_data == val_a
+    # write + verify second parameter in AXI lite memory mapped regs
+    val_b = 5
+    addr_b = 0x20
+    axilite_write(sim, addr_b, val_b)
+    ret_data = axilite_read(sim, addr_b)
+    assert ret_data == val_b
+    # launch accelerator and wait for completion
+    addr_ctrl_status = 0x00
+    # check for ap_idle
+    assert axilite_read(sim, addr_ctrl_status) and (1 << 2) != 0
+    # set ap_start
+    axilite_write(sim, addr_ctrl_status, 1)
+    # wait until ap_done
+    while 1:
+        ap_done = axilite_read(sim, addr_ctrl_status) and (1 << 1)
+        if ap_done != 0:
+            break
+    # read out and verify result
+    addr_return = 0x10
+    val_ret = axilite_read(sim, addr_return)
+    assert val_ret == val_a + val_b