The Memory Management Unit (MMU) in the CPU utilizes a large virtual memory space to map to various physical addresses in different ways. All memory accesses made by the CPU, whether instruction fetches or load/store instructions, use virtual addresses. The MMU uses five virtual memory segments to decide how the addresses will be mapped to the physical memory space.
Internally, all addresses are 64-bits wide. However, when in 32-bit addressing mode, the upper 32 bits are sign-extended.
Virtual Memory Map
|0x00000000||0x7FFFFFFF||KUSEG||User segment, TLB mapped|
|0x80000000||0x9FFFFFFF||KSEG0||Kernel segment 0, directly mapped, cached|
|0xA0000000||0xBFFFFFFF||KSEG1||Kernel segment 1, directly mapped, uncached|
|0xC0000000||0xDFFFFFFF||KSSEG||Kernel supervisor segment, TLB mapped|
|0xE0000000||0xFFFFFFFF||KSEG3||Kernel segment 3, TLB mapped|
For the directly mapped segments KSEG0 and KSEG1, addresses are directly translated to physical addresses by subtracting by the base address of the respective segment. Thus they can only map to the physical address range
0x00000000 - 0x1FFFFFFF.
Physical Memory Map
|0x00000000||0x003FFFFF||RDRAM||RDRAM located on motherboard|
|0x00400000||0x007FFFFF||RDRAM||RDRAM from Expansion Pak (if present)|
|0x03F00000||0x03FFFFFF||RDRAM Registers||RDRAM configuration registers|
|0x04000000||0x04000FFF||RSP DMEM||RSP Data Memory|
|0x04001000||0x04001FFF||RSP IMEM||RSP Instruction Memory|
|0x04040000||0x040FFFFF||RSP Registers||RSP DMAs, status, semaphore, program counter, IMEM BIST status|
|0x04100000||0x041FFFFF||RDP Command Registers||RDP DMAs, clock counters for: clock, buffer busy, pipe busy, and TMEM load|
|0x04200000||0x042FFFFF||RDP Span Registers||TMEM BIST status, DP Span testing mode|
|0x04300000||0x043FFFFF||MIPS Interface (MI)||Init mode, ebus test mode, RDRAM register mode, hardware version, interrupt status, interrupt masks|
|0x04400000||0x044FFFFF||Video Interface (VI)||Video control registers|
|0x04500000||0x045FFFFF||Audio Interface (AI)||Audio DMAs, Audio DAC clock divider|
|0x04600000||0x046FFFFF||Peripheral Interface (PI)||Cartridge port DMAs, status, Domain 1 and 2 speed/latency/page-size controls|
|0x04700000||0x047FFFFF||RDRAM Interface (RI)||Operating mode, current load, refresh/select config, latency, error and bank status|
|0x04800000||0x048FFFFF||Serial Interface (SI)||SI DMAs, PIF status|
|0x05000000||0x05FFFFFF||Cartridge Domain 2 Address 1||N64DD control registers (if present)|
|0x06000000||0x07FFFFFF||Cartridge Domain 1 Address 1||N64DD IPL ROM (if present)|
|0x08000000||0x0FFFFFFF||Cartridge Domain 2 Address 2||Cartridge SRAM (if present)|
|0x10000000||0x1FBFFFFF||Cartridge Domain 1 Address 2||Cartridge ROM|
|0x1FC00000||0x1FC007BF||PIF ROM (IPL1/2)||Executed on boot|
|0x1FC007C0||0x1FC007FF||PIF RAM||Controller and EEPROM communication, and during IPL1/2 is used to read startup data from the PIF|
|0x1FD00000||0x7FFFFFFF||Cartridge Domain 1 Address 3||Mapped to same address range on physical cartridge port|
|0x80000000||0xFFFFFFFF||External SysAD device bus||Unknown usage (Unconfirmed: possibly used by processors, cache, and/or RAM for communication)|
Cartridge Domains 1 and 2 are mapped one-to-one on the cartridge/bottom port. It is not known at this time what Domain 1 Address 3 was used for, if at all, but flash carts may have some use for that address range.
Memory Map accesses
The whole memory map is implemented by RCP, as the VR4300 only talks directly to RCP. The bus between VR4300 and RCP is called SysAD. The RCP behaves differently with different access sizes depending on the specific area of the map and the subcomponent in charge of implementing it.
The SysAD bus is described at the hardware level in the SysAD page, but to understand the effects on memory map it is sufficient to understand how data is marshalled for reads and writes. Since SysAD is a 32-bit bus, 32-bit accesses are the "native" ones, and the others are made to work around a 32-bit data exchange.
- Reads: VR4300 puts the address on the bus and the size of the access (8, 16, 32, 64). The RCP typically returns a full (aligned) 32-bit word address (or two, in case of a 64-bit read), from which the VR4300 extracts the correct portion. For instance, when reading 8-bit from address
0x0000'0001, the RCP will put on the bus the 32-bit values at
0x0000'0000 - 0x0000'0003, and the VR4300 will then just isolate the requested 8 bits.
- Writes: VR4300 puts the address on the bus, the size of the access, and then the 32-bit value to be written. When the access is made using 8 or 16 bits, the value on the bus is prepared to match with the aligned 32-bit address. This is the same of what happens for reads, but this time it is the VR4300 to prepare the data. For instance, if register
A0=0x0000'0001and the opcode
SB S0, 0(A0)is run, the VR4300 puts on the bus the value
S0 << 24, that is
0x5678'0000. It is then up to the RCP to see that, since the address is
0x0000'0001, it needs to isolate the the second byte
0x78. So even if it is a 8-bit write opcode, other bits of the register
S0"leak" on the bus.
Notice that misaligned address are forbidden by MIPS architecture and they will result in an Address Exception. So all accesses that go through the memory map are always aligned to the access size (eg: aligned to 2 bytes for 16-bit reads/writes).
Range 0x0000'0000 - 0x007F'FFFF (RDRAM)
The accesses in this area are handled by RCP via RI (Ram Interface). When the VR4300 reads or writes a location in this range, it gets stalled while the RI communicates with the RDRAM via the RAMBUS serial protocol. As soon as the read or write is finished, the VR4300 is released. Effectively, all reads and writes are synchronous (blocking) from the point of view of the VR4300, as you would expect when accessing a RAM. All access sizes work correctly: 8-bit, 16-bit, 32-bit, 64-bit.
Range 0x03F0'0000 - 0x004F'FFFF (RCP registers)
The accesses in this area are handled by RCP itself without going to an external bus, and are dispatched internally to the correct subsystem. Access to a register might optionally stall the VR4300 if the subsystem is designed to do so (eg: to perform a long blocking operation on write), but in general for standard registers, they are immediate and take only 1 PClock cycle.
Accesses in this area are affected by a simplified hardware implementation of the RCP SysAD bus, so access size is ignored. This means that:
- Reads: RCP will ignore the requested access size and will just put the requested 32-bit word on the bus. Luckily, this is the correct behavior for 8-bit and 16-bit accesses (as explained above), so the VR4300 will be able to extract the correct portion. For 64-bit accesses, the 32-bit word stays on the bus for two cycles, so basically the VR4300 will see a 64-bit value which contains the 32-bit value duplicated in both MSB and LSB.
- Writes: RCP will ignore the requested access size and just write the word that was put on the bus directly into the hardware register. For 8-bit and 16-bit accesses, this means that the shifted value prepared by the VR4300 is the one that will be written verbatim. Reprising the example above, if
SB S0, 0(A0)will write the value
0x5678'0000to the RCP hardware register
0x0460'0010. For 64-bit accesses, as they are written on the bus MSB-first, the RCP will write the MSB to the hardware register, ignoring the LSB.
Range 0x1FC0'0000 - 0x1FCF'FFFF (PIF access via SI)
All other ranges 0x0500'0000 - 0xFFFF'FFFF (External SysAD bus via PI)
All accesses made by the VR4300 in these ranges are forward externally by RCP on the external bus via PI. This allows the CPU to access external devices like the cartridge ROM and SRAM.
Accesses in this area are affected by the same simplified SysAD implementation described above, so access size is ignored. The effect is the same described before.
Moreover, there are two important additional details:
- All writes are performed asynchronously by the PI. Making a write in this area will in fact just cause the PI to latch the value internally, and release the VR4300 immediately. The write will then happen in background. The status of the ongoing write will be reflected by the PI "I/O busy" status bit, which will be set to 1 until the write is finalized. While a write is ongoing, further writes are ignored, and reads (from any address) return the 32-bit value that is being written. For further information on this, please check the PI page.
- All external accesses are made by RCP through a 16-bit bus. Given that the RCP only knows of 32-bit accesses (as access size is ignored), this means that each read or write performed by the VR4300 will cause exactly two reads or two writes on the 16-bit bus: first the MSB at the address specified by the CPU (ignoring bit 0, so that the address is aligned to 16 bit), then the LSB at address+2. This might seem a small implementation detail, but it does actually cause an important and visible bug. For instance, if the VR4300 requests a 16-bit read at address
0x1000'0002, the RCP (that ignores access sizes) will do two 16-bit reads on the cartridge bus at
0x1000'0004, and will put on the SysAD bus the 32-bit word at
0x1000'0002 - 0x1000'0005. This is a violation of the SysAD protocol explained above: in fact, in reply to a 16-bit read at
0x1000'0002, the RCP should have put on the bus the 32-bit word at
0x1000'0000 - 0x1000'0003instead. Because of this, effectively a 16-bit read at
0x1000'0002returns the 16-bit word at