mrg.cloud¶
Direct cloud silicon for a key-holding user or agent: declare an App, program
a real board, and drive it over MMIO.
manhattan_reasoning_gym.cloud ¶
Direct cloud silicon — for a user or unrestricted agent holding an API key.
Talks to the orchestrator directly (needs MRG_API_KEY + network) to program
and drive a real FPGA: define an App and mrg run it, or use the session
helpers. Sandboxed agents do not use this surface — they have no key and no
egress; they reach silicon through the broker (see mrg.sandbox).
App ¶
A Manhattan Reasoning Gym application: design source + register map + API config.
The API key is resolved automatically (explicit api_key arg >
$MRG_API_KEY > the key stored by mrg login), so it
usually doesn't need to be passed explicitly.
fpga_id is optional and normally left unset -- a fresh build never
picks a board itself, the server assigns whichever one frees up first once
the build finishes, and this fills in automatically once _program()
completes. Pass it explicitly only to reconnect to a board you already
have a live session on without rebuilding (mrg run --no-program
--fpga-id N / App(..., fpga_id=N) with programming skipped).
Typical usage::
app = manhattan_reasoning_gym.App(
"my_design",
design="path/to/design.py",
)
@app.local_entrypoint()
def main():
app.write(Regs.DATA_IN, 0xDEADBEEF)
print(hex(app.read(Regs.DATA_OUT)))
Run with::
mrg run myfile.py
Source code in _code/src/manhattan_reasoning_gym/_app.py
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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
local_entrypoint ¶
Decorator that marks a function as the CLI entrypoint for this app.
The decorated function is called by mrg run after the FPGA
has been programmed. It is NOT called automatically when the file is
imported or run with python.
Source code in _code/src/manhattan_reasoning_gym/_app.py
read ¶
Read count 32-bit words starting at byte address addr.
Returns a single int when count is 1, or a list[int] otherwise.
Programs the FPGA first if not already done.
Source code in _code/src/manhattan_reasoning_gym/_app.py
write ¶
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.
fixed_address=True repeats addr for every word instead of
incrementing it -- for a hardware FIFO/push-register port (a design
that keeps its own internal write_idx), where a normal burst would
scatter words across whatever registers happen to sit at
address+1, address+2, ... Programs the FPGA first if not already done.
Source code in _code/src/manhattan_reasoning_gym/_app.py
stream ¶
Open a persistent, low-latency session for many small ops.
Unlike write()/read(), ops on the returned Stream skip the
per-call job queue and poll loop -- use this for tight loops like
streaming a CNF instance in one literal per write, or an RL reward
loop that needs many ops per episode. Programs the FPGA first if not
already done.
Source code in _code/src/manhattan_reasoning_gym/_app.py
release ¶
Release the active session, returning the reset job_id.
RegisterMap ¶
Base class for FPGA register address maps.
Subclass and define integer class attributes for named register byte addresses:
class Regs(manhattan_reasoning_gym.RegisterMap):
CTRL = 0x0000
DATA_IN = 0x0004
DATA_OUT = 0x0008
Source code in _code/src/manhattan_reasoning_gym/_app.py
Stream ¶
A persistent, low-latency Wishbone session -- bypasses the job queue.
Every op on App.write()/App.read() dispatches its own job against
the cloud API and polls for completion every _RUN_POLL_INTERVAL
seconds, so each one costs roughly that much wall-clock time regardless
of payload size. For a tight loop -- streaming a CNF instance in one
literal per write, an RL reward loop that loads and grades many episodes
per training step -- that per-op cost dominates. A Stream instead holds
one WebSocket open to the orchestrator (relayed straight through to the
FPGA's Wishbone bridge, bypassing the Redis job queue entirely) and pays
the connection cost once instead of once per op.
Use via App.stream(), not directly::
with app:
with app.stream() as s:
for word in literals:
s.write(LITERAL_IN, word, fixed_address=True)
s.write(REG_CTRL, 1)
while not (s.read(REG_CTRL) & 1):
pass
Source code in _code/src/manhattan_reasoning_gym/_client.py
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | |
write ¶
Write one or more 32-bit words to byte address addr.
fixed_address=True repeats addr for every word instead of
incrementing it -- for a hardware FIFO/push-register port (a design
that keeps its own internal write_idx), where a normal burst would
scatter words across whatever registers happen to sit at
address+1, address+2, ...
Source code in _code/src/manhattan_reasoning_gym/_client.py
read ¶
Read count 32-bit words starting at byte address addr.
Source code in _code/src/manhattan_reasoning_gym/_client.py
get_session ¶
release_session ¶
Source code in _code/src/manhattan_reasoning_gym/_client.py
secret ¶
Read a required environment variable (for API keys, etc.).
Raises ValueError immediately if the variable is not set, so the error
surfaces at import time rather than at first use.
Example::
app = manhattan_reasoning_gym.cloud.App(
"my_design",
design="design.py",
fpga_id=0,
api_key=manhattan_reasoning_gym.cloud.secret("MRG_API_KEY"),
)