fw: mostly implement smoltcp driver? unsure if it works

This commit is contained in:
David Lenfesty 2023-04-15 17:06:59 -06:00
parent d207181ef5
commit 1204be4fa0
9 changed files with 423 additions and 131 deletions

121
firmware/Cargo.lock generated
View File

@ -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"

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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::<u32>(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<Self> {
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::<u32>(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::<u32>(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<Self::TxToken<'_>> {
// 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::<u32>(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<R, F>(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<R, F>(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::<u32>(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
}
}

View File

@ -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 }
}

View File

@ -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<T>(addr: u32, value: T) {
@ -91,3 +109,7 @@ unsafe fn write_reg<T>(addr: u32, value: T) {
unsafe fn read_reg<T>(addr: u32) -> T {
return read_volatile(addr as *mut T);
}
fn millis() -> u32 {
unsafe { read_reg(0x01300000) }
}

View File

@ -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<Error>(&mut self, i2c: &mut dyn Write<Error = Error>, config: Config) -> Result<(), Error> {
pub fn write_config<Error>(
&mut self,
i2c: &mut dyn Write<Error = Error>,
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<Error>(&self, i2c: &mut dyn Write<Error = Error>, dac: u16) -> Result<(), Error> {
pub fn write_dac<Error>(
&self,
i2c: &mut dyn Write<Error = Error>,
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<Error>(&mut self, i2c: &mut dyn Write<Error = Error>, config: Config, dac: u16) -> Result<(), Error> {
pub fn write_nonvolatile<Error>(
&mut self,
i2c: &mut dyn Write<Error = Error>,
config: Config,
dac: u16,
) -> Result<(), Error> {
todo!()
}
/// Reads all memory, up to 6 bytes into the out buffer.
fn read_all_mem<Error>(&self, i2c: &mut dyn Read<Error = Error>, out: &mut [u8]) -> Result<u8, Error> {
let buf = if out.len() <= 6 {
out
} else {
&mut out[0..6]
};
fn read_all_mem<Error>(
&self,
i2c: &mut dyn Read<Error = Error>,
out: &mut [u8],
) -> Result<u8, Error> {
let buf = if out.len() <= 6 { out } else { &mut out[0..6] };
i2c.read(self.address, buf)?;
Ok(buf.len() as u8)

View File

@ -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<u8, Error> {
@ -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