Skip to content

Update SPI hal API #6782

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

Closed
wants to merge 16 commits into from
Closed

Update SPI hal API #6782

wants to merge 16 commits into from

Conversation

ithinuel
Copy link
Member

@ithinuel ithinuel commented May 1, 2018

Description

This PR bring a new and well defined SPI api at HAL level.

This disables SPI for all targets as tests needs to be added & all low-level drivers needs to be re-qualified against these tests.

Pull request type

[ ] Fix
[ ] Refactor
[ ] New target
[x] Feature
[ ] Breaking change

@0xc0170
Copy link
Contributor

0xc0170 commented May 2, 2018

@ithinuel Who should review this new specs?

@c1728p9 and @bulislaw or anyone else involved?

Copy link
Member

@bulislaw bulislaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Wilfried that looks awesome!

hal/spi_api.h Outdated
#if DEVICE_SPI
/**
* \defgroup hal_new_spi Serial peripheral interface HAL API.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hal_new_spi -> hal_spi

hal/spi_api.h Outdated
#else
/** Non-asynch SPI HAL structure
/**
* SPI object.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to make a note (highlight) of that in the PG.

hal/spi_api.h Outdated
*/
typedef struct spi_s spi_t;

#endif
/**
* This structure groups all initialization parameters required by an SPI interface.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a SPI I think

hal/spi_api.h Outdated
* This structure groups all initialization parameters required by an SPI interface.
*/
typedef struct spi_init_t {
bool is_master; /**< True to configure the device in Master mode */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need detailed description of all the fields, what happens when I set is_master to false? Will it be slave? what will happen if I give SS and set is_master to false? etc

hal/spi_api.h Outdated
PinName MCLK;

uint32_t fill_symbol; /**< only the n lower bits will be used. */
uint32_t clock_frequency; /**< MCLK frequency in Hz. */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have requirements on range?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The supported clock frequency range highly depends on the device capability (PLL, clock divisors etc).

hal/spi_api.h Outdated
* The total number of bytes sent and received will be the maximum of
* tx_length and rx_length. The bytes written will be padded with the
* value 0xff.
* It sends `tx_count` symbols from the buffer pointed by `tx` or a place holder is `tx` is NULL.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r a place holder is tx is NULL -> r a place holder if tx is NULL

Also you are using place holder here and fill symbol before can we align please.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch 👍

hal/spi_api.h Outdated
*
* @param[in] obj The SPI peripheral to check
* @return The module number
* @param[in] handle This.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are confusing This you shouldn't use it in different contexts. First you refer it to the spi_t context and now to async. Can you reword all of the occurrences to spell out what exactly it is.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed the object type is not the same. I'll reword that.

hal/spi_api.h Outdated
/**
* \defgroup AsynchSPI Asynchronous SPI Hardware Abstraction Layer
* @{
* Frees the SPI instance.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to specify what does it mean? eg should it be clocked out?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how clocking should be defined as if you only use masters then you could even clock the peripheral out between transaction where you might want to keep it enabled in slave mode.
Unless slave selection is handled externally to this API and then we can rely 100% on the spi_transfer to manage clocking of the peripheral.

hal/spi_api.h Outdated
* @param[in] event The logical OR of events to be registered
* @param[in] handler SPI interrupt handler
* @param[in] hint A suggestion for how to use DMA with this transfer
/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get why do we have it, can't we reimplement the slave driver using the above? If not let's create a doxygen group spi_hal_deprecated or something like that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could implement spi_read and spi_write but not spi_data_available.
However, we would not need it by handling externally the SS event and triggering a spi_tranfer from there instead of polling on the peripheral to see if something is coming in.

hal/spi_api.h Outdated
* channel towards a slave (or a master).
* Two SPI instances using the same peripheral can be identified by their SS pin.
*
* # Defined behaviour
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we copy the behaviours into each of the functions descriptions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to slightly rephrase them in each header to make them more readable.

@bulislaw bulislaw requested a review from c1728p9 May 2, 2018 12:08
hal/spi_api.h Outdated
* @param[in] ssel The pin to use for SSEL
spi_mode_t mode; /**< Transmission mode. See spi_mode_t. */

PinName SS; /**< Slave select pin. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are these pin names uppercase?

hal/spi_api.h Outdated

bool clock_phase; /**< True if data line is valid when leaving active state. */
bool clock_polarity; /**< True if the clock's rest state is high (+Vcc). */
uint32_t word_length; /**< Length of a symbol in bit. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this then uint32_t ? The maximum is 32 I would assume?

hal/spi_api.h Outdated
*/
void spi_format(spi_t *obj, int bits, int mode, int slave);
typedef struct spi_transfer_args_t {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this first should be spi_transfer_args_s, however I see where this is going. But from the codebase, tags are named with suffix _s (see _api headers for instance).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I switched to _t because doxygen was not linking them properly when I generated the doc locally. All references to spi_transfer_args_t would not work unless spi_transfer_args_s is used in documentation too, which I find misleading.
For consistency it is better to use either one or the other every where.
Also, if we decide to use _s for typdef struct identifier_s identifier_t;, for consistency, we would also be required to name enumerations with _e (or something similar) to distinguish typedef enum identifier_e identifier_t;.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at enums, seems like no tags there just a name.

The doxygen issue - was not aware of it. If we keep both the same or leave it as it is now in HAL, this should be probably captured in the code style.

hal/spi_api.h Outdated
SPI_EVENT_TYPE_ON_DONE, /**< The operation has completed successfully. */
SPI_EVENT_TYPE_ON_ABORT, /**< The operation has been aborted. */
SPI_EVENT_TYPE_ON_ERROR /**< An error occured. */
} spi_even_type_t;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing t in _even_ ?

hal/spi_api.h Outdated
*/
typedef enum spi_event_type_t {
SPI_EVENT_TYPE_ON_DONE, /**< The operation has completed successfully. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we changing callbacks to on_done or on_abort - separate callbacks for each event? (previously events were bitmasked and a user could choose multiple).

hal/spi_api.h Outdated
typedef enum spi_event_type_t {
SPI_EVENT_TYPE_ON_DONE, /**< The operation has completed successfully. */
SPI_EVENT_TYPE_ON_ABORT, /**< The operation has been aborted. */
SPI_EVENT_TYPE_ON_ERROR /**< An error occured. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If error happens, is there any hal function that could retrieve what has happened (mode fault, data loss) ? Or not needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no uniform way to implement that. Error cause & reporting vary too much across the devices.
Retrieving such information would have to be platform specific.

Copy link
Contributor

@0xc0170 0xc0170 May 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We used to have overflow, and noticed in CMSIS SPI driver they have this as well (as data loss) and also bus fault (for spi slave).

hal/spi_api.h Outdated
*/
uint8_t spi_get_module(spi_t *obj);
spi_async_handle_t* spi_async_transfer(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

async as a suffix (spi_transfer and spi_transfer_async) ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be seen as a constructor for spi_async_handle_t. Thus all method taking an instance of spi_async_handle_t as its this is prefixed by spi_async_.

hal/spi_api.h Outdated
*
* @param[in] obj The SPI peripheral to check
* @return The module number
* @return A reference counted handle to the transaction.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this returning? spi_async_handle_t is specified as being implementation specific , why ? what is expected to be defined there? each async transfer generates unique handle?

What if SPI peripheral is already taken - already transfer registered (SPI_0 peripheral already in use)? What this returns?

Copy link
Member Author

@ithinuel ithinuel May 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where the peripheral is in use or not, this method creates and schedule a transaction to be processed as the peripheral becomes available.

This handle is used by the _abort method. If the user does not want to abort he can spi_async_free_handle() right away.
The resources attached to the handle (passed in spi_async_tranfer()) must stay allocated until completion of the transaction. This completion is notified by a call to the callback.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if for some reason the transaction cannot be scheduled (queue is full etc) this returns NULL and does not take ownership of the data. The callback is therefore not invoked.

Copy link
Contributor

@0xc0170 0xc0170 May 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont follow, not clear to me what spi_async_handle_t is the meaning and why is it passed nor how transactions should work (I would assume transactions are handled in the upper layer, this one is just getting data and handles it).

Queue? HAL driver does not have any queue , does it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I see. I will rework that to make it clearer then.

hal/spi_api.h Outdated
*/
void spi_async_abort(spi_async_handle_t* handle);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is handle enough here? I would expect obj to be passed as well to be able to access internal SPI registers/drivers

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is up to the driver to keep track of which peripheral a scheduled transaction is attached to.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to abort, do anything with peripheral, we need "this" pointer. or this one is just touching handle data , nothing connected to peripheral? It might be answered by my above question

@cmonr
Copy link
Contributor

cmonr commented May 7, 2018

@ithinuel Looks like this still needs some massaging. Travis isn't happy.

@ithinuel
Copy link
Member Author

ithinuel commented May 7, 2018

@cmonr LittleFS' test depends on SPI. I'm not sure if LittleFS should be completely disabled or should I raise an issue to make this test ran only if SPI is enabled ?

@cmonr
Copy link
Contributor

cmonr commented May 7, 2018

Hmm, I could see this going either way since this would need to be dealt with before merging into master anyways.

@geky , thoughts?

Imo, disabling the test(s) for now would be fine since this is a feature branch, but you run the risk of things changing out from underneath the branch that makes re-merging a pita.

@cmonr cmonr mentioned this pull request May 8, 2018
5 tasks
hal/spi_api.h Outdated
* \struct spi_async_handle_t
* This needs to be declared and defined by the low level device driver.
* It is used to eventually abort an async request before its completion. see spi_async_abort.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would be easier to read with a linebreak after each function's comments.

hal/spi_api.h Outdated
*/
typedef struct spi_init_s {
bool is_master; /**< True to configure the device in Master mode */
bool msb_first; /**< True to send/receive the most significant bit first. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This being a C-library, you should add #include <stdbool.h>, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As well as #include <stdint.h> for uint32_t and #include "device.h" for PinName. Good catch !

@bmcdonnell-ionx
Copy link
Contributor

bmcdonnell-ionx commented May 8, 2018

Question

With this API, how would a user implement an asynchronous receiving SPI Slave? (Whether simplex or duplex, for simplicity I'm just speaking about the receiving aspect here.)

Observation, Suggestion

It looks to me like you'd have a thread that calls spi_async_transfer() repeatedly to capture data as they arrive. I'm inferring that the callback will only be executed max once per call to spi_async_transfer(). Is that right? Assuming so...

Data would likely be captured into a CircularBuffer. Then you're probably going to collect those data into messages, and act on those messages - which may take some time. While processing your received message, you don't want to miss any data, so then you need yet another thread to do the processing. So, now you need two threads - right?

I think you should include the ability to attach callbacks which will execute as interrupt handlers. Then in this scenario, the interrupt handler can capture data from your microcontroller SPI peripheral FIFO (e.g. into a circular buffer), and [you / the user / the SPISlave driver] only need to run one thread to handle messages.

My Implementation

... That's what I did in the project I described in #6504. I implemented it for LPC4088. The API is attached here for your reference. (In my project, I implemented my enhancements as separate files - an "extended" API, and an EnhancedSPISlave driver which uses it. I had thought per #6504 that I'd integrate those into Mbed OS for the PR.)

spi_api_extended.h.txt

I have an enum in my EnhancedSPISlave driver which mirrors the one in the API.

The tricky thing is that I think the interrupt conditions often vary from micro to micro. I imagined that you'd just add conditions to SpiIrqType, and #define some of them in/out according to the target.

@ithinuel ithinuel changed the title Update SPI peripheral Update SPI hal API May 8, 2018
@ithinuel
Copy link
Member Author

ithinuel commented May 8, 2018

With this API, how would a user implement an asynchronous receiving SPI Slave? (Whether simplex or duplex, for simplicity I'm just speaking about the receiving aspect here.)

In such case a user would, on SS assertion, schedule a reception passing a

spi_transfer_args_t transfer_args = {
    .tx = NULL, .tx_count = 0,
    .rx = the_rx_buffer,
    .rx_count = the_expected_rx_size_or_rx_size
};

The callback would be triggered when rx_count reaches 0 or SS is de-asserted.

It looks to me like you'd have a thread that calls spi_async_transfer() repeatedly to capture data as they arrive. I'm inferring that the callback will only be executed max once per call to spi_async_transfer(). Is that right? Assuming so...

That is a correct assumption.

While processing your received message, you don't want to miss any data, so then you need yet another thread to do the processing. So, now you need two threads - right?

Not necessarily. You can for example schedule two reception only transfers. Both callback would ping a semaphore that the duty thread is waiting on. This is in a way some double buffering.
Once the first transfer is completed (rx_count symbol is reached), [you/the user/the SPISlave class] can process and schedule another transfer (eventually a response) while the rest of frame/message is being received.

It is the responsibility of the hal implementation to pull data from the fifo (if there's any) and pushing them into the buffer (not circular). Whether it be using an interrupt, a DMA or even a thread polling on the reception register, this is implementation details.
The last triggered transaction would be completed by reaching the transaction requirements rx_count & tx_count. Some target may even be able to detect the end of the transaction when SS is de-asserted (if SS is controlled in hardware).
What we might eventually need is some arguments in the callback telling how many symbols were received/sent.

NB: I am preparing an update to this PR moving the async API to sai_api_async.h.
This will ease the understanding of how synchronous and asynchronous API are related.

@bmcdonnell-ionx
Copy link
Contributor

@ithinuel,

The callback would be triggered when rx_count reaches 0 or SS is de-asserted.

What if the messages are streaming, the master is not using SS as a delimiter, and the message length is not known until the end of message is detected using some in-band means?

You can for example schedule two reception only transfers.

What if the message handling takes longer than two transfers? Or if the user [etc] wants to send a message with the second call, but doesn't know what that message will be until after the first? Or if the user wants to stream full duplex messages which are not simultaneously delimited?

@ithinuel
Copy link
Member Author

ithinuel commented May 8, 2018

@bmcdonnell-ionx
Nothing prevents the driver (C++) to schedule transfers 1 symbol at a time and to expose the interrupt context to the user.
The user could then do from here whatever (s)he wants.

@bmcdonnell-ionx
Copy link
Contributor

@ithinuel,

Nothing prevents the driver (C++) to schedule transfers 1 symbol at a time and to expose the interrupt context to the user.

Do I understand correctly as follows? The API can only register one-shot interrupt handlers. This will generally be implemented internally as disabling the interrupt after every occurrence.

For a streaming mode of operation as discussed, you would require the driver or user to repeatedly register these one-shot interrupt handlers, which are disabled after every symbol by the API.

@ithinuel
Copy link
Member Author

ithinuel commented May 8, 2018

@bmcdonnell-ionx Sorry, I think I actually missed your point.

The aim of this API is to provide a way to offload the transmission and reception of the symbols to an interrupt or, even better, a DMA if available.

As the method used to process these transactions is a hal implementation details, the [driver(C++)/user] is unable to use the interrupt to process each symbol individually.

A slave could process a stream of data using large buffer thus inducing some latency. This is something to be expected when using a DMA. Even though some platform offer multiple interrupts from the DMA (1/4 full, half full etc) this is not portable/consistent enough across platforms to be reliably used in this API.

Do you have a concrete use case (a specific chip or set up) of a streamed SPI protocol that require live computation of the output that I could use ?

@studavekar
Copy link
Contributor

Disabled littlefs tests

/morph build

@mbed-ci
Copy link

mbed-ci commented May 9, 2018

Build : FAILURE

Build number : 1957
Build artifacts/logs : http://mbed-os.s3-website-eu-west-1.amazonaws.com/?prefix=builds/6782/

@geky
Copy link
Contributor

geky commented May 9, 2018

@studavekar thanks!

@bmcdonnell-ionx
Copy link
Contributor

@ithinuel,

As the method used to process these transactions is a hal implementation details, the [driver(C++)/user] is unable to use the interrupt to process each symbol individually.

I don't know that processing each symbol individually is important. I do think the ability to process the streams independently with messages of varying and unknown lengths is. I think it's desirable to be able to make it a dumb pipe as such.

A slave could process a stream of data using large buffer thus inducing some latency. This is something to be expected when using a DMA. Even though some platform offer multiple interrupts from the DMA (1/4 full, half full etc) this is not portable/consistent enough across platforms to be reliably used in this API.

This scheme of interrupting on chunks of data is part of what motivated me to design a streaming protocol for my project in the first place.

Another part is that the SPI peripheral on my target (LPC4088) uses "frames" which are delimited by the Slave Select (SS) signal. Frames are constant-width of (user-configurable) 4 to 16 bits each. The internal FIFOs are 8-frames each for Tx and Rx. This means that on this platform, SS is not usable for delimiting messages. Thus the dumb pipe / streaming protocol I devised (details below).

Do you have a concrete use case (a specific chip or set up) of a streamed SPI protocol that require live computation of the output that I could use ?

I'm not sure exactly what you mean by "concrete". I can describe what I did in my project in more detail.

The SPI master and slave devices (both microcontrollers) reside on separate boards, connected via cable, each running their own applications.

My EnhancedSPISlave runs on Mbed OS on the LPC4088, underclocked to 24 MHz. The SPI bus acts as a half-duplex dumb pipe, with 0xff as a fill character. Messages are in XML format (thus we have < / > delimiters). Messages consist of commands sent from the master, and responses from the slave. One command yields one response.

Timely responses are desirable, but it's not hard realtime. (Arguably not realtime at all.) By decoupling the inherent synchronization between Tx and Rx in the lower layer protocol, we don't have to have the lower layer (SPI) protocol coupled to the upper layer message timing.

On the slave side, my application attaches interrupt handlers to the driver, which:

  • Read received symbols from the micro's SPI peripheral Rx FIFO into a receiving CircularBuffer, and signal the processing thread that data is available.
  • Write data to the SPI peripheral Tx FIFO. It writes any symbols enqueued in the sending CircularBuffer, or fill characters when CircularBuffer::empty().

@cmonr
Copy link
Contributor

cmonr commented Jun 4, 2018

@bulislaw @0xc0170 @deepikabhavnani @mprse @bmcdonnell-ionx @c1728p9 Looks like this is ready for a re-review!

@cmonr cmonr dismissed stale reviews from c1728p9 and 0xc0170 June 4, 2018 16:18

Comments appear to be addressed.

c1728p9
c1728p9 previously requested changes Jun 7, 2018
hal/spi_api.h Outdated
*/
void spi_frequency(spi_t *obj, int hz);
const spi_capabilities_t *const spi_get_capabilities(const spi_pins_t *pins);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capabilities should be per device and not per pin.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this level of abstraction, device have already been abstracted in favour of channel.
spi_pins_t identifies channel that can then internally be translated to device.

As capabilities are required before initialisation, it cannot rely on a spi_t channel instance.

cf https://github.com/ithinuel/mbed-os/blob/967bd308b01e6b04758678f73535d1fddd6bb262/hal/spi_api.h#L119

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this function returns address of the spi_capabilities_t structure? In such scenario spi_get_capabilities() function would have to allocate special memory for the result on heap.
I think that the better option is to take address of the spi_capabilities_t structure as a parameter and fills the structure with proper data.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because the capabilities are not dynamic and should be defined as static structures.

const spi_capabilities_t my_capabilities_for_spi1 = {
[...]
};
const spi_capabilities_t my_capabilities_for_spi2= {
[...]
};

This method should identify the correct structure and return a pointer to this flash stored object.

hal/spi_api.h Outdated
*/
int spi_slave_receive(spi_t *obj);
void spi_init(spi_t *obj, const spi_init_t *init);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is init the only way to set the frequency and other settings? This should support at least changing the frequency after init, and probably the other settings as well. I know the current SD card driver connects at 100KHz and then once reading the supported speed switches to a higher frequency.

Copy link
Member Author

@ithinuel ithinuel Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, it was agreed with @bulislaw that as there is no use-case where frequency & settings are changed on-the-fly/mid transfer and that it only happens at the early stage of the channel lifetime it is fine to free the channel & re-initialise it.

It is unlikely to be a problem for the slave, neither electrically wise as pull-ups should be provided externally for the sake of startup/reset safety, nor software wise as such operations only happen while SS is not asserted .

hal/spi_api.h Outdated
bool clock_polarity; /**< True if the clock's rest state is high (+Vcc). */
bool continuous_mode; /**< True to use the continuous mode. */

spi_mode_t mode; /**< Transmission mode. See spi_mode_t. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition of the spi_mode_t type has been removed. It looks like mode field should be removed from spi_init_t.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Fixed

Copy link
Contributor

@bmcdonnell-ionx bmcdonnell-ionx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor questions and suggestions.

* # Defined behaviours

## Defined behaviours
* - A repeating transfer must not restart until the invokation of the completion callback has not returned.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo - "invocation" (c not k).
Here and again below.

* - Freeing buffers before freeing the associated transaction.
*
* # Lexicon
* ## `Completion`/`Cancelletion`/`Abortion`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider a different word/phrase in place of "abortion" (here and elsewhere), to avoid unrelated connotations. "Abort" or "aborting" are fine if the context fits, IMO.

#include "hal/spi_api.h"

#if DEVICE_SPI
#if DEVICE_SPI_ASYNCH
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think DEVICE_SPI_ASYNCH has so far only been implemented on a very few targets. (I think I heard one vendor implemented it for their own product, but others didn't follow.) Should it remain as an optionally-implemented feature, or will it be required for all SPI implementations? If required, the #define should go away, right? If optional, will we see more targets implementing it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually a lot of target do implement it.
All the stm32 family, and a lot of other vendors including SiLabs, Atmel, Nordic etc.
It is still optional as some device cannot implement it though.

@cmonr cmonr dismissed c1728p9’s stale review June 21, 2018 15:26

New commits.

@cmonr
Copy link
Contributor

cmonr commented Jun 21, 2018

Copy link

@deepikabhavnani deepikabhavnani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spi_format and spi_frequency API's are missing from new HAL header file.
SD specification states initialize device at 100-400Khz and later device switches to 25Mhz once initialization is complete. SD cards will not be accessible if frequency is not updated runtime.

Also how do you plan to support multiple SPI slave devices operating on different configuration. Whenever SPI driver acquires SPI bus it does frequency/format change for that particular peripheral.
https://github.com/ARMmbed/mbed-os/blob/master/drivers/SPI.cpp#L84

@screamerbg
Copy link
Contributor

There's a huge Mbed partner impact from these changes that I cannot agree with. This is not a specification, but almost feel like a full SPI HAL rewrite. How is this going to be mitigated?
CC @maclobdell @toyowata @samchuarm @bentcooke

@ithinuel
Copy link
Member Author

@deepikabhavnani Change in frequency (and/or format) are done between transfers when SS is de-asserted which is supported by this API.

Format and frequency selection in Slave mode on SS assertion are handled by hal implementation and does not require to be exposed and handled by higher layer. Some target like the MK66F supports multiple configuration in master mode. Other targets may support that in slave mode too.

@maclobdell
Copy link
Contributor

Is there a design document or overview of the proposed architecture including a review of the customer/partner/developer impacts? Breaking changes generate quite a lot of difficulties and work, so they must be justified by benefits to our partner and developer ecosystems - and of course buy-in by those people.

@ithinuel ithinuel mentioned this pull request Jun 26, 2018
@cmonr
Copy link
Contributor

cmonr commented Jul 3, 2018

@ithinuel In aiming to keep this PR warm, are there any updates?

@ithinuel
Copy link
Member Author

ithinuel commented Jul 3, 2018

@cmonr I am prepare a design document (.md) that will be placed somewhere in the hal subdirectory.
This will serve as a kind of RFC material to explain the incentive behind these modifications and keep track of the various questions that are raised and answered.

@cmonr
Copy link
Contributor

cmonr commented Jul 3, 2018

@ithinuel I'm not sure if you've ever experienced this, but once the conversation in a PR passes ~150 messages, users may start getting the GitHub Unicorn indicating that they could not load the page.

Feel free to consider migrating this to an issue or opening another PR. Afaik, there's no other current workaround.

@deepikabhavnani
Copy link

@ithinuel

Format and frequency selection in Slave mode on SS assertion are handled by hal implementation

I would be interested in understanding how this is done at HAL layer, do we save format / frequency of each slave (SPI instance) in HAL layer? We do not have any synchronization at HAL layer it was always done at driver level, how will we handle runtime frequency change?

Runtime frequency change is the requirement for SD driver.

@ithinuel
Copy link
Member Author

Closing as per @cmonr comment.
A new one will soon be created.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.