Browse Source

initial commit

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

10
.gitignore

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

70
experiments/dds_timing.py

@ -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
experiments/device_db.py

@ -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
experiments/mandelbrot.py

@ -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
experiments/minimal.py

@ -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
experiments/ttl.py

@ -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
experiments/wall_time.py

@ -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
firmware/Cargo.toml

@ -0,0 +1,7 @@
[workspace]
members = ["runtime"]
[profile.dev]
incremental = true
lto = false
opt-level = 2

14
firmware/libboard_misoc/Cargo.toml

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

56
firmware/libboard_misoc/lib.rs

@ -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
firmware/libboard_misoc/uart.rs

@ -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
firmware/libboard_misoc/uart_console.rs

@ -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
firmware/libdyld/Cargo.toml

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

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

339
firmware/libdyld/lib.rs

@ -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

20
firmware/libksupport/Cargo.toml

@ -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
firmware/libksupport/api.rs

@ -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
firmware/libksupport/build.rs

@ -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
firmware/libksupport/eh.rs

@ -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;
};