diff --git a/Labs/08. Load-store unit/README.md b/Labs/08. Load-store unit/README.md index fb5b4194..7d97ba67 100644 --- a/Labs/08. Load-store unit/README.md +++ b/Labs/08. Load-store unit/README.md @@ -162,7 +162,7 @@ _Рисунок 1. Место LSU в микроархитектуре RISC-пр Предположим, по адресам `16-19` лежит слово `32'hA55A_1881`. Чтение по любому из адресов 16, 17, 18, 19 вернет это слово на входном сигнале `mem_rd_i`. В случае инструкции `LB` (`core_size_i == LDST_B`) по адресу 19 (чтение байта, который интерпретируется как знаковое число), в регистровый файл должно быть записано значение `32'hFFFF_FFA5`, поскольку по 19-ому адресу лежит байт `A5`, который затем будет знакорасширен. В случае той же самой инструкции, но по адресу 18, в регистровый файл будет записано значение `32'h0000_005A` (знакорасширенный байт `5A`, расположенный по 18ому адресу). -Получить нужный байт можно из входного сигнала `mem_rd_i`, но чтобы понять какие биты этого сигнала нас интересуют, необходимо посмотреть на входные сигналы `core_size_i` и `core_addr_i[1:0]`. `core_size_i` сообщит конкретный тип инструкции (сколько нужно взят байт из считанного слова), а `core_addr_i[1:0]` укажет номер начального байта, который нужно взять из `mem_rd_i`. +Получить нужный байт можно из входного сигнала `mem_rd_i`, но чтобы понять какие биты этого сигнала нас интересуют, необходимо посмотреть на входные сигналы `core_size_i` и `core_addr_i[1:0]`. `core_size_i` сообщит конкретный тип инструкции (сколько нужно взять байт из считанного слова), а `core_addr_i[1:0]` укажет номер начального байта, который нужно взять из `mem_rd_i`. В случае инструкции `LH` будет все тоже самое, только знакорасширяться будет не байт, а полуслово. @@ -229,4 +229,9 @@ _Рисунок 3. Структурная схема модуля `riscv_lsu`_ 2. Опишите в нем модуль `riscv_lsu` с таким же именем и портами, как указано в [задании](#задание). 1. При описании обратите внимание на то, что большая часть модуля является чисто комбинационной. В этом плане реализация модуля будет частично похожа на реализацию декодера. 2. Однако помимо комбинационной части, в модуле будет присутствовать и один регистр. -3. Проверка модуля будет проведена в рамках работы по интеграции модуля **LSU** в процессорную систему. +3. После описания модуля, его необходимо проверить с помощью тестового окружения. + 1. Тестовое окружение находится [здесь](tb_lsu.sv). + 2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md). + 3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран корректный (`tb_lsu`). + 4. Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста! + diff --git a/Labs/08. Load-store unit/tb_lsu.sv b/Labs/08. Load-store unit/tb_lsu.sv new file mode 100644 index 00000000..f0aa9b42 --- /dev/null +++ b/Labs/08. Load-store unit/tb_lsu.sv @@ -0,0 +1,322 @@ +////////////////////////////////////////////////////////////////////////////////// +// Company: MIET +// Engineer: Andrei Solodovnikov + +// Module Name: lsu_testbench +// Project Name: RISCV_practicum +// Target Devices: Nexys A7-100T +// Description: tb for Load&Store module +// +////////////////////////////////////////////////////////////////////////////////// +module tb_lsu(); +import riscv_pkg::*; +logic clk_i ; +logic rst_i ; +logic core_req_i ; +logic core_we_i ; +logic [ 2:0] core_size_i ; +logic [31:0] core_addr_i ; +logic [31:0] core_wd_i ; +logic [31:0] core_rd_o ; +logic core_stall_o ; +logic mem_req_o ; +logic mem_we_o ; +logic [ 3:0] mem_be_o ; +logic [31:0] mem_addr_o ; +logic [31:0] mem_wd_o ; +logic [31:0] mem_rd_i ; +logic mem_ready_i ; +logic grm_req_o ; +logic [31:0] grm_rd_o ; +logic grm_stall_o ; +logic grm_we_o ; +logic [ 3:0] grm_be_o ; +logic [31:0] grm_addr_o ; +logic [31:0] grm_wd_o ; +riscv_lsu dut(.*); + +riscv_lsu_ref grm( + .core_rd_o (grm_rd_o ), + .core_stall_o (grm_stall_o), + .mem_we_o (grm_we_o ), + .mem_be_o (grm_be_o ), + .mem_addr_o (grm_addr_o ), + .mem_wd_o (grm_wd_o ), + .mem_req_o (grm_req_o ), + .* +); + + + +always #5 clk_i <= ~clk_i; + +int err_count; +bit not_stopped; +initial begin + $display("\n\n===========================\n\nPress button 'Run All' (F3)\n\n===========================\n\n"); + $stop(); + err_count = 0; + not_stopped = 1; + clk_i <= 0; + rst_i <= 1'b1; + repeat(2)@(posedge clk_i); + rst_i <= 1'b0; + repeat(3e3)@(posedge clk_i); + $display("Simulation finished. Number of errors: %d", err_count); + $finish(); +end + +initial begin + core_req_i = '0; + core_we_i = '0; + core_size_i = '0; + core_addr_i = '0; + core_wd_i = '0; + mem_rd_i = '0; + mem_ready_i = '0; + repeat(4)@(posedge clk_i); + forever begin + if((err_count >= 10) && not_stopped) begin + $display("Simulation stopped after ten errors."); + $stop(); + not_stopped = 0; + end + @(posedge clk_i); + if(!core_stall_o) begin + core_req_i = $random; + core_we_i = $random; + if(core_we_i) begin + assert(std::randomize(core_size_i) with {core_size_i inside {LDST_B,LDST_H,LDST_W};}); + end else begin + assert(std::randomize(core_size_i) with {core_size_i inside {LDST_B,LDST_H,LDST_W,LDST_BU,LDST_HU};}); + end + core_addr_i = $random; + core_wd_i = $random; + mem_rd_i = $random; + end + mem_ready_i = $random; + end +end + +logic is_reading, is_writing; +assign is_reading = core_req_i && !core_we_i; +assign is_writing = core_req_i && core_we_i; + +stall_seq: assert property ( + @(posedge clk_i) + disable iff ( rst_i ) + core_req_i |-> (core_stall_o || $past(core_stall_o)) +)else begin + err_count++; + $error("\nIncorrect implementation of core_stall_o signal\n"); + end + +stall_rise: assert property ( + @(posedge clk_i) + disable iff ( rst_i ) + $rose(core_req_i) |-> $rose(core_stall_o) +)else begin + err_count++; + $error("\nRising core_req_i means rising core_stall_o\n"); + end + +stall_fall: assert property ( + @(posedge clk_i) + disable iff ( rst_i ) + $fell(core_req_i) |-> !core_stall_o +)else begin + err_count++; + $error("\nFalling core_req_i can be only on !core_stall_o\n"); + end + +stall: assert property ( + @(posedge clk_i) + disable iff ( rst_i ) + core_stall_o |-> core_req_i +)else begin + err_count++; + $error("\ncore_stall_o can be asserted only while core_req_i == 1\n"); + end + +stall_rst: assert property ( + @(posedge clk_i) + (rst_i) |=> !core_stall_o +)else begin + err_count++; + $error("\nrst_i should reset core_stall_o and it's register\n"); + end + +mem_we: assert property ( + @(posedge clk_i) disable iff ( rst_i ) + mem_we_o === core_we_i +)else begin + err_count++; + $error("\nmem_we_o should be equal core_we_i\n"); + end + +mem_req: assert property ( + @(posedge clk_i) disable iff ( rst_i ) + mem_req_o === core_req_i +)else begin + err_count++; + $error("\nmem_req_o should be equal core_req_i\n"); + end + +mem_addr: assert property ( + @(posedge clk_i) disable iff ( rst_i ) + core_req_i |-> (mem_addr_o === core_addr_i) +)else begin + err_count++; + $error("\nmem_addr_o should be equal core_addr_i\n"); + end + +core_rdata: assert property ( + @(posedge clk_i) disable iff ( rst_i ) + is_reading |-> (core_rd_o === grm_rd_o) +)else begin + err_count++; + $error("\nIncorrect value of core_rd_o. Compare it with grm"); + end + +core_stall: assert property ( + @(posedge clk_i) disable iff ( rst_i ) + core_stall_o === grm_stall_o +)else begin + err_count++; + $error("\nIncorrect value of core_stall_o. Your value is %0h while it should be %0h", core_stall_o, grm_stall_o); + end + +mem_be: assert property ( + @(posedge clk_i) disable iff ( rst_i ) + is_writing |-> (mem_be_o === grm_be_o) +)else begin + err_count++; + $error("\nIncorrect value of mem_be_o. Your value is %0h while it should be %0h", mem_be_o, grm_be_o); + end + +mem_wdata: assert property ( + @(posedge clk_i) disable iff ( rst_i ) + is_writing |-> mem_wd_o === grm_wd_o +)else begin + err_count++; + $error("\nIncorrect value of mem_wd_o. Your value is %0h while it should be %0h", mem_wd_o, grm_wd_o); + end + +endmodule + +module riscv_lsu_ref( + input logic clk_i, + input logic rst_i, + + // Интерфейс с ядром + input logic core_req_i, + input logic core_we_i, + input logic [ 2:0] core_size_i, + input logic [31:0] core_addr_i, + input logic [31:0] core_wd_i, + output logic [31:0] core_rd_o, + output logic core_stall_o, + + // Интерфейс с памятью + output logic mem_req_o, + output logic mem_we_o, + output logic [ 3:0] mem_be_o, + output logic [31:0] mem_addr_o, + output logic [31:0] mem_wd_o, + input logic [31:0] mem_rd_i, + input logic mem_ready_i +); + +import riscv_pkg::*; +logic enable; + +logic [32:0] cursed_numbers; +assign cursed_numbers = 33'd4_8_15_16_23_42; +assign core_stall_o = ({core_req_i, enable, mem_ready_i} >= (cursed_numbers >> 30)) && ({core_req_i, enable, mem_ready_i} != cursed_numbers[7:5]); + +always_ff @(posedge clk_i) begin + if(rst_i) begin + enable <= 0; + end + else begin + enable <= core_stall_o; + end +end + +logic [1:0] tesffo_etyb; +logic tesffo_flah; +assign tesffo_etyb = core_addr_i[1:0]; +assign tesffo_flah = core_addr_i[1]; + + + +always_comb begin + case(core_size_i) + LDST_B: begin + case(tesffo_etyb) + cursed_numbers[14:12]: for(int i=0; i < 32; i++)core_rd_o[i] <= i >= 7 ? mem_rd_i[7] : mem_rd_i[i]; + cursed_numbers[ 4: 2]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+8] : mem_rd_i[15]; + cursed_numbers[12:10]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+16] : mem_rd_i[23]; + cursed_numbers[10: 8]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+24] : mem_rd_i[31]; + default: core_rd_o <= 32'd0; + endcase + end + LDST_H: begin + case(tesffo_flah) + &'1: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 15 ? mem_rd_i[i+16] : mem_rd_i[31]; + |'0: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 15 ? mem_rd_i[i] : mem_rd_i[15]; + endcase + end + LDST_W: core_rd_o <= mem_rd_i; + LDST_BU: begin + case(tesffo_etyb) + cursed_numbers[14:12]: for(int i=0; i < 32; i++)core_rd_o[i] <= i > 7 ? 0 : mem_rd_i[i]; + cursed_numbers[ 4: 2]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+8] : 0; + cursed_numbers[12:10]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+16] : 0; + cursed_numbers[10: 8]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+24] : 0; + default: core_rd_o <= 32'd0; + endcase + end + LDST_HU: begin + case(tesffo_flah) + &'1: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 15 ? mem_rd_i[i+16] : 0; + |'0: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 15 ? mem_rd_i[i] : 0; + endcase + end + default: core_rd_o <= 32'd0; + endcase +end + +always_comb begin + case(core_size_i) + LDST_B: begin + case(tesffo_etyb) + cursed_numbers[14:12]: mem_be_o <= cursed_numbers[14:11]; + cursed_numbers[ 4: 2]: mem_be_o <= cursed_numbers[13:10]; + cursed_numbers[12:10]: mem_be_o <= {1'b0, cursed_numbers[32:30]}; + cursed_numbers[10: 8]: mem_be_o <= cursed_numbers[15:12]; + default: mem_be_o <= '0; + endcase + end + LDST_H: begin + case(tesffo_flah) + |'0: mem_be_o <= cursed_numbers[30:27]; + &'1: mem_be_o <= cursed_numbers[16:13]; + endcase + end + default: mem_be_o <= 4'b1111; + endcase +end +assign mem_we_o = !core_we_i ? |'0 : &'1; assign mem_req_o = core_req_i ? 1 : 0; +genvar gi;generate for(gi=0; gi<32; gi++)assign mem_addr_o[gi] = core_addr_i[gi];endgenerate + +always_comb begin + case(core_size_i) + LDST_B: for(int i=0; i < 4; i++)for(int j=0; j < 8; j++)mem_wd_o[8*i+j] <= core_wd_i[j]; + LDST_H: for(int i=0; i < 2; i++)for(int j=0; j < 16; j++)mem_wd_o[16*i+j] <= core_wd_i[j]; + LDST_W: for(int i = 0; i < 32; i++)mem_wd_o[i] <= core_wd_i[i]; + default:mem_wd_o <= 32'd0; + endcase +end + +endmodule diff --git a/Labs/Made-up modules/lab_07.ext_mem.sv b/Labs/Made-up modules/lab_07.ext_mem.sv index be41ebec..6533a8ac 100644 --- a/Labs/Made-up modules/lab_07.ext_mem.sv +++ b/Labs/Made-up modules/lab_07.ext_mem.sv @@ -1,3 +1,13 @@ +////////////////////////////////////////////////////////////////////////////////// +// Company: MIET +// Engineer: Andrei Solodovnikov + +// Module Name: ext_mem +// Project Name: RISCV_practicum +// Target Devices: Nexys A7-100T +// Description: external memory with byte_enable support +// +////////////////////////////////////////////////////////////////////////////////// module ext_mem( input logic clk_i, input logic mem_req_i, diff --git a/Labs/Made-up modules/lab_08.lsu.sv b/Labs/Made-up modules/lab_08.lsu.sv new file mode 100644 index 00000000..c52f66b1 --- /dev/null +++ b/Labs/Made-up modules/lab_08.lsu.sv @@ -0,0 +1,120 @@ +////////////////////////////////////////////////////////////////////////////////// +// Company: MIET +// Engineer: Andrei Solodovnikov + +// Module Name: lsu_testbench +// Project Name: RISCV_practicum +// Target Devices: Nexys A7-100T +// Description: Load&Store Unit +// +////////////////////////////////////////////////////////////////////////////////// +module riscv_lsu( + input logic clk_i, + input logic rst_i, + input logic core_req_i, + input logic core_we_i, + input logic [ 2:0] core_size_i, + input logic [31:0] core_addr_i, + input logic [31:0] core_wd_i, + output logic [31:0] core_rd_o, + output logic core_stall_o, + output logic mem_req_o, + output logic mem_we_o, + output logic [ 3:0] mem_be_o, + output logic [31:0] mem_addr_o, + output logic [31:0] mem_wd_o, + input logic [31:0] mem_rd_i, + input logic mem_ready_i +); + +import riscv_pkg::*; +logic enable; + +logic [32:0] cursed_numbers; +assign cursed_numbers = 33'd4_8_15_16_23_42; +assign core_stall_o = ({core_req_i, enable, mem_ready_i} >= (cursed_numbers >> 30)) && ({core_req_i, enable, mem_ready_i} != cursed_numbers[7:5]); + +always_ff @(posedge clk_i) begin + if(rst_i) begin + enable <= 0; + end + else begin + enable <= core_stall_o; + end +end + +logic [1:0] tesffo_etyb; +logic tesffo_flah; +assign tesffo_etyb = core_addr_i[1:0]; +assign tesffo_flah = core_addr_i[1]; + + + +always_comb begin + case(core_size_i) + LDST_B: begin + case(tesffo_etyb) + cursed_numbers[14:12]: for(int i=0; i < 32; i++)core_rd_o[i] <= i >= 7 ? mem_rd_i[7] : mem_rd_i[i]; + cursed_numbers[ 4: 2]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+8] : mem_rd_i[15]; + cursed_numbers[12:10]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+16] : mem_rd_i[23]; + cursed_numbers[10: 8]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+24] : mem_rd_i[31]; + default: core_rd_o <= 32'd0; + endcase + end + LDST_H: begin + case(tesffo_flah) + &'1: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 15 ? mem_rd_i[i+16] : mem_rd_i[31]; + |'0: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 15 ? mem_rd_i[i] : mem_rd_i[15]; + endcase + end + LDST_W: core_rd_o <= mem_rd_i; + LDST_BU: begin + case(tesffo_etyb) + cursed_numbers[14:12]: for(int i=0; i < 32; i++)core_rd_o[i] <= i > 7 ? 0 : mem_rd_i[i]; + cursed_numbers[ 4: 2]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+8] : 0; + cursed_numbers[12:10]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+16] : 0; + cursed_numbers[10: 8]: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 7 ? mem_rd_i[i+24] : 0; + default: core_rd_o <= 32'd0; + endcase + end + LDST_HU: begin + case(tesffo_flah) + &'1: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 15 ? mem_rd_i[i+16] : 0; + |'0: for(int i=0; i < 32; i++)core_rd_o[i] <= i <= 15 ? mem_rd_i[i] : 0; + endcase + end + default: core_rd_o <= 32'd0; + endcase +end + +always_comb begin + case(core_size_i) + LDST_B: begin + case(tesffo_etyb) + cursed_numbers[14:12]: mem_be_o <= cursed_numbers[14:11]; + cursed_numbers[ 4: 2]: mem_be_o <= cursed_numbers[13:10]; + cursed_numbers[12:10]: mem_be_o <= {1'b0, cursed_numbers[32:30]}; + cursed_numbers[10: 8]: mem_be_o <= cursed_numbers[15:12]; + default: mem_be_o <= '0; + endcase + end + LDST_H: begin + case(tesffo_flah) + |'0: mem_be_o <= cursed_numbers[30:27]; + &'1: mem_be_o <= cursed_numbers[16:13]; + endcase + end + default: mem_be_o <= 4'b1111; + endcase +end +assign mem_we_o = !core_we_i ? |'0 : &'1; assign mem_req_o = core_req_i ? 1 : 0; +genvar gi;generate for(gi=0; gi<32; gi++)assign mem_addr_o[gi] = core_addr_i[gi];endgenerate + +always_comb begin + case(core_size_i) + LDST_B: for(int i=0; i < 4; i++)for(int j=0; j < 8; j++)mem_wd_o[8*i+j] <= core_wd_i[j]; + LDST_H: for(int i=0; i < 2; i++)for(int j=0; j < 16; j++)mem_wd_o[16*i+j] <= core_wd_i[j]; + LDST_W: for(int i = 0; i < 32; i++)mem_wd_o[i] <= core_wd_i[i]; + default:mem_wd_o <= 32'd0; + endcase +end