blob: c6f1acb590e74c05b5eec3900df4f10ce116b61c [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_spi_passthrough_vseq extends chip_sw_base_vseq;
`uvm_object_utils(chip_sw_spi_passthrough_vseq)
`uvm_object_new
// The sequence of opcodes used for the test.
spi_flash_cmd_e test_opcodes[$];
// Whether to check for transactions appearing on the unused spi_host.
bit en_unused_spi_host_monitoring = 1'b1;
// Frequencies to use for testing the sequences.
time sck_periods_ps[] = '{
41_000, // 24 MHz
33_000, // 30 MHz
167_000 // 6 MHz
};
// A bit map of command slots that will have passthrough filters enabled.
rand bit [spi_device_pkg::NumTotalCmdInfo-1:0] passthrough_filters;
// Generate a random permutation of the following opcodes, and place them in
// the test_opcodes queue:
// - SpiFlashReadNormal
// - SpiFlashReadFast
// - SpiFlashReadDual
// - SpiFlashReadQuad
// - SpiFlashChipErase
// - SpiFlashPageProgram
virtual function void generate_spi_flash_sequence();
test_opcodes = '{
SpiFlashReadNormal,
SpiFlashReadFast,
SpiFlashReadDual,
SpiFlashReadQuad,
SpiFlashChipErase,
SpiFlashPageProgram
};
test_opcodes.shuffle();
endfunction
// Assert that spi_host does not send any transactions.
virtual task monitor_unused_spi_host();
spi_agent_cfg agent_cfg = cfg.m_spi_device_agent_cfgs[1];
// CSB should never assert
wait (agent_cfg.vif.rst_n && !agent_cfg.vif.csb[0]);
`uvm_fatal(`gfn, $sformatf(
"Observed SPI transaction on spi_host1, which should be inactive"));
endtask
task get_filtered_commands(output bit [255:0] filter_map);
filter_map = '0;
for (int i = 0; i < spi_device_pkg::NumTotalCmdInfo; i++) begin
uvm_object csr;
uvm_reg_data_t opcode;
case (spi_device_pkg::cmd_info_index_e'(i))
spi_device_pkg::CmdInfoEn4B: csr = ral.spi_device.cmd_info_en4b.opcode;
spi_device_pkg::CmdInfoEx4B: csr = ral.spi_device.cmd_info_ex4b.opcode;
spi_device_pkg::CmdInfoWrEn: csr = ral.spi_device.cmd_info_wren.opcode;
spi_device_pkg::CmdInfoWrDi: csr = ral.spi_device.cmd_info_wrdi.opcode;
default: csr = ral.spi_device.cmd_info[i].opcode;
endcase
if (passthrough_filters[i]) begin
csr_rd(.ptr(csr), .value(opcode), .backdoor(1));
filter_map[8'(opcode)] = 1'b1;
end
end
endtask
// Send the sequence of commands indicated by the `test_opcodes` member.
// These commands must be registered with the host and device agents, as the
// command info determines the actions takes for each opcode. Random data of
// random size will be generated for each command that has a payload phase.
virtual task execute_spi_flash_sequence();
const int max_payload_size = 1024;
spi_device_flash_seq m_spi_device_seq;
spi_host_flash_seq m_spi_host_seq;
spi_item host_rsp, device_rsp;
spi_item device_rsp_q[$];
spi_agent_cfg agent_cfg = cfg.m_spi_host_agent_cfg;
bit [255:0] filter_map;
get_filtered_commands(filter_map);
fork begin : isolation_fork
// The device agent handles the incoming command, if it is not filtered.
fork
forever begin
`uvm_create_on(m_spi_device_seq, p_sequencer.spi_device_sequencer_hs[0]);
`uvm_send(m_spi_device_seq);
device_rsp_q.push_back(m_spi_device_seq.rsp);
end
join_none
while (test_opcodes.size() > 0) begin
bit [7:0] opcode = test_opcodes.pop_front();
`uvm_create_on(m_spi_host_seq, p_sequencer.spi_host_sequencer_h);
// Prepare for specific opcode. The address_q is kept empty, which will
// trigger m_spi_host_seq to do the lookup and supply a random value.
`DV_CHECK_RANDOMIZE_WITH_FATAL(m_spi_host_seq,
opcode == local::opcode;
address_q.size() == 0;
payload_q.size() <= local::max_payload_size;
read_size == payload_q.size(););
`uvm_send(m_spi_host_seq);
// Wait for a small delay to allow the device agent to push the response
// into the queue.
#1ps;
if (!filter_map[opcode]) begin
// Check that the command, address, and data sent matches on both sides.
`DV_CHECK_EQ(device_rsp_q.size(), 1);
host_rsp = m_spi_host_seq.rsp;
device_rsp = device_rsp_q.pop_front();
if (!host_rsp.compare(device_rsp)) begin
`uvm_error(`gfn, $sformatf("Compare mismatch\nhost_rsp:\n%sdevice_rsp:\n%s",
host_rsp.sprint(), device_rsp.sprint()))
end
end else begin
`DV_CHECK_EQ(device_rsp_q.size(), 0);
end
end
disable fork;
end join
endtask
virtual task cpu_init();
bit [7:0] sw_filter_config[4];
super.cpu_init();
`DV_CHECK_MEMBER_RANDOMIZE_FATAL(passthrough_filters);
sw_filter_config = {<<byte{passthrough_filters}};
sw_symbol_backdoor_overwrite("kFilteredCommands", sw_filter_config);
endtask
virtual task body();
// Turn off the FIFO data output assertion, as spi_device issues a read
// before it knows whether it needs the data.
$assertoff(0, "tb.dut.top_earlgrey.u_spi_device.u_readcmd.u_readsram.u_sram_fifo.DataKnown_A");
$assertoff(0, "tb.dut.top_earlgrey.u_spi_device.u_readcmd.u_readsram.u_fifo.DataKnown_A");
super.body();
cfg.m_spi_device_agent_cfgs[0].byte_order = '0;
// Set CSB inactive times to reasonable values. sys_clk is at 24 MHz, and
// it needs to capture CSB pulses.
cfg.m_spi_host_agent_cfg.min_idle_ns_after_csb_drop = 50;
cfg.m_spi_host_agent_cfg.max_idle_ns_after_csb_drop = 200;
spi_agent_configure_flash_cmds(cfg.m_spi_host_agent_cfg);
spi_agent_configure_flash_cmds(cfg.m_spi_device_agent_cfgs[0]);
// Enable the spi agents.
cfg.chip_vif.enable_spi_host = 1;
cfg.chip_vif.enable_spi_device(.inst_num(0), .enable(1));
cfg.chip_vif.enable_spi_device(.inst_num(1), .enable(1));
// Wait until we reach the SW test state.
`DV_WAIT(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInTest,
"Timed out waiting for SwTestStatusInTest",
10_000_000)
`DV_WAIT(cfg.sw_logger_vif.printed_log == "Test setup complete.",
"Timeout waiting for software to complete setup.")
if (en_unused_spi_host_monitoring) begin
fork
monitor_unused_spi_host();
join_none
end
foreach (sck_periods_ps[i]) begin
cfg.m_spi_host_agent_cfg.sck_period_ps = sck_periods_ps[i];
generate_spi_flash_sequence();
execute_spi_flash_sequence();
end
override_test_status_and_finish(1'b1);
endtask
endclass : chip_sw_spi_passthrough_vseq