From 02afc1a6893588723b5cdd0cecce704ab08933d3 Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Thu, 23 Jul 2026 14:51:06 +0200 Subject: [PATCH] =?UTF-8?q?feat(gust):=20adc-thin=20caller-supplied=20CR2-?= =?UTF-8?q?extra=20mask=20=E2=80=94=20unblock=20F1=20internal=20channels?= =?UTF-8?q?=20(gale#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dissolved ADC driver wrote CR2 absolutely (ADON, ADON|SWSTART), dropping CR2.TSVREFE (bit 23) at the SWSTART instant. TSVREFE must stay set during the conversion to read an F1 internal channel (Vrefint ch17 / temp ch16). Thread a caller-supplied cr2_extra: u32 through adc_configure/adc_enable/adc_start, OR'd into each content-bearing CR2 write; adc_disable's teardown write (CR2=0) is unchanged. Regenerate adc-thin-cm3.o (synth 0.49.0/loom 1.2.0) and update both callers (gust_adc.rs, gust_adc_probe.rs) to the new ABI, passing 0 to preserve prior behavior. Co-Authored-By: Claude Opus 4.8 --- benches/gust/drivers/adc-thin/adc-thin-cm3.o | Bin 1811 -> 1843 bytes benches/gust/drivers/adc-thin/src/lib.rs | 27 ++++++++++++------- benches/gust/src/bin/gust_adc.rs | 16 +++++------ benches/gust/src/bin/gust_adc_probe.rs | 16 +++++------ 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/benches/gust/drivers/adc-thin/adc-thin-cm3.o b/benches/gust/drivers/adc-thin/adc-thin-cm3.o index e2630885bea9b398c71e36a21faaf91c16898807..15e1fa3d405d8b941f56ace16927b4b44aba099d 100644 GIT binary patch delta 823 zcmXw!&1+LZ6vgkn%=>6anzY1_l$O2}A&C}@(uLw?OzBW?k>Wx@U5Hp*bdw5IP$U)H zD1HPUuKWiC-G~IbuvV-K!DdnEHn?%2S-tlP@zH?sOqeVVXMS_%-Z|GB|1jQk+2Q-b zG1e$0+fjzvQ7+w%rrmbrRz#SNi7>%BQBK6?&+g>qSS~Hew>fTBL>RYP{a)nsvsXkI zBfC$b;m;=cQR2{%c{%v|Z_tS{f(Hv0-q&q^mbD|I%CN#^2WQk>BbYZuNN22)JUuK~ zs+2~lZU+vYxNg;FCdu-!x;bnVG8vS-pKPhoj*cTJ@n4G=E|Rj=8{FN6TGeMetVl&V zRoHLJtzaSrE>MBW`F%qgfx~CylFw{cp3_m?O%di9!wSw(BE-ztSE$;JaWxs4R!ZvE z5mCB@pVq&7uZ@A1HKx|^-rR$D*R*i9(QALIW_D^mb80?0TLGnJ_9<`-Y=p#& ze+~s->~b@$H|s>xH$-o6Z?{Lct*-503i}2vcbS0%E)rC7!@X1dR^uZP8N_K0z73tw zya4|LJ=J^>zQKsHnyA=5}4H>}DEc_J0K{&%+Izt`=`#opk~F7#&cNu5p7B%N|wCq$+Ua>=FAXba;V z7H|w6sX_` zXNPF^XV4<>$QK z;P!5hR^ykS@p7qSC6HqSIjYC|mW|cQHW6uLPyt?prWMb__n-%gFTmFr(TL&|_$K(S z;v4Y4;46wZ;g>a{wBo1mHE>lit`bxs8DIzjWaBu1 u32 { +pub extern "C" fn adc_enable(base: u32, state: u32, channel: u32, cr2_extra: u32) -> u32 { match enable(unpack(state), channel) { Ok(next) => { - wr(base + CR2, CR2_ADON); + wr(base + CR2, CR2_ADON | cr2_extra); pack(next) } Err(_) => ADC_FAULT, @@ -286,11 +292,14 @@ pub extern "C" fn adc_enable(base: u32, state: u32, channel: u32) -> u32 { /// Start one conversion (SWSTART): Ready → Converting. Returns the packed Converting /// state or `ADC_FAULT` if not Ready. Sets CR2.SWSTART (keeps ADON, CONT stays 0). +/// `cr2_extra` = caller-supplied CR2 bits OR'd into the managed write (e.g. +/// `TSVREFE` (1<<23) to read F1 internal channels Vrefint/temp; also ALIGN, +/// EXTTRIG). The managed bits (ADON, SWSTART, CONT=0) stay authoritative — gale#216. #[no_mangle] -pub extern "C" fn adc_start(base: u32, state: u32) -> u32 { +pub extern "C" fn adc_start(base: u32, state: u32, cr2_extra: u32) -> u32 { match begin(unpack(state)) { Ok(next) => { - wr(base + CR2, CR2_ADON | CR2_SWSTART); + wr(base + CR2, CR2_ADON | CR2_SWSTART | cr2_extra); pack(next) } Err(_) => ADC_FAULT, diff --git a/benches/gust/src/bin/gust_adc.rs b/benches/gust/src/bin/gust_adc.rs index e04eb986..c2f058b7 100644 --- a/benches/gust/src/bin/gust_adc.rs +++ b/benches/gust/src/bin/gust_adc.rs @@ -28,9 +28,9 @@ pub extern "C" fn mmio_write32(addr: u32, val: u32) { } extern "C" { - fn adc_configure(base: u32, channel: u32, sample_code: u32); - fn adc_enable(base: u32, state: u32, channel: u32) -> u32; - fn adc_start(base: u32, state: u32) -> u32; + fn adc_configure(base: u32, channel: u32, sample_code: u32, cr2_extra: u32); + fn adc_enable(base: u32, state: u32, channel: u32, cr2_extra: u32) -> u32; + fn adc_start(base: u32, state: u32, cr2_extra: u32) -> u32; fn adc_poll(base: u32, state: u32) -> u32; fn adc_read(base: u32, state: u32) -> u32; fn adc_sample(state: u32) -> u32; @@ -99,7 +99,7 @@ fn main() -> ! { // 0) channel bounds: enable with a channel above MAX_CHANNEL is rejected, no // CR2 write. - let bad = adc_enable(ADC1, 0, MAX_CHANNEL + 1); + let bad = adc_enable(ADC1, 0, MAX_CHANNEL + 1, 0); let cr2_bad = read_volatile((ADC1 + CR2) as *const u32); tx(if bad == ADC_FAULT && cr2_bad == 0 { b"adc-chanbound-ok\n" @@ -108,7 +108,7 @@ fn main() -> ! { }); // 1) enable: Off → Ready, the DRIVER writes CR2=ADON. - let s1 = adc_enable(ADC1, 0, CH); + let s1 = adc_enable(ADC1, 0, CH, 0); let cr2_1 = read_volatile((ADC1 + CR2) as *const u32); tx(if s1 != ADC_FAULT && phase_of(s1) == PH_READY && cr2_1 == CR2_ADON { b"adc-enable-ok\n" @@ -118,7 +118,7 @@ fn main() -> ! { // 2) configure: table-free SMPR2/SQR3/SQR1 land exactly. // smpr_bits(3,4)=4<<9=0x800; SQR3 SQ1=3; SQR1 length(1)=0; CR2=ADON. - adc_configure(ADC1, CH, SAMPLE_CODE); + adc_configure(ADC1, CH, SAMPLE_CODE, 0); let smpr2 = read_volatile((ADC1 + SMPR2) as *const u32); let sqr3 = read_volatile((ADC1 + SQR3) as *const u32); let sqr1 = read_volatile((ADC1 + SQR1) as *const u32); @@ -130,7 +130,7 @@ fn main() -> ! { }); // 3) start: Ready → Converting, the DRIVER writes CR2=ADON|SWSTART. - let s3 = adc_start(ADC1, s1); + let s3 = adc_start(ADC1, s1, 0); let cr2_3 = read_volatile((ADC1 + CR2) as *const u32); tx(if s3 != ADC_FAULT && phase_of(s3) == PH_CONVERTING && cr2_3 == (CR2_ADON | CR2_SWSTART) { b"adc-start-ok\n" @@ -172,7 +172,7 @@ fn main() -> ! { // 7) single-shot: the read landed Ready (not Converting); reading again is // rejected and re-converting needs an explicit start. let twice = adc_read(ADC1, s6); - let rearm = adc_start(ADC1, s6); + let rearm = adc_start(ADC1, s6, 0); tx(if twice == ADC_FAULT && rearm != ADC_FAULT && phase_of(rearm) == PH_CONVERTING { b"adc-single-shot-ok\n" } else { diff --git a/benches/gust/src/bin/gust_adc_probe.rs b/benches/gust/src/bin/gust_adc_probe.rs index 712c35eb..50aeb211 100644 --- a/benches/gust/src/bin/gust_adc_probe.rs +++ b/benches/gust/src/bin/gust_adc_probe.rs @@ -30,9 +30,9 @@ pub extern "C" fn mmio_write32(addr: u32, val: u32) { } extern "C" { - fn adc_configure(base: u32, channel: u32, sample_code: u32); - fn adc_enable(base: u32, state: u32, channel: u32) -> u32; - fn adc_start(base: u32, state: u32) -> u32; + fn adc_configure(base: u32, channel: u32, sample_code: u32, cr2_extra: u32); + fn adc_enable(base: u32, state: u32, channel: u32, cr2_extra: u32) -> u32; + fn adc_start(base: u32, state: u32, cr2_extra: u32) -> u32; fn adc_poll(base: u32, state: u32) -> u32; fn adc_read(base: u32, state: u32) -> u32; fn adc_sample(state: u32) -> u32; @@ -88,7 +88,7 @@ fn main() -> ! { // 0) channel bounds: enable with a channel above MAX_CHANNEL (18) MUST be rejected // — ADC_FAULT, no CR2 write. A dissolve that silently no-ops the bound check // would latch an out-of-range mux input. - let bad = unsafe { adc_enable(base, 0, MAX_CHANNEL + 1) }; + let bad = unsafe { adc_enable(base, 0, MAX_CHANNEL + 1, 0) }; let cr2_after_bad = rw(CR2); if bad != ADC_FAULT || cr2_after_bad != 0 { hprintln!("adc-chanbound FAIL: enable(18)={:#x} CR2={:#x}", bad, cr2_after_bad); @@ -98,7 +98,7 @@ fn main() -> ! { } // 1) enable: Off(state=0) → Ready, driver WRITES CR2=ADON (a no-op leaves it 0). - let s1 = unsafe { adc_enable(base, 0, CH) }; + let s1 = unsafe { adc_enable(base, 0, CH, 0) }; let cr2_1 = rw(CR2); if s1 == ADC_FAULT || phase_of(s1) != PH_READY || cr2_1 != CR2_ADON { hprintln!("adc-enable FAIL: s1={:#x} phase={} CR2={:#x}", s1, phase_of(s1), cr2_1); @@ -110,7 +110,7 @@ fn main() -> ! { // 2) configure: table-free bit arithmetic lands in SMPR2/SQR3/SQR1 exactly. // smpr_bits(3,4) = 4 << ((3%10)*3) = 4 << 9 = 0x800 in SMPR2; SQR3 SQ1 = 3; // SQR1 length(1) = 0; CR2 = ADON (single-shot, CONT=0). - unsafe { adc_configure(base, CH, SAMPLE_CODE) }; + unsafe { adc_configure(base, CH, SAMPLE_CODE, 0) }; let smpr2 = rw(SMPR2); let sqr3 = rw(SQR3); let sqr1 = rw(SQR1); @@ -126,7 +126,7 @@ fn main() -> ! { } // 3) start: Ready → Converting, driver WRITES CR2=ADON|SWSTART (keeps CONT=0). - let s3 = unsafe { adc_start(base, s1) }; + let s3 = unsafe { adc_start(base, s1, 0) }; let cr2_3 = rw(CR2); if s3 == ADC_FAULT || phase_of(s3) != PH_CONVERTING || cr2_3 != (CR2_ADON | CR2_SWSTART) { hprintln!("adc-start FAIL: s3={:#x} phase={} CR2={:#x}", s3, phase_of(s3), cr2_3); @@ -175,7 +175,7 @@ fn main() -> ! { // free-runs. Reading AGAIN from Ready is rejected (exactly-once), and the only // way back to Converting is an explicit start — not an implicit re-arm. let twice = unsafe { adc_read(base, s6) }; - let rearm = unsafe { adc_start(base, s6) }; + let rearm = unsafe { adc_start(base, s6, 0) }; if twice != ADC_FAULT || rearm == ADC_FAULT || phase_of(rearm) != PH_CONVERTING { hprintln!("adc-single-shot FAIL: read-twice={:#x} restart={:#x} phase={}", twice, rearm, phase_of(rearm)); ok = false;