blob: 5adc8135b5010e1dd58f1195f4395507d971591a [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 spi_agent_cfg extends dv_base_agent_cfg;
// enable monitor collection and checker
bit en_monitor = 1'b1;
bit en_monitor_checks = 1'b1;
// byte order (configured by vseq)
bit byte_order = 1'b1;
// host mode cfg knobs
time sck_period_ps = 50_000; // 20MHz
bit host_bit_dir; // 0 - msb -> lsb, 1 - lsb -> msb
bit device_bit_dir; // 0 - msb -> lsb, 1 - lsb -> msb
bit sck_on; // keep sck on
bit partial_byte; // Transfering less than byte
bit [3:0] bits_to_transfer; // Bits to transfer if using less than byte
bit decode_commands; // Used in monitor if decoding of commands needed
bit [2:0] cmd_addr_size = 4; //Address size for command
//-------------------------
// spi_host regs
//-------------------------
// csid reg
bit [CSB_WIDTH-1:0] csid;
// indicate csb is selected in cfg or spi_item
bit csb_sel_in_cfg = 1;
// configopts register fields
bit sck_polarity[NUM_CSB]; // aka CPOL
bit sck_phase[NUM_CSB]; // aka CPHA
bit full_cyc[NUM_CSB];
bit [3:0] csn_lead[NUM_CSB];
bit [3:0] csn_trail[NUM_CSB];
bit [3:0] csn_idle[NUM_CSB];
// command register fields
spi_mode_e spi_mode;
// Cmd info configs
spi_flash_cmd_info cmd_infos[bit[7:0]];
// Controls address size for flash_cmd_info of type SpiFlashAddrCfg.
bit flash_addr_4b_en;
// set this mode before starting an item
spi_func_mode_e spi_func_mode;
// address width in bytes (default is 4 bytes)
int spi_cmd_width = 4;
// how many bytes monitor samples per transaction
int num_bytes_per_trans_in_mon = 4;
// enable randomly injecting extra delay between 2 sck/word
bit en_extra_dly_btw_sck;
uint min_extra_dly_ns_btw_sck = 1;
uint max_extra_dly_ns_btw_sck = 100; // small delay to avoid transfer timeout
uint extra_dly_chance_pc_btw_sck = 5; // percentage of extra delay btw each spi clock edge
// Note: can't handle word delay, if a word is splitted into multiple csb.
// In that case, control delay in seq level
bit en_extra_dly_btw_word;
uint min_extra_dly_ns_btw_word = 1;
uint max_extra_dly_ns_btw_word = 1000; // no timeout btw word
uint extra_dly_chance_pc_btw_word = 5; // percentage of extra delay btw each word
// min_idle_ns_after_csb_drop and its max_* sibling specify the range for
// which CSB will be held inactive at the end of a host sequence item.
// A value will be randomly chosen in the range. CSB is guaranteed to be inactive for
// at least min_idle_ns_after_csb_drop before the next transaction begins.
uint min_idle_ns_after_csb_drop = 1;
uint max_idle_ns_after_csb_drop = 1000;
// interface handle used by driver, monitor & the sequencer
virtual spi_if vif;
`uvm_object_utils_begin(spi_agent_cfg)
`uvm_field_int (sck_period_ps, UVM_DEFAULT)
`uvm_field_int (host_bit_dir, UVM_DEFAULT)
`uvm_field_int (device_bit_dir, UVM_DEFAULT)
`uvm_field_int (partial_byte, UVM_DEFAULT)
`uvm_field_int (decode_commands, UVM_DEFAULT)
`uvm_field_int (cmd_addr_size, UVM_DEFAULT)
`uvm_field_int (bits_to_transfer, UVM_DEFAULT)
`uvm_field_int (en_extra_dly_btw_sck, UVM_DEFAULT)
`uvm_field_int (max_extra_dly_ns_btw_sck, UVM_DEFAULT)
`uvm_field_int (extra_dly_chance_pc_btw_sck, UVM_DEFAULT)
`uvm_field_int (en_extra_dly_btw_word, UVM_DEFAULT)
`uvm_field_int (max_extra_dly_ns_btw_word, UVM_DEFAULT)
`uvm_field_int (extra_dly_chance_pc_btw_word, UVM_DEFAULT)
`uvm_field_int (min_idle_ns_after_csb_drop, UVM_DEFAULT)
`uvm_field_int (max_idle_ns_after_csb_drop, UVM_DEFAULT)
`uvm_field_sarray_int(sck_polarity, UVM_DEFAULT)
`uvm_field_sarray_int(sck_phase, UVM_DEFAULT)
`uvm_field_sarray_int(full_cyc, UVM_DEFAULT)
`uvm_field_sarray_int(csn_lead, UVM_DEFAULT)
`uvm_field_sarray_int(csn_trail, UVM_DEFAULT)
`uvm_field_sarray_int(csn_idle, UVM_DEFAULT)
`uvm_field_enum(spi_mode_e, spi_mode, UVM_DEFAULT)
`uvm_field_enum(spi_func_mode_e, spi_func_mode, UVM_DEFAULT)
`uvm_object_utils_end
`uvm_object_new
virtual task wait_sck_edge(sck_edge_type_e sck_edge_type, bit [CSB_WIDTH-1:0] csb_id);
bit wait_posedge;
bit [1:0] sck_mode = {sck_polarity[csb_id], sck_phase[csb_id]};
// sck pola sck_pha mode
// 0 0 0: sample at leading posedge_sck (drive @ prev negedge_sck)
// 1 0 2: sample at leading negedge_sck (drive @ prev posedge_sck)
// 0 1 1: sample at trailing negedge_sck (drive @ curr posedge_sck)
// 1 1 3: sample at trailing posedge_sck (drive @ curr negedge_sck)
case (sck_edge_type)
LeadingEdge: begin
// wait for leading edge applies to mode 1 and 3 only
if (sck_mode inside {2'b00, 2'b10}) return;
if (sck_mode == 2'b01) wait_posedge = 1'b1;
end
DrivingEdge: wait_posedge = (sck_mode inside {2'b01, 2'b10});
SamplingEdge: wait_posedge = (sck_mode inside {2'b00, 2'b11});
endcase
if (wait_posedge) @(posedge vif.sck);
else @(negedge vif.sck);
`uvm_info(`gfn, $sformatf("\n spi_agent_cfg, %s at %s clock, sck_mode %b",
sck_edge_type.name(), wait_posedge ? "posedge" : "negedge", sck_mode), UVM_DEBUG)
endtask
// TODO use dv_utils_pkg::endian_swap_byte_arr() if possible
virtual function void swap_byte_order(ref logic [7:0] data[$]);
bit [7:0] data_arr[];
data_arr = data;
`uvm_info(`gfn, $sformatf("\n spi_agent_cfg, data_q_baseline: %p", data), UVM_DEBUG)
dv_utils_pkg::endian_swap_byte_arr(data_arr);
`uvm_info(`gfn, $sformatf("\n spi_agent_cfg, data_q_swapped: %p", data_arr), UVM_DEBUG)
data = data_arr;
endfunction : swap_byte_order
virtual function int get_sio_size();
case (spi_mode)
Standard: return 1;
Dual: return 2;
Quad: return 4;
default: return 0;
endcase
endfunction : get_sio_size
virtual function void add_cmd_info(spi_flash_cmd_info info);
// opcode must be unique
`DV_CHECK_EQ(is_opcode_supported(info.opcode), 0)
cmd_infos[info.opcode] = info;
endfunction : add_cmd_info
virtual function bit is_opcode_supported(bit [7:0] opcode);
return cmd_infos.exists(opcode);
endfunction : is_opcode_supported
virtual function void extract_cmd_info_from_opcode(input bit [7:0] opcode,
output bit [2:0] num_addr_bytes,
output bit write_command,
output bit [2:0] num_lanes,
output int dummy_cycles);
if (cmd_infos.exists(opcode)) begin
num_addr_bytes = get_num_addr_byte(opcode);
write_command = cmd_infos[opcode].write_command;
num_lanes = cmd_infos[opcode].num_lanes;
dummy_cycles = cmd_infos[opcode].dummy_cycles;
end else begin
// if it's invalid opcode, here is the default setting
`uvm_info(`gfn, $sformatf("extract invalid opcode: 0x%0h", opcode), UVM_MEDIUM)
write_command = 1;
num_addr_bytes = 0;
num_lanes = 1;
dummy_cycles = 0;
end
endfunction : extract_cmd_info_from_opcode
// this task collects one byte data based on num_lanes, which is used in both monitor and driver
task read_byte(input int num_lanes,
input bit is_device_rsp,
input bit [CSB_WIDTH-1:0] csb_id,
output logic [7:0] data);
int which_bit = 8;
while (which_bit != 0) begin
wait_sck_edge(SamplingEdge, csb_id);
case (num_lanes)
1: data[--which_bit] = is_device_rsp ? vif.sio[1] : vif.sio[0];
2: begin
data[--which_bit] = vif.sio[1];
data[--which_bit] = vif.sio[0];
end
4: begin
data[--which_bit] = vif.sio[3];
data[--which_bit] = vif.sio[2];
data[--which_bit] = vif.sio[1];
data[--which_bit] = vif.sio[0];
end
default: `uvm_fatal(`gfn, $sformatf("Unsupported lanes num 0x%0h", num_lanes))
endcase
end
`uvm_info(`gfn, $sformatf("sampled one byte data for flash: 0x%0h", data), UVM_HIGH)
endtask
virtual function int get_num_addr_byte(bit [7:0] opcode);
`DV_CHECK(cmd_infos.exists(opcode))
case (cmd_infos[opcode].addr_mode)
SpiFlashAddrDisabled: return 0;
SpiFlashAddrCfg: return flash_addr_4b_en ? 4 : 3;
SpiFlashAddr3b: return 3;
SpiFlashAddr4b: return 4;
default: `uvm_fatal(`gfn, "Impossible value")
endcase
endfunction : get_num_addr_byte
endclass : spi_agent_cfg