-
-
Notifications
You must be signed in to change notification settings - Fork 80
[EV3] Implement a SPI flash driver for the EV3 #314
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
|
Hmm, there seems to be a strange heisenbug here. I'm currently working on integrating the ADC into this code, but something I've done seems to have made code that (I promise!) was previously working no longer reach the REPL (hanging without any debug output). Still investigating... |
|
With the latest commit the flash + ADC seem to both work (not at the same time but instead using the suggested hack). I rewrote the entire ADC sampling logic to use DMA. |
Rebasing on some changes I just pushed might help as I found and fixed a bug with the wrong entry point address in the uImage. Different builds could have the linker put the |
dlech
left a comment
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 didn't do an in-depth review since I know this is still a work in progress, but I like what I see so far. 😄
| SPIModeConfigure(SOC_SPI_0_REGS, SPI_MASTER_MODE); | ||
| unsigned int spipc0 = SPI_SPIPC0_SOMIFUN | SPI_SPIPC0_SIMOFUN | SPI_SPIPC0_CLKFUN | SPI_SPIPC0_SCS0FUN0 | SPI_SPIPC0_SCS0FUN3; | ||
| SPIPinControl(SOC_SPI_0_REGS, 0, 0, &spipc0); | ||
| SPIDefaultCSSet(SOC_SPI_0_REGS, (1 << 0) | (1 << 3)); |
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.
Let's use macros here so we know what these bits are.
SPI_DATA_FORMATx are not really better though. I would introduce a new one that says SPI_BITS_PER_WORD(x)
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 added macros for 0 and 3
Not sure what you want to do about SPI_DATA_FORMATx? There is the large comment explaining how format 0 is allocated for the SPI flash while format 1 is allocated for the ADC.
| SPIConfigClkFormat(SOC_SPI_0_REGS, SPI_CLK_POL_LOW | SPI_CLK_OUTOFPHASE, SPI_DATA_FORMAT1); | ||
| SPIShiftMsbFirst(SOC_SPI_0_REGS, SPI_DATA_FORMAT1); | ||
| SPICharLengthSet(SOC_SPI_0_REGS, 16, SPI_DATA_FORMAT1); | ||
| } |
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 would also set other config explicitly here even if it is the same as flash.
For example, SPIDefaultCSSet() is an interesting one. Omitting SPI_CSHOLD there is important for using the ADC with DMA so that it toggles the CS line between each word.
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.
Which other config settings were you thinking of? SPIDefaultCSSet doesn't configure the CSHOLD bit. That bit is in fact cleared by the DMA commands to SPIDAT1
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.
CSHOLD was the main one. I guess I copied and pasted the wrong fucntion name. But it is nice to see what the full config is all in once place without relying on the flash memory driver setting up some of this for us. (And could save us some trouble if we ever tweak the flash memory driver without realizing it also affects the ADC).
Sounds great! Looking forward to check it out after the weekend and help you get it merged. |
|
I've cleaned up the history of this PR and marking it as ready for review. |
We still need to add some logic to selectively enable the block device driver and the ADC driver, which both use SPI0 on EV3.
Can be used as a starting point for new contributors for the EV3 driver.
…ash driver. This is derived from the W25Qxx_STM32 driver, but with most of the logic removed to make room for the EV3 implementation.
…performing flash commands. This is all of the logic needed to program the EDMA3 controller and the SPI peripheral, but the logic to read/write flash is not implemented yet.
…riting. This completes the storage implementation for EV3.
The ADC driver will be rewritten to use DMA, but the code for setting it up is placed in the SPI flash driver in order to centralize the logic in one place.
This stub driver uses pbio rather than Contiki, and it is ready to be integrated with the SPI flash driver (including waiting for the SPI bus to be available), but the actual logic for talking to the ADC chip has not been implemented yet.
This ensures that the specified frequency is never exceeced.
|
Thanks! Rebased and applied I've been working on pybricks/support#2264, but I propose to merge yours first for simplicity. Since EV3 is under heavy development, I think we can merge it as-is and address open TODOs in follow up pull requests. |
| (1 << 12) | \ | ||
| (1 << 11) | \ | ||
| (((x) & 0xf) << 7) | \ | ||
| (1 << 6) |
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.
Would be nice to have some macros or comments to explain these bits.
| // XXX We probably need to figure out how atomicity works between the DMA and the CPU. | ||
| // For now, read the value twice and assume it's good (not torn) if the values are the same. |
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.
Instead of worrying about tearing, I would do a double buffer. Just before we call pbdrv_adc_callbacks in the protothread where we drive the sampling, copy the DMA buffer to a 2nd buffer. We know we are not in the middle of a DMA operation at this point. Then have this function use the values from the 2nd buffer so that we know the data is good.
| SPIConfigClkFormat(SOC_SPI_0_REGS, SPI_CLK_POL_LOW | SPI_CLK_OUTOFPHASE, SPI_DATA_FORMAT1); | ||
| SPIShiftMsbFirst(SOC_SPI_0_REGS, SPI_DATA_FORMAT1); | ||
| SPICharLengthSet(SOC_SPI_0_REGS, 16, SPI_DATA_FORMAT1); | ||
| } |
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.
CSHOLD was the main one. I guess I copied and pasted the wrong fucntion name. But it is nice to see what the full config is all in once place without relying on the flash memory driver setting up some of this for us. (And could save us some trouble if we ever tweak the flash memory driver without realizing it also affects the ADC).
| static inline void spi0_setup_for_flash() { | ||
| HWREG(SOC_SPI_0_REGS + SPI_SPIDELAY) = 0; | ||
|
|
||
| *(volatile uint16_t *)(SOC_SPI_0_REGS + SPI_SPIDAT1 + 2) = |
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.
Why not use SPIDat1Config()?
|
|
||
| // Helper functions for setting up the high control bits of a data transfer | ||
| static inline void spi0_setup_for_flash() { | ||
| HWREG(SOC_SPI_0_REGS + SPI_SPIDELAY) = 0; |
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.
Why not use SPIDelayConfigure()?
|
|
||
| // SPI module data formats | ||
| SPIClkConfigure(SOC_SPI_0_REGS, SOC_SPI_0_MODULE_FREQ, SPI_CLK_SPEED_FLASH, SPI_DATA_FORMAT0); | ||
| // For reasons which have not yet been fully investigated, attempting to switch between |
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.
In the TRM on the DEFSEL field description of SPIDAT1, it says:
Note: Preselecting a Format Register. Writing to just the control field (using byte writes) does
not initiate any SPI transfer in master mode. This feature can be used to set up SPIx_CLK phase
or polarity before actually starting the transfer by just updating the DFSEL fields in the control field
to select the required phase/polarity combination.
so maybe not a glitch, but intentional?
So it sounds like we should swap the order of the config here.
| SPIOutOfReset(SOC_SPI_0_REGS); | ||
| SPIModeConfigure(SOC_SPI_0_REGS, SPI_MASTER_MODE); | ||
| unsigned int spipc0 = SPI_SPIPC0_SOMIFUN | SPI_SPIPC0_SIMOFUN | SPI_SPIPC0_CLKFUN | SPI_SPIPC0_SCS0FUN0 | SPI_SPIPC0_SCS0FUN3; | ||
| SPIPinControl(SOC_SPI_0_REGS, 0, 0, &spipc0); |
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.
Would be nice to split SPIPinControl() into two function so we don't have this goofy pass by reference when writing.
| SPIReset(SOC_SPI_0_REGS); | ||
| SPIOutOfReset(SOC_SPI_0_REGS); | ||
| SPIModeConfigure(SOC_SPI_0_REGS, SPI_MASTER_MODE); | ||
| unsigned int spipc0 = SPI_SPIPC0_SOMIFUN | SPI_SPIPC0_SIMOFUN | SPI_SPIPC0_CLKFUN | SPI_SPIPC0_SCS0FUN0 | SPI_SPIPC0_SCS0FUN3; |
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 is another place where I would rather just have the ADC reconfigure things here instead. Or at least add some more comments on what parts of this are for the ADC. I.e. one chip select is for this driver and one is for the ADC driver.
This PR implements a driver for the EV3 SPI flash memory.