Exponential ramp for PWM
Here's my best prototype-design:
Choose a base that is easy to multiply in RISC. The easiest is 1/2 which only requires shift operations. Successive accuracy is gained by using an exponent of 1/2, 3/4,  7/8 (etc), which progressively require more and more computation. For these purposes 1/2 is totally fine.
The ideal curve is
$$
y = 1 − 0.1 ^ {t/2}
$$
Approximation requires converting the base:
$$0.1 ^ {t/2} = 2 ^ {-t / \tau}$$
$$
\tau = - \frac {2 \ln 2} {\ln 0.1} \approx 0.6021 s
$$
where \$ \tau \$ is the differential halving time.
To approximate this with PWM, the output is the mean of the duty cycle. From here on in I don't examine the PWM waveform directly, and only the &quot;instantaneous equivalent duty cycle&quot; ranging from 0.0 to 1.0.
The PIC16F1773 includes a powerful complementary output generator (COG) that supports &quot;steered PWM&quot;. In this mode, there is a rising and falling block, each accepting a variety of input clocks. For these purposes the most appropriate clocks are PWM5 and 6, respectively; each has 16-bit period resolution. The duty cycle of the individual PWM inputs does not matter and should be small; it's only used for the timing of its edge. Using superimposed pulse-modulated waves with a given beat frequency in this manner produces a duty cycle whose derivative is based on the beat frequency. In this scheme it's crucial to have a starting phase for the two sub-PWMs that is equal, and for no phase error to be introduced during execution.

For set and reset frequencies,
$$
f_{reset} = \frac {f_{set}^2} {
\frac {\partial DC} {\partial t} + f_{set}
}
$$
where
$$
\frac {\partial DC} {\partial t} = \frac {\ln 2} \tau \cdot 2 ^{-t/\tau}
$$
Over the first segment, the mean derivative of the duty cycle is
$$
DC'(0) =
\frac 1 \tau 
\int_0^\tau
\frac {\ln 2} \tau \cdot 2 ^{-t/\tau} dt 
= \frac 1 {2 \tau} \approx 0.8304
$$
Getting it into the discrete domain is a little tricky. I want to select a fixed set period that is a multiple of 256 and has an initial corresponding reset period offset by exactly 128:
$$ P_{set} \mod 2^8 := 0 $$
$$ P_{set} + 2^7 := P_{reset} (0) $$
$$ 2^8 \le P_{set} \le P_{reset} &lt; 2^{16} $$
This property allows the low byte of the reset period to be shifted in one operation every time a segment update occurs without needing to touch the high byte and without loss of accuracy.
The initial beat frequency between the set and reset PWMs is:
$$
f_{set} - f_{reset} (0) = f_{set}
\left(
1 - \frac {f_{set}} {f_{set} + DC'(0)}
\right)
$$
In reality the clock is 16 MHz (HFINTOSC before the PLL). In theory, with a 32 MHz clock, the lowest \$ f_{set} \$ that offers sufficient resolution to satisfy the above is

Prescaler = 8
\$ P_{set} = 24,832 \$ = 0x6100
\$ P_{reset} (0) = 24,960 \$ = 0x6180
\$ f_{set} = \frac {32 MHz} {prescale (P_{set} + 1)} \approx 161.08 Hz \$
\$ f_{reset} (0) = \frac {32 MHz} {prescale (P_{reset} + 1)} \approx 160.25 Hz \$
Segment length (samples) = \$ \tau f_{set} \approx 97 \$

The ramp routine is considered complete when the beat frequency drops to 0, i.e. the set and reset periods become equal, the duty cycle becomes constant, and the duty cycle reaches approximately 100%.
A numerical simulation shows that, for my purposes, the result is a good approximation:

Switching to a base of 3/4 improves the approximation even further but that's not necessary for me. The real advantage to this approach is very minimal runtime instruction execution needed, and ability to run (mostly) during sleep.