forked from M-Labs/artiq
runtime: rewrite i2c support code in Rust.
This commit is contained in:
parent
a825584ac0
commit
18b7ccea4e
|
@ -105,9 +105,9 @@ 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),
|
||||||
|
|
||||||
api!(i2c_init),
|
api!(i2c_init = ::i2c::init),
|
||||||
api!(i2c_start),
|
api!(i2c_start = ::i2c::start),
|
||||||
api!(i2c_stop),
|
api!(i2c_stop = ::i2c::stop),
|
||||||
api!(i2c_write),
|
api!(i2c_write = ::i2c::write),
|
||||||
api!(i2c_read),
|
api!(i2c_read = ::i2c::read),
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
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: 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: u32) {
|
||||||
|
// Set SCL high then SDA low
|
||||||
|
scl_o(busno, true);
|
||||||
|
half_period();
|
||||||
|
sda_oe(busno, true);
|
||||||
|
half_period();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern 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 extern 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 extern 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
|
||||||
|
}
|
|
@ -50,6 +50,7 @@ macro_rules! artiq_raise {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod rtio;
|
mod rtio;
|
||||||
|
mod i2c;
|
||||||
|
|
||||||
use core::{mem, ptr, slice, str};
|
use core::{mem, ptr, slice, str};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
|
@ -4,7 +4,7 @@ include $(MISOC_DIRECTORY)/software/common.mak
|
||||||
PYTHON ?= python3.5
|
PYTHON ?= python3.5
|
||||||
|
|
||||||
OBJECTS := flash_storage.o main.o
|
OBJECTS := flash_storage.o main.o
|
||||||
OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o i2c.o
|
OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o
|
||||||
|
|
||||||
RUSTOUT_DIRECTORY := cargo/or1k-unknown-none/debug
|
RUSTOUT_DIRECTORY := cargo/or1k-unknown-none/debug
|
||||||
CORE_IO_COMMIT := d40c593f42fafbac1ff3d827f6df96338b5b7d8b
|
CORE_IO_COMMIT := d40c593f42fafbac1ff3d827f6df96338b5b7d8b
|
||||||
|
|
|
@ -1,193 +0,0 @@
|
||||||
#include <generated/csr.h>
|
|
||||||
|
|
||||||
#include "artiq_personality.h"
|
|
||||||
#include "rtio.h"
|
|
||||||
#include "i2c.h"
|
|
||||||
|
|
||||||
|
|
||||||
static void i2c_halfperiod()
|
|
||||||
{
|
|
||||||
timer_kernel_en_write(0);
|
|
||||||
timer_kernel_load_write(CONFIG_CLOCK_FREQUENCY/10000);
|
|
||||||
timer_kernel_reload_write(0);
|
|
||||||
timer_kernel_en_write(1);
|
|
||||||
|
|
||||||
timer_kernel_update_value_write(1);
|
|
||||||
while(timer_kernel_value_read() != 0)
|
|
||||||
timer_kernel_update_value_write(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if (defined CONFIG_I2C_BUS_COUNT) && (CONFIG_I2C_BUS_COUNT > 0)
|
|
||||||
|
|
||||||
#define SDA_BIT (1 << (2*busno + 1))
|
|
||||||
#define SCL_BIT (1 << (2*busno))
|
|
||||||
|
|
||||||
static int i2c_sda_i(int busno)
|
|
||||||
{
|
|
||||||
if(busno >= CONFIG_I2C_BUS_COUNT)
|
|
||||||
return 1;
|
|
||||||
else
|
|
||||||
return i2c_in_read() & SDA_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void i2c_sda_oe(int busno, int oe)
|
|
||||||
{
|
|
||||||
int reg;
|
|
||||||
|
|
||||||
reg = i2c_oe_read();
|
|
||||||
if(oe)
|
|
||||||
reg |= SDA_BIT;
|
|
||||||
else
|
|
||||||
reg &= ~SDA_BIT;
|
|
||||||
i2c_oe_write(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void i2c_sda_o(int busno, int o)
|
|
||||||
{
|
|
||||||
int reg;
|
|
||||||
|
|
||||||
reg = i2c_out_read();
|
|
||||||
if(o)
|
|
||||||
reg |= SDA_BIT;
|
|
||||||
else
|
|
||||||
reg &= ~SDA_BIT;
|
|
||||||
i2c_out_write(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void i2c_scl_oe(int busno, int oe)
|
|
||||||
{
|
|
||||||
int reg;
|
|
||||||
|
|
||||||
reg = i2c_oe_read();
|
|
||||||
if(oe)
|
|
||||||
reg |= SCL_BIT;
|
|
||||||
else
|
|
||||||
reg &= ~SCL_BIT;
|
|
||||||
i2c_oe_write(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void i2c_scl_o(int busno, int o)
|
|
||||||
{
|
|
||||||
int reg;
|
|
||||||
|
|
||||||
reg = i2c_out_read();
|
|
||||||
if(o)
|
|
||||||
reg |= SCL_BIT;
|
|
||||||
else
|
|
||||||
reg &= ~SCL_BIT;
|
|
||||||
i2c_out_write(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static int i2c_sda_i(int busno)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
static void i2c_sda_oe(int busno, int oe) {}
|
|
||||||
static void i2c_sda_o(int busno, int o) {}
|
|
||||||
static void i2c_scl_oe(int busno, int oe) {}
|
|
||||||
static void i2c_scl_o(int busno, int o) {}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void i2c_init(int busno)
|
|
||||||
{
|
|
||||||
/* Set SCL as output, and high level */
|
|
||||||
i2c_scl_o(busno, 1);
|
|
||||||
i2c_scl_oe(busno, 1);
|
|
||||||
/* Prepare a zero level on SDA so that i2c_sda_oe pulls it down */
|
|
||||||
i2c_sda_o(busno, 0);
|
|
||||||
/* Release SDA */
|
|
||||||
i2c_sda_oe(busno, 0);
|
|
||||||
|
|
||||||
/* Check the I2C bus is ready */
|
|
||||||
i2c_halfperiod();
|
|
||||||
i2c_halfperiod();
|
|
||||||
if(!i2c_sda_i(busno))
|
|
||||||
artiq_raise_from_c("I2CError", "SDA is stuck low", 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void i2c_start(int busno)
|
|
||||||
{
|
|
||||||
/* Set SCL high then SDA low */
|
|
||||||
i2c_scl_o(busno, 1);
|
|
||||||
i2c_halfperiod();
|
|
||||||
i2c_sda_oe(busno, 1);
|
|
||||||
i2c_halfperiod();
|
|
||||||
}
|
|
||||||
|
|
||||||
void i2c_stop(int busno)
|
|
||||||
{
|
|
||||||
/* First, make sure SCL is low, so that the target releases the SDA line */
|
|
||||||
i2c_scl_o(busno, 0);
|
|
||||||
i2c_halfperiod();
|
|
||||||
/* Set SCL high then SDA high */
|
|
||||||
i2c_sda_oe(busno, 1);
|
|
||||||
i2c_scl_o(busno, 1);
|
|
||||||
i2c_halfperiod();
|
|
||||||
i2c_sda_oe(busno, 0);
|
|
||||||
i2c_halfperiod();
|
|
||||||
}
|
|
||||||
|
|
||||||
int i2c_write(int busno, int b)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* MSB first */
|
|
||||||
for(i=7;i>=0;i--) {
|
|
||||||
/* Set SCL low and set our bit on SDA */
|
|
||||||
i2c_scl_o(busno, 0);
|
|
||||||
i2c_sda_oe(busno, b & (1 << i) ? 0 : 1);
|
|
||||||
i2c_halfperiod();
|
|
||||||
/* Set SCL high ; data is shifted on the rising edge of SCL */
|
|
||||||
i2c_scl_o(busno, 1);
|
|
||||||
i2c_halfperiod();
|
|
||||||
}
|
|
||||||
/* Check ack */
|
|
||||||
/* Set SCL low, then release SDA so that the I2C target can respond */
|
|
||||||
i2c_scl_o(busno, 0);
|
|
||||||
i2c_halfperiod();
|
|
||||||
i2c_sda_oe(busno, 0);
|
|
||||||
/* Set SCL high and check for ack */
|
|
||||||
i2c_scl_o(busno, 1);
|
|
||||||
i2c_halfperiod();
|
|
||||||
/* returns 1 if acked (I2C target pulled SDA low) */
|
|
||||||
return !i2c_sda_i(busno);
|
|
||||||
}
|
|
||||||
|
|
||||||
int i2c_read(int busno, int ack)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
unsigned char b;
|
|
||||||
|
|
||||||
/* 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.
|
|
||||||
*/
|
|
||||||
i2c_scl_o(busno, 0);
|
|
||||||
i2c_halfperiod(); /* make sure SCL has settled low */
|
|
||||||
i2c_sda_oe(busno, 0);
|
|
||||||
|
|
||||||
b = 0;
|
|
||||||
/* MSB first */
|
|
||||||
for(i=7;i>=0;i--) {
|
|
||||||
i2c_scl_o(busno, 0);
|
|
||||||
i2c_halfperiod();
|
|
||||||
/* Set SCL high and shift data */
|
|
||||||
i2c_scl_o(busno, 1);
|
|
||||||
i2c_halfperiod();
|
|
||||||
if(i2c_sda_i(busno)) b |= (1 << i);
|
|
||||||
}
|
|
||||||
/* Send ack */
|
|
||||||
/* Set SCL low and pull SDA low when acking */
|
|
||||||
i2c_scl_o(busno, 0);
|
|
||||||
if(ack)
|
|
||||||
i2c_sda_oe(busno, 1);
|
|
||||||
i2c_halfperiod();
|
|
||||||
/* then set SCL high */
|
|
||||||
i2c_scl_o(busno, 1);
|
|
||||||
i2c_halfperiod();
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
#ifndef __I2C_H
|
|
||||||
#define __I2C_H
|
|
||||||
|
|
||||||
void i2c_init(int busno);
|
|
||||||
void i2c_start(int busno);
|
|
||||||
void i2c_stop(int busno);
|
|
||||||
int i2c_write(int busno, int b);
|
|
||||||
int i2c_read(int busno, int ack);
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue