firmware: Remove N230 firmware

USRP N230 is no longer supported starting with UHD 4, and thus, the
firmware code is also no longer required.
This commit is contained in:
Martin Braun 2022-02-23 10:18:51 +01:00 committed by Aaron Rossetto
parent b92825b3b9
commit 1da0db3dae
11 changed files with 0 additions and 1742 deletions

View file

@ -134,4 +134,3 @@ endmacro(GEN_OUTPUTS)
########################################################################
add_subdirectory(lib)
add_subdirectory(x300)
add_subdirectory(n230)

View file

@ -1,40 +0,0 @@
#
# 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_fw_comm_protocol.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
#)

View file

@ -1,359 +0,0 @@
#!/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)

View file

@ -1,387 +0,0 @@
#!/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()

View file

@ -1,196 +0,0 @@
//
// 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);
}

View file

@ -1,340 +0,0 @@
//
// 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/n230/n230_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;
}

View file

@ -1,48 +0,0 @@
//
// 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 */

View file

@ -1,105 +0,0 @@
//
// 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_fw_comm_protocol.h"
#include <trace.h>
#include <string.h> //memcmp
bool process_fw_comm_protocol_pkt(
const fw_comm_pkt_t* request,
fw_comm_pkt_t* response,
uint8_t product_id,
uint32_t iface_id,
poke32_func poke_callback,
peek32_func peek_callback)
{
bool send_response = false;
uint16_t signature = request->id;
uint8_t version = FW_COMM_GET_PROTOCOL_VER(request->id);
uint8_t product = FW_COMM_GET_PRODUCT_ID(request->id);
if (signature == FW_COMM_PROTOCOL_SIGNATURE && //Verify protocol
version <= FW_COMM_PROTOCOL_VERSION && //Verify protocol version (older versions supported)
product == product_id) //Verify device
{
//Request is valid. Copy it into the reply.
memcpy(response, request, sizeof(fw_comm_pkt_t));
//Start assuming no error
response->flags &= ~FW_COMM_FLAGS_ERROR_MASK;
//Otherwise, run the command set by the flags
switch (request->flags & FW_COMM_FLAGS_CMD_MASK) {
case FW_COMM_CMD_ECHO: {
UHD_FW_TRACE(DEBUG, "fw_comm_protocol::echo()");
response->data_words = 1;
response->data[0] = iface_id;
} break;
case FW_COMM_CMD_POKE32: {
UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::poke32(0x%x)=0x%x",
request->addr,*(request->data));
poke_callback(request->addr, *(request->data));
} break;
case FW_COMM_CMD_PEEK32: {
*(response->data) = peek_callback(request->addr);
UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::peek32(0x%x)=0x%x",
request->addr,*(response->data));
} break;
case FW_COMM_CMD_BLOCK_POKE32: {
if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
response->flags |= FW_COMM_ERR_SIZE_ERROR;
response->data_words = FW_COMM_MAX_DATA_WORDS;
} else {
response->data_words = request->data_words;
}
UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_poke32(0x%x,%d)",request->addr,response->data_words);
for (uint32_t i = 0; i < response->data_words; i++) {
poke_callback(request->addr + (i * sizeof(uint32_t)), request->data[i]);
}
} break;
case FW_COMM_CMD_BLOCK_PEEK32: {
if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
response->flags |= FW_COMM_ERR_SIZE_ERROR;
response->data_words = FW_COMM_MAX_DATA_WORDS;
} else {
response->data_words = request->data_words;
}
for (uint32_t i = 0; i < response->data_words; i++) {
response->data[i] = peek_callback(request->addr + (i * sizeof(uint32_t)));
}
UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_peek32(0x%x,%d)",request->addr,response->data_words);
} break;
default: {
UHD_FW_TRACE(ERROR, "fw_comm_protocol got an invalid command.");
response->flags |= FW_COMM_ERR_CMD_ERROR;
}
}
//Send a reply if ack requested
send_response = (request->flags & FW_COMM_FLAGS_ACK);
} else { //Size, protocol, product check failed
UHD_FW_TRACE(WARN, "fw_comm_protocol ignored an unknown request.");
send_response = false;
}
return send_response;
}

View file

@ -1,125 +0,0 @@
//
// 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)));
}

View file

@ -1,28 +0,0 @@
//
// 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 */

View file

@ -1,113 +0,0 @@
//
// 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;
}
}