From b25a17fa370a061fd9638871263dd118dec09c43 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 5 Nov 2019 16:28:49 +0800 Subject: [PATCH] netboot: support slave FPGA loading --- artiq/firmware/bootloader/main.rs | 97 +++++++++++++++++++-- artiq/firmware/libboard_misoc/slave_fpga.rs | 2 +- artiq/frontend/artiq_netboot.py | 31 +++++-- 3 files changed, 111 insertions(+), 19 deletions(-) diff --git a/artiq/firmware/bootloader/main.rs b/artiq/firmware/bootloader/main.rs index 3d2ae4fbd..facc2a72e 100644 --- a/artiq/firmware/bootloader/main.rs +++ b/artiq/firmware/bootloader/main.rs @@ -140,15 +140,14 @@ fn load_slave_fpga() { println!(" ...Error during loading: {}", e); return } - if let Err(e) = slave_fpga::complete() { - println!(" ...Error during completion: {}", e); + if let Err(e) = slave_fpga::startup() { + println!(" ...Error during startup: {}", e); return } println!(" ...done"); } - fn flash_boot() { const FIRMWARE: *mut u8 = board_mem::FLASH_BOOT_ADDRESS as *mut u8; const MAIN_RAM: *mut u8 = board_mem::MAIN_RAM_BASE as *mut u8; @@ -193,8 +192,16 @@ enum NetConnState { WaitCommand, FirmwareLength(usize, u8), FirmwareDownload(usize, usize), - WaitO, - WaitK + FirmwareWaitO, + FirmwareWaitK, + #[cfg(has_slave_fpga_cfg)] + GatewareLength(usize, u8), + #[cfg(has_slave_fpga_cfg)] + GatewareDownload(usize, usize), + #[cfg(has_slave_fpga_cfg)] + GatewareWaitO, + #[cfg(has_slave_fpga_cfg)] + GatewareWaitK } #[cfg(has_ethmac)] @@ -228,6 +235,12 @@ impl NetConn { self.state = NetConnState::FirmwareLength(0, 0); Ok(1) }, + #[cfg(has_slave_fpga_cfg)] + b'G' => { + println!("Received gateware load command"); + self.state = NetConnState::GatewareLength(0, 0); + Ok(1) + } b'B' => { if self.firmware_downloaded { println!("Received boot command"); @@ -245,6 +258,7 @@ impl NetConn { } } }, + NetConnState::FirmwareLength(firmware_length, recv_bytes) => { let firmware_length = (firmware_length << 8) | (buf[0] as usize); let recv_bytes = recv_bytes + 1; @@ -269,23 +283,23 @@ impl NetConn { let recv_bytes = recv_bytes + length; if recv_bytes == firmware_length { - self.state = NetConnState::WaitO; + self.state = NetConnState::FirmwareWaitO; Ok(length) } else { self.state = NetConnState::FirmwareDownload(firmware_length, recv_bytes); Ok(length) } }, - NetConnState::WaitO => { + NetConnState::FirmwareWaitO => { if buf[0] == b'O' { - self.state = NetConnState::WaitK; + self.state = NetConnState::FirmwareWaitK; Ok(1) } else { println!("End-of-firmware confirmation failed"); Err(()) } }, - NetConnState::WaitK => { + NetConnState::FirmwareWaitK => { if buf[0] == b'K' { println!("Firmware successfully downloaded"); self.state = NetConnState::WaitCommand; @@ -296,6 +310,71 @@ impl NetConn { Err(()) } } + + #[cfg(has_slave_fpga_cfg)] + NetConnState::GatewareLength(gateware_length, recv_bytes) => { + let gateware_length = (gateware_length << 8) | (buf[0] as usize); + let recv_bytes = recv_bytes + 1; + if recv_bytes == 4 { + if let Err(e) = slave_fpga::prepare() { + println!(" Error during slave FPGA preparation: {}", e); + return Err(()) + } + self.state = NetConnState::GatewareDownload(gateware_length, 0); + } else { + self.state = NetConnState::GatewareLength(gateware_length, recv_bytes); + } + Ok(1) + }, + #[cfg(has_slave_fpga_cfg)] + NetConnState::GatewareDownload(gateware_length, recv_bytes) => { + let max_length = gateware_length - recv_bytes; + let buf = if buf.len() > max_length { + &buf[..max_length] + } else { + &buf[..] + }; + let length = buf.len(); + + if let Err(e) = slave_fpga::input(buf) { + println!("Error during slave FPGA loading: {}", e); + return Err(()) + } + + let recv_bytes = recv_bytes + length; + if recv_bytes == gateware_length { + self.state = NetConnState::GatewareWaitO; + Ok(length) + } else { + self.state = NetConnState::GatewareDownload(gateware_length, recv_bytes); + Ok(length) + } + }, + #[cfg(has_slave_fpga_cfg)] + NetConnState::GatewareWaitO => { + if buf[0] == b'O' { + self.state = NetConnState::GatewareWaitK; + Ok(1) + } else { + println!("End-of-gateware confirmation failed"); + Err(()) + } + }, + #[cfg(has_slave_fpga_cfg)] + NetConnState::GatewareWaitK => { + if buf[0] == b'K' { + if let Err(e) = slave_fpga::startup() { + println!("Error during slave FPGA startup: {}", e); + return Err(()) + } + println!("Gateware successfully downloaded"); + self.state = NetConnState::WaitCommand; + Ok(1) + } else { + println!("End-of-gateware confirmation failed"); + Err(()) + } + } } } diff --git a/artiq/firmware/libboard_misoc/slave_fpga.rs b/artiq/firmware/libboard_misoc/slave_fpga.rs index 40c6795e0..bafe1d7fe 100644 --- a/artiq/firmware/libboard_misoc/slave_fpga.rs +++ b/artiq/firmware/libboard_misoc/slave_fpga.rs @@ -59,7 +59,7 @@ pub fn input(data: &[u8]) -> Result<(), &'static str> { Ok(()) } -pub fn complete() -> Result<(), &'static str> { +pub fn startup() -> Result<(), &'static str> { unsafe { let t = clock::get_ms(); while csr::slave_fpga_cfg::in_read() & DONE_BIT == 0 { diff --git a/artiq/frontend/artiq_netboot.py b/artiq/frontend/artiq_netboot.py index 9adcc294a..4f72b2c45 100755 --- a/artiq/frontend/artiq_netboot.py +++ b/artiq/frontend/artiq_netboot.py @@ -5,12 +5,29 @@ import socket import struct import os + +def send_file(sock, filename): + with open(filename, "rb") as input_file: + sock.sendall(struct.pack(">I", os.fstat(input_file.fileno()).st_size)) + while True: + data = input_file.read(4096) + if not data: + break + sock.sendall(data) + sock.sendall(b"OK") + + def main(): parser = argparse.ArgumentParser(description="ARTIQ netboot tool") parser.add_argument("hostname", metavar="HOSTNAME", help="hostname of the target board") parser.add_argument("-f", "--firmware", nargs=1, help="firmware to load") + # Note that on softcore systems, the main gateware cannot be replaced + # with -g. This option is used for loading the RTM FPGA from the AMC + # on Sayma, and the PL on Zynq. + parser.add_argument("-g", "--gateware", nargs=1, + help="gateware to load") parser.add_argument("-b", "--boot", action="store_true", help="boot the device") args = parser.parse_args() @@ -18,15 +35,11 @@ def main(): sock = socket.create_connection((args.hostname, 4269)) try: if args.firmware is not None: - with open(args.firmware[0], "rb") as input_file: - sock.sendall(b"F") - sock.sendall(struct.pack(">I", os.fstat(input_file.fileno()).st_size)) - while True: - data = input_file.read(4096) - if not data: - break - sock.sendall(data) - sock.sendall(b"OK") + sock.sendall(b"F") + send_file(sock, args.firmware[0]) + if args.gateware is not None: + sock.sendall(b"G") + send_file(sock, args.gateware[0]) if args.boot: sock.sendall(b"B") finally: