Skip to content

Commit 839149c

Browse files
committed
ohci: Align TDs to cache lines
1 parent 2d979ee commit 839149c

File tree

1 file changed

+29
-4
lines changed

1 file changed

+29
-4
lines changed

src/portable/ohci/ohci.h

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,31 @@ typedef struct {
6161

6262
TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" );
6363

64+
// An OHCI host controller is controlled using data structures placed in memory (RAM).
65+
// It needs to both read and write these data structures (as defined by the OHCI specification),
66+
// and this can be mentally conceptualized similar to two software threads running on
67+
// two different CPUs. In order to prevent a _data race_ where data gets corrupted,
68+
// the CPU and the OHCI host controller need to agree on how the memory should be accessed.
69+
// In this driver, we do this by transferring logical ownership of transfer descriptors (TDs)
70+
// between the CPU and the OHCI host controller. Only the device which holds the logical ownership
71+
// is allowed to read or write the TD. This ownership is not visible anywhere in the code,
72+
// but it instead must be inferred based on the logical state of the transfer.
73+
//
74+
// If dcache-supporting mode is enabled, we need to do additional manual cache operations
75+
// in order to correctly transfer this logical ownership and prevent data corruption.
76+
// In order to do this, we also choose to align each OHCI TD so that it doesn't
77+
// share CPU cache lines with other TDs. This is because manual cache operations
78+
// can only be performed on cache line granularity. In other words, one cache line is
79+
// the _smallest_ amount that can be read/written at a time. If there were to be multiple TDs
80+
// in the same cache line, they would be required to always have the same logical ownership.
81+
// This ends up being impossible to guarantee, so we choose a design which avoids the situation entirely.
82+
//
83+
// TDs have a minimum alignment requirement according to the OHCI specification. This is 16 bytes for
84+
// a general TD but 32 bytes for an isochronous TD. It happens that typical CPU cache line sizes are usually
85+
// a power of 2 at least 32. In order to simplify code later in this file, we assume this
86+
// as an additional requirement.
87+
TU_VERIFY_STATIC( (CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 0) % 32 == 0, "cache line not multiple of 32" );
88+
6489
// common link item for gtd and itd for list travel
6590
// use as pointer only
6691
typedef struct TU_ATTR_ALIGNED(16) {
@@ -69,7 +94,7 @@ typedef struct TU_ATTR_ALIGNED(16) {
6994
uint32_t reserved2;
7095
}ohci_td_item_t;
7196

72-
typedef struct TU_ATTR_ALIGNED(16)
97+
typedef struct TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 16)
7398
{
7499
// Word 0
75100
uint32_t used : 1;
@@ -92,7 +117,7 @@ typedef struct TU_ATTR_ALIGNED(16)
92117
uint8_t* buffer_end;
93118
} ohci_gtd_t;
94119

95-
TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == 16, "size is not correct" );
120+
TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 16, "size is not correct" );
96121

97122
typedef struct TU_ATTR_ALIGNED(16)
98123
{
@@ -129,7 +154,7 @@ typedef struct TU_ATTR_ALIGNED(16)
129154

130155
TU_VERIFY_STATIC( sizeof(ohci_ed_t) == 16, "size is not correct" );
131156

132-
typedef struct TU_ATTR_ALIGNED(32)
157+
typedef struct TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 32)
133158
{
134159
/*---------- Word 1 ----------*/
135160
uint32_t starting_frame : 16;
@@ -152,7 +177,7 @@ typedef struct TU_ATTR_ALIGNED(32)
152177
volatile uint16_t offset_packetstatus[8];
153178
} ochi_itd_t;
154179

155-
TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" );
180+
TU_VERIFY_STATIC( sizeof(ochi_itd_t) == CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 32, "size is not correct" );
156181

157182
typedef struct {
158183
uint16_t expected_bytes; // up to 8192 bytes so max is 13 bits

0 commit comments

Comments
 (0)