Peripheral Interface
The Peripheral Interface (or PI, or Parallel Interface) is one of multiple I/O interfaces in the RCP, which is used to communicate with game cartridges or other devices connected to either the cartridge port or expansion port on the bottom of the console. (e.g. 64DD)
Memory mapped registers are used to configure the Peripheral Interface and initiate DMA reads and writes. The base address for these registers is 0x0460 0000
, also known as PI_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 0xA000 0000
to the address. As an example, to directly write to the PI_DRAM_ADDR register, use address 0xA460 0000
.
The PI Bus
The PI bus is the bus where external devices can be connected, via either the cartridge port on the top of the console, or the expansion port at the bottom of the console. Notice both ports are electrically connected to the same bus, even if the connector is different.
The bus address is 32-bit and the values being transferred are 16-bits. So each access (read or write) is made to a 32-bit address with a 16-bit data. The PI (as master device) issues reads and writes to the bus with a wire protocol detailed below. Each device is expected to use an address range (a subset of the whole 32-bit address space); the device will receive all reads and writes requests from PI, and is expected to reply / execute those falling within the address range of interest. The PI has no way of knowing if one or more devices are attached to the bus, it does not know which address ranges are used by what device (there is no "address registration / reservation system"), and there is no handling of conflicts.
The PI will issue reads or writes as drive by the CPU via two different systems:
- DMA: this allows to transfer multiple words. In general, the PI bus protocol allows the PI to write the address once, and then either reads or writes multiple consecutive words, and the DMA will use this mechanism to do quicker transfers. In fact, addresses in the PI bus are virtually split in "pages" of configurable size. The PI is allowed to read/write multiple words within the same page, so during the DMA will issue the address only once for page, and then read/write multiple words as requested. This is done to speed up transfers (as issuing a new address after every word would waste time).
- Direct I/O: part of the 32-bit PI address space is memory mapped to the CPU address space. This means that when the CPU accesses one of these memory mapped addresses, the PI will perform a read or write on the bus. The mapped addresses are only those in the range
0x0500_0000 - 0x1FBF_FFFF
and0x1FD0_0000 - 0x7FFF_FFFF
. Addresses outside of these ranges can only be accessed via DMA. Notice also that direct I/O accesses can only be done as 32-bit words (concatenating two consecutive 16-bit reads), see Memory map#Ranges 0x0500'0000 - 0x1FBF'FFFF and 0x1FD0'0000 - 0x7FFF'FFFF (PI external bus) for more information.
NOTE: it is easy to get confused with the different kind of addresses. Addresses mentioned here are PI bus addresses, which is a 32-bit namespace by itself. Addresses in the CPU physical memory map are a different namespace. They can be confused because of the memory mapped addresses: accessing physical address 0x0700_0000 in the CPU does map exactly to PI address 0x0700_0000, but in general the two namespaces are technically separated. For instance, PI address 0x0000_1234 is a valid PI address on the bus where a device could be attached, but reading from physical address 0x0000_1234 on the CPU accesses RDRAM instead; in fact PI address 0x0000_1234 is not memory mapped, so the only way to access it is via DMA.
Domains
To cope with different peripherals, the PI allows to configure some parameters that affect the bus protocol:
- PGS (page size). This is the size of a virtual page, and defines how often the PI must issue a new address during a DMA transfer. For instance, if the configure page size is 32 16-bit words, assuming an aligned transfer, the PI will issue an address at the start, and then read (or write) 32 consecutive words.
- LAT (latency). Number of RCP clock cycles to wait between the address and the transfer of the first word
- PWD
- RLS
The PI stores two set of configurations for these 4 registers, and uses them for different ranges of the address space. These two sets are called "domain 1" and "domain 2". Most of the address space is accessed using the "domain 1" configuration, but a few ranges are accessed as "domain 2". See this table for the mapping:
PI address range | Domain | Device |
---|---|---|
0x0000_0000 - 0x04FF_FFFF | Domain 1 | No known device exists that operates in this range |
0x0500_0000 - 0x05FF_FFFF | Domain 2 | 64DD registers |
0x0600_0000 - 0x07FF_FFFF | Domain 1 | 64DD ROM |
0x0800_0000 - 0x0FFF_FFFF | Domain 2 | SRAM |
0x1000_0000 - 0xFFFF_FFFF | Domain 1 | ROM (though this address range is huge, and ROM only typically occupied a small portion of it) |
Open bus behavior
Writes made to addresses with no "receiver" devices cause no harm; the writes are just ignored. As explained above, the PI has absolutely no notion if devices are attached or not (and whether they care about some addresses) so all writes will always be performed as if somebody cared about them.
Reads made to addresses with no "receiver" devices cause an open-bus behavior: the 32-bit word returned by PI is the 16-bit lowest part of the address put on the bus, repeated in both halves. For instance, a direct I/O read from PI address 0x6666_DCBA
will return the value 0xDCBA_DCBA
. When reading unmapped areas via DMA, the rule is the same but the address returned is the address of the page being accessed, and it is repeated for all words read until page change.
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
PI_DRAM_ADDR 0x0460 0000
| ||||||||
---|---|---|---|---|---|---|---|---|
31:24 | U-0 | U-0 | U-0 | U-0 | U-0 | U-0 | U-0 | U-0 |
— | — | — | — | — | — | — | — | |
23:16 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 |
DRAM_ADDR[23:16] | ||||||||
15:8 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 |
DRAM_ADDR[15:8] | ||||||||
7:0 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 | RW-0 |
DRAM_ADDR[7:0] |
bit 31-24 | Undefined: Initialized to 0
|
bit 23-0 | DRAM_ADDR[23:0]: RDRAM address used in PI DMAs |
TODO
Physical Bus Pinout
The PI Bus is a Bi-directional and MUX'ed interface where there is a 16bit data path to the Rom, 64DD, Flash Ram and SRAM ram chips is used to send both the wanted address and data to and from the RCP. This is not to be confused with the serial EEPROM, CIC and RTC (real time clock) chips that go through the SI interface and PIF chip via the cartridge port as well.
Pin Name | Cart pins | Discription |
---|---|---|
AD0 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[16] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[0] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [0] | |
AD1 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[17] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[1] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [1] | |
AD2 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[18] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[2] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [2] | |
AD3 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[19] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[3] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [3] | |
AD4 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[20] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[4] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [4] | |
AD5 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[21] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[5] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [5] | |
AD6 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[22] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[6] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [6] | |
AD7 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[23] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[7] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [7] | |
AD8 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[24] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[8] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [8] | |
AD9 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[25] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[9] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [9] | |
AD10 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[26] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[10] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [10] | |
AD11 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[27] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[11] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [11] | |
AD12 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[28] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[12] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [12] | |
AD13 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[29] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[13] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [13] | |
AD14 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[30] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[14] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [14] | |
AD15 | This data bit is used with the following signals to represent the following
/ALEH - Signal changes from HIGH to LOW: Address Bit[31] is latched internally in the ROM /ALEL - Signal changes from HIGH to LOW: Address Bit[15] is latched internally in the ROM /WR or /RD - Signal changes from LOW to HIGH: This will read/write to that ROM location latched in the Cart. Bit [15] | |
/ALEH | This internally latches the high address (Bits[31:16]) requested from the RCP in the rom when it goes from HIGH to LOW.
When this signal goes from LOW to HIGH it resets the internal address system so it can await for a new address request. This stays HIGH when in idle and LOW when processing data. | |
/ALEL | This internally latches the low address (Bits[15:0]) requested from the RCP in the rom when it goes from HIGH to LOW.
No action has been seen when this goes from LOW to HIGH. This stays HIGH when in idle and LOW when processing data. | |
/WR | This is the signal that sends a write command to the FLASH ram, SRAM or 64DD
When this signal goes from LOW to HIGH it the RCP will then send data (The ROM will go in to a High-z State to not affect the data bus) This will also increase the internal address counter in the FLASH/SRAM so the data is collected for the next cycle of the /WR signaling. No action has been seen when this goes from HIGH to LOW. It will do the HIGH to LOW change when the domain latency (PI_BSD_DOMX_RLS_REG) counter has been reached and also then the PI_BSD_DOMX_RLS_REG counter.(More on this counters later) This stays HIGH when in idle and LOW when processing write data. | |
/RD | This is the signal that sends a read command to the ROM, FLASH ram, SRAM or 64DD.
When this signal goes from LOW to HIGH it the address selected rom/FLASH/SRAM/64DD will then send data. This will also increase the internal address counter in the ROM/FLASH/SRAM/64DD so the data is collected for the next cycle of the /RD signaling. No action has been seen when this goes from HIGH to LOW. It will do the HIGH to LOW change when the domain latency (PI_BSD_DOMX_RLS_REG) counter has been reached and also then the PI_BSD_DOMX_RLS_REG counter. (More on this counters later) This stays HIGH when in idle and LOW when processing Read data. |
Mapped Domains
Reads or writes to these physical memory segments are mapped to the PI bus. For example, attempting to read from address 0x10000000 will cause a PI bus read at address 0x10000000. This means that even if a cartridge/device has data stored at addresses outside these segments, software running on the console will not be able to access it. Historically, each segment was intended for a different purpose. Game cartridges always expect software to read from the 0x10000000 - 0x1FBFFFFF
segment in order to access the game's ROM data. Custom hardware and software could potentially utilize any or all of these segments in different/unintended ways. Some flashcarts are known to use 0x1FD00000 - 0x7FFFFFFF
for special features or debugging.
Address Range | Name | Original Intended Usage | |
---|---|---|---|
0x05000000 | 0x05FFFFFF | Domain 2, Address 1 | N64DD control registers |
0x06000000 | 0x07FFFFFF | Domain 1, Address 1 | N64DD IPL ROM |
0x08000000 | 0x0FFFFFFF | Domain 2, Address 2 | Cartridge SRAM |
0x10000000 | 0x1FBFFFFF | Domain 1, Address 2 | Cartridge ROM |
0x1FD00000 | 0x7FFFFFFF | Domain 1, Address 3 | Unknown |
The domain number determines which PI timing/page settings apply to which memory segments. Thus, reads/writes to the cartridge ROM segment use the settings for domain 1. It's possible to configure certain characteristics of the communication protocol: latency, pulse-width, release, and page-size. Domain 1 is set automatically during IPL2, by reading the first 4 bytes of the cartridge/device that's connected to the console at boot. Both domains can be configured by software at any time.
Parallel Interface Registers
0x0460 0000 to 0x046F FFFF Address range:
Reg Name | Reg Address (32Bit) | Read or Write | Discription |
---|---|---|---|
PI_DRAM_ADDR_REG [23:0] | 32'h0460_0000 | R/W | This is the starting address in RDRAM that the PI DMA is to read or write from. This is a 16bit (2Byte) Aligned address |
PI_CART_ADDR_REG [31:0] | 32'h0460_0004 | R/W | This is the starting address in ROM that the PI DMA is to read or write from. This is a 16bit (2Byte) Aligned address |
PI_RD_LEN_REG [23:0] | 32'h0460_0008 | R/W | The is the amount of bytes to be transferred from RDRAM to the ROM location (this has to be a value in a multiple of 16bits (2 Bytes)).
This number is the total amount of bytes you want to transfer - 1 |
PI_WR_LEN_REG [23:0] | 32'h0460_000C | R/W | The is the amount of bytes to be transferred from ROM to the RDRAM location (this can be byte amount but weird un-alignment issues happen. more on this later)
This number is the total amount of bytes you want to transfer - 1 |
PI_STATUS_REG | 32'h0460_0010 | R/W | On Read:
Bit [0] : DMA is busy Bit [1] : IO Busy Bit [2] : DMA Error Bit [3] : Interrupt (DMA Completed) On Write: Bit [0] : Reset the DMA controller and stop any transfer begin done Bit [1] : Clear Interrupt |
PI_BSD_DOM1_LAT_REG
PI_BSD_DOM2_LAT_REG [7:0] |
32'h0460_0014
32'h0460_0024 |
R/W | This is a 8 bit counter for the timing between sending the address to the ROM and when to read or write the block of data over the cart bus
This counter runs after the Low Address is asserted on the bus and increases by 1 every clock cycle of the RCP (62.5mhz). Once the counter reaches this value, the bus can then start the Read or write process. Then the PI_BSD_DOMX_RLS_REG process starts for the data transfer Default value set by games is (8'h40) |
PI_BSD_DOM1_PWD_REG
PI_BSD_DOM2_PWD_REG [7:0] |
32'h0460_0018
32'h0460_0028 |
R/W | This is a 8 bit counter for the timing on how long the Read or Write signal is to stay LOW before it can go HIGH. Once the Read signal goes high Data on the bus from the rom is read to the N64. The same is with the Write signal but the RCP outputs the data
This counter runs after the PI_BSD_DOMX_RLS_REG counter and increases by 1 every clock cycle of the RCP (62.5mhz). Once the counter reaches this value, the process will go to PI_BSD_DOM1_RLS_REG. Default value set by games is (8'h12) |
PI_BSD_DOM1_PGS_REG
PI_BSD_DOM2_PGS_REG [7:0] |
32'h0460_001C
32'h0460_002C |
R/W | This is a 4 bit counter for the timing on how many concurrent reads or writes can happen after 128 bytes of data are process and then the Cart bus has to advise to the Cart an update address.
Default value set by games is (4'h07) |
PI_BSD_DOM1_RLS_REG
PI_BSD_DOM2_RLS_REG [7:0] |
32'h0460_0020
32'h0460_0030 |
R/W | This is a 2 bit counter for the timing on how long the Read or Write signal is to stay HIGH before it can go LOW.
This counter runs after the PI_BSD_DOMX_RLS_REG counter and increases by 1 every clock cycle of the RCP (62.5mhz). Once the counter reaches this value, the process will go to idle or back to PI_BSD_DOMX_LAT_REG. Default value set by games is (2'h3) |
PI Interface Process
Address output:
Data Read:
Constant Read:
Aligned DMA Transfer
An aligned DMA transfer is when the PI_DRAM_ADDR_REG is set to a 64bit (8byte) aligned address. The PI_CART_ADDR_REG can be any 16bit (2Byte) value as will transfer from that offset to RDRAM.
The PI_RD_LEN_REG and PI_WR_LEN_REG can be any length, as long as it is a 2 byte aligned amount (more testing is to be done on this to confirm this)
Unaligned DMA transfer
An un-aligned ROM dma transfer is when you use the PI_DRAM_ADDR_REG and not set it as a 8 Byte aligned address and use variable PI_RD_LEN_REG and PI_WR_LEN_REG lengths.
The following rules are based on assumptions via the created test ROMs by Krom, Mazamars312 and Lemmy (https://github.com/PeterLemon/N64/tree/master/CPUTest/DMAAlignment-PI-cart)
RDRAM Address | ROM Address | Read or Write | Length | What happens |
---|---|---|---|---|
0000_0100 | 1000_1000 | Read | 0x7F (128 Bytes) | This is a normal aligned transfer |
0000_0102 | 1000_1000 | Read | 0x7F (128 Bytes) | The start of the ROM data is transferred to RDRAM offset as expected (So the first two bytes of RDRAM are not affected by this write).
However, this is where we see that the last 2 bytes are dropped from the transfer. Thus only making it a 0x7D length transfer (126 bytes - 1) |
0000_0106 | 1000_1000 | Read | 0x7F (128 Bytes) | The start of the ROM data is transferred to RDRAM offset as expected (So the first 6 bytes of RDRAM are not affected by this write).
However, this is where we see that the last 6 bytes are dropped from the transfer. Thus only making it a 0x79 length transfer (122 bytes - 1) |
0000_0106 | 1000_1000 | Read | 0x17 (24 Bytes) | The start of the ROM data is transferred to RDRAM offset as expected (So the first 6 bytes of RDRAM are not affected by this write).
However, this is where we see that the last 6 bytes are dropped from the transfer. Thus only making it a 0x11 length transfer (18 bytes - 1) |
0000_0106 | 1000_1000 | Read | 0xFF (256 Bytes) | This is where we have found that internally the N64 can only DMA blocks of 128 at a time to and from RDRAM as a burst to the PI controller.
The First 128 Bytes: The start of the ROM data is transferred to RDRAM offset as expected (So the first two bytes of RDRAM are not affected by this write). However, this is where we see that the last 6 bytes are dropped from the transfer. Thus only making it a 0x79 length transfer (122 bytes - 1) The Second 128 Bytes: This will do a normal Aligned DMA transfer from the RDRAM offset 128 to 255. From this we believe the first DMA transfer is corrupted due to some internal issue with the PI controller and the RDRAM controller. The image blow shows this example (look at address 112 -> 127) this shows the last 6 Bytes are not transferred. (Confirmed by Krom) |
0000_0106 | 1000_1000 | Write | 0x7F (128 Bytes) | *** Writes to Flash and SRAM to be tested *** |