Skip to content

Commit 82d3a26

Browse files
committed
feat(usbd): dramatically reduce size impact of USBBuiltin type
This commit dramatically reduces the size impact of the USBBuiltin type, especially in static mode, by: - Eliminating the heavyweight Python type for USBBuiltin. - Using a lightweight `mp_obj_usb_builtin_t` for runtime mode only, with dynamic attribute access. - Storing `builtin_driver` as a `uint8_t` directly in static mode. - Unifying the `mp_obj_usb_device_t` struct definition in `mp_usbd.h` with conditional compilation. - Re-introducing necessary `mp_usbd_init`, `mp_usbd_deinit`, `mp_usbd_runtime_string_cb`, and `MP_USBD_MAX_PEND_EXCS` definitions. Size impact for NUCLEO_H563ZI: - Runtime mode: Reduced from 514,632 bytes to 514,488 bytes (144 bytes reduction). - Static mode: Reduced from 512,256 bytes to 511,976 bytes (280 bytes reduction).
1 parent 21ad366 commit 82d3a26

File tree

3 files changed

+82
-161
lines changed

3 files changed

+82
-161
lines changed

extmod/machine_usb_device.c

Lines changed: 25 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -47,30 +47,22 @@
4747

4848
// USB class flags are defined in mp_usbd.h
4949

50-
// Structure for combinable built-in USB driver configurations
50+
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
51+
// Structure for the lightweight USBBuiltin object
5152
typedef struct _mp_obj_usb_builtin_t {
5253
mp_obj_base_t base;
5354
uint8_t flags; // Combination of USB_BUILTIN_FLAG_* values
5455
} mp_obj_usb_builtin_t;
5556

56-
const mp_obj_type_t machine_usb_device_type;
57+
// Forward declaration for the USBBuiltin type
5758
const mp_obj_type_t mp_type_usb_builtin;
59+
#endif
5860

59-
// Forward declaration for builtin config creation
60-
static mp_obj_t mp_usbd_create_builtin_config(uint8_t flags);
61+
const mp_obj_type_t machine_usb_device_type;
6162

6263
// Return true if any built-in driver is enabled
6364
bool mp_usb_device_builtin_enabled(const mp_obj_usb_device_t *usbd) {
64-
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
6565
return usbd->builtin_driver != USB_BUILTIN_FLAG_NONE;
66-
#else
67-
// In static mode, builtin_driver is an object
68-
if (mp_obj_is_type(usbd->builtin_driver, &mp_type_usb_builtin)) {
69-
mp_obj_usb_builtin_t *builtin = MP_OBJ_TO_PTR(usbd->builtin_driver);
70-
return builtin->flags != USB_BUILTIN_FLAG_NONE;
71-
}
72-
return false;
73-
#endif
7466
}
7567

7668
static mp_obj_t usb_device_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
@@ -84,16 +76,8 @@ static mp_obj_t usb_device_make_new(const mp_obj_type_t *type, size_t n_args, si
8476
o->base.type = &machine_usb_device_type;
8577

8678
// Initialize fields common to both minimal and full modes
87-
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
88-
// Runtime mode: read current class state to reflect default CDC configuration
8979
o->builtin_driver = mp_usbd_class_state.flags;
9080
o->active = tud_inited();
91-
#else
92-
// In static mode, USB is always initialized at boot
93-
// Class state is set during early boot, create a USBBuiltin object from it
94-
o->builtin_driver = mp_usbd_create_builtin_config(mp_usbd_class_state.flags);
95-
o->active = tud_inited();
96-
#endif
9781

9882
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
9983
// Initialize runtime-only fields
@@ -210,10 +194,7 @@ static mp_obj_t usb_device_active(size_t n_args, const mp_obj_t *args) {
210194
mp_usbd_init(); // Ensure TinyUSB has initialised by this point
211195

212196
// Update class state based on current builtin_driver
213-
if (mp_obj_is_type(usbd->builtin_driver, &mp_type_usb_builtin)) {
214-
mp_obj_usb_builtin_t *builtin = MP_OBJ_TO_PTR(usbd->builtin_driver);
215-
mp_usbd_class_state.flags = builtin->flags;
216-
}
197+
mp_usbd_class_state.flags = usbd->builtin_driver;
217198
} else {
218199
// Disable all classes when deactivating
219200
mp_usbd_class_state.flags = USB_BUILTIN_FLAG_NONE;
@@ -304,65 +285,26 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(usb_device_config_obj, 1, usb_device_config);
304285
// Device descriptor object (needed by bitfield builtin functions)
305286
static const MP_DEFINE_BYTES_OBJ(builtin_default_desc_dev_obj,
306287
&mp_usbd_builtin_desc_dev, sizeof(tusb_desc_device_t));
307-
308-
// Forward declarations for bitfield builtin functions
309-
static uint8_t mp_usbd_get_itf_max(uint8_t flags);
310-
static uint8_t mp_usbd_get_ep_max(uint8_t flags);
311-
static uint8_t mp_usbd_get_str_max(uint8_t flags);
312-
static const uint8_t *mp_usbd_get_builtin_desc_cfg(uint8_t flags);
313-
static size_t mp_usbd_get_desc_cfg_len(uint8_t flags);
314-
315288
#endif // MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
316289

290+
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
317291
// Create a new builtin configuration object with specified flags
318292
static mp_obj_t mp_usbd_create_builtin_config(uint8_t flags) {
319293
mp_obj_usb_builtin_t *builtin = mp_obj_malloc(mp_obj_usb_builtin_t, &mp_type_usb_builtin);
320294
builtin->flags = flags;
321295
return MP_OBJ_FROM_PTR(builtin);
322296
}
323297

324-
// Print method for USBBuiltin objects
325-
static void builtin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
326-
mp_obj_usb_builtin_t *self = MP_OBJ_TO_PTR(self_in);
327-
mp_printf(print, "USBBuiltin(0x%02x)", self->flags);
328-
}
329-
330-
// Binary operator for equality checks (implements == and !=)
331-
static mp_obj_t builtin_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
332-
if (op == MP_BINARY_OP_EQUAL) {
333-
mp_obj_usb_builtin_t *lhs = MP_OBJ_TO_PTR(lhs_in);
334-
uint8_t rhs_flags;
335-
if (mp_obj_is_type(rhs_in, &mp_type_usb_builtin)) {
336-
rhs_flags = ((mp_obj_usb_builtin_t *)MP_OBJ_TO_PTR(rhs_in))->flags;
337-
} else if (mp_obj_is_int(rhs_in)) {
338-
rhs_flags = mp_obj_get_int(rhs_in);
339-
} else {
340-
return mp_const_false;
341-
}
342-
return mp_obj_new_bool(lhs->flags == rhs_flags);
343-
}
344-
return MP_OBJ_NULL;
345-
}
346-
347-
// Unary operator for int() and bool() conversions
348-
static mp_obj_t builtin_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
349-
mp_obj_usb_builtin_t *self = MP_OBJ_TO_PTR(self_in);
350-
if (op == MP_UNARY_OP_INT_MAYBE || op == MP_UNARY_OP_BOOL) {
351-
return MP_OBJ_NEW_SMALL_INT(self->flags);
352-
}
353-
return MP_OBJ_NULL;
354-
}
355-
356-
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
357-
// Dynamic attribute access for builtin config objects (runtime mode only)
298+
// Dynamic attribute access for the lightweight USBBuiltin object
358299
static void builtin_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
359300
mp_obj_usb_builtin_t *self = MP_OBJ_TO_PTR(self_in);
360301

361302
if (dest[0] == MP_OBJ_NULL) {
362303
// Load attribute
363304
if (attr == MP_QSTR_desc_cfg) {
364-
size_t len = mp_usbd_get_desc_cfg_len(self->flags);
365-
dest[0] = mp_obj_new_bytes(mp_usbd_get_builtin_desc_cfg(self->flags), len);
305+
size_t len;
306+
const uint8_t *desc = mp_usbd_get_builtin_desc_cfg(self->flags, &len);
307+
dest[0] = mp_obj_new_bytes(desc, len);
366308
} else if (attr == MP_QSTR_itf_max) {
367309
dest[0] = MP_OBJ_NEW_SMALL_INT(mp_usbd_get_itf_max(self->flags));
368310
} else if (attr == MP_QSTR_ep_max) {
@@ -375,61 +317,14 @@ static void builtin_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
375317
}
376318
}
377319

378-
// Helper functions for dynamic property calculation (runtime mode only)
379-
static uint8_t mp_usbd_get_itf_max(uint8_t flags) {
380-
uint8_t count = 0;
381-
if ((flags & USB_BUILTIN_FLAG_CDC) && MICROPY_HW_USB_CDC) {
382-
count += 2; // CDC uses 2 interfaces
383-
}
384-
if ((flags & USB_BUILTIN_FLAG_MSC) && MICROPY_HW_USB_MSC) {
385-
count += 1;
386-
}
387-
return count;
388-
}
389-
390-
static uint8_t mp_usbd_get_ep_max(uint8_t flags) {
391-
uint8_t ep_max = 1; // Endpoint 0 is always used
392-
if ((flags & USB_BUILTIN_FLAG_CDC) && MICROPY_HW_USB_CDC) {
393-
ep_max = 3; // CDC uses endpoints 1, 2, 3
394-
}
395-
if ((flags & USB_BUILTIN_FLAG_MSC) && MICROPY_HW_USB_MSC) {
396-
ep_max = (ep_max > 2) ? ep_max : 2; // MSC uses endpoints 1, 2
397-
}
398-
return ep_max;
399-
}
400-
401-
static uint8_t mp_usbd_get_str_max(uint8_t flags) {
402-
uint8_t str_max = 1; // String 0 is always used (language descriptor)
403-
if ((flags & USB_BUILTIN_FLAG_CDC) && MICROPY_HW_USB_CDC) {
404-
str_max = 4; // CDC uses strings 1, 2, 3, 4
405-
}
406-
if ((flags & USB_BUILTIN_FLAG_MSC) && MICROPY_HW_USB_MSC) {
407-
str_max = (str_max > 2) ? str_max : 2; // MSC uses strings 1, 2
408-
}
409-
return str_max;
410-
}
411-
412-
static const uint8_t *mp_usbd_get_builtin_desc_cfg(uint8_t flags) {
413-
return mp_usbd_generate_desc_cfg_unified(flags, mp_usbd_desc_cfg_buffer);
414-
}
415-
416-
static size_t mp_usbd_get_desc_cfg_len(uint8_t flags) {
417-
return mp_usbd_get_descriptor_cfg_len_from_flags(flags);
418-
}
419-
#endif // MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
420-
421-
// Type definition for builtin config objects
320+
// Type definition for the lightweight USBBuiltin object
422321
MP_DEFINE_CONST_OBJ_TYPE(
423322
mp_type_usb_builtin,
424323
MP_QSTR_USBBuiltin,
425-
MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE,
426-
print, builtin_print,
427-
unary_op, builtin_unary_op,
428-
binary_op, builtin_binary_op
429-
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
430-
, attr, builtin_attr
431-
#endif
324+
MP_TYPE_FLAG_NONE,
325+
attr, builtin_attr
432326
);
327+
#endif
433328

434329
// Calculate BUILTIN_DEFAULT flags at compile time
435330
#if MICROPY_HW_USB_CDC && MICROPY_HW_USB_MSC
@@ -487,11 +382,11 @@ static void usb_device_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
487382
// Load attribute.
488383
if (attr == MP_QSTR_builtin_driver) {
489384
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
490-
// Runtime mode: create USBBuiltin object from flags
385+
// Runtime mode: create lightweight USBBuiltin object from flags
491386
dest[0] = mp_usbd_create_builtin_config(self->builtin_driver);
492387
#else
493-
// Static mode: return the stored object directly
494-
dest[0] = self->builtin_driver;
388+
// Static mode: return integer flags
389+
dest[0] = mp_obj_new_int(self->builtin_driver);
495390
#endif
496391
} else {
497392
// Continue lookup in locals_dict.
@@ -505,35 +400,22 @@ static void usb_device_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
505400
if (self->active) {
506401
mp_raise_OSError(MP_EINVAL); // Need to deactivate first
507402
}
508-
#else
509-
// In static mode, allow changing builtin_driver when active
510-
// This will update the class state and trigger re-enumeration
511403
#endif
512404

513-
// Extract flags from input (integer or USBBuiltin object)
514-
uint8_t flags;
515-
if (mp_obj_is_type(dest[1], &mp_type_usb_builtin)) {
516-
// USBBuiltin object - extract flags
517-
mp_obj_usb_builtin_t *builtin = MP_OBJ_TO_PTR(dest[1]);
518-
flags = builtin->flags;
519-
} else if (mp_obj_is_int(dest[1])) {
520-
// Integer value - use directly
521-
flags = mp_obj_get_int(dest[1]);
522-
} else {
523-
mp_raise_TypeError(MP_ERROR_TEXT("builtin_driver must be int or USBBuiltin"));
405+
// Extract flags from input (must be an integer)
406+
if (!mp_obj_is_int(dest[1])) {
407+
mp_raise_TypeError(MP_ERROR_TEXT("builtin_driver must be an int"));
524408
}
409+
uint8_t flags = mp_obj_get_int(dest[1]);
525410

526411
// Update the internal class state based on flags
527412
mp_usbd_class_state.flags = flags;
528413

529-
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
530-
// In runtime mode, store flags directly
414+
// Store the integer flags directly
531415
self->builtin_driver = flags;
532-
#else
533-
// In static mode, store as a USBBuiltin object
534-
self->builtin_driver = mp_usbd_create_builtin_config(flags);
535416

536-
// If USB is active, trigger re-enumeration to update the host
417+
#if !MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
418+
// In static mode, if USB is active, trigger re-enumeration to update the host
537419
if (self->active) {
538420
#ifndef NO_QSTR
539421
// Disconnect and reconnect to trigger enumeration with new descriptors

shared/tinyusb/mp_usbd.h

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -184,14 +184,18 @@ extern const uint8_t mp_usbd_builtin_desc_cfg[MP_USBD_BUILTIN_DESC_CFG_LEN];
184184
extern uint8_t mp_usbd_desc_cfg_buffer[MP_USBD_BUILTIN_DESC_CFG_LEN];
185185
const uint8_t *mp_usbd_get_default_desc(void);
186186
const uint8_t *mp_usbd_generate_desc_cfg_unified(uint8_t flags, uint8_t *buffer);
187+
uint8_t mp_usbd_get_itf_max(uint8_t flags);
188+
uint8_t mp_usbd_get_ep_max(uint8_t flags);
189+
uint8_t mp_usbd_get_str_max(uint8_t flags);
190+
const uint8_t *mp_usbd_get_builtin_desc_cfg(uint8_t flags, size_t *len);
187191
#endif
188192

189193
void mp_usbd_task_callback(mp_sched_node_t *node);
190194

195+
// Need init/deinit for both runtime and static, but runtime is more complex
191196
#if !MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
192-
193197
static inline void mp_usbd_init(void) {
194-
// Without runtime USB support, this can be a thin wrapper wrapper around tusb_init()
198+
// Without runtime USB support, this can be a thin wrapper around tusb_init()
195199
// which is called in the below helper function.
196200
mp_usbd_init_tud();
197201
}
@@ -200,27 +204,24 @@ static inline void mp_usbd_deinit(void) {
200204
// Called in soft reset path. No-op if no runtime USB devices require cleanup.
201205
}
202206

203-
// Minimal USB device structure for static mode (builtin_driver control only)
204-
typedef struct {
205-
mp_obj_base_t base;
206-
mp_obj_t builtin_driver; // Points to a USBBuiltin constant object
207-
bool active; // Has the user set the USB device active?
208-
} mp_obj_usb_device_t;
209-
210207
#else
211-
// Runtime USB Device support requires more complex init/deinit
212208
void mp_usbd_init(void);
213209
void mp_usbd_deinit(void);
214-
215210
const char *mp_usbd_runtime_string_cb(uint8_t index);
216211

217212
// Maximum number of pending exceptions per single TinyUSB task execution
218213
#define MP_USBD_MAX_PEND_EXCS 2
219214

220-
// Full runtime USB device structure
215+
#endif
216+
217+
// This struct is defined for both static and runtime builds.
218+
// In static mode, only the base, builtin_driver and active fields are used.
221219
typedef struct {
222220
mp_obj_base_t base;
221+
uint8_t builtin_driver; // Bitfield of USB_BUILTIN_FLAG_* values
222+
bool active; // Has the user set the USB device active?
223223

224+
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
224225
mp_obj_t desc_dev; // Device descriptor bytes
225226
mp_obj_t desc_cfg; // Configuration descriptor bytes
226227
mp_obj_t desc_strs; // List/dict/similar to look up string descriptors by index
@@ -231,28 +232,25 @@ typedef struct {
231232
mp_obj_t control_xfer_cb;
232233
mp_obj_t xfer_cb;
233234

234-
uint8_t builtin_driver; // Bitfield of USB_BUILTIN_FLAG_* values
235-
236-
bool active; // Has the user set the USB device active?
237235
bool trigger; // Has the user requested the active state change (or re-activate)?
238236

239237
// Temporary pointers for xfer data in progress on each endpoint
240-
// Ensuring they aren't garbage collected until the xfer completes
238+
// Ensuring they arent garbage collected until the xfer completes
241239
mp_obj_t xfer_data[CFG_TUD_ENDPPOINT_MAX][2];
242240

243241
// Pointer to a memoryview that is reused to refer to various pieces of
244242
// control transfer data that are pushed to USB control transfer
245-
// callbacks. Python code can't rely on the memoryview contents
243+
// callbacks. Python code cant rely on the memoryview contents
246244
// to remain valid after the callback returns!
247245
mp_obj_array_t *control_data;
248246

249247
// Pointers to exceptions thrown inside Python callbacks. See
250248
// usbd_callback_function_n().
251249
mp_uint_t num_pend_excs;
252250
mp_obj_t pend_excs[MP_USBD_MAX_PEND_EXCS];
251+
#endif
253252
} mp_obj_usb_device_t;
254253

255-
#endif
256254

257255
// Return true if any built-in driver is enabled
258256
bool mp_usb_device_builtin_enabled(const mp_obj_usb_device_t *usbd);

shared/tinyusb/mp_usbd_descriptor.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,44 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
301301
#endif // !MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
302302

303303
#endif // MICROPY_HW_ENABLE_USBDEV
304+
305+
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
306+
// Helper functions for dynamic property calculation (runtime mode only)
307+
uint8_t mp_usbd_get_itf_max(uint8_t flags) {
308+
uint8_t count = 0;
309+
if ((flags & USB_BUILTIN_FLAG_CDC) && MICROPY_HW_USB_CDC) {
310+
count += 2; // CDC uses 2 interfaces
311+
}
312+
if ((flags & USB_BUILTIN_FLAG_MSC) && MICROPY_HW_USB_MSC) {
313+
count += 1;
314+
}
315+
return count;
316+
}
317+
318+
uint8_t mp_usbd_get_ep_max(uint8_t flags) {
319+
uint8_t ep_max = 1; // Endpoint 0 is always used
320+
if ((flags & USB_BUILTIN_FLAG_CDC) && MICROPY_HW_USB_CDC) {
321+
ep_max = 3; // CDC uses endpoints 1, 2, 3
322+
}
323+
if ((flags & USB_BUILTIN_FLAG_MSC) && MICROPY_HW_USB_MSC) {
324+
ep_max = (ep_max > 2) ? ep_max : 2; // MSC uses endpoints 1, 2
325+
}
326+
return ep_max;
327+
}
328+
329+
uint8_t mp_usbd_get_str_max(uint8_t flags) {
330+
uint8_t str_max = 1; // String 0 is always used (language descriptor)
331+
if ((flags & USB_BUILTIN_FLAG_CDC) && MICROPY_HW_USB_CDC) {
332+
str_max = 4; // CDC uses strings 1, 2, 3, 4
333+
}
334+
if ((flags & USB_BUILTIN_FLAG_MSC) && MICROPY_HW_USB_MSC) {
335+
str_max = (str_max > 2) ? str_max : 2; // MSC uses strings 1, 2
336+
}
337+
return str_max;
338+
}
339+
340+
const uint8_t *mp_usbd_get_builtin_desc_cfg(uint8_t flags, size_t *len) {
341+
*len = mp_usbd_get_descriptor_cfg_len_from_flags(flags);
342+
return mp_usbd_generate_desc_cfg_unified(flags, mp_usbd_desc_cfg_buffer);
343+
}
344+
#endif

0 commit comments

Comments
 (0)