mirror of https://github.com/m-labs/artiq.git
add kernel access to non-realtime SPI buses (#740)
This commit is contained in:
parent
424b2bfbd8
commit
8399f8893d
|
@ -30,6 +30,9 @@ def i2c_read(busno: TInt32, ack: TBool) -> TInt32:
|
||||||
class PCA9548:
|
class PCA9548:
|
||||||
"""Driver for the PCA9548 I2C bus switch.
|
"""Driver for the PCA9548 I2C bus switch.
|
||||||
|
|
||||||
|
I2C transactions not real-time, and are performed by the CPU without
|
||||||
|
involving RTIO.
|
||||||
|
|
||||||
On the KC705, this chip is used for selecting the I2C buses on the two FMC
|
On the KC705, this chip is used for selecting the I2C buses on the two FMC
|
||||||
connectors. HPC=1, LPC=2.
|
connectors. HPC=1, LPC=2.
|
||||||
"""
|
"""
|
||||||
|
@ -72,6 +75,9 @@ class PCA9548:
|
||||||
class TCA6424A:
|
class TCA6424A:
|
||||||
"""Driver for the TCA6424A I2C I/O expander.
|
"""Driver for the TCA6424A I2C I/O expander.
|
||||||
|
|
||||||
|
I2C transactions not real-time, and are performed by the CPU without
|
||||||
|
involving RTIO.
|
||||||
|
|
||||||
On the NIST QC2 hardware, this chip is used for switching the directions
|
On the NIST QC2 hardware, this chip is used for switching the directions
|
||||||
of TTL buffers."""
|
of TTL buffers."""
|
||||||
def __init__(self, dmgr, busno=0, address=0x44, core_device="core"):
|
def __init__(self, dmgr, busno=0, address=0x44, core_device="core"):
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from artiq.language.core import kernel, portable, now_mu, delay_mu
|
from artiq.language.core import syscall, kernel, portable, now_mu, delay_mu
|
||||||
|
from artiq.language.types import TInt32, TNone
|
||||||
from artiq.language.units import MHz
|
from artiq.language.units import MHz
|
||||||
from artiq.coredevice.rtio import rtio_output, rtio_input_data
|
from artiq.coredevice.rtio import rtio_output, rtio_input_data
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"SPI_DATA_ADDR", "SPI_XFER_ADDR", "SPI_CONFIG_ADDR",
|
||||||
|
"SPI_OFFLINE", "SPI_ACTIVE", "SPI_PENDING",
|
||||||
|
"SPI_CS_POLARITY", "SPI_CLK_POLARITY", "SPI_CLK_PHASE",
|
||||||
|
"SPI_LSB_FIRST", "SPI_HALF_DUPLEX",
|
||||||
|
"SPIMaster", "NRTSPIMaster"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3)
|
SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3)
|
||||||
(
|
(
|
||||||
SPI_OFFLINE,
|
SPI_OFFLINE,
|
||||||
|
@ -262,3 +272,65 @@ class SPIMaster:
|
||||||
rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ,
|
rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ,
|
||||||
0)
|
0)
|
||||||
return rtio_input_data(self.channel)
|
return rtio_input_data(self.channel)
|
||||||
|
|
||||||
|
|
||||||
|
@syscall(flags={"nounwind", "nowrite"})
|
||||||
|
def spi_set_config(busno: TInt32, flags: TInt32, write_div: TInt32, read_div: TInt32) -> TNone:
|
||||||
|
raise NotImplementedError("syscall not simulated")
|
||||||
|
|
||||||
|
|
||||||
|
@syscall(flags={"nounwind", "nowrite"})
|
||||||
|
def spi_set_xfer(busno: TInt32, chip_select: TInt32, write_length: TInt32, read_length: TInt32) -> TNone:
|
||||||
|
raise NotImplementedError("syscall not simulated")
|
||||||
|
|
||||||
|
|
||||||
|
@syscall(flags={"nounwind", "nowrite"})
|
||||||
|
def spi_write(busno: TInt32, data: TInt32) -> TNone:
|
||||||
|
raise NotImplementedError("syscall not simulated")
|
||||||
|
|
||||||
|
|
||||||
|
@syscall(flags={"nounwind", "nowrite"})
|
||||||
|
def spi_read(busno: TInt32) -> TInt32:
|
||||||
|
raise NotImplementedError("syscall not simulated")
|
||||||
|
|
||||||
|
|
||||||
|
class NRTSPIMaster:
|
||||||
|
"""Core device non-realtime Serial Peripheral Interface (SPI) bus master.
|
||||||
|
Owns one non-realtime SPI bus.
|
||||||
|
|
||||||
|
With this driver, SPI transactions and are performed by the CPU without
|
||||||
|
involving RTIO.
|
||||||
|
|
||||||
|
Realtime and non-realtime buses are separate and defined at bitstream
|
||||||
|
compilation time.
|
||||||
|
|
||||||
|
See :class:`SPIMaster` for a description of the methods.
|
||||||
|
"""
|
||||||
|
def __init__(self, dmgr, busno, core_device="core"):
|
||||||
|
self.core = dmgr.get(core_device)
|
||||||
|
self.busno = busno
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_config_mu(self, flags=0, write_div=6, read_div=6):
|
||||||
|
"""Set the ``config`` register.
|
||||||
|
|
||||||
|
Note that the non-realtime SPI cores are usually clocked by the system
|
||||||
|
clock and not the RTIO clock. In many cases, the SPI configuration is
|
||||||
|
already set by the firmware and you do not need to call this method.
|
||||||
|
|
||||||
|
The offline bit cannot be set using this method.
|
||||||
|
The SPI bus is briefly taken offline when this method is called.
|
||||||
|
"""
|
||||||
|
spi_set_config(self.busno, flags, write_div, read_div)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_xfer(self, chip_select=0, write_length=0, read_length=0):
|
||||||
|
spi_set_xfer(self.busno, chip_select, write_length, read_length)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def write(self, data=0):
|
||||||
|
spi_write(self.busno, data)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def read(self):
|
||||||
|
return spi_read(self.busno)
|
||||||
|
|
|
@ -117,8 +117,13 @@ static mut API: &'static [(&'static str, *const ())] = &[
|
||||||
api!(drtio_get_packet_counts = ::rtio::drtio_dbg::get_packet_counts),
|
api!(drtio_get_packet_counts = ::rtio::drtio_dbg::get_packet_counts),
|
||||||
api!(drtio_get_fifo_space_req_count = ::rtio::drtio_dbg::get_fifo_space_req_count),
|
api!(drtio_get_fifo_space_req_count = ::rtio::drtio_dbg::get_fifo_space_req_count),
|
||||||
|
|
||||||
api!(i2c_start = ::i2c_start),
|
api!(i2c_start = ::nrt_bus::i2c::start),
|
||||||
api!(i2c_stop = ::i2c_stop),
|
api!(i2c_stop = ::nrt_bus::i2c::stop),
|
||||||
api!(i2c_write = ::i2c_write),
|
api!(i2c_write = ::nrt_bus::i2c::write),
|
||||||
api!(i2c_read = ::i2c_read),
|
api!(i2c_read = ::nrt_bus::i2c::read),
|
||||||
|
|
||||||
|
api!(spi_set_config = ::nrt_bus::spi::set_config),
|
||||||
|
api!(spi_set_xfer = ::nrt_bus::spi::set_xfer),
|
||||||
|
api!(spi_write = ::nrt_bus::spi::write),
|
||||||
|
api!(spi_read = ::nrt_bus::spi::read),
|
||||||
];
|
];
|
||||||
|
|
|
@ -89,6 +89,7 @@ macro_rules! raise {
|
||||||
pub mod eh;
|
pub mod eh;
|
||||||
mod api;
|
mod api;
|
||||||
mod rtio;
|
mod rtio;
|
||||||
|
mod nrt_bus;
|
||||||
|
|
||||||
static mut NOW: u64 = 0;
|
static mut NOW: u64 = 0;
|
||||||
static mut LIBRARY: Option<Library<'static>> = None;
|
static mut LIBRARY: Option<Library<'static>> = None;
|
||||||
|
@ -220,24 +221,6 @@ extern fn cache_put(key: CSlice<u8>, list: CSlice<i32>) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn i2c_start(busno: i32) {
|
|
||||||
send(&I2cStartRequest { busno: busno as u8 });
|
|
||||||
}
|
|
||||||
|
|
||||||
extern fn i2c_stop(busno: i32) {
|
|
||||||
send(&I2cStopRequest { busno: busno as u8 });
|
|
||||||
}
|
|
||||||
|
|
||||||
extern fn i2c_write(busno: i32, data: i32) -> bool {
|
|
||||||
send(&I2cWriteRequest { busno: busno as u8, data: data as u8 });
|
|
||||||
recv!(&I2cWriteReply { ack } => ack)
|
|
||||||
}
|
|
||||||
|
|
||||||
extern fn i2c_read(busno: i32, ack: bool) -> i32 {
|
|
||||||
send(&I2cReadRequest { busno: busno as u8, ack: ack });
|
|
||||||
recv!(&I2cReadReply { data } => data) as i32
|
|
||||||
}
|
|
||||||
|
|
||||||
const DMA_BUFFER_SIZE: usize = 64 * 1024;
|
const DMA_BUFFER_SIZE: usize = 64 * 1024;
|
||||||
|
|
||||||
struct DmaRecorder {
|
struct DmaRecorder {
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
pub mod i2c {
|
||||||
|
use ::send;
|
||||||
|
use ::recv;
|
||||||
|
use kernel_proto::*;
|
||||||
|
|
||||||
|
pub extern fn start(busno: i32) {
|
||||||
|
send(&I2cStartRequest { busno: busno as u8 });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern fn stop(busno: i32) {
|
||||||
|
send(&I2cStopRequest { busno: busno as u8 });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern fn write(busno: i32, data: i32) -> bool {
|
||||||
|
send(&I2cWriteRequest { busno: busno as u8, data: data as u8 });
|
||||||
|
recv!(&I2cWriteReply { ack } => ack)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern fn read(busno: i32, ack: bool) -> i32 {
|
||||||
|
send(&I2cReadRequest { busno: busno as u8, ack: ack });
|
||||||
|
recv!(&I2cReadReply { data } => data) as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod spi {
|
||||||
|
use ::send;
|
||||||
|
use ::recv;
|
||||||
|
use kernel_proto::*;
|
||||||
|
|
||||||
|
pub extern fn set_config(busno: i32, flags: i32, write_div: i32, read_div: i32) {
|
||||||
|
send(&SpiSetConfigRequest { busno: busno as u32, flags: flags as u8,
|
||||||
|
write_div: write_div as u8, read_div: read_div as u8 });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern fn set_xfer(busno: i32, chip_select: i32, write_length: i32, read_length: i32) {
|
||||||
|
send(&SpiSetXferRequest { busno: busno as u32, chip_select: chip_select as u16,
|
||||||
|
write_length: write_length as u8, read_length: read_length as u8 });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern fn write(busno: i32, data: i32) {
|
||||||
|
send(&SpiWriteRequest { busno: busno as u32, data: data as u32 });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern fn read(busno: i32) -> i32 {
|
||||||
|
send(&SpiReadRequest { busno: busno as u32 });
|
||||||
|
recv!(&SpiReadReply { data } => data) as i32
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,147 +1,171 @@
|
||||||
use csr;
|
use csr;
|
||||||
use clock;
|
|
||||||
|
|
||||||
fn half_period() { clock::spin_us(100) }
|
#[cfg(has_i2c)]
|
||||||
fn sda_bit(busno: u8) -> u8 { 1 << (2 * busno + 1) }
|
mod io {
|
||||||
fn scl_bit(busno: u8) -> u8 { 1 << (2 * busno) }
|
use csr;
|
||||||
|
use clock;
|
||||||
|
|
||||||
fn sda_i(busno: u8) -> bool {
|
pub fn half_period() { clock::spin_us(100) }
|
||||||
|
fn sda_bit(busno: u8) -> u8 { 1 << (2 * busno + 1) }
|
||||||
|
fn scl_bit(busno: u8) -> u8 { 1 << (2 * busno) }
|
||||||
|
|
||||||
|
pub fn sda_i(busno: u8) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::i2c::in_read() & sda_bit(busno) != 0
|
csr::i2c::in_read() & sda_bit(busno) != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sda_oe(busno: u8, oe: bool) {
|
pub fn sda_oe(busno: u8, oe: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let reg = csr::i2c::oe_read();
|
let reg = csr::i2c::oe_read();
|
||||||
let reg = if oe { reg | sda_bit(busno) } else { reg & !sda_bit(busno) };
|
let reg = if oe { reg | sda_bit(busno) } else { reg & !sda_bit(busno) };
|
||||||
csr::i2c::oe_write(reg)
|
csr::i2c::oe_write(reg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sda_o(busno: u8, o: bool) {
|
pub fn sda_o(busno: u8, o: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let reg = csr::i2c::out_read();
|
let reg = csr::i2c::out_read();
|
||||||
let reg = if o { reg | sda_bit(busno) } else { reg & !sda_bit(busno) };
|
let reg = if o { reg | sda_bit(busno) } else { reg & !sda_bit(busno) };
|
||||||
csr::i2c::out_write(reg)
|
csr::i2c::out_write(reg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scl_oe(busno: u8, oe: bool) {
|
pub fn scl_oe(busno: u8, oe: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let reg = csr::i2c::oe_read();
|
let reg = csr::i2c::oe_read();
|
||||||
let reg = if oe { reg | scl_bit(busno) } else { reg & !scl_bit(busno) };
|
let reg = if oe { reg | scl_bit(busno) } else { reg & !scl_bit(busno) };
|
||||||
csr::i2c::oe_write(reg)
|
csr::i2c::oe_write(reg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scl_o(busno: u8, o: bool) {
|
pub fn scl_o(busno: u8, o: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let reg = csr::i2c::out_read();
|
let reg = csr::i2c::out_read();
|
||||||
let reg = if o { reg | scl_bit(busno) } else { reg & !scl_bit(busno) };
|
let reg = if o { reg | scl_bit(busno) } else { reg & !scl_bit(busno) };
|
||||||
csr::i2c::out_write(reg)
|
csr::i2c::out_write(reg)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_i2c)]
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
for busno in 0..csr::CONFIG_I2C_BUS_COUNT {
|
for busno in 0..csr::CONFIG_I2C_BUS_COUNT {
|
||||||
let busno = busno as u8;
|
let busno = busno as u8;
|
||||||
// Set SCL as output, and high level
|
// Set SCL as output, and high level
|
||||||
scl_o(busno, true);
|
io::scl_o(busno, true);
|
||||||
scl_oe(busno, true);
|
io::scl_oe(busno, true);
|
||||||
// Prepare a zero level on SDA so that sda_oe pulls it down
|
// Prepare a zero level on SDA so that sda_oe pulls it down
|
||||||
sda_o(busno, false);
|
io::sda_o(busno, false);
|
||||||
// Release SDA
|
// Release SDA
|
||||||
sda_oe(busno, false);
|
io::sda_oe(busno, false);
|
||||||
|
|
||||||
// Check the I2C bus is ready
|
// Check the I2C bus is ready
|
||||||
half_period();
|
io::half_period();
|
||||||
half_period();
|
io::half_period();
|
||||||
if !sda_i(busno) {
|
if !io::sda_i(busno) {
|
||||||
error!("SDA is stuck low on bus #{}", busno)
|
error!("SDA is stuck low on bus #{}", busno)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_i2c)]
|
||||||
pub fn start(busno: u8) {
|
pub fn start(busno: u8) {
|
||||||
// Set SCL high then SDA low
|
// Set SCL high then SDA low
|
||||||
scl_o(busno, true);
|
io::scl_o(busno, true);
|
||||||
half_period();
|
io::half_period();
|
||||||
sda_oe(busno, true);
|
io::sda_oe(busno, true);
|
||||||
half_period();
|
io::half_period();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_i2c)]
|
||||||
pub fn restart(busno: u8) {
|
pub fn restart(busno: u8) {
|
||||||
// Set SCL low then SDA high */
|
// Set SCL low then SDA high */
|
||||||
scl_o(busno, false);
|
io::scl_o(busno, false);
|
||||||
half_period();
|
io::half_period();
|
||||||
sda_oe(busno, false);
|
io::sda_oe(busno, false);
|
||||||
half_period();
|
io::half_period();
|
||||||
// Do a regular start
|
// Do a regular start
|
||||||
start(busno);
|
start(busno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_i2c)]
|
||||||
pub fn stop(busno: u8) {
|
pub fn stop(busno: u8) {
|
||||||
// First, make sure SCL is low, so that the target releases the SDA line
|
// First, make sure SCL is low, so that the target releases the SDA line
|
||||||
scl_o(busno, false);
|
io::scl_o(busno, false);
|
||||||
half_period();
|
io::half_period();
|
||||||
// Set SCL high then SDA high
|
// Set SCL high then SDA high
|
||||||
sda_oe(busno, true);
|
io::sda_oe(busno, true);
|
||||||
scl_o(busno, true);
|
io::scl_o(busno, true);
|
||||||
half_period();
|
io::half_period();
|
||||||
sda_oe(busno, false);
|
io::sda_oe(busno, false);
|
||||||
half_period();
|
io::half_period();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_i2c)]
|
||||||
pub fn write(busno: u8, data: u8) -> bool {
|
pub fn write(busno: u8, data: u8) -> bool {
|
||||||
// MSB first
|
// MSB first
|
||||||
for bit in (0..8).rev() {
|
for bit in (0..8).rev() {
|
||||||
// Set SCL low and set our bit on SDA
|
// Set SCL low and set our bit on SDA
|
||||||
scl_o(busno, false);
|
io::scl_o(busno, false);
|
||||||
sda_oe(busno, data & (1 << bit) == 0);
|
io::sda_oe(busno, data & (1 << bit) == 0);
|
||||||
half_period();
|
io::half_period();
|
||||||
// Set SCL high ; data is shifted on the rising edge of SCL
|
// Set SCL high ; data is shifted on the rising edge of SCL
|
||||||
scl_o(busno, true);
|
io::scl_o(busno, true);
|
||||||
half_period();
|
io::half_period();
|
||||||
}
|
}
|
||||||
// Check ack
|
// Check ack
|
||||||
// Set SCL low, then release SDA so that the I2C target can respond
|
// Set SCL low, then release SDA so that the I2C target can respond
|
||||||
scl_o(busno, false);
|
io::scl_o(busno, false);
|
||||||
half_period();
|
io::half_period();
|
||||||
sda_oe(busno, false);
|
io::sda_oe(busno, false);
|
||||||
// Set SCL high and check for ack
|
// Set SCL high and check for ack
|
||||||
scl_o(busno, true);
|
io::scl_o(busno, true);
|
||||||
half_period();
|
io::half_period();
|
||||||
// returns true if acked (I2C target pulled SDA low)
|
// returns true if acked (I2C target pulled SDA low)
|
||||||
!sda_i(busno)
|
!io::sda_i(busno)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_i2c)]
|
||||||
pub fn read(busno: u8, ack: bool) -> u8 {
|
pub fn read(busno: u8, ack: bool) -> u8 {
|
||||||
// Set SCL low first, otherwise setting SDA as input may cause a transition
|
// Set SCL low first, otherwise setting SDA as input may cause a transition
|
||||||
// on SDA with SCL high which will be interpreted as START/STOP condition.
|
// on SDA with SCL high which will be interpreted as START/STOP condition.
|
||||||
scl_o(busno, false);
|
io::scl_o(busno, false);
|
||||||
half_period(); // make sure SCL has settled low
|
io::half_period(); // make sure SCL has settled low
|
||||||
sda_oe(busno, false);
|
io::sda_oe(busno, false);
|
||||||
|
|
||||||
let mut data: u8 = 0;
|
let mut data: u8 = 0;
|
||||||
|
|
||||||
// MSB first
|
// MSB first
|
||||||
for bit in (0..8).rev() {
|
for bit in (0..8).rev() {
|
||||||
scl_o(busno, false);
|
io::scl_o(busno, false);
|
||||||
half_period();
|
io::half_period();
|
||||||
// Set SCL high and shift data
|
// Set SCL high and shift data
|
||||||
scl_o(busno, true);
|
io::scl_o(busno, true);
|
||||||
half_period();
|
io::half_period();
|
||||||
if sda_i(busno) { data |= 1 << bit }
|
if io::sda_i(busno) { data |= 1 << bit }
|
||||||
}
|
}
|
||||||
// Send ack
|
// Send ack
|
||||||
// Set SCL low and pull SDA low when acking
|
// Set SCL low and pull SDA low when acking
|
||||||
scl_o(busno, false);
|
io::scl_o(busno, false);
|
||||||
if ack { sda_oe(busno, true) }
|
if ack { io::sda_oe(busno, true) }
|
||||||
half_period();
|
io::half_period();
|
||||||
// then set SCL high
|
// then set SCL high
|
||||||
scl_o(busno, true);
|
io::scl_o(busno, true);
|
||||||
half_period();
|
io::half_period();
|
||||||
|
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(has_i2c))]
|
||||||
|
pub fn init() {}
|
||||||
|
#[cfg(not(has_i2c))]
|
||||||
|
pub fn start(_busno: u8) {}
|
||||||
|
#[cfg(not(has_i2c))]
|
||||||
|
pub fn restart(_busno: u8) {}
|
||||||
|
#[cfg(not(has_i2c))]
|
||||||
|
pub fn stop(_busno: u8) {}
|
||||||
|
#[cfg(not(has_i2c))]
|
||||||
|
pub fn write(_busno: u8, _data: u8) -> bool { false }
|
||||||
|
#[cfg(not(has_i2c))]
|
||||||
|
pub fn read(_busno: u8, _ack: bool) { 0xff }
|
||||||
|
|
|
@ -19,8 +19,9 @@ pub mod uart_console;
|
||||||
#[cfg(has_spiflash)]
|
#[cfg(has_spiflash)]
|
||||||
pub mod spiflash;
|
pub mod spiflash;
|
||||||
|
|
||||||
#[cfg(has_i2c)]
|
|
||||||
pub mod i2c;
|
pub mod i2c;
|
||||||
|
pub mod spi;
|
||||||
|
|
||||||
#[cfg(has_si5324)]
|
#[cfg(has_si5324)]
|
||||||
pub mod si5324;
|
pub mod si5324;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
#[cfg(has_converter_spi)]
|
||||||
|
use csr;
|
||||||
|
|
||||||
|
// Later this module should support other buses than the converter SPI bus,
|
||||||
|
// and add a busno parameter to differentiate them.
|
||||||
|
|
||||||
|
#[cfg(has_converter_spi)]
|
||||||
|
pub fn set_config(flags: u8, write_div: u8, read_div: u8) {
|
||||||
|
unsafe {
|
||||||
|
csr::converter_spi::offline_write(1);
|
||||||
|
csr::converter_spi::cs_polarity_write(flags >> 3 & 1);
|
||||||
|
csr::converter_spi::clk_polarity_write(flags >> 4 & 1);
|
||||||
|
csr::converter_spi::clk_phase_write(flags >> 5 & 1);
|
||||||
|
csr::converter_spi::lsb_first_write(flags >> 6 & 1);
|
||||||
|
csr::converter_spi::half_duplex_write(flags >> 7 & 1);
|
||||||
|
csr::converter_spi::clk_div_write_write(write_div);
|
||||||
|
csr::converter_spi::clk_div_read_write(read_div);
|
||||||
|
csr::converter_spi::offline_write(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_converter_spi)]
|
||||||
|
pub fn set_xfer(chip_select: u16, write_length: u8, read_length: u8) {
|
||||||
|
unsafe {
|
||||||
|
csr::converter_spi::cs_write(chip_select as _);
|
||||||
|
csr::converter_spi::xfer_len_write_write(write_length);
|
||||||
|
csr::converter_spi::xfer_len_read_write(read_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_converter_spi)]
|
||||||
|
pub fn write(data: u32) {
|
||||||
|
unsafe {
|
||||||
|
csr::converter_spi::data_write_write(data);
|
||||||
|
while csr::converter_spi::pending_read() != 0 {}
|
||||||
|
while csr::converter_spi::active_read() != 0 {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_converter_spi)]
|
||||||
|
pub fn read() -> u32 {
|
||||||
|
unsafe {
|
||||||
|
csr::converter_spi::data_read_read()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(has_converter_spi))]
|
||||||
|
pub fn set_config(_flags: u8, _write_div: u8, _read_div: u8) {}
|
||||||
|
#[cfg(not(has_converter_spi))]
|
||||||
|
pub fn set_xfer(_chip_select: u16, _write_length: u8, _read_length: u8) {}
|
||||||
|
#[cfg(not(has_converter_spi))]
|
||||||
|
pub fn write(_data: u32) {}
|
||||||
|
#[cfg(not(has_converter_spi))]
|
||||||
|
pub fn read() -> u32 { 0 }
|
|
@ -87,6 +87,12 @@ pub enum Message<'a> {
|
||||||
I2cReadRequest { busno: u8, ack: bool },
|
I2cReadRequest { busno: u8, ack: bool },
|
||||||
I2cReadReply { data: u8 },
|
I2cReadReply { data: u8 },
|
||||||
|
|
||||||
|
SpiSetConfigRequest { busno: u32, flags: u8, write_div: u8, read_div: u8 },
|
||||||
|
SpiSetXferRequest { busno: u32, chip_select: u16, write_length: u8, read_length: u8 },
|
||||||
|
SpiWriteRequest { busno: u32, data: u32 },
|
||||||
|
SpiReadRequest { busno: u32 },
|
||||||
|
SpiReadReply { data: u32 },
|
||||||
|
|
||||||
Log(fmt::Arguments<'a>),
|
Log(fmt::Arguments<'a>),
|
||||||
LogSlice(&'a str)
|
LogSlice(&'a str)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
use session::{kern_acknowledge, kern_send};
|
||||||
|
use rtio_mgt;
|
||||||
|
use board;
|
||||||
|
use kernel_proto as kern;
|
||||||
|
use std::io;
|
||||||
|
use sched::Io;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
mod drtio_spi {
|
||||||
|
pub fn set_config(_busno: u32, _flags: u8, _write_div: u8, _read_div: u8) {}
|
||||||
|
pub fn set_xfer(_busno: u32, _chip_select: u16, _write_length: u8, _read_length: u8) {}
|
||||||
|
pub fn write(_busno: u32, _data: u32) {}
|
||||||
|
pub fn read(_busno: u32) -> u32 { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
mod spi {
|
||||||
|
use board;
|
||||||
|
use super::drtio_spi;
|
||||||
|
|
||||||
|
pub fn set_config(busno: u32, flags: u8, write_div: u8, read_div: u8) {
|
||||||
|
let drtio = busno >> 16;
|
||||||
|
if drtio == 0 {
|
||||||
|
board::spi::set_config(flags, write_div, read_div)
|
||||||
|
} else {
|
||||||
|
drtio_spi::set_config(busno, flags, write_div, read_div)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_xfer(busno: u32, chip_select: u16, write_length: u8, read_length: u8) {
|
||||||
|
let drtio = busno >> 16;
|
||||||
|
if drtio == 0 {
|
||||||
|
board::spi::set_xfer(chip_select, write_length, read_length)
|
||||||
|
} else {
|
||||||
|
drtio_spi::set_xfer(busno, chip_select, write_length, read_length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(busno: u32, data: u32) {
|
||||||
|
let drtio = busno >> 16;
|
||||||
|
if drtio == 0 {
|
||||||
|
board::spi::write(data)
|
||||||
|
} else {
|
||||||
|
drtio_spi::write(busno, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(busno: u32) -> u32 {
|
||||||
|
let drtio = busno >> 16;
|
||||||
|
if drtio == 0 {
|
||||||
|
board::spi::read()
|
||||||
|
} else {
|
||||||
|
drtio_spi::read(busno)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_kern_hwreq(io: &Io, request: &kern::Message) -> io::Result<bool> {
|
||||||
|
match request {
|
||||||
|
&kern::RtioInitRequest => {
|
||||||
|
info!("resetting RTIO");
|
||||||
|
rtio_mgt::init_core();
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::DrtioChannelStateRequest { channel } => {
|
||||||
|
let (fifo_space, last_timestamp) = rtio_mgt::drtio_dbg::get_channel_state(channel);
|
||||||
|
kern_send(io, &kern::DrtioChannelStateReply { fifo_space: fifo_space,
|
||||||
|
last_timestamp: last_timestamp })
|
||||||
|
}
|
||||||
|
&kern::DrtioResetChannelStateRequest { channel } => {
|
||||||
|
rtio_mgt::drtio_dbg::reset_channel_state(channel);
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
&kern::DrtioGetFifoSpaceRequest { channel } => {
|
||||||
|
rtio_mgt::drtio_dbg::get_fifo_space(channel);
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
&kern::DrtioPacketCountRequest => {
|
||||||
|
let (tx_cnt, rx_cnt) = rtio_mgt::drtio_dbg::get_packet_counts();
|
||||||
|
kern_send(io, &kern::DrtioPacketCountReply { tx_cnt: tx_cnt, rx_cnt: rx_cnt })
|
||||||
|
}
|
||||||
|
&kern::DrtioFifoSpaceReqCountRequest => {
|
||||||
|
let cnt = rtio_mgt::drtio_dbg::get_fifo_space_req_count();
|
||||||
|
kern_send(io, &kern::DrtioFifoSpaceReqCountReply { cnt: cnt })
|
||||||
|
}
|
||||||
|
|
||||||
|
&kern::I2cStartRequest { busno } => {
|
||||||
|
board::i2c::start(busno);
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
&kern::I2cStopRequest { busno } => {
|
||||||
|
board::i2c::stop(busno);
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
&kern::I2cWriteRequest { busno, data } => {
|
||||||
|
let ack = board::i2c::write(busno, data);
|
||||||
|
kern_send(io, &kern::I2cWriteReply { ack: ack })
|
||||||
|
}
|
||||||
|
&kern::I2cReadRequest { busno, ack } => {
|
||||||
|
let data = board::i2c::read(busno, ack);
|
||||||
|
kern_send(io, &kern::I2cReadReply { data: data })
|
||||||
|
},
|
||||||
|
|
||||||
|
&kern::SpiSetConfigRequest { busno, flags, write_div, read_div } => {
|
||||||
|
spi::set_config(busno, flags, write_div, read_div);
|
||||||
|
kern_acknowledge()
|
||||||
|
},
|
||||||
|
&kern::SpiSetXferRequest { busno, chip_select, write_length, read_length } => {
|
||||||
|
spi::set_xfer(busno, chip_select, write_length, read_length);
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
&kern::SpiWriteRequest { busno, data } => {
|
||||||
|
spi::write(busno, data);
|
||||||
|
kern_acknowledge()
|
||||||
|
}
|
||||||
|
&kern::SpiReadRequest { busno } => {
|
||||||
|
let data = spi::read(busno);
|
||||||
|
kern_send(io, &kern::SpiReadReply { data: data })
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => return Ok(false)
|
||||||
|
}.and(Ok(true))
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ mod rtio_dma;
|
||||||
|
|
||||||
mod mgmt;
|
mod mgmt;
|
||||||
mod kernel;
|
mod kernel;
|
||||||
|
mod kern_hwreq;
|
||||||
mod session;
|
mod session;
|
||||||
#[cfg(any(has_rtio_moninj, has_drtio))]
|
#[cfg(any(has_rtio_moninj, has_drtio))]
|
||||||
mod moninj;
|
mod moninj;
|
||||||
|
|
|
@ -15,6 +15,7 @@ use board;
|
||||||
use rpc_proto as rpc;
|
use rpc_proto as rpc;
|
||||||
use session_proto as host;
|
use session_proto as host;
|
||||||
use kernel_proto as kern;
|
use kernel_proto as kern;
|
||||||
|
use kern_hwreq;
|
||||||
|
|
||||||
macro_rules! unexpected {
|
macro_rules! unexpected {
|
||||||
($($arg:tt)*) => {
|
($($arg:tt)*) => {
|
||||||
|
@ -125,7 +126,7 @@ fn host_write(stream: &mut Write, reply: host::Reply) -> io::Result<()> {
|
||||||
reply.write_to(stream)
|
reply.write_to(stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kern_send(io: &Io, request: &kern::Message) -> io::Result<()> {
|
pub fn kern_send(io: &Io, request: &kern::Message) -> io::Result<()> {
|
||||||
match request {
|
match request {
|
||||||
&kern::LoadRequest(_) => debug!("comm->kern LoadRequest(...)"),
|
&kern::LoadRequest(_) => debug!("comm->kern LoadRequest(...)"),
|
||||||
&kern::DmaRetrieveReply { trace, duration } => {
|
&kern::DmaRetrieveReply { trace, duration } => {
|
||||||
|
@ -176,7 +177,7 @@ fn kern_recv<R, F>(io: &Io, f: F) -> io::Result<R>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kern_acknowledge() -> io::Result<()> {
|
pub fn kern_acknowledge() -> io::Result<()> {
|
||||||
mailbox::acknowledge();
|
mailbox::acknowledge();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -364,6 +365,9 @@ fn process_kern_message(io: &Io, mut stream: Option<&mut TcpStream>,
|
||||||
}
|
}
|
||||||
|
|
||||||
kern_recv_dotrace(request);
|
kern_recv_dotrace(request);
|
||||||
|
if kern_hwreq::process_kern_hwreq(io, request)? {
|
||||||
|
return Ok(false)
|
||||||
|
}
|
||||||
match request {
|
match request {
|
||||||
&kern::Log(args) => {
|
&kern::Log(args) => {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
@ -387,12 +391,6 @@ fn process_kern_message(io: &Io, mut stream: Option<&mut TcpStream>,
|
||||||
kern_acknowledge()
|
kern_acknowledge()
|
||||||
}
|
}
|
||||||
|
|
||||||
&kern::RtioInitRequest => {
|
|
||||||
info!("resetting RTIO");
|
|
||||||
rtio_mgt::init_core();
|
|
||||||
kern_acknowledge()
|
|
||||||
}
|
|
||||||
|
|
||||||
&kern::DmaRecordStart(name) => {
|
&kern::DmaRecordStart(name) => {
|
||||||
session.congress.dma_manager.record_start(name);
|
session.congress.dma_manager.record_start(name);
|
||||||
kern_acknowledge()
|
kern_acknowledge()
|
||||||
|
@ -419,28 +417,6 @@ fn process_kern_message(io: &Io, mut stream: Option<&mut TcpStream>,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
&kern::DrtioChannelStateRequest { channel } => {
|
|
||||||
let (fifo_space, last_timestamp) = rtio_mgt::drtio_dbg::get_channel_state(channel);
|
|
||||||
kern_send(io, &kern::DrtioChannelStateReply { fifo_space: fifo_space,
|
|
||||||
last_timestamp: last_timestamp })
|
|
||||||
}
|
|
||||||
&kern::DrtioResetChannelStateRequest { channel } => {
|
|
||||||
rtio_mgt::drtio_dbg::reset_channel_state(channel);
|
|
||||||
kern_acknowledge()
|
|
||||||
}
|
|
||||||
&kern::DrtioGetFifoSpaceRequest { channel } => {
|
|
||||||
rtio_mgt::drtio_dbg::get_fifo_space(channel);
|
|
||||||
kern_acknowledge()
|
|
||||||
}
|
|
||||||
&kern::DrtioPacketCountRequest => {
|
|
||||||
let (tx_cnt, rx_cnt) = rtio_mgt::drtio_dbg::get_packet_counts();
|
|
||||||
kern_send(io, &kern::DrtioPacketCountReply { tx_cnt: tx_cnt, rx_cnt: rx_cnt })
|
|
||||||
}
|
|
||||||
&kern::DrtioFifoSpaceReqCountRequest => {
|
|
||||||
let cnt = rtio_mgt::drtio_dbg::get_fifo_space_req_count();
|
|
||||||
kern_send(io, &kern::DrtioFifoSpaceReqCountReply { cnt: cnt })
|
|
||||||
}
|
|
||||||
|
|
||||||
&kern::WatchdogSetRequest { ms } => {
|
&kern::WatchdogSetRequest { ms } => {
|
||||||
let id = session.watchdog_set.set_ms(ms)
|
let id = session.watchdog_set.set_ms(ms)
|
||||||
.map_err(|()| io_error("out of watchdogs"))?;
|
.map_err(|()| io_error("out of watchdogs"))?;
|
||||||
|
@ -478,44 +454,6 @@ fn process_kern_message(io: &Io, mut stream: Option<&mut TcpStream>,
|
||||||
kern_send(io, &kern::CachePutReply { succeeded: succeeded })
|
kern_send(io, &kern::CachePutReply { succeeded: succeeded })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(has_i2c)]
|
|
||||||
&kern::I2cStartRequest { busno } => {
|
|
||||||
board::i2c::start(busno);
|
|
||||||
kern_acknowledge()
|
|
||||||
}
|
|
||||||
#[cfg(has_i2c)]
|
|
||||||
&kern::I2cStopRequest { busno } => {
|
|
||||||
board::i2c::stop(busno);
|
|
||||||
kern_acknowledge()
|
|
||||||
}
|
|
||||||
#[cfg(has_i2c)]
|
|
||||||
&kern::I2cWriteRequest { busno, data } => {
|
|
||||||
let ack = board::i2c::write(busno, data);
|
|
||||||
kern_send(io, &kern::I2cWriteReply { ack: ack })
|
|
||||||
}
|
|
||||||
#[cfg(has_i2c)]
|
|
||||||
&kern::I2cReadRequest { busno, ack } => {
|
|
||||||
let data = board::i2c::read(busno, ack);
|
|
||||||
kern_send(io, &kern::I2cReadReply { data: data })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(has_i2c))]
|
|
||||||
&kern::I2cStartRequest { .. } => {
|
|
||||||
kern_acknowledge()
|
|
||||||
}
|
|
||||||
#[cfg(not(has_i2c))]
|
|
||||||
&kern::I2cStopRequest { .. } => {
|
|
||||||
kern_acknowledge()
|
|
||||||
}
|
|
||||||
#[cfg(not(has_i2c))]
|
|
||||||
&kern::I2cWriteRequest { .. } => {
|
|
||||||
kern_send(io, &kern::I2cWriteReply { ack: false })
|
|
||||||
}
|
|
||||||
#[cfg(not(has_i2c))]
|
|
||||||
&kern::I2cReadRequest { .. } => {
|
|
||||||
kern_send(io, &kern::I2cReadReply { data: 0xff })
|
|
||||||
}
|
|
||||||
|
|
||||||
&kern::RunFinished => {
|
&kern::RunFinished => {
|
||||||
unsafe { kernel::stop() }
|
unsafe { kernel::stop() }
|
||||||
session.kernel_state = KernelState::Absent;
|
session.kernel_state = KernelState::Absent;
|
||||||
|
|
Loading…
Reference in New Issue