#!/usr/bin/env python # # Copyright 2010-2011 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import optparse import math import socket import struct import array import os.path import sys import time try: import fcntl N230_DEVICE_DISCOVERY_AVAILABLE = True except: N230_DEVICE_DISCOVERY_AVAILABLE = False ######################################################################## # constants ######################################################################## N230_FW_COMMS_UDP_PORT = 49152 N230_FW_COMMS_MAX_DATA_WORDS = 16 N230_FW_COMMS_FLAGS_ACK = 0x00000001 N230_FW_COMMS_FLAGS_ERROR_MASK = 0xF0000000 N230_FW_COMMS_FLAGS_CMD_MASK = 0x000000F0 N230_FW_COMMS_CMD_ECHO = 0x00000000 N230_FW_COMMS_CMD_POKE32 = 0x00000010 N230_FW_COMMS_CMD_PEEK32 = 0x00000020 N230_FW_COMMS_CMD_BLOCK_POKE32 = 0x00000030 N230_FW_COMMS_CMD_BLOCK_PEEK32 = 0x00000040 N230_FW_COMMS_ERR_PKT_ERROR = 0x80000000 N230_FW_COMMS_ERR_CMD_ERROR = 0x40000000 N230_FW_COMMS_ERR_SIZE_ERROR = 0x20000000 N230_FW_COMMS_ID = 0x0001ACE3 N230_FW_LOADER_ADDR = 0xfa00 N230_FW_LOADER_DATA = 0xfa04 N230_FW_LOADER_NUM_WORDS = 8192 N230_FW_LOADER_BOOT_DONE_ADDR = 0xA004 N230_FW_LOADER_BOOT_TIMEOUT = 5 N230_JESD204_TEST = 0xA014 N230_FPGA_HASH_ADDR = 0xA010 N230_FW_HASH_ADDR = 0x10004 N230_ICAP_ADDR = 0xF800 #ICAP_DUMMY_WORD = 0xFFFFFFFF #ICAP_SYNC_WORD = 0xAA995566 #ICAP_TYPE1_NOP = 0x20000000 #ICAP_WRITE_WBSTAR = 0x30020001 #ICAP_WBSTAR_ADDR = 0x00000000 #ICAP_WRITE_CMD = 0x30008001 #ICAP_IPROG_CMD = 0x0000000F # Bit reversed values per Xilinx UG470 - Bits reversed within bytes. ICAP_DUMMY_WORD = 0xFFFFFFFF ICAP_SYNC_WORD = 0x5599AA66 ICAP_TYPE1_NOP = 0x04000000 ICAP_WRITE_WBSTAR = 0x0C400080 ICAP_WBSTAR_ADDR = 0x00000000 ICAP_WRITE_CMD = 0x0C000180 ICAP_IPROG_CMD = 0x000000F0 UDP_MAX_XFER_BYTES = 256 UDP_TIMEOUT = 3 FPGA_LOAD_TIMEOUT = 10 _seq = -1 def seq(): global _seq _seq = _seq+1 return _seq ######################################################################## # helper functions ######################################################################## def pack_fw_command(flags, seq, num_words, addr, data_arr): if (num_words > N230_FW_COMMS_MAX_DATA_WORDS): raise Exception("Data size too large. Firmware supports a max 16 words per block." % (addr)) buf = bytes() buf = struct.pack('!IIIII', N230_FW_COMMS_ID, flags, seq, num_words, addr) for i in range(N230_FW_COMMS_MAX_DATA_WORDS): if (i < num_words): buf += struct.pack('!I', data_arr[i]) else: buf += struct.pack('!I', 0) return buf def unpack_fw_command(buf, fmt=None): (id, flags, seq, num_words, addr) = struct.unpack_from('!IIIII', buf) fw_check_error(flags) data = [] if fmt is None: fmt = 'I' for i in xrange(20, len(buf), 4): data.append(struct.unpack('!'+fmt, buf[i:i+4])[0]) return (flags, seq, num_words, addr, data) def fw_check_error(flags): if flags & N230_FW_COMMS_ERR_PKT_ERROR == N230_FW_COMMS_ERR_PKT_ERROR: raise Exception("The fiwmware operation returned a packet error") if flags & N230_FW_COMMS_ERR_CMD_ERROR == N230_FW_COMMS_ERR_CMD_ERROR: raise Exception("The fiwmware operation returned a command error") if flags & N230_FW_COMMS_ERR_SIZE_ERROR == N230_FW_COMMS_ERR_SIZE_ERROR: raise Exception("The fiwmware operation returned a size error") def chunkify(stuff, n): return [stuff[i:i+n] for i in range(0, len(stuff), n)] def draw_progress_bar(percent, bar_len = 32): sys.stdout.write("\r") progress = "" for i in range(bar_len): if i < int((bar_len * percent) / 100): progress += "=" else: progress += "-" sys.stdout.write("[%s] %d%%" % (progress, percent)) sys.stdout.flush() ######################################################################## # Discovery class ######################################################################## class discovery_socket(object): def __init__(self): self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) self._sock.settimeout(0.250) def get_bcast_addrs(self): max_possible = 128 # arbitrary. raise if needed. num_bytes = max_possible * 32 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) names = array.array('B', '\0' * num_bytes) outbytes = struct.unpack('iL', fcntl.ioctl( s.fileno(), 0x8912, # SIOCGIFCONF struct.pack('iL', num_bytes, names.buffer_info()[0]) ))[0] namestr = names.tostring() lst = [] for i in range(0, outbytes, 40): name = namestr[i:i+16].split('\0', 1)[0] ip = map(ord, namestr[i+20:i+24]) mask = map(ord, fcntl.ioctl(s.fileno(), 0x891B, struct.pack('256s', name))[20:24]) bcast = [] for i in range(len(ip)): bcast.append((ip[i] | (~mask[i])) & 0xFF) if (name != 'lo'): lst.append(str(bcast[0]) + '.' + str(bcast[1]) + '.' + str(bcast[2]) + '.' + str(bcast[3])) return lst def discover(self): addrs = [] for bcast_addr in self.get_bcast_addrs(): out_pkt = pack_fw_command(N230_FW_COMMS_CMD_ECHO|N230_FW_COMMS_FLAGS_ACK, seq(), 0, 0, [0]) self._sock.sendto(out_pkt, (bcast_addr, N230_FW_COMMS_UDP_PORT)) while 1: try: (in_pkt, addr_pair) = self._sock.recvfrom(UDP_MAX_XFER_BYTES) (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt) addrs.append(addr_pair[0]) except socket.error: break return addrs ######################################################################## # Communications class, holds a socket and send/recv routine ######################################################################## class ctrl_socket(object): def __init__(self, addr): self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._sock.settimeout(UDP_TIMEOUT) self._sock.connect((addr, N230_FW_COMMS_UDP_PORT)) self.set_callbacks(lambda *a: None, lambda *a: None) def set_callbacks(self, progress_cb, status_cb): self._progress_cb = progress_cb self._status_cb = status_cb def send(self, pkt): self._sock.send(pkt) def recv(self): return self._sock.recv(UDP_MAX_XFER_BYTES) def send_and_recv(self, pkt): self.send(pkt) return self.recv() def read_time_stats(self): print regs = [' ingress1',' ingress2',' egress1',' egress1'] for reg in range (0, 4): print("%s " % regs[reg]), data = self.peek64(0xA000 + 32 + (reg*8), fmt='i') print("%10d " % (data)), print("%10f uS" % (data * 0.0217)) print def peek(self, peek_addr, fmt=None): out_pkt = pack_fw_command(N230_FW_COMMS_CMD_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 1, peek_addr, [0]) in_pkt = self.send_and_recv(out_pkt) (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt, fmt) return data[0] def peek64(self, peek_addr, fmt=None): out_pkt = pack_fw_command(N230_FW_COMMS_CMD_BLOCK_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 2, peek_addr, [0,0]) in_pkt = self.send_and_recv(out_pkt) (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt, fmt) return (data[0] | (data[1] << 32)) def poke(self, poke_addr, poke_data, ack=True): ack_flag = N230_FW_COMMS_FLAGS_ACK if ack else 0 out_pkt = pack_fw_command(N230_FW_COMMS_CMD_POKE32|ack_flag, seq(), 1, poke_addr, [poke_data]) self.send(out_pkt) if ack: in_pkt = self.recv() (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt) def live_load_firmware_bin(self, bin_path): raise Exception("live_load_firmware_bin not implemented yet!") def live_load_firmware_coe(self, coe_path): with open(coe_path, 'r') as coe_file: print("Loading %s..." % coe_path) coe_lines = [line.strip(',;\n ') for line in coe_file] start_index = coe_lines.index("memory_initialization_vector=") + 1 coe_words = coe_lines[start_index:] if len(coe_words) != N230_FW_LOADER_NUM_WORDS: raise Exception("invalid COE file. Must contain 8192 words!") self.poke(N230_FW_LOADER_ADDR, 0) #Load start address for i in range(0, len(coe_words)): self.poke(N230_FW_LOADER_DATA, int(coe_words[i],16), (i 0x%x (%d)" % (addr,addr,data,data)) if options.poke is not None and options.data is not None: addr = options.poke data = options.data ctrl_sock.poke(addr,data) print("POKE[0x%x (%d)] <= 0x%x (%d)" % (addr,addr,data,data)) if options.time: ctrl_sock.read_time_stats() if options.reset: ctrl_sock.reset_fpga() if options.jesd204test: ctrl_sock.jesd204_test_connector()