RAM com simulação simples de cache mapeado diretamente no FPGA no Verilog

Simulação simples de cache mapeado diretamente no FPGA




Este artigo é parte de um curso para alunos do primeiro ano de graduação da Universidade de Innopolis. Todo o trabalho é feito em equipe. O objetivo deste artigo é mostrar um entendimento do tópico ou ajudar a entendê-lo usando simulação.




Link do repositório Git




Princípio do trabalho, mas do lado do usuário deve se parecer com:


  • Para gravar quaisquer dados na memória, você precisa acessar a RAM com dados e endereço nos quais queremos escrever.
  • Para acessar os dados, precisamos endereçar ao cache. Se o cache não conseguir encontrar os dados necessários, ele acessará a RAM, copiando os dados de lá.

Ao trabalhar com a Verilog, deve-se entender que cada bloco individual do programa é representado como um módulo. Como você sabe, o cache não é uma parte independente da memória rápida e, para seu funcionamento adequado, ele precisa coletar dados de outro bloco de memória - RAM. Portanto, para simular o trabalho do cache no FPGA, precisamos simular todo o módulo de RAM, que também inclui o cache, mas o ponto principal é a simulação do cache.


A implementação consiste em tais módulos:


  • ram.v - módulo de memória RAM
  • cache.v - módulo de memória cache
  • cache_and_ram.v - módulo que opera com dados e memória.
  • testbench.v e testbench2.v - módulo para mostrar que os módulos principais funcionam perfeitamente.

Módulo de RAM:


Código
module ram(); parameter size = 4096; //size of a ram in bits reg [31:0] ram [0:size-1]; //data matrix for ram endmodule 

Descrição do produto

O módulo representa a memória usada como RAM. Possui 4096 células endereçáveis ​​de 32 bits para armazenar alguns dados.





Módulo de cache:


Código
 module cache(); parameter size = 64; // cache size parameter index_size = 6; // index size reg [31:0] cache [0:size - 1]; //registers for the data in cache reg [11 - index_size:0] tag_array [0:size - 1]; // for all tags in cache reg valid_array [0:size - 1]; //0 - there is no data 1 - there is data initial begin: initialization integer i; for (i = 0; i < size; i = i + 1) begin valid_array[i] = 6'b000000; tag_array[i] = 6'b000000; end end endmodule 

Descrição do produto

Portanto, o cache contém mais do que apenas cópias dos dados em
memória ele também possui bits para nos ajudar a encontrar dados no cache e
verifique sua validade.





Módulo de cache e RAM:


Código
 module cache_and_ram( input [31:0] address, input [31:0] data, input clk, input mode, //mode equal to 1 when we write and equal to 0 when we read output [31:0] out ); //previous values reg [31:0] prev_address, prev_data; reg prev_mode; reg [31:0] temp_out; reg [cache.index_size - 1:0] index; // for keeping index of current address reg [11 - cache.index_size:0] tag; // for keeping tag of ceurrent address ram ram(); cache cache(); initial begin index = 0; tag = 0; prev_address = 0; prev_data = 0; prev_mode = 0; end always @(posedge clk) begin //check if the new input is updated if (prev_address != address || prev_data != data || prev_mode != mode) begin prev_address = address % ram.size; prev_data = data; prev_mode = mode; tag = prev_address >> cache.index_size; // tag = first bits of address except index ones (In our particular case - 6) index = address % cache.size; // index value = last n (n = size of cache) bits of address if (mode == 1) begin ram.ram[prev_address] = data; //write new data to the relevant cache block if there is such one if (cache.valid_array[index] == 1 && cache.tag_array[index] == tag) cache.cache[index] = data; end else begin //write new data to the relevant cache's block, because the one we addressing to will be possibly addressed one more time soon if (cache.valid_array[index] != 1 || cache.tag_array[index] != tag) begin cache.valid_array[index] = 1; cache.tag_array[index] = tag; cache.cache[index] = ram.ram[prev_address]; end temp_out = cache.cache[index]; end end end assign out = temp_out; endmodule 

Descrição do produto

Representa operações para trabalhar com dados em módulos de memória. Obtém entrada em cada borda positiva do relógio. Verifica se há novas entradas - dependendo do modo (1 para gravação / 0 para leitura) executa operações relevantes. Se o modo for 1 (gravação):
• Escreva os dados no endereço e verifique se o endereço de entrada existe no cache; se houver - substitua os dados, caso contrário, fique parado.
Se o modo for 0 (lido):
• Verifique se o endereço de entrada existe no cache. Se sim - retorne os dados, caso contrário, obtenha os dados do ram. Atualize o endereço no cache com novos dados.


Bancadas de teste:


Code1
 module testbench; reg [31:0] address, data; reg mode, clk; wire [31:0] out; cache_and_ram tb( .address(address), .data(data), .mode(mode), .clk(clk), .out(out) ); initial begin clk = 1'b1; address = 32'b00000000000000000000000000000000; // 0 data = 32'b00000000000000000011100011000000; // 14528 mode = 1'b1; #200 address = 32'b10100111111001011111101111011100; // 2816867292 % size = 3036 data = 32'b00000000000010000000100001010101; // 526421 mode = 1'b1; #200 address = 32'b00000000000011110100011111010001; // 1001425 % size = 2001 data = 32'b00000001100000110001101100010110; // 25369366 mode = 1'b1; #200 address = 32'b10100111111001011111101111011100; // 2816867292 % size = 3036 data = 32'b00000000000000000011100011000000; // 14528 mode = 1'b1; #200 address = 32'b00000000000011110100011111010001; // 1001425 % size = 2001 data = 32'b00000000000000000011100011000000; // 14528 mode = 1'b1; #200 address = 32'b00000000000011110100011111010001; // 1001425 % size = 2001 data = 32'b00000000000000000000000000000000; // 0 mode = 1'b0; #200 address = 32'b10100111111001011111101111011100; // 2816867292 % size = 3036 data = 32'b00000000000000000000000000000000; // 0 mode = 1'b0; #200 address = 32'b00000000000000000000000000000000; // 0 data = 32'b00000000000000000011100011000000; // 14528 mode = 1'b0; end initial $monitor("address = %d data = %d mode = %d out = %d", address % 4096, data, mode, out); always #25 clk = ~clk; endmodule 

Code2
 module testbench2; reg [31:0] address, data; reg mode, clk; wire [31:0] out; cache_and_ram tb( .address(address), .data(data), .mode(mode), .clk(clk), .out(out) ); initial begin clk = 1'b1; address = 32'b00000000000000000000000000000000; // 0 data = 32'b00000000000000000011100011000000; // 14528 mode = 1'b1; #200 address = 32'b10100111111001011111101111011100; // 2816867292 % size = 3036 data = 32'b00000000000010000000100001010101; // 526421 mode = 1'b1; #200 address = 32'b00000000000000000000000000000000; // 0 data = 32'b00000000000000000011100011000000; // 14528 mode = 1'b0; #200 address = 32'b10100111111001011111101111011100; // 2816867292 % size = 3036 data = 32'b00000000000010000000100001010101; // 526421 mode = 1'b0; #200 address = 32'b00000000000011110100011111010001; // 1001425 % size = 2001 data = 32'b00000001100000110001101100010110; // 25369366 mode = 1'b1; #200 address = 32'b00000000000011110100011111010001; // 1001425 % size = 2001 data = 32'b00000001100000110001101100010110; // 25369366 mode = 1'b0; #200 address = 32'b10100111111001011111101111011100; // 2816867292 % size = 3036 data = 32'b00000000000000000011100011000000; // 14528 mode = 1'b1; #200 address = 32'b00000000000011110100011111010001; // 1001425 % size = 2001 data = 32'b00000000000000000011100011000000; // 14528 mode = 1'b1; #200 address = 32'b00000000000011110100011111010001; // 1001425 % size = 2001 data = 32'b00000000000000000000000000000000; // 0 mode = 1'b0; #200 address = 32'b10100111111001011111101111011100; // 2816867292 % size = 3036 data = 32'b00000000000000000000000000000000; // 0 mode = 1'b0; end initial $monitor("address = %d data = %d mode = %d out = %d", address % 4096, data, mode, out); always #25 clk = ~clk; endmodule 

Descrição do produto

Para executar um testbench, carregue todos os arquivos no projeto ModelSim e execute uma simulação de um dos arquivos testbench.


Source: https://habr.com/ru/post/pt432320/


All Articles