SPI: SCLK polarity (CPOL) is inverted #1583

Open
opened 2023-12-18 12:03:09 -06:00 by maxgerhardt · 18 comments
maxgerhardt commented 2023-12-18 12:03:09 -06:00 (Migrated from github.com)

(derived from https://github.com/earlephilhower/arduino-pico/issues/1900)

Wikipedia says

grafik

so for CPOL = 0, the SCLK pin should idle LOW before and after a SPI transfer.

However, testing with

#include <hardware/spi.h>
static int _sck = 2;
static int _mosi = 3;
static int _miso = 4;
static int _cs = 5;
static spi_inst_t* _spi = spi0;

int main(){
  gpio_set_function(_sck, GPIO_FUNC_SPI);
  gpio_set_function(_mosi, GPIO_FUNC_SPI);
  gpio_set_function(_miso, GPIO_FUNC_SPI);
  gpio_set_function(_cs, GPIO_FUNC_SPI);

  spi_init(_spi, 100000);
  spi_set_format(_spi, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);

  while(1) {
    uint8_t value = 0xAA;
    (void) spi_write_blocking(_spi, &value, sizeof(value));
    delay(100);
  }
  return 0;
}

I see

grafik

on GP2, 3, 4 and 5 respectively. This is exactly inverted, it idles HIGH. Wut?

Note that changing to SPI_CPOL_1 in the above example will make the clock idle low, which is again opposite to how it should be. No matter what, in my logic analyzer, I always have to select the opposite SPI mode / clock polarity than what I set in the code to make it decodable.

grafik

(derived from https://github.com/earlephilhower/arduino-pico/issues/1900) [Wikipedia says](https://en.wikipedia.org/wiki/Serial_Peripheral_Interface#Clock_polarity_and_phase) ![grafik](https://github.com/earlephilhower/arduino-pico/assets/26485477/0768859d-4b1e-4a13-a372-dc0d542fbf53) so for CPOL = 0, the SCLK pin should idle LOW before and after a SPI transfer. However, testing with ```cpp #include <hardware/spi.h> static int _sck = 2; static int _mosi = 3; static int _miso = 4; static int _cs = 5; static spi_inst_t* _spi = spi0; int main(){ gpio_set_function(_sck, GPIO_FUNC_SPI); gpio_set_function(_mosi, GPIO_FUNC_SPI); gpio_set_function(_miso, GPIO_FUNC_SPI); gpio_set_function(_cs, GPIO_FUNC_SPI); spi_init(_spi, 100000); spi_set_format(_spi, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); while(1) { uint8_t value = 0xAA; (void) spi_write_blocking(_spi, &value, sizeof(value)); delay(100); } return 0; } ``` I see ![grafik](https://github.com/earlephilhower/arduino-pico/assets/26485477/dc8aab6c-cc36-4990-8c7e-e76872ce9cd2) on GP2, 3, 4 and 5 respectively. This is exactly inverted, it idles HIGH. Wut? Note that changing to `SPI_CPOL_1` in the above example will make the clock idle low, which is again opposite to how it should be. No matter what, in my logic analyzer, I always have to select the opposite SPI mode / clock polarity than what I set in the code to make it decodable. ![grafik](https://github.com/earlephilhower/arduino-pico/assets/26485477/dc412472-d894-43df-9b6e-bf2d655cf8fc)
maxgerhardt commented 2024-01-17 13:30:48 -06:00 (Migrated from github.com)

Push.

Push.
peterharperuk commented 2024-08-13 08:46:21 -05:00 (Migrated from github.com)

Sorry it took so long. I ran your code on rp2040 (and rp2350) and it seems to work correctly. So I'm at a bit of a loss why you're seeing that. Screen grab is where SPI_CPOL_0. If I change to SPI_CPOL_1 it uses clock idle high.
Screenshot 2024-08-13 144217

Sorry it took so long. I ran your code on rp2040 (and rp2350) and it seems to work correctly. So I'm at a bit of a loss why you're seeing that. Screen grab is where SPI_CPOL_0. If I change to SPI_CPOL_1 it uses clock idle high. ![Screenshot 2024-08-13 144217](https://github.com/user-attachments/assets/e39367fb-0c57-4f22-b913-e2e25f49bd6e)
peterharperuk commented 2024-11-06 11:13:38 -06:00 (Migrated from github.com)

Checked again - seems to work ok for me. Mysterious!

Checked again - seems to work ok for me. Mysterious!
maxgerhardt commented 2024-11-06 11:46:05 -06:00 (Migrated from github.com)

Have there been multiple revisions of the RP2040 silicon, and this only happens on the older one? I'm very sure I measured this correctly on my board. Any way to read out the silicon rev from a register?

Have there been multiple revisions of the RP2040 silicon, and this only happens on the older one? I'm very sure I measured this correctly on my board. Any way to read out the silicon rev from a register?
peterharperuk commented 2024-11-06 12:24:38 -06:00 (Migrated from github.com)

Yes. I would expect the following to show "2"

uint version = (chip_id & SYSINFO_CHIP_ID_REVISION_BITS) >> SYSINFO_CHIP_ID_REVISION_LSB;
printf("Chip version %d\n", version);

chip_version.zip
chip_version.uf2.zip

Yes. I would expect the following to show "2" uint version = (chip_id & SYSINFO_CHIP_ID_REVISION_BITS) >> SYSINFO_CHIP_ID_REVISION_LSB; printf("Chip version %d\n", version); [chip_version.zip](https://github.com/user-attachments/files/17651414/chip_version.zip) [chip_version.uf2.zip](https://github.com/user-attachments/files/17651433/chip_version.uf2.zip)
maxgerhardt commented 2024-11-06 12:53:46 -06:00 (Migrated from github.com)

Running this on my Pico, I get

Chip version 1

though. Is that SPI thing a known errata there?

Running this on my Pico, I get ``` Chip version 1 ``` though. Is that SPI thing a known errata there?
Wren6991 commented 2024-11-07 04:33:49 -06:00 (Migrated from github.com)

Is that SPI thing a known errata there?

No, the only non-ROM change between B1 and B2 was fixing the USB enumeration erratum (RP2040-E5)

> Is that SPI thing a known errata there? No, the only non-ROM change between B1 and B2 was fixing the USB enumeration erratum (RP2040-E5)
peterharperuk commented 2024-11-07 05:33:40 -06:00 (Migrated from github.com)

I've manged to find B0 and B1 devices (can you check what you have by takin a photo of the text on the chip?) and it still appears as if CPOL works correctly. So I still don't understand what you're seeing. I was seeing CS behaving oddly on my B0 but changing it to GP1 fixed that - maybe just dodgy solder on this device? You could try using GP6 for the SPI0 clock or switch to SPI1 (e.g. GP10-13?). Just to see if the other peripheral behaves the same.

I've manged to find B0 and B1 devices (can you check what you have by takin a photo of the text on the chip?) and it still appears as if CPOL works correctly. So I still don't understand what you're seeing. I was seeing CS behaving oddly on my B0 but changing it to GP1 fixed that - maybe just dodgy solder on this device? You could try using GP6 for the SPI0 clock or switch to SPI1 (e.g. GP10-13?). Just to see if the other peripheral behaves the same.
jakerr commented 2024-12-18 09:20:54 -06:00 (Migrated from github.com)

Have there been multiple revisions of the RP2040 silicon, and this only happens on the older one? I'm very sure I measured this correctly on my board. Any way to read out the silicon rev from a register?

I've been experiencing similar behavior where a Pico 1 board in SPI slave mode has to have CPOL and CPHA set oposite to what the master (Pico 2 in my case) has set in order to decode bits correctly.

Happy to do some testing with my available boards.
I'll try to come back and add some results of those tests in the next few days.

> Have there been multiple revisions of the RP2040 silicon, and this only happens on the older one? I'm very sure I measured this correctly on my board. Any way to read out the silicon rev from a register? I've been experiencing similar behavior where a Pico 1 board in SPI slave mode has to have CPOL and CPHA set oposite to what the master (Pico 2 in my case) has set in order to decode bits correctly. Happy to do some testing with my available boards. I'll try to come back and add some results of those tests in the next few days.
peterharperuk commented 2024-12-18 09:29:12 -06:00 (Migrated from github.com)

@jakerr Can you take a photo of the top of the chip? How old is the Pico 1?

@jakerr Can you take a photo of the top of the chip? How old is the Pico 1?
jakerr commented 2024-12-18 09:39:27 -06:00 (Migrated from github.com)

Here's the patient. This is the best photo I could get with my poor phone camera.
Can you make out everything you need?

It's a Pico H ©2021
I think the chip says "RP2-B2 21/36"

IMG_7872

Here's the patient. This is the best photo I could get with my poor phone camera. Can you make out everything you need? It's a Pico H ©2021 I think the chip says "RP2-B2 21/36" ![IMG_7872](https://github.com/user-attachments/assets/591075fd-45a3-47b8-bcec-d722c06e2306)
peterharperuk commented 2024-12-18 10:32:03 -06:00 (Migrated from github.com)

It would be "interesting" if you could run the uf2 here https://github.com/user-attachments/files/17651433/chip_version.uf2.zip
Just to rule out the chip version. I have a Pico H that looks the same as yours.

It would be "interesting" if you could run the uf2 here https://github.com/user-attachments/files/17651433/chip_version.uf2.zip Just to rule out the chip version. I have a Pico H that looks the same as yours.
peterharperuk commented 2024-12-18 10:56:48 -06:00 (Migrated from github.com)

And my Pico H is working ok...

Screenshot 2024-12-18 165451

And my Pico H is working ok... ![Screenshot 2024-12-18 165451](https://github.com/user-attachments/assets/43ff348d-dc97-47e4-86c8-3ffd8271658e)
jakerr commented 2024-12-18 19:56:39 -06:00 (Migrated from github.com)

@peterharperuk Thanks very much for testing this again.

I've run the code in this issue and the chip id snippet ("Chip version 2") and determined that in master mode my chip is behaving as specified in the datasheet. Sorry for the confusion.

That said I still do have the issue that the polarity and phase must be swapped in slave mode in order for it to properly decode received data. I will keep looking into that and file a new issue if I can discover a reproducible case.

I also did some consistency checking to make sure the set spi_set_format was having the expected effect on the spi cr0 register and that all looked fine to me.

Chip version 2
SPI before setting format
SPI(0) CR0: 0000 0000 0000 0000 1101 0000 0000 0111 
            .... .... .... .... .... .... ││.. ....
                                          │└ CPOL: 0
                                          └─ CPHA: 0
SPI after setting format SPI_POL_0, SPI_CPHA_0
SPI(0) CR0: 0000 0000 0000 0000 1101 0000 0000 0111 
            .... .... .... .... .... .... ││.. ....
                                          │└ CPOL: 0
                                          └─ CPHA: 0
SPI after setting format SPI_POL_1, SPI_CPHA_1
SPI(0) CR0: 0000 0000 0000 0000 1101 0000 1100 0111 
            .... .... .... .... .... .... ││.. ....
                                          │└ CPOL: 1
                                          └─ CPHA: 1

SPI_POL_0, SPI_CPHA_0:
Screenshot 2024-12-19 at 10 41 48
SPI_POL_1, SPI_CPHA_1:
Screenshot 2024-12-19 at 10 46 29

I tried this test on the pins in the original issue:

static int _sck = 2;
static int _mosi = 3;
static int _miso = 4;
static int _cs = 5;

and also on these pins:

static int _sck = 18;
static int _mosi = 19;
static int _miso = 16;
static int _cs = 17;

The result was the same.

@peterharperuk Thanks very much for testing this again. I've run the code in this issue and the chip id snippet ("Chip version 2") and determined that in master mode my chip is behaving as specified in the datasheet. Sorry for the confusion. That said I still do have the issue that the polarity and phase must be swapped in slave mode in order for it to properly decode received data. I will keep looking into that and file a new issue if I can discover a reproducible case. I also did some consistency checking to make sure the set `spi_set_format` was having the expected effect on the spi cr0 register and that all looked fine to me. ``` Chip version 2 SPI before setting format SPI(0) CR0: 0000 0000 0000 0000 1101 0000 0000 0111 .... .... .... .... .... .... ││.. .... │└ CPOL: 0 └─ CPHA: 0 SPI after setting format SPI_POL_0, SPI_CPHA_0 SPI(0) CR0: 0000 0000 0000 0000 1101 0000 0000 0111 .... .... .... .... .... .... ││.. .... │└ CPOL: 0 └─ CPHA: 0 SPI after setting format SPI_POL_1, SPI_CPHA_1 SPI(0) CR0: 0000 0000 0000 0000 1101 0000 1100 0111 .... .... .... .... .... .... ││.. .... │└ CPOL: 1 └─ CPHA: 1 ``` SPI_POL_0, SPI_CPHA_0: ![Screenshot 2024-12-19 at 10 41 48](https://github.com/user-attachments/assets/00c63f5f-df0a-42f1-be3b-1753f64e11df) SPI_POL_1, SPI_CPHA_1: ![Screenshot 2024-12-19 at 10 46 29](https://github.com/user-attachments/assets/2b149557-bfaf-48d6-ba7c-eb86cd01a9c6) I tried this test on the pins in the original issue: ``` static int _sck = 2; static int _mosi = 3; static int _miso = 4; static int _cs = 5; ``` and also on these pins: ``` static int _sck = 18; static int _mosi = 19; static int _miso = 16; static int _cs = 17; ``` The result was the same.
peterharperuk commented 2024-12-19 05:23:00 -06:00 (Migrated from github.com)

Ok, I'll check slave mode.

Ok, I'll check slave mode.
jakerr commented 2024-12-19 05:54:33 -06:00 (Migrated from github.com)

Hi @peterharperuk,

Thank you. Before sending you down that rabbit hole, I did some investigation today and think I've convinced myself that, at least the board I have, is behaving as specified.

I want to do a more thorough write up with some sample code for the next person that needs to investigate,
but here's what I discovered, briefly:

I was driving the communication from a pico 2 running in master mode and:
SPI_POL_0, SPI_CPHA_0 but without enabling the SPI function for the CS (Chip Select) pin.
I was driving the CS pin from software and sending it low for the duration of a transfer.
The thing I did wrong was failed to implement the pulse between words of the multi word
transfer that is required by the two modes that have SPI_CPHA_0.

So my communication of two words consecutively looked like this:

mosi:   <8-bits> <8-bits>
miso:   <8-bits> hangs 
  cs: ─┐                  ┌─
       └──────────────────┘

But should have been:

mosi:   <8-bits>  <8-bits>
miso:   <8-bits>  <8-bits>
  cs: ─┐        ┌┐       ┌─
       └────────┘└───────┘

So I was saying the host is MODE_0 but it was actually more like MODE_2 or MODE_4 in its behavior (though with reversed polarity), so it's unsurprising that this was producing strange results.

The surprising thing was setting the slave to
SPI_POL_1, SPI_CPHA_1 while in that mode, the slave was able to adapt to the master's behavior because
at least the transaction framing was as expected for SPI_CPHA_1.
I suppose the slave peripheral adapted to the inverted phase of my invalid mode.

Hi @peterharperuk, Thank you. Before sending you down that rabbit hole, I did some investigation today and think I've convinced myself that, at least the board I have, is behaving as specified. I want to do a more thorough write up with some sample code for the next person that needs to investigate, but here's what I discovered, briefly: I was driving the communication from a pico 2 running in master mode and: `SPI_POL_0, SPI_CPHA_0` but without enabling the SPI function for the CS (Chip Select) pin. I was driving the CS pin from software and sending it low for the duration of a transfer. The thing I did wrong was failed to implement the pulse between words of the multi word transfer that is required by the two modes that have `SPI_CPHA_0`. So my communication of two words consecutively looked like this: ``` mosi: <8-bits> <8-bits> miso: <8-bits> hangs cs: ─┐ ┌─ └──────────────────┘ ``` But should have been: ``` mosi: <8-bits> <8-bits> miso: <8-bits> <8-bits> cs: ─┐ ┌┐ ┌─ └────────┘└───────┘ ``` So I was saying the host is MODE_0 but it was actually more like MODE_2 or MODE_4 in its behavior (though with reversed polarity), so it's unsurprising that this was producing strange results. The surprising thing was setting the slave to `SPI_POL_1, SPI_CPHA_1` while in that mode, the slave was able to adapt to the master's behavior because at least the transaction framing was as expected for SPI_CPHA_1. I suppose the slave peripheral adapted to the inverted phase of my invalid mode.
lurch commented 2024-12-19 05:58:49 -06:00 (Migrated from github.com)

I tried this test on the pins in the original issue:

static int _sck = 2;
static int _mosi = 3;
static int _miso = 4;
static int _cs = 5;

Looking at https://www.raspberrypi.com/documentation/microcontrollers/pico-series.html#pinout-and-design-files-3 says that GP3 is SPI0 TX, i.e. the output from the SPI block. IIIRC this means that if you're running the SPI in master mode, then GP3 is MOSI; but if you're running the SPI in slave mode then GP3 is MISO. (Although I dunno if this has any bearing on the problem being discussed here or not?)

> I tried this test on the pins in the original issue: > > ``` > static int _sck = 2; > static int _mosi = 3; > static int _miso = 4; > static int _cs = 5; > ``` Looking at https://www.raspberrypi.com/documentation/microcontrollers/pico-series.html#pinout-and-design-files-3 says that GP3 is SPI0 TX, i.e. the _output_ from the SPI block. IIIRC this means that if you're running the SPI in master mode, then GP3 is MOSI; but if you're running the SPI in slave mode then GP3 is MISO. (Although I dunno if this has any bearing on the problem being discussed here or not?)
peterharperuk commented 2024-12-19 06:15:12 -06:00 (Migrated from github.com)

but if you're running the SPI in slave mode then GP3 is MISO

I think SDO is less confusing. Presumably this is the case or else it wouldn't work at all.

MOSI (GP3) -> SDI (GP4)
MISO (GP4) <- SDO (GP3)

> but if you're running the SPI in slave mode then GP3 is MISO I think SDO is less confusing. Presumably this is the case or else it wouldn't work at all. MOSI (GP3) -> SDI (GP4) MISO (GP4) <- SDO (GP3)
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-raspberrypi/pico-sdk#1583
No description provided.