extern crate libc;
mod def;
mod error;
mod pubdef;
pub mod util;
use def::*;
pub use error::Error;
pub use pubdef::*;
use std::cmp::Eq;
use std::ffi::CString;
use std::fs::File;
use std::io::{self, Read};
use std::marker::PhantomData;
use std::mem;
use std::ops::{BitOrAssign, Shl};
use std::ptr;
use std::result;
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT, compiler_fence};
static PRUSS_IS_INSTANTIATED: AtomicBool = ATOMIC_BOOL_INIT;
pub type Result<T> = result::Result<T, Error>;
pub struct Pruss<'a> {
_prumap: MemMap,
_hostmap: MemMap,
pub intc: Intc,
pub pru0: PruLoader,
pub pru1: PruLoader,
pub dram0: MemSegment<'a>,
pub dram1: MemSegment<'a>,
pub dram2: MemSegment<'a>,
pub hostram: MemSegment<'a>,
}
impl<'a> Pruss<'a> {
pub fn new(intc_config: &IntcConfig) -> Result<Pruss<'a>> {
if PRUSS_IS_INSTANTIATED.swap(true, Ordering::Acquire) {
return Err(Error::AlreadyInstantiated);
}
fn memsize(path: &str) -> io::Result<usize> {
let mut f = try!(File::open(path));
let mut buffer = String::new();
try!(f.read_to_string(&mut buffer));
Ok(usize::from_str_radix(&buffer[2..].trim(), 16).unwrap())
};
let file = try!(SyncFile::new(PRUSS_DEVICE_PATH));
let prumem_size = try!(memsize(UIO_PRUMEM_SIZE_PATH));
let hostmem_size = try!(memsize(UIO_HOSTMEM_SIZE_PATH));
let prumap = try!(MemMap::new(file.fd, prumem_size, 0));
let hostmap = try!(MemMap::new(file.fd, hostmem_size, 1));
let mut intc = Intc::new(unsafe { prumap.base.offset(INTC_OFFSET as isize) as *mut u32 });
intc.map_interrupts(intc_config);
let pru0 =
PruLoader::new(unsafe { prumap.base.offset(PRU0CTRL_OFFSET as isize) as *mut u32 },
unsafe { prumap.base.offset(IRAM0_OFFSET as isize) },
IRAM0_SIZE);
let pru1 =
PruLoader::new(unsafe { prumap.base.offset(PRU1CTRL_OFFSET as isize) as *mut u32 },
unsafe { prumap.base.offset(IRAM1_OFFSET as isize) },
IRAM1_SIZE);
let dram0 = MemSegment::new(prumap.base, DRAM0_OFFSET, DRAM0_OFFSET + DRAM0_SIZE);
let dram1 = MemSegment::new(prumap.base, DRAM1_OFFSET, DRAM1_OFFSET + DRAM1_SIZE);
let dram2 = MemSegment::new(prumap.base, DRAM2_OFFSET, DRAM2_OFFSET + DRAM2_SIZE);
let hostram = MemSegment::new(hostmap.base, 0, hostmem_size);
Ok(Pruss {
_prumap: prumap,
_hostmap: hostmap,
intc: intc,
pru0: pru0,
pru1: pru1,
dram0: dram0,
dram1: dram1,
dram2: dram2,
hostram: hostram,
})
}
}
impl<'a> Drop for Pruss<'a> {
fn drop(&mut self) {
self.pru0.reset();
self.pru1.reset();
PRUSS_IS_INSTANTIATED.store(false, Ordering::Release);
}
}
unsafe impl<'a> Send for Pruss<'a> {}
unsafe impl<'a> Sync for Pruss<'a> {}
pub struct Intc {
intc_reg: *mut u32,
}
impl Intc {
fn new(intc_reg: *mut u32) -> Self {
let intc = Intc { intc_reg: intc_reg };
intc
}
pub fn map_interrupts(&mut self, interrupts: &IntcConfig) {
unsafe {
ptr::write_volatile(self.intc_reg.offset(SIPR1_REG), 0xffffffff);
ptr::write_volatile(self.intc_reg.offset(SIPR2_REG), 0xffffffff);
for cmrx in 0..NUM_CMRX {
ptr::write_volatile(self.intc_reg.offset(CMR_REG + cmrx), 0);
}
for m in &interrupts.sysevt_to_channel_map {
let cmrx = (m.sysevt >> 2) as isize;
debug_assert!(cmrx < NUM_CMRX);
let val = ptr::read_volatile(self.intc_reg.offset(CMR_REG + cmrx));
ptr::write_volatile(self.intc_reg.offset(CMR_REG + cmrx),
val | (m.channel as u32) << ((m.sysevt as u32 & 0b11) * 8));
}
for hmrx in 0..NUM_HMRX {
ptr::write_volatile(self.intc_reg.offset(HMR_REG + hmrx), 0);
}
for m in &interrupts.channel_to_host_map {
let hmrx = (m.channel >> 2) as isize;
debug_assert!(hmrx < NUM_HMRX);
let val = ptr::read_volatile(self.intc_reg.offset(HMR_REG + hmrx));
ptr::write_volatile(self.intc_reg.offset(HMR_REG + hmrx),
val | (m.host as u32) << ((m.channel as u32 & 0b11) * 8));
}
ptr::write_volatile(self.intc_reg.offset(SITR1_REG), 0x0);
ptr::write_volatile(self.intc_reg.offset(SITR2_REG), 0x0);
let (mut mask1, mut mask2) = (0u32, 0u32);
for se in &interrupts.sysevt_enable {
match *se {
0...31 => mask1 |= 1u32 << se,
32...63 => mask2 |= 1u32 << (se - 32),
_ => unreachable!(),
};
}
ptr::write_volatile(self.intc_reg.offset(ESR1_REG), mask1);
ptr::write_volatile(self.intc_reg.offset(SECR1_REG), mask1);
ptr::write_volatile(self.intc_reg.offset(ESR2_REG), mask2);
ptr::write_volatile(self.intc_reg.offset(SECR2_REG), mask2);
for h in &interrupts.host_enable {
ptr::write_volatile(self.intc_reg.offset(HIEISR_REG), *h as u32);
}
ptr::write_volatile(self.intc_reg.offset(GER_REG), 0x1);
}
}
pub fn send_sysevt(&self, sysevt: Sysevt) {
unsafe {
match sysevt as u8 {
se @ 0...31 => ptr::write_volatile(self.intc_reg.offset(SRSR1_REG),
1u32 << se),
se @ 32...63 => ptr::write_volatile(self.intc_reg.offset(SRSR2_REG),
1u32 << (se - 32)),
_ => unreachable!(),
};
}
}
pub fn clear_sysevt(&self, sysevt: Sysevt) {
unsafe {
ptr::write_volatile(self.intc_reg.offset(SICR_REG), sysevt as u32);
}
}
pub fn enable_sysevt(&self, sysevt: Sysevt) {
unsafe {
ptr::write_volatile(self.intc_reg.offset(EISR_REG), sysevt as u32 );
}
}
pub fn disable_sysevt(&self, sysevt: Sysevt) {
unsafe {
ptr::write_volatile(self.intc_reg.offset(EICR_REG), sysevt as u32 );
}
}
pub fn enable_host<T: Into<Host>>(&self, host: T) {
let host: Host = host.into();
unsafe {
ptr::write_volatile(self.intc_reg.offset(HIEISR_REG), host as u32 );
}
}
pub fn disable_host<T: Into<Host>>(&self, host: T) {
let host: Host = host.into();
unsafe {
ptr::write_volatile(self.intc_reg.offset(HIDISR_REG), host as u32 );
}
}
pub fn register_irq(&self, e: Evtout) -> EvtoutIrq {
EvtoutIrq::new(e)
}
}
pub struct PruLoader {
pructrl_reg: *mut u32,
iram_base: *mut u8,
iram_size: usize,
}
impl PruLoader {
fn new(pructrl_reg: *mut u32, iram_base: *mut u8, iram_size: usize) -> PruLoader {
PruLoader {
pructrl_reg: pructrl_reg,
iram_base: iram_base,
iram_size: iram_size,
}
}
pub fn load_code<R: Read>(&mut self, code: &mut R) -> io::Result<PruCode> {
self.reset();
let n: usize = try!(code.read( unsafe {
std::slice::from_raw_parts_mut(self.iram_base, self.iram_size)
}));
match n {
0 => {
Err(io::Error::new(io::ErrorKind::InvalidInput,
"size of PRU code exceeding instruction RAM capacity"))
}
_ => {
compiler_fence(Ordering::Release);
Ok(PruCode::new(self.pructrl_reg))
}
}
}
fn reset(&mut self) {
unsafe {
ptr::write_volatile(self.pructrl_reg, 0);
}
}
}
pub struct MemSegment<'a> {
base: *mut u8,
from: usize,
to: usize,
_memory_marker: PhantomData<&'a [u8]>,
}
impl<'a> MemSegment<'a> {
fn new<'b>(base: *mut u8, from: usize, to: usize) -> MemSegment<'b> {
MemSegment {
base: base,
from: from,
to: to,
_memory_marker: PhantomData,
}
}
#[inline]
pub fn alloc<T: Copy>(&mut self, source: T) -> &mut T {
let target: &mut T = unsafe { self.alloc_uninitialized() };
*target = source;
target
}
pub unsafe fn alloc_uninitialized<T: Copy>(&mut self) -> &mut T {
assert!(self.from % mem::align_of::<T>() == 0);
assert!(self.to - self.from >= mem::size_of::<T>());
&mut *(self.base.offset(self.from as isize) as *mut T)
}
pub fn begin(&self) -> usize {
self.from
}
pub fn end(&self) -> usize {
self.to
}
pub fn split_at(&mut self, position: usize) -> (MemSegment, MemSegment) {
assert!(position >= self.from && position <= self.to);
(MemSegment {
base: self.base,
from: self.from,
to: position,
_memory_marker: PhantomData,
},
MemSegment {
base: self.base,
from: position,
to: self.to,
_memory_marker: PhantomData,
})
}
}
unsafe impl<'a> Send for MemSegment<'a> {}
unsafe impl<'a> Sync for MemSegment<'a> {}
#[derive(Clone)]
pub struct IntcConfig {
sysevt_to_channel_map: Vec<SysevtToChannel>,
channel_to_host_map: Vec<ChannelToHost>,
sysevt_enable: Vec<u8>,
host_enable: Vec<u8>,
}
impl IntcConfig {
pub fn new_empty() -> IntcConfig {
IntcConfig {
sysevt_to_channel_map: Vec::new(),
channel_to_host_map: Vec::new(),
sysevt_enable: Vec::new(),
host_enable: Vec::new(),
}
}
pub fn new_populated() -> IntcConfig {
let mut config_data = Self::new_empty();
config_data.map_sysevts_to_channels(&[(Sysevt::S17, Channel::C1),
(Sysevt::S18, Channel::C0),
(Sysevt::S19, Channel::C2),
(Sysevt::S20, Channel::C3),
(Sysevt::S21, Channel::C0),
(Sysevt::S22, Channel::C1)]);
config_data.map_channels_to_hosts(&[(Channel::C0, Host::Pru0),
(Channel::C1, Host::Pru1),
(Channel::C2, Host::Evtout0),
(Channel::C3, Host::Evtout1)]);
config_data.auto_enable_sysevts();
config_data.auto_enable_hosts();
config_data
}
pub fn enable_sysevts(&mut self, sysevts: &[Sysevt]) {
let mut bitfield = BitField64::new(NUM_SYSEVTS);
self.sysevt_enable = sysevts.iter()
.map(|&sysevt| {
assert!(bitfield.try_set(sysevt as u8));
sysevt as u8
})
.collect();
}
pub fn enable_hosts(&mut self, hosts: &[Host]) {
let mut bitfield = BitField32::new(NUM_HOSTS);
self.host_enable = hosts.iter()
.map(|&host| {
assert!(bitfield.try_set(host as u8));
host as u8
})
.collect()
}
pub fn auto_enable_sysevts(&mut self) {
self.sysevt_enable = self.sysevt_to_channel_map
.iter()
.map(|sysevt_to_channel| sysevt_to_channel.sysevt)
.collect();
}
pub fn auto_enable_hosts(&mut self) {
self.host_enable = self.channel_to_host_map
.iter()
.map(|channel_to_host| channel_to_host.host)
.collect()
}
pub fn map_sysevts_to_channels(&mut self, scmap: &[(Sysevt, Channel)]) {
let mut bitfield = BitField64::new(NUM_SYSEVTS);
self.sysevt_to_channel_map = scmap.iter()
.map(|&(s, c)| {
assert!(bitfield.try_set(s as u8));
SysevtToChannel {
sysevt: s as u8,
channel: c as u8,
}
})
.collect();
}
pub fn map_channels_to_hosts(&mut self, chmap: &[(Channel, Host)]) {
let mut bitfield = BitField32::new(NUM_CHANNELS);
self.channel_to_host_map = chmap.iter()
.map(|&(c, h)| {
assert!(bitfield.try_set(c as u8));
ChannelToHost {
channel: c as u8,
host: h as u8,
}
})
.collect();
}
}
pub struct EvtoutIrq {
file: File,
event: Evtout,
}
impl EvtoutIrq {
fn new(e: Evtout) -> EvtoutIrq {
EvtoutIrq {
file: File::open(format!("{}{}", EVTOUT_DEVICE_ROOT_PATH, e as usize)).unwrap(),
event: e,
}
}
pub fn wait(&self) -> u32 {
let mut buffer = [0u8; 4];
(&mut &(self.file)).read_exact(&mut buffer).unwrap();
unsafe { mem::transmute::<[u8; 4], u32>(buffer) }
}
pub fn get_evtout(&self) -> Evtout {
self.event
}
}
pub struct PruCode<'a> {
pructrl_reg: *mut u32,
_pructrl_marker: PhantomData<&'a mut u32>,
}
impl<'a> PruCode<'a> {
fn new<'b>(pructrl_reg: *mut u32) -> PruCode<'b> {
PruCode {
pructrl_reg: pructrl_reg,
_pructrl_marker: PhantomData,
}
}
pub unsafe fn run(&mut self) {
ptr::write_volatile(self.pructrl_reg, 2);
}
pub fn halt(&mut self) {
unsafe {
ptr::write_volatile(self.pructrl_reg, 1);
}
}
pub fn reset(&mut self) {
unsafe {
ptr::write_volatile(self.pructrl_reg, 0);
}
}
}
unsafe impl<'a> Send for PruCode<'a> {}
unsafe impl<'a> Sync for PruCode<'a> {}
#[derive(Copy, Clone)]
struct SysevtToChannel {
sysevt: u8,
channel: u8,
}
#[derive(Copy, Clone)]
struct ChannelToHost {
channel: u8,
host: u8,
}
struct SyncFile {
fd: libc::c_int,
}
impl SyncFile {
fn new(path: &str) -> io::Result<SyncFile> {
let fd = unsafe {
libc::open(CString::new(path).unwrap().as_ptr(),
libc::O_RDWR | libc::O_SYNC)
};
match fd {
err if err < 0 => Err(io::Error::from_raw_os_error(err as i32)),
_ => Ok(SyncFile { fd: fd }),
}
}
}
impl Drop for SyncFile {
fn drop(&mut self) {
unsafe {
libc::close(self.fd);
}
}
}
struct MemMap {
base: *mut u8,
size: usize,
}
impl MemMap {
fn new(fd: libc::c_int, size: usize, page: isize) -> io::Result<MemMap> {
unsafe {
let base = libc::mmap(ptr::null_mut(),
size as libc::size_t,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd,
(PAGE_SIZE * page) as libc::off_t);
if base == libc::MAP_FAILED {
Err(io::Error::last_os_error())
} else {
Ok(MemMap {
base: base as *mut u8,
size: size,
})
}
}
}
}
impl Drop for MemMap {
fn drop(&mut self) {
unsafe {
libc::munmap(self.base as *mut libc::c_void, self.size as libc::size_t);
}
}
}
#[derive(Copy, Clone)]
struct BitField<T> {
bits: T,
width: u8,
}
impl<T: Eq + BitOrAssign + From<u8> + Copy + Shl<u8, Output = T>> BitField<T> {
fn new(width: u8) -> Self {
assert!((mem::size_of::<T>() * 8) >= width as usize);
BitField {
bits: 0u8.into(),
width: width,
}
}
fn try_set(&mut self, bit: u8) -> bool {
assert!(bit < self.width);
let mask: T = Into::<T>::into(1u8) << bit;
let old = self.bits;
self.bits |= mask;
old != self.bits
}
}
type BitField32 = BitField<u32>;
type BitField64 = BitField<u64>;