Skip to content

Conversation

@BryanKadzban
Copy link
Contributor

Basic SPI slave support, starting to implement #469.

DMA only, as in slave mode the clock is controlled externally, so there's no way to force a send or receive. So the API shouldn't really be "send this set of bytes", but rather "here's a set of bytes to send whenever the master wants to get around to asking for them". DMA fits the latter quite well.

Thank you!

Thank you for your contribution.
Please make sure that your submission includes the following:

Must

  • The code compiles without errors or warnings.
  • All examples work.
  • cargo fmt was run.
  • Your changes were added to the CHANGELOG.md in the proper section.
  • You updated existing examples or added examples (if applicable).
  • Added examples are checked in CI

Nice to have

  • You add a description of your work to this PR.
  • You added proper docs for your newly added features and code.

Notes

I don't know for sure if the example works. It should, but it requires hardware mods to the board holding the -c3 chip in order to separate the SPI slave and master. If it's ok to try a remote debugging sequence like I did in PR #475, that works for me too, but if nobody wants to try that, i can mark this PR private and wait until I have access to the -c3 hardware again. That being said, I can't test on any other variant of the hardware at all, even after I wait.

I also don't think this is fully ready yet - its feature flags support non -c3 chips, but I haven't read through their TRMs to see if the code is doing the right thing (or even the documented thing). Inverting the SPI mode bits on the original ESP32 is the only thing I've seen so far, and that was a happy accident. I need to read the SPI chapters of several other reference manuals before it's truly ready - but the main structure is here, and that's the big thing I'd like to get reviewed at this point.

Thanks!

@BryanKadzban
Copy link
Contributor Author

Looking at the xtensa failures ... Ah, at least part of this is the duplex mode generic arg, and some if the rest is the SPI3 peripheral. Sorting those out.

@bjoernQ
Copy link
Contributor

bjoernQ commented Jun 9, 2023

I gave this a quick try and with the bit-banged master I didn't got it to work.
Then I removed the bit-bangging master code and connected an ESP32 running the spi_loopback_dma example.

I got this which looks quite good (despite the 0 reading in the first iteration)

slave got [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] .. [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
slave got [1, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 1]
slave got [2, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 2]
slave got [3, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 3]
slave got [4, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 4]
slave got [5, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 5]
slave got [6, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 6]
slave got [7, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 7]

On the ESP32 side I got

[b9, ba, bb, bc, bd, be, bf, c0, c1, c2] .. [75, 75, 75, 75, 75, 75, 75, 75, 75, 75]
[8, 9, a, b, c, d, e, f, 10, 11] .. [7b, 1, 75, 75, 75, 75, 75, 75, 75, 75]
[8, 9, a, b, c, d, e, f, 10, 11] .. [7b, 2, 75, 75, 75, 75, 75, 75, 75, 75]
[8, 9, a, b, c, d, e, f, 10, 11] .. [7b, 3, 75, 75, 75, 75, 75, 75, 75, 75]
[8, 9, a, b, c, d, e, f, 10, 11] .. [7b, 4, 75, 75, 75, 75, 75, 75, 75, 75]
[8, 9, a, b, c, d, e, f, 10, 11] .. [7b, 5, 75, 75, 75, 75, 75, 75, 75, 75]
[8, 9, a, b, c, d, e, f, 10, 11] .. [7b, 6, 75, 75, 75, 75, 75, 75, 75, 75]
[8, 9, a, b, c, d, e, f, 10, 11] .. [7b, 7, 75, 75, 75, 75, 75, 75, 75, 75]

That also looks almost good beside the read on first iteration - the slave should have set the iteration count as the first and last element.

It looks like the master is picking up the data starting at the 8th / 9th byte (I changed the slave example to fill the send buffer with bytes counting up)

I will be travelling for business next week so I probably won't be able to do testing next week

@BryanKadzban
Copy link
Contributor Author

Turns out I haven't been able to get much time for this either lately. :-/

Maybe rewriting what's going on will help sort things out. On the first transfer, the first b9 = 185 bytes are missing -- or actually, it's 185+256*N for some integer N. So 185, 441, 697, 953, 1209, 1465, 1721, 1977, 2233, ... None of those look like particularly memorable numbers to me except possibly 1465, but even that is only because of MTU values on an old PPP encapsulated network link, so it has no relevance here.

Can you change the master side code to hunt down where the incrementing bytes stop, and print out how many extra 0x75 bytes are at the end? Something like an rposition call on the slice's iterator, maybe. Not sure if this number will make anything obvious, but maybe.

Further transfers always lose 8 bytes, as you said.

I wonder where the 0x75 is coming from. Is that transmitted on the SPI bus, or are those bytes untouched by the DMA on the master side? What happens if you fill the RX buffer with some other fixed pattern before the transfer starts, on the master side?

One other possibility is that the DMA on the slave side is getting triggered a bunch of times before the master sends clock pulses ... I wonder if the FIFO resets are happening at the right time. That wouldn't necessarily explain the first transfer (nor the zeros printed in the slave side), but of course there might be more than one issue. (Or actually, if it's timing related, maybe the first transfer takes longer to set up so it loses more into the FIFO?) I'll have to re check all of that with the TRM at some point soon.

Or, I wonder if the slave hardware is looking for an 8 byte segmented transfer setup packet - the place that the TRM says to disable segmented transfers seems a bit late (it's after DMA is already started). That feels unlikely, but might explain something. I'll have to try to find some time to carefully reread the DMA chapter as well as the SPI one, with that in mind.

But one other question I do have when you get time again - does the logic analyzer show the earlier bytes getting sent out at all, possibly not synchronized to a clock pulse? I don't think there could be spurious pulses (and I think a FIFO reset is more likely to be the cause either way), but I wonder what shows up on the bus.

Thank you!

@BryanKadzban
Copy link
Contributor Author

... Yeah, section 26.5.7.3 of the -c3 TRM has a diagram. The various FIFOs involved in the send and receive paths are all 8*8 bits long. That's super suspicious.

I think section 26.5.9.4 looks odd. Nowhere does it specifically say to turn on the DMA start bit, but the code does it at the "start the GDMA tx/rx engine" step, before it resets the three different afifos, and even before it configures the SPI peripheral for single transfer mode. I am guessing DMA needs to be started in between step 7 and 8, or maybe in the middle of step 8.

Let's see if I can get that code together any time soon.

@BryanKadzban
Copy link
Contributor Author

Ok, let's see if that makes any difference. The last commit before rustfmt (74e14ef) makes some changes to the DMA code to let the SPI code start both tx and rx after the afifos get reset, but it also keeps compatibility for other peripherals, like i2s and the SPI master. That compatibility isn't strictly needed, but it's less to write to get this testable. I'll sort it out later, if it works.

(I partly wonder if SPI master code needs to do something similar ... It looks like the send path is working, at least on the esp32, though. So probably not.)

If this doesn't fix the first missing chunk, I would still be interested to find out how many 0x75 bytes are at the end of that one.

@bjoernQ
Copy link
Contributor

bjoernQ commented Jun 19, 2023

Unfortunately looks worse now (again using spi_loopback_dma on the other side since the bitbanging doesn't seem to work for me)

Slave

slave got [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] .. [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
slave got [7e, 7f, 80, 81, 82, 83, 84, 85, 86, 87] .. [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
slave got [be, bf, c0, c1, c2, c3, c4, c5, c6, c7] .. [1f, 20, 21, 22, 23, 24, 25, 26, 27, 28]
slave got [fe, 0, 1, 2, 3, 4, 5, 6, 7, 8] .. [2b, 2c, 2d, 2e, 2f, 30, 31, 32, 33, 34]
slave got [3f, 40, 41, 42, 43, 44, 45, 46, 47, 48] .. [37, 38, 39, 3a, 3b, 3c, 3d, 3e, 3f, 40]
slave got [7f, 80, 81, 82, 83, 84, 85, 86, 87, 88] .. [43, 44, 45, 46, 47, 48, 49, 4a, 4b, 4c]
slave got [bf, c0, c1, c2, c3, c4, c5, c6, c7, c8] .. [43, 44, 45, 46, 47, 48, 49, 4a, 4b, 4c]
slave got [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [4f, 50, 51, 52, 53, 54, 55, 56, 57, 58]
slave got [40, 41, 42, 43, 44, 45, 46, 47, 48, 49] .. [5b, 5c, 5d, 5e, 5f, 60, 61, 62, 63, 64]
slave got [80, 81, 82, 83, 84, 85, 86, 87, 88, 89] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [c0, c1, c2, c3, c4, c5, c6, c7, c8, c9] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [b, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, b]
slave got [c, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, c]
slave got [d, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, d]
slave got [e, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, e]
slave got [f, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, f]
slave got [10, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 10]
slave got [11, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 11]
slave got [12, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 12]
slave got [13, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 13]
slave got [14, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 14]
slave got [15, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 15]
slave got [16, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 16]
slave got [17, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 17]
slave got [18, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 18]
slave got [19, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 19]
slave got [1a, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 1a]
slave got [1b, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 1b]
slave got [1c, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 1c]
slave got [1d, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 1d]

Master

[bf, be, bd, bc, bb, ba, b9, b8, b7, b6] .. [c, b, a, 9, 8, 7, 6, 5, 4, 3]
[2, 1, ff, fe, fd, fc, fb, fa, f9, f8] .. [4c, 4b, 4a, 49, 48, 47, 46, 45, 44, 43]
[42, 41, 40, 3f, 3e, 3d, 3c, 3b, 3a, 39] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 83]
[82, 81, 80, 7f, 7e, 7d, 7c, 7b, 7a, 79] .. [cc, cb, ca, c9, c8, c7, c6, c5, c4, c3]
[c2, c1, c0, bf, be, bd, bc, bb, ba, b9] .. [d, c, b, a, 9, 8, 7, 6, 5, 4]
[3, 2, 1, ff, fe, fd, fc, fb, fa, f9] .. [4d, 4c, 4b, 4a, 49, 48, 47, 46, 45, 44]
[43, 42, 41, 40, 3f, 3e, 3d, 3c, 3b, 3a] .. [8d, 8c, 8b, 8a, 89, 88, 87, 86, 85, 84]
[83, 82, 81, 80, 7f, 7e, 7d, 7c, 7b, 7a] .. [cd, cc, cb, ca, c9, c8, c7, c6, c5, c4]
[c3, c2, c1, c0, bf, be, bd, bc, bb, ba] .. [e, d, c, b, a, 9, 8, 7, 6, 5]
[4, 3, 2, 1, ff, fe, fd, fc, fb, fa] .. [4e, 4d, 4c, 4b, 4a, 49, 48, 47, 46, 45]
[44, 43, 42, 41, 40, 3f, 3e, 3d, 3c, 3b] .. [8a, 8a, 8a, 8a, 8a, 8a, 8a, 8a, 8a, 8a]
[b, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, b]
[c, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, c]
[d, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, d]
[e, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, e]
[f, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, f]
[10, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 10]
[11, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 11]
[12, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 12]
[13, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 13]
[14, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 14]
[15, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 15]
[16, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 16]
[17, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 17]
[18, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 18]
[19, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 19]
[1a, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 1a]
[1b, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 1b]
[1c, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 84, 1c]

@BryanKadzban
Copy link
Contributor Author

BryanKadzban commented Jun 21, 2023

I'm not sure what's going on with the bit banging, but I think since you have an esp32 that can plug in here, that's fine for testing for now - we can fix the bit banged master later. We'll need the logic analyzer (that you were using in the LEDC fading PR) for that anyway, I think.

But for what's there now, both sides did eventually start producing the right numbers, on transmission number 0xb (so either 11 or 12; I can't remember for sure if that number starts at 0 or 1). So I think this is actually better in one way and worse in another: it's better in that it stops losing the first eight bytes, but it's worse in that it takes a lot more transmissions to get to that point. The earlier code got to that point after only one transmission. So I think the DMA change is needed, to only set the "start" bit after resetting the afifos.

It's also odd that the master side shows a continuous sequence of numbers even across transmissions - the first row ends with 5, 4, 3, and the second row starts with 2, 1, ff. It looks like something is out of sync between the two - and here, again, I think the logic analyzer would be most useful.

Not sure how many bits it can capture in a row, but if you can get it to sample all four lines (cs, miso, mosi, and sck) until the two sides get in sync, that would be best. If it can do that, can you post the capture file?

The other question is, how long are the slices that the code is printing chunks of? They're supposed to be 32000 bytes, but I'd like to actually check. Can you change the code to print out the length on both sides before the first and last bytes, as well?

Thank you very much!

@BryanKadzban
Copy link
Contributor Author

(There, let's get rid of ten of those commits, and squash them into wherever is appropriate. The one remaining change to fix the build for non -c3 devices can be squashed as well, but it's different enough for now that I don't think I will yet.)

@bjoernQ
Copy link
Contributor

bjoernQ commented Jun 22, 2023

Output by slave

slave got [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] .. [5b, 5c, 5d, 5e, 5f, 60, 61, 62, 63, 64]
slave got [7a, 7b, 0, 0, 0, 0, 0, 0, 0, 0] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [75, 76, 77, 78, 79, 7a, 7b, 1, 0, 0] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [70, 71, 72, 73, 74, 75, 76, 77, 78, 79] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [6b, 6c, 6d, 6e, 6f, 70, 71, 72, 73, 74] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [66, 67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [61, 62, 63, 64, 65, 66, 67, 68, 69, 6a] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [5c, 5d, 5e, 5f, 60, 61, 62, 63, 64, 65] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [57, 58, 59, 5a, 5b, 5c, 5d, 5e, 5f, 60] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [52, 53, 54, 55, 56, 57, 58, 59, 5a, 5b] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [4d, 4e, 4f, 50, 51, 52, 53, 54, 55, 56] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [48, 49, 4a, 4b, 4c, 4d, 4e, 4f, 50, 51] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [43, 44, 45, 46, 47, 48, 49, 4a, 4b, 4c] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [3e, 3f, 40, 41, 42, 43, 44, 45, 46, 47] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [39, 3a, 3b, 3c, 3d, 3e, 3f, 40, 41, 42] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [34, 35, 36, 37, 38, 39, 3a, 3b, 3c, 3d] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [2f, 30, 31, 32, 33, 34, 35, 36, 37, 38] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [2a, 2b, 2c, 2d, 2e, 2f, 30, 31, 32, 33] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [25, 26, 27, 28, 29, 2a, 2b, 2c, 2d, 2e] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [20, 21, 22, 23, 24, 25, 26, 27, 28, 29] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [1b, 1c, 1d, 1e, 1f, 20, 21, 22, 23, 24] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [11, 12, 13, 14, 15, 16, 17, 18, 19, 1a] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [c, d, e, f, 10, 11, 12, 13, 14, 15] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [7, 8, 9, a, b, c, d, e, f, 10] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [2, 3, 4, 5, 6, 7, 8, 9, a, b] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [fc, fd, fe, 0, 1, 2, 3, 4, 5, 6] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [f7, f8, f9, fa, fb, fc, fd, fe, 0, 1] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [f2, f3, f4, f5, f6, f7, f8, f9, fa, fb] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [ed, ee, ef, f0, f1, f2, f3, f4, f5, f6] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [e8, e9, ea, eb, ec, ed, ee, ef, f0, f1] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [e3, e4, e5, e6, e7, e8, e9, ea, eb, ec] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [de, df, e0, e1, e2, e3, e4, e5, e6, e7] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [d9, da, db, dc, dd, de, df, e0, e1, e2] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [d4, d5, d6, d7, d8, d9, da, db, dc, dd] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]
slave got [cf, d0, d1, d2, d3, d4, d5, d6, d7, d8] .. [67, 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, 70]

Output by master

[0, fe, fd, fc, fb, fa, f9, f8, f7, f6] .. [8c, 8b, 8a, 89, 88, 87, 86, 85, 1, fe]
[fd, fc, fb, fa, f9, f8, f7, f6, f5, f4] .. [8a, 89, 88, 2, fe, fd, fc, fb, fa, f9]
[f8, f7, f6, f5, f4, f3, f2, f1, f0, ef] .. [fd, fc, fb, fa, f9, f8, f7, f6, f5, f4]
[f3, f2, f1, f0, ef, ee, ed, ec, eb, ea] .. [f8, f7, f6, f5, f4, f3, f2, f1, f0, ef]
[ee, ed, ec, be, ea, e9, e8, e7, e6, e5] .. [f3, f2, f1, f0, ef, ee, ed, ec, be, ea]
[e9, e8, e7, e6, e5, e4, e3, e2, e1, e0] .. [ee, ed, ec, eb, ea, e9, e8, e7, e6, e5]
[e4, e3, e2, e1, e0, df, de, dd, dc, db] .. [e9, e8, e7, e6, e5, e4, e3, e2, e1, e0]
[df, de, dd, dc, db, da, d9, d8, d7, d6] .. [e4, e3, e2, e1, e0, df, de, dd, dc, db]
[da, d9, d8, d7, d6, d5, d4, d3, d2, d1] .. [df, de, dd, dc, db, da, d9, d8, d7, d6]
[d5, d4, d3, d2, d1, d0, cf, ce, cd, cc] .. [da, d9, d8, d7, d6, d5, d4, d3, d2, d1]
[d0, cf, ce, cd, cc, cb, ca, c9, c8, c7] .. [d5, d4, d3, d2, d1, d0, cf, ce, cd, cc]
[cb, ca, c9, c8, c7, c6, c5, c4, c3, c2] .. [d0, cf, ce, cd, cc, cb, ca, c9, c8, c7]
[c6, c5, c4, c3, c2, c1, c0, bf, be, bd] .. [cb, ca, c9, c8, c7, c6, c5, c4, c3, c2]
[c1, c0, bf, be, bd, bc, bb, ba, b9, b8] .. [c6, c5, c4, c3, c2, c1, c0, bf, be, bd]
[bc, bb, ba, b9, b8, b7, b6, b5, b4, b3] .. [c1, c0, bf, be, bd, bc, bb, ba, b9, b8]
[b7, b6, b5, b4, b3, b2, b1, b0, af, ae] .. [bc, bb, ba, b9, b8, b7, b6, b5, b4, b3]
[b2, b1, b0, af, ae, ad, ac, ab, aa, a9] .. [b7, b6, b5, b4, b3, b2, b1, b0, af, ae]
[ad, ac, ab, aa, a9, a8, a7, a6, a5, a4] .. [b2, b1, b0, af, ae, ad, ac, ab, aa, a9]
[a8, a7, a6, a5, a4, a3, a2, a1, a0, 9f] .. [ad, ac, ab, aa, a9, a8, a7, a6, a5, a4]
[a3, a2, a1, a0, 9f, 9e, 9d, 9c, 9b, 9a] .. [a8, a7, a6, a5, a4, a3, a2, a1, a0, 9f]
[9e, 9d, 9c, 9b, 9a, 99, 98, 97, 96, 95] .. [a3, a2, a1, a0, 9f, 9e, 9d, 9c, 9b, 9a]
[99, 98, 97, 96, 95, 94, 93, 92, 91, 90] .. [9e, 9d, 9c, 9b, 9a, 99, 98, 97, 96, 95]
[94, 93, 92, 91, 90, 8f, 8e, 8d, 8c, 8b] .. [99, 98, 97, 96, 95, 94, 93, 92, 91, 90]
[8f, 8e, 8d, 8c, 8b, 8a, 89, 88, 87, 86] .. [94, 93, 92, 91, 90, 8f, 8e, 8d, 8c, 8b]
[8a, 89, 88, 87, 86, 85, 84, 83, 82, 81] .. [8f, 8e, 8d, 8c, 8b, 8a, 89, 88, 87, 86]
[85, 84, 83, 82, 81, 80, 7f, 7e, 7d, 7c] .. [8a, 89, 88, 87, 86, 85, 84, 83, 82, 81]
[80, 7f, 7e, 7d, 7c, 7b, 7a, 79, 78, 77] .. [85, 84, 83, 82, 81, 80, 7f, 7e, 7d, 7c]
[7b, 7a, 79, 78, 77, 76, 75, 74, 73, 72] .. [80, 7f, 7e, 7d, 7c, 7b, 7a, 79, 78, 77]
[76, 75, 74, 73, 72, 71, 70, 6f, 6e, 6d] .. [7b, 7a, 79, 78, 77, 76, 75, 74, 73, 72]
[71, 70, 6f, 6e, 6d, 6c, 6b, 6a, 69, 68] .. [76, 75, 74, 73, 72, 71, 70, 6f, 6e, 6d]
[6c, 6b, 6a, 69, 68, 67, 66, 65, 64, 63] .. [71, 70, 6f, 6e, 6d, 6c, 6b, 6a, 69, 68]
[67, 66, 65, 64, 63, 62, 61, 60, 5f, 5e] .. [6c, 6b, 6a, 69, 68, 67, 66, 65, 64, 63]
[62, 61, 60, 5f, 5e, 5d, 5c, 5b, 5a, 59] .. [67, 66, 65, 64, 63, 62, 61, 60, 5f, 5e]
[5d, 5c, 5b, 5a, 59, 58, 57, 56, 55, 54] .. [62, 61, 60, 5f, 5e, 5d, 5c, 5b, 5a, 59]
[58, 57, 56, 55, 54, 53, 52, 51, 50, 4f] .. [5d, 5c, 5b, 5a, 59, 58, 57, 56, 55, 54]
[53, 52, 51, 50, 4f, 4e, 4d, 4c, 4b, 4a] .. [58, 57, 56, 55, 54, 53, 52, 51, 50, 4f]
[4e, 4d, 4c, 4b, 4a, 49, 48, 47, 46, 45] .. [53, 52, 51, 50, 4f, 4e, 4d, 4c, 4b, 4a]

Session 0.zip

Using the unmodified spi_loopback_dma.rs as master.

I guess there won't be much progress in this working mode so it would be probably good to get (ideally) two dev-boards

@BryanKadzban
Copy link
Contributor Author

Yeah, maybe sorting it out on my own would be faster in terms of turnaround time. There will definitely be some downtime before I can start that, though, as while I can mess with software here, I can't mess with hardware (even if I did have two of the dev boards).

But, weirdly, what I see from the logic analyzer capture looks perfect. The MISO line is registering bytes that go down starting at 0xfe, while the MOSI line is registering bytes that go up starting at 1.

That tells me that the problem is somewhere on the DMA side, or the SPI peripheral isn't interpreting some of the registers the way I expected. But while I could try to describe the changes I'd like to use to narrow things down more, I don't think the changes I'd be describing will be the only ones. So I can see why you might not want to do a long remote debugging session -- so I'll definitely wait. I might look through the code a couple more times to make sure I'm not missing something obvious.

@BryanKadzban BryanKadzban marked this pull request as draft June 24, 2023 20:43
@BryanKadzban
Copy link
Contributor Author

So part of the rebase here was making changes to the Drop impl on the various transfer structs, so they checked both the DMA channel status as well as the SPI peripheral's idea of done. On transmit, especially, these are different - DMA is done when the 8-byte afifo gets filled, while we can't actually do anything with the peripheral until trans_done is set when the data is shifted all the way out onto the bus.

I'm not sure if that will help though. The transmit side isn't the issue anyway, or at least not in the LA sample from before - the bus is doing the right thing, just the data reception isn't working right.

... Hmm, there's an idea. Maybe clearing the afifos needs to be split as well, around the start bit being set. Maybe the one direction afifo needs to be cleared before starting, as today, but the other needs to be cleared after, as before the "split" commits I added. Maybe DMA is reading old data out of those afifos when it first starts up.

I'll see what I can do here - not sure if you were planning to test anything at all here, but don't do it yet if so.

Thank you very much again :)

@BryanKadzban
Copy link
Contributor Author

Ok, so in the "received by master" in the most recent run, I see a pattern that I missed before. Maybe the issue isn't in the afifo resets, although I still wonder.

Because the code in the slave is filling the tx buffer with 255..1 sequences (not 255..0), I should not expect the end of the 32000 byte buffer to be on a byte rollover value (either 255 or 0). And indeed, in the very first transmission, I see the buffer ends with 87, 86, 85, 1, fe (where the 1 looks like the transmission index from the second iteration). So if the last two bytes had been transmitted (or received?), that buffer would end in 85, 84, 83.

And ff to 83, inclusive, is 125 - matching, exactly, the value of 32000 mod 255. So that first transmission is fine, other than missing the last two bytes. I'm not entirely sure why the code logged the first two bytes of the next transmission, but the DMA engine on the master side will save unused chunks of rx buffer and fill them the next time around, so maybe that's happening.

Well, except no, because the master side logs before it starts to fill the rx buffer again. So maybe that's from the slave side; maybe it moved on to the next descriptor in the DMA setup somehow, or maybe it managed to reset the DMA engine and re register the buffer in time for the master to get those two bytes when it sent the last 16 clocks.

The other thing is, it's not always two bytes. It's two the first time around, but it's those two plus five more, then plus five more, etc., each subsequent time around. Hmm.

On the slave side, the first transmission is all wonky, but the second starts with the three expected closing bytes of the first transmission - 7b, 7c, 7d (but 7d got overwritten with the transmission index 0). That's the same 125 bytes as on the send side, but we're off by three this time instead of two. Future transmissions have the same off by five pattern going on.

I don't see anything obvious in the code that would generate an off by five error, but maybe it's a consistent timing issue or something. Maybe there's something wrong in how the code decides the transfers are done, still.

@BryanKadzban
Copy link
Contributor Author

Hmm, at least some of that last comment is nonsense - there's a 250ms delay between when the transfer says it's done, and when the code sets up another transfer. So it's not likely the case that the slave side is setting up another transfer before the master side sends the last 16 clocks.

So then where are those bytes coming from...

I might just have to dump out the DMA descriptors to see what's happening... That will of course require access to the hardware, so not until at least next week. Or maybe I could have it flip gpio lines when certain events happen, and see about that timing.

It's still odd that the SPI bus has all the right bits being sent in the right order too, though. Hopefully this all makes sense soon.

@BryanKadzban
Copy link
Contributor Author

I still haven't been able to make much progress here. I did manage to get two dev boards connected together, and the right data is definitely being sent on both the miso and mosi lines. So I think something is off on the receive side only, but I still haven't been able to put a lot of effort into figuring out what yet.

@BryanKadzban
Copy link
Contributor Author

There we go, that seems helpful.

On the first transmission, everything is fine except that the receive side logs a bunch of zeros before the data starts. After adding a ton more logging, and trying several .fill() calls on the rx buffer before starting DMA, I finally realized today that there are unchanged bytes in there - and the total number of them is exactly 4092*3, i.e. the first three descriptors are not being filled with SPI data. But the first byte of each of these chunks was being set to 0 -- even if I change the master side to never send a 0 byte.

I also tried changing the CHUNK_SIZE const to 124, and adding a bunch more descriptor u32s to match. It's always the first two or three descriptors that have a single zero byte added, regardless of the byte count.

I wonder if this is related to the glitches I see on the chip select and clock lines when setting up the master ... perhaps the zero byte is some kind of timeout or error result, after only one bit gets shifted into the SPI peripheral's buffer. Or after no bits get shifted in, if the issue is the high->low->high glitch on the chip select line.

And I'm surprised the recent change to report a Result<> from the wait method didn't show anything either ... if this is some kind of error, maybe there are more bits to check or something. That will need another read through the TRM.

Not sure on the best way to fix it either, but I think I see what's going on now. Two or three descriptors just aren't getting used the first time around.

@BryanKadzban
Copy link
Contributor Author

Hmm. According to the esp32c3 datasheet, pages 20-21, in table 6 the default pins for SPI2 clock and chip select (which the SPI loopback example also configures) are gpio6 (mtck) and 10. And on page 22, in table 7, both of those lines are documented to have a 5 microsecond low level glitch at startup.

When the master device resets and this glitch hits the listening slave device, I bet it's causing at least some of the issues.

Fortunately SPI2 can run through the gpio matrix to any data lines. Next week when I'm back in front of the hardware I'll try to find time to rewire the bus so that so least cs is on a line that doesn't glitch at startup, and let's see if that makes any difference.

This probably doesn't affect the single device example because it doesn't initialize the receive channel before reset is done so the glitches don't affect anything. Or actually, because its receive channel is not the same as an SPI slave (it's just doing a full duplex transfer), so nothing looks at chip select at all.

But it would really be nice if there was a way to ignore zero byte transfers in the SPI DMA handling, to let it ignore these on its own. Because really, it should be robust to something like this. Sadly I don't see any way to do that, so if this is the problem, then changing the wiring might be the only fix

@BryanKadzban
Copy link
Contributor Author

I can't find any gpio that doesn't have those glitches at startup, even the ones that Espressif documents as not having the "G" note. All of them glitch low when the bootloader is running, before the SPI master code even starts.

So I went back to the bit bang master, because within a single MCU it's possible to avoid starting the slave until after the glitches are done. (And in fact it's impossible to start it before the glitches, because they're at bootloader time.) This fixes all the problems with missing bytes and with the slave receive side losing bytes.

But I had to fix the bit bang master, obviously. Turns out it had two bugs, one where it was iterating over a list of arrays instead of a list of integers (so it only spit out one bit per byte), and one where I got the default bit order of SPI backwards (so it went in lsb first mode instead of msb first).

Fixing those bugs makes it work perfectly, with only one device. As the checks are all passing as well, I think this is ready for review again.

If you think it looks reasonable, I'll see if all the intermediate commits are still needed, and drop the ones that aren't, then squash everything and rebase one more time.

Thank you for waiting!

@BryanKadzban BryanKadzban marked this pull request as ready for review August 1, 2023 21:48
@BryanKadzban
Copy link
Contributor Author

Ok, I'm pretty sure the intermediate commits are needed (though most of them can be squashed). And I think this is ready for another review; I just rebased to the new head.

let transfer = spi.dma_transfer(send, receive).unwrap();
// here we could do something else while DMA transfer is in progress
let mut i = 0;
let mut n = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO we should rename this variable in this example for all other chips, too if we do it here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed - that was the plan when I copied the new example to the other chips' directories. In this function, i is being used for three different things now - this change at least drops that to 2.

@bjoernQ
Copy link
Contributor

bjoernQ commented Aug 11, 2023

Thank you! I might not get to review this today since because of the changes to DMA I should test all DMA related examples on all chips

I understand you only have access to ESP32-C3 but it seems this is also intended for other targets - would be nice to have the example adapted for them - but you can wait doing that after the first tests on ESP32-C3 certainly

@bjoernQ
Copy link
Contributor

bjoernQ commented Aug 11, 2023

BTW if anyone else wants to take a look at this ... go ahead - I haven't started reviewing this

@bjoernQ
Copy link
Contributor

bjoernQ commented Aug 14, 2023

I was able to successfully test this on ESP32-C3 🚀

There are some minor nitpicks but overall looks really good to me!

Also briefly testing other DMA related examples was successful

@BryanKadzban
Copy link
Contributor Author

Addressed your comments.

Time to copy the example to all the other chips' directories?

@bjoernQ
Copy link
Contributor

bjoernQ commented Aug 16, 2023

Addressed your comments.

Time to copy the example to all the other chips' directories?

Nice! Yes - let's see if this will work for all targets. Would be great

@BryanKadzban
Copy link
Contributor Author

Ok, that passes the automatic build for everything - it doesn't include an example for the -c6-lp variant, though, since that looks very new in the codebase, and only has one blinky example so far. Hopefully it works on all of them. :)

There might be register differences I'll need to fix up, but let's see.

Thanks again!

@bjoernQ
Copy link
Contributor

bjoernQ commented Aug 23, 2023

Unfortunately, no difference

@BryanKadzban
Copy link
Contributor Author

Still trying to figure out what else it might be... It seems likely to be related to the pdma (instead of gdma) peripheral in the esp32 and s2 ... but it's not clear where the difference is.

Here's a question: Does the SPI loopback DMA example still work on the s2 chip with this change? I wonder if I broke pdma somehow. If it doesn't work here, I assume it does work at head, right?

If the master does work, I'll start digging through the differences between this code and the master side. Maybe I'll start that anyway, actually...

@bjoernQ
Copy link
Contributor

bjoernQ commented Aug 24, 2023

spi_loopback_dma still works on S2 but the changed variable (i to n) causes it to print different numbers

e.g. here

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 0]
[b, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, b]
[16, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 16]
[21, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 21]

vs main

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 0]
[1, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 1]
[2, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 2]
[3, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 3]
[4, 1, 2, 3, 4, 5, 6, 7, 8, 9] .. [73, 74, 75, 76, 77, 78, 79, 7a, 7b, 4]

@BryanKadzban
Copy link
Contributor Author

Sigh, that's a pretty obvious thinko -- I'm incrementing i in the inner loop instead of n. It's also a pretty easy fix.

I haven't had any time to dig into this recently; going to try a few more things here shortly though. I think the rebase above ought to fix the spi_loopback_dma example again though.

@jessebraham jessebraham linked an issue Sep 5, 2023 that may be closed by this pull request
7 tasks
@jessebraham
Copy link
Member

What's the status on this? I'd really like to get it merged if possible, this PR has been open for ~4 months now. If you're not able to complete the work here I'm sure we can find somebody else to bring it across the finish line.

@BryanKadzban
Copy link
Contributor Author

I've been super distracted with a lot of other stuff lately. :( I just managed to rebase it tonight, but yeah, it's been several weeks since I really had time to dig into the -s2 device's issues. (And it seems the rebase is off somehow ... oh, right, I dropped the frequency arguments; at least that's relatively easy to fix.)

What do you think about disabling this module on esp32 and esp32s2, dropping the conditionals for those chips in spi_slave.rs, and merging it as-is? Then, yeah, others can sort out the support for those two chips. That would work for me.

@bjoernQ
Copy link
Contributor

bjoernQ commented Sep 29, 2023

What do you think about disabling this module on esp32 and esp32s2

I would be fine with that - we did similar things before

@BryanKadzban
Copy link
Contributor Author

OK, I'm making slow progress on that -- still need to go through spi_slave.rs and drop cfg checks.

...Or actually, maybe not -- leaving it as-is means whoever follows up has less to do. :) I'll still take another look through before another review, though. (I think maybe the DMA prepare_transfer split could be used by other peripherals too, and drop the compat fn that's still there now.) Then I'll still have to squash down to 3 commits before merging.

@BryanKadzban
Copy link
Contributor Author

That, I think, should be all - it gets everyone off the prepare_transfer fn, and deletes it, in favor of a prepare_transfer_without_start. I could go back and rename the fn again, but my concern is that leaving it as-is wouldn't let others know the contract had changed.

I'll need to test it again, I think, and will only get a chance in a couple days. But let's see what people think before too much more time passes.

Thank you!

@BryanKadzban
Copy link
Contributor Author

The example still works, so the changes I made don't seem to affect functionality for the -c3 device. :) Is there anything else missing?

@bjoernQ
Copy link
Contributor

bjoernQ commented Oct 6, 2023

I was able to test on all supported targets. It worked fine
However, on H2 I needed to change pins (since some are not available)
4 instead of 6, 12 instead of 7, 11 instead of 8

Generally, looks good to me.
One thing I'm not sure about: usage of the .and_then(|_| ....)? pattern.

We don't have it in the code base and in optlevel 0 it produces slightly more code than calling the two functions like function1()?; function2()?;

@jessebraham what do you think about that?

This setup allows registering buffers for future transactions the master
does (lowering cs, toggling sclk, and raising cs again). The transfer
struct returned from the registration API will complete its wait() or
return true from is_done() after cs has been raised.

Copied from spi.rs, so most of the changes are deleting code that
handles e.g. segmented transfers or synchronous operations.

Fix non-c3 devices' builds
Ensure the API "feels" right.

Since there's no way to route GPIOs to other peripherals, we choose four
other wires and bit-bang SPI for the master side, relying on the person
running the example to connect the bus.  This way we ensure the slave
code works, since we created the master ourselves.

Also, it's not really possible to use a second ESP device as the master
anyway: all the digital lines have glitches on startup, and those
glitches cause the slave's DMA engine to skip descriptors (it thinks
they're intended CS indicators); this causes it to lose data.

Then, fix the bitbang master (recording the progression here)

- When bitbanging, iterate the bits by "for _ in 0..8", instead of the
  broken "for _ in [0..8]".  The latter only runs the iteration once,
  since there's only one list given ... and because the code uses _
  instead of a real loop variable, type checking didn't save us.
- When bitbanging, send the bits out (and read them in) MSB first, since
  that's actually how we have the slave configured.
The first does everything but write to the start bit and check for an
error. The second does those. We need 2 fns because the  SPI slave needs
to start the transfer only after resetting the various afifo hardware
components (if it starts the transfer before, the first 8 bytes will be
lost when that reset happens).

Use the split fns everywhere.

Also split flush().  It needs to be pollable, so split it into one fn that
polls and one that waits until the poll returns clear. Also call the poll
fn from the is_done() fn, so we don't trample in-progress transfers.
This way we can tell if it's ever touching certain bytes - 0xff is never
added to the master transmit buffer.

While I'm changing this, make the slave tx buffer never contain 0xff
either (go from 254 to 0).
@BryanKadzban
Copy link
Contributor Author

Rebased onto head, and fixed the pins.

I think the and_then construction reads better than the double question mark ("do this and then do that" makes sense to me) - but if opt-level zero is important, let me know and I'll switch it back. I'm sure the difference is just that the callback isn't being inlined.

Thanks!

@bjoernQ
Copy link
Contributor

bjoernQ commented Oct 9, 2023

I guess opt-level 0 is not really important - you can see the difference in https://godbolt.org/ ... the difference is also not huge.
It's 99.9% a matter of taste and consistency - I'm fine with it as it but would really like to hear a third opinion before merging

Thanks

@BryanKadzban
Copy link
Contributor Author

Totally fair. I'll change it back if desired; let me know.

@bjoernQ
Copy link
Contributor

bjoernQ commented Oct 10, 2023

@jessebraham @MabezDev any opinions on the usage of the ?.and_then(|_| ...)?; pattern - if not I guess this is fine to merge

Copy link
Member

@jessebraham jessebraham left a comment

Choose a reason for hiding this comment

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

We have really not put any energy into code size or consistency, so no reason to start now. Makes no difference to me, so LGTM.

Copy link
Contributor

@bjoernQ bjoernQ left a comment

Choose a reason for hiding this comment

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

LGTM

@jessebraham jessebraham merged commit 0aa0232 into esp-rs:main Oct 10, 2023
@Studiedlist Studiedlist mentioned this pull request Oct 13, 2023
7 tasks
MabezDev pushed a commit to MabezDev/esp-hal that referenced this pull request Oct 20, 2023
* Duplicate spi to spi_slave

* Restore spi

* Add barebones SPI slave mode, DMA only.

This setup allows registering buffers for future transactions the master
does (lowering cs, toggling sclk, and raising cs again). The transfer
struct returned from the registration API will complete its wait() or
return true from is_done() after cs has been raised.

Copied from spi.rs, so most of the changes are deleting code that
handles e.g. segmented transfers or synchronous operations.

Fix non-c3 devices' builds

* Limit spi_slave to non-pdma devices

* SPI slave DMA example

Ensure the API "feels" right.

Since there's no way to route GPIOs to other peripherals, we choose four
other wires and bit-bang SPI for the master side, relying on the person
running the example to connect the bus.  This way we ensure the slave
code works, since we created the master ourselves.

Also, it's not really possible to use a second ESP device as the master
anyway: all the digital lines have glitches on startup, and those
glitches cause the slave's DMA engine to skip descriptors (it thinks
they're intended CS indicators); this causes it to lose data.

Then, fix the bitbang master (recording the progression here)

- When bitbanging, iterate the bits by "for _ in 0..8", instead of the
  broken "for _ in [0..8]".  The latter only runs the iteration once,
  since there's only one list given ... and because the code uses _
  instead of a real loop variable, type checking didn't save us.
- When bitbanging, send the bits out (and read them in) MSB first, since
  that's actually how we have the slave configured.

* Add changelog entry

* Split DMA prepare_transfer into two fns.

The first does everything but write to the start bit and check for an
error. The second does those. We need 2 fns because the  SPI slave needs
to start the transfer only after resetting the various afifo hardware
components (if it starts the transfer before, the first 8 bytes will be
lost when that reset happens).

Use the split fns everywhere.

Also split flush().  It needs to be pollable, so split it into one fn that
polls and one that waits until the poll returns clear. Also call the poll
fn from the is_done() fn, so we don't trample in-progress transfers.

* Make example code fill rx buffer before transfer

This way we can tell if it's ever touching certain bytes - 0xff is never
added to the master transmit buffer.

While I'm changing this, make the slave tx buffer never contain 0xff
either (go from 254 to 0).

---------

Co-authored-by: Jesse Braham <[email protected]>
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.

SPI slave support

4 participants