Commit 0edc8d4a authored by Jason Manley's avatar Jason Manley Committed by GitHub
Browse files

Merge pull request #50 from ska-sa/devel

Merge devel into master for AR1.3
parents 2dcffcab ce42306c
......@@ -19,20 +19,28 @@ try:
except ImportError:
corr2 = None
parser = argparse.ArgumentParser(description='Display TenGBE interface information about a MeerKAT fpga host.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--hosts', dest='hosts', type=str, action='store', default='',
help='comma-delimited list of hosts, or a corr2 config file')
parser.add_argument('-c', '--core', dest='core', action='store', default='all', type=str,
help='which core to query')
parser.add_argument('--arp', dest='arp', action='store_true', default=False,
help='print the ARP table')
parser.add_argument('--cpu', dest='cpu', action='store_true', default=False,
help='print the CPU details')
parser.add_argument('--comms', dest='comms', action='store', default='katcp', type=str,
help='katcp (default) or dcp?')
parser.add_argument('--loglevel', dest='log_level', action='store', default='',
help='log level to use, default None, options INFO, DEBUG, ERROR')
parser = argparse.ArgumentParser(
description='Display TenGBE interface information '
'about a MeerKAT fpga host.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument(
'--hosts', dest='hosts', type=str, action='store', default='',
help='comma-delimited list of hosts, or a corr2 config file')
parser.add_argument(
'-c', '--core', dest='core', action='store', default='all', type=str,
help='which core to query')
parser.add_argument(
'--arp', dest='arp', action='store_true', default=False,
help='print the ARP table')
parser.add_argument(
'--cpu', dest='cpu', action='store_true', default=False,
help='print the CPU details')
parser.add_argument(
'--comms', dest='comms', action='store', default='katcp', type=str,
help='katcp (default) or dcp?')
parser.add_argument(
'--loglevel', dest='log_level', action='store', default='',
help='log level to use, default None, options INFO, DEBUG, ERROR')
args = parser.parse_args()
if args.log_level != '':
......@@ -65,8 +73,9 @@ for fpga in fpgas:
numgbes = len(fpga.tengbes)
if numgbes < 1:
raise RuntimeWarning('Host %s has no 10gbe cores', fpga.host)
print '%s: found %i 10gbe core%s.' % (fpga.host, numgbes, '' if numgbes == 1 else 's')
print '%s: found %i 10gbe core%s.' % (fpga.host,
numgbes,
'' if numgbes == 1 else 's')
for fpga in fpgas:
if args.core == 'all':
......@@ -77,7 +86,8 @@ for fpga in fpgas:
print '%s:' % fpga.host
print 50*'#'
for core in cores:
fpga.tengbes[core].print_10gbe_core_details(arp=args.arp, cpu=args.cpu, refresh=True)
fpga.tengbes[core].print_10gbe_core_details(arp=args.arp, cpu=args.cpu,
refresh=True)
# handle exits cleanly
utils.threaded_fpga_function(fpgas, 10, 'disconnect')
......
......@@ -98,7 +98,15 @@ def get_gbe_data(fpga):
"""
returndata = {}
for gbecore in fpga.tengbes:
returndata[gbecore.name] = gbecore.read_counters()
ctr_data = gbecore.read_counters()
for regname in ctr_data:
regdata = ctr_data[regname]
try:
if ('timestamp' in regdata.keys()) and ('data' in regdata.keys()):
ctr_data[regname] = regdata['data']['reg']
except AttributeError:
pass
returndata[gbecore.name] = ctr_data
return returndata
......@@ -112,9 +120,14 @@ def get_tap_data(fpga):
return data
# get gbe and tap data
tap_data = utils.threaded_fpga_operation(fpgas, 10, get_tap_data)
gbe_data = utils.threaded_fpga_operation(fpgas, 10, get_gbe_data)
# print gbe_data['roach020956']['gbe0'].keys()
tap_data = utils.threaded_fpga_operation(fpgas, 15, get_tap_data)
gbe_data = utils.threaded_fpga_operation(fpgas, 15, get_gbe_data)
# for fpga in gbe_data:
# fpga_data = gbe_data[fpga]
# print fpga, ':'
# for gbe in fpga_data:
# print gbe, ':'
# print fpga_data[gbe]
# utils.threaded_fpga_function(fpgas, 10, 'disconnect')
# sys.exit()
......@@ -185,26 +198,19 @@ try:
fpga_data = gbe_data[fpga.host]
scroller.add_line(fpga.host)
for core, core_data in fpga_data.items():
fpga_data[core]['tap_running'] = {
'data': {
'reg': not(tap_data[fpga.host][core]['name'] == '')
}
}
fpga_data[core]['ip'] = {
'data': {
'reg': tap_data[fpga.host][core]['ip']
}
}
tap_running = tap_data[fpga.host][core]['name'] == ''
fpga_data[core]['tap_running'] = not tap_running
fpga_data[core]['ip'] = tap_data[fpga.host][core]['ip']
start_pos = 20
scroller.add_line(core, 5)
for header_register in fpga_headers[0]:
core_regname = header_register.replace('gbe', core)
if start_pos < 200:
if core_regname in core_data.keys():
if not isinstance(core_data[core_regname]['data']['reg'], str):
regval = '%d' % core_data[core_regname]['data']['reg']
if not isinstance(core_data[core_regname], str):
regval = '%d' % core_data[core_regname]
else:
regval = core_data[core_regname]['data']['reg']
regval = core_data[core_regname]
else:
regval = 'n/a'
# all on the same line
......
......@@ -62,6 +62,20 @@ class CasperFpga(object):
:return:
"""
self.host = host
# this is just for code introspection
self.devices = None
self.memory_devices = None
self.other_devices = None
self.sbrams = None
self.qdrs = None
self.registers = None
self.tengbes = None
self.snapshots = None
self.system_info = None
self.rcs_info = None
# /just for introspection
self.__reset_device_info()
LOGGER.debug('%s: now a CasperFpga' % self.host)
......@@ -319,16 +333,19 @@ class CasperFpga(object):
def __create_memory_devices(self, device_dict, memorymap_dict):
"""
Create memory devices from dictionaries of design information.
:param device_dict: raw dictionary of information from tagged blocks in Simulink design, keyed on device name
:param memorymap_dict: dictionary of information that would have been in coreinfo.tab - memory bus information
:param device_dict: raw dictionary of information from tagged
blocks in Simulink design, keyed on device name
:param memorymap_dict: dictionary of information that would have been
in coreinfo.tab - memory bus information
:return:
"""
# create and add memory devices to the memory device dictionary
for device_name, device_info in device_dict.items():
if device_name == '':
raise NameError('There\'s a problem somewhere, got a blank device name?')
raise NameError('There\'s a problem somewhere, got a blank '
'device name?')
if device_name in self.memory_devices.keys():
raise NameError('Memory device %s already exists.' % device_name)
raise NameError('Memory device %s already exists' % device_name)
# get the class from the known devices, if it exists there
tag = device_info['tag']
try:
......@@ -338,16 +355,20 @@ class CasperFpga(object):
pass
else:
if not callable(known_device_class):
raise TypeError('%s is not a callable Memory class - that\'s a problem.' % known_device_class)
new_device = known_device_class.from_device_info(self, device_name, device_info, memorymap_dict)
raise TypeError('%s is not a callable Memory class - '
'that\'s a problem.' % known_device_class)
new_device = known_device_class.from_device_info(
self, device_name, device_info, memorymap_dict)
if new_device.name in self.memory_devices.keys():
raise NameError('Device called %s of type %s already exists in devices list.' %
(new_device.name, type(new_device)))
raise NameError(
'Device called %s of type %s already exists in '
'devices list.' % (new_device.name, type(new_device)))
self.devices[device_name] = new_device
self.memory_devices[device_name] = new_device
container = getattr(self, known_device_container)
setattr(container, device_name, new_device)
assert id(getattr(container, device_name)) == id(new_device) == id(self.memory_devices[device_name])
assert id(getattr(container, device_name)) == id(new_device)
assert id(new_device) == id(self.memory_devices[device_name])
# allow created devices to update themselves with full device info
# link control registers, etc
for name, device in self.memory_devices.items():
......@@ -359,12 +380,14 @@ class CasperFpga(object):
def __create_other_devices(self, device_dict):
"""
Store non-memory device information in a dictionary
:param device_dict: raw dictionary of information from tagged blocks in Simulink design, keyed on device name
:param device_dict: raw dictionary of information from tagged
blocks in Simulink design, keyed on device name
:return:
"""
for device_name, device_info in device_dict.items():
if device_name == '':
raise NameError('There\'s a problem somewhere, got a blank device name?')
raise NameError('There\'s a problem somewhere, got a '
'blank device name?')
if device_name in self.other_devices.keys():
raise NameError('Other device %s already exists.' % device_name)
if device_info['tag'] in CASPER_OTHER_DEVICES.keys():
......@@ -372,25 +395,30 @@ class CasperFpga(object):
self.other_devices[device_name] = device_info
def device_names_by_container(self, container_name):
"""Return a list of devices in a certain container.
"""
return [devname for devname, container in self.memory_devices.iteritems() if container == container_name]
Return a list of devices in a certain container.
"""
return [devname for devname, container
in self.memory_devices.iteritems()
if container == container_name]
def devices_by_container(self, container):
"""Get devices using container type.
"""
Get devices using container type.
"""
return getattr(self, container)
def get_system_information(self, filename=None, fpg_info=None):
"""
Get information about the design running on the FPGA.
If filename is given, get it from there, otherwise query the host via KATCP.
If filename is given, get it from file, otherwise query the host via KATCP.
:param filename: fpg filename
:param fpg_info: a tuple containing device_info and coreinfo dictionaries
:return: <nothing> the information is populated in the class
"""
if (filename is None) and (fpg_info is None):
raise RuntimeError('Either filename or parsed fpg data must be given.')
raise RuntimeError('Either filename or parsed fpg data '
'must be given.')
if filename is not None:
device_dict, memorymap_dict = parse_fpg(filename)
else:
......@@ -398,7 +426,7 @@ class CasperFpga(object):
memorymap_dict = fpg_info[1]
# add system registers
device_dict.update(self.__add_sys_registers())
# reset current devices and create new ones from the new design information
# reset current devices, create new ones from the new design information
self.__reset_device_info()
self.__create_memory_devices(device_dict, memorymap_dict)
self.__create_other_devices(device_dict)
......@@ -424,11 +452,38 @@ class CasperFpga(object):
secondpass += (2**32)
return (secondpass - firstpass) / 2000000.0
def check_tx_raw(self, wait_time=0.2, checks=10):
"""
Check to see whether this host is transmitting packets without
error on all its GBE interfaces.
:param wait_time: seconds to wait between checks
:param checks: times to run check
:return:
"""
for gbecore in self.tengbes:
if not gbecore.tx_okay(wait_time=wait_time, checks=checks):
return False
return True
def check_rx_raw(self, wait_time=0.2, checks=10):
"""
Check to see whether this host is receiving packets without
error on all its GBE interfaces.
:param wait_time: seconds to wait between checks
:param checks: times to run check
:return:
"""
for gbecore in self.tengbes:
if not gbecore.rx_okay(wait_time=wait_time, checks=checks):
return False
return True
@staticmethod
def __add_sys_registers():
standard_reg = {'tag': 'xps:sw_reg', 'mode': 'one value', 'io_dir': 'To Processor',
'io_delay': '1', 'sample_period': '1', 'sim_port': 'off', 'show_format': 'off',
'names': 'reg', 'bitwidths': '32', 'arith_types': '0', 'bin_pts': '0'}
standard_reg = {'tag': 'xps:sw_reg', 'io_dir': 'To Processor',
'io_delay': '1', 'sample_period': '1', 'names': 'reg',
'bitwidths': '32', 'bin_pts': '0', 'arith_types': '0',
'sim_port': 'off', 'show_format': 'off', }
sys_registers = {'sys_board_id': standard_reg.copy(),
'sys_rev': standard_reg.copy(),
'sys_rev_rcs': standard_reg.copy(),
......
......@@ -598,8 +598,15 @@ class KatcpFpga(CasperFpga, async_requester.AsyncRequester,
metalist = []
for inform in informs:
if len(inform.arguments) < 4:
raise ValueError('Incorrect number of meta inform '
if len(inform.arguments) == 3:
LOGGER.warn('Incorrect number of meta inform '
'arguments, missing value '
'field: %s' % str(inform.arguments))
inform.arguments.append('-1')
else:
LOGGER.error('FEWER than THREE meta inform '
'arguments: %s' % str(inform.arguments))
continue
for arg in inform.arguments:
arg = arg.replace('\_', ' ')
name = inform.arguments[0]
......
......@@ -6,7 +6,8 @@ Created on Feb 28, 2013
import logging
import struct
import time
from utils import check_changing_status
from memory import Memory
......@@ -157,6 +158,13 @@ class IpAddress(object):
ip.extend([int(byte, base=10)])
return struct.pack('>4B', *ip)
def is_multicast(self):
"""
Does the data source's IP address begin with 239?
:return:
"""
return (self.ip_int >> 24) == 239
def __int__(self):
return self.ip_int
......@@ -283,17 +291,19 @@ class TenGbe(Memory):
self.parent.read(self.name, 1)
def read_txsnap(self):
return self.parent.memory_devices[self.name + '_txs_ss'].read(timeout=10)['data']
tmp = self.parent.memory_devices[self.name + '_txs_ss'].read(timeout=10)
return tmp['data']
def read_rxsnap(self):
return self.parent.memory_devices[self.name + '_rxs_ss'].read(timeout=10)['data']
tmp = self.parent.memory_devices[self.name + '_rxs_ss'].read(timeout=10)
return tmp['data']
def read_rx_counters(self):
"""Read all rx counters in gbe block
"""
results = {}
for reg in self.registers['rx']:
results[reg] = self.parent.memory_devices[reg].read()
results[reg] = self.parent.memory_devices[reg].read()['data']['reg']
return results
def read_tx_counters(self):
......@@ -301,7 +311,7 @@ class TenGbe(Memory):
"""
results = {}
for reg in self.registers['tx']:
results[reg] = self.parent.memory_devices[reg].read()
results[reg] = self.parent.memory_devices[reg].read()['data']['reg']
return results
def read_counters(self):
......@@ -310,62 +320,59 @@ class TenGbe(Memory):
results = {}
for direction in ['tx', 'rx']:
for reg in self.registers[direction]:
results[reg] = self.parent.memory_devices[reg].read()
tmp = self.parent.memory_devices[reg].read()
results[reg] = tmp['data']['reg']
return results
def tx_okay(self, wait_time=1):
def rx_okay(self, wait_time=0.2, checks=10):
"""
Is this gbe block okay?
i.e. _txctr incrementing and _txerrctr not incrementing
Is this gbe core receiving okay?
i.e. _rxctr incrementing and _rxerrctr not incrementing
:param wait_time: seconds to wait between checks
:param checks: times to run check
:return: True/False
"""
TOTAL_TRIES = 2
def _runtest():
result0 = self.read_tx_counters()
# does the required tx counter exist?
if self.name+'_txctr' not in result0.keys():
LOGGER.error('%s: missing registers in gbe block' %
self.fullname)
return False
time.sleep(wait_time)
result1 = self.read_tx_counters()
# check that the tx counter is ticking over
key = self.name+'_txctr'
if (result0[key]['data']['reg'] == result1[key]['data']['reg']):
LOGGER.error('%s: %s ok - FALSE' % (self.fullname, key))
return False
else:
LOGGER.debug('%s: %s ok - TRUE' % (self.fullname, key))
optional = [self.name+'_txfullctr', self.name+'_txofctr',
self.name+'_txvldctr', self.name+'_txerrctr']
for key in optional:
if key not in result0.keys():
LOGGER.debug('%s: %s not implemented' % (self.fullname, key))
continue
res0 = result0[key]['data']['reg']
res1 = result1[key]['data']['reg']
key_suffix = key.replace(self.name, '')
if key_suffix in ['_txerrctr', '_txfullctr', '_txofctr']:
if res0 == res1:
LOGGER.debug('%s: %s ok - TRUE' % (self.fullname, key))
else:
LOGGER.error('%s: %s ok - FALSE' % (self.fullname, key))
return False
elif key_suffix == '_txvldctr':
if res0 != res1:
LOGGER.debug('%s: %s ok - TRUE' % (self.fullname, key))
else:
LOGGER.error('%s: %s ok - FALSE' % (self.fullname, key))
return False
LOGGER.info('%s: tx_okay() - TRUE.' % self.fullname)
return True
for tries in range(TOTAL_TRIES):
if _runtest():
return True
LOGGER.error('%s: tx_okay() - FALSE.' % self.fullname)
return False
if checks < 2:
raise RuntimeError('Cannot check less often than twice?')
fields = {
# name, required, True=same|False=different
self.name + '_rxctr': (True, False),
self.name + '_rxfullctr': (False, True),
self.name + '_rxofctr': (False, True),
self.name + '_rxerrctr': (True, True),
self.name + '_rxvldctr': (False, False),
}
result, message = check_changing_status(fields, self.read_rx_counters,
wait_time, checks)
if not result:
LOGGER.error('%s: %s' % (self.fullname, message))
return False
return True
def tx_okay(self, wait_time=0.2, checks=10):
"""
Is this gbe core transmitting okay?
i.e. _txctr incrementing and _txerrctr not incrementing
:param wait_time: seconds to wait between checks
:param checks: times to run check
:return: True/False
"""
if checks < 2:
raise RuntimeError('Cannot check less often than twice?')
fields = {
# name, required, True=same|False=different
self.name + '_txctr': (True, False),
self.name + '_txfullctr': (False, True),
self.name + '_txofctr': (False, True),
self.name + '_txerrctr': (False, True),
self.name + '_txvldctr': (False, False),
}
result, message = check_changing_status(fields, self.read_tx_counters,
wait_time, checks)
if not result:
LOGGER.error('%s: %s' % (self.fullname, message))
return False
return True
#def read_raw(self, **kwargs):
# # size is in bytes
......@@ -599,6 +606,9 @@ class TenGbe(Memory):
0x29 : RX_eq_pol
0x2a : TX_preemph
0x2b : TX_diff_ctrl
0x30 - 0x33: Multicast IP RX base address
0x34 - 0x37: Multicast IP mask
0x38 - 0x3b: Multicast subnet mask
0x1000 : CPU TX buffer
0x2000 : CPU RX buffer
0x3000 : ARP tables start
......@@ -636,26 +646,55 @@ class TenGbe(Memory):
#self.add_field(Bitfield.Field('arp_table', 0, 0x1000 * word_width, 0, 0x3000 * word_width))
"""
returnval = {}
port_dump = list(struct.unpack('>16384B', self.parent.read(self.name, 16384)))
returnval['ip_prefix'] = '%i.%i.%i.' % (port_dump[0x10], port_dump[0x11], port_dump[0x12])
returnval['ip'] = IpAddress('%i.%i.%i.%i' % (port_dump[0x10], port_dump[0x11],
port_dump[0x12], port_dump[0x13]))
returnval['mac'] = Mac('%i:%i:%i:%i:%i:%i' % (port_dump[0x02], port_dump[0x03],
port_dump[0x04], port_dump[0x05],
port_dump[0x06], port_dump[0x07]))
returnval['gateway_ip'] = IpAddress('%i.%i.%i.%i' % (port_dump[0x0c], port_dump[0x0d],
port_dump[0x0e], port_dump[0x0f]))
port_dump = list(struct.unpack('>16384B',
self.parent.read(self.name, 16384)))
returnval['ip_prefix'] = '%i.%i.%i.' % (
port_dump[0x10], port_dump[0x11], port_dump[0x12])
returnval['ip'] = IpAddress('%i.%i.%i.%i' % (
port_dump[0x10], port_dump[0x11], port_dump[0x12], port_dump[0x13]))
returnval['mac'] = Mac('%i:%i:%i:%i:%i:%i' % (
port_dump[0x02], port_dump[0x03],
port_dump[0x04], port_dump[0x05],
port_dump[0x06], port_dump[0x07]))
returnval['gateway_ip'] = IpAddress('%i.%i.%i.%i' % (
port_dump[0x0c], port_dump[0x0d], port_dump[0x0e], port_dump[0x0f]))
returnval['fabric_port'] = ((port_dump[0x22] << 8) + (port_dump[0x23]))
returnval['fabric_en'] = bool(port_dump[0x21] & 1)
returnval['xaui_lane_sync'] = [bool(port_dump[0x27] & 4), bool(port_dump[0x27] & 8),
bool(port_dump[0x27] & 16), bool(port_dump[0x27] & 32)]
returnval['xaui_status'] = [port_dump[0x24], port_dump[0x25], port_dump[0x26], port_dump[0x27]]
returnval['xaui_lane_sync'] = [
bool(port_dump[0x27] & 4), bool(port_dump[0x27] & 8),
bool(port_dump[0x27] & 16), bool(port_dump[0x27] & 32)]
returnval['xaui_status'] = [
port_dump[0x24], port_dump[0x25], port_dump[0x26], port_dump[0x27]]
returnval['xaui_chan_bond'] = bool(port_dump[0x27] & 64)
returnval['xaui_phy'] = {}
returnval['xaui_phy']['rx_eq_mix'] = port_dump[0x28]
returnval['xaui_phy']['rx_eq_pol'] = port_dump[0x29]
returnval['xaui_phy']['tx_preemph'] = port_dump[0x2a]
returnval['xaui_phy']['tx_swing'] = port_dump[0x2b]
returnval['multicast'] = {}
returnval['multicast']['base_ip'] = IpAddress(
'%i.%i.%i.%i' % (port_dump[0x30], port_dump[0x31],
port_dump[0x32], port_dump[0x33]))
returnval['multicast']['ip_mask'] = IpAddress(
'%i.%i.%i.%i' % (port_dump[0x34], port_dump[0x35],
port_dump[0x36], port_dump[0x37]))
returnval['multicast']['subnet_mask'] = IpAddress(
'%i.%i.%i.%i' % (port_dump[0x38], port_dump[0x39],
port_dump[0x3a], port_dump[0x3b]))
possible_addresses = [int(returnval['multicast']['base_ip'])]
mask_int = int(returnval['multicast']['ip_mask'])
for ctr in range(32):
mask_bit = (mask_int >> ctr) & 1
if not mask_bit:
new_ips = []
for ip in possible_addresses:
new_ips.append(ip & (~(1 << ctr)))
new_ips.append(new_ips[-1] | (1 << ctr))
possible_addresses.extend(new_ips)
returnval['multicast']['rx_ips'] = []
tmp = list(set(possible_addresses))
for ip in tmp:
returnval['multicast']['rx_ips'].append(IpAddress(ip))
if read_arp:
returnval['arp'] = self.get_arp_details(port_dump)
if read_cpu:
......@@ -708,27 +747,16 @@ class TenGbe(Memory):
"""
if refresh or (self.core_details is None):
self.get_10gbe_core_details(arp, cpu)
#print self.core_details
print '------------------------'
print '%s configuration:' % self.fullname
print 'MAC: ',
for mac in self.core_details['mac']:
print '%02X' % mac,
print ''
print 'Gateway: ',
for gw_ip in self.core_details['gateway_ip']:
print '%3d' % gw_ip,
print ''
print 'IP: ',
for ip_addr in self.core_details['ip']:
print '%3d' % ip_addr,
print ''
print 'MAC: ', Mac.mac2str(int(self.core_details['mac']))
print 'Gateway: ', self.core_details['gateway_ip']
print 'IP: ', self.core_details['ip']
print 'Fabric port: ',
print '%5d' % self.core_details['fabric_port']
print 'Fabric interface is currently:', 'Enabled' if self.core_details['fabric_en'] else 'Disabled'
print 'XAUI Status: ',
for xaui_status in self.core_details['xaui_status']:
print '%02X' % xaui_status,
print ''
print 'XAUI Status: ', self.core_details['xaui_status']
for i in range(0, 4):
print '\tlane sync %i: %i' % (i, self.core_details['xaui_lane_sync'][i])
print '\tChannel bond: %i' % self.core_details['xaui_chan_bond']
......@@ -737,6 +765,10 @@ class TenGbe(Memory):
print '\tRX_eq_pol: %2X' % self.core_details['xaui_phy']['rx_eq_pol']
print '\tTX_pre-emph: %2X' % self.core_details['xaui_phy']['tx_preemph']
print '\tTX_diff_ctrl: %2X' % self.core_details['xaui_phy']['tx_swing']
print 'Multicast:'
for k in self.core_details['multicast']:
print '\t%s: %s' % (k, self.core_details['multicast'][k])