College Online
0%

Caminho de Dados

Módulo 4 · Aula 1 ~20 min de leitura Nível: Intermediário

Vídeo da aula estará disponível em breve

O que é o Caminho de Dados

O caminho de dados (datapath) é o circuito que executa as operações sobre os dados dentro do processador. Ele contém os componentes funcionais — registradores, ULA, memórias, multiplexadores — e as conexões entre eles. Junto com a unidade de controle, o datapath forma o núcleo do processador.

Nesta aula, vamos construir o datapath do MIPS monociclo: um processador que executa cada instrução em exatamente um ciclo de clock. É simples o suficiente para entender como tudo se conecta, mas poderoso o suficiente para executar um subconjunto real do MIPS.

i
Monociclo vs Multiciclo No processador monociclo, cada instrução começa e termina em um único ciclo de clock. Isso simplifica o controle, mas o ciclo precisa ser longo o suficiente para a instrução mais demorada (tipicamente lw). O processador multiciclo divide a execução em etapas, permitindo ciclos mais curtos — mas é mais complexo. Nesta aula focamos no monociclo.

Componentes do Datapath

O datapath do MIPS monociclo usa os seguintes componentes:

Componentes do Datapath MIPS
Componente                Função
────────────────────────  ─────────────────────────────────────────────
PC (Program Counter)      Armazena o endereço da instrução atual.
                          Atualizado a cada ciclo: PC ← PC + 4 ou
                          endereço do branch.

Memória de Instruções     Recebe o endereço (PC), retorna a instrução
                          de 32 bits. Somente leitura durante execução.

Banco de Registradores    32 registradores de 32 bits ($0 a $31).
                          2 portas de leitura (Read Register 1/2)
                          1 porta de escrita (Write Register + Write Data)
                          $0 é hardwired em zero.

ULA (ALU)                 Executa operações aritméticas e lógicas:
                          ADD, SUB, AND, OR, SLT, etc.
                          Saída: resultado + flag Zero (para branches).

Memória de Dados          Lê ou escreve dados (lw/sw).
                          Entradas: endereço (calculado pela ULA),
                          dado a escrever. Saída: dado lido.

Extensão de Sinal         Estende o imediato de 16 bits para 32 bits
                          (preservando o sinal para operações corretas).

MUXes (Multiplexadores)   Selecionam entre duas entradas com base em
                          um sinal de controle. Fundamentais para
                          reutilizar o datapath entre instruções.

Somadores (Adders)        PC+4 (próxima instrução sequencial) e
                          cálculo do endereço de branch (PC + offset).

O Datapath Completo

O diagrama abaixo mostra como os componentes se conectam. Cada instrução segue um caminho diferente pelo datapath, controlado pelos sinais de controle (que veremos na aula 4.3).

Diagrama — Datapath MIPS Monociclo
                    +4
                     |
              ┌──────┴──────┐
              │   Somador   │
              └──────┬──────┘
                     │                    ┌─────────────┐
     ┌───┐    ┌──────┴──────┐             │  Extensão   │
     │PC │───►│  Memória de │             │  de Sinal   │
     │   │    │ Instruções  │             │  16→32 bits │
     └─┬─┘    └──────┬──────┘             └──────┬──────┘
       │             │                           │
       │      ┌──────┴──────────────────┐        │  ┌───────────┐
       │      │  Instrução (32 bits)    │        │  │  Somador  │
       │      │  [25:21] [20:16] [15:0] │        │  │  (branch) │
       │      └──┬────┬────┬────┬───────┘        │  └─────┬─────┘
       │         │    │    │    │                 │        │
       │    ┌────┴────┴────┴────┐                │        │
       │    │  Banco de         │         MUX ◄──┘   MUX ◄┘
       │    │  Registradores    │          │          │
       │    │  Read1  Read2     │     ┌────┴────┐    │
       │    │  Write  WData     │     │   ULA   │    │
       │    └────┬────┬─────────┘     │ ADD/SUB │    │
       │         │    │               │ AND/OR  │    │
       │    dado1│    │dado2          │  SLT    │    │
       │         │    │               └────┬────┘    │
       │         │    │                    │ Zero    │
       │         └────┤              ┌─────┴─────┐   │
       │              │              │ Memória   │   │
       │              └──►MUX───────►│ de Dados  │   │
       │                             │  R / W    │   │
       │                             └─────┬─────┘   │
       │                                   │         │
       │                              MUX ◄┘         │
       │                               │             │
       └───────────────────────────────┘             │
                        ▲                             │
                   MUX (PCSrc)◄───────────────────────┘

Sinais de controle (vindos da Unidade de Controle):
  RegDst, ALUSrc, MemtoReg, RegWrite, MemRead, MemWrite, Branch, ALUOp

Execução de Instruções no Datapath

Vamos rastrear três tipos de instrução pelo datapath, passo a passo.

Instrução R-type: add $t0, $s1, $s2

Assembly MIPS
# add $t0, $s1, $s2    →    $t0 = $s1 + $s2
# Formato R: [op=0 | rs=$s1 | rt=$s2 | rd=$t0 | shamt=0 | funct=0x20]

Passo 1: PC fornece endereço → Memória de Instruções retorna instrução
Passo 2: Campos rs ($s1) e rt ($s2) → portas Read1 e Read2 do banco
         Campo rd ($t0) → porta Write Register (via MUX RegDst=1)
Passo 3: Valores de $s1 e $s2 → entradas da ULA
         ALUSrc=0 (segundo operando vem do registrador, não do imediato)
         ALUOp sinaliza ADD
Passo 4: Resultado da ULA → porta Write Data do banco de registradores
         MemtoReg=0 (dado vem da ULA, não da memória)
         RegWrite=1 (escrita habilitada)
Passo 5: PC ← PC + 4 (Branch=0, sem desvio)

Instrução Load Word: lw $t0, 8($s1)

Assembly MIPS
# lw $t0, 8($s1)    →    $t0 = Mem[$s1 + 8]
# Formato I: [op=0x23 | rs=$s1 | rt=$t0 | imm=8]

Passo 1: PC → Memória de Instruções → instrução
Passo 2: Campo rs ($s1) → Read1 do banco
         Campo rt ($t0) → Write Register (RegDst=0: rt é destino em lw)
         Imediato (8) → Extensão de Sinal → 0x00000008
Passo 3: $s1 + 8 na ULA (ALUSrc=1: segundo operando é o imediato estendido)
Passo 4: Resultado da ULA (endereço) → Memória de Dados (MemRead=1)
Passo 5: Dado lido da memória → Write Data do banco
         MemtoReg=1 (dado vem da memória, não da ULA)
         RegWrite=1
Passo 6: PC ← PC + 4

Instrução Branch: beq $s1, $s2, offset

Assembly MIPS
# beq $s1, $s2, L1    →    se $s1 == $s2, desvia para L1
# Formato I: [op=0x04 | rs=$s1 | rt=$s2 | imm=offset]

Passo 1: PC → Memória de Instruções → instrução
Passo 2: $s1 → Read1, $s2 → Read2 do banco
         Imediato (offset) → Extensão de Sinal → shift left 2
Passo 3: ULA calcula $s1 - $s2 (ALUOp=SUB)
         Se resultado = 0, flag Zero = 1 (são iguais)
         Somador de branch: PC + 4 + (offset × 4)
Passo 4: Se Branch=1 AND Zero=1:
           PC ← endereço do branch (PCSrc=1)
         Senão:
           PC ← PC + 4 (PCSrc=0)
         RegWrite=0, MemWrite=0 (beq não escreve em registrador nem memória)

Tempo de Ciclo e Caminho Crítico

No processador monociclo, o ciclo de clock deve ser longo o suficiente para que a instrução mais demorada complete toda a execução. O caminho crítico é o caminho mais longo que um sinal percorre no datapath.

Análise de Tempo — Caminho Crítico
Tempos típicos (simplificados):
  Memória de Instruções:  200 ps
  Banco de Registradores: 100 ps (leitura), 100 ps (escrita)
  ULA:                    200 ps
  Memória de Dados:       200 ps
  MUX / Extensão / Somador: desprezíveis (~0 ps para simplificar)

Instrução     Caminho                                          Tempo
───────────   ─────────────────────────────────────────────     ─────
R-type        MemInst → RegRead → ULA → RegWrite               600 ps
lw            MemInst → RegRead → ULA → MemDados → RegWrite    800 ps  ←
sw            MemInst → RegRead → ULA → MemDados                700 ps
beq           MemInst → RegRead → ULA                           500 ps

Caminho crítico = lw = 800 ps

Ciclo de clock ≥ 800 ps  →  Frequência ≤ 1/800ps = 1.25 GHz

Problema: TODAS as instruções demoram 800 ps, mesmo as que
precisariam de apenas 500 ps. O processador monociclo
desperdiça tempo nas instruções rápidas.

Solução: pipeline (aula 4.2) — divide a execução em estágios,
permitindo que diferentes instruções usem diferentes partes
do datapath ao mesmo tempo.
i
Por que não usar clock variável? Em teoria, poderíamos usar ciclos de tamanhos diferentes para cada instrução. Na prática, a lógica de clock síncrono exige ciclo fixo — seria extremamente complexo (e pouco confiável) variar o clock instrução a instrução. O pipeline resolve isso de forma mais elegante.

Store Word e o Papel dos MUXes

A instrução sw $t0, 12($s1) usa o datapath de forma diferente: o dado lido da porta Read2 do banco ($t0) vai para a entrada de escrita da Memória de Dados, enquanto a ULA calcula o endereço ($s1 + 12).

Papel dos MUXes no Datapath
MUX RegDst:     Seleciona o registrador de destino
                0 → rt (usado por lw, instruções I-type)
                1 → rd (usado por R-type)

MUX ALUSrc:     Seleciona o segundo operando da ULA
                0 → Read Data 2 (registrador, R-type e beq)
                1 → Imediato estendido (lw, sw, addi)

MUX MemtoReg:   Seleciona o que será escrito no registrador
                0 → Resultado da ULA (R-type)
                1 → Dado lido da memória (lw)

MUX PCSrc:      Seleciona o próximo PC
                0 → PC + 4 (sequencial)
                1 → PC + 4 + offset×4 (branch taken)

Sem os MUXes, precisaríamos de datapaths separados para cada
tipo de instrução. Os MUXes permitem reutilizar os mesmos
componentes para todas as instruções.

No harness.os

O caminho de dados é como uma pipeline de processamento — a instrução flui por componentes especializados, assim como uma requisição no harness.os flui por validação, execução e logging.

Mapeamento: Datapath → harness.os
Datapath MIPS                     harness.os
─────────────────────────────     ─────────────────────────────────────
PC (aponta para instrução)        Session ID (identifica a operação atual)

Memória de Instruções             Prompt / Tool definition
  (busca a instrução a executar)    (busca a instrução para o agente)

Banco de Registradores            Contexto da sessão
  (armazena dados temporários)      (handoff, rules, project state)

ULA (executa a operação)          Tool execution
                                    (executa a ferramenta chamada)

Memória de Dados (lê/escreve)     Database (Neon Postgres)
                                    (persiste decisões, learnings, events)

MUXes (selecionam caminhos)       Rules / Conditions
                                    (decidem qual workflow seguir)

Sinais de Controle                Governance concern
                                    (controla quem pode fazer o quê)

Cada requisição no harness flui por:
  1. Fetch:   carregar contexto (start_session → handoff)
  2. Decode:  interpretar o pedido (analisar a tarefa)
  3. Execute: executar a ação (tool calls, file edits)
  4. Store:   persistir resultado (log_decision, log_learning)
  5. Update:  avançar para próxima tarefa (end_session → handoff)

Homework

  1. Desenhe (no papel ou digitalmente) o caminho que a instrução addi $t1, $t2, 100 segue pelo datapath. Quais sinais de controle precisam estar ativos?
  2. Se a memória de instruções demora 250ps, o banco de registradores 150ps (leitura e escrita), a ULA 200ps e a memória de dados 250ps, qual é o ciclo de clock mínimo do processador monociclo? Qual instrução define o caminho crítico?
  3. Explique por que o MUX ALUSrc é essencial: o que aconteceria se a ULA sempre recebesse o valor do registrador rt como segundo operando?
  4. No harness.os, identifique o equivalente do "caminho crítico": qual operação na pipeline de uma sessão é a mais demorada e determina o tempo total?

Resumo

Verifique seu entendimento

No processador MIPS monociclo, qual instrução define o caminho crítico (maior tempo de execução)?

  • add (R-type) — usa banco de registradores e ULA
  • beq — precisa calcular endereço de branch
  • lw — percorre memória de instruções, banco de registradores, ULA, memória de dados e escrita no banco
  • sw — precisa escrever na memória de dados