← Volver al blogArquitectura de CPU

Contador de programa explicado: el marcador de la CPU

Cómo un contador de programa identifica la siguiente instrucción, avanza por código secuencial y es redirigido por bifurcaciones, saltos, excepciones y reset.

DennyDenny
10 min de lectura
Chalkboard feature illustration for “Contador de programa explicado: el marcador de la CPU”

En resumen: El contador de programa (PC), también llamado puntero de instrucción en algunas arquitecturas, identifica la siguiente instrucción que se debe buscar. La ejecución secuencial lo avanza según la longitud decodificada de la instrucción; las bifurcaciones, los saltos, las excepciones, las interrupciones y el reset seleccionan otro valor para el siguiente PC.

El contador de programa es un elemento de estado arquitectónico de propósito especial situado en el centro de la búsqueda de instrucciones. En una CPU didáctica puede ser un registro literal que controla una dirección de memoria de instrucciones. En un procesador moderno, la predicción, las colas, las cachés, la traducción de direcciones y la búsqueda especulativa hacen que la implementación esté más distribuida, pero la arquitectura sigue definiendo una dirección de la siguiente instrucción.

Esta publicación explica qué almacena el PC, cómo se incrementa, cómo las bifurcaciones y los saltos lo redirigen, y cómo cablear uno a partir de un registro, un sumador y un multiplexor. Cada componente enlaza con un circuito funcional que puedes explorar en la plantilla Ejecutor secuencial de instrucciones.

¿Qué es el contador de programa?

El contador de programa es un registro dedicado a una tarea: apuntar a la siguiente instrucción en memoria. No es un registro de propósito general que el código de usuario lea y escriba libremente. Es un registro de propósito especial controlado por la lógica de control de la CPU.

En la máquina más simple, su anchura y la unidad direccionable de la memoria limitan el espacio de direcciones de código:

  • Un PC de 8 bits puede seleccionar 28=2562^8 = 256 ubicaciones direccionables.
  • Un PC de 16 bits puede seleccionar 216=65,5362^{16} = 65{,}536 ubicaciones.
  • Un PC de 32 bits tiene 2322^{32} patrones de bits posibles.
  • Un PC de 64 bits tiene 2642^{64} patrones de bits posibles.

Esas ubicaciones son bytes solo en una arquitectura direccionada por bytes. Los procesadores reales pueden implementar menos bits de dirección que la anchura nominal del registro, traducir direcciones virtuales de instrucciones a memoria física e imponer reglas de alineación o de direcciones canónicas.

El componente PROGRAM_COUNTER_8BIT de DigiSim implementa el caso de 8 bits, suficientemente grande para ejecutar programas reales en el simulador y a la vez lo bastante pequeño para inspeccionarlo bit a bit.

El PC está al inicio de la etapa de búsqueda

La etapa de búsqueda del ciclo búsqueda-decodificación-ejecución lee cuatro señales en secuencia:

  1. El PC coloca su valor actual en el bus de direcciones.
  2. La memoria devuelve la palabra de instrucción en esa dirección.
  3. La palabra de instrucción se retiene en el registro de instrucciones.
  4. El PC se actualiza para que la siguiente búsqueda lea la siguiente instrucción.

Los pasos 1–3 se tratan en la cobertura más amplia de búsqueda. El paso 4 es el tema de esta publicación.

¿Por qué debe incrementarse el PC automáticamente?

Si el PC permaneciera quieto, la CPU buscaría la misma instrucción para siempre. Por eso, el hardware cablea un incremento automático en el PC, de modo que, de forma predeterminada, cada ciclo de búsqueda va seguido de un paso hacia adelante.

El incremento se calcula mediante un sumador dedicado —normalmente un pequeño sumador de acarreo en cascada como el detallado en Dominar la suma binaria— cuyas entradas son el PC actual y un desplazamiento constante. La salida de ese sumador vuelve a la entrada de datos del PC, y el PC la retiene en el siguiente flanco de reloj.

PC + 1 frente a PC + 4: por qué importa la anchura de instrucción

La constante sumada al PC es la anchura de una instrucción en unidades direccionables, no siempre el valor literal 1.

ArquitecturaAnchura de instrucciónIncrementoMotivo
CPU didáctica DigiSim de 8 bits1 byte (1 palabra)PC + 1Cada dirección contiene una instrucción completa
MIPS; RISC-V sin instrucciones comprimidas4 bytesPC + 4La memoria se direcciona por bytes; estas codificaciones ocupan 4 bytes
ARM (A32)4 bytesPC + 4El mismo motivo
RISC-V con extensión C; Arm Thumb/Thumb-22 o 4 bytesPC + longitud decodificadaAmbos incluyen codificaciones de 16 bits; también pueden aparecer codificaciones de 32 bits
x861–15 bytesPC + (longitud decodificada)Instrucciones de longitud variable; el decodificador informa la longitud

El principio arquitectónico es el mismo: la dirección secuencial siguiente sigue la codificación de la instrucción actual. Una ISA de ancho fijo puede usar un incremento constante. Una ISA de longitud variable debe determinar la longitud de la instrucción actual, incluso si un front end de alto rendimiento predice y busca más allá de ese límite antes de que termine la decodificación.

Descripción booleana

Para una arquitectura con anchura de instrucción fija WW, el valor predeterminado para el siguiente PC es:

PCn+1=PCn+W\text{PC}_{n+1} = \text{PC}_{n} + W

En el caso DigiSim de 8 bits, W=1W = 1, por lo que el incremento es simplemente un sumador +1.

¿Cómo anulan las bifurcaciones y los saltos el PC?

La ejecución secuencial es el caso aburrido. Los programas reales necesitan bucles, condicionales, llamadas a funciones y retornos, todo lo cual exige que el PC salte a algún lugar distinto de la siguiente dirección secuencial.

Dos clases comunes de transferencia de control ilustran el cambio:

  • Salto (incondicional): sustituye el PC por una dirección objetivo sin importar ninguna condición.
  • Bifurcación (condicional): selecciona un objetivo solo si la condición definida por la ISA es verdadera; de lo contrario selecciona la dirección secuencial. Algunas ISA prueban indicadores de estado como cero, acarreo, signo o desbordamiento. Otras, incluida RISC-V base, comparan registros directamente y no tienen registro de códigos de condición aritméticos. La guía de flags de CPU explica el diseño basado en flags.

Ambas operaciones se reducen a escribir un valor no secuencial en el PC.

¿De dónde procede la dirección objetivo?

El objetivo se codifica de una de estas tres formas:

  1. Inmediato (relativo al PC): la palabra de instrucción contiene un desplazamiento. El siguiente PC es PC + offset. RISC-V BEQ, ARM B y los saltos cortos x86 usan esta forma. La ventaja es el código independiente de la posición.
  2. Directo o relativo a región: la instrucción aporta bits de objetivo que pueden combinarse con una base fija o bits altos del PC actual. El clásico MIPS J es un ejemplo conocido. Una dirección absoluta completa rara vez cabe en una instrucción normal de ancho fijo.
  3. Indirecto por registro: el objetivo reside en un registro de propósito general o en la salida de la ALU. RISC-V JALR, ARM BX y x86 JMP rax son ejemplos. Esta forma admite punteros a función, despacho virtual y goto calculado.

En una ruta de datos pequeña de un solo ciclo, esos candidatos pueden alimentar un multiplexor. Las implementaciones más avanzadas pueden calcular, predecir y encolar objetivos en varias unidades antes de que la ruta arquitectónica elegida se retire.

Implementación: registro + incremento + mux

La estructura de hardware del PC es una de las rutas de datos más limpias de una CPU:

                        ┌──────────────┐
       PC current ─────▶│  Adder (+W)  │──── PC + W ──┐
                        └──────────────┘              │
                                                      ▼
                                              ┌───────────────┐
                                              │ MUX (2:1)     │
                target_addr ─────────────────▶│  sel = take?  │──── next_PC
                                              └───────────────┘
                                                      │
                                                      ▼
                                              ┌───────────────┐
                  clock ─────────────────────▶│   PC register │──── current_PC
                                              └───────────────┘

Tres piezas:

  1. Un registro para guardar la dirección actual. En la ruta de datos didáctica, es un registro de 8 bits, construido con almacenamiento disparado por flanco y ensanchado para coincidir con el bus de direcciones.
  2. Un sumador cableado con una entrada como PC y la otra como la anchura constante WW.
  3. Un multiplexor 2:1 con una entrada procedente del sumador (ruta secuencial predeterminada) y otra del objetivo de bifurcación (ruta de bifurcación tomada). La línea de selección la controla la unidad de control y se activa cuando la instrucción actual es un salto o una bifurcación cuya condición se evalúa como verdadera. Los multiplexores son el tema de El controlador del tráfico de datos.

En cada flanco de reloj, el PC retiene lo que el mux esté seleccionando en ese momento. Ese único almacenamiento es todo el mecanismo que distingue «avanzar» de «bifurcarse».

Entradas opcionales al mux

Los diseños reales añaden más fuentes al mismo mux:

FuenteCuándo se selecciona
PC + WPredeterminado — ejecución secuencial
PC + offset (relativo al PC)Bifurcaciones condicionales, saltos cortos
Objetivo directo / vectorSaltos directos, reset o transferencias de control definidas por la arquitectura
Registro / salida de ALUSaltos indirectos, retornos de función, despacho de switch
Vector de reset / arranqueTras el encendido o reset externo
Vector de excepción / interrupciónTras una trampa

La línea de selección se ensancha de 1 bit a 3 bits, pero la idea estructural no cambia.

Comportamiento de reset: ¿dónde empieza el PC?

Cuando la CPU se enciende o se reinicia, el PC debe contener una dirección conocida; de lo contrario, la ejecución comienza al azar.

Hay dos convenciones comunes:

  • PC = 0: la ejecución comienza en la dirección 0x0000…0. La CPU didáctica DigiSim usa esta convención simple, con la primera instrucción en la ubicación cero.
  • Vector de arranque: una dirección fija distinta de cero. x86 se reinicia en frío en la dirección física 0xFFFFFFF0 (la parte superior del espacio de 4 GB, cerca de la ROM BIOS). Los núcleos ARM Cortex-M leen el PC inicial de un vector de reset almacenado en la dirección 0x00000004. Algunos chips de la familia 6502 leen el vector de reset de 0xFFFC/0xFFFD.

En hardware, «reset» es un cable hacia la entrada de borrado asíncrono (o carga síncrona) del PC. Cuando se activa, el PC se fuerza al valor de arranque independientemente del mux.

Ejemplo resuelto: un bucle de cinco instrucciones

Considera el siguiente seudoprograma en una CPU de 8 bits con W=1W = 1:

0x00: LDI R0, 5      ; load 5 into R0
0x01: LDI R1, 0      ; load 0 into R1
0x02: ADD R1, R0     ; R1 = R1 + R0
0x03: DEC R0         ; R0 = R0 - 1, sets Z flag if R0 hits 0
0x04: BNZ 0x02       ; if Z == 0, branch back to 0x02
0x05: HLT

Recorramos los valores del PC:

CicloPC en búsquedaInstrucciónTras la búsqueda¿Bifurcación tomada?Siguiente PC
10x00LDI R0, 5PC + 1 = 0x01no aplica0x01
20x01LDI R1, 0PC + 1 = 0x02no aplica0x02
30x02ADD R1, R0PC + 1 = 0x03no aplica0x03
40x03DEC R0 (R0=4, Z=0)PC + 1 = 0x04no aplica0x04
50x04BNZ 0x02PC + 1 = 0x05; target = 0x02sí (Z=0)0x02
60x02ADD R1, R0………

En el ciclo 5, la ruta de datos didáctica selecciona el objetivo de bifurcación (0x02) en lugar de la siguiente dirección secuencial (0x05) porque el flag Z está borrado. La maquinaria de flujo de control real es más elaborada, pero el resultado arquitectónico sigue siendo una elección entre la ruta de continuación y un objetivo de transferencia de control.

Errores habituales

  • Error de una unidad en PC + W. Un error común de simulador es usar PC + 1 para una arquitectura cuyas instrucciones tienen 4 bytes de ancho. La CPU buscará el segundo byte de cada instrucción como inicio de la siguiente y se comportará como si cada programa fuese datos aleatorios.
  • Actualizar el PC antes de que la búsqueda lea la memoria. El PC debe mantener la dirección actual mientras se lee la memoria. Actualízalo en el flanco de reloj, no combinacionalmente.
  • Olvidar el reset. Un PC sin cable de reset arranca con cualquier valor al que los biestables se aleatoricen al encenderse. Conecta siempre el reset.
  • Bifurcar a una alineación no válida. La alineación requerida de instrucciones depende de la ISA y de la extensión. Una máquina con solo instrucciones alineadas de 32 bits rechaza un objetivo como 0x1003; una ISA con codificaciones de 16 bits puede permitir más objetivos. Aplica la regla de la arquitectura en lugar de cablear una suposición universal.

Construirlo en DigiSim

Abre la plantilla Ejecutor secuencial de instrucciones. Verás:

  • Un bloque PROGRAM_COUNTER_8BIT en la parte superior de la ruta de búsqueda.
  • Un sumador cableado para añadir 1 en cada ciclo de reloj.
  • Un mux que selecciona entre PC + 1 y una dirección objetivo impulsada por la unidad de control.
  • Un interruptor de reset que fuerza el PC a 0x00.

Avanza el reloj manualmente y observa cómo el PC progresa. Después carga un programa que contenga una instrucción de bifurcación y observa el momento en que el mux cambia de «secuencial» a «objetivo»: ese único bit es el flujo de control en su forma más pura.

Dónde se sitúa el PC en el panorama general

El contador de programa es un nodo dentro del flujo de datos mayor de la CPU. La instrucción que identifica se captura para el procesamiento de registro de instrucciones y decodificación. Los resultados aritméticos pueden actualizar los flags de CPU que consulta un diseño de bifurcación basado en flags.

Lee el caso de estudio de búsqueda-decodificación-ejecución para ver el bucle completo, carga después la plantilla Ejecutor secuencial de instrucciones y avanza una instrucción de bifurcación paso a paso. Ver al PC saltar de manera no secuencial por primera vez es el momento en que la abstracción deja de ser un diagrama y se convierte en un circuito.

Convierte la idea en un circuito funcional.