forked from M-Labs/artiq
1
0
Fork 0

move i2c to libboard, do bit-banging on comms CPU

This commit is contained in:
Sebastien Bourdeauducq 2017-01-04 21:04:38 +01:00
parent 3dd9d54b22
commit 082fdaf450
15 changed files with 212 additions and 210 deletions

View File

@ -126,10 +126,6 @@ class DDSError(Exception):
incorrect. incorrect.
""" """
class I2CError(Exception):
"""Raised with a I2C transaction fails."""
artiq_builtin = True
class WatchdogExpired(Exception): class WatchdogExpired(Exception):
"""Raised when a watchdog expires.""" """Raised when a watchdog expires."""

View File

@ -1,11 +1,10 @@
from artiq.language.core import syscall, kernel from artiq.language.core import syscall, kernel
from artiq.language.types import TBool, TInt32, TNone from artiq.language.types import TBool, TInt32, TNone
from artiq.coredevice.exceptions import I2CError
@syscall(flags={"nowrite"}) class I2CError(Exception):
def i2c_init(busno: TInt32) -> TNone: """Raised with a I2C transaction fails."""
raise NotImplementedError("syscall not simulated") pass
@syscall(flags={"nounwind", "nowrite"}) @syscall(flags={"nounwind", "nowrite"})
@ -48,7 +47,6 @@ class PCA9548:
:param channel: channel number (0-7) :param channel: channel number (0-7)
""" """
i2c_init(self.busno)
i2c_start(self.busno) i2c_start(self.busno)
try: try:
if not i2c_write(self.busno, self.address): if not i2c_write(self.busno, self.address):
@ -60,7 +58,6 @@ class PCA9548:
@kernel @kernel
def readback(self): def readback(self):
i2c_init(self.busno)
i2c_start(self.busno) i2c_start(self.busno)
r = 0 r = 0
try: try:
@ -84,7 +81,6 @@ class TCA6424A:
@kernel @kernel
def _write24(self, command, value): def _write24(self, command, value):
i2c_init(self.busno)
i2c_start(self.busno) i2c_start(self.busno)
try: try:
if not i2c_write(self.busno, self.address): if not i2c_write(self.busno, self.address):

View 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
}

View File

@ -11,6 +11,8 @@ include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs"));
pub mod spr; pub mod spr;
pub mod irq; pub mod irq;
pub mod clock; pub mod clock;
#[cfg(has_i2c)]
pub mod i2c;
#[cfg(has_ad9516)] #[cfg(has_ad9516)]
#[allow(dead_code)] #[allow(dead_code)]
mod ad9516_reg; mod ad9516_reg;

View File

@ -106,14 +106,8 @@ static mut API: &'static [(&'static str, *const ())] = &[
api!(rtio_input_timestamp = ::rtio::input_timestamp), api!(rtio_input_timestamp = ::rtio::input_timestamp),
api!(rtio_input_data = ::rtio::input_data), api!(rtio_input_data = ::rtio::input_data),
#[cfg(has_i2c)] api!(i2c_start = ::i2c_start),
api!(i2c_init = ::i2c::init), api!(i2c_stop = ::i2c_stop),
#[cfg(has_i2c)] api!(i2c_write = ::i2c_write),
api!(i2c_start = ::i2c::start), api!(i2c_read = ::i2c_read),
#[cfg(has_i2c)]
api!(i2c_stop = ::i2c::stop),
#[cfg(has_i2c)]
api!(i2c_write = ::i2c::write),
#[cfg(has_i2c)]
api!(i2c_read = ::i2c::read),
]; ];

View File

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

View File

@ -48,8 +48,6 @@ macro_rules! artiq_raise {
} }
mod rtio; mod rtio;
#[cfg(has_i2c)]
mod i2c;
use core::{mem, ptr, slice, str}; use core::{mem, ptr, slice, str};
use std::io::Cursor; 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 ()) { unsafe fn attribute_writeback(typeinfo: *const ()) {
struct Attr { struct Attr {
offset: usize, offset: usize,

View File

@ -57,6 +57,13 @@ pub enum Message<'a> {
CachePutRequest { key: &'a str, value: &'a [i32] }, CachePutRequest { key: &'a str, value: &'a [i32] },
CachePutReply { succeeded: bool }, 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>), Log(fmt::Arguments<'a>),
LogSlice(&'a str) LogSlice(&'a str)
} }

View File

@ -120,6 +120,8 @@ pub unsafe extern fn rust_main() {
} }
info!("continuing boot"); info!("continuing boot");
#[cfg(has_i2c)]
board::i2c::init();
#[cfg(has_ad9516)] #[cfg(has_ad9516)]
board::ad9516::init().unwrap(); board::ad9516::init().unwrap();
#[cfg(has_converter_spi)] #[cfg(has_converter_spi)]

View File

@ -422,6 +422,44 @@ fn process_kern_message(waiter: Waiter,
kern_send(waiter, &kern::CachePutReply { succeeded: succeeded }) 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 => { &kern::RunFinished => {
unsafe { kernel::stop() } unsafe { kernel::stop() }
session.kernel_state = KernelState::Absent; session.kernel_state = KernelState::Absent;

View File

@ -36,9 +36,6 @@ class AMPSoC:
self.add_memory_region("mailbox", self.add_memory_region("mailbox",
self.mem_map["mailbox"] | 0x80000000, 4) 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): def register_kernel_cpu_csrdevice(self, name, csrs=None):
if csrs is None: if csrs is None:
csrs = getattr(self, name).get_csrs() csrs = getattr(self, name).get_csrs()

View File

@ -100,10 +100,8 @@ _ams101_dac = [
class _NIST_Ions(MiniSoC, AMPSoC): class _NIST_Ions(MiniSoC, AMPSoC):
mem_map = { mem_map = {
"timer_kernel": 0x10000000,
"rtio": 0x20000000, "rtio": 0x20000000,
"rtio_dma": 0x30000000, "rtio_dma": 0x30000000,
"i2c": 0x50000000,
"mailbox": 0x70000000 "mailbox": 0x70000000
} }
mem_map.update(MiniSoC.mem_map) mem_map.update(MiniSoC.mem_map)
@ -134,7 +132,7 @@ class _NIST_Ions(MiniSoC, AMPSoC):
i2c = self.platform.request("i2c") i2c = self.platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) 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["I2C_BUS_COUNT"] = 1
self.config["HAS_DDS"] = None self.config["HAS_DDS"] = None

View File

@ -20,7 +20,6 @@ from artiq import __version__ as artiq_version
class Master(MiniSoC, AMPSoC): class Master(MiniSoC, AMPSoC):
mem_map = { mem_map = {
"timer_kernel": 0x10000000,
"rtio": 0x20000000, "rtio": 0x20000000,
"rtio_dma": 0x30000000, "rtio_dma": 0x30000000,
"drtio_aux": 0x50000000, "drtio_aux": 0x50000000,

View File

@ -156,10 +156,8 @@ class AD9154(Module, AutoCSR):
class Phaser(MiniSoC, AMPSoC): class Phaser(MiniSoC, AMPSoC):
mem_map = { mem_map = {
"timer_kernel": 0x10000000,
"rtio": 0x20000000, "rtio": 0x20000000,
# "rtio_dma": 0x30000000, # "rtio_dma": 0x30000000,
"i2c": 0x30000000,
"mailbox": 0x70000000, "mailbox": 0x70000000,
"ad9154": 0x50000000, "ad9154": 0x50000000,
} }
@ -188,7 +186,7 @@ class Phaser(MiniSoC, AMPSoC):
i2c = platform.request("i2c") i2c = platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) 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["I2C_BUS_COUNT"] = 1
ad9154_spi = platform.request("ad9154_spi") ad9154_spi = platform.request("ad9154_spi")

View File

@ -149,7 +149,6 @@ _ttl_io = [
class Demo(BaseSoC, AMPSoC): class Demo(BaseSoC, AMPSoC):
mem_map = { mem_map = {
"timer_kernel": 0x10000000, # (shadow @0x90000000)
"rtio": 0x20000000, # (shadow @0xa0000000) "rtio": 0x20000000, # (shadow @0xa0000000)
"mailbox": 0x70000000 # (shadow @0xf0000000) "mailbox": 0x70000000 # (shadow @0xf0000000)
} }