Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 082fdaf45056
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4f97d00e7932
Choose a head ref
  • 3 commits
  • 5 files changed
  • 1 contributor

Commits on Jan 5, 2017

  1. Copy the full SHA
    6c68520 View commit details
  2. Copy the full SHA
    13c45c8 View commit details
  3. Copy the full SHA
    4f97d00 View commit details
Showing with 197 additions and 19 deletions.
  1. +22 −11 artiq/firmware/libboard/i2c.rs
  2. +4 −0 artiq/firmware/libboard/lib.rs
  3. +163 −0 artiq/firmware/libboard/si5324.rs
  4. +4 −4 artiq/firmware/libksupport/lib.rs
  5. +4 −4 artiq/firmware/runtime/kernel_proto.rs
33 changes: 22 additions & 11 deletions artiq/firmware/libboard/i2c.rs
Original file line number Diff line number Diff line change
@@ -2,40 +2,40 @@ 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_bit(busno: u8) -> u8 { 1 << (2 * busno + 1) }
fn scl_bit(busno: u8) -> u8 { 1 << (2 * busno) }

fn sda_i(busno: u32) -> bool {
fn sda_i(busno: u8) -> bool {
unsafe {
csr::i2c::in_read() & sda_bit(busno) != 0
}
}

fn sda_oe(busno: u32, oe: bool) {
fn sda_oe(busno: u8, 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) {
fn sda_o(busno: u8, 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) {
fn scl_oe(busno: u8, 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) {
fn scl_o(busno: u8, o: bool) {
unsafe {
let reg = csr::i2c::out_read();
let reg = if o { reg | scl_bit(busno) } else { reg & !scl_bit(busno) };
@@ -45,6 +45,7 @@ fn scl_o(busno: u32, o: bool) {

pub fn init() {
for busno in 0..csr::CONFIG_I2C_BUS_COUNT {
let busno = busno as u8;
// Set SCL as output, and high level
scl_o(busno, true);
scl_oe(busno, true);
@@ -62,15 +63,25 @@ pub fn init() {
}
}

pub fn start(busno: u32) {
pub fn start(busno: u8) {
// Set SCL high then SDA low
scl_o(busno, true);
half_period();
sda_oe(busno, true);
half_period();
}

pub fn stop(busno: u32) {
pub fn restart(busno: u8) {
// Set SCL low then SDA high */
scl_o(busno, false);
half_period();
sda_oe(busno, false);
half_period();
// Do a regular start
start(busno);
}

pub fn stop(busno: u8) {
// First, make sure SCL is low, so that the target releases the SDA line
scl_o(busno, false);
half_period();
@@ -82,7 +93,7 @@ pub fn stop(busno: u32) {
half_period();
}

pub fn write(busno: u32, data: u8) -> bool {
pub fn write(busno: u8, data: u8) -> bool {
// MSB first
for bit in (0..8).rev() {
// Set SCL low and set our bit on SDA
@@ -105,7 +116,7 @@ pub fn write(busno: u32, data: u8) -> bool {
!sda_i(busno)
}

pub fn read(busno: u32, ack: bool) -> u8 {
pub fn read(busno: u8, 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);
4 changes: 4 additions & 0 deletions artiq/firmware/libboard/lib.rs
Original file line number Diff line number Diff line change
@@ -11,8 +11,12 @@ 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_i2c)]
pub mod si5324;

#[cfg(has_ad9516)]
#[allow(dead_code)]
mod ad9516_reg;
163 changes: 163 additions & 0 deletions artiq/firmware/libboard/si5324.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use i2c;
use clock;

const BUSNO: u8 = 0;
const ADDRESS: u8 = 0x68;

#[cfg(soc_platform = "kc705")]
fn pca9548_select(channel: u8) -> Result<(), &'static str> {
i2c::start(BUSNO);
if !i2c::write(BUSNO, (0x74 << 1)) {
return Err("PCA9548 failed to ack write address")
}
if !i2c::write(BUSNO, 1 << channel) {
return Err("PCA9548 failed to ack control word")
}
i2c::stop(BUSNO);
Ok(())
}

// NOTE: the logical parameters DO NOT MAP to physical values written
// into registers. They have to be mapped; see the datasheet.
// DSPLLsim reports the logical parameters in the design summary, not
// the physical register values (but those are present separately).
pub struct FrequencySettings {
n1_hs: u8,
nc1_ls: u32,
n2_hs: u8,
n2_ls: u32,
n31: u32,
n32: u32
}

fn map_frequency_settings(settings: &FrequencySettings) -> Result<FrequencySettings, &'static str> {
if settings.nc1_ls != 0 && (settings.nc1_ls % 2) == 1 {
return Err("NC1_LS must be 0 or even")
}
if settings.nc1_ls > (1 << 20) {
return Err("NC1_LS is too high")
}
if (settings.n2_ls % 2) == 1 {
return Err("N2_LS must be even")
}
if settings.n2_ls > (1 << 20) {
return Err("N2_LS is too high")
}
if settings.n31 > (1 << 19) {
return Err("N31 is too high")
}
if settings.n32 > (1 << 19) {
return Err("N32 is too high")
}
let r = FrequencySettings {
n1_hs: match settings.n1_hs {
4 => 0b000,
5 => 0b001,
6 => 0b010,
7 => 0b011,
8 => 0b100,
9 => 0b101,
10 => 0b110,
11 => 0b111,
_ => return Err("n1_hs has an invalid value")
},
nc1_ls: settings.nc1_ls - 1,
n2_hs: match settings.n2_hs {
4 => 0b000,
5 => 0b001,
6 => 0b010,
7 => 0b011,
8 => 0b100,
9 => 0b101,
10 => 0b110,
11 => 0b111,
_ => return Err("n2_hs has an invalid value")
},
n2_ls: settings.n2_ls - 1,
n31: settings.n31 - 1,
n32: settings.n32 - 1
};
Ok(r)
}

fn write(reg: u8, val: u8) -> Result<(), &'static str> {
i2c::start(BUSNO);
if !i2c::write(BUSNO, (ADDRESS << 1)) {
return Err("Si5324 failed to ack write address")
}
if !i2c::write(BUSNO, reg) {
return Err("Si5324 failed to ack register")
}
if !i2c::write(BUSNO, val) {
return Err("Si5324 failed to ack value")
}
i2c::stop(BUSNO);
Ok(())
}

fn read(reg: u8) -> Result<u8, &'static str> {
i2c::start(BUSNO);
if !i2c::write(BUSNO, (ADDRESS << 1)) {
return Err("Si5324 failed to ack write address")
}
if !i2c::write(BUSNO, reg) {
return Err("Si5324 failed to ack register")
}
i2c::restart(BUSNO);
if !i2c::write(BUSNO, (ADDRESS << 1) | 1) {
return Err("Si5324 failed to ack read address")
}
let val = i2c::read(BUSNO, false);
i2c::stop(BUSNO);
Ok(val)
}

fn ident() -> Result<u16, &'static str> {
Ok(((read(134)? as u16) << 8) | (read(135)? as u16))
}

fn locked() -> Result<bool, &'static str> {
Ok((read(130)? & 0x01) == 0) // LOL_INT=1
}

pub fn setup_hitless_clock_switching(settings: &FrequencySettings) -> Result<(), &'static str> {
let s = map_frequency_settings(settings)?;

#[cfg(soc_platform = "kc705")]
pca9548_select(7)?;

if ident()? != 0x0182 {
return Err("Si5324 does not have expected product number");
}

write(0, 0b01010000)?; // FREE_RUN=1
write(1, 0b11100100)?; // CK_PRIOR2=1 CK_PRIOR1=0
write(2, 0b0010 | (4 << 4))?; // BWSEL=4
write(3, 0b0101 | 0x10)?; // SQ_ICAL=1
write(4, 0b10010010)?; // AUTOSEL_REG=b10
write(6, 0x07)?; // SFOUT1_REG=b111
write(25, (s.n1_hs << 5 ) as u8)?;
write(31, (s.nc1_ls >> 16) as u8)?;
write(32, (s.nc1_ls >> 8 ) as u8)?;
write(33, (s.nc1_ls) as u8)?;
write(40, (s.n2_hs << 5 ) as u8 | (s.n2_ls >> 16) as u8)?;
write(41, (s.n2_ls >> 8 ) as u8)?;
write(42, (s.n2_ls) as u8)?;
write(43, (s.n31 >> 16) as u8)?;
write(44, (s.n31 >> 8) as u8)?;
write(45, (s.n31) as u8)?;
write(46, (s.n32 >> 16) as u8)?;
write(47, (s.n32 >> 8) as u8)?;
write(48, (s.n32) as u8)?;
write(137, 0x01)?; // FASTLOCK=1
write(136, 0x40)?; // ICAL=1

let t = clock::get_ms();
while !locked()? {
if clock::get_ms() > t + 1000 {
return Err("Si5324 lock timeout");
}
}

Ok(())
}
8 changes: 4 additions & 4 deletions artiq/firmware/libksupport/lib.rs
Original file line number Diff line number Diff line change
@@ -242,20 +242,20 @@ extern fn cache_put(key: *const u8, list: ArtiqList<i32>) {
}

extern fn i2c_start(busno: i32) {
send(&I2CStartRequest { busno: busno as u32 });
send(&I2CStartRequest { busno: busno as u8 });
}

extern fn i2c_stop(busno: i32) {
send(&I2CStopRequest { busno: busno as u32 });
send(&I2CStopRequest { busno: busno as u8 });
}

extern fn i2c_write(busno: i32, data: i8) -> bool {
send(&I2CWriteRequest { busno: busno as u32, data: data as u8 });
send(&I2CWriteRequest { busno: busno as u8, 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 });
send(&I2CReadRequest { busno: busno as u8, ack: ack });
recv!(&I2CReadReply { data } => data) as i8
}

8 changes: 4 additions & 4 deletions artiq/firmware/runtime/kernel_proto.rs
Original file line number Diff line number Diff line change
@@ -57,11 +57,11 @@ 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 },
I2CStartRequest { busno: u8 },
I2CStopRequest { busno: u8 },
I2CWriteRequest { busno: u8, data: u8 },
I2CWriteReply { ack: bool },
I2CReadRequest { busno: u32, ack: bool },
I2CReadRequest { busno: u8, ack: bool },
I2CReadReply { data: u8 },

Log(fmt::Arguments<'a>),