محاكاة ذاكرة التخزين المؤقت بسيطة المعينة على FPGA
هذا المقال جزء من دورة تدريبية لطلاب البكالوريوس في السنة الأولى من جامعة Innopolis. كل العمل يتم في فريق. الغرض من هذه المقالة هو إظهار فهم الموضوع أو المساعدة في فهمه باستخدام المحاكاة.
بوابة مستودع مستودع
يجب أن يبدو مبدأ العمل ولكن من جانب المستخدم:
- لكتابة أي بيانات في الذاكرة ، تحتاج إلى الوصول إلى ذاكرة الوصول العشوائي مع البيانات والعنوان الذي نريد أن نكتب فيه.
- للوصول إلى البيانات ، يتعين علينا معالجة ذاكرة التخزين المؤقت. إذا لم تتمكن ذاكرة التخزين المؤقت من العثور على البيانات اللازمة ، فعندئذ تصل إلى ذاكرة الوصول العشوائي عن طريق نسخ البيانات من هناك.
عند العمل مع Verilog ، يجب فهم أن كل كتلة فردية من البرنامج يتم تمثيلها كوحدة نمطية. كما تعلم ، فإن ذاكرة التخزين المؤقت ليست جزءًا مستقلاً من الذاكرة السريعة ، وللتشغيل المناسب ، يجب أن تأخذ البيانات من كتلة ذاكرة أخرى - ذاكرة الوصول العشوائي. لذلك ، من أجل محاكاة عمل ذاكرة التخزين المؤقت في FPGA ، يتعين علينا محاكاة وحدة ذاكرة الوصول العشوائي بأكملها التي تتضمن ذاكرة التخزين المؤقت أيضًا ، ولكن النقطة الأساسية هي محاكاة ذاكرة التخزين المؤقت.
يتكون التنفيذ من هذه الوحدات:
- ram.v - وحدة ذاكرة RAM
- cache.v - وحدة ذاكرة التخزين المؤقت
- cache_and_ram.v - الوحدة النمطية التي تعمل مع البيانات والذاكرة.
- testbench.v و testbench2.v - وحدة لإظهار أن الوحدات الرئيسية تعمل بشكل مثالي.
وحدة ذاكرة الوصول العشوائي:
كودmodule ram(); parameter size = 4096; //size of a ram in bits reg [31:0] ram [0:size-1]; //data matrix for ram endmodule
الوصفوحدة تمثل الذاكرة التي تستخدم ذاكرة الوصول العشوائي. لديها 4096 خلايا عنونة 32 بت لتخزين بعض البيانات.

وحدة ذاكرة التخزين المؤقت:
كود 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
الوصفوبالتالي فإن ذاكرة التخزين المؤقت تحتوي على أكثر من مجرد نسخ من البيانات في
الذاكرة كما أنه يحتوي على وحدات البت لمساعدتنا في العثور على البيانات داخل ذاكرة التخزين المؤقت و
تحقق من صلاحيتها.

وحدة ذاكرة التخزين المؤقت وذاكرة الوصول العشوائي:
كود 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
الوصفيمثل عمليات العمل مع البيانات في وحدات الذاكرة. يحصل على المدخلات على كل حافة إيجابية على مدار الساعة. يتم التحقق مما إذا كانت هناك مدخلات جديدة - وفقًا للوضع (1 للكتابة / 0 للقراءة) - تنفذ العمليات ذات الصلة. إذا كان الوضع هو 1 (الكتابة):
• كتابة البيانات إلى عنوان ثم تحقق ما إذا كان عنوان الإدخال موجود في ذاكرة التخزين المؤقت ، إذا كان الأمر كذلك - استبدال البيانات ، وإلا توقف.
إذا كان الوضع 0 (اقرأ):
• تحقق مما إذا كان عنوان الإدخال موجودًا في ذاكرة التخزين المؤقت ، وإذا كان الأمر كذلك - فقم بإرجاع البيانات ، وحصل على البيانات من ذاكرة الوصول العشوائي. قم بتحديث العنوان في ذاكرة التخزين المؤقت ببيانات جديدة.
طاولات الاختبار:
كود 1 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
كود 2 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
الوصفلتشغيل testbench ، قم بتحميل جميع الملفات في مشروع ModelSim وقم بتشغيل محاكاة لأحد ملفات testbench.