forked from M-Labs/artiq
satman: support subkernels
This commit is contained in:
parent
e05be2f8e4
commit
1a0fc317df
|
@ -352,6 +352,9 @@ dependencies = [
|
||||||
"board_artiq",
|
"board_artiq",
|
||||||
"board_misoc",
|
"board_misoc",
|
||||||
"build_misoc",
|
"build_misoc",
|
||||||
|
"cslice",
|
||||||
|
"eh",
|
||||||
|
"io",
|
||||||
"log",
|
"log",
|
||||||
"proto_artiq",
|
"proto_artiq",
|
||||||
"riscv",
|
"riscv",
|
||||||
|
|
|
@ -14,8 +14,11 @@ build_misoc = { path = "../libbuild_misoc" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = { version = "0.4", default-features = false }
|
log = { version = "0.4", default-features = false }
|
||||||
|
io = { path = "../libio", features = ["byteorder", "alloc"] }
|
||||||
|
cslice = { version = "0.3" }
|
||||||
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "log"] }
|
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "log"] }
|
||||||
board_artiq = { path = "../libboard_artiq", features = ["alloc"] }
|
board_artiq = { path = "../libboard_artiq", features = ["alloc"] }
|
||||||
alloc_list = { path = "../liballoc_list" }
|
alloc_list = { path = "../liballoc_list" }
|
||||||
riscv = { version = "0.6.0", features = ["inline-asm"] }
|
riscv = { version = "0.6.0", features = ["inline-asm"] }
|
||||||
proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] }
|
proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] }
|
||||||
|
eh = { path = "../libeh" }
|
|
@ -1,9 +1,14 @@
|
||||||
include ../include/generated/variables.mak
|
include ../include/generated/variables.mak
|
||||||
include $(MISOC_DIRECTORY)/software/common.mak
|
include $(MISOC_DIRECTORY)/software/common.mak
|
||||||
|
|
||||||
LDFLAGS += -L../libbase
|
CFLAGS += \
|
||||||
|
-I$(LIBUNWIND_DIRECTORY) \
|
||||||
|
-I$(LIBUNWIND_DIRECTORY)/../unwinder/include
|
||||||
|
|
||||||
RUSTFLAGS += -Cpanic=abort
|
LDFLAGS += \
|
||||||
|
-L../libunwind
|
||||||
|
|
||||||
|
RUSTFLAGS += -Cpanic=unwind
|
||||||
|
|
||||||
export XBUILD_SYSROOT_PATH=$(BUILDINC_DIRECTORY)/../sysroot
|
export XBUILD_SYSROOT_PATH=$(BUILDINC_DIRECTORY)/../sysroot
|
||||||
|
|
||||||
|
@ -15,8 +20,12 @@ $(RUSTOUT)/libsatman.a:
|
||||||
--manifest-path $(SATMAN_DIRECTORY)/Cargo.toml \
|
--manifest-path $(SATMAN_DIRECTORY)/Cargo.toml \
|
||||||
--target $(SATMAN_DIRECTORY)/../$(CARGO_TRIPLE).json
|
--target $(SATMAN_DIRECTORY)/../$(CARGO_TRIPLE).json
|
||||||
|
|
||||||
satman.elf: $(RUSTOUT)/libsatman.a
|
satman.elf: $(RUSTOUT)/libsatman.a ksupport_data.o
|
||||||
$(link) -T $(SATMAN_DIRECTORY)/satman.ld
|
$(link) -T $(SATMAN_DIRECTORY)/satman.ld \
|
||||||
|
-lunwind-vexriscv-bare -m elf32lriscv
|
||||||
|
|
||||||
|
ksupport_data.o: ../ksupport/ksupport.elf
|
||||||
|
$(LD) -r -m elf32lriscv -b binary -o $@ $<
|
||||||
|
|
||||||
%.bin: %.elf
|
%.bin: %.elf
|
||||||
$(objcopy) -O binary
|
$(objcopy) -O binary
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
use alloc::{vec, vec::Vec, string::String, collections::btree_map::BTreeMap};
|
||||||
|
use cslice::{CSlice, AsCSlice};
|
||||||
|
use core::mem::transmute;
|
||||||
|
|
||||||
|
struct Entry {
|
||||||
|
data: Vec<i32>,
|
||||||
|
slice: CSlice<'static, i32>,
|
||||||
|
borrowed: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for Entry {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("Entry")
|
||||||
|
.field("data", &self.data)
|
||||||
|
.field("borrowed", &self.borrowed)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cache {
|
||||||
|
entries: BTreeMap<String, Entry>,
|
||||||
|
empty: CSlice<'static, i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for Cache {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("Cache")
|
||||||
|
.field("entries", &self.entries)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cache {
|
||||||
|
pub fn new() -> Cache {
|
||||||
|
let empty_vec = vec![];
|
||||||
|
let empty = unsafe {
|
||||||
|
transmute::<CSlice<'_, i32>, CSlice<'static, i32>>(empty_vec.as_c_slice())
|
||||||
|
};
|
||||||
|
Cache { entries: BTreeMap::new(), empty }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&mut self, key: &str) -> *const CSlice<'static, i32> {
|
||||||
|
match self.entries.get_mut(key) {
|
||||||
|
None => &self.empty,
|
||||||
|
Some(ref mut entry) => {
|
||||||
|
entry.borrowed = true;
|
||||||
|
&entry.slice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put(&mut self, key: &str, data: &[i32]) -> Result<(), ()> {
|
||||||
|
match self.entries.get_mut(key) {
|
||||||
|
None => (),
|
||||||
|
Some(ref mut entry) => {
|
||||||
|
if entry.borrowed { return Err(()) }
|
||||||
|
entry.data = Vec::from(data);
|
||||||
|
unsafe {
|
||||||
|
entry.slice = transmute::<CSlice<'_, i32>, CSlice<'static, i32>>(
|
||||||
|
entry.data.as_c_slice());
|
||||||
|
}
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = Vec::from(data);
|
||||||
|
let slice = unsafe {
|
||||||
|
transmute::<CSlice<'_, i32>, CSlice<'static, i32>>(data.as_c_slice())
|
||||||
|
};
|
||||||
|
self.entries.insert(String::from(key), Entry {
|
||||||
|
data,
|
||||||
|
slice,
|
||||||
|
borrowed: false
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn unborrow(&mut self) {
|
||||||
|
for (_key, entry) in self.entries.iter_mut() {
|
||||||
|
entry.borrowed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
use board_misoc::{csr, cache::flush_l2_cache};
|
use board_misoc::{csr, cache::flush_l2_cache};
|
||||||
use alloc::{vec::Vec, collections::btree_map::BTreeMap};
|
use alloc::{vec::Vec, collections::btree_map::BTreeMap};
|
||||||
|
use ::{cricon_select, RtioMaster};
|
||||||
|
|
||||||
const ALIGNMENT: usize = 64;
|
const ALIGNMENT: usize = 64;
|
||||||
|
|
||||||
|
@ -126,14 +127,14 @@ impl Manager {
|
||||||
csr::rtio_dma::base_address_write(ptr as u64);
|
csr::rtio_dma::base_address_write(ptr as u64);
|
||||||
csr::rtio_dma::time_offset_write(timestamp as u64);
|
csr::rtio_dma::time_offset_write(timestamp as u64);
|
||||||
|
|
||||||
csr::cri_con::selected_write(1);
|
cricon_select(RtioMaster::Dma);
|
||||||
csr::rtio_dma::enable_write(1);
|
csr::rtio_dma::enable_write(1);
|
||||||
// playback has begun here, for status call check_state
|
// playback has begun here, for status call check_state
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_state(&mut self) -> Option<RtioStatus> {
|
pub fn get_status(&mut self) -> Option<RtioStatus> {
|
||||||
if self.state != ManagerState::Playback {
|
if self.state != ManagerState::Playback {
|
||||||
// nothing to report
|
// nothing to report
|
||||||
return None;
|
return None;
|
||||||
|
@ -141,12 +142,11 @@ impl Manager {
|
||||||
let dma_enable = unsafe { csr::rtio_dma::enable_read() };
|
let dma_enable = unsafe { csr::rtio_dma::enable_read() };
|
||||||
if dma_enable != 0 {
|
if dma_enable != 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
self.state = ManagerState::Idle;
|
self.state = ManagerState::Idle;
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::cri_con::selected_write(0);
|
cricon_select(RtioMaster::Drtio);
|
||||||
let error = csr::rtio_dma::error_read();
|
let error = csr::rtio_dma::error_read();
|
||||||
let channel = csr::rtio_dma::error_channel_read();
|
let channel = csr::rtio_dma::error_channel_read();
|
||||||
let timestamp = csr::rtio_dma::error_timestamp_read();
|
let timestamp = csr::rtio_dma::error_timestamp_read();
|
||||||
if error != 0 {
|
if error != 0 {
|
||||||
|
@ -161,4 +161,8 @@ impl Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn running(&self) -> bool {
|
||||||
|
self.state == ManagerState::Playback
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,823 @@
|
||||||
|
use core::{mem, option::NoneError, cmp::min};
|
||||||
|
use alloc::{string::String, format, vec::Vec, collections::{btree_map::BTreeMap, vec_deque::VecDeque}};
|
||||||
|
use cslice::AsCSlice;
|
||||||
|
|
||||||
|
use board_artiq::{mailbox, spi};
|
||||||
|
use board_misoc::{csr, clock, i2c};
|
||||||
|
use proto_artiq::{kernel_proto as kern, session_proto::Reply::KernelException as HostKernelException, rpc_proto as rpc};
|
||||||
|
use eh::eh_artiq;
|
||||||
|
use io::{Cursor, ProtoRead};
|
||||||
|
use kernel::eh_artiq::StackPointerBacktrace;
|
||||||
|
|
||||||
|
use ::{cricon_select, RtioMaster};
|
||||||
|
use cache::Cache;
|
||||||
|
use SAT_PAYLOAD_MAX_SIZE;
|
||||||
|
use MASTER_PAYLOAD_MAX_SIZE;
|
||||||
|
|
||||||
|
mod kernel_cpu {
|
||||||
|
use super::*;
|
||||||
|
use core::ptr;
|
||||||
|
|
||||||
|
use proto_artiq::kernel_proto::{KERNELCPU_EXEC_ADDRESS, KERNELCPU_LAST_ADDRESS, KSUPPORT_HEADER_SIZE};
|
||||||
|
|
||||||
|
pub unsafe fn start() {
|
||||||
|
if csr::kernel_cpu::reset_read() == 0 {
|
||||||
|
panic!("attempted to start kernel CPU when it is already running")
|
||||||
|
}
|
||||||
|
|
||||||
|
stop();
|
||||||
|
|
||||||
|
extern {
|
||||||
|
static _binary____ksupport_ksupport_elf_start: u8;
|
||||||
|
static _binary____ksupport_ksupport_elf_end: u8;
|
||||||
|
}
|
||||||
|
let ksupport_start = &_binary____ksupport_ksupport_elf_start as *const _;
|
||||||
|
let ksupport_end = &_binary____ksupport_ksupport_elf_end as *const _;
|
||||||
|
ptr::copy_nonoverlapping(ksupport_start,
|
||||||
|
(KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as *mut u8,
|
||||||
|
ksupport_end as usize - ksupport_start as usize);
|
||||||
|
|
||||||
|
csr::kernel_cpu::reset_write(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn stop() {
|
||||||
|
csr::kernel_cpu::reset_write(1);
|
||||||
|
cricon_select(RtioMaster::Drtio);
|
||||||
|
|
||||||
|
mailbox::acknowledge();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate(ptr: usize) -> bool {
|
||||||
|
ptr >= KERNELCPU_EXEC_ADDRESS && ptr <= KERNELCPU_LAST_ADDRESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum KernelState {
|
||||||
|
Absent,
|
||||||
|
Loaded,
|
||||||
|
Running,
|
||||||
|
MsgAwait { max_time: u64 },
|
||||||
|
MsgSending
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Load(String),
|
||||||
|
KernelNotFound,
|
||||||
|
InvalidPointer(usize),
|
||||||
|
Unexpected(String),
|
||||||
|
NoMessage,
|
||||||
|
AwaitingMessage,
|
||||||
|
SubkernelIoError,
|
||||||
|
KernelException(Sliceable)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NoneError> for Error {
|
||||||
|
fn from(_: NoneError) -> Error {
|
||||||
|
Error::KernelNotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error<!>> for Error {
|
||||||
|
fn from(_value: io::Error<!>) -> Error {
|
||||||
|
Error::SubkernelIoError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! unexpected {
|
||||||
|
($($arg:tt)*) => (return Err(Error::Unexpected(format!($($arg)*))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* represents data that has to be sent to Master */
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Sliceable {
|
||||||
|
it: usize,
|
||||||
|
data: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
/* represents interkernel messages */
|
||||||
|
struct Message {
|
||||||
|
count: u8,
|
||||||
|
tag: u8,
|
||||||
|
data: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum OutMessageState {
|
||||||
|
NoMessage,
|
||||||
|
MessageReady,
|
||||||
|
MessageBeingSent,
|
||||||
|
MessageSent,
|
||||||
|
MessageAcknowledged
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for dealing with incoming and outgoing interkernel messages */
|
||||||
|
struct MessageManager {
|
||||||
|
out_message: Option<Sliceable>,
|
||||||
|
out_state: OutMessageState,
|
||||||
|
in_queue: VecDeque<Message>,
|
||||||
|
in_buffer: Option<Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-run state
|
||||||
|
struct Session {
|
||||||
|
kernel_state: KernelState,
|
||||||
|
log_buffer: String,
|
||||||
|
last_exception: Option<Sliceable>,
|
||||||
|
messages: MessageManager
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct KernelLibrary {
|
||||||
|
library: Vec<u8>,
|
||||||
|
complete: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Manager {
|
||||||
|
kernels: BTreeMap<u32, KernelLibrary>,
|
||||||
|
current_id: u32,
|
||||||
|
session: Session,
|
||||||
|
cache: Cache,
|
||||||
|
last_finished: Option<SubkernelFinished>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SubkernelFinished {
|
||||||
|
pub id: u32,
|
||||||
|
pub with_exception: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SliceMeta {
|
||||||
|
pub len: u16,
|
||||||
|
pub last: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_slice_fn {
|
||||||
|
( $name:tt, $size:expr ) => {
|
||||||
|
pub fn $name(&mut self, data_slice: &mut [u8; $size]) -> SliceMeta {
|
||||||
|
if self.data.len() == 0 {
|
||||||
|
return SliceMeta { len: 0, last: true };
|
||||||
|
}
|
||||||
|
let len = min($size, self.data.len() - self.it);
|
||||||
|
let last = self.it + len == self.data.len();
|
||||||
|
|
||||||
|
data_slice[..len].clone_from_slice(&self.data[self.it..self.it+len]);
|
||||||
|
self.it += len;
|
||||||
|
|
||||||
|
SliceMeta {
|
||||||
|
len: len as u16,
|
||||||
|
last: last
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sliceable {
|
||||||
|
pub fn new(data: Vec<u8>) -> Sliceable {
|
||||||
|
Sliceable {
|
||||||
|
it: 0,
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_slice_fn!(get_slice_sat, SAT_PAYLOAD_MAX_SIZE);
|
||||||
|
get_slice_fn!(get_slice_master, MASTER_PAYLOAD_MAX_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageManager {
|
||||||
|
pub fn new() -> MessageManager {
|
||||||
|
MessageManager {
|
||||||
|
out_message: None,
|
||||||
|
out_state: OutMessageState::NoMessage,
|
||||||
|
in_queue: VecDeque::new(),
|
||||||
|
in_buffer: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_incoming(&mut self, last: bool, length: usize, data: &[u8; MASTER_PAYLOAD_MAX_SIZE]) {
|
||||||
|
// called when receiving a message from master
|
||||||
|
match self.in_buffer.as_mut() {
|
||||||
|
Some(message) => message.data.extend(&data[..length]),
|
||||||
|
None => {
|
||||||
|
self.in_buffer = Some(Message {
|
||||||
|
count: data[0],
|
||||||
|
tag: data[1],
|
||||||
|
data: data[2..length].to_vec()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if last {
|
||||||
|
// when done, remove from working queue
|
||||||
|
self.in_queue.push_back(self.in_buffer.take().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_outgoing_ready(&mut self) -> bool {
|
||||||
|
// called by main loop, to see if there's anything to send, will send it afterwards
|
||||||
|
match self.out_state {
|
||||||
|
OutMessageState::MessageReady => {
|
||||||
|
self.out_state = OutMessageState::MessageBeingSent;
|
||||||
|
true
|
||||||
|
},
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn was_message_acknowledged(&mut self) -> bool {
|
||||||
|
match self.out_state {
|
||||||
|
OutMessageState::MessageAcknowledged => {
|
||||||
|
self.out_state = OutMessageState::NoMessage;
|
||||||
|
true
|
||||||
|
},
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_outgoing_slice(&mut self, data_slice: &mut [u8; MASTER_PAYLOAD_MAX_SIZE]) -> Option<SliceMeta> {
|
||||||
|
if self.out_state != OutMessageState::MessageBeingSent {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let meta = self.out_message.as_mut()?.get_slice_master(data_slice);
|
||||||
|
if meta.last {
|
||||||
|
// clear the message slot
|
||||||
|
self.out_message = None;
|
||||||
|
// notify kernel with a flag that message is sent
|
||||||
|
self.out_state = OutMessageState::MessageSent;
|
||||||
|
}
|
||||||
|
Some(meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ack_slice(&mut self) -> bool {
|
||||||
|
// returns whether or not there's more to be sent
|
||||||
|
match self.out_state {
|
||||||
|
OutMessageState::MessageBeingSent => true,
|
||||||
|
OutMessageState::MessageSent => {
|
||||||
|
self.out_state = OutMessageState::MessageAcknowledged;
|
||||||
|
false
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
warn!("received unsolicited SubkernelMessageAck");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn accept_outgoing(&mut self, count: u8, tag: &[u8], data: *const *const ()) -> Result<(), Error> {
|
||||||
|
let mut writer = Cursor::new(Vec::new());
|
||||||
|
rpc::send_args(&mut writer, 0, tag, data)?;
|
||||||
|
// skip service tag, but write the count
|
||||||
|
let mut data = writer.into_inner().split_off(3);
|
||||||
|
data[0] = count;
|
||||||
|
self.out_message = Some(Sliceable::new(data));
|
||||||
|
self.out_state = OutMessageState::MessageReady;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_incoming(&mut self) -> Option<Message> {
|
||||||
|
self.in_queue.pop_front()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Session {
|
||||||
|
pub fn new() -> Session {
|
||||||
|
Session {
|
||||||
|
kernel_state: KernelState::Absent,
|
||||||
|
log_buffer: String::new(),
|
||||||
|
last_exception: None,
|
||||||
|
messages: MessageManager::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn running(&self) -> bool {
|
||||||
|
match self.kernel_state {
|
||||||
|
KernelState::Absent | KernelState::Loaded => false,
|
||||||
|
KernelState::Running | KernelState::MsgAwait { .. } |
|
||||||
|
KernelState::MsgSending => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_log_buffer(&mut self) {
|
||||||
|
if &self.log_buffer[self.log_buffer.len() - 1..] == "\n" {
|
||||||
|
for line in self.log_buffer.lines() {
|
||||||
|
info!(target: "kernel", "{}", line);
|
||||||
|
}
|
||||||
|
self.log_buffer.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Manager {
|
||||||
|
pub fn new() -> Manager {
|
||||||
|
Manager {
|
||||||
|
kernels: BTreeMap::new(),
|
||||||
|
current_id: 0,
|
||||||
|
session: Session::new(),
|
||||||
|
cache: Cache::new(),
|
||||||
|
last_finished: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, id: u32, last: bool, data: &[u8], data_len: usize) -> Result<(), Error> {
|
||||||
|
let kernel = match self.kernels.get_mut(&id) {
|
||||||
|
Some(kernel) => {
|
||||||
|
if kernel.complete {
|
||||||
|
// replace entry
|
||||||
|
self.kernels.remove(&id);
|
||||||
|
self.kernels.insert(id, KernelLibrary {
|
||||||
|
library: Vec::new(),
|
||||||
|
complete: false });
|
||||||
|
self.kernels.get_mut(&id)?
|
||||||
|
} else {
|
||||||
|
kernel
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
self.kernels.insert(id, KernelLibrary {
|
||||||
|
library: Vec::new(),
|
||||||
|
complete: false });
|
||||||
|
self.kernels.get_mut(&id)?
|
||||||
|
},
|
||||||
|
};
|
||||||
|
kernel.library.extend(&data[0..data_len]);
|
||||||
|
|
||||||
|
kernel.complete = last;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_running(&self) -> bool {
|
||||||
|
self.session.running()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_current_id(&self) -> Option<u32> {
|
||||||
|
match self.is_running() {
|
||||||
|
true => Some(self.current_id),
|
||||||
|
false => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self) {
|
||||||
|
unsafe { kernel_cpu::stop() }
|
||||||
|
self.session.kernel_state = KernelState::Absent;
|
||||||
|
unsafe { self.cache.unborrow() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self, id: u32) -> Result<(), Error> {
|
||||||
|
info!("starting subkernel #{}", id);
|
||||||
|
if self.session.kernel_state != KernelState::Loaded
|
||||||
|
|| self.current_id != id {
|
||||||
|
self.load(id)?;
|
||||||
|
}
|
||||||
|
self.session.kernel_state = KernelState::Running;
|
||||||
|
cricon_select(RtioMaster::Kernel);
|
||||||
|
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_handle_incoming(&mut self, last: bool, length: usize, slice: &[u8; MASTER_PAYLOAD_MAX_SIZE]) {
|
||||||
|
if !self.is_running() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.session.messages.handle_incoming(last, length, slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_get_slice(&mut self, slice: &mut [u8; MASTER_PAYLOAD_MAX_SIZE]) -> Option<SliceMeta> {
|
||||||
|
if !self.is_running() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.session.messages.get_outgoing_slice(slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_ack_slice(&mut self) -> bool {
|
||||||
|
if !self.is_running() {
|
||||||
|
warn!("received unsolicited SubkernelMessageAck");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.session.messages.ack_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_is_ready(&mut self) -> bool {
|
||||||
|
self.session.messages.is_outgoing_ready()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_last_finished(&mut self) -> Option<SubkernelFinished> {
|
||||||
|
self.last_finished.take()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(&mut self, id: u32) -> Result<(), Error> {
|
||||||
|
if self.current_id == id && self.session.kernel_state == KernelState::Loaded {
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
if !self.kernels.get(&id)?.complete {
|
||||||
|
return Err(Error::KernelNotFound)
|
||||||
|
}
|
||||||
|
self.current_id = id;
|
||||||
|
self.session = Session::new();
|
||||||
|
self.stop();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
kernel_cpu::start();
|
||||||
|
|
||||||
|
kern_send(&kern::LoadRequest(&self.kernels.get(&id)?.library)).unwrap();
|
||||||
|
kern_recv(|reply| {
|
||||||
|
match reply {
|
||||||
|
kern::LoadReply(Ok(())) => {
|
||||||
|
self.session.kernel_state = KernelState::Loaded;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
kern::LoadReply(Err(error)) => {
|
||||||
|
kernel_cpu::stop();
|
||||||
|
Err(Error::Load(format!("{}", error)))
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
unexpected!("unexpected kernel CPU reply to load request: {:?}", other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exception_get_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> SliceMeta {
|
||||||
|
match self.session.last_exception.as_mut() {
|
||||||
|
Some(exception) => exception.get_slice_sat(data_slice),
|
||||||
|
None => SliceMeta { len: 0, last: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtime_exception(&mut self, cause: Error) {
|
||||||
|
let raw_exception: Vec<u8> = Vec::new();
|
||||||
|
let mut writer = Cursor::new(raw_exception);
|
||||||
|
match (HostKernelException {
|
||||||
|
exceptions: &[Some(eh_artiq::Exception {
|
||||||
|
id: 11, // SubkernelError, defined in ksupport
|
||||||
|
message: format!("in subkernel id {}: {:?}", self.current_id, cause).as_c_slice(),
|
||||||
|
param: [0, 0, 0],
|
||||||
|
file: file!().as_c_slice(),
|
||||||
|
line: line!(),
|
||||||
|
column: column!(),
|
||||||
|
function: format!("subkernel id {}", self.current_id).as_c_slice(),
|
||||||
|
})],
|
||||||
|
stack_pointers: &[StackPointerBacktrace {
|
||||||
|
stack_pointer: 0,
|
||||||
|
initial_backtrace_size: 0,
|
||||||
|
current_backtrace_size: 0
|
||||||
|
}],
|
||||||
|
backtrace: &[],
|
||||||
|
async_errors: 0
|
||||||
|
}).write_to(&mut writer) {
|
||||||
|
Ok(_) => self.session.last_exception = Some(Sliceable::new(writer.into_inner())),
|
||||||
|
Err(_) => error!("Error writing exception data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_kern_requests(&mut self, rank: u8) {
|
||||||
|
if !self.is_running() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.process_external_messages() {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(Error::AwaitingMessage) => return, // kernel still waiting, do not process kernel messages
|
||||||
|
Err(Error::KernelException(exception)) => {
|
||||||
|
unsafe { kernel_cpu::stop() }
|
||||||
|
self.session.kernel_state = KernelState::Absent;
|
||||||
|
unsafe { self.cache.unborrow() }
|
||||||
|
self.session.last_exception = Some(exception);
|
||||||
|
self.last_finished = Some(SubkernelFinished { id: self.current_id, with_exception: true })
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error while running processing external messages: {:?}", e);
|
||||||
|
self.stop();
|
||||||
|
self.runtime_exception(e);
|
||||||
|
self.last_finished = Some(SubkernelFinished { id: self.current_id, with_exception: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.process_kern_message(rank) {
|
||||||
|
Ok(Some(with_exception)) => {
|
||||||
|
self.last_finished = Some(SubkernelFinished { id: self.current_id, with_exception: with_exception })
|
||||||
|
},
|
||||||
|
Ok(None) | Err(Error::NoMessage) => (),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error while running kernel: {:?}", e);
|
||||||
|
self.stop();
|
||||||
|
self.runtime_exception(e);
|
||||||
|
self.last_finished = Some(SubkernelFinished { id: self.current_id, with_exception: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_external_messages(&mut self) -> Result<(), Error> {
|
||||||
|
match self.session.kernel_state {
|
||||||
|
KernelState::MsgAwait { max_time } => {
|
||||||
|
if clock::get_ms() > max_time {
|
||||||
|
kern_send(&kern::SubkernelMsgRecvReply { status: kern::SubkernelStatus::Timeout, count: 0 })?;
|
||||||
|
self.session.kernel_state = KernelState::Running;
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
if let Some(message) = self.session.messages.get_incoming() {
|
||||||
|
kern_send(&kern::SubkernelMsgRecvReply { status: kern::SubkernelStatus::NoError, count: message.count })?;
|
||||||
|
self.session.kernel_state = KernelState::Running;
|
||||||
|
pass_message_to_kernel(&message)
|
||||||
|
} else {
|
||||||
|
Err(Error::AwaitingMessage)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
KernelState::MsgSending => {
|
||||||
|
if self.session.messages.was_message_acknowledged() {
|
||||||
|
self.session.kernel_state = KernelState::Running;
|
||||||
|
kern_acknowledge()
|
||||||
|
} else {
|
||||||
|
Err(Error::AwaitingMessage)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_kern_message(&mut self, rank: u8) -> Result<Option<bool>, Error> {
|
||||||
|
// returns Ok(with_exception) on finish
|
||||||
|
// None if the kernel is still running
|
||||||
|
kern_recv(|request| {
|
||||||
|
match (request, self.session.kernel_state) {
|
||||||
|
(&kern::LoadReply(_), KernelState::Loaded) => {
|
||||||
|
// We're standing by; ignore the message.
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
(_, KernelState::Running) => (),
|
||||||
|
_ => {
|
||||||
|
unexpected!("unexpected request {:?} from kernel CPU in {:?} state",
|
||||||
|
request, self.session.kernel_state)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if process_kern_hwreq(request, rank)? {
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
match request {
|
||||||
|
&kern::Log(args) => {
|
||||||
|
use core::fmt::Write;
|
||||||
|
self.session.log_buffer
|
||||||
|
.write_fmt(args)
|
||||||
|
.unwrap_or_else(|_| warn!("cannot append to session log buffer"));
|
||||||
|
self.session.flush_log_buffer();
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::LogSlice(arg) => {
|
||||||
|
self.session.log_buffer += arg;
|
||||||
|
self.session.flush_log_buffer();
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::RpcFlush => {
|
||||||
|
// we do not have to do anything about this request,
|
||||||
|
// it is sent by the kernel firmware regardless of RPC being used
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::CacheGetRequest { key } => {
|
||||||
|
let value = self.cache.get(key);
|
||||||
|
kern_send(&kern::CacheGetReply {
|
||||||
|
value: unsafe { mem::transmute(value) }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::CachePutRequest { key, value } => {
|
||||||
|
let succeeded = self.cache.put(key, value).is_ok();
|
||||||
|
kern_send(&kern::CachePutReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::RunFinished => {
|
||||||
|
unsafe { kernel_cpu::stop() }
|
||||||
|
self.session.kernel_state = KernelState::Absent;
|
||||||
|
unsafe { self.cache.unborrow() }
|
||||||
|
|
||||||
|
return Ok(Some(false))
|
||||||
|
}
|
||||||
|
&kern::RunException { exceptions, stack_pointers, backtrace } => {
|
||||||
|
unsafe { kernel_cpu::stop() }
|
||||||
|
self.session.kernel_state = KernelState::Absent;
|
||||||
|
unsafe { self.cache.unborrow() }
|
||||||
|
let exception = slice_kernel_exception(&exceptions, &stack_pointers, &backtrace)?;
|
||||||
|
self.session.last_exception = Some(exception);
|
||||||
|
return Ok(Some(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::SubkernelMsgSend { id: _, count, tag, data } => {
|
||||||
|
self.session.messages.accept_outgoing(count, tag, data)?;
|
||||||
|
// acknowledge after the message is sent
|
||||||
|
self.session.kernel_state = KernelState::MsgSending;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::SubkernelMsgRecvRequest { id: _, timeout } => {
|
||||||
|
let max_time = clock::get_ms() + timeout as u64;
|
||||||
|
self.session.kernel_state = KernelState::MsgAwait { max_time: max_time };
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|
||||||
|
request => unexpected!("unexpected request {:?} from kernel CPU", request)
|
||||||
|
}.and(Ok(None))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Manager {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
cricon_select(RtioMaster::Drtio);
|
||||||
|
unsafe {
|
||||||
|
kernel_cpu::stop()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kern_recv<R, F>(f: F) -> Result<R, Error>
|
||||||
|
where F: FnOnce(&kern::Message) -> Result<R, Error> {
|
||||||
|
if mailbox::receive() == 0 {
|
||||||
|
return Err(Error::NoMessage);
|
||||||
|
};
|
||||||
|
if !kernel_cpu::validate(mailbox::receive()) {
|
||||||
|
return Err(Error::InvalidPointer(mailbox::receive()))
|
||||||
|
}
|
||||||
|
f(unsafe { &*(mailbox::receive() as *const kern::Message) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kern_recv_w_timeout<R, F>(timeout: u64, f: F) -> Result<R, Error>
|
||||||
|
where F: FnOnce(&kern::Message) -> Result<R, Error> + Copy {
|
||||||
|
// sometimes kernel may be too slow to respond immediately
|
||||||
|
// (e.g. when receiving external messages)
|
||||||
|
// we cannot wait indefinitely to keep the satellite responsive
|
||||||
|
// so a timeout is used instead
|
||||||
|
let max_time = clock::get_ms() + timeout;
|
||||||
|
while clock::get_ms() < max_time {
|
||||||
|
match kern_recv(f) {
|
||||||
|
Err(Error::NoMessage) => continue,
|
||||||
|
anything_else => return anything_else
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::NoMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kern_acknowledge() -> Result<(), Error> {
|
||||||
|
mailbox::acknowledge();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kern_send(request: &kern::Message) -> Result<(), Error> {
|
||||||
|
unsafe { mailbox::send(request as *const _ as usize) }
|
||||||
|
while !mailbox::acknowledged() {}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slice_kernel_exception(exceptions: &[Option<eh_artiq::Exception>],
|
||||||
|
stack_pointers: &[eh_artiq::StackPointerBacktrace],
|
||||||
|
backtrace: &[(usize, usize)]
|
||||||
|
) -> Result<Sliceable, Error> {
|
||||||
|
error!("exception in kernel");
|
||||||
|
for exception in exceptions {
|
||||||
|
error!("{:?}", exception.unwrap());
|
||||||
|
}
|
||||||
|
error!("stack pointers: {:?}", stack_pointers);
|
||||||
|
error!("backtrace: {:?}", backtrace);
|
||||||
|
// master will only pass the exception data back to the host:
|
||||||
|
let raw_exception: Vec<u8> = Vec::new();
|
||||||
|
let mut writer = Cursor::new(raw_exception);
|
||||||
|
match (HostKernelException {
|
||||||
|
exceptions: exceptions,
|
||||||
|
stack_pointers: stack_pointers,
|
||||||
|
backtrace: backtrace,
|
||||||
|
async_errors: 0
|
||||||
|
}).write_to(&mut writer) {
|
||||||
|
// save last exception data to be received by master
|
||||||
|
Ok(_) => Ok(Sliceable::new(writer.into_inner())),
|
||||||
|
Err(_) => Err(Error::SubkernelIoError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pass_message_to_kernel(message: &Message) -> Result<(), Error> {
|
||||||
|
let mut reader = Cursor::new(&message.data);
|
||||||
|
let mut tag: [u8; 1] = [message.tag];
|
||||||
|
let count = message.count;
|
||||||
|
let mut i = 0;
|
||||||
|
loop {
|
||||||
|
let slot = kern_recv_w_timeout(100, |reply| {
|
||||||
|
match reply {
|
||||||
|
&kern::RpcRecvRequest(slot) => Ok(slot),
|
||||||
|
&kern::RunException { exceptions, stack_pointers, backtrace } => {
|
||||||
|
let exception = slice_kernel_exception(&exceptions, &stack_pointers, &backtrace)?;
|
||||||
|
Err(Error::KernelException(exception))
|
||||||
|
},
|
||||||
|
other => unexpected!(
|
||||||
|
"expected root value slot from kernel CPU, not {:?}", other)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let res = rpc::recv_return(&mut reader, &tag, slot, &|size| -> Result<_, Error> {
|
||||||
|
if size == 0 {
|
||||||
|
return Ok(0 as *mut ())
|
||||||
|
}
|
||||||
|
kern_send(&kern::RpcRecvReply(Ok(size)))?;
|
||||||
|
Ok(kern_recv_w_timeout(100, |reply| {
|
||||||
|
match reply {
|
||||||
|
&kern::RpcRecvRequest(slot) => Ok(slot),
|
||||||
|
&kern::RunException {
|
||||||
|
exceptions,
|
||||||
|
stack_pointers,
|
||||||
|
backtrace
|
||||||
|
}=> {
|
||||||
|
let exception = slice_kernel_exception(&exceptions, &stack_pointers, &backtrace)?;
|
||||||
|
Err(Error::KernelException(exception))
|
||||||
|
},
|
||||||
|
other => unexpected!(
|
||||||
|
"expected nested value slot from kernel CPU, not {:?}", other)
|
||||||
|
}
|
||||||
|
})?)
|
||||||
|
});
|
||||||
|
match res {
|
||||||
|
Ok(_) => kern_send(&kern::RpcRecvReply(Ok(0)))?,
|
||||||
|
Err(_) => unexpected!("expected valid subkernel message data")
|
||||||
|
};
|
||||||
|
i += 1;
|
||||||
|
if i < count {
|
||||||
|
// update the tag for next read
|
||||||
|
tag[0] = reader.read_u8()?;
|
||||||
|
} else {
|
||||||
|
// should be done by then
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_kern_hwreq(request: &kern::Message, rank: u8) -> Result<bool, Error> {
|
||||||
|
match request {
|
||||||
|
&kern::RtioInitRequest => {
|
||||||
|
unsafe {
|
||||||
|
csr::drtiosat::reset_write(1);
|
||||||
|
clock::spin_us(100);
|
||||||
|
csr::drtiosat::reset_write(0);
|
||||||
|
}
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::RtioDestinationStatusRequest { destination } => {
|
||||||
|
// only local destination is considered "up"
|
||||||
|
// no access to other DRTIO destinations
|
||||||
|
kern_send(&kern::RtioDestinationStatusReply {
|
||||||
|
up: destination == rank })
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::I2cStartRequest { busno } => {
|
||||||
|
let succeeded = i2c::start(busno as u8).is_ok();
|
||||||
|
kern_send(&kern::I2cBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
&kern::I2cRestartRequest { busno } => {
|
||||||
|
let succeeded = i2c::restart(busno as u8).is_ok();
|
||||||
|
kern_send(&kern::I2cBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
&kern::I2cStopRequest { busno } => {
|
||||||
|
let succeeded = i2c::stop(busno as u8).is_ok();
|
||||||
|
kern_send(&kern::I2cBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
&kern::I2cWriteRequest { busno, data } => {
|
||||||
|
match i2c::write(busno as u8, data) {
|
||||||
|
Ok(ack) => kern_send(
|
||||||
|
&kern::I2cWriteReply { succeeded: true, ack: ack }),
|
||||||
|
Err(_) => kern_send(
|
||||||
|
&kern::I2cWriteReply { succeeded: false, ack: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&kern::I2cReadRequest { busno, ack } => {
|
||||||
|
match i2c::read(busno as u8, ack) {
|
||||||
|
Ok(data) => kern_send(
|
||||||
|
&kern::I2cReadReply { succeeded: true, data: data }),
|
||||||
|
Err(_) => kern_send(
|
||||||
|
&kern::I2cReadReply { succeeded: false, data: 0xff })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&kern::I2cSwitchSelectRequest { busno, address, mask } => {
|
||||||
|
let succeeded = i2c::switch_select(busno as u8, address, mask).is_ok();
|
||||||
|
kern_send(&kern::I2cBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::SpiSetConfigRequest { busno, flags, length, div, cs } => {
|
||||||
|
let succeeded = spi::set_config(busno as u8, flags, length, div, cs).is_ok();
|
||||||
|
kern_send(&kern::SpiBasicReply { succeeded: succeeded })
|
||||||
|
},
|
||||||
|
&kern::SpiWriteRequest { busno, data } => {
|
||||||
|
let succeeded = spi::write(busno as u8, data).is_ok();
|
||||||
|
kern_send(&kern::SpiBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
&kern::SpiReadRequest { busno } => {
|
||||||
|
match spi::read(busno as u8) {
|
||||||
|
Ok(data) => kern_send(
|
||||||
|
&kern::SpiReadReply { succeeded: true, data: data }),
|
||||||
|
Err(_) => kern_send(
|
||||||
|
&kern::SpiReadReply { succeeded: false, data: 0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => return Ok(false)
|
||||||
|
}.and(Ok(true))
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
#![feature(never_type, panic_info_message, llvm_asm, default_alloc_error_handler)]
|
#![feature(never_type, panic_info_message, llvm_asm, default_alloc_error_handler, try_trait)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -9,12 +9,15 @@ extern crate board_artiq;
|
||||||
extern crate riscv;
|
extern crate riscv;
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
extern crate proto_artiq;
|
extern crate proto_artiq;
|
||||||
|
extern crate cslice;
|
||||||
|
extern crate io;
|
||||||
|
extern crate eh;
|
||||||
|
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp};
|
use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp};
|
||||||
#[cfg(has_si5324)]
|
#[cfg(has_si5324)]
|
||||||
use board_artiq::si5324;
|
use board_artiq::si5324;
|
||||||
use board_artiq::{spi, drtioaux};
|
use board_artiq::{spi, drtioaux, drtio_routing};
|
||||||
#[cfg(soc_platform = "efc")]
|
#[cfg(soc_platform = "efc")]
|
||||||
use board_artiq::ad9117;
|
use board_artiq::ad9117;
|
||||||
use proto_artiq::drtioaux_proto::{SAT_PAYLOAD_MAX_SIZE, MASTER_PAYLOAD_MAX_SIZE};
|
use proto_artiq::drtioaux_proto::{SAT_PAYLOAD_MAX_SIZE, MASTER_PAYLOAD_MAX_SIZE};
|
||||||
|
@ -22,6 +25,7 @@ use proto_artiq::drtioaux_proto::{SAT_PAYLOAD_MAX_SIZE, MASTER_PAYLOAD_MAX_SIZE}
|
||||||
use board_artiq::drtio_eem;
|
use board_artiq::drtio_eem;
|
||||||
use riscv::register::{mcause, mepc, mtval};
|
use riscv::register::{mcause, mepc, mtval};
|
||||||
use dma::Manager as DmaManager;
|
use dma::Manager as DmaManager;
|
||||||
|
use kernel::Manager as KernelManager;
|
||||||
use analyzer::Analyzer;
|
use analyzer::Analyzer;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
|
@ -30,6 +34,8 @@ static mut ALLOC: alloc_list::ListAlloc = alloc_list::EMPTY;
|
||||||
mod repeater;
|
mod repeater;
|
||||||
mod dma;
|
mod dma;
|
||||||
mod analyzer;
|
mod analyzer;
|
||||||
|
mod kernel;
|
||||||
|
mod cache;
|
||||||
|
|
||||||
fn drtiosat_reset(reset: bool) {
|
fn drtiosat_reset(reset: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -59,6 +65,22 @@ fn drtiosat_tsc_loaded() -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum RtioMaster {
|
||||||
|
Drtio,
|
||||||
|
Dma,
|
||||||
|
Kernel
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cricon_select(master: RtioMaster) {
|
||||||
|
let val = match master {
|
||||||
|
RtioMaster::Drtio => 0,
|
||||||
|
RtioMaster::Dma => 1,
|
||||||
|
RtioMaster::Kernel => 2
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
csr::cri_con::selected_write(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(has_drtio_routing)]
|
#[cfg(has_drtio_routing)]
|
||||||
macro_rules! forward {
|
macro_rules! forward {
|
||||||
|
@ -80,8 +102,8 @@ macro_rules! forward {
|
||||||
($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr) => {}
|
($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_aux_packet(_manager: &mut DmaManager, analyzer: &mut Analyzer, _repeaters: &mut [repeater::Repeater],
|
fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmgr: &mut KernelManager,
|
||||||
_routing_table: &mut drtio_routing::RoutingTable, _rank: &mut u8,
|
_repeaters: &mut [repeater::Repeater], _routing_table: &mut drtio_routing::RoutingTable, _rank: &mut u8,
|
||||||
packet: drtioaux::Packet) -> Result<(), drtioaux::Error<!>> {
|
packet: drtioaux::Packet) -> Result<(), drtioaux::Error<!>> {
|
||||||
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
|
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
|
||||||
// and u16 otherwise; hence the `as _` conversion.
|
// and u16 otherwise; hence the `as _` conversion.
|
||||||
|
@ -101,18 +123,30 @@ fn process_aux_packet(_manager: &mut DmaManager, analyzer: &mut Analyzer, _repea
|
||||||
drtioaux::send(0, &drtioaux::Packet::ResetAck)
|
drtioaux::send(0, &drtioaux::Packet::ResetAck)
|
||||||
},
|
},
|
||||||
|
|
||||||
drtioaux::Packet::DestinationStatusRequest { destination: _destination } => {
|
drtioaux::Packet::DestinationStatusRequest { destination } => {
|
||||||
#[cfg(has_drtio_routing)]
|
#[cfg(has_drtio_routing)]
|
||||||
let hop = _routing_table.0[_destination as usize][*_rank as usize];
|
let hop = _routing_table.0[destination as usize][*_rank as usize];
|
||||||
#[cfg(not(has_drtio_routing))]
|
#[cfg(not(has_drtio_routing))]
|
||||||
let hop = 0;
|
let hop = 0;
|
||||||
|
|
||||||
if hop == 0 {
|
if hop == 0 {
|
||||||
// async messages
|
// async messages
|
||||||
if let Some(status) = _manager.check_state() {
|
if let Some(status) = dmamgr.get_status() {
|
||||||
info!("playback done, error: {}, channel: {}, timestamp: {}", status.error, status.channel, status.timestamp);
|
info!("playback done, error: {}, channel: {}, timestamp: {}", status.error, status.channel, status.timestamp);
|
||||||
drtioaux::send(0, &drtioaux::Packet::DmaPlaybackStatus {
|
drtioaux::send(0, &drtioaux::Packet::DmaPlaybackStatus {
|
||||||
destination: _destination, id: status.id, error: status.error, channel: status.channel, timestamp: status.timestamp })?;
|
destination: destination, id: status.id, error: status.error, channel: status.channel, timestamp: status.timestamp })?;
|
||||||
|
} else if let Some(subkernel_finished) = kernelmgr.get_last_finished() {
|
||||||
|
info!("subkernel {} finished, with exception: {}", subkernel_finished.id, subkernel_finished.with_exception);
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::SubkernelFinished {
|
||||||
|
id: subkernel_finished.id, with_exception: subkernel_finished.with_exception
|
||||||
|
})?;
|
||||||
|
} else if kernelmgr.message_is_ready() {
|
||||||
|
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||||
|
let meta = kernelmgr.message_get_slice(&mut data_slice).unwrap();
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::SubkernelMessage {
|
||||||
|
destination: destination, id: kernelmgr.get_current_id().unwrap(),
|
||||||
|
last: meta.last, length: meta.len as u16, data: data_slice
|
||||||
|
})?;
|
||||||
} else {
|
} else {
|
||||||
let errors;
|
let errors;
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -156,7 +190,7 @@ fn process_aux_packet(_manager: &mut DmaManager, analyzer: &mut Analyzer, _repea
|
||||||
if hop <= csr::DRTIOREP.len() {
|
if hop <= csr::DRTIOREP.len() {
|
||||||
let repno = hop - 1;
|
let repno = hop - 1;
|
||||||
match _repeaters[repno].aux_forward(&drtioaux::Packet::DestinationStatusRequest {
|
match _repeaters[repno].aux_forward(&drtioaux::Packet::DestinationStatusRequest {
|
||||||
destination: _destination
|
destination: destination
|
||||||
}) {
|
}) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(drtioaux::Error::LinkDown) => drtioaux::send(0, &drtioaux::Packet::DestinationDownReply)?,
|
Err(drtioaux::Error::LinkDown) => drtioaux::send(0, &drtioaux::Packet::DestinationDownReply)?,
|
||||||
|
@ -336,28 +370,80 @@ fn process_aux_packet(_manager: &mut DmaManager, analyzer: &mut Analyzer, _repea
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(has_rtio_dma)]
|
|
||||||
drtioaux::Packet::DmaAddTraceRequest { destination: _destination, id, last, length, trace } => {
|
drtioaux::Packet::DmaAddTraceRequest { destination: _destination, id, last, length, trace } => {
|
||||||
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
let succeeded = _manager.add(id, last, &trace, length as usize).is_ok();
|
let succeeded = dmamgr.add(id, last, &trace, length as usize).is_ok();
|
||||||
drtioaux::send(0,
|
drtioaux::send(0,
|
||||||
&drtioaux::Packet::DmaAddTraceReply { succeeded: succeeded })
|
&drtioaux::Packet::DmaAddTraceReply { succeeded: succeeded })
|
||||||
}
|
}
|
||||||
#[cfg(has_rtio_dma)]
|
|
||||||
drtioaux::Packet::DmaRemoveTraceRequest { destination: _destination, id } => {
|
drtioaux::Packet::DmaRemoveTraceRequest { destination: _destination, id } => {
|
||||||
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
let succeeded = _manager.erase(id).is_ok();
|
let succeeded = dmamgr.erase(id).is_ok();
|
||||||
drtioaux::send(0,
|
drtioaux::send(0,
|
||||||
&drtioaux::Packet::DmaRemoveTraceReply { succeeded: succeeded })
|
&drtioaux::Packet::DmaRemoveTraceReply { succeeded: succeeded })
|
||||||
}
|
}
|
||||||
#[cfg(has_rtio_dma)]
|
|
||||||
drtioaux::Packet::DmaPlaybackRequest { destination: _destination, id, timestamp } => {
|
drtioaux::Packet::DmaPlaybackRequest { destination: _destination, id, timestamp } => {
|
||||||
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
let succeeded = _manager.playback(id, timestamp).is_ok();
|
// no DMA with a running kernel
|
||||||
|
let succeeded = !kernelmgr.is_running() && dmamgr.playback(id, timestamp).is_ok();
|
||||||
drtioaux::send(0,
|
drtioaux::send(0,
|
||||||
&drtioaux::Packet::DmaPlaybackReply { succeeded: succeeded })
|
&drtioaux::Packet::DmaPlaybackReply { succeeded: succeeded })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drtioaux::Packet::SubkernelAddDataRequest { destination: _destination, id, last, length, data } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
let succeeded = kernelmgr.add(id, last, &data, length as usize).is_ok();
|
||||||
|
drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::SubkernelAddDataReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
drtioaux::Packet::SubkernelLoadRunRequest { destination: _destination, id, run } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
let mut succeeded = kernelmgr.load(id).is_ok();
|
||||||
|
// allow preloading a kernel with delayed run
|
||||||
|
if run {
|
||||||
|
if dmamgr.running() {
|
||||||
|
// cannot run kernel while DDMA is running
|
||||||
|
succeeded = false;
|
||||||
|
} else {
|
||||||
|
succeeded |= kernelmgr.run(id).is_ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::SubkernelLoadRunReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
drtioaux::Packet::SubkernelExceptionRequest { destination: _destination } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
let mut data_slice: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
|
||||||
|
let meta = kernelmgr.exception_get_slice(&mut data_slice);
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::SubkernelException {
|
||||||
|
last: meta.last,
|
||||||
|
length: meta.len,
|
||||||
|
data: data_slice,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
drtioaux::Packet::SubkernelMessage { destination, id: _id, last, length, data } => {
|
||||||
|
forward!(_routing_table, destination, *_rank, _repeaters, &packet);
|
||||||
|
kernelmgr.message_handle_incoming(last, length as usize, &data);
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::SubkernelMessageAck {
|
||||||
|
destination: destination
|
||||||
|
})
|
||||||
|
}
|
||||||
|
drtioaux::Packet::SubkernelMessageAck { destination: _destination } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
if kernelmgr.message_ack_slice() {
|
||||||
|
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
||||||
|
if let Some(meta) = kernelmgr.message_get_slice(&mut data_slice) {
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::SubkernelMessage {
|
||||||
|
destination: *_rank, id: kernelmgr.get_current_id().unwrap(),
|
||||||
|
last: meta.last, length: meta.len as u16, data: data_slice
|
||||||
|
})?
|
||||||
|
} else {
|
||||||
|
error!("Error receiving message slice");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
warn!("received unexpected aux packet");
|
warn!("received unexpected aux packet");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -366,12 +452,12 @@ fn process_aux_packet(_manager: &mut DmaManager, analyzer: &mut Analyzer, _repea
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_aux_packets(dma_manager: &mut DmaManager, analyzer: &mut Analyzer,
|
fn process_aux_packets(dma_manager: &mut DmaManager, analyzer: &mut Analyzer,
|
||||||
repeaters: &mut [repeater::Repeater],
|
kernelmgr: &mut KernelManager, repeaters: &mut [repeater::Repeater],
|
||||||
routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8) {
|
routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8) {
|
||||||
let result =
|
let result =
|
||||||
drtioaux::recv(0).and_then(|packet| {
|
drtioaux::recv(0).and_then(|packet| {
|
||||||
if let Some(packet) = packet {
|
if let Some(packet) = packet {
|
||||||
process_aux_packet(dma_manager, analyzer, repeaters, routing_table, rank, packet)
|
process_aux_packet(dma_manager, analyzer, kernelmgr, repeaters, routing_table, rank, packet)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -383,10 +469,7 @@ fn process_aux_packets(dma_manager: &mut DmaManager, analyzer: &mut Analyzer,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drtiosat_process_errors() {
|
fn drtiosat_process_errors() {
|
||||||
let errors;
|
let errors = unsafe { csr::drtiosat::protocol_error_read() };
|
||||||
unsafe {
|
|
||||||
errors = csr::drtiosat::protocol_error_read();
|
|
||||||
}
|
|
||||||
if errors & 1 != 0 {
|
if errors & 1 != 0 {
|
||||||
error!("received packet of an unknown type");
|
error!("received packet of an unknown type");
|
||||||
}
|
}
|
||||||
|
@ -394,26 +477,29 @@ fn drtiosat_process_errors() {
|
||||||
error!("received truncated packet");
|
error!("received truncated packet");
|
||||||
}
|
}
|
||||||
if errors & 4 != 0 {
|
if errors & 4 != 0 {
|
||||||
let destination;
|
let destination = unsafe {
|
||||||
unsafe {
|
csr::drtiosat::buffer_space_timeout_dest_read()
|
||||||
destination = csr::drtiosat::buffer_space_timeout_dest_read();
|
};
|
||||||
}
|
|
||||||
error!("timeout attempting to get buffer space from CRI, destination=0x{:02x}", destination)
|
error!("timeout attempting to get buffer space from CRI, destination=0x{:02x}", destination)
|
||||||
}
|
}
|
||||||
if errors & 8 != 0 {
|
let drtiosat_active = unsafe { csr::cri_con::selected_read() == 0 };
|
||||||
let channel;
|
if drtiosat_active {
|
||||||
let timestamp_event;
|
// RTIO errors are handled by ksupport and dma manager
|
||||||
let timestamp_counter;
|
if errors & 8 != 0 {
|
||||||
unsafe {
|
let channel;
|
||||||
channel = csr::drtiosat::underflow_channel_read();
|
let timestamp_event;
|
||||||
timestamp_event = csr::drtiosat::underflow_timestamp_event_read() as i64;
|
let timestamp_counter;
|
||||||
timestamp_counter = csr::drtiosat::underflow_timestamp_counter_read() as i64;
|
unsafe {
|
||||||
|
channel = csr::drtiosat::underflow_channel_read();
|
||||||
|
timestamp_event = csr::drtiosat::underflow_timestamp_event_read() as i64;
|
||||||
|
timestamp_counter = csr::drtiosat::underflow_timestamp_counter_read() as i64;
|
||||||
|
}
|
||||||
|
error!("write underflow, channel={}, timestamp={}, counter={}, slack={}",
|
||||||
|
channel, timestamp_event, timestamp_counter, timestamp_event-timestamp_counter);
|
||||||
|
}
|
||||||
|
if errors & 16 != 0 {
|
||||||
|
error!("write overflow");
|
||||||
}
|
}
|
||||||
error!("write underflow, channel={}, timestamp={}, counter={}, slack={}",
|
|
||||||
channel, timestamp_event, timestamp_counter, timestamp_event-timestamp_counter);
|
|
||||||
}
|
|
||||||
if errors & 16 != 0 {
|
|
||||||
error!("write overflow");
|
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::drtiosat::protocol_error_write(errors);
|
csr::drtiosat::protocol_error_write(errors);
|
||||||
|
@ -612,21 +698,23 @@ pub extern fn main() -> i32 {
|
||||||
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");
|
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");
|
||||||
}
|
}
|
||||||
|
|
||||||
// DMA manager created here, so when link is dropped, all DMA traces
|
// various managers created here, so when link is dropped, DMA traces,
|
||||||
// are cleared out for a clean slate on subsequent connections,
|
// analyzer logs, kernels are cleared and/or stopped for a clean slate
|
||||||
// without a manual intervention.
|
// on subsequent connections, without a manual intervention.
|
||||||
let mut dma_manager = DmaManager::new();
|
let mut dma_manager = DmaManager::new();
|
||||||
|
|
||||||
// Reset the analyzer as well.
|
|
||||||
let mut analyzer = Analyzer::new();
|
let mut analyzer = Analyzer::new();
|
||||||
|
let mut kernelmgr = KernelManager::new();
|
||||||
|
|
||||||
|
cricon_select(RtioMaster::Drtio);
|
||||||
drtioaux::reset(0);
|
drtioaux::reset(0);
|
||||||
drtiosat_reset(false);
|
drtiosat_reset(false);
|
||||||
drtiosat_reset_phy(false);
|
drtiosat_reset_phy(false);
|
||||||
|
|
||||||
while drtiosat_link_rx_up() {
|
while drtiosat_link_rx_up() {
|
||||||
drtiosat_process_errors();
|
drtiosat_process_errors();
|
||||||
process_aux_packets(&mut dma_manager, &mut analyzer, &mut repeaters, &mut routing_table, &mut rank);
|
process_aux_packets(&mut dma_manager, &mut analyzer,
|
||||||
|
&mut kernelmgr, &mut repeaters,
|
||||||
|
&mut routing_table, &mut rank);
|
||||||
for rep in repeaters.iter_mut() {
|
for rep in repeaters.iter_mut() {
|
||||||
rep.service(&routing_table, rank);
|
rep.service(&routing_table, rank);
|
||||||
}
|
}
|
||||||
|
@ -649,6 +737,7 @@ pub extern fn main() -> i32 {
|
||||||
error!("aux packet error: {}", e);
|
error!("aux packet error: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
kernelmgr.process_kern_requests(rank);
|
||||||
}
|
}
|
||||||
|
|
||||||
drtiosat_reset_phy(true);
|
drtiosat_reset_phy(true);
|
||||||
|
|
|
@ -22,6 +22,19 @@ SECTIONS
|
||||||
__eh_frame_end = .;
|
__eh_frame_end = .;
|
||||||
} > main_ram
|
} > main_ram
|
||||||
|
|
||||||
|
.eh_frame_hdr :
|
||||||
|
{
|
||||||
|
KEEP(*(.eh_frame_hdr))
|
||||||
|
} > main_ram
|
||||||
|
|
||||||
|
__eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
|
||||||
|
__eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
|
||||||
|
|
||||||
|
.gcc_except_table :
|
||||||
|
{
|
||||||
|
*(.gcc_except_table)
|
||||||
|
} > main_ram
|
||||||
|
|
||||||
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
|
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
|
||||||
.got :
|
.got :
|
||||||
{
|
{
|
||||||
|
@ -68,11 +81,11 @@ SECTIONS
|
||||||
_fstack = . - 16;
|
_fstack = . - 16;
|
||||||
} > main_ram
|
} > main_ram
|
||||||
|
|
||||||
/* 64MB heap for alloc use */
|
/* remainder of 64MB for heap for alloc use */
|
||||||
.heap (NOLOAD) : ALIGN(16)
|
.heap (NOLOAD) : ALIGN(16)
|
||||||
{
|
{
|
||||||
_fheap = .;
|
_fheap = .;
|
||||||
. = . + 0x4000000;
|
. = 0x44000000; // not to overwrite RPC queue
|
||||||
_eheap = .;
|
_eheap = .;
|
||||||
} > main_ram
|
} > main_ram
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue