blob: 50d9f4bd70c0c75b7ca9d7a94029360fddeecccb [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class chip_sw_rom_e2e_jtag_debug_vseq extends chip_sw_base_vseq;
`uvm_object_utils(chip_sw_rom_e2e_jtag_debug_vseq)
`uvm_object_new
virtual task pre_start();
cfg.chip_vif.tap_straps_if.drive(JtagTapRvDm);
super.pre_start();
endtask
virtual task body();
string elf_file = {cfg.sw_images[SwTypeRom], ".elf"};
string uart_msg_via_mem_access = "OK!";
string uart_msg_via_call = "GDB-OK!";
string uart_msg_received;
breakpoint_t breakpoints[$];
byte status;
super.body();
cfg.sw_test_status_vif.can_pass_only_in_test = 0;
// The steps in this test closely follow the gdb steps, which is embedded directly into the
// `opentitan_gdb_fpga_cw310_test` target `rom_e2e_debug_test_otp__<lc_state>` in the bazel
// BUILD file located at sw/device/silicon_creator/rom/e2e/BUILD.
// Halt the CPU before the first instruction is executed. The chip just came out of POR - so
// there is no need to issue an NDM reset again. Just wait for lc_ready signal and immediately
// issue a halt req.
cfg.debugger.set_elf_file(elf_file);
`DV_WAIT(cfg.chip_vif.pinmux_lc_hw_debug_en)
cfg.chip_vif.aon_clk_por_rst_if.wait_clks(1);
cfg.debugger.set_dmactive(1);
cfg.debugger.set_haltreq(1);
cfg.debugger.wait_cpu_halted();
cfg.debugger.set_haltreq(0);
// Verify that we are in ROM's reset vector.
begin
bit result;
longint unsigned addr, size;
result = dv_utils_pkg::sw_symbol_get_addr_size(.elf_file(elf_file),
.symbol("_rom_interrupt_vector_asm"), .does_not_exist_ok(0), .addr(addr), .size(size));
`DV_CHECK(result)
addr += 32 * 4; // 33rd entry in the reset vector.
`uvm_info(`gfn, $sformatf("CPU reset vector: 0x%0h", addr), UVM_LOW)
cfg.debugger.check_pc(.exp_addr(addr));
end
// Single step and verify we are in `_rom_start_boot` (this is where the reset vector jumps to).
cfg.debugger.step();
cfg.debugger.check_pc(.exp_symbol("_rom_start_boot"));
cfg.debugger.check_debug_cause(jtag_rv_debugger_pkg::RvDebugCauseStep);
cfg.debugger.info_breakpoints(breakpoints);
`DV_CHECK(breakpoints.size() == 0)
`uvm_info(`gfn, "Run until we check whether ROM execution is enabled", UVM_LOW)
cfg.debugger.add_breakpoint(.symbol("kRomStartBootMaybeHalt"));
cfg.debugger.continue_execution();
cfg.chip_vif.cpu_clk_rst_if.wait_clks(200);
cfg.debugger.wait_cpu_halted();
cfg.debugger.check_pc(.exp_symbol("kRomStartBootMaybeHalt"));
cfg.debugger.check_debug_cause(jtag_rv_debugger_pkg::RvDebugCauseTrigger);
`uvm_info(`gfn, "Set all but one available breakpoints", UVM_LOW)
cfg.debugger.add_breakpoint(.symbol("rom_main"));
cfg.debugger.add_breakpoint(.symbol("uart_init"));
cfg.debugger.info_breakpoints(breakpoints);
`DV_CHECK(breakpoints.size() == 3)
`uvm_info(`gfn, "Pretend execution is enabled", UVM_LOW)
cfg.debugger.write_pc(.symbol("kRomStartBootExecEn"));
`uvm_info(`gfn, "Continue until watchdog config", UVM_LOW)
cfg.debugger.add_breakpoint(.symbol("kRomStartWatchdogEnabled"));
cfg.debugger.info_breakpoints(breakpoints);
`DV_CHECK(breakpoints.size() == 4)
cfg.debugger.continue_execution();
cfg.chip_vif.cpu_clk_rst_if.wait_clks(200);
cfg.debugger.wait_cpu_halted();
cfg.debugger.check_pc(.exp_symbol("kRomStartWatchdogEnabled"));
cfg.debugger.check_debug_cause(jtag_rv_debugger_pkg::RvDebugCauseTrigger);
`uvm_info(`gfn, "Disable watchdog config", UVM_LOW)
cfg.debugger.mem_write(.addr(ral.aon_timer_aon.wdog_ctrl.get_address()),
.value_q({0}),
.status(status));
`DV_CHECK_EQ(status, 0)
cfg.debugger.delete_breakpoint(.index(3));
cfg.debugger.info_breakpoints(breakpoints);
`DV_CHECK(breakpoints.size() == 3)
`uvm_info(`gfn, "Continue until rom_main", UVM_LOW)
cfg.debugger.continue_execution();
cfg.chip_vif.cpu_clk_rst_if.wait_clks(200);
cfg.debugger.wait_cpu_halted();
cfg.debugger.check_pc(.exp_symbol("rom_main"));
cfg.debugger.check_debug_cause(jtag_rv_debugger_pkg::RvDebugCauseTrigger);
`uvm_info(`gfn, "Continue until uart_init", UVM_LOW)
cfg.debugger.continue_execution();
cfg.chip_vif.cpu_clk_rst_if.wait_clks(200);
cfg.debugger.wait_cpu_halted();
cfg.debugger.check_pc(.exp_symbol("uart_init"));
cfg.debugger.check_debug_cause(jtag_rv_debugger_pkg::RvDebugCauseTrigger);
cfg.debugger.finish();
configure_uart_agent(.uart_idx(ROM_CONSOLE_UART), .enable(1));
fork get_uart_tx_items(ROM_CONSOLE_UART); join_none
`uvm_info(`gfn, "Read and write GPRs and selected CPU CSRs", UVM_LOW)
test_cpu_gprs_csrs_access();
`uvm_info(`gfn, "Read and write memory (both SRAM and device)", UVM_LOW)
test_chip_mem_access();
`uvm_info(`gfn, "Manually write bytes to UART0", UVM_LOW)
foreach (uart_msg_via_mem_access[i]) begin
cfg.debugger.mem_write(.addr(ral.uart0.wdata.get_address()),
.value_q({BUS_DW'(uart_msg_via_mem_access[i])}),
.status(status));
`DV_CHECK_EQ(status, 0)
end
`uvm_info(`gfn, "Execute code from GDB with `call` command", UVM_LOW)
foreach (uart_msg_via_call[i]) begin
cfg.debugger.call(.symbol("uart_putchar"), .args({BUS_DW'(uart_msg_via_call[i])}));
end
`uvm_info(`gfn, "Verify UART messages received over the TX port", UVM_LOW)
configure_uart_agent(.uart_idx(ROM_CONSOLE_UART), .enable(0));
`DV_WAIT(uart_tx_data_q.size() == (uart_msg_via_mem_access.len() + uart_msg_via_call.len()))
uart_msg_received = {>>{uart_tx_data_q}};
`DV_CHECK_STREQ(uart_msg_received, {uart_msg_via_mem_access, uart_msg_via_call})
`uvm_info(`gfn, "Have the CPU write pass pattern", UVM_LOW)
cfg.debugger.mem_write(.addr(SW_DV_TEST_STATUS_ADDR),
.value_q({sw_test_status_pkg::SwTestStatusPassed}),
.status(status));
`DV_CHECK_EQ(status, 0)
`uvm_info(`gfn, "Silence the CPU by jumping to boot halted loop", UVM_LOW)
cfg.debugger.write_pc(.symbol("kRomStartBootHalted"));
cfg.debugger.continue_execution();
endtask
// Write-read check CPU GPRs and CSRs.
//
// Save and restore original values at the end, since it may corrupt the flow of execution.
task test_cpu_gprs_csrs_access();
logic [BUS_DW-1:0] orig_data[jtag_rv_debugger_pkg::abstract_cmd_regno_t];
logic [BUS_DW-1:0] new_data[jtag_rv_debugger_pkg::abstract_cmd_regno_t];
jtag_rv_debugger_pkg::abstract_cmd_err_e status;
jtag_rv_debugger_pkg::abstract_cmd_regno_t csrs[$] = '{
// Hand-picked CSRs that are RW.
jtag_rv_debugger_pkg::RvCoreCsrDScratch0,
jtag_rv_debugger_pkg::RvCoreCsrDScratch1,
ibex_pkg::CSR_MSCRATCH,
ibex_pkg::CSR_MCYCLEH,
ibex_pkg::CSR_MINSTRETH
};
// Add the X1-X31 GPRs starting at RA. Skip S0 and A0 since it is actively used by the CPU.
for (jtag_rv_debugger_pkg::abstract_cmd_regno_t gpr = jtag_rv_debugger_pkg::RvCoreCsrGprRa;
gpr <= jtag_rv_debugger_pkg::RvCoreCsrGprT6;
gpr++) begin
if (gpr inside {jtag_rv_debugger_pkg::RvCoreCsrGprS0,
jtag_rv_debugger_pkg::RvCoreCsrGprA0}) continue;
csrs.push_back(gpr);
end
`uvm_info(`gfn, $sformatf("Testing CPU registers:\n%p", csrs), UVM_MEDIUM)
// Read original CSR values.
csrs.shuffle();
foreach (csrs[i]) begin
logic [BUS_DW-1:0] value_q[$];
cfg.debugger.abstract_cmd_reg_read(.regno(csrs[i]), .value_q(value_q), .status(status));
`DV_CHECK_EQ(status, jtag_rv_debugger_pkg::AbstractCmdErrNone)
orig_data[csrs[i]] = value_q[0];
end
// Write new random values.
csrs.shuffle();
foreach (csrs[i]) begin
logic [BUS_DW-1:0] data;
`DV_CHECK_STD_RANDOMIZE_FATAL(data)
cfg.debugger.abstract_cmd_reg_write(.regno(csrs[i]), .value_q({data}), .status(status));
`DV_CHECK_EQ(status, jtag_rv_debugger_pkg::AbstractCmdErrNone)
new_data[csrs[i]] = data;
end
// Read CSRs and verify new values.
csrs.shuffle();
foreach (csrs[i]) begin
logic [BUS_DW-1:0] value_q[$];
cfg.debugger.abstract_cmd_reg_read(.regno(csrs[i]), .value_q(value_q), .status(status));
`DV_CHECK_EQ(status, jtag_rv_debugger_pkg::AbstractCmdErrNone)
`DV_CHECK_EQ(value_q[0], new_data[csrs[i]], $sformatf("for CSR 0x%0h", csrs[i]))
end
// Restore original values.
csrs.shuffle();
foreach (csrs[i]) begin
cfg.debugger.abstract_cmd_reg_write(.regno(csrs[i]), .value_q({orig_data[csrs[i]]}),
.status(status));
`DV_CHECK_EQ(status, jtag_rv_debugger_pkg::AbstractCmdErrNone)
end
// Read CSRs and verify restored values.
csrs.shuffle();
foreach (csrs[i]) begin
logic [BUS_DW-1:0] value_q[$];
cfg.debugger.abstract_cmd_reg_read(.regno(csrs[i]), .value_q(value_q), .status(status));
`DV_CHECK_EQ(status, jtag_rv_debugger_pkg::AbstractCmdErrNone)
`DV_CHECK_EQ(value_q[0], orig_data[csrs[i]], $sformatf("for CSR 0x%0h", csrs[i]))
end
endtask
// Write-read check the SRAM main and ret memories.
//
// Since we have taken full control of the CPU execution using the debugger, it is not necessary
// to save and restore the SRAM addresses we write to as a part of this task.
task test_chip_mem_access();
byte status;
logic [BUS_DW-1:0] test_addrs[$];
logic [BUS_DW-1:0] test_data[$];
addr_range_t sram_main = '{
start_addr: top_earlgrey_pkg::TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR,
end_addr: top_earlgrey_pkg::TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR +
top_earlgrey_pkg::TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_SIZE_BYTES - 1
};
addr_range_t sram_ret = '{
start_addr: top_earlgrey_pkg::TOP_EARLGREY_SRAM_CTRL_RET_AON_RAM_BASE_ADDR,
end_addr: top_earlgrey_pkg::TOP_EARLGREY_SRAM_CTRL_RET_AON_RAM_BASE_ADDR +
top_earlgrey_pkg::TOP_EARLGREY_SRAM_CTRL_RET_AON_RAM_SIZE_BYTES - 1
};
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(test_addrs,
test_addrs.size() inside {[5:20]};
foreach (test_addrs[i]) {
test_addrs[i] inside {[sram_main.start_addr:sram_main.end_addr]} ||
test_addrs[i] inside {[sram_ret.start_addr:sram_ret.end_addr]};
test_addrs[i][1:0] == '0;
}
)
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(test_data, test_data.size() == test_addrs.size();)
foreach (test_addrs[i]) begin
logic [BUS_DW-1:0] value_q[$];
cfg.debugger.mem_write(.addr(test_addrs[i]), .value_q({test_data[i]}), .status(status));
`DV_CHECK_EQ(status, 0)
end
foreach (test_addrs[i]) begin
logic [BUS_DW-1:0] value_q[$];
cfg.debugger.mem_read(.addr(test_addrs[i]), .value_q(value_q), .status(status));
`DV_CHECK_EQ(status, 0)
`DV_CHECK_EQ(value_q[0], test_data[i], $sformatf("for addr 0x%0h", test_addrs[i]))
end
endtask
endclass