diff --git a/docs/changelog.md b/docs/changelog.md index 7136d15..fb4bc93 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,6 +7,21 @@ description: Release notes for the manhattan-reasoning-gym SDK and CLI. Notable changes to the `manhattan-reasoning-gym` SDK and CLI. The project is in private beta, so surfaces may still change between `0.1.x` releases. +## 0.1.5 (private beta) + +### Added +- `App.write(addr, value, fixed_address=True)`: repeats `addr` for every word + in a burst instead of incrementing it, for a FIFO or push-register port + where a design keeps its own internal write index (a common streaming-load + pattern — writing a sequence of words one at a time to a single register, + with the RTL auto-advancing into the next clause/slot). +- `App.stream()`: a persistent, low-latency session for many small + `read`/`write` ops. Unlike `App.write()`/`App.read()`, which each dispatch + their own job against the cloud API and poll for completion every 0.5s, a + `Stream` holds one WebSocket connection open for the life of a `with` + block — useful for tight loops like loading a CNF instance one literal per + write, or an RL reward loop grading many episodes per training step. + ## 0.1.4 (private beta) The current beta surface: diff --git a/docs/guides/sdk.md b/docs/guides/sdk.md index 14ce0d5..94ff596 100644 --- a/docs/guides/sdk.md +++ b/docs/guides/sdk.md @@ -80,10 +80,47 @@ Read `count` 32-bit words starting at byte address `addr`. Returns a single `int` when `count == 1`, otherwise a `list[int]`. Programs the FPGA first if it hasn't been programmed yet. -#### `app.write(addr, value)` +#### `app.write(addr, value, fixed_address=False)` Write one or more 32-bit words to byte address `addr`. `value` may be a single -`int` or a `list[int]` for a burst write. +`int` or a `list[int]` for a burst write, which increments the address by 4 +per word (loading a register array, like a RAM). Pass `fixed_address=True` to +instead repeat `addr` for every word in the burst -- for a FIFO or +push-register port where your design keeps its own internal write index +(a common streaming-load pattern: writing a sequence of words one at a time to +a single register, with the RTL auto-advancing into the next clause/slot). A +plain burst would scatter those words across whatever registers happen to sit +at `addr+4`, `addr+8`, ... instead of pushing them all through the one port. + +#### `app.stream()` + +```python +with app: + with app.stream() as s: + for word in words: + s.write(LITERAL_IN, word, fixed_address=True) + s.write(REG_CTRL, 1) + while not (s.read(REG_CTRL) & 1): + pass +``` + +Opens a persistent, low-latency session for many small `read`/`write` ops. It +is a context manager exposing the same `write(addr, value, fixed_address=False)` +and `read(addr, count=1)` methods as `App`, but skips the per-call job queue: +every op on `app.write()`/`app.read()` directly dispatches its own job against +the cloud API and then polls for completion every 0.5s, so each individual +call costs roughly that much wall-clock time no matter how small the payload +is. That's fine for a handful of calls, but it dominates for a tight loop -- +loading a CNF instance one literal per write, or an RL reward loop that needs +to load and grade many episodes per training step. A `Stream` instead holds +one WebSocket open for the whole `with` block, relayed straight through to the +FPGA's Wishbone bus, and pays that connection cost once instead of once per +operation. + +A stream and `app.write()`/`app.read()` (or a second stream) can't be used on +the same FPGA at the same time -- the bridge firmware only safely serves one +open connection per board, so opening a stream holds an exclusive lock on the +FPGA's link for the life of the `with` block. #### `app.release()`