diff options
Diffstat (limited to 'firmware/usrp3/n230')
-rw-r--r-- | firmware/usrp3/n230/CMakeLists.txt | 35 | ||||
-rwxr-xr-x | firmware/usrp3/n230/n230_burner.py | 359 | ||||
-rwxr-xr-x | firmware/usrp3/n230/n230_debug.py | 387 | ||||
-rw-r--r-- | firmware/usrp3/n230/n230_eeprom.c | 196 | ||||
-rw-r--r-- | firmware/usrp3/n230/n230_eth_handlers.c | 340 | ||||
-rw-r--r-- | firmware/usrp3/n230/n230_eth_handlers.h | 48 | ||||
-rw-r--r-- | firmware/usrp3/n230/n230_init.c | 125 | ||||
-rw-r--r-- | firmware/usrp3/n230/n230_init.h | 28 | ||||
-rw-r--r-- | firmware/usrp3/n230/n230_main.c | 113 |
9 files changed, 1631 insertions, 0 deletions
diff --git a/firmware/usrp3/n230/CMakeLists.txt b/firmware/usrp3/n230/CMakeLists.txt new file mode 100644 index 000000000..6247477f0 --- /dev/null +++ b/firmware/usrp3/n230/CMakeLists.txt @@ -0,0 +1,35 @@ +# +# Copyright 2010-2014,2016 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 <http://www.gnu.org/licenses/>. +# + +######################################################################## +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_SOURCE_DIR}/../../host/lib/usrp/n230) + +list(APPEND n230_sources n230_eeprom.c n230_eth_handlers.c n230_init.c n230_main.c) + +######################################################################## +set(GEN_OUTPUTS_BIN_SIZE 0x7fff) + +add_executable(n230_main.elf ${n230_sources}) +target_link_libraries(n230_main.elf usrp3fw) +GEN_OUTPUTS(n230_main.elf n230) + +#INSTALL( +# FILES ${CMAKE_CURRENT_BINARY_DIR}/n230_main.bin +# DESTINATION share/uhd/images +# RENAME usrp_n230_fw.bin +#) diff --git a/firmware/usrp3/n230/n230_burner.py b/firmware/usrp3/n230/n230_burner.py new file mode 100755 index 000000000..7b9920de7 --- /dev/null +++ b/firmware/usrp3/n230/n230_burner.py @@ -0,0 +1,359 @@ +#!/usr/bin/env python +# +# Copyright 2014 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 <http://www.gnu.org/licenses/>. +# + +import optparse +import math +import socket +import struct +import os.path +import sys +from array import array + +######################################################################## +# constants +######################################################################## +N230_FLASH_COMM_UDP_PORT = 49154 +N230_FLASH_COMM_PAYLOAD_SIZE = 128 +N230_FLASH_COMM_SECTOR_SIZE = 65536 + +N230_FLASH_COMM_FLAGS_ACK = 0x00000001 +N230_FLASH_COMM_FLAGS_CMD_MASK = 0x00000FF0 +N230_FLASH_COMM_FLAGS_ERROR_MASK = 0xFF000000 + +N230_FLASH_COMM_CMD_READ_NV_DATA = 0x00000010 +N230_FLASH_COMM_CMD_WRITE_NV_DATA = 0x00000020 +N230_FLASH_COMM_CMD_READ_FPGA = 0x00000030 +N230_FLASH_COMM_CMD_WRITE_FPGA = 0x00000040 +N230_FLASH_COMM_CMD_ERASE_FPGA = 0x00000050 + +N230_FLASH_COMM_ERR_PKT_ERROR = 0x80000000 +N230_FLASH_COMM_ERR_CMD_ERROR = 0x40000000 +N230_FLASH_COMM_ERR_SIZE_ERROR = 0x20000000 + +N230_FLASH_COMM_SAFE_IMG_BASE = 0x000000 +N230_FLASH_COMM_PROD_IMG_BASE = 0x400000 +N230_FLASH_COMM_FPGA_IMG_MAX_SIZE = 0x400000 + +UDP_MAX_XFER_BYTES = 256 +UDP_TIMEOUT = 3 + +_seq = -1 +def next_seq(): + global _seq + _seq = _seq+1 + return _seq + +def seq(): + return _seq + +######################################################################## +# helper functions +######################################################################## + +short = struct.Struct('>H') +ulong = struct.Struct('>I') + +def unpack_flash_transaction(buf): + (flags, seqno, offset, size) = struct.unpack_from('!LLLL', buf) + check_error(flags) + if (seqno != seq()): + raise Exception("The flash transaction operation returned an incorrect sequence number") + data = bytes() + for i in xrange(16, len(buf), 1): + data += buf[i] + return (flags, offset, size, data) + +def pack_flash_transaction(flags, offset, size, data): + buf = bytes() + buf = struct.pack('!LLLL', flags, next_seq(), offset, size) + for i in range(N230_FLASH_COMM_PAYLOAD_SIZE): + if (i < size): + buf += struct.pack('!B', data[i]) + else: + buf += struct.pack('!B', 0) + return buf + +def check_error(flags): + if flags & N230_FLASH_COMM_ERR_PKT_ERROR == N230_FLASH_COMM_ERR_PKT_ERROR: + raise Exception("The flash transaction operation returned a packet error") + if flags & N230_FLASH_COMM_ERR_CMD_ERROR == N230_FLASH_COMM_ERR_CMD_ERROR: + raise Exception("The flash transaction operation returned a command error") + if flags & N230_FLASH_COMM_ERR_SIZE_ERROR == N230_FLASH_COMM_ERR_SIZE_ERROR: + raise Exception("The flash transaction 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() + +######################################################################## +# Burner class, holds a socket and send/recv routines +######################################################################## +class ctrl_socket(object): + def __init__(self, addr): + self._safe_image = False + self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self._sock.settimeout(UDP_TIMEOUT) + self._sock.connect((addr, N230_FLASH_COMM_UDP_PORT)) + self.set_callbacks(lambda *a: None, lambda *a: None) + + def set_safe_image(self, noprompt): + confirm_msg = ('----------------------------------------------------------------------\n' + 'WARNING!!! You are about to access the safe-image stored in the flash \n' + '----------------------------------------------------------------------\n' + 'Writing a non-functional image will brick the device.\n' + 'Are you sure you want to proceed?') + if not noprompt: + if raw_input("%s (y/N) " % confirm_msg).lower() == 'y': + self._safe_image = True + else: + print 'Aborted by user' + sys.exit(1) + else: + print '[WARNING] Operating on safe image without a prompt as requested' + self._safe_image = True + + def set_callbacks(self, progress_cb, status_cb): + self._progress_cb = progress_cb + self._status_cb = status_cb + + def send_and_recv(self, pkt): + self._sock.send(pkt) + return self._sock.recv(UDP_MAX_XFER_BYTES) + + def compute_offset(self, offset): + base = N230_FLASH_COMM_SAFE_IMG_BASE if (self._safe_image) else N230_FLASH_COMM_PROD_IMG_BASE + return base + offset + + def burn_fpga_to_flash(self, bitfile_path, noprompt): + print '[BURN] Reading ' + bitfile_path + '...' + with open(bitfile_path, 'rb') as bitfile: + header = file_bytes = bitfile.read() + if (self._safe_image != self.parse_bitfile_header(file_bytes)['safe-image']): + confirm_msg = ('----------------------------------------------------------------------\n' + 'WARNING!!! You are about to burn a safe image into a production slot \n' + ' or a production image into a safe slot. \n' + '----------------------------------------------------------------------\n' + 'This is dangerous and can cause the device to boot incorrectly.\n' + 'Are you sure you want to proceed?') + if not noprompt: + if raw_input("%s (y/N) " % confirm_msg).lower() != 'y': + print '[BURN] Aborted by user' + return + else: + print '[WARNING] Burning image to the wrong slot without a prompt as requested' + + print '[BURN] Writing to flash...' + pkt_chunks = chunkify(file_bytes, N230_FLASH_COMM_PAYLOAD_SIZE) + offset = 0 + for pkt_data in pkt_chunks: + pkt_data = array("B", pkt_data) + size = N230_FLASH_COMM_PAYLOAD_SIZE if (len(pkt_data) >= N230_FLASH_COMM_PAYLOAD_SIZE) else len(pkt_data) + #Erase sector + if (offset % N230_FLASH_COMM_SECTOR_SIZE == 0): + flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA + out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, pkt_data) + (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) + #Write data + flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_FPGA + out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, pkt_data) + (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) + #Increment + offset += N230_FLASH_COMM_PAYLOAD_SIZE + draw_progress_bar((((offset/N230_FLASH_COMM_PAYLOAD_SIZE)+1)*100)/len(pkt_chunks)) + print('\n[BURN] DONE') + + def parse_bitfile_header(self, header_bytes): + xil_header = dict() + n230_header = dict() + n230_header['valid'] = False + ptr = 0 + #Field 1 + if short.unpack(header_bytes[ptr:ptr+2])[0] == 9 and ulong.unpack(header_bytes[ptr+2:ptr+6])[0] == 0x0ff00ff0: + #Headers + ptr += short.unpack(header_bytes[ptr:ptr+2])[0] + 2 + ptr += short.unpack(header_bytes[ptr:ptr+2])[0] + 1 + #Fields a-d + for keynum in range(0, 4): + key = header_bytes[ptr] + ptr += 1 + val_len = short.unpack(header_bytes[ptr:ptr+2])[0] + ptr += 2 + val = header_bytes[ptr:ptr+val_len] + ptr += val_len + xil_header[key] = val + #Field e + ptr += 1 + length = ulong.unpack(header_bytes[ptr:ptr+4])[0] + xil_header['bl'] = length #Bitstream length + ptr += 4 + xil_header['hl'] = ptr #Header lengt + + #Map Xilinx header field to N230 specific ones + if xil_header and xil_header['a'].split(';')[0] == 'n230': + n230_header['valid'] = True + n230_header['user-id'] = int(xil_header['a'].split(';')[1].split('=')[1], 16) + n230_header['safe-image'] = (n230_header['user-id'] >> 16 == 0x5AFE) + n230_header['product'] = xil_header['b'] + n230_header['timestamp'] = xil_header['c'] + ' ' + xil_header['d'] + n230_header['filesize'] = xil_header['hl'] + xil_header['bl'] + return n230_header + + def read_bitfile_header_from_flash(self): + max_header_size = 1024 #Should be enough + header_bytes = bytes() + for offset in range(0, max_header_size, N230_FLASH_COMM_PAYLOAD_SIZE): + #Read data + flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_READ_FPGA + out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), N230_FLASH_COMM_PAYLOAD_SIZE, [0]*N230_FLASH_COMM_PAYLOAD_SIZE) + (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) + header_bytes += data + return self.parse_bitfile_header(header_bytes) + + def extract_fpga_from_flash(self, bitfile_path): + header = self.read_bitfile_header_from_flash(); + if not header['valid']: + raise Exception("Could not detect a vaild Xilinx .bit burned into the flash") + max_offset = header['filesize'] + print '[EXTRACT] Writing ' + bitfile_path + '...' + with open(bitfile_path, 'wb') as bitfile: + for i in range(0, int(math.ceil(float(max_offset)/N230_FLASH_COMM_PAYLOAD_SIZE))): + offset = i * N230_FLASH_COMM_PAYLOAD_SIZE + size = N230_FLASH_COMM_PAYLOAD_SIZE if (max_offset - offset >= N230_FLASH_COMM_PAYLOAD_SIZE) else (max_offset - offset) + #Read data + flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_READ_FPGA + out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, [0]*N230_FLASH_COMM_PAYLOAD_SIZE) + (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) + bitfile.write(data[:size]) + draw_progress_bar(((offset*100)/max_offset) + 1) + print('\n[EXTRACT] DONE') + + def erase_fpga_from_flash(self): + print '[ERASE] Erasing image from flash...' + for offset in range(0, N230_FLASH_COMM_FPGA_IMG_MAX_SIZE, N230_FLASH_COMM_SECTOR_SIZE): + flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA + out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), N230_FLASH_COMM_PAYLOAD_SIZE, [0]*N230_FLASH_COMM_PAYLOAD_SIZE) + (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) + draw_progress_bar(((offset+N230_FLASH_COMM_SECTOR_SIZE)*100)/N230_FLASH_COMM_FPGA_IMG_MAX_SIZE) + print('\n[ERASE] DONE') + + def wipe_user_data(self, noprompt): + confirm_msg = ('-------------------------------------------------------------------\n' + 'WARNING!!! You are about to erase all the user data from the flash \n' + '-------------------------------------------------------------------\n' + 'This will cause the device to lose the following:\n' + ' * IP Address (Will default to 192.168.10.2)\n' + ' * Subnet Mask (Will default to 255.255.255.2)\n' + ' * MAC Address\n' + ' * Serial Number\n' + ' * Hardware Revision\n' + ' * ...and other identification info\n' + 'Are you sure you want to proceed?') + if not noprompt: + if raw_input("%s (y/N) " % confirm_msg).lower() == 'y': + wipe_ok = True + else: + print '[WIPE] Aborted by user' + wipe_ok = False + else: + print '[WARNING] Wiping user data without prompt a as requested' + wipe_ok = True + + if wipe_ok: + print '[WIPE] Erasing all user data from flash...' + flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_NV_DATA + out_pkt = pack_flash_transaction(flags, 0, N230_FLASH_COMM_PAYLOAD_SIZE, [0xFF]*N230_FLASH_COMM_PAYLOAD_SIZE) + (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) + print('[WIPE] DONE. Please power-cycle the device.') + + def print_status(self): + header = self.read_bitfile_header_from_flash(); + if header['valid']: + print('[STATUS] Detected a valid .bit header in the flash (Product = %s, Datestamp = %s%s)' % \ + (header['product'], header['timestamp'], ', Safe-Image' if header['safe-image'] else '')) + else: + print('[STATUS] No .bit header detected. Either the flash is uninitialized or the image is corrupt.') + + +######################################################################## +# command line options +######################################################################## +def get_options(): + parser = optparse.OptionParser() + parser.add_option("--addr", type="string", help="N230 device address", default='') + parser.add_option("--status", action="store_true", help="Print out the status of the burned image", default=False) + parser.add_option("--erase", action="store_true", help="Erase FPGA bitstream from flash", default=False) + parser.add_option("--burn", type="string", help="Path to FPGA bitstream (.bit) to burn to flash", default=None) + parser.add_option("--extract", type="string", help="Destination bitfile to dump contents of the extracted image", default=None) + parser.add_option("--safe_image", action="store_true", help="Operate on the safe image. WARNING: This could be dangerous", default=False) + parser.add_option("--wipe_user_data", action="store_true", help="Erase all user data like IP, MAC, S/N, etc from flash", default=False) + parser.add_option("--no_prompt", action="store_true", help="Suppress all warning prompts", default=False) + (options, args) = parser.parse_args() + return options + +######################################################################## +# main +######################################################################## +if __name__=='__main__': + options = get_options() + + if not options.addr: raise Exception('No address specified') + + ctrl_sock = ctrl_socket(addr=options.addr) + + # Initialize safe image selector first + if options.safe_image: + ctrl_sock.set_safe_image(options.no_prompt) + + if options.status: + ctrl_sock.print_status() + + # Order of operations: + # 1. Extract (if specified) + # 2. Erase (if specified) + # 3. Burn (if specified) + + if options.extract is not None: + file_path = options.extract + ctrl_sock.print_status() + ctrl_sock.extract_fpga_from_flash(file_path) + + if options.erase: + ctrl_sock.erase_fpga_from_flash() + ctrl_sock.print_status() + + if options.burn is not None: + file_path = options.burn + extension = os.path.splitext(file_path)[1] + if (extension.lower() == '.bit'): + ctrl_sock.burn_fpga_to_flash(file_path, options.no_prompt) + ctrl_sock.print_status() + else: + raise Exception("Unsupported FPGA bitfile format. You must use a .bit file.") + + if options.wipe_user_data: + ctrl_sock.wipe_user_data(options.no_prompt) diff --git a/firmware/usrp3/n230/n230_debug.py b/firmware/usrp3/n230/n230_debug.py new file mode 100755 index 000000000..f9ff64ab7 --- /dev/null +++ b/firmware/usrp3/n230/n230_debug.py @@ -0,0 +1,387 @@ +#!/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 <http://www.gnu.org/licenses/>. +# + +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) + if len(in_pkt) < 20: + continue + (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, port): + self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self._sock.settimeout(UDP_TIMEOUT) + self._sock.connect((addr, port)) + self.set_callbacks(lambda *a: None, lambda *a: None) + self.peek(0) #Dummy read + + 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 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%10==0) and (i<len(coe_words)-1)) + draw_progress_bar(((i+1)*100)/len(coe_words)) + print("\nRebooting...") + out_pkt = pack_fw_command(N230_FW_COMMS_CMD_POKE32, seq(), 1, N230_FW_LOADER_BOOT_DONE_ADDR, [1]) + self._sock.send(out_pkt) + self._sock.settimeout(1) + out_pkt = pack_fw_command(N230_FW_COMMS_CMD_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 1, 0, [0]) + for i in range(N230_FW_LOADER_BOOT_TIMEOUT): + try: + self._sock.send(out_pkt) + in_pkt = self._sock.recv(UDP_MAX_XFER_BYTES) + print("Firmware is alive!") + self._sock.settimeout(UDP_TIMEOUT) + return + except socket.error: + pass + print("Firmware boot FAILED!!!") + self._sock.settimeout(UDP_TIMEOUT) + + def read_hash(self): + fpga_hash = self.peek(N230_FPGA_HASH_ADDR) + fpga_status = "clean" if (fpga_hash & 0xf0000000 == 0x0) else "modified" + fw_hash = self.peek(N230_FW_HASH_ADDR) + fw_status = "clean" if (fw_hash & 0xf0000000 == 0x0) else "modified" + print("FPGA Version : %x (%s)" % (fpga_hash & 0xfffffff, fpga_status)) + print("Firmware Version : %x (%s)" % (fw_hash & 0xfffffff, fw_status)) + + def is_claimed(self): + claimed = self.peek(0x10008) + print("Claimed : %s") % ('YES' if claimed else 'NO') + + def reset_fpga(self): + print("Reseting USRP...") + ctrl_sock.poke(N230_ICAP_ADDR,ICAP_DUMMY_WORD) + ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP) + ctrl_sock.poke(N230_ICAP_ADDR,ICAP_SYNC_WORD) + ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP) + ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WRITE_WBSTAR) + ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WBSTAR_ADDR) + ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP) + ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WRITE_CMD) + ctrl_sock.poke(N230_ICAP_ADDR,ICAP_IPROG_CMD, False) + print("Waiting for FPGA to load...") + self._sock.settimeout(1) + out_pkt = pack_fw_command(N230_FW_COMMS_CMD_ECHO|N230_FW_COMMS_FLAGS_ACK, seq(), 1, 0, [0]) + for i in range(FPGA_LOAD_TIMEOUT): + try: + in_pkt = self.send_and_recv(out_pkt) + (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt) + print("FPGA loaded successfully.") + self._sock.settimeout(UDP_TIMEOUT) + return + except socket.error: + pass + print("FPGA load FAILED!!!") + self._sock.settimeout(UDP_TIMEOUT) + + def jesd204_test_connector(self): + print("Testing JESD204 connectors. Molex cable #79576-2102 must be connected") + ctrl_sock.poke(N230_JESD204_TEST,0x1) + while True: + jesd204_test_status = ctrl_sock.peek(N230_JESD204_TEST) + if (jesd204_test_status & 0x10000 == 0x10000): + break + ctrl_sock.poke(N230_JESD204_TEST,0x0) + if (jesd204_test_status & 0xFFFF != 0x0): + print("JESD204 loopback test Failed!: Returned status is %4x" % (jesd204_test_status & 0xFFFF)) + else: + print("JESD204 loopback test Passed.") + +######################################################################## +# command line options +######################################################################## +def get_options(): + parser = optparse.OptionParser() + parser.add_option("--discover", action="store_true",help="Find all devices connected N230 devices", default=False) + parser.add_option("--addr", type="string", help="N230 device address", default='') + parser.add_option("--peek", type="int", help="Read from memory map", default=None) + parser.add_option("--poke", type="int", help="Write to memory map", default=None) + parser.add_option("--data", type="int", help="Data for poke", default=None) + parser.add_option("--fw", type="string", help="Path to FW image to load", default=None) + parser.add_option("--hash", action="store_true",help="Display FPGA git hash", default=False) + parser.add_option("--reset", action="store_true",help="Reset and Reload USRP FPGA.", default=False) + parser.add_option("--jesd204test", action="store_true",help="Test mini-SAS connectors with loopback cable..", default=False) + + (options, args) = parser.parse_args() + return options + +######################################################################## +# main +######################################################################## +if __name__=='__main__': + options = get_options() + + if options.discover: + if N230_DEVICE_DISCOVERY_AVAILABLE: + disc_sock = discovery_socket() + for addr in disc_sock.discover(): + print '==== FOUND ' + addr + ' ====' + ctrl_sock = ctrl_socket(addr, N230_FW_COMMS_UDP_PORT) + ctrl_sock.read_hash() + ctrl_sock.is_claimed() + sys.exit() + else: + raise Exception('Discovery is only supported on Linux.') + + if not options.addr: + raise Exception('No address specified') + + ctrl_sock = ctrl_socket(options.addr, N230_FW_COMMS_UDP_PORT) + + if options.fw is not None: + file_path = options.fw + extension = os.path.splitext(file_path)[1] + if (extension.lower() == '.coe'): + ctrl_sock.live_load_firmware_coe(file_path) + elif (extension.lower() == '.bin'): + ctrl_sock.live_load_firmware_bin(file_path) + else: + raise Exception("Unsupported firmware file format") + + if options.hash: + ctrl_sock.read_hash() + + if options.peek is not None: + addr = options.peek + data = ctrl_sock.peek(addr) + print("PEEK[0x%x (%d)] => 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.reset: + ctrl_sock.reset_fpga() + + if options.jesd204test: + ctrl_sock.jesd204_test_connector() diff --git a/firmware/usrp3/n230/n230_eeprom.c b/firmware/usrp3/n230/n230_eeprom.c new file mode 100644 index 000000000..8f756d41f --- /dev/null +++ b/firmware/usrp3/n230/n230_eeprom.c @@ -0,0 +1,196 @@ +// +// Copyright 2014 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 <http://www.gnu.org/licenses/>. +// + +#include "../../../host/lib/usrp/n230/n230_eeprom.h" + +#include <trace.h> +#include <stddef.h> +#include <flash/spi_flash.h> +#include <flash/spif_spsn_s25flxx.h> +#include <string.h> //memcpy + +#include "../../../host/lib/usrp/n230/n230_fw_defs.h" +#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h" + +static const wb_spi_slave_t flash_spi_slave = { + .base = (void*) 0xB000, + .slave_sel = 0x0001, + .clk_div = 4, //80MHz/4 = 20MHz + .mosi_edge = RISING, + .miso_edge = FALLING, + .lsb_first = false +}; + +static const spi_flash_dev_t spi_flash_device = { + .page_size = 256, + .sector_size = 65536, + .num_sectors = 254, + .bus = &flash_spi_slave +}; + +/*********************************************************************** + * Non-volatile device data + **********************************************************************/ +#define N230_FLASH_NV_DATA_OFFSET 0x800000 + +//Default values in case the EEPROM is not read, corrupt +const n230_eeprom_map_t default_eeprom = { + .data_version_major = N230_EEPROM_VER_MAJOR, + .data_version_minor = N230_EEPROM_VER_MINOR, + .hw_revision = 0, + .hw_product = 0x01, + .gateway = N230_DEFAULT_GATEWAY, + .eth_info = { + { //eth0 + .mac_addr = N230_DEFAULT_ETH0_MAC, + .subnet = N230_DEFAULT_ETH0_MASK, + .ip_addr = N230_DEFAULT_ETH0_IP + }, + { //eth1 + .mac_addr = N230_DEFAULT_ETH1_MAC, + .subnet = N230_DEFAULT_ETH1_MASK, + .ip_addr = N230_DEFAULT_ETH1_IP + } + } +}; + +//EEPROM cache +static spi_flash_session_t flash_session = {.device = NULL}; +static n230_eeprom_map_t eeprom_cache; +static bool cache_dirty = true; + +bool read_n230_eeprom() +{ + bool status = false; + if (flash_session.device == NULL) { //Initialize flash session structure for the first time + wb_spi_init(spi_flash_device.bus); + spif_init(&flash_session, &spi_flash_device, spif_spsn_s25flxx_operations()); + } + spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t)); + + //Verify data format + status = (eeprom_cache.data_version_major == default_eeprom.data_version_major); + //Sanity communication info + if (eeprom_cache.eth_info[0].ip_addr == 0xFFFFFFFF) + eeprom_cache.eth_info[0].ip_addr = default_eeprom.eth_info[0].ip_addr; + if (eeprom_cache.eth_info[1].ip_addr == 0xFFFFFFFF) + eeprom_cache.eth_info[1].ip_addr = default_eeprom.eth_info[1].ip_addr; + if (eeprom_cache.eth_info[0].subnet == 0xFFFFFFFF) + eeprom_cache.eth_info[0].subnet = default_eeprom.eth_info[0].subnet; + if (eeprom_cache.eth_info[1].subnet == 0xFFFFFFFF) + eeprom_cache.eth_info[1].subnet = default_eeprom.eth_info[1].subnet; + + if (!status) { + UHD_FW_TRACE(WARN, "read_n230_eeprom: Initialized cache to the default map."); + memcpy(&eeprom_cache, &default_eeprom, sizeof(n230_eeprom_map_t)); + } + cache_dirty = !status; + return status; +} + +bool write_n230_eeprom() +{ + //Assumption: sizeof(n230_eeprom_map_t) <= flash_page_size + //This function would need to be reimplemented if this assumption is no longer true + if (sizeof(n230_eeprom_map_t) > flash_session.device->page_size) { + UHD_FW_TRACE(ERROR, "write_n230_eeprom: sizeof(n230_eeprom_map_t) > flash_page_size"); + return false; + } + + bool status = true; + if (cache_dirty) { + n230_eeprom_map_t device_eeprom; + spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &device_eeprom, sizeof(n230_eeprom_map_t)); + if (memcmp(&eeprom_cache, &device_eeprom, sizeof(n230_eeprom_map_t)) != 0) { + //Cache does not match read state. Write. + UHD_FW_TRACE(DEBUG, "write_n230_eeprom: Writing data to flash..."); + status = spif_erase_sector_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET); + if (status) { + status = spif_write_page_sync( + &flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t)); + } + if (!status) { + UHD_FW_TRACE(ERROR, "write_n230_eeprom: Operation failed!"); + } + cache_dirty = !status; + } else { + UHD_FW_TRACE(DEBUG, "write_n230_eeprom: No new data. Write skipped."); + //Cache matches read state. So mark as clean + cache_dirty = false; + } + } + return status; +} + +bool is_n230_eeprom_cache_dirty() +{ + return cache_dirty; +} + +n230_eeprom_map_t* get_n230_eeprom_map() +{ + cache_dirty = true; + return &eeprom_cache; +} + +const n230_eeprom_map_t* get_n230_const_eeprom_map() +{ + return &eeprom_cache; +} + +const n230_eth_eeprom_map_t* get_n230_ethernet_info(uint32_t iface) { + if (iface >= N230_NUM_ETH_PORTS) { + UHD_FW_TRACE_FSTR(ERROR, + "get_n230_ethernet_info called with iface=%d when there are only %d ports!!!", + iface, N230_NUM_ETH_PORTS); + } + return &(get_n230_const_eeprom_map()->eth_info[iface]); +} + + +/*********************************************************************** + * Storage for bootstrap FPGA Image + **********************************************************************/ +#define N230_FLASH_FPGA_IMAGE_OFFSET 0x000000 +#define N230_FLASH_FPGA_IMAGE_SIZE 0x400000 +#define N230_FLASH_NUM_FPGA_IMAGES 2 + +void read_n230_fpga_image_page(uint32_t offset, void *buf, uint32_t num_bytes) +{ + if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) { + UHD_FW_TRACE_FSTR(ERROR, "read_n230_fpga_image_page: Offset 0x%x out of bounds", offset); + } + spif_read_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes); +} + +bool write_n230_fpga_image_page(uint32_t offset, const void *buf, uint32_t num_bytes) +{ + if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) { + UHD_FW_TRACE_FSTR(ERROR, "write_n230_fpga_image_page: Offset 0x%x out of bounds", offset); + return false; + } + return spif_write_page_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes); +} + +bool erase_n230_fpga_image_sector(uint32_t offset) +{ + if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) { + UHD_FW_TRACE_FSTR(ERROR, "erase_n230_fpga_image_sector: Offset 0x%x out of bounds", offset); + return false; + } + return spif_erase_sector_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset); +} diff --git a/firmware/usrp3/n230/n230_eth_handlers.c b/firmware/usrp3/n230/n230_eth_handlers.c new file mode 100644 index 000000000..b291bb39f --- /dev/null +++ b/firmware/usrp3/n230/n230_eth_handlers.c @@ -0,0 +1,340 @@ +// +// Copyright 2014 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 <http://www.gnu.org/licenses/>. +// + +#include "n230_eth_handlers.h" + +#include <wb_utils.h> +#include <string.h> //memcmp +#include <u3_net_stack.h> +#include <print_addrs.h> +#include <trace.h> +#include "../../../host/lib/usrp/common/fw_comm_protocol.h" +#include "../../../host/lib/usrp/n230/n230_fw_defs.h" +#include "../n230/n230_fw_host_iface.h" +#include "../../../host/lib/usrp/n230/n230_eeprom.h" + +static n230_host_shared_mem_t* host_shared_mem_ptr; + +static const soft_reg_field_t LED_REG_FIELD_ETH_LINK2 = {.num_bits=1, .shift=0}; +static const soft_reg_field_t LED_REG_FIELD_ETH_LINK1 = {.num_bits=1, .shift=1}; +static const soft_reg_field_t LED_REG_FIELD_ETH_ACT2 = {.num_bits=1, .shift=2}; +static const soft_reg_field_t LED_REG_FIELD_ETH_ACT1 = {.num_bits=1, .shift=3}; + +/*********************************************************************** + * Handler for host <-> firmware communication + **********************************************************************/ + +static inline void n230_poke32(const uint32_t addr, const uint32_t data) +{ + if (addr >= N230_FW_HOST_SHMEM_RW_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) { + host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)] = data; + } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) { + wb_poke32(addr, data); + } +} + +static inline uint32_t n230_peek32(const uint32_t addr) +{ + if (addr >= N230_FW_HOST_SHMEM_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) { + return host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)]; + } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) { + return wb_peek32(addr); + } else { + return 0; + } +} + +void n230_handle_udp_fw_comms( + const uint8_t ethno, + const struct ip_addr *src, const struct ip_addr *dst, + const uint16_t src_port, const uint16_t dst_port, + const void *buff, const size_t num_bytes) +{ + if (buff == NULL) { + UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an ICMP_DUR"); + /* We got here from ICMP_DUR undeliverable packet */ + /* Future space for hooks to tear down streaming radios etc */ + } else if (num_bytes != sizeof(fw_comm_pkt_t)) { + UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an unknown request (bad size)."); + } else { + const fw_comm_pkt_t *request = (const fw_comm_pkt_t *)buff; + fw_comm_pkt_t response; + bool send_response = process_fw_comm_protocol_pkt( + request, &response, + N230_FW_PRODUCT_ID, + (uint32_t)ethno, + n230_poke32, n230_peek32); + + if (send_response) { + u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response)); + } + } +} + +void n230_register_udp_fw_comms_handler(n230_host_shared_mem_t* shared_mem_ptr) +{ + host_shared_mem_ptr = shared_mem_ptr; + u3_net_stack_register_udp_handler(N230_FW_COMMS_UDP_PORT, &n230_handle_udp_fw_comms); +} + + +/*********************************************************************** + * Handler for UDP framer program packets + **********************************************************************/ +void program_udp_framer( + const uint8_t ethno, + const uint32_t sid, + const struct ip_addr *dst_ip, + const uint16_t dst_port, + const uint16_t src_port) +{ + const eth_mac_addr_t *dst_mac = u3_net_stack_arp_cache_lookup(dst_ip); + const size_t vdest = (sid >> 16) & 0xff; + + uint32_t framer_base = + ((ethno == 1) ? SR_ZPU_ETHINT1 : SR_ZPU_ETHINT0) + SR_ZPU_ETHINT_FRAMER_BASE; + + //setup source framer + const eth_mac_addr_t *src_mac = u3_net_stack_get_mac_addr(ethno); + wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_HI), + (((uint32_t)src_mac->addr[0]) << 8) | (((uint32_t)src_mac->addr[1]) << 0)); + wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_LO), + (((uint32_t)src_mac->addr[2]) << 24) | (((uint32_t)src_mac->addr[3]) << 16) | + (((uint32_t)src_mac->addr[4]) << 8) | (((uint32_t)src_mac->addr[5]) << 0)); + wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_IP_ADDR), u3_net_stack_get_ip_addr(ethno)->addr); + wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_UDP_PORT), src_port); + + //setup destination framer + wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_RAM_ADDR), vdest); + wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_IP_ADDR), dst_ip->addr); + wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_UDP_MAC), + (((uint32_t)dst_port) << 16) | + (((uint32_t)dst_mac->addr[0]) << 8) | (((uint32_t)dst_mac->addr[1]) << 0)); + wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_MAC_LO), + (((uint32_t)dst_mac->addr[2]) << 24) | (((uint32_t)dst_mac->addr[3]) << 16) | + (((uint32_t)dst_mac->addr[4]) << 8) | (((uint32_t)dst_mac->addr[5]) << 0)); +} + +void handle_udp_prog_framer( + const uint8_t ethno, + const struct ip_addr *src, const struct ip_addr *dst, + const uint16_t src_port, const uint16_t dst_port, + const void *buff, const size_t num_bytes) +{ + if (buff == NULL) { + /* We got here from ICMP_DUR undeliverable packet */ + /* Future space for hooks to tear down streaming radios etc */ + } else { + const uint32_t sid = ((const uint32_t *)buff)[1]; + program_udp_framer(ethno, sid, src, src_port, dst_port); + UHD_FW_TRACE_FSTR(INFO, "Reprogrammed eth%d framer. Src=%s:%d, Dest=%s:%d", + ethno,ip_addr_to_str(src),src_port,ip_addr_to_str(dst),dst_port); + } +} + +void n230_register_udp_prog_framer() +{ + u3_net_stack_register_udp_handler(N230_FW_COMMS_CVITA_PORT, &handle_udp_prog_framer); +} + + +/*********************************************************************** + * Handler for flash programming interface over UDP + **********************************************************************/ + +void n230_handle_flash_prog_comms( + const uint8_t ethno, + const struct ip_addr *src, const struct ip_addr *dst, + const uint16_t src_port, const uint16_t dst_port, + const void *buff, const size_t num_bytes) +{ + if (buff == NULL) { + UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an ICMP_DUR"); + /* We got here from ICMP_DUR undeliverable packet */ + /* Future space for hooks to tear down streaming radios etc */ + } else if (num_bytes != sizeof(n230_flash_prog_t)) { + UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an unknown request (bad size)."); + } else { + const n230_flash_prog_t *request = (const n230_flash_prog_t *)buff; + n230_flash_prog_t response; + bool ack_requested = request->flags & N230_FLASH_COMM_FLAGS_ACK; + + //Request is valid. Copy it into the reply. + memcpy(&response, request, sizeof(n230_flash_prog_t)); + + switch (request->flags & N230_FLASH_COMM_FLAGS_CMD_MASK) { + case N230_FLASH_COMM_CMD_READ_NV_DATA: { + UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::read_nv_data()"); + //Offset ignored because all non-volatile data fits in a packet. + if (is_n230_eeprom_cache_dirty()) { + read_n230_eeprom(); + } + //EEPROM cache is up-to-date. Copy it into the packet. + //Assumption: Cache size < 256. If this is no longer true, the offset field + //will have to be used. + memcpy(response.data, get_n230_const_eeprom_map(), sizeof(n230_eeprom_map_t)); + ack_requested = true; + } break; + + case N230_FLASH_COMM_CMD_WRITE_NV_DATA: { + UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::write_nv_data()"); + //Offset ignored because all non-volatile data fits in a packet. + memcpy(get_n230_eeprom_map(), request->data, sizeof(n230_eeprom_map_t)); + if (!write_n230_eeprom()) { + response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR; + } + } break; + + case N230_FLASH_COMM_CMD_READ_FPGA: { + UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::read_fpga_page(offset=0x%x, size=%d)", + request->offset, request->size); + read_n230_fpga_image_page(request->offset, response.data, request->size); + ack_requested = true; + } break; + + case N230_FLASH_COMM_CMD_WRITE_FPGA: { + UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::write_fpga_page(offset=0x%x, size=%d)", + request->offset, request->size); + if (!write_n230_fpga_image_page(request->offset, request->data, request->size)) { + response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR; + } + } break; + + case N230_FLASH_COMM_CMD_ERASE_FPGA: { + UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::erase_fpga_sector(offset=0x%x)", + request->offset); + if (!erase_n230_fpga_image_sector(request->offset)) { + response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR; + } + } break; + + default :{ + UHD_FW_TRACE(ERROR, "n230_handle_flash_prog_comms got an invalid command."); + response.flags |= FW_COMM_ERR_CMD_ERROR; + } + } + //Send a reply if ack requested + if (ack_requested) { + u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response)); + } + } +} + +void n230_register_flash_comms_handler() +{ + u3_net_stack_register_udp_handler(N230_FW_COMMS_FLASH_PROG_PORT, &n230_handle_flash_prog_comms); +} + +/*********************************************************************** + * Handler for SFP state changes + **********************************************************************/ +#define SFPP_STATUS_MODABS_CHG (1 << 5) // Has MODABS changed since last read? +#define SFPP_STATUS_TXFAULT_CHG (1 << 4) // Has TXFAULT changed since last read? +#define SFPP_STATUS_RXLOS_CHG (1 << 3) // Has RXLOS changed since last read? +#define SFPP_STATUS_MODABS (1 << 2) // MODABS state +#define SFPP_STATUS_TXFAULT (1 << 1) // TXFAULT state +#define SFPP_STATUS_RXLOS (1 << 0) // RXLOS state + +static bool links_up[N230_MAX_NUM_ETH_PORTS] = {}; +static uint32_t packet_count[N230_MAX_NUM_ETH_PORTS] = {}; + +void n230_poll_sfp_status(const uint32_t eth, bool force, bool* state_updated) +{ + // Has MODDET/MODAbS changed since we last looked? + uint32_t rb = wb_peek32(SR_ADDR(WB_SBRB_BASE, (eth==0) ? RB_ZPU_SFP_STATUS0 : RB_ZPU_SFP_STATUS1)); + + if (rb & SFPP_STATUS_RXLOS_CHG) + UHD_FW_TRACE_FSTR(DEBUG, "eth%1d RXLOS changed state: %d", eth, (rb & SFPP_STATUS_RXLOS)); + if (rb & SFPP_STATUS_TXFAULT_CHG) + UHD_FW_TRACE_FSTR(DEBUG, "eth%1d TXFAULT changed state: %d", eth, ((rb & SFPP_STATUS_TXFAULT) >> 1)); + if (rb & SFPP_STATUS_MODABS_CHG) + UHD_FW_TRACE_FSTR(DEBUG, "eth%1d MODABS changed state: %d", eth, ((rb & SFPP_STATUS_MODABS) >> 2)); + + //update the link up status + if ((rb & SFPP_STATUS_RXLOS_CHG) || (rb & SFPP_STATUS_TXFAULT_CHG) || (rb & SFPP_STATUS_MODABS_CHG) || force) + { + const bool old_link_up = links_up[eth]; + const uint32_t status_reg_addr = (eth==0) ? RB_ZPU_SFP_STATUS0 : RB_ZPU_SFP_STATUS1; + + uint32_t sfpp_status = wb_peek32(SR_ADDR(WB_SBRB_BASE, status_reg_addr)) & 0xFFFF; + if ((sfpp_status & (SFPP_STATUS_RXLOS|SFPP_STATUS_TXFAULT|SFPP_STATUS_MODABS)) == 0) { + int8_t timeout = 100; + bool link_up = false; + do { + link_up = ((wb_peek32(SR_ADDR(WB_SBRB_BASE, status_reg_addr)) >> 16) & 0x1) != 0; + } while (!link_up && timeout-- > 0); + + links_up[eth] = link_up; + } else { + links_up[eth] = false; + } + + if (!old_link_up && links_up[eth]) u3_net_stack_send_arp_request(eth, u3_net_stack_get_ip_addr(eth)); + UHD_FW_TRACE_FSTR(INFO, "The link on eth port %u is %s", eth, links_up[eth]?"up":"down"); + if (rb & SFPP_STATUS_MODABS_CHG) { + // MODDET has changed state since last checked + if (rb & SFPP_STATUS_MODABS) { + // MODDET is high, module currently removed. + UHD_FW_TRACE_FSTR(INFO, "An SFP+ module has been removed from eth port %d.", eth); + } else { + // MODDET is low, module currently inserted. + // Return status. + UHD_FW_TRACE_FSTR(INFO, "A new SFP+ module has been inserted into eth port %d.", eth); + } + } + *state_updated = true; + } else { + *state_updated = false; + } +} + +void n230_update_link_act_state(soft_reg_t* led_reg) +{ + static bool first_poll = 1; + static uint32_t poll_cnt; + + bool activity[N230_MAX_NUM_ETH_PORTS] = {}; + for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) { + if (first_poll) { + links_up[i] = 0; + packet_count[i] = 0; + poll_cnt = 0; + } + + //Check SFP status and update links_up + bool link_state_from_sfp = false; + n230_poll_sfp_status(i, first_poll, &link_state_from_sfp); + + //Check packet counters less frequently to keep the LED on for a visible duration + uint32_t cnt = wb_peek32(SR_ADDR(WB_SBRB_BASE, (i==0)?RB_ZPU_ETH0_PKT_CNT:RB_ZPU_ETH1_PKT_CNT)); + activity[i] = (cnt != packet_count[i]); + packet_count[i] = cnt; + + //Update links_up if there is activity only if the SFP + //handler has not updated it + if (activity[i] && !link_state_from_sfp) links_up[i] = true; + } + + //TODO: Swap this when Ethernet port swap issues is fixed + soft_reg_write(led_reg, LED_REG_FIELD_ETH_LINK2, links_up[0]?1:0); + soft_reg_write(led_reg, LED_REG_FIELD_ETH_LINK1, links_up[1]?1:0); + soft_reg_write(led_reg, LED_REG_FIELD_ETH_ACT2, activity[0]?1:0); + soft_reg_write(led_reg, LED_REG_FIELD_ETH_ACT1, activity[1]?1:0); + + first_poll = 0; +} + diff --git a/firmware/usrp3/n230/n230_eth_handlers.h b/firmware/usrp3/n230/n230_eth_handlers.h new file mode 100644 index 000000000..67afbb246 --- /dev/null +++ b/firmware/usrp3/n230/n230_eth_handlers.h @@ -0,0 +1,48 @@ +// +// Copyright 2014 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 <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_ETH_HANDLERS_H +#define INCLUDED_N230_ETH_HANDLERS_H + +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <lwip/ip_addr.h> +#include <wb_soft_reg.h> +#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h" + +/*! + * Registrar for host firmware communications handler. + */ +void n230_register_udp_fw_comms_handler(n230_host_shared_mem_t* shared_mem_ptr); + +/*! + * Registrar for framer programmer handler. + */ +void n230_register_udp_prog_framer(); + +/*! + * Registrar for host firmware communications handler. + */ +void n230_register_flash_comms_handler(); + +/*! + * Handle SFP updates. + */ +void n230_update_link_act_state(soft_reg_t* led_reg); + +#endif /* INCLUDED_N230_ETH_HANDLERS_H */ diff --git a/firmware/usrp3/n230/n230_init.c b/firmware/usrp3/n230/n230_init.c new file mode 100644 index 000000000..14f5ebd77 --- /dev/null +++ b/firmware/usrp3/n230/n230_init.c @@ -0,0 +1,125 @@ +// +// Copyright 2014 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 <http://www.gnu.org/licenses/>. +// + +#include <cron.h> +#include <printf.h> +#include <wb_utils.h> +#include <wb_uart.h> +#include <wb_i2c.h> +#include <wb_pkt_iface64.h> +#include <u3_net_stack.h> +#include <print_addrs.h> +#include <trace.h> +#include "../../../host/lib/usrp/n230/n230_eeprom.h" +#include "n230_init.h" +#include "../../../host/lib/usrp/n230/n230_fw_defs.h" + +static wb_pkt_iface64_config_t pkt_config; + +static void putc(void *p, char c) +{ +//If FW_TRACE_LEVEL is defined, then the trace level is set +//to a non-zero number. Turn on the debug UART to enable tracing +#ifdef UHD_FW_TRACE_LEVEL + wb_uart_putc(WB_DBG_UART_BASE, c); +#endif +} + +static uint32_t get_counter_val() +{ + return wb_peek32(SR_ADDR(WB_SBRB_BASE, RB_ZPU_COUNTER)); +} + +void n230_init(void) +{ + //TODO: We may need to remove the debug UART before we release. + //Initialize the debug UART first. + wb_uart_init(WB_DBG_UART_BASE, CPU_CLOCK_FREQ/DBG_UART_BAUD); + init_printf(NULL, putc); + + //Now we can init the rest with prints + UHD_FW_TRACE_FSTR(INFO, "[ZPU Init Begin -- CPU CLOCK is %d MHz]", (CPU_CLOCK_FREQ/1000000)); + + //Initialize cron and the per millisecond cron job + UHD_FW_TRACE(INFO, "Initializing cron..."); + cron_init(get_counter_val, CPU_CLOCK_FREQ); + cron_job_init(PER_MILLISEC_CRON_JOBID, 1); + cron_job_init(PER_SECOND_CRON_JOBID, 1000); + + //Initialize rate for I2C cores + UHD_FW_TRACE(INFO, "Initializing I2C..."); + for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) { + wb_i2c_init((i==1)?WB_ETH1_I2C_BASE:WB_ETH0_I2C_BASE, CPU_CLOCK_FREQ); + } + + //Initialize eeprom + read_n230_eeprom(); + + UHD_FW_TRACE(INFO, "Initializing network stack..."); + init_network_stack(); +} + +void init_network_stack(void) +{ + //Hold Ethernet PHYs in reset + wb_poke32(SR_ADDR(WB_SBRB_BASE, SR_ZPU_SW_RST), SR_ZPU_SW_RST_PHY); + + //Initialize ethernet packet interface + pkt_config = wb_pkt_iface64_init(WB_PKT_RAM_BASE, WB_PKT_RAM_CTRL_OFFSET); + u3_net_stack_init(&pkt_config); + + //Initialize MACs + for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) { + init_ethernet_mac(i); + } + + //Pull Ethernet PHYs out of reset + wb_poke32(SR_ADDR(WB_SBRB_BASE, SR_ZPU_SW_RST), SR_ZPU_SW_RST_NONE); +} + +void init_ethernet_mac(uint32_t iface_num) +{ + UHD_FW_TRACE_FSTR(INFO, "Initializing eth%d...", iface_num); + + //Get interface info from the EEPROM (or defaults otherwise) + const n230_eth_eeprom_map_t* eth_eeprom_map = get_n230_ethernet_info(iface_num); + const eth_mac_addr_t *my_mac = (const eth_mac_addr_t *) &(eth_eeprom_map->mac_addr); + const struct ip_addr *my_ip = (const struct ip_addr *) &(eth_eeprom_map->ip_addr); + const struct ip_addr *subnet = (const struct ip_addr *) &(eth_eeprom_map->subnet); + + //Init software fields related to ethernet + u3_net_stack_init_eth(iface_num, my_mac, my_ip, subnet); + + uint32_t dispatcher_base = + ((iface_num == 1) ? SR_ZPU_ETHINT1 : SR_ZPU_ETHINT0) + SR_ZPU_ETHINT_DISPATCHER_BASE; + + //Program dispatcher + wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 0), + (my_mac->addr[5] << 0) | (my_mac->addr[4] << 8) | (my_mac->addr[3] << 16) | (my_mac->addr[2] << 24)); + wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 1), (my_mac->addr[1] << 0) | (my_mac->addr[0] << 8)); + wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 2), my_ip->addr); + wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 4), 0/*nofwd*/); + wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 5), (ICMP_IRQ << 8) | 0); //no fwd: type, code + + //DEBUG: Print initialized info + UHD_FW_TRACE_FSTR(INFO, "-- MAC%u: %s", iface_num, mac_addr_to_str(u3_net_stack_get_mac_addr(iface_num))); + UHD_FW_TRACE_FSTR(INFO, "-- IP%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_ip_addr(iface_num))); + UHD_FW_TRACE_FSTR(INFO, "-- SUBNET%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_subnet(iface_num))); + UHD_FW_TRACE_FSTR(INFO, "-- BCAST%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_bcast(iface_num))); +} + + diff --git a/firmware/usrp3/n230/n230_init.h b/firmware/usrp3/n230/n230_init.h new file mode 100644 index 000000000..e2231909e --- /dev/null +++ b/firmware/usrp3/n230/n230_init.h @@ -0,0 +1,28 @@ +// +// Copyright 2014 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 <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_INIT_H +#define INCLUDED_N230_INIT_H + +#include <stdint.h> +#include <stdbool.h> + +void n230_init(void); +void init_network_stack(void); +void init_ethernet_mac(uint32_t iface_num); + +#endif /* INCLUDED_B250_INIT_H */ diff --git a/firmware/usrp3/n230/n230_main.c b/firmware/usrp3/n230/n230_main.c new file mode 100644 index 000000000..a6c12e56d --- /dev/null +++ b/firmware/usrp3/n230/n230_main.c @@ -0,0 +1,113 @@ +// +// Copyright 2014 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 <http://www.gnu.org/licenses/>. +// + +#include <cron.h> +#include <wb_soft_reg.h> +#include <u3_net_stack.h> +#include <trace.h> +#include "../../../host/lib/usrp/n230/n230_fw_defs.h" +#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h" +#include "n230_eth_handlers.h" +#include "n230_init.h" + +//The version hash should come from a cmake build variable +//If it doesn't then the build system does not support the feature +//so just default to 0xFFFFFFFF +#ifndef UHD_VERSION_HASH +#define UHD_VERSION_HASH 0xFFFFFFFF +#endif + +//TODO: This is just for initial debugging. +static soft_reg_t g_led_register; + +//Shared memory +static n230_host_shared_mem_t g_host_shared_mem; + +//Functions +static void n230_handle_claim(); + +/*********************************************************************** + * Main loop runs all the handlers + **********************************************************************/ +int main(void) +{ + //Initialize host shared mem + g_host_shared_mem.data.fw_compat_num = N230_FW_COMPAT_NUM; + g_host_shared_mem.data.fw_version_hash = UHD_VERSION_HASH; + + //Main initialization function + n230_init(); + + //Initialize UDP Handlers + n230_register_udp_fw_comms_handler(&g_host_shared_mem); + n230_register_udp_prog_framer(); + n230_register_flash_comms_handler(); + + initialize_writeonly_soft_reg(&g_led_register, SR_ADDR(WB_SBRB_BASE, SR_ZPU_LEDS)); + + uint32_t heart_beat = 0; + while(true) + { + //TODO: This is just for initial debugging. Once the firmware + //is somewhat stable we should delete this cron job + if (cron_job_run_due(PER_SECOND_CRON_JOBID)) { + //Everything in this block runs approx once per second + if (heart_beat % 10 == 0) { + UHD_FW_TRACE_FSTR(INFO, "0.1Hz Heartbeat (%u)", heart_beat); + } + heart_beat++; + } + + if (cron_job_run_due(PER_MILLISEC_CRON_JOBID)) { + //Everything in this block runs approx once per millisecond + n230_handle_claim(); + n230_update_link_act_state(&g_led_register); + } + + //run the network stack - poll and handle + u3_net_stack_handle_one(); + } + return 0; +} + +// Watchdog timer for claimer +static void n230_handle_claim() +{ + static uint32_t last_time = 0; + static size_t timeout = 0; + + if (g_host_shared_mem.data.claim_time == 0) { + //If time is 0 if the claim was forfeit + g_host_shared_mem.data.claim_status = 0; + } else if (last_time != g_host_shared_mem.data.claim_time) { + //If the time changes, reset timeout + g_host_shared_mem.data.claim_status = 1; + timeout = 0; + } else { + //Otherwise increment for timeout + timeout++; + } + + //Always stash the last seen time + last_time = g_host_shared_mem.data.claim_time; + + //Timeout logic + if (timeout > N230_CLAIMER_TIMEOUT_IN_MS) { + g_host_shared_mem.data.claim_time = 0; + } +} + |