1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! `defmt` logging over shared memory: the `CDBG` ring, its firmware-side
//! [`global_logger`], its host-side reader/decoder, and an optional panic handler.
//!
//! [`global_logger`]: https://docs.rs/defmt/latest/defmt/attr.global_logger.html
//!
//! # The problem
//!
//! A coprocessor on a Consortium SoC usually has no UART of its own to log
//! through, or has one already spoken for. But it does share memory with a
//! Linux-capable core that has a filesystem, a clock, and somewhere to put logs.
//! So the debug path runs the same way as everything else here: firmware writes
//! into a shared window, Linux reads it.
//!
//! `defmt` is the right encoder for the firmware end. Format strings and type
//! metadata stay in the ELF and never reach the wire, so a log line costs a few
//! bytes and no formatting code — which is what makes logging affordable on a
//! Cortex-M at all. The cost is that the bytes are meaningless without the ELF
//! they came from, so the host side needs a decoder.
//!
//! # Architecture
//!
//! ```text
//! firmware (no_std) Linux / host
//! ──────────────────── ─────────────────────
//! defmt::info!(..)
//! └─ logger::ConsortiumLogger host::HostRing
//! (global_logger; rzcobs-encodes (validate CDBG, drain frames)
//! one frame, wraps it in a └─ host::DefmtSession
//! packet, appends to the ring) (decode against the ELF)
//! │ │
//! └──────── CDBG shared window ────────┘
//! descriptor + circular buffer
//! panic::(handler) ─ latches Flags::Panic in the descriptor
//! ```
//!
//! | Module | Role |
//! | --------------- | --------------------------------------------------------------- |
//! | `config` | The ring base/length `ConsortiumLogger::init` records. |
//! | [`descriptor`] | The `CDBG` descriptor and single-writer/single-reader ring rules. |
//! | [`packet`] | Per-frame envelope inside the ring. |
//! | `logger` | Firmware `#[global_logger]`; encodes and appends. |
//! | `host` | Host-side validation, draining, and `defmt` decoding. |
//! | `panic` | Optional `#[panic_handler]` that records a crash in the ring. |
//!
//! # Descriptor discipline
//!
//! The ring has exactly one writer (firmware) and one reader (host), and there is
//! no lock. What keeps it correct is that **each side only mutates the fields it
//! owns**: the writer owns `write_ptr`, `capacity`, and `flags`; the reader owns
//! `read_ptr`; `write_ptr == read_ptr` means empty. Preserve that split when
//! changing anything here — it is the whole synchronization story.
//!
//! # Configuration
//!
//! Layout comes from the manifest, not from the firmware source. `csti build`
//! lowers `[dbg.<core>]` in `Consortium.toml` into the generated `init()`, which
//! calls `ConsortiumLogger::init(base, len)` first thing. Until that call, the
//! logger and the panic handler are **no-ops**, so a firmware crate built without
//! generated code still links and runs; it just logs nothing.
//!
//! # Features
//!
//! Defaults are `host` and `logger`.
//!
//! | Feature | Effect |
//! | --------------- | -------------------------------------------------------------- |
//! | `logger` | Firmware `#[global_logger]` writing into the ring. |
//! | `host` | Host-side `HostRing` reader; implies `std`. |
//! | `decode` | Adds `defmt-decoder`/`defmt-parser` for `host::DefmtSession`; implies `host`. |
//! | `panic-handler` | Installs `#[panic_handler]`; implies `logger`. |
//! | `arch-cortex-m` | Interrupt-disable and halt for Cortex-M. Required by `panic-handler`. |
//! | `arch-riscv` | Interrupt-disable and halt for RISC-V. Required by `panic-handler`. |
//!
//! `panic-handler` needs **exactly one** arch feature, and the two arch features
//! are mutually exclusive; both conditions are `compile_error!`s below rather than
//! link-time surprises.
//!
//! # Testing
//!
//! ```bash
//! cargo test -p consortium-dbg # descriptor, ring, packet
//! cargo test -p consortium-dbg --features decode
//! just test dbg host
//! ```
//!
//! Host-side tests cover descriptor initialization, ring read/write behaviour,
//! decoding fixtures, and the overflow/error flags — no target hardware needed.
/// Re-export of the defmt stream decoder, so host consumers (e.g.
/// `consortium-runtime-app`) can name `host::DefmtSession` callback types
/// (`Frame`) without a separate `defmt-decoder` dependency.
pub use defmt_decoder;
/// Re-export of `defmt-parser`, whose [`Level`](defmt_parser::Level) is the type
/// returned by `Frame::level()` (defmt-decoder does not re-export it publicly).
pub use defmt_parser;
compile_error!;
compile_error!;