Skip to content

Commit

Permalink
[sival, spi_dev_flash] Add smoke test and link to test plan
Browse files Browse the repository at this point in the history
Signed-off-by: Douglas Reis <[email protected]>
  • Loading branch information
engdoreis committed Feb 14, 2024
1 parent d2f315a commit a0c7f5d
Show file tree
Hide file tree
Showing 5 changed files with 347 additions and 3 deletions.
6 changes: 3 additions & 3 deletions hw/top_earlgrey/data/ip/chip_spi_device_testplan.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
- Load a firmware image (bootstrap) through flash commands to the spi_device memory.
- SW verifies the integrity of the image upon reception by reading the spi_device memory.
- Ensure the image is executed correctly.
- Verify the LAST_READ_ADDR.
'''
stage: V2
si_stage: SV3
Expand All @@ -58,9 +59,10 @@
"SPI_DEVICE.MODE.FLASH_EMULATION.COMMANDS",
"SPI_DEVICE.MODE.FLASH_EMULATION.READ_COMMAND_PROCESSOR",
"SPI_DEVICE.HW.FLASH_EMULATION_BLOCKS",
"SPI_DEVICE.HW.LAST_READ_ADDR",
]
tests: ["rom_e2e_smoke"]
bazel: ["//sw/device/silicon_creator/rom/e2e:rom_e2e_smoke"]
bazel: ["//sw/device/silicon_creator/rom/e2e:rom_e2e_smoke", "//sw/device/tests:spi_device_flash_smoketest"]
}
{
name: chip_sw_spi_device_pass_through
Expand All @@ -72,7 +74,6 @@
- Send random flash commands over the SPI device interface (chip IOs) from the
testbench.
- Verify the flash commands which pass through spi_host0 are received on chip IOs.
- Verify the LAST_READ_ADDR.
- Verify that only the payloads that are not filtered show up on the SPI host interface
at chip IOs.
- Verify spi_host1 doesn't send out any data from spi_device.
Expand All @@ -93,7 +94,6 @@
"SPI_DEVICE.MODE.PASSTHROUGH",
"SPI_DEVICE.HW.LANES",
"SPI_DEVICE.MODE.PASSTHROUGH.CMD_FILTER",
"SPI_DEVICE.HW.LAST_READ_ADDR",
]
tests: ["chip_sw_spi_device_pass_through"]
bazel: []
Expand Down
43 changes: 43 additions & 0 deletions sw/device/tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -3008,6 +3008,48 @@ opentitan_test(
],
)

opentitan_test(
name = "spi_device_flash_smoketest",
srcs = ["spi_device_flash_smoketest.c"],
cw310 = new_cw310_params(
test_cmd = """
--bitstream="{bitstream}"
--bootstrap="{firmware}"
"{firmware:elf}"
""",
test_harness = "//sw/host/tests/chip/spi_device_flash_smoketest",
),
exec_env = dicts.add(
EARLGREY_SILICON_OWNER_ROM_EXT_ENVS,
{
"//hw/top_earlgrey:fpga_cw310_sival": None,
"//hw/top_earlgrey:fpga_cw310_test_rom": None,
},
),
silicon = silicon_params(
test_cmd = """
--bootstrap={firmware}
"{firmware:elf}"
""",
test_harness = "//sw/host/tests/chip/spi_device_flash_smoketest",
),
deps = [
"//hw/top_earlgrey/sw/autogen:top_earlgrey",
"//sw/device/lib/arch:device",
"//sw/device/lib/base:mmio",
"//sw/device/lib/dif:base",
"//sw/device/lib/dif:rv_plic",
"//sw/device/lib/dif:spi_device",
"//sw/device/lib/runtime:hart",
"//sw/device/lib/runtime:irq",
"//sw/device/lib/runtime:log",
"//sw/device/lib/testing:spi_device_testutils",
"//sw/device/lib/testing:spi_flash_testutils",
"//sw/device/lib/testing/test_framework:ottf_main",
"//sw/device/lib/testing/test_framework:status",
],
)

cc_library(
name = "spi_host_flash_test_impl",
srcs = ["spi_host_flash_test_impl.c"],
Expand Down Expand Up @@ -3856,6 +3898,7 @@ opentitan_test(
),
exec_env = {
"//hw/top_earlgrey:fpga_cw310_test_rom": None,
"//hw/top_earlgrey:fpga_cw310_sival": None,
},
deps = [
"//sw/device/lib/arch:device",
Expand Down
166 changes: 166 additions & 0 deletions sw/device/tests/spi_device_flash_smoketest.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include <stdbool.h>
#include <stdint.h>

#include "sw/device/lib/arch/device.h"
#include "sw/device/lib/base/status.h"
#include "sw/device/lib/dif/dif_spi_device.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/spi_device_testutils.h"
#include "sw/device/lib/testing/spi_flash_emulator.h"
#include "sw/device/lib/testing/spi_flash_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"

#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"

OTTF_DEFINE_TEST_CONFIG(.enable_uart_flow_control = true);

enum {
kSpiDeviceDatasetSize = 128,
kSpiDeviceFlashAddress = 0x00,
};

// A set of bytes to be send out by spi_device.
const uint8_t kSpiTxData[kSpiDeviceDatasetSize] = {
0xe8, 0x50, 0xc6, 0xb4, 0xbe, 0x16, 0xed, 0x55, 0x16, 0x1d, 0xe6, 0x1c,
0xde, 0x9f, 0xfd, 0x24, 0x89, 0x81, 0x4d, 0x0d, 0x1a, 0x12, 0x4f, 0x57,
0xea, 0xd6, 0x6f, 0xc0, 0x7d, 0x46, 0xe7, 0x37, 0x81, 0xd3, 0x8e, 0x16,
0xad, 0x7b, 0xd0, 0xe2, 0x4f, 0xff, 0x39, 0xe6, 0x71, 0x3c, 0x82, 0x04,
0xec, 0x3a, 0x27, 0xcc, 0x3d, 0x58, 0x0e, 0x56, 0xd2, 0xd2, 0xb9, 0xa3,
0xb5, 0x3d, 0xc0, 0x40, 0xba, 0x90, 0x16, 0xd8, 0xe3, 0xa4, 0x22, 0x74,
0x80, 0xcb, 0x7b, 0xde, 0xd7, 0x3f, 0x4d, 0x93, 0x4d, 0x59, 0x79, 0x88,
0x24, 0xe7, 0x68, 0x8b, 0x7a, 0x78, 0xb7, 0x07, 0x09, 0x26, 0xcf, 0x6b,
0x52, 0xd9, 0x4c, 0xd3, 0x33, 0xdf, 0x2e, 0x0d, 0x3b, 0xab, 0x45, 0x85,
0xc2, 0xc2, 0x19, 0xe5, 0xc7, 0x2b, 0xb0, 0xf6, 0xcb, 0x06, 0xf6, 0xe2,
0xf5, 0xb1, 0xab, 0xef, 0x6f, 0xd8, 0x23, 0xfd,
};

static status_t configure_jedec_id(dif_spi_device_handle_t *spid) {
dif_spi_device_flash_id_t id = {
.device_id = 0x2298,
.manufacturer_id = 0x74,
.continuation_code = 0x17,
.num_continuation_code = 2,
};
TRY(dif_spi_device_set_flash_id(spid, id));
return OK_STATUS();
}

static status_t configure_flash_mode(dif_spi_device_handle_t *spid) {
dif_spi_device_config_t spi_device_config = {
.clock_polarity = kDifSpiDeviceEdgePositive,
.data_phase = kDifSpiDeviceEdgeNegative,
.tx_order = kDifSpiDeviceBitOrderMsbToLsb,
.rx_order = kDifSpiDeviceBitOrderMsbToLsb,
.device_mode = kDifSpiDeviceModeFlashEmulation,
};
TRY(dif_spi_device_configure(spid, spi_device_config));

dif_spi_device_flash_command_t read_commands[] = {
{
// Slot 0: ReadStatus1
.opcode = kSpiDeviceFlashOpReadStatus1,
.address_type = kDifSpiDeviceFlashAddrDisabled,
.dummy_cycles = 0,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
{
// Slot 1: ReadStatus2
.opcode = kSpiDeviceFlashOpReadStatus2,
.address_type = kDifSpiDeviceFlashAddrDisabled,
.dummy_cycles = 0,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
{
// Slot 2: ReadStatus3
.opcode = kSpiDeviceFlashOpReadStatus3,
.address_type = kDifSpiDeviceFlashAddrDisabled,
.dummy_cycles = 0,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
{
// Slot 3: ReadJedecID
.opcode = kSpiDeviceFlashOpReadJedec,
.address_type = kDifSpiDeviceFlashAddrDisabled,
.dummy_cycles = 0,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
{
// Slot 4: ReadSfdp
.opcode = kSpiDeviceFlashOpReadSfdp,
.address_type = kDifSpiDeviceFlashAddr3Byte,
.dummy_cycles = 8,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
{
// Slot 1: ReadNormal
.opcode = kSpiDeviceFlashOpReadNormal,
.address_type = kDifSpiDeviceFlashAddr3Byte,
.dummy_cycles = 0,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = true,
},
};

for (uint8_t i = 0; i < ARRAYSIZE(read_commands); ++i) {
uint8_t slot = i + kSpiDeviceReadCommandSlotBase;
TRY(dif_spi_device_set_flash_command_slot(spid, slot, kDifToggleEnabled,
read_commands[i]));
}

dif_spi_device_flash_command_t write_commands[] = {
{
// Slot 1: PageProgram
.opcode = kSpiDeviceFlashOpPageProgram,
.address_type = kDifSpiDeviceFlashAddrCfg,
.payload_io_type = kDifSpiDevicePayloadIoSingle,
.payload_dir_to_host = false,
.upload = true,
.set_busy_status = true,
},
};

for (uint8_t i = 0; i < ARRAYSIZE(write_commands); ++i) {
uint8_t slot = i + (uint8_t)kSpiDeviceWriteCommandSlotBase;
TRY(dif_spi_device_set_flash_command_slot(spid, slot, kDifToggleEnabled,
write_commands[i]));
}
return OK_STATUS();
}

bool test_main(void) {
dif_spi_device_handle_t spid;
CHECK_DIF_OK(dif_spi_device_init_handle(
mmio_region_from_addr(TOP_EARLGREY_SPI_DEVICE_BASE_ADDR), &spid));

CHECK_STATUS_OK(configure_flash_mode(&spid));

LOG_INFO("SYNC: Waiting for write page");
upload_info_t info;
CHECK_STATUS_OK(spi_device_testutils_wait_for_upload(&spid, &info));

CHECK_ARRAYS_EQ(info.data, kSpiTxData, ARRAYSIZE(kSpiTxData));
CHECK_DIF_OK(dif_spi_device_set_flash_status_registers(&spid, 0x00));

CHECK_DIF_OK(dif_spi_device_write_flash_buffer(
&spid, kDifSpiDeviceFlashBufferTypeEFlash, 0, ARRAYSIZE(kSpiTxData),
kSpiTxData));

LOG_INFO("SYNC: Waiting for read");

busy_spin_micros(1000);
uint32_t address;
CHECK_DIF_OK(dif_spi_device_get_last_read_address(&spid, &address));
CHECK(address == (kSpiDeviceFlashAddress + kSpiDeviceDatasetSize - 1));

return true;
}
23 changes: 23 additions & 0 deletions sw/host/tests/chip/spi_device_flash_smoketest/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

load("@rules_rust//rust:defs.bzl", "rust_binary")

package(default_visibility = ["//visibility:public"])

rust_binary(
name = "spi_device_flash_smoketest",
srcs = [
"src/main.rs",
],
deps = [
"//sw/host/opentitanlib",
"//third_party/rust/crates:anyhow",
"//third_party/rust/crates:clap",
"//third_party/rust/crates:humantime",
"//third_party/rust/crates:log",
"//third_party/rust/crates:regex",
"@crate_index//:object",
],
)
112 changes: 112 additions & 0 deletions sw/host/tests/chip/spi_device_flash_smoketest/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::{bail, Result};
use clap::Parser;
use regex::Regex;
use std::fs;
use std::path::PathBuf;
use std::time::Duration;

use opentitanlib::app::TransportWrapper;
use opentitanlib::execute_test;
use opentitanlib::io::eeprom::AddressMode;
use opentitanlib::io::spi::{Target, Transfer};
use opentitanlib::spiflash::sfdp::SectorErase;
use opentitanlib::spiflash::{EraseMode, ReadMode, Sfdp, SpiFlash};
use opentitanlib::test_utils;
use opentitanlib::test_utils::init::InitializeTest;
use opentitanlib::test_utils::spi_passthru::{
ConfigJedecId, SfdpData, SpiFlashEraseSector, SpiFlashReadSfdp, SpiFlashWrite, SpiMailboxMap,
SpiMailboxWrite, SpiPassthruSwapMap, StatusRegister, UploadInfo,
};
use opentitanlib::transport::Capability;
use opentitanlib::uart::console::{ExitStatus, UartConsole};

#[derive(Debug, Parser)]
struct Opts {
#[command(flatten)]
init: InitializeTest,

/// Console receive timeout.
#[arg(long, value_parser = humantime::parse_duration, default_value = "600s")]
timeout: Duration,

/// Name of the SPI interface to connect to the OTTF console.
#[arg(long, default_value = "BOOTSTRAP")]
spi: String,

/// Path to the firmware's ELF file, for querying symbol addresses.
#[arg(value_name = "FIRMWARE_ELF")]
firmware_elf: PathBuf,
}

const SYNC_MSG: &str = r"SYNC:.*\r\n";

fn device_tx_rx_test(
opts: &Opts,
transport: &TransportWrapper,
data: &[u8],
address: u32,
) -> Result<()> {
let uart = transport.uart("console")?;
let spi = transport.spi(&opts.spi)?;

let mut flash = SpiFlash {
// Double the flash size so we can test 3b and 4b addresses.
size: 32 * 1024 * 1024,
..Default::default()
};

// Make sure we're in a mode appropriate for the address.
let mode = if address < 0x1000000 {
AddressMode::Mode3b
} else {
AddressMode::Mode4b
};
flash.set_address_mode(&*spi, mode)?;

/* Wait sync message. */
let _ = UartConsole::wait_for(&*uart, SYNC_MSG, opts.timeout)?;
flash.program(&*spi, address, &data)?;

/* Wait sync message. */
let _ = UartConsole::wait_for(&*uart, SYNC_MSG, opts.timeout)?;
let mut buffer = vec![0xff; data.len()];
flash.read(&*spi, address, &mut buffer)?;

assert_eq!(buffer, data);

Ok(())
}

fn main() -> Result<()> {
let opts = Opts::parse();
opts.init.init_logging();

/* Load the ELF binary and get the expect data.*/
let elf_binary = fs::read(&opts.firmware_elf)?;
let object = object::File::parse(&*elf_binary)?;

let tx_data = test_utils::object::symbol_data(&object, "kSpiTxData")?;

let transport = opts.init.init_target()?;
execute_test!(device_tx_rx_test, &opts, &transport, &tx_data, 0x00);

let uart = transport.uart("console")?;
let mut console = UartConsole {
timeout: Some(opts.timeout),
exit_success: Some(Regex::new(r"PASS!\r\n")?),
exit_failure: Some(Regex::new(r"FAIL:")?),
..Default::default()
};

// Now watch the console for the exit conditions.
let result = console.interact(&*uart, None, Some(&mut std::io::stdout()))?;
if result != ExitStatus::ExitSuccess {
bail!("FAIL: {:?}", result);
};

Ok(())
}

0 comments on commit a0c7f5d

Please sign in to comment.