Browse Source

initial commit

master
cjb 1 year ago
commit
e139aa0ae9
46 changed files with 7880 additions and 0 deletions
  1. 10
    0
      .gitignore
  2. 70
    0
      experiments/dds_timing.py
  3. 147
    0
      experiments/device_db.py
  4. 46
    0
      experiments/mandelbrot.py
  5. 14
    0
      experiments/minimal.py
  6. 93
    0
      experiments/ttl.py
  7. 18
    0
      experiments/wall_time.py
  8. 7
    0
      firmware/Cargo.toml
  9. 14
    0
      firmware/libboard_misoc/Cargo.toml
  10. 56
    0
      firmware/libboard_misoc/lib.rs
  11. 14
    0
      firmware/libboard_misoc/uart.rs
  12. 30
    0
      firmware/libboard_misoc/uart_console.rs
  13. 8
    0
      firmware/libdyld/Cargo.toml
  14. 2737
    0
      firmware/libdyld/elf.rs
  15. 339
    0
      firmware/libdyld/lib.rs
  16. BIN
      firmware/libksupport/._eh.rs
  17. 20
    0
      firmware/libksupport/Cargo.toml
  18. 89
    0
      firmware/libksupport/api.rs
  19. 12
    0
      firmware/libksupport/build.rs
  20. 602
    0
      firmware/libksupport/eh.rs
  21. 68
    0
      firmware/libksupport/glue.c
  22. 59
    0
      firmware/libksupport/lib.rs
  23. 337
    0
      firmware/libksupport/rtio.rs
  24. 15
    0
      firmware/libunwind-rs/Cargo.toml
  25. 41
    0
      firmware/libunwind-rs/build.rs
  26. 29
    0
      firmware/libunwind-rs/lib.rs
  27. 273
    0
      firmware/libunwind-rs/libunwind.rs
  28. 20
    0
      firmware/runtime/Cargo.toml
  29. 27
    0
      firmware/runtime/Makefile
  30. 17
    0
      firmware/runtime/build.rs
  31. 68
    0
      firmware/runtime/glue.c
  32. 83
    0
      firmware/runtime/lscript.ld
  33. 92
    0
      firmware/runtime/main.rs
  34. 320
    0
      firmware/runtime/startup.S
  35. 185
    0
      firmware/runtime/translation_table.S
  36. 106
    0
      hp_dma.py
  37. 90
    0
      load.py
  38. 284
    0
      maxi_dma.py
  39. 771
    0
      ps7_init.tcl
  40. 24
    0
      run.py
  41. 46
    0
      test_hp_dma.py
  42. 68
    0
      test_maxi_dma.py
  43. 79
    0
      test_rtio_dma.py
  44. 61
    0
      xilinx-tcl.cfg
  45. 180
    0
      zedboard.py
  46. 211
    0
      zynq-7000.cfg

+ 10
- 0
.gitignore View File

@@ -0,0 +1,10 @@
gateware/build
misoc_*
*.o
*.a
*.d
*.elf
*.bin
*.lock
*.vcd
__pycache__

+ 70
- 0
experiments/dds_timing.py View File

@@ -0,0 +1,70 @@
from artiq.experiment import *
import numpy as np

class Benchmark(EnvExperiment):
def build(self):
self.setattr_device("core")
self.dds = self.get_device("urukul0_ch0")

@kernel
def bisect_underflow(self, f, t_max_mu=1000, t_min_mu=0):
t = 0
while (t_max_mu-t_min_mu) > 2:
t = np.int64( (t_max_mu+t_min_mu)/2 )
print(np.int32(t_min_mu), np.int32(t_max_mu))
try:
f(t)
except RTIOUnderflow:
print("Underflow")
if t == t_max_mu:
raise ValueError("Upper bound underflowed")
t_min_mu = t
else:
t_max_mu = t
return t

@kernel
def dds_update_rate(self):
t=0
def f(t):
self.core.reset()
for i in range(1000):
with parallel:
delay_mu(t)
self.dds.set(80*MHz, amplitude=0.1, phase=0.5)
t = self.bisect_underflow(lambda t: f(t), t_max_mu=50000)
print("Sustained DDS update time: ", np.int32(t), "mu")

@kernel
def dds_setmu_update_rate(self):
t=0

def f(t):
f_mu = self.dds.frequency_to_ftw(80*MHz)
amp_mu = self.dds.amplitude_to_asf(0.1)
phase_mu = self.dds.turns_to_pow(0.5)
self.core.reset()
for i in range(1000):
with parallel:
delay_mu(t)
self.dds.set_mu(f_mu, asf=amp_mu, pow=phase_mu)
t = self.bisect_underflow(lambda t: f(t), t_max_mu=50000)
print("Sustained DDS set_mu update time: ", np.int32(t), "mu")

@kernel
def measure_dds_timeline_advance(self):
self.core.break_realtime()
t0 = now_mu()
self.dds.set(80*MHz, amplitude=0.1, phase=0.5)
dt = now_mu()-t0
core_log("DDS timeline advance:", np.int32(dt), "mu")

@kernel
def run(self):
self.core.reset()
self.dds.cpld.init(blind=True)
self.dds.init(blind=True)

# self.measure_dds_timeline_advance()
self.dds_update_rate()
# self.dds_setmu_update_rate()

+ 147
- 0
experiments/device_db.py View File

@@ -0,0 +1,147 @@
device_db = {
"core": {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {"host": "lalala", "ref_period": 1/(125e6), "ref_multiplier": 1}
},

"ttl0": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": 0},
},



"spi_urukul0": {
"type": "local",
"module": "artiq.coredevice.spi2",
"class": "SPIMaster",
"arguments": {"channel": 12}
},
"ttl_urukul0_io_update": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 13}
},
"urukul0_cpld": {
"type": "local",
"module": "artiq.coredevice.urukul",
"class": "CPLD",
"arguments": {
"spi_device": "spi_urukul0",
"io_update_device": "ttl_urukul0_io_update",
"refclk": 125e6,
"clk_sel": 1
}
},
"urukul0_ch0": {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 32,
"chip_select": 4,
"cpld_device": "urukul0_cpld",
}
},
"urukul0_ch1": {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 32,
"chip_select": 5,
"cpld_device": "urukul0_cpld",
}
},
"urukul0_ch2": {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 32,
"chip_select": 6,
"cpld_device": "urukul0_cpld",
}
},
"urukul0_ch3": {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 32,
"chip_select": 7,
"cpld_device": "urukul0_cpld",
}
},



"spi_urukul1": {
"type": "local",
"module": "artiq.coredevice.spi2",
"class": "SPIMaster",
"arguments": {"channel": 17}
},
"ttl_urukul1_io_update": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 18}
},
"urukul1_cpld": {
"type": "local",
"module": "artiq.coredevice.urukul",
"class": "CPLD",
"arguments": {
"spi_device": "spi_urukul1",
"io_update_device": "ttl_urukul1_io_update",
"refclk": 125e6,
"clk_sel": 1
}
},
"urukul1_ch0": {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 32,
"chip_select": 4,
"cpld_device": "urukul1_cpld",
}
},
"urukul1_ch1": {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 32,
"chip_select": 5,
"cpld_device": "urukul1_cpld",
}
},
"urukul1_ch2": {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 32,
"chip_select": 6,
"cpld_device": "urukul1_cpld",
}
},
"urukul1_ch3": {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 32,
"chip_select": 7,
"cpld_device": "urukul1_cpld",
}
},
}

+ 46
- 0
experiments/mandelbrot.py View File

@@ -0,0 +1,46 @@
from artiq.experiment import *
import numpy as np

class Mandelbrot(EnvExperiment):
def build(self):
self.setattr_device("core")

def col(self, i):
sys.stdout.write(" .,-:;i+hHM$*#@ "[i])

def row(self):
print("")

# based on: http://warp.povusers.org/MandScripts/python.html
@kernel
def run(self):
minX = -2.0
maxX = 1.0
width = 78
height = 36
aspectRatio = 2

yScale = (maxX-minX)*(height/width)*aspectRatio

accum = 0.

t0 = self.core.get_rtio_counter_mu()

for y in range(height):
for x in range(width):
c_r = minX+x*(maxX-minX)/width
c_i = y*yScale/height-yScale/2
z_r = c_r
z_i = c_i
i = 0
for i in range(16):
if z_r*z_r + z_i*z_i > 4:
break
new_z_r = (z_r*z_r)-(z_i*z_i) + c_r
z_i = 2*z_r*z_i + c_i
z_r = new_z_r
accum += i

dt = self.core.get_rtio_counter_mu() - t0
core_log("Execution time:", np.int32(dt), "mu")
# Takes 666763336 mu on Kasli

+ 14
- 0
experiments/minimal.py View File

@@ -0,0 +1,14 @@
from artiq.experiment import *


class Minimal(EnvExperiment):
def build(self):
self.setattr_device("core")

@kernel
def run(self):
core_log(" :: Hello from kernel")
# try:
raise ValueError
# except ValueError as e:
# core_log(" :: Caught exception.")

+ 93
- 0
experiments/ttl.py View File

@@ -0,0 +1,93 @@
from artiq.experiment import *
import numpy as np

class TtlTests(EnvExperiment):
def build(self):
self.setattr_device("core")
self.ttlio = self.get_device("ttl0")

@kernel
def bisect_underflow(self, f, t_max_mu=1000, t_min_mu=0):
t = 0
while (t_max_mu-t_min_mu) > 1:
t = np.int64( (t_max_mu+t_min_mu)/2 )
print(np.int32(t_min_mu), np.int32(t_max_mu))
try:
f(t)
except RTIOUnderflow:
print("Underflow")
if t == t_max_mu:
raise ValueError("Upper bound underflowed")
t_min_mu = t
else:
t_max_mu = t
return t

@kernel
def test_input_operation(self):
core_log("")
core_log("Test input operation ...")
self.core.reset()
self.ttlio.output()
delay(10*us)
with parallel:
self.ttlio.gate_rising(10*us)
with sequential:
delay(1*us)
t_out_mu = now_mu()
self.ttlio.pulse(1*us)
t_in_mu = self.ttlio.timestamp_mu()
dt = np.int32(t_in_mu-t_out_mu)
core_log("t_in-t_out:", dt, "mu")


@kernel
def event_response(self):
"""How soon after an input event can we reliably schedule an output event"""
core_log("")
core_log("Measuring minimum event response time")
t = 0
def f(t_delay):
self.core.reset()

self.ttlio.output()
delay(1*us)
for i in range(10):
# Make sure we have plenty of slack
delay(1000*us)
self.ttlio.off()
delay(1*us)
with parallel:
self.ttlio.gate_rising(4*us)
with sequential:
delay(100*ns)
self.ttlio.pulse(100*ns)

t_input = self.ttlio.timestamp_mu()
at_mu(t_input+t_delay)
self.ttlio.pulse(10*us)
t = self.bisect_underflow(lambda t: f(t), t_max_mu=10000)
core_log("Event response time: ", np.int32(t), "mu")

@kernel
def output_pulse_rate(self):
"""Sustained TTL output pulse rate"""
core_log("")
core_log("Measuring sustained output pulse rate")
t=0
def f(t):
self.core.break_realtime()
for i in range(20000):
delay_mu(t)
self.ttlio.pulse_mu(t)
t = self.bisect_underflow(lambda t: f(t), t_max_mu=1000)
core_log("Sustained pulse rate: ", np.int32(2*t), "mu")



@kernel
def run(self):
self.core.reset()
self.test_input_operation()
self.output_pulse_rate()
self.event_response()

+ 18
- 0
experiments/wall_time.py View File

@@ -0,0 +1,18 @@
from artiq.experiment import *

class WallTime(EnvExperiment):
def build(self):
self.setattr_device("core")

@kernel
def run(self):
t0 = self.core.get_rtio_counter_mu()
i = 0
while True:
t = self.core.get_rtio_counter_mu()
dt_s = (t-t0)
if dt_s < 1e9:
continue
t0 = t
core_log(i)
i += 1

+ 7
- 0
firmware/Cargo.toml View File

@@ -0,0 +1,7 @@
[workspace]
members = ["runtime"]

[profile.dev]
incremental = true
lto = false
opt-level = 2

+ 14
- 0
firmware/libboard_misoc/Cargo.toml View File

@@ -0,0 +1,14 @@
[package]
name = "board_misoc"
version = "0.1.0"
authors = ["cjb <cjb>"]




[dependencies]

[lib]
path="lib.rs"
name = "board_misoc"


+ 56
- 0
firmware/libboard_misoc/lib.rs View File

@@ -0,0 +1,56 @@
#![no_std]
#![feature(asm)]

include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs"));

pub mod uart;
#[macro_use]
pub mod uart_console;

// Clean = expunge cache line with writeback
pub fn clean_data_cache(base: usize, len: usize) {
const CACHE_SYNC_OFFSET: isize = 0x0730/4; // Cache Sync
const CACHE_CLEAN_PA_OFFSET: isize = 0x7B0/4;
const L2CC_BASE: *mut u32 = 0xF8F02000 as *mut u32;
const CACHE_LINE_LENGTH: usize = 32;
let mut addr = base & !(CACHE_LINE_LENGTH-1);
loop {
if addr > base+len {break}
unsafe {
write_volatile(L2CC_BASE.offset(CACHE_CLEAN_PA_OFFSET), addr as u32);
write_volatile(L2CC_BASE.offset(CACHE_SYNC_OFFSET), 0);

// Clean data cache line by virtual address
asm!("mcr p15, 0, $0, c7, c10, 1"::"r"(addr))
}
addr += CACHE_LINE_LENGTH;
}
}


use core::ptr::write_volatile;

// Invalidate = expunge cache line without writeback
pub fn invalidate_data_cache(base: usize, len: usize) {
const CACHE_SYNC_OFFSET: isize = 0x0730/4; // Cache Sync
const CACHE_INVLD_PA_OFFSET: isize = 0x0770/4; // Cache Invalid by PA
const L2CC_BASE: *mut u32 = 0xF8F02000 as *mut u32;
const CACHE_LINE_LENGTH: usize = 32;

let mut addr = base & !(CACHE_LINE_LENGTH-1);
loop {
if addr > base+len {break}

unsafe {
write_volatile(L2CC_BASE.offset(CACHE_INVLD_PA_OFFSET), addr as u32);
write_volatile(L2CC_BASE.offset(CACHE_SYNC_OFFSET), 0);

// Invalidate data cache line by virtual address
asm!("mcr p15, 0, $0, c7, c6, 1"::"r"(addr))
}
addr += CACHE_LINE_LENGTH;
}
}




+ 14
- 0
firmware/libboard_misoc/uart.rs View File

@@ -0,0 +1,14 @@
use core::ptr::{read_volatile, write_volatile};


pub const UART_BASE: *mut u32 = 0xE0001000 as *mut u32;
// pub const UART_BASE: *mut u32 = 0x101000 as *mut u32;


#[export_name="uart_write"]
pub extern fn write(c: u8) {
unsafe {
while read_volatile(UART_BASE.offset(0x2c/4)) & 0x10 != 0 {}
write_volatile(UART_BASE.offset(0x30/4), c as u32);
}
}

+ 30
- 0
firmware/libboard_misoc/uart_console.rs View File

@@ -0,0 +1,30 @@
use core::fmt;


pub struct Console;

impl fmt::Write for Console {
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
use uart;

for c in s.bytes() {
unsafe { uart::write(c) }
}

Ok(())
}
}

#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({
use core::fmt::Write;
write!($crate::uart_console::Console, $($arg)*).unwrap()
})
}

#[macro_export]
macro_rules! println {
($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

+ 8
- 0
firmware/libdyld/Cargo.toml View File

@@ -0,0 +1,8 @@
[package]
authors = ["M-Labs"]
name = "dyld"
version = "0.0.0"

[lib]
name = "dyld"
path = "lib.rs"

+ 2737
- 0
firmware/libdyld/elf.rs
File diff suppressed because it is too large
View File


+ 339
- 0
firmware/libdyld/lib.rs View File

@@ -0,0 +1,339 @@
#![no_std]

use core::{mem, ptr, fmt, slice, str, convert};
use elf::*;

pub mod elf;

fn read_unaligned<T: Copy>(data: &[u8], offset: usize) -> Result<T, ()> {
if data.len() < offset + mem::size_of::<T>() {
Err(())
} else {
let ptr = data.as_ptr().wrapping_offset(offset as isize) as *const T;
Ok(unsafe { ptr::read_unaligned(ptr) })
}
}

fn get_ref<T: Copy>(data: &[u8], offset: usize) -> Result<&T, ()> {
if data.len() < offset + mem::size_of::<T>() {
Err(())
} else if (data.as_ptr() as usize + offset) & (mem::align_of::<T>() - 1) != 0 {
Err(())
} else {
let ptr = data.as_ptr().wrapping_offset(offset as isize) as *const T;
Ok(unsafe { &*ptr })
}
}

fn get_ref_slice<T: Copy>(data: &[u8], offset: usize, len: usize) -> Result<&[T], ()> {
if data.len() < offset + mem::size_of::<T>() * len {
Err(())
} else if (data.as_ptr() as usize + offset) & (mem::align_of::<T>() - 1) != 0 {
Err(())
} else {
let ptr = data.as_ptr().wrapping_offset(offset as isize) as *const T;
Ok(unsafe { slice::from_raw_parts(ptr, len) })
}
}

fn elf_hash(name: &[u8]) -> u32 {
let mut h: u32 = 0;
for c in name {
h = (h << 4) + *c as u32;
let g = h & 0xf0000000;
if g != 0 {
h ^= g >> 24;
h &= !g;
}
}
h
}

#[derive(Debug)]
pub enum Error<'a> {
Parsing(&'static str),
Lookup(&'a [u8])
}

impl<'a> convert::From<&'static str> for Error<'a> {
fn from(desc: &'static str) -> Error<'a> {
Error::Parsing(desc)
}
}

impl<'a> fmt::Display for Error<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Error::Parsing(desc) =>
write!(f, "parse error: {}", desc),
&Error::Lookup(sym) =>
match str::from_utf8(sym) {
Ok(sym) => write!(f, "symbol lookup error: {}", sym),
Err(_) => write!(f, "symbol lookup error: {:?}", sym)
}
}
}
}

pub struct Library<'a> {
image_off: Elf32_Addr,
image_sz: usize,
strtab: &'a [u8],
symtab: &'a [Elf32_Sym],
pltrel: &'a [Elf32_Rela],
hash_bucket: &'a [Elf32_Word],
hash_chain: &'a [Elf32_Word],
}

impl<'a> Library<'a> {
pub fn lookup(&self, name: &[u8]) -> Option<Elf32_Word> {
let hash = elf_hash(name);
let mut index = self.hash_bucket[hash as usize % self.hash_bucket.len()] as usize;

loop {
if index == STN_UNDEF { return None }

let sym = &self.symtab[index];
let sym_name_off = sym.st_name as usize;
match self.strtab.get(sym_name_off..sym_name_off + name.len()) {
Some(sym_name) if sym_name == name => {
if ELF32_ST_BIND(sym.st_info) & STB_GLOBAL == 0 {
return None
}

match sym.st_shndx {
SHN_UNDEF => return None,
SHN_ABS => return Some(sym.st_value),
_ => return Some(self.image_off + sym.st_value)
}
}
_ => (),
}

index = self.hash_chain[index] as usize;
}
}

fn name_starting_at(&self, offset: usize) -> Result<&'a [u8], Error<'a>> {
let size = self.strtab.iter().skip(offset).position(|&x| x == 0)
.ok_or("symbol in symbol table not null-terminated")?;
Ok(self.strtab.get(offset..offset + size)
.ok_or("cannot read symbol name")?)
}

fn update_rela(&self, rela: &Elf32_Rela, value: Elf32_Word) -> Result<(), Error<'a>> {
if rela.r_offset as usize + mem::size_of::<Elf32_Addr>() > self.image_sz {
return Err("relocation out of image bounds")?
}

let ptr = (self.image_off + rela.r_offset) as *mut Elf32_Addr;
Ok(unsafe { *ptr = value })
}

// This is unsafe because it mutates global data (the PLT).
pub unsafe fn rebind(&self, name: &[u8], addr: Elf32_Word) -> Result<(), Error<'a>> {
for rela in self.pltrel.iter() {
match ELF32_R_TYPE(rela.r_info) {
R_OR1K_32 | R_OR1K_GLOB_DAT | R_OR1K_JMP_SLOT => {
let sym = self.symtab.get(ELF32_R_SYM(rela.r_info) as usize)
.ok_or("symbol out of bounds of symbol table")?;
let sym_name = self.name_starting_at(sym.st_name as usize)?;

if sym_name == name {
self.update_rela(rela, addr)?
}
}

// No associated symbols for other relocation types.
_ => ()
}
}
Ok(())
}

fn resolve_rela(&self, rela: &Elf32_Rela, resolve: &Fn(&[u8]) -> Option<Elf32_Word>)
-> Result<(), Error<'a>> {
let sym;
if ELF32_R_SYM(rela.r_info) == 0 {
sym = None;
} else {
sym = Some(self.symtab.get(ELF32_R_SYM(rela.r_info) as usize)
.ok_or("symbol out of bounds of symbol table")?)
}

let value;
match ELF32_R_TYPE(rela.r_info) {
R_OR1K_NONE =>
return Ok(()),

R_OR1K_RELATIVE =>
value = self.image_off + rela.r_addend as Elf32_Word,

R_OR1K_32 | R_OR1K_GLOB_DAT | R_OR1K_JMP_SLOT => {
let sym = sym.ok_or("relocation requires an associated symbol")?;
let sym_name = self.name_starting_at(sym.st_name as usize)?;

// First, try to resolve against itself.
match self.lookup(sym_name) {
Some(addr) => value = addr,
None => {
// Second, call the user-provided function.
match resolve(sym_name) {
Some(addr) => value = addr,
None => {
// We couldn't find it anywhere.
return Err(Error::Lookup(sym_name))
}
}
}
}
}

_ => return Err("unsupported relocation type")?
}

self.update_rela(rela, value)
}

pub fn load(data: &[u8], image: &'a mut [u8], resolve: &Fn(&[u8]) -> Option<Elf32_Word>)
-> Result<Library<'a>, Error<'a>> {
#![allow(unused_assignments)]

let ehdr = read_unaligned::<Elf32_Ehdr>(data, 0)
.map_err(|()| "cannot read ELF header")?;

const IDENT: [u8; EI_NIDENT] = [
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
ELFCLASS32, ELFDATA2MSB, EV_CURRENT, ELFOSABI_NONE,
/* ABI version */ 0, /* padding */ 0, 0, 0, 0, 0, 0, 0
];

#[cfg(target_arch = "or1k")]
const ARCH: u16 = EM_OPENRISC;
#[cfg(not(target_arch = "or1k"))]
const ARCH: u16 = EM_NONE;

if ehdr.e_ident != IDENT || ehdr.e_type != ET_DYN || ehdr.e_machine != ARCH {
return Err("not a shared library for current architecture")?
}

let mut dyn_off = None;
for i in 0..ehdr.e_phnum {
let phdr_off = ehdr.e_phoff as usize + mem::size_of::<Elf32_Phdr>() * i as usize;
let phdr = read_unaligned::<Elf32_Phdr>(data, phdr_off)
.map_err(|()| "cannot read program header")?;

match phdr.p_type {
PT_LOAD => {
if (phdr.p_vaddr + phdr.p_filesz) as usize > image.len() ||
(phdr.p_offset + phdr.p_filesz) as usize > data.len() {
return Err("program header requests an out of bounds load")?
}
let dst = image.get_mut(phdr.p_vaddr as usize..
(phdr.p_vaddr + phdr.p_filesz) as usize)
.ok_or("cannot write to program header destination")?;
let src = data.get(phdr.p_offset as usize..
(phdr.p_offset + phdr.p_filesz) as usize)
.ok_or("cannot read from program header source")?;
dst.copy_from_slice(src);
}

PT_DYNAMIC =>
dyn_off = Some(phdr.p_vaddr),

_ => ()
}
}

let (mut strtab_off, mut strtab_sz) = (0, 0);
let (mut symtab_off, mut symtab_sz) = (0, 0);
let (mut rela_off, mut rela_sz) = (0, 0);
let (mut pltrel_off, mut pltrel_sz) = (0, 0);
let (mut hash_off, mut hash_sz) = (0, 0);
let mut sym_ent = 0;
let mut rela_ent = 0;
let mut nbucket = 0;
let mut nchain = 0;

let dyn_off = dyn_off.ok_or("cannot find a dynamic header")?;
for i in 0.. {
let dyn_off = dyn_off as usize + i * mem::size_of::<Elf32_Dyn>();
let dyn = get_ref::<Elf32_Dyn>(image, dyn_off)
.map_err(|()| "cannot read dynamic header")?;

let val = unsafe { dyn.d_un.d_val } as usize;
match dyn.d_tag {
DT_NULL => break,
DT_REL => return Err("relocations with implicit addend are not supported")?,
DT_STRTAB => strtab_off = val,
DT_STRSZ => strtab_sz = val,
DT_SYMTAB => symtab_off = val,
DT_SYMENT => sym_ent = val,
DT_RELA => rela_off = val,
DT_RELASZ => rela_sz = val / mem::size_of::<Elf32_Rela>(),
DT_RELAENT => rela_ent = val,
DT_JMPREL => pltrel_off = val,
DT_PLTRELSZ => pltrel_sz = val / mem::size_of::<Elf32_Rela>(),
DT_HASH => {
nbucket = *get_ref::<Elf32_Word>(image, val + 0)
.map_err(|()| "cannot read hash bucket count")? as usize;
nchain = *get_ref::<Elf32_Word>(image, val + 4)
.map_err(|()| "cannot read hash chain count")? as usize;
hash_off = val + 8;
hash_sz = nbucket + nchain;
}
_ => ()
}
}

if sym_ent != mem::size_of::<Elf32_Sym>() {
return Err("incorrect symbol entry size")?
}
if rela_ent != mem::size_of::<Elf32_Rela>() {
return Err("incorrect relocation entry size")?
}

// These are the same--there are as many chains as buckets, and the chains only contain
// the symbols that overflowed the bucket.
symtab_sz = nchain;

// Drop the mutability. See also the comment below.
let image = &*image;

let strtab = get_ref_slice::<u8>(image, strtab_off, strtab_sz)
.map_err(|()| "cannot read string table")?;
let symtab = get_ref_slice::<Elf32_Sym>(image, symtab_off, symtab_sz)
.map_err(|()| "cannot read symbol table")?;
let rela = get_ref_slice::<Elf32_Rela>(image, rela_off, rela_sz)
.map_err(|()| "cannot read rela entries")?;
let pltrel = get_ref_slice::<Elf32_Rela>(image, pltrel_off, pltrel_sz)
.map_err(|()| "cannot read pltrel entries")?;
let hash = get_ref_slice::<Elf32_Word>(image, hash_off, hash_sz)
.map_err(|()| "cannot read hash entries")?;

let library = Library {
image_off: image.as_ptr() as Elf32_Word,
image_sz: image.len(),
strtab: strtab,
symtab: symtab,
pltrel: pltrel,
hash_bucket: &hash[..nbucket],
hash_chain: &hash[nbucket..nbucket + nchain],
};

// If a borrow exists anywhere, the borrowed memory cannot be mutated except
// through that pointer or it's UB. However, we need to retain pointers
// to the symbol tables and relocations, and at the same time mutate the code
// to resolve the relocations.
//
// To avoid invoking UB, we drop the only pointer to the entire area (which is
// unique since it's a &mut); we retain pointers to the various tables, but
// we never write to the memory they refer to, so it's safe.
mem::drop(image);

for r in rela { library.resolve_rela(r, resolve)? }
for r in pltrel { library.resolve_rela(r, resolve)? }

Ok(library)
}
}

BIN
firmware/libksupport/._eh.rs View File


+ 20
- 0
firmware/libksupport/Cargo.toml View File

@@ -0,0 +1,20 @@
[package]
name = "ksupport"
version = "0.1.0"
authors = ["cjb <cjb>"]


[build-dependencies]
cc = "1.0"

[dependencies]
board_misoc = { path = "../libboard_misoc"}
cslice = { version = "0.3" }
unwind = { path = "../libunwind-rs" }
libc = { default-features = false }
volatile_cell = "1.0"

[lib]
path="lib.rs"
name = "ksupport"


+ 89
- 0
firmware/libksupport/api.rs View File

@@ -0,0 +1,89 @@
macro_rules! api {
($i:ident) => ({
extern { static $i: u8; }
api!($i = &$i as *const _)
});
($i:ident, $d:item) => ({
$d
api!($i = $i)
});
($i:ident = $e:expr) => {
(stringify!($i), unsafe { $e as *const () })
}
}


#[allow(unused_unsafe)]
static mut API: &'static [(&'static str, *const ())] = &[
api!(__divsi3),
api!(__modsi3),
api!(__ledf2),
api!(__gedf2),
api!(__unorddf2),
api!(__eqdf2),
api!(__ltdf2),
api!(__nedf2),
api!(__gtdf2),
api!(__addsf3),
api!(__subsf3),
api!(__mulsf3),
api!(__divsf3),
api!(__lshrdi3),
api!(__muldi3),
api!(__divdi3),
api!(__ashldi3),
api!(__ashrdi3),
api!(__udivmoddi4),
api!(__floatsisf),
api!(__floatunsisf),
api!(__fixsfsi),
api!(__fixunssfsi),
api!(__adddf3),
api!(__subdf3),
api!(__muldf3),
api!(__divdf3),
api!(__floatsidf),
api!(__floatunsidf),
api!(__floatdidf),
api!(__fixdfsi),
api!(__fixdfdi),
api!(__fixunsdfsi),
api!(__udivdi3),
api!(__umoddi3),
api!(__moddi3),
api!(__powidf2),

/* libc */
// api!(abort = ::abort),
api!(memcmp, extern { fn memcmp(a: *const u8, b: *mut u8, size: usize); }),

/* libm */
api!(sqrt),
api!(round),
api!(floor),

/* exceptions */
api!(_Unwind_Resume = ::unwind::_Unwind_Resume),
api!(__artiq_personality = ::eh::personality),
api!(__artiq_raise = ::eh::raise),
api!(__artiq_reraise = ::eh::reraise),

/* proxified syscalls */
api!(core_log),

api!(now = &::NOW as *const _),


/* direct syscalls */
api!(rtio_init = ::rtio::init),
api!(rtio_get_counter = ::rtio::get_counter),
api!(rtio_log),
api!(rtio_output = ::rtio::output),
api!(rtio_output_wide = ::rtio::output_wide),
api!(rtio_input_timestamp = ::rtio::input_timestamp),
api!(rtio_input_data = ::rtio::input_data),
api!(rtio_input_data_timeout = ::rtio::input_data_timeout),
api!(rtio_input_timestamp_data = ::rtio::input_timestamp_data),

];


+ 12
- 0
firmware/libksupport/build.rs View File

@@ -0,0 +1,12 @@
extern crate cc;


fn main() {
let glue_path = "glue.c";

println!("cargo:rerun-if-changed={}", glue_path);

cc::Build::new()
.file(glue_path)
.compile("glue");
}

+ 602
- 0
firmware/libksupport/eh.rs View File

@@ -0,0 +1,602 @@
// Portions of the code in this file are derived from code by:
//
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(non_upper_case_globals, non_camel_case_types, dead_code)]

use core::{ptr, mem};
use cslice::CSlice;
use unwind as uw;
use libc::{c_int, c_void};

#[cfg(not(target_arch = "arm"))]
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code;

#[cfg(target_arch = "arm")]
type _Unwind_Stop_Fn = extern "C" fn(state: uw::_Unwind_State,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code;

extern {
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
stop_fn: _Unwind_Stop_Fn,
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
}

const DW_EH_PE_omit: u8 = 0xFF;
const DW_EH_PE_absptr: u8 = 0x00;

const DW_EH_PE_uleb128: u8 = 0x01;
const DW_EH_PE_udata2: u8 = 0x02;
const DW_EH_PE_udata4: u8 = 0x03;
const DW_EH_PE_udata8: u8 = 0x04;
const DW_EH_PE_sleb128: u8 = 0x09;
const DW_EH_PE_sdata2: u8 = 0x0A;
const DW_EH_PE_sdata4: u8 = 0x0B;
const DW_EH_PE_sdata8: u8 = 0x0C;

const DW_EH_PE_pcrel: u8 = 0x10;
const DW_EH_PE_textrel: u8 = 0x20;
const DW_EH_PE_datarel: u8 = 0x30;
const DW_EH_PE_funcrel: u8 = 0x40;
const DW_EH_PE_aligned: u8 = 0x50;

const DW_EH_PE_indirect: u8 = 0x80;

#[derive(Clone)]
struct DwarfReader {
pub ptr: *const u8,
}

impl DwarfReader {
fn new(ptr: *const u8) -> DwarfReader {
DwarfReader { ptr: ptr }
}

// DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
// on a 4-byte boundary. This may cause problems on platforms with strict
// alignment requirements. By wrapping data in a "packed" struct, we are
// telling the backend to generate "misalignment-safe" code.
unsafe fn read<T: Copy>(&mut self) -> T {
let result = ptr::read_unaligned(self.ptr as *const T);
self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
result
}

// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
// Length Data".
unsafe fn read_uleb128(&mut self) -> u64 {
let mut shift: usize = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
result
}

unsafe fn read_sleb128(&mut self) -> i64 {
let mut shift: usize = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
// sign-extend
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
result |= (!0 as u64) << shift;
}
result as i64
}

unsafe fn read_encoded_pointer(&mut self, encoding: u8) -> usize {
fn round_up(unrounded: usize, align: usize) -> usize {
debug_assert!(align.is_power_of_two());
(unrounded + align - 1) & !(align - 1)
}

debug_assert!(encoding != DW_EH_PE_omit);

// DW_EH_PE_aligned implies it's an absolute pointer value
if encoding == DW_EH_PE_aligned {
self.ptr = round_up(self.ptr as usize, mem::size_of::<usize>()) as *const u8;
return self.read::<usize>()
}

let value_ptr = self.ptr;
let mut result = match encoding & 0x0F {
DW_EH_PE_absptr => self.read::<usize>(),
DW_EH_PE_uleb128 => self.read_uleb128() as usize,
DW_EH_PE_udata2 => self.read::<u16>() as usize,
DW_EH_PE_udata4 => self.read::<u32>() as usize,
DW_EH_PE_udata8 => self.read::<u64>() as usize,
DW_EH_PE_sleb128 => self.read_sleb128() as usize,
DW_EH_PE_sdata2 => self.read::<i16>() as usize,
DW_EH_PE_sdata4 => self.read::<i32>() as usize,
DW_EH_PE_sdata8 => self.read::<i64>() as usize,
_ => panic!(),
};

result += match encoding & 0x70 {
DW_EH_PE_absptr => 0,
// relative to address of the encoded value, despite the name
DW_EH_PE_pcrel => value_ptr as usize,
// DW_EH_PE_funcrel => {
// if context.func_start == 0 {
// return Err(())
// }
// context.func_start
// }
// DW_EH_PE_textrel => (*context.get_text_start)(),
// DW_EH_PE_datarel => (*context.get_data_start)(),
_ => panic!(),
};

if encoding & DW_EH_PE_indirect != 0 {
result = *(result as *const usize);
}

result
}
}

fn encoding_size(encoding: u8) -> usize {
if encoding == DW_EH_PE_omit {
return 0
}

match encoding & 0x0F {
DW_EH_PE_absptr => mem::size_of::<usize>(),
DW_EH_PE_udata2 => 2,
DW_EH_PE_udata4 => 4,
DW_EH_PE_udata8 => 8,
DW_EH_PE_sdata2 => 2,
DW_EH_PE_sdata4 => 4,
DW_EH_PE_sdata8 => 8,
_ => panic!()
}
}

pub enum EHAction {
None,
Cleanup(usize),
Catch(usize),
Terminate,
}

unsafe fn find_eh_action(uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context) -> EHAction {
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let func_start = uw::_Unwind_GetRegionStart(context);

if lsda.is_null() {
return EHAction::None
}

let mut reader = DwarfReader::new(lsda);

let start_encoding = reader.read::<u8>();
// base address for landing pad offsets
let lpad_base = if start_encoding != DW_EH_PE_omit {
reader.read_encoded_pointer(start_encoding)
} else {
func_start
};

let mut ttype_encoding = reader.read::<u8>();
let ttype_encoding_size = encoding_size(ttype_encoding) as isize;

let class_info;
if ttype_encoding != DW_EH_PE_omit {
// // FIXME: ARM-only override; refactor this into read_target2 or something.
// // Alternatively, pass --target2=rel.
// ttype_encoding = DW_EH_PE_pcrel | DW_EH_PE_indirect;

let class_info_offset = reader.read_uleb128();
class_info = reader.ptr.offset(class_info_offset as isize);
} else {
class_info = ptr::null();
}
assert!(!class_info.is_null());

let call_site_encoding = reader.read::<u8>();
let call_site_table_length = reader.read_uleb128();
let action_table = reader.ptr.offset(call_site_table_length as isize);

let ip = uw::_Unwind_GetIP(context) - 1;
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
let exn_name = &exception_info.exception.unwrap().name;

while reader.ptr < action_table {
let cs_start = reader.read_encoded_pointer(call_site_encoding);
let cs_len = reader.read_encoded_pointer(call_site_encoding);
let cs_lpad = reader.read_encoded_pointer(call_site_encoding);
let cs_action = reader.read_uleb128();

if ip < func_start + cs_start {
// Callsite table is sorted by cs_start, so if we've passed the ip, we
// may stop searching.
break
}
if ip > func_start + cs_start + cs_len {
continue
}

if cs_lpad == 0 {
return EHAction::None
}

let lpad = lpad_base + cs_lpad;
if cs_action == 0 {
return EHAction::Cleanup(lpad)
}

let action_entry = action_table.offset((cs_action - 1) as isize);
let mut action_reader = DwarfReader::new(action_entry);
loop {
let type_info_offset = action_reader.read_sleb128() as isize;
let action_offset = action_reader.clone().read_sleb128() as isize;
assert!(type_info_offset >= 0);

if type_info_offset > 0 {
let type_info_ptr_ptr = class_info.offset(-type_info_offset * ttype_encoding_size);
let type_info_ptr = DwarfReader::new(type_info_ptr_ptr)
.read_encoded_pointer(ttype_encoding);
let type_info = *(type_info_ptr as *const CSlice<u8>);

if type_info.as_ref() == exn_name.as_ref() {
return EHAction::Catch(lpad)
}

if type_info.len() == 0 {
// This is a catch-all clause. We don't compare type_info_ptr with null here
// because, in PIC mode, the OR1K LLVM backend emits a literal zero
// encoded with DW_EH_PE_pcrel, which of course doesn't result in
// a proper null pointer.
return EHAction::Catch(lpad)
}
}

if action_offset == 0 {
break
} else {
action_reader.ptr = action_reader.ptr.offset(action_offset)
}
}

return EHAction::None
}

// the function has a personality but no landing pads; this is fine
EHAction::None
}

#[repr(C)]
#[derive(Clone, Copy)]
pub struct Exception<'a> {
pub name: CSlice<'a, u8>,
pub file: CSlice<'a, u8>,
pub line: u32,
pub column: u32,
pub function: CSlice<'a, u8>,
pub message: CSlice<'a, u8>,
pub param: [i64; 3]
}

const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */

const MAX_BACKTRACE_SIZE: usize = 128;

#[repr(C)]
struct ExceptionInfo {
uw_exception: uw::_Unwind_Exception,
exception: Option<Exception<'static>>,
handled: bool,
backtrace: [usize; MAX_BACKTRACE_SIZE],
backtrace_size: usize
}

#[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX

#[cfg(target_arch = "arm")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1

#[cfg(any(target_arch = "or1k"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4

// Personality routine for non-ARM-EHABI (which uses a slightly different ABI).
#[cfg(not(target_arch = "arm"))]
#[export_name="__artiq_personality"]
pub extern fn personality(version: c_int,
actions: uw::_Unwind_Action,
uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
unsafe {
if version != 1 || uw_exception_class != EXCEPTION_CLASS {
return uw::_URC_FATAL_PHASE1_ERROR
}

let eh_action = find_eh_action(uw_exception, context);
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
match eh_action {
EHAction::None |
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
}
} else {
match eh_action {
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
if actions as u32 & uw::_UA_HANDLER_FRAME as u32 != 0 {
exception_info.handled = true
}

// Pass a pair of the unwinder exception and ARTIQ exception
// (which immediately follows).
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
uw_exception as uw::_Unwind_Word);
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1,
exception as *const _ as uw::_Unwind_Word);
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
}
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
}
}
}
}

// ARM EHABI personality routine.
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf
#[cfg(target_arch = "arm")]
#[export_name="__artiq_personality"]
unsafe extern "C" fn personality(state: uw::_Unwind_State,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
let state = state as c_int;
let action = state & uw::_US_ACTION_MASK as c_int;
let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int {
// Backtraces on ARM will call the personality routine with
// state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases
// we want to continue unwinding the stack.
if state & uw::_US_FORCE_UNWIND as c_int != 0 {
return continue_unwind(exception_object, context);
}
true
} else if action == uw::_US_UNWIND_FRAME_STARTING as c_int {
false
} else if action == uw::_US_UNWIND_FRAME_RESUME as c_int {
return continue_unwind(exception_object, context);
} else {
return uw::_URC_FAILURE;
};

// The DWARF unwinder assumes that _Unwind_Context holds things like the function
// and LSDA pointers, however ARM EHABI places them into the exception object.
// To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which
// take only the context pointer, GCC personality routines stash a pointer to exception_object
// in the context, using location reserved for ARM's "scratch register" (r12).
uw::_Unwind_SetGR(context,
uw::UNWIND_POINTER_REG,
exception_object as uw::_Unwind_Ptr);
// ...A more principled approach would be to provide the full definition of ARM's
// _Unwind_Context in our libunwind bindings and fetch the required data from there directly,
// bypassing DWARF compatibility functions.

let eh_action = find_eh_action(exception_object, context);
if search_phase {
match eh_action {
EHAction::None |
EHAction::Cleanup(_) => return continue_unwind(exception_object, context),
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Terminate => {
return uw::_URC_FAILURE
}
}
} else {
match eh_action {
EHAction::None => return continue_unwind(exception_object, context),
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
let exception_info = &mut *(exception_object as *mut ExceptionInfo);

match eh_action {
EHAction::Catch(_) => exception_info.handled = true,
_ => (),
}

// Pass a pair of the unwinder exception and ARTIQ exception
// (which immediately follows).
let exception = &exception_info.exception.unwrap();
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
exception_object as uw::_Unwind_Word);
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1,
exception as *const _ as uw::_Unwind_Word);
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
},
EHAction::Terminate => {
return uw::_URC_FAILURE
}
}
}

// On ARM EHABI the personality routine is responsible for actually
// unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON {
uw::_URC_CONTINUE_UNWIND
} else {
uw::_URC_FAILURE
}
}
// Defined in libunwind, matching the GNU implementation in libgcc.
extern "C" {
fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code;
}
}

extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
uw_exception: *mut uw::_Unwind_Exception) {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);

exception_info.exception = None;
}
}

#[cfg(not(target_arch = "arm"))]
extern fn uncaught_exception(_version: c_int,
actions: uw::_Unwind_Action,
_uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
_stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);

if exception_info.backtrace_size < exception_info.backtrace.len() {
let ip = uw::_Unwind_GetIP(context);
exception_info.backtrace[exception_info.backtrace_size] = ip;
exception_info.backtrace_size += 1;
}

if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
::terminate(&exception_info.exception.unwrap(),
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
} else {
uw::_URC_NO_REASON
}
}
}

#[cfg(target_arch = "arm")]
extern "C" fn uncaught_exception(state: uw::_Unwind_State,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
unsafe {
let exception_info = &mut *(exception_object as *mut ExceptionInfo);

if exception_info.backtrace_size < exception_info.backtrace.len() {
let ip = uw::_Unwind_GetIP(context);
exception_info.backtrace[exception_info.backtrace_size] = ip;
exception_info.backtrace_size += 1;
}

// if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
// ::terminate(&exception_info.exception.unwrap(),
// exception_info.backtrace[..exception_info.backtrace_size].as_mut())
// } else {
uw::_URC_NO_REASON
// }
}
}

// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround.
// See https://github.com/rust-lang/rust/issues/39498.
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
uw_exception: uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
},
exception: None,
handled: false,
backtrace: [0; MAX_BACKTRACE_SIZE],
backtrace_size: 0
};

#[export_name="__artiq_raise"]
#[unwind(allowed)]
pub unsafe extern fn raise(exception: *const Exception) -> ! {
// Zing! The Exception<'a> to Exception<'static> transmute is not really sound in case
// the exception is ever captured. Fortunately, they currently aren't, and we save
// on the hassle of having to allocate exceptions somewhere except on stack.
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
INFLIGHT.handled = false;

let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
if result != uw::_URC_END_OF_STACK {
println!("Unwinding error: {}", result as u32);
}
assert!(result == uw::_URC_END_OF_STACK);

// INFLIGHT.backtrace_size = 0;
// let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
// uncaught_exception, ptr::null_mut());
print!("Uncaught exception");
loop {}
unreachable!()
}

#[export_name="__artiq_reraise"]
#[unwind(allowed)]
pub unsafe extern fn reraise() -> ! {
if INFLIGHT.handled {
raise(&INFLIGHT.exception.unwrap())
} else {
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
}
}

// Stub implementations for the functions the panic_unwind crate expects to be provided.
// These all do nothing in libunwind, but aren't built for OR1K.
#[cfg(target_arch = "or1k")]
pub mod stubs {
#![allow(bad_style, unused_variables)]

use super::{uw, c_int};

#[export_name="_Unwind_GetIPInfo"]
pub unsafe extern fn _Unwind_GetIPInfo(ctx: *mut uw::_Unwind_Context,
ip_before_insn: *mut c_int) -> uw::_Unwind_Word {
*ip_before_insn = 0;
uw::_Unwind_GetIP(ctx)
}

#[export_name="_Unwind_GetTextRelBase"]
pub unsafe extern fn _Unwind_GetTextRelBase(ctx: *mut uw::_Unwind_Context) -> uw::_Unwind_Ptr {
unimplemented!()
}

#[export_name="_Unwind_GetDataRelBase"]
pub unsafe extern fn _Unwind_GetDataRelBase(ctx: *mut uw::_Unwind_Context) -> uw::_Unwind_Ptr {
unimplemented!()
}
}

+ 68
- 0
firmware/libksupport/glue.c View File

@@ -0,0 +1,68 @@
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdint.h>

struct slice {
void *ptr;
size_t len;
};

void send_to_core_log(struct slice str);

#define KERNELCPU_EXEC_ADDRESS 0x40800000
#define KERNELCPU_PAYLOAD_ADDRESS 0x40840000
#define KERNELCPU_LAST_ADDRESS 0x4fffffff
#define KSUPPORT_HEADER_SIZE 0x80


/* called by kernel */
double round(double x);
double round(double x)
{
union {double f; uint64_t i;} u = {x};
int e = u.i >> 52 & 0x7ff;
double y;

if (e >= 0x3ff+52)
return x;
if (u.i >> 63)
x = -x;
if (e < 0x3ff-1) {
/* we don't do it in ARTIQ */
/* raise inexact if x!=0 */
// FORCE_EVAL(x + 0x1p52);
return 0*u.f;
}
y = (double)(x + 0x1p52) - 0x1p52 - x;
if (y > 0.5)
y = y + x - 1;
else if (y <= -0.5)
y = y + x + 1;
else
y = y + x;
if (u.i >> 63)
y = -y;
return y;
}

/* called by kernel */
int core_log(const char *fmt, ...);
int core_log(const char *fmt, ...)
{
va_list args;

va_start(args, fmt);
size_t size = vsnprintf(NULL, 0, fmt, args);
char *buf = __builtin_alloca(size + 1);
va_end(args);

va_start(args, fmt);
vsnprintf(buf, size + 1, fmt, args);
va_end(args);

struct slice str = { buf, size };
send_to_core_log(str);
return 0;
}

+ 59
- 0
firmware/libksupport/lib.rs View File

@@ -0,0 +1,59 @@
#![feature(lang_items, asm, libc, panic_unwind, unwind_attributes, global_allocator,
needs_panic_runtime)]
#![no_std]

extern crate unwind;
extern crate libc;
extern crate cslice;
#[macro_use]
extern crate board_misoc;
extern crate volatile_cell;

use cslice::CSlice;
use core::str;

#[no_mangle]
pub static mut now: u64 = 0;

pub mod eh;

#[no_mangle]
pub extern fn send_to_core_log(text: CSlice<u8>) {
match str::from_utf8(text.as_ref()) {
Ok(s) => print!("{}", s),
Err(_e) => println!("kernel send invalid utf8")
}
}

fn terminate(exception: &eh::Exception, backtrace: &mut [usize]) -> ! {
print!("Uncaught exception");
// at {}:{}: {}({})",
// str::from_utf8(exception.file.as_ref()).unwrap(),
// exception.line,
// str::from_utf8(exception.name.as_ref()).unwrap(),
// str::from_utf8(exception.message.as_ref()).unwrap());
loop {}
}

macro_rules! raise {
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
use cslice::AsCSlice;
let exn = $crate::eh::Exception {
name: concat!("0:artiq.coredevice.exceptions.", $name).as_bytes().as_c_slice(),
file: file!().as_bytes().as_c_slice(),
line: line!(),
column: column!(),
// https://github.com/rust-lang/rfcs/pull/1719
function: "(Rust function)".as_bytes().as_c_slice(),
message: $message.as_bytes().as_c_slice(),
param: [$param0, $param1, $param2]
};
#[allow(unused_unsafe)]
unsafe { $crate::eh::raise(&exn) }
});
($name:expr, $message:expr) => ({
raise!($name, $message, 0, 0, 0)
});
}

pub mod rtio;

+ 337
- 0
firmware/libksupport/rtio.rs View File

@@ -0,0 +1,337 @@


use core::ptr::{read_volatile, write_volatile};
use cslice::CSlice;
use board_misoc::csr;

use volatile_cell::VolatileCell;

pub const RTIO_O_STATUS_WAIT: u8 = 1;
pub const RTIO_O_STATUS_UNDERFLOW: u8 = 2;
pub const RTIO_O_STATUS_LINK_ERROR: u8 = 4;
pub const RTIO_I_STATUS_WAIT_EVENT: u8 = 1;
pub const RTIO_I_STATUS_OVERFLOW: u8 = 2;
pub const RTIO_I_STATUS_WAIT_STATUS: u8 = 4;
pub const RTIO_I_STATUS_LINK_ERROR: u8 = 8;

#[repr(C)]
struct RtioCmd {
cmd_channel: i32,
address: i32,
timestamp: i64,
data: i64
}

#[repr(C)]
struct RtioResponse {
status: VolatileCell<i32>,
data: VolatileCell<i32>,
timestamp: VolatileCell<u64>
}

pub const DMA_BASE: isize = 0x200000;



#[no_mangle]
pub extern fn rtio_init() {
unsafe {
csr::rtio_core::reset_write(1);
csr::rtio_core::reset_phy_write(1);
}
}

#[no_mangle]
pub extern fn rtio_get_counter() -> i64 {
unsafe {
csr::rtio::counter_update_write(1);
csr::rtio::counter_read() as i64
}
}

#[inline(always)]
pub unsafe fn rtio_o_data_write(offset: usize, data: u32) {
write_volatile(
csr::rtio::O_DATA_ADDR.offset((csr::rtio::O_DATA_SIZE - 1 - offset) as isize),
data);
}

#[inline(always)]
pub unsafe fn rtio_i_data_read(offset: usize) -> u32 {
read_volatile(
csr::rtio::I_DATA_ADDR.offset((csr::rtio::I_DATA_SIZE - 1 - offset) as isize))
}

#[inline(never)]
unsafe fn process_exceptional_status(timestamp: i64, channel: i32, status: u8) {
if status & RTIO_O_STATUS_WAIT != 0 {
while csr::rtio::o_status_read() & RTIO_O_STATUS_WAIT != 0 {}
}
if status & RTIO_O_STATUS_UNDERFLOW != 0 {
raise!("RTIOUnderflow",
"RTIO underflow at {0} mu, channel {1}, slack {2} mu",
timestamp, channel as i64, timestamp - rtio_get_counter());
}
if status & RTIO_O_STATUS_LINK_ERROR != 0 {
raise!("RTIOLinkError",
"RTIO output link error at {0} mu, channel {1}",
timestamp, channel as i64, 0);
}
}

#[no_mangle]
pub extern fn rtio_output_csr(timestamp: i64, channel: i32, addr: i32, data: i32) {
unsafe {
csr::rtio::chan_sel_write(channel as _);
// writing timestamp clears o_data
csr::rtio::timestamp_write(timestamp as u64);
csr::rtio::o_address_write(addr as _);
rtio_o_data_write(0, data as _);
csr::rtio::o_we_write(1);
let status = csr::rtio::o_status_read();
if status != 0 {
process_exceptional_status(timestamp, channel, status);
}
}
}

#[no_mangle]
pub extern fn rtio_output(timestamp: i64, channel: i32, address: i32, data: i32) {
unsafe {
let rtio_cmd: *mut RtioCmd = DMA_BASE as *mut _;
let rtio_response: *mut RtioResponse = (DMA_BASE+0x20) as *mut _;

// Clear status so we can observe response
(*rtio_response).status.set(0);

(*rtio_cmd).cmd_channel = (0<<24) | channel;
(*rtio_cmd).address = address;
(*rtio_cmd).timestamp = timestamp;
(*rtio_cmd).data = data as i64;

asm!("dmb");
asm!("sev");

let mut status;
loop {
status = (*rtio_response).status.get();
if status != 0 {break}
}
let status = status & !0x10000;
if status != 0 {process_exceptional_status(timestamp, channel, status as u8)}
}
}

#[no_mangle]
pub extern fn rtio_input_timestamp(timestamp: i64, channel: i32) -> u64 {
unsafe {
let rtio_cmd: *mut RtioCmd = DMA_BASE as *mut _;
let rtio_response: *mut RtioResponse = (DMA_BASE+0x20) as *mut _;

// Clear status so we can observe response
(*rtio_response).status.set(0);

(*rtio_cmd).cmd_channel = (1<<24) | channel;
(*rtio_cmd).timestamp = timestamp;

asm!("dmb");
asm!("sev");

let mut status;
loop {
status = (*rtio_response).status.get();
if status != 0 {break}
}
let status = status & !0x10000;
let status = status as u8;

if status & RTIO_I_STATUS_OVERFLOW != 0 {
csr::cri_con::selected_write(0);
csr::rtio::i_overflow_reset_write(1);
csr::cri_con::selected_write(1);
raise!("RTIOOverflow",
"RTIO input overflow on channel {0}",
channel as i64, 0, 0);
}
if status & RTIO_I_STATUS_WAIT_EVENT != 0 {
return !0
}
if status & RTIO_I_STATUS_LINK_ERROR != 0 {
raise!("RTIOLinkError",
"RTIO input link error on channel {0}",
channel as i64, 0, 0);
}

(*rtio_response).timestamp.get()
}
}




#[no_mangle]
pub extern fn output_wide(timestamp: i64, channel: i32, addr: i32, data: CSlice<i32>) {
unsafe {
csr::rtio::chan_sel_write(channel as _);
// writing timestamp clears o_data
csr::rtio::timestamp_write(timestamp as u64);
csr::rtio::o_address_write(addr as _);
for i in 0..data.len() {
rtio_o_data_write(i, data[i] as _)
}
csr::rtio::o_we_write(1);
let status = csr::rtio::o_status_read();
if status != 0 {
process_exceptional_status(timestamp, channel, status);
}
}
}

#[no_mangle]
pub extern fn rtio_input_timestamp_csr(timeout: i64, channel: i32) -> u64 {
unsafe {
csr::rtio::chan_sel_write(channel as _);
csr::rtio::timestamp_write(timeout as u64);
csr::rtio::i_request_write(1);

let mut status = RTIO_I_STATUS_WAIT_STATUS;
while status & RTIO_I_STATUS_WAIT_STATUS != 0 {
status = csr::rtio::i_status_read();
}

if status & RTIO_I_STATUS_OVERFLOW != 0 {
csr::rtio::i_overflow_reset_write(1);
raise!("RTIOOverflow",
"RTIO input overflow on channel {0}",
channel as i64, 0, 0);
}
if status & RTIO_I_STATUS_WAIT_EVENT != 0 {
return !0
}
if status & RTIO_I_STATUS_LINK_ERROR != 0 {
raise!("RTIOLinkError",
"RTIO input link error on channel {0}",
channel as i64, 0, 0);
}

csr::rtio::i_timestamp_read()
}
}

#[no_mangle]
pub extern fn rtio_input_data(channel: i32) -> i32 {
unsafe {
csr::rtio::chan_sel_write(channel as _);
csr::rtio::timestamp_write(0xffffffff_ffffffff);
csr::rtio::i_request_write(1);

let mut status = RTIO_I_STATUS_WAIT_STATUS;
while status & RTIO_I_STATUS_WAIT_STATUS != 0 {
status = csr::rtio::i_status_read();
}

if status & RTIO_I_STATUS_OVERFLOW != 0 {
csr::rtio::i_overflow_reset_write(1);
raise!("RTIOOverflow",
"RTIO input overflow on channel {0}",
channel as i64, 0, 0);
}
if status & RTIO_I_STATUS_LINK_ERROR != 0 {
raise!("RTIOLinkError",
"RTIO input link error on channel {0}",
channel as i64, 0, 0);
}

rtio_i_data_read(0) as i32
}
}

#[no_mangle]
pub extern fn rtio_input_data_timeout(timeout: i64, channel: i32) -> i32 {
unsafe {
csr::rtio::chan_sel_write(channel as _);
csr::rtio::timestamp_write(timeout as u64);
csr::rtio::i_request_write(1);

let mut status = RTIO_I_STATUS_WAIT_STATUS;
while status & RTIO_I_STATUS_WAIT_STATUS != 0 {
status = csr::rtio::i_status_read();
}

if status & RTIO_I_STATUS_OVERFLOW != 0 {
csr::rtio::i_overflow_reset_write(1);
raise!("RTIOOverflow",
"RTIO input overflow on channel {0}",
channel as i64, 0, 0);
}

if status & RTIO_I_STATUS_WAIT_EVENT != 0 {
return !0
}

rtio_i_data_read(0) as i32
}
}


#[repr(C)]
pub struct TimestampData {
timestamp: u64,
data: i32,
}

pub extern fn input_timestamp_data(channel: i32) -> TimestampData {
unsafe {
csr::rtio::chan_sel_write(channel as _);
csr::rtio::timestamp_write(0xffffffff_ffffffff);
csr::rtio::i_request_write(1);

let mut status = RTIO_I_STATUS_WAIT_STATUS;
while status & RTIO_I_STATUS_WAIT_STATUS != 0 {
status = csr::rtio::i_status_read();
}

if status & RTIO_I_STATUS_OVERFLOW != 0 {
csr::rtio::i_overflow_reset_write(1);
println!("RTIO input overflow on channel {0}",channel as i64);
}


TimestampData {
timestamp: csr::rtio::i_timestamp_read(),
data: rtio_i_data_read(0) as i32
}
}
}


#[cfg(has_rtio_log)]
pub fn log(timestamp: i64, data: &[u8]) {
unsafe {
csr::rtio::chan_sel_write(csr::CONFIG_RTIO_LOG_CHANNEL);
csr::rtio::timestamp_write(timestamp as u64);

let mut word: u32 = 0;
for i in 0..data.len() {
word <<= 8;
word |= data[i] as u32;
if i % 4 == 3 {
rtio_o_data_write(0, word);
csr::rtio::o_we_write(1);
word = 0;
}
}

if word != 0 {
rtio_o_data_write(0, word);
csr::rtio::o_we_write(1);
}
}
}

#[cfg(not(has_rtio_log))]
pub fn log(_timestamp: i64, _data: &[u8]) {
unimplemented!("not(has_rtio_log)")
}

+ 15
- 0
firmware/libunwind-rs/Cargo.toml View File

@@ -0,0 +1,15 @@
[package]
authors = ["The Rust Project Developers"]
name = "unwind"
version = "0.0.0"
build = "build.rs"

[lib]
name = "unwind"
path = "lib.rs"
test = false
bench = false
doc = false

[dependencies]
libc = { default-features = false }

+ 41
- 0
firmware/libunwind-rs/build.rs View File

@@ -0,0 +1,41 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::env;

fn main() {
println!("cargo:rustc-cfg=cargobuild");

let target = env::var("TARGET").expect("TARGET was not set");

if target.contains("linux") {
if target.contains("musl") && !target.contains("mips") {
println!("cargo:rustc-link-lib=static=unwind");
} else if !target.contains("android") {
println!("cargo:rustc-link-lib=gcc_s");