From 15fd386a1d246221e4d2cabd256eeec42feea22d Mon Sep 17 00:00:00 2001 From: rohan Date: Tue, 23 Jan 2024 11:50:55 +0500 Subject: [PATCH] Added Zcmp Extension Support (Design and test files) --- core/Flist.cva6 | 1 + core/compressed_decoder.sv | 42 +- core/cva6.sv | 1 + core/id_stage.sv | 51 +- core/include/config_pkg.sv | 1 + core/include/cv32a60x_config_pkg.sv | 2 + core/include/cv64a6_imafdc_sv39_config_pkg.sv | 2 + .../cv64a6_imafdc_sv39_hpdcache_config_pkg.sv | 2 + .../cv64a6_imafdc_sv39_wb_config_pkg.sv | 2 + .../include/cv64a6_imafdcv_sv39_config_pkg.sv | 2 + core/zcmp_decoder.sv | 703 ++++++++++++++++++ verif/tests/custom/Zcmp/cm_mva01s_test.S | 32 + verif/tests/custom/Zcmp/cm_mvsa01_test.S | 32 + verif/tests/custom/Zcmp/cm_popret_test.S | 58 ++ verif/tests/custom/Zcmp/cm_popretz_test.S | 57 ++ verif/tests/custom/Zcmp/cm_push_pop_test.S | 59 ++ verif/tests/custom/Zcmp/link.ld | 66 ++ verif/tests/custom/Zcmp/readme.txt | 2 + verif/tests/custom/Zcmp/riscv_test.h | 269 +++++++ verif/tests/custom/Zcmp/syscalls.c | 521 +++++++++++++ verif/tests/custom/Zcmp/util.h | 92 +++ 21 files changed, 1972 insertions(+), 25 deletions(-) create mode 100644 core/zcmp_decoder.sv create mode 100644 verif/tests/custom/Zcmp/cm_mva01s_test.S create mode 100644 verif/tests/custom/Zcmp/cm_mvsa01_test.S create mode 100644 verif/tests/custom/Zcmp/cm_popret_test.S create mode 100644 verif/tests/custom/Zcmp/cm_popretz_test.S create mode 100644 verif/tests/custom/Zcmp/cm_push_pop_test.S create mode 100644 verif/tests/custom/Zcmp/link.ld create mode 100644 verif/tests/custom/Zcmp/readme.txt create mode 100644 verif/tests/custom/Zcmp/riscv_test.h create mode 100644 verif/tests/custom/Zcmp/syscalls.c create mode 100644 verif/tests/custom/Zcmp/util.h diff --git a/core/Flist.cva6 b/core/Flist.cva6 index 342f053e3c..c9d5af0812 100644 --- a/core/Flist.cva6 +++ b/core/Flist.cva6 @@ -103,6 +103,7 @@ ${CVA6_REPO_DIR}/core/alu.sv ${CVA6_REPO_DIR}/core/fpu_wrap.sv ${CVA6_REPO_DIR}/core/branch_unit.sv ${CVA6_REPO_DIR}/core/compressed_decoder.sv +${CVA6_REPO_DIR}/core/zcmp_decoder.sv ${CVA6_REPO_DIR}/core/controller.sv ${CVA6_REPO_DIR}/core/csr_buffer.sv ${CVA6_REPO_DIR}/core/csr_regfile.sv diff --git a/core/compressed_decoder.sv b/core/compressed_decoder.sv index cc9c77df87..7b22fdcd6b 100644 --- a/core/compressed_decoder.sv +++ b/core/compressed_decoder.sv @@ -24,6 +24,7 @@ module compressed_decoder #( ) ( input logic [31:0] instr_i, output logic [31:0] instr_o, + output logic is_zcmp_instr_o, output logic illegal_instr_o, output logic is_compressed_o ); @@ -32,10 +33,11 @@ module compressed_decoder #( // Compressed Decoder // ------------------- always_comb begin - illegal_instr_o = 1'b0; - instr_o = '0; - is_compressed_o = 1'b1; - instr_o = instr_i; + illegal_instr_o = 1'b0; + instr_o = '0; + is_compressed_o = 1'b1; + instr_o = instr_i; + is_zcmp_instr_o = 0; // I: | imm[11:0] | rs1 | funct3 | rd | opcode | // S: | imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode | @@ -849,18 +851,25 @@ module compressed_decoder #( riscv::OpcodeC2Fsdsp: begin if (CVA6Cfg.FpPresent) begin - // c.fsdsp -> fsd rs2, imm(x2) - instr_o = { - 3'b0, - instr_i[9:7], - instr_i[12], - instr_i[6:2], - 5'h02, - 3'b011, - instr_i[11:10], - 3'b000, - riscv::OpcodeStoreFp - }; + // c.fsdsp -> fsd rs2, imm(x2) + instr_o = { + 3'b0, + instr_i[9:7], + instr_i[12], + instr_i[6:2], + 5'h02, + 3'b011, + instr_i[11:10], + 3'b000, + riscv::OpcodeStoreFp + }; + end else if (CVA6Cfg.RVZCMP) begin + if (instr_i[12:10] == 3'b110 || instr_i[12:10] == 3'b111 || instr_i[12:10] == 3'b011) begin //is a push/pop instruction + is_zcmp_instr_o = 1; + instr_o = instr_i; + end else begin + illegal_instr_o = 1'b1; + end end else begin illegal_instr_o = 1'b1; end @@ -933,3 +942,4 @@ module compressed_decoder #( end end endmodule + diff --git a/core/cva6.sv b/core/cva6.sv index ff29f29633..b5580cee3a 100644 --- a/core/cva6.sv +++ b/core/cva6.sv @@ -180,6 +180,7 @@ module cva6 CVA6Cfg.RVV, CVA6Cfg.RVC, CVA6Cfg.RVZCB, + CVA6Cfg.RVZCMP, CVA6Cfg.XFVec, CVA6Cfg.CvxifEn, CVA6Cfg.ZiCondExtEn, diff --git a/core/id_stage.sv b/core/id_stage.sv index dfd8dd698f..3877686f95 100644 --- a/core/id_stage.sv +++ b/core/id_stage.sv @@ -54,8 +54,13 @@ module id_stage #( ariane_pkg::scoreboard_entry_t decoded_instruction; logic is_illegal; + logic is_illegal_cmp; logic [31:0] instruction; + logic [31:0] compressed_instr; logic is_compressed; + logic is_compressed_cmp; + logic is_zcmp_instr_i; + logic stall_instr_fetch; if (CVA6Cfg.RVC) begin // --------------------------------------------------------- @@ -64,15 +69,38 @@ module id_stage #( compressed_decoder #( .CVA6Cfg(CVA6Cfg) ) compressed_decoder_i ( - .instr_i (fetch_entry_i.instruction), - .instr_o (instruction), - .illegal_instr_o(is_illegal), - .is_compressed_o(is_compressed) + .instr_i (fetch_entry_i.instruction), + .instr_o (compressed_instr), + .illegal_instr_o (is_illegal), + .is_compressed_o (is_compressed), + .is_zcmp_instr_o (is_zcmp_instr_i) ); + if (CVA6Cfg.RVZCMP) begin + //sequencial decoder + zcmp_decoder #( + .CVA6Cfg(CVA6Cfg) + ) zcmp_decoder_i ( + .instr_i (compressed_instr), + .is_zcmp_instr_i (is_zcmp_instr_i), + .clk_i (clk_i), + .rst_ni (rst_ni), + .instr_o (instruction), + .illegal_instr_i (is_illegal), + .is_compressed_i (is_compressed), + .issue_ack_i (issue_instr_ack_i), + .illegal_instr_o (is_illegal_cmp), + .is_compressed_o (is_compressed_cmp), + .fetch_stall_o (stall_instr_fetch) + ); + end else begin + assign instruction = compressed_instr; + assign is_illegal_cmp = is_illegal; + assign is_compressed_cmp = is_compressed; + end end else begin assign instruction = fetch_entry_i.instruction; - assign is_illegal = '0; - assign is_compressed = '0; + assign is_illegal_cmp = '0; + assign is_compressed_cmp = '0; end // --------------------------------------------------------- // 2. Decode and emit instruction to issue stage @@ -84,8 +112,8 @@ module id_stage #( .irq_ctrl_i, .irq_i, .pc_i (fetch_entry_i.address), - .is_compressed_i (is_compressed), - .is_illegal_i (is_illegal), + .is_compressed_i (is_compressed_cmp), + .is_illegal_i (is_illegal_cmp), .instruction_i (instruction), .compressed_instr_i (fetch_entry_i.instruction[15:0]), .branch_predict_i (fetch_entry_i.branch_predict), @@ -120,7 +148,11 @@ module id_stage #( // or the issue stage is currently acknowledging an instruction, which means that we will have space // for a new instruction if ((!issue_q.valid || issue_instr_ack_i) && fetch_entry_valid_i) begin - fetch_entry_ready_o = 1'b1; + if (stall_instr_fetch) begin + fetch_entry_ready_o = 1'b0; + end else begin + fetch_entry_ready_o = 1'b1; + end issue_n = '{1'b1, decoded_instruction, is_control_flow_instr}; end @@ -138,3 +170,4 @@ module id_stage #( end end endmodule + diff --git a/core/include/config_pkg.sv b/core/include/config_pkg.sv index 7c455dd08e..ac01e83a71 100644 --- a/core/include/config_pkg.sv +++ b/core/include/config_pkg.sv @@ -55,6 +55,7 @@ package config_pkg; bit RVV; bit RVC; bit RVZCB; + bit RVZCMP; bit XFVec; bit CvxifEn; bit ZiCondExtEn; diff --git a/core/include/cv32a60x_config_pkg.sv b/core/include/cv32a60x_config_pkg.sv index cf2a399875..6a92a94bda 100644 --- a/core/include/cv32a60x_config_pkg.sv +++ b/core/include/cv32a60x_config_pkg.sv @@ -21,6 +21,7 @@ package cva6_config_pkg; localparam CVA6ConfigCvxifEn = 1; localparam CVA6ConfigCExtEn = 1; localparam CVA6ConfigZcbExtEn = 1; + localparam CVA6ConfigZcmpExtEn = 1; localparam CVA6ConfigAExtEn = 1; localparam CVA6ConfigBExtEn = 1; localparam CVA6ConfigVExtEn = 0; @@ -87,6 +88,7 @@ package cva6_config_pkg; RVV: bit'(CVA6ConfigVExtEn), RVC: bit'(CVA6ConfigCExtEn), RVZCB: bit'(CVA6ConfigZcbExtEn), + RVZCMP: bit'(CVA6ConfigZcmpExtEn), XFVec: bit'(CVA6ConfigFVecEn), CvxifEn: bit'(CVA6ConfigCvxifEn), ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn), diff --git a/core/include/cv64a6_imafdc_sv39_config_pkg.sv b/core/include/cv64a6_imafdc_sv39_config_pkg.sv index 1ee4e4dfad..b76d7513f0 100644 --- a/core/include/cv64a6_imafdc_sv39_config_pkg.sv +++ b/core/include/cv64a6_imafdc_sv39_config_pkg.sv @@ -21,6 +21,7 @@ package cva6_config_pkg; localparam CVA6ConfigCvxifEn = 1; localparam CVA6ConfigCExtEn = 1; localparam CVA6ConfigZcbExtEn = 1; + localparam CVA6ConfigZcmpExtEn = 0; localparam CVA6ConfigAExtEn = 1; localparam CVA6ConfigBExtEn = 1; localparam CVA6ConfigVExtEn = 0; @@ -87,6 +88,7 @@ package cva6_config_pkg; RVV: bit'(CVA6ConfigVExtEn), RVC: bit'(CVA6ConfigCExtEn), RVZCB: bit'(CVA6ConfigZcbExtEn), + RVZCMP: bit'(CVA6ConfigZcmpExtEn), XFVec: bit'(CVA6ConfigFVecEn), CvxifEn: bit'(CVA6ConfigCvxifEn), ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn), diff --git a/core/include/cv64a6_imafdc_sv39_hpdcache_config_pkg.sv b/core/include/cv64a6_imafdc_sv39_hpdcache_config_pkg.sv index df23b29b4b..c2b87c7bd3 100644 --- a/core/include/cv64a6_imafdc_sv39_hpdcache_config_pkg.sv +++ b/core/include/cv64a6_imafdc_sv39_hpdcache_config_pkg.sv @@ -28,6 +28,7 @@ package cva6_config_pkg; localparam CVA6ConfigCvxifEn = 1; localparam CVA6ConfigCExtEn = 1; localparam CVA6ConfigZcbExtEn = 1; + localparam CVA6ConfigZcmpExtEn = 0; localparam CVA6ConfigAExtEn = 1; localparam CVA6ConfigBExtEn = 1; localparam CVA6ConfigVExtEn = 0; @@ -94,6 +95,7 @@ package cva6_config_pkg; RVV: bit'(CVA6ConfigVExtEn), RVC: bit'(CVA6ConfigCExtEn), RVZCB: bit'(CVA6ConfigZcbExtEn), + RVZCMP: bit'(CVA6ConfigZcmpExtEn), XFVec: bit'(CVA6ConfigFVecEn), CvxifEn: bit'(CVA6ConfigCvxifEn), ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn), diff --git a/core/include/cv64a6_imafdc_sv39_wb_config_pkg.sv b/core/include/cv64a6_imafdc_sv39_wb_config_pkg.sv index 6b3a5eb395..9016107cbd 100644 --- a/core/include/cv64a6_imafdc_sv39_wb_config_pkg.sv +++ b/core/include/cv64a6_imafdc_sv39_wb_config_pkg.sv @@ -21,6 +21,7 @@ package cva6_config_pkg; localparam CVA6ConfigCvxifEn = 1; localparam CVA6ConfigCExtEn = 1; localparam CVA6ConfigZcbExtEn = 1; + localparam CVA6ConfigZcmpExtEn = 0; localparam CVA6ConfigAExtEn = 1; localparam CVA6ConfigBExtEn = 1; localparam CVA6ConfigVExtEn = 0; @@ -87,6 +88,7 @@ package cva6_config_pkg; RVV: bit'(CVA6ConfigVExtEn), RVC: bit'(CVA6ConfigCExtEn), RVZCB: bit'(CVA6ConfigZcbExtEn), + RVZCMP: bit'(CVA6ConfigZcmpExtEn), XFVec: bit'(CVA6ConfigFVecEn), CvxifEn: bit'(CVA6ConfigCvxifEn), ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn), diff --git a/core/include/cv64a6_imafdcv_sv39_config_pkg.sv b/core/include/cv64a6_imafdcv_sv39_config_pkg.sv index 66bc4caefa..f1947a6e28 100644 --- a/core/include/cv64a6_imafdcv_sv39_config_pkg.sv +++ b/core/include/cv64a6_imafdcv_sv39_config_pkg.sv @@ -21,6 +21,7 @@ package cva6_config_pkg; localparam CVA6ConfigCvxifEn = 0; localparam CVA6ConfigCExtEn = 1; localparam CVA6ConfigZcbExtEn = 0; + localparam CVA6ConfigZcmpExtEn = 0; localparam CVA6ConfigAExtEn = 1; localparam CVA6ConfigBExtEn = 0; localparam CVA6ConfigVExtEn = 1; @@ -86,6 +87,7 @@ package cva6_config_pkg; RVV: bit'(CVA6ConfigVExtEn), RVC: bit'(CVA6ConfigCExtEn), RVZCB: bit'(CVA6ConfigZcbExtEn), + RVZCMP: bit'(CVA6ConfigZcmpExtEn), XFVec: bit'(CVA6ConfigFVecEn), CvxifEn: bit'(CVA6ConfigCvxifEn), ZiCondExtEn: bit'(0), diff --git a/core/zcmp_decoder.sv b/core/zcmp_decoder.sv new file mode 100644 index 0000000000..566987687e --- /dev/null +++ b/core/zcmp_decoder.sv @@ -0,0 +1,703 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Author: Rohan Arshid, 10xEngineers +// Date: 22.01.2024 +// Description: Contains the logic for decoding cm.push, cm.pop, cm.popret, +// cm.popretz, cm.mvsa01, and cm.mva01s instructions of the +// Zcmp Extension + +module zcmp_decoder #( + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty +) ( + input logic [31:0] instr_i, + input logic clk_i, // Clock + input logic rst_ni, // Synchronous reset + input logic is_zcmp_instr_i, // Intruction is of zcmp extension + input logic illegal_instr_i, // From compressed decoder + input logic is_compressed_i, + input logic issue_ack_i, // Check if the intruction is acknowledged + output logic [31:0] instr_o, + output logic illegal_instr_o, + output logic is_compressed_o, + output logic fetch_stall_o //Wait while push/pop/move instructions expand +); + + // FSM States + enum logic [2:0] { + IDLE, + INIT, + PUSH_ADDI, + POPRETZ_1, + MOVE, + PUSH_POP_INSTR_2 + } + state_d, state_q; + + // Instruction Types + enum logic [2:0] { + PUSH, + POP, + POPRETZ, + POPRET, + MVA01S, + MVSA01 + } zcmp_instr_type; + + // Temporary registers + logic [3:0] reg_numbers,reg_numbers_q,reg_numbers_d; + logic [8:0] stack_adj; + logic [4:0] xreg1,xreg2,store_reg,store_reg_q,store_reg_d; + logic [1:0] popretz_inst_q,popretz_inst_d; + logic [11:0] offset_reg,offset_q,offset_d; + logic [31:0] instr_o_reg; + + riscv::itype_t itype_inst; + assign instr_o = instr_o_reg; + always_comb begin + illegal_instr_o = 1'b0; + fetch_stall_o = 1'b0; + is_compressed_o = is_zcmp_instr_i ? 1'b1 : is_compressed_i; + reg_numbers = '0; + stack_adj = '0; + state_d = state_q; + offset_d = offset_q; + reg_numbers_d = reg_numbers_q; + store_reg_d = store_reg_q; + popretz_inst_d = popretz_inst_q; + + if (is_zcmp_instr_i) begin + + unique case (instr_i[12:10]) + // push or pop + 3'b110: begin + unique case (instr_i[9:8]) + 2'b00: begin + zcmp_instr_type = PUSH; + end + 2'b10: begin + zcmp_instr_type = POP; + end + default: begin + illegal_instr_o = 1'b1; + instr_o_reg = instr_i; + end + endcase + end + // popret or popretz + 3'b111: begin + unique case (instr_i[9:8]) + 2'b00: begin + zcmp_instr_type = POPRETZ; + end + 2'b10: begin + zcmp_instr_type = POPRET; + end + default: begin + illegal_instr_o = 1'b1; + instr_o_reg = instr_i; + end + endcase + end + // mvq01s or mvsa01 + 3'b011: begin + unique case (instr_i[6:5]) + 2'b01: begin + zcmp_instr_type = MVSA01; + end + 2'b11: begin + zcmp_instr_type = MVA01S; + end + default: begin + illegal_instr_o = 1'b1; + instr_o_reg = instr_i; + end + endcase + end + default: begin + illegal_instr_o = 1'b1; + instr_o_reg = instr_i; + end + endcase + + // Calculate xreg1 & xreg2 for move instructions + if (zcmp_instr_type == MVSA01 || zcmp_instr_type == MVA01S) begin + if (instr_i[9:7] != instr_i[4:2]) begin + xreg1 = {instr_i[9:8] > 0, instr_i[9:8] == 0, instr_i[9:7]}; + xreg2 = {instr_i[4:3] > 0, instr_i[4:3] == 0, instr_i[4:2]}; + end else begin + illegal_instr_o = 1'b1; + instr_o_reg = instr_i; + end + end else begin + xreg1 = '0; + xreg2 = '0; + end + + // push/pop/popret/popretz instructions + unique case (instr_i[7:4]) + 4'b0100: reg_numbers = 4'b0001; // 4 + 4'b0101: reg_numbers = 4'b0010; // 5 + 4'b0110: reg_numbers = 4'b0011; // 6 + 4'b0111: reg_numbers = 4'b0100; // 7 + 4'b1000: reg_numbers = 4'b0101; // 8 + 4'b1001: reg_numbers = 4'b0110; // 9 + 4'b1010: reg_numbers = 4'b0111; // 10 + 4'b1011: reg_numbers = 4'b1000; // 11 + 4'b1100: reg_numbers = 4'b1001; // 12 + 4'b1101: reg_numbers = 4'b1010; // 13 + 4'b1110: reg_numbers = 4'b1011; // 14 + 4'b1111: reg_numbers = 4'b1100; // 15 + default: reg_numbers = '0; + endcase + + if (riscv::XLEN == 32) begin + unique case (instr_i[7:4]) + 4'b0100, 4'b0101, 4'b0110, 4'b0111: begin + unique case (instr_i[3:2]) + 2'b00: stack_adj = 16; + 2'b01: stack_adj = 32; + 2'b10: stack_adj = 48; + 2'b11: stack_adj = 64; + endcase + end + 4'b1000, 4'b1001, 4'b1010, 4'b1011: begin + unique case (instr_i[3:2]) + 2'b00: stack_adj = 32; + 2'b01: stack_adj = 48; + 2'b10: stack_adj = 64; + 2'b11: stack_adj = 80; + endcase + end + 4'b1100, 4'b1101, 4'b1110: begin + unique case (instr_i[3:2]) + 2'b00: stack_adj = 48; + 2'b01: stack_adj = 64; + 2'b10: stack_adj = 80; + 2'b11: stack_adj = 96; + endcase + end + 4'b1111: begin + unique case (instr_i[3:2]) + 2'b00: stack_adj = 64; + 2'b01: stack_adj = 80; + 2'b10: stack_adj = 96; + 2'b11: stack_adj = 112; + endcase + end + default: ; + endcase + end else begin + unique case (instr_i[7:4]) + 4'b0100, 4'b0101: begin + unique case (instr_i[3:2]) + 2'b00: stack_adj = 16; + 2'b01: stack_adj = 32; + 2'b10: stack_adj = 48; + 2'b11: stack_adj = 64; + endcase + end + 4'b0110, 4'b0111: begin + unique case (instr_i[3:2]) + 2'b00: stack_adj = 32; + 2'b01: stack_adj = 48; + 2'b10: stack_adj = 64; + 2'b11: stack_adj = 80; + endcase + end + 4'b1000, 4'b1001: begin + unique case (instr_i[3:2]) + 2'b00: stack_adj = 48; + 2'b01: stack_adj = 64; + 2'b10: stack_adj = 80; + 2'b11: stack_adj = 96; + endcase + end + 4'b1010, 4'b1011: begin + unique case (instr_i[3:2]) + 2'b00: stack_adj = 64; + 2'b01: stack_adj = 80; + 2'b10: stack_adj = 96; + 2'b11: stack_adj = 112; + endcase + end + 4'b1100, 4'b1101: begin + unique case (instr_i[3:2]) + 2'b00: stack_adj = 80; + 2'b01: stack_adj = 96; + 2'b10: stack_adj = 112; + 2'b11: stack_adj = 128; + endcase + end + 4'b1110: begin + unique case (instr_i[3:2]) + 2'b00: stack_adj = 96; + 2'b01: stack_adj = 112; + 2'b10: stack_adj = 128; + 2'b11: stack_adj = 144; + endcase + end + 4'b1111: begin + unique case (instr_i[3:2]) + 2'b00: stack_adj = 112; + 2'b01: stack_adj = 128; + 2'b10: stack_adj = 144; + 2'b11: stack_adj = 160; + endcase + end + endcase + end + + //Take 2's compliment in case of PUSH instruction + if (zcmp_instr_type == PUSH) begin + itype_inst.imm = ~stack_adj + 1'b1; + end else begin + itype_inst.imm = stack_adj - 3'b100; + end + end else begin + illegal_instr_o = illegal_instr_i; + instr_o_reg = instr_i; + end + + unique case (state_q) + IDLE: begin + if (is_zcmp_instr_i && issue_ack_i) begin + reg_numbers_d = reg_numbers - 1'b1; + state_d = INIT; + case (zcmp_instr_type) + PUSH: begin + offset_d = 12'hFFC + 12'hFFC; + end + POP, POPRETZ, POPRET: begin + offset_d = itype_inst.imm + 12'hFFC; + offset_reg = itype_inst.imm; + case (zcmp_instr_type) + POPRETZ: begin + popretz_inst_d = 2'b11; + end + POPRET: begin + popretz_inst_d = 2'b01; + end + default: begin + popretz_inst_d = 'b0; + end + endcase + end + default: ; + endcase + // when rlist is 4, max reg is x18 i.e. 14(const) + 4 + // when rlist is 12, max reg is x27 i.e. 15(const) + 12 + if (reg_numbers == 4'b1100) begin + store_reg_d = 4'b1110 + reg_numbers; + store_reg = 4'b1111 + reg_numbers; + end else begin + store_reg_d = 4'b1101 + reg_numbers; + store_reg = 4'b1110 + reg_numbers; + end + + if (zcmp_instr_type == MVSA01) begin + fetch_stall_o = 1; + // addi xreg1, a0, 0 + instr_o_reg = {12'h0, 5'hA, 3'h0, xreg1, riscv::OpcodeOpImm}; + state_d = MOVE; + end + + if (zcmp_instr_type == MVA01S) begin + fetch_stall_o = 1; + // addi a0, xreg1, 0 + instr_o_reg = {12'h0, xreg1, 3'h0, 5'hA, riscv::OpcodeOpImm}; + state_d = MOVE; + end + + if (zcmp_instr_type == PUSH) begin + + fetch_stall_o = 1'b1; // stall inst fetch + + if (reg_numbers == 4'b0001) begin + if (riscv::XLEN == 64) begin + instr_o_reg = { + 7'b1111111, 5'h1, 5'h2, 3'h3, 5'b11100, riscv::OpcodeStore + }; // sd store_reg, -4(sp) + end else begin + instr_o_reg = { + 7'b1111111, 5'h1, 5'h2, 3'h2, 5'b11100, riscv::OpcodeStore + }; // sw store_reg, -4(sp) + end + state_d = PUSH_ADDI; + end + + if (reg_numbers == 4'b0010) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {7'b1111111, 5'h8, 5'h2, 3'h3, 5'b11100, riscv::OpcodeStore}; + end else begin + instr_o_reg = {7'b1111111, 5'h8, 5'h2, 3'h2, 5'b11100, riscv::OpcodeStore}; + end + end + + if (reg_numbers == 4'b0011) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {7'b1111111, 5'h9, 5'h2, 3'h3, 5'b11100, riscv::OpcodeStore}; + end else begin + instr_o_reg = {7'b1111111, 5'h9, 5'h2, 3'h2, 5'b11100, riscv::OpcodeStore}; + end + + end + + if (reg_numbers >= 4 && reg_numbers <= 12) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {7'b1111111, store_reg, 5'h2, 3'h3, 5'b11100, riscv::OpcodeStore}; + end else begin + instr_o_reg = {7'b1111111, store_reg, 5'h2, 3'h2, 5'b11100, riscv::OpcodeStore}; + end + + if (reg_numbers == 12) begin + state_d = PUSH_POP_INSTR_2; + end + end + end + + if ((zcmp_instr_type == POP || zcmp_instr_type == POPRETZ || zcmp_instr_type == POPRET)) begin + fetch_stall_o = 1; // stall inst fetch + if (reg_numbers == 1) begin + if (riscv::XLEN == 64) begin + instr_o_reg = { + offset_reg, 5'h2, 3'h3, 5'h1, riscv::OpcodeLoad + }; // ld store_reg, Imm(sp) + end else begin + instr_o_reg = { + offset_reg, 5'h2, 3'h2, 5'h1, riscv::OpcodeLoad + }; // lw store_reg, Imm(sp) + end + unique case (zcmp_instr_type) + PUSH, POP, POPRET: begin + state_d = PUSH_ADDI; + end + POPRETZ: begin + state_d = POPRETZ_1; + end + default: ; + endcase + end + + if (reg_numbers == 2) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {offset_reg, 5'h2, 3'h3, 5'h8, riscv::OpcodeLoad}; + end else begin + instr_o_reg = {offset_reg, 5'h2, 3'h2, 5'h8, riscv::OpcodeLoad}; + end + end + + if (reg_numbers == 3) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {offset_reg, 5'h2, 3'h3, 5'h9, riscv::OpcodeLoad}; + end else begin + instr_o_reg = {offset_reg, 5'h2, 3'h2, 5'h9, riscv::OpcodeLoad}; + end + end + + if (reg_numbers >= 4 && reg_numbers <= 12) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {offset_reg, 5'h2, 3'h3, store_reg, riscv::OpcodeLoad}; + end else begin + instr_o_reg = {offset_reg, 5'h2, 3'h2, store_reg, riscv::OpcodeLoad}; + end + + if (reg_numbers == 12) begin + state_d = PUSH_POP_INSTR_2; + end + end + end + end + end + INIT: begin + fetch_stall_o = 1'b1; // stall inst fetch + if (issue_ack_i && is_zcmp_instr_i && zcmp_instr_type == PUSH) begin + if (reg_numbers_q == 4'b0001) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {offset_d[11:5], 5'h1, 5'h2, 3'h3, offset_d[4:0], riscv::OpcodeStore}; + end else begin + instr_o_reg = {offset_d[11:5], 5'h1, 5'h2, 3'h2, offset_d[4:0], riscv::OpcodeStore}; + end + state_d = PUSH_ADDI; + end + + if (reg_numbers_q == 4'b0010) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {offset_d[11:5], 5'h8, 5'h2, 3'h3, offset_d[4:0], riscv::OpcodeStore}; + end else begin + instr_o_reg = {offset_d[11:5], 5'h8, 5'h2, 3'h2, offset_d[4:0], riscv::OpcodeStore}; + end + reg_numbers_d = reg_numbers_q - 1; + offset_d = offset_q + 12'hFFC; // decrement offset by -4 i.e. add 2's compilment of 4 + end + + if (reg_numbers_q == 4'b0011) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {offset_d[11:5], 5'h9, 5'h2, 3'h3, offset_d[4:0], riscv::OpcodeStore}; + end else begin + instr_o_reg = {offset_d[11:5], 5'h9, 5'h2, 3'h2, offset_d[4:0], riscv::OpcodeStore}; + end + reg_numbers_d = reg_numbers_q - 1; + offset_d = offset_q + 12'hFFC; + end + + if (reg_numbers_q >= 4 && reg_numbers_q <= 12) begin + if (riscv::XLEN == 64) begin + instr_o_reg = { + offset_d[11:5], store_reg_q, 5'h2, 3'h3, offset_d[4:0], riscv::OpcodeStore + }; + end else begin + instr_o_reg = { + offset_d[11:5], store_reg_q, 5'h2, 3'h2, offset_d[4:0], riscv::OpcodeStore + }; + end + reg_numbers_d = reg_numbers_q - 1; + store_reg_d = store_reg_q - 1; + offset_d = offset_q + 12'hFFC; + if (reg_numbers_q == 12) begin + state_d = PUSH_POP_INSTR_2; + end + end + end + + if (issue_ack_i && is_zcmp_instr_i && (zcmp_instr_type == POP || zcmp_instr_type == POPRETZ || zcmp_instr_type == POPRET)) begin + + if (reg_numbers_q == 1) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {offset_d[11:0], 5'h2, 3'h3, 5'h1, riscv::OpcodeLoad}; + end else begin + instr_o_reg = {offset_d[11:0], 5'h2, 3'h2, 5'h1, riscv::OpcodeLoad}; + end + unique case (zcmp_instr_type) + PUSH, POP, POPRET: begin + state_d = PUSH_ADDI; + end + POPRETZ: begin + state_d = POPRETZ_1; + end + default: ; + endcase + end + + if (reg_numbers_q == 2) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {offset_d[11:0], 5'h2, 3'h3, 5'h8, riscv::OpcodeLoad}; + end else begin + instr_o_reg = {offset_d[11:0], 5'h2, 3'h2, 5'h8, riscv::OpcodeLoad}; + end + reg_numbers_d = reg_numbers_q - 1; + offset_d = offset_q + 12'hFFC; // decrement offset by -4 i.e. add 2's compilment of 4 + end + + if (reg_numbers_q == 3) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {offset_d[11:0], 5'h2, 3'h3, 5'h9, riscv::OpcodeLoad}; + end else begin + instr_o_reg = {offset_d[11:0], 5'h2, 3'h2, 5'h9, riscv::OpcodeLoad}; + end + reg_numbers_d = reg_numbers_q - 1; + offset_d = offset_q + 12'hFFC; + end + + if (reg_numbers_q >= 4 && reg_numbers_q <= 12) begin + if (riscv::XLEN == 64) begin + instr_o_reg = {offset_d[11:0], 5'h2, 3'h3, store_reg_q, riscv::OpcodeLoad}; + end else begin + instr_o_reg = {offset_d[11:0], 5'h2, 3'h2, store_reg_q, riscv::OpcodeLoad}; + end + reg_numbers_d = reg_numbers_q - 1; + store_reg_d = store_reg_q - 1; + offset_d = offset_q + 12'hFFC; + if (reg_numbers_q == 12) begin + state_d = PUSH_POP_INSTR_2; + end + end + end + end + + MOVE: begin + case (zcmp_instr_type) + MVSA01: begin + if (issue_ack_i) begin + // addi xreg2, a1, 0 + instr_o_reg = {12'h0, 5'hB, 3'h0, xreg2, riscv::OpcodeOpImm}; + fetch_stall_o = 0; + state_d = IDLE; + end + end + MVA01S: begin + if (issue_ack_i) begin + // addi a1, xreg2, 0 + instr_o_reg = {12'h0, xreg2, 3'h0, 5'hB, riscv::OpcodeOpImm}; + fetch_stall_o = 0; + state_d = IDLE; + end + end + default: begin + illegal_instr_o = 1'b1; + instr_o_reg = instr_i; + end + endcase + fetch_stall_o = 1; + end + + PUSH_ADDI: begin + if (riscv::XLEN == 64) begin + if (issue_ack_i && is_zcmp_instr_i && zcmp_instr_type == PUSH) begin + // addi sp, sp, stack_adj + instr_o_reg = {itype_inst.imm, 5'h2, 3'h0, 5'h2, riscv::OpcodeOpImm}; + end else begin + if (issue_ack_i) begin + instr_o_reg = {stack_adj, 5'h2, 3'h0, 5'h2, riscv::OpcodeOpImm}; + end + end + if (issue_ack_i && is_zcmp_instr_i && (zcmp_instr_type == POPRETZ || zcmp_instr_type == POPRET)) begin + state_d = POPRETZ_1; + fetch_stall_o = 1; + end else begin + if (issue_ack_i) begin + state_d = IDLE; + fetch_stall_o = 0; + end else begin + fetch_stall_o = 1; + end + end + end else begin + if (issue_ack_i && is_zcmp_instr_i && zcmp_instr_type == PUSH) begin + // addi sp, sp, stack_adj + instr_o_reg = {itype_inst.imm, 5'h2, 3'h0, 5'h2, riscv::OpcodeOpImm}; + end else begin + if (issue_ack_i) begin + instr_o_reg = {stack_adj, 5'h2, 3'h0, 5'h2, riscv::OpcodeOpImm}; + end + end + if (issue_ack_i && is_zcmp_instr_i && (zcmp_instr_type == POPRETZ || zcmp_instr_type == POPRET)) begin + state_d = POPRETZ_1; + fetch_stall_o = 1; + end else begin + if (issue_ack_i) begin + state_d = IDLE; + fetch_stall_o = 0; + end else begin + fetch_stall_o = 1; + end + end + end + end + + PUSH_POP_INSTR_2: begin + if (riscv::XLEN == 64) begin + case (zcmp_instr_type) + PUSH: begin + if (issue_ack_i) begin + instr_o_reg = { + offset_d[11:5], store_reg_q, 5'h2, 3'h3, offset_d[4:0], riscv::OpcodeStore + }; + offset_d = offset_q + 12'hFFC; + store_reg_d = store_reg_q - 1; + state_d = INIT; + end + end + POP, POPRETZ, POPRET: begin + if (issue_ack_i) begin + instr_o_reg = {offset_d[11:0], 5'h2, 3'h3, store_reg_q, riscv::OpcodeLoad}; + offset_d = offset_q + 12'hFFC; + store_reg_d = store_reg_q - 1; + state_d = INIT; + end + end + default: begin + illegal_instr_o = 1'b1; + instr_o_reg = instr_i; + end + endcase + end else begin + case (zcmp_instr_type) + PUSH: begin + if (issue_ack_i) begin + instr_o_reg = { + offset_d[11:5], store_reg_q, 5'h2, 3'h2, offset_d[4:0], riscv::OpcodeStore + }; + offset_d = offset_q + 12'hFFC; + store_reg_d = store_reg_q - 1; + state_d = INIT; + end + end + POP, POPRETZ, POPRET: begin + if (issue_ack_i) begin + instr_o_reg = {offset_d[11:0], 5'h2, 3'h2, store_reg_q, riscv::OpcodeLoad}; + offset_d = offset_q + 12'hFFC; + store_reg_d = store_reg_q - 1; + state_d = INIT; + end + end + default: begin + illegal_instr_o = 1'b1; + instr_o_reg = instr_i; + end + endcase + end + fetch_stall_o = 1; + end + + POPRETZ_1: begin + unique case (popretz_inst_q) + 2'b11: begin + if (issue_ack_i) begin + instr_o_reg = {20'h0, 5'hA, riscv::OpcodeLui}; //lui a0, 0x0 + popretz_inst_d = popretz_inst_q - 1; + end + fetch_stall_o = 1; + end + 2'b10: begin + if (issue_ack_i) begin + instr_o_reg = {12'h0, 5'hA, 3'h0, 5'hA, riscv::OpcodeOpImm}; //addi a0, a0, 0x0 + popretz_inst_d = popretz_inst_q - 1; + state_d = PUSH_ADDI; + end + fetch_stall_o = 1; + end + 2'b01: begin + if (issue_ack_i) begin + instr_o_reg = {12'h0, 5'h1, 3'h0, 5'h0, riscv::OpcodeJalr}; //ret - jalr x0, x1, 0 + state_d = IDLE; + fetch_stall_o = 0; + popretz_inst_d = popretz_inst_q - 1; + end + end + default: begin + illegal_instr_o = 1'b1; + instr_o_reg = instr_i; + end + endcase + end + default: begin + state_d = IDLE; + end + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + state_q <= IDLE; + offset_q <= '0; + popretz_inst_q <= '0; + reg_numbers_q <= '0; + store_reg_q <= '0; + end else begin + state_q <= state_d; + offset_q <= offset_d; + reg_numbers_q <= reg_numbers_d; + store_reg_q <= store_reg_d; + popretz_inst_q <= popretz_inst_d; + end + end +endmodule + diff --git a/verif/tests/custom/Zcmp/cm_mva01s_test.S b/verif/tests/custom/Zcmp/cm_mva01s_test.S new file mode 100644 index 0000000000..3c6b38a8f6 --- /dev/null +++ b/verif/tests/custom/Zcmp/cm_mva01s_test.S @@ -0,0 +1,32 @@ +#include "riscv_test.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + li s1, 5 + li s2, 7 + cm.mva01s s1, s2 + + bne a0, s1, failure + beq a1, s2, success +failure: + li x1, 1 + slli x1, x1, 1 + addi x1, x1, 1 + sw x1, tohost, t5 + self_loop_2: j self_loop_2 + +success: + li x1, 0 + slli x1, x1, 1 + addi x1, x1, 1 + sw x1, tohost, t5 + self_loop: j self_loop + +RVTEST_CODE_END + +.data + +RVTEST_DATA_BEGIN + +RVTEST_DATA_END diff --git a/verif/tests/custom/Zcmp/cm_mvsa01_test.S b/verif/tests/custom/Zcmp/cm_mvsa01_test.S new file mode 100644 index 0000000000..6c99794bd7 --- /dev/null +++ b/verif/tests/custom/Zcmp/cm_mvsa01_test.S @@ -0,0 +1,32 @@ +#include "riscv_test.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + li a0, 5 + li a1, 7 + cm.mvsa01 s1, s2 + + bne a0, s1, failure + beq a1, s2, success +failure: + li x1, 1 + slli x1, x1, 1 + addi x1, x1, 1 + sw x1, tohost, t5 + self_loop_2: j self_loop_2 + +success: + li x1, 0 + slli x1, x1, 1 + addi x1, x1, 1 + sw x1, tohost, t5 + self_loop: j self_loop + +RVTEST_CODE_END + +.data + +RVTEST_DATA_BEGIN + +RVTEST_DATA_END diff --git a/verif/tests/custom/Zcmp/cm_popret_test.S b/verif/tests/custom/Zcmp/cm_popret_test.S new file mode 100644 index 0000000000..55669c22bd --- /dev/null +++ b/verif/tests/custom/Zcmp/cm_popret_test.S @@ -0,0 +1,58 @@ +#include "riscv_test.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + //Initialize the stack + la sp, stack + + // Load the first number into register a0 + lw s0, num1 + + // Load the second number into register a1 + lw s1, num2 + + //Load the result into register a2 + lw a2, result + + //Two consecutive move instructions replaced by the compressed move instruction + //mv s0, a0 + //mv s1, a1 + + //Push the registers to stack + jal push_pop + + // Add the numbers and store the result in register t2 + add t2, s0, s1 + + // Store the result in the memory location reserved for it + beq t2, a2, success +failure: + li x1, 1 + slli x1, x1, 1 + addi x1, x1, 1 + sw x1, tohost, t5 + self_loop_2: j self_loop_2 + +success: + li x1, 0 + slli x1, x1, 1 + addi x1, x1, 1 + sw x1, tohost, t5 + self_loop: j self_loop + +push_pop: + cm.push {ra, s0-s1}, -16 + nop + cm.popret {ra, s0-s1}, 16 + +RVTEST_CODE_END + +.data + num1: .word 5 // First number + num2: .word 7 // Second number + result: .word 12 // Result + stack: .space 100 + +RVTEST_DATA_BEGIN + +RVTEST_DATA_END diff --git a/verif/tests/custom/Zcmp/cm_popretz_test.S b/verif/tests/custom/Zcmp/cm_popretz_test.S new file mode 100644 index 0000000000..4a5f4eb07b --- /dev/null +++ b/verif/tests/custom/Zcmp/cm_popretz_test.S @@ -0,0 +1,57 @@ +#include "riscv_test.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + //Initialize the stack + la sp, stack + + // Load the first number into register a0 + lw s0, num1 + + // Load the second number into register a1 + lw s1, num2 + + //Load the result into register a2 + lw a2, result + + //Two consecutive move instructions replaced by the compressed move instruction + //mv s0, a0 + //mv s1, a1 + + //Push the registers to stack + jal push_pop + + // Add the numbers and store the result in register t2 + add t2, s0, s1 + + // Store the result in the memory location reserved for it + beq t2, a2, success +failure: + li x1, 1 + slli x1, x1, 1 + addi x1, x1, 1 + sw x1, tohost, t5 + self_loop_2: j self_loop_2 + +success: + li x1, 0 + slli x1, x1, 1 + addi x1, x1, 1 + sw x1, tohost, t5 + self_loop: j self_loop + +push_pop: + cm.push {ra, s0-s1}, -16 + nop + cm.popretz {ra, s0-s1}, 16 +RVTEST_CODE_END + +.data + num1: .word 5 // First number + num2: .word 7 // Second number + result: .word 12 // Result + stack: .space 100 + +RVTEST_DATA_BEGIN + +RVTEST_DATA_END diff --git a/verif/tests/custom/Zcmp/cm_push_pop_test.S b/verif/tests/custom/Zcmp/cm_push_pop_test.S new file mode 100644 index 0000000000..28a0af8f08 --- /dev/null +++ b/verif/tests/custom/Zcmp/cm_push_pop_test.S @@ -0,0 +1,59 @@ +#include "riscv_test.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + //Initialize the stack + la sp, stack + + // Load the first number into register a0 + lw s0, num1 + + // Load the second number into register a1 + lw s1, num2 + + //Load the result into register a2 + lw a2, result + + //Two consecutive move instructions replaced by the compressed move instruction + //mv s0, a0 + //mv s1, a1 + + //Push the registers to stack + jal push_pop + + // Add the numbers and store the result in register t2 + add t2, s0, s1 + + // Store the result in the memory location reserved for it + beq t2, a2, success +failure: + li x1, 1 + slli x1, x1, 1 + addi x1, x1, 1 + sw x1, tohost, t5 + self_loop_2: j self_loop_2 + +success: + li x1, 0 + slli x1, x1, 1 + addi x1, x1, 1 + sw x1, tohost, t5 + self_loop: j self_loop + +push_pop: + cm.push {ra, s0-s1}, -16 + nop + cm.pop {ra, s0-s1}, 16 + jr ra + +RVTEST_CODE_END + +.data + num1: .word 5 // First number + num2: .word 7 // Second number + result: .word 12 // Result + stack: .space 100 + +RVTEST_DATA_BEGIN + +RVTEST_DATA_END diff --git a/verif/tests/custom/Zcmp/link.ld b/verif/tests/custom/Zcmp/link.ld new file mode 100644 index 0000000000..6135e79df0 --- /dev/null +++ b/verif/tests/custom/Zcmp/link.ld @@ -0,0 +1,66 @@ +/*======================================================================*/ +/* Proxy kernel linker script */ +/*======================================================================*/ +/* This is the linker script used when building the proxy kernel. */ + +/*----------------------------------------------------------------------*/ +/* Setup */ +/*----------------------------------------------------------------------*/ + +/* The OUTPUT_ARCH command specifies the machine architecture where the + argument is one of the names used in the BFD library. More + specifically one of the entires in bfd/cpu-mips.c */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +/*----------------------------------------------------------------------*/ +/* Sections */ +/*----------------------------------------------------------------------*/ + +SECTIONS +{ + + /* text: test code section */ + . = 0x80000000; + .text.init : { *(.text.init) } + + . = ALIGN(0x1000); + .tohost : { *(.tohost) } + + . = ALIGN(0x1000); + .text : { *(.text) } + + /* data segment */ + .data : { *(.data) } + + .sdata : { + __global_pointer$ = . + 0x800; + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + + /* bss segment */ + .sbss : { + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + } + .bss : { *(.bss) } + + /* thread-local data segment */ + .tdata : + { + _tdata_begin = .; + *(.tdata) + _tdata_end = .; + } + .tbss : + { + *(.tbss) + _tbss_end = .; + } + + /* End of uninitalized data segement */ + _end = .; +} + diff --git a/verif/tests/custom/Zcmp/readme.txt b/verif/tests/custom/Zcmp/readme.txt new file mode 100644 index 0000000000..fe08325bca --- /dev/null +++ b/verif/tests/custom/Zcmp/readme.txt @@ -0,0 +1,2 @@ +Use the following command to build the test binaries (example for cm_push_pop_test.S): + riscv32-corev-elf-gcc -static -mcmodel=medany -fvisibility=hidden -nostdlib -g syscalls.c -lgcc -Tlink.ld -march=rv32gc_zcmp -o cm_push_pop_test.elf cm_push_pop_test.S diff --git a/verif/tests/custom/Zcmp/riscv_test.h b/verif/tests/custom/Zcmp/riscv_test.h new file mode 100644 index 0000000000..91a2e2b785 --- /dev/null +++ b/verif/tests/custom/Zcmp/riscv_test.h @@ -0,0 +1,269 @@ +// See LICENSE for license details. + +#ifndef _ENV_PHYSICAL_SINGLE_CORE_H +#define _ENV_PHYSICAL_SINGLE_CORE_H + +#include "encoding.h" + +//----------------------------------------------------------------------- +// Begin Macro +//----------------------------------------------------------------------- + +#define RVTEST_RV64U \ + .macro init; \ + .endm + +#define RVTEST_RV64UF \ + .macro init; \ + RVTEST_FP_ENABLE; \ + .endm + +#define RVTEST_RV64UV \ + .macro init; \ + RVTEST_VECTOR_ENABLE; \ + .endm + +#define RVTEST_RV32U \ + .macro init; \ + .endm + +#define RVTEST_RV32UF \ + .macro init; \ + RVTEST_FP_ENABLE; \ + .endm + +#define RVTEST_RV32UV \ + .macro init; \ + RVTEST_VECTOR_ENABLE; \ + .endm + +#define RVTEST_RV64M \ + .macro init; \ + RVTEST_ENABLE_MACHINE; \ + .endm + +#define RVTEST_RV64S \ + .macro init; \ + RVTEST_ENABLE_SUPERVISOR; \ + .endm + +#define RVTEST_RV32M \ + .macro init; \ + RVTEST_ENABLE_MACHINE; \ + .endm + +#define RVTEST_RV32S \ + .macro init; \ + RVTEST_ENABLE_SUPERVISOR; \ + .endm + +#if __riscv_xlen == 64 +# define CHECK_XLEN li a0, 1; slli a0, a0, 31; bgez a0, 1f; RVTEST_PASS; 1: +#else +# define CHECK_XLEN li a0, 1; slli a0, a0, 31; bltz a0, 1f; RVTEST_PASS; 1: +#endif + +#define INIT_XREG \ + li x1, 0; \ + li x2, 0; \ + li x3, 0; \ + li x4, 0; \ + li x5, 0; \ + li x6, 0; \ + li x7, 0; \ + li x8, 0; \ + li x9, 0; \ + li x10, 0; \ + li x11, 0; \ + li x12, 0; \ + li x13, 0; \ + li x14, 0; \ + li x15, 0; \ + li x16, 0; \ + li x17, 0; \ + li x18, 0; \ + li x19, 0; \ + li x20, 0; \ + li x21, 0; \ + li x22, 0; \ + li x23, 0; \ + li x24, 0; \ + li x25, 0; \ + li x26, 0; \ + li x27, 0; \ + li x28, 0; \ + li x29, 0; \ + li x30, 0; \ + li x31, 0; + +#define INIT_PMP \ + la t0, 1f; \ + csrw mtvec, t0; \ + /* Set up a PMP to permit all accesses */ \ + li t0, (1 << (31 + (__riscv_xlen / 64) * (53 - 31))) - 1; \ + csrw pmpaddr0, t0; \ + li t0, PMP_NAPOT | PMP_R | PMP_W | PMP_X; \ + csrw pmpcfg0, t0; \ + .align 2; \ +1: + +#define INIT_SATP \ + la t0, 1f; \ + csrw mtvec, t0; \ + csrwi sptbr, 0; \ + .align 2; \ +1: + +#define DELEGATE_NO_TRAPS \ + la t0, 1f; \ + csrw mtvec, t0; \ + csrwi medeleg, 0; \ + csrwi mideleg, 0; \ + csrwi mie, 0; \ + .align 2; \ +1: + +#define RVTEST_ENABLE_SUPERVISOR \ + li a0, MSTATUS_MPP & (MSTATUS_MPP >> 1); \ + csrs mstatus, a0; \ + li a0, SIP_SSIP | SIP_STIP; \ + csrs mideleg, a0; \ + +#define RVTEST_ENABLE_MACHINE \ + li a0, MSTATUS_MPP; \ + csrs mstatus, a0; \ + +#define RVTEST_FP_ENABLE \ + li a0, MSTATUS_FS & (MSTATUS_FS >> 1); \ + csrs mstatus, a0; \ + csrwi fcsr, 0 + +#define RVTEST_VECTOR_ENABLE \ + li a0, (MSTATUS_VS & (MSTATUS_VS >> 1)) | \ + (MSTATUS_FS & (MSTATUS_FS >> 1)); \ + csrs mstatus, a0; \ + csrwi fcsr, 0; \ + csrwi vcsr, 0; + +#define RISCV_MULTICORE_DISABLE \ + csrr a0, mhartid; \ + 1: bnez a0, 1b + +#define EXTRA_TVEC_USER +#define EXTRA_TVEC_MACHINE +#define EXTRA_INIT +#define EXTRA_INIT_TIMER + +#define INTERRUPT_HANDLER j other_exception /* No interrupts should occur */ + +#define RVTEST_CODE_BEGIN \ + .section .text.init; \ + .align 6; \ + .weak stvec_handler; \ + .weak mtvec_handler; \ + .globl _start; \ +_start: \ + /* reset vector */ \ + j reset_vector; \ + .align 2; \ +trap_vector: \ + /* test whether the test came from pass/fail */ \ + csrr t5, mcause; \ + li t6, CAUSE_USER_ECALL; \ + beq t5, t6, write_tohost; \ + li t6, CAUSE_SUPERVISOR_ECALL; \ + beq t5, t6, write_tohost; \ + li t6, CAUSE_MACHINE_ECALL; \ + beq t5, t6, write_tohost; \ + /* if an mtvec_handler is defined, jump to it */ \ + la t5, mtvec_handler; \ + beqz t5, 1f; \ + jr t5; \ + /* was it an interrupt or an exception? */ \ + 1: csrr t5, mcause; \ + bgez t5, handle_exception; \ + INTERRUPT_HANDLER; \ +handle_exception: \ + /* we don't know how to handle whatever the exception was */ \ + other_exception: \ + /* some unhandlable exception occurred */ \ + 1: ori TESTNUM, TESTNUM, 1337; \ + write_tohost: \ + sw TESTNUM, tohost, t5; \ + j write_tohost; \ +reset_vector: \ + INIT_XREG; \ + RISCV_MULTICORE_DISABLE; \ + INIT_SATP; \ + INIT_PMP; \ + DELEGATE_NO_TRAPS; \ + li TESTNUM, 0; \ + la t0, trap_vector; \ + csrw mtvec, t0; \ + CHECK_XLEN; \ + /* if an stvec_handler is defined, delegate exceptions to it */ \ + la t0, stvec_handler; \ + beqz t0, 1f; \ + csrw stvec, t0; \ + li t0, (1 << CAUSE_LOAD_PAGE_FAULT) | \ + (1 << CAUSE_STORE_PAGE_FAULT) | \ + (1 << CAUSE_FETCH_PAGE_FAULT) | \ + (1 << CAUSE_MISALIGNED_FETCH) | \ + (1 << CAUSE_USER_ECALL) | \ + (1 << CAUSE_BREAKPOINT); \ + csrw medeleg, t0; \ +1: csrwi mstatus, 0; \ + init; \ + EXTRA_INIT; \ + EXTRA_INIT_TIMER; \ + la t0, 1f; \ + csrw mepc, t0; \ + csrr a0, mhartid; \ + mret; \ +1: + +//----------------------------------------------------------------------- +// End Macro +//----------------------------------------------------------------------- + +#define RVTEST_CODE_END \ + unimp + +//----------------------------------------------------------------------- +// Pass/Fail Macro +//----------------------------------------------------------------------- + +#define RVTEST_PASS \ + fence; \ + li TESTNUM, 1; \ + li a7, 93; \ + li a0, 0; \ + ecall + +#define TESTNUM gp +#define RVTEST_FAIL \ + fence; \ +1: beqz TESTNUM, 1b; \ + sll TESTNUM, TESTNUM, 1; \ + or TESTNUM, TESTNUM, 1; \ + li a7, 93; \ + addi a0, TESTNUM, 0; \ + ecall + +//----------------------------------------------------------------------- +// Data Section Macro +//----------------------------------------------------------------------- + +#define EXTRA_DATA + +#define RVTEST_DATA_BEGIN \ + EXTRA_DATA \ + .pushsection .tohost,"aw",@progbits; \ + .align 6; .global tohost; tohost: .dword 0; \ + .align 6; .global fromhost; fromhost: .dword 0; \ + .popsection; \ + .align 4; .global begin_signature; begin_signature: + +#define RVTEST_DATA_END .align 4; .global end_signature; end_signature: + +#endif diff --git a/verif/tests/custom/Zcmp/syscalls.c b/verif/tests/custom/Zcmp/syscalls.c new file mode 100644 index 0000000000..ebb9a87275 --- /dev/null +++ b/verif/tests/custom/Zcmp/syscalls.c @@ -0,0 +1,521 @@ +// See LICENSE for license details. + +#include +#include +#include +#include +#include +#include +#include "util.h" + +#define SYS_write 64 + +#undef strcmp + +extern volatile uint64_t tohost; +extern volatile uint64_t fromhost; + +// tohost is 64 bits wide, irrespective of XLEN. The structure expected in Spike is: +// - tohost[63:56] == device (syscall: 0) +// - tohost[55:48] == command (syscall: 0) +// - tohost[47:0] == payload (syscall: address of magic_mem) +// +// magic_mem for a syscall contains the following elements (XLEN bits each) +// - syscall index (93 dec for syscall_exit, cf. Spike values in +// riscv-isa-sim/fesvr/syscall.cc:140 and ff.) +// - syscall args in the declaration order of the given syscall + +static uintptr_t syscall(uintptr_t which, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2) +{ + // Arguments in magic_mem have XLEN bits each. + volatile uintptr_t magic_mem[8] __attribute__((aligned(64))); + magic_mem[0] = which; + magic_mem[1] = arg0; + magic_mem[2] = arg1; + magic_mem[3] = arg2; +#ifdef __riscv_atomic // __sync_synchronize requires A extension + __sync_synchronize(); +#endif + + // A WRITE_MEM transaction writing non-zero value to TOHOST triggers + // the environment (Spike or RTL harness). + // - here tohost is guaranteed non-NULL because magic_mem is a valid RISC-V + // pointer. + // - the environment acknowledges the env request by writing 0 into tohost. + // - the completion of the request is signalled by the environment through + // a write of a non-zero value into fromhost. + tohost = (((uint64_t) ((unsigned long int) magic_mem)) << 16) >> 16; // clear the DEV and CMD bytes, clip payload. + while (fromhost == 0) + ; + fromhost = 0; + +#ifdef __riscv_atomic // __sync_synchronize requires A extension + __sync_synchronize(); +#endif + return magic_mem[0]; +} + +#define NUM_COUNTERS 2 +static uintptr_t counters[NUM_COUNTERS]; +static char* counter_names[NUM_COUNTERS]; + +void setStats(int enable) +{ + int i = 0; +#define READ_CTR(name) do { \ + while (i >= NUM_COUNTERS) ; \ + uintptr_t csr = read_csr(name); \ + if (!enable) { csr -= counters[i]; counter_names[i] = #name; } \ + counters[i++] = csr; \ + } while (0) + + READ_CTR(mcycle); + READ_CTR(minstret); + +#undef READ_CTR +} + +uintptr_t getStats(int counterid) +{ + return counters[counterid]; +} + +void __attribute__((noreturn)) tohost_exit(uintptr_t code) +{ + // Simply write PASS/FAIL result into 'tohost'. + // Left shift 'code' by 1 and set bit 0 to 1, but leave the 16 uppermost bits clear + // so that the syscall is properly recognized even if 'code' value is very large. + tohost = ((((uint64_t) code) << 17) >> 16) | 1; + + // Do not care about the value returned by host. + // Leave 1 cycle of slack (one NOP instruction) to help debugging + // the termination mechanism if needed. + __asm__("nop\n\t"); + + // Go into an endless loop if the write into 'tohost' did not terminate the simulation. + while (1); +} + +uintptr_t __attribute__((weak)) handle_trap(uintptr_t cause, uintptr_t epc, uintptr_t regs[32]) +{ + tohost_exit(1337); +} + +void exit(int code) +{ + tohost_exit(code); +} + +void abort() +{ + exit(128 + SIGABRT); +} + +void printstr(const char* s) +{ +#if !NOPRINT + syscall(SYS_write, 1, (uintptr_t)s, strlen(s)); +#endif +} + +void __attribute__((weak)) thread_entry(int cid, int nc) +{ + // multi-threaded programs override this function. + // for the case of single-threaded programs, only let core 0 proceed. + while (cid != 0); +} + +int __attribute__((weak)) main(int argc, char** argv) +{ + // single-threaded programs override this function. + printstr("Implement main(), foo!\n"); + return -1; +} + +static void init_tls() +{ + register void* thread_pointer asm("tp"); + extern char _tdata_begin, _tdata_end, _tbss_end; + size_t tdata_size = &_tdata_end - &_tdata_begin; + memcpy(thread_pointer, &_tdata_begin, tdata_size); + size_t tbss_size = &_tbss_end - &_tdata_end; + memset(thread_pointer + tdata_size, 0, tbss_size); +} + +void _init(int cid, int nc) +{ + init_tls(); + thread_entry(cid, nc); + + // only single-threaded programs should ever get here. + int ret = main(0, 0); + + char buf[NUM_COUNTERS * 32] __attribute__((aligned(64))); + char* pbuf = buf; + for (int i = 0; i < NUM_COUNTERS; i++) + if (counters[i]) + pbuf += sprintf(pbuf, "%s = %d\n", counter_names[i], counters[i]); + if (pbuf != buf) + printstr(buf); + + exit(ret); +} + +int puts(const char *s) +{ + const char *p = s; + + while (*p) + putchar(*p++); + + putchar('\n'); + return 0; +} + +#undef putchar +int putchar(int ch) +{ +#if !NOPRINT + static __thread char buf[64] __attribute__((aligned(64))); + static __thread int buflen = 0; + + buf[buflen++] = ch; + + if (ch == '\n' || buflen == sizeof(buf)) + { + syscall(SYS_write, 1, (uintptr_t)buf, buflen); + buflen = 0; + } +#endif + + return 0; +} + +void printhex(uint64_t x) +{ + char str[17]; + int i; + for (i = 0; i < 16; i++) + { + str[15-i] = (x & 0xF) + ((x & 0xF) < 10 ? '0' : 'a'-10); + x >>= 4; + } + str[16] = 0; + + printstr(str); +} + +static inline void printnum(void (*putch)(int, void**), void **putdat, + unsigned long long num, unsigned base, int width, int padc) +{ + unsigned digs[sizeof(num)*CHAR_BIT]; + int pos = 0; + + while (1) + { + digs[pos++] = num % base; + if (num < base) + break; + num /= base; + } + + while (width-- > pos) + putch(padc, putdat); + + while (pos-- > 0) + putch(digs[pos] + (digs[pos] >= 10 ? 'a' - 10 : '0'), putdat); +} + +static unsigned long long getuint(va_list *ap, int lflag) +{ + if (lflag >= 2) + return va_arg(*ap, unsigned long long); + else if (lflag) + return va_arg(*ap, unsigned long); + else + return va_arg(*ap, unsigned int); +} + +static long long getint(va_list *ap, int lflag) +{ + if (lflag >= 2) + return va_arg(*ap, long long); + else if (lflag) + return va_arg(*ap, long); + else + return va_arg(*ap, int); +} + +static void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_list ap) +{ + register const char* p; + const char* last_fmt; + register int ch, err; + unsigned long long num; + int base, lflag, width, precision, altflag; + char padc; + + while (1) { + while ((ch = *(unsigned char *) fmt) != '%') { + if (ch == '\0') + return; + fmt++; + putch(ch, putdat); + } + fmt++; + + // Process a %-escape sequence + last_fmt = fmt; + padc = ' '; + width = -1; + precision = -1; + lflag = 0; + altflag = 0; + reswitch: + switch (ch = *(unsigned char *) fmt++) { + + // flag to pad on the right + case '-': + padc = '-'; + goto reswitch; + + // flag to pad with 0's instead of spaces + case '0': + padc = '0'; + goto reswitch; + + // width field + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + for (precision = 0; ; ++fmt) { + precision = precision * 10 + ch - '0'; + ch = *fmt; + if (ch < '0' || ch > '9') + break; + } + goto process_precision; + + case '*': + precision = va_arg(ap, int); + goto process_precision; + + case '.': + if (width < 0) + width = 0; + goto reswitch; + + case '#': + altflag = 1; + goto reswitch; + + process_precision: + if (width < 0) + width = precision, precision = -1; + goto reswitch; + + // long flag (doubled for long long) + case 'l': + lflag++; + goto reswitch; + + // character + case 'c': + putch(va_arg(ap, int), putdat); + break; + + // string + case 's': + if ((p = va_arg(ap, char *)) == NULL) + p = "(null)"; + if (width > 0 && padc != '-') + for (width -= strnlen(p, precision); width > 0; width--) + putch(padc, putdat); + for (; (ch = *p) != '\0' && (precision < 0 || --precision >= 0); width--) { + putch(ch, putdat); + p++; + } + for (; width > 0; width--) + putch(' ', putdat); + break; + + // (signed) decimal + case 'd': + num = getint(&ap, lflag); + if ((long long) num < 0) { + putch('-', putdat); + num = -(long long) num; + } + base = 10; + goto signed_number; + + // unsigned decimal + case 'u': + base = 10; + goto unsigned_number; + + // (unsigned) octal + case 'o': + // should do something with padding so it's always 3 octits + base = 8; + goto unsigned_number; + + // pointer + case 'p': + static_assert(sizeof(long) == sizeof(void*)); + lflag = 1; + putch('0', putdat); + putch('x', putdat); + /* fall through to 'x' */ + + // (unsigned) hexadecimal + case 'x': + base = 16; + unsigned_number: + num = getuint(&ap, lflag); + signed_number: + printnum(putch, putdat, num, base, width, padc); + break; + + // escaped '%' character + case '%': + putch(ch, putdat); + break; + + // unrecognized escape sequence - just print it literally + default: + putch('%', putdat); + fmt = last_fmt; + break; + } + } +} + +int printf(const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + vprintfmt((void*)putchar, 0, fmt, ap); + + va_end(ap); + return 0; // incorrect return value, but who cares, anyway? +} + +int sprintf(char* str, const char* fmt, ...) +{ + va_list ap; + char* str0 = str; + va_start(ap, fmt); + + void sprintf_putch(int ch, void** data) + { + char** pstr = (char**)data; + **pstr = ch; + (*pstr)++; + } + + vprintfmt(sprintf_putch, (void**)&str, fmt, ap); + *str = 0; + + va_end(ap); + return str - str0; +} + +void* memcpy(void* dest, const void* src, size_t len) +{ + if ((((uintptr_t)dest | (uintptr_t)src | len) & (sizeof(uintptr_t)-1)) == 0) { + const uintptr_t* s = src; + uintptr_t *d = dest; + while (d < (uintptr_t*)(dest + len)) + *d++ = *s++; + } else { + const char* s = src; + char *d = dest; + while (d < (char*)(dest + len)) + *d++ = *s++; + } + return dest; +} + +void* memset(void* dest, int byte, size_t len) +{ + if ((((uintptr_t)dest | len) & (sizeof(uintptr_t)-1)) == 0) { + uintptr_t word = byte & 0xFF; + word |= word << 8; + word |= word << 16; + word |= word << 16 << 16; + + uintptr_t *d = dest; + while (d < (uintptr_t*)(dest + len)) + *d++ = word; + } else { + char *d = dest; + while (d < (char*)(dest + len)) + *d++ = byte; + } + return dest; +} + +size_t strlen(const char *s) +{ + const char *p = s; + while (*p) + p++; + return p - s; +} + +size_t strnlen(const char *s, size_t n) +{ + const char *p = s; + while (n-- && *p) + p++; + return p - s; +} + +int strcmp(const char* s1, const char* s2) +{ + unsigned char c1, c2; + + do { + c1 = *s1++; + c2 = *s2++; + } while (c1 != 0 && c1 == c2); + + return c1 - c2; +} + +char* strcpy(char* dest, const char* src) +{ + char* d = dest; + while ((*d++ = *src++)) + ; + return dest; +} + +long atol(const char* str) +{ + long res = 0; + int sign = 0; + + while (*str == ' ') + str++; + + if (*str == '-' || *str == '+') { + sign = *str == '-'; + str++; + } + + while (*str) { + res *= 10; + res += *str++ - '0'; + } + + return sign ? -res : res; +} diff --git a/verif/tests/custom/Zcmp/util.h b/verif/tests/custom/Zcmp/util.h new file mode 100644 index 0000000000..78fa62401a --- /dev/null +++ b/verif/tests/custom/Zcmp/util.h @@ -0,0 +1,92 @@ +// See LICENSE for license details. + +#ifndef __UTIL_H +#define __UTIL_H + +extern void setStats(int enable); + +#include + +#define static_assert(cond) switch(0) { case 0: case !!(long)(cond): ; } + +static int verify(int n, const volatile int* test, const int* verify) +{ + int i; + // Unrolled for faster verification + for (i = 0; i < n/2*2; i+=2) + { + int t0 = test[i], t1 = test[i+1]; + int v0 = verify[i], v1 = verify[i+1]; + if (t0 != v0) return i+1; + if (t1 != v1) return i+2; + } + if (n % 2 != 0 && test[n-1] != verify[n-1]) + return n; + return 0; +} + +static int verifyDouble(int n, const volatile double* test, const double* verify) +{ + int i; + // Unrolled for faster verification + for (i = 0; i < n/2*2; i+=2) + { + double t0 = test[i], t1 = test[i+1]; + double v0 = verify[i], v1 = verify[i+1]; + int eq1 = t0 == v0, eq2 = t1 == v1; + if (!(eq1 & eq2)) return i+1+eq1; + } + if (n % 2 != 0 && test[n-1] != verify[n-1]) + return n; + return 0; +} + +static void __attribute__((noinline)) barrier(int ncores) +{ +#ifdef __riscv_atomic // __sync_* builtins require A extension + static volatile int sense; + static volatile int count; + static __thread int threadsense; + + __sync_synchronize(); + + threadsense = !threadsense; + if (__sync_fetch_and_add(&count, 1) == ncores-1) + { + count = 0; + sense = threadsense; + } + else while(sense != threadsense) + ; + + __sync_synchronize(); +#endif // __riscv_atomic +} + +static uint64_t lfsr(uint64_t x) +{ + uint64_t bit = (x ^ (x >> 1)) & 1; + return (x >> 1) | (bit << 62); +} + +static uintptr_t insn_len(uintptr_t pc) +{ + return (*(unsigned short*)pc & 3) ? 4 : 2; +} + +#ifdef __riscv +#include "encoding.h" +#endif + +#define stringify_1(s) #s +#define stringify(s) stringify_1(s) +#define stats(code, iter) do { \ + unsigned long _c = -read_csr(mcycle), _i = -read_csr(minstret); \ + code; \ + _c += read_csr(mcycle), _i += read_csr(minstret); \ + if (cid == 0) \ + printf("\n%s: %ld cycles, %ld.%ld cycles/iter, %ld.%ld CPI\n", \ + stringify(code), _c, _c/iter, 10*_c/iter%10, _c/_i, 10*_c/_i%10); \ + } while(0) + +#endif //__UTIL_H