diff --git a/verif/env/uvme/frontend/uvme_cva6_frontend_model.sv b/verif/env/uvme/frontend/uvme_cva6_frontend_model.sv new file mode 100644 index 0000000000..3779ac165d --- /dev/null +++ b/verif/env/uvme/frontend/uvme_cva6_frontend_model.sv @@ -0,0 +1,1150 @@ +// Copyright 2024 Thales DIS SAS +// +// Licensed under the Solderpad Hardware Licence, 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 +// +// https://solderpad.org/licenses/ +// +// 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. +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0 +// +// Original Author: Alae Eddine EZ ZEJJARI (alae-eddine.ez-zejjari@external.thalesgroup.com) – sub-contractor MU-Electronics for Thales group + +`ifndef __UVME_CVA6_FRONTEND_MODEL_SV__ +`define __UVME_CVA6_FRONTEND_MODEL_SV__ + + +// Return Address Stack class model +// The class contains a queue with a length equal to RASDepth to store the addresses of JALR instructions. +// The class provides a coverage model to cover all possible cases of push and pop operations. +class uvme_ras_sb_c extends uvm_scoreboard; + + // Objects + uvme_cva6_cfg_c cfg; + + // Address queue + bit[RTLCVA6Cfg.VLEN-1:0] ras_lifo[$]; + + `uvm_component_utils_begin(uvme_ras_sb_c) + `uvm_component_utils_end + + covergroup frontend_ras_cg with function sample(bit push_n_pop, int ras_size); //AZ + push_ras_cp : coverpoint ras_size { + bins push = {[0:RTLCVA6Cfg.RASDepth-1]} iff(push_n_pop); + bins push_full = {RTLCVA6Cfg.RASDepth} iff(push_n_pop); + } + pop_ras_cp : coverpoint ras_size { + bins pop = {[1:RTLCVA6Cfg.RASDepth]} iff(!push_n_pop); + bins pop_empty = {0} iff(!push_n_pop); + } + endgroup + + /** + * Default constructor. + */ + function new(string name="uvme_ras_sb", uvm_component parent=null); + super.new(name, parent); + frontend_ras_cg = new(); + endfunction + + /** + * Create and configures RAS via: + */ + virtual function void build_phase(uvm_phase phase); + void'(uvm_config_db#(uvme_cva6_cfg_c)::get(this, "", "cfg", cfg)); + if (!cfg) begin + `uvm_fatal("CFG", "Configuration handle is null") + end + endfunction + + /** + * Push address to the RAS queue + */ + virtual function void add_address(bit[RTLCVA6Cfg.VLEN-1:0] ret_address, bit enable); + if(enable) begin + frontend_ras_cg.sample(1, ras_lifo.size()); + if(ras_lifo.size() == RTLCVA6Cfg.RASDepth) begin + ras_lifo.delete(0); + ras_lifo.push_back(ret_address); + end else begin + ras_lifo.push_back(ret_address); + end + end + endfunction + + /** + * Pop address from the RAS queue + */ + virtual function uvme_frontend_ras_t pop_address(bit enable); + uvme_frontend_ras_t ras_predicted; + + if(enable) begin + frontend_ras_cg.sample(0, ras_lifo.size()); + if(ras_lifo.size() > 0) begin + ras_predicted.valid = '1; + ras_predicted.predicted_address = ras_lifo[ras_lifo.size()-1]; + ras_lifo.delete(ras_lifo.size()-1); + end else begin + ras_predicted.valid = '0; + ras_predicted.predicted_address = '0; + end + end else begin + ras_predicted.valid = '0; + ras_predicted.predicted_address = '0; + end + + return ras_predicted; + endfunction + + /** + * Flush the RAS queue + */ + virtual function bit[1:0] flush_ras(); + ras_lifo.delete(); + endfunction + +endclass : uvme_ras_sb_c + + +// Branch History Table class model +// The class contains a two-dimensional table of addresses and index, with a length dependent on BHTDepth and INSTR_PER_FETCH. +// The table type includes a two-bit counter and the prediction status, indicating whether it is valid or not. +// The class provides a coverage model to cover all possible cases of BHT updates and branch predictions. +class uvme_bht_sb_c extends uvm_scoreboard; + + // Objects + uvme_cva6_cfg_c cfg; + + uvme_frontend_bht_t branch_prediction[RTLCVA6Cfg.BHTEntries/RTLCVA6Cfg.INSTR_PER_FETCH][RTLCVA6Cfg.INSTR_PER_FETCH]; + + `uvm_component_utils_begin(uvme_bht_sb_c) + `uvm_component_utils_end + + covergroup frontend_bht_mispredict_cg with function sample(int update_pc, int update_row, int saturation); //AZ + pc_cp : coverpoint update_pc { + bins pc[] = {[0:RTLCVA6Cfg.BHTEntries/RTLCVA6Cfg.INSTR_PER_FETCH-1]}; + } + row_cp : coverpoint update_row { + bins pc[] = {[0:RTLCVA6Cfg.INSTR_PER_FETCH-1]}; + } + saturation_cp : coverpoint saturation { + bins pc[] = {[0:3]}; + } + cross pc_cp, row_cp, saturation_cp; + endgroup + + covergroup frontend_bht_predict_cg with function sample(bit prediction, bit taken); //AZ + prediction_cp : coverpoint prediction; + taken_cp : coverpoint taken; + cross prediction_cp, taken_cp; + endgroup + + /** + * Default constructor. + */ + function new(string name="uvme_bht_sb", uvm_component parent=null); + super.new(name, parent); + frontend_bht_mispredict_cg = new(); + frontend_bht_predict_cg = new(); + endfunction + + /** + * Create and configures BHT via: + */ + virtual function void build_phase(uvm_phase phase); + void'(uvm_config_db#(uvme_cva6_cfg_c)::get(this, "", "cfg", cfg)); + if (!cfg) begin + `uvm_fatal("CFG", "Configuration handle is null") + end + endfunction + + /** + * This function update the BHT table in case of valid mispredict in a branch + * The function take in input the mispredict information provided by frontend and he recalculate the access address + * The valid states of BHT boxes change if it's is the first time we access to an address + * Updating the two bit counter by the successive execution of the instructions + */ + virtual function void update_bht(uvme_frontend_bp_resolve_t bp); + int update_pc; + int update_row; + int upper_index; + int lower_index; + int index = 0; + + if(RTLCVA6Cfg.BHTEntries) begin + upper_index = $clog2(RTLCVA6Cfg.BHTEntries / RTLCVA6Cfg.INSTR_PER_FETCH) + (RTLCVA6Cfg.RVC == 1'b1 ? 1 : 2) + $clog2(RTLCVA6Cfg.INSTR_PER_FETCH); + lower_index = (RTLCVA6Cfg.RVC == 1'b1 ? 1 : 2) + $clog2(RTLCVA6Cfg.INSTR_PER_FETCH); + for(int i = lower_index; i < upper_index; i++) begin + update_pc[index] = bp.pc[i]; + index++; + end + + update_row = bp.pc[($clog2(RTLCVA6Cfg.INSTR_PER_FETCH) + (RTLCVA6Cfg.RVC == 1'b1 ? 1 : 2) - 1) :(RTLCVA6Cfg.RVC == 1'b1 ? 1 : 2)]; + branch_prediction[update_pc][update_row].valid = 1; + + if(branch_prediction[update_pc][update_row].saturation == 3) begin + if(!bp.is_taken) + branch_prediction[update_pc][update_row].saturation--; + end else if(branch_prediction[update_pc][update_row].saturation == 0) begin + if(bp.is_taken) + branch_prediction[update_pc][update_row].saturation++; + end else begin + if(bp.is_taken) + branch_prediction[update_pc][update_row].saturation++; + else + branch_prediction[update_pc][update_row].saturation--; + end + frontend_bht_mispredict_cg.sample(update_pc, update_row, branch_prediction[update_pc][update_row].saturation); + end + endfunction + + /** + * This function provides the taken or not prediction. + * The function take in input the address of fetch and he recalculate the access address, and instruction immediat. + * The prediction is valid if the address is in the BHT if not the instruction immediat is used to provide the prediction + */ + virtual function bit[1:0] bht_prediction(bit[RTLCVA6Cfg.VLEN-1:0] vpc ,bit[RTLCVA6Cfg.VLEN-1:0] imm, int index); + bit[RTLCVA6Cfg.VLEN:0] pc_index; + int pc_row; + bit[1:0] prediction; + int upper_index; + int lower_index; + int index_pc; + + if(RTLCVA6Cfg.BHTEntries) begin + upper_index = $clog2(RTLCVA6Cfg.BHTEntries / RTLCVA6Cfg.INSTR_PER_FETCH) + (RTLCVA6Cfg.RVC == 1'b1 ? 1 : 2) + $clog2(RTLCVA6Cfg.INSTR_PER_FETCH); + lower_index = (RTLCVA6Cfg.RVC == 1'b1 ? 1 : 2) + $clog2(RTLCVA6Cfg.INSTR_PER_FETCH); + index_pc = 0; + for(int i = lower_index; i < upper_index; i++) begin + pc_index[index_pc] = vpc[i]; + index_pc++; + end + for(int i = 0; i < RTLCVA6Cfg.INSTR_PER_FETCH; i++) begin + end + pc_row = vpc[1]; + if(branch_prediction[pc_index][index].valid) begin + prediction[0] = 1; + end + end else begin + prediction[0] = 0; + end + + if(prediction[0] == 1) begin + if(branch_prediction[pc_index][index].saturation[1]) + prediction[1] = 1; + end else begin + if(imm[RTLCVA6Cfg.VLEN-1] == 1) + prediction[1] = 1; + else + prediction[1] = 0; + end + + frontend_bht_predict_cg.sample(prediction[0], prediction[1]); + return prediction; + endfunction + + /** + * Instanciate the BHT + */ + virtual function bit[1:0] init_bht(); + for (int i = 0; i < (RTLCVA6Cfg.BHTEntries / RTLCVA6Cfg.INSTR_PER_FETCH); i++) begin + for (int j = 0; j < RTLCVA6Cfg.INSTR_PER_FETCH; j++) begin + branch_prediction[i][j].valid = 1'b0; + branch_prediction[i][j].saturation = 2'b00; + end + end + endfunction + + /** + * Flush the BHT + */ + virtual function bit[1:0] flush_bht(); + for (int i = 0; i < (RTLCVA6Cfg.BHTEntries / RTLCVA6Cfg.INSTR_PER_FETCH); i++) begin + for (int j = 0; j < RTLCVA6Cfg.INSTR_PER_FETCH; j++) begin + branch_prediction[i][j].valid = 1'b0; + branch_prediction[i][j].saturation = 2'b10; + end + end + endfunction + +endclass : uvme_bht_sb_c + + +// Instruction queue class model +// The class contains a queue of instruction that is ready to be sent to the Decode. +// The queue type includes the instruction with the address of fetch and the type of prediction with the prediction address. +// The class contain the status of the instruction queue the check the capability to push the data, and the status of address queue to check capability to push the data in case of prediction. +// The queue can hold up to 4*INSTR_PER_FETCH data entries, including maximum two prediction addresses. +// The class provides a coverage model to cover all possible cases of push and pop operations. +class uvme_queue_sb_c extends uvm_scoreboard; + + // Objects + uvme_cva6_cfg_c cfg; + + uvme_frontend_fetched_data_t data_queue[$]; + + int count_address; // counter of valid prediction address + bit instr_queue_available; + bit address_queue_available; + bit previous_prediction; + int instr_queue_depth; + + `uvm_component_utils_begin(uvme_queue_sb_c) + `uvm_component_utils_end + + covergroup frontend_queue_cg with function sample(bit push_n_pop, int queue_size); //AZ + push_queue_cp : coverpoint queue_size { + bins push = {[0:FETCH_FIFO_DEPTH * RTLCVA6Cfg.INSTR_PER_FETCH-1]} iff(push_n_pop); + bins push_full = {FETCH_FIFO_DEPTH * RTLCVA6Cfg.INSTR_PER_FETCH} iff(push_n_pop); + } + pop_queue_cp : coverpoint queue_size { + bins pop = {[1:FETCH_FIFO_DEPTH * RTLCVA6Cfg.INSTR_PER_FETCH]} iff(!push_n_pop); + illegal_bins ILLEGAL_BINS = {0} iff(!push_n_pop); + } + endgroup + + /** + * Default constructor. + */ + function new(string name="uvme_queue_sb", uvm_component parent=null); + super.new(name, parent); + frontend_queue_cg = new(); + endfunction + + /** + * Create and configures instr_queue via: + */ + virtual function void build_phase(uvm_phase phase); + instr_queue_depth = FETCH_FIFO_DEPTH * RTLCVA6Cfg.INSTR_PER_FETCH; + void'(uvm_config_db#(uvme_cva6_cfg_c)::get(this, "", "cfg", cfg)); + if (!cfg) begin + `uvm_fatal("CFG", "Configuration handle is null") + end + endfunction + + /** + * Push instruction into the queue + * Update instr_queue_available and address_queue_available + */ + virtual function int update_queue(uvme_frontend_fetched_data_t instr); + int valid_push; + + frontend_queue_cg.sample(1, data_queue.size()); + if(instr.predict) begin + if(count_address < ADDRESS_FIFO_DEPTH && data_queue.size() < instr_queue_depth) begin + data_queue.push_back(instr); + valid_push = 1; + count_address++; + end else begin + valid_push = -1; + end + end else begin + if(data_queue.size() < instr_queue_depth) begin + data_queue.push_back(instr); + valid_push = 1; + end else begin + valid_push = -1; + end + end + + if(data_queue.size() > 1) begin + if(previous_prediction) begin + data_queue[data_queue.size()-1].address = data_queue[data_queue.size()-2].address + ((data_queue[data_queue.size()-2].instruction[1:0] != 3) ? 2 : 4); + end + if(data_queue[data_queue.size()-2].predict) begin + data_queue[data_queue.size()-1].address = data_queue[data_queue.size()-2].predicted_address; + previous_prediction = 1; + end + end + + if(count_address < ADDRESS_FIFO_DEPTH) + address_queue_available = 1; + else + address_queue_available = 0; + + if(data_queue.size() < instr_queue_depth) begin + instr_queue_available = 1; + end else begin + instr_queue_available = 0; + address_queue_available = 0; + end + + return valid_push; + endfunction + + /** + * Pop instruction from the queue + * Update instr_queue_available and address_queue_available + */ + virtual function uvme_frontend_fetched_data_t pop_instruction(); + uvme_frontend_fetched_data_t instr; + + frontend_queue_cg.sample(0, data_queue.size()); + if(data_queue.size() > 0) begin + //instr = frontend_out.pop_front(); + instr = data_queue[0]; + data_queue.delete(0); + if(instr.predict) begin + count_address--; + end + end else begin + `uvm_fatal(get_type_name(), "ERROR -> Zero instruction is ready to be send to decode") + end + + if(count_address < ADDRESS_FIFO_DEPTH) + address_queue_available = 1; + else + address_queue_available = 0; + + if(data_queue.size() < instr_queue_depth) begin + instr_queue_available = 1; + end else begin + instr_queue_available = 0; + address_queue_available = 0; + end + + return instr; + endfunction + + /** + * Flush into the instruction queue + */ + virtual function bit[1:0] flush_queue(); + data_queue.delete(); + count_address = 0; + instr_queue_available = 1; + address_queue_available = 1; + previous_prediction = 0; + endfunction + +endclass : uvme_queue_sb_c + + +// Frontend class model +// This class models the behavior of the frontend, providing the same frontend output and using the same input. It is also configurable with the same CVA6 parameters. +// The model contains all the components needed to calculate the instructions supplied to the Decode stage. +// It retrieves inputs from the monitor through an analysis port in the form of transactions, then aligns the instructions, pre-decodes them, +// calculates the prediction address if there is a branch or jump, and places the processed instructions in the queue. +// The model detects if a response is sent on the bus to the Decode stage and compares it with the first element in the queue. +// It also calculates the next PC, taking into account previous instructions and given input, and compares the calculated PC with the PC sent to the cache every cycle +class uvme_cva6_frontend_model_c extends uvm_scoreboard; + + // Objects + uvme_cva6_cfg_c cfg; + + //Handles to sequencer FIFOS + uvm_tlm_analysis_fifo #(uvme_cva6_frontend_transaction_c) mon2mod_fifo_port; + + //Handles to sequencer port connected to the FIFOS + uvm_analysis_export #(uvme_cva6_frontend_transaction_c) mon2mod_export; + + uvme_bht_sb_c bht_sb; + uvme_ras_sb_c ras_sb; + uvme_queue_sb_c instr_queue_sb; + + logic[RTLCVA6Cfg.FETCH_WIDTH-1:0] fetched_data; + logic[RTLCVA6Cfg.VLEN-1:0] fetched_address; + bit[RTLCVA6Cfg.INSTR_PER_FETCH-1:0] valid_fetch; + bit[ILEN-1:0] valid_data[RTLCVA6Cfg.INSTR_PER_FETCH-1:0]; + bit[RTLCVA6Cfg.VLEN-1:0] valid_address[RTLCVA6Cfg.INSTR_PER_FETCH-1:0]; + uvme_frontend_fetched_data_t decode_entry; + uvme_frontend_icache_rsp_t cache_entry; + logic[RTLCVA6Cfg.VLEN-1:0] next_pc; + logic[RTLCVA6Cfg.VLEN-1:0] next_predicted_address; + int valid_push; + int bp_valid; + int next_bp_valid; + int next_kill_s1 = 0; + int next_kill_s2 = 0; + + bit unalined_access; + bit[RTLCVA6Cfg.VLEN-1:0] unaligned_address; + bit[ILEN-1:0] unaligned_data; + + uvme_cva6_frontend_transaction_c frontend_transaction; // transaction sent by the monitor + + // JR compressed instruction + bit rvc_jr[RTLCVA6Cfg.INSTR_PER_FETCH]; + // -1 : compressed / 0 : not / 1 : not compressed + // Branch compressed instruction + int branch[RTLCVA6Cfg.INSTR_PER_FETCH]; + // Unconditional jump compressed instruction + int jump[RTLCVA6Cfg.INSTR_PER_FETCH]; + // Return compressed instruction + int ret[RTLCVA6Cfg.INSTR_PER_FETCH]; + // JALR compressed instruction + int jalr[RTLCVA6Cfg.INSTR_PER_FETCH]; + // JAL compressed instruction + int call[RTLCVA6Cfg.INSTR_PER_FETCH]; + // Instruction compressed immediat + bit [RTLCVA6Cfg.VLEN:0] rvc_imm[RTLCVA6Cfg.INSTR_PER_FETCH]; + // Instruction compressed immediat + bit [RTLCVA6Cfg.VLEN:0] rvi_imm[RTLCVA6Cfg.INSTR_PER_FETCH]; + + bit[1:0] bht_bp; + bit resolve_aligned; + + `uvm_component_utils_begin(uvme_cva6_frontend_model_c) + `uvm_component_utils_end + + covergroup frontend_decode_cg with function sample(uvme_frontend_fetched_data_t decode_entry); + addr_cp : coverpoint decode_entry.address[2:1]; + + compressed_cp : coverpoint decode_entry.instruction[1:0] { + bins compressed = {[0:2]}; + bins Ncompressed = {3}; + } + + predict_cp : coverpoint decode_entry.predict { + ignore_bins IGN_BINS = {3} iff(!RTLCVA6Cfg.BTBEntries); + } + + cross addr_cp, compressed_cp, predict_cp; + endgroup + + covergroup frontend_flush_cg with function sample(bit flush, bit flush_bp); //AZ + flush_cp: coverpoint flush; + // Not Supported By CVA6 + // flush_bp_cp : coverpoint flush_bp; + // cross flush_cp, flush_bp_cp; + endgroup + + covergroup frontend_cache_cg with function sample(uvme_cva6_frontend_transaction_c frontend_transaction, bit bp_valid, bit replay_addr); + + reset : coverpoint frontend_transaction.boot_valid; + prediction : coverpoint bp_valid; + Default : coverpoint frontend_transaction.icache_req.ready && frontend_transaction.icache_rsp.req; + replay_request : coverpoint replay_addr; + mispredict : coverpoint (frontend_transaction.resolve_branch.valid && frontend_transaction.resolve_branch.is_mispredict); + env_call : coverpoint frontend_transaction.eret; + interrupt : coverpoint frontend_transaction.ex_valid; + pipeline_flush : coverpoint frontend_transaction.set_pc_commit; + Debug : coverpoint frontend_transaction.set_debug_pc { + ignore_bins IGN_BINS = {1} iff(!RTLCVA6Cfg.DebugEn); + } + + cross reset, prediction, Default, replay_request, mispredict, env_call, interrupt, pipeline_flush, Debug; + endgroup + + covergroup frontend_execut_cg with function sample(uvme_cva6_frontend_transaction_c frontend_transaction); + + bht_mispredict : coverpoint (frontend_transaction.resolve_branch.cf_type == 1); + + ras_mispredict : coverpoint (frontend_transaction.resolve_branch.cf_type == 4); + + taken : coverpoint frontend_transaction.resolve_branch.is_taken; + + mispredict : coverpoint frontend_transaction.resolve_branch.is_mispredict; + + cross bht_mispredict, mispredict, taken { + ignore_bins IGN_BINS = binsof(bht_mispredict) intersect{0} && + binsof(taken) intersect{0}; + } + + cross ras_mispredict, mispredict; + endgroup + + /** + * Default constructor. + */ + function new(string name="uvme_cva6_frontend_model_c", uvm_component parent=null); + super.new(name, parent); + frontend_decode_cg = new(); + frontend_flush_cg = new(); + frontend_cache_cg = new(); + frontend_execut_cg = new(); + endfunction + + /** + * Create and configures sub-Frontend via: + */ + virtual function void build_phase(uvm_phase phase); + + void'(uvm_config_db#(uvme_cva6_cfg_c)::get(this, "", "cfg", cfg)); + if (!cfg) + `uvm_fatal("CFG", "Configuration handle is null") + + this.mon2mod_fifo_port = new("mon2mod_fifo_port", this); + this.mon2mod_export = new("mon2mod_export", this); + + instr_queue_sb = uvme_queue_sb_c::type_id::create("instr_queue_sb", this); + bht_sb = uvme_bht_sb_c::type_id::create("bht_sb", this); + + if (RTLCVA6Cfg.RASDepth != 0) begin + ras_sb = uvme_ras_sb_c::type_id::create("ras_sb", this); + end + + endfunction + + /** + * Connect get ports to FIFO get peek_export ports + */ + function void connect_phase(uvm_phase phase); + super.connect_phase(phase); + // Connect get ports to FIFO get peek_export ports + this.mon2mod_export.connect(this.mon2mod_fifo_port.analysis_export); + endfunction + + /** + * Frontend run phase. + * instantiate all the component. + * Start geting transaction from monitor. + */ + task run_phase(uvm_phase phase); + super.run_phase(phase); + instr_queue_sb.flush_queue(); + bht_sb.init_bht(); + if (RTLCVA6Cfg.RASDepth != 0) begin + ras_sb.flush_ras(); + end + forever begin + modeling_frontend_module(); + end + endtask + + task modeling_frontend_module(); + + mon2mod_fifo_port.get(frontend_transaction); + check_decode_req_out(); + misprediction(); + receive_frontend_input(); + perform_cache_entry(); + check_cache_req_out(); + + endtask + + /** + * Fetch Stage flow + * Decide which function will handle the alignment. + * Pre-decode only the valid instructions, prepare the instruction with all related prediction information + * Push all the valid instruction into the queue. + */ + task receive_frontend_input(); + bit[RTLCVA6Cfg.VLEN-1:0] predicted_address; + bit[RTLCVA6Cfg.VLEN-1:0] ras_addr; + uvme_frontend_ras_t ras_prediction; + bit[1:0] branch_predict; + int branch_index; + + if(RTLCVA6Cfg.FETCH_WIDTH == 32) + align_fetched_data_32(); + else + align_fetched_data_64(); + + decode_entry = '0; + + for(int i = 0; i < RTLCVA6Cfg.INSTR_PER_FETCH; i++) begin + if(i != 0 && decode_entry.predict != 0) begin + for(int j = i; j < RTLCVA6Cfg.INSTR_PER_FETCH; j++) begin + valid_fetch[j] = 0; + end + end + frontend_decode_instr(i, valid_data[i]); + // if there is a JUMP we change the prediction type and the prediction address equal to the fetch address + instruction immediat + if(jump[i] != 0) begin + decode_entry.predict = Jump; + if(jump[i] == 1) predicted_address = valid_address[i] + rvi_imm[i]; + else if (jump[i] == -1) predicted_address = valid_address[i] + rvc_imm[i]; + end + // if there is a RET we change the prediction type and the prediction address is poped from the RAS. if the RAS is empty prediction address equal to 0 + else if(ret[i] != 0) begin + decode_entry.predict = Return; + if(RTLCVA6Cfg.RASDepth && valid_fetch[i]) begin + ras_prediction = ras_sb.pop_address(instr_queue_sb.address_queue_available); + predicted_address = ras_prediction.predicted_address; + end + end + // if there is a BRANCH we check if there is an outstanding unaligned access or not to see whish address we will use + else if(branch[i] != 0) begin + // if the access is not aligned we use the address of the first access to calculate the prediction + if(resolve_aligned && valid_fetch[i]) begin + if(bht_bp[0] == 1) decode_entry.predict = (bht_bp[1] == 1) ? 1 : 0; + else decode_entry.predict = (rvi_imm[i][RTLCVA6Cfg.VLEN-1] == 1) ? 1 : 0; + predicted_address = valid_address[i] + ((decode_entry.predict == 1) ? rvi_imm[i] : 0); + // if the access is aligned we use the current address to calculate the prediction + end else begin + branch_index = valid_address[i][$clog2(RTLCVA6Cfg.INSTR_PER_FETCH):1]; + if(valid_data[i][1:0] != 3) begin + branch_predict = bht_sb.bht_prediction(fetched_address ,rvc_imm[i], branch_index); + predicted_address = valid_address[i] + ((branch_predict[1] == 1) ? rvc_imm[i] : 0); + end else begin + branch_predict = bht_sb.bht_prediction(fetched_address, rvi_imm[i], branch_index); + predicted_address = valid_address[i] + ((branch_predict[1] == 1) ? rvi_imm[i] : 0); + end + decode_entry.predict = (branch_predict[1] == 1) ? 1 : 0; + if(unalined_access) begin + bht_bp = branch_predict; + end + end + end + // TODO : Jalr is not supported + else if(jalr[i] != 0 && RTLCVA6Cfg.BTBEntries) begin + decode_entry.predict = JumpR; + end + else begin + decode_entry.predict = NoCF; + predicted_address = 0; + end + // If there is a CALL we push the fetch address into the RAS + if(call[i] != 0 && valid_fetch[i] && RTLCVA6Cfg.RASDepth) begin + ras_addr = valid_address[i] + ((call[i] == -1) ? 2 : 4); + ras_sb.add_address(ras_addr, instr_queue_sb.address_queue_available); + end + + // Push the instruction type in to the instruction queue + if(valid_fetch[i]) begin + resolve_aligned = 0; + decode_entry.predicted_address = predicted_address; + decode_entry.address = valid_address[i]; + decode_entry.instruction = valid_data[i]; + valid_push = instr_queue_sb.update_queue(decode_entry); + frontend_decode_cg.sample(decode_entry); + + if((decode_entry.predict != 0 && decode_entry.predict != 4) || (decode_entry.predict == 4 && ras_prediction.valid)) begin + next_bp_valid = 1; + next_kill_s2 = 1; + end + if(valid_push == -1) begin + next_kill_s1 = 1; + break; + end + end + end + for(int i = 0; i < RTLCVA6Cfg.INSTR_PER_FETCH; i++) begin + valid_fetch[i] = 0; + valid_data[i] = 0; + valid_address[i] = 0; + end + flush_frontend(); + if(cache_entry.kill_s1) begin + cache_entry.kill_s2 = 1; + end + + endtask : receive_frontend_input + + /** + * Flush the frontend sub-moduls + */ + function void flush_frontend(); + + if(frontend_transaction.flush) begin + instr_queue_sb.flush_queue(); + cache_entry.kill_s1 = 1; + end + + if(frontend_transaction.flush_bp) begin + if (RTLCVA6Cfg.RASDepth != 0) begin + ras_sb.flush_ras(); + end + bht_sb.flush_bht(); + end + frontend_flush_cg.sample(frontend_transaction.flush, frontend_transaction.flush_bp); + + endfunction : flush_frontend + + /** + * Detect misprediction to update the BHT + */ + function void misprediction(); + + if(frontend_transaction.resolve_branch.valid) begin + frontend_execut_cg.sample(frontend_transaction); + if(frontend_transaction.resolve_branch.is_mispredict) begin + cache_entry.kill_s1 = 1; + end + if(frontend_transaction.resolve_branch.cf_type == 1) begin + bht_sb.update_bht(frontend_transaction.resolve_branch); + end + end + + endfunction : misprediction + + /** + * PC Gen flow + * Calculate the next PC each clock cycle + */ + function void perform_cache_entry(); + bit replay; + + replay = (valid_push == -1); + if (frontend_transaction.boot_valid == 1) begin + next_pc = cfg.boot_addr; + cache_entry.vaddr = cfg.boot_addr; + end else begin + cache_entry.vaddr = next_pc; + end + + // 0. Branch Prediction + if (bp_valid) begin + next_pc = next_predicted_address; + cache_entry.vaddr = next_predicted_address; + end + // 1. Default assignment + if (frontend_transaction.icache_req.ready && frontend_transaction.icache_rsp.req) begin + next_pc = { + cache_entry.vaddr[RTLCVA6Cfg.VLEN-1:RTLCVA6Cfg.FETCH_ALIGN_BITS] + 1, {RTLCVA6Cfg.FETCH_ALIGN_BITS{1'b0}} + }; + end + // 2. Replay instruction fetch + if (replay) begin + next_pc = decode_entry.address; + valid_push = 0; + end + // 3. Mispredict + if (frontend_transaction.resolve_branch.valid && frontend_transaction.resolve_branch.is_mispredict) + next_pc = frontend_transaction.resolve_branch.target_address; + // 4. Return from environment call + if (frontend_transaction.eret) + next_pc = frontend_transaction.epc; + // 5. Exception/Interrupt + if (frontend_transaction.ex_valid) + next_pc = frontend_transaction.trap_vector_base; + // 6. Pipeline Flush + if (frontend_transaction.set_pc_commit) + next_pc = frontend_transaction.pc_commit + (frontend_transaction.halt ? '0 : {{RTLCVA6Cfg.VLEN - 3{1'b0}}, 3'b100}); + // 7. Debug + if (RTLCVA6Cfg.DebugEn && frontend_transaction.set_debug_pc) + next_pc = RTLCVA6Cfg.DmBaseAddress[RTLCVA6Cfg.VLEN-1:0] + RTLCVA6Cfg.HaltAddress[RTLCVA6Cfg.VLEN-1:0]; + + frontend_cache_cg.sample(frontend_transaction, bp_valid, replay); + bp_valid = 0; + + endfunction : perform_cache_entry + + /** + * Compare the instruction sent to the Decode with the two first instruction poped from the instruction queue + */ + function void check_decode_req_out(); + uvme_frontend_fetched_data_t frontend_output; + + for(int i = 0; i < RTLCVA6Cfg.NrIssuePorts; i++) begin + if(frontend_transaction.fetch_valid[i] && frontend_transaction.fetch_ready[i]) begin + frontend_output = instr_queue_sb.pop_instruction(); + if(frontend_output.address != frontend_transaction.fetch_instr[i].address) `uvm_error(get_type_name(), "ERROR -> Wrong address") + if(frontend_output.instruction != frontend_transaction.fetch_instr[i].instruction) `uvm_error(get_type_name(), "ERROR -> Wrong instruction") + if(frontend_transaction.fetch_instr[i].predict != 0 && frontend_output.predicted_address != frontend_transaction.fetch_instr[i].predicted_address) `uvm_error(get_type_name(), "ERROR -> Wrong predicted address") + if(frontend_output.predict != frontend_transaction.fetch_instr[i].predict) `uvm_error(get_type_name(), "ERROR -> Wrong prediction") + end + end + + endfunction : check_decode_req_out + + /** + * Check the next PC and all the information performed by the Frontend + */ + function void check_cache_req_out(); + + if(cache_entry.kill_s1 != frontend_transaction.icache_rsp.kill_s1) `uvm_error(get_type_name(), "ERROR -> the frontend sent wrong kill_s1") + if(cache_entry.kill_s2 != frontend_transaction.icache_rsp.kill_s2) `uvm_error(get_type_name(), "ERROR -> the frontend sent wrong kill_s2") + if(frontend_transaction.icache_rsp.req) begin + if(cache_entry.vaddr != frontend_transaction.icache_rsp.vaddr) `uvm_error(get_type_name(), "ERROR -> the frontend sent wrong npc") + end + + cache_entry.kill_s1 = next_kill_s1; + cache_entry.kill_s2 = next_kill_s2; + bp_valid = next_bp_valid; + next_predicted_address = decode_entry.predicted_address; + next_kill_s1 = 0; + next_kill_s2 = 0; + next_bp_valid = 0; + + endfunction : check_cache_req_out + + /** + * Decode the instruction if it's compressed or not + * calculate to instruction immediat + */ + function void frontend_decode_instr(int i, bit[31:0] instr); + bit[RTLCVA6Cfg.VLEN-1:0] uj_imm, sb_imm; + if(instr[1:0] == 2'b11) begin + branch[i] = (instr[6:0] == 7'b11_000_11) ? 1 : 0; + jalr[i] = (instr[6:0] == 7'b11_001_11) ? 1 : 0; + jump[i] = ((instr[6:0] == 7'b11_011_11) | (instr[31:30] == 2'b00 & instr[28:0] == 29'b10000001000000000000001110011)) ? 1 : 0; + ret[i] = ((jalr[i] == 1) & ((instr[19:15] == 5'd1) | instr[19:15] == 5'd5) & (instr[19:15] != instr[11:7])) ? 1 : 0; + call[i] = ((jalr[i] == 1 | jump[i] == 1) & ((instr[11:7] == 5'd1) | instr[11:7] == 5'd5)) ? 1 : 0; + + uj_imm = {{44 + RTLCVA6Cfg.VLEN - 64{instr[31]}}, instr[19:12], instr[20], instr[30:21], 1'b0}; + sb_imm = {{51 + RTLCVA6Cfg.VLEN - 64{instr[31]}}, instr[31], instr[7], instr[30:25], instr[11:8], 1'b0}; + rvi_imm[i]= (instr[31:30] == 2'b00 & instr[28:0] == 29'b10000001000000000000001110011) ? '0 : (instr[3]) ? uj_imm : sb_imm; + end else begin + jump[i] = (((instr[15:13] == 3'b101) & (instr[1:0] == 2'b01)) | ((RTLCVA6Cfg.XLEN == 32) & ((instr[15:13] == 3'b001) & (instr[1:0] == 2'b01)))) ? -1 : 0; + rvc_jr[i] = ((instr[15:13] == 3'b100) & (instr[6:2] == 5'b00000) & (instr[1:0] == 2'b10) & ~instr[12]) ? 1 : 0; + jalr[i] = ((instr[15:13] == 3'b100) & (instr[6:2] == 5'b00000) & (instr[1:0] == 2'b10) & instr[12]) ? -1 : 0; + call[i] = ((jalr[i] == -1) | ((RTLCVA6Cfg.XLEN == 32) & ((instr[15:13] == 3'b001) & (instr[1:0] == 2'b01)))) ? -1 : 0; + branch[i] = (((instr[15:13] == 3'b110) | (instr[15:13] == 3'b111)) & (instr[1:0] == 2'b01))? -1 : 0; + ret[i] = (((instr[11:7] == 5'd1) | (instr[11:7] == 5'd5)) & rvc_jr[i]) ? -1 : 0; + rvc_imm[i]= (instr[14]) ? {{(56+RTLCVA6Cfg.VLEN-64){instr[12]}}, instr[6:5], instr[2], instr[11:10], instr[4:3], 1'b0} + : {{(53+RTLCVA6Cfg.VLEN-64){instr[12]}}, instr[8], instr[10:9], instr[6], instr[7], instr[2], instr[11], instr[5:3], 1'b0}; + end + + endfunction : frontend_decode_instr + + /** + * Detect the valid instruction from the fetched data (32 bits) + */ + function void align_fetched_data_32(); + + if(frontend_transaction.icache_rsp.kill_s2) begin + unalined_access = 0; + end else if(frontend_transaction.icache_req.valid) begin + fetched_address = frontend_transaction.icache_req.vaddr; + fetched_data = frontend_transaction.icache_req.data >> {fetched_address[$clog2(RTLCVA6Cfg.INSTR_PER_FETCH):1], 4'b0}; + if(unalined_access) begin + valid_fetch[0] = 1; + valid_fetch[1] = 0; + valid_data[0] = {fetched_data[15:0], unaligned_data[15:0]}; + valid_data[1] = 0; + valid_address[0] = unaligned_address; + valid_address[1] = {fetched_address[31:2], 2'b10}; + unalined_access = 0; + if(fetched_address[1]) begin + if(fetched_data[1:0] != 3) begin + valid_fetch = 2'b01; + end else begin + valid_fetch = '0; + unalined_access = 1; + unaligned_address = {fetched_address[31:2], 2'b10}; + unaligned_data = fetched_data[15:0]; + valid_data[1] = {'0, unaligned_data[15:0]}; + end + end else begin + if(fetched_data[17:16] != 3) begin + unalined_access = 0; + valid_fetch[1] = 1; + valid_data[1] = {16'b0, fetched_data[31:16]}; + end else begin + unalined_access = 1'b1; + unaligned_data = fetched_data[31:16]; + unaligned_address = {fetched_address[31:2], 2'b10}; + valid_data[1] = {'0, unaligned_data[15:0]}; + end + end + resolve_aligned = 1; + end else begin + valid_fetch[0] = 1; + valid_fetch[1] = 0; + valid_data[0] = fetched_data; + valid_data[1] = 0; + valid_address[0] = fetched_address; + valid_address[1] = {fetched_address[31:2], 2'b10}; + if(fetched_address[1]) begin + if(fetched_data[1:0] != 3) begin + valid_fetch = 2'b01; + end else begin + valid_fetch = '0; + unalined_access = 1; + unaligned_address = {fetched_address[31:2], 2'b10}; + unaligned_data = fetched_data[15:0]; + valid_data[1] = {'0, unaligned_data[15:0]}; + end + end else begin + if(fetched_data[1:0] != 3) begin + if(fetched_data[17:16] != 3) begin + unalined_access = 0; + valid_fetch[1] = 1; + valid_data[1] = {16'b0, fetched_data[31:16]}; + end else begin + unalined_access = 1'b1; + unaligned_data = fetched_data[31:16]; + unaligned_address = {fetched_address[31:2], 2'b10}; + valid_data[1] = {'0, unaligned_data[15:0]}; + end + end + end + end + end + endfunction : align_fetched_data_32 + + /** + * Detect the valid instruction from the fetched data (64 bits) + */ + function void align_fetched_data_64(); + + if(frontend_transaction.icache_rsp.kill_s2) begin + unalined_access = 0; + end else if(frontend_transaction.icache_req.valid) begin + fetched_address = frontend_transaction.icache_req.vaddr; + fetched_data = frontend_transaction.icache_req.data >> {fetched_address[$clog2(RTLCVA6Cfg.INSTR_PER_FETCH):1], 4'b0}; + valid_fetch = '0; + valid_data[0] = '0; + valid_address[0] = '0; + valid_data[1] = '0; + valid_address[1] = '0; + valid_data[2] = '0; + valid_address[2] = '0; + valid_data[3] = {16'b0, fetched_data[63:48]}; + valid_address[3] = {fetched_address[31:3], 3'b110}; + + case (fetched_address[2:1]) + 2'b00: begin + valid_fetch[0] = 1; + valid_fetch[1] = 1; + if (unalined_access) begin + resolve_aligned = 1; + + valid_data[0] = {fetched_data[15:0], unaligned_data[15:0]}; + valid_address[0] = unaligned_address; + + valid_data[1] = fetched_data[47:16]; + valid_address[1] = {fetched_address[31:3], 3'b010}; + + if (fetched_data[17:16] != 3) begin + valid_data[2] = fetched_data[63:32]; + valid_address[2] = {fetched_address[31:3], 3'b100}; + valid_fetch[2] = 1; + + if (fetched_data[33:32] != 3) begin + if (fetched_data[49:48] != 3) begin + unalined_access = 1'b0; + valid_fetch[3] = 1; + end else begin + unaligned_data = valid_data[3]; + unaligned_address = valid_address[3]; + end + end else begin + unalined_access = 1'b0; + end + end else begin + valid_data[2] = valid_data[3]; + valid_address[2] = valid_address[3]; + if (fetched_data[49:48] != 3) begin + unalined_access = 1'b0; + valid_fetch[2] = 1; + end else begin + unaligned_data = valid_data[3]; + unaligned_address = valid_address[3]; + end + end + end else begin + valid_data[0] = fetched_data[31:0]; + valid_address[0] = fetched_address; + + if (fetched_data[1:0] != 3) begin + valid_data[1] = fetched_data[47:16]; + valid_address[1] = {fetched_address[31:3], 3'b010}; + if (fetched_data[17:16] != 3) begin + valid_data[2] = fetched_data[63:32]; + valid_address[2] = {fetched_address[RTLCVA6Cfg.VLEN-1:3], 3'b100}; + valid_fetch[2] = 1; + + if (fetched_data[33:32] != 3) begin + if (fetched_data[49:48] != 3) begin + valid_fetch[3] = 1; + end else begin + unalined_access = 1'b1; + unaligned_data = valid_data[3]; + unaligned_address = valid_address[3]; + end + end + end else begin + valid_data[2] = valid_data[3]; + valid_address[2] = valid_address[3]; + + if (fetched_data[49:48] != 3) begin + valid_fetch[2] = 1; + end else begin + unalined_access = 1'b1; + unaligned_data = valid_data[3]; + unaligned_address = valid_address[3]; + end + end + end else begin + valid_data[1] = fetched_data[63:32]; + valid_address[1] = {fetched_address[31:3], 3'b100}; + + valid_data[2] = valid_data[3]; + valid_address[2] = valid_address[3]; + + if (fetched_data[33:32] != 3) begin + if (fetched_data[49:48] != 3) begin + valid_fetch[2] = 1; + end else begin + unalined_access = 1'b1; + unaligned_data = valid_data[3]; + unaligned_address = valid_address[3]; + end + end + end + end + end + 2'b01: begin + + valid_data[0] = fetched_data[31:0]; + valid_address[0] = {fetched_address[31:3], 3'b010}; + valid_fetch[0] = 1; + + valid_data[2] = fetched_data[63:32]; + valid_address[2] = {fetched_address[31:3], 3'b110}; + + if (fetched_data[1:0] != 3) begin + valid_data[1] = fetched_data[47:16]; + valid_address[1] = {fetched_address[31:3], 3'b100}; + valid_fetch[1] = 1; + + if (fetched_data[17:16] != 3) begin + if (fetched_data[33:32] != 3) begin + valid_fetch[2] = 1; + end else begin + unalined_access = 1'b1; + unaligned_data = valid_data[2]; + unaligned_address = valid_address[2]; + end + end + end else begin + valid_data[1] = valid_data[2]; + valid_address[1] = valid_address[2]; + + if (fetched_data[33:32] != 3) begin + valid_fetch[1] = 1; + end else begin + unalined_access = 1'b1; + unaligned_data = valid_data[2]; + unaligned_address = valid_address[2]; + end + end + end + 2'b10: begin + + valid_data[0] = fetched_data[31:0]; + valid_address[0] = {fetched_address[31:3], 3'b100}; + valid_fetch[0] = 1; + + valid_data[1] = fetched_data[47:16]; + valid_address[1] = {fetched_address[31:3], 3'b110}; + + if (fetched_data[1:0] != 3) begin + if (fetched_data[17:16] != 3) begin + valid_fetch[1] = 1; + end else begin + unalined_access = 1'b1; + unaligned_data = valid_data[1]; + unaligned_address = valid_address[1]; + end + end + end + 2'b11: begin + valid_data[0] = fetched_data[31:0]; + valid_address[0] = {fetched_address[31:3], 3'b110}; + if (fetched_data[1:0] != 3) begin + valid_fetch[0] = 1; + end else begin + unalined_access = 1'b1; + unaligned_data = valid_data[0]; + unaligned_address = valid_address[0]; + end + end + endcase + end + + endfunction : align_fetched_data_64 + +endclass : uvme_cva6_frontend_model_c + +`endif // __UVME_CVA6_FRONTEND_MODEL_SV__ + diff --git a/verif/env/uvme/frontend/uvme_cva6_frontend_mon.sv b/verif/env/uvme/frontend/uvme_cva6_frontend_mon.sv new file mode 100644 index 0000000000..9948411346 --- /dev/null +++ b/verif/env/uvme/frontend/uvme_cva6_frontend_mon.sv @@ -0,0 +1,204 @@ +// Copyright 2024 Thales DIS SAS +// +// Licensed under the Solderpad Hardware Licence, 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 +// +// https://solderpad.org/licenses/ +// +// 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. +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0 +// +// Original Author: Alae Eddine EZ ZEJJARI (alae-eddine.ez-zejjari@external.thalesgroup.com) – sub-contractor MU-Electronics for Thales group + + +`ifndef __UVME_CVA6_FRONTEND_MON_SV__ +`define __UVME_CVA6_FRONTEND_MON_SV__ + + +// Frontend transaction +class uvme_cva6_frontend_transaction_c extends uvm_sequence_item; + + logic flush; + logic flush_bp; + + uvme_frontend_icache_req_t icache_req; + uvme_frontend_icache_rsp_t icache_rsp; + + uvme_frontend_bp_resolve_t resolve_branch; + + logic fetch_valid[RTLCVA6Cfg.NrIssuePorts]; + logic fetch_ready[RTLCVA6Cfg.NrIssuePorts]; + uvme_frontend_fetched_data_t fetch_instr[RTLCVA6Cfg.NrIssuePorts]; + + logic boot_valid; + + logic eret; + logic[RTLCVA6Cfg.VLEN-1:0] epc; + + logic ex_valid; + logic halt; + logic[RTLCVA6Cfg.VLEN-1:0] trap_vector_base; + + logic set_pc_commit; + logic[RTLCVA6Cfg.VLEN-1:0] pc_commit; + + logic set_debug_pc; + + `uvm_object_utils_begin(uvme_cva6_frontend_transaction_c) + `uvm_field_int ( flush , UVM_DEFAULT | UVM_BIN) + `uvm_field_int ( flush_bp , UVM_DEFAULT | UVM_BIN) + `uvm_object_utils_end + + /** + * Default constructor. + */ + function new(string name="uvme_cva6_frontend_transaction"); + super.new(name); + endfunction + +endclass : uvme_cva6_frontend_transaction_c + + +// Frontend Monitor +// Instantiate a virtual interface of the frontend +// Capture all the necessary signals in a frontend transaction and send them through an analysis port. +class uvme_cva6_frontend_mon_c extends uvm_monitor; + + `uvm_component_utils(uvme_cva6_frontend_mon_c) + + // Objects + uvme_cva6_cfg_c cfg; + + // Analysis Ports + uvm_analysis_port #(uvme_cva6_frontend_transaction_c) frontend_packets_collected; + + // Handle to agent switch interface + virtual uvmt_frontend_intf frontend_vif; + + event reset_asserted ; + event reset_deasserted ; + + /** + * Constructor + */ + function new(string name="uvme_cva6_frontend_mon", uvm_component parent=null); + super.new(name, parent); + endfunction + + /** + * Build Phase + */ + function void build_phase(uvm_phase phase); + + super.build_phase(phase); + + // Instantiation of the uvm_analysis_port + frontend_packets_collected = new("frontend_packets_collected" , this) ; + + if (!uvm_config_db#(virtual uvmt_frontend_intf)::get(this, "", "cva6_frontend_bus", frontend_vif)) begin + `uvm_fatal("VIF", $sformatf("Could not find vif handle of type %s in uvm_config_db", $typename(frontend_vif))) + end + else begin + `uvm_info("VIF", $sformatf("Found vif handle of type %s in uvm_config_db", $typename(frontend_vif)), UVM_DEBUG) + end + + void'(uvm_config_db#(uvme_cva6_cfg_c)::get(this, "", "cfg", cfg)); + if (!cfg) + `uvm_fatal("CFG", "Configuration handle is null") + + endfunction : build_phase + + /** + * Pre reset phase + */ + virtual task pre_reset_phase(uvm_phase phase); + -> reset_asserted; + endtask : pre_reset_phase + + /** + * Reset phase + */ + virtual task reset_phase(uvm_phase phase); + super.reset_phase(phase); + endtask : reset_phase + + /** + * Post reset phase + */ + virtual task post_reset_phase(uvm_phase phase); + -> reset_deasserted; + endtask : post_reset_phase + + /** + * Run Phase + */ + task run_phase(uvm_phase phase); + forever begin + @(reset_deasserted); + fork + receive_frontend_transaction_task(phase); + join_none + @(reset_asserted); + disable fork; + end + endtask : run_phase + + /** + * Task receive frontend request and response + */ + task receive_frontend_transaction_task(uvm_phase phase); + uvme_cva6_frontend_transaction_c frontend_transaction; + int predict; + int boot_valid = 1; + + forever begin + @(posedge frontend_vif.clk); + frontend_transaction = new(); + frontend_transaction.flush = frontend_vif.flush_i; + frontend_transaction.flush_bp = frontend_vif.flush_bp_i; + frontend_transaction.icache_rsp.req = frontend_vif.icache_dreq_o.req; + frontend_transaction.icache_rsp.kill_s1 = frontend_vif.icache_dreq_o.kill_s1; + frontend_transaction.icache_rsp.kill_s2 = frontend_vif.icache_dreq_o.kill_s2; + frontend_transaction.icache_rsp.vaddr = frontend_vif.icache_dreq_o.vaddr; + frontend_transaction.icache_req.ready = frontend_vif.icache_dreq_i.ready; + frontend_transaction.icache_req.valid = frontend_vif.icache_dreq_i.valid; + frontend_transaction.icache_req.data = frontend_vif.icache_dreq_i.data; + frontend_transaction.icache_req.vaddr = frontend_vif.icache_dreq_i.vaddr; + frontend_transaction.resolve_branch = frontend_vif.resolved_branch_i; + for(int i = 0; i < RTLCVA6Cfg.NrIssuePorts; i++) begin + frontend_transaction.fetch_valid[i] = frontend_vif.fetch_entry_valid_o[i]; + frontend_transaction.fetch_ready[i] = frontend_vif.fetch_entry_ready_i[i]; + frontend_transaction.fetch_instr[i].address = frontend_vif.fetch_entry_o[i].address; + frontend_transaction.fetch_instr[i].instruction = frontend_vif.fetch_entry_o[i].instruction; + predict = frontend_vif.fetch_entry_o[i].branch_predict.cf; + frontend_transaction.fetch_instr[i].predict = predict; + frontend_transaction.fetch_instr[i].predicted_address = frontend_vif.fetch_entry_o[i].branch_predict.predict_address; + end + frontend_transaction.eret = frontend_vif.eret_i; + frontend_transaction.epc = frontend_vif.epc_i; + frontend_transaction.ex_valid = frontend_vif.ex_valid_i; + frontend_transaction.halt = frontend_vif.halt_i; + frontend_transaction.trap_vector_base = frontend_vif.trap_vector_base_i; + frontend_transaction.set_pc_commit = frontend_vif.set_pc_commit_i; + frontend_transaction.pc_commit = frontend_vif.pc_commit_i; + frontend_transaction.set_debug_pc = frontend_vif.set_debug_pc_i; + if(boot_valid == 1) begin + frontend_transaction.boot_valid = 1; + end else begin + frontend_transaction.boot_valid = 0; + end + boot_valid = 0; + + frontend_packets_collected.write(frontend_transaction); + end // forever + endtask : receive_frontend_transaction_task +endclass : uvme_cva6_frontend_mon_c + + +`endif // __UVME_CVA6_FRONTEND_MON_SV__ diff --git a/verif/env/uvme/frontend/uvme_cva6_frontend_sb.sv b/verif/env/uvme/frontend/uvme_cva6_frontend_sb.sv new file mode 100644 index 0000000000..997e1a5524 --- /dev/null +++ b/verif/env/uvme/frontend/uvme_cva6_frontend_sb.sv @@ -0,0 +1,170 @@ +// Copyright 2024 Thales DIS SAS +// +// Licensed under the Solderpad Hardware Licence, 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 +// +// https://solderpad.org/licenses/ +// +// 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. +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0 +// +// Original Author: Alae Eddine EZ ZEJJARI (alae-eddine.ez-zejjari@external.thalesgroup.com) – sub-contractor MU-Electronics for Thales group + + +`ifndef __UVME_CVA6_FRONTEND_SB_SV__ +`define __UVME_CVA6_FRONTEND_SB_SV__ + + +// Frontend Scoreboard +// The scoreboard contains a frontend model and monitor of the Frontend interface +// The scoreboard captures the decoded committed instructions using an analysis port and counts all instruction types. +// It also captures transactions monitored through an analysis port and counts all types of instructions sent to the decode stage, requests sent to the cache, and cache responses. +class uvme_cva6_frontend_sb_c extends uvm_scoreboard; + + // Objects + uvme_cva6_cfg_c cfg; + + uvm_tlm_analysis_fifo#(uvma_isacov_mon_trn_c) isa_trn_fifo; + uvm_tlm_analysis_fifo#(uvme_cva6_frontend_transaction_c) frontend_trn_fifo; + uvma_isacov_mon_trn_c#(ILEN,XLEN) isa_instr; + uvme_cva6_frontend_transaction_c frontend_instr; + + uvme_cva6_frontend_model_c frontend_model; + uvme_cva6_frontend_mon_c frontend_mon; + + int fetched_inst_count; + int req_cache_inst_count; + int cached_inst_count; + int axi_inst_count; + int mispredict_count; + int branch_inst_count; + int branch_mispredict_count; + int ret_inst_count; + int ret_mispredict_count; + int jump_inst_count; + int jump_mispredict_count; + int isa_inst_count; + + `uvm_component_utils_begin(uvme_cva6_frontend_sb_c) + `uvm_component_utils_end + + /** + * Default constructor. + */ + function new(string name="uvme_cva6_frontend_sb_c", uvm_component parent=null); + super.new(name, parent); + endfunction + + /** + * Create and configures sub-scoreboards via: + */ + virtual function void build_phase(uvm_phase phase); + + void'(uvm_config_db#(uvme_cva6_cfg_c)::get(this, "", "cfg", cfg)); + if (!cfg) + `uvm_fatal("CFG", "Configuration handle is null") + + isa_trn_fifo = new("isa_trn_fifo", this); + frontend_trn_fifo = new("frontend_trn_fifo", this); + + this.frontend_mon = uvme_cva6_frontend_mon_c::type_id::create("frontend_mon", this); + if(cfg.enable_frontend_model) begin + this.frontend_model = uvme_cva6_frontend_model_c::type_id::create("frontend_model", this); + end + + endfunction + + /** + * Connect the analysis port + */ + function void connect_phase(uvm_phase phase); + + super.connect_phase(phase); + + //Establishing connections between monitor ports and scoreboard + frontend_mon.frontend_packets_collected.connect(this.frontend_trn_fifo.analysis_export); + if( cfg.enable_frontend_model) begin + //Establishing connections between monitor ports and model if the model is enabled + this.frontend_mon.frontend_packets_collected.connect(frontend_model.mon2mod_export); + `uvm_info(get_type_name(), $sformatf("FRENTEND MODEL IS ACTIVE"), UVM_LOW) + end + + endfunction + + /** + * Frontend-scoreboard run phase. + */ + task run_phase(uvm_phase phase); + super.run_phase(phase); + fork + begin + // Count the instruction coming from the isacove + forever begin + isa_trn_fifo.get(isa_instr); + isa_inst_count++; + if(isa_instr.instr.group == BRANCH_GROUP) + branch_inst_count++; + else if(isa_instr.instr.group == RET_GROUP) + ret_inst_count++; + else if(isa_instr.instr.group == JUMP_GROUP) + jump_inst_count++; + end + end + begin + // Count the instruction coming from the Frontend + forever begin + frontend_trn_fifo.get(frontend_instr); + scoreboarding_frontend(); + end + end + join_any + endtask + + + function void scoreboarding_frontend(); + + if(frontend_instr.icache_req.valid) cached_inst_count++; + + if(frontend_instr.icache_rsp.req && frontend_instr.icache_req.ready) req_cache_inst_count++; + + for(int i = 0; i < RTLCVA6Cfg.NrIssuePorts; i++) begin + if(frontend_instr.fetch_valid[i] && frontend_instr.fetch_ready[i]) fetched_inst_count++; + end + + if(frontend_instr.resolve_branch.is_mispredict) mispredict_count++; + + if(frontend_instr.resolve_branch.is_mispredict && frontend_instr.resolve_branch.cf_type == 1) branch_mispredict_count++; + + if(frontend_instr.resolve_branch.is_mispredict && frontend_instr.resolve_branch.cf_type == 4) ret_mispredict_count++; + + if(frontend_instr.resolve_branch.is_mispredict && (frontend_instr.resolve_branch.cf_type == 2 || frontend_instr.resolve_branch.cf_type == 3)) jump_mispredict_count++; + + endfunction : scoreboarding_frontend + + /** + * Disply the final results of the scoreboarding + */ + function void check_phase(uvm_phase phase); + + super.check_phase(phase); + `uvm_info("Frontend scorboard checks", $sformatf("number of fetch requested by the frotend req_cache_inst_count = %d", req_cache_inst_count), UVM_NONE) + `uvm_info("Frontend scorboard checks", $sformatf("number of fetch sent by the cache cached_inst_count = %d", cached_inst_count), UVM_NONE) + `uvm_info("Frontend scorboard checks", $sformatf("number of instruction sent to decode fetched_inst_count = %d", fetched_inst_count), UVM_NONE) + `uvm_info("Frontend scorboard checks", $sformatf("number of instruction seen in the isa isa_inst_count = %d", isa_inst_count), UVM_NONE) + `uvm_info("Frontend scorboard checks", $sformatf("number of mispredict seen in the execut stage mispredict_count = %d", mispredict_count), UVM_NONE) + `uvm_info("Frontend scorboard checks", $sformatf("number of branch instruction seen in the isa branch_inst_count = %d", branch_inst_count), UVM_NONE) + `uvm_info("Frontend scorboard checks", $sformatf("number of branch mispredict seen in the execute stage branch_commit_count = %d", branch_mispredict_count), UVM_NONE) + `uvm_info("Frontend scorboard checks", $sformatf("number of ret instruction seen in the isa ret_inst_count = %d", ret_inst_count), UVM_NONE) + `uvm_info("Frontend scorboard checks", $sformatf("number of ret mispredict seen in the execute stage ret_commit_count = %d", ret_mispredict_count), UVM_NONE) + `uvm_info("Frontend scorboard checks", $sformatf("number of ret instruction seen in the isa jump_inst_count = %d", jump_inst_count), UVM_NONE) + `uvm_info("Frontend scorboard checks", $sformatf("number of jump mispredict seen in the execute stage jump_mispredict_count = %d", jump_mispredict_count), UVM_NONE) + endfunction + +endclass : uvme_cva6_frontend_sb_c +`endif // __UVME_CVA6_FRONTEND_SB_SV__ diff --git a/verif/env/uvme/uvme_cva6_cfg.sv b/verif/env/uvme/uvme_cva6_cfg.sv index dfcbbe7932..711f4dd647 100644 --- a/verif/env/uvme/uvme_cva6_cfg.sv +++ b/verif/env/uvme/uvme_cva6_cfg.sv @@ -65,6 +65,8 @@ class uvme_cva6_cfg_c extends uvma_core_cntrl_cfg_c; // Software interrupt supported rand bit sw_int_supported; + rand bit enable_frontend_model; + `uvm_object_utils_begin(uvme_cva6_cfg_c) `uvm_field_int ( enabled , UVM_DEFAULT ) `uvm_field_enum(uvm_active_passive_enum, is_active , UVM_DEFAULT ) @@ -103,6 +105,7 @@ class uvme_cva6_cfg_c extends uvma_core_cntrl_cfg_c; soft trn_log_enabled == 1; soft force_disable_csr_checks == 0; soft sys_clk_period == uvme_cva6_sys_default_clk_period; // see uvme_cva6_constants.sv + soft enable_frontend_model == 1; // see uvme_cva6_constants.sv } constraint cva6_riscv_cons { diff --git a/verif/env/uvme/uvme_cva6_constants.sv b/verif/env/uvme/uvme_cva6_constants.sv index 4ee516292c..a678fec062 100644 --- a/verif/env/uvme/uvme_cva6_constants.sv +++ b/verif/env/uvme/uvme_cva6_constants.sv @@ -26,6 +26,8 @@ parameter uvme_cva6_debug_default_clk_period = 10_000; // 10ns parameter XLEN = 32; parameter ILEN = 32; +parameter FETCH_FIFO_DEPTH = 4; +parameter ADDRESS_FIFO_DEPTH = 2; // Control how often to print core scoreboard checked heartbeat messages diff --git a/verif/env/uvme/uvme_cva6_env.sv b/verif/env/uvme/uvme_cva6_env.sv index 12ebc85adf..50c2349eea 100644 --- a/verif/env/uvme/uvme_cva6_env.sv +++ b/verif/env/uvme/uvme_cva6_env.sv @@ -360,6 +360,7 @@ function void uvme_cva6_env_c::connect_scoreboard(); if (cfg.scoreboard_enabled) begin isacov_agent.monitor.ap.connect(sb.instr_trn_fifo.analysis_export); + isacov_agent.monitor.ap.connect(sb.frontend_sb.isa_trn_fifo.analysis_export); end endfunction: connect_scoreboard diff --git a/verif/env/uvme/uvme_cva6_pkg.flist b/verif/env/uvme/uvme_cva6_pkg.flist index a4cb9fe372..bf60fae6d6 100644 --- a/verif/env/uvme/uvme_cva6_pkg.flist +++ b/verif/env/uvme/uvme_cva6_pkg.flist @@ -22,6 +22,7 @@ +incdir+${CVA6_UVME_PATH}/cov +incdir+${CVA6_UVME_PATH}/vseq +incdir+${CVA6_UVME_PATH}/uvma_interrupt ++incdir+${CVA6_UVME_PATH}/frontend // Files ${CVA6_UVME_PATH}/uvme_cva6_pkg.sv diff --git a/verif/env/uvme/uvme_cva6_pkg.sv b/verif/env/uvme/uvme_cva6_pkg.sv index 68b978bac9..6de71f5395 100644 --- a/verif/env/uvme/uvme_cva6_pkg.sv +++ b/verif/env/uvme/uvme_cva6_pkg.sv @@ -99,6 +99,9 @@ package uvme_cva6_pkg; `include "reg/cva6_csr_reg_predictor.sv" // Environment components + `include "uvme_cva6_frontend_mon.sv" + `include "uvme_cva6_frontend_model.sv" + `include "uvme_cva6_frontend_sb.sv" `include "uvma_cva6_core_cntrl_drv.sv" `include "uvma_cva6_core_cntrl_agent.sv" `include "uvme_cva6_sb.sv" diff --git a/verif/env/uvme/uvme_cva6_sb.sv b/verif/env/uvme/uvme_cva6_sb.sv index ddc7eeeca3..6411ef988c 100644 --- a/verif/env/uvme/uvme_cva6_sb.sv +++ b/verif/env/uvme/uvme_cva6_sb.sv @@ -36,6 +36,7 @@ class uvme_cva6_sb_c extends uvm_scoreboard; // Ex: uvme_cva6_sb_simplex_c egress_sb; // uvme_cva6_sb_simplex_c ingress_sb; uvmc_rvfi_scoreboard_c#(ILEN,XLEN) m_rvfi_scoreboard; + uvme_cva6_frontend_sb_c frontend_sb; // TLM uvm_tlm_analysis_fifo#(uvma_isacov_mon_trn_c) instr_trn_fifo; @@ -196,6 +197,9 @@ function void uvme_cva6_sb_c::create_sbs(); // TODO Implement uvme_cva6_sb_c::create_sbs() // Ex: egress_sb = uvme_cva6_sb_simplex_c::type_id::create("egress_sb" , this); // ingress_sb = uvme_cva6_sb_simplex_c::type_id::create("ingress_sb", this); + + frontend_sb = uvme_cva6_frontend_sb_c::type_id::create("frontend_sb", this); + if (cfg.tandem_enabled) m_rvfi_scoreboard = uvmc_rvfi_scoreboard_c#(ILEN,XLEN)::type_id::create("m_rvfi_scoreboard", this); diff --git a/verif/env/uvme/uvme_cva6_tdefs.sv b/verif/env/uvme/uvme_cva6_tdefs.sv index 890e1e2707..4059241705 100644 --- a/verif/env/uvme/uvme_cva6_tdefs.sv +++ b/verif/env/uvme/uvme_cva6_tdefs.sv @@ -110,4 +110,52 @@ typedef struct { bit[31:0] pc; } ins_t; +typedef struct packed { + logic ready; // icache is ready + logic valid; // signals a valid read + logic [RTLCVA6Cfg.FETCH_WIDTH-1:0] data; // 2+ cycle out: tag + logic [RTLCVA6Cfg.VLEN-1:0] vaddr; // virtual address out +} uvme_frontend_icache_req_t; + +typedef struct packed { + logic req; // we request a new word + logic kill_s1; // kill the current request + logic kill_s2; // kill the last request + logic [RTLCVA6Cfg.VLEN-1:0] vaddr; // 1st cycle: 12 bit index is taken for lookup +} uvme_frontend_icache_rsp_t; + +typedef enum logic [2:0] { + NoCF, // No control flow prediction + Branch, // Branch + Jump, // Jump to address from immediate + JumpR, // Jump to address from registers + Return // Return Address Prediction +} uvme_frontend_prediction_t; + +typedef struct packed { + logic [RTLCVA6Cfg.VLEN-1:0] address; // instruction word + logic [31:0] instruction; // instruction word + uvme_frontend_prediction_t predict; // this field contains branch prediction information regarding the forward branch path + logic [RTLCVA6Cfg.VLEN-1:0] predicted_address; +} uvme_frontend_fetched_data_t; + +typedef struct packed{ + logic valid; // prediction with all its values is valid + logic [RTLCVA6Cfg.VLEN-1:0] pc; // PC of predict or mis-predict + logic [RTLCVA6Cfg.VLEN-1:0] target_address; // target address at which to jump, or not + logic is_mispredict; // set if this was a mis-predict + logic is_taken; // branch is taken + uvme_frontend_prediction_t cf_type; // Type of control flow change +} uvme_frontend_bp_resolve_t; + +typedef struct packed { + logic [1:0] saturation; + logic valid; +} uvme_frontend_bht_t; + +typedef struct packed { + logic [RTLCVA6Cfg.VLEN-1:0] predicted_address; + logic valid; +} uvme_frontend_ras_t; + `endif // __UVME_CVA6_TDEFS_SV__ diff --git a/verif/tb/uvmt/cva6_tb_wrapper.sv b/verif/tb/uvmt/cva6_tb_wrapper.sv index 6a063ef844..fc2f32f4e2 100644 --- a/verif/tb/uvmt/cva6_tb_wrapper.sv +++ b/verif/tb/uvmt/cva6_tb_wrapper.sv @@ -76,7 +76,8 @@ module cva6_tb_wrapper import uvmt_cva6_pkg::*; #( uvma_debug_if debug_if, uvma_axi_intf axi_slave, uvmt_axi_switch_intf axi_switch_vif, - uvmt_default_inputs_intf default_inputs_vif + uvmt_default_inputs_intf default_inputs_vif, + uvmt_frontend_intf cva6_frontend_bus ); ariane_axi::req_t axi_ariane_req; @@ -124,6 +125,25 @@ module cva6_tb_wrapper import uvmt_cva6_pkg::*; #( end end + assign cva6_frontend_bus.boot_addr_i = i_cva6.i_frontend.boot_addr_i; + assign cva6_frontend_bus.flush_bp_i = i_cva6.i_frontend.flush_bp_i; + assign cva6_frontend_bus.flush_i = i_cva6.i_frontend.flush_i; + assign cva6_frontend_bus.halt_i = i_cva6.i_frontend.halt_i; + assign cva6_frontend_bus.set_pc_commit_i = i_cva6.i_frontend.set_pc_commit_i; + assign cva6_frontend_bus.pc_commit_i = i_cva6.i_frontend.pc_commit_i; + assign cva6_frontend_bus.ex_valid_i = i_cva6.i_frontend.ex_valid_i; + assign cva6_frontend_bus.resolved_branch_i = i_cva6.i_frontend.resolved_branch_i; + assign cva6_frontend_bus.eret_i = i_cva6.i_frontend.eret_i; + assign cva6_frontend_bus.epc_i = i_cva6.i_frontend.epc_i; + assign cva6_frontend_bus.trap_vector_base_i = i_cva6.i_frontend.trap_vector_base_i; + assign cva6_frontend_bus.set_debug_pc_i = i_cva6.i_frontend.set_debug_pc_i; + assign cva6_frontend_bus.debug_mode_i = i_cva6.i_frontend.debug_mode_i; + assign cva6_frontend_bus.icache_dreq_o = i_cva6.i_frontend.icache_dreq_o; + assign cva6_frontend_bus.icache_dreq_i = i_cva6.i_frontend.icache_dreq_i; + assign cva6_frontend_bus.fetch_entry_o = i_cva6.i_frontend.fetch_entry_o; + assign cva6_frontend_bus.fetch_entry_valid_o = i_cva6.i_frontend.fetch_entry_valid_o; + assign cva6_frontend_bus.fetch_entry_ready_i = i_cva6.i_frontend.fetch_entry_ready_i; + //---------------------------------------------------------------------------- // RVFI //---------------------------------------------------------------------------- diff --git a/verif/tb/uvmt/uvmt_cva6_dut_wrap.sv b/verif/tb/uvmt/uvmt_cva6_dut_wrap.sv index 500afcf45e..e004a003cc 100644 --- a/verif/tb/uvmt/uvmt_cva6_dut_wrap.sv +++ b/verif/tb/uvmt/uvmt_cva6_dut_wrap.sv @@ -32,6 +32,7 @@ module uvmt_cva6_dut_wrap # ( uvma_axi_intf axi_if, uvmt_axi_switch_intf axi_switch_vif, uvmt_default_inputs_intf default_inputs_vif, + uvmt_frontend_intf frontend_intf, uvme_cva6_core_cntrl_if core_cntrl_if, uvma_interrupt_if interrupt_vif, uvma_debug_if debug_if, @@ -64,6 +65,7 @@ module uvmt_cva6_dut_wrap # ( .axi_slave ( axi_if ), .axi_switch_vif ( axi_switch_vif ), .default_inputs_vif ( default_inputs_vif ), + .cva6_frontend_bus ( frontend_intf ), .tb_exit_o ( tb_exit_o ), .rvfi_csr_o ( rvfi_csr_o ), .rvfi_o ( rvfi_o ) diff --git a/verif/tb/uvmt/uvmt_cva6_pkg.sv b/verif/tb/uvmt/uvmt_cva6_pkg.sv index 9641dfc41c..0e0b93108c 100644 --- a/verif/tb/uvmt/uvmt_cva6_pkg.sv +++ b/verif/tb/uvmt/uvmt_cva6_pkg.sv @@ -30,6 +30,7 @@ `include "uvmt_axi_switch_intf.sv" `include "uvmt_default_inputs_intf.sv" `include "uvma_axi_intf.sv" +`include "uvmt_frontend_intf.sv" /** * Encapsulates all the types and test cases for the verification of an diff --git a/verif/tb/uvmt/uvmt_cva6_tb.sv b/verif/tb/uvmt/uvmt_cva6_tb.sv index 5955f53697..a25df34fae 100644 --- a/verif/tb/uvmt/uvmt_cva6_tb.sv +++ b/verif/tb/uvmt/uvmt_cva6_tb.sv @@ -81,6 +81,14 @@ module uvmt_cva6_tb; uvmt_default_inputs_intf default_inputs_vif(); + + uvmt_frontend_intf #( + CVA6Cfg + ) cva6_frontend_bus( + clknrst_if.clk, + clknrst_if.reset_n + ); + //bind assertion module for axi interface bind uvmt_cva6_dut_wrap uvmt_axi_assert #(CVA6Cfg.DCacheType) axi_assert(.axi_assert_if(axi_if)); @@ -120,6 +128,7 @@ module uvmt_cva6_tb; .axi_if (axi_if), .axi_switch_vif (axi_switch_vif), .default_inputs_vif (default_inputs_vif), + .frontend_intf (cva6_frontend_bus), .core_cntrl_if(core_cntrl_if), .interrupt_vif(interrupt_vif), .tb_exit_o(tb_exit_if.tb_exit_o), @@ -384,6 +393,7 @@ module uvmt_cva6_tb; uvm_config_db#(virtual uvma_interrupt_if)::set(.cntxt(null), .inst_name("*"), .field_name("interrupt_vif"), .value(interrupt_vif)); uvm_config_db#(virtual uvmt_tb_exit_if)::set(.cntxt(null), .inst_name("*"), .field_name("tb_exit_vif"), .value(tb_exit_if)); + uvm_config_db#(virtual uvmt_frontend_intf)::set(.cntxt(null), .inst_name("*"), .field_name("cva6_frontend_bus"), .value(cva6_frontend_bus)); // DUT and ENV parameters uvm_config_db#(int)::set(.cntxt(null), .inst_name("*"), .field_name("ENV_PARAM_INSTR_ADDR_WIDTH"), .value(ENV_PARAM_INSTR_ADDR_WIDTH) ); diff --git a/verif/tb/uvmt/uvmt_frontend_intf.sv b/verif/tb/uvmt/uvmt_frontend_intf.sv new file mode 100644 index 0000000000..c1e5b52ec6 --- /dev/null +++ b/verif/tb/uvmt/uvmt_frontend_intf.sv @@ -0,0 +1,112 @@ +// Copyright 2024 Thales DIS SAS +// +// Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0 +// You may obtain a copy of the License at https://solderpad.org/licenses/ +// +// Original Author: Alae Eddine EZ ZEJJARI (alae-eddine.ez-zejjari@external.thalesgroup.com) – sub-contractor MU-Electronics for Thales group + +/**** Frontend interface with parametrized : ****/ + +interface uvmt_frontend_intf #( + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty +) ( + input bit clk, + input bit rst_n + ); + + typedef enum logic [2:0] { + NoCF, // No control flow prediction + Branch, // Branch + Jump, // Jump to address from immediate + JumpR, // Jump to address from registers + Return // Return Address Prediction + } cf_t; + + typedef struct packed { + logic [CVA6Cfg.XLEN-1:0] cause; // cause of exception + logic [CVA6Cfg.XLEN-1:0] tval; // additional information of causing exception (e.g.: instruction causing it), + // address of LD/ST fault + logic [CVA6Cfg.GPLEN-1:0] tval2; // additional information when the causing exception in a guest exception + logic [31:0] tinst; // transformed instruction information + logic gva; // signals when a guest virtual address is written to tval + logic valid; + } exception_t; + + typedef struct packed { + cf_t cf; // type of control flow prediction + logic [CVA6Cfg.VLEN-1:0] predict_address; // target address at which to jump, or not + } branchpredict_sbe_t; + + typedef struct packed{ + logic valid; // prediction with all its values is valid + logic [CVA6Cfg.VLEN-1:0] pc; // PC of predict or mis-predict + logic [CVA6Cfg.VLEN-1:0] target_address; // target address at which to jump, or not + logic is_mispredict; // set if this was a mis-predict + logic is_taken; // branch is taken + cf_t cf_type; // Type of control flow change + } bp_resolve_t; + + typedef struct packed { + logic req; // we request a new word + logic kill_s1; // kill the current request + logic kill_s2; // kill the last request + logic spec; // request is speculative + logic [CVA6Cfg.VLEN-1:0] vaddr; // 1st cycle: 12 bit index is taken for lookup + } icache_dreq_t; + + typedef struct packed { + logic ready; // icache is ready + logic valid; // signals a valid read + logic [CVA6Cfg.FETCH_WIDTH-1:0] data; // 2+ cycle out: tag + logic [CVA6Cfg.FETCH_USER_WIDTH-1:0] user; // User bits + logic [CVA6Cfg.VLEN-1:0] vaddr; // virtual address out + exception_t ex; // we've encountered an exception + } icache_drsp_t; + + typedef struct packed { + logic [CVA6Cfg.VLEN-1:0] address; // the address of the instructions from below + logic [31:0] instruction; // instruction word + branchpredict_sbe_t branch_predict; // this field contains branch prediction information regarding the forward branch path + exception_t ex; // this field contains exceptions which might have happened earlier, e.g.: fetch exceptions + } fetch_entry_t; + + // Next PC when reset - SUBSYSTEM + logic [CVA6Cfg.VLEN-1:0] boot_addr_i; + // Flush branch prediction - zero + logic flush_bp_i; + // Flush requested by FENCE, mis-predict and exception - CONTROLLER + logic flush_i; + // Halt requested by WFI and Accelerate port - CONTROLLER + logic halt_i; + // Set COMMIT PC as next PC requested by FENCE, CSR side-effect and Accelerate port - CONTROLLER + logic set_pc_commit_i; + // COMMIT PC - COMMIT + logic [CVA6Cfg.VLEN-1:0] pc_commit_i; + // Exception event - COMMIT + logic ex_valid_i; + // Mispredict event and next PC - EXECUTE + bp_resolve_t resolved_branch_i; + // Return from exception event - CSR + logic eret_i; + // Next PC when returning from exception - CSR + logic [CVA6Cfg.VLEN-1:0] epc_i; + // Next PC when jumping into exception - CSR + logic [CVA6Cfg.VLEN-1:0] trap_vector_base_i; + // Debug event - CSR + logic set_debug_pc_i; + // Debug mode state - CSR + logic debug_mode_i; + // Handshake between CACHE and FRONTEND (fetch) - CACHES + icache_dreq_t icache_dreq_o; + // Handshake between CACHE and FRONTEND (fetch) - CACHES + icache_drsp_t icache_dreq_i; + // Handshake's data between fetch and decode - ID_STAGE + fetch_entry_t [CVA6Cfg.NrIssuePorts:0] fetch_entry_o; + // Handshake's valid between fetch and decode - ID_STAGE + logic [CVA6Cfg.NrIssuePorts:0] fetch_entry_valid_o; + // Handshake's ready between fetch and decode - ID_STAGE + logic [CVA6Cfg.NrIssuePorts:0] fetch_entry_ready_i; + +endinterface : uvmt_frontend_intf