Beispiel #1
0
def pyverilate_stitched_ip(model):
    "Given a model with stitched IP, return a PyVerilator sim object."
    if PyVerilator is None:
        raise ImportError("Installation of PyVerilator is required.")

    vivado_stitch_proj_dir = model.get_metadata_prop("vivado_stitch_proj")
    with open(vivado_stitch_proj_dir + "/all_verilog_srcs.txt", "r") as f:
        all_verilog_srcs = f.read().split()

    def file_to_dir(x):
        return os.path.dirname(os.path.realpath(x))

    def file_to_basename(x):
        return os.path.basename(os.path.realpath(x))

    all_verilog_dirs = list(map(file_to_dir, all_verilog_srcs))
    all_verilog_files = list(
        set(
            filter(
                lambda x: x.endswith(".v"),
                list(map(file_to_basename, all_verilog_srcs)),
            )))
    top_module_name = model.get_metadata_prop("wrapper_filename")
    top_module_name = file_to_basename(top_module_name).strip(".v")
    build_dir = make_build_dir("pyverilator_ipstitched_")
    sim = PyVerilator.build(
        all_verilog_files,
        verilog_path=all_verilog_dirs,
        build_dir=build_dir,
        trace_depth=get_rtlsim_trace_depth(),
        top_module_name=top_module_name,
        auto_eval=False,
    )
    return sim
Beispiel #2
0
    def prepare_rtlsim(self):
        """Creates a Verilator emulation library for the RTL code generated
        for this node, sets the rtlsim_so attribute to its path and returns
        a PyVerilator wrapper around it."""

        if PyVerilator is None:
            raise ImportError("Installation of PyVerilator is required.")
        # ensure that code is generated
        code_gen_dir = self.get_nodeattr("code_gen_dir_ipgen")
        assert (code_gen_dir != ""), """Node attribute "code_gen_dir_ipgen" is
        not set. Please run HLSSynthIP first."""
        verilog_file = self.get_verilog_top_filename()
        assert os.path.isfile(
            verilog_file), "Cannot find top-level Verilog file."
        # build the Verilator emu library
        sim = PyVerilator.build(
            verilog_file,
            build_dir=make_build_dir("pyverilator_" + self.onnx_node.name +
                                     "_"),
            verilog_path=[
                "{}/project_{}/sol1/impl/verilog/".format(
                    code_gen_dir, self.onnx_node.name)
            ],
            trace_depth=get_rtlsim_trace_depth(),
        )
        # save generated lib filename in attribute
        self.set_nodeattr("rtlsim_so", sim.lib._name)
        return sim
Beispiel #3
0
 def verilog_inference(self, verilog_dir, top_module_filename, logfile: bool = False, add_registers: bool = False):
     self.verilog_dir = realpath(verilog_dir)
     self.top_module_filename = top_module_filename
     self.dut = PyVerilator.build(f"{self.verilog_dir}/{self.top_module_filename}", verilog_path=[self.verilog_dir], build_dir=f"{self.verilog_dir}/verilator")
     self.is_verilog_inference = True
     self.logfile = logfile
     if add_registers:
         self.latency = len(self.num_neurons)
Beispiel #4
0
 def verilog_inference(self, verilog_dir, top_module_filename):
     self.verilog_dir = realpath(verilog_dir)
     self.top_module_filename = top_module_filename
     self.dut = PyVerilator.build(
         f"{self.verilog_dir}/{self.top_module_filename}",
         verilog_path=[self.verilog_dir],
         build_dir=f"{self.verilog_dir}/verilator")
     self.is_verilog_inference = True
Beispiel #5
0
    def get_rtlsim(self):
        """Return a PyVerilator wrapper for the Verilator emulation library
        for this node."""

        rtlsim_so = self.get_nodeattr("rtlsim_so")
        assert os.path.isfile(rtlsim_so), "Cannot find rtlsim library."
        # create PyVerilator wrapper
        sim = PyVerilator(rtlsim_so)
        return sim
Beispiel #6
0
def init_simulator(top, args):
    try:
        sim = PyVerilator.build(top, build_dir='obj_dir.py',
                                quiet=True, command_args=args,
                                verilog_defines=["BRAM_RUNTIME_INIT", "SIMULATION"])
        sim.set_vl_finish_callback(lambda *args: vl_finish_callback(sim, *args))
        return sim
    except subprocess.CalledProcessError as e:
        stderr = (e.stderr or b"").decode("utf-8")
        stdout = (e.stdout or b"").decode("utf-8")
        print("Compilation failed:\nstderr:\n{}\nstdout:\n{}".format(stderr, stdout))
        sys.exit(1)
Beispiel #7
0
def rtlsim_exec(model, execution_context):
    """Use PyVerilator to execute given model with stitched IP. The execution
    context contains the input values."""

    if PyVerilator is None:
        raise ImportError("Installation of PyVerilator is required.")
    # ensure stitched ip project already exists
    assert os.path.isfile(model.get_metadata_prop("wrapper_filename")), """The
    file name from metadata property "wrapper_filename" doesn't exist."""
    assert os.path.isdir(model.get_metadata_prop("vivado_stitch_proj")), """The
    directory from metadata property "vivado_stitch_proj" doesn't exist"""
    trace_file = model.get_metadata_prop("rtlsim_trace")
    # extract input shape
    # TODO extend for multiple inputs
    i_name = model.graph.input[0].name
    i_tensor = execution_context[i_name]
    i_dt = model.get_tensor_datatype(i_name)
    first_node = getCustomOp(model.find_consumer(i_name))
    i_stream_w = first_node.get_instream_width()
    # convert input into time multiplexed shape
    i_folded_shape = first_node.get_folded_input_shape()
    # TODO any other layout transformations need to happen here!
    i_tensor = i_tensor.reshape(i_folded_shape)
    # extract output shape
    o_name = model.graph.output[0].name
    o_shape = model.get_tensor_shape(o_name)
    o_dt = model.get_tensor_datatype(o_name)
    last_node = getCustomOp(model.find_producer(o_name))
    o_folded_shape = last_node.get_folded_output_shape()
    o_stream_w = last_node.get_outstream_width()
    packedBits = o_stream_w
    targetBits = o_dt.bitwidth()
    # pack input
    packed_input = npy_to_rtlsim_input(i_tensor, i_dt, i_stream_w)
    num_out_values = last_node.get_number_output_values()
    # prepare pyverilator model
    rtlsim_so = model.get_metadata_prop("rtlsim_so")
    if (rtlsim_so is None) or (not os.path.isfile(rtlsim_so)):
        sim = pyverilate_stitched_ip(model)
        model.set_metadata_prop("rtlsim_so", sim.lib._name)
    else:
        sim = PyVerilator(rtlsim_so)
    _reset_rtlsim(sim)
    _toggle_clk(sim)
    ret = _run_rtlsim(sim, packed_input, num_out_values, trace_file)
    packed_output = ret[0]
    model.set_metadata_prop("sim_cycles", str(ret[1]))
    # unpack output and put into context
    o_folded_tensor = rtlsim_output_to_npy(packed_output, None, o_dt,
                                           o_folded_shape, packedBits,
                                           targetBits)
    execution_context[o_name] = o_folded_tensor.reshape(o_shape)
Beispiel #8
0
def pyverilate_stitched_ip(model):
    "Given a model with stitched IP, return a PyVerilator sim object."
    vivado_stitch_proj_dir = model.get_metadata_prop("vivado_stitch_proj")
    with open(vivado_stitch_proj_dir + "/all_verilog_srcs.txt", "r") as f:
        all_verilog_srcs = f.read().split()

    def file_to_dir(x):
        return os.path.dirname(os.path.realpath(x))

    all_verilog_dirs = list(map(file_to_dir, all_verilog_srcs))
    top_verilog = model.get_metadata_prop("wrapper_filename")
    sim = PyVerilator.build(top_verilog, verilog_path=all_verilog_dirs)
    return sim
Beispiel #9
0
    def prepare_rtlsim(self):
        """Creates a Verilator emulation library for the RTL code generated
        for this node, sets the rtlsim_so attribute to its path and returns
        a PyVerilator wrapper around it."""

        if PyVerilator is None:
            raise ImportError("Installation of PyVerilator is required.")
        verilog_paths = self.get_all_verilog_paths()
        verilog_files = self.get_all_verilog_filenames()
        # build the Verilator emu library
        sim = PyVerilator.build(
            verilog_files,
            build_dir=make_build_dir("pyverilator_" + self.onnx_node.name + "_"),
            verilog_path=verilog_paths,
            trace_depth=get_rtlsim_trace_depth(),
            top_module_name=self.get_verilog_top_module_name(),
        )
        # save generated lib filename in attribute
        self.set_nodeattr("rtlsim_so", sim.lib._name)
        return sim
Beispiel #10
0
def pyverilate_stitched_ip(model):
    "Given a model with stitched IP, return a PyVerilator sim object."
    if PyVerilator is None:
        raise ImportError("Installation of PyVerilator is required.")

    vivado_stitch_proj_dir = model.get_metadata_prop("vivado_stitch_proj")
    with open(vivado_stitch_proj_dir + "/all_verilog_srcs.txt", "r") as f:
        all_verilog_srcs = f.read().split()

    def file_to_dir(x):
        return os.path.dirname(os.path.realpath(x))

    all_verilog_dirs = list(map(file_to_dir, all_verilog_srcs))
    top_verilog = model.get_metadata_prop("wrapper_filename")
    build_dir = make_build_dir("pyverilator_ipstitched_")
    sim = PyVerilator.build(
        top_verilog,
        verilog_path=all_verilog_dirs,
        build_dir=build_dir,
        trace_depth=get_rtlsim_trace_depth(),
    )
    return sim
    def execute_node(self, context, graph):
        mode = self.get_nodeattr("exec_mode")
        node = self.onnx_node
        mw = self.get_nodeattr("MW")
        mh = self.get_nodeattr("MH")
        simd = self.get_nodeattr("SIMD")
        pe = self.get_nodeattr("PE")
        sf = mw // simd
        nf = mh // pe

        # TODO ensure codegen dir exists
        if mode == "npysim":
            code_gen_dir = self.get_nodeattr("code_gen_dir_npysim")
        elif mode == "rtlsim":
            code_gen_dir = self.get_nodeattr("code_gen_dir_ipgen")
        else:
            raise Exception(
                """Invalid value for attribute exec_mode! Is currently set to: {}
            has to be set to one of the following value ("npysim", "rtlsim")"""
                .format(mode))

        # create a npy file fore each input of the node (in_ind is input index)
        in_ind = 0
        for inputs in node.input:
            # it is assumed that the first input of the node is the data input
            # the second input are the weights
            # the third input are the thresholds
            if in_ind == 0:
                assert (str(
                    context[inputs].dtype) == "float32"), """Input datatype is
                not float32 as expected."""
                expected_inp_shape = (1, sf, simd)
                reshaped_input = context[inputs].reshape(expected_inp_shape)
                if self.get_input_datatype() == DataType.BIPOLAR:
                    # store bipolar activations as binary
                    reshaped_input = (reshaped_input + 1) / 2
                    export_idt = DataType.BINARY
                else:
                    export_idt = self.get_input_datatype()
                np.save(
                    os.path.join(code_gen_dir, "input_{}.npy".format(in_ind)),
                    reshaped_input,
                )
            elif in_ind > 2:
                raise Exception("Unexpected input found for StreamingFCLayer")
            in_ind += 1

        if mode == "npysim":
            # execute the precompiled model
            super().exec_precompiled_singlenode_model()
            # load output npy file
            super().npy_to_dynamic_output(context)
            # reinterpret binary output as bipolar where needed
            if self.get_output_datatype() == DataType.BIPOLAR:
                out = context[node.output[0]]
                out = 2 * out - 1
                context[node.output[0]] = out
            assert context[node.output[0]].shape == (
                1,
                nf,
                pe,
            ), """Output shape is not
            as expected (1, nf, pe)"""
            # reshape output to have expected shape
            context[node.output[0]] = context[node.output[0]].reshape(1, mh)
        elif mode == "rtlsim":
            if PyVerilator is None:
                raise ImportError("Installation of PyVerilator is required.")

            prefixed_top_name = "%s_%s" % (node.name, node.name)
            # check if needed file exists
            verilog_file = "{}/project_{}/sol1/impl/verilog/{}.v".format(
                code_gen_dir, node.name, prefixed_top_name)
            if os.path.isfile(verilog_file):
                nbits = self.get_instream_width()
                inp = npy_to_rtlsim_input(
                    "{}/input_0.npy".format(code_gen_dir), export_idt, nbits)
                sim = PyVerilator.build(
                    verilog_file,
                    verilog_path=[
                        "{}/project_{}/sol1/impl/verilog/".format(
                            code_gen_dir, node.name)
                    ],
                )
                super().reset_rtlsim(sim)
                super().toggle_clk(sim)
                output = self.rtlsim(sim, inp)
                odt = self.get_output_datatype()
                target_bits = odt.bitwidth()
                packed_bits = self.get_outstream_width()
                out_npy_path = "{}/output.npy".format(code_gen_dir)
                rtlsim_output_to_npy(output, out_npy_path, odt, (1, nf, pe),
                                     packed_bits, target_bits)

                # load and reshape output
                output = np.load(out_npy_path)
                output = np.asarray([output], dtype=np.float32).reshape(1, mh)
                context[node.output[0]] = output

            else:
                raise Exception("""Found no verilog files for this node,
                    did you run the codegen_ipgen transformation?""")

        else:
            raise Exception(
                """Invalid value for attribute exec_mode! Is currently set to: {}
            has to be set to one of the following value ("npysim", "rtlsim")"""
                .format(mode))
def rtlsim_exec(model, execution_context, pre_hook=None, post_hook=None):
    """Use PyVerilator to execute given model with stitched IP. The execution
    context contains the input values. Hook functions can be optionally
    specified to observe/alter the state of the circuit, receiving the
    PyVerilator sim object as their first argument:
    - pre_hook : hook function to be called before sim start (after reset)
    - post_hook : hook function to be called after sim end
    """

    if PyVerilator is None:
        raise ImportError("Installation of PyVerilator is required.")
    # ensure stitched ip project already exists
    assert os.path.isfile(model.get_metadata_prop("wrapper_filename")), """The
    file name from metadata property "wrapper_filename" doesn't exist."""
    assert os.path.isdir(model.get_metadata_prop("vivado_stitch_proj")), """The
    directory from metadata property "vivado_stitch_proj" doesn't exist"""
    trace_file = model.get_metadata_prop("rtlsim_trace")
    # extract input shape
    # TODO extend for multiple inputs
    i_name = model.graph.input[0].name
    i_tensor = execution_context[i_name]
    i_dt = model.get_tensor_datatype(i_name)
    first_node = getCustomOp(model.find_consumer(i_name))
    i_stream_w = first_node.get_instream_width()
    # convert input into time multiplexed shape
    i_folded_shape = first_node.get_folded_input_shape()
    batchsize = i_tensor.shape[0]
    # override batch size for input
    i_folded_shape = list(i_folded_shape)
    i_folded_shape[0] = batchsize
    i_folded_shape = tuple(i_folded_shape)
    # TODO any other layout transformations need to happen here!
    i_tensor = i_tensor.reshape(i_folded_shape)
    # extract output shape
    o_name = model.graph.output[0].name
    o_shape = model.get_tensor_shape(o_name)
    o_dt = model.get_tensor_datatype(o_name)
    last_node = getCustomOp(model.find_producer(o_name))
    o_folded_shape = last_node.get_folded_output_shape()
    # override batch size from actual input
    o_shape = list(o_shape)
    o_shape[0] = batchsize
    o_shape = tuple(o_shape)
    o_folded_shape = list(o_folded_shape)
    o_folded_shape[0] = batchsize
    o_folded_shape = tuple(o_folded_shape)
    o_stream_w = last_node.get_outstream_width()
    packedBits = o_stream_w
    targetBits = o_dt.bitwidth()
    # pack input
    packed_input = npy_to_rtlsim_input(i_tensor, i_dt, i_stream_w)
    num_out_values = last_node.get_number_output_values()
    num_out_values *= batchsize
    # prepare pyverilator model
    rtlsim_so = model.get_metadata_prop("rtlsim_so")
    if (rtlsim_so is None) or (not os.path.isfile(rtlsim_so)):
        sim = pyverilate_stitched_ip(model)
        model.set_metadata_prop("rtlsim_so", sim.lib._name)
    else:
        sim = PyVerilator(rtlsim_so, auto_eval=False)
    ret = _run_rtlsim(
        sim,
        packed_input,
        num_out_values,
        trace_file,
        pre_hook=pre_hook,
        post_hook=post_hook,
    )
    packed_output = ret[0]
    model.set_metadata_prop("cycles_rtlsim", str(ret[1]))
    # unpack output and put into context
    o_folded_tensor = rtlsim_output_to_npy(packed_output, None, o_dt,
                                           o_folded_shape, packedBits,
                                           targetBits)
    execution_context[o_name] = o_folded_tensor.reshape(o_shape)
def test_pyverilator_axilite():
    example_root = pk.resource_filename("finn.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
Beispiel #14
0
def pyverilate_stitched_ip(
    model,
    read_internal_signals=True,
    disable_common_warnings=True,
    extra_verilator_args=[],
):
    """Given a model with stitched IP, return a PyVerilator sim object.
    Trace depth is also controllable, see get_rtlsim_trace_depth()

    :param read_internal_signals  If set, it will be possible to examine the
        internal (not only port) signals of the Verilog module, but this may
        slow down compilation and emulation.

    :param disable_common_warnings If set, disable the set of warnings that
        Vivado-HLS-generated Verilog typically triggers in Verilator
        (which can be very verbose otherwise)

    """
    if PyVerilator is None:
        raise ImportError("Installation of PyVerilator is required.")

    vivado_stitch_proj_dir = model.get_metadata_prop("vivado_stitch_proj")
    with open(vivado_stitch_proj_dir + "/all_verilog_srcs.txt", "r") as f:
        all_verilog_srcs = f.read().split()

    def file_to_dir(x):
        return os.path.dirname(os.path.realpath(x))

    def file_to_basename(x):
        return os.path.basename(os.path.realpath(x))

    top_module_file_name = file_to_basename(
        model.get_metadata_prop("wrapper_filename"))
    top_module_name = top_module_file_name.strip(".v")
    build_dir = make_build_dir("pyverilator_ipstitched_")

    # dump all Verilog code to a single file
    # this is because large models with many files require
    # a verilator command line too long for bash on most systems
    # NOTE: there are duplicates in this list, and some files
    # are identical but in multiple directories (regslice_core.v)

    # remove duplicates from list by doing list -> set -> list
    all_verilog_files = list(
        set(filter(lambda x: x.endswith(".v"), all_verilog_srcs)))

    # remove all but one instances of regslice_core.v
    filtered_verilog_files = []
    remove_entry = False
    for vfile in all_verilog_files:
        if "regslice_core" in vfile:
            if not remove_entry:
                filtered_verilog_files.append(vfile)
            remove_entry = True
        else:
            filtered_verilog_files.append(vfile)

    # concatenate all verilog code into a single file
    with open(vivado_stitch_proj_dir + "/" + top_module_file_name, "w") as wf:
        for vfile in filtered_verilog_files:
            with open(vfile) as rf:
                wf.write("//Added from " + vfile + "\n\n")
                wf.write(rf.read())

    verilator_args = []
    # disable common verilator warnings that should be harmless but commonly occur
    # in large quantities for Vivado HLS-generated verilog code
    if disable_common_warnings:
        verilator_args += ["-Wno-STMTDLY"]
        verilator_args += ["-Wno-PINMISSING"]
        verilator_args += ["-Wno-IMPLICIT"]
        verilator_args += ["-Wno-WIDTH"]
        verilator_args += ["-Wno-COMBDLY"]
    # force inlining of all submodules to ensure we can read internal signals properly
    if read_internal_signals:
        verilator_args += ["--inline-mult", "0"]

    sim = PyVerilator.build(
        top_module_file_name,
        verilog_path=[vivado_stitch_proj_dir],
        build_dir=build_dir,
        trace_depth=get_rtlsim_trace_depth(),
        top_module_name=top_module_name,
        auto_eval=False,
        read_internal_signals=read_internal_signals,
        extra_args=verilator_args + extra_verilator_args,
    )
    return sim
Beispiel #15
0
def pyverilate_stitched_ip(model, read_internal_signals=True):
    """Given a model with stitched IP, return a PyVerilator sim object.
    If read_internal_signals is True, it will be possible to examine the
    internal (not only port) signals of the Verilog module, but this may
    slow down compilation and emulation.
    Trace depth is also controllable, see get_rtlsim_trace_depth()
    """
    if PyVerilator is None:
        raise ImportError("Installation of PyVerilator is required.")

    vivado_stitch_proj_dir = model.get_metadata_prop("vivado_stitch_proj")
    with open(vivado_stitch_proj_dir + "/all_verilog_srcs.txt", "r") as f:
        all_verilog_srcs = f.read().split()

    def file_to_dir(x):
        return os.path.dirname(os.path.realpath(x))

    def file_to_basename(x):
        return os.path.basename(os.path.realpath(x))

    top_module_file_name = file_to_basename(
        model.get_metadata_prop("wrapper_filename"))
    top_module_name = top_module_file_name.strip(".v")
    build_dir = make_build_dir("pyverilator_ipstitched_")

    # dump all Verilog code to a single file
    # this is because large models with many files require
    # a verilator command line too long for bash on most systems
    # NOTE: there are duplicates in this list, and some files
    # are identical but in multiple directories (regslice_core.v)

    # remove duplicates from list by doing list -> set -> list
    all_verilog_files = list(
        set(filter(lambda x: x.endswith(".v"), all_verilog_srcs)))

    # remove all but one instances of regslice_core.v
    filtered_verilog_files = []
    remove_entry = False
    for vfile in all_verilog_files:
        if "regslice_core" in vfile:
            if not remove_entry:
                filtered_verilog_files.append(vfile)
            remove_entry = True
        else:
            filtered_verilog_files.append(vfile)

    # concatenate all verilog code into a single file
    with open(vivado_stitch_proj_dir + "/" + top_module_file_name, "w") as wf:
        for vfile in filtered_verilog_files:
            with open(vfile) as rf:
                wf.write("//Added from " + vfile + "\n\n")
                wf.write(rf.read())

    sim = PyVerilator.build(
        top_module_file_name,
        verilog_path=[vivado_stitch_proj_dir],
        build_dir=build_dir,
        trace_depth=get_rtlsim_trace_depth(),
        top_module_name=top_module_name,
        auto_eval=False,
        read_internal_signals=read_internal_signals,
    )
    return sim
Beispiel #16
0
    def execute_node(self, context, graph):
        mode = self.get_nodeattr("exec_mode")
        node = self.onnx_node
        k = self.get_nodeattr("ConvKernelDim")
        ifm_dim = self.get_nodeattr("IFMDim")
        ifm_ch = self.get_nodeattr("IFMChannels")
        ofm_dim = self.get_nodeattr("OFMDim")
        out_pix = ofm_dim * ofm_dim

        if mode == "npysim":
            idt = self.get_input_datatype()
            if idt == DataType.BIPOLAR:
                # use binary for bipolar storage
                idt = DataType.BINARY

            # TODO ensure codegen dir exists
            code_gen_dir = self.get_nodeattr("code_gen_dir_npysim")
            # create a npy file for input of the node

            inp = context[node.input[0]]
            assert str(inp.dtype) == "float32", "Input datatype is not float32"
            assert inp.shape == (
                1,
                ifm_ch,
                ifm_dim,
                ifm_dim,
            ), """Input shape doesn't
            match expected shape (1, ifm_ch, ifm_dim, ifm_dim)."""
            reshaped_inp = inp.transpose(0, 2, 3, 1)
            np.save(os.path.join(code_gen_dir, "input_0.npy"), reshaped_inp)
            # execute the precompiled model
            super().exec_precompiled_singlenode_model()
            # load output npy file
            super().npy_to_dynamic_output(context)
            if self.get_output_datatype() == DataType.BIPOLAR:
                out = context[node.output[0]]
                out = 2 * out - 1
                context[node.output[0]] = out
            assert context[node.output[0]].shape == (
                1,
                out_pix,
                k * k,
                ifm_ch,
            ), """Output
            shape doesn't match expected shape (1, out_pix, k*k, ifm_ch)."""
            # reshape output to have expected shape
            context[node.output[0]] = context[node.output[0]].reshape(
                1, out_pix, k * k * ifm_ch
            )
        elif mode == "rtlsim":
            code_gen_dir = self.get_nodeattr("code_gen_dir_ipgen")
            prefixed_top_name = "%s_%s" % (node.name, node.name)
            # check if needed file exists
            verilog_file = "{}/project_{}/sol1/impl/verilog/{}.v".format(
                code_gen_dir, node.name, prefixed_top_name
            )
            if os.path.isfile(verilog_file):
                inp = context[node.input[0]]
                inp = inp.transpose(0, 2, 3, 1)
                inp = inp.flatten()

                # TODO: check how to sort inputs for multichannel inputs
                # a = []
                # for i in range(len(inp)):
                #     if (i+1) % 2 == 0:
                #         a.append((int(inp[i-1]) << 1) + int(inp[i]))
                # inp = a
                sim = PyVerilator.build(
                    verilog_file,
                    verilog_path=[
                        "{}/project_{}/sol1/impl/verilog/".format(
                            code_gen_dir, node.name
                        )
                    ],
                )
                super().reset_rtlsim(sim)
                super().toggle_clk(sim)
                output = self.rtlsim(sim, inp)
                output = [int(x) for x in output]
                odt = self.get_output_datatype()
                if odt == DataType.BIPOLAR:
                    output = [2 * x - 1 for x in output]

                # pyverilator interprets int2 as uint2, so output has to be corrected
                elif odt == DataType.INT2:
                    mask = 2 ** (odt.bitwidth() - 1)
                    output = [-(x & mask) + (x & ~mask) for x in output]
                # TODO: check how to sort inputs for multichannel inputs
                # output = [bin(x)[2:].zfill(ifm_ch) for x in output]
                # output_ch1 = [int(x[:1]) for x in output]
                # output_ch2 = [int(x[1:]) for x in output]

                # reshape output
                output = np.asarray([output], dtype=np.float32).reshape(
                    1, out_pix, k * k * ifm_ch
                )
                context[node.output[0]] = output

            else:
                raise Exception(
                    """Found no verilog files for this node,
                    did you run the codegen_ipgen transformation?"""
                )
        else:
            raise Exception(
                """Invalid value for attribute exec_mode! Is currently set to: {}
            has to be set to one of the following value ("npysim", "rtlsim")""".format(
                    mode
                )
            )
Beispiel #17
0
def rtlsim_exec(model, execution_context, pre_hook=None, post_hook=None):
    """Use PyVerilator to execute given model with stitched IP. The execution
    context contains the input values. Hook functions can be optionally
    specified to observe/alter the state of the circuit, receiving the
    PyVerilator sim object as their first argument:
    - pre_hook : hook function to be called before sim start (after reset)
    - post_hook : hook function to be called after sim end
    """
    if PyVerilator is None:
        raise ImportError("Installation of PyVerilator is required.")
    # ensure stitched ip project already exists
    assert os.path.isfile(
        model.get_metadata_prop("wrapper_filename")
    ), """The
    file name from metadata property "wrapper_filename" doesn't exist."""
    assert os.path.isdir(
        model.get_metadata_prop("vivado_stitch_proj")
    ), """The
    directory from metadata property "vivado_stitch_proj" doesn't exist"""
    trace_file = model.get_metadata_prop("rtlsim_trace")
    if trace_file is None:
        trace_file = ""
    extra_verilator_args = model.get_metadata_prop("extra_verilator_args")
    if extra_verilator_args is None:
        extra_verilator_args = []
    else:
        extra_verilator_args = eval(extra_verilator_args)

    # extract i/o info to prepare io_dict
    io_dict = {"inputs": {}, "outputs": {}}
    if_dict = eval(model.get_metadata_prop("vivado_stitch_ifnames"))
    # go over and prepare inputs
    for i, i_vi in enumerate(model.graph.input):
        i_name = i_vi.name
        i_tensor = execution_context[i_name]
        i_dt = model.get_tensor_datatype(i_name)
        first_node_onnx = model.find_consumer(i_name)
        first_node = getCustomOp(first_node_onnx)
        node_inp_ind = list(first_node_onnx.input).index(i_name)
        if node_inp_ind == 0:
            # default node input (input 0)
            i_stream_w = first_node.get_instream_width()
            i_folded_shape = first_node.get_folded_input_shape()
        else:
            # not input 0; node must support specifying inp index
            # for these functions
            i_stream_w = first_node.get_instream_width(node_inp_ind)
            i_folded_shape = first_node.get_folded_input_shape(node_inp_ind)
        batchsize = i_tensor.shape[0]
        # override batch size for input
        i_folded_shape = list(i_folded_shape)
        i_folded_shape[0] = batchsize
        i_folded_shape = tuple(i_folded_shape)
        # TODO any other layout transformations need to happen here!
        i_tensor = i_tensor.reshape(i_folded_shape)
        # pack input for rtlsim
        packed_input = npy_to_rtlsim_input(i_tensor, i_dt, i_stream_w)
        # add to io_dict
        if_name = if_dict["s_axis"][i][0]
        io_dict["inputs"][if_name] = packed_input
    # go over outputs to determine how many values will be produced
    num_out_values = 0
    o_tensor_info = []
    for o, o_vi in enumerate(model.graph.output):
        # output in io_dict just needs an empty list
        if_name = if_dict["m_axis"][o][0]
        io_dict["outputs"][if_name] = []
        # extract output shape
        o_name = o_vi.name
        o_shape = model.get_tensor_shape(o_name)
        o_dt = model.get_tensor_datatype(o_name)
        last_node = getCustomOp(model.find_producer(o_name))
        o_folded_shape = last_node.get_folded_output_shape()
        # override batch size from actual input
        o_shape = list(o_shape)
        o_shape[0] = batchsize
        o_shape = tuple(o_shape)
        o_folded_shape = list(o_folded_shape)
        o_folded_shape[0] = batchsize
        o_folded_shape = tuple(o_folded_shape)
        o_stream_w = last_node.get_outstream_width()
        o_tensor_info.append((o_stream_w, o_dt, o_folded_shape, o_shape))
        num_out_values += batchsize * last_node.get_number_output_values()

    # prepare pyverilator model
    rtlsim_so = model.get_metadata_prop("rtlsim_so")
    if (rtlsim_so is None) or (not os.path.isfile(rtlsim_so)):
        sim = pyverilate_stitched_ip(model, extra_verilator_args=extra_verilator_args)
        model.set_metadata_prop("rtlsim_so", sim.lib._name)
    else:
        sim = PyVerilator(rtlsim_so, auto_eval=False)

    # reset and call rtlsim, including any pre/post hooks
    reset_rtlsim(sim)
    if pre_hook is not None:
        pre_hook(sim)
    n_cycles = rtlsim_multi_io(sim, io_dict, num_out_values, trace_file, sname="_")
    if post_hook is not None:
        post_hook(sim)

    # unpack outputs and put back into execution context
    for o, o_vi in enumerate(model.graph.output):
        o_name = o_vi.name
        if_name = if_dict["m_axis"][o][0]
        o_stream_w, o_dt, o_folded_shape, o_shape = o_tensor_info[o]
        packed_output = io_dict["outputs"][if_name]
        o_folded_tensor = rtlsim_output_to_npy(
            packed_output, None, o_dt, o_folded_shape, o_stream_w, o_dt.bitwidth()
        )
        execution_context[o_name] = o_folded_tensor.reshape(o_shape)

    model.set_metadata_prop("cycles_rtlsim", str(n_cycles))