Penerapan Cache Verilog

Artikel ini membahas implementasi RAM paling sederhana di Verilog.

Sebelum melanjutkan dengan parsing kode, Anda disarankan untuk mempelajari sintaks dasar Verilog.

Di sini Anda dapat menemukan materi pelatihan .

RAM


Langkah 1: mendeklarasikan modul dengan sinyal input / output yang sesuai


module ram ( input [word_size - 1:0] data, input [word_size - 1:0] addr, input wr, input clk, output response, output [word_size - 1:0] out ); parameter word_size = 32; 

  • data - data untuk ditulis.
  • addr - address ke memori dalam RAM.
  • wr - status (baca / tulis).
  • CLK - sistem siklus jam.
  • response - readiness of RAM (1 - jika RAM memproses permintaan baca / tulis, 0 - jika tidak).
  • keluar - data dibaca dari RAM.

Implementasi ini diintegrasikan ke dalam Altera Max 10 FPGA, yang memiliki arsitektur 32-bit, dan oleh karena itu ukuran untuk data dan alamat (word_size) adalah 32 bit.

Langkah 2: mendeklarasikan register di dalam modul


Deklarasi array untuk menyimpan data:

 parameter size = 1<<32; reg [word_size-1:0] ram [size-1:0]; 

Kita juga perlu menyimpan parameter input sebelumnya untuk melacak perubahannya di blok selalu:

 reg [word_size-1:0] data_reg; reg [word_size-1:0] addr_reg; reg wr_reg; 

Dan dua register terakhir untuk memperbarui sinyal output setelah perhitungan di blok selalu:

 reg [word_size-1:0] out_reg; reg response_reg; 

Kami menginisialisasi register:

 initial begin response_reg = 1; data_reg = 0; addr_reg = 0; wr_reg = 0; end 

Langkah 3: menerapkan logika selalu dari blok


 always @(negedge clk) begin if ((data != data_reg) || (addr%size != addr_reg)|| (wr != wr_reg)) begin response_reg = 0; data_reg = data; addr_reg = addr%size; wr_reg = wr; end else begin if (response_reg == 0) begin if (wr) ram[addr] = data; else out_reg = ram[addr]; response_reg = 1; end end end 

Blokir selalu dipicu oleh negedje, mis. saat ini jam bergerak dari 1 ke 0. Ini dilakukan untuk menyinkronkan RAM dengan cache dengan benar. Jika tidak, mungkin ada kasus ketika RAM tidak memiliki waktu untuk mengatur ulang status siap dari 1 ke 0 dan pada jam berikutnya, cache memutuskan bahwa RAM telah berhasil memproses permintaannya, yang pada dasarnya salah.

Logika algoritma selalu blok adalah sebagai berikut: jika data diperbarui, setel ulang status kesiapan ke 0 dan tulis / baca data, jika tulis / baca selesai, perbarui status kesiapan ke 1.

Pada akhirnya, tambahkan bagian kode berikut:

 assign out = out_reg; assign response = response_reg; 

Jenis sinyal keluaran modul kami adalah kawat. Satu-satunya cara untuk mengubah sinyal dari jenis ini adalah penugasan jangka panjang, yang dilarang di dalam blok selalu. Untuk alasan ini, blok selalu menggunakan register, yang selanjutnya ditugaskan untuk sinyal output.

Cache pemetaan langsung


Cache pemetaan langsung adalah salah satu jenis cache yang paling sederhana. Dalam implementasi ini, cache terdiri dari n elemen, dan RAM secara kondisional dibagi menjadi blok-blok oleh n, maka elemen ke-i dalam cache berhubungan dengan semua elemen k-ke dalam RAM yang memenuhi kondisi i = k% n.

Gambar di bawah ini menunjukkan cache ukuran 4 dan RAM ukuran 16.



Setiap elemen cache berisi informasi berikut:

  • bit validity - apakah informasi dalam cache relevan atau tidak.
  • tag adalah nomor blok dalam RAM tempat elemen ini berada.
  • data - informasi yang kami tulis / baca.

Ketika diminta untuk membaca, cache membagi alamat input menjadi dua bagian - tag dan indeks. Ukuran indeks adalah log (n), di mana n adalah ukuran cache.

Langkah 1: mendeklarasikan modul dengan sinyal input / output yang sesuai


 module direct_mapping_cache ( input [word_size-1:0] data, input [word_size-1:0] addr, input wr, input clk, output response, output is_missrate, output [word_size-1:0] out ); parameter word_size = 32; 

Deklarasi modul cache identik dengan RAM, dengan pengecualian sinyal output baru is_missrate. Output ini menyimpan informasi tentang apakah permintaan baca terakhir salah.

Langkah 2: mendeklarasikan register dan RAM


Sebelum mendeklarasikan register, kami menentukan ukuran cache dan indeks:

 parameter size = 64; parameter index_size = 6; 

Selanjutnya, kita mendeklarasikan sebuah array di mana data yang kita tulis dan baca akan disimpan:

 reg [word_size-1:0] data_array [size-1:0]; 

Kami juga perlu menyimpan bit dan tag validitas untuk setiap item dalam cache:

 reg validity_array [size-1:0]; reg [word_size-index_size-1:0] tag_array [size-1:0]; reg [index_size-1:0] index_array [size-1:0]; 

Register dimana alamat input akan dibagi:

 reg [word_size-index_size-1:0] tag; reg [index_size-1:0] index; 

Register yang menyimpan nilai input pada jam sebelumnya (untuk melacak perubahan pada data input):

 reg [word_size-1:0] data_reg; reg [word_size-1:0] addr_reg; reg wr_reg; 

Register untuk memperbarui sinyal output setelah perhitungan di blok selalu:

 reg response_reg; reg is_missrate_reg; reg [word_size-1:0] out_reg; 

Nilai Input untuk RAM:

 reg [word_size-1:0] ram_data; reg [word_size-1:0] ram_addr; reg ram_wr; 

Nilai Output untuk RAM:

 wire ram_response; wire [word_size-1:0] ram_out; 

Mendeklarasikan modul RAM dan menghubungkan sinyal input dan output:

 ram ram( .data(ram_data), .addr(ram_addr), .wr(ram_wr), .clk(clk), .response(ram_response), .out(ram_out)); 

Daftar inisialisasi:

 initial integer i initial begin data_reg = 0; addr_reg = 0; wr_reg = 0; for (i = 0; i < size; i=i+1) begin data_array[i] = 0; tag_array[i] = 0; validity_array[i] = 0; end end 

Langkah 3: menerapkan logika selalu dari blok


Untuk mulai dengan, untuk setiap jam kami memiliki dua negara - data input diubah atau tidak diubah. Berdasarkan ini, kami memiliki kondisi berikut:

 always @(posedge clk) begin if (data_reg != data || addr_reg != addr || wr_reg != wr) begin end // 1:    else begin // 2:     end end 

Blok 1. Jika input data diubah, hal pertama yang kami lakukan adalah mengatur ulang status kesiapan ke 0:

 response_reg = 0; 

Selanjutnya, kami memperbarui register yang menyimpan nilai input jam sebelumnya:

 data_reg = data; addr_reg = addr; wr_reg = wr; 

Kami memecah alamat input menjadi tag dan indeks:

 tag = addr >> index_size; index = addr; 

Untuk menghitung tag, pergeseran bitwise ke kanan digunakan, untuk indeks, cukup dengan menetapkannya saja, karena Bit tambahan dari alamat tidak diperhitungkan.

Langkah selanjutnya adalah memilih antara menulis dan membaca:

 if (wr) begin //  data_array[index] = data; tag_array[index] = tag; validity_array[index] = 1; ram_data = data; ram_addr = addr; ram_wr = wr; end else begin //  if ((validity_array[index]) && (tag == tag_array[index])) begin //    is_missrate_reg = 0; out_reg = data_array[index]; response_reg = 1; end else begin //     is_missrate_reg = 1; ram_data = data; ram_addr = addr; ram_wr = wr; end end 

Dalam hal perekaman, kami awalnya memodifikasi data dalam cache, kemudian memperbarui data input untuk RAM. Dalam hal membaca, kami memeriksa keberadaan elemen ini di cache dan, jika ada, tuliskan ke out_reg, jika tidak, kita beralih ke RAM.

Blok 2. Jika data belum diubah sejak jam sebelumnya dieksekusi, maka kita memiliki kode berikut:

 if ((ram_response) && (!response_reg)) begin if (wr == 0) begin validity_array [index] = 1; data_array [index] = ram_out; tag_array[index] = tag; out_reg = ram_out; end response_reg = 1; end 

Di sini kita menunggu penyelesaian akses ke RAM (jika tidak ada akses, ram_response adalah 1), perbarui data jika ada perintah baca dan atur kesiapan cache ke 1.

Dan terakhir, perbarui nilai-nilai output:

 assign out = out_reg; assign is_missrate = is_missrate_reg; assign response = response_reg; 

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


All Articles