master carries bootloader.atsln, so main does too. Two projects, because a .cppproj is one binary at one flag set and this build has hundreds: the stock 328P pureboot loader (USART0 at 115200 on a 16 MHz crystal), and the tsb_asm tier that occupies the same 512-byte section master's own tsb project targeted. Both come out byte-identical to the Ninja build — 404 B and 510 B of .text — in both configurations. Debug keeps -Os and adds only -gdwarf-4. A loader's section is a correctness bound, and -Og builds this source to 590 B: the link at 0x7e00 accepts that without a diagnostic, 78 bytes past flash end, where rcall/rjmp wrap modulo flash size and the image dies just after activation. Debug info costs no flash, so the optimisation level stays where correctness needs it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
79 lines
3.9 KiB
Markdown
79 lines
3.9 KiB
Markdown
# Atmel Studio
|
||
|
||
`master` carries `bootloader.atsln`, so this branch does too: `ide/bootloader.atsln`
|
||
builds the loaders from the same sources Ninja does, to a **byte-identical
|
||
`.text`** — 404 B for the 328P pureboot loader, 510 B for the `tsb_asm` tier in
|
||
its 512-byte section. CMake remains the build system; the solution is here so the
|
||
port opens in Studio as its predecessor did.
|
||
|
||
## The two projects, and why two
|
||
|
||
pureboot is a chip × backend × clock × baud matrix — `pureboot_add_loader()`
|
||
resolves a deployment into compile definitions — and a `.cppproj` is one binary
|
||
at one set of flags, so a project can only ever be one point of it. `pureboot`
|
||
is that point: the stock 328P deployment, USART0 at 115200 on a 16 MHz crystal,
|
||
an 8-second activation window. `tsb_asm` is the TinySafeBoot tier that occupies
|
||
the same 512-byte section `master`'s `tsb` project targeted.
|
||
|
||
The other three tsb tiers (`tsb_pure`, `tsb_tricks`, `tsb_policy`) are not here.
|
||
They differ from `tsb_asm` in their source file, their section size, and — for
|
||
`tsb_policy` — two loop flags; nothing about that is a Studio concern, and what
|
||
they exist to demonstrate is a size gradient only the CMake size tests measure.
|
||
Adding one is a copy of `tsb_asm/tsb_asm.cppproj` in its own directory, with its
|
||
name, its GUID, its source path and its `--section-start` changed (`0x7c00` for
|
||
the 1 KiB tiers), plus four lines in the solution.
|
||
|
||
`avrdevice` is a project property, so each project gets its own directory:
|
||
Studio builds into `<project dir>/<Configuration>` whatever `OutputDirectory`
|
||
says, and two projects sharing a directory would share one object file.
|
||
|
||
## Debug keeps `-Os`
|
||
|
||
Both configurations compile at `-Os`; Debug adds only `-gdwarf-4`. The `.text`
|
||
is therefore identical in both, which is the point — a loader's section is a
|
||
**correctness** bound and not a budget. `-Og` builds this same source to 590 B,
|
||
and linking it at `--section-start=.text=0x7e00` on a 32 KiB part puts 78 bytes
|
||
past flash end **without a diagnostic**: `rcall`/`rjmp` targets there wrap
|
||
modulo flash size, so the image dies right after activation. A debug
|
||
configuration that silently produces that is worse than none, and DWARF costs no
|
||
flash, so the optimisation level stays where correctness needs it.
|
||
|
||
## What Studio needs from the machine
|
||
|
||
libavr checked out **beside this repo**, found at
|
||
`$(MSBuildProjectDirectory)\..\..\..\libavr\include` — anchored to the project,
|
||
because a plain relative path resolves against the generated makefile's
|
||
directory (the configuration's output directory), not the project's. There is no
|
||
`LIBAVR_ROOT` escape hatch: a variable exported in a shell is invisible to Studio
|
||
launched from the Start menu, and the failure reads as a missing
|
||
`libavr/libavr.hpp`. Pinning libavr as a submodule is the better answer and is on
|
||
libavr's task list.
|
||
|
||
A GCC 16.1 toolchain registered as flavour `avr-g++-16.1.0`, nothing older
|
||
reaching `-std=c++26`.
|
||
|
||
## Generating and gating
|
||
|
||
One generated file is required before a project will load at all, and one command
|
||
per project checks the flags have not drifted (both from libavr's
|
||
`tools/atmelstudio/`):
|
||
|
||
```sh
|
||
for name in pureboot tsb_asm; do
|
||
python ../libavr/tools/atmelstudio/componentinfo.py \
|
||
"ide/$name/$name.componentinfo.xml" --device ATmega328P
|
||
python ../libavr/tools/atmelstudio/check-flags.py \
|
||
--solution ide/bootloader.atsln --project "$name" --target "$name" \
|
||
--compile-commands build/atmega328p-generated/compile_commands.json \
|
||
--log "build/as-$name.log"
|
||
done
|
||
```
|
||
|
||
`--project` because one reference describes one binary; `--target` because
|
||
`pureboot.cpp` is compiled by every point of the size matrix and the flags
|
||
differ per point, so the basename alone does not name a reference. Release is
|
||
what the gate compares — the presets define no debug build, and Debug differs
|
||
from Release only in `-gdwarf-4`.
|
||
|
||
Legacy (the yazoalfa-era submodules) stays on `master`.
|