PMC: Master control unit


PMC is the “master control” engine of the card. Its purpose is to provide card identication, manage enable/disable bits of other engines, and handle top-level interrupt routing.

MMIO register list

8-bit space pmc [0x1000]
nv1-mmio 0x0: PMC
nv3-mmio 0x0: PMC
g80-mmio 0x0: PMC
gf100-mmio 0x0: PMC

The PMC register range is always active.

Address Variants Name Description
0x0 NV1:NV4 ID card identification
0x0 NV4:NV10 ID card identification
0x0 NV10: ID card identification
0x4 NV1A: ENDIAN endian switch
0x8 G92: BOOT_2 ???
0x100 all INTR_HOST interrupt status - host
0x104 GT215: INTR_NRHOST interrupt status - non-redirectable host
0x108 GT215: INTR_DAEMON interrupt status - PDAEMON
0x140 all INTR_ENABLE_HOST interrupt enable - host
0x144 GT215: INTR_ENABLE_NRHOST interrupt enable - non-redirectable host
0x148 GT215: INTR_ENABLE_DAEMON interrupt enable - PDAEMON
0x160 all INTR_LINE_HOST interrupt line status - host
0x164 GT215: INTR_LINE_NRHOST interrupt line status - non-redirectable host
0x168 GT215: INTR_LINE_DAEMON interrupt line status - PDAEMON
0x17c GF100: INTR_PMFB PMFB interrupt status
0x180 GF100: INTR_PBFB PBFB interrupt status
0x200 all ENABLE engine master enable
0x204 GF100: SPOON_ENABLE PSPOON enables
0x208 GF100: ENABLE_UNK08 ??? related to enable
0x20c GF104: ENABLE_UNK0C ??? related to enable
0x260+i*0x4 (i<6) GF100: FIFO_ENG_UNK260[i] ??? related to PFIFO engines
0x300 NV17:GK110 VRAM_HIDE_LOW VRAM hidden area low address
0x304 NV17:GK110 VRAM_HIDE_HIGH VRAM hidden area high address
0x640 GT215: INTR_MASK_HOST interrupt mask - host
0x644 GT215: INTR_MASK_NRHOST interrupt mask - non-redirectable host
0x648 GT215: INTR_MASK_DAEMON interrupt mask - PDAEMON
0xa00 G94: NEW_ID card identification

Card identification

The main register used to identify the card is the ID register. However, the ID register has different formats depending on the GPU family:

reg32 pmc-id-nv1
pmc 0x0: ID [NV1:NV4]
  • bits 0-3: minor revision.
  • bits 4-7: major revision. These two bitfields together are also visible as PCI revision. For NV3, revisions equal or higher than 0x20 mean NV3T.
  • bits 8-11: implementation - always 1 except on NV2
  • bits 12-15: always 0
  • bits 16-19: GPU - 1 is NV1, 2 is NV2, 3 is NV3 or NV3T
  • bits 20-27: always 0
  • bits 28-31: foundry - 0 is SGS, 1 is Helios, 2 is TMSC
reg32 pmc-id-nv4
pmc 0x0: ID [NV4:NV10]
  • bits 0-3: ???
  • bits 4-11: always 0
  • bits 12-15: architecture - always 4
  • bits 16-19: minor revision
  • bits 20-23: major revision - 0 is NV4, 1 and 2 are NV5. These two bitfields together are also visible as PCI revision.
  • bits 24-27: always 0
  • bits 28-31: foundry - 0 is SGS, 1 is Helios, 2 is TMSC
reg32 pmc-id-nv10
pmc 0x0: ID [NV10:]
  • bits 0-7: stepping
  • bits 16-19: device id [NV10:G92]
  • bits 15-19: device id [G92:GF119]
  • bits 12-19: device id [GF119-] The value of this bitfield is equal to low 4, 5, or 6 bits of the PCI device id. The bitfield size and position changed between cards due to varying amount of changeable bits. See PSTRAPS: straps readout and override and GPU chips for more details.
  • bits 20-27: GPU id. This is THE GPU id that comes after “NV”. See GPU chips for the list.
  • bits 28-31: ???


unk bitfields

G92[?] introduced another identification register in PMC, with unknown purpose:

reg32 pmc-boot-2
pmc 0x8: BOOT_2 [G92:]



what is this? when was it introduced? seen non-0 on at least G92

G94 introduced a new identification register with rearranged bitfields:

reg32 pmc-new-id
pmc 0xa00: NEW_ID [G94:]
  • bits 0-7: device id
  • bits 8-11: same value as BOOT_2 register
  • bits 12-19: stepping
  • bits 20-27: GPU id


there are cards where the steppings don’t match between registers - does this mean something or is it just a random screwup?

Endian switch

PMC also contains the endian switch register. The endian switch can be set to either little or big endian, and affects all accesses to BAR0 and, if present, BAR2/BAR3 - see PCI BARs and other means of accessing the GPU for more details. It is controlled by the ENDIAN register:

reg32 pmc-endian
pmc 0x4: ENDIAN [NV1A:]

When read, returns 0x01000001 if in big-endian mode, 0 if in little-endian mode. When written, if bit 24 of the written value is 1, flips the endian switch to the opposite value, otherwise does nothing.

The register operates in such idiosyncratic way because it is itself affected by the endian switch - thus the read value was chosen to be unaffected by wrong endian setting, while write behavior was chosen so that writing “1” in either endianness will switch the card to that endianness.

This register and the endian switch don’t exist on pre-NV1A cards - they’re always little-endian.

Note that this switch is also used by G80+ PFIFO as its default endianness - see G80+ PFIFO for details.

The MMIO areas containing aliases of 8-bit VGA registers are unaffected by this switch, despite being in BAR0.

Engine enables

PMC contains the main engine enable register, which is used to turn whole engines on and off:

reg32 pmc-enable
pmc 0x200: ENABLE

When given bit is set to 0, the corresponding engine is disabled, when set to 1, it is enabled. Most engines disappear from MMIO space and reset to default state when disabled.

On NV1, the bits are:

On NV3:NV4, the bits are:

On NV4:G80, the bits are:


figure out the CS thing, figure out the variants. Known not to exist on NV40, NV43, NV44, C51, G71; known to exist on MCP73

On G80:GF100, the bits are:



On GF100+, the bits are:

GF100 also introduced SUBFIFO_ENABLE register:

reg32 pmc-spoon-enable
pmc 0x204: SPOON_ENABLE [GF100:]

Enables PFIFO’s PSPOONs. Bit i corresponds to PSPOON[i]. See GF100+ PFIFO for details.

There are also two other registers looking like ENABLE, but with seemingly no effect and currently unknown purpose:

reg32 pmc-enable-unk08
pmc 0x208: ENABLE_UNK08 [GF100:]

Has the same bits as ENABLE, comes up as all-1 on boot, except for PDISPLAY bit which comes up as 0.

reg32 pmc-enable-unk0c
pmc 0x20c: ENABLE_UNK0C [GF104:]

Has bits which correspond to PFIFO engines in ENABLE, ie.

  • 1: PPPP
  • 6: PCOPY[0]
  • 7: PCOPY[1]
  • 12: PGRAPH
  • 15: PVLD
  • 17: PPDEC

Comes up as all-1.

reg32 pmc-fifo-eng-unk260
pmc 0x260+i*0x4: FIFO_ENG_UNK260[i] (i<6) [GF100:]

Single-bit registers, 6 of them.


RE these three


Another thing that PMC handles is the top-level interrupt routing. On cards earlier than GT215, PMC gets interrupt lines from all interested engines on the card, aggregates them together, adds in an option to trigger a “software” interrupt manually, and routes them to the PCI INTA pin. There is an enable register, but it only allows one to enable/disable all hardware or all software interrupts.

GT215 introduced fine-grained interrupt masking, as well as an option to route interrupts to PDAEMON. The HOST interrupts have a new redirection stage in PDAEMON [see PMC interrupt redirection] - while normally routed to the PCI interrupt line, they may be switched over to PDAEMON delivery when it so decides. As a side effect of that, powering off PDAEMON will disable host interrupt delivery. A subset of interrupt types can also be routed to NRHOST destination, which is identical to HOST, but doesn’t go through the PDAEMON redirection circuitry.


change all this duplication to indexing

reg32 pmc-intr-host
pmc 0x100: INTR_HOST

Interrupt status. Bits 0-30 are hardware interrupts, bit 31 is software interrupt. 1 if the relevant input interrupt line is active and, for GT215+ GPUs, enabled in INTR_MASK_*. Bits 0-30 are read-only, bit 31 can be written to set/clear the software interrupt. Bit 31 can only be set to 1 if software interrupts are enabled in INTR_MASK_*, except for NRHOST on GF100+, where it works even if masked.

reg32 pmc-intr-nrhost
pmc 0x104: INTR_NRHOST [GT215:]

Like pmc-intr-host, but for NRHOST.

reg32 pmc-intr-daemon
pmc 0x108: INTR_DAEMON [GT215:]

Like pmc-intr-host, but for DAEMON.

reg32 pmc-intr-enable-host
  • bit 0: hardware interrupt enable - if 1, and any of bits 0-30 of INTR_* are active, the corresponding output interrupt line will be asserted.
  • bit 1: software interrupt enable - if 1, bit 31 of INTR_* is active, the corresponding output interrupt line will be asserted.
reg32 pmc-intr-enable-nrhost
pmc 0x144: INTR_ENABLE_NRHOST [GT215:]

Like pmc-intr-enable-nrhost, but for NRHOST.

reg32 pmc-intr-enable-daemon
pmc 0x148: INTR_ENABLE_DAEMON [GT215:]

Like pmc-intr-enable-host, but for DAEMON.

reg32 pmc-intr-line-host
pmc 0x160: INTR_LINE_HOST

Provides a way to peek at the status of corresponding output interrupt line. On NV1:GF100, 0 if the output line is active, 1 if inactive. On GF100+, 1 if active, 0 if inactive.

reg32 pmc-intr-line-nrhost
pmc 0x164: INTR_LINE_NRHOST [GT215:]

Like pmc-intr-line-host, but for NRHOST.

reg32 pmc-intr-line-daemon
pmc 0x168: INTR_LINE_DAEMON [GT215:]

Like pmc-intr-line-host, but for DAEMON.

reg32 pmc-intr-mask-host
pmc 0x640: INTR_MASK_HOST [GT215:]

Interrupt mask. If a bit is set to 0 here, it’ll be masked off to always-0 in the INTR_* register, otherwise it’ll be connected to the corresponding input interrupt line. For HOST and DAEMON, all interrupts can be enabled. For NRHOST on pre-GF100 cards, only input line #8 [PFIFO] can be enabled, for NRHOST on GF100+ cards all interrupts but the software interrupt can be enabled - however in this case software interrupt works even without being enabled.

reg32 pmc-intr-mask-nrhost
pmc 0x644: INTR_MASK_NRHOST [GT215:]

Like pmc-intr-mask-host, but for NRHOST.

reg32 pmc-intr-mask-daemon
pmc 0x648: INTR_MASK_DAEMON [GT215:]

Like pmc-intr-mask-host, but for DAEMON.

The HOST and NRHOST output interrupt lines are connected to the PCI INTA pin on the card. HOST goes through PDAEMON’s HOST interrupt redirection circuitry [IREDIR], while NRHOST doesn’t. DAEMON goes to PDAEMON’s falcon interrupt line #10 [PMC_DAEMON].

On pre-GT215, each PMC interrupt input is a single 0/1 line. On GT215+, some inputs have a single line for all three outputs, while some others have 2 lines: one for HOST and DAEMON outputs, and one for NRHOST outuput.

The input interrupts are, for NV1:



For NV3:

For NV4:G80:

For G80:GF100:


figure out unknown interrupts. They could’ve been introduced much earlier, but we only know them from bitscanning the INTR_MASK regs. on GT215+.

For GF100+:

  • 0: PPPP - has separate NRHOST line [GF100:GM107]
  • 4: PMEDIA [GF100:GM107]
  • 5: PCOPY[0] [GF100, GK104] - has separate NRHOST line
  • 6: PCOPY[1] [GF100, GK104] - has separate NRHOST line
  • 7: PCOPY[2] [GK104-] - has separate NRHOST line
  • 8: PFIFO
  • 9: ??? allegedly remapper
  • 12: PGRAPH - has separate NRHOST line
  • 13: PBFB
  • 15: PSEC - has separate NRHOST line [GM107:]
  • 15: PVLD - has separate NRHOST line [GF100:GM107]
  • 16: PVENC [GK104-] - has separate NRHOST line
  • 17: PPDEC - has separate NRHOST line [GF100:GM107]
  • 17: PVDEC - has separate NRHOST line [GM107:]
  • 18: PTHERM
  • 19: ??? allegedly HDA codec [GF119-]
  • 20: PTIMER
  • 21: PNVIO’s GPIO interrupts
  • 23: ??? allegedly dfd
  • 24: PDAEMON
  • 25: PMFB
  • 26: PDISPLAY
  • 27: PFFB
  • 28: PBUS - has separate NRHOST line
  • 29: PPCI
  • 30: PRING
  • 31: software




document these two

reg32 pmc-intr-pmfb
pmc 0x17c: INTR_PMFB [GF100:]

Bit x == interrupt for PMFB part x pending.

reg32 pmc-intr-pbfb
pmc 0x180: INTR_PBFB [GF100:]

Bit x == interrupt for PBFB part x pending.


verify variants for these?

VRAM hidden area

NV17/NV20 added a feature to disable host reads through selected range of VRAM. The registers are:

reg32 pmc-vram-hide-low
pmc 0x300: VRAM_HIDE_LOW [NV17:GK110]
  • bits 0-28: address of start of the hidden area. bits 0-1 are ignored, the area is always 4-byte aligned.
  • bit 31: hidden area enabled
reg32 pmc-vram-hide-high
pmc 0x304: VRAM_HIDE_HIGH [NV17:GK110]
  • bits 0-28: address of end of the hidden area. bits 0-1 are ignored, the area is always 4-byte aligned.

The start and end addresses are both inclusive. All BAR1, BAR2/BAR3, PEEPHOLE and PMEM/PRAMIN reads whose offsets fall into this window will be silently mangled to read 0 instead. Writes are unaffected. Note that offset from start of the BAR/PEEPHOLE/PRAMIN/PMEM is used for the comparison, not the actual VRAM address - thus the selected window will cover a different thing in each affected space.

The VRAM hidden area functionality got silently nuked on GF100+ GPUs. The registers are still present, but they don’t do anything.