From 18b7ccea4e0eea63a4324bed3862883f14594ef4 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 21 Nov 2016 18:25:43 +0000 Subject: [PATCH] runtime: rewrite i2c support code in Rust. --- artiq/runtime.rs/libksupport/api.rs | 10 +- artiq/runtime.rs/libksupport/i2c.rs | 166 ++++++++++++++++++++++++ artiq/runtime.rs/libksupport/lib.rs | 1 + artiq/runtime/Makefile | 2 +- artiq/runtime/i2c.c | 193 ---------------------------- artiq/runtime/i2c.h | 10 -- 6 files changed, 173 insertions(+), 209 deletions(-) create mode 100644 artiq/runtime.rs/libksupport/i2c.rs delete mode 100644 artiq/runtime/i2c.c delete mode 100644 artiq/runtime/i2c.h diff --git a/artiq/runtime.rs/libksupport/api.rs b/artiq/runtime.rs/libksupport/api.rs index fd069d8c4..5a12ea691 100644 --- a/artiq/runtime.rs/libksupport/api.rs +++ b/artiq/runtime.rs/libksupport/api.rs @@ -105,9 +105,9 @@ static mut API: &'static [(&'static str, *const ())] = &[ api!(rtio_input_timestamp = ::rtio::input_timestamp), api!(rtio_input_data = ::rtio::input_data), - api!(i2c_init), - api!(i2c_start), - api!(i2c_stop), - api!(i2c_write), - api!(i2c_read), + api!(i2c_init = ::i2c::init), + api!(i2c_start = ::i2c::start), + api!(i2c_stop = ::i2c::stop), + api!(i2c_write = ::i2c::write), + api!(i2c_read = ::i2c::read), ]; diff --git a/artiq/runtime.rs/libksupport/i2c.rs b/artiq/runtime.rs/libksupport/i2c.rs new file mode 100644 index 000000000..8a9f6da2d --- /dev/null +++ b/artiq/runtime.rs/libksupport/i2c.rs @@ -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 +} diff --git a/artiq/runtime.rs/libksupport/lib.rs b/artiq/runtime.rs/libksupport/lib.rs index f46d5efaf..6b285f889 100644 --- a/artiq/runtime.rs/libksupport/lib.rs +++ b/artiq/runtime.rs/libksupport/lib.rs @@ -50,6 +50,7 @@ macro_rules! artiq_raise { } mod rtio; +mod i2c; use core::{mem, ptr, slice, str}; use std::io::Cursor; diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 36685661c..a8387466b 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -4,7 +4,7 @@ include $(MISOC_DIRECTORY)/software/common.mak PYTHON ?= python3.5 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 CORE_IO_COMMIT := d40c593f42fafbac1ff3d827f6df96338b5b7d8b diff --git a/artiq/runtime/i2c.c b/artiq/runtime/i2c.c deleted file mode 100644 index c8ec3f131..000000000 --- a/artiq/runtime/i2c.c +++ /dev/null @@ -1,193 +0,0 @@ -#include - -#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; -} diff --git a/artiq/runtime/i2c.h b/artiq/runtime/i2c.h deleted file mode 100644 index c7aab6e22..000000000 --- a/artiq/runtime/i2c.h +++ /dev/null @@ -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