Skip to main content

esp_emac/regs/
mac.rs

1// SPDX-License-Identifier: GPL-2.0-or-later OR Apache-2.0
2// Copyright (c) Viacheslav Bocharov <v@baodeep.com> and JetHome (r)
3
4//! MAC core register definitions.
5//!
6//! The MAC core handles frame transmission and reception per IEEE 802.3.
7//! Base address: `0x3FF6_A000`.
8
9#![allow(dead_code)]
10
11// =============================================================================
12// Base Address
13// =============================================================================
14
15/// MAC register block base address (ESP32).
16pub const BASE: usize = 0x3FF6_A000;
17
18// =============================================================================
19// Register Offsets
20// =============================================================================
21
22/// GMAC Configuration Register.
23pub const GMACCONFIG: usize = 0x00;
24/// GMAC Frame Filter Register.
25pub const GMACFF: usize = 0x04;
26/// GMAC Hash Table High Register.
27pub const GMACHASTH: usize = 0x08;
28/// GMAC Hash Table Low Register.
29pub const GMACHASTL: usize = 0x0C;
30/// GMAC MII Address Register.
31pub const GMACMIIADDR: usize = 0x10;
32/// GMAC MII Data Register.
33pub const GMACMIIDATA: usize = 0x14;
34/// GMAC Flow Control Register.
35pub const GMACFC: usize = 0x18;
36/// GMAC VLAN Tag Register.
37pub const GMACVLAN: usize = 0x1C;
38/// GMAC Debug Register (read-only).
39pub const GMACDEBUG: usize = 0x24;
40/// GMAC Interrupt Status Register.
41pub const GMACINTS: usize = 0x38;
42/// GMAC Interrupt Mask Register.
43pub const GMACINTMASK: usize = 0x3C;
44/// GMAC Address 0 High Register (upper 16 bits of primary MAC).
45pub const GMACADDR0H: usize = 0x40;
46/// GMAC Address 0 Low Register (lower 32 bits of primary MAC).
47pub const GMACADDR0L: usize = 0x44;
48
49// =============================================================================
50// GMACCONFIG bits
51// =============================================================================
52
53/// Bit-field constants for the GMAC Configuration Register.
54pub mod config {
55    /// Receiver Enable.
56    pub const RX_ENABLE: u32 = 1 << 2;
57    /// Transmitter Enable.
58    pub const TX_ENABLE: u32 = 1 << 3;
59    /// Automatic Pad/CRC Stripping.
60    pub const AUTO_PAD_CRC_STRIP: u32 = 1 << 7;
61    /// Link Up/Down (ESP32-specific).
62    pub const LINK_UP: u32 = 1 << 8;
63    /// Retry Disable.
64    pub const RETRY_DISABLE: u32 = 1 << 9;
65    /// Checksum Offload (IPC).
66    pub const CHECKSUM_OFFLOAD: u32 = 1 << 10;
67    /// Duplex Mode: 1 = full duplex.
68    pub const DUPLEX_FULL: u32 = 1 << 11;
69    /// Speed: 1 = 100 Mbps, 0 = 10 Mbps.
70    pub const SPEED_100: u32 = 1 << 14;
71    /// Port Select: must be 1 for MII/RMII.
72    pub const PORT_SELECT: u32 = 1 << 15;
73    /// Inter-Frame Gap shift (3-bit field at bits 19:17).
74    pub const IFG_SHIFT: u32 = 17;
75    /// Inter-Frame Gap mask.
76    pub const IFG_MASK: u32 = 0x07 << 17;
77    /// Jumbo Frame Enable.
78    pub const JUMBO_FRAME: u32 = 1 << 20;
79    /// Frame Burst Enable.
80    pub const FRAME_BURST: u32 = 1 << 21;
81    /// Jabber Disable.
82    pub const JABBER_DISABLE: u32 = 1 << 22;
83    /// Watchdog Disable.
84    pub const WATCHDOG_DISABLE: u32 = 1 << 23;
85}
86
87// =============================================================================
88// GMACFF bits
89// =============================================================================
90
91/// Bit-field constants for the GMAC Frame Filter Register.
92pub mod frame_filter {
93    /// Promiscuous Mode.
94    pub const PROMISCUOUS: u32 = 1 << 0;
95    /// Hash Unicast.
96    pub const HASH_UNICAST: u32 = 1 << 1;
97    /// Hash Multicast.
98    pub const HASH_MULTICAST: u32 = 1 << 2;
99    /// DA Inverse Filtering.
100    pub const DA_INVERSE: u32 = 1 << 3;
101    /// Pass All Multicast.
102    pub const PASS_ALL_MULTICAST: u32 = 1 << 4;
103    /// Disable Broadcast Frames.
104    pub const DISABLE_BROADCAST: u32 = 1 << 5;
105    /// Receive All.
106    pub const RECEIVE_ALL: u32 = 1 << 31;
107}
108
109// =============================================================================
110// GMACMIIADDR bits
111// =============================================================================
112
113/// Bit-field constants for the GMAC MII Address Register.
114///
115/// Also used by `mdio.rs` (which has its own private copies).
116/// This module provides the complete reference.
117pub mod miiaddr {
118    /// Physical Layer Address shift (5-bit field at bits 15:11).
119    pub const PA_SHIFT: u32 = 11;
120    /// Physical Layer Address mask.
121    pub const PA_MASK: u32 = 0x1F << 11;
122    /// MII Register Address shift (5-bit field at bits 10:6).
123    pub const GR_SHIFT: u32 = 6;
124    /// MII Register Address mask.
125    pub const GR_MASK: u32 = 0x1F << 6;
126    /// CSR Clock Range shift (4-bit field at bits 5:2).
127    pub const CR_SHIFT: u32 = 2;
128    /// CSR Clock Range mask.
129    pub const CR_MASK: u32 = 0x0F << 2;
130    /// MII Write.
131    pub const GW: u32 = 1 << 1;
132    /// MII Busy.
133    pub const GB: u32 = 1 << 0;
134}
135
136// =============================================================================
137// GMACFC bits
138// =============================================================================
139
140/// Bit-field constants for the GMAC Flow Control Register.
141pub mod flow_control {
142    /// Flow Control Busy / Backpressure Activate.
143    pub const FCB_BPA: u32 = 1 << 0;
144    /// Transmit Flow Control Enable.
145    pub const TX_ENABLE: u32 = 1 << 1;
146    /// Receive Flow Control Enable.
147    pub const RX_ENABLE: u32 = 1 << 2;
148    /// Unicast PAUSE Frame Detect.
149    pub const UNICAST_PAUSE: u32 = 1 << 3;
150    /// PAUSE Low Threshold shift (2-bit field at bits 5:4).
151    pub const PLT_SHIFT: u32 = 4;
152    /// PAUSE Low Threshold mask.
153    pub const PLT_MASK: u32 = 0x03 << 4;
154    /// Zero-Quanta PAUSE Disable.
155    pub const ZERO_QUANTA_DISABLE: u32 = 1 << 7;
156    /// PAUSE Time shift (16-bit field at bits 31:16).
157    pub const PT_SHIFT: u32 = 16;
158    /// PAUSE Time mask.
159    pub const PT_MASK: u32 = 0xFFFF << 16;
160}
161
162// =============================================================================
163// Register access helpers
164// =============================================================================
165
166/// Read a MAC register at `offset` from BASE.
167///
168/// # Safety
169/// Caller must ensure the EMAC peripheral clock is enabled and
170/// `offset` is a valid register offset within this block.
171#[inline(always)]
172pub unsafe fn read(offset: usize) -> u32 {
173    // SAFETY: caller guarantees address validity.
174    core::ptr::read_volatile((BASE + offset) as *const u32)
175}
176
177/// Write a MAC register at `offset` from BASE.
178///
179/// # Safety
180/// Caller must ensure the EMAC peripheral clock is enabled and
181/// `offset` is a valid register offset within this block.
182#[inline(always)]
183pub unsafe fn write(offset: usize, val: u32) {
184    // SAFETY: caller guarantees address validity.
185    core::ptr::write_volatile((BASE + offset) as *mut u32, val);
186}
187
188/// Read-modify-write: set bits in a MAC register.
189///
190/// # Safety
191/// Same requirements as [`read`] and [`write`].
192#[inline(always)]
193pub unsafe fn set_bits(offset: usize, bits: u32) {
194    let val = read(offset);
195    write(offset, val | bits);
196}
197
198/// Read-modify-write: clear bits in a MAC register.
199///
200/// # Safety
201/// Same requirements as [`read`] and [`write`].
202#[inline(always)]
203pub unsafe fn clear_bits(offset: usize, bits: u32) {
204    let val = read(offset);
205    write(offset, val & !bits);
206}
207
208// =============================================================================
209// Composite operations (formerly ph_esp32_mac::unsafe_registers::MacRegs)
210// =============================================================================
211
212/// Read the GMAC Configuration register.
213#[inline(always)]
214pub fn config() -> u32 {
215    // SAFETY: GMACCONFIG is a known-valid 32-bit memory-mapped register.
216    unsafe { read(GMACCONFIG) }
217}
218
219/// Write the GMAC Configuration register.
220#[inline(always)]
221pub fn set_config(val: u32) {
222    // SAFETY: GMACCONFIG is a known-valid 32-bit memory-mapped register.
223    unsafe { write(GMACCONFIG, val) }
224}
225
226/// Write the GMAC Frame Filter register.
227#[inline(always)]
228pub fn set_frame_filter(val: u32) {
229    // SAFETY: GMACFF is a known-valid 32-bit memory-mapped register.
230    unsafe { write(GMACFF, val) }
231}
232
233/// Write the 64-bit hash filter table (low → `GMACHASTL`, high → `GMACHASTH`).
234#[inline(always)]
235pub fn set_hash_table(value: u64) {
236    // SAFETY: both registers are known-valid 32-bit memory-mapped registers.
237    unsafe {
238        write(GMACHASTL, value as u32);
239        write(GMACHASTH, (value >> 32) as u32);
240    }
241}
242
243/// Apply the 100 Mbps / 10 Mbps speed setting (RMW on `GMACCONFIG.FES`).
244#[inline(always)]
245pub fn set_speed_100mbps(is_100: bool) {
246    // SAFETY: GMACCONFIG is a known-valid 32-bit memory-mapped register.
247    unsafe {
248        if is_100 {
249            set_bits(GMACCONFIG, config::SPEED_100);
250        } else {
251            clear_bits(GMACCONFIG, config::SPEED_100);
252        }
253    }
254}
255
256/// Apply the full / half duplex setting (RMW on `GMACCONFIG.DM`).
257#[inline(always)]
258pub fn set_duplex_full(full: bool) {
259    // SAFETY: GMACCONFIG is a known-valid 32-bit memory-mapped register.
260    unsafe {
261        if full {
262            set_bits(GMACCONFIG, config::DUPLEX_FULL);
263        } else {
264            clear_bits(GMACCONFIG, config::DUPLEX_FULL);
265        }
266    }
267}
268
269/// Programme the primary MAC address into ADDR0H / ADDR0L (with AE bit).
270///
271/// The Synopsys DesignWare GMAC core latches the filter address on the
272/// write to **ADDR0L** (the low half). Therefore the high half (with the
273/// `AE` bit) MUST be written first, then the low half. Writing in the
274/// opposite order leaves the internal filter latched with the stale
275/// reset/efuse value while register read-back happily shows the new
276/// address — and unicast RX dies silently.
277pub fn set_mac_address(addr: &[u8; 6]) {
278    let high = (addr[4] as u32) | ((addr[5] as u32) << 8) | (1u32 << 31);
279    let low = (addr[0] as u32)
280        | ((addr[1] as u32) << 8)
281        | ((addr[2] as u32) << 16)
282        | ((addr[3] as u32) << 24);
283    // SAFETY: ADDR0H/ADDR0L are valid 32-bit MAC registers; caller has
284    // already enabled the EMAC peripheral clock by the time `init()`
285    // reaches this step.
286    unsafe {
287        write(GMACADDR0H, high);
288        write(GMACADDR0L, low);
289    }
290}
291
292// =============================================================================
293// Tests
294// =============================================================================
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299
300    #[test]
301    fn base_address() {
302        assert_eq!(BASE, 0x3FF6_A000);
303    }
304
305    #[test]
306    fn register_offsets_within_block() {
307        // MAC register block is 0x1000 (4 KiB)
308        let offsets = [
309            GMACCONFIG,
310            GMACFF,
311            GMACHASTH,
312            GMACHASTL,
313            GMACMIIADDR,
314            GMACMIIDATA,
315            GMACFC,
316            GMACVLAN,
317            GMACDEBUG,
318            GMACINTS,
319            GMACINTMASK,
320            GMACADDR0H,
321            GMACADDR0L,
322        ];
323        for off in offsets {
324            assert!(off < 0x1000, "offset {:#x} exceeds MAC block size", off);
325        }
326    }
327
328    #[test]
329    fn config_bits_no_overlap() {
330        let bits = [
331            config::RX_ENABLE,
332            config::TX_ENABLE,
333            config::RETRY_DISABLE,
334            config::CHECKSUM_OFFLOAD,
335            config::DUPLEX_FULL,
336            config::SPEED_100,
337            config::PORT_SELECT,
338            config::JUMBO_FRAME,
339            config::FRAME_BURST,
340            config::JABBER_DISABLE,
341            config::WATCHDOG_DISABLE,
342            config::LINK_UP,
343            config::AUTO_PAD_CRC_STRIP,
344        ];
345        for i in 0..bits.len() {
346            for j in (i + 1)..bits.len() {
347                assert_eq!(
348                    bits[i] & bits[j],
349                    0,
350                    "config bits {:#x} and {:#x} overlap",
351                    bits[i],
352                    bits[j]
353                );
354            }
355        }
356    }
357
358    #[test]
359    fn miiaddr_fields_no_overlap() {
360        // Single-bit fields
361        assert_eq!(miiaddr::GB & miiaddr::GW, 0);
362        // Mask fields should not overlap each other
363        assert_eq!(miiaddr::CR_MASK & miiaddr::GR_MASK, 0);
364        assert_eq!(miiaddr::GR_MASK & miiaddr::PA_MASK, 0);
365        assert_eq!(miiaddr::CR_MASK & miiaddr::PA_MASK, 0);
366    }
367
368    #[test]
369    fn flow_control_bits_no_overlap() {
370        let bits = [
371            flow_control::FCB_BPA,
372            flow_control::TX_ENABLE,
373            flow_control::RX_ENABLE,
374            flow_control::UNICAST_PAUSE,
375            flow_control::ZERO_QUANTA_DISABLE,
376        ];
377        for i in 0..bits.len() {
378            for j in (i + 1)..bits.len() {
379                assert_eq!(
380                    bits[i] & bits[j],
381                    0,
382                    "flow_control bits {:#x} and {:#x} overlap",
383                    bits[i],
384                    bits[j]
385                );
386            }
387        }
388    }
389
390    #[test]
391    fn frame_filter_bits_no_overlap() {
392        let bits = [
393            frame_filter::PROMISCUOUS,
394            frame_filter::HASH_UNICAST,
395            frame_filter::HASH_MULTICAST,
396            frame_filter::DA_INVERSE,
397            frame_filter::PASS_ALL_MULTICAST,
398            frame_filter::DISABLE_BROADCAST,
399            frame_filter::RECEIVE_ALL,
400        ];
401        for i in 0..bits.len() {
402            for j in (i + 1)..bits.len() {
403                assert_eq!(
404                    bits[i] & bits[j],
405                    0,
406                    "frame_filter bits {:#x} and {:#x} overlap",
407                    bits[i],
408                    bits[j]
409                );
410            }
411        }
412    }
413}