diff --git a/firmware/src/eth.rs b/firmware/src/eth.rs index 8e3f657..805b373 100644 --- a/firmware/src/eth.rs +++ b/firmware/src/eth.rs @@ -36,7 +36,7 @@ const NUM_TX_SLOTS: u32 = 2; const MTU: usize = 1530; const SLOT_LEN: u32 = 2048; -use crate::{busy_wait, read_reg, write_reg}; +use crate::{read_reg, timer::busy_wait_ms, write_reg}; pub struct LiteEthDevice { csr_addr: u32, diff --git a/firmware/src/i2c.rs b/firmware/src/i2c.rs index e2d7ff8..8f7d2cd 100644 --- a/firmware/src/i2c.rs +++ b/firmware/src/i2c.rs @@ -2,146 +2,289 @@ //! //! See `gateware/i2c.py` for register information +use crate::timer::busy_wait_us; use crate::{read_reg, write_reg}; use core::arch::asm; -/// TODO repr(C) a register bank, and add instances -// Using the blocking API because the peripheral requires fairly tight timing to operate -// correctly, and I don't feel like writing the gateware to resolve that. -use embedded_hal::blocking::i2c::{Read, SevenBitAddress, Write}; +use embedded_hal::blocking::i2c::{Operation, Read, SevenBitAddress, Transactional, Write}; -const CR: u32 = 0; -const SR: u32 = 1; -const DWR: u32 = 2; -const DRR: u32 = 3; +pub const BASE: u32 = 0xF000_1800; + +const REG_W: u32 = 0x00; +const FIELD_W_SCL: u32 = 0; +const FIELD_W_OE: u32 = 1; +const FIELD_W_SDA: u32 = 2; + +const REG_R: u32 = 0x04; +const FIELD_R_SDA: u32 = 0; + +const WAIT_PERIOD_US: u64 = (1_000_000 / 100_000) / 2; #[derive(Clone, Copy, Debug)] pub enum Error { - /// Device is busy for some reason - Busy, /// I2C bus returned a NACK Nack, } -pub struct AmlibI2c { +pub struct LitexI2c { base_addr: u32, } use core::fmt::Write as _; -impl AmlibI2c { +impl LitexI2c { pub fn new(base_addr: u32) -> Self { - AmlibI2c { base_addr } + LitexI2c { base_addr } } - fn wait_while_busy(&mut self) { + fn set_scl(&mut self, val: u32) { unsafe { - while read_reg::(self.base_addr + SR) & 1 != 0 { - asm!("nop"); - } + write_reg(self.base_addr + REG_W, (val & 0x01) << FIELD_W_SCL); } } - fn is_nack(&self) -> bool { - unsafe { read_reg::(self.base_addr + SR) & 0x02 == 0 } + fn set_sda(&mut self, val: u32) { + unsafe { + write_reg(self.base_addr + REG_W, (val & 0x01) << FIELD_W_SDA); + } + } + + fn set_oe(&mut self, val: u32) { + unsafe { + write_reg(self.base_addr + REG_W, (val & 0x01) << FIELD_W_OE); + } + } + + fn get_sda(&mut self) -> u8 { + unsafe { read_reg::(self.base_addr + REG_R) & (1 << FIELD_R_SDA) } + } + + /// Generate a START condition. The next operation should be a clock + /// transition to low. + /// + /// Assumes SCL and SDA have been high due to bus inactivity + unsafe fn start(&mut self) { + self.set_oe(1); + self.set_scl(1); + self.set_sda(1); + busy_wait_us(WAIT_PERIOD_US); + + self.set_sda(0); + busy_wait_us(WAIT_PERIOD_US); + } + + /// Generate a repeated START condition. + /// + /// Assumes it was called after an ACK bit + unsafe fn repeated_start(&mut self) { + self.set_scl(0); + self.set_sda(1); + self.set_oe(1); + busy_wait_us(WAIT_PERIOD_US); + + self.set_scl(1); + busy_wait_us(WAIT_PERIOD_US); + + // TODO does this third half-clock break timing? + self.set_sda(0); + busy_wait_us(WAIT_PERIOD_US); + } + + /// Handles STOP conditions. + /// + /// Assumes it was called after an ACK bit + unsafe fn stop(&mut self) { + self.set_scl(0); + self.set_sda(0); + self.set_oe(1); + busy_wait_us(WAIT_PERIOD_US); + + self.set_scl(1); + busy_wait_us(WAIT_PERIOD_US); + + // TODO does this third half-clock break timing? + //self.set_oe(0); // Disable and let pullup take bus high + self.set_sda(1); + busy_wait_us(WAIT_PERIOD_US); + } + + unsafe fn write_byte(&mut self, byte: u8) { + self.set_oe(1); // Take SDA to write data out + + for i in 0..8 { + // Write data out before we clock high + self.set_scl(0); + busy_wait_us(WAIT_PERIOD_US); + + // Clock data in + self.set_scl(1); + self.set_sda(byte as u32 >> (7 - i)); + busy_wait_us(WAIT_PERIOD_US) + } + } + + unsafe fn read_byte(&mut self) -> u8 { + self.set_oe(0); // De-assert to read data + + let mut out = 0; + for i in 0..8 { + self.set_scl(0); + busy_wait_us(WAIT_PERIOD_US); + + self.set_scl(1); + busy_wait_us(WAIT_PERIOD_US); + + // TODO sampling at the end of the clock may not work 100% of the time + // ideally should sample in the middle + out |= (self.get_sda() << 7 - i); + } + + out + } + + // Returns if there was an ACK + unsafe fn get_ack(&mut self) -> bool { + self.set_oe(0); // Release SDA to read ACK + self.set_sda(1); + self.set_scl(0); + busy_wait_us(WAIT_PERIOD_US); + + self.set_scl(1); + busy_wait_us(WAIT_PERIOD_US); + + self.get_sda() == 0 + } + + unsafe fn set_ack(&mut self, ack: bool) { + self.set_oe(1); // Take SDA + self.set_sda(!ack as u32); + self.set_scl(0); + busy_wait_us(WAIT_PERIOD_US); + + self.set_scl(1); + busy_wait_us(WAIT_PERIOD_US); } } -impl Write for AmlibI2c { +impl Write for LitexI2c { // TODO errors type Error = Error; fn write(&mut self, address: SevenBitAddress, bytes: &[u8]) -> Result<(), Self::Error> { unsafe { - if (read_reg::(self.base_addr + SR) & 1) != 0 { - return Err(Error::Busy); - } + self.start(); - // START - write_reg(self.base_addr + CR, 0x01u8); - - self.wait_while_busy(); - - // Send address byte (R/~W = 0) - write_reg(self.base_addr + DWR, address << 1); - write_reg(self.base_addr + CR, 0x04u8); - self.wait_while_busy(); - - // Check NACK - if self.is_nack() { + // Write address and write bit + self.write_byte((address << 1) | 0); + if !self.get_ack() { + self.stop(); return Err(Error::Nack); } - // Write data - for byte in bytes { - // Write byte - write_reg(self.base_addr + DWR, *byte); - // Send byte - write_reg(self.base_addr + CR, 0x04u8); - self.wait_while_busy(); - - if self.is_nack() { + for b in bytes { + self.write_byte(*b); + if !self.get_ack() { + self.stop(); return Err(Error::Nack); } } - // STOP - write_reg(self.base_addr + CR, 0x02u8); + self.stop(); - self.wait_while_busy(); Ok(()) } } } -impl Read for AmlibI2c { +impl Read for LitexI2c { // TODO errors type Error = Error; fn read(&mut self, address: SevenBitAddress, buffer: &mut [u8]) -> Result<(), Self::Error> { unsafe { - if (read_reg::(self.base_addr + SR) & 1) != 0 { - return Err(Error::Busy); - } + self.start(); - // Set read ACK to 1. Should probably ideally be done in an init() function - // but it doesn't really matter - write_reg(self.base_addr + CR, 0x30u8); - - // START - write_reg(self.base_addr + CR, 0x01u8); - - self.wait_while_busy(); - - // Send address byte (R/~W = 1) - write_reg(self.base_addr + DWR, (address << 1) + 1); - write_reg(self.base_addr + CR, 0x04u8); - self.wait_while_busy(); - if self.is_nack() { + self.write_byte((address << 1) | 1); + if !self.get_ack() { + self.stop(); return Err(Error::Nack); } - for byte in buffer { - // Start reading in a byte - write_reg(self.base_addr + CR, 0x08u8); - self.wait_while_busy(); - // Value is available once busy is clear - *byte = read_reg::(self.base_addr + DRR); + for b in buffer { + *b = self.read_byte(); + self.set_ack(true); } - // STOP - write_reg(self.base_addr + CR, 0x02u8); - + self.stop(); Ok(()) } } } -// I don't need this for MCP4726 -//impl Transactional for AmlibI2c { -// type Error = Error; -// -// fn exec<'a>(&mut self, address: u8, operations: &mut [Operation<'a>]) -// -> Result<(), Self::Error> { -// Ok(()) -// } -//} +impl Transactional for LitexI2c { + type Error = Error; + + fn exec<'a>( + &mut self, + address: u8, + operations: &mut [Operation<'a>], + ) -> Result<(), Self::Error> { + unsafe { + let mut last_operation_was_read: Option = None; + + for op in operations { + let op_is_read = match op { + Operation::Write(_) => false, + Operation::Read(_) => true, + }; + + match last_operation_was_read { + None => { + // First operation; send regular start and address + self.start(); + self.write_byte((address << 1) | op_is_read as u8); + if !self.get_ack() { + self.stop(); + return Err(Error::Nack); + } + } + Some(last_op_was_read) => { + // Not the first, send repeated start + self.repeated_start(); + + if last_op_was_read != op_is_read { + // Operations don't match, so we need to send address byte + self.write_byte((address << 1) | op_is_read as u8); + if !self.get_ack() { + self.stop(); + return Err(Error::Nack); + } + } + } + } + + match op { + Operation::Read(buffer) => { + for b in buffer.iter_mut() { + *b = self.read_byte(); + self.set_ack(true); + } + } + Operation::Write(buffer) => { + for b in buffer.iter() { + self.write_byte(*b); + if !self.get_ack() { + self.stop(); + return Err(Error::Nack); + } + } + } + } + + last_operation_was_read = Some(op_is_read); + } + + self.stop(); + } + Ok(()) + } +} diff --git a/firmware/src/logging.rs b/firmware/src/logging.rs index 81c3d4e..34c9325 100644 --- a/firmware/src/logging.rs +++ b/firmware/src/logging.rs @@ -24,7 +24,7 @@ unsafe impl defmt::Logger for DefmtLogger { unsafe fn write(bytes: &[u8]) { static mut UART: Option = None; if UART.is_none() { - UART = Some(LitexUart::new(0xf000_4000)); + UART = Some(LitexUart::new(0xf000_4800)); } let mut dev = UART.unwrap(); diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 22eb0af..aa0369c 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -6,9 +6,7 @@ extern crate panic_halt; use core::fmt::Write; -use core::{ - ptr::{read_volatile, write_volatile}, -}; +use core::ptr::{read_volatile, write_volatile}; use embedded_hal::prelude::{_embedded_hal_blocking_i2c_Read, _embedded_hal_blocking_i2c_Write}; use mcp4726::Status; @@ -30,9 +28,9 @@ mod i2c; mod logging; mod mcp4726; mod proto; -mod uart; mod sampler; mod timer; +mod uart; use timer::millis; @@ -115,6 +113,9 @@ fn main() -> ! { let mut cmd = command_interface::CommandInterface::new(); + let mut i2c = i2c::LitexI2c::new(i2c::BASE); + let mut dac = mcp4726::MCP4726::new(3); + loop { let now = millis(); iface.poll(Instant::from_millis(now), &mut device, &mut socket_set); @@ -123,7 +124,7 @@ fn main() -> ! { cmd.run(socket_set.get_mut(command_socket)); // TODO the need for the second check screams something is unsound somewhere - if now - last_blink > 1000 && now > last_blink { + if now - last_blink >= 1000 && now > last_blink { last_blink = now; toggle = !toggle; write_led(if toggle { 1 } else { 0 }); @@ -133,31 +134,59 @@ fn main() -> ! { sock.listen(3000); } - if toggle { - sampler::clear_buffers(); - sampler::start_sampling(); + defmt::debug!("heartbeat, time: {}", millis()); - let buf = unsafe {sampler::get_sample_buffer(0) }; - let status = sampler::read_status(); - let raw_reg: u32 = unsafe { read_reg(0x8040_0004) }; - defmt::debug!("Start: len: {}, complete: {}, running: {}, status: {}", buf.len(), status.capture_complete, status.sampling, raw_reg); - } else { - sampler::stop_sampling(); - let buf = unsafe {sampler::get_sample_buffer(0) }; - defmt::debug!("Stopped, len: {}", buf.len()); + match dac.write_config( + &mut i2c, + mcp4726::Config { + vref_source: mcp4726::VRef::UnbufferedVRef, + operation: mcp4726::PowerDown::NormalOperation, + use_2x_gain: false, + }, + ) { + Ok(_) => defmt::debug!("Write config success"), + Err(_) => defmt::debug!("Write config failed"), } + + //match dac.read_status(&mut i2c) { + // Ok(status) => { + // defmt::debug!( + // "DAC status: ready: {}, powered: {}", + // status.ready, + // status.device_powered + // ); + + // if sock.can_send() { + // write!(sock, "DAC: ready: {}, powered: {}", status.ready, status.device_powered); + // } + // } + // Err(_) => defmt::debug!("Error reading from DAC"), + //}; + + //if toggle { + // sampler::clear_buffers(); + // sampler::start_sampling(); + + // let buf = unsafe {sampler::get_sample_buffer(0) }; + // let status = sampler::read_status(); + // let raw_reg: u32 = unsafe { read_reg(0x8040_0004) }; + // defmt::debug!("Start: len: {}, complete: {}, running: {}, status: {}", buf.len(), status.capture_complete, status.sampling, raw_reg); + //} else { + // sampler::stop_sampling(); + // let buf = unsafe {sampler::get_sample_buffer(0) }; + // defmt::debug!("Stopped, len: {}", buf.len()); + //} } // TODO I think the timer might actually stop until the event is cleared? this may pose // problems, might explain why moving this above the smoltcp stuff "broke" things timer::poll(); - } } fn write_led(val: u32) { unsafe { - write_reg(0xf000_2000, val); + write_reg(0xf000_2800, val); } } diff --git a/firmware/src/sampler.rs b/firmware/src/sampler.rs index 1e21c9f..ef6b7ee 100644 --- a/firmware/src/sampler.rs +++ b/firmware/src/sampler.rs @@ -1,6 +1,6 @@ //! Driver for custom sampler peripheral -use crate::{write_reg, read_reg}; +use crate::{read_reg, write_reg}; const BASE: u32 = 0x8040_0000; @@ -61,4 +61,4 @@ pub unsafe fn get_sample_buffer(sampler: u8) -> &'static [u32] { let addr = BASE + SAMPLE_MEM_LEN * (sampler as u32 + 1); core::slice::from_raw_parts(addr as *const u32, len as usize) -} \ No newline at end of file +} diff --git a/firmware/src/timer.rs b/firmware/src/timer.rs index 951a467..a06fd4f 100644 --- a/firmware/src/timer.rs +++ b/firmware/src/timer.rs @@ -3,15 +3,17 @@ use crate::{read_reg, write_reg}; use core::arch::asm; +const BASE: u32 = 0xf000_4000; + /// Initializes the timer to use for polling events as a wall clock. /// /// Timer is running at system frequency (60MHz) pub fn init() { unsafe { - write_reg(0xf000_3808, 0u32); // Disable timer - write_reg(0xf000_3800, 0u32); // Set LOAD value - write_reg(0xf000_3804, 60_000_000u32); // Set RELOAD value - write_reg(0xf000_3808, 1u32); // Enable timer + write_reg(BASE + 0x08, 0u32); // Disable timer + write_reg(BASE + 0x00, 0u32); // Set LOAD value + write_reg(BASE + 0x04, 60_000_000u32); // Set RELOAD value + write_reg(BASE + 0x08, 1u32); // Enable timer } } @@ -21,13 +23,13 @@ static mut SECONDS: u32 = 0; /// Processes potential timer events. Must be polled regularly pub fn poll() { unsafe { - if read_reg::(0xf000_3818) == 0 { + if read_reg::(BASE + 0x18) == 0 { // No event yet, continue return; } // Clear TIMER0 event status, and update time - write_reg(0xf000_3818, 1u32); + write_reg(BASE + 0x18, 1u32); SECONDS += 1; } } @@ -37,9 +39,9 @@ pub fn millis() -> u32 { riscv::interrupt::free(|| { unsafe { // Latch timer value - write_reg(0xf000_380c, 1u32); + write_reg(BASE + 0x0C, 1u32); // Read timer value - let val: u32 = read_reg(0xf000_3810); + let val: u32 = read_reg(BASE + 0x10); let val = 60_000_000 - val; (SECONDS * 1000) + val / 60_000 } @@ -51,9 +53,9 @@ pub fn micros() -> u64 { riscv::interrupt::free(|| { unsafe { // Latch timer value - write_reg(0xf000_380c, 1u32); + write_reg(BASE + 0x0C, 1u32); // Read timer value - let val: u32 = read_reg(0xf000_3810); + let val: u32 = read_reg(BASE + 0x10); let val = 60_000_000 - val; (SECONDS as u64 * 1_000_000) + (val as u64 / 60) } @@ -71,7 +73,7 @@ pub fn busy_wait_ms(ms: u32) { pub fn busy_wait_us(us: u64) { let start = micros(); - while micros() - start < us { + while micros() - start < us { unsafe { asm!("nop"); }