-
-
Notifications
You must be signed in to change notification settings - Fork 80
[EV3] Four misc fixups #365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This prevents hearing a pop sound whenever the brick boots up. The amplifier stays on after any sound is played in order to prevent repeated popping noises.
lib/pbio/include/pbdrv/cache.h
Outdated
|
|
||
| // Accesses a variable via the uncached memory alias | ||
| #define PBDRV_UNCACHED(x) (*(volatile __typeof__(x) *)((uint32_t)(&(x)) + 0x10000000)) | ||
| #define PBDRV_EV3_UNCACHED(x) (*(volatile __typeof__(x) *)((uintptr_t)(&(x)) + PBDRV_EV3_UNCACHED_OFFSET)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You explained that these aren't necessarily specific to the EV3, so why are we making this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Er, it's specific to the way we're currently configuring the EV3, and you had originally suggested renaming it.
Perhaps I misunderstood what you were asking for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are putting it in include/pbdrv then it shouldn't be anything platform-specific by name. If we expect other platforms with memory managers that do something similar, then we could make a PBDRV_CONFIG_MM_UNCACHED_OFFSET config option, e.g. for the 0x10000000 and leave the macro names generic (no EV3).
Or if this is something that only EV3 drivers will ever use, we should move this to an "internal" header file that is specific to the EV3 memory manager.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking that the macros would be generic but would have platform-specific definitions.
Is there a generic "target is an EV3" macro? Perhaps these can be surrounded by #if PBDRV_EV3?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just added a new PBDRV_CONFIG_CACHE and PBDRV_CONFIG_CACHE_EV3 for this purpose.
| const char *name_str = qstr_str(name); | ||
|
|
||
| for (mpy_info_t *info = mpy_first; info < mpy_end; | ||
| for (mpy_info_t *info = mpy_first; (uintptr_t)info + sizeof(uint32_t) < (uintptr_t)mpy_end; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently only support 32-bit platforms, but I think this should be pointer-size aligned rather than 32-bit aligned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mpy_info_t is explicitly defined with a 32-bit size regardless of platform pointer size
uint8_t mpy_size[4];There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what that has to do with the binary file itself needing to be pointer-aligned in memory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It sounds like we are planning to fix alignment issues later.
The check here is sufficient for preventing an invalid read.
|
Added yet another miscellaneous fix which was found as I was going through other work |
lib/pbio/platform/ev3/platform.c
Outdated
| for (unsigned int i = 0; i < SYSTEM_RAM_SZ_MB; i++) { | ||
| uint32_t addr_phys = 0xC0000000 + i * MMU_SECTION_SZ; | ||
| uint32_t addr_virt = 0xD0000000 + i * MMU_SECTION_SZ; | ||
| uint32_t addr_virt = addr_phys + PBDRV_EV3_UNCACHED_OFFSET; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should move this function (renamed to pbdrv_cache_ev3_early_init) and pbdrv_cache_* out of platform.c to lib/pbio/drv/cache/cache_ev3.c to make a proper driver file for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems problematic to move because of all the MMU-related macros. Moving the other functions makes sense though.
This makes this common header extensible in case future platforms also require cache management. It also makes it clear which functionality is specific to how Pybricks sets up the EV3.
Because the storage layer rounds up code size to a multiple of the word size, it is possible for the set of code modules to have trailing padding. Previously, this could cause the module search function to not terminate as expected, read out of bounds, and crash.
Otherwise the recent addition of the .dma section defaults to PROGBITS and causes the entire .bss section to be filled with 0s *on disk*, thus bloating the firmware.elf file.
|
What is here is good, but I think the For example: pybricks-micropython/lib/pbio/drv/block_device/block_device_ev3.c Lines 106 to 118 in b77115b
Should be: static struct {
// This is used when transmitting so that the last byte clears CSHOLD.
uint32_t tx_last_word;
// This is used to hold the initial command to the SPI peripheral.
uint8_t spi_cmd_buf_tx[SPI_CMD_BUF_SZ] PBDRV_DMA_BUF;
// This is used to hold the replies to commands to the SPI peripheral.
uint8_t spi_cmd_buf_rx[SPI_CMD_BUF_SZ] PBDRV_DMA_BUF;
// This is used when SPI only needs to receive. It should always stay as 0.
uint8_t tx_dummy_byte;
// This is used when received data is to be discarded. Its value should be ignored.
uint8_t rx_dummy_byte;
} spi_dev_bufs;So that e.g And the align of the |
|
Actually... static struct {
// This is used when transmitting so that the last byte clears CSHOLD.
uint32_t tx_last_word;
// This is used when SPI only needs to receive. It should always stay as 0.
uint8_t tx_dummy_byte;
// This is used when received data is to be discarded. Its value should be ignored.
uint8_t rx_dummy_byte;
// This is used to hold the initial command to the SPI peripheral.
uint8_t spi_cmd_buf_tx[SPI_CMD_BUF_SZ] PBDRV_DMA_BUF;
// This is used to hold the replies to commands to the SPI peripheral.
uint8_t spi_cmd_buf_rx[SPI_CMD_BUF_SZ];
} spi_dev_bufs;Is probably sufficient. |
|
The alignment is not entirely sufficient, because there is a need to bump subsequent data out of the same cache line as well. This was the entire reason for the separate memory section. |
|
Having the TX and RX buffers share a cache line is fine (edit: and was intentional) in this particular case. The cache line is flushed for TX and invalidated when RX is finished, and only one operation can be in flight at a given time, so they cannot overlap. |
ok
The align attribute does (should do?) this. This came up in some work I was doing in Linux recently and pretty sure this is what I saw. |
|
I specifically saw the attribute being insufficient for this. For example, with this change: diff --git a/lib/pbio/include/pbdrv/cache.h b/lib/pbio/include/pbdrv/cache.h
index 2f036ff2..611d2464 100644
--- a/lib/pbio/include/pbdrv/cache.h
+++ b/lib/pbio/include/pbdrv/cache.h
@@ -28,7 +28,7 @@ void pbdrv_cache_prepare_after_dma(const void *buf, size_t sz);
#define PBDRV_CACHE_LINE_SZ 32
// Align data to a cache line, which is needed for clean RX DMA
-#define PBDRV_DMA_BUF __attribute__((aligned(PBDRV_CACHE_LINE_SZ), section(".dma")))
+#define PBDRV_DMA_BUF __attribute__((aligned(PBDRV_CACHE_LINE_SZ)))
#endif // PBDRV_CONFIG_CACHE
diff --git a/lib/pbio/platform/ev3/platform.ld b/lib/pbio/platform/ev3/platform.ld
index aaf520c2..b16b0608 100644
--- a/lib/pbio/platform/ev3/platform.ld
+++ b/lib/pbio/platform/ev3/platform.ld
@@ -66,11 +66,6 @@ SECTIONS
_bss_start = .;
*(.bss)
*(.bss.*)
- /* Put DMA RX buffers here so that they never share cache lines with
- unrelated data. This makes sure that they can be invalidated properly. */
- . = ALIGN(32);
- *(.dma)
- . = ALIGN(32);
_bss_end = .;
} > DDRI see in the map file So the |
|
Ah, OK. For it to work correctly, it looks like the align attribute needs to be inside of a struct (this is how we are doing in the Linux stuff I mentioned). static struct {
// This is used when transmitting so that the last byte clears CSHOLD.
uint32_t tx_last_word PBDRV_DMA_BUF;
// This is used to hold the initial command to the SPI peripheral.
uint8_t spi_cmd_buf_tx[SPI_CMD_BUF_SZ];
// This is used to hold the replies to commands to the SPI peripheral.
uint8_t spi_cmd_buf_rx[SPI_CMD_BUF_SZ];
// This is used when SPI only needs to receive. It should always stay as 0.
uint8_t tx_dummy_byte;
// This is used when received data is to be discarded. Its value should be ignored.
uint8_t rx_dummy_byte;
} spi_dev_bufs;Then we get the expected alignment before and after: So I think we should do it like this everywhere. Otherwise, we will need a separate section for every single buffer to do the alignment in the linker script. |
|
That doesn't work for the ADC's |
|
It currently doesn't create a unique section for each buffer. They all go into a single |
What is stopping us from putting it in a struct? |
|
We could. It seems slightly more error-prone than using a separate |
OK, I guess that works too. Still have a slight preference for not having an extra section though. |
|
The section doesn't appear in the final firmware.elf, since the linker script merges it into |
|
I know. The issue I have with the separate section is that we can't apply the attribute to a member of a struct if we ever need to do that. We can only apply it to a whole static struct. |
|
Ah, that is indeed the case. However, applying alignment to a single field of a struct also has potentially unexpected effects? (It'll change the alignment of the entire struct), so I would've never even thought to try doing that... (I always put it on the whole struct) |
.dmabuffers as NOBITS