Skip to content

Ugn grooming embedded#1324

Draft
lmbollen wants to merge 21 commits into
mainfrom
lucas/ugn-grooming-embedded
Draft

Ugn grooming embedded#1324
lmbollen wants to merge 21 commits into
mainfrom
lucas/ugn-grooming-embedded

Conversation

@lmbollen

@lmbollen lmbollen commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

What (what did you do)

Why (context, issues, etc.)

Dear reviewer (anything you'd like the reviewer to pay close attention to?)

AI disclaimer (heads-up for more than inline autocomplete)

TODO

Cross-out any that do not apply

  • Write (regression) test
  • Update documentation, including docs/
  • Link to existing issue

@google-cla

google-cla Bot commented Jun 4, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@lmbollen
lmbollen force-pushed the lucas/ugn-grooming-embedded branch from a938ba6 to 989e3d5 Compare June 4, 2026 15:43
lmbollen added 21 commits June 15, 2026 15:10
Increase MU processing element instruction and data memory from 16KB
to 160KB (10 RAMB18E2 blocks each) to accommodate the larger
smoltcp-demo firmware binary.

Refactor the RingBuffer simulation test infrastructure to load firmware
binaries by name instead of building PeConfig from Haskell. Add
simSmolTcp for simulating the smoltcp ring buffer test. Support
multiple TX/RX ring buffer pairs in the DUT circuit. Remove IO from
simResultRingBuffer return type.
Update all internal doc references to use the new hyphenated
ring-buffer naming convention.
Replace the pinned git smoltcp 0.11 dependency in bittide-hal with
crates.io smoltcp 0.12. Add the medium-ip feature needed for
point-to-point links. Remove workspace lints inherited section.

Fix ring_buffer_test to use the renamed device instance fields
(transmit_ring_buffer_0, receive_ring_buffer_0) after the ring buffer
HAL was updated to support multiple ring buffers per device.
Move MAC address utilities from smoltcp.rs into smoltcp/mac.rs and
convert the smoltcp module from a single file to a directory module.
Update smoltcp_client to use the new import path.

Also update smoltcp_client to remove workspace lints, add medium-ip
feature, and fix format string and mutability warnings.
Log state changes in the link startup state machine to aid debugging
link bring-up issues on hardware.
Add modules for TCP-over-ring-buffer communication between nodes:
- net_state: UGN graph types (UgnEdge, UgnReport) for collecting
  network state across nodes
- smoltcp/ring_buffer: smoltcp Device implementation over aligned
  ring buffers with CRC integrity and sequence numbers
- smoltcp/link_protocol: wire format types for inter-node commands
  and UGN edge data
- smoltcp/link_interface: high-level LinkInterface combining smoltcp
  TCP with ring buffer transport, providing typed send/recv over
  point-to-point links

Add crc and zerocopy dependencies. Enable generic_const_exprs
feature gate. Remove workspace lints inherited section. Add medium-ip
feature to smoltcp.
Add three new firmware crates:
- smoltcp-demo: HITL demo for soft UGN collection over TCP links,
  with ring buffer alignment, DNA exchange, and UGN report
  aggregation via a manager/subordinate protocol
- ring_buffer_smoltcp_test: simulation test for TCP over ring buffers
- tcp_simultaneous_open_test: simulation test for TCP simultaneous
  open (both sides connect at the same time)
One demo needs significantly larger memories, this way we dont have to do that for each demo
A small directed, weighted graph backed by a Data.Map adjacency structure, with the
Bellman-Ford shortest-path / negative-cycle machinery (potentials, relabel,
negativeCycle, cycleSum). Node and weight types are generic.

This is the reusable foundation for UGN grooming: deciding whether a set of UGNs is
physically allowed reduces to "no negative cycle". Kept standalone (no UGN specifics)
so it can be tested and reused independently.
UGNs are signed per-link clock/frame offsets (edge weights). They are physically
allowed iff the sum around every directed cycle is nonnegative, and one boot can be
relabeled onto another iff a difference-constraint system is feasible. Both reduce to
the negative-cycle / potential machinery from Bittide.Graph.Weighted.

Provides isAllowed and groomCorrection (Infeasible | Feasible{correction, frames}),
using Integer weights to avoid overflow during relaxation. Pure and demo-agnostic.
A small Wishbone peripheral with one management-unit-writable register (release_cycle,
default maxBound, with a description) that takes a local counter and holds its output
reset asserted until localCounter > release_cycle, then deasserts (monotonic counter,
so it stays deasserted).

This is how the large boot-time counter offset (the grooming relabel q, far too big
for the depth-bounded elastic buffer) is realised: gating a domain's reset on a chosen
cycle sets that domain's counter origin. Generic over the Wishbone width; unit test
verifies the reset deasserts exactly at the written cycle.
A small Wishbone scratch peripheral with two read/write registers: a vector of n
signed per-link corrections (init 0) and a valid flag (init False).

The host computes the per-link grooming frame corrections and writes them here over
GDB; the management-unit CPU polls valid, reads the corrections and applies them to
its elastic buffers. Plain bus-accessible scratch (no fabric writes), mirroring the
CaptureUgn idiom.
… the user core

Add both host-facing grooming peripherals to the generic management unit
(NmuInternalBusses 3 -> 5):

  - UgnCorrections: the host writes per-link frame corrections for the MU CPU to
    apply.
  - TimedReset: the management unit chooses the local-counter cycle at which the
    application leaves reset (the boot-offset relabel). It lives here, in the layer
    around the user core, and exposes an `appReset` output.

`core` routes `appReset` into the user core via a new `UserCoreCircuit` argument. The
wire demo's user core uses it as the relabeled time base: an application counter that
starts at release and drives the programmable mux (UserCoreBusses 3 -> 2, since the
timed reset no longer sits on a user-core bus). The application counter stays in the
user core; the soft-UGN user core ignores the reset.
Host-side helpers for the grooming flow, placed beside the existing HITL
utilities: GDB-based component HAL accessors (readCurrentTime, writeReleaseCycle,
writeCorrections, readHardwareUgns, readAppCounter) in Utils.Gdb, and the UgnEdge
conversions (ugnEdges, hardwareUgnEdges) in Utils.Ugn.
After printing the captured UGNs, the management unit polls the UgnCorrections
peripheral's valid flag, then applies each link's frame correction to its elastic
buffer as a single atomic wishbone write with hardware ack (rather than one frame
at a time). A per-frame loop is vulnerable to concurrent Callisto adjustments
between iterations, which shrink the net data_count change; a single write limits
the Callisto race window to at most one adjustment, keeping the error to ±1 frame.
It self-reports a success line the host waits on.

The firmware half of the grooming apply path; the corrections values and the
reset-release timing are computed and written by the host.
…rdware

Diagnostic instrumentation to pin down why the canonical-gauge grooming breaks the
XOR chain partway (nodes 0-2 pass, 3-7 fail) despite the schedule math being
equivalent to main's proven generateSchedule and the frame path being exercised by
an earlier green run.

- UserCore: expose the application counter via a read-only Wishbone register
  (new AppCounterReadback device, UserCoreBusses 2 -> 3). With the app running,
  localCounter - app_counter == tReset for a node iff its timed-reset relabel
  released at the intended (relabeled) cycle.
- Driver: after the app runs, read each node's app_counter + localCounter and print
  loc-app vs tReset, so a divergence pinpoints which node's reset did not land.

This is instrumentation, not the fix; it isolates whether the failure is the
Bellman-Ford reset relabel not landing on-device vs the frame corrections.
Stacking the UGN-grooming work on top of the ring-buffer/smoltcp branch
surfaces two integration points in the async-comms demo, which the
grooming branch never saw:

  * grooming added an application-reset argument to 'UserCoreCircuit';
    forward it (unused) in the demo's 'mkUserCore'.
  * the demo's driver still used 'Utils.Picocom'; main has since renamed
    it to 'Utils.Serial', so move to 'initSerial' (matching the other
    demo drivers).
@lmbollen
lmbollen force-pushed the lucas/ugn-grooming-embedded branch from a080ba8 to 17a2c8c Compare June 15, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant