mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-14 20:58:09 +00:00
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:
parent
b92825b3b9
commit
1da0db3dae
11 changed files with 0 additions and 1742 deletions
|
|
@ -134,4 +134,3 @@ endmacro(GEN_OUTPUTS)
|
|||
########################################################################
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(x300)
|
||||
add_subdirectory(n230)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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()
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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)));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue