Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion src/isa/riscv_instr.sv
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class riscv_instr extends uvm_object;
rand riscv_reg_t rs1;
rand riscv_reg_t rd;
rand bit [31:0] imm;
rand bit [3:0] rlist;
rand int stack_adj;

// Helper fields
bit [31:0] imm_mask = 32'hFFFF_FFFF;
Expand All @@ -66,6 +68,8 @@ class riscv_instr extends uvm_object;
bit has_rs2 = 1'b1;
bit has_rd = 1'b1;
bit has_imm = 1'b1;
bit has_rlist = 1'b0;
bit has_stack_adj = 1'b0;

constraint imm_c {
if (instr_name inside {SLLIW, SRLIW, SRAIW}) {
Expand Down Expand Up @@ -111,7 +115,8 @@ 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, RV32ZCB, RV64ZCB})) &&
(instr_inst.group inside {RV32C, RV64C, RV32DC, RV32FC, RV128C,
RV32ZCB, RV64ZCB, RV32ZCMP, RV64ZCMP})) &&
!(!cfg.enable_floating_point &&
(instr_inst.group inside {RV32F, RV64F, RV32D, RV64D})) &&
!(!cfg.enable_vector_extension &&
Expand Down Expand Up @@ -296,6 +301,7 @@ class riscv_instr extends uvm_object;
rs2.rand_mode(has_rs2);
rd.rand_mode(has_rd);
imm.rand_mode(has_imm);
stack_adj.rand_mode(has_stack_adj);
if (category != CSR) begin
csr.rand_mode(0);
end
Expand Down Expand Up @@ -586,6 +592,40 @@ class riscv_instr extends uvm_object;
return imm_str;
endfunction

virtual function string get_rlist();
// rlist 0-3 are reserved for future extensions
if (rlist < 4 || rlist > 15) begin
`uvm_fatal(`gfn, $sformatf("Unsupported rlist: %0d", rlist))
end
// Handle the specific cases and use a default for the general pattern.
case(rlist)
4: return "{ra}";
5: return "{ra, s0}";
15: return "{ra, s0-s11}"; // The special case for s10/s11
// The default case handles the general pattern for rlist 6 through 14
default: return $sformatf("{ra, s0-s%0d}", rlist - 5);
endcase
endfunction

virtual function riscv_reglist_t get_rlist_as_list();
case(rlist)
4: return '{RA};
5: return '{RA, S0};
6: return '{RA, S0, S1};
7: return '{RA, S0, S1, S2};
8: return '{RA, S0, S1, S2, S3};
9: return '{RA, S0, S1, S2, S3, S4};
10: return '{RA, S0, S1, S2, S3, S4, S5};
11: return '{RA, S0, S1, S2, S3, S4, S5, S6};
12: return '{RA, S0, S1, S2, S3, S4, S5, S6, S7};
13: return '{RA, S0, S1, S2, S3, S4, S5, S6, S7, S8};
14: return '{RA, S0, S1, S2, S3, S4, S5, S6, S7, S8, S9};
15: return '{RA, S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11};
default: `uvm_fatal(`gfn, $sformatf("Unsupported rlist: %0d", rlist))
endcase
return '{};
endfunction

virtual function void clear_unused_label();
if(has_label && !is_branch_target && is_local_numeric_label) begin
has_label = 1'b0;
Expand Down Expand Up @@ -620,6 +660,10 @@ class riscv_instr extends uvm_object;
imm_str = $sformatf("%0d", $signed(imm));
endfunction

virtual function int get_imm_val();
return $signed(imm);
endfunction

`include "isa/riscv_instr_cov.svh"

endclass
16 changes: 16 additions & 0 deletions src/isa/riscv_instr_cov.svh
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,22 @@
rs1 = get_gpr(operands[1]);
rs1_value = get_gpr_state(operands[1]);
end
CMMV_FORMAT: begin
// cm.mva01s rs1, rs2
// cm.mvsa01 r1s, r2s
rs1 = get_gpr(operands[0]);
rs1_value = get_gpr_state(operands[0]);
rs2 = get_gpr(operands[1]);
rs2_value = get_gpr_state(operands[1]);
end
CMPP_FORMAT: begin
// cm.push {reg_list}, -stack_adj
// cm.pop {reg_list}, stack_adj
// cm.popret {reg_list}, stack_adj
// cm.popretz {reg_list}, stack_adj
get_val(operands[0], rlist);
get_val(operands[1], stack_adj);
end
default: `uvm_fatal(`gfn, $sformatf("Unsupported format %0s", format))
endcase
endfunction : update_src_regs
Expand Down
225 changes: 225 additions & 0 deletions src/isa/riscv_zcmp_instr.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/*
* Copyright 2025 Google LLC
* Copyright 2025 lowRISC CIC
*
* 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_zcmp_instr extends riscv_instr;

constraint rvc_csr_c {
// Solve the stack adjustment first, so we can control the stack size
solve stack_adj before rlist;
if (format == CMPP_FORMAT) {
if (instr_name == CM_PUSH) {
// For PUSH, stack adjustment is negative
stack_adj inside {-16, -32, -48, -64, -80, -96, -112};
(stack_adj == -16) -> rlist inside {[4:7]};
(stack_adj == -32) -> rlist inside {[4:11]};
(stack_adj == -48) -> rlist inside {[4:14]};
(stack_adj == -64) -> rlist inside {[4:15]};
(stack_adj == -80) -> rlist inside {[8:15]};
(stack_adj == -96) -> rlist inside {[12:15]};
(stack_adj == -112) -> rlist inside {15};
} else {
// For POP and POPRET, stack adjustment is positive
stack_adj inside {16, 32, 48, 64, 80, 96, 112};
(rlist inside {[4:7]}) -> stack_adj inside {16, 32, 48, 64};
(rlist inside {[8:11]}) -> stack_adj inside {32, 48, 64, 80};
(rlist inside {[12:14]}) -> stack_adj inside {48, 64, 80, 96};
(rlist == 15) -> stack_adj inside {64, 80, 96, 112};
}
// rlist can be anything between 4 and 15. 0-3 are reserved for future use.
rlist inside {[4:15]};
}
if (format == CMMV_FORMAT) {
// Always has rs1 and rs2 and they must be different
rs1 != rs2;
// Those instructions use a special encoding, only S0, S1, S2-S7 are allowed
// which correspond to x8, x9, x18-x23, so the actual registers used are
// {r1sc[2:1]>0,r1sc[2:1]==0,r1sc[2:0]};
// So for registers beyond x16 we prepend 0x10 to the three LSB
// For the registers x8 and x9 we prepend 0x01 to the three LSB
rs1 inside {S0, S1, [S2:S7]};
rs2 inside {S0, S1, [S2:S7]};
}
}

`uvm_object_utils(riscv_zcmp_instr)

function new(string name = "");
super.new(name);
rs1 = S0;
rs2 = S0;
rlist = 4;
is_compressed = 1'b1;
stack_adj = instr_name == CM_PUSH ? -16 : 16;
endfunction : new

virtual function void set_rand_mode();
case (format) inside
CMMV_FORMAT : begin
has_imm = 1'b0;
end
CMPP_FORMAT : begin
has_rs1 = 1'b0;
has_rs2 = 1'b0;
has_rd = 1'b0;
has_rlist = 1'b1;
has_imm = 1'b0;
has_stack_adj = 1'b1;
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)
CMMV_FORMAT:
asm_str = $sformatf("%0s %0s, %0s", asm_str, rs1.name(), rs2.name());
CMPP_FORMAT: begin
asm_str = $sformatf("%0s %0s, %0d", asm_str, get_rlist(), get_stack_adj());
end
default: `uvm_info(`gfn, $sformatf("Unsupported format %0s", format.name()), UVM_LOW)
endcase

if (comment != "")
asm_str = {asm_str, " #",comment};
return asm_str.tolower();
endfunction : convert2asm

// Convert the instruction to binary code
virtual function string convert2bin(string prefix = "");
string binary;
case (instr_name) inside
CM_PUSH:
binary = $sformatf(
"0x%4h", {get_func6(), get_func2(), rlist, get_stack_adj_encoding(), get_c_opcode()});
CM_POP:
binary = $sformatf(
"0x%4h", {get_func6(), get_func2(), rlist, get_stack_adj_encoding(), get_c_opcode()});
CM_POPRETZ:
binary = $sformatf(
"0x%4h", {get_func6(), get_func2(), rlist, get_stack_adj_encoding(), get_c_opcode()});
CM_POPRET:
binary = $sformatf(
"0x%4h", {get_func6(), get_func2(), rlist, get_stack_adj_encoding(), get_c_opcode()});
CM_MVA01S:
binary = $sformatf(
"0x%4h", {get_func6(), get_c_gpr(rs1), get_func2(), get_c_gpr(rs2), get_c_opcode()});
CM_MVSA01:
binary = $sformatf(
"0x%4h", {get_func6(), get_c_gpr(rs1), get_func2(), 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 zcmp instruction
virtual function bit [1:0] get_c_opcode();
case (instr_name) inside
CM_PUSH, CM_POP, CM_POPRETZ, CM_POPRET, CM_MVA01S, CM_MVSA01 : get_c_opcode = 2'b10;
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) inside
CM_PUSH: get_func6 = 6'b101110;
CM_POP: get_func6 = 6'b101110;
CM_POPRETZ: get_func6 = 6'b101111;
CM_POPRET: get_func6 = 6'b101111;
CM_MVSA01: get_func6 = 6'b101011;
CM_MVA01S: get_func6 = 6'b101011;
default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
endcase
endfunction : get_func6

virtual function bit [1:0] get_func2();
case (instr_name) inside
CM_PUSH: get_func2 = 2'b00;
CM_POP: get_func2 = 2'b10;
CM_POPRETZ: get_func2 = 2'b00;
CM_POPRET: get_func2 = 2'b10;
CM_MVSA01: get_func2 = 2'b01;
CM_MVA01S: get_func2 = 2'b11;
default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
endcase
endfunction : get_func2

virtual function bit is_supported(riscv_instr_gen_config cfg);
`uvm_info(`gfn, "ZCMP Check supported", UVM_LOW)
return (cfg.enable_zcmp_extension &&
// RV32C, RV32Zbb, RV32Zba, M/Zmmul is prerequisites for this extension
(RV32C inside {supported_isa} || RV64C inside {supported_isa}) &&
(RV32ZCMP inside {supported_isa} || RV64ZCMP inside {supported_isa}) &&
instr_name inside {
CM_PUSH, CM_POP, CM_POPRET, CM_POPRETZ, CM_MVA01S, CM_MVSA01
});
endfunction : is_supported

// For coverage
virtual function void update_src_regs(string operands[$]);
case(format)
CMMV_FORMAT: begin
rs1 = get_gpr(operands[0]);
rs1_value = get_gpr_state(operands[0]);
rs2 = get_gpr(operands[1]);
rs2_value = get_gpr_state(operands[1]);
end
CMPP_FORMAT: begin
get_val(operands[0], rlist);
get_val(operands[1], stack_adj);
end
default: ;
endcase
super.update_src_regs(operands);
endfunction : update_src_regs

virtual function bit [1:0] get_stack_adj_encoding();
int unsigned stack_adj_base;
int unsigned stack_adj_abs;
bit [1:0] stack_adj_encoding;
if (XLEN == 32) begin
case (rlist)
4, 5, 6, 7: stack_adj_base = 16;
8, 9, 10, 11: stack_adj_base = 32;
12, 13, 14: stack_adj_base = 48;
15: stack_adj_base = 64;
default: stack_adj_base = 0;
endcase
end else begin
case (rlist)
4, 5: stack_adj_base = 16;
6, 7: stack_adj_base = 32;
8, 9: stack_adj_base = 48;
10, 11: stack_adj_base = 64;
12, 13: stack_adj_base = 80;
14: stack_adj_base = 96;
15: stack_adj_base = 122;
default: stack_adj_base = 0;
endcase
end
stack_adj_abs = instr_name == CM_PUSH ? -stack_adj : stack_adj;
stack_adj_encoding = (stack_adj_abs - stack_adj_base) / 16;
return stack_adj_encoding;
endfunction

virtual function int get_stack_adj();
return stack_adj;
endfunction

endclass : riscv_zcmp_instr;
24 changes: 24 additions & 0 deletions src/isa/rv32zcmp_instr.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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_ZCMP_INSTR(CM_PUSH, CMPP_FORMAT, STORE, RV32ZCMP)
`DEFINE_ZCMP_INSTR(CM_POP, CMPP_FORMAT, LOAD, RV32ZCMP)
`DEFINE_ZCMP_INSTR(CM_POPRETZ, CMPP_FORMAT, LOAD, RV32ZCMP)
`DEFINE_ZCMP_INSTR(CM_POPRET, CMPP_FORMAT, LOAD, RV32ZCMP)
`DEFINE_ZCMP_INSTR(CM_MVA01S, CMMV_FORMAT, ARITHMETIC, RV32ZCMP)
`DEFINE_ZCMP_INSTR(CM_MVSA01, CMMV_FORMAT, ARITHMETIC, RV32ZCMP)

2 changes: 1 addition & 1 deletion src/riscv_asm_program_gen.sv
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +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
RV32ZCB, RV64ZCB, RV32ZCMP, RV64ZCMP : ; // No Misa bit for Zc* extensions
default : `uvm_fatal(`gfn, $sformatf("%0s is not yet supported",
supported_isa[i].name()))
endcase
Expand Down
5 changes: 5 additions & 0 deletions src/riscv_defines.svh
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,8 @@
`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)

//Zcb-extension instruction
`define DEFINE_ZCMP_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \
class riscv_``instr_n``_instr extends riscv_zcmp_instr; \
`INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp)
3 changes: 2 additions & 1 deletion src/riscv_directed_instr_lib.sv
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,8 @@ 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, RV32ZCB, RV64ZCB, RV32F, RV64F, RV32D, RV64D}));
.exclude_group({RV32C, RV64C, RV32ZCB, RV64ZCB, RV32ZCMP, RV64ZCMP,
RV32F, RV64F, RV32D, RV64D}));
randomize_gpr(instr);
instr_list.push_back(instr);
end
Expand Down
Loading