diff options
| author | R <[email protected]> | 2025-09-12 22:16:46 +0100 |
|---|---|---|
| committer | R <[email protected]> | 2025-09-12 22:17:10 +0100 |
| commit | 839149c5c0b667a8080de39c85cdbdd291be4dc5 (patch) | |
| tree | c903545fb447344bc78359a1f61ff21ef65efa11 | |
| parent | 2d979ee4a9e7de6b956c373842e8c81c2e6382b4 (diff) | |
ohci: Align TDs to cache lines
| -rw-r--r-- | src/portable/ohci/ohci.h | 33 |
1 files changed, 29 insertions, 4 deletions
diff --git a/src/portable/ohci/ohci.h b/src/portable/ohci/ohci.h index 94bad5df7..12c411764 100644 --- a/src/portable/ohci/ohci.h +++ b/src/portable/ohci/ohci.h @@ -61,6 +61,31 @@ typedef struct { TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" ); +// An OHCI host controller is controlled using data structures placed in memory (RAM). +// It needs to both read and write these data structures (as defined by the OHCI specification), +// and this can be mentally conceptualized similar to two software threads running on +// two different CPUs. In order to prevent a _data race_ where data gets corrupted, +// the CPU and the OHCI host controller need to agree on how the memory should be accessed. +// In this driver, we do this by transferring logical ownership of transfer descriptors (TDs) +// between the CPU and the OHCI host controller. Only the device which holds the logical ownership +// is allowed to read or write the TD. This ownership is not visible anywhere in the code, +// but it instead must be inferred based on the logical state of the transfer. +// +// If dcache-supporting mode is enabled, we need to do additional manual cache operations +// in order to correctly transfer this logical ownership and prevent data corruption. +// In order to do this, we also choose to align each OHCI TD so that it doesn't +// share CPU cache lines with other TDs. This is because manual cache operations +// can only be performed on cache line granularity. In other words, one cache line is +// the _smallest_ amount that can be read/written at a time. If there were to be multiple TDs +// in the same cache line, they would be required to always have the same logical ownership. +// This ends up being impossible to guarantee, so we choose a design which avoids the situation entirely. +// +// TDs have a minimum alignment requirement according to the OHCI specification. This is 16 bytes for +// a general TD but 32 bytes for an isochronous TD. It happens that typical CPU cache line sizes are usually +// a power of 2 at least 32. In order to simplify code later in this file, we assume this +// as an additional requirement. +TU_VERIFY_STATIC( (CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 0) % 32 == 0, "cache line not multiple of 32" ); + // common link item for gtd and itd for list travel // use as pointer only typedef struct TU_ATTR_ALIGNED(16) { @@ -69,7 +94,7 @@ typedef struct TU_ATTR_ALIGNED(16) { uint32_t reserved2; }ohci_td_item_t; -typedef struct TU_ATTR_ALIGNED(16) +typedef struct TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 16) { // Word 0 uint32_t used : 1; @@ -92,7 +117,7 @@ typedef struct TU_ATTR_ALIGNED(16) uint8_t* buffer_end; } ohci_gtd_t; -TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == 16, "size is not correct" ); +TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 16, "size is not correct" ); typedef struct TU_ATTR_ALIGNED(16) { @@ -129,7 +154,7 @@ typedef struct TU_ATTR_ALIGNED(16) TU_VERIFY_STATIC( sizeof(ohci_ed_t) == 16, "size is not correct" ); -typedef struct TU_ATTR_ALIGNED(32) +typedef struct TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 32) { /*---------- Word 1 ----------*/ uint32_t starting_frame : 16; @@ -152,7 +177,7 @@ typedef struct TU_ATTR_ALIGNED(32) volatile uint16_t offset_packetstatus[8]; } ochi_itd_t; -TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" ); +TU_VERIFY_STATIC( sizeof(ochi_itd_t) == CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 32, "size is not correct" ); typedef struct { uint16_t expected_bytes; // up to 8192 bytes so max is 13 bits |
