diff --git a/artiq/coredevice/comm_mgmt.py b/artiq/coredevice/comm_mgmt.py index 1b218afec..f8f95fc84 100644 --- a/artiq/coredevice/comm_mgmt.py +++ b/artiq/coredevice/comm_mgmt.py @@ -1,5 +1,7 @@ from enum import Enum +import binascii import logging +import io import struct from sipyco.keepalive import create_connection @@ -23,6 +25,8 @@ class Request(Enum): DebugAllocator = 8 + Flash = 9 + class Reply(Enum): Success = 1 @@ -196,3 +200,45 @@ class CommMgmt: def debug_allocator(self): self._write_header(Request.DebugAllocator) + + def flash(self, **bin_paths): + self._write_header(Request.Flash) + + addr_table = {} + with io.BytesIO() as image_buf, io.BytesIO() as bin_buf: + offset = 0 + # Reserve 4-bytes for CRC + image_buf.write(struct.pack(self.endian + "I", 0)) + # Reserve 4-bytes for header length + image_buf.write(struct.pack(self.endian + "I", 0)) + image_buf.write(struct.pack(self.endian + "I", len(bin_paths))) + for bin_name, filename in bin_paths.items(): + with open(filename, "rb") as fi: + bin_ = fi.read() + length = bin_buf.write(bin_) + + bin_name_str = bin_name.encode("utf-8") + image_buf.write(struct.pack(self.endian + "I", len(bin_name_str))) + image_buf.write(bin_name_str) + image_buf.write(struct.pack(self.endian + "II", offset, length)) + + offset += length + + # header = image_buf.getvalue() + # image = image_buf.getvalue() + + assert(image_buf.tell() == len(image_buf.getvalue())) + header_len = image_buf.tell() - 8 + image_buf.seek(4, 0) + image_buf.write(struct.pack(self.endian + "I", header_len)) + image_buf.seek(0, 2) + image_buf.write(bin_buf.getvalue()) + + image_buf.seek(4, 0) + crc = binascii.crc32(image_buf.read()) + image_buf.seek(0, 0) + image_buf.write(struct.pack(self.endian + "I", crc)) + + self._write_bytes(image_buf.getvalue()) + + self._read_expect(Reply.RebootImminent) diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index 527a662ee..a14353341 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -513,6 +513,7 @@ dependencies = [ "board_misoc", "build_misoc", "byteorder", + "crc", "cslice", "dyld", "eh", diff --git a/artiq/firmware/libboard_misoc/config.rs b/artiq/firmware/libboard_misoc/config.rs index a7c3d4a63..e5e62ad0c 100644 --- a/artiq/firmware/libboard_misoc/config.rs +++ b/artiq/firmware/libboard_misoc/config.rs @@ -259,39 +259,12 @@ mod imp { } pub fn write(key: &str, value: &[u8]) -> Result<(), Error> { - fn flash_binary(origin: usize, payload: &[u8]) { - let mut offset = 0; - while offset < payload.len() { - unsafe { - spiflash::erase_sector(origin + offset); - } - offset += spiflash::SECTOR_SIZE; - } - unsafe { - spiflash::write(origin, payload); - } - } - - match key { - "gateware" => { - flash_binary(0, value); - Ok(()) - } - "bootloader" => { - flash_binary(::mem::ROM_BASE, value); - Ok(()) - } - "firmware" => { - flash_binary(::mem::FLASH_BOOT_ADDRESS, value); - Ok(()) - } - _ => match append(key, value) { - Err(Error::SpaceExhausted) => { - compact()?; - append(key, value) - } - res => res + match append(key, value) { + Err(Error::SpaceExhausted) => { + compact()?; + append(key, value) } + res => res } } diff --git a/artiq/firmware/libboard_misoc/spiflash.rs b/artiq/firmware/libboard_misoc/spiflash.rs index 598d96633..0f25ecbbc 100644 --- a/artiq/firmware/libboard_misoc/spiflash.rs +++ b/artiq/firmware/libboard_misoc/spiflash.rs @@ -114,6 +114,50 @@ pub unsafe fn write(mut addr: usize, mut data: &[u8]) { } } +// pub unsafe fn write_image(image: &[u8]) { +// let image = &image[..]; +// let actual_crc = crc32::checksum_ieee(image); + +// if actual_crc == expected_crc { +// let mut reader = Cursor::new(header); +// let bin_no = reader.read_u32().unwrap() as usize; +// for _ in 0..bin_no { +// let bin_name = reader.read_string().unwrap(); +// let offset = reader.read_u32().unwrap() as usize; +// let len = reader.read_u32().unwrap() as usize; + +// let origin = match bin_name.as_str() { +// "gateware" => 0, +// "bootloader" => mem::ROM_BASE, +// "firmware" => mem::FLASH_BOOT_ADDRESS, +// _ => { +// error!("unexpected binary component {}", bin_name); +// return Ok(Reply::Error.write_to(stream)?); +// } +// }; + +// unsafe { +// spiflash::flash_binary(origin, &image[offset..offset+len]); +// } +// } + +// reboot(_io, stream)?; +// } else { +// error!("CRC failed in SDRAM (actual {:08x}, expected {:08x})", actual_crc, expected_crc); +// Reply::Error.write_to(stream)?; +// } +// } + +pub unsafe fn flash_binary(origin: usize, payload: &[u8]) { + assert!((origin & (SECTOR_SIZE - 1)) == 0); + let mut offset = 0; + while offset < payload.len() { + erase_sector(origin + offset); + offset += SECTOR_SIZE; + } + write(origin, payload); +} + #[cfg(any(soc_platform = "kasli", soc_platform = "kc705"))] pub unsafe fn reload () -> ! { csr::icap::iprog_write(1); diff --git a/artiq/firmware/libproto_artiq/mgmt_proto.rs b/artiq/firmware/libproto_artiq/mgmt_proto.rs index 74724df13..bfc8cc004 100644 --- a/artiq/firmware/libproto_artiq/mgmt_proto.rs +++ b/artiq/firmware/libproto_artiq/mgmt_proto.rs @@ -67,6 +67,8 @@ pub enum Request { Reboot, + Flash { image: Vec }, + DebugAllocator, } @@ -125,6 +127,10 @@ impl Request { 8 => Request::DebugAllocator, + 9 => Request::Flash { + image: reader.read_bytes()?, + }, + ty => return Err(Error::UnknownPacket(ty)) }) } diff --git a/artiq/firmware/runtime/Cargo.toml b/artiq/firmware/runtime/Cargo.toml index 0d132d5a9..f06b93e49 100644 --- a/artiq/firmware/runtime/Cargo.toml +++ b/artiq/firmware/runtime/Cargo.toml @@ -16,6 +16,7 @@ build_misoc = { path = "../libbuild_misoc" } failure = { version = "0.1", default-features = false } failure_derive = { version = "0.1", default-features = false } byteorder = { version = "1.0", default-features = false } +crc = { version = "1.7", default-features = false } cslice = { version = "0.3" } log = { version = "=0.4.14", default-features = false } managed = { version = "^0.7.1", default-features = false, features = ["alloc", "map"] } diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index 9fee39218..a4f3938c0 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -1,6 +1,7 @@ #![feature(lang_items, panic_info_message, const_btree_new, iter_advance_by, never_type)] #![no_std] +extern crate crc; extern crate dyld; extern crate eh; #[macro_use] diff --git a/artiq/firmware/runtime/mgmt.rs b/artiq/firmware/runtime/mgmt.rs index 27e1c83d6..e41ee416a 100644 --- a/artiq/firmware/runtime/mgmt.rs +++ b/artiq/firmware/runtime/mgmt.rs @@ -14,10 +14,11 @@ impl From for Error { mod local_coremgmt { use alloc::{string::String, vec::Vec}; + use crc::crc32; use log::LevelFilter; - use board_misoc::{config, spiflash}; - use io::{Write, ProtoWrite, Error as IoError}; + use board_misoc::{config, mem, spiflash}; + use io::{Cursor, Write, ProtoWrite, ProtoRead, Error as IoError}; use logger_artiq::BufferLogger; use mgmt_proto::{Error, Reply}; use sched::{Io, TcpStream, Error as SchedError}; @@ -134,6 +135,54 @@ mod local_coremgmt { unsafe { println!("{}", ::ALLOC) } Ok(()) } + + pub fn flash(_io: &Io, stream: &mut TcpStream, image: &Vec) -> Result<(), Error> { + let mut reader = Cursor::new(&image[..]); + let expected_crc = reader.read_u32().unwrap(); + + let image = &image[4..]; + let actual_crc = crc32::checksum_ieee(image); + + if actual_crc == expected_crc { + info!("Checksum matched"); + let header_size = reader.read_u32().unwrap() as usize; + let header_offset = reader.position(); + let bin_offset = header_offset + header_size; + + let header = &image[header_offset..bin_offset]; + let binaries = &image[bin_offset..]; + + info!("found header of size {}", header.len()); + + let mut reader = Cursor::new(header); + let bin_no = reader.read_u32().unwrap() as usize; + for _ in 0..bin_no { + let bin_name = reader.read_string().unwrap(); + let offset = reader.read_u32().unwrap() as usize; + let len = reader.read_u32().unwrap() as usize; + + let origin = match bin_name.as_str() { + "gateware" => 0, + "bootloader" => mem::ROM_BASE, + "firmware" => mem::FLASH_BOOT_ADDRESS, + _ => { + error!("unexpected binary component {}", bin_name); + return Ok(Reply::Error.write_to(stream)?); + } + }; + + unsafe { + spiflash::flash_binary(origin, &binaries[offset..offset+len]); + } + } + + reboot(_io, stream)?; + } else { + error!("CRC failed in SDRAM (actual {:08x}, expected {:08x})", actual_crc, expected_crc); + Reply::Error.write_to(stream)?; + } + Ok(()) + } } #[cfg(has_drtio)] @@ -481,6 +530,13 @@ mod remote_coremgmt { } } } + + pub fn flash(io: &Io, aux_mutex: &Mutex, + ddma_mutex: &Mutex, subkernel_mutex: &Mutex, + routing_table: &drtio_routing::RoutingTable, linkno: u8, + destination: u8, stream: &mut TcpStream, image: &Vec) -> Result<(), Error> { + todo!() + } } #[cfg(has_drtio)] @@ -523,6 +579,7 @@ fn worker(io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex, _subkernel_mutex: &M Request::ConfigErase => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, config_erase), Request::Reboot => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, reboot), Request::DebugAllocator => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, debug_allocator), + Request::Flash { ref image } => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, flash, image), }?; } } diff --git a/artiq/frontend/artiq_coremgmt.py b/artiq/frontend/artiq_coremgmt.py index 2fbf450c0..d6c1826ae 100755 --- a/artiq/frontend/artiq_coremgmt.py +++ b/artiq/frontend/artiq_coremgmt.py @@ -1,7 +1,10 @@ #!/usr/bin/env python3 import argparse +import os import struct +import tempfile +import atexit from sipyco import common_args @@ -9,6 +12,8 @@ from artiq import __version__ as artiq_version from artiq.master.databases import DeviceDB from artiq.coredevice.comm_kernel import CommKernel from artiq.coredevice.comm_mgmt import CommMgmt +from artiq.frontend.bit2bin import bit2bin +from misoc.tools.mkmscimg import insert_crc def get_argparser(): @@ -85,6 +90,13 @@ def get_argparser(): t_boot = tools.add_parser("reboot", help="reboot the running system") + # flashing + t_flash = tools.add_parser("flash", + help="flash the running system") + + p_directory = t_flash.add_argument("directory", metavar="DIRECTORY", type=str, + help="directory that contains the binaries") + # misc debug t_debug = tools.add_parser("debug", help="specialized debug functions") @@ -142,6 +154,37 @@ def main(): mgmt.config_remove(key) if args.action == "erase": mgmt.config_erase() + + if args.tool == "flash": + def convert_gateware(bit_filename): + bin_handle, bin_filename = tempfile.mkstemp( + prefix="artiq_", suffix="_" + os.path.basename(bit_filename)) + with open(bit_filename, "rb") as bit_file, open(bin_handle, "wb") as bin_file: + bit2bin(bit_file, bin_file) + atexit.register(lambda: os.unlink(bin_filename)) + return bin_filename + + gateware = convert_gateware(os.path.join(args.directory, "top.bit")) + bootloader = os.path.join(args.directory, "bootloader.bin") + + firmwares = [] + for firmware in "satman", "runtime": + filename = os.path.join(args.directory, firmware + ".fbi") + if os.path.exists(filename): + firmwares.append(filename) + if not firmwares: + raise FileNotFoundError("no firmware found") + if len(firmwares) > 1: + raise ValueError("more than one firmware file, please clean up your build directory. " + "Found firmware files: {}".format(" ".join(firmwares))) + firmware = firmwares[0] + + bins = { + "gateware": gateware, + "bootloader": bootloader, + "firmware": firmware, + } + mgmt.flash(**bins) if args.tool == "reboot": mgmt.reboot()