<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// The spi_device_driver responds to bus transactions initiated by a SPI host,
// and its responses depend on cfg.spi_func_mode.
//
// For SpiModeGeneric, the driver waits for CSB to be asserted, then drives
// the contents of spi_item.data sequentially, timed with the SPI host's clock
// output.
//
// For SpiModeFlash, the spi_item must be submitted at exactly the moment when
// the driver is to begin driving the response. The driver sends the contents
// of spi_item.payload_q sequentially, timed with the SPI host's clock output.
// Note that spi_device_driver does NOT observe the command, address, or dummy
// cycles, and it assumes those phases have already passed. For this mode,
// spi_device_driver is anticipated to be used in conjunction with the
// spi_monitor's req_analysis_port, which writes a spi_item into the
// spi_sequencer's connected req_analysis_fifo at the moment those phases have
// completed.
class spi_device_driver extends spi_driver;
  `uvm_component_utils(spi_device_driver)
  `uvm_component_new

  bit [CSB_WIDTH-1:0] active_csb;

  virtual task reset_signals();
    forever begin
      @(negedge cfg.vif.rst_n);
      `uvm_info(`gfn, "\n  dev_drv: in reset progress", UVM_DEBUG)
      under_reset = 1'b1;
      cfg.vif.sio_out = 'z;
      @(posedge cfg.vif.rst_n);
      under_reset = 1'b0;
      `uvm_info(`gfn, "\n  dev_drv: out of reset", UVM_DEBUG)
    end
  endtask

  virtual task get_and_drive();
    spi_item req, rsp;

    forever begin
      seq_item_port.get_next_item(req);
      `DV_CHECK_EQ(cfg.vif.disconnected, 0)

      active_csb = req.csb_sel;
      $cast(rsp, req.clone());
      rsp.set_id_info(req);

      wait (!under_reset && !cfg.vif.csb[active_csb]);
      fork
        begin: iso_fork
          fork
            case (cfg.spi_func_mode)
              SpiModeGeneric: send_rx_item(req, rsp);
              SpiModeFlash: send_flash_item(req, rsp);
              SpiModeTpm: send_tpm_item(req, rsp);
              default: begin
                `uvm_fatal(`gfn, $sformatf("Invalid mode %s", cfg.spi_func_mode.name))
              end
            endcase
            drive_bus_to_highz();
            drive_bus_for_reset();
          join_any
          disable fork;
        end: iso_fork
      join
      seq_item_port.item_done();
      seq_item_port.put_response(rsp);
      `uvm_info(`gfn, "\n  dev_drv: item done", UVM_HIGH)
    end
  endtask : get_and_drive

  virtual task send_rx_item(spi_item item, spi_item rsp);
    logic [3:0] sio_bits;
    bit         bits_q[$];
    int         max_tx_bits;

    if (cfg.byte_order) cfg.swap_byte_order(item.data);
    bits_q = {>> 1 {item.data}};
    max_tx_bits = cfg.get_sio_size();

    `uvm_info(`gfn, $sformatf("\n  dev_drv: send_rx_item, return %0d bits to dut",
                              bits_q.size()), UVM_DEBUG)
    // pop enough bits do drive all needed sio
    while (bits_q.size() > 0) begin
       if (bits_q.size() > 0) cfg.wait_sck_edge(DrivingEdge);
      for (int i = 0; i < 4; i++) begin
        sio_bits[i] = (i < max_tx_bits) ? bits_q.pop_front() : 1'bz;
      end
      send_data_to_sio(cfg.spi_mode, sio_bits);
      `uvm_info(`gfn, $sformatf("\n  dev_drv: assert data bit[%0d] %b",
                                bits_q.size(), sio_bits[0]), UVM_DEBUG)

    end
    // TODO, ideally we should capture the data device receives and put in rsp.
  endtask : send_rx_item

  virtual task send_flash_item(spi_item item, spi_item rsp);
    logic [3:0] sio_bits;
    bit         bits_q[$];
    logic [7:0] data[$] = {item.payload_q};
    spi_mode_e spi_mode;

    `uvm_info(`gfn, $sformatf("sending rx_item:\n%s", item.sprint()), UVM_MEDIUM)

    fork
      begin : thread_collect_payload
        forever begin
          logic [7:0] byte_data;
          cfg.read_byte(item.num_lanes, !item.write_command, byte_data);
          rsp.payload_q.push_back(byte_data);
        end
      end : thread_collect_payload

      begin : thread_send_read_data
        if (!item.write_command) begin
          if (cfg.byte_order) cfg.swap_byte_order(data);
          bits_q = {>> 1 {data}};

          if (item.num_lanes == 1) spi_mode = Standard;
          else if (item.num_lanes == 2) spi_mode = Dual;
          else spi_mode = Quad;

          forever begin
            cfg.wait_sck_edge(DrivingEdge);
            for (int i = 0; i < item.num_lanes; i++) begin
              sio_bits[i] = bits_q.size > 0 ? bits_q.pop_front() : $urandom_range(0, 1);
            end
            send_data_to_sio(spi_mode, sio_bits);
          end
        end
      end : thread_send_read_data
    join
  endtask : send_flash_item

  virtual task send_tpm_item(spi_item item, spi_item rsp);
    // TODO, this mode isn't used in OT project
    `uvm_fatal(`gfn, "TPM device mode isn't supported")
  endtask : send_tpm_item

  virtual task send_data_to_sio(spi_mode_e mode, input logic [3:0] sio_bits);
    case (mode)
      Standard: cfg.vif.sio_out[1]   <= sio_bits[0];
      Dual:     cfg.vif.sio_out[1:0] <= sio_bits[1:0];
      default:  cfg.vif.sio_out      <= sio_bits;
    endcase
  endtask : send_data_to_sio

  virtual task drive_bus_to_highz();
    @(posedge cfg.vif.csb[active_csb]);
    cfg.vif.sio_out = 'z;
    `uvm_info(`gfn, "\n  dev_drv: drive_bus_to_highz is done", UVM_DEBUG)
  endtask : drive_bus_to_highz

  virtual task drive_bus_for_reset();
    @(negedge cfg.vif.rst_n);
    cfg.vif.sio_out = 'z;
    `uvm_info(`gfn, "\n  dev_drv: drive_bus_for_reset is done", UVM_DEBUG)
  endtask : drive_bus_for_reset
endclass : spi_device_driver
<commit_msg>[dv/spi] Fix device driver bit mapping<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// The spi_device_driver responds to bus transactions initiated by a SPI host,
// and its responses depend on cfg.spi_func_mode.
//
// For SpiModeGeneric, the driver waits for CSB to be asserted, then drives
// the contents of spi_item.data sequentially, timed with the SPI host's clock
// output.
//
// For SpiModeFlash, the spi_item must be submitted at exactly the moment when
// the driver is to begin driving the response. The driver sends the contents
// of spi_item.payload_q sequentially, timed with the SPI host's clock output.
// Note that spi_device_driver does NOT observe the command, address, or dummy
// cycles, and it assumes those phases have already passed. For this mode,
// spi_device_driver is anticipated to be used in conjunction with the
// spi_monitor's req_analysis_port, which writes a spi_item into the
// spi_sequencer's connected req_analysis_fifo at the moment those phases have
// completed.
class spi_device_driver extends spi_driver;
  `uvm_component_utils(spi_device_driver)
  `uvm_component_new

  bit [CSB_WIDTH-1:0] active_csb;

  virtual task reset_signals();
    forever begin
      @(negedge cfg.vif.rst_n);
      `uvm_info(`gfn, "\n  dev_drv: in reset progress", UVM_DEBUG)
      under_reset = 1'b1;
      cfg.vif.sio_out = 'z;
      @(posedge cfg.vif.rst_n);
      under_reset = 1'b0;
      `uvm_info(`gfn, "\n  dev_drv: out of reset", UVM_DEBUG)
    end
  endtask

  virtual task get_and_drive();
    spi_item req, rsp;

    forever begin
      seq_item_port.get_next_item(req);
      `DV_CHECK_EQ(cfg.vif.disconnected, 0)

      active_csb = req.csb_sel;
      $cast(rsp, req.clone());
      rsp.set_id_info(req);

      wait (!under_reset && !cfg.vif.csb[active_csb]);
      fork
        begin: iso_fork
          fork
            case (cfg.spi_func_mode)
              SpiModeGeneric: send_rx_item(req, rsp);
              SpiModeFlash: send_flash_item(req, rsp);
              SpiModeTpm: send_tpm_item(req, rsp);
              default: begin
                `uvm_fatal(`gfn, $sformatf("Invalid mode %s", cfg.spi_func_mode.name))
              end
            endcase
            drive_bus_to_highz();
            drive_bus_for_reset();
          join_any
          disable fork;
        end: iso_fork
      join
      seq_item_port.item_done();
      seq_item_port.put_response(rsp);
      `uvm_info(`gfn, "\n  dev_drv: item done", UVM_HIGH)
    end
  endtask : get_and_drive

  virtual task send_rx_item(spi_item item, spi_item rsp);
    logic [3:0] sio_bits;
    bit         bits_q[$];
    int         max_tx_bits;

    if (cfg.byte_order) cfg.swap_byte_order(item.data);
    bits_q = {>> 1 {item.data}};
    max_tx_bits = cfg.get_sio_size();

    `uvm_info(`gfn, $sformatf("\n  dev_drv: send_rx_item, return %0d bits to dut",
                              bits_q.size()), UVM_DEBUG)
    // pop enough bits do drive all needed sio
    while (bits_q.size() > 0) begin
       if (bits_q.size() > 0) cfg.wait_sck_edge(DrivingEdge);
      for (int i = 0; i < 4; i++) begin
        sio_bits[i] = (i < max_tx_bits) ? bits_q.pop_front() : 1'bz;
      end
      send_data_to_sio(cfg.spi_mode, sio_bits);
      `uvm_info(`gfn, $sformatf("\n  dev_drv: assert data bit[%0d] %b",
                                bits_q.size(), sio_bits[0]), UVM_DEBUG)

    end
    // TODO, ideally we should capture the data device receives and put in rsp.
  endtask : send_rx_item

  virtual task send_flash_item(spi_item item, spi_item rsp);
    logic [3:0] sio_bits;
    bit         bits_q[$];
    logic [7:0] data[$] = {item.payload_q};
    spi_mode_e spi_mode;

    `uvm_info(`gfn, $sformatf("sending rx_item:\n%s", item.sprint()), UVM_MEDIUM)

    fork
      begin : thread_collect_payload
        forever begin
          logic [7:0] byte_data;
          cfg.read_byte(item.num_lanes, !item.write_command, byte_data);
          rsp.payload_q.push_back(byte_data);
        end
      end : thread_collect_payload

      begin : thread_send_read_data
        if (!item.write_command) begin
          if (cfg.byte_order) cfg.swap_byte_order(data);
          bits_q = {>> 1 {data}};

          if (item.num_lanes == 1) spi_mode = Standard;
          else if (item.num_lanes == 2) spi_mode = Dual;
          else spi_mode = Quad;

          forever begin
            cfg.wait_sck_edge(DrivingEdge);
            // The first bit in bits_q is the MSB, which is driven on the sio
            // lane with the highest active index.
            for (int i = $high(sio_bits); i >= $low(sio_bits); i--) begin
              if (i >= item.num_lanes) begin
                sio_bits[i] = 1'bz;
              end else begin
                sio_bits[i] = bits_q.size > 0 ? bits_q.pop_front() : $urandom_range(0, 1);
              end
            end
            send_data_to_sio(spi_mode, sio_bits);
          end
        end
      end : thread_send_read_data
    join
  endtask : send_flash_item

  virtual task send_tpm_item(spi_item item, spi_item rsp);
    // TODO, this mode isn't used in OT project
    `uvm_fatal(`gfn, "TPM device mode isn't supported")
  endtask : send_tpm_item

  virtual task send_data_to_sio(spi_mode_e mode, input logic [3:0] sio_bits);
    case (mode)
      Standard: cfg.vif.sio_out[1]   <= sio_bits[0];
      Dual:     cfg.vif.sio_out[1:0] <= sio_bits[1:0];
      default:  cfg.vif.sio_out      <= sio_bits;
    endcase
  endtask : send_data_to_sio

  virtual task drive_bus_to_highz();
    @(posedge cfg.vif.csb[active_csb]);
    cfg.vif.sio_out = 'z;
    `uvm_info(`gfn, "\n  dev_drv: drive_bus_to_highz is done", UVM_DEBUG)
  endtask : drive_bus_to_highz

  virtual task drive_bus_for_reset();
    @(negedge cfg.vif.rst_n);
    cfg.vif.sio_out = 'z;
    `uvm_info(`gfn, "\n  dev_drv: drive_bus_for_reset is done", UVM_DEBUG)
  endtask : drive_bus_for_reset
endclass : spi_device_driver

{'fraction_non_alphanumeric': 0.10408700824199167, 'fraction_numerical': 0.007307332823519416, 'mean_word_length': 2.617086662569146, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6632258', 'n_tokens_mistral': 4292, 'n_tokens_neox': 4160, 'n_words': 1041}