From ce31ffddb0bca6f805b7a3e7f23a57255d994a43 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 18 Jan 2017 16:50:32 -0600 Subject: [PATCH] firmware: add satellite manager The code duplication with the runtime should be cleaned up later. --- artiq/firmware/Cargo.lock | 12 +++ artiq/firmware/Cargo.toml | 2 +- artiq/firmware/runtime/lib.rs | 2 +- artiq/firmware/satman/Cargo.toml | 20 ++++ artiq/firmware/satman/Makefile | 49 ++++++++++ artiq/firmware/satman/build.rs | 53 +++++++++++ artiq/firmware/satman/lib.rs | 93 +++++++++++++++++++ artiq/firmware/satman/logger.rs | 83 +++++++++++++++++ artiq/firmware/satman/main.c | 22 +++++ artiq/firmware/satman/satman.ld | 86 +++++++++++++++++ .../gateware/targets/kc705_drtio_satellite.py | 4 + 11 files changed, 424 insertions(+), 2 deletions(-) create mode 100644 artiq/firmware/satman/Cargo.toml create mode 100644 artiq/firmware/satman/Makefile create mode 100644 artiq/firmware/satman/build.rs create mode 100644 artiq/firmware/satman/lib.rs create mode 100644 artiq/firmware/satman/logger.rs create mode 100644 artiq/firmware/satman/main.c create mode 100644 artiq/firmware/satman/satman.ld diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index 659180025..3e14fd830 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -91,6 +91,18 @@ dependencies = [ "walkdir 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "satman" +version = "0.0.0" +dependencies = [ + "alloc_artiq 0.0.0", + "board 0.0.0", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "std_artiq 0.0.0", + "walkdir 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "walkdir" version = "1.0.3" diff --git a/artiq/firmware/Cargo.toml b/artiq/firmware/Cargo.toml index 58aec9c96..6af1a7ffb 100644 --- a/artiq/firmware/Cargo.toml +++ b/artiq/firmware/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["runtime", "libksupport"] +members = ["runtime", "libksupport", "satman"] diff --git a/artiq/firmware/runtime/lib.rs b/artiq/firmware/runtime/lib.rs index b7316b35c..07973b032 100644 --- a/artiq/firmware/runtime/lib.rs +++ b/artiq/firmware/runtime/lib.rs @@ -104,7 +104,7 @@ pub unsafe extern fn rust_main() { BufferLogger::new(&mut LOG_BUFFER[..]) .register(move || { board::clock::init(); - info!("booting ARTIQ"); + info!("ARTIQ runtime starting..."); info!("software version {}", GIT_COMMIT); info!("gateware version {}", board::ident(&mut [0; 64])); diff --git a/artiq/firmware/satman/Cargo.toml b/artiq/firmware/satman/Cargo.toml new file mode 100644 index 000000000..c35a67e13 --- /dev/null +++ b/artiq/firmware/satman/Cargo.toml @@ -0,0 +1,20 @@ +[package] +authors = ["M-Labs"] +name = "satman" +version = "0.0.0" +build = "build.rs" + +[build-dependencies] +walkdir = "1.0" + +[lib] +name = "satman" +crate-type = ["staticlib"] +path = "lib.rs" + +[dependencies] +alloc_artiq = { path = "../liballoc_artiq" } +std_artiq = { path = "../libstd_artiq", features = ["alloc"] } +board = { path = "../libboard" } +log = { version = "0.3", default-features = false } +log_buffer = { version = "1.0" } diff --git a/artiq/firmware/satman/Makefile b/artiq/firmware/satman/Makefile new file mode 100644 index 000000000..230586203 --- /dev/null +++ b/artiq/firmware/satman/Makefile @@ -0,0 +1,49 @@ +include ../include/generated/variables.mak +include $(MISOC_DIRECTORY)/software/common.mak + +PYTHON ?= python3.5 + +OBJECTS := main.o + +RUSTOUT := cargo/or1k-unknown-none/debug + +CFLAGS += -I$(LIBALLOC_DIRECTORY) + +LDFLAGS += --gc-sections \ + -L../libcompiler-rt \ + -L../libbase \ + -L../liballoc + +all: satman.bin satman.fbi + +.PHONY: $(RUSTOUT)/libsatman.a +$(RUSTOUT)/libsatman.a: + RUSTFLAGS="-C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s -Cpanic=abort" \ + CARGO_TARGET_DIR=$(realpath .)/cargo \ + cargo build --target=or1k-unknown-none \ + --manifest-path $(realpath $(SATMAN_DIRECTORY))/Cargo.toml + +satman.elf: $(OBJECTS) $(RUSTOUT)/libsatman.a + $(LD) $(LDFLAGS) \ + -T $(SATMAN_DIRECTORY)/satman.ld \ + -o $@ \ + $^ \ + -lbase-nofloat -lcompiler-rt -lalloc + @chmod -x $@ + +%.o: $(SATMAN_DIRECTORY)/%.c + $(compile) + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + @chmod -x $@ + +%.fbi: %.bin + @echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $< + +clean: + $(RM) $(OBJECTS) + $(RM) satman.elf satman.bin satman.fbi .*~ *~ + $(RM) -rf cargo + +.PHONY: all clean diff --git a/artiq/firmware/satman/build.rs b/artiq/firmware/satman/build.rs new file mode 100644 index 000000000..5ab08cdd6 --- /dev/null +++ b/artiq/firmware/satman/build.rs @@ -0,0 +1,53 @@ +extern crate walkdir; + +use std::env; +use std::fs::File; +use std::io::{Write, BufRead, BufReader}; +use std::path::Path; +use std::process::Command; + +use walkdir::WalkDir; + +fn main() { + let out_dir = env::var("BUILDINC_DIRECTORY").unwrap(); + let cfg_path = Path::new(&out_dir).join("generated").join("rust-cfg"); + println!("cargo:rerun-if-changed={}", cfg_path.to_str().unwrap()); + + let f = BufReader::new(File::open(&cfg_path).unwrap()); + for line in f.lines() { + println!("cargo:rustc-cfg={}", line.unwrap()); + } + + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("git_info.rs"); + let mut f = File::create(&dest_path).unwrap(); + + let id = git_describe().unwrap(); + let id = id.split("-").collect::>(); + let id = format!("{}+{}.{}", id[0], id[1], id[2]); + writeln!(f, "const GIT_COMMIT: &'static str = {:?};", id).unwrap(); + + println!("cargo:rerun-if-changed=../../../.git/HEAD"); + for entry in WalkDir::new("../../../.git/refs") { + let entry = entry.unwrap(); + println!("cargo:rerun-if-changed={}", entry.path().display()); + } +} + +// Returns `None` if git is not available. +fn git_describe() -> Option { + Command::new("git") + .arg("describe") + .arg("--tags") + .arg("--dirty") + .arg("--always") + .arg("--long") + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .map(|mut s| { + let len = s.trim_right().len(); + s.truncate(len); + s + }) +} diff --git a/artiq/firmware/satman/lib.rs b/artiq/firmware/satman/lib.rs new file mode 100644 index 000000000..b8cd5f7b7 --- /dev/null +++ b/artiq/firmware/satman/lib.rs @@ -0,0 +1,93 @@ +#![no_std] +#![feature(libc, const_fn, repr_simd, asm, lang_items)] + +extern crate alloc_artiq; +#[macro_use] +extern crate std_artiq as std; +extern crate libc; +#[macro_use] +extern crate log; +extern crate log_buffer; +extern crate board; + +use core::fmt::Write; +use logger::BufferLogger; + +extern { + fn putchar(c: libc::c_int) -> libc::c_int; + fn readchar() -> libc::c_char; + fn readchar_nonblock() -> libc::c_int; +} + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print_fmt(format_args!($($arg)*))); +} + +#[macro_export] +macro_rules! println { + ($fmt:expr) => (print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); +} + +pub struct Console; + +impl core::fmt::Write for Console { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + for c in s.bytes() { unsafe { putchar(c as i32); } } + Ok(()) + } +} + +pub fn print_fmt(args: self::core::fmt::Arguments) { + let _ = Console.write_fmt(args); +} + +#[no_mangle] +#[lang = "panic_fmt"] +pub extern fn panic_fmt(args: self::core::fmt::Arguments, file: &'static str, line: u32) -> ! { + let _ = write!(Console, "panic at {}:{}: {}\n", file, line, args); + let _ = write!(Console, "waiting for debugger...\n"); + unsafe { + let _ = readchar(); + loop { asm!("l.trap 0") } + } +} + +mod logger; + +include!(concat!(env!("OUT_DIR"), "/git_info.rs")); + +// Allow linking with crates that are built as -Cpanic=unwind even if we use -Cpanic=abort. +// This is never called. +#[allow(non_snake_case)] +#[no_mangle] +pub extern "C" fn _Unwind_Resume() -> ! { + loop {} +} + +#[no_mangle] +pub unsafe extern fn rust_main() { + static mut LOG_BUFFER: [u8; 65536] = [0; 65536]; + BufferLogger::new(&mut LOG_BUFFER[..]) + .register(move || { + board::clock::init(); + info!("ARTIQ satellite manager starting..."); + info!("software version {}", GIT_COMMIT); + info!("gateware version {}", board::ident(&mut [0; 64])); + + loop { + } + }) +} + +#[no_mangle] +pub unsafe extern fn isr() { + use board::{irq, csr}; + extern { fn uart_isr(); } + + let irqs = irq::pending() & irq::get_mask(); + if irqs & (1 << csr::UART_INTERRUPT) != 0 { + uart_isr() + } +} diff --git a/artiq/firmware/satman/logger.rs b/artiq/firmware/satman/logger.rs new file mode 100644 index 000000000..4177f8e24 --- /dev/null +++ b/artiq/firmware/satman/logger.rs @@ -0,0 +1,83 @@ +use core::{mem, ptr}; +use core::cell::{Cell, RefCell}; +use log::{self, Log, LogLevel, LogMetadata, LogRecord, LogLevelFilter}; +use log_buffer::LogBuffer; +use board; + +pub struct BufferLogger { + buffer: RefCell>, + trace_to_uart: Cell +} + +unsafe impl Sync for BufferLogger {} + +static mut LOGGER: *const BufferLogger = ptr::null(); + +impl BufferLogger { + pub fn new(buffer: &'static mut [u8]) -> BufferLogger { + BufferLogger { + buffer: RefCell::new(LogBuffer::new(buffer)), + trace_to_uart: Cell::new(true) + } + } + + pub fn register(&self, f: F) { + // log::set_logger_raw captures a pointer to ourselves, so we must prevent + // ourselves from being moved or dropped after that function is called (and + // before log::shutdown_logger_raw is called). + unsafe { + log::set_logger_raw(|max_log_level| { + max_log_level.set(LogLevelFilter::Trace); + self as *const Log + }).expect("global logger can only be initialized once"); + LOGGER = self; + } + f(); + log::shutdown_logger_raw().unwrap(); + unsafe { + LOGGER = ptr::null(); + } + } + + pub fn with_instance R>(f: F) -> R { + f(unsafe { mem::transmute::<*const BufferLogger, &BufferLogger>(LOGGER) }) + } + + pub fn clear(&self) { + self.buffer.borrow_mut().clear() + } + + pub fn extract R>(&self, f: F) -> R { + f(self.buffer.borrow_mut().extract()) + } + + pub fn disable_trace_to_uart(&self) { + if self.trace_to_uart.get() { + trace!("disabling tracing to UART; all further trace messages \ + are sent to core log only"); + self.trace_to_uart.set(false) + } + } +} + +impl Log for BufferLogger { + fn enabled(&self, _metadata: &LogMetadata) -> bool { + true + } + + fn log(&self, record: &LogRecord) { + if self.enabled(record.metadata()) { + use core::fmt::Write; + writeln!(self.buffer.borrow_mut(), + "[{:12}us] {:>5}({}): {}", + board::clock::get_us(), record.level(), record.target(), record.args()).unwrap(); + + // Printing to UART is really slow, so avoid doing that when we have an alternative + // route to retrieve the debug messages. + if self.trace_to_uart.get() || record.level() <= LogLevel::Info { + println!("[{:12}us] {:>5}({}): {}", + board::clock::get_us(), record.level(), record.target(), record.args()); + } + } + } +} diff --git a/artiq/firmware/satman/main.c b/artiq/firmware/satman/main.c new file mode 100644 index 000000000..92b838013 --- /dev/null +++ b/artiq/firmware/satman/main.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include +#include + +extern void _fheap, _eheap; + +extern void rust_main(); + +int main(void) +{ + irq_setmask(0); + irq_setie(1); + uart_init(); + + alloc_give(&_fheap, &_eheap - &_fheap); + + rust_main(); + + return 0; +} diff --git a/artiq/firmware/satman/satman.ld b/artiq/firmware/satman/satman.ld new file mode 100644 index 000000000..42e7e2cd2 --- /dev/null +++ b/artiq/firmware/satman/satman.ld @@ -0,0 +1,86 @@ +INCLUDE generated/output_format.ld +STARTUP(crt0-or1k.o) +ENTRY(_start) + +INCLUDE generated/regions.ld + +/* Assume ORIGIN(main_ram) = 0x40000000. Unfortunately, + * ld does not allow this expression here. + */ +MEMORY { + runtime (RWX) : ORIGIN = 0x40000000, LENGTH = 0x400000 /* 4M */ +} + +SECTIONS +{ + .text : + { + _ftext = .; + *(.text .stub .text.* .gnu.linkonce.t.*) + _etext = .; + } > runtime + + /* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */ + .got : { + _GLOBAL_OFFSET_TABLE_ = .; + *(.got) + } > runtime + + .got.plt : { + *(.got.plt) + } > runtime + + .rodata : + { + . = ALIGN(4); + _frodata = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.rodata1) + _erodata = .; + } > runtime + + .data : + { + . = ALIGN(4); + _fdata = .; + *(.data .data.* .gnu.linkonce.d.*) + *(.data1) + *(.sdata .sdata.* .gnu.linkonce.s.*) + _edata = .; + } > runtime + + .bss : + { + . = ALIGN(4); + _fbss = .; + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + } > runtime + + .stack : + { + . = ALIGN(0x1000); + _estack = .; + . += 0x4000; + _fstack = . - 4; + } > runtime + + .heap : + { + _fheap = .; + . = ORIGIN(runtime) + LENGTH(runtime); + _eheap = .; + } > runtime + + /DISCARD/ : + { + *(.eh_frame) + *(.gcc_except_table) + } +} diff --git a/artiq/gateware/targets/kc705_drtio_satellite.py b/artiq/gateware/targets/kc705_drtio_satellite.py index dc638bc46..151092586 100755 --- a/artiq/gateware/targets/kc705_drtio_satellite.py +++ b/artiq/gateware/targets/kc705_drtio_satellite.py @@ -1,4 +1,5 @@ import argparse +import os from migen import * from migen.build.generic_platform import * @@ -14,6 +15,7 @@ from artiq.gateware.rtio.phy import ttl_simple from artiq.gateware.drtio.transceiver import gtx_7series from artiq.gateware.drtio import DRTIOSatellite from artiq import __version__ as artiq_version +from artiq import __artiq_dir__ as artiq_dir # TODO: parameters for sawg_3g @@ -215,6 +217,8 @@ def main(): soc = Satellite(args.config, args.medium, **soc_kc705_argdict(args)) builder = Builder(soc, **builder_argdict(args)) + builder.add_software_package("liballoc") + builder.add_software_package("satman", os.path.join(artiq_dir, "firmware", "satman")) builder.build()