From 9b48a585cf65e8c30f4cb4b453e40746481b0fe5 Mon Sep 17 00:00:00 2001 From: Harry Ho Date: Tue, 16 Jun 2020 17:34:16 +0800 Subject: [PATCH] Add packet RX --- Cargo.toml | 4 +- src/lib.rs | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/rx.rs | 125 ++++++++++++++++++++++++++++++++++++++++++++ src/spi.rs | 121 ++++++++++++++++++++++++++++++++++-------- 4 files changed, 376 insertions(+), 25 deletions(-) create mode 100644 src/rx.rs diff --git a/Cargo.toml b/Cargo.toml index 581538d..c734370 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,10 @@ volatile-register = "0.2" aligned = "0.3" stm32f4xx-hal = { version = "0.8" , optional = true } smoltcp = { version = "0.6.0", default-features = false, features = ["proto-ipv4", "proto-ipv6", "socket-icmp", "socket-udp", "socket-tcp", "log", "verbose", "ethernet"], optional = true } -log = { version = "0.4", optional = true } +log = { version = "0.4" } [features] -smoltcp-phy = ["smoltcp", "log"] +smoltcp-phy = ["smoltcp"] stm32f407 = ["stm32f4xx-hal/stm32f407"] default = [] diff --git a/src/lib.rs b/src/lib.rs index 544911f..132499a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,155 @@ #![no_std] +use core::fmt; + /// STM32F4xx-HAL specific implementations pub mod spi; +use stm32f4xx_hal::{ + hal::{ + blocking::spi::Transfer, + digital::v2::OutputPin, + } +}; + +pub mod rx; + +#[cfg(feature="smoltcp")] +pub mod smoltcp_phy; + +pub trait EthController { + fn init_dev(&mut self) -> Result<(), EthControllerError>; + fn init_rxbuf(&mut self) -> Result<(), EthControllerError>; + fn receive_next(&mut self) -> Result; + fn set_promiscuous(&mut self) -> Result<(), EthControllerError>; + fn read_from_mac(&mut self, mac: &mut [u8]) -> Result<(), EthControllerError>; +} + +/// TODO: Improve these error types +pub enum EthControllerError { + SpiPortError, + GeneralError +} + +impl From for EthControllerError { + fn from(e: spi::SpiPortError) -> EthControllerError { + EthControllerError::SpiPortError + } +} + +/// Ethernet controller using SPI interface +pub struct SpiEth, + NSS: OutputPin> { + spi_port: spi::SpiPort, + rx_buf: rx::RxBuffer +} + +impl , + NSS: OutputPin> SpiEth { + pub fn new(spi: SPI, mut nss: NSS) -> Self { + SpiEth { + spi_port: spi::SpiPort::new(spi, nss), + rx_buf: rx::RxBuffer::new(), + // TODO: tx_buf + } + } +} + +impl , + NSS: OutputPin> EthController for SpiEth { + fn init_dev(&mut self) -> Result<(), EthControllerError> { + // Write 0x1234 to EUDAST + self.spi_port.write_reg_16b(spi::EUDAST, 0x1234)?; + // Verify that EUDAST is 0x1234 + let mut eudast = self.spi_port.read_reg_16b(spi::EUDAST)?; + if eudast != 0x1234 { + return Err(EthControllerError::GeneralError) + } + // Poll CLKRDY (ESTAT<12>) to check if it is set + loop { + let estat = self.spi_port.read_reg_16b(spi::ESTAT)?; + if estat & 0x1000 == 0x1000 { break } + } + // Set ETHRST (ECON2<4>) to 1 + let econ2 = self.spi_port.read_reg_8b(spi::ECON2)?; + self.spi_port.write_reg_8b(spi::ECON2, 0x10 | (econ2 & 0b11101111))?; + // Verify that EUDAST is 0x0000 + eudast = self.spi_port.read_reg_16b(spi::EUDAST)?; + if eudast != 0x0000 { + return Err(EthControllerError::GeneralError) + } + Ok(()) + } + + fn init_rxbuf(&mut self) -> Result<(), EthControllerError> { + // Set ERXST pointer + self.spi_port.write_reg_16b(spi::ERXST, self.rx_buf.get_wrap_addr()); + // Set ERXTAIL pointer + self.spi_port.write_reg_16b(spi::ERXTAIL, self.rx_buf.get_tail_addr()); + // Set MAMXFL to maximum number of bytes in each accepted packet + self.spi_port.write_reg_16b(spi::MAMXFL, rx::RAW_FRAME_LENGTH_MAX as u16); + // Enable RXEN (ECON1<0>) + let econ1 = self.spi_port.read_reg_16b(spi::ECON1)?; + self.spi_port.write_reg_16b(spi::ECON1, 0x1 | (econ1 & 0xfffe)); + Ok(()) + } + + /// Receive the next packet + fn receive_next(&mut self) -> Result { + // Poll PKTIF (EIR<4>) to check if it is set + loop { + let eir = self.spi_port.read_reg_16b(spi::EIR)?; + if eir & 0x40 == 0x40 { break } + } + // Set ERXRDPT pointer to next_addr + self.spi_port.write_reg_16b(spi::ERXRDPT, self.rx_buf.get_next_addr())?; + // Read 2 bytes to update next_addr + let mut next_addr_buf = [0; 3]; + self.spi_port.read_rxdat(&mut next_addr_buf, 2)?; + self.rx_buf.set_next_addr((next_addr_buf[1] as u16) | ((next_addr_buf[2] as u16) << 8)); + // Read 6 bytes to update rsv + let mut rsv_buf = [0; 7]; + self.spi_port.read_rxdat(&mut rsv_buf, 6)?; + // Construct an RxPacket + // TODO: can we directly assign to fields instead of using functions? + let mut rx_packet = rx::RxPacket::new(); + // Get and update frame length + rx_packet.write_to_rsv(&rsv_buf[1..]); + rx_packet.update_frame_length(); + // Read frame bytes + let mut frame_buf = [0; rx::RAW_FRAME_LENGTH_MAX]; + self.spi_port.read_rxdat(&mut frame_buf, rx_packet.get_frame_length() as u32)?; + rx_packet.write_to_frame(&frame_buf[1..]); + // Set ERXTAIL pointer to (next_addr - 2) + if self.rx_buf.get_next_addr() > rx::ERXST_DEFAULT { + self.spi_port.write_reg_16b(spi::ERXTAIL, self.rx_buf.get_next_addr() - 2)?; + } else { + self.spi_port.write_reg_16b(spi::ERXTAIL, rx::RX_MAX_ADDRESS - 1)?; + } + // Set PKTDEC to decrement PKTCNT + let econ1_hi = self.spi_port.read_reg_8b(spi::ECON1 + 1)?; + self.spi_port.write_reg_8b(spi::ECON1 + 1, 0x01 | (econ1_hi & 0xfe))?; + // Return the RxPacket + Ok(rx_packet) + } + + /// Set controller to Promiscuous Mode + fn set_promiscuous(&mut self) -> Result<(), EthControllerError> { + // From ENC424J600 Data Sheet Section 10.12: + // "To accept all incoming frames regardless of content (Promiscuous mode), + // set the CRCEN, RUNTEN, UCEN, NOTMEEN and MCEN bits." + let mut erxfcon_lo = self.spi_port.read_reg_8b(spi::ERXFCON)?; + self.spi_port.write_reg_8b(spi::ERXFCON, 0b0101_1110 | (erxfcon_lo & 0b1010_0001)); + Ok(()) + } + + /// Read MAC to [u8; 6] + fn read_from_mac(&mut self, mac: &mut [u8]) -> Result<(), EthControllerError> { + mac[0] = self.spi_port.read_reg_8b(spi::MAADR1)?; + mac[1] = self.spi_port.read_reg_8b(spi::MAADR1 + 1)?; + mac[2] = self.spi_port.read_reg_8b(spi::MAADR2)?; + mac[3] = self.spi_port.read_reg_8b(spi::MAADR2 + 1)?; + mac[4] = self.spi_port.read_reg_8b(spi::MAADR3)?; + mac[5] = self.spi_port.read_reg_8b(spi::MAADR3 + 1)?; + Ok(()) + } +} diff --git a/src/rx.rs b/src/rx.rs new file mode 100644 index 0000000..f5a4a72 --- /dev/null +++ b/src/rx.rs @@ -0,0 +1,125 @@ +/// SRAM Addresses +pub const ERXST_DEFAULT: u16 = 0x5340; +pub const ERXTAIL_DEFAULT: u16 = 0x5ffe; +pub const RX_MAX_ADDRESS: u16 = 0x5fff; + +/// Max raw frame array size +pub const RAW_FRAME_LENGTH_MAX: usize = 0x1000; +/// Receive Status Vector Length +pub const RSV_LENGTH: usize = 6; + +/// Struct for RX Buffer +/// TODO: Should be a singleton +pub struct RxBuffer { + wrap_addr: u16, + next_addr: u16, + tail_addr: u16 +} + +impl RxBuffer { + pub fn new() -> Self { + RxBuffer { + wrap_addr: ERXST_DEFAULT, + next_addr: ERXST_DEFAULT, + tail_addr: ERXTAIL_DEFAULT + } + } + + pub fn set_wrap_addr(&mut self, addr: u16) { + self.wrap_addr = addr; + } + pub fn get_wrap_addr(& self) -> u16{ + self.wrap_addr + } + + pub fn set_next_addr(&mut self, addr: u16) { + self.next_addr = addr; + } + pub fn get_next_addr(& self) -> u16{ + self.next_addr + } + + pub fn set_tail_addr(&mut self, addr: u16) { + self.tail_addr = addr; + } + pub fn get_tail_addr(& self) -> u16{ + self.tail_addr + } +} + +/// Struct for RX Packet +/// TODO: Generalise MAC addresses +pub struct RxPacket { + rsv: Rsv, + frame: [u8; RAW_FRAME_LENGTH_MAX], + frame_length: usize +} + +impl RxPacket { + pub fn new() -> Self { + RxPacket { + rsv: Rsv::new(), + frame: [0; RAW_FRAME_LENGTH_MAX], + frame_length: 0 + } + } + + pub fn write_to_rsv(&mut self, raw_rsv: &[u8]) { + self.rsv.write_to_rsv(raw_rsv); + } + pub fn read_raw_rsv(&self) -> &[u8] { + self.rsv.read_raw_rsv() + } + + pub fn update_frame_length(&mut self) { + self.rsv.set_frame_length(); + self.frame_length = self.rsv.get_frame_length() as usize; + } + + pub fn get_frame_length(&self) -> usize { + self.frame_length + } + + pub fn write_to_frame(&mut self, raw_frame: &[u8]) { + for i in 0..self.frame_length { + self.frame[i] = raw_frame[i]; + } + } + + pub fn get_frame_byte(&self, i: usize) -> u8 { + self.frame[i] + } +} + +/// Struct for Receive Status Vector +/// See: Table 9-1, ENC424J600 Data Sheet +struct Rsv { + raw_rsv: [u8; RSV_LENGTH], + // TODO: Add more definitions + frame_length: u16 +} + +impl Rsv { + fn new() -> Self { + Rsv { + raw_rsv: [0; RSV_LENGTH], + frame_length: 0_u16 + } + } + + fn write_to_rsv(&mut self, raw_rsv: &[u8]) { + for i in 0..RSV_LENGTH { + self.raw_rsv[i] = raw_rsv[i]; + } + } + fn read_raw_rsv(&self) -> &[u8] { + &self.raw_rsv + } + + fn set_frame_length(&mut self) { + self.frame_length = (self.raw_rsv[0] as u16) | ((self.raw_rsv[1] as u16) << 8); + } + fn get_frame_length(&self) -> u16 { + self.frame_length + } +} \ No newline at end of file diff --git a/src/spi.rs b/src/spi.rs index 4977290..8b6ebfd 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -7,6 +7,10 @@ use stm32f4xx_hal::{ time::MegaHertz, spi, }; +/// +/// FIXME: Move the following to somewhere else +/// +use crate::rx; /// Must use SPI mode cpol=0, cpha=0 pub const SPI_MODE: spi::Mode = spi::Mode { @@ -17,8 +21,29 @@ pub const SPI_MODE: spi::Mode = spi::Mode { pub const SPI_CLOCK: MegaHertz = MegaHertz(14); /// SPI Opcodes -const RCRU: u8 = 0b00100000; -const WCRU: u8 = 0b00100010; +const RCRU: u8 = 0b0010_0000; +const WCRU: u8 = 0b0010_0010; +const ERXDATA: u8 = 0b0010_1100; // Treated as 8-bit opcode followed by data + +/// SPI Register Mapping +/// Note: PSP interface use different address mapping +// SPI Init Reset Registers +pub const EUDAST: u8 = 0x16; // 16-bit data +pub const ESTAT: u8 = 0x1a; // 16-bit data +pub const ECON2: u8 = 0x6e; // 16-bit data +// +pub const ERXFCON: u8 = 0x34; // 16-bit data +// +pub const MAADR3: u8 = 0x60; // 16-bit data +pub const MAADR2: u8 = 0x62; // 16-bit data +pub const MAADR1: u8 = 0x64; // 16-bit data +// RX Registers +pub const ERXRDPT: u8 = 0x8a; // 16-bit data +pub const ERXST: u8 = 0x04; // 16-bit data +pub const ERXTAIL: u8 = 0x06; // 16-bit data +pub const EIR: u8 = 0x1c; // 16-bit data +pub const ECON1: u8 = 0x1e; // 16-bit data +pub const MAMXFL: u8 = 0x4a; // 16-bit data /// Struct for SPI I/O interface on ENC424J600 /// Note: stm32f4xx_hal::spi's pins include: SCK, MISO, MOSI @@ -28,42 +53,60 @@ pub struct SpiPort, nss: NSS, } -impl , - NSS: OutputPin, - E: fmt::Debug> SpiPort { +pub enum SpiPortError { + TransferError +} + +impl , + NSS: OutputPin> SpiPort { // TODO: return as Result() pub fn new(spi: SPI, mut nss: NSS) -> Self { nss.set_high(); - + SpiPort { spi, nss } } - pub fn read_reg_8b(&mut self, addr: u8) -> Result { + pub fn read_reg_8b(&mut self, addr: u8) -> Result { // Using RCRU instruction to read using unbanked (full) address - let mut r_data = self.transfer(RCRU, addr, 0)?; + let mut r_data = self.rw_addr_u8(RCRU, addr, 0)?; Ok(r_data) } - pub fn read_reg_16b(&mut self, lo_addr: u8) -> Result { + pub fn read_reg_16b(&mut self, lo_addr: u8) -> Result { let mut r_data_lo = self.read_reg_8b(lo_addr)?; let mut r_data_hi = self.read_reg_8b(lo_addr + 1)?; // Combine top and bottom 8-bit to return 16-bit Ok(((r_data_hi as u16) << 8) | r_data_lo as u16) } - pub fn write_reg_8b(&mut self, addr: u8, data: u8) -> Result { - // TODO: addr should be separated from w_data - // Using WCRU instruction to write using unbanked (full) address - self.transfer(WCRU, addr, data)?; - Ok(0x01) // TODO: should not be just 0x01 + // Currently requires manual slicing (buf[1:]) for the data read back + pub fn read_rxdat<'a>(&mut self, buf: &'a mut [u8], data_length: u32) + -> Result { + let r_valid = self.r_n(buf, ERXDATA, data_length)?; + Ok(r_valid) } - fn transfer(&mut self, opcode: u8, addr: u8, data: u8) - -> Result { - // TODO: Currently assumes read/write data is only 1-byte + pub fn write_reg_8b(&mut self, addr: u8, data: u8) -> Result<(), SpiPortError> { + // TODO: addr should be separated from w_data + // Using WCRU instruction to write using unbanked (full) address + self.rw_addr_u8(WCRU, addr, data)?; + Ok(()) + } + + pub fn write_reg_16b(&mut self, lo_addr: u8, data: u16) -> Result<(), SpiPortError> { + self.write_reg_8b(lo_addr, (data & 0xff) as u8)?; + self.write_reg_8b(lo_addr + 1, ((data & 0xff00) >> 8) as u8)?; + Ok(()) + } + + // TODO: Generalise transfer functions + // TODO: (Make data read/write as reference to array) + // Currently requires 1-byte addr, read/write data is only 1-byte + fn rw_addr_u8(&mut self, opcode: u8, addr: u8, data: u8) + -> Result { // Enable chip select self.nss.set_low(); // Start writing to SLAVE @@ -72,13 +115,45 @@ impl , buf[0] = opcode; buf[1] = addr; buf[2] = data; - let result = self.spi.transfer(&mut buf); - // Disable chip select - self.nss.set_high(); + match self.spi.transfer(&mut buf) { + Ok(_) => { + // Disable chip select + self.nss.set_high(); + Ok(buf[2]) + }, + // TODO: Maybe too naive? + Err(e) => { + // Disable chip select + self.nss.set_high(); + Err(SpiPortError::TransferError) + } + } + } - match result { - Ok(_) => Ok(buf[2]), - Err(e) => Err(e), + // TODO: Generalise transfer functions + // Currently does NOT accept addr, read data is N-byte long + // Returns a reference to the data returned + // Note: buf must be at least (data_length + 1)-byte long + // TODO: Check and raise error for array size < (data_length + 1) + fn r_n<'a>(&mut self, buf: &'a mut [u8], opcode: u8, data_length: u32) + -> Result { + // Enable chip select + self.nss.set_low(); + // Start writing to SLAVE + buf[0] = opcode; + match self.spi.transfer(buf) { + // TODO: Now returns a boolean, maybe use Option later on? + Ok(_) => { + // Disable chip select + self.nss.set_high(); + Ok(1) + }, + // TODO: Maybe too naive? + Err(e) => { + // Disable chip select + self.nss.set_high(); + Err(SpiPortError::TransferError) + } } } } \ No newline at end of file