Exemple #1
0
 def __init__(self, host, katcp_port=7147, boffile=None, connect=True, config=None):
     if config:
         if boffile:
             raise ValueError('Cannot specify "boffile" parameter if config is used')
         boffile = config['bitstream']
     self.config = config
     FpgaHost.__init__(
         self, host, katcp_port=katcp_port, boffile=boffile, connect=connect)
     self.boffile = boffile
     self.sine_sources = AttributeContainer()
     self.noise_sources = AttributeContainer()
     self.pulsar_sources = AttributeContainer()
     self.outputs = AttributeContainer()
Exemple #2
0
    def __init__(self, host, katcp_port=7147, bitstream=None,
                 connect=True, config=None, config_file=None, **kwargs):
        """

        :param host:
        :param katcp_port:
        :param bitstream:
        :param connect:
        :param config:
        """
        FpgaHost.__init__(self, host=host, katcp_port=katcp_port, **kwargs)
        if config is not None and config_file is not None:
            LOGGER.warn('config file and config supplied, defaulting to config')
        self.config = config or parse_ini_file(config_file)['dsimengine']
        self.bitstream = bitstream
        self.sine_sources = AttributeContainer()
        self.noise_sources = AttributeContainer()
        self.pulsar_sources = AttributeContainer()
        self.outputs = AttributeContainer()
Exemple #3
0
class FpgaDsimHost(FpgaHost):
    """
    An FpgaHost that acts as a Digitiser unit.
    """

    def __init__(self, host, katcp_port=7147, bitstream=None,
                 connect=True, config=None, config_file=None, **kwargs):
        """

        :param host:
        :param katcp_port:
        :param bitstream:
        :param connect:
        :param config:
        """
        FpgaHost.__init__(self, host=host, katcp_port=katcp_port, **kwargs)
        if config is not None and config_file is not None:
            LOGGER.warn('config file and config supplied, defaulting to config')
        self.config = config or parse_ini_file(config_file)['dsimengine']
        self.bitstream = bitstream
        self.sine_sources = AttributeContainer()
        self.noise_sources = AttributeContainer()
        self.pulsar_sources = AttributeContainer()
        self.outputs = AttributeContainer()

    def get_system_information(self, filename=None, fpg_info=None, **kwargs):
        """
        Get system information and build D-engine sources
        :param filename:
        :param fpg_info:
        :return:
        """
        FpgaHost.get_system_information(
            self, filename=filename, fpg_info=fpg_info, **kwargs)
        self.sine_sources.clear()
        self.noise_sources.clear()
        self.pulsar_sources.clear()
        self.outputs.clear()
        for reg in self.registers:
            sin_name = get_prefixed_name('freq_cwg', reg.name)
            noise_name = get_prefixed_name('scale_wng', reg.name)
            pulsar_name = get_prefixed_name('freq_pulsar', reg.name)
            output_scale_name = get_prefixed_name('scale_out', reg.name)
            if sin_name is not None:
                scale_reg_postfix = ('_' + sin_name if reg.name.endswith(
                    '_' + sin_name) else sin_name)
                scale_reg = getattr(self.registers, 'scale_cwg' +
                                    scale_reg_postfix)
                repeat_en_reg_name = 'rpt_en_cwg' + scale_reg_postfix
                repeat_len_reg_name = 'rpt_length_cwg{}'.format(scale_reg_postfix)
                repeat_en_reg = getattr(self.registers,
                                        repeat_en_reg_name, None)
                repeat_len_reg = getattr(self.registers,
                                         repeat_len_reg_name, None)
                repeat_len_field_name = 'repeat_length'
                setattr(self.sine_sources, 'sin_' + sin_name, SineSource(
                    reg, scale_reg, sin_name,
                    repeat_len_register=repeat_len_reg,
                    repeat_en_register=repeat_en_reg,
                    repeat_len_field_name=repeat_len_field_name))
            elif noise_name is not None:
                setattr(self.noise_sources, 'noise_' + noise_name,
                        NoiseSource(reg, noise_name))
            elif pulsar_name is not None:
                scale_reg_postfix = ('_' + pulsar_name if reg.name.endswith(
                    '_' + pulsar_name) else pulsar_name)
                scale_reg = getattr(self.registers, 'scale_pulsar' + scale_reg_postfix)
                setattr(self.pulsar_sources, 'pulsar_' + pulsar_name,
                        PulsarSource(reg, scale_reg, pulsar_name))
            elif output_scale_name is not None:
                # TODO TEMP hack due to misnamed register
                if output_scale_name.startswith('arb'):
                    continue
                setattr(self.outputs, 'out_' + output_scale_name,
                        Output(output_scale_name, reg, self.registers.control))

    def initialise(self):
        """
        Program (if self.bitstream is specified) and init Dsim host
        :return:
        """
        if not self.is_connected():
            self.connect()
        if self.bitstream:
            self._program()
        else:
            LOGGER.info('Not programming host {} since no bitstream is configured'.format(self.host))
        if not self.is_running():
            raise RuntimeError('D-engine {host} not running'.format(**self.__dict__))
        self.get_system_information(self.bitstream)
        self.setup_gbes()
        # Set digitizer polarisation IDs, 0 - h, 1 - v
        self.registers.receptor_id.write(pol0_id=0, pol1_id=1)
        self.data_resync()
        # Default to generating test-vectors
        for output in self.outputs:
            output.select_output('test_vectors')

    def reset(self):
        self.registers.control.write(mrst='pulse')

    def data_resync(self):
        """
        Start the local timer on the test d-engine - mrst, then a fake sync
        """
        self.reset()
        self.registers.control.write(msync='pulse')

    def enable_data_output(self, enabled=True):
        """
        En/disable 10GbE data output
        """
        enabled = bool(enabled)
        pol_tx_reg = self.registers.pol_tx_always_on
        reg_vals = {n: enabled for n in pol_tx_reg.field_names()
                    if n.endswith('_tx_always_on')}
        pol_tx_reg.write(**reg_vals)
        if 'gbe_control' in self.registers.names():
            self.registers.gbe_control.write_int(15 if enabled else 0)
        elif 'gbecontrol' in self.registers.names():
            self.registers.gbecontrol.write_int(15 if enabled else 0)
        if enabled:
            self.registers.control_output.write(load_en_time='pulse')

    def pulse_data_output(self, no_packets):
        """
        Produce a data output pulse of no_packets per polarisation

        Does nothing if data is already being transmitted
        """
        num_regs = [
            r for r in self.registers if re.match(r'^pol\d_num_pkts$', r.name)
        ]
        for r in num_regs:
            r.write(**{r.name: no_packets})
        reg_field_names = self.registers.pol_traffic_trigger.field_names()
        self.registers.pol_traffic_trigger.write(**{n: 'pulse' for n in reg_field_names})

    def _program(self):
        """
        Program the bitstream to fpga and ensure 10GbE's are not transmitting
        """
        LOGGER.info('Programming Dsim roach {host} with file {bitstream}'.format(**self.__dict__))
        stime = time.time()
        self.upload_to_ram_and_program(self.bitstream)
        LOGGER.info('Programmed {} in {:.2f} seconds.'.format(self.host, time.time() - stime))
        # Ensure data is not sent before the gbes are configured
        self.enable_data_output(False)

    def setup_gbes_skarab(self):
        """
        Set up the 40gbe core on a SKARAB DSIM
        :return:
        """
        port = int(self.config['10gbe_port'])
        gbe = self.gbes[self.gbes.names()[0]]
        if gbe.get_port() != port:
            gbe.set_port(port)
        self.write_int('gbe_porttx', port)
        for pol in [0, 1]:
            pol_start = self.config['pol%1i_destination_start_ip' % pol]
            pol_bits = pol_start.split('.')
            pol_base = int(pol_bits[3])
            pol_prefix = '.'.join(pol_bits[0:3])
            for polstream in [0, 1]:
                txip = pol_base + polstream
                txid = (pol * 2) + polstream
                self.write_int('gbe_iptx%1i' % txid, IpAddress.str2ip('{}.{}'.format(pol_prefix, txip)))
        self.registers.control.write(gbe_rst=False)

    def setup_gbes(self):
        """
        Set up 10GbE MACs, IPs and destination address/port
        """

        if isinstance(self.transport, SkarabTransport):
            return self.setup_gbes_skarab()
        port = int(self.config['10gbe_port'])
        num_gbes = len(self.gbes)
        if num_gbes < 1:
            raise RuntimeError('D-engine with no 10gbe cores {}'.format(self.host))
        gbes_per_pol = 2        # Hardcoded assumption
        num_pols = num_gbes // gbes_per_pol
        mac_ctr = 1
        for ctr in range(num_gbes):
            this_mac = Mac.from_roach_hostname(self.host, mac_ctr)
            self.gbes['gbe{}'.format(ctr)].setup(mac=this_mac, ipaddress='0.0.0.0', port=port)
            mac_ctr += 1
        for gbe in self.gbes:
            gbe.dhcp_start()
        # set the destination IP and port for the tx
        gbe_ctr = 0             # pol-global counter for gbes
        for pol in range(0, num_pols):
            # Get address configuration for current polarisation
            single_destination = int(self.config.get(
                'pol{}_single_destination'.format(pol), 0))
            txaddr_start = self.config['pol{}_destination_start_ip'.format(pol)]
            txaddr_bits = txaddr_start.split('.')
            txaddr_base = int(txaddr_bits[3])
            txaddr_prefix = '.'.join(txaddr_bits[0:3])

            # Set the gbe destination address for each gbe used by this pol
            addr_offset = 0
            for pol_gbe_ctr in range(0, gbes_per_pol):
                txip = txaddr_base + addr_offset
                LOGGER.info('{} sending to: {}.{} port {}'.format(self.host, txaddr_prefix, txip, port))
                self.write_int('gbe_iptx{}'.format(gbe_ctr), IpAddress.str2ip('{}.{}'.format(
                    txaddr_prefix, txip)))
                if not single_destination:
                    addr_offset += 1
                gbe_ctr += 1
        self.write_int('gbe_porttx', port)
        self.registers.control.write(gbe_rst=False)
Exemple #4
0
class FpgaDsimHost(FpgaHost):
    def __init__(self, host, katcp_port=7147, boffile=None, connect=True, config=None):
        if config:
            if boffile:
                raise ValueError('Cannot specify "boffile" parameter if config is used')
            boffile = config['bitstream']
        self.config = config
        FpgaHost.__init__(
            self, host, katcp_port=katcp_port, boffile=boffile, connect=connect)
        self.boffile = boffile
        self.sine_sources = AttributeContainer()
        self.noise_sources = AttributeContainer()
        self.pulsar_sources = AttributeContainer()
        self.outputs = AttributeContainer()

    def get_system_information(self, filename=None, fpg_info=None):
        """Get system information and build D-engine sources"""
        FpgaHost.get_system_information(self, filename=filename, fpg_info=fpg_info)
        self.sine_sources.clear()
        self.noise_sources.clear()
        self.pulsar_sources.clear()
        self.outputs.clear()
        for reg in self.registers:
            sin_name = get_prefixed_name('freq_cwg', reg.name)
            noise_name = get_prefixed_name('scale_wng', reg.name)
            pulsar_name = get_prefixed_name('freq_pulsar', reg.name)
            output_scale_name = get_prefixed_name('scale_out', reg.name)
            if sin_name is not None:
                scale_reg_postfix = (
                    '_'+sin_name if reg.name.endswith('_'+sin_name) else sin_name)
                scale_reg = getattr(self.registers, 'scale_cwg' + scale_reg_postfix)
                repeat_en_reg_name = 'rpt_en_cwg' + scale_reg_postfix
                repeat_len_reg_name = 'rpt_length_cwg' + scale_reg_postfix
                repeat_en_reg = getattr(self.registers, repeat_en_reg_name, None)
                repeat_len_reg = getattr(self.registers, repeat_len_reg_name, None)
                repeat_len_field_name = 'cwg' + scale_reg_postfix + '_repeat_length'
                setattr(self.sine_sources, 'sin_'+ sin_name, SineSource(
                    reg, scale_reg, sin_name,
                    repeat_len_register=repeat_len_reg,
                    repeat_en_register=repeat_en_reg,
                    repeat_len_field_name=repeat_len_field_name))
            elif noise_name is not None:
                setattr(self.noise_sources, 'noise_' + noise_name,
                        NoiseSource(reg, noise_name))
            elif pulsar_name is not None:
                scale_reg_postfix = (
                    '_'+pulsar_name if reg.name.endswith('_'+pulsar_name) else pulsar_name)
                scale_reg = getattr(self.registers, 'scale_pulsar' + scale_reg_postfix)
                setattr(self.pulsar_sources, 'pulsar_'+pulsar_name, PulsarSource(
                    reg, scale_reg, pulsar_name))
            elif output_scale_name is not None:
                # TEMP hack due to misnamed register
                if output_scale_name.startswith('arb'):
                    continue
                setattr(self.outputs, 'out_' + output_scale_name,
                        Output(output_scale_name, reg, self.registers.control))

    def initialise(self):
        """Program (if self.boffile is specified) and init Dsim roach"""
        if not self.is_connected():
            self.connect()
        if self.boffile:
            self._program()
        else:
            LOGGER.info('Not programming host {} since no boffile is configured'
                        .format(self.host))
        if not self.is_running():
            raise RuntimeError('D-engine {host} not running'
                               .format(**self.__dict__))
        self.get_system_information()
        self.setup_tengbes()
        # Set digitizer polarisation IDs, 0 - h, 1 - v
        self.registers.receptor_id.write(pol0_id=0, pol1_id=1)
        self.data_resync()
        # Default to generating test-vectors
        for output in self.outputs:
            output.select_output('test_vectors')

    def reset(self):
        self.registers.control.write(mrst='pulse')

    def data_resync(self):
        """start the local timer on the test d-engine - mrst, then a fake sync"""
        self.reset()
        self.registers.control.write(msync='pulse')

    def enable_data_output(self, enabled=True):
        """(dis)Enable 10GbE data output"""
        enabled = bool(enabled)
        pol_tx_reg = self.registers.pol_tx_always_on
        reg_vals = {n: enabled for n in pol_tx_reg.field_names()
                    if n.endswith('_tx_always_on')}
        pol_tx_reg.write(**reg_vals)
        if enabled:
            self.registers.control_output.write(load_en_time='pulse')

    def pulse_data_output(self, no_packets):
        """Produce a data output pulse of no_packets per polarisation

        Does nothing if data is already being transmitted
        """
        num_regs = [r for r in self.registers if re.match(
            r'^pol\d_num_pkts$', r.name)]
        for r in num_regs:
            r.write(**{r.name: no_packets})
        self.registers.pol_traffic_trigger.write(**{
            n: 'pulse' for n in self.registers.pol_traffic_trigger.field_names()})

    def _program(self):
        """Program the boffile to fpga and ensure 10GbE's are not transmitting"""
        LOGGER.info('Programming Dsim roach {host} with file {boffile}'
                    .format(**self.__dict__))
        stime = time.time()
        self.upload_to_ram_and_program(self.boffile)
        LOGGER.info('Programmed %s in %.2f seconds.' % (
            self.host, time.time() - stime))
        # Ensure data is not sent before the tengbe's are configured
        self.enable_data_output(False)

    def setup_tengbes(self):
        """Set up 10GbE MACs, IPs and destination address/port"""
        port = int(self.config['10gbe_port'])
        num_tengbes = len(self.tengbes)
        if num_tengbes < 1:
            raise RuntimeError('D-engine with no 10gbe cores %s' % self.host)
        gbes_per_pol = 2        # Hardcoded assumption
        num_pols = num_tengbes // gbes_per_pol

        mac_ctr = 1
        for ctr in range(num_tengbes):
            this_mac = tengbe.Mac.from_roach_hostname(self.host, mac_ctr)
            self.tengbes['gbe%d' % ctr].setup(
                mac=this_mac, ipaddress='0.0.0.0', port=port)
            mac_ctr += 1
        for gbe in self.tengbes:
            gbe.dhcp_start()

        # set the destination IP and port for the tx
        gbe_ctr = 0             # pol-global counter for gbes
        for pol in range(0, num_pols):
            # Get address configuration for current polarisation
            single_destination = int(self.config.get(
                'pol{}_single_destination'.format(pol), 0))
            txaddr_start = self.config['pol{}_destination_start_ip'.format(pol)]
            txaddr_bits = txaddr_start.split('.')
            txaddr_base = int(txaddr_bits[3])
            txaddr_prefix = '.'.join(txaddr_bits[0:3])

            # Set the gbe destination address for each gbe used by this pol
            addr_offset = 0
            for pol_gbe_ctr in range(0, gbes_per_pol):
                txip = txaddr_base + addr_offset
                LOGGER.info('%s sending to: %s.%d port %d' % (
                    self.host, txaddr_prefix, txip, port))
                self.write_int('gbe_iptx%i' % gbe_ctr,
                               tengbe.IpAddress.str2ip(
                                   '%s.%d' % (txaddr_prefix, txip)))
                if not single_destination:
                    addr_offset += 1

                gbe_ctr += 1

        self.write_int('gbe_porttx', port)
        self.registers.control.write(gbe_rst=False)