musb_readconf/
lib.rs

1#![no_std]
2
3pub use musb::{MusbInstance, regs};
4
5/// Describes the FIFO sizing strategy of the MUSB core.
6#[derive(defmt::Format)]
7pub enum FifoSizing {
8    /// FIFO sizes are fixed at synthesis time.
9    Static,
10    /// FIFO sizes can be dynamically configured by software.
11    Dynamic,
12}
13
14/// Describes the UTMI+ interface data width.
15#[derive(defmt::Format)]
16pub enum UtmiDataWidth {
17    /// 8-bit data width.
18    Bit8,
19    /// 16-bit data width.
20    Bit16,
21    /// An unknown or unsupported value was read.
22    Unknown(u8),
23}
24
25/// Holds the decoded configuration of the MUSB IP core.
26///
27/// This structure is populated by reading hardware registers.
28pub struct Configuration {
29    // --- Fields from CONFIGDATA register ---
30    pub mprxe: bool,
31    pub mptxe: bool,
32    pub big_endian: bool,
33    pub hbrxe: bool,
34    pub hbtxe: bool,
35    pub dyn_fifo_sizing: FifoSizing,
36    pub soft_connect: bool,
37    pub utmi_data_width: UtmiDataWidth,
38
39     // --- Fields from FIFOSIZE registers ---
40    pub tx_fifo_sizes: [u16; 16],
41    pub rx_fifo_sizes: [u16; 16],
42
43    // --- Fields from Additional Registers ---
44    pub rx_endpoints: u8,
45    pub tx_endpoints: u8,
46    pub dma_channels: u8,
47    pub ram_bits: u8,
48    pub wtcon: u8,
49    pub wtid: u8,
50    pub vplen: u8,
51    pub hs_eof1: u8,
52    pub fs_eof1: u8,
53    pub ls_eof1: u8,
54
55    // --- Flags to track if registers read all-zeros ---
56    epinfo_is_zero: bool,
57    raminfo_is_zero: bool,
58    linkinfo_is_zero: bool,
59    vplen_is_zero: bool,
60    hs_eof1_is_zero: bool,
61    fs_eof1_is_zero: bool,
62    ls_eof1_is_zero: bool,
63}
64
65impl Configuration {
66    /// Reads the MUSB IP configuration by accessing its hardware registers.
67    ///
68    /// This function uses high-level field accessor methods and checks for all-zero
69    /// register values to detect potentially masked hardware features.
70    pub fn read<T: MusbInstance>() -> Self {
71        let regs = T::regs();
72
73        defmt::trace!("INDEX addr: 0x{:X}", &regs.index().as_ptr());
74        // --- Read and Decode CONFIGDATA ---
75        regs.index().write(|w| w.set_index(0));
76        let configdata = regs.configdata().read();
77        let mprxe = configdata.mprxe();
78        let mptxe = configdata.mptxe();
79        let big_endian = configdata.big_endian();
80        let hbrxe = configdata.hbrxe();
81        let hbtxe = configdata.hbtxe();
82        let dyn_fifo_sizing = if configdata.dyn_fifo_sizing() {
83            FifoSizing::Dynamic
84        } else {
85            FifoSizing::Static
86        };
87        let soft_connect = configdata.soft_con_e();
88        let utmi_data_width = match configdata.utmi_data_width().into() {
89            0 => UtmiDataWidth::Bit8,
90            1 => UtmiDataWidth::Bit16,
91            val => UtmiDataWidth::Unknown(val),
92        };
93
94                // --- Read and Decode FIFOSIZE for each endpoint (if static) ---
95        let mut tx_fifo_sizes = [0u16; 16];
96        let mut rx_fifo_sizes = [0u16; 16];
97
98        if let FifoSizing::Static = dyn_fifo_sizing {
99            for i in 0..=15 {
100                regs.index().write(|w| w.set_index(i));
101                let tx_nibble = regs.fifosize().read().tx_fifo_size();
102                let rx_nibble = regs.fifosize().read().rx_fifo_size();
103                // defmt::trace!("Endpoint {}: FIFOSIZE register = {:b}, TX nibble = 0x{:x}, RX nibble = 0x{:x}", i, regs.fifosize().read().0, tx_nibble, rx_nibble);
104
105                tx_fifo_sizes[i as usize] = Self::decode_fifo_size_nibble(tx_nibble);
106                
107                if rx_nibble == 0xF {
108                    // 0xF indicates the RX endpoint shares the TX FIFO
109                    rx_fifo_sizes[i as usize] = u16::MAX; 
110                } else {
111                    rx_fifo_sizes[i as usize] = Self::decode_fifo_size_nibble(rx_nibble);
112                }
113            }
114        }
115        regs.index().write(|w| w.set_index(0));
116
117        // --- Read and Decode Additional Configuration Registers ---
118
119        // EPINFO (Address 0x78)
120        let epinfo_reader = regs.epinfo().read();
121        let epinfo_is_zero = epinfo_reader.0 == 0;
122        let rx_endpoints = epinfo_reader.rx_end_points();
123        let tx_endpoints = epinfo_reader.tx_end_points();
124
125        // RAMINFO (Address 0x79)
126        let raminfo_reader = regs.raminfo().read();
127        let raminfo_is_zero = raminfo_reader.0 == 0;
128        let dma_channels = raminfo_reader.dmachans();
129        let ram_bits = raminfo_reader.ram_bits();
130
131        // LINKINFO (Address 0x7A)
132        let linkinfo_reader = regs.linkinfo().read();
133        let linkinfo_is_zero = linkinfo_reader.0 == 0;
134        let wtcon = linkinfo_reader.wtcon();
135        let wtid = linkinfo_reader.wtid();
136
137        // VPLEN (Address 0x7B)
138        let vplen_reader = regs.vplen().read();
139        let vplen_is_zero = vplen_reader.0 == 0;
140        let vplen = vplen_reader.vplen();
141
142        // HS_EOF1 (Address 0x7C)
143        let hs_eof1_reader = regs.hs_eof1().read();
144        let hs_eof1_is_zero = hs_eof1_reader.0 == 0;
145        let hs_eof1 = hs_eof1_reader.hs_eof1();
146
147        // FS_EOF1 (Address 0x7D)
148        let fs_eof1_reader = regs.fs_eof1().read();
149        let fs_eof1_is_zero = fs_eof1_reader.0 == 0;
150        let fs_eof1 = fs_eof1_reader.fs_eof1();
151
152        // LS_EOF1 (Address 0x7E)
153        let ls_eof1_reader = regs.ls_eof1().read();
154        let ls_eof1_is_zero = ls_eof1_reader.0 == 0;
155        let ls_eof1 = ls_eof1_reader.ls_eof1();
156
157        Self {
158            mprxe, mptxe, big_endian, hbrxe, hbtxe, dyn_fifo_sizing,
159            tx_fifo_sizes, rx_fifo_sizes,
160            soft_connect, utmi_data_width,
161            rx_endpoints, tx_endpoints, dma_channels, ram_bits,
162            wtcon, wtid, vplen, hs_eof1, fs_eof1, ls_eof1,
163            epinfo_is_zero, raminfo_is_zero, linkinfo_is_zero,
164            vplen_is_zero, hs_eof1_is_zero, fs_eof1_is_zero, ls_eof1_is_zero,
165        }
166    }
167
168    /// Decodes a 4-bit FIFOSIZE nibble into the corresponding size in bytes.
169    /// According to the spec, values 3-13 correspond to 2^n bytes.
170    /// Other values mean the endpoint is not configured.
171    fn decode_fifo_size_nibble(nibble: u8) -> u16 {
172        match nibble {
173            3..=13 => 1u16 << nibble,
174            _ => 0, // Not configured or invalid
175        }
176    }
177
178    /// Prints the decoded configuration information using `defmt`.
179    pub fn print_defmt(&self) {
180        defmt::info!("--- MUSB Core Configuration ---");
181
182        // CONFIGDATA Details
183        defmt::info!("Core Configuration (from CONFIGDATA):");
184        defmt::info!("  - Automatic Bulk Packet Amalgamation (MPRxE): {}", self.mprxe);
185        defmt::info!("  - Automatic Bulk Packet Splitting (MPTxE): {}", self.mptxe);
186        // ... (other CONFIGDATA fields remain the same)
187        if self.big_endian {
188            defmt::warn!("  - Endianness: Big Endian. (Documentation states this should be Little Endian)");
189        } else {
190            defmt::info!("  - Endianness: Little Endian");
191        }
192        defmt::info!("  - High-Bandwidth Rx ISO Endpoint Support (HBRxE): {}", self.hbrxe);
193        defmt::info!("  - High-Bandwidth Tx ISO Endpoint Support (HBTxE): {}", self.hbtxe);
194        defmt::info!("  - FIFO Sizing: {}", self.dyn_fifo_sizing);
195        if self.soft_connect {
196            defmt::info!("  - Connection Type: Soft Connect/Disconnect");
197        } else {
198            defmt::warn!("  - Connection Type: Hard-wired. (Documentation states this should be Soft Connect)");
199        }
200        defmt::info!("  - UTMI+ Data Width: {}", self.utmi_data_width);
201
202        // FIFO Size Details
203        defmt::info!("Endpoint FIFO Configuration (from FIFOSIZE):");
204        match self.dyn_fifo_sizing {
205            FifoSizing::Dynamic => {
206                defmt::info!("  - Dynamic FIFO Sizing is enabled. The FIFOSIZE register is not applicable.");
207            }
208            FifoSizing::Static => {
209                let mut found_any = false;
210                for i in 1..=15 {
211                    let tx_size = self.tx_fifo_sizes[i];
212                    let rx_size = self.rx_fifo_sizes[i];
213
214                    if tx_size > 0 || rx_size > 0 {
215                        found_any = true;
216                        if tx_size > 0 {
217                            if rx_size == u16::MAX {
218                                defmt::info!("  - Endpoint {}: TX FIFO = {=u16} bytes, RX FIFO = (Shared with TX)", i, tx_size);
219                            } else if rx_size > 0 {
220                                defmt::info!("  - Endpoint {}: TX FIFO = {=u16} bytes, RX FIFO = {=u16} bytes", i, tx_size, rx_size);
221                            } else {
222                                defmt::info!("  - Endpoint {}: TX FIFO = {=u16} bytes, RX FIFO = (Not configured)", i, tx_size);
223                            }
224                        } else {
225                            // This case handles when only RX is configured (tx_size is 0)
226                            defmt::info!("  - Endpoint {}: TX FIFO = (Not configured), RX FIFO = {=u16} bytes", i, rx_size);
227                        }
228                    }
229                }
230                if !found_any {
231                    defmt::warn!("  - No configured static FIFOs found for endpoints 1-15.");
232                }
233            }
234        }
235
236        // EPINFO Details
237        defmt::info!("Endpoint Configuration (from EPINFO):");
238        if self.epinfo_is_zero {
239            defmt::warn!("  - The EPINFO register returned all zeros. It may be unimplemented or masked by the vendor.");
240        }
241        defmt::info!("  - Implemented TX Endpoints: {}", self.tx_endpoints);
242        defmt::info!("  - Implemented RX Endpoints: {}", self.rx_endpoints);
243
244        // RAMINFO Details
245        defmt::info!("RAM and DMA Configuration (from RAMINFO):");
246        if self.raminfo_is_zero {
247            defmt::warn!("  - The RAMINFO register returned all zeros. It may be unimplemented or masked by the vendor.");
248        }
249        defmt::info!("  - Implemented DMA Channels: {}", self.dma_channels);
250        defmt::info!("  - RAM Address Bus Width: {} bits", self.ram_bits);
251        
252
253        // LINKINFO Details
254        defmt::info!("Timing and Delay Configuration (from LINKINFO):");
255        if self.linkinfo_is_zero {
256             // Default is 0x5C, so 0 is suspicious and worth a warning.
257            defmt::warn!("  - The LINKINFO register returned all zeros. It may be unimplemented or masked by the vendor.");
258        }
259        let wtcon_us = (self.wtcon as f32) * 0.5333;
260        let wtid_ms = (self.wtid as f32) * 4.369;
261        defmt::info!("  - Connection Wait Time (WTCON): {} (~{=f32} us)", self.wtcon, wtcon_us);
262        defmt::info!("  - ID Reading Wait Time (WTID): {} (~{=f32} ms)", self.wtid, wtid_ms);
263        
264        // VPLEN Details
265        defmt::info!("VBUS Pulsing Charge Duration (VPLEN):");
266        if self.vplen_is_zero {
267            // Default is 0x3C
268            defmt::warn!("  - The VPLEN register returned all zeros. It may be unimplemented or masked by the vendor.");
269        }
270        let vplen_ms = (self.vplen as f32) * 0.5461;
271        defmt::info!("  - Value: {} (~{=f32} ms)", self.vplen, vplen_ms);
272        
273
274        // EOF Timing Details
275        defmt::info!("End-of-Frame (EOF) Gap Configuration:");
276        if self.hs_eof1_is_zero { // Default 0x80
277             defmt::warn!("  - High-Speed (HS_EOF1): Register returned all zeros. May be unimplemented or masked.");
278        }
279        let hs_eof1_us = (self.hs_eof1 as f32) * 0.1333;
280        defmt::info!("  - High-Speed (HS_EOF1): {} (~{=f32} us)", self.hs_eof1, hs_eof1_us);
281        
282        if self.fs_eof1_is_zero { // Default 0x77
283            defmt::warn!("  - Full-Speed (FS_EOF1): Register returned all zeros. May be unimplemented or masked.");
284        }
285        let fs_eof1_us = (self.fs_eof1 as f32) * 0.5333;
286        defmt::info!("  - Full-Speed (FS_EOF1): {} (~{=f32} us)", self.fs_eof1, fs_eof1_us);
287        
288        if self.ls_eof1_is_zero { // Default 0x72
289             defmt::warn!("  - Low-Speed (LS_EOF1): Register returned all zeros. May be unimplemented or masked.");
290        }
291        let ls_eof1_us = (self.ls_eof1 as f32) * 1.067;
292        defmt::info!("  - Low-Speed (LS_EOF1): {} (~{=f32} us)", self.ls_eof1, ls_eof1_us);
293        
294
295        defmt::info!("--- End of Configuration ---");
296    }
297}