diff --git a/tests/fpgadataflow/test_runtime_weights.py b/tests/fpgadataflow/test_runtime_weights.py
new file mode 100644
index 0000000000000000000000000000000000000000..807483cd17c3ffe435fbd9b1c07fc7d97be1219f
--- /dev/null
+++ b/tests/fpgadataflow/test_runtime_weights.py
@@ -0,0 +1,107 @@
+# 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.
+
+from finn.util.create import hls_random_mlp_maker
+from finn.core.datatype import DataType
+from finn.transformation.general import GiveUniqueNodeNames
+from finn.transformation.fpgadataflow.prepare_ip import PrepareIP
+from finn.transformation.fpgadataflow.hlssynth_ip import HLSSynthIP
+from finn.transformation.fpgadataflow.create_stitched_ip import CreateStitchedIP
+from finn.transformation.fpgadataflow.prepare_rtlsim import PrepareRTLSim
+from finn.custom_op.registry import getCustomOp
+from finn.core.rtlsim_exec import rtlsim_exec
+from finn.util.basic import gen_finn_dt_tensor
+from finn.util.pyverilator import axilite_read, axilite_write
+import numpy as np
+import os
+
+test_fpga_part = "xc7z020clg400-1"
+target_clk_ns = 5
+
+
+def test_runtime_weights_single_layer():
+    idt = DataType.UINT32
+    wdt = DataType.UINT8
+    act = None
+    mw = 4
+    mh = 4
+    pe = 1
+    simd = 1
+    layer_spec = {
+        "idt": idt,
+        "wdt": wdt,
+        "mw": mw,
+        "mh": mh,
+        "act": act,
+        "pe": pe,
+        "simd": simd,
+    }
+    layer_spec_list = [layer_spec]
+    model = hls_random_mlp_maker(layer_spec_list)
+    for fcl in model.get_nodes_by_op_type("StreamingFCLayer_Batch"):
+        op_inst = getCustomOp(fcl)
+        op_inst.set_nodeattr("mem_mode", "decoupled")
+        op_inst.set_nodeattr("runtime_writeable_weights", 1)
+    model.set_initializer("W_0", np.eye(mh).astype(np.float32))
+    model = model.transform(GiveUniqueNodeNames())
+    model = model.transform(PrepareIP(test_fpga_part, target_clk_ns))
+    model = model.transform(HLSSynthIP())
+    model = model.transform(CreateStitchedIP(test_fpga_part, target_clk_ns))
+    model = model.transform(PrepareRTLSim())
+    model.set_metadata_prop("exec_mode", "rtlsim")
+    os.environ["RTLSIM_TRACE_DEPTH"] = "5"
+    model.set_metadata_prop("rtlsim_trace", "trace.vcd")
+    in_tensor = np.asarray(range(mw), dtype=np.float32).reshape(1, mw)
+    exec_ctx = {"act_0": in_tensor}
+    extracted_weights = np.zeros((mh, mw))
+
+    def read_weights(sim):
+        addr = 0
+        for w in range(mw):
+            for h in range(mh):
+                extracted_weights[h, w] = axilite_read(
+                    sim, addr, basename="s_axilite_0_"
+                )
+                addr += 4
+
+    rtlsim_exec(model, exec_ctx, pre_hook=read_weights)
+    assert (extracted_weights == model.get_initializer("W_0")).all()
+    y = exec_ctx["act_1"]
+    assert (y == np.dot(in_tensor, extracted_weights.T)).all()
+    new_weights = gen_finn_dt_tensor(wdt, (mh, mw))
+
+    def write_weights(sim):
+        addr = 0
+        for w in range(mw):
+            for h in range(mh):
+                axilite_write(sim, addr, new_weights[h, w], basename="s_axilite_0_")
+                addr += 4
+
+    rtlsim_exec(model, exec_ctx, pre_hook=write_weights)
+    y = exec_ctx["act_1"]
+    assert (y == np.dot(in_tensor, new_weights.T)).all()