| /* |
| * Copyright 2018 Google LLC |
| * |
| * 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. |
| */ |
| |
| // This class provides some common routines for privileged mode operations |
| class riscv_privileged_common_seq extends uvm_sequence; |
| |
| riscv_instr_gen_config cfg; |
| riscv_privil_reg mstatus; |
| riscv_privil_reg mie; |
| riscv_privil_reg sstatus; |
| riscv_privil_reg sie; |
| riscv_privil_reg ustatus; |
| riscv_privil_reg uie; |
| |
| `uvm_object_utils(riscv_privileged_common_seq) |
| |
| function new(string name = ""); |
| super.new(name); |
| endfunction |
| |
| virtual function void enter_privileged_mode(input privileged_mode_t mode, |
| output string instrs[$]); |
| string label = format_string({"init_", mode.name(), ":"}, LABEL_STR_LEN); |
| string ret_instr[] = {"mret"}; |
| riscv_privil_reg regs[$]; |
| label = label.tolower(); |
| setup_mmode_reg(mode, regs); |
| if(mode != MACHINE_MODE) begin |
| setup_smode_reg(mode, regs); |
| setup_satp(instrs); |
| ret_instr.shuffle(); |
| end |
| gen_csr_instr(regs, instrs); |
| // Use mret/sret to switch to the target privileged mode |
| instrs.push_back(ret_instr[0]); |
| foreach(instrs[i]) begin |
| instrs[i] = {indent, instrs[i]}; |
| end |
| instrs.push_front(label); |
| endfunction |
| |
| virtual function void setup_mmode_reg(privileged_mode_t mode, ref riscv_privil_reg regs[$]); |
| mstatus = riscv_privil_reg::type_id::create("mstatus"); |
| mstatus.init_reg(MSTATUS); |
| if (cfg.randomize_csr) begin |
| mstatus.set_val(cfg.mstatus); |
| end |
| mstatus.set_field("MPRV", cfg.mstatus_mprv); |
| mstatus.set_field("MXR", cfg.mstatus_mxr); |
| mstatus.set_field("SUM", cfg.mstatus_sum); |
| mstatus.set_field("TVM", cfg.mstatus_tvm); |
| mstatus.set_field("FS", cfg.mstatus_fs); |
| if(XLEN==64) begin |
| mstatus.set_field("UXL", 2'b10); |
| mstatus.set_field("SXL", 2'b10); |
| end |
| mstatus.set_field("XS", 0); |
| mstatus.set_field("SD", 0); |
| mstatus.set_field("UIE", 0); |
| // Set the previous privileged mode as the target mode |
| mstatus.set_field("MPP", mode); |
| mstatus.set_field("SPP", 0); |
| // Enable interrupt |
| mstatus.set_field("MPIE", cfg.enable_interrupt); |
| mstatus.set_field("MIE", cfg.enable_interrupt); |
| mstatus.set_field("SPIE", cfg.enable_interrupt); |
| mstatus.set_field("SIE", cfg.enable_interrupt); |
| mstatus.set_field("UPIE", cfg.enable_interrupt); |
| mstatus.set_field("UIE", riscv_instr_pkg::support_umode_trap); |
| `uvm_info(`gfn, $sformatf("mstatus_val: 0x%0x", mstatus.get_val()), UVM_LOW) |
| regs.push_back(mstatus); |
| // Enable external and timer interrupt |
| if (MIE inside {implemented_csr}) begin |
| mie = riscv_privil_reg::type_id::create("mie"); |
| mie.init_reg(MIE); |
| if (cfg.randomize_csr) begin |
| mie.set_val(cfg.mie); |
| end |
| mie.set_field("UEIE", cfg.enable_interrupt); |
| mie.set_field("SEIE", cfg.enable_interrupt); |
| mie.set_field("MEIE", cfg.enable_interrupt); |
| mie.set_field("USIE", cfg.enable_interrupt); |
| mie.set_field("SSIE", cfg.enable_interrupt); |
| mie.set_field("MSIE", cfg.enable_interrupt); |
| mie.set_field("MTIE", cfg.enable_interrupt & cfg.enable_timer_irq); |
| mie.set_field("STIE", cfg.enable_interrupt & cfg.enable_timer_irq); |
| mie.set_field("UTIE", cfg.enable_interrupt & cfg.enable_timer_irq); |
| regs.push_back(mie); |
| end |
| endfunction |
| |
| virtual function void setup_smode_reg(privileged_mode_t mode, ref riscv_privil_reg regs[$]); |
| sstatus = riscv_privil_reg::type_id::create("sstatus"); |
| sstatus.init_reg(SSTATUS); |
| `DV_CHECK_RANDOMIZE_FATAL(sstatus, "cannot randomize sstatus") |
| if (cfg.randomize_csr) begin |
| sstatus.set_val(cfg.sstatus); |
| end |
| sstatus.set_field("SPIE", cfg.enable_interrupt); |
| sstatus.set_field("SIE", cfg.enable_interrupt); |
| sstatus.set_field("UPIE", cfg.enable_interrupt); |
| sstatus.set_field("UIE", riscv_instr_pkg::support_umode_trap); |
| if(XLEN==64) begin |
| sstatus.set_field("UXL", 2'b10); |
| end |
| sstatus.set_field("FS", cfg.mstatus_fs); |
| sstatus.set_field("XS", 0); |
| sstatus.set_field("SD", 0); |
| sstatus.set_field("UIE", 0); |
| sstatus.set_field("SPP", 0); |
| regs.push_back(sstatus); |
| // Enable external and timer interrupt |
| if (SIE inside {implemented_csr}) begin |
| sie = riscv_privil_reg::type_id::create("sie"); |
| sie.init_reg(SIE); |
| if (cfg.randomize_csr) begin |
| sie.set_val(cfg.sie); |
| end |
| sie.set_field("UEIE", cfg.enable_interrupt); |
| sie.set_field("SEIE", cfg.enable_interrupt); |
| sie.set_field("USIE", cfg.enable_interrupt); |
| sie.set_field("SSIE", cfg.enable_interrupt); |
| sie.set_field("STIE", cfg.enable_interrupt & cfg.enable_timer_irq); |
| sie.set_field("UTIE", cfg.enable_interrupt & cfg.enable_timer_irq); |
| regs.push_back(sie); |
| end |
| endfunction |
| |
| virtual function void setup_umode_reg(privileged_mode_t mode, ref riscv_privil_reg regs[$]); |
| ustatus = riscv_privil_reg::type_id::create("ustatus"); |
| ustatus.init_reg(USTATUS); |
| `DV_CHECK_RANDOMIZE_FATAL(ustatus, "cannot randomize ustatus") |
| if (cfg.randomize_csr) begin |
| ustatus.set_val(cfg.ustatus); |
| end |
| ustatus.set_field("UIE", cfg.enable_interrupt); |
| ustatus.set_field("UPIE", cfg.enable_interrupt); |
| regs.push_back(ustatus); |
| if (UIE inside {implemented_csr}) begin |
| uie = riscv_privil_reg::type_id::create("uie"); |
| uie.init_reg(UIE); |
| if (cfg.randomize_csr) begin |
| uie.set_val(cfg.uie); |
| end |
| uie.set_field("UEIE", cfg.enable_interrupt); |
| uie.set_field("USIE", cfg.enable_interrupt); |
| uie.set_field("UTIE", cfg.enable_interrupt & cfg.enable_timer_irq); |
| regs.push_back(uie); |
| end |
| endfunction |
| |
| virtual function void gen_csr_instr(riscv_privil_reg regs[$], ref string instrs[$]); |
| foreach(regs[i]) begin |
| instrs.push_back($sformatf("li x%0d, 0x%0x", cfg.gpr[0], regs[i].get_val())); |
| instrs.push_back($sformatf("csrw 0x%0x, x%0d # %0s", |
| regs[i].reg_name, cfg.gpr[0], regs[i].reg_name.name())); |
| end |
| endfunction |
| |
| virtual function void setup_satp(ref string instrs[$]); |
| riscv_privil_reg satp; |
| bit [XLEN-1:0] satp_ppn_mask; |
| if(SATP_MODE == BARE) return; |
| satp = riscv_privil_reg::type_id::create("satp"); |
| satp.init_reg(SATP); |
| satp.set_field("MODE", SATP_MODE); |
| instrs.push_back($sformatf("li x%0d, 0x%0x", cfg.gpr[0], satp.get_val())); |
| instrs.push_back($sformatf("csrw 0x%0x, x%0d // satp", SATP, cfg.gpr[0])); |
| satp_ppn_mask = '1 >> (XLEN - satp.get_field_by_name("PPN").bit_width); |
| // Load the root page table physical address |
| instrs.push_back($sformatf("la x%0d, page_table_0", cfg.gpr[0])); |
| // Right shift to get PPN at 4k granularity |
| instrs.push_back($sformatf("srli x%0d, x%0d, 12", cfg.gpr[0], cfg.gpr[0])); |
| instrs.push_back($sformatf("li x%0d, 0x%0x", cfg.gpr[1], satp_ppn_mask)); |
| instrs.push_back($sformatf("and x%0d, x%0d, x%0d", cfg.gpr[0], cfg.gpr[0], cfg.gpr[1])); |
| // Set the PPN field for SATP |
| instrs.push_back($sformatf("csrs 0x%0x, x%0d // satp", SATP, cfg.gpr[0])); |
| endfunction |
| |
| endclass |