Commit 2dcffcab authored by Jason Manley's avatar Jason Manley
Browse files

Merge pull request #48 from ska-sa/devel

Merge devel into master
parents 0d2eb8ac cafad727
......@@ -23,18 +23,25 @@ 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('-p', '--polltime', dest='polltime', action='store', default=1, type=int,
help='time at which to poll data, in seconds')
parser.add_argument('-r', '--reset', dest='resetctrs', action='store_true', default=False,
help='reset the GBE debug counters')
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(
'-p', '--polltime', dest='polltime', action='store', default=1, type=int,
help='time at which to poll data, in seconds')
parser.add_argument(
'-r', '--reset', dest='resetctrs', action='store_true', default=False,
help='reset the GBE debug counters')
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()
polltime = args.polltime
......@@ -68,7 +75,8 @@ 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')
if args.resetctrs:
def reset_gbe_debug(fpga_):
......@@ -121,9 +129,10 @@ for fpga in fpgas:
fpga_headers.append(reg)
fpga_headers = [fpga_headers]
fpga_headers = [['tap_running', 'ip', 'gbe_rxctr', 'gbe_rxofctr', 'gbe_rxerrctr',
'gbe_rxbadctr', 'gbe_txerrctr', 'gbe_txfullctr', 'gbe_txofctr',
'gbe_txctr', 'gbe_txvldctr']]
fpga_headers = [['tap_running', 'ip', 'gbe_rxctr', 'gbe_rxofctr',
'gbe_rxerrctr', 'gbe_rxbadctr', 'gbe_txerrctr',
'gbe_txfullctr', 'gbe_txofctr', 'gbe_txctr', 'gbe_txvldctr']]
def exit_gracefully(sig, frame):
print sig, frame
......@@ -146,22 +155,25 @@ try:
if keypress == -1:
break
elif keypress > 0:
# if character == 'c':
# for f in fpgas:
# f.reset_counters()
# if character == 'c':
# for f in fpgas:
# f.reset_counters()
scroller.draw_screen()
if time.time() > last_refresh + polltime:
scroller.clear_buffer()
scroller.add_line('Polling %i host%s every %s - %is elapsed.' %
(len(fpgas), '' if len(fpgas) == 1 else 's',
'second' if polltime == 1 else ('%i seconds' % polltime),
time.time() - STARTTIME), 0, 0, absolute=True)
scroller.add_line(
'Polling %i host%s every %s - %is elapsed.' % (
len(fpgas),
'' if len(fpgas) == 1 else 's',
'second' if polltime == 1 else ('%i seconds' % polltime),
time.time() - STARTTIME), 0, 0, absolute=True)
start_pos = 20
pos_increment = 15
if len(fpga_headers) == 1:
scroller.add_line('Host', 0, 1, absolute=True)
for reg in fpga_headers[0]:
scroller.add_line(reg.rjust(pos_increment), start_pos, 1, absolute=True)
scroller.add_line(
reg.rjust(pos_increment), start_pos, 1, absolute=True)
start_pos += pos_increment
scroller.set_ypos(newpos=2)
scroller.set_ylimits(ymin=2)
......@@ -173,23 +185,32 @@ 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': False if tap_data[fpga.host][core]['name'] == '' else True}}
fpga_data[core]['ip'] = {'data': {'reg': tap_data[fpga.host][core]['ip']}}
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']
}
}
start_pos = 20
scroller.add_line(core, 5)
for header_register in fpga_headers[0]:
core_register_name = header_register.replace('gbe', core)
core_regname = header_register.replace('gbe', core)
if start_pos < 200:
if core_register_name in core_data.keys():
if not isinstance(core_data[core_register_name]['data']['reg'], str):
regval = '%d' % core_data[core_register_name]['data']['reg']
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']
else:
regval = core_data[core_register_name]['data']['reg']
regval = core_data[core_regname]['data']['reg']
else:
regval = 'n/a'
# all on the same line
scroller.add_line(regval.rjust(pos_increment), start_pos, scroller.get_current_line() - 1)
scroller.add_line(regval.rjust(pos_increment),
start_pos,
scroller.get_current_line() - 1)
start_pos += pos_increment
scroller.draw_screen()
last_refresh = time.time()
......
......@@ -3,6 +3,33 @@ import logging
LOGGER = logging.getLogger(__name__)
def clean_fields(parent_name, parent_type, field_str):
"""
Take the Simulink string for a field and return a list
:param parent_name: the BitField that will run this
:param parent_type: register, snapshot, etc
:param field_str: the string to be parsed
:return:
"""
_fstr = field_str.replace('[', '').replace(']', '')
_fstr = _fstr.strip().replace(', ', ',').replace(' ', ' ')
if (_fstr.find(' ') > -1) and (_fstr.find(',') > -1):
LOGGER.error('Parameter string %s contains spaces and commas '
'as delimiters. This is confusing.' % field_str)
if _fstr.find(' ') > -1:
_flist = _fstr.split(' ')
else:
_flist = _fstr.split(',')
_rv = []
for _fname in _flist:
if _fname.strip() == '':
LOGGER.DEBUG('Throwing away empty field in %s %s' % (
parent_type, parent_name))
else:
_rv.append(_fname)
return _rv
class Bitfield(object):
"""
Describes a chunk of memory that consists of a number of Fields.
......@@ -13,7 +40,8 @@ class Bitfield(object):
self._fields = {}
if fields is not None:
self.fields_add(fields)
LOGGER.debug('New Bitfield(%s) with %i fields' % (self.name, len(self._fields)))
LOGGER.debug('New Bitfield(%s) with %i fields' % (self.name,
len(self._fields)))
# def __dir__(self):
# return self._fields.keys()
......
......@@ -86,6 +86,19 @@ class CasperFpga(object):
"""
self.__reset_device_info()
def upload_to_ram_and_program(self, filename, port=-1, timeout=10,
wait_complete=True):
"""
Upload an FPG file to RAM and then program the FPGA.
:param filename: the file to upload
:param port: the port to use on the rx end, -1 means a random port
:param timeout: how long to wait, seconds
:param wait_complete: wait for the transaction to complete, return
after upload if False
:return:
"""
raise NotImplementedError
def __reset_device_info(self):
"""
Reset information of devices this FPGA knows about.
......@@ -250,9 +263,9 @@ class CasperFpga(object):
try:
data = struct.pack('>i' if integer < 0 else '>I', integer)
except Exception as ve:
LOGGER.error('Writing integer %i failed with error %s' % (
LOGGER.error('Writing integer %i failed with error: %s' % (
integer, ve.message))
raise ValueError('Writing integer %i failed with error %s' % (
raise ValueError('Writing integer %i failed with error: %s' % (
integer, ve.message))
if blindwrite:
self.blindwrite(device_name, data, word_offset*4)
......
......@@ -82,7 +82,7 @@ class KatcpFpga(CasperFpga, async_requester.AsyncRequester,
if connect:
self.connect()
LOGGER.info('%s: port(%s) created%s.' %
(self.host, port,' & connected' if connect else ''))
(self.host, port, ' & connected' if connect else ''))
def connect(self, timeout=None):
"""
......@@ -106,7 +106,7 @@ class KatcpFpga(CasperFpga, async_requester.AsyncRequester,
self.start(daemon=True)
connected = self.wait_connected(timeout)
if not connected:
raise RuntimeError('Connection to {} not established witin {}s'
raise RuntimeError('Connection to {} not established within {}s'
.format(self.bind_address_string, timeout))
# check that an actual katcp command gets through
got_ping = False
......@@ -118,6 +118,15 @@ class KatcpFpga(CasperFpga, async_requester.AsyncRequester,
if not got_ping:
raise RuntimeError('Could not connect to KATCP '
'server %s' % self.host)
# set a higher write buffer size than standard
try:
if self._stream.max_write_buffer_size <= 262144:
self._stream.max_buffer_size *= 2
self._stream.max_write_buffer_size *= 2
except AttributeError:
LOGGER.warn('%s: no ._stream instance found.' % self.host)
LOGGER.info('%s: connection established' % self.host)
def disconnect(self):
......
......@@ -35,78 +35,58 @@ def bin2fp(raw_word, bitwidth, bin_pt, signed):
return quotient + (float(rem) / (2**bin_pt))
raise RuntimeError
def fp2fixed_int(num, bitwidth, bin_pt, signed):
def fp2fixed(num, bitwidth, bin_pt, signed):
"""
Convert a given float to an integer representation of the fixed-point number
described by the params.
Provides the same output as a Xilinx block in Simulink would if you cast
it to an unsigned int.
Convert a floating point number to its fixed point equivalent.
:param num:
:param bitwidth:
:param bin_pt:
:param signed:
:return:
"""
_format = '%s%i.%i' % ('fix' if signed else 'ufix', bitwidth, bin_pt)
LOGGER.debug('Converting %f to %s' % (num, _format))
if bin_pt >= bitwidth:
raise ValueError('Cannot have bin_pt >= bitwidth')
if bin_pt < 0:
raise ValueError('bin_pt < 0 makes no sense')
if (not signed) and (num < 0):
raise ValueError('Cannot represent %f in %s' % (num, _format))
raise ValueError('Cannot represent negative number (%f) in %s' % (
num, _format))
if num == 0:
return 0
left_bits = bitwidth - bin_pt
scaled = num * (2**bin_pt)
scaled = round(scaled)
if signed:
left_limits = -1 * 2**(left_bits-1), 2**(left_bits-1) - 1
_nbits = bitwidth - 1
limits = [-1 * (2**_nbits), (2**_nbits) - 1]
else:
left_limits = 0, 2**left_bits - 1
negnum = num < 0
_original_num = num
num = abs(num)
right, left = np.modf(num)
left = int(left)
right = int(right*(2**bin_pt))
# left = int(num)
if left > left_limits[1]:
raise ValueError('Cannot represent %f in %s' % (_original_num, _format))
# right = int(round((abs(num) % 1) * (2**bin_pt)))
assert left >= 0 and right >= 0
_lsbin = bin(left)[2:]
_lsbin = '0'*(left_bits-len(_lsbin)) + _lsbin
if bin_pt == 0:
_rsbin = ''
else:
_rsbin = bin(right)[2:]
_rsbin = '0'*(bin_pt-len(_rsbin)) + _rsbin
rv = int(_lsbin + _rsbin, 2)
if negnum:
rv = 2**bitwidth - rv
LOGGER.debug(' returning %i' % rv)
return rv
limits = [0, (2**bitwidth) - 1]
scaled = min(limits[1], max(limits[0], scaled))
unscaled = scaled / ((2**bin_pt) * 1.0)
return unscaled
def bin2fp_old(bits, mantissa=8, exponent=7, signed=False):
def cast_fixed(fpnum, bitwidth, bin_pt):
"""
Convert a raw fixed-point number to a float based on a given
mantissa and exponent.
Represent a fixed point number as an unsigned number, like the Xilinx
reinterpret block.
:param fpnum:
:param bitwidth:
:param bin_pt:
:return:
"""
if bits == 0:
if fpnum == 0:
return 0
if exponent >= mantissa:
raise TypeError('Unsupported fixed format: %i.%i' % (mantissa, exponent))
if not signed:
if exponent == 0:
if mantissa <= 63:
return int(bits)
else:
# print bits, mantissa, exponent
return long(bits)
else:
return float(bits) / (2**exponent)
if bits >= 2**(mantissa-1):
rnum = float(bits - (1 << mantissa)) / (2**exponent)
else:
rnum = float(bits) / (2**exponent)
return rnum
val = int(fpnum * (2**bin_pt))
if fpnum < 0:
val += 2**bitwidth
return val
def fp2fixed_int(num, bitwidth, bin_pt, signed):
"""
Compatability function, rather use the other functions explicitly.
"""
val = fp2fixed(num, bitwidth, bin_pt, signed)
return cast_fixed(val, bitwidth, bin_pt)
class Memory(bitfield.Bitfield):
"""
......
This diff is collapsed.
......@@ -10,18 +10,21 @@ class Register(Memory):
"""
A CASPER register on an FPGA.
"""
def __init__(self, parent, name, address, device_info=None, auto_update=False):
def __init__(self, parent, name, address, device_info=None,
auto_update=False):
self.auto_update = auto_update
self.parent = parent
self.last_values = {}
Memory.__init__(self, name=name, width_bits=32, address=address, length_bytes=4)
Memory.__init__(self, name=name, width_bits=32,
address=address, length_bytes=4)
self.process_info(device_info)
LOGGER.debug('New Register %s' % self)
@classmethod
def from_device_info(cls, parent, device_name, device_info, memorymap_dict):
"""
Process device info and the memory map to get all necessary info and return a Register instance.
Process device info and the memory map to get all necessary info and
return a Register instance.
:param device_name: the unique device name
:param device_info: information about this device
:param memorymap_dict: a dictionary containing the device memory map
......@@ -36,8 +39,10 @@ class Register(Memory):
if address == -1 or length_bytes == -1:
LOGGER.error(memorymap_dict)
print memorymap_dict
raise RuntimeError('Could not find address or length for Register %s' % device_name)
return cls(parent, device_name, address=address, device_info=device_info)
raise RuntimeError('Could not find address or length for '
'Register %s' % device_name)
return cls(parent, device_name, address=address,
device_info=device_info)
def info(self):
"""
......@@ -69,7 +74,6 @@ class Register(Memory):
:param kwargs:
:return:
"""
rawdata = self.parent.read(device_name=self.name, size=4, offset=0*4)
return rawdata, time.time()
......@@ -86,7 +90,8 @@ class Register(Memory):
"""
Write an unsigned integer to this device using the fpga client.
"""
self.parent.write_int(device_name=self.name, integer=uintvalue, blindwrite=blindwrite, word_offset=word_offset)
self.parent.write_int(device_name=self.name, integer=uintvalue,
blindwrite=blindwrite, word_offset=word_offset)
def _write_common(self, **kwargs):
"""
......@@ -101,8 +106,8 @@ class Register(Memory):
new_values = {_field: None for _field in self.field_names()}
for k in kwargs:
if k not in new_values:
raise ValueError('Field {} not found in register {} on '
'host {}'.format(k, self.name, self.parent.host))
raise ValueError('Field {} not found in register {} on host '
'{}'.format(k, self.name, self.parent.host))
if kwargs[k] in ['pulse', 'toggle']:
_read_necessary = True
new_values[k] = kwargs[k]
......@@ -130,9 +135,26 @@ class Register(Memory):
# pack the values into a 32-bit integer
fixed_int = 0
for _f in self._fields.values():
_intval = fp2fixed_int(
new_values[_f.name], _f.width_bits, _f.binary_pt, _f.numtype == 1)
_intval = fp2fixed_int(new_values[_f.name], _f.width_bits,
_f.binary_pt, _f.numtype == 1)
fixed_int |= (_intval << _f.offset)
# double-check the integer value is not too large
if fixed_int > (2**32)-1:
LOGGER.error('%s: problem writing to register %s:' %
(self.parent.host, self.name))
for _f in self._fields.values():
_intval = fp2fixed_int(new_values[_f.name], _f.width_bits,
_f.binary_pt, _f.numtype == 1)
LOGGER.error('%s:%s:%s:%i(%sfix%i.%i) = %.8e -> %i' % (
self.parent.host, self.name, _f.name, _f.offset,
'u' if _f.numtype != 1 else '',
_f.width_bits, _f.binary_pt,
new_values[_f.name], _intval
))
LOGGER.error('%s:%s - gave int value of %i' %
(self.parent.host, self.name, fixed_int))
return fixed_int, pulse
def blindwrite(self, **kwargs):
......@@ -164,7 +186,8 @@ class Register(Memory):
return
self.block_info = info
self.fields_clear()
if 'mode' in self.block_info.keys():
# current and current-but-one have names field
if 'names' in self.block_info.keys():
self._process_info_current()
elif 'numios' in self.block_info.keys():
# aborted tabbed one
......@@ -174,7 +197,8 @@ class Register(Memory):
LOGGER.error('Old registers are deprecated!')
self.field_add(bitfield.Field('reg', 0, 32, 0, 0))
else:
LOGGER.error('That is a seriously old register - please swap it out!')
LOGGER.error('That is a seriously old register - please swap it '
'out!')
LOGGER.error(self)
LOGGER.error(self.block_info)
self.field_add(bitfield.Field('reg', 0, 32, 0, 0))
......@@ -182,61 +206,41 @@ class Register(Memory):
def _process_info_current(self):
# current one
def clean_fields(fstr):
_fstr = fstr.replace('[', '').replace(']', '').strip().replace(', ', ',').replace(' ', ' ')
if (_fstr.find(' ') > -1) and (_fstr.find(',') > -1):
LOGGER.error(
'Parameter string %s contains spaces and commas as delimiters. '
'This is confusing.' % fstr)
if _fstr.find(' ') > -1:
_flist = _fstr.split(' ')
else:
_flist = _fstr.split(',')
_rv = []
for _fname in _flist:
if _fname.strip() == '':
LOGGER.DEBUG('Throwing away empty field in register %s' % self.name)
else:
_rv.append(_fname)
return _rv
clean_fields = bitfield.clean_fields
# a single value may have been used for width, type or binary point
field_names = clean_fields(self.block_info['names'])
field_widths = clean_fields(self.block_info['bitwidths'])
field_types = clean_fields(self.block_info['arith_types'])
field_bin_pts = clean_fields(self.block_info['bin_pts'])
field_names.reverse()
field_widths.reverse()
field_types.reverse()
field_bin_pts.reverse()
# convert the number-based fields to integers
for avar in [field_widths, field_bin_pts, field_types]:
for index, value in enumerate(avar):
fields = {'names': clean_fields(self.name, 'register',
self.block_info['names']),
'widths': clean_fields(self.name, 'register',
self.block_info['bitwidths']),
'types': clean_fields(self.name, 'register',
self.block_info['arith_types']),
'bps': clean_fields(self.name, 'register',
self.block_info['bin_pts'])}
fields['names'].reverse()
fields['widths'].reverse()
fields['types'].reverse()
fields['bps'].reverse()
len_names = len(fields['names'])
for fld in ['widths', 'types', 'bps']:
# convert the number-based fields to integers
for n, val in enumerate(fields[fld]):
try:
intvalue = int(value)
intvalue = int(val)
except ValueError:
intvalue = eval(value)
avar[index] = intvalue
num_fields = len(field_names)
if self.block_info['mode'] == 'fields of equal size':
for avar in [field_widths, field_bin_pts, field_types]:
if len(avar) != 1:
raise RuntimeError('register %s has equal size fields set, field parameters != 1?', self.name)
avar[:] = num_fields * avar
elif self.block_info['mode'] == 'fields of arbitrary size':
if num_fields == 1:
if (len(field_widths) != 1) or (len(field_types) != 1) or (len(field_bin_pts) != 1):
raise RuntimeError('register %s has equal size fields set, unequal field parameters?', self.name)
else:
for avar in [field_widths, field_bin_pts, field_types]:
len_avar = len(avar)
if len_avar != num_fields:
if len_avar == 1:
avar[:] = num_fields * avar
else:
raise RuntimeError('register %s: number of fields is %s, given %s', self.name, num_fields,
len_avar)
for ctr, name in enumerate(field_names):
field = bitfield.Field(name, field_types[ctr], field_widths[ctr], field_bin_pts[ctr], -1)
intvalue = eval(val)
fields[fld][n] = intvalue
# accommodate new snapshots where the fields may have length one
len_fld = len(fields[fld])
if len_fld != len_names:
if len_fld != 1:
raise RuntimeError('%i names, but %i %s?' % (
len_names, len_fld, fld))
fields[fld] = [fields[fld][0]] * len_names
# construct the fields and add them to this BitField
for ctr, name in enumerate(fields['names']):
field = bitfield.Field(name, fields['types'][ctr],
fields['widths'][ctr],
fields['bps'][ctr], -1)
self.field_add(field, auto_offset=True)
def _process_info_tabbed(self):
......
......@@ -11,25 +11,29 @@ class Snap(Memory):
"""
Snap blocks are triggered/controlled blocks of RAM on FPGAs.
"""
def __init__(self, parent, name, width_bits, address, length_bytes, device_info=None):
def __init__(self, parent, name, width_bits, address, length_bytes,
device_info=None):
super(Snap, self).__init__(name=name, width_bits=width_bits, address=address, length_bytes=length_bytes)
super(Snap, self).__init__(name=name, width_bits=width_bits,
address=address, length_bytes=length_bytes)
self.parent = parent
self.block_info = device_info
self.field_add(bitfield.Field(
name='data', numtype=0, width_bits=self.width_bits, binary_pt=0, lsb_offset=0))
self.field_add(bitfield.Field(name='data', numtype=0,
width_bits=self.width_bits,
binary_pt=0, lsb_offset=0))
self.control_registers = {
'control': {'register': None, 'name': self.name + '_ctrl'},
'status': {'register': None, 'name': self.name + '_status'},
'trig_offset': {'register': None, 'name': self.name + '_trig_offset'},
'extra_value': {'register': None, 'name': self.name + '_val'},
'tr_en_cnt': {'register': None, 'name': self.name + '_tr_en_cnt'}}
'control': {'register': None, 'name': self.name + '_ctrl'},
'status': {'register': None, 'name': self.name + '_status'},
'trig_offset': {'register': None, 'name': self.name + '_trig_offset'},
'extra_value': {'register': None, 'name': self.name + '_val'},
'tr_en_cnt': {'register': None, 'name': self.name + '_tr_en_cnt'}}
LOGGER.debug('New Snap %s' % self)