Commit Graph

9 Commits

Author SHA1 Message Date
925106394b build: the filed i2c cost comes back, -70 B on the tiny85
1316 -> 1246. libavr took the finding this repo filed when its pin last moved:
the shared transaction ladder's cost was never the ladder but the argument
list reaching it - AVR passes eight bytes in call-clobbered registers and the
rest in call-saved ones, so twelve bytes made every forwarder save four it
never reads. A write has no read span, so the write path now passes eight.

Worth recording that the candidate this repo suggested was measured and
rejected upstream, and the numbers filed from here are why: it wins 66 B on a
driver like this one and charges a single-byte reader 20, which is a class
rather than one program.

The mega328P does not move at 1480 - its TWI backend has its own ladder and
was never charged. Net over the whole 22-commit advance the tiny85 is +8 B,
and all of it is the bit-period fix that made the wire rate stop following the
inliner.

Sixteen tests green, cross-mode identity held on both chips.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 06:22:23 +02:00
bd604ec288 build: the libavr pin advances, and the tiny85 image grows 78 B
43fc479 -> aec9955. The mega328P does not move at 1480 B - it drives the RTC
from the TWI block, which neither change touches. The tiny85 has no TWI, so
dev::i2c resolves to the bit-banged master there and takes both:

  cb40a13, the bit period                      1238 -> 1262  (+24)
  943ffbe, one call shape over every master    1262 -> 1316  (+54)

The first is a bug being paid for. software_master delayed a fixed quarter
period and let its own instructions make up the rest of the bit, so the rate
on the wire followed the inliner; each bit now delays a low and a high phase
net of what that phase already spends. The DS3231 is a 400 kHz part and this
example asks for 100, so nothing here was out of spec - the bytes buy a rate
that no longer moves when the image does.

The second is a trade, and it is worth writing down which way it went. The
shared ladder is smaller than the two it replaced - run_transfer is 232 B
against read_regs 162 + write_regs 102 + stop 30 - but the call-site thunks
that marshal spans into it cost 116 B where the specialised ladders were
inlined, so a driver making both calls nets +54. Filed upstream with these
numbers rather than worked around here: which way the trade falls is a
property of the consumer's mix, and that is the library owner's call.

Size re-recorded in the two places that state it. Four presets green,
cross-mode identity held on both chips.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 02:17:44 +02:00
6e179fbac6 test: the example's size stops being a number nobody holds
Every other port in the fleet states its image size and is held to it; this
one stated none, so a library advance could move `clock` and no check would
say a word - which is exactly what the size claim was built after. The two
numbers are what the presets build today, byte-identical across generated
and reflect, and the README now says both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 19:20:24 +02:00
2e9c929a19 fix: an alarm's interrupt could be turned on and never off, and nothing tested any of it
`set_alarm1/2`'s `enable_interrupt = false` did nothing at all - it skipped the
control-register write rather than clearing A1IE/A2IE - and there was no other
way to clear them, so an alarm armed once with its interrupt on drove INT/SQW
for good. It is `alarm_interrupt::off` now and it clears the bit, with
`listen_alarm1/2()` and `mute_alarm1/2()` beside it for a caller that arms once
and changes its mind later. The bare bool went with it (rule 37): `set_alarm1(at,
rate, false)` said nothing at the point of use.

`bool SetWeekday` was the same shape one level up - `device<bus, false>` names
nothing - and its two writers disagreed about what it meant: `write_date`
skipped the weekday register, `write_clock` stamped a placeholder 1 into it, so
a program that turned the maintenance off still had the register overwritten
and no way to set it. `weekday_source::external` now means the register is the
program's, in both writers.

test/consteval.cpp is the battery this driver never had, over the arithmetic
that has no bus in it: the BCD round trip across every representable value and
its nibble layout, the hours register in both formats including all four
noon/midnight cases, Sakamoto's weekday against six calendar dates (leap day,
the day after it, and 2100's missing leap), and both alarms' rate encodings
transcribed against 19-5170 Table 2 - including that alarm 2's masks sit one
place below alarm 1's, which is the property that makes one distribution loop
wrong for the other. Red-green: two assertions fire on a flipped DY bit.

Beside it, one decode rather than two: `read_clock` and `read_time` spelled the
seconds/minutes/hours triplet out separately (rule 6).

Every image byte-identical on both chips in both modes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 15:40:52 +02:00
87e1f5e8a1 build: the libavr pin advances past phase 6, and the driver takes the bus whole
The register sugar now rides the bus's own master role - device<Bus>
resolves avr::i2c::device<typename Bus::master, bus_address>, so a
consumer keeps handing over its declared dev::i2c<...> unchanged - and
the DS3231's hardwired 0x68 is a named constant. The example's four
discards became the LED's own error signal (a failed seed or a failed
alarm clear holds it dark, the same word a stuck bus says), the tree is
reformatted under InsertBraces, the sources are ASCII, and the README's
stale Studio byte counts are replaced by the claim its check-flags gate
holds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:46:10 +02:00
d219b299f4 build: libavr rides as the pinned submodule
The submodule replaces FetchContent and the unpinned forge fallback;
LIBAVR_ROOT stays as the tandem-development override, the presets take the
toolchain file from the submodule, and the Studio project's include path
anchors there — correct by construction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 00:29:38 +02:00
0ecd53856b ide: the attiny85 beside the atmega328p, one directory each
The presets cover both parts, so the solution should too. Studio carries
avrdevice as a project property, so a part is a project rather than a
configuration, and both build example/main.cpp to a .text byte-identical to
their CMake counterpart: 1242 B on the mega, 1212 B on the tiny, each with the
architecture its own object file says.

They need separate directories, not just separate names. Studio builds into
<project dir>/<Configuration> whatever OutputDirectory claims — setting it per
project creates the directory and then ignores it — so two projects in one
directory share example/main.o. Verified rather than assumed: with both in ide/,
the single shared object read avr:25 while the mega's ELF read avr:5, so a
build of one after the other without a rebuild links the wrong part's object.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 19:51:50 +02:00
dc89d0779a ide: the Atmel Studio solution master has, on the libavr port
master opens in Studio, so this branch should too. The solution builds
example/main.cpp — the port's only executable, the driver itself being an
INTERFACE library — for the ATmega328P that solution targeted, and the
acceptance is not that it builds but that it builds the same firmware: .text
comes out byte-identical to CMake's at 1242 B.

One project, one chip. Studio carries avrdevice as a project property, so the
attiny85 half of the presets would need a second project rather than a second
configuration; CMake stays the build system and covers both.

libavr is found beside this repo, anchored to the project directory rather than
written relative to the generated makefile, which runs from the configuration's
output directory. No LIBAVR_ROOT: a variable exported in a shell is invisible to
Studio launched from the Start menu.

Both configurations verified headless with LIBAVR_ROOT unset, and the flag
mirror checked against the CMake build's own compile_commands.json.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 19:33:39 +02:00
cdca5219d0 Rewrite on libavr
Same driver surface as the yazoalfa version — clock and alarm get/set,
alarm interrupts, flag check/clear — plus oscillator-stop detection and
die temperature. One source for tiny85 (software I2C) and mega328P (TWI),
built against libavr in both generated and reflect mode, byte-identical
.text across modes. Errors surface as std::expected instead of being
dropped; weekday-rate alarms now really set the DY bit (legacy cleared
it); multi-register access is one coherent bus transaction. Legacy stays
on master.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-18 01:09:43 +02:00