spi: Introduce certain 1 & 3-byte opcodes to replace reg read/writes

This commit is contained in:
Harry Ho 2021-05-26 17:34:29 +08:00
parent edb1f64f26
commit 27ba42c4fb
2 changed files with 83 additions and 53 deletions

View File

@ -81,9 +81,8 @@ impl <SPI: Transfer<u8>,
let estat = self.spi_port.read_reg_16b(spi::addrs::ESTAT)?; let estat = self.spi_port.read_reg_16b(spi::addrs::ESTAT)?;
if estat & 0x1000 == 0x1000 { break } if estat & 0x1000 == 0x1000 { break }
} }
// Set ETHRST (ECON2<4>) to 1 // Issue system reset - set ETHRST (ECON2<4>) to 1
let econ2 = self.spi_port.read_reg_8b(spi::addrs::ECON2)?; self.spi_port.send_opcode(spi::opcodes::SETETHRST)?;
self.spi_port.write_reg_8b(spi::addrs::ECON2, 0x10 | (econ2 & 0b11101111))?;
self.spi_port.delay_us(25); self.spi_port.delay_us(25);
// Verify that EUDAST is 0x0000 // Verify that EUDAST is 0x0000
eudast = self.spi_port.read_reg_16b(spi::addrs::EUDAST)?; eudast = self.spi_port.read_reg_16b(spi::addrs::EUDAST)?;
@ -101,9 +100,8 @@ impl <SPI: Transfer<u8>,
self.spi_port.write_reg_16b(spi::addrs::ERXTAIL, self.rx_buf.get_tail_addr())?; self.spi_port.write_reg_16b(spi::addrs::ERXTAIL, self.rx_buf.get_tail_addr())?;
// Set MAMXFL to maximum number of bytes in each accepted packet // Set MAMXFL to maximum number of bytes in each accepted packet
self.spi_port.write_reg_16b(spi::addrs::MAMXFL, RAW_FRAME_LENGTH_MAX as u16)?; self.spi_port.write_reg_16b(spi::addrs::MAMXFL, RAW_FRAME_LENGTH_MAX as u16)?;
// Enable RXEN (ECON1<0>) // Enable RX - set RXEN (ECON1<0>) to 1
let econ1 = self.spi_port.read_reg_16b(spi::addrs::ECON1)?; self.spi_port.send_opcode(spi::opcodes::ENABLERX)?;
self.spi_port.write_reg_16b(spi::addrs::ECON1, 0x1 | (econ1 & 0xfffe))?;
Ok(()) Ok(())
} }
@ -188,9 +186,8 @@ impl <SPI: Transfer<u8>,
} else { } else {
self.spi_port.write_reg_16b(spi::addrs::ERXTAIL, rx::RX_MAX_ADDRESS - 1)?; self.spi_port.write_reg_16b(spi::addrs::ERXTAIL, rx::RX_MAX_ADDRESS - 1)?;
} }
// Set PKTDEC (ECON1<88>) to decrement PKTCNT // Decrement PKTCNT - set PKTDEC (ECON1<8>)
let econ1_hi = self.spi_port.read_reg_8b(spi::addrs::ECON1 + 1)?; self.spi_port.send_opcode(spi::opcodes::SETPKTDEC)?;
self.spi_port.write_reg_8b(spi::addrs::ECON1 + 1, 0x01 | (econ1_hi & 0xfe))?;
// Return the RxPacket // Return the RxPacket
Ok(rx_packet) Ok(rx_packet)
} }
@ -208,12 +205,11 @@ impl <SPI: Transfer<u8>,
self.spi_port.write_reg_16b(spi::addrs::ETXST, self.tx_buf.get_next_addr())?; self.spi_port.write_reg_16b(spi::addrs::ETXST, self.tx_buf.get_next_addr())?;
// Set ETXLEN to packet length // Set ETXLEN to packet length
self.spi_port.write_reg_16b(spi::addrs::ETXLEN, packet.get_frame_length() as u16)?; self.spi_port.write_reg_16b(spi::addrs::ETXLEN, packet.get_frame_length() as u16)?;
// Set TXRTS (ECON1<1>) to start transmission // Send packet - set TXRTS (ECON1<1>) to start transmission
let mut econ1_lo = self.spi_port.read_reg_8b(spi::addrs::ECON1)?; self.spi_port.send_opcode(spi::opcodes::SETTXRTS)?;
self.spi_port.write_reg_8b(spi::addrs::ECON1, 0x02 | (econ1_lo & 0xfd))?;
// Poll TXRTS (ECON1<1>) to check if it is reset // Poll TXRTS (ECON1<1>) to check if it is reset
loop { loop {
econ1_lo = self.spi_port.read_reg_8b(spi::addrs::ECON1)?; let econ1_lo = self.spi_port.read_reg_8b(spi::addrs::ECON1)?;
if econ1_lo & 0x02 == 0 { break } if econ1_lo & 0x02 == 0 { break }
} }
// TODO: Read ETXSTAT to understand Ethernet transmission status // TODO: Read ETXSTAT to understand Ethernet transmission status

View File

@ -15,7 +15,17 @@ pub mod interfaces {
} }
pub mod opcodes { pub mod opcodes {
/// SPI Opcodes /// 1-byte Instructions
pub const SETETHRST: u8 = 0b1100_1010;
pub const SETPKTDEC: u8 = 0b1100_1100;
pub const SETTXRTS: u8 = 0b1101_0100;
pub const ENABLERX: u8 = 0b1110_1000;
/// 3-byte Instructions
pub const WRXRDPT: u8 = 0b0110_0100; // 8-bit opcode followed by data
pub const RRXRDPT: u8 = 0b0110_0110; // 8-bit opcode followed by data
pub const WGPWRPT: u8 = 0b0110_1100; // 8-bit opcode followed by data
pub const RGPWRPT: u8 = 0b0110_1110; // 8-bit opcode followed by data
/// N-byte Instructions
pub const RCRU: u8 = 0b0010_0000; pub const RCRU: u8 = 0b0010_0000;
pub const WCRU: u8 = 0b0010_0010; pub const WCRU: u8 = 0b0010_0010;
pub const RRXDATA: u8 = 0b0010_1100; // 8-bit opcode followed by data pub const RRXDATA: u8 = 0b0010_1100; // 8-bit opcode followed by data
@ -60,6 +70,7 @@ pub struct SpiPort<SPI: Transfer<u8>,
} }
pub enum Error { pub enum Error {
OpcodeError,
TransferError TransferError
} }
@ -85,23 +96,38 @@ impl <SPI: Transfer<u8>,
} }
pub fn read_reg_16b(&mut self, lo_addr: u8) -> Result<u16, Error> { pub fn read_reg_16b(&mut self, lo_addr: u8) -> Result<u16, Error> {
match lo_addr {
addrs::ERXRDPT | addrs::EGPWRPT => {
let mut buf: [u8; 3] = [0; 3];
self.rw_n(
&mut buf, match lo_addr {
addrs::ERXRDPT => opcodes::RRXRDPT,
addrs::EGPWRPT => opcodes::RGPWRPT,
_ => unreachable!()
}, 2
)?;
Ok((buf[2] as u16) << 8 | buf[1] as u16)
}
_ => {
let r_data_lo = self.read_reg_8b(lo_addr)?; let r_data_lo = self.read_reg_8b(lo_addr)?;
let r_data_hi = self.read_reg_8b(lo_addr + 1)?; let r_data_hi = self.read_reg_8b(lo_addr + 1)?;
// Combine top and bottom 8-bit to return 16-bit // Combine top and bottom 8-bit to return 16-bit
Ok(((r_data_hi as u16) << 8) | r_data_lo as u16) Ok(((r_data_hi as u16) << 8) | r_data_lo as u16)
} }
}
}
// Currently requires manual slicing (buf[1..]) for the data read back // Currently requires manual slicing (buf[1..]) for the data read back
pub fn read_rxdat<'a>(&mut self, buf: &'a mut [u8], data_length: usize) pub fn read_rxdat<'a>(&mut self, buf: &'a mut [u8], data_length: usize)
-> Result<(), Error> { -> Result<(), Error> {
self.r_n(buf, opcodes::RRXDATA, data_length) self.rw_n(buf, opcodes::RRXDATA, data_length)
} }
// Currently requires actual data to be stored in buf[1..] instead of buf[0..] // Currently requires actual data to be stored in buf[1..] instead of buf[0..]
// TODO: Maybe better naming? // TODO: Maybe better naming?
pub fn write_txdat<'a>(&mut self, buf: &'a mut [u8], data_length: usize) pub fn write_txdat<'a>(&mut self, buf: &'a mut [u8], data_length: usize)
-> Result<(), Error> { -> Result<(), Error> {
self.w_n(buf, opcodes::WGPDATA, data_length) self.rw_n(buf, opcodes::WGPDATA, data_length)
} }
pub fn write_reg_8b(&mut self, addr: u8, data: u8) -> Result<(), Error> { pub fn write_reg_8b(&mut self, addr: u8, data: u8) -> Result<(), Error> {
@ -112,18 +138,46 @@ impl <SPI: Transfer<u8>,
} }
pub fn write_reg_16b(&mut self, lo_addr: u8, data: u16) -> Result<(), Error> { pub fn write_reg_16b(&mut self, lo_addr: u8, data: u16) -> Result<(), Error> {
match lo_addr {
addrs::ERXRDPT | addrs::EGPWRPT => {
let mut buf: [u8; 3] = [0; 3];
buf[1] = data as u8 & 8;
buf[2] = (data >> 8) as u8 & 8;
self.rw_n(
&mut buf, match lo_addr {
addrs::ERXRDPT => opcodes::WRXRDPT,
addrs::EGPWRPT => opcodes::WGPWRPT,
_ => unreachable!()
}, 2
)
}
_ => {
self.write_reg_8b(lo_addr, (data & 0xff) as u8)?; self.write_reg_8b(lo_addr, (data & 0xff) as u8)?;
self.write_reg_8b(lo_addr + 1, ((data & 0xff00) >> 8) as u8)?; self.write_reg_8b(lo_addr + 1, ((data & 0xff00) >> 8) as u8)?;
Ok(()) Ok(())
} }
}
}
pub fn send_opcode(&mut self, opcode: u8) -> Result<(), Error> {
match opcode {
opcodes::SETETHRST | opcodes::SETPKTDEC |
opcodes::SETTXRTS | opcodes::ENABLERX => {
let mut buf: [u8; 1] = [0];
self.rw_n(&mut buf, opcode, 0)
}
_ => Err(Error::OpcodeError)
}
}
pub fn delay_us(&mut self, duration: u32) { pub fn delay_us(&mut self, duration: u32) {
(self.delay_ns)(duration * 1000) (self.delay_ns)(duration * 1000)
} }
// TODO: Generalise transfer functions // TODO: Generalise transfer functions
// TODO: (Make data read/write as reference to array) // Completes an SPI transfer for reading or writing 8-bit data,
// Currently requires 1-byte addr, read/write data is only 1-byte // and returns the data read/written.
// It starts with an 8-bit instruction, followed by an 8-bit address.
fn rw_addr_u8(&mut self, opcode: u8, addr: u8, data: u8) fn rw_addr_u8(&mut self, opcode: u8, addr: u8, data: u8)
-> Result<u8, Error> { -> Result<u8, Error> {
// Enable chip select // Enable chip select
@ -154,40 +208,20 @@ impl <SPI: Transfer<u8>,
} }
// TODO: Generalise transfer functions // 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: usize)
-> Result<(), Error> {
// Enable chip select
self.nss.set_low();
// Start writing to SLAVE
buf[0] = opcode;
match self.spi.transfer(&mut buf[..data_length+1]) {
Ok(_) => {
// Disable chip select
self.nss.set_high();
Ok(())
},
// TODO: Maybe too naive?
Err(_) => {
// Disable chip select
self.nss.set_high();
Err(Error::TransferError)
}
}
}
// Note: buf[0] is currently reserved for opcode to overwrite
// TODO: Actual data should start from buf[0], not buf[1] // TODO: Actual data should start from buf[0], not buf[1]
fn w_n<'a>(&mut self, buf: &'a mut [u8], opcode: u8, data_length: usize) // Completes an SPI transfer for reading data to the given buffer,
// or writing data from the buffer.
// It sends an 8-bit instruction, followed by either
// receiving or sending n*8-bit data.
// The slice of buffer provided must begin with the 8-bit instruction.
// If n = 0, the transfer will only involve sending the instruction.
fn rw_n<'a>(&mut self, buf: &'a mut [u8], opcode: u8, data_length: usize)
-> Result<(), Error> { -> Result<(), Error> {
assert!(buf.len() > data_length);
// Enable chip select // Enable chip select
self.nss.set_low(); self.nss.set_low();
// Start writing to SLAVE // Start writing to SLAVE
buf[0] = opcode; buf[0] = opcode;
// TODO: Maybe need to copy data to buf later on
match self.spi.transfer(&mut buf[..data_length+1]) { match self.spi.transfer(&mut buf[..data_length+1]) {
Ok(_) => { Ok(_) => {
// Disable chip select // Disable chip select