Laboratório: Cache

Este laboratório propõe o desenvolvimento de uma cache diretamente mapeada. O objetivo envolve a utilização gerenciamento de pedidos e respostas, e tarefas multiciclo.

Ver histórico de mudanças

Changelog

Versão Data Descrição
v2025.1 26/abr/2025 Versão inicial
v2025.2 14/mai/2025 Corrigidas declarações de entidade
v2025.3 16/mai/2025 Corrigidos erros de digitação

Disciplina

Este laboratório faz parte da disciplina MC613 - Laboratório de Circuitos Digitais. Ver oferecimento mais recente.

Entrega

  • A entrega deverá estar em único arquivo .ZIP contendo todos e apenas os arquivos listados abaixo.
  • O nome do arquivo ZIP deve ser RA<RA>.zip, onde <RA> é o RA do componente do grupo que fará a entrega. Por exemplo, RA123456.zip é a entrega do grupo do aluno com o RA 123456.
  • Os nomes dos arquivos dentro do ZIP devem ser seguidos.
  • Se mais do que um arquivo for recebido para a mesma entrega, apenas o último recebido dentro do prazo será considerado.
  • O cumprimento ou não dessas instruções fará parte dos critérios de avaliação.

Arquivos que fazem parte da entrega:

  • diagrama.pdf: Ilustração da sua implementação das entidade INNER_CACHE e OUTER_CACHE (em diagramas separados).
  • inner_cache.vhd: Descrição em VHDL da entidade da INNER_CACHE.
  • cache_tb.vhd: Testbench da entidade da INNER_CACHE.
  • outer_cache.vhd: Descrição em VHDL da entidade da OUTER_CACHE.
  • cache_hierarchy_tb.vhd: Testbench da entidade da OUTER_CACHE.
  • *.vhd: Descrição em VHDL de quaisquer módulos auxiliares.

Link para entrega: https://ic.unicamp.br/~isaias/mc613/entrega.

Cache

Desde o início da computação, programadores sempre desejaram memória rápida e ilimitada. Embora isso não seja possível, podemos criar uma situação que se aproxima usando caches. A ideia é colocar uma pequena e rápida memória entre o processador e a memória mais lenta, permitindo que os acessos a uma porção dos dados, com os quais se está trabalhando, seja consideravelmente mais rápido. Isso se alinha com o princípio da localidade, o qual estabelece que os programas acessam uma pequena porção da memória em cada dado instante.

Internamente, uma cache é organizada como uma tabela hash. Uma função permite mapear um endereço de memória a uma linha. Cada linha agrega três informações: um bit para indicar se a entrada está válida, uma tag para verificar se o dado na cache corresponde ao endereço sendo acessado, e o dado em si. A figura abaixo exemplifica essa organização, chamada Cache Diretamente Mapeada.

Diagrama de funcionamento da cache.

Atividades do laboratório

Parte I - Implementação da Cache

Seguindo o modelo apresentado acima, projete uma cache diretamente mapeada seguindo a interface abaixo e salve em um arquivo inner_cache.vhd.

entity INNER_CACHE is
  generic(
    DATA_WIDTH   : integer := 32; -- Tamanho em bits dos dados
    ADDR_WIDTH   : integer := 16; -- Tamanho em bits dos endereços recebidos
    TAG_WIDTH    : integer := 10;  -- Número de bits pra indicar tag
    OFFSET_WIDTH : integer := 2   -- Número de bits de offset no endereço
  );
	port (
		CLK      : in  std_logic;
    ADDR     : in  std_logic_vector(ADDR_WIDTH-1 downto 0);
    DATA_OUT : out std_logic_vector(DATA_WIDTH-1 downto 0);
    HIT      : out std_logic;
    DATA_IN  : in  std_logic_vector(DATA_WIDTH-1 downto 0);
    WRITE    : in  std_logic
	);
end INNER_CACHE;

Em um comentário no começo do arquivo, responda a seguinte pergunta: Baseado nos parâmetros da entidade, como você calcula o tamanho em bytes da capacidade sua cache? Qual o tamanho total da sua cache em bytes? Qual o aproveitamento de espaço da sua cache (capacidade / espaço total)?

Faça um diagrama de como sua cache foi implementada, mostrando os arrays utilizados e os comparadores.

Desenvolva uma simulação para testar o funcionamento da sua cache, descrito em uma entidade VHDL cache_tb, que escreve alguns dados na cache e depois verifica se ocorreu hit ou miss. Lembre-se de conferir, durante a simulação, se o comportamento é o esperado. Salve o testbench no arquivo cache_tb.vhd.

Parte II - Cache multinível

Nos sistemas modernos, uma cache pequena cache não é suficiente para garantir um bom desempenho. Assim, é empregada uma estratégia de combinar múltiplas caches, uma conectada na outra, de forma a garantir um pouco mais de espaço, porém com um tempo de resposta maior.

entity OUTER_CACHE is
  generic(
    DATA_WIDTH   : integer := 32; -- Tamanho em bits dos dados
    ADDR_WIDTH   : integer := 16; -- Tamanho em bits dos endereços recebidos
    TAG_WIDTH    : integer := 10; -- Número de bits pra indicar tag
    OFFSET_WIDTH : integer := 2   -- Número de bits de offset no endereço
  );
	port (
    CLK       : in  std_logic;
    -- Interface com o nível anterior
    C_ADDR_IN  : in  std_logic_vector(ADDR_WIDTH-1 downto 0);
    C_DATA_OUT : out std_logic_vector(DATA_WIDTH-1 downto 0);
    C_HIT      : out std_logic;
    C_DATA_IN  : in  std_logic_vector(DATA_WIDTH-1 downto 0);
    C_WRITE    : in  std_logic;
    -- Interface com o próximo nível
    M_ADDR_OUT : out std_logic_vector(ADDR_WIDTH-1 downto 0);
    M_DATA_IN  : in  std_logic_vector(DATA_WIDTH-1 downto 0);
    M_HIT      : in std_logic;
    M_DATA_OUT : out std_logic_vector(DATA_WIDTH-1 downto 0);
    M_WRITE    : out std_logic
    --- Adicione outros sinais que julgar necessário.
	);
end OUTER_CACHE;

Para conectar os diferentes níveis de cache, você deve implementar uma entidade outer_cache que encapsula a entidade inner_cache e adiciona uma lógica de controle para gerenciar a comunicação de leitura e escrita, seguindo a interface apresentada acima. Adicione outros sinais que julgar necessário.

Para simplificação, não é necessário implementar operações de escrita vindas de níveis inferiores da hierarquia. Ou seja, os sinais C_DATA_IN e C_WRITE não têm função e podem ficar sem uso.

Desenvolva uma simulação para testar o funcionamento da sua hierarquia de cache descrito em uma entidade VHDL cache_hierarchy_tb. Sua simulação deve instanciar dois níveis de cache, sendo o segundo maior que o primeiro.

Utilize endereços de 16 bits, em que cada endereço aponta para um byte. Cada linha de cache armazena 32 bits (4 palavras). No primeiro nível, utilize uma cache com 16 linhas. No segundo nível, utilize 256 linhas.

Além disso, modele, no testbench, de uma memória de apenas leitura (algo como um vetor grande), e um módulo que busque por diversos endereços aguardando uma resposta da hierarquia de caches, que chamamos de CPU. Utilize o diagrama abaixo para basear seu teste.

Diagrama de sistema com cache multinivel.

Sua memória deve ter um atraso de resposta de 20 ciclos, isto é, o dado só estará disponível na saída 20 ciclos após o endereço ser acionado na entrada, você pode usar um contador para implementar esse comportamento. Preencha essa memória com dados diretamente no bloco initial e então faça leituras para que os dados saiam da memória e cheguem até o processador. Salve a entidade do testbench no arquivo cache_hierarchy_tb.vhd. Durante seu teste, verifique o comportamento e a movimentação dos dados entre a memória e os blocos de cache.

Observações

  1. Este laboratório se baseia apenas em simulação e não exige o uso da placa DE1-SoC.
  2. Cada parte do laboratório equivale a 50% da nota da atividade.
  3. O laboratório já será considerado entregue se apenas a parte 1 for concluída.