fw: mostly implement smoltcp driver? unsure if it works
This commit is contained in:
parent
d207181ef5
commit
1204be4fa0
121
firmware/Cargo.lock
generated
121
firmware/Cargo.lock
generated
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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) }
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user