mirror of
https://github.com/m-labs/artiq.git
synced 2024-12-27 04:08:27 +08:00
Support CoreMgmt over DRTIO
This commit is contained in:
commit
8b335c4459
@ -22,6 +22,8 @@ ARTIQ-9 (Unreleased)
|
||||
* Idle kernels now restart when written with ``artiq_coremgmt`` and stop when erased/removed from config.
|
||||
* New support for the EBAZ4205 Zynq-SoC control card.
|
||||
* New core device driver for the AD9834 DDS, tested with the ZonRi Technology Co., Ltd. AD9834-Module.
|
||||
* Support for coredevice reflashing through the new ``flash`` tool in ``artiq_coremgmt``.
|
||||
* ``artiq_coremgmt`` now supports configuring satellites.
|
||||
* artiq.coredevice.fmcdio_vhdci_eem has been removed.
|
||||
|
||||
ARTIQ-8
|
||||
|
@ -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
|
||||
@ -46,15 +50,17 @@ class LogLevel(Enum):
|
||||
|
||||
|
||||
class CommMgmt:
|
||||
def __init__(self, host, port=1380):
|
||||
def __init__(self, host, port=1380, drtio_dest=0):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.drtio_dest = drtio_dest
|
||||
|
||||
def open(self):
|
||||
if hasattr(self, "socket"):
|
||||
return
|
||||
self.socket = create_connection(self.host, self.port)
|
||||
self.socket.sendall(b"ARTIQ management\n")
|
||||
self._write_int8(self.drtio_dest)
|
||||
endian = self._read(1)
|
||||
if endian == b"e":
|
||||
self.endian = "<"
|
||||
@ -194,3 +200,22 @@ class CommMgmt:
|
||||
|
||||
def debug_allocator(self):
|
||||
self._write_header(Request.DebugAllocator)
|
||||
|
||||
def flash(self, bin_paths):
|
||||
self._write_header(Request.Flash)
|
||||
|
||||
with io.BytesIO() as image_buf:
|
||||
for filename in bin_paths:
|
||||
with open(filename, "rb") as fi:
|
||||
bin_ = fi.read()
|
||||
if (len(bin_paths) > 1):
|
||||
image_buf.write(
|
||||
struct.pack(self.endian + "I", len(bin_)))
|
||||
image_buf.write(bin_)
|
||||
|
||||
crc = binascii.crc32(image_buf.getvalue())
|
||||
image_buf.write(struct.pack(self.endian + "I", crc))
|
||||
|
||||
self._write_bytes(image_buf.getvalue())
|
||||
|
||||
self._read_expect(Reply.RebootImminent)
|
||||
|
4
artiq/firmware/Cargo.lock
generated
4
artiq/firmware/Cargo.lock
generated
@ -513,6 +513,7 @@ dependencies = [
|
||||
"board_misoc",
|
||||
"build_misoc",
|
||||
"byteorder",
|
||||
"crc",
|
||||
"cslice",
|
||||
"dyld",
|
||||
"eh",
|
||||
@ -553,10 +554,13 @@ dependencies = [
|
||||
"board_artiq",
|
||||
"board_misoc",
|
||||
"build_misoc",
|
||||
"byteorder",
|
||||
"crc",
|
||||
"cslice",
|
||||
"eh",
|
||||
"io",
|
||||
"log",
|
||||
"logger_artiq",
|
||||
"proto_artiq",
|
||||
"riscv",
|
||||
]
|
||||
|
@ -114,7 +114,17 @@ pub unsafe fn write(mut addr: usize, mut data: &[u8]) {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(soc_platform = "kasli", soc_platform = "kc705"))]
|
||||
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", soc_platform = "efc"))]
|
||||
pub unsafe fn reload () -> ! {
|
||||
csr::icap::iprog_write(1);
|
||||
loop {}
|
||||
|
@ -127,6 +127,25 @@ pub enum Packet {
|
||||
SubkernelException { destination: u8, last: bool, length: u16, data: [u8; MASTER_PAYLOAD_MAX_SIZE] },
|
||||
SubkernelMessage { source: u8, destination: u8, id: u32, status: PayloadStatus, length: u16, data: [u8; MASTER_PAYLOAD_MAX_SIZE] },
|
||||
SubkernelMessageAck { destination: u8 },
|
||||
|
||||
CoreMgmtGetLogRequest { destination: u8, clear: bool },
|
||||
CoreMgmtClearLogRequest { destination: u8 },
|
||||
CoreMgmtSetLogLevelRequest { destination: u8, log_level: u8 },
|
||||
CoreMgmtSetUartLogLevelRequest { destination: u8, log_level: u8 },
|
||||
CoreMgmtConfigReadRequest { destination: u8, length: u16, key: [u8; MASTER_PAYLOAD_MAX_SIZE] },
|
||||
CoreMgmtConfigReadContinue { destination: u8 },
|
||||
CoreMgmtConfigWriteRequest { destination: u8, last: bool, length: u16, data: [u8; MASTER_PAYLOAD_MAX_SIZE] },
|
||||
CoreMgmtConfigRemoveRequest { destination: u8, length: u16, key: [u8; MASTER_PAYLOAD_MAX_SIZE] },
|
||||
CoreMgmtConfigEraseRequest { destination: u8 },
|
||||
CoreMgmtRebootRequest { destination: u8 },
|
||||
CoreMgmtAllocatorDebugRequest { destination: u8 },
|
||||
CoreMgmtFlashRequest { destination: u8, payload_length: u32 },
|
||||
CoreMgmtFlashAddDataRequest { destination: u8, last: bool, length: u16, data: [u8; MASTER_PAYLOAD_MAX_SIZE] },
|
||||
CoreMgmtDropLinkAck { destination: u8 },
|
||||
CoreMgmtDropLink,
|
||||
CoreMgmtGetLogReply { last: bool, length: u16, data: [u8; SAT_PAYLOAD_MAX_SIZE] },
|
||||
CoreMgmtConfigReadReply { last: bool, length: u16, value: [u8; SAT_PAYLOAD_MAX_SIZE] },
|
||||
CoreMgmtReply { succeeded: bool },
|
||||
}
|
||||
|
||||
impl Packet {
|
||||
@ -405,6 +424,115 @@ impl Packet {
|
||||
destination: reader.read_u8()?
|
||||
},
|
||||
|
||||
0xd0 => Packet::CoreMgmtGetLogRequest {
|
||||
destination: reader.read_u8()?,
|
||||
clear: reader.read_bool()?,
|
||||
},
|
||||
0xd1 => Packet::CoreMgmtClearLogRequest {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xd2 => Packet::CoreMgmtSetLogLevelRequest {
|
||||
destination: reader.read_u8()?,
|
||||
log_level: reader.read_u8()?,
|
||||
},
|
||||
0xd3 => Packet::CoreMgmtSetUartLogLevelRequest {
|
||||
destination: reader.read_u8()?,
|
||||
log_level: reader.read_u8()?,
|
||||
},
|
||||
0xd4 => {
|
||||
let destination = reader.read_u8()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut key[0..length as usize])?;
|
||||
Packet::CoreMgmtConfigReadRequest {
|
||||
destination: destination,
|
||||
length: length,
|
||||
key: key,
|
||||
}
|
||||
},
|
||||
0xd5 => Packet::CoreMgmtConfigReadContinue {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xd6 => {
|
||||
let destination = reader.read_u8()?;
|
||||
let last = reader.read_bool()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut data: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut data[0..length as usize])?;
|
||||
Packet::CoreMgmtConfigWriteRequest {
|
||||
destination: destination,
|
||||
last: last,
|
||||
length: length,
|
||||
data: data,
|
||||
}
|
||||
},
|
||||
0xd7 => {
|
||||
let destination = reader.read_u8()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut key[0..length as usize])?;
|
||||
Packet::CoreMgmtConfigRemoveRequest {
|
||||
destination: destination,
|
||||
length: length,
|
||||
key: key,
|
||||
}
|
||||
},
|
||||
0xd8 => Packet::CoreMgmtConfigEraseRequest {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xd9 => Packet::CoreMgmtRebootRequest {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xda => Packet::CoreMgmtAllocatorDebugRequest {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xdb => Packet::CoreMgmtFlashRequest {
|
||||
destination: reader.read_u8()?,
|
||||
payload_length: reader.read_u32()?,
|
||||
},
|
||||
0xdc => {
|
||||
let destination = reader.read_u8()?;
|
||||
let last = reader.read_bool()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut data: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut data[0..length as usize])?;
|
||||
Packet::CoreMgmtFlashAddDataRequest {
|
||||
destination: destination,
|
||||
last: last,
|
||||
length: length,
|
||||
data: data,
|
||||
}
|
||||
},
|
||||
0xdd => Packet::CoreMgmtDropLinkAck {
|
||||
destination: reader.read_u8()?,
|
||||
},
|
||||
0xde => Packet::CoreMgmtDropLink,
|
||||
0xdf => {
|
||||
let last = reader.read_bool()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut data: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut data[0..length as usize])?;
|
||||
Packet::CoreMgmtGetLogReply {
|
||||
last: last,
|
||||
length: length,
|
||||
data: data,
|
||||
}
|
||||
},
|
||||
0xe0 => {
|
||||
let last = reader.read_bool()?;
|
||||
let length = reader.read_u16()?;
|
||||
let mut value: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||
reader.read_exact(&mut value[0..length as usize])?;
|
||||
Packet::CoreMgmtConfigReadReply {
|
||||
last: last,
|
||||
length: length,
|
||||
value: value,
|
||||
}
|
||||
},
|
||||
0xe1 => Packet::CoreMgmtReply {
|
||||
succeeded: reader.read_bool()?,
|
||||
},
|
||||
|
||||
ty => return Err(Error::UnknownPacket(ty))
|
||||
})
|
||||
}
|
||||
@ -693,6 +821,108 @@ impl Packet {
|
||||
writer.write_u8(0xcc)?;
|
||||
writer.write_u8(destination)?;
|
||||
},
|
||||
|
||||
Packet::CoreMgmtGetLogRequest { destination, clear } => {
|
||||
writer.write_u8(0xd0)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_bool(clear)?;
|
||||
},
|
||||
Packet::CoreMgmtClearLogRequest { destination } => {
|
||||
writer.write_u8(0xd1)?;
|
||||
writer.write_u8(destination)?;
|
||||
},
|
||||
Packet::CoreMgmtSetLogLevelRequest { destination, log_level } => {
|
||||
writer.write_u8(0xd2)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_u8(log_level)?;
|
||||
},
|
||||
Packet::CoreMgmtSetUartLogLevelRequest { destination, log_level } => {
|
||||
writer.write_u8(0xd3)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_u8(log_level)?;
|
||||
},
|
||||
Packet::CoreMgmtConfigReadRequest {
|
||||
destination,
|
||||
length,
|
||||
key,
|
||||
} => {
|
||||
writer.write_u8(0xd4)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&key[0..length as usize])?;
|
||||
},
|
||||
Packet::CoreMgmtConfigReadContinue { destination } => {
|
||||
writer.write_u8(0xd5)?;
|
||||
writer.write_u8(destination)?;
|
||||
},
|
||||
Packet::CoreMgmtConfigWriteRequest {
|
||||
destination,
|
||||
last,
|
||||
length,
|
||||
data,
|
||||
} => {
|
||||
writer.write_u8(0xd6)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_bool(last)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&data[0..length as usize])?;
|
||||
},
|
||||
Packet::CoreMgmtConfigRemoveRequest {
|
||||
destination,
|
||||
length,
|
||||
key,
|
||||
} => {
|
||||
writer.write_u8(0xd7)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&key[0..length as usize])?;
|
||||
},
|
||||
Packet::CoreMgmtConfigEraseRequest { destination } => {
|
||||
writer.write_u8(0xd8)?;
|
||||
writer.write_u8(destination)?;
|
||||
},
|
||||
Packet::CoreMgmtRebootRequest { destination } => {
|
||||
writer.write_u8(0xd9)?;
|
||||
writer.write_u8(destination)?;
|
||||
},
|
||||
Packet::CoreMgmtAllocatorDebugRequest { destination } => {
|
||||
writer.write_u8(0xda)?;
|
||||
writer.write_u8(destination)?;
|
||||
},
|
||||
Packet::CoreMgmtFlashRequest { destination, payload_length } => {
|
||||
writer.write_u8(0xdb)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_u32(payload_length)?;
|
||||
},
|
||||
Packet::CoreMgmtFlashAddDataRequest { destination, last, length, data } => {
|
||||
writer.write_u8(0xdc)?;
|
||||
writer.write_u8(destination)?;
|
||||
writer.write_bool(last)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&data[..length as usize])?;
|
||||
},
|
||||
Packet::CoreMgmtDropLinkAck { destination } => {
|
||||
writer.write_u8(0xdd)?;
|
||||
writer.write_u8(destination)?;
|
||||
},
|
||||
Packet::CoreMgmtDropLink =>
|
||||
writer.write_u8(0xde)?,
|
||||
Packet::CoreMgmtGetLogReply { last, length, data } => {
|
||||
writer.write_u8(0xdf)?;
|
||||
writer.write_bool(last)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&data[0..length as usize])?;
|
||||
},
|
||||
Packet::CoreMgmtConfigReadReply { last, length, value } => {
|
||||
writer.write_u8(0xe0)?;
|
||||
writer.write_bool(last)?;
|
||||
writer.write_u16(length)?;
|
||||
writer.write_all(&value[0..length as usize])?;
|
||||
},
|
||||
Packet::CoreMgmtReply { succeeded } => {
|
||||
writer.write_u8(0xe1)?;
|
||||
writer.write_bool(succeeded)?;
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -726,7 +956,7 @@ impl Packet {
|
||||
Packet::DmaAddTraceReply { .. } | Packet::DmaRemoveTraceReply { .. } |
|
||||
Packet::DmaPlaybackReply { .. } | Packet::SubkernelLoadRunReply { .. } |
|
||||
Packet::SubkernelMessageAck { .. } | Packet::DmaPlaybackStatus { .. } |
|
||||
Packet::SubkernelFinished { .. } => false,
|
||||
Packet::SubkernelFinished { .. } | Packet::CoreMgmtDropLinkAck { .. } => false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,9 @@ pub enum Error<T> {
|
||||
#[fail(display = "invalid UTF-8: {}", _0)]
|
||||
Utf8(Utf8Error),
|
||||
#[fail(display = "{}", _0)]
|
||||
Io(#[cause] IoError<T>)
|
||||
Io(#[cause] IoError<T>),
|
||||
#[fail(display = "drtio error")]
|
||||
DrtioError,
|
||||
}
|
||||
|
||||
impl<T> From<IoError<T>> for Error<T> {
|
||||
@ -65,6 +67,8 @@ pub enum Request {
|
||||
|
||||
Reboot,
|
||||
|
||||
Flash { image: Vec<u8> },
|
||||
|
||||
DebugAllocator,
|
||||
}
|
||||
|
||||
@ -123,6 +127,10 @@ impl Request {
|
||||
|
||||
8 => Request::DebugAllocator,
|
||||
|
||||
9 => Request::Flash {
|
||||
image: reader.read_bytes()?,
|
||||
},
|
||||
|
||||
ty => return Err(Error::UnknownPacket(ty))
|
||||
})
|
||||
}
|
||||
|
@ -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"] }
|
||||
|
@ -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]
|
||||
@ -209,7 +210,11 @@ fn startup() {
|
||||
rtio_mgt::startup(&io, &aux_mutex, &drtio_routing_table, &up_destinations, &ddma_mutex, &subkernel_mutex);
|
||||
{
|
||||
let restart_idle = restart_idle.clone();
|
||||
io.spawn(4096, move |io| { mgmt::thread(io, &restart_idle) });
|
||||
let aux_mutex = aux_mutex.clone();
|
||||
let ddma_mutex = ddma_mutex.clone();
|
||||
let subkernel_mutex = subkernel_mutex.clone();
|
||||
let drtio_routing_table = drtio_routing_table.clone();
|
||||
io.spawn(4096, move |io| { mgmt::thread(io, &restart_idle, &aux_mutex, &ddma_mutex, &subkernel_mutex, &drtio_routing_table) });
|
||||
}
|
||||
{
|
||||
let aux_mutex = aux_mutex.clone();
|
||||
|
@ -1,11 +1,9 @@
|
||||
use log::{self, LevelFilter};
|
||||
use core::cell::Cell;
|
||||
use core::cell::{Cell, RefCell};
|
||||
|
||||
use io::{Write, ProtoWrite, Error as IoError};
|
||||
use board_misoc::{config, spiflash};
|
||||
use logger_artiq::BufferLogger;
|
||||
use board_artiq::drtio_routing::RoutingTable;
|
||||
use io::{ProtoRead, Write, Error as IoError};
|
||||
use mgmt_proto::*;
|
||||
use sched::{Io, TcpListener, TcpStream, Error as SchedError};
|
||||
use sched::{Io, Mutex, TcpListener, TcpStream, Error as SchedError};
|
||||
use urc::Urc;
|
||||
|
||||
impl From<SchedError> for Error<SchedError> {
|
||||
@ -14,29 +12,39 @@ impl From<SchedError> for Error<SchedError> {
|
||||
}
|
||||
}
|
||||
|
||||
fn worker(io: &Io, stream: &mut TcpStream, restart_idle: &Urc<Cell<bool>>) -> Result<(), Error<SchedError>> {
|
||||
read_magic(stream)?;
|
||||
Write::write_all(stream, "e".as_bytes())?;
|
||||
info!("new connection from {}", stream.remote_endpoint());
|
||||
mod local_coremgmt {
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use byteorder::{ByteOrder, NativeEndian};
|
||||
use crc::crc32;
|
||||
use log::LevelFilter;
|
||||
|
||||
loop {
|
||||
match Request::read_from(stream)? {
|
||||
Request::GetLog => {
|
||||
use board_misoc::{config, mem, spiflash};
|
||||
use io::ProtoWrite;
|
||||
use logger_artiq::BufferLogger;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
||||
pub fn get_log(io: &Io, stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
BufferLogger::with(|logger| {
|
||||
let mut buffer = io.until_ok(|| logger.buffer())?;
|
||||
Reply::LogContent(buffer.extract()).write_to(stream)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
Request::ClearLog => {
|
||||
BufferLogger::with(|logger| -> Result<(), Error<SchedError>> {
|
||||
|
||||
pub fn clear_log(io: &Io, stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
BufferLogger::with(|logger| -> Result<(), IoError<SchedError>> {
|
||||
let mut buffer = io.until_ok(|| logger.buffer())?;
|
||||
Ok(buffer.clear())
|
||||
})?;
|
||||
|
||||
Reply::Success.write_to(stream)?;
|
||||
Ok(())
|
||||
}
|
||||
Request::PullLog => {
|
||||
BufferLogger::with(|logger| -> Result<(), Error<SchedError>> {
|
||||
|
||||
pub fn pull_log(io: &Io, stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
BufferLogger::with(|logger| -> Result<(), IoError<SchedError>> {
|
||||
loop {
|
||||
// Do this *before* acquiring the buffer, since that sets the log level
|
||||
// to OFF.
|
||||
@ -63,28 +71,35 @@ fn worker(io: &Io, stream: &mut TcpStream, restart_idle: &Urc<Cell<bool>>) -> Re
|
||||
buffer.clear();
|
||||
}
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
Request::SetLogFilter(level) => {
|
||||
|
||||
pub fn set_log_filter(_io: &Io, stream: &mut TcpStream, level: LevelFilter) -> Result<(), Error<SchedError>> {
|
||||
info!("changing log level to {}", level);
|
||||
log::set_max_level(level);
|
||||
Reply::Success.write_to(stream)?;
|
||||
Ok(())
|
||||
}
|
||||
Request::SetUartLogFilter(level) => {
|
||||
|
||||
pub fn set_uart_log_filter(_io: &Io, stream: &mut TcpStream, level: LevelFilter) -> Result<(), Error<SchedError>> {
|
||||
info!("changing UART log level to {}", level);
|
||||
BufferLogger::with(|logger|
|
||||
logger.set_uart_log_level(level));
|
||||
Reply::Success.write_to(stream)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Request::ConfigRead { ref key } => {
|
||||
pub fn config_read(_io: &Io, stream: &mut TcpStream, key: &String) -> Result<(), Error<SchedError>>{
|
||||
config::read(key, |result| {
|
||||
match result {
|
||||
Ok(value) => Reply::ConfigData(&value).write_to(stream),
|
||||
Err(_) => Reply::Error.write_to(stream)
|
||||
}
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
Request::ConfigWrite { ref key, ref value } => {
|
||||
|
||||
pub fn config_write(io: &Io, stream: &mut TcpStream, key: &String, value: &Vec<u8>, restart_idle: &Urc<Cell<bool>>) -> Result<(), Error<SchedError>> {
|
||||
match config::write(key, value) {
|
||||
Ok(_) => {
|
||||
if key == "idle_kernel" {
|
||||
@ -95,8 +110,10 @@ fn worker(io: &Io, stream: &mut TcpStream, restart_idle: &Urc<Cell<bool>>) -> Re
|
||||
},
|
||||
Err(_) => Reply::Error.write_to(stream)
|
||||
}?;
|
||||
Ok(())
|
||||
}
|
||||
Request::ConfigRemove { ref key } => {
|
||||
|
||||
pub fn config_remove(io: &Io, stream: &mut TcpStream, key: &String, restart_idle: &Urc<Cell<bool>>) -> Result<(), Error<SchedError>> {
|
||||
match config::remove(key) {
|
||||
Ok(()) => {
|
||||
if key == "idle_kernel" {
|
||||
@ -107,9 +124,10 @@ fn worker(io: &Io, stream: &mut TcpStream, restart_idle: &Urc<Cell<bool>>) -> Re
|
||||
},
|
||||
Err(_) => Reply::Error.write_to(stream)
|
||||
}?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Request::ConfigErase => {
|
||||
|
||||
pub fn config_erase(io: &Io, stream: &mut TcpStream, restart_idle: &Urc<Cell<bool>>) -> Result<(), Error<SchedError>> {
|
||||
match config::erase() {
|
||||
Ok(()) => {
|
||||
io.until(|| !restart_idle.get())?;
|
||||
@ -118,9 +136,10 @@ fn worker(io: &Io, stream: &mut TcpStream, restart_idle: &Urc<Cell<bool>>) -> Re
|
||||
},
|
||||
Err(_) => Reply::Error.write_to(stream)
|
||||
}?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Request::Reboot => {
|
||||
pub fn reboot(_io: &Io, stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
Reply::RebootImminent.write_to(stream)?;
|
||||
stream.close()?;
|
||||
stream.flush()?;
|
||||
@ -129,23 +148,524 @@ fn worker(io: &Io, stream: &mut TcpStream, restart_idle: &Urc<Cell<bool>>) -> Re
|
||||
unsafe { spiflash::reload(); }
|
||||
}
|
||||
|
||||
Request::DebugAllocator =>
|
||||
unsafe { println!("{}", ::ALLOC) },
|
||||
pub fn debug_allocator(_io: &Io, _stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
unsafe { println!("{}", ::ALLOC) }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flash(_io: &Io, stream: &mut TcpStream, image: &[u8]) -> Result<(), Error<SchedError>> {
|
||||
let (expected_crc, mut image) = {
|
||||
let (image, crc_slice) = image.split_at(image.len() - 4);
|
||||
(NativeEndian::read_u32(crc_slice), image)
|
||||
};
|
||||
|
||||
let actual_crc = crc32::checksum_ieee(image);
|
||||
|
||||
if actual_crc == expected_crc {
|
||||
let bin_origins = [
|
||||
("gateware" , 0 ),
|
||||
("bootloader", mem::ROM_BASE ),
|
||||
("firmware" , mem::FLASH_BOOT_ADDRESS),
|
||||
];
|
||||
|
||||
for (name, origin) in bin_origins {
|
||||
info!("Flashing {} binary...", name);
|
||||
let size = NativeEndian::read_u32(&image[..4]) as usize;
|
||||
image = &image[4..];
|
||||
|
||||
let (bin, remaining) = image.split_at(size);
|
||||
image = remaining;
|
||||
|
||||
unsafe { spiflash::flash_binary(origin, bin) };
|
||||
}
|
||||
|
||||
reboot(_io, stream)?;
|
||||
} else {
|
||||
error!("CRC failed, images have not been written to flash.\n(actual {:08x}, expected {:08x})", actual_crc, expected_crc);
|
||||
Reply::Error.write_to(stream)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread(io: Io, restart_idle: &Urc<Cell<bool>>) {
|
||||
#[cfg(has_drtio)]
|
||||
mod remote_coremgmt {
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use log::LevelFilter;
|
||||
|
||||
use board_artiq::{drtioaux, drtioaux::Packet};
|
||||
use io::ProtoWrite;
|
||||
use rtio_mgt::drtio;
|
||||
use proto_artiq::drtioaux_proto::MASTER_PAYLOAD_MAX_SIZE;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl From<drtio::Error> for Error<SchedError> {
|
||||
fn from(_value: drtio::Error) -> Error<SchedError> {
|
||||
Error::DrtioError
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_log(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
let mut buffer = String::new();
|
||||
loop {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtGetLogRequest { destination, clear: false }
|
||||
);
|
||||
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtGetLogReply { last, length, data }) => {
|
||||
buffer.push_str(
|
||||
core::str::from_utf8(&data[..length as usize]).map_err(|_| Error::DrtioError)?);
|
||||
if last {
|
||||
Reply::LogContent(&buffer).write_to(stream)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Reply::Error.write_to(stream)?;
|
||||
return Err(drtio::Error::UnexpectedReply.into());
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Reply::Error.write_to(stream)?;
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_log(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtClearLogRequest { destination }
|
||||
);
|
||||
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||
Reply::Success.write_to(stream)?;
|
||||
Ok(())
|
||||
}
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(drtio::Error::UnexpectedReply.into())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pull_log(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
let mut buffer = Vec::new();
|
||||
loop {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtGetLogRequest { destination, clear: true }
|
||||
);
|
||||
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtGetLogReply { last, length, data }) => {
|
||||
buffer.extend(&data[..length as usize]);
|
||||
|
||||
if last {
|
||||
stream.write_bytes(&buffer)?;
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
return Err(drtio::Error::UnexpectedReply.into());
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_log_filter(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, stream: &mut TcpStream, level: LevelFilter) -> Result<(), Error<SchedError>> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtSetLogLevelRequest { destination, log_level: level as u8 }
|
||||
);
|
||||
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||
Reply::Success.write_to(stream)?;
|
||||
Ok(())
|
||||
}
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(drtio::Error::UnexpectedReply.into())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_uart_log_filter(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, stream: &mut TcpStream, level: LevelFilter) -> Result<(), Error<SchedError>> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtSetUartLogLevelRequest { destination, log_level: level as u8 }
|
||||
);
|
||||
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||
Reply::Success.write_to(stream)?;
|
||||
Ok(())
|
||||
}
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(drtio::Error::UnexpectedReply.into())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config_read(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, stream: &mut TcpStream, key: &String) -> Result<(), Error<SchedError>> {
|
||||
let mut config_key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||
let len = key.len();
|
||||
config_key[..len].clone_from_slice(key.as_bytes());
|
||||
|
||||
let mut reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtConfigReadRequest {
|
||||
destination: destination,
|
||||
length: len as u16,
|
||||
key: config_key,
|
||||
}
|
||||
);
|
||||
|
||||
let mut buffer = Vec::<u8>::new();
|
||||
loop {
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtConfigReadReply { length, last, value }) => {
|
||||
buffer.extend(&value[..length as usize]);
|
||||
|
||||
if last {
|
||||
Reply::ConfigData(&buffer).write_to(stream)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtConfigReadContinue {
|
||||
destination: destination,
|
||||
}
|
||||
);
|
||||
}
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Reply::Error.write_to(stream)?;
|
||||
return Err(drtio::Error::UnexpectedReply.into());
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Reply::Error.write_to(stream)?;
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config_write(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, stream: &mut TcpStream, key: &String, value: &Vec<u8>,
|
||||
_restart_idle: &Urc<Cell<bool>>) -> Result<(), Error<SchedError>> {
|
||||
let mut message = Vec::with_capacity(key.len() + value.len() + 4 * 2);
|
||||
message.write_string(key).unwrap();
|
||||
message.write_bytes(value).unwrap();
|
||||
|
||||
match drtio::partition_data(&message, |slice, status, len: usize| {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtConfigWriteRequest {
|
||||
destination: destination, length: len as u16, last: status.is_last(), data: *slice});
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtReply { succeeded: true }) => Ok(()),
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Err(drtio::Error::UnexpectedReply)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Ok(()) => {
|
||||
Reply::Success.write_to(stream)?;
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => {
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(e.into())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config_remove(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, stream: &mut TcpStream, key: &String,
|
||||
_restart_idle: &Urc<Cell<bool>>) -> Result<(), Error<SchedError>> {
|
||||
let mut config_key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||
let len = key.len();
|
||||
config_key[..len].clone_from_slice(key.as_bytes());
|
||||
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtConfigRemoveRequest {
|
||||
destination: destination,
|
||||
length: key.len() as u16,
|
||||
key: config_key,
|
||||
});
|
||||
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||
Reply::Success.write_to(stream)?;
|
||||
Ok(())
|
||||
}
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(drtio::Error::UnexpectedReply.into())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config_erase(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, stream: &mut TcpStream, _restart_idle: &Urc<Cell<bool>>) -> Result<(), Error<SchedError>> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtConfigEraseRequest {
|
||||
destination: destination,
|
||||
});
|
||||
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||
Reply::Success.write_to(stream)?;
|
||||
Ok(())
|
||||
}
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(drtio::Error::UnexpectedReply.into())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reboot(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtRebootRequest {
|
||||
destination: destination,
|
||||
});
|
||||
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtReply { succeeded: true }) => {
|
||||
Reply::RebootImminent.write_to(stream)?;
|
||||
Ok(())
|
||||
}
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(drtio::Error::UnexpectedReply.into())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn debug_allocator(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, _stream: &mut TcpStream) -> Result<(), Error<SchedError>> {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtAllocatorDebugRequest {
|
||||
destination: destination,
|
||||
});
|
||||
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtReply { succeeded: true }) => Ok(()),
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Err(drtio::Error::UnexpectedReply.into())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flash(io: &Io, aux_mutex: &Mutex,
|
||||
ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
|
||||
routing_table: &RoutingTable, linkno: u8,
|
||||
destination: u8, stream: &mut TcpStream, image: &[u8]) -> Result<(), Error<SchedError>> {
|
||||
|
||||
let alloc_reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtFlashRequest {
|
||||
destination: destination,
|
||||
payload_length: image.len() as u32,
|
||||
});
|
||||
|
||||
match alloc_reply {
|
||||
Ok(Packet::CoreMgmtReply { succeeded: true }) => Ok(()),
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(drtio::Error::UnexpectedReply)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(e)
|
||||
}
|
||||
}?;
|
||||
|
||||
match drtio::partition_data(&image, |slice, status, len: usize| {
|
||||
let reply = drtio::aux_transact(io, aux_mutex, ddma_mutex, subkernel_mutex, routing_table, linkno,
|
||||
&Packet::CoreMgmtFlashAddDataRequest {
|
||||
destination: destination, length: len as u16, last: status.is_last(), data: *slice});
|
||||
match reply {
|
||||
Ok(Packet::CoreMgmtReply { succeeded: true }) => Ok(()),
|
||||
Ok(Packet::CoreMgmtDropLink) => {
|
||||
if status.is_last() {
|
||||
drtioaux::send(
|
||||
linkno, &Packet::CoreMgmtDropLinkAck { destination: destination }
|
||||
).map_err(|_| drtio::Error::AuxError)
|
||||
} else {
|
||||
error!("received unexpected drop link packet");
|
||||
Err(drtio::Error::UnexpectedReply)
|
||||
}
|
||||
}
|
||||
Ok(packet) => {
|
||||
error!("received unexpected aux packet: {:?}", packet);
|
||||
Err(drtio::Error::UnexpectedReply)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("aux packet error ({})", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Ok(()) => {
|
||||
Reply::RebootImminent.write_to(stream)?;
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => {
|
||||
Reply::Error.write_to(stream)?;
|
||||
Err(e.into())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(has_drtio)]
|
||||
macro_rules! process {
|
||||
($io:ident, $aux_mutex:ident, $ddma_mutex:ident, $subkernel_mutex:ident, $routing_table:ident, $tcp_stream:ident, $destination: ident, $func:ident $(, $param:expr)*) => {{
|
||||
let hop = $routing_table.0[$destination as usize][0];
|
||||
if hop == 0 {
|
||||
local_coremgmt::$func($io, $tcp_stream, $($param, )*)
|
||||
} else {
|
||||
let linkno = hop - 1;
|
||||
remote_coremgmt::$func($io, $aux_mutex, $ddma_mutex, $subkernel_mutex, $routing_table, linkno, $destination, $tcp_stream, $($param, )*)
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
#[cfg(not(has_drtio))]
|
||||
macro_rules! process {
|
||||
($io:ident, $aux_mutex:ident, $ddma_mutex:ident, $subkernel_mutex:ident, $routing_table:ident, $tcp_stream:ident, $_destination: ident, $func:ident $(, $param:expr)*) => {{
|
||||
local_coremgmt::$func($io, $tcp_stream, $($param, )*)
|
||||
}}
|
||||
}
|
||||
|
||||
fn worker(io: &Io, stream: &mut TcpStream, restart_idle: &Urc<Cell<bool>>,
|
||||
_aux_mutex: &Mutex, _ddma_mutex: &Mutex, _subkernel_mutex: &Mutex,
|
||||
_routing_table: &RoutingTable) -> Result<(), Error<SchedError>> {
|
||||
read_magic(stream)?;
|
||||
let _destination = stream.read_u8()?;
|
||||
Write::write_all(stream, "e".as_bytes())?;
|
||||
info!("new connection from {}", stream.remote_endpoint());
|
||||
|
||||
loop {
|
||||
match Request::read_from(stream)? {
|
||||
Request::GetLog => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, get_log),
|
||||
Request::ClearLog => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, clear_log),
|
||||
Request::PullLog => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, pull_log),
|
||||
Request::SetLogFilter(level) => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, set_log_filter, level),
|
||||
Request::SetUartLogFilter(level) => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, set_uart_log_filter, level),
|
||||
Request::ConfigRead { ref key } => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, config_read, key),
|
||||
Request::ConfigWrite { ref key, ref value } => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, config_write, key, value, restart_idle),
|
||||
Request::ConfigRemove { ref key } => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, config_remove, key, restart_idle),
|
||||
Request::ConfigErase => process!(io, _aux_mutex, _ddma_mutex, _subkernel_mutex, _routing_table, stream, _destination, config_erase, restart_idle),
|
||||
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[..]),
|
||||
}?;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread(io: Io, restart_idle: &Urc<Cell<bool>>, aux_mutex: &Mutex, ddma_mutex: &Mutex, subkernel_mutex: &Mutex, routing_table: &Urc<RefCell<RoutingTable>>) {
|
||||
let listener = TcpListener::new(&io, 8192);
|
||||
listener.listen(1380).expect("mgmt: cannot listen");
|
||||
info!("management interface active");
|
||||
|
||||
loop {
|
||||
let stream = listener.accept().expect("mgmt: cannot accept").into_handle();
|
||||
let restart_idle = restart_idle.clone();
|
||||
io.spawn(4096, move |io| {
|
||||
let aux_mutex = aux_mutex.clone();
|
||||
let ddma_mutex = ddma_mutex.clone();
|
||||
let subkernel_mutex = subkernel_mutex.clone();
|
||||
let routing_table = routing_table.clone();
|
||||
let stream = listener.accept().expect("mgmt: cannot accept").into_handle();
|
||||
io.spawn(16384, move |io| {
|
||||
let routing_table = routing_table.borrow();
|
||||
let mut stream = TcpStream::from_handle(&io, stream);
|
||||
match worker(&io, &mut stream, &restart_idle) {
|
||||
match worker(&io, &mut stream, &restart_idle, &aux_mutex, &ddma_mutex, &subkernel_mutex, &routing_table) {
|
||||
Ok(()) => (),
|
||||
Err(Error::Io(IoError::UnexpectedEnd)) => (),
|
||||
Err(err) => error!("aborted: {}", err)
|
||||
|
@ -506,7 +506,7 @@ pub mod drtio {
|
||||
}
|
||||
}
|
||||
|
||||
fn partition_data<F>(data: &[u8], send_f: F) -> Result<(), Error>
|
||||
pub fn partition_data<F>(data: &[u8], send_f: F) -> Result<(), Error>
|
||||
where F: Fn(&[u8; MASTER_PAYLOAD_MAX_SIZE], PayloadStatus, usize) -> Result<(), Error> {
|
||||
let mut i = 0;
|
||||
while i < data.len() {
|
||||
|
@ -15,9 +15,12 @@ build_misoc = { path = "../libbuild_misoc" }
|
||||
[dependencies]
|
||||
log = { version = "0.4", default-features = false }
|
||||
io = { path = "../libio", features = ["byteorder", "alloc"] }
|
||||
byteorder = { version = "1.0", default-features = false }
|
||||
crc = { version = "1.7", default-features = false }
|
||||
cslice = { version = "0.3" }
|
||||
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "log"] }
|
||||
board_artiq = { path = "../libboard_artiq", features = ["alloc"] }
|
||||
logger_artiq = { path = "../liblogger_artiq" }
|
||||
alloc_list = { path = "../liballoc_list" }
|
||||
riscv = { version = "0.6.0", features = ["inline-asm"] }
|
||||
proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] }
|
||||
|
@ -6,21 +6,25 @@ extern crate log;
|
||||
#[macro_use]
|
||||
extern crate board_misoc;
|
||||
extern crate board_artiq;
|
||||
extern crate logger_artiq;
|
||||
extern crate riscv;
|
||||
extern crate alloc;
|
||||
extern crate proto_artiq;
|
||||
extern crate byteorder;
|
||||
extern crate crc;
|
||||
extern crate cslice;
|
||||
extern crate io;
|
||||
extern crate eh;
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use board_misoc::{csr, ident, clock, config, uart_logger, i2c, pmp};
|
||||
use board_misoc::{csr, ident, clock, config, i2c, pmp};
|
||||
#[cfg(has_si5324)]
|
||||
use board_artiq::si5324;
|
||||
#[cfg(has_si549)]
|
||||
use board_artiq::si549;
|
||||
#[cfg(soc_platform = "kasli")]
|
||||
use board_misoc::irq;
|
||||
use board_misoc::{boot, spiflash};
|
||||
use board_artiq::{spi, drtioaux, drtio_routing};
|
||||
#[cfg(soc_platform = "efc")]
|
||||
use board_artiq::ad9117;
|
||||
@ -30,6 +34,7 @@ use board_artiq::drtio_eem;
|
||||
use riscv::register::{mcause, mepc, mtval};
|
||||
use dma::Manager as DmaManager;
|
||||
use kernel::Manager as KernelManager;
|
||||
use mgmt::Manager as CoreManager;
|
||||
use analyzer::Analyzer;
|
||||
|
||||
#[global_allocator]
|
||||
@ -41,6 +46,7 @@ mod dma;
|
||||
mod analyzer;
|
||||
mod kernel;
|
||||
mod cache;
|
||||
mod mgmt;
|
||||
|
||||
fn drtiosat_reset(reset: bool) {
|
||||
unsafe {
|
||||
@ -129,7 +135,7 @@ macro_rules! forward {
|
||||
($router:expr, $routing_table:expr, $destination:expr, $rank:expr, $self_destination:expr, $repeaters:expr, $packet:expr) => {}
|
||||
}
|
||||
|
||||
fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmgr: &mut KernelManager,
|
||||
fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmgr: &mut KernelManager, coremgr: &mut CoreManager,
|
||||
_repeaters: &mut [repeater::Repeater], _routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8,
|
||||
router: &mut routing::Router, self_destination: &mut u8, packet: drtioaux::Packet
|
||||
) -> Result<(), drtioaux::Error<!>> {
|
||||
@ -495,6 +501,167 @@ fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmg
|
||||
Ok(())
|
||||
}
|
||||
|
||||
drtioaux::Packet::CoreMgmtGetLogRequest { destination: _destination, clear } => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
let mut data_slice = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||
if let Ok(meta) = coremgr.log_get_slice(&mut data_slice, clear) {
|
||||
drtioaux::send(
|
||||
0,
|
||||
&drtioaux::Packet::CoreMgmtGetLogReply {
|
||||
last: meta.status.is_last(),
|
||||
length: meta.len as u16,
|
||||
data: data_slice,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
}
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtClearLogRequest { destination: _destination } => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: mgmt::clear_log().is_ok() })
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtSetLogLevelRequest {destination: _destination, log_level } => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
if let Ok(level_filter) = mgmt::byte_to_level_filter(log_level) {
|
||||
info!("changing log level to {}", level_filter);
|
||||
log::set_max_level(level_filter);
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })
|
||||
} else {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
}
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtSetUartLogLevelRequest { destination: _destination, log_level } => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
if let Ok(level_filter) = mgmt::byte_to_level_filter(log_level) {
|
||||
info!("changing UART log level to {}", level_filter);
|
||||
logger_artiq::BufferLogger::with(|logger|
|
||||
logger.set_uart_log_level(level_filter));
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })
|
||||
} else {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
}
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtConfigReadRequest {
|
||||
destination: _destination,
|
||||
length,
|
||||
key,
|
||||
} => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
let mut value_slice = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||
|
||||
let key_slice = &key[..length as usize];
|
||||
if !key_slice.is_ascii() {
|
||||
error!("invalid key");
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
} else {
|
||||
let key = core::str::from_utf8(key_slice).unwrap();
|
||||
if coremgr.fetch_config_value(key).is_ok() {
|
||||
let meta = coremgr.get_config_value_slice(&mut value_slice);
|
||||
drtioaux::send(
|
||||
0,
|
||||
&drtioaux::Packet::CoreMgmtConfigReadReply {
|
||||
length: meta.len as u16,
|
||||
last: meta.status.is_last(),
|
||||
value: value_slice,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false })
|
||||
}
|
||||
}
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtConfigReadContinue {
|
||||
destination: _destination,
|
||||
} => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
let mut value_slice = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||
let meta = coremgr.get_config_value_slice(&mut value_slice);
|
||||
drtioaux::send(
|
||||
0,
|
||||
&drtioaux::Packet::CoreMgmtConfigReadReply {
|
||||
length: meta.len as u16,
|
||||
last: meta.status.is_last(),
|
||||
value: value_slice,
|
||||
},
|
||||
)
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtConfigWriteRequest { destination: _destination, last, length, data } => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
coremgr.add_config_data(&data, length as usize);
|
||||
if last {
|
||||
coremgr.write_config()
|
||||
} else {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })
|
||||
}
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtConfigRemoveRequest { destination: _destination, length, key } => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
let key = core::str::from_utf8(&key[..length as usize]).unwrap();
|
||||
let succeeded = config::remove(key)
|
||||
.map_err(|err| warn!("error on removing config: {:?}", err))
|
||||
.is_ok();
|
||||
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded })
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtConfigEraseRequest { destination: _destination } => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
let succeeded = config::erase()
|
||||
.map_err(|err| warn!("error on erasing config: {:?}", err))
|
||||
.is_ok();
|
||||
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded })
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtRebootRequest { destination: _destination } => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })?;
|
||||
warn!("restarting");
|
||||
unsafe { spiflash::reload(); }
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtFlashRequest { destination: _destination, payload_length } => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
coremgr.allocate_image_buffer(payload_length as usize);
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtFlashAddDataRequest { destination: _destination, last, length, data } => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
coremgr.add_image_data(&data, length as usize);
|
||||
if last {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtDropLink)
|
||||
} else {
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })
|
||||
}
|
||||
}
|
||||
drtioaux::Packet::CoreMgmtDropLinkAck { destination: _destination } => {
|
||||
forward!(router, _routing_table, _destination, *rank, *self_destination, _repeaters, &packet);
|
||||
|
||||
#[cfg(not(has_drtio_eem))]
|
||||
unsafe {
|
||||
csr::gt_drtio::txenable_write(0);
|
||||
}
|
||||
|
||||
#[cfg(has_drtio_eem)]
|
||||
unsafe {
|
||||
csr::eem_transceiver::txenable_write(0);
|
||||
}
|
||||
|
||||
coremgr.flash_image();
|
||||
warn!("restarting");
|
||||
unsafe { spiflash::reload(); }
|
||||
}
|
||||
|
||||
_ => {
|
||||
warn!("received unexpected aux packet");
|
||||
Ok(())
|
||||
@ -503,13 +670,13 @@ fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmg
|
||||
}
|
||||
|
||||
fn process_aux_packets(dma_manager: &mut DmaManager, analyzer: &mut Analyzer,
|
||||
kernelmgr: &mut KernelManager, repeaters: &mut [repeater::Repeater],
|
||||
kernelmgr: &mut KernelManager, coremgr: &mut CoreManager, repeaters: &mut [repeater::Repeater],
|
||||
routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8, router: &mut routing::Router,
|
||||
destination: &mut u8) {
|
||||
let result =
|
||||
drtioaux::recv(0).and_then(|packet| {
|
||||
if let Some(packet) = packet.or_else(|| router.get_local_packet()) {
|
||||
process_aux_packet(dma_manager, analyzer, kernelmgr,
|
||||
process_aux_packet(dma_manager, analyzer, kernelmgr, coremgr,
|
||||
repeaters, routing_table, rank, router, destination, packet)
|
||||
} else {
|
||||
Ok(())
|
||||
@ -664,6 +831,27 @@ fn sysclk_setup() {
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_log_levels() {
|
||||
match config::read_str("log_level", |r| r.map(|s| s.parse())) {
|
||||
Ok(Ok(log_level_filter)) => {
|
||||
info!("log level set to {} by `log_level` config key",
|
||||
log_level_filter);
|
||||
log::set_max_level(log_level_filter);
|
||||
}
|
||||
_ => info!("log level set to INFO by default")
|
||||
}
|
||||
match config::read_str("uart_log_level", |r| r.map(|s| s.parse())) {
|
||||
Ok(Ok(uart_log_level_filter)) => {
|
||||
info!("UART log level set to {} by `uart_log_level` config key",
|
||||
uart_log_level_filter);
|
||||
logger_artiq::BufferLogger::with(|logger|
|
||||
logger.set_uart_log_level(uart_log_level_filter));
|
||||
}
|
||||
_ => info!("UART log level set to INFO by default")
|
||||
}
|
||||
}
|
||||
|
||||
static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17];
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn main() -> i32 {
|
||||
@ -683,12 +871,21 @@ pub extern fn main() -> i32 {
|
||||
irq::enable(csr::WRPLL_INTERRUPT);
|
||||
|
||||
clock::init();
|
||||
uart_logger::ConsoleLogger::register();
|
||||
unsafe {
|
||||
logger_artiq::BufferLogger::new(&mut LOG_BUFFER[..]).register(||
|
||||
boot::start_user(startup as usize));
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn startup() {
|
||||
info!("ARTIQ satellite manager starting...");
|
||||
info!("software ident {}", csr::CONFIG_IDENTIFIER_STR);
|
||||
info!("gateware ident {}", ident::read(&mut [0; 64]));
|
||||
|
||||
setup_log_levels();
|
||||
|
||||
#[cfg(has_i2c)]
|
||||
i2c::init().expect("I2C initialization failed");
|
||||
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
|
||||
@ -834,6 +1031,7 @@ pub extern fn main() -> i32 {
|
||||
let mut dma_manager = DmaManager::new();
|
||||
let mut analyzer = Analyzer::new();
|
||||
let mut kernelmgr = KernelManager::new();
|
||||
let mut coremgr = CoreManager::new();
|
||||
|
||||
cricon_select(RtioMaster::Drtio);
|
||||
drtioaux::reset(0);
|
||||
@ -843,7 +1041,7 @@ pub extern fn main() -> i32 {
|
||||
while drtiosat_link_rx_up() {
|
||||
drtiosat_process_errors();
|
||||
process_aux_packets(&mut dma_manager, &mut analyzer,
|
||||
&mut kernelmgr, &mut repeaters, &mut routing_table,
|
||||
&mut kernelmgr, &mut coremgr, &mut repeaters, &mut routing_table,
|
||||
&mut rank, &mut router, &mut destination);
|
||||
for rep in repeaters.iter_mut() {
|
||||
rep.service(&routing_table, rank, destination, &mut router);
|
||||
|
149
artiq/firmware/satman/mgmt.rs
Normal file
149
artiq/firmware/satman/mgmt.rs
Normal file
@ -0,0 +1,149 @@
|
||||
use alloc::vec::Vec;
|
||||
use byteorder::{ByteOrder, NativeEndian};
|
||||
use crc::crc32;
|
||||
|
||||
use routing::{Sliceable, SliceMeta};
|
||||
use board_artiq::drtioaux;
|
||||
use board_misoc::{mem, config, spiflash};
|
||||
use log::LevelFilter;
|
||||
use logger_artiq::BufferLogger;
|
||||
use io::{Cursor, ProtoRead, ProtoWrite};
|
||||
use proto_artiq::drtioaux_proto::SAT_PAYLOAD_MAX_SIZE;
|
||||
|
||||
|
||||
pub fn clear_log() -> Result<(), ()> {
|
||||
BufferLogger::with(|logger| {
|
||||
let mut buffer = logger.buffer()?;
|
||||
Ok(buffer.clear())
|
||||
}).map_err(|()| error!("error on clearing log buffer"))
|
||||
}
|
||||
|
||||
pub fn byte_to_level_filter(level_byte: u8) -> Result<LevelFilter, ()> {
|
||||
Ok(match level_byte {
|
||||
0 => LevelFilter::Off,
|
||||
1 => LevelFilter::Error,
|
||||
2 => LevelFilter::Warn,
|
||||
3 => LevelFilter::Info,
|
||||
4 => LevelFilter::Debug,
|
||||
5 => LevelFilter::Trace,
|
||||
lv => {
|
||||
error!("unknown log level: {}", lv);
|
||||
return Err(());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub struct Manager {
|
||||
config_payload: Cursor<Vec<u8>>,
|
||||
image_payload: Cursor<Vec<u8>>,
|
||||
last_value: Sliceable,
|
||||
last_log: Sliceable,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
pub fn new() -> Manager {
|
||||
Manager {
|
||||
config_payload: Cursor::new(Vec::new()),
|
||||
image_payload: Cursor::new(Vec::new()),
|
||||
last_value: Sliceable::new(0, Vec::new()),
|
||||
last_log: Sliceable::new(0, Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fetch_config_value(&mut self, key: &str) -> Result<(), ()> {
|
||||
config::read(key, |result| result.map(
|
||||
|value| self.last_value = Sliceable::new(0, value.to_vec())
|
||||
)).map_err(|_err| warn!("read error: no such key"))
|
||||
}
|
||||
|
||||
pub fn log_get_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE], consume: bool) -> Result<SliceMeta, ()> {
|
||||
// Populate buffer if depleted
|
||||
if self.last_log.at_end() {
|
||||
BufferLogger::with(|logger| {
|
||||
let mut buffer = logger.buffer()?;
|
||||
self.last_log = Sliceable::new(0, buffer.extract().as_bytes().to_vec());
|
||||
if consume {
|
||||
buffer.clear();
|
||||
}
|
||||
Ok(())
|
||||
}).map_err(|()| error!("error on getting log buffer"))?;
|
||||
}
|
||||
|
||||
Ok(self.last_log.get_slice_satellite(data_slice))
|
||||
}
|
||||
|
||||
pub fn get_config_value_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> SliceMeta {
|
||||
self.last_value.get_slice_satellite(data_slice)
|
||||
}
|
||||
|
||||
pub fn add_config_data(&mut self, data: &[u8], data_len: usize) {
|
||||
self.config_payload.write_all(&data[..data_len]).unwrap();
|
||||
}
|
||||
|
||||
pub fn clear_config_data(&mut self) {
|
||||
self.config_payload.get_mut().clear();
|
||||
self.config_payload.set_position(0);
|
||||
}
|
||||
|
||||
pub fn write_config(&mut self) -> Result<(), drtioaux::Error<!>> {
|
||||
let key = match self.config_payload.read_string() {
|
||||
Ok(key) => key,
|
||||
Err(err) => {
|
||||
self.clear_config_data();
|
||||
error!("error on reading key: {:?}", err);
|
||||
return drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false });
|
||||
}
|
||||
};
|
||||
|
||||
let value = self.config_payload.read_bytes().unwrap();
|
||||
|
||||
let succeeded = config::write(&key, &value).map_err(|err| {
|
||||
error!("error on writing config: {:?}", err);
|
||||
}).is_ok();
|
||||
|
||||
self.clear_config_data();
|
||||
|
||||
drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded })
|
||||
}
|
||||
|
||||
pub fn allocate_image_buffer(&mut self, image_size: usize) {
|
||||
self.image_payload = Cursor::new(Vec::with_capacity(image_size));
|
||||
}
|
||||
|
||||
pub fn add_image_data(&mut self, data: &[u8], data_len: usize) {
|
||||
self.image_payload.write_all(&data[..data_len]).unwrap();
|
||||
}
|
||||
|
||||
pub fn flash_image(&self) {
|
||||
let image = &self.image_payload.get_ref()[..];
|
||||
|
||||
let (expected_crc, mut image) = {
|
||||
let (image, crc_slice) = image.split_at(image.len() - 4);
|
||||
(NativeEndian::read_u32(crc_slice), image)
|
||||
};
|
||||
|
||||
let actual_crc = crc32::checksum_ieee(image);
|
||||
|
||||
if actual_crc == expected_crc {
|
||||
let bin_origins = [
|
||||
("gateware" , 0 ),
|
||||
("bootloader", mem::ROM_BASE ),
|
||||
("firmware" , mem::FLASH_BOOT_ADDRESS),
|
||||
];
|
||||
|
||||
for (name, origin) in bin_origins {
|
||||
info!("flashing {} binary...", name);
|
||||
let size = NativeEndian::read_u32(&image[..4]) as usize;
|
||||
image = &image[4..];
|
||||
|
||||
let (bin, remaining) = image.split_at(size);
|
||||
image = remaining;
|
||||
|
||||
unsafe { spiflash::flash_binary(origin, bin) };
|
||||
}
|
||||
|
||||
} else {
|
||||
panic!("CRC failed, images have not been written to flash.\n(actual {:08x}, expected {:08x})", actual_crc, expected_crc);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ use board_artiq::{drtioaux, drtio_routing};
|
||||
use board_misoc::csr;
|
||||
use core::cmp::min;
|
||||
use proto_artiq::drtioaux_proto::PayloadStatus;
|
||||
use SAT_PAYLOAD_MAX_SIZE;
|
||||
use MASTER_PAYLOAD_MAX_SIZE;
|
||||
|
||||
/* represents data that has to be sent with the aux protocol */
|
||||
@ -56,6 +57,7 @@ impl Sliceable {
|
||||
self.data.extend(data);
|
||||
}
|
||||
|
||||
get_slice_fn!(get_slice_satellite, SAT_PAYLOAD_MAX_SIZE);
|
||||
get_slice_fn!(get_slice_master, MASTER_PAYLOAD_MAX_SIZE);
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,9 @@ def get_argparser():
|
||||
help="Simulation - does not connect to device")
|
||||
parser.add_argument("core_addr", metavar="CORE_ADDR",
|
||||
help="hostname or IP address of the core device")
|
||||
parser.add_argument("-s", "--drtio-dest", default=0,
|
||||
metavar="DRTIO_DEST", type=int,
|
||||
help="specifies the DRTIO destination")
|
||||
return parser
|
||||
|
||||
|
||||
@ -39,7 +42,7 @@ async def get_logs_sim(host):
|
||||
log_with_name("firmware.simulation", logging.INFO, "hello " + host)
|
||||
|
||||
|
||||
async def get_logs(host):
|
||||
async def get_logs(host, drtio_dest):
|
||||
try:
|
||||
reader, writer = await async_open_connection(
|
||||
host,
|
||||
@ -49,6 +52,7 @@ async def get_logs(host):
|
||||
max_fails=3,
|
||||
)
|
||||
writer.write(b"ARTIQ management\n")
|
||||
writer.write(drtio_dest.to_bytes(1))
|
||||
endian = await reader.readexactly(1)
|
||||
if endian == b"e":
|
||||
endian = "<"
|
||||
@ -96,7 +100,7 @@ def main():
|
||||
signal_handler.setup()
|
||||
try:
|
||||
get_logs_task = asyncio.ensure_future(
|
||||
get_logs_sim(args.core_addr) if args.simulation else get_logs(args.core_addr),
|
||||
get_logs_sim(args.core_addr) if args.simulation else get_logs(args.core_addr, args.drtio_dest),
|
||||
loop=loop)
|
||||
try:
|
||||
server = Server({"corelog": PingTarget()}, None, True)
|
||||
|
@ -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,7 @@ 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.flash_tools import bit2bin, fetch_bin
|
||||
|
||||
|
||||
def get_argparser():
|
||||
@ -85,6 +89,20 @@ 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")
|
||||
|
||||
p_srcbuild = t_flash.add_argument("--srcbuild",
|
||||
help="board binaries directory is laid "
|
||||
"out as a source build tree",
|
||||
default=False, action="store_true")
|
||||
|
||||
# misc debug
|
||||
t_debug = tools.add_parser("debug",
|
||||
help="specialized debug functions")
|
||||
@ -95,6 +113,12 @@ def get_argparser():
|
||||
p_allocator = subparsers.add_parser("allocator",
|
||||
help="show heap layout")
|
||||
|
||||
# manage target
|
||||
p_drtio_dest = parser.add_argument("-s", "--drtio-dest", default=0,
|
||||
metavar="DRTIO_DEST", type=int,
|
||||
help="specify DRTIO destination that "
|
||||
"receives this command")
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
@ -107,7 +131,7 @@ def main():
|
||||
core_addr = ddb.get("core", resolve_alias=True)["arguments"]["host"]
|
||||
else:
|
||||
core_addr = args.device
|
||||
mgmt = CommMgmt(core_addr)
|
||||
mgmt = CommMgmt(core_addr, drtio_dest=args.drtio_dest)
|
||||
|
||||
if args.tool == "log":
|
||||
if args.action == "set_level":
|
||||
@ -138,6 +162,39 @@ def main():
|
||||
if args.action == "erase":
|
||||
mgmt.config_erase()
|
||||
|
||||
if args.tool == "flash":
|
||||
retrieved_bins = []
|
||||
bin_dict = {
|
||||
"zynq":[
|
||||
["boot"]
|
||||
],
|
||||
"riscv": [
|
||||
["gateware"],
|
||||
["bootloader"],
|
||||
["runtime", "satman"],
|
||||
],
|
||||
}
|
||||
|
||||
for bin_list in bin_dict.values():
|
||||
try:
|
||||
bins = []
|
||||
for bin_name in bin_list:
|
||||
bins.append(fetch_bin(
|
||||
args.directory, bin_name, args.srcbuild))
|
||||
retrieved_bins.append(bins)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
if retrieved_bins is None:
|
||||
raise FileNotFoundError("neither risc-v nor zynq binaries were found")
|
||||
|
||||
if len(retrieved_bins) > 1:
|
||||
raise ValueError("both risc-v and zynq binaries were found, "
|
||||
"please clean up your build directory. ")
|
||||
|
||||
bins = retrieved_bins[0]
|
||||
mgmt.flash(bins)
|
||||
|
||||
if args.tool == "reboot":
|
||||
mgmt.reboot()
|
||||
|
||||
|
@ -14,7 +14,7 @@ from sipyco import common_args
|
||||
|
||||
from artiq import __version__ as artiq_version
|
||||
from artiq.remoting import SSHClient, LocalClient
|
||||
from artiq.frontend.bit2bin import bit2bin
|
||||
from artiq.frontend.flash_tools import artifact_path, bit2bin, fetch_bin
|
||||
|
||||
|
||||
def get_argparser():
|
||||
@ -302,46 +302,19 @@ def main():
|
||||
|
||||
programmer = config["programmer"](client, preinit_script=args.preinit_command)
|
||||
|
||||
def artifact_path(this_binary_dir, *path_filename):
|
||||
if args.srcbuild:
|
||||
# source tree - use path elements to locate file
|
||||
return os.path.join(this_binary_dir, *path_filename)
|
||||
else:
|
||||
# flat tree - all files in the same directory, discard path elements
|
||||
*_, filename = path_filename
|
||||
return os.path.join(this_binary_dir, filename)
|
||||
|
||||
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
|
||||
|
||||
for action in args.action:
|
||||
if action == "gateware":
|
||||
gateware_bin = convert_gateware(
|
||||
artifact_path(binary_dir, "gateware", "top.bit"))
|
||||
gateware_bin = fetch_bin(binary_dir, ["gateware"], args.srcbuild)
|
||||
programmer.write_binary(*config["gateware"], gateware_bin)
|
||||
elif action == "bootloader":
|
||||
bootloader_bin = artifact_path(binary_dir, "software", "bootloader", "bootloader.bin")
|
||||
bootloader_bin = fetch_bin(binary_dir, ["bootloader"], args.srcbuild)
|
||||
programmer.write_binary(*config["bootloader"], bootloader_bin)
|
||||
elif action == "storage":
|
||||
storage_img = args.storage
|
||||
programmer.write_binary(*config["storage"], storage_img)
|
||||
elif action == "firmware":
|
||||
firmware_fbis = []
|
||||
for firmware in "satman", "runtime":
|
||||
filename = artifact_path(binary_dir, "software", firmware, firmware + ".fbi")
|
||||
if os.path.exists(filename):
|
||||
firmware_fbis.append(filename)
|
||||
if not firmware_fbis:
|
||||
raise FileNotFoundError("no firmware found")
|
||||
if len(firmware_fbis) > 1:
|
||||
raise ValueError("more than one firmware file, please clean up your build directory. "
|
||||
"Found firmware files: {}".format(" ".join(firmware_fbis)))
|
||||
programmer.write_binary(*config["firmware"], firmware_fbis[0])
|
||||
firmware_fbi = fetch_bin(binary_dir, ["satman", "runtime"], args.srcbuild)
|
||||
programmer.write_binary(*config["firmware"], firmware_fbi)
|
||||
elif action == "load":
|
||||
gateware_bit = artifact_path(binary_dir, "gateware", "top.bit")
|
||||
programmer.load(gateware_bit, 0)
|
||||
|
@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2014-2017 Robert Jordens <jordens@gmail.com>
|
||||
# after
|
||||
# https://github.com/mfischer/fpgadev-zynq/blob/master/top/python/bit_to_zynq_bin.py
|
||||
|
||||
import struct
|
||||
|
||||
|
||||
def flip32(data):
|
||||
sl = struct.Struct("<I")
|
||||
sb = struct.Struct(">I")
|
||||
b = memoryview(data)
|
||||
d = bytearray(len(data))
|
||||
for offset in range(0, len(data), sl.size):
|
||||
sb.pack_into(d, offset, *sl.unpack_from(b, offset))
|
||||
return d
|
||||
|
||||
|
||||
def bit2bin(bit, bin, flip=False):
|
||||
l, = struct.unpack(">H", bit.read(2))
|
||||
if l != 9:
|
||||
raise ValueError("Missing <0009> header, not a bit file")
|
||||
_ = bit.read(l) # unknown data
|
||||
l, = struct.unpack(">H", bit.read(2))
|
||||
if l != 1:
|
||||
raise ValueError("Missing <0001> header, not a bit file")
|
||||
|
||||
while True:
|
||||
key = bit.read(1).decode()
|
||||
if not key:
|
||||
break
|
||||
if key in "abcd":
|
||||
d = bit.read(*struct.unpack(">H", bit.read(2)))
|
||||
assert d.endswith(b"\x00")
|
||||
d = d[:-1].decode()
|
||||
name = {
|
||||
"a": "Design",
|
||||
"b": "Part name",
|
||||
"c": "Date",
|
||||
"d": "Time"
|
||||
}[key]
|
||||
print("{}: {}".format(name, d))
|
||||
elif key == "e":
|
||||
l, = struct.unpack(">I", bit.read(4))
|
||||
print("Bitstream payload length: {:#x}".format(l))
|
||||
d = bit.read(l)
|
||||
if flip:
|
||||
d = flip32(d)
|
||||
bin.write(d)
|
||||
else:
|
||||
d = bit.read(*struct.unpack(">H", bit.read(2)))
|
||||
print("Unexpected key: {}: {}".format(key, d))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Convert FPGA bit files to raw bin format "
|
||||
"suitable for flashing")
|
||||
parser.add_argument("-f", "--flip", dest="flip", action="store_true",
|
||||
default=False, help="Flip 32-bit endianess (needed for Zynq)")
|
||||
parser.add_argument("bitfile", metavar="BITFILE",
|
||||
help="Input bit file name")
|
||||
parser.add_argument("binfile", metavar="BINFILE",
|
||||
help="Output bin file name")
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.bitfile, "rb") as f, open(args.binfile, "wb") as g:
|
||||
bit2bin(f, g, args.flip)
|
112
artiq/frontend/flash_tools.py
Normal file
112
artiq/frontend/flash_tools.py
Normal file
@ -0,0 +1,112 @@
|
||||
import atexit
|
||||
import os
|
||||
import tempfile
|
||||
import struct
|
||||
|
||||
|
||||
def artifact_path(this_binary_dir, *path_filename, srcbuild=False):
|
||||
if srcbuild:
|
||||
# source tree - use path elements to locate file
|
||||
return os.path.join(this_binary_dir, *path_filename)
|
||||
else:
|
||||
# flat tree - all files in the same directory, discard path elements
|
||||
*_, filename = path_filename
|
||||
return os.path.join(this_binary_dir, filename)
|
||||
|
||||
|
||||
def fetch_bin(binary_dir, components, srcbuild=False):
|
||||
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
|
||||
|
||||
if len(components) > 1:
|
||||
bins = []
|
||||
for option in components:
|
||||
try:
|
||||
bins.append(fetch_bin(binary_dir, [option], srcbuild))
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
if bins is None:
|
||||
raise FileNotFoundError("multiple components not found: {}".format(
|
||||
" ".join(components)))
|
||||
|
||||
if len(bins) > 1:
|
||||
raise ValueError("more than one file, "
|
||||
"please clean up your build directory. "
|
||||
"Found files: {}".format(
|
||||
" ".join(bins)))
|
||||
|
||||
return bins[0]
|
||||
|
||||
else:
|
||||
component = components[0]
|
||||
path = artifact_path(binary_dir, *{
|
||||
"gateware": ["gateware", "top.bit"],
|
||||
"boot": ["boot.bin"],
|
||||
"bootloader": ["software", "bootloader", "bootloader.bin"],
|
||||
"runtime": ["software", "runtime", "runtime.fbi"],
|
||||
"satman": ["software", "satman", "satman.fbi"],
|
||||
}[component], srcbuild=srcbuild)
|
||||
|
||||
if not os.path.exists(path):
|
||||
raise FileNotFoundError("{} not found".format(component))
|
||||
|
||||
if component == "gateware":
|
||||
path = convert_gateware(path)
|
||||
|
||||
return path
|
||||
|
||||
|
||||
# Copyright 2014-2017 Robert Jordens <jordens@gmail.com>
|
||||
# after
|
||||
# https://github.com/mfischer/fpgadev-zynq/blob/master/top/python/bit_to_zynq_bin.py
|
||||
|
||||
def flip32(data):
|
||||
sl = struct.Struct("<I")
|
||||
sb = struct.Struct(">I")
|
||||
b = memoryview(data)
|
||||
d = bytearray(len(data))
|
||||
for offset in range(0, len(data), sl.size):
|
||||
sb.pack_into(d, offset, *sl.unpack_from(b, offset))
|
||||
return d
|
||||
|
||||
|
||||
def bit2bin(bit, bin, flip=False):
|
||||
l, = struct.unpack(">H", bit.read(2))
|
||||
if l != 9:
|
||||
raise ValueError("Missing <0009> header, not a bit file")
|
||||
_ = bit.read(l) # unknown data
|
||||
l, = struct.unpack(">H", bit.read(2))
|
||||
if l != 1:
|
||||
raise ValueError("Missing <0001> header, not a bit file")
|
||||
|
||||
while True:
|
||||
key = bit.read(1).decode()
|
||||
if not key:
|
||||
break
|
||||
if key in "abcd":
|
||||
d = bit.read(*struct.unpack(">H", bit.read(2)))
|
||||
assert d.endswith(b"\x00")
|
||||
d = d[:-1].decode()
|
||||
name = {
|
||||
"a": "Design",
|
||||
"b": "Part name",
|
||||
"c": "Date",
|
||||
"d": "Time"
|
||||
}[key]
|
||||
print("{}: {}".format(name, d))
|
||||
elif key == "e":
|
||||
l, = struct.unpack(">I", bit.read(4))
|
||||
print("Bitstream payload length: {:#x}".format(l))
|
||||
d = bit.read(l)
|
||||
if flip:
|
||||
d = flip32(d)
|
||||
bin.write(d)
|
||||
else:
|
||||
d = bit.read(*struct.unpack(">H", bit.read(2)))
|
||||
print("Unexpected key: {}: {}".format(key, d))
|
Loading…
Reference in New Issue
Block a user