diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index 5ad3d17..547b46e 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -11,12 +11,45 @@ dependencies = [ "memchr", ] +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bit_field" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "critical-section" version = "1.1.1" @@ -40,6 +73,29 @@ dependencies = [ "embedded-hal", "panic-halt", "riscv-rt", + "smoltcp", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "spin", + "stable_deref_trait", ] [[package]] @@ -48,6 +104,22 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "memchr" version = "2.5.0" @@ -160,6 +232,55 @@ dependencies = [ "regex", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "smoltcp" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9786ac45091b96f946693e05bfa4d8ca93e2d3341237d97a380107a6b38dea" +dependencies = [ + "bitflags", + "byteorder", + "cfg-if", + "heapless", + "managed", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "syn" version = "1.0.107" diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index edde5e4..039e5b2 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -10,5 +10,10 @@ riscv-rt = "0.11.0" panic-halt = "0.2.0" embedded-hal = "0.2.7" +[dependencies.smoltcp] +version = "0.9.1" +default-features = false +features = ["medium-ethernet", "socket-icmp", "proto-ipv4"] + [profile.release] debug = true diff --git a/firmware/hello_world_c/memory.ld b/firmware/hello_world_c/memory.ld index 6b6b310..02246a6 100644 --- a/firmware/hello_world_c/memory.ld +++ b/firmware/hello_world_c/memory.ld @@ -2,8 +2,8 @@ MEMORY { - ROM (rx) : ORIGIN = 0x01000000, LENGTH = 0x1000 /* 4KiB */ - RAM (xrw) : ORIGIN = 0x01001000, LENGTH = 0x1000 /* 4KiB */ + ROM (rx) : ORIGIN = 0x01000000, LENGTH = 0x4000 /* 16KiB */ + RAM (xrw) : ORIGIN = 0x01100000, LENGTH = 0x1000 /* 4KiB */ } SECTIONS { diff --git a/firmware/memory.x b/firmware/memory.x index aae3d83..98ea264 100644 --- a/firmware/memory.x +++ b/firmware/memory.x @@ -1,7 +1,7 @@ MEMORY { - RAM : ORIGIN = 0x01004000, LENGTH = 4K - FLASH : ORIGIN = 0x01000000, LENGTH = 12K + RAM : ORIGIN = 0x01100000, LENGTH = 4K + FLASH : ORIGIN = 0x01000000, LENGTH = 64K } REGION_ALIAS("REGION_TEXT", FLASH); diff --git a/firmware/src/eth.rs b/firmware/src/eth.rs index a0b0247..a4ceca1 100644 --- a/firmware/src/eth.rs +++ b/firmware/src/eth.rs @@ -1,70 +1,211 @@ -//! Quick and hacky ethernet thing to test +//! Smoltcp ethernet driver for LiteETH instance. +//! +//! Much of the code and implementation ideas are stolen from https://docs.tockos.org/src/litex/liteeth.rs.html#1-307 + +// God I hate how there's 0 documentation for LiteEth. No explanation of all the +// registers or anything, just here's a bunch of things, you've got to look at +// the gateware to figure out how it works. + +// Some notes I'm not too sure where to put: +// - The SRAM writer (i.e. RX DMA) must have the event cleared to mark the slot as ready +// - Do the event registers change with the selected slot? I don't think so. So how +// are you supposed to know which RX slot to use? In this case I only use one, so I'm +// not going to care, but it's not obvious. +// - Slots are sized to ethernet MTU (1530), and addressed by the closest log2 +// thing, so 2048 bytes each const LITEETH_BASE: u32 = 0x0300_0000; -const ETHMAC_SRAM_WRITER_EV_PENDING: u32 = LITEETH_BASE + 0x810; -const ETHMAC_SRAM_WRITER_EV_ENABLE: u32 = LITEETH_BASE + 0x814; -const ETHMAC_SRAM_READER_EV_PENDING: u32 = LITEETH_BASE + 0x830; -const ETHMAC_SRAM_READER_EV_ENABLE: u32 = LITEETH_BASE + 0x834; +const CTRL_RESET: u32 = 0x000; +const CTRL_SCRATCH: u32 = 0x004; -// OMFG, READER is TX, WRITER is RX -const ETHMAC_SRAM_READER_SLOT: u32 = LITEETH_BASE + 0x824; -const ETHMAC_SRAM_READER_LENGTH: u32 = LITEETH_BASE + 0x828; -const ETHMAC_SRAM_READER_START: u32 = LITEETH_BASE + 0x818; -const ETHMAC_SRAM_READER_READY: u32 = LITEETH_BASE + 0x81c; +// Writer, or RX register blocks +const ETHMAC_SRAM_WRITER_SLOT: u32 = 0x800; +const ETHMAC_SRAM_WRITER_LENGTH: u32 = 0x804; +const ETHMAC_SRAM_WRITER_EV_STATUS: u32 = 0x80c; +const ETHMAC_SRAM_WRITER_EV_PENDING: u32 = 0x810; +const ETHMAC_SRAM_WRITER_EV_ENABLE: u32 = 0x814; -use crate::{write_reg, read_reg}; +// Reader, or TX register blocks +const ETHMAC_SRAM_READER_START: u32 = 0x818; +const ETHMAC_SRAM_READER_READY: u32 = 0x81c; +const ETHMAC_SRAM_READER_SLOT: u32 = 0x824; +const ETHMAC_SRAM_READER_LENGTH: u32 = 0x828; +const ETHMAC_SRAM_READER_EV_STATUS: u32 = 0x82c; +const ETHMAC_SRAM_READER_EV_PENDING: u32 = 0x830; +const ETHMAC_SRAM_READER_EV_ENABLE: u32 = 0x834; -pub unsafe fn is_wishbone_correct() -> bool { - let value: u32 = read_reg(LITEETH_BASE + 4); +const NUM_RX_SLOTS: u32 = 2; +const NUM_TX_SLOTS: u32 = 2; +const MTU: usize = 1530; - // If this isn't true, we screwed. - return value == 0x12345678; +use crate::{busy_wait, read_reg, write_reg}; + +pub struct LiteEthDevice { + base_addr: u32, } -pub unsafe fn init() { - // Clear any potential pending events - write_reg(ETHMAC_SRAM_WRITER_EV_PENDING, 1u32); - write_reg(ETHMAC_SRAM_READER_EV_PENDING, 1u32); - - // Disable all events - write_reg(ETHMAC_SRAM_WRITER_EV_ENABLE, 0u32); - write_reg(ETHMAC_SRAM_READER_EV_ENABLE, 0u32); +pub struct LiteEthTxToken { + pub base_addr: u32, + pub slot: u32, } -// a8:a1:59:32:a7:a5 -const ares_mac: [u8; 6] = [0xA8, 0xA1, 0x59, 0x32, 0xA7, 0xA5]; -const fake_mac: [u8; 6] = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05]; - -/// Just make an ethernet frame and yeet it -pub unsafe fn tranmsit() { - // Preamble/start delimiter/crc are all handled for us by the MAC - let frame: [u8; 18] = [ - // TODO endianness of MAC addresses? - 0xA8, 0xA1, 0x59, 0x32, 0xA7, 0xA5, // Destination MAC (ares) - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, // Source MAC (fake) - 0x08, 0x00, // Ethertype frame - 0xDE, 0xAD, 0xBE, 0xEF, // Data! - ]; - - // buffer_depth = 2048 - // it goes, base, RX slot 0, RX slot ..., Tx slot 0, tx slot ..., - - const TX_SLOT_LOC: u32 = LITEETH_BASE + 1 * 2048; - let tx_slot: &mut [u8] = unsafe { - core::slice::from_raw_parts_mut(TX_SLOT_LOC as *mut u8, 2048) - }; - - // Copy our frame into the slot buffer - tx_slot[..18].copy_from_slice(&frame); - - // Set slot and packet length - write_reg(ETHMAC_SRAM_READER_SLOT, 0u32); - write_reg(ETHMAC_SRAM_READER_LENGTH, 18u32); - - // Wait to be ready - while read_reg::(ETHMAC_SRAM_READER_READY) == 0 {} - - // Write! - write_reg(ETHMAC_SRAM_READER_START, 1u32); +pub struct LiteEthRxToken { + pub base_addr: u32, +} + +impl LiteEthDevice { + /// Initialises the device and returns an instance. Unsafe because there are + /// no checks for other users. + pub unsafe fn try_init(base_addr: u32) -> Option { + if !LiteEthDevice::check_wishbone_access(base_addr) { + return None; + } + // Reset liteeth + write_reg(base_addr + CTRL_RESET, 1u32); + busy_wait(200); + write_reg(base_addr + CTRL_RESET, 0u32); + busy_wait(200); + + // Set up RX slot 0 + write_reg(base_addr + ETHMAC_SRAM_WRITER_SLOT, 0); + // Clear to mark the slot as available + write_reg(base_addr + ETHMAC_SRAM_WRITER_EV_PENDING, 1u32); + + // Clear TX event (unsure if necessary) + write_reg(base_addr + ETHMAC_SRAM_READER_EV_PENDING, 1u32); + + // Disable event interrupts, we poll, so no use for an interrupt + write_reg(base_addr + ETHMAC_SRAM_READER_EV_ENABLE, 0u32); + write_reg(base_addr + ETHMAC_SRAM_WRITER_EV_ENABLE, 0u32); + + // Return a new device + Some(Self { base_addr }) + } + + /// Checks that wishbone memory access is correct for the given base address + unsafe fn check_wishbone_access(base_addr: u32) -> bool { + // Read scratch register, which resets to 0x12345678 + let value: u32 = read_reg(base_addr + CTRL_SCRATCH); + + // If this isn't true, we screwed. + return value == 0x12345678; + } +} + +impl smoltcp::phy::Device for LiteEthDevice { + type RxToken<'a> = LiteEthRxToken; + type TxToken<'a> = LiteEthTxToken; + + fn receive( + &mut self, + _timestamp: smoltcp::time::Instant, + ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + // Check if available, if so , return a RxToken + a TxToken to slot 1 + + unsafe { + if read_reg::(self.base_addr + ETHMAC_SRAM_WRITER_EV_STATUS) == 0 { + // No data is available in writer slot 0 + return None; + } + + // Check if TX slot 1 is available for the "return" packet + write_reg(self.base_addr + ETHMAC_SRAM_READER_SLOT, 1u32); + if read_reg::(self.base_addr + ETHMAC_SRAM_READER_READY) != 1 { + return None; + } + + // We have data, and TX slot 1 is ready for something to be potentially transmitted, + // so we can return valid tokens + Some(( + LiteEthRxToken { + base_addr: self.base_addr, + }, + LiteEthTxToken { + base_addr: self.base_addr, + slot: 1, + }, + )) + } + } + + fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { + // Check if slot 0 is ready, if so, return TxToken to slot 0 + unsafe { + write_reg(self.base_addr + ETHMAC_SRAM_READER_SLOT, 0u32); + if read_reg::(self.base_addr + ETHMAC_SRAM_READER_READY) == 0 { + return None; + } + } + + Some(LiteEthTxToken { + base_addr: self.base_addr, + slot: 0, + }) + } + + fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { + use smoltcp::phy::*; + let mut caps = DeviceCapabilities::default(); + caps.medium = Medium::Ethernet; + caps.max_transmission_unit = MTU; + caps.max_burst_size = Some(MTU); + + caps + } +} + +impl smoltcp::phy::TxToken for LiteEthTxToken { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let tx_slot_base: u32 = self.base_addr + NUM_RX_SLOTS * 2048; + let tx_slot_addr = tx_slot_base + (self.slot as u32) * 2048; + let tx_slot: &mut [u8] = + unsafe { core::slice::from_raw_parts_mut(tx_slot_addr as *mut u8, MTU) }; + + // Write data to buffer + let res = f(tx_slot); + + // Write length, and start sending data + unsafe { + // set slot + write_reg(self.base_addr + ETHMAC_SRAM_READER_SLOT, self.slot); + // set length + write_reg(self.base_addr + ETHMAC_SRAM_READER_LENGTH, len as u32); + // send data + write_reg(self.base_addr + ETHMAC_SRAM_READER_START, 1u32); + } + + res + } +} + +impl smoltcp::phy::RxToken for LiteEthRxToken { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let len = unsafe { + // Select slot 0 + write_reg(self.base_addr + ETHMAC_SRAM_WRITER_SLOT, 0u32); + // Read the available length + read_reg::(self.base_addr + ETHMAC_SRAM_READER_LENGTH) + }; + + let rx_slot_addr: u32 = self.base_addr + 2048; + let rx_slot: &mut [u8] = + unsafe { core::slice::from_raw_parts_mut(rx_slot_addr as *mut u8, len as usize) }; + + // Read data from buffer + let res = f(rx_slot); + + // Clear event to mark slot as available + unsafe { + write_reg(self.base_addr + ETHMAC_SRAM_WRITER_EV_PENDING, 1u32); + } + + res + } } diff --git a/firmware/src/i2c.rs b/firmware/src/i2c.rs index f50fb59..e2d7ff8 100644 --- a/firmware/src/i2c.rs +++ b/firmware/src/i2c.rs @@ -2,14 +2,12 @@ //! //! See `gateware/i2c.py` for register information - -/// 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::{Write, Read, SevenBitAddress}; 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}; const CR: u32 = 0; const SR: u32 = 1; @@ -30,7 +28,7 @@ pub struct AmlibI2c { use core::fmt::Write as _; -impl AmlibI2c{ +impl AmlibI2c { pub fn new(base_addr: u32) -> Self { AmlibI2c { base_addr } } diff --git a/firmware/src/main.rs b/firmware/src/main.rs index f59a89b..ece2532 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -3,17 +3,28 @@ extern crate panic_halt; -use core::{arch::asm, ptr::{write_volatile, read_volatile}}; use core::fmt::Write; +use core::{ + arch::asm, + ptr::{read_volatile, write_volatile}, +}; -use embedded_hal::prelude::{_embedded_hal_blocking_i2c_Write, _embedded_hal_blocking_i2c_Read}; +use embedded_hal::prelude::{_embedded_hal_blocking_i2c_Read, _embedded_hal_blocking_i2c_Write}; use mcp4726::Status; use riscv_rt::entry; +use smoltcp::wire::{IpAddress, Ipv4Address}; +use smoltcp::{ + iface::{SocketSet, SocketStorage}, + time::Instant, + wire::HardwareAddress, +}; mod eth; mod i2c; -mod uart; mod mcp4726; +mod uart; + +const mac: [u8; 6] = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]; // use `main` as the entry point of this application // `main` is not allowed to return @@ -29,51 +40,56 @@ fn main() -> ! { // } //}; let blink_period = 10_000_000u32; - let mut uart = uart::AmlibUart::new(0x0200_0040); - // Configure DAC - let mut i2c = i2c::AmlibI2c::new(0x0200_0000); - //let mut buf = [0u8; 1]; - //i2c.read(0b110_0011, &mut buf); + let mut device = unsafe { eth::LiteEthDevice::try_init(0x0300_0000).unwrap() }; - let mut buf = [0u8; 8]; - i2c.read(0x63, &mut buf[0..4]).unwrap(); - writeln!(uart, "DAC Read before config: {:x}, {:x}, {:x}, {:x}", buf[0], buf[1], buf[2], buf[3]).unwrap(); + use smoltcp::wire::{EthernetAddress, HardwareAddress}; + let mut config = smoltcp::iface::Config::default(); + config.hardware_addr = Some(HardwareAddress::Ethernet(EthernetAddress::from_bytes(&mac))); + let mut iface = smoltcp::iface::Interface::new(config, &mut device); + // Set address + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(smoltcp::wire::IpCidr::new( + IpAddress::Ipv4(Ipv4Address::new(192, 168, 88, 69)), + 24, + )) + .unwrap(); + }); - let mut dac = mcp4726::MCP4726::new(3); - writeln!(uart, "Reading DAC status").unwrap(); - match dac.read_status(&mut i2c) { - Ok(status) => writeln!(uart, "Is ready? {}, device powered? {}", status.ready, status.device_powered).unwrap(), - Err(e) => { - writeln!(uart, "Error: {:?}", e).unwrap(); - panic!(); - } - } - writeln!(uart, "Configuring DAC").unwrap(); - dac.write_config(&mut i2c, mcp4726::Config { - vref_source: mcp4726::VRef::UnbufferedVDD, - operation: mcp4726::PowerDown::NormalOperation, - use_2x_gain: false, - }).unwrap(); - writeln!(uart, "Setting DAC").unwrap(); - dac.write_dac(&mut i2c, 0x0800).unwrap(); + iface + .routes_mut() + .add_default_ipv4_route(Ipv4Address::new(192, 168, 88, 1)) + .unwrap(); + + // Create socket set with 4 available + let mut socket_storage = [SocketStorage::EMPTY; 4]; + let mut socket_set = SocketSet::new(&mut socket_storage[..]); + + let mut last_blink: u32 = 0; + let mut toggle = false; + writeln!(uart, "boot").unwrap(); - i2c.read(0x63, &mut buf[0..4]).unwrap(); - writeln!(uart, "DAC Read after config: {:x}, {:x}, {:x}, {:x}", buf[0], buf[1], buf[2], buf[3]).unwrap(); loop { - //eth::tranmsit(); - //writeln!(uart, "Hello world!"); - write_led(0); - busy_wait(blink_period); - write_led(1); - busy_wait(blink_period); + let now = millis(); + if now - last_blink > 1000 { + last_blink = now; + toggle = !toggle; + write_led(if toggle { 1 } else { 0 }); + } + + // TODO timestamp + if iface.poll(Instant::from_millis(now), &mut device, &mut socket_set) { + writeln!(uart, "iface did something").unwrap(); + } } } -fn busy_wait(num_nops: u32) { - for _ in 0..num_nops { +fn busy_wait(ms: u32) { + let start = millis(); + while millis() - start < ms { unsafe { asm!("nop"); } @@ -81,7 +97,9 @@ fn busy_wait(num_nops: u32) { } fn write_led(val: u32) { - unsafe { write_reg(0x01003000, val); } + unsafe { + write_reg(0x01200000, val); + } } unsafe fn write_reg(addr: u32, value: T) { @@ -91,3 +109,7 @@ unsafe fn write_reg(addr: u32, value: T) { unsafe fn read_reg(addr: u32) -> T { return read_volatile(addr as *mut T); } + +fn millis() -> u32 { + unsafe { read_reg(0x01300000) } +} diff --git a/firmware/src/mcp4726.rs b/firmware/src/mcp4726.rs index f52e09b..145b9e1 100644 --- a/firmware/src/mcp4726.rs +++ b/firmware/src/mcp4726.rs @@ -1,6 +1,6 @@ //! Blocking driver for MCP2746 -use embedded_hal::blocking::i2c::{Write, Read}; +use embedded_hal::blocking::i2c::{Read, Write}; const COMMAND_WRITE_VOLATILE_DAC: u8 = 0x00; const COMMAND_WRITE_VOLATILE_MEM: u8 = 0x40; @@ -69,7 +69,11 @@ impl MCP4726 { }) } - pub fn write_config(&mut self, i2c: &mut dyn Write, config: Config) -> Result<(), Error> { + pub fn write_config( + &mut self, + i2c: &mut dyn Write, + config: Config, + ) -> Result<(), Error> { let mut buf = [0u8; 1]; buf[0] |= COMMAND_WRITE_VOLATILE_CONFIG; buf[0] |= MCP4726::vref_config(config.vref_source) << 3; @@ -85,7 +89,11 @@ impl MCP4726 { Ok(()) } - pub fn write_dac(&self, i2c: &mut dyn Write, dac: u16) -> Result<(), Error> { + pub fn write_dac( + &self, + i2c: &mut dyn Write, + dac: u16, + ) -> Result<(), Error> { let mut buf = [0u8; 2]; buf[0] |= COMMAND_WRITE_VOLATILE_DAC; // Note this "overlaps" the command bits, the lowest command bit is not used for this one @@ -98,17 +106,22 @@ impl MCP4726 { Ok(()) } - pub fn write_nonvolatile(&mut self, i2c: &mut dyn Write, config: Config, dac: u16) -> Result<(), Error> { + pub fn write_nonvolatile( + &mut self, + i2c: &mut dyn Write, + config: Config, + dac: u16, + ) -> Result<(), Error> { todo!() } /// Reads all memory, up to 6 bytes into the out buffer. - fn read_all_mem(&self, i2c: &mut dyn Read, out: &mut [u8]) -> Result { - let buf = if out.len() <= 6 { - out - } else { - &mut out[0..6] - }; + fn read_all_mem( + &self, + i2c: &mut dyn Read, + out: &mut [u8], + ) -> Result { + let buf = if out.len() <= 6 { out } else { &mut out[0..6] }; i2c.read(self.address, buf)?; Ok(buf.len() as u8) diff --git a/firmware/src/uart.rs b/firmware/src/uart.rs index ba16c8c..4c9542a 100644 --- a/firmware/src/uart.rs +++ b/firmware/src/uart.rs @@ -1,9 +1,9 @@ //! Quick and dirty uart driver +use core::fmt::Write; /// TODO repr(C) a register bank, and add instances -/// +/// use core::ptr::{read_volatile, write_volatile}; -use core::fmt::{Write}; // TODO these offsets may be wrong. I'm still unsure about CSR semantics const REG_DIVISOR_OFFSET: u32 = 0; @@ -26,16 +26,11 @@ pub struct AmlibUart { impl AmlibUart { pub fn new(base_addr: u32) -> Self { - Self { - base_addr, - } + Self { base_addr } } - pub fn read_status(&mut self) -> u8 { - unsafe { - read_volatile((self.base_addr + REG_SR_OFFSET) as *const u8) - } + unsafe { read_volatile((self.base_addr + REG_SR_OFFSET) as *const u8) } } pub fn try_get_char(&mut self) -> Result { @@ -43,9 +38,7 @@ impl AmlibUart { return Err(Error::RxEmpty); } - unsafe { - Ok(read_volatile((self.base_addr + REG_DR_OFFSET) as *const u8)) - } + unsafe { Ok(read_volatile((self.base_addr + REG_DR_OFFSET) as *const u8)) } } pub fn try_put_char(&mut self, c: u8) -> Result<(), Error> { @@ -58,7 +51,6 @@ impl AmlibUart { } Ok(()) } - } // Blocking implementation of write