From 0e19699e6d7ad10e8bc784f0309e3a0a93775074 Mon Sep 17 00:00:00 2001 From: Jonathan Jonsson Date: Mon, 30 Jan 2023 16:15:30 +0100 Subject: [PATCH 1/5] Add the Zcb extension This commit is a cherry-pick of only the Zcb extension from Jonathan Jonssons's commit: 5e72708e9e517ff48e249a2cd3c6f3f050c73330 Zcb (code reduction): since no gcc support is in the mainline assembler yet these instructions are injected as raw data. Coverage class has been extended with these instructions and the illegal instruction class should not be able to generate legal Zcb instruction if the extension is enabled. load_store_instr_lib: Added the Zcb load and store instructions to this class. Co-authored-by: Samuel Riedel " --- src/isa/riscv_instr.sv | 2 +- src/isa/riscv_instr_cov.svh | 16 +++ src/isa/riscv_zcb_instr.sv | 231 ++++++++++++++++++++++++++++++ src/isa/rv32zcb_instr.sv | 29 ++++ src/isa/rv64zcb_instr.sv | 18 +++ src/riscv_asm_program_gen.sv | 1 + src/riscv_defines.svh | 5 + src/riscv_directed_instr_lib.sv | 2 +- src/riscv_illegal_instr.sv | 31 ++++ src/riscv_instr_cover_group.sv | 82 ++++++++++- src/riscv_instr_gen_config.sv | 8 ++ src/riscv_instr_pkg.sv | 26 ++++ src/riscv_load_store_instr_lib.sv | 16 ++- test/riscv_instr_cov_test.sv | 4 +- 14 files changed, 464 insertions(+), 7 deletions(-) create mode 100644 src/isa/riscv_zcb_instr.sv create mode 100644 src/isa/rv32zcb_instr.sv create mode 100644 src/isa/rv64zcb_instr.sv diff --git a/src/isa/riscv_instr.sv b/src/isa/riscv_instr.sv index f44e972a2..8ef885e5a 100644 --- a/src/isa/riscv_instr.sv +++ b/src/isa/riscv_instr.sv @@ -111,7 +111,7 @@ class riscv_instr extends uvm_object; if (cfg.no_fence && (instr_name inside {FENCE, FENCE_I, SFENCE_VMA})) continue; if ((instr_inst.group inside {supported_isa}) && !(cfg.disable_compressed_instr && - (instr_inst.group inside {RV32C, RV64C, RV32DC, RV32FC, RV128C})) && + (instr_inst.group inside {RV32C, RV64C, RV32DC, RV32FC, RV128C, RV32ZCB, RV64ZCB})) && !(!cfg.enable_floating_point && (instr_inst.group inside {RV32F, RV64F, RV32D, RV64D})) && !(!cfg.enable_vector_extension && diff --git a/src/isa/riscv_instr_cov.svh b/src/isa/riscv_instr_cov.svh index 6819f3836..c5e344111 100644 --- a/src/isa/riscv_instr_cov.svh +++ b/src/isa/riscv_instr_cov.svh @@ -408,6 +408,22 @@ // c.j imm get_val(operands[0], imm); end + CSZN_FORMAT: begin + rs1 = get_gpr(operands[0]); + rs1_value = get_gpr_state(operands[0]); + end + CLB_FORMAT, CLH_FORMAT: begin + get_val(operands[2], imm); + rs1 = get_gpr(operands[1]); + rs1_value = get_gpr_state(operands[1]); + end + CSB_FORMAT, CSH_FORMAT: begin + rs2 = get_gpr(operands[0]); + rs2_value = get_gpr_state(operands[0]); + get_val(operands[2], imm); + rs1 = get_gpr(operands[1]); + rs1_value = get_gpr_state(operands[1]); + end default: `uvm_fatal(`gfn, $sformatf("Unsupported format %0s", format)) endcase endfunction : update_src_regs diff --git a/src/isa/riscv_zcb_instr.sv b/src/isa/riscv_zcb_instr.sv new file mode 100644 index 000000000..5cdf69817 --- /dev/null +++ b/src/isa/riscv_zcb_instr.sv @@ -0,0 +1,231 @@ +/* + * Copyright 2020 Google LLC + * Copyright 2023 Frontgrade Gaisler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the 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. + */ + +class riscv_zcb_instr extends riscv_instr; + + // This code is based on the frozen v.1.0.1 code reduction specification. + // Most of the code is copied from the riscv_compressed_instr class. + // gcc_support is used to insert instructions as raw data rather than going + // through the assembler. The reason being that GCC doesn't have support for + // zcb in the major releases yet. Remove the flag once it is supported. + int gcc_support = 0; // TODO: We have GCC support now. + + constraint rvc_csr_c { + // Registers specified by the three-bit rs1’, rs2’, and rd/rs1’ + if (format inside {CLB_FORMAT, CSB_FORMAT, CLH_FORMAT, CSH_FORMAT, CSZN_FORMAT, CA_FORMAT}) { + // reuse rs1 for special rd'/rs1' randomize_gpr function makes sure + // a reserved register is not used. + if (has_rs1) { + rs1 inside {[S0:A5]}; + } + if (has_rs2) { + rs2 inside {[S0:A5]}; + } + if (has_rd) { + rd inside {[S0:A5]}; + } + } + } + + `uvm_object_utils(riscv_zcb_instr) + + function new(string name = ""); + super.new(name); + rs1 = S0; + rs2 = S0; + rd = S0; + is_compressed = 1'b1; + endfunction : new + + virtual function void set_imm_len(); + if (format inside {CLB_FORMAT, CSB_FORMAT}) begin + imm_len = 2; + end else if (format inside {CLH_FORMAT, CSH_FORMAT}) begin + imm_len = 1; + end + endfunction : set_imm_len + + virtual function void set_rand_mode(); + case (format) inside + CLB_FORMAT : begin + has_rs2 = 1'b0; + end + C_SB : begin + has_rd = 1'b0; + end + C_LHU, C_LH : begin + has_rs2 = 1'b0; + end + C_SH : begin + has_rd = 1'b0; + end + C_ZEXT_B, C_ZEXT_H, C_SEXT_B, C_SEXT_H, C_ZEXT_W : begin + // Similar to CB format, where rs/rd is shared. When randomizing GPRs + // don't allow rd in rs1. + has_rd = 1'b0; + has_rs2 = 1'b0; + has_imm = 1'b0; + end + CA_FORMAT : begin + has_rs1 = 1'b0; + has_imm = 1'b0; + end + default: `uvm_info(`gfn, $sformatf("Unsupported format %0s", format.name()), UVM_LOW) + endcase + endfunction + + // Convert the instruction to assembly code + virtual function string convert2asm(string prefix = ""); + string asm_str; + asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN); + case(format) + CLB_FORMAT, CLH_FORMAT: + asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rd.name(), get_imm(), rs1.name()); + CSB_FORMAT, CSH_FORMAT: + asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rs2.name(), get_imm(), rs1.name()); + CSZN_FORMAT: + asm_str = $sformatf("%0s%0s", asm_str, rs1.name); + CA_FORMAT: + asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), rs2.name()); + default: `uvm_info(`gfn, $sformatf("Unsupported format %0s", format.name()), UVM_LOW) + endcase + + if (!gcc_support) begin + asm_str = {convert2bin(".2byte "), " #zcb until official GCC support: ", asm_str}; + end + + if (comment != "") + asm_str = {asm_str, " #",comment}; + return asm_str.tolower(); + endfunction : convert2asm + + // Convert the instruction to assembly code + virtual function string convert2bin(string prefix = ""); + string binary; + case (instr_name) inside + C_LBU: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rs1), imm[0], imm[1], + get_c_gpr(rd), get_c_opcode()}); + C_LHU: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rs1), 1'b0, imm[1], + get_c_gpr(rd), get_c_opcode()}); + C_LH: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rs1), 1'b1, imm[1], + get_c_gpr(rd), get_c_opcode()}); + C_SB: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rs1), imm[0], imm[1], + get_c_gpr(rs2), get_c_opcode()}); + C_SH: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rs1), 1'b0, imm[1], + get_c_gpr(rs2), get_c_opcode()}); + C_ZEXT_B: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11000, + get_c_opcode()}); + C_SEXT_B: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11001, + get_c_opcode()}); + C_ZEXT_H: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11010, + get_c_opcode()}); + C_SEXT_H: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11011, + get_c_opcode()}); + C_ZEXT_W: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11100, + get_c_opcode()}); + C_NOT: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11101, + get_c_opcode()}); + C_MUL: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 2'b10, + get_c_gpr(rs2), get_c_opcode()}); + default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name())) + endcase + return {prefix, binary}; + endfunction : convert2bin + + // Get opcode for zcb instruction + virtual function bit [1:0] get_c_opcode(); + case (instr_name) inside + C_ZEXT_B, C_SEXT_B, C_ZEXT_H, C_SEXT_H, + C_ZEXT_W, C_NOT, C_MUL : get_c_opcode = 2'b01; + C_LBU, C_LHU, C_LH, C_SB, C_SH : get_c_opcode = 2'b00; + default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name())) + endcase + endfunction : get_c_opcode + + virtual function bit [5:0] get_func6(); + case (instr_name) + C_LBU: get_func6 = 6'b100000; + C_LHU: get_func6 = 6'b100001; + C_LH: get_func6 = 6'b100001; + C_SB: get_func6 = 6'b100010; + C_SH: get_func6 = 6'b100011; + C_ZEXT_B: get_func6 = 6'b100011; + C_SEXT_B: get_func6 = 6'b100111; + C_ZEXT_H: get_func6 = 6'b100111; + C_SEXT_H: get_func6 = 6'b100111; + C_ZEXT_W: get_func6 = 6'b100111; + C_NOT: get_func6 = 6'b100111; + C_MUL: get_func6 = 6'b100111; + default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name())) + endcase + endfunction : get_func6 + + virtual function bit is_supported(riscv_instr_gen_config cfg); + return (cfg.enable_zcb_extension && + // RV32C, RV32Zbb, RV32Zba, M/Zmmul is prerequisites for this extension + (RV32ZBB inside {supported_isa}) && + (RV32C inside {supported_isa}) && + (RV32ZBA inside {supported_isa}) && + ((RV32M inside {supported_isa}) || (RV32ZMMUL inside {supported_isa})) && + ((RV32ZCB inside {supported_isa} || RV64ZCB inside {supported_isa})) && + instr_name inside { + C_ZEXT_B, C_SEXT_B, C_ZEXT_H, C_SEXT_H, C_ZEXT_W, + C_NOT, C_MUL, C_LBU, C_LHU, C_LH, C_SB, C_SH + }); + endfunction : is_supported + + // For coverage + virtual function void update_src_regs(string operands[$]); + case(format) + CSZN_FORMAT: begin + rs1 = get_gpr(operands[0]); + rs1_value = get_gpr_state(operands[0]); + end + CLB_FORMAT, CLH_FORMAT: begin + get_val(operands[2], imm); + rs1 = get_gpr(operands[1]); + rs1_value = get_gpr_state(operands[1]); + end + CSB_FORMAT, CSH_FORMAT: begin + rs2 = get_gpr(operands[0]); + rs2_value = get_gpr_state(operands[0]); + get_val(operands[2], imm); + rs1 = get_gpr(operands[1]); + rs1_value = get_gpr_state(operands[1]); + end + CSZN_FORMAT: begin + rs1 = get_gpr(operands[0]); + rs1_value = get_gpr_state(operands[0]); + end + default: ; + endcase + super.update_src_regs(operands); + endfunction : update_src_regs + +endclass : riscv_zcb_instr; diff --git a/src/isa/rv32zcb_instr.sv b/src/isa/rv32zcb_instr.sv new file mode 100644 index 000000000..eafe7cfea --- /dev/null +++ b/src/isa/rv32zcb_instr.sv @@ -0,0 +1,29 @@ +/* + * Copyright 2020 Google LLC + * Copyright 2023 Frontgrade Gaisler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the 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. + */ + +`DEFINE_ZCB_INSTR(C_LBU, CLB_FORMAT, LOAD, RV32ZCB, UIMM) +`DEFINE_ZCB_INSTR(C_LHU, CLH_FORMAT, LOAD, RV32ZCB, UIMM) +`DEFINE_ZCB_INSTR(C_LH, CLH_FORMAT, LOAD, RV32ZCB, UIMM) +`DEFINE_ZCB_INSTR(C_SB, CSB_FORMAT, STORE, RV32ZCB, UIMM) +`DEFINE_ZCB_INSTR(C_SH, CSH_FORMAT, STORE, RV32ZCB, UIMM) +`DEFINE_ZCB_INSTR(C_ZEXT_B, CSZN_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_SEXT_B, CSZN_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_ZEXT_H, CSZN_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_SEXT_H, CSZN_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_NOT, CSZN_FORMAT, LOGICAL, RV32ZCB) +`DEFINE_ZCB_INSTR(C_MUL, CA_FORMAT, ARITHMETIC, RV32ZCB) + diff --git a/src/isa/rv64zcb_instr.sv b/src/isa/rv64zcb_instr.sv new file mode 100644 index 000000000..5ec9b5103 --- /dev/null +++ b/src/isa/rv64zcb_instr.sv @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Google LLC + * Copyright 2023 Frontgrade Gaisler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the 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. + */ + +`DEFINE_ZCB_INSTR(C_ZEXT_W, CSZN_FORMAT, ARITHMETIC, RV64ZCB) diff --git a/src/riscv_asm_program_gen.sv b/src/riscv_asm_program_gen.sv index 4c969241a..ed3be75d8 100644 --- a/src/riscv_asm_program_gen.sv +++ b/src/riscv_asm_program_gen.sv @@ -459,6 +459,7 @@ class riscv_asm_program_gen extends uvm_object; RV32X, RV64X : misa[MISA_EXT_X] = 1'b1; RV32ZBA, RV32ZBB, RV32ZBC, RV32ZBS, RV64ZBA, RV64ZBB, RV64ZBC, RV64ZBS : ; // No Misa bit for Zb* extensions + RV32ZCB, RV64ZCB : ; // No Misa bit for Zc* extensions default : `uvm_fatal(`gfn, $sformatf("%0s is not yet supported", supported_isa[i].name())) endcase diff --git a/src/riscv_defines.svh b/src/riscv_defines.svh index 4d078e164..b6cb34efb 100644 --- a/src/riscv_defines.svh +++ b/src/riscv_defines.svh @@ -129,3 +129,8 @@ `define DEFINE_ZBS_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \ class riscv_``instr_n``_instr extends riscv_zbs_instr; \ `INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp) + +//Zcb-extension instruction +`define DEFINE_ZCB_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \ + class riscv_``instr_n``_instr extends riscv_zcb_instr; \ + `INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp) diff --git a/src/riscv_directed_instr_lib.sv b/src/riscv_directed_instr_lib.sv index f3bc7080a..bafb1bca2 100644 --- a/src/riscv_directed_instr_lib.sv +++ b/src/riscv_directed_instr_lib.sv @@ -474,7 +474,7 @@ class riscv_int_numeric_corner_stream extends riscv_directed_instr_stream; for (int i = 0; i < num_of_instr; i++) begin riscv_instr instr = riscv_instr::get_rand_instr( .include_category({ARITHMETIC}), - .exclude_group({RV32C, RV64C, RV32F, RV64F, RV32D, RV64D})); + .exclude_group({RV32C, RV64C, RV32ZCB, RV64ZCB, RV32F, RV64F, RV32D, RV64D})); randomize_gpr(instr); instr_list.push_back(instr); end diff --git a/src/riscv_illegal_instr.sv b/src/riscv_illegal_instr.sv index 60ff4b546..48233358b 100644 --- a/src/riscv_illegal_instr.sv +++ b/src/riscv_illegal_instr.sv @@ -213,6 +213,37 @@ class riscv_illegal_instr extends uvm_object; } } + constraint zcb_extension_c { + // zcb adds instructions to the reserved funct6 space 6'b100111 + // this constraint defines the subset of that op-code space which is still + // reserved. Only way to trigger this constraint should be with + // kReservedC0/C1 or by kIllegalCompressedOpcode. Since the reserved + // instructions at c_msb = 3'b100 && c_op = 2'b00 are not added to + // legal_c00_opcode this combination could occur. + if (RV32ZCB inside {supported_isa}) { + if (exception inside {kIllegalCompressedOpcode, kReservedCompressedInstr} + && c_msb == 3'b100) { + if (c_op == 2'b00) { + !(instr_bin[12:10] inside {3'b000, 3'b001, 3'b010, 3'b011}); //load/store + // bits 9:2 don't have any inavlid combinations, hence only bit 12:10 + // can yield an op-code which is reserved. + } else if (c_op == 2'b01 && instr_bin[12:10] == 3'b111) { + // if funct2 = 2'b10 there are no reserved op-codes + !(instr_bin[6:5] inside {2'b10}); //funct2 + if (instr_bin[6:5] == 2'b11) { + if (XLEN == 32){ + // c.zext.w not allowed in 32-bit mode + (instr_bin[4:2] inside {3'b100, 3'b110, 3'b111}); + } else { + // reserved opcodes + (instr_bin[4:2] inside {3'b110, 3'b111}); + } + } + } + } + } + } + constraint illegal_compressed_op_c { if (exception == kIllegalCompressedOpcode) { c_op != 2'b01; diff --git a/src/riscv_instr_cover_group.sv b/src/riscv_instr_cover_group.sv index badc423d6..1b6521216 100644 --- a/src/riscv_instr_cover_group.sv +++ b/src/riscv_instr_cover_group.sv @@ -38,6 +38,7 @@ `define SAMPLE_ZBB(cg, val) `SAMPLE_W_TYPE(cg, val, riscv_zbb_instr) `define SAMPLE_ZBC(cg, val) `SAMPLE_W_TYPE(cg, val, riscv_zbc_instr) `define SAMPLE_ZBS(cg, val) `SAMPLE_W_TYPE(cg, val, riscv_zbs_instr) +`define SAMPLE_ZCB(cg, val) `SAMPLE_W_TYPE(cg, val, riscv_zcb_instr) `define INSTR_CG_BEGIN(INSTR_NAME, INSTR_CLASS = riscv_instr) \ covergroup ``INSTR_NAME``_cg with function sample(INSTR_CLASS instr); @@ -433,6 +434,13 @@ cp_rd : coverpoint instr.rd; \ `DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;) \ +`define ZCB_I_INSTR_CG_BEGIN(INSTR_NAME) \ + `INSTR_CG_BEGIN(INSTR_NAME) \ + cp_rs1 : coverpoint instr.rs1 { \ + bins gpr[] = {S0, S1, A0, A1, A2, A3, A4, A5}; \ + } \ + `DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;) + `define B_I_INSTR_CG_BEGIN(INSTR_NAME) \ `INSTR_CG_BEGIN(INSTR_NAME, riscv_b_instr) \ cp_rs1 : coverpoint instr.rs1; \ @@ -1646,6 +1654,44 @@ class riscv_instr_cover_group; `CA_INSTR_CG_BEGIN(c_addw) `CG_END + // RV32ZCB + `CL_INSTR_CG_BEGIN(c_lbu) + `CG_END + + `CL_INSTR_CG_BEGIN(c_lhu) + `CG_END + + `CL_INSTR_CG_BEGIN(c_lh) + `CG_END + + `CS_INSTR_CG_BEGIN(c_sb) + `CG_END + + `CS_INSTR_CG_BEGIN(c_sh) + `CG_END + + // Similar to zbb with the exception of which registers can be used + `ZCB_I_INSTR_CG_BEGIN(c_zext_b) + `CG_END + + `ZCB_I_INSTR_CG_BEGIN(c_sext_b) + `CG_END + + `ZCB_I_INSTR_CG_BEGIN(c_zext_h) + `CG_END + + `ZCB_I_INSTR_CG_BEGIN(c_sext_h) + `CG_END + + `ZCB_I_INSTR_CG_BEGIN(c_not) + `CG_END + + `CA_INSTR_CG_BEGIN(c_mul) + `CG_END + + // RV64ZCB + `ZCB_I_INSTR_CG_BEGIN(c_zext_w) + `CG_END `INSTR_CG_BEGIN(hint) cp_hint : coverpoint instr.binary[15:0] { wildcard bins addi = {16'b0000_1xxx_x000_0001, @@ -2071,6 +2117,20 @@ class riscv_instr_cover_group; bseti_cg = new(); `CG_SELECTOR_END + `CG_SELECTOR_BEGIN(RV32ZCB) + c_lbu_cg = new(); + c_lhu_cg = new(); + c_lh_cg = new(); + c_sb_cg = new(); + c_sh_cg = new(); + c_zext_b_cg = new(); + c_sext_b_cg = new(); + c_zext_h_cg = new(); + c_sext_h_cg = new(); + c_not_cg = new(); + c_mul_cg = new(); + `CG_SELECTOR_END + `CG_SELECTOR_BEGIN(RV32B) pack_cg = new(); packh_cg = new(); @@ -2118,6 +2178,10 @@ class riscv_instr_cover_group; roriw_cg = new(); `CG_SELECTOR_END + `CG_SELECTOR_BEGIN(RV64ZCB) + c_zext_w_cg = new(); + `CG_SELECTOR_END + `CG_SELECTOR_BEGIN(RV64B) packw_cg = new(); packuw_cg = new(); @@ -2396,6 +2460,18 @@ class riscv_instr_cover_group; BINVI : `SAMPLE_ZBS(binvi_cg, instr) BSET : `SAMPLE_ZBS(bset_cg, instr) BSETI : `SAMPLE_ZBS(bseti_cg, instr) + // RV32ZCB + C_LBU : `SAMPLE_ZCB(c_lbu_cg, instr) + C_LHU : `SAMPLE_ZCB(c_lhu_cg, instr) + C_LH : `SAMPLE_ZCB(c_lh_cg, instr) + C_SB : `SAMPLE_ZCB(c_sb_cg, instr) + C_SH : `SAMPLE_ZCB(c_sh_cg, instr) + C_ZEXT_B : `SAMPLE_ZCB(c_zext_b_cg, instr) + C_SEXT_B : `SAMPLE_ZCB(c_sext_b_cg, instr) + C_ZEXT_H : `SAMPLE_ZCB(c_zext_h_cg, instr) + C_SEXT_H : `SAMPLE_ZCB(c_sext_h_cg, instr) + C_NOT : `SAMPLE_ZCB(c_not_cg, instr) + C_MUL : `SAMPLE_ZCB(c_mul_cg, instr) // RV32B PACK : `SAMPLE_B(pack_cg, instr) PACKH : `SAMPLE_B(packh_cg, instr) @@ -2438,6 +2514,8 @@ class riscv_instr_cover_group; ROLW : `SAMPLE_ZBB(rolw_cg, instr) RORW : `SAMPLE_ZBB(rorw_cg, instr) RORIW : `SAMPLE_ZBB(roriw_cg, instr) + // RV64ZCB + C_ZEXT_W : `SAMPLE_ZCB(c_zext_w_cg, instr) // RV64B PACKW : `SAMPLE_B(packw_cg, instr) PACKUW : `SAMPLE_B(packuw_cg, instr) @@ -2544,8 +2622,8 @@ class riscv_instr_cover_group; if ((instr.group inside {supported_isa}) && (instr.group inside {RV32I, RV32M, RV64M, RV64I, RV32C, RV64C, RVV, RV64B, RV32B, - RV32ZBA, RV32ZBB, RV32ZBC, RV32ZBS, - RV64ZBA, RV64ZBB, RV64ZBC, RV64ZBS})) begin + RV32ZBA, RV32ZBB, RV32ZBC, RV32ZBS, RV32ZCB, + RV64ZBA, RV64ZBB, RV64ZBC, RV64ZBS, RV64ZCB})) begin if (((instr_name inside {URET}) && !support_umode_trap) || ((instr_name inside {SRET, SFENCE_VMA}) && !(SUPERVISOR_MODE inside {supported_privileged_mode})) || diff --git a/src/riscv_instr_gen_config.sv b/src/riscv_instr_gen_config.sv index 0712821e5..b5b8e73b9 100644 --- a/src/riscv_instr_gen_config.sv +++ b/src/riscv_instr_gen_config.sv @@ -264,6 +264,7 @@ class riscv_instr_gen_config extends uvm_object; bit enable_zbb_extension; bit enable_zbc_extension; bit enable_zbs_extension; + bit enable_zcb_extension; b_ext_group_t enable_bitmanip_groups[] = {ZBB, ZBS, ZBP, ZBE, ZBF, ZBC, ZBR, ZBM, ZBT, ZB_TMP}; @@ -541,6 +542,7 @@ class riscv_instr_gen_config extends uvm_object; `uvm_field_int(enable_zbb_extension, UVM_DEFAULT) `uvm_field_int(enable_zbc_extension, UVM_DEFAULT) `uvm_field_int(enable_zbs_extension, UVM_DEFAULT) + `uvm_field_int(enable_zcb_extension, UVM_DEFAULT) `uvm_field_int(use_push_data_section, UVM_DEFAULT) `uvm_object_utils_end @@ -611,6 +613,7 @@ class riscv_instr_gen_config extends uvm_object; get_bool_arg_value("+enable_zbb_extension=", enable_zbb_extension); get_bool_arg_value("+enable_zbc_extension=", enable_zbc_extension); get_bool_arg_value("+enable_zbs_extension=", enable_zbs_extension); + get_bool_arg_value("+enable_zcb_extension=", enable_zcb_extension); cmdline_enum_processor #(b_ext_group_t)::get_array_values("+enable_bitmanip_groups=", 1'b0, enable_bitmanip_groups); if(inst.get_arg_value("+boot_mode=", boot_mode_opts)) begin @@ -657,6 +660,11 @@ class riscv_instr_gen_config extends uvm_object; enable_zbs_extension = 0; end + + if (!((RV32ZCB inside {supported_isa}) || + (RV64ZCB inside {supported_isa}))) begin + enable_zcb_extension = 0; + end vector_cfg = riscv_vector_cfg::type_id::create("vector_cfg"); pmp_cfg = riscv_pmp_cfg::type_id::create("pmp_cfg"); pmp_cfg.rand_mode(pmp_cfg.pmp_randomize); diff --git a/src/riscv_instr_pkg.sv b/src/riscv_instr_pkg.sv index 96207667a..1ba75cc55 100644 --- a/src/riscv_instr_pkg.sv +++ b/src/riscv_instr_pkg.sv @@ -108,6 +108,8 @@ package riscv_instr_pkg; RV64ZBB, RV64ZBC, RV64ZBS, + RV32ZCB, + RV64ZCB, RV32X, RV64X } riscv_instr_group_t; @@ -271,6 +273,20 @@ package riscv_instr_pkg; PACKW, PACKUW, XPERM_W, + // RV32ZCB + C_LBU, + C_LHU, + C_LH, + C_SB, + C_SH, + C_ZEXT_B, + C_SEXT_B, + C_ZEXT_H, + C_SEXT_H, + C_NOT, + C_MUL, + // RV64ZCB instructions + C_ZEXT_W, // RV32M instructions MUL, MULH, @@ -693,6 +709,12 @@ package riscv_instr_pkg; CS_FORMAT, CSS_FORMAT, CIW_FORMAT, + // Zc compressed instruction format + CLB_FORMAT, + CSB_FORMAT, + CLH_FORMAT, + CSH_FORMAT, + CSZN_FORMAT, // Vector instruction format VSET_FORMAT, VA_FORMAT, @@ -1532,6 +1554,7 @@ package riscv_instr_pkg; typedef class riscv_zbb_instr; typedef class riscv_zbc_instr; typedef class riscv_zbs_instr; + typedef class riscv_zcb_instr; typedef class riscv_b_instr; `include "riscv_instr_gen_config.sv" `include "isa/riscv_instr.sv" @@ -1557,6 +1580,9 @@ package riscv_instr_pkg; `include "isa/rv32zbb_instr.sv" `include "isa/rv32zbc_instr.sv" `include "isa/rv32zbs_instr.sv" + `include "isa/riscv_zcb_instr.sv" + `include "isa/rv32zcb_instr.sv" + `include "isa/rv64zcb_instr.sv" `include "isa/rv32m_instr.sv" `include "isa/rv64a_instr.sv" `include "isa/rv64b_instr.sv" diff --git a/src/riscv_load_store_instr_lib.sv b/src/riscv_load_store_instr_lib.sv index cae087313..d822fd010 100644 --- a/src/riscv_load_store_instr_lib.sv +++ b/src/riscv_load_store_instr_lib.sv @@ -119,19 +119,33 @@ class riscv_load_store_base_instr_stream extends riscv_mem_access_stream; // Generate each load/store instruction virtual function void gen_load_store_instr(); - bit enable_compressed_load_store; + bit enable_compressed_load_store, enable_zcb; riscv_instr instr; randomize_avail_regs(); if ((rs1_reg inside {[S0 : A5], SP}) && !cfg.disable_compressed_instr) begin enable_compressed_load_store = 1; end + if ((RV32C inside {riscv_instr_pkg::supported_isa}) && + (RV32ZCB inside {riscv_instr_pkg::supported_isa} && cfg.enable_zcb_extension)) begin + enable_zcb = 1; + end foreach (addr[i]) begin // Assign the allowed load/store instructions based on address alignment // This is done separately rather than a constraint to improve the randomization performance allowed_instr = {LB, LBU, SB}; + if((offset[i] inside {[0:2]}) && enable_compressed_load_store && + enable_zcb && rs1_reg != SP) begin + `uvm_info(`gfn, "Add ZCB byte load/store to allowed instr", UVM_LOW) + allowed_instr = {C_LBU, C_SB}; + end if (!cfg.enable_unaligned_load_store) begin if (addr[i][0] == 1'b0) begin allowed_instr = {LH, LHU, SH, allowed_instr}; + if(((offset[i] == 0) || (offset[i] == 2)) && enable_compressed_load_store && + enable_zcb && rs1_reg != SP) begin + `uvm_info(`gfn, "Add ZCB half-word load/store to allowed instr", UVM_LOW) + allowed_instr = {C_LHU, C_LH, C_SH}; + end end if (addr[i] % 4 == 0) begin allowed_instr = {LW, SW, allowed_instr}; diff --git a/test/riscv_instr_cov_test.sv b/test/riscv_instr_cov_test.sv index 8d3ceff89..b8f0370c5 100644 --- a/test/riscv_instr_cov_test.sv +++ b/test/riscv_instr_cov_test.sv @@ -133,8 +133,8 @@ class riscv_instr_cov_test extends uvm_test; instr = riscv_instr::get_instr(instr_name); if ((instr.group inside {RV32I, RV32M, RV32C, RV64I, RV64M, RV64C, RV32F, RV64F, RV32D, RV64D, RV32B, RV64B, - RV32ZBA, RV32ZBB, RV32ZBC, RV32ZBS, - RV64ZBA, RV64ZBB, RV64ZBC, RV64ZBS}) && + RV32ZBA, RV32ZBB, RV32ZBC, RV32ZBS, RV32ZCB, + RV64ZBA, RV64ZBB, RV64ZBC, RV64ZBS, RV64ZCB}) && (instr.group inside {supported_isa})) begin assign_trace_info_to_instr(instr); instr.pre_sample(); From 25e427aef027cb092ac25376860f0adb7f1b7622 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Thu, 24 Jul 2025 15:03:23 +0000 Subject: [PATCH 2/5] Define the Zmmul extension as a possible prerequisite for Zcb --- src/riscv_instr_pkg.sv | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/riscv_instr_pkg.sv b/src/riscv_instr_pkg.sv index 1ba75cc55..e3fcc4a8e 100644 --- a/src/riscv_instr_pkg.sv +++ b/src/riscv_instr_pkg.sv @@ -80,7 +80,7 @@ package riscv_instr_pkg; MACHINE_MODE = 2'b11 } privileged_mode_t; - typedef enum bit [4:0] { + typedef enum bit [5:0] { RV32I, RV64I, RV32M, @@ -109,7 +109,9 @@ package riscv_instr_pkg; RV64ZBC, RV64ZBS, RV32ZCB, - RV64ZCB, + RV64ZCB, + RV32ZMMUL, + RV64ZMMUL, RV32X, RV64X } riscv_instr_group_t; From b9e32cef159944bdfd7fd540f2eb319af4b18ca2 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Thu, 24 Jul 2025 15:27:22 +0000 Subject: [PATCH 3/5] Remove workaround for missing compiler support --- src/isa/riscv_zcb_instr.sv | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/isa/riscv_zcb_instr.sv b/src/isa/riscv_zcb_instr.sv index 5cdf69817..c13fb9751 100644 --- a/src/isa/riscv_zcb_instr.sv +++ b/src/isa/riscv_zcb_instr.sv @@ -19,10 +19,6 @@ class riscv_zcb_instr extends riscv_instr; // This code is based on the frozen v.1.0.1 code reduction specification. // Most of the code is copied from the riscv_compressed_instr class. - // gcc_support is used to insert instructions as raw data rather than going - // through the assembler. The reason being that GCC doesn't have support for - // zcb in the major releases yet. Remove the flag once it is supported. - int gcc_support = 0; // TODO: We have GCC support now. constraint rvc_csr_c { // Registers specified by the three-bit rs1’, rs2’, and rd/rs1’ @@ -104,10 +100,6 @@ class riscv_zcb_instr extends riscv_instr; default: `uvm_info(`gfn, $sformatf("Unsupported format %0s", format.name()), UVM_LOW) endcase - if (!gcc_support) begin - asm_str = {convert2bin(".2byte "), " #zcb until official GCC support: ", asm_str}; - end - if (comment != "") asm_str = {asm_str, " #",comment}; return asm_str.tolower(); From 79253f6b4122f2f643c20fb0de117492338ab8fb Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Mon, 15 Dec 2025 09:48:11 +0000 Subject: [PATCH 4/5] Fix Zcb formats --- src/isa/riscv_zcb_instr.sv | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/isa/riscv_zcb_instr.sv b/src/isa/riscv_zcb_instr.sv index c13fb9751..a6be81055 100644 --- a/src/isa/riscv_zcb_instr.sv +++ b/src/isa/riscv_zcb_instr.sv @@ -23,8 +23,6 @@ class riscv_zcb_instr extends riscv_instr; constraint rvc_csr_c { // Registers specified by the three-bit rs1’, rs2’, and rd/rs1’ if (format inside {CLB_FORMAT, CSB_FORMAT, CLH_FORMAT, CSH_FORMAT, CSZN_FORMAT, CA_FORMAT}) { - // reuse rs1 for special rd'/rs1' randomize_gpr function makes sure - // a reserved register is not used. if (has_rs1) { rs1 inside {[S0:A5]}; } @@ -35,6 +33,12 @@ class riscv_zcb_instr extends riscv_instr; rd inside {[S0:A5]}; } } + // CSZN_FORMAT and CA_FORMAT has rd == rs1 + if (format inside {CSZN_FORMAT, CA_FORMAT}) { + if (has_rd && has_rs1) { + rd == rs1; + } + } } `uvm_object_utils(riscv_zcb_instr) @@ -57,27 +61,23 @@ class riscv_zcb_instr extends riscv_instr; virtual function void set_rand_mode(); case (format) inside - CLB_FORMAT : begin + CLB_FORMAT: begin has_rs2 = 1'b0; end - C_SB : begin + CSB_FORMAT: begin has_rd = 1'b0; end - C_LHU, C_LH : begin + CLH_FORMAT: begin has_rs2 = 1'b0; end - C_SH : begin + CSH_FORMAT: begin has_rd = 1'b0; end - C_ZEXT_B, C_ZEXT_H, C_SEXT_B, C_SEXT_H, C_ZEXT_W : begin - // Similar to CB format, where rs/rd is shared. When randomizing GPRs - // don't allow rd in rs1. - has_rd = 1'b0; + CSZN_FORMAT: begin has_rs2 = 1'b0; has_imm = 1'b0; end - CA_FORMAT : begin - has_rs1 = 1'b0; + CA_FORMAT: begin has_imm = 1'b0; end default: `uvm_info(`gfn, $sformatf("Unsupported format %0s", format.name()), UVM_LOW) @@ -211,7 +211,9 @@ class riscv_zcb_instr extends riscv_instr; rs1 = get_gpr(operands[1]); rs1_value = get_gpr_state(operands[1]); end - CSZN_FORMAT: begin + CA_FORMAT: begin + rs2 = get_gpr(operands[1]); + rs2_value = get_gpr_state(operands[1]); rs1 = get_gpr(operands[0]); rs1_value = get_gpr_state(operands[0]); end From 0b914a5edc102f676b77eb4faab899d191b6b8e1 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Mon, 15 Dec 2025 12:25:29 +0000 Subject: [PATCH 5/5] Refactor CSZN_FORMAT to CU_FORMAT to match the RISC-V specification --- src/isa/riscv_instr_cov.svh | 2 +- src/isa/riscv_zcb_instr.sv | 12 ++++++------ src/isa/rv32zcb_instr.sv | 10 +++++----- src/isa/rv64zcb_instr.sv | 2 +- src/riscv_instr_pkg.sv | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/isa/riscv_instr_cov.svh b/src/isa/riscv_instr_cov.svh index c5e344111..3fd3b223b 100644 --- a/src/isa/riscv_instr_cov.svh +++ b/src/isa/riscv_instr_cov.svh @@ -408,7 +408,7 @@ // c.j imm get_val(operands[0], imm); end - CSZN_FORMAT: begin + CU_FORMAT: begin rs1 = get_gpr(operands[0]); rs1_value = get_gpr_state(operands[0]); end diff --git a/src/isa/riscv_zcb_instr.sv b/src/isa/riscv_zcb_instr.sv index a6be81055..7eb7ce4b6 100644 --- a/src/isa/riscv_zcb_instr.sv +++ b/src/isa/riscv_zcb_instr.sv @@ -22,7 +22,7 @@ class riscv_zcb_instr extends riscv_instr; constraint rvc_csr_c { // Registers specified by the three-bit rs1’, rs2’, and rd/rs1’ - if (format inside {CLB_FORMAT, CSB_FORMAT, CLH_FORMAT, CSH_FORMAT, CSZN_FORMAT, CA_FORMAT}) { + if (format inside {CLB_FORMAT, CSB_FORMAT, CLH_FORMAT, CSH_FORMAT, CU_FORMAT, CA_FORMAT}) { if (has_rs1) { rs1 inside {[S0:A5]}; } @@ -33,8 +33,8 @@ class riscv_zcb_instr extends riscv_instr; rd inside {[S0:A5]}; } } - // CSZN_FORMAT and CA_FORMAT has rd == rs1 - if (format inside {CSZN_FORMAT, CA_FORMAT}) { + // CU_FORMAT and CA_FORMAT has rd == rs1 + if (format inside {CU_FORMAT, CA_FORMAT}) { if (has_rd && has_rs1) { rd == rs1; } @@ -73,7 +73,7 @@ class riscv_zcb_instr extends riscv_instr; CSH_FORMAT: begin has_rd = 1'b0; end - CSZN_FORMAT: begin + CU_FORMAT: begin has_rs2 = 1'b0; has_imm = 1'b0; end @@ -93,7 +93,7 @@ class riscv_zcb_instr extends riscv_instr; asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rd.name(), get_imm(), rs1.name()); CSB_FORMAT, CSH_FORMAT: asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rs2.name(), get_imm(), rs1.name()); - CSZN_FORMAT: + CU_FORMAT: asm_str = $sformatf("%0s%0s", asm_str, rs1.name); CA_FORMAT: asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), rs2.name()); @@ -195,7 +195,7 @@ class riscv_zcb_instr extends riscv_instr; // For coverage virtual function void update_src_regs(string operands[$]); case(format) - CSZN_FORMAT: begin + CU_FORMAT: begin rs1 = get_gpr(operands[0]); rs1_value = get_gpr_state(operands[0]); end diff --git a/src/isa/rv32zcb_instr.sv b/src/isa/rv32zcb_instr.sv index eafe7cfea..d83fbd806 100644 --- a/src/isa/rv32zcb_instr.sv +++ b/src/isa/rv32zcb_instr.sv @@ -20,10 +20,10 @@ `DEFINE_ZCB_INSTR(C_LH, CLH_FORMAT, LOAD, RV32ZCB, UIMM) `DEFINE_ZCB_INSTR(C_SB, CSB_FORMAT, STORE, RV32ZCB, UIMM) `DEFINE_ZCB_INSTR(C_SH, CSH_FORMAT, STORE, RV32ZCB, UIMM) -`DEFINE_ZCB_INSTR(C_ZEXT_B, CSZN_FORMAT, ARITHMETIC, RV32ZCB) -`DEFINE_ZCB_INSTR(C_SEXT_B, CSZN_FORMAT, ARITHMETIC, RV32ZCB) -`DEFINE_ZCB_INSTR(C_ZEXT_H, CSZN_FORMAT, ARITHMETIC, RV32ZCB) -`DEFINE_ZCB_INSTR(C_SEXT_H, CSZN_FORMAT, ARITHMETIC, RV32ZCB) -`DEFINE_ZCB_INSTR(C_NOT, CSZN_FORMAT, LOGICAL, RV32ZCB) +`DEFINE_ZCB_INSTR(C_ZEXT_B, CU_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_SEXT_B, CU_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_ZEXT_H, CU_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_SEXT_H, CU_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_NOT, CU_FORMAT, LOGICAL, RV32ZCB) `DEFINE_ZCB_INSTR(C_MUL, CA_FORMAT, ARITHMETIC, RV32ZCB) diff --git a/src/isa/rv64zcb_instr.sv b/src/isa/rv64zcb_instr.sv index 5ec9b5103..8225b8235 100644 --- a/src/isa/rv64zcb_instr.sv +++ b/src/isa/rv64zcb_instr.sv @@ -15,4 +15,4 @@ * limitations under the License. */ -`DEFINE_ZCB_INSTR(C_ZEXT_W, CSZN_FORMAT, ARITHMETIC, RV64ZCB) +`DEFINE_ZCB_INSTR(C_ZEXT_W, CU_FORMAT, ARITHMETIC, RV64ZCB) diff --git a/src/riscv_instr_pkg.sv b/src/riscv_instr_pkg.sv index e3fcc4a8e..df29cfeba 100644 --- a/src/riscv_instr_pkg.sv +++ b/src/riscv_instr_pkg.sv @@ -716,7 +716,7 @@ package riscv_instr_pkg; CSB_FORMAT, CLH_FORMAT, CSH_FORMAT, - CSZN_FORMAT, + CU_FORMAT, // Vector instruction format VSET_FORMAT, VA_FORMAT,