예제 #1
0
    def test_basic(self):
        dut = self.A1('saturate', 'round')
        with RegisterBehaviour.enable():
            with AutoResize.enable():
                dut.main(Sfix(0.1, 2, -27))

                assert dut.a._pyha_next[0].left == 0
                assert dut.a._pyha_next[0].right == -4
                assert dut.a._pyha_next[0].val == 0.125

                assert dut.a._pyha_next[1].left == 0
                assert dut.a._pyha_next[1].right == -4
                assert dut.a._pyha_next[1].val == 0.125
예제 #2
0
파일: core.py 프로젝트: gasparka/pyha
    def __call__(self, *args, **kwargs):
        if PyhaFunc.bypass:
            return self.func(*args, **kwargs)

        self.update_input_types(args, kwargs)
        self.calls += 1

        with SimPath(f'{self.class_name}.{self.function_name}()'):
            with RegisterBehaviour.enable():
                with AutoResize.enable():
                    ret = self.call_with_locals_discovery(*args, **kwargs)

        self.update_output_types(ret)
        return ret
예제 #3
0
def simulate(model,
             *args,
             simulations=None,
             conversion_path=None,
             input_types=None,
             pipeline_flush='self.DELAY',
             trace=False):
    """
    Run simulations on model.

    :param model: Object derived from ``Hardware``. Must have ``main`` function with input/outputs.
    :param *x: Inputs to the 'main' function.
    :param conversion_path: Where the VHDL sources are written, default is temporary directory.
    :param input_types: Force inputs types, default for floats is Sfix[0:-17].
    :param simulations: List of simulations to execute:
    * ``'MODEL'`` passes inputs directly to ``model`` function. If ``model`` does not exist, uses the ``main`` function by turning off register- and fixed point effects.
    * ``'HARDWARE'`` cycle accurate simulator in Python domain, debuggable.
    * ``'RTL'`` converts sources to VHDL and runs RTL simulation by using GHDL simulator.
    * ``'NETLIST'`` runs VHDL sources trough Quartus and uses the generated generated netlist for simulation. Use to gain ~full confidence in your design. It is slow!

    :returns: Dict of output lists for each simulation. Select the output like ``out['MODEL']``.

    Args:
        model: Object derived from *Hardware*. Must have a *main* function.
        *args: Simulation inputs, a list for each argument of the *main* function.
        simulations: List of simulations to execute:
            * 'MODEL'    : executes the *model* function.
            * 'HARDWARE' : executes the *main* function for each input sample i.e. each sample is tied to a clock cycle.
                           Uses a cycle-accurate simulator implemented in Python.
                           TIP: use a debugger to step trough the simulation.
            * 'RTL'      : converts to VHDL and executes with GHDL simulator. Slow!
            * 'NETLIST'  : converts VHDL sources to a 'Quartus netlist' and simulates with GHDL. Painfully slow!

        conversion_path:
        input_types:
        pipeline_flush:
        trace:

    Returns:
        dict:

    """
    from pyha.simulation.tracer import Tracer
    Tracer.traced_objects.clear()
    set_simulator_quartus(None)

    if simulations is None:
        if hasattr(model, 'model'):
            simulations = ['MODEL', 'HARDWARE', 'RTL', 'NETLIST']
        else:
            simulations = ['MODEL_PYHA', 'HARDWARE', 'RTL', 'NETLIST']

    if 'MODEL' in simulations:
        if not hasattr(model, 'model'):
            logger.info('SKIPPING **MODEL** simulations -> no "model()" found')
            simulations.remove('MODEL')

    if 'RTL' in simulations:
        if 'HARDWARE' not in simulations:
            logger.warning(
                'SKIPPING **RTL** simulations -> You need to run "HARDWARE" simulation before "RTL" simulation'
            )
            simulations.remove('RTL')
        elif 'PYHA_SKIP_RTL' in os.environ:
            logger.warning(
                'SKIPPING **RTL** simulations -> "PYHA_SKIP_RTL" environment variable is set'
            )
            simulations.remove('RTL')
        elif Sfix._float_mode.enabled:
            logger.warning(
                'SKIPPING **RTL** simulations -> Sfix._float_mode is active')
            simulations.remove('RTL')

        # TODO: test for docker instead...
        # elif not have_ghdl():
        #     logger.warning('SKIPPING **RTL** simulations -> no GHDL found')

    if 'NETLIST' in simulations:
        if 'HARDWARE' not in simulations:
            logger.warning(
                'SKIPPING **GATE** simulations -> You need to run "HARDWARE" simulation before "NETLIST" simulation'
            )
            simulations.remove('NETLIST')
        elif 'PYHA_SKIP_GATE' in os.environ:
            logger.warning(
                'SKIPPING **GATE** simulations -> "PYHA_SKIP_GATE" environment variable is set'
            )
            simulations.remove('NETLIST')
        elif Sfix._float_mode.enabled:
            logger.warning(
                'SKIPPING **GATE** simulations -> Sfix._float_mode is active')
            simulations.remove('NETLIST')

    if trace:
        simulations = ['MODEL', 'HARDWARE']
        logger.info(
            f'Tracing is enabled, running "MODEL" and "HARDWARE" simulations')
        model._pyha_insert_tracer(label='self')

    out = {}
    if 'MODEL' in simulations:
        logger.info(f'Running "MODEL" simulation...')

        r = model.model(*args)

        try:
            if r.size != 1:
                r = r.squeeze()
        except:
            pass

        # r = np_to_py(r)
        if isinstance(r, tuple):
            r = list(r)

        out['MODEL'] = r
        logger.info(f'OK!')

    if 'MODEL_PYHA' in simulations:
        logger.info(f'Running "MODEL_PYHA" simulation...')
        with RegisterBehaviour.force_disable():
            with Sfix._float_mode:
                tmpmodel = deepcopy(model)
                tmpmodel._pyha_floats_to_fixed(silence=True)

                tmpargs = deepcopy(args)
                tmpargs = convert_input_types(tmpargs,
                                              input_types,
                                              silence=True)
                tmpargs = transpose(tmpargs)

                ret = []
                for input in tmpargs:
                    returns = tmpmodel.main(*input)
                    returns = pyha_to_python(returns)
                    ret.append(returns)
                    tmpmodel._pyha_update_registers()

                ret = process_outputs(0, ret)
        out['MODEL_PYHA'] = ret
        logger.info(f'OK!')

    if 'HARDWARE' in simulations:
        if 'RTL' in simulations or 'NETLIST' in simulations:
            logger.info(
                f'Simulaton needs to support conversion to VHDL -> major slowdown'
            )
            model._pyha_enable_function_profiling_for_types()

        model._pyha_floats_to_fixed()
        if hasattr(model, '_pyha_simulation_input_callback'):
            args = model._pyha_simulation_input_callback(args)
        else:
            args = convert_input_types(args, input_types)
            args = transpose(args)

        delay_compensate = 0
        if pipeline_flush == 'self.DELAY':
            with suppress(AttributeError):
                delay_compensate = model.DELAY

            # duplicate input args to flush pipeline
            target_len = len(args) + delay_compensate
            args += args * int(np.ceil(delay_compensate / len(args)))
            args = args[:target_len]

        logger.info(f'Running "HARDWARE" simulation...')
        ret = []
        valid_samples = 0
        with SimulationRunning.enable():
            with RegisterBehaviour.enable():
                with AutoResize.enable():
                    for input in tqdm(args, file=sys.stderr):
                        returns = model.main(*input)
                        returns = pyha_to_python(returns)
                        if returns is not None:
                            valid_samples += 1
                            ret.append(returns)
                        model._pyha_update_registers()

                    if pipeline_flush == 'auto' and valid_samples != len(
                            out['MODEL']):
                        args = list(args)
                        logger.info(
                            f'Flushing the pipeline to collect {len(out["MODEL"])} valid samples (currently have {valid_samples})'
                        )
                        hardware_delay = 0
                        while valid_samples != len(out["MODEL"]):
                            hardware_delay += 1
                            returns = model.main(*args[-1])
                            returns = pyha_to_python(returns)
                            if returns is not None:
                                valid_samples += 1
                                ret.append(returns)
                            model._pyha_update_registers()
                            args.append(
                                args[-1]
                            )  # collect samples needed to flush the system, so RTL and GATE sims work also!
                        logger.info(f'Flush took {hardware_delay} cycles.')

        out['HARDWARE'] = process_outputs(delay_compensate, ret)
        logger.info(f'OK!')

    if 'RTL' in simulations or 'NETLIST' in simulations:
        converter = Converter(model, output_dir=conversion_path).to_vhdl()
        if 'NETLIST' in simulations:
            make_quartus_project(converter)

    if 'RTL' in simulations:
        logger.info(f'Running "RTL" simulation...')
        ret = run_ghdl_cocotb(*args, converter=converter)
        out['RTL'] = process_outputs(delay_compensate, ret)
        logger.info(f'OK!')

    if 'NETLIST' in simulations:

        logger.info(f'Running "NETLIST" simulation...')
        quartus = QuartusDockerWrapper(converter.base_path)
        set_simulator_quartus(quartus)

        ret = run_ghdl_cocotb(*args,
                              converter=converter,
                              netlist=quartus.get_netlist())
        out['NETLIST'] = process_outputs(delay_compensate, ret)
        logger.info(f'OK!')

    logger.info('Simulations completed!')
    return out