| // 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 |