forked from M-Labs/artiq
move i2c to libboard, do bit-banging on comms CPU
This commit is contained in:
parent
3dd9d54b22
commit
082fdaf450
@ -126,10 +126,6 @@ class DDSError(Exception):
|
||||
incorrect.
|
||||
"""
|
||||
|
||||
class I2CError(Exception):
|
||||
"""Raised with a I2C transaction fails."""
|
||||
artiq_builtin = True
|
||||
|
||||
class WatchdogExpired(Exception):
|
||||
"""Raised when a watchdog expires."""
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
from artiq.language.core import syscall, kernel
|
||||
from artiq.language.types import TBool, TInt32, TNone
|
||||
from artiq.coredevice.exceptions import I2CError
|
||||
|
||||
|
||||
@syscall(flags={"nowrite"})
|
||||
def i2c_init(busno: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
class I2CError(Exception):
|
||||
"""Raised with a I2C transaction fails."""
|
||||
pass
|
||||
|
||||
|
||||
@syscall(flags={"nounwind", "nowrite"})
|
||||
@ -48,7 +47,6 @@ class PCA9548:
|
||||
|
||||
:param channel: channel number (0-7)
|
||||
"""
|
||||
i2c_init(self.busno)
|
||||
i2c_start(self.busno)
|
||||
try:
|
||||
if not i2c_write(self.busno, self.address):
|
||||
@ -60,7 +58,6 @@ class PCA9548:
|
||||
|
||||
@kernel
|
||||
def readback(self):
|
||||
i2c_init(self.busno)
|
||||
i2c_start(self.busno)
|
||||
r = 0
|
||||
try:
|
||||
@ -84,7 +81,6 @@ class TCA6424A:
|
||||
|
||||
@kernel
|
||||
def _write24(self, command, value):
|
||||
i2c_init(self.busno)
|
||||
i2c_start(self.busno)
|
||||
try:
|
||||
if not i2c_write(self.busno, self.address):
|
||||
|
136
artiq/firmware/libboard/i2c.rs
Normal file
136
artiq/firmware/libboard/i2c.rs
Normal file
@ -0,0 +1,136 @@
|
||||
use csr;
|
||||
use clock;
|
||||
|
||||
fn half_period() { clock::spin_us(100) }
|
||||
fn sda_bit(busno: u32) -> u8 { 1 << (2 * busno + 1) }
|
||||
fn scl_bit(busno: u32) -> u8 { 1 << (2 * busno) }
|
||||
|
||||
fn sda_i(busno: u32) -> bool {
|
||||
unsafe {
|
||||
csr::i2c::in_read() & sda_bit(busno) != 0
|
||||
}
|
||||
}
|
||||
|
||||
fn sda_oe(busno: u32, oe: bool) {
|
||||
unsafe {
|
||||
let reg = csr::i2c::oe_read();
|
||||
let reg = if oe { reg | sda_bit(busno) } else { reg & !sda_bit(busno) };
|
||||
csr::i2c::oe_write(reg)
|
||||
}
|
||||
}
|
||||
|
||||
fn sda_o(busno: u32, o: bool) {
|
||||
unsafe {
|
||||
let reg = csr::i2c::out_read();
|
||||
let reg = if o { reg | sda_bit(busno) } else { reg & !sda_bit(busno) };
|
||||
csr::i2c::out_write(reg)
|
||||
}
|
||||
}
|
||||
|
||||
fn scl_oe(busno: u32, oe: bool) {
|
||||
unsafe {
|
||||
let reg = csr::i2c::oe_read();
|
||||
let reg = if oe { reg | scl_bit(busno) } else { reg & !scl_bit(busno) };
|
||||
csr::i2c::oe_write(reg)
|
||||
}
|
||||
}
|
||||
|
||||
fn scl_o(busno: u32, o: bool) {
|
||||
unsafe {
|
||||
let reg = csr::i2c::out_read();
|
||||
let reg = if o { reg | scl_bit(busno) } else { reg & !scl_bit(busno) };
|
||||
csr::i2c::out_write(reg)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
for busno in 0..csr::CONFIG_I2C_BUS_COUNT {
|
||||
// Set SCL as output, and high level
|
||||
scl_o(busno, true);
|
||||
scl_oe(busno, true);
|
||||
// Prepare a zero level on SDA so that sda_oe pulls it down
|
||||
sda_o(busno, false);
|
||||
// Release SDA
|
||||
sda_oe(busno, false);
|
||||
|
||||
// Check the I2C bus is ready
|
||||
half_period();
|
||||
half_period();
|
||||
if !sda_i(busno) {
|
||||
error!("SDA is stuck low on bus #{}", busno)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(busno: u32) {
|
||||
// Set SCL high then SDA low
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
sda_oe(busno, true);
|
||||
half_period();
|
||||
}
|
||||
|
||||
pub fn stop(busno: u32) {
|
||||
// First, make sure SCL is low, so that the target releases the SDA line
|
||||
scl_o(busno, false);
|
||||
half_period();
|
||||
// Set SCL high then SDA high
|
||||
sda_oe(busno, true);
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
sda_oe(busno, false);
|
||||
half_period();
|
||||
}
|
||||
|
||||
pub fn write(busno: u32, data: u8) -> bool {
|
||||
// MSB first
|
||||
for bit in (0..8).rev() {
|
||||
// Set SCL low and set our bit on SDA
|
||||
scl_o(busno, false);
|
||||
sda_oe(busno, data & (1 << bit) == 0);
|
||||
half_period();
|
||||
// Set SCL high ; data is shifted on the rising edge of SCL
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
}
|
||||
// Check ack
|
||||
// Set SCL low, then release SDA so that the I2C target can respond
|
||||
scl_o(busno, false);
|
||||
half_period();
|
||||
sda_oe(busno, false);
|
||||
// Set SCL high and check for ack
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
// returns true if acked (I2C target pulled SDA low)
|
||||
!sda_i(busno)
|
||||
}
|
||||
|
||||
pub fn read(busno: u32, ack: bool) -> u8 {
|
||||
// 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.
|
||||
scl_o(busno, false);
|
||||
half_period(); // make sure SCL has settled low
|
||||
sda_oe(busno, false);
|
||||
|
||||
let mut data: u8 = 0;
|
||||
|
||||
// MSB first
|
||||
for bit in (0..8).rev() {
|
||||
scl_o(busno, false);
|
||||
half_period();
|
||||
// Set SCL high and shift data
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
if sda_i(busno) { data |= 1 << bit }
|
||||
}
|
||||
// Send ack
|
||||
// Set SCL low and pull SDA low when acking
|
||||
scl_o(busno, false);
|
||||
if ack { sda_oe(busno, true) }
|
||||
half_period();
|
||||
// then set SCL high
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
|
||||
data
|
||||
}
|
@ -11,6 +11,8 @@ include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs"));
|
||||
pub mod spr;
|
||||
pub mod irq;
|
||||
pub mod clock;
|
||||
#[cfg(has_i2c)]
|
||||
pub mod i2c;
|
||||
#[cfg(has_ad9516)]
|
||||
#[allow(dead_code)]
|
||||
mod ad9516_reg;
|
||||
|
@ -106,14 +106,8 @@ static mut API: &'static [(&'static str, *const ())] = &[
|
||||
api!(rtio_input_timestamp = ::rtio::input_timestamp),
|
||||
api!(rtio_input_data = ::rtio::input_data),
|
||||
|
||||
#[cfg(has_i2c)]
|
||||
api!(i2c_init = ::i2c::init),
|
||||
#[cfg(has_i2c)]
|
||||
api!(i2c_start = ::i2c::start),
|
||||
#[cfg(has_i2c)]
|
||||
api!(i2c_stop = ::i2c::stop),
|
||||
#[cfg(has_i2c)]
|
||||
api!(i2c_write = ::i2c::write),
|
||||
#[cfg(has_i2c)]
|
||||
api!(i2c_read = ::i2c::read),
|
||||
api!(i2c_start = ::i2c_start),
|
||||
api!(i2c_stop = ::i2c_stop),
|
||||
api!(i2c_write = ::i2c_write),
|
||||
api!(i2c_read = ::i2c_read),
|
||||
];
|
||||
|
@ -1,176 +0,0 @@
|
||||
use board::csr;
|
||||
|
||||
fn half_period() {
|
||||
unsafe {
|
||||
csr::timer_kernel::en_write(0);
|
||||
csr::timer_kernel::load_write(csr::CONFIG_CLOCK_FREQUENCY/10000);
|
||||
csr::timer_kernel::reload_write(0);
|
||||
csr::timer_kernel::en_write(1);
|
||||
|
||||
csr::timer_kernel::update_value_write(1);
|
||||
while csr::timer_kernel::value_read() != 0 {
|
||||
csr::timer_kernel::update_value_write(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(has_i2c)]
|
||||
mod imp {
|
||||
use board::csr;
|
||||
|
||||
fn sda_bit(busno: u32) -> u32 { 1 << (2 * busno + 1) }
|
||||
fn scl_bit(busno: u32) -> u32 { 1 << (2 * busno) }
|
||||
|
||||
pub fn sda_i(busno: u32) -> bool {
|
||||
unsafe {
|
||||
if busno >= csr::CONFIG_I2C_BUS_COUNT {
|
||||
true
|
||||
} else {
|
||||
csr::i2c::in_read() & sda_bit(busno) != 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sda_oe(busno: u32, oe: bool) {
|
||||
unsafe {
|
||||
let reg = csr::i2c::oe_read();
|
||||
let reg = if oe { reg | sda_bit(busno) } else { reg & !sda_bit(busno) };
|
||||
csr::i2c::oe_write(reg);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sda_o(busno: u32, o: bool) {
|
||||
unsafe {
|
||||
let reg = csr::i2c::out_read();
|
||||
let reg = if o { reg | sda_bit(busno) } else { reg & !sda_bit(busno) };
|
||||
csr::i2c::out_write(reg)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scl_oe(busno: u32, oe: bool) {
|
||||
unsafe {
|
||||
let reg = csr::i2c::oe_read();
|
||||
let reg = if oe { reg | scl_bit(busno) } else { reg & !scl_bit(busno) };
|
||||
csr::i2c::oe_write(reg)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scl_o(busno: u32, o: bool) {
|
||||
unsafe {
|
||||
let reg = csr::i2c::out_read();
|
||||
let reg = if o { reg | scl_bit(busno) } else { reg & !scl_bit(busno) };
|
||||
csr::i2c::out_write(reg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(has_i2c))]
|
||||
mod imp {
|
||||
pub fn sda_i(busno: u32) -> bool { true }
|
||||
pub fn sda_oe(busno: u32, oe: bool) {}
|
||||
pub fn sda_o(busno: u32, o: bool) {}
|
||||
pub fn scl_oe(busno: u32, oe: bool) {}
|
||||
pub fn scl_o(busno: u32, o: bool) {}
|
||||
}
|
||||
|
||||
use self::imp::*;
|
||||
|
||||
pub extern fn init(busno: i32) {
|
||||
let busno = busno as u32;
|
||||
|
||||
// Set SCL as output, and high level
|
||||
scl_o(busno, true);
|
||||
scl_oe(busno, true);
|
||||
// Prepare a zero level on SDA so that sda_oe pulls it down
|
||||
sda_o(busno, false);
|
||||
// Release SDA
|
||||
sda_oe(busno, false);
|
||||
|
||||
// Check the I2C bus is ready
|
||||
half_period();
|
||||
half_period();
|
||||
if !sda_i(busno) {
|
||||
artiq_raise!("I2CError", "SDA is stuck low")
|
||||
}
|
||||
}
|
||||
|
||||
pub extern fn start(busno: i32) {
|
||||
let busno = busno as u32;
|
||||
|
||||
// Set SCL high then SDA low
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
sda_oe(busno, true);
|
||||
half_period();
|
||||
}
|
||||
|
||||
pub extern fn stop(busno: i32) {
|
||||
let busno = busno as u32;
|
||||
|
||||
// First, make sure SCL is low, so that the target releases the SDA line
|
||||
scl_o(busno, false);
|
||||
half_period();
|
||||
// Set SCL high then SDA high
|
||||
sda_oe(busno, true);
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
sda_oe(busno, false);
|
||||
half_period();
|
||||
}
|
||||
|
||||
pub extern fn write(busno: i32, data: i8) -> bool {
|
||||
let (busno, data) = (busno as u32, data as u8);
|
||||
|
||||
// MSB first
|
||||
for bit in (0..8).rev() {
|
||||
// Set SCL low and set our bit on SDA
|
||||
scl_o(busno, false);
|
||||
sda_oe(busno, data & (1 << bit) == 0);
|
||||
half_period();
|
||||
// Set SCL high ; data is shifted on the rising edge of SCL
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
}
|
||||
// Check ack
|
||||
// Set SCL low, then release SDA so that the I2C target can respond
|
||||
scl_o(busno, false);
|
||||
half_period();
|
||||
sda_oe(busno, false);
|
||||
// Set SCL high and check for ack
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
// returns true if acked (I2C target pulled SDA low)
|
||||
!sda_i(busno)
|
||||
}
|
||||
|
||||
pub extern fn read(busno: i32, ack: bool) -> i8 {
|
||||
let busno = busno as u32;
|
||||
|
||||
// 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.
|
||||
scl_o(busno, false);
|
||||
half_period(); // make sure SCL has settled low
|
||||
sda_oe(busno, false);
|
||||
|
||||
let mut data: u8 = 0;
|
||||
|
||||
// MSB first
|
||||
for bit in (0..8).rev() {
|
||||
scl_o(busno, false);
|
||||
half_period();
|
||||
// Set SCL high and shift data
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
if sda_i(busno) { data |= 1 << bit }
|
||||
}
|
||||
// Send ack
|
||||
// Set SCL low and pull SDA low when acking
|
||||
scl_o(busno, false);
|
||||
if ack { sda_oe(busno, true) }
|
||||
half_period();
|
||||
// then set SCL high
|
||||
scl_o(busno, true);
|
||||
half_period();
|
||||
|
||||
data as i8
|
||||
}
|
@ -48,8 +48,6 @@ macro_rules! artiq_raise {
|
||||
}
|
||||
|
||||
mod rtio;
|
||||
#[cfg(has_i2c)]
|
||||
mod i2c;
|
||||
|
||||
use core::{mem, ptr, slice, str};
|
||||
use std::io::Cursor;
|
||||
@ -243,6 +241,24 @@ extern fn cache_put(key: *const u8, list: ArtiqList<i32>) {
|
||||
})
|
||||
}
|
||||
|
||||
extern fn i2c_start(busno: i32) {
|
||||
send(&I2CStartRequest { busno: busno as u32 });
|
||||
}
|
||||
|
||||
extern fn i2c_stop(busno: i32) {
|
||||
send(&I2CStopRequest { busno: busno as u32 });
|
||||
}
|
||||
|
||||
extern fn i2c_write(busno: i32, data: i8) -> bool {
|
||||
send(&I2CWriteRequest { busno: busno as u32, data: data as u8 });
|
||||
recv!(&I2CWriteReply { ack } => ack)
|
||||
}
|
||||
|
||||
extern fn i2c_read(busno: i32, ack: bool) -> i8 {
|
||||
send(&I2CReadRequest { busno: busno as u32, ack: ack });
|
||||
recv!(&I2CReadReply { data } => data) as i8
|
||||
}
|
||||
|
||||
unsafe fn attribute_writeback(typeinfo: *const ()) {
|
||||
struct Attr {
|
||||
offset: usize,
|
||||
|
@ -57,6 +57,13 @@ pub enum Message<'a> {
|
||||
CachePutRequest { key: &'a str, value: &'a [i32] },
|
||||
CachePutReply { succeeded: bool },
|
||||
|
||||
I2CStartRequest { busno: u32 },
|
||||
I2CStopRequest { busno: u32 },
|
||||
I2CWriteRequest { busno: u32, data: u8 },
|
||||
I2CWriteReply { ack: bool },
|
||||
I2CReadRequest { busno: u32, ack: bool },
|
||||
I2CReadReply { data: u8 },
|
||||
|
||||
Log(fmt::Arguments<'a>),
|
||||
LogSlice(&'a str)
|
||||
}
|
||||
|
@ -120,6 +120,8 @@ pub unsafe extern fn rust_main() {
|
||||
}
|
||||
info!("continuing boot");
|
||||
|
||||
#[cfg(has_i2c)]
|
||||
board::i2c::init();
|
||||
#[cfg(has_ad9516)]
|
||||
board::ad9516::init().unwrap();
|
||||
#[cfg(has_converter_spi)]
|
||||
|
@ -422,6 +422,44 @@ fn process_kern_message(waiter: Waiter,
|
||||
kern_send(waiter, &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(waiter, &kern::I2CWriteReply { ack: ack })
|
||||
}
|
||||
#[cfg(has_i2c)]
|
||||
&kern::I2CReadRequest { busno, ack } => {
|
||||
let data = board::i2c::read(busno, ack);
|
||||
kern_send(waiter, &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(waiter, &kern::I2CWriteReply { ack: false })
|
||||
}
|
||||
#[cfg(not(has_i2c))]
|
||||
&kern::I2CReadRequest { .. } => {
|
||||
kern_send(waiter, &kern::I2CReadReply { data: 0xff })
|
||||
}
|
||||
|
||||
&kern::RunFinished => {
|
||||
unsafe { kernel::stop() }
|
||||
session.kernel_state = KernelState::Absent;
|
||||
|
@ -36,9 +36,6 @@ class AMPSoC:
|
||||
self.add_memory_region("mailbox",
|
||||
self.mem_map["mailbox"] | 0x80000000, 4)
|
||||
|
||||
self.submodules.timer_kernel = timer.Timer()
|
||||
self.register_kernel_cpu_csrdevice("timer_kernel")
|
||||
|
||||
def register_kernel_cpu_csrdevice(self, name, csrs=None):
|
||||
if csrs is None:
|
||||
csrs = getattr(self, name).get_csrs()
|
||||
|
@ -100,10 +100,8 @@ _ams101_dac = [
|
||||
|
||||
class _NIST_Ions(MiniSoC, AMPSoC):
|
||||
mem_map = {
|
||||
"timer_kernel": 0x10000000,
|
||||
"rtio": 0x20000000,
|
||||
"rtio_dma": 0x30000000,
|
||||
"i2c": 0x50000000,
|
||||
"mailbox": 0x70000000
|
||||
}
|
||||
mem_map.update(MiniSoC.mem_map)
|
||||
@ -134,7 +132,7 @@ class _NIST_Ions(MiniSoC, AMPSoC):
|
||||
|
||||
i2c = self.platform.request("i2c")
|
||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
self.register_kernel_cpu_csrdevice("i2c")
|
||||
self.csr_devices.append("i2c")
|
||||
self.config["I2C_BUS_COUNT"] = 1
|
||||
|
||||
self.config["HAS_DDS"] = None
|
||||
|
@ -20,7 +20,6 @@ from artiq import __version__ as artiq_version
|
||||
|
||||
class Master(MiniSoC, AMPSoC):
|
||||
mem_map = {
|
||||
"timer_kernel": 0x10000000,
|
||||
"rtio": 0x20000000,
|
||||
"rtio_dma": 0x30000000,
|
||||
"drtio_aux": 0x50000000,
|
||||
|
@ -156,10 +156,8 @@ class AD9154(Module, AutoCSR):
|
||||
|
||||
class Phaser(MiniSoC, AMPSoC):
|
||||
mem_map = {
|
||||
"timer_kernel": 0x10000000,
|
||||
"rtio": 0x20000000,
|
||||
# "rtio_dma": 0x30000000,
|
||||
"i2c": 0x30000000,
|
||||
"mailbox": 0x70000000,
|
||||
"ad9154": 0x50000000,
|
||||
}
|
||||
@ -188,7 +186,7 @@ class Phaser(MiniSoC, AMPSoC):
|
||||
|
||||
i2c = platform.request("i2c")
|
||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
self.register_kernel_cpu_csrdevice("i2c")
|
||||
self.csr_devices.append("i2c")
|
||||
self.config["I2C_BUS_COUNT"] = 1
|
||||
|
||||
ad9154_spi = platform.request("ad9154_spi")
|
||||
|
@ -149,7 +149,6 @@ _ttl_io = [
|
||||
|
||||
class Demo(BaseSoC, AMPSoC):
|
||||
mem_map = {
|
||||
"timer_kernel": 0x10000000, # (shadow @0x90000000)
|
||||
"rtio": 0x20000000, # (shadow @0xa0000000)
|
||||
"mailbox": 0x70000000 # (shadow @0xf0000000)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user