-
Notifications
You must be signed in to change notification settings - Fork 3k
SPIFBlockDevice doesn't play nice on shared SPI bus #11732. #12681
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
…x by use valid constructor SPI
@mtomczykmobica, thank you for your changes. |
@@ -1129,29 +1129,22 @@ void SDBlockDevice::_spi_wait(uint8_t count) | |||
|
|||
void SDBlockDevice::_spi_init() | |||
{ | |||
_spi.lock(); |
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 are the locks removed in here?
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.
lock and unlock is made in select and unselect.
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.
but _select and _deselect are not called here.
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.
lock
and unlock
are called separately in each frequency
, format
and set_default_write_value
-function.
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.
@VeijoPesonen exactly Jarno is right.
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.
Ok, so there is no reason to protect the sequence somehow. Which raises a question that should SPI.h expose those lock/unlock-functions if all the public functions call those implicitly? @kjbracey-arm any thoughts.
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.
Arguably lock/unlock shouldn't be public API any more. Most driver classes have virtual protected lock/unlock that lets you override them for a UnlockedX
class or whatever, but for this class they were then made public to let apps use them as a a bus lock.
select
/deselect
basically replace them for app use. But there's no way to deprecate lock
as a public method while keeping it available for the override purpose.
Now, you don't normally need to manually call select
or deselect
either, but you do need to call them if you want to group multiple transactions together.
An analogue in POSIX is flock
/funlock
- each stdio call like printf
does call them internally, but you'd want to call them yourself if you wanted to keep two printf
s together.
So these two successfully lock against each other (X can't occur between A and B).
// Task 1
spi.write(X);
// Task 2
spi.select();
spi.write(A);
spi.write(B);
spi.deselect();
But only because X has the implicit select.
(Don't make the mistake I made a few years ago of writing code like that when I had a bus for which a single op DIDN'T have a built-in implicit mutex lock. If it didn't you'd need to put the manual select around the single op too. I got confused between the "lock two things" together function and the "mutex against other tasks" function).
@@ -470,9 +470,6 @@ spif_bd_error SPIFBlockDevice::_spi_send_read_command(int read_inst, uint8_t *bu | |||
uint32_t dummy_bytes = _dummy_and_mode_cycles / 8; | |||
int dummy_byte = 0; | |||
|
|||
// csel must go low for the entire command (Inst, Address and Data) | |||
_cs = 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.
Shouldn't this be replaced with _spi.select()
?
This also applies to the lines below...
Maybe there is a good reason, but I don't understand 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 is write function:
int SPI::write(int value)
{
select();
int ret = spi_master_write(&_peripheral->spi, value);
deselect();
return ret;
}
so there is done select and deselect.
additionaly lock and unlock is made in select and unselect.
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.
True about select and deselect, but looking at the comment csel must go low for the entire command (Inst, Address and Data)
flipping the chip select off and on in every write function actually changes the behaviour of the whole function here. Chip select isn't low for the entire command now. Was that intentional?
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 i correct understand SPI is only interface between block device, so when we want write/ready to block device something we select cs and do operations on spi (one block device operation can take few spi actions). Function _
int DataFlashBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
_ make few that operation and i cannot see reason to all time hold down cs (in my opinion cs should be down only when write spi operation).
@SeppoTakalo please review my understanding.
_spi_wait(10); | ||
_spi.unlock(); | ||
} | ||
|
||
void SDBlockDevice::_select() |
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 don't actually understand what is the purpose of this function. I would have assumed lock would be acquired here and released in _deselect
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.
spi_wait() is:
// SPI function to wait for count
void SDBlockDevice::_spi_wait(uint8_t count)
{
for (uint8_t i = 0; i < count; ++i) {
_spi.write(SPI_FILL_CHAR);
}
}
and _spi.write:
int SPI::write(int value)
{
select();
int ret = spi_master_write(&_peripheral->spi, value);
deselect();
return ret;
}
and deselect:
void SPI::deselect()
{
if (--_select_count == 0) {
_set_ssel(1);
}
unlock();
}
So unlock is in spi_wait.
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 like some clarification about the issue mentioned about chip select must be low for the entire read command.
@@ -470,9 +470,6 @@ spif_bd_error SPIFBlockDevice::_spi_send_read_command(int read_inst, uint8_t *bu | |||
uint32_t dummy_bytes = _dummy_and_mode_cycles / 8; | |||
int dummy_byte = 0; | |||
|
|||
// csel must go low for the entire command (Inst, Address and Data) | |||
_cs = 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.
True about select and deselect, but looking at the comment csel must go low for the entire command (Inst, Address and Data)
flipping the chip select off and on in every write function actually changes the behaviour of the whole function here. Chip select isn't low for the entire command now. Was that intentional?
If i correct understand SPI is only interface between block device, so when we want write/ready to block device something we select cs and do operations on spi (one block device operation can take few spi actions). Function _
_ make few that operation and i cannot see reason to all time hold down cs (in my opinion cs should be down only when write spi operation). |
SPI flash memories define transactions as a complete sequences, I don't believe we could toggle the select line within a block read/write operation without having side effect. Instead, I suggest that you use SPI::select() and SPI::deselect() in places where Secondly, study whether you can completely drop |
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.
AFAICT looks ok
CI started |
Test run: FAILEDSummary: 3 of 6 test jobs failed Failed test jobs:
|
605f631
to
5e4b161
Compare
OK, I've changed code as suggested. let's wait for result. |
5e4b161
to
064bd61
Compare
Even if you could (from the device protocol point of view) it might make it non-thread-safe. (Unless there's another higher-level locking mechanism). Another thread might be accessing the same device. If your two commands are "set address pointer" and "read data from address", or something like that, then you need to maintain some lock on the device to stop anyone else messing with the address pointer between the two. Doing the If they were slow commands, on the other hand, a high-level mutex separate from the SPI bus lock would be worthwhile. You might not want to hold the SPI bus for the entire duration of an erase cycle, say.
Absolutely, you need to do this to maintain the same grouping and timing, adding only the locking.
But I would be a bit wary about pulling out those timing fudges. SD devices are many and varied, and I wouldn't be at all surprised if some couldn't cope with the "immediate" select that |
SPI select and deselect functions have included lock and unlock function. So it is save. |
select and deselect have included lock and unlock functions. New fix implements using select and deselect SPI functions.
Done in this way now.
Done
|
components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.cpp
Outdated
Show resolved
Hide resolved
064bd61
to
c4458a0
Compare
components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.cpp
Outdated
Show resolved
Hide resolved
c4458a0
to
39d1f29
Compare
Pull request has been modified.
…ck to use select and deselect, use correct spi constructor
39d1f29
to
699a689
Compare
CI started |
Test run: SUCCESSSummary: 6 of 6 test jobs passed |
Fixes #11732 by use valid constructor SPI
With current
SPI
passuse_gpio_ssel
to theSPI
constructor. SPI write command use lock/unlock and select deselect inside. So no more needed to use this methods outside the SPI class.Summary of changes
Impact of changes
Migration actions required
Documentation
Not needed to change documentation.
Pull request type
Test results
Reviewers
@SeppoTakalo
@VeijoPesonen
@0xc0170