Repository navigation
fuel: add the SGI Fuel (IP34 board, IP35 platform) - #368
Open
jamesbraid wants to merge 20 commits into
Open
jamesbraid wants to merge 20 commits into
jamesbraid wants to merge 20 commits into
Conversation
PCI functions need different writable and write-one-to-clear header fields. Describe those fields in one construction policy while keeping the current header defaults for existing functions. Forward narrow Bridge configuration writes with their byte enables so write-one-to-clear bits are handled correctly when a function supplies them. Existing Octane functions do not use that status policy, so their behavior stays unchanged.
Fuel uses two independent SCSI buses behind one ISP12160 function. Reuse the ISP1040 command engine and let the part trait own the firmware, register and channel differences. A bus reset reaches every attached target as unit attention rather than a media change. Keep the reset attention on the shared target interface so a controller can notify each device through the same bus. The ISP12160 is its only caller: the WD33C93A, AIC-7880 and ISP1040 models do not call it, so their targets behave as before. A bus reset restores each disk's write cache bit per channel: a reset of one channel leaves the other's disks alone, and a chip or soft reset restores both. The engine changes reach the Octane's ISP1040: mailbox commands now run with dispatch held, and the target interface gains the bus reset. The Octane's IRIX 6.5.30 SCSI disk (Octane2, R10000, 256 MiB, V12, jitv2, release build, headless, NAT) boots to the login prompt and runs commands on this branch's tip and on main c6dea8c: login after 126 s on the branch and 109 s on main in one run each (host load makes the times indicative only), the same `hinv` listing of both integral controllers and the disk, and the same command output.
Fuel fits a DP83843 at MII management address 1, where the Octane has an ICS1890. The IOC3 Ethernet controller needs the same packet and link machinery with either PHY, so keep that machinery shared: move the Octane's ICS1890 register model behind a PHY interface, and supply the DP83843's register and negotiation behavior through it. The MAC keeps its existing packet path. The interface carries the snapshot hooks too: the ICS1890 keeps the register save and load it had in the IOC3 model, and the DP83843 saves its registers and negotiation timers the same way, so the shared Ethernet block saves whichever PHY it holds.
Fuel stores its clock and nonvolatile configuration in an M48T35 on the IOC3 byte bus. Keep the register overlay and battery-backed bytes together so latching and persistence share one owner.
Fuel attaches an M48T35 and has different PCI header policy from the Octane. Carry those choices in the population configuration and wire the timekeeper once so byte-bus accesses need no mutable attachment slot. The Fuel's onboard IOC3 also carries the PS/2 keyboard and mouse controller: OpenBSD configures a keyboard controller through the onboard IOC3 (pci/ioc.c, dev/iockbc.c), and the controller core is the one the Octane already has. Turn the controller on in the Fuel's population, as the Octane's is, and test it by sending a host key through the IOC3 registers: it latches the keyboard interrupt and arrives in the K_RX register, and a mouse command answers from its own port, both before and after a chip reset.
Fuel boot flash uses AMD unlock, program and erase commands. Keep that state machine separate from array storage and bus decode so its command handling can serve each fitted flash array. The Intel and AMD command sets need the same rules for seeding and writing flash images, so share the file storage while each command engine keeps its own command state.
Fuel PROM 6.210's memory test fails when uncached SB and SH reach the bus carrying only their low bits. Its L1 console shows: mtest_bank_main failed: Walking Address RSLT mtest_bank_mai FAIL diag_rc = 43 Walking Address In both memory banks the expected halfwords 0xa55a, 0x5aa5, 0x9999 and 0x6666 read back from the ECC check-bit view as 0x0000. The PROM stores each check word with SH from a register whose low 16 bits are zero; the word sits higher in the register, at a position that depends on the tested address. With the whole register visible to the ECC view and the same PROM configuration it reports ALL DIAGS PASSED and enables all 512 MiB. Add BusDevice::write_narrow_from, whose default stores the architectural byte or halfword exactly as write8/write16 do, and call it for every uncached SB and SH. Ordinary devices therefore still see the low bits of the register, and only a device that models a path observing the other lanes overrides it; Bedrock's ECC view does, in its own commit. Which lanes real hardware drives for a narrow store is undefined. Debug writes keep the ordinary-width path, and the JIT callouts pass the same source register. The default adds one virtual call to a path that already makes one.
I/O (bedrock_io): Fuel needs Bedrock I/O to route programmed PIO windows to PCI widgets, check inbound DMA permissions and run its two block-transfer engines. Fault reporting and interrupt delivery stay with the block that owns the state, and unsupported accesses are reported. Register map and behaviour are ported from the QEMU model. Node routing (bedrock_ni): Fuel runs as one node with no NUMAlink peer, but the guest still reads and programs these local control words during machine setup. Retain the writes and keep the network fabric out of the model. Crossbar (bedrock_xb): Record the fabric error queues and let software service them, with the outputs available to the Bedrock processor interface. Unsupported queue reads return zero and writes are logged and ignored. Local bus (bedrock_lb): Add the node and region registers, atomic scratch aliases, error and vector status, and the MicroLAN master that board identity parts sit on. The tests drive it together with the I/O, node-interface and crossbar blocks it reports through, which land in this same commit.
The Fuel CPU receives interrupts and timing events through Bedrock PI. Model the fitted processor slot and its node callbacks while keeping unconnected graphics control explicit.
Fuel reaches its AMD flash through executable PROM aliases and programming windows. Share the array bytes and persistent store while preserving the command and erase state of each flash part. The configuration block's CPU type word is 1, the R14000, as the IP35 PROM's own configuration editor lists it (0 for the R10000 and R12000, 2 for the R16000); a test pins it.
Fuel routes PCI configuration, DMA and interrupt packets through two XBridge heads. Keep small sparse PCI decode in window lists so the bridge can share the machine memory map without allocating more Physical tables. A board attached to one of the crossbar's external ports raises its interrupts as crosstalk packets, the same way the PCI heads do. Route them through the same sender, so the Fuel's graphics board reaches the Bedrock's processor interface with no path of its own, and say whether the destination took the packet. The crossbar answers with the Crossbow's widget 0 register map, so it takes its offsets from the Octane's Crossbow model instead of restating them. What stays separate is behavior that differs between the two: the XBridge crossbar's status register carries the requesting port, wide and malformed accesses set the access-error status and answer zero, its interrupts go out as crosstalk packets to any destination, and it resets the heads and ports its fences select.
The Fuel console first passes through the L1 system controller. Reuse the shared 16550 core and keep controller framing, power commands and identity records in its protocol model.
Fuel enumerates a Lucent OHCI controller on PCI. Provide its registers and descriptor scheduling through the USB function interface while leaving devices unattached in the machine configuration. The frame counter follows the machine clock the board hands the controller, and every register read and write first delivers the frame boundaries that clock has passed. Advancing it only from the board's poll, which runs between CPU batches, left HcFmNumber at zero after the guest's own 1 ms wait had expired, and the Fuel PROM reported that SOF did not start within 2 ms. The sync holds one lock from reading the clock to applying the frames, so a timer thread cannot leave a concurrent reader behind it. Entering UsbOperational from Reset or Suspend re-anchors the frame: it loads HcFmRemaining from HcFmInterval, toggle included, and increments HcFmNumber, with the first SOF one frame later (OpenHCI 1.0a 6.2.1, 7.3.2, 7.3.3). The tests cover that entry with frame numbers at and around the overflow flag. Add two low-speed USB functions for the OHCI root ports: a HID boot keyboard and a HID boot mouse, each with one configuration, one boot interface and one interrupt-IN endpoint. They answer the standard requests (descriptors, SET_ADDRESS taking effect after its status stage, SET_CONFIGURATION, status and interface queries) and the HID class requests (GET/SET_REPORT, GET/SET_IDLE, GET/SET_PROTOCOL), and report host keys and pointer moves as boot reports. Reports queue one per change so a key tapped between two polls still shows both edges. Written from the USB 1.1 and HID 1.11 specifications (boot descriptors from appendix B) and the HID Usage Tables, with no code from another emulator. The wheel, remote wakeup and the host layout translation that the PS/2 path has are left out. Ohci::attach loses its test-only gate so a machine can plug the devices in.
Build the Fuel IP34 board around its R14000, Bedrock, XBridge, IOC3 and dual-channel ISP12160. Use one Physical memory table with sparse processor and peripheral views, and retain the CPU memory fast paths after the PROM startup store retires. Uncached byte and halfword stores reach a device through BusDevice::write_narrow_from, which each window forwards at the device-local address; only the memory controller's ECC view reads more than the low bits of the register. With the jitv2 feature, start the compile pool after the memory windows are published, as the Octane and O2 do. Each worker copies the cache geometry once as it starts, and without the windows that geometry has no inline load or store path. Workers read code through the cached plane, which serves plain physical addresses; the uncached views would decode them as node registers. The machine describes where its memory can answer to the CPU: the four 1 GiB slots Bedrock's controller places its two 256 MiB banks in, and the reset PROM. Its disks on both ISP12160 channels are flushed at power-off, quit and exit like the other machines', so a copy-on-write overlay keeps its record of held sectors after a guest `halt -p`. The processor is R14000FuelShadowCache, the Octane's R14000 at revision 2.3 (PRId 0x0f23), the revision a public software inventory of a Fuel prints, where the Octane's part reports 2.4. The raw CP0 word, the FIR and the 128-byte lines are taken from the Octane part, not read from a Fuel. Like the Octane's R14000 it reports "R12000" as its name. The machine loads its PROM through prom_image. Feed the IOC3 PS/2 controller from the window, VNC and CI input, and let a USB keyboard on root port 0 and a USB mouse on port 1 share it. The Fuel's manual shows PS/2 as the standard keyboard and mouse, so ps2_input under [fuel] defaults on and usb_input defaults off; setting usb_input fits the USB pair, and ps2_input = false leaves the PS/2 pair out. The monitor's ps2 command reaches the keyboard and mouse too. PROM loader (prom_image): Add a device-independent loader for PROM images. It accepts a raw payload or a version-six stored or gzip container, decodes the recognized containers explicitly and reports malformed inputs with their path before any bytes reach guest memory. Config profile (config): The Fuel is a new machine profile with two memory banks, wide SCSI buses, no Ethernet adapter choice beyond its built-in one, and no graphics board by default. The [fuel] section selects the serial console (the L1 controller or either IOC3 port), the L1 TCP port and log, and the persistent boot flash store. l1_revision and brick_serial replace the L1's reported firmware revision (default 1.48.1) and brick serial (default EMUN00). ps2_input defaults on and usb_input off, as the Fuel's manual shows PS/2 as the standard keyboard and mouse; setting usb_input fits a USB keyboard and mouse, and ps2_input = false leaves the PS/2 pair out. The key names the part: machine.cpu = "r14000" (r10000, the family, is still accepted), and a Fuel config cannot claim another processor. The Octane's R14000 is revision 2.4; the Fuel's is 2.3.
Every Fuel has a VPro graphics slot: a Fuel IRIX inventory lists its board under xtalk/13 and OpenBSD finds Odyssey at crossbar widget 13. Build the board there when the machine is given a V12 or a V10, through the widget-offset constructor, and keep none, the serial machine, as the default. The XBridge now reports link 13 alive, the board's DMA enters Bedrock's inbound path as widget 13, and a link reset of port 13 resets it. Its interrupt packets cross the crossbar to Bedrock like the PCI heads'. The status bar's activity (SCSI, IOC3 and Ethernet) and the CPU's Compare ticks reach its LEDs as on the Octane. IRIX's VPro driver writes each processor interface's graphics flow-control registers at attach and tears the board down when the credit counter reads zero, so the interfaces keep those registers; they count no stores and return no credits, so no processor interface is linked to the board's credit accounting. With PROM 6.210 the board passes the graphics diagnostic, and IRIX 6.5.30 with the VPro driver lists Graphics board: V12 and draws the X login screen on it. Input comes from the PS/2 pair the Fuel already has. Configuration accepts the board on the Fuel too, see the config note below. Tests build a Fuel board with each board and check that the processor reaches the board at widget 13 and finds its link alive, that its interrupt packets set and clear a processor interface's pending bit, that a link reset of its port resets it, that a headless Fuel has nothing there, and that the processor interfaces keep their graphics registers. Config (config): A Fuel is reported with both the V12 and the V10, so take either with [graphics] board; the other boards stay refused, with a message that names the Fuel's choices, and none, the serial machine, stays the default. The doc comments for the two boards now say they fit the Fuel as well as the Octane. The Fuel's validation case now accepts both boards and refuses the V6.
Document the supported serial configuration, image inputs, the host input options and the explicit limits for graphics, USB devices and snapshots, in the feature list, hacking notes, debug notes, the sample config and a Fuel page. Startup storage remains a PROM-specific compatibility approximation.
…sses The R10000 drives a two-bit uncached attribute on the system bus. For an unmapped xkphys access it is address bits 58:57; for a mapped reference it comes from the TLB's uncached-attribute field, which EntryLo0/1 bits 63:62 load (R10000 User's Manual 007-2490-001). EntryLo already keeps those bits on the family, and a TLB entry holds them, but the entry's physical base dropped them, so a mapped uncached page always reached attribute zero. Carry them to physical bits 58:57 only for pages the model's C table decodes as uncached, so cached mappings are unchanged. The TLB already holds that table for its shadow entries; reading it there, and recomputing each entry's base when a table is installed, avoids a second decode of C. An entry with no attribute bits set, the usual case, skips the C table. The attribute reaches the bus only on a board that decodes physical bits 58:57. The Octane, Indigo2 and O2 mask the physical address to 40, 32 and 32 bits before the bus, which folds it away, and a test shows that at the executor. The R4400 and R5000 never store the bits. Tests cover the attribute and the board masks, and the C table (a value the model decodes as cached carries nothing; a table installed after the entry was written decides). The uncached-attribute comments cite NEC U10278EJ4V0UM, the fourth edition of the VR10000/VR12000 user's manual, section 11.3, by document number; the manual itself is not added to the repository.
Bedrock memory configuration changes the RAM decode. Keep ordinary CPU and DMA memory on Physical directly, divert diagnostic pages through MD, and update their generation state when memory configuration or ECC changes. The ECC view overrides write_narrow_from to take an uncached SH's check word from the whole source register, at the position Fuel PROM 6.210's memory test uses. Every other view stores the architectural low bits.
jamesbraid
marked this pull request as draft
October 31, 2026 05:28
Collaborator
Author
|
wip, doing some rework and cleanup |
jamesbraid
force-pushed
the
fuel-ip35
branch
2 times, most recently
from
October 31, 2026 09:49
1b77e64 to
687bc0e
Compare
The IP35 PROM fetches its own code uncached at plain physical 0x1fcxxxxx. Bedrock serves that from the reset PROM, but the compile pool read through the cached view, and once a memory bank covers the window the cached view answers RAM there. The memory test zeroes it, so the JIT compiled pages of NOPs in place of PROM routines and the PROM faulted (an instruction fetch bus error, or endless refills). Code pages are keyed by physical frame, so the cached and uncached fetches of that address were one page. Make them two addresses, the way the board decodes them. An uncached instruction fetch of the reset window is now presented to the bus at the same PROM at an address of its own (MipsExecutor::set_uncached_fetch_alias, called by the Fuel; RESET_WINDOW_FETCH, above every RAM slot). The JIT stays as it is: it keys, reads and tracks that page by address like any other, the flash device's generation counter invalidates it, and a store to RAM over the window never does. The alias wraps the translation the CPU installs when it is set, so a cached fetch, a data access and every other machine's fetch path are untouched. The evidence is partial. Which space a request reaches follows the processor's uncached attribute (ip35-address-map.md, processor-side address spaces, established), so an uncached and a cached access of one address are different requests. What attribute 0 returns at 0x1fc00000 is not established: no public source says how the reset request reaches flash, and the cached side there is open too. The split rests on what PROM 6.210's memory test needs, and the code and CHANGELOG say so. The compile pool restarted after a page-pool flush used the CPU's uncached bus instead of the cached plane it was started with; the executor now keeps the compile bus (set_jitv2_code_bus) and passes it to every restart and to the inline compile. The monitor uses it too: `j2 inline off`, `dumppcp`, `corpus` and `j2 html` read pages through it. Tests: an uncached reset-vector fetch compiles the PROM's ADDIU and the cached frame compiles RAM's boundary word, and a flash write invalidates only the PROM's page; a pool restart after a flush keeps the compile bus; a cached fetch and an unaliased executor key the frame they name; uncached attributes 1 to 3 and addresses past the end of memory get no code page.
…unting The board assumed the Octane: its interrupt packets went only to HEART at widget 8, and it took the processor's addresses in the IP30 widget windows. A Fuel's processor reaches widget 13 through Bedrock, which hands the board widget offsets, and the board's packets cross the XBridge's crossbar to Bedrock like any widget's. Add a constructor for such a machine: it takes a packet sender for the interrupts and decodes every address as an aperture offset. The Octane's constructor and behaviour are unchanged. A test routes a packet to a widget other than HEART and requires the sender to get it, not HEART's line. Graphics credits (odyssey): A Bedrock or Hub processor interface counts graphics credits: each counted store to a graphics head takes one, and the head returns it as the store leaves its command FIFO. The Octane's HEART has no such block, so the board left the accounting out. Bring it back for a machine that links a processor interface: the PiGfx register block and the PiGfxLink trait (return, owe and repay credits), a counted and an owed mark on each queued store, the credit loop in retire and clear, the one-store runs while a counted store waits, and the processor's one-shot store mark. With no link, as on the Octane, every credit call is a no-op. How long a V12 takes to return a credit is not published. The host's credit window is 100 doublewords (IP35) or 256 (IP30), a credit is inferred to come back when the board takes the doubleword into its FIFO, and the FIFO must hold more than the window. The model credits a store as it enters while fewer than 16,384 stores wait (cfifo::DEPTH). The initialise call stores its start-up program, about 12,291 words, with no level check before the low-water interrupt is routed; with a depth of 512 two boots in five timed out with "CFIFO timeout" and no screens, and 16,384 is the smallest power of two that covers the program even at one word per store. The depth is a model choice derived from that program. A deeper store owes its credit and hurries the board thread; a reset refunds what the abandoned stores owed. Inside the depth the board thread takes full runs, so a credited backlog does not drain one store at a time. The board thread reads the count of counted stores once per run taken and once per run retired, on every machine. Both reads are Relaxed: a run's length is only a hint, a stale count costs a longer run and never a lost credit, and the retire reads it under the lock a counted push takes to change it. Nothing else on the Octane's store path changed. The tests drive the credit rules with a stand-in link: a counted store is credited on entry, a reset cancels owed credits, the mark counts exactly one store, a widget that runs out of credits starves, the FIFO set-up words do not widen the credit depth, and an owed credit hurries the board thread. Others fill the largest host window plus one with the board taking nothing and require every credit back with none owed, and post 16,384 counted stores and require each credited at entry and the next one owed. The refused-push test used to sleep 150 ms and require a push thread to be unfinished, which a loaded host could break once the board thread's timed hold ran out first. The hold now lasts until the test releases it, and the test waits for the push to be refused before it asserts.
jamesbraid
force-pushed
the
fuel-ip35
branch
6 times, most recently
from
October 31, 2026 23:11
2e1e4bf to
0787452
Compare
jamesbraid
marked this pull request as ready for review
November 2, 2026 11:09
jamesbraid
marked this pull request as draft
November 2, 2026 16:30
jamesbraid
force-pushed
the
fuel-ip35
branch
9 times, most recently
from
November 6, 2026 02:24
0a6ed7d to
093f29b
Compare
jamesbraid
force-pushed
the
fuel-ip35
branch
6 times, most recently
from
November 7, 2026 17:06
3dcbc5d to
4b74098
Compare
jamesbraid
force-pushed
the
fuel-ip35
branch
5 times, most recently
from
November 8, 2026 00:28
5467215 to
7571c3a
Compare
Collaborator
Author
|
good to go |
jamesbraid
marked this pull request as ready for review
November 8, 2026 00:55
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This adds the SGI Fuel: a 500 MHz R14000 on a Bedrock node with 512 MiB, the dual-head XBridge, the IOC3 (Ethernet, serial, parallel, PS/2), the two-channel ISP12160, the L1 system controller, AMD boot flash and OHCI USB. PROM 6.210 passes its diagnostics and boots IRIX 6.5.30 to the 4Dwm desktop, and with a V12 board SGI Quake, SGI Doom and Performer draw. Most devices are ported from my sgi-origami QEMU branch.
Processor
r14000_500: R14000 rev 2.3 at 500 MHz with a 2 MB secondary cache, as the IP35hinvdumps show. It is the only entry in the Fuel'sCPUS.cpu = "r14000"now meansr14000_500, andcpu = "r10000"is refused. Leavingcpuout takes the default.fuel.toml.exampleis updated.Fuel-only CPU behaviour
PS/2 through the IOC3 is the default keyboard and mouse;
[fuel] usb_inputadds USB HID. There is no audio: the Fuel had none built in.Testing
cargo check --features jitv2 --lib --testsis clean on every commit;cargo test --features jitv2 --libpasses at the tip in release and debug, andcargo check -p iris-gui --all-targetsis clean.hinvshows the R14000 rev 2.3 at 500 MHz, 2 MB secondary, 512 MB, both QL12160s and the V12, andhalt -ppowers off.Performance
No cost to other machines: on the Indy bench in deterministic mode the whole suite is within 0.003% of #403, with identical guest instruction counts.