-
Notifications
You must be signed in to change notification settings - Fork 226
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
[RFC] How to apply configuration #2416
Comments
Should all other drivers be reworked to use a config struct? My main worry is that clock speed calculations on SPI aren't trivial and changing any configuration means changing everything with this approach. Should we keep |
Considering #2389 and the cost of a full reconfig, do we even want config structs? What if we got rid of them in favour of single option setters? |
Hmm, I quite like the idea of a config struct because it neatly packages everything in one place. In the case of Uart for example, we have to forward methods from Regarding the cost, we can actually be quite smart about this in the future. We can keep a copy of the current config and then only update the options that have changed. If we didn't want to pay the memory cost of storing the config in the driver, we could also just check the registers themselves (though this wouldn't work for registers that require some calculation from config inputs). |
Checking the registers before writing the same or different value into them doesn't make much sense :) The read-and-compare is probably more costly than a blind write/update. On the other hand, if you only want to update a single field, you have to lug around all your previous configuration anyway, so why pay the memory cost twice? We can store the config in the driver, and read from there, but that might be wasted memory also if config update isn't needed. |
There's a solution for this one. #1666 (comment) |
I would recommend always constructing a peripheral using |
I'd like to batch #1919 into this issue. Not necessarily for UART, but mainly for SPI - we get per-device configuration for free using https://docs.embassy.dev/embassy-embedded-hal/git/default/shared_bus/asynch/spi/struct.SpiDeviceWithConfig.html |
We have a few options on how to apply initial configuration, but we'll have to pick which one we'll do:
|
My gut feeling is 4 but my common sense would prefer 3 I assume that people will use fixed configurations so they will see the panic early in development and not have devices crash randomly in the field weeks later |
I guess if we choose 3 and someone absolutely needs the driver to not panic but provide an error, they can create the driver with |
I like the 3 the most. |
I also like 3 most. I sorta like 4 but I'd prefer if the error handling happened before creating the driver. (i.e. make it impossible to create an invalid config) which just makes it look like 3 anyway. |
Most of the difficulty comes from baud rates, especially where the clock source is configurable. In that case, it is unfortunately rather difficult to check a config in compile time. (or even in runtime, pre-calculating values and changing clocks might end up causing surprises). |
I mean something along the lines of |
I also agree with the 3rd option the most. |
I'm leaning towards 3, but I'm wondering about the case where driver initialization might happen later in the program. I see the value in panicking early if it's initialized early, but is it acceptable to panic later? Another consideration, what if the config is stored on an eeprom at runtime, the next reboot the settings are loaded from there. If someone somehow stored an invalid config we'll essentially bootloop. The solution to this in this case is to always use apply and pass |
Quoting our API-GUIDELINES
I feel like this pushes us closer to 4 or 2, unless we want to rethink this guideline. |
We also have the following which conflicts a bit:
Also note the two weakening conditions:
|
Just a reminder that we should spell out the decision taken here, especially since the last comment is essentially an objection to (3). I'm not confident whether I should treat this as a vote and move forward implementing (3). |
Whilst 3 is my favorite ergonomically, I think to support a wide range of use cases we should go with option 4. Option 3 relies to heavily on the fact a driver is created at the beginning of the program and lives the entire time. This doesn't play nicely with the peripheral pattern for example, and may end up with unexpected panics. Option 3 also pushes users to emulate option 2 for the case where they don't want to deal with those panics, meaning they call with default config and use
This would be nice, but I don't think it's possible to implement this for every driver, as it can depend on a lot of variables outside of what the config can see. One example would be to choosing a clock source that's not enabled, should the config be checking this and returning an error, or the driver? I would argue a config shouldn't be poking around in clock registers. I think if we go with option 4 now, we can come up with some more ergonomic helpers to alleviate the pain of |
Uart::new()
should be infallible, only applying some invalid config should be invalidRemove the independentFlatten Uart module, remove unnecessary data, replace methods withchange_x
mutator methods in favour ofapply_config(&mut self, config: &Config)
apply_config
#2449The text was updated successfully, but these errors were encountered: