Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions hw/dv/sv/ottf_spi_console/ottf_spi_console.core
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
CAPI=2:
# Copyright lowRISC contributors (OpenTitan project).
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:dv:ottf_spi_console"
description: "Routines to interact with an OTTF SPI console using an OpenTitan spi_host agent."
filesets:
files_dv:
depend:
- lowrisc:dv:dv_lib
- lowrisc:dv:spi_agent
- lowrisc:dv:csr_utils
files:
- ottf_spi_console_pkg.sv
- ottf_spi_console.sv: {is_include_file: true}
file_type: systemVerilogSource

targets:
default:
filesets:
- files_dv
544 changes: 544 additions & 0 deletions hw/dv/sv/ottf_spi_console/ottf_spi_console.sv

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions hw/dv/sv/ottf_spi_console/ottf_spi_console_pkg.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// Routines to interact with an OTTF SPI console using an OpenTitan spi_host agent.
//
// This package provides the 'ottf_spi_console' class which can be instantiated as a testbench
// component to provide methods to drive HOST-side communications of an OpenTitan OTTF SPI console.
// The env can configure this object by connecting the virtual interfaces (clk_rst and flow_ctrl),
// and the base_vseq should assign handles for the parent sequence (seq_h) and the sequencer for
// the OpenTitan spi_host UVC (spi_host_sequencer_h).
//
// After configuration, the primary interface for sequences to make use of this are the methods:
// - host_spi_console_read_wait_for()
// - host_spi_console_read_payload()
// - host_spi_console_write_when_ready()
//
// Note.
// This is a bare-bones library for driving a console interaction. The methods here are crude.
// Many methods fatally timeout if they see no stimulus or response to stimulus within a relatively
// short window of time, and they are not currently written to handle errors / timeouts robustly.
// They should be used with prior knowledge about how the DEVICE-side will behave with regards to
// message types and message content.
// It also only works with the sideband wired indicator signal-based flow control mechanism.

package ottf_spi_console_pkg;

import dv_utils_pkg::uint;

import uvm_pkg::*;
import spi_agent_pkg::*;
import csr_utils_pkg::*;

`include "uvm_macros.svh"
`include "dv_macros.svh"

// Default timeouts and delays used in this package.
// Due to console activity being very slow relative to many other parts of the system, these
// delays and timeouts are large. They are also very top and application specific, and would
// require tuning for significantly different use cases.
// Note that this stimulus may be timing-sensitive due to interactions with the software running
// the DEVICE-side of the OTTF SPI Console, so test appropriately if changing these values.
uint await_flow_ctrl_timeout_ns = 200_000_000; // 200ms
uint wait_on_busy_timeout_ns = 200_000_000; // 200ms
uint write_completion_timeout_ns = 200_000_000; // 200ms
uint min_interval_ns = 3_000; // 3us

// Typical opcodes used by SPI NOR-flash devices.
// Ensure that both parties are using the same encodings.
typedef enum bit [7:0] {
SpiFlashReadJedec = 8'h9F,
SpiFlashReadSfdp = 8'h5A,
SpiFlashReadNormal = 8'h03,
SpiFlashReadFast = 8'h0B,
SpiFlashReadDual = 8'h3B,
SpiFlashReadQuad = 8'h6B,
SpiFlashReadSts1 = 8'h05,
SpiFlashReadSts2 = 8'h35,
SpiFlashReadSts3 = 8'h15,
SpiFlashWriteDisable = 8'h04,
SpiFlashWriteEnable = 8'h06,
SpiFlashWriteSts1 = 8'h01,
SpiFlashWriteSts2 = 8'h31,
SpiFlashWriteSts3 = 8'h11,
SpiFlashChipErase = 8'hC7,
SpiFlashSectorErase = 8'h20,
SpiFlashPageProgram = 8'h02,
SpiFlashEn4B = 8'hB7,
SpiFlashEx4B = 8'hE9
} spi_flash_cmd_e;

typedef enum int {
rx_ready = 1,
tx_ready = 0
} flow_ctrl_idx_e;

`include "ottf_spi_console.sv"

endpackage : ottf_spi_console_pkg
1 change: 1 addition & 0 deletions hw/top_earlgrey/dv/env/chip_env.core
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ filesets:
- lowrisc:dv:ralgen
- lowrisc:dv:spi_agent
- lowrisc:dv:spi_host_sva
- lowrisc:dv:ottf_spi_console
- lowrisc:dv:str_utils
- lowrisc:dv:sw_test_status
- lowrisc:dv:sw_logger_if
Expand Down
6 changes: 6 additions & 0 deletions hw/top_earlgrey/dv/env/chip_env.sv
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ class chip_env extends cip_base_env #(
foreach (LIST_OF_ALERTS[i]) begin
cfg.m_alert_agent_cfgs[LIST_OF_ALERTS[i]].is_active = 0;
end

// Initialize the spi_console object.
cfg.ottf_spi_console_h = ottf_spi_console::type_id::create("m_ottf_spi_console", this);
// Connect the spi_console object to the VIFs.
cfg.ottf_spi_console_h.clk_rst_vif = cfg.clk_rst_vif;
cfg.ottf_spi_console_h.flow_ctrl_vif = cfg.chip_vif.spi_host_console_flow_ctrl_if;
endfunction

function void connect_phase(uvm_phase phase);
Expand Down
3 changes: 3 additions & 0 deletions hw/top_earlgrey/dv/env/chip_env_cfg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ class chip_env_cfg #(type RAL_T = chip_ral_pkg::chip_reg_block) extends cip_base
// The JTAG RV debugger model.
jtag_rv_debugger debugger;

// Instantiate an object to handle OTTF SPI Console interaction.
ottf_spi_console ottf_spi_console_h;

// Run small page rma
bit en_small_rma = 0;

Expand Down
6 changes: 6 additions & 0 deletions hw/top_earlgrey/dv/env/chip_env_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ package chip_env_pkg;
import rv_core_ibex_reg_pkg::RV_CORE_IBEX_DV_SIM_WINDOW_OFFSET;
import i2c_agent_pkg::*;
import pattgen_agent_pkg::*;
import ottf_spi_console_pkg::*;

// macro includes
`include "uvm_macros.svh"
Expand Down Expand Up @@ -171,6 +172,11 @@ package chip_env_pkg;
SpiFlashEx4B = 8'hE9
} spi_flash_cmd_e;

typedef enum int {
ottf_spi_console_flow_ctrl_mio_idx_rx_ready = top_earlgrey_pkg::MioPadIoa6,
ottf_spi_console_flow_ctrl_mio_idx_tx_ready = top_earlgrey_pkg::MioPadIoa5
} ottf_spi_console_flow_ctrl_mio_idx_e;

// package sources
`include "chip_env_cfg.sv"
`include "chip_env_cov.sv"
Expand Down
6 changes: 6 additions & 0 deletions hw/top_earlgrey/dv/env/chip_if.sv
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,12 @@ interface chip_if;
end while (1);
end

// Bind the sideband signals used to flow-control the OTTF spi_console into a interface.
pins_if#(.Width(2), .PullStrength("Weak")) spi_host_console_flow_ctrl_if (
.pins({mios[ottf_spi_console_flow_ctrl_mio_idx_rx_ready],
mios[ottf_spi_console_flow_ctrl_mio_idx_tx_ready]})
);

// Functional (dedicated) interface: SPI device 0 interface (receives traffic from the chip).
// TODO: Update spi_if to emit all signals as inout ports and internal drivers on all ports.
bit [NUM_SPI_HOSTS-1:0] __enable_spi_device;
Expand Down
30 changes: 22 additions & 8 deletions sw/device/lib/testing/test_framework/ottf_console_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,32 +213,46 @@ static size_t spi_device_send_frame(ottf_console_t *console, const char *buf,
(next_write_address + frame_size_bytes) % kSpiDeviceReadBufferSizeBytes;
console->data.spi.frame_num++;

// Block until the host to reads out the frame by toggling the GPIO TX-ready
// indicator pin to signal to the host to clock out data from the spi_device
// egress buffer.
// If using the GPIO TX-ready indicator pin, toggle it high to signal to the
// host that there is data in the egress buffer, and then block until the host
// reads out the entire frame. Clear the TX-ready indicator pin before
// continuing.
if (console->data.spi.tx_ready_gpio != kOttfSpiNoTxGpio) {
OT_DISCARD(dif_gpio_write(&ottf_console_gpio,
console->data.spi.tx_ready_gpio, true));
bool cs_state = true;
bool target_cs_state = false;
// There will be two bulk transfers that can be synchronized by the
// chip-select action. First the host will read out the 12-byte frame
// header, followed by the N-byte payload. Each transfer can be observed by
// the chip-select toggling low then high. After the first toggle low, when
// the host begins reading out the frame header, we can deassert the
// TX-ready pin as the host has already initiated the two SPI transactions.
// header, followed by the N-byte payload. Each transfer is framed on the
// wire by observing the chip-select first toggling high->low then
// low->high.
//
// After the first toggle low, when the host begins reading out the frame
// header, we can deassert the TX-ready pin as the host will continue to
// complete two SPI transfers regardless.
for (size_t i = 0; i < 4; ++i) {
// Repeat four times, because there are 4 CSB-toggle events in two
// standard SPI transfers.

// Query the state of CSB until it changes.
do {
if (dif_spi_device_get_csb_status(spi_device, &cs_state) != kDifOk) {
return 0;
}
} while (cs_state != target_cs_state);
target_cs_state = !target_cs_state;

// After the first toggle of CSB (high->low, the start of the header
// transfer), the HOST is already going to queue up and complete 2 full
// transfers, so we can de-assert the TX-Ready indicator GPIO.
if (i == 0) {
OT_DISCARD(dif_gpio_write(&ottf_console_gpio,
console->data.spi.tx_ready_gpio, false));
}
target_cs_state = !target_cs_state;
}

// Reset the write_address before returning.
next_write_address = 0;
}

Expand Down
Loading