Serial Interface

The Serial Interface (or SI) is one of multiple I/O interfaces in the RCP, which is used to communicate with the PIF-NUS and in turn, Joybus devices.

Memory mapped registers are used to configure the Serial Interface and initiate DMA reads and writes. The base address for these registers is, also known as SI_BASE. However, because all memory accesses in the CPU are made using virtual addresses, the following addresses must be offset appropriately. For non-cached reads/writes, add  to the address. As an example, to directly write to the SI_DRAM_ADDR register, use address.

In addition to these registers, SI is also in charge of handling the memory mapping of PIF-ROM and PIF-RAM to VR4300. These memories are mapped at physical address  (and normally accessed via the uncached segment at  ).

Mapped PIF-ROM and PIF-RAM
When the VR4300 access the physical area at  - , RCP handles the request via SI; the memory access performed via standard MIPS opcode like LW or SW is converted into a I/O communication with PIF, using the serial bus.

Read accesses to the mapped area works as 32-bit, 16-bit or 8-bit loads. 64-bit loads freeze the VR4300 instead, so they must be avoided. All read accesses are blocking: the VR4300 will be locked until the data is read via serial from PIF. In general, it is preferable to use DMA instead so that the transfer can happen in background.

Reading from the PIF_ROM area ( -  ) will simply return 0 after boot is finished, because the PIF locks PIF_ROM accesses for security reasons. This is not something that SI is aware of: it will still request the data via the serial bus to PIF, and PIF will simply return 0.

Write accesses to the mapped area are meant to be done as 32-bit words. 16-bit and 8-bit writes behave in a non-standard way, as they affect the whole 32-bit word they are written to: higher bits are sign-extended, while lower bits are reset to 0. For instance, writing the 8-bit value  at offset 2 in PIF_RAM has the same effect as writing the 32-bit word   to offset 0 in PIF_RAM. This is the same behavior of write accesses to IMEM/DMEM in RSP. 64-bit writes only actually write the higher 32-bit word into the written location, so the second half of the write is basically ignored.

Write accesses are non-blocking: the value to be written is cached by the SI interface, and the VR4300 is released. The actual write is performed in background. During the write, the "I/O busy" bit in the  register is set 1. While the bit is set, no further writes, reads or DMAs should be performed as that might cause bus conflicts and thus unwanted results.

Writing to the PIF_ROM area works at the SI level, but the write is then discarded by the PIF.

Registers
Table Notation: R = Readable bit W = Writable bit U = Undefined/Unused bit -n = Default value n at power on [x:y] = Specifies bits x to y, inclusively

0x0480 0000 - SI_DRAM_ADDR

 * U-0 || U-0 || U-0 || U-0 || U-0 || U-0 || U-0 || U-0


 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0
 * colspan="8" | DRAM_ADDR[23:16]
 * colspan="8" | DRAM_ADDR[23:16]


 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0
 * colspan="8" | DRAM_ADDR[15:8]
 * colspan="8" | DRAM_ADDR[15:8]


 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0
 * colspan="8" | DRAM_ADDR[7:0]
 * colspan="8" | DRAM_ADDR[7:0]

0x0480 0004 - SI_PIF_AD_RD64B

 * U-0 || U-0 || U-0 || U-0 || U-0 || U-0 || U-0 || U-0


 * U-0 || U-0 || U-0 || U-0 || U-0 || U-0 || U-0 || U-0


 * U-0 || U-0 || U-0 || U-0 || U-0 || RW-0 || RW-0 || RW-0
 * — || — || — || — || — || colspan="3" | PIF_ADDR[10:8]
 * — || — || — || — || — || colspan="3" | PIF_ADDR[10:8]


 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || R-0 || R-0
 * colspan="6" | PIF_ADDR[7:2] || 0 || 0
 * colspan="6" | PIF_ADDR[7:2] || 0 || 0

Writing to this register triggers a SI DMA transfer from PIF to RDRAM. The RDRAM address is the one stored in SI_DRAM_ADDR, while the address within PIF_ROM/PIF_RAM must be written to this register. Notice that the lowest two bits of the PIF address are fixed to zero, so only aligned transfers can be run.

This transfer is done by sending a RD64B serial packet to PIF, waiting for acknowledge and then writing to RDRAM the data sent back via serial. Notice that this command has a special meaning for PIF: if the PIF_RAM command byte has either bit 0 or bit 1 set, the respective commands will first be executed (both of them will write to PIF_RAM), and then the requested data is transferred. So the transfer could take a while to run, because the SI might be waiting for the acknowledge for a long time. See PIF-NUS for more information about RD64B.

In the normal case, VR4300 would have prepared a Joybus packet in PIF_RAM to poll controllers. When the SI DMA read is run, the PIF will receive the RD64B packet and will actually perform the full joybus exchange with controllers, writing the state in PIF_RAM. It will then send back the results to SI that will in turn write them to RDRAM. This means that the actual Joybus protocol is only executed when VR4300 asks to read the results via SI DMA, and the actual DMA will be delayed until the data is ready.

During the DMA transfer, SI_DRAM_ADDR is updated. At the end of the transfer, it points to the last word in RDRAM that was written to.

0x0480 0008 - SI_PIF_AD_WR4B

 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0
 * colspan="8" | DATA[31:24]
 * colspan="8" | DATA[31:24]


 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0
 * colspan="8" | DATA[23:16]
 * colspan="8" | DATA[23:16]


 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0
 * colspan="8" | DATA[15:8]
 * colspan="8" | DATA[15:8]


 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0
 * colspan="8" | DATA[7:0]
 * colspan="8" | DATA[7:0]

This register is basically broken: it was probably meant to allow for a DMA transfer of 4 bytes to PIF_RAM, but in reality it is just directly connected to an internal register of SI holding the "current data" word for PIF transfers.

Curiously enough, writing to it does seem to trigger a WR4B serial packet to PIF, so it can actually be used to transfer a word, with the following sequence:


 * First, we need to populate the internal SI register that holds the "current address" to PIF. To do so, we can simply trigger a read from PIF_RAM at the desired location. The read will be executed, and the internal SI register will hold that address.
 * Now, write a 32-bit word of data to `SI_PIF_AD_WR4B`. This goes into the internal "current data" register of SI, and triggers a WR4B transfer using the "current address" (loaded with the previous trick) and the "current data", effectively writing the word to PIF RAM.

The sequence triggers a non-blocking write, but also direct writes to the memory mapped area of PIF_RAM are non-blocking, so there does not seem to be any reason of using this register.

After writing to this register, the DMA_BUSY bit in SI_STATUS goes to 1 for a small amount of time, even though no actual DMA transfer is executed.

0x0480 0010 - SI_PIF_AD_WR64B
TODO

0x0480 0014 - SI_PIF_AD_RD4B

 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0
 * colspan="8" | DATA[31:24]
 * colspan="8" | DATA[31:24]


 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0
 * colspan="8" | DATA[23:16]
 * colspan="8" | DATA[23:16]


 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0
 * colspan="8" | DATA[15:8]
 * colspan="8" | DATA[15:8]


 * RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0 || RW-0
 * colspan="8" | DATA[7:0]
 * colspan="8" | DATA[7:0]

This register is very similar to SI_PIF_AD_WR4B: it also is directly mapped to the internal "current data" word for PIF transfers, but writing to it triggers a RD4B serial packet to PIF. So a read is actually executed and the data is fetched into "current data" and is thus available for reading later. No DMA is performed though.

Using a trick similar to that described in SI_PIF_AD_WR4B, it is possible to actually triggers a memory read from a selected location, but it is superfluous since it is sufficient to access the memory mapped PIF-RAM to do the same.

After writing to this register, the DMA_BUSY bit in SI_STATUS goes to 1 for a small amount of time, even though no actual DMA transfer is executed.

0x0480 0018 - SI_STATUS

 * U-0 || U-0 || U-0 || U-0 || U-0 || U-0 || U-0 || U-0


 * U-0 || U-0 || U-0 || U-0 || U-0 || U-0 || U-0 || U-0


 * U-0 || U-0 || U-0 || R-0 || U-0 || U-0 || U-0 || U-0
 * — || — || — || INTERRUPT || colspan="4" | DMA_STATE[3:0]
 * — || — || — || INTERRUPT || colspan="4" | DMA_STATE[3:0]


 * U-0 || U-0 || U-0 || U-0 || R-0 || R-0 || R-0 || R-0
 * colspan="4" | PCH_STATE[3:0] || style="font-size: 88%;" | DMA_ERROR || style="font-size: 88%;" | READ_PENDING || IO_BUSY || DMA_BUSY
 * colspan="4" | PCH_STATE[3:0] || style="font-size: 88%;" | DMA_ERROR || style="font-size: 88%;" | READ_PENDING || IO_BUSY || DMA_BUSY