imxrt-rs / imxrt-hal

Rust for NXP i.MX RT
Apache License 2.0
122 stars 29 forks source link

SAI: audio pll, sai peripheral driver, and small rtic synthesizing sample #143

Open SpinFast opened 7 months ago

SpinFast commented 7 months ago

A work in progress port of the mcux sdk SAI peripheral driver to imxrt-hal

This has to be one of the more complicated peripherals to configure given the sheer number of clocking, polarity, timing, and input/output options.

The first SAI peripheral additionally seems to allow for more than a single tx/rx channel which adds to the complications. After an initial attempt I decided to retry basing things on the mcux sdk driver which works well enough.

The basic idea is a configuration struct is constructed followed by handles for tx or rx channels. I've gotten most of the enums/config structs setup and a bare bones sample sort of in progress.

mciantyre commented 7 months ago

Thanks for adding this. I'm not familiar with the SAI peripheral, but I'll keep studying the reference manual to ramp up.

No preference on designs; using a primary Sai object as a factory for TX / RX channels looks like it will work, especially if channels have distinct registers they're working with. If you need a RAL trick for fabricating new sai::Instance<N> objects for the channels, these advanced usage tips might help.

SpinFast commented 7 months ago

Thanks for adding this. I'm not familiar with the SAI peripheral, but I'll keep studying the reference manual to ramp up.

No preference on designs; using a primary Sai object as a factory for TX / RX channels looks like it will work, especially if channels have distinct registers they're working with. If you need a RAL trick for fabricating new sai::Instance<N> objects for the channels, these advanced usage tips might help.

That was helpful thank you. I have 3 layers to this cake.

The peripheral, opt in tx/rx things with pins, opt in tx/rx channels with a data pin each (each with its own FIFO and DMA address and such), and then in the end I'd like to be able to read/write to each channel or maybe all channels at the same time.

The audio frame data can represent multichannel audio itself, so for example mono, stereo, or N many audio channels being tx/rx'ed from a codec chip which itself has multiple dacs/adcs for talking to headphones, analog mics, line in/out, etc.

It's all very confusing as I'm relatively new to these serial audio formats but its interesting to learn as I go.

SpinFast commented 7 months ago

Added various needed clocking modules/functions for setting up the audio pll (pll4), connecting it up to each sai clock root and setting a divider up.

It's not entirely clear the pll setup is correct but I believe it to be. The pll is bypassed, disabled, and powered down before then updating the various pll num, denom, div_select options, and then restarting it. I'd definitely appreciate some feedback on this part at the moment @mciantyre

mciantyre commented 7 months ago

I think the PLL4 setup is working. If you have a scope / logic analyzer available, the hal_clock_out example might be helpful. Using a serial console menu, we can route clocks to the CLKO1 and CLKO2 outputs, which have test points on some EVKs. Sorry in advanced for not having it set up for 1060 MCUs and EVKs.

I gave this a shot on my 1010EVK. After configuring PLL4 with the reset function, I routed PLL4 to CLKO1 and introduced an extra divide-by-8. I saw 22.7 MHz on TP34, which about what I expected to see. I pushed my test if you'd like to reproduce.

Screenshot 2023-11-10 at 21 29 16
SpinFast commented 7 months ago

As I noted on matrix, I concocted a python script to find optimal clock tree values given a desired output clock and it works pretty well! It requires python and gekko, and uses apopt to find a solution (in 1.6s on my machine). Sadly the solver (apopt) isn't FOSS so probably not great to add to the tree as I'm unsure of what the implications would be.

https://gist.github.com/SpinFast/aa1ec02f177b89858c7e1ffb9e77b543

@Dirbaio noted Z3 could perhaps be used to solve the same sort of problem and is FOSS licensed, it also happens to have a Rust wrapper. Maybe a build.rs clock tree builder could be concocted one day. Would be neat!

I setup the 1060evk and 1010evk clocks to match in board file for 10xx (presumably the other 10xxevks could vary if needed but I'd guess stereo 48KHz 16bit audio would be just fine anywhere)

In the end I'd like to playback audio with sai1 to the codec and sai3 to medium quality sound, so you can hear the sound out of the little speaker on the evk

SpinFast commented 7 months ago

I got the sai1 root clock working like I'd expect I believe in looking at the output of the hal_clock_out sample. That's a helpful example to have around

teburd commented 7 months ago

Glad to see this being done, I did try out the clock out sample with this PR and got what measured around 2.3MHz, I would have expected 48000*16*2 = 1536000 so maybe a divider is off or something.

SpinFast commented 7 months ago

Depends on https://github.com/imxrt-rs/imxrt-iomuxc/pull/40

SpinFast commented 6 months ago

The sine wave lut generation works (though again probably less than ideally) and produces a nice clean sine wave for me when I hook up the 3.5mm output from imxrt1010evk to my computer and record with sonic visualizer.

I'm pretty happy with this as a first pass at a SAI driver at this point and after some cleanup will mark the PR as good to go

Screenshot from 2023-12-06 21-34-04

SpinFast commented 6 months ago

When defmt prints periodically in the timer isr it seems like a glitch still occurs in the audio output even with the timer reduced in rate. It's pretty unclear whats happening as the sai fifo never reports an under run.

SpinFast commented 6 months ago

I think this is mostly ready to review, still waiting on the chiptool merge and release I guess for the wm8960 driver, I might give that another week before simply including the generated files in a release and moving on, leaving the generation at build stuff in branch

SpinFast commented 6 months ago

1176 issue is tied to an issue in the 1176 svd, see https://github.com/imxrt-rs/imxrt-ral/pull/45

SpinFast commented 3 weeks ago

I'm still looking to work on this at some point this summer and clean it up, but if anyone wishes to continue the work please don't let me stop you!

mciantyre commented 1 week ago

I pushed some commits after a deeper review of the SAI clock contributions. If they look questionable, we can drop them.

Florob commented 1 week ago

I have fixups for bitflags based Interrupts/Status as well as a rebase to current main (RTIC v2) on https://github.com/Florob/imxrt-hal/tree/imxrt-sai, if someone wants to apply those here.

dwbrite commented 5 days ago

For a demo sai1 impl on teensy we'll need:

TX_DATA0
    GPIO_B1_01
TX_DATA1 xor RX_DATA3
    GPIO_B0_12
TX_DATA2 xor RX_DATA2
    GPIO_B0_11
TX_DATA3 xor RX_DATA1
    GPIO_B0_10
RX_DATA0
    GPIO_B1_00

// SAI1 Clock and Sync

TX_BCLK / TX_SYNC nc on 4.0 (generally tied to rx counterparts?) GPIO_B1_02 / GPIO_B1_03 on 4.1
RX_BCLK
    GPIO_AD_B1_11
RX_SYNC
    GPIO_AD_B1_10
MCLK
    GPIO_AD_B1_09
dwbrite commented 5 days ago

And, for my own sake, the action items in this thread, since it can be difficult to keep track:

merge interrupt/API fixes+ rebase to main 🔲 address SAI chip portability + bug fixes 🔲 delete SAI_CLOCK_GATES array? 🔲 per-board clock gate collections? 🔲 support teensy4 😉

Is there anything I'm missing?

mciantyre commented 4 days ago

I think that's the gist!

My imxrt-sai branch has Florob's and my suggestions. If it's OK with @SpinFast, I'm happy to apply the fixups and force push to this branch.

SpinFast commented 4 days ago

I think that's the gist!

My imxrt-sai branch has Florob's and my suggestions. If it's OK with @SpinFast, I'm happy to apply the fixups and force push to this branch.

Please do! Let me know if there's anything easy I can do to help.