diff --git a/src/eth/mod.rs b/src/eth/mod.rs index fa878552..b7e400f0 100644 --- a/src/eth/mod.rs +++ b/src/eth/mod.rs @@ -3,13 +3,16 @@ use crate::slcr; pub mod phy; mod regs; +mod rx; +mod tx; -pub struct Eth { +pub struct Eth<'rx> { regs: &'static mut regs::RegisterBlock, + rx: Option>, } -impl Eth { - pub fn default() -> Self { +impl<'rx> Eth<'rx> { + pub fn default(macaddr: [u8; 6]) -> Self { slcr::RegisterBlock::unlocked(|slcr| { // MDIO slcr.mio_pin_53.write( @@ -116,10 +119,10 @@ impl Eth { ); }); - Self::gem0() + Self::gem0(macaddr) } - pub fn gem0() -> Self { + pub fn gem0(macaddr: [u8; 6]) -> Self { slcr::RegisterBlock::unlocked(|slcr| { // Enable gem0 ref clock slcr.gem0_rclk_ctrl.write( @@ -137,12 +140,18 @@ impl Eth { }); let regs = regs::RegisterBlock::gem0(); - Eth { regs }.init() + let rx = None; + let mut eth = Eth { regs, rx }.init(); + eth.configure(macaddr); + eth } - pub fn gem1() -> Self { + pub fn gem1(macaddr: [u8; 6]) -> Self { let regs = regs::RegisterBlock::gem1(); - Eth { regs }.init() + let rx = None; + let mut eth = Eth { regs, rx }.init(); + eth.configure(macaddr); + eth } fn init(mut self) -> Self { @@ -208,11 +217,10 @@ impl Eth { regs::TxQbar::zeroed() ); - self.configure(); self } - fn configure(&mut self) { + fn configure(&mut self, macaddr: [u8; 6]) { self.regs.net_cfg.write( regs::NetCfg::zeroed() .full_duplex(true) @@ -224,8 +232,39 @@ impl Eth { .copy_all(true) .mdc_clk_div(0b111) ); - // TODO: mac addr - // TODO: Program the DMA Configuration register (gem.dma_cfg). + + let macaddr_msbs = + (u16::from(macaddr[0]) << 8) | + u16::from(macaddr[1]); + let macaddr_lsbs = + (u32::from(macaddr[2]) << 24) | + (u32::from(macaddr[3]) << 16) | + (u32::from(macaddr[4]) << 8) | + u32::from(macaddr[5]); + self.regs.spec_addr1_top.write( + regs::SpecAddrTop::zeroed() + .addr_msbs(macaddr_msbs) + ); + self.regs.spec_addr1_bot.write( + regs::SpecAddrBot::zeroed() + .addr_lsbs(macaddr_lsbs) + ); + + + self.regs.dma_cfg.write( + regs::DmaCfg::zeroed() + // 1600 bytes + .ahb_mem_rx_buf_size(0x19) + // 8 KB + .rx_pktbuf_memsz_sel(0x3) + // 4 KB + .tx_pktbuf_memsz_sel(true) + // .csum_gen_offload_en(true) + // Little-endian + .ahb_endian_swp_mgmt_en(false) + // INCR16 AHB burst + .ahb_fixed_burst_len(0x10) + ); self.regs.net_ctrl.write( regs::NetCtrl::zeroed() @@ -235,12 +274,21 @@ impl Eth { ); } + pub fn start_rx(&mut self, rx_buffers: [&'rx mut [u8]; rx::DESCS]) { + self.rx = Some(rx::DescList::new(rx_buffers)); + let list_addr = self.rx.as_ref().unwrap() as *const _ as u32; + self.regs.rx_qbar.write( + regs::RxQbar::zeroed() + .rx_q_baseaddr(list_addr >> 2) + ); + } + fn wait_phy_idle(&self) { while !self.regs.net_status.read().phy_mgmt_idle() {} } } -impl phy::PhyAccess for Eth { +impl<'rx> phy::PhyAccess for Eth<'rx> { fn read_phy(&mut self, addr: u8, reg: u8) -> u16 { self.wait_phy_idle(); self.regs.phy_maint.write( diff --git a/src/eth/regs.rs b/src/eth/regs.rs index 60d81e93..a136770f 100644 --- a/src/eth/regs.rs +++ b/src/eth/regs.rs @@ -8,7 +8,7 @@ pub struct RegisterBlock { pub net_cfg: NetCfg, pub net_status: NetStatus, pub unused0: RO, - pub dma_cfg: RW, + pub dma_cfg: DmaCfg, pub tx_status: TxStatus, pub rx_qbar: RxQbar, pub tx_qbar: TxQbar, @@ -23,14 +23,14 @@ pub struct RegisterBlock { pub unused1: [RO; 16], pub hash_bot: RW, pub hash_top: RW, - pub spec_addr1_bot: RW, - pub spec_addr1_top: RW, - pub spec_addr2_bot: RW, - pub spec_addr2_top: RW, - pub spec_addr3_bot: RW, - pub spec_addr3_top: RW, - pub spec_addr4_bot: RW, - pub spec_addr4_top: RW, + pub spec_addr1_bot: SpecAddrBot, + pub spec_addr1_top: SpecAddrTop, + pub spec_addr2_bot: SpecAddrBot, + pub spec_addr2_top: SpecAddrTop, + pub spec_addr3_bot: SpecAddrBot, + pub spec_addr3_top: SpecAddrTop, + pub spec_addr4_bot: SpecAddrBot, + pub spec_addr4_top: SpecAddrTop, pub type_id_match1: RW, pub type_id_match2: RW, pub type_id_match3: RW, @@ -233,6 +233,16 @@ register_bit!(net_status, pcs_autoneg_pause_rx_res, 4); register_bit!(net_status, pcs_autoneg_pause_tx_res, 5); register_bit!(net_status, pfc_pri_pause_neg, 6); +register!(dma_cfg, DmaCfg, RW, u32); +register_bits!(dma_cfg, ahb_fixed_burst_len, u8, 0, 4); +register_bit!(dma_cfg, ahb_endian_swp_mgmt_en, 6); +register_bit!(dma_cfg, ahb_endian_swp_pkt_en, 7); +register_bits!(dma_cfg, rx_pktbuf_memsz_sel, u8, 8, 9); +register_bit!(dma_cfg, tx_pktbuf_memsz_sel, 10); +register_bit!(dma_cfg, csum_gen_offload_en, 11); +register_bits!(dma_cfg, ahb_mem_rx_buf_size, u8, 16, 23); +register_bit!(dma_cfg, disc_when_no_ahb, 24); + register!(tx_status, TxStatus, RW, u32); register_bit!(tx_status, used_bit_read, 0); register_bit!(tx_status, collision, 1); @@ -305,3 +315,11 @@ register_bits!(phy_maint, register_bits_typed!(phy_maint, operation, u8, PhyOperation, 28, 29); // PHY clause 22 compliant (not clause 45)? register_bit!(phy_maint, clause_22, 30); + +register!(spec_addr_top, SpecAddrTop, RW, u32); +register_bits!(spec_addr_top, + addr_msbs, u16, 0, 15); + +register!(spec_addr_bot, SpecAddrBot, RW, u32); +register_bits!(spec_addr_bot, + addr_lsbs, u32, 0, 31); diff --git a/src/eth/rx.rs b/src/eth/rx.rs new file mode 100644 index 00000000..b4dd25b1 --- /dev/null +++ b/src/eth/rx.rs @@ -0,0 +1,62 @@ +use core::mem::uninitialized; +use crate::{register, register_bit, register_bits, register_bits_typed, regs::*}; + +/// Descriptor entry +#[repr(C)] +struct DescEntry { + word0: DescWord0, + word1: DescWord1, +} + +register!(desc_word0, DescWord0, RW, u32); +/// true if owned by software, false if owned by hardware +register_bit!(desc_word0, used, 0); +/// mark last desc in list +register_bit!(desc_word0, wrap, 1); +register_bits!(desc_word0, address, u32, 2, 31); + +register!(desc_word1, DescWord1, RW, u32); +register_bits!(desc_word1, frame_length_lsbs, u16, 0, 12); +register_bit!(desc_word1, bad_fcs, 13); +register_bit!(desc_word1, start_of_frame, 14); +register_bit!(desc_word1, end_of_frame, 15); +register_bit!(desc_word1, cfi, 16); +register_bits!(desc_word1, vlan_priority, u8, 17, 19); +register_bit!(desc_word1, priority_tag, 20); +register_bit!(desc_word1, vlan_tag, 21); +register_bits!(desc_word1, bits_22_23, u8, 22, 23); +register_bit!(desc_word1, bit_24, 24); +register_bits!(desc_word1, spec_addr_which, u8, 25, 26); +register_bit!(desc_word1, spec_addr_match, 27); +register_bit!(desc_word1, uni_hash_match, 29); +register_bit!(desc_word1, multi_hash_match, 30); +register_bit!(desc_word1, global_broadcast, 31); + +/// Number of descriptors +pub const DESCS: usize = 8; + +#[repr(C)] +pub struct DescList<'a> { + list: [DescEntry; DESCS], + buffers: [&'a mut [u8]; DESCS], +} + +impl<'a> DescList<'a> { + pub fn new(buffers: [&'a mut [u8]; DESCS]) -> Self { + let mut list: [DescEntry; DESCS] = unsafe { uninitialized() }; + for i in 0..DESCS { + let buffer_addr = &mut buffers[i][0] as *mut _ as u32; + list[i].word0.write( + DescWord0::zeroed() + .used(false) + .wrap(i == DESCS - 1) + .address(buffer_addr >> 2) + ); + list[i].word1.write( + DescWord1::zeroed() + ); + } + + DescList { list, buffers } + } +} diff --git a/src/eth/tx.rs b/src/eth/tx.rs new file mode 100644 index 00000000..650c34d8 --- /dev/null +++ b/src/eth/tx.rs @@ -0,0 +1,23 @@ +use crate::{register, register_bit, register_bits, register_bits_typed, regs::*}; + +/// Descriptor entry +struct DescEntry { + word0: DescWord0, + word1: DescWord1, +} + +register!(desc_word0, DescWord0, RW, u32); +register_bits!(desc_word0, address, u32, 0, 31); + +register!(desc_word1, DescWord1, RW, u32); +register_bits!(desc_word1, length, u16, 0, 13); +register_bit!(desc_word1, last_buffer, 15); +register_bit!(desc_word1, no_crc_append, 16); +register_bits!(desc_word1, csum_offload_errors, u8, 20, 22); +register_bit!(desc_word1, late_collision_tx_error, 26); +register_bit!(desc_word1, ahb_frame_corruption, 27); +register_bit!(desc_word1, retry_limit_exceeded, 29); +/// marks last descriptor in list +register_bit!(desc_word1, wrap, 30); +/// true if owned by software, false if owned by hardware +register_bit!(desc_word1, used, 31); diff --git a/src/main.rs b/src/main.rs index 9fa02419..f3c6df55 100644 --- a/src/main.rs +++ b/src/main.rs @@ -81,7 +81,7 @@ fn main() { let mut uart = Uart::serial(UART_RATE); writeln!(uart, "\r\nHello World!\r"); - let mut eth = eth::Eth::default(); + let mut eth = eth::Eth::default([0x0, 0x17, 0xde, 0xea, 0xbe, 0xef]); writeln!(uart, "Eth on\r"); use eth::phy::PhyAccess; for addr in 1..=31 {