Einfache direkt zugeordnete Cache-Simulation auf FPGA
Dieser Artikel ist Teil einer Kursarbeit für Bachelor-Studenten der Innopolis University im ersten Jahr. Alle Arbeiten werden in einem Team erledigt. Der Zweck dieses Artikels ist es, ein Verständnis des Themas zu zeigen oder es mithilfe von Simulationen zu verstehen.
Git Repository Link
Das Arbeitsprinzip sollte aber von der Benutzerseite aus wie folgt aussehen:
- Um Daten in den Speicher zu schreiben, müssen Sie auf den RAM mit Daten und Adressen zugreifen, in die wir schreiben möchten.
- Um auf die Daten zugreifen zu können, müssen wir uns an den Cache wenden. Wenn der Cache die erforderlichen Daten nicht finden kann, greift er auf den RAM zu, indem er Daten von dort kopiert.
Bei der Arbeit mit Verilog ist zu verstehen, dass jeder einzelne Programmblock als Modul dargestellt wird. Wie Sie wissen, ist der Cache kein unabhängiger Teil des schnellen Speichers, und für seinen ordnungsgemäßen Betrieb müssen Daten aus einem anderen Speicherblock - dem RAM - entnommen werden. Um die Arbeit des Caches am FPGA zu simulieren, müssen wir daher das gesamte RAM-Modul simulieren, das auch den Cache enthält. Der Hauptpunkt ist jedoch die Cache-Simulation.
Die Implementierung besteht aus folgenden Modulen:
- ram.v - RAM-Speichermodul
- cache.v - Cache-Speichermodul
- cache_and_ram.v - Modul, das mit Daten und Speicher arbeitet.
- testbench.v und testbench2.v - Modul, um zu zeigen, dass die Hauptmodule perfekt funktionieren.
RAM-Modul:
Codemodule ram(); parameter size = 4096; //size of a ram in bits reg [31:0] ram [0:size-1]; //data matrix for ram endmodule
BeschreibungDas Modul repräsentiert den Speicher, der als RAM verwendet wird. Es verfügt über 4096 32-Bit-adressierbare Zellen zum Speichern einiger Daten.

Cache-Modul:
Code 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
BeschreibungDer Cache enthält also mehr als nur Kopien der Daten in
Speicher Es hat auch Bits, die uns helfen, Daten im Cache zu finden und
Überprüfen Sie die Gültigkeit.

Cache- und RAM-Modul:
Code 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
BeschreibungRepräsentiert Operationen für die Arbeit mit Daten in Speichermodulen. Ruft bei jeder positiven Taktflanke ein. Überprüft, ob neue Eingaben vorhanden sind - je nach Modus (1 für Schreiben / 0 für Lesen) werden relevante Operationen ausgeführt. Wenn der Modus 1 ist (Schreiben):
• Schreiben Sie Daten in die Adresse und prüfen Sie, ob die Eingabeadresse im Cache vorhanden ist. Wenn ja, ersetzen Sie die Daten, andernfalls stehen Sie still.
Wenn der Modus 0 ist (lesen):
• Überprüfen Sie, ob die Eingabeadresse im Cache vorhanden ist. Wenn ja, geben Sie die Daten zurück, andernfalls holen Sie die Daten vom RAM ab. Aktualisieren Sie die Adresse im Cache mit neuen Daten.
Testbenches:
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
BeschreibungLaden Sie zum Ausführen einer Testbench alle Dateien in das ModelSim-Projekt und führen Sie eine Simulation einer der Testbench-Dateien aus.