diff --git a/sw/device/lib/dif/dif_spi_host.c b/sw/device/lib/dif/dif_spi_host.c index dabebd3739450..8d7add1997b47 100644 --- a/sw/device/lib/dif/dif_spi_host.c +++ b/sw/device/lib/dif/dif_spi_host.c @@ -369,10 +369,14 @@ static dif_result_t issue_data_phase(const dif_spi_host_t *spi_host, return kDifOk; } -dif_result_t dif_spi_host_transaction(const dif_spi_host_t *spi_host, - uint32_t csid, - dif_spi_host_segment_t *segments, - size_t length) { +dif_result_t dif_spi_host_start_transaction(const dif_spi_host_t *spi_host, + uint32_t csid, + dif_spi_host_segment_t *segments, + size_t length) { + if (spi_host == NULL || segments == NULL) { + return kDifBadArg; + } + // Write to chip select ID. mmio_region_write32(spi_host->base_addr, SPI_HOST_CSID_REG_OFFSET, csid); @@ -405,6 +409,15 @@ dif_result_t dif_spi_host_transaction(const dif_spi_host_t *spi_host, return kDifBadArg; } } + return kDifOk; +} + +dif_result_t dif_spi_host_transaction(const dif_spi_host_t *spi_host, + uint32_t csid, + dif_spi_host_segment_t *segments, + size_t length) { + DIF_RETURN_IF_ERROR( + dif_spi_host_start_transaction(spi_host, csid, segments, length)); // For each segment which receives data, read from the receive FIFO. for (size_t i = 0; i < length; ++i) { diff --git a/sw/device/lib/dif/dif_spi_host.h b/sw/device/lib/dif/dif_spi_host.h index a7c7ae8e41386..3548b1d3f8114 100644 --- a/sw/device/lib/dif/dif_spi_host.h +++ b/sw/device/lib/dif/dif_spi_host.h @@ -197,6 +197,21 @@ OT_WARN_UNUSED_RESULT dif_result_t dif_spi_host_fifo_read(const dif_spi_host_t *spi_host, void *dst, uint16_t len); +/** + * Begins a SPI Host transaction without reading the FIFOs. + * + * @param spi_host A SPI Host handle. + * @param csid The chip-select ID of the SPI target. + * @param segments The SPI segments to send in this transaction. + * @param length The number of SPI segments in this transaction. + * @return The result of the operation. + */ +OT_WARN_UNUSED_RESULT +dif_result_t dif_spi_host_start_transaction(const dif_spi_host_t *spi_host, + uint32_t csid, + dif_spi_host_segment_t *segments, + size_t length); + /** * Begins a SPI Host transaction. * diff --git a/sw/device/lib/dif/dif_spi_host_unittest.cc b/sw/device/lib/dif/dif_spi_host_unittest.cc index da85ddb7800b4..d9433990a213d 100644 --- a/sw/device/lib/dif/dif_spi_host_unittest.cc +++ b/sw/device/lib/dif/dif_spi_host_unittest.cc @@ -278,13 +278,13 @@ TEST_F(ConfigTest, SpiTxRxWatermark) { EXPECT_DIF_OK(dif_spi_host_configure(&spi_host_, config_)); } -class TransactionTest : public SpiHostTest { +class TransactionStartTest : public SpiHostTest { protected: MockFifo fifo_; }; // Checks that an opcode segment is sent correctly. -TEST_F(TransactionTest, IssueOpcode) { +TEST_F(TransactionStartTest, IssueOpcode) { dif_spi_host_segment segment; segment.type = kDifSpiHostSegmentTypeOpcode; segment.opcode.opcode = 0x5a; @@ -302,7 +302,7 @@ TEST_F(TransactionTest, IssueOpcode) { } // Checks that an address segment is sent correctly in 3-byte mode. -TEST_F(TransactionTest, IssueAddressMode3b) { +TEST_F(TransactionStartTest, IssueAddressMode3b) { dif_spi_host_segment segment; segment.type = kDifSpiHostSegmentTypeAddress; segment.address.width = kDifSpiHostWidthStandard; @@ -321,7 +321,7 @@ TEST_F(TransactionTest, IssueAddressMode3b) { } // Checks that an address segment is sent correctly in 4-byte mode. -TEST_F(TransactionTest, IssueAddressMode4b) { +TEST_F(TransactionStartTest, IssueAddressMode4b) { dif_spi_host_segment segment; segment.type = kDifSpiHostSegmentTypeAddress; segment.address.width = kDifSpiHostWidthStandard; @@ -340,7 +340,7 @@ TEST_F(TransactionTest, IssueAddressMode4b) { } // Checks that a dummy segment is sent correctly. -TEST_F(TransactionTest, IssueDummy) { +TEST_F(TransactionStartTest, IssueDummy) { dif_spi_host_segment segment; segment.type = kDifSpiHostSegmentTypeDummy; segment.dummy.width = kDifSpiHostWidthStandard; @@ -354,6 +354,11 @@ TEST_F(TransactionTest, IssueDummy) { EXPECT_DIF_OK(dif_spi_host_transaction(&spi_host_, 0, &segment, 1)); } +class TransactionTest : public SpiHostTest { + protected: + MockFifo fifo_; +}; + // Checks that a transmit segment is sent correctly. TEST_F(TransactionTest, TransmitDual) { uint8_t buf[32]; diff --git a/sw/device/lib/testing/BUILD b/sw/device/lib/testing/BUILD index e9c2c076f12cd..5eb67eae63613 100644 --- a/sw/device/lib/testing/BUILD +++ b/sw/device/lib/testing/BUILD @@ -573,3 +573,18 @@ cc_library( "//sw/device/lib/testing/test_framework:check", ], ) + +cc_library( + name = "dma_testutils", + srcs = ["dma_testutils.c"], + hdrs = ["dma_testutils.h"], + target_compatible_with = [OPENTITAN_CPU], + deps = [ + "//hw/top_darjeeling/sw/autogen:top_darjeeling", + "//sw/device/lib/dif:dma", + "//sw/device/lib/dif:pinmux", + "//sw/device/lib/dif:spi_host", + "//sw/device/lib/runtime:ibex", + "//sw/device/lib/testing/test_framework:check", + ], +) diff --git a/sw/device/lib/testing/dma_testutils.c b/sw/device/lib/testing/dma_testutils.c new file mode 100644 index 0000000000000..8391b25e32407 --- /dev/null +++ b/sw/device/lib/testing/dma_testutils.c @@ -0,0 +1,89 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/lib/testing/dma_testutils.h" + +#include "hw/top_darjeeling/sw/autogen/top_darjeeling.h" +#include "spi_host_regs.h" // Generated. + +static const top_darjeeling_direct_pads_t spi_host0_direct_pads[6] = { + kTopDarjeelingDirectPadsSpiHost0Sck, // sck + kTopDarjeelingDirectPadsSpiHost0Csb, // csb + kTopDarjeelingDirectPadsSpiHost0Sd3, // sio[3] + kTopDarjeelingDirectPadsSpiHost0Sd2, // sio[2] + kTopDarjeelingDirectPadsSpiHost0Sd1, // sio[1] + kTopDarjeelingDirectPadsSpiHost0Sd0}; // sio[0] + +/** + * Initialize the provided SPI host for being used in the DMA hardware handshake + * mode. + */ +void init_spi_host(dif_spi_host_t *spi_host, uint32_t peripheral_clock_freq_hz, + uint32_t rx_watermark) { + dif_spi_host_config_t config = { + .spi_clock = peripheral_clock_freq_hz / 2, + .peripheral_clock_freq_hz = peripheral_clock_freq_hz, + .chip_select = {.idle = 2, .trail = 2, .lead = 2}, + .full_cycle = true, + .cpha = true, + .cpol = true, + .rx_watermark = rx_watermark}; + CHECK_DIF_OK(dif_spi_host_configure(spi_host, config)); + CHECK_DIF_OK(dif_spi_host_output_set_enabled(spi_host, /*enabled=*/true)); +} + +/** + * Setup pads for spi_host0 + * + * This peripheral is 'direct' connected to the pads. + */ +void setup_pads_spi_host0(dif_pinmux_t *pinmux) { + // set weak pull-ups for all the pads + dif_pinmux_pad_attr_t out_attr; + dif_pinmux_pad_attr_t in_attr = { + .slew_rate = 0, + .drive_strength = 0, + .flags = kDifPinmuxPadAttrPullResistorEnable | + kDifPinmuxPadAttrPullResistorUp}; + for (uint32_t i = 0; i <= ARRAYSIZE(spi_host0_direct_pads); ++i) { + CHECK_DIF_OK(dif_pinmux_pad_write_attrs(pinmux, spi_host0_direct_pads[i], + kDifPinmuxPadKindDio, in_attr, + &out_attr)); + } +} + +void setup_spi_dma_transaction(dif_spi_host_t *spi_host, dif_dma_t *dma, + uint8_t *rx_buffer, uint32_t chunk_size, + uint32_t total_size) { + dif_spi_host_segment_t host_operations[1] = { + {.type = kDifSpiHostSegmentTypeRx, + .tx = {.width = kDifSpiHostWidthStandard, + .buf = NULL, + .length = total_size}}, + }; + + // Issue the SPI transaction + CHECK_DIF_OK(dif_spi_host_start_transaction( + spi_host, /*csid=*/0, host_operations, ARRAYSIZE(host_operations))); + + // Configure the DMA to read from SPI in hardware-handshake mode + dif_dma_transaction_t transaction = { + .source = {.address = TOP_DARJEELING_SPI_HOST0_BASE_ADDR + + SPI_HOST_RXDATA_REG_OFFSET, + .asid = kDifDmaOpentitanInternalBus}, + .destination = {.address = (uint32_t)&rx_buffer[0], + .asid = kDifDmaOpentitanInternalBus}, + .src_config = {.wrap = false, .increment = false}, + .dst_config = {.wrap = false, .increment = true}, + .total_size = total_size, + .chunk_size = chunk_size, + .width = kDifDmaTransWidth4Bytes}; + + CHECK_DIF_OK(dif_dma_memory_range_set(dma, TOP_DARJEELING_RAM_MAIN_BASE_ADDR, + TOP_DARJEELING_RAM_MAIN_SIZE_BYTES)); + // Enable LSIO trigger for SPI host at bit 1 + CHECK_DIF_OK(dif_dma_handshake_irq_enable(dma, 0x2)); + CHECK_DIF_OK(dif_dma_configure(dma, transaction)); + CHECK_DIF_OK(dif_dma_handshake_enable(dma)); +} diff --git a/sw/device/lib/testing/dma_testutils.h b/sw/device/lib/testing/dma_testutils.h new file mode 100644 index 0000000000000..a8fe54398b833 --- /dev/null +++ b/sw/device/lib/testing/dma_testutils.h @@ -0,0 +1,58 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_LIB_TESTING_DMA_TESTUTILS_H_ +#define OPENTITAN_SW_DEVICE_LIB_TESTING_DMA_TESTUTILS_H_ + +#include "sw/device/lib/dif/dif_dma.h" +#include "sw/device/lib/dif/dif_pinmux.h" +#include "sw/device/lib/dif/dif_spi_host.h" +#include "sw/device/lib/testing/test_framework/check.h" + +/** + * Initialize the provided SPI host for being used in the DMA hardware handshake + * mode. + * + * @param spi_host An spi_host DIF handle. + * @param peripheral_clock_freq_hz The peripheral clock frequency in hertz. + * @param rx_watermark The watermark level of the FIFO to raise the status IRQ + * for the DMA. + */ +void init_spi_host(dif_spi_host_t *spi_host, uint32_t peripheral_clock_freq_hz, + uint32_t rx_watermark); + +/** + * Setup pads for spi_host0 + * + * This peripheral is 'direct' connected to the pads. + * + * @param pinmux An pinmux DIF handle. + */ +void setup_pads_spi_host0(dif_pinmux_t *pinmux); + +/** + * Configure the SPI for a receiving transaction and configure the DMA for the + * hardware handshake mode to read the SPI host. + * + * @param spi_host An spi_host DIF handle. + * @param dma An DMA DIF handle. + * @param rx_buffer Pointer to the receiving buffer, where the DMA writes the + * received data. + * @param chunk_size The size of one chunk read from the LSIO peripheral. + * @param total_size The total size of data to be read from the LSIO + * peripheral. + */ +void setup_spi_dma_transaction(dif_spi_host_t *spi_host, dif_dma_t *dma, + uint8_t *rx_buffer, uint32_t chunk_size, + uint32_t total_size); + +/** + * Return the digest length given a DMA opcode. + * + * @param opcode A DMA opcode. + * @return The digest length. + */ +uint32_t get_digest_length(dif_dma_transaction_opcode_t opcode); + +#endif // OPENTITAN_SW_DEVICE_LIB_TESTING_DMA_TESTUTILS_H_ diff --git a/sw/device/tests/BUILD b/sw/device/tests/BUILD index 5d4e0c4b013d5..5179a1321f7f3 100644 --- a/sw/device/tests/BUILD +++ b/sw/device/tests/BUILD @@ -7432,3 +7432,45 @@ opentitan_test( ), deps = [":rom_exit_immediately_lib"], ) + +opentitan_test( + name = "dma_inline_hashing", + srcs = ["dma_inline_hashing.c"], + exec_env = { + "//hw/top_darjeeling:sim_dv": None, + }, + deps = [ + "//hw/top:dt", + "//sw/device/lib/base:macros", + "//sw/device/lib/base:mmio", + "//sw/device/lib/dif:dma", + "//sw/device/lib/dif:pinmux", + "//sw/device/lib/dif:rv_plic", + "//sw/device/lib/dif:spi_host", + "//sw/device/lib/runtime:log", + "//sw/device/lib/runtime:print", + "//sw/device/lib/testing:dma_testutils", + "//sw/device/lib/testing:pinmux_testutils", + "//sw/device/lib/testing/test_framework:ottf_main", + ], +) + +opentitan_test( + name = "dma_abort", + srcs = ["dma_abort.c"], + exec_env = { + "//hw/top_darjeeling:sim_dv": None, + }, + deps = [ + "//hw/top:dt", + "//sw/device/lib/base:macros", + "//sw/device/lib/base:mmio", + "//sw/device/lib/dif:dma", + "//sw/device/lib/dif:spi_host", + "//sw/device/lib/runtime:log", + "//sw/device/lib/runtime:print", + "//sw/device/lib/testing:dma_testutils", + "//sw/device/lib/testing:pinmux_testutils", + "//sw/device/lib/testing/test_framework:ottf_main", + ], +) diff --git a/sw/device/tests/dma_abort.c b/sw/device/tests/dma_abort.c new file mode 100644 index 0000000000000..a47611a72959d --- /dev/null +++ b/sw/device/tests/dma_abort.c @@ -0,0 +1,104 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "dt/dt_dma.h" +#include "dt/dt_pinmux.h" +#include "dt/dt_spi_host.h" +#include "sw/device/lib/arch/device.h" +#include "sw/device/lib/base/mmio.h" +#include "sw/device/lib/dif/dif_base.h" +#include "sw/device/lib/dif/dif_dma.h" +#include "sw/device/lib/dif/dif_spi_host.h" +#include "sw/device/lib/runtime/hart.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/dma_testutils.h" +#include "sw/device/lib/testing/pinmux_testutils.h" +#include "sw/device/lib/testing/rand_testutils.h" +#include "sw/device/lib/testing/test_framework/check.h" +#include "sw/device/lib/testing/test_framework/ottf_main.h" +#include "sw/device/lib/testing/test_framework/status.h" + +// The TX_SIZE must be in sync with the data size in spi_device_dma_seq.sv +// 1 SPI segment can only transfer at maximum 512 bytes +#define TX_SIZE 512 +#define CHUNK_SIZE 32 * 4 // Half the SPI host FIFO size + +OTTF_DEFINE_TEST_CONFIG(); + +enum { + kSoftwareBarrierTimeoutUsec = 500, +}; + +// This location will be update from SV +static volatile const uint8_t kSoftwareBarrier = 0; + +// Expected digest value gets backdoor'ed from the hardware +// Although not used, we need to keep it here as the shared vseq +// wants to write it. +static volatile uint32_t kShaDigestExpData[16]; +static volatile uint8_t kShaMode; + +uint32_t digest[16]; +uint8_t received_data[TX_SIZE] __attribute__((aligned(4))); + +static dif_spi_host_t spi_host; +static dif_pinmux_t pinmux; +static dif_dma_t dma; + +bool test_main(void) { + // Initialize the pinmux. + CHECK_DIF_OK(dif_pinmux_init_from_dt(kDtPinmuxAon, &pinmux)); + pinmux_testutils_init(&pinmux); + + // Initialise DMA. + CHECK_DIF_OK(dif_dma_init_from_dt(kDtDma, &dma)); + + // Setup pinmux if required, enable weak pull-up on relevant pads + setup_pads_spi_host0(&pinmux); // direct + + // Setup spi host configuration + CHECK_DIF_OK(dif_spi_host_init_from_dt((dt_spi_host_t)0, &spi_host)); + init_spi_host(&spi_host, (uint32_t)kClockFreqHiSpeedPeripheralHz, + CHUNK_SIZE / 4); + + // DV sync message + LOG_INFO("spi host configuration complete"); + + // Dummy assignment to avoid any unused variable warnings + kShaDigestExpData[0] = 0; + + setup_spi_dma_transaction(&spi_host, &dma, &received_data[0], CHUNK_SIZE, + TX_SIZE); + + CHECK_DIF_OK(dif_dma_start(&dma, kShaMode)); + + // Wait until the DMA has started to abort the transfer + IBEX_SPIN_FOR(kSoftwareBarrier == 1, kSoftwareBarrierTimeoutUsec); + + CHECK_DIF_OK(dif_dma_abort(&dma)); + + dif_dma_status_t status; + CHECK_DIF_OK(dif_dma_status_get(&dma, &status)); + + CHECK(status & kDifDmaStatusAborted, "Abort bit not set"); + + // Reset and re-init the SPI + init_spi_host(&spi_host, (uint32_t)kClockFreqHiSpeedPeripheralHz, + CHUNK_SIZE / 4); + LOG_INFO("spi host re-configuration complete"); + + // Do the second transaction + setup_spi_dma_transaction(&spi_host, &dma, &received_data[0], CHUNK_SIZE, + TX_SIZE); + CHECK_DIF_OK(dif_dma_start(&dma, kShaMode)); + CHECK_DIF_OK(dif_dma_status_poll(&dma, kDifDmaStatusDone)); + + CHECK_DIF_OK(dif_dma_sha2_digest_get(&dma, kShaMode, digest)); + + uint32_t digest_len; + CHECK_DIF_OK(dif_dma_get_digest_length(kShaMode, &digest_len)); + CHECK_ARRAYS_EQ((uint8_t *)digest, (uint8_t *)kShaDigestExpData, digest_len); + + return true; +} diff --git a/sw/device/tests/dma_inline_hashing.c b/sw/device/tests/dma_inline_hashing.c new file mode 100644 index 0000000000000..ee0e5c2bf2448 --- /dev/null +++ b/sw/device/tests/dma_inline_hashing.c @@ -0,0 +1,251 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "dt/dt_dma.h" +#include "dt/dt_pinmux.h" +#include "dt/dt_rv_core_ibex.h" +#include "dt/dt_rv_plic.h" +#include "dt/dt_spi_host.h" +#include "sw/device/lib/arch/device.h" +#include "sw/device/lib/base/mmio.h" +#include "sw/device/lib/dif/dif_base.h" +#include "sw/device/lib/dif/dif_dma.h" +#include "sw/device/lib/dif/dif_pinmux.h" +#include "sw/device/lib/dif/dif_rv_plic.h" +#include "sw/device/lib/runtime/hart.h" +#include "sw/device/lib/runtime/irq.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/dma_testutils.h" +#include "sw/device/lib/testing/pinmux_testutils.h" +#include "sw/device/lib/testing/rand_testutils.h" +#include "sw/device/lib/testing/rv_core_ibex_testutils.h" +#include "sw/device/lib/testing/test_framework/check.h" +#include "sw/device/lib/testing/test_framework/ottf_main.h" +#include "sw/device/lib/testing/test_framework/status.h" + +// The TX_SIZE must be in sync with the data size in spi_device_dma_seq.sv +// 1 SPI segment can only transfer at maximum 512 bytes +#define TX_SIZE 512 +#define CHUNK_SIZE 32 * 4 // Half the SPI host FIFO size + +OTTF_DEFINE_TEST_CONFIG(); + +enum { + kHart = kTopDarjeelingPlicTargetIbex0, + kIrqVoid = UINT32_MAX, +}; + +dif_dma_transaction_width_t dma_transfer_widths[] = { + kDifDmaTransWidth1Byte, kDifDmaTransWidth2Bytes, kDifDmaTransWidth4Bytes}; + +// Expected digest value gets backdoor'ed from the hardware +static volatile const uint32_t kShaDigestExpData[16]; +static volatile const uint8_t kShaMode; + +uint32_t digest[16], digest_2[16]; +uint8_t received_data[TX_SIZE] __attribute__((aligned(4))); +uint8_t target_ot_internal_data[TX_SIZE] __attribute__((aligned(4))); +uint8_t target_ctn_data[TX_SIZE] __attribute__((aligned(4))) +__attribute__((section(".ctn_data"))); +static volatile bool is_finished; + +static dif_spi_host_t spi_host; +static dif_pinmux_t pinmux; +static dif_dma_t dma; +static dif_rv_core_ibex_t rv_core_ibex; +static dif_rv_plic_t rv_plic; + +/** + * Enable the interrupts required by this test. + */ +static void init_interrupts(void) { + irq_global_ctrl(false); + irq_external_ctrl(false); + + // Set Ibex IRQ priority threshold level to lowest (0) + // - All IRQs with prio > 0 will not be masked + CHECK_DIF_OK( + dif_rv_plic_target_set_threshold(&rv_plic, kHart, kDifRvPlicMinPriority)); + + // Enable IRQs at rv_plic + // - enable + // - set prio > 0 + CHECK_DIF_OK(dif_rv_plic_irq_set_enabled( + &rv_plic, kTopDarjeelingPlicIrqIdDmaDmaDone, kHart, kDifToggleEnabled)); + CHECK_DIF_OK(dif_rv_plic_irq_set_priority( + &rv_plic, kTopDarjeelingPlicIrqIdDmaDmaDone, kDifRvPlicMaxPriority)); + // Enable IRQs at the peripheral + CHECK_DIF_OK( + dif_dma_irq_set_enabled(&dma, kDifDmaIrqDmaDone, kDifToggleEnabled)); + + irq_external_ctrl(true); + irq_global_ctrl(true); +} + +/** + * External ISR handler for this test. + * (Our overridden ottf_external_isr() calls this function only.) + * + * - Claim the interrupt + * - Check this irq_id is valid for this test + * continue + */ +static status_t external_isr(void) { + dif_dma_irq_t dma_irq_id; + dif_rv_plic_irq_id_t plic_irq_id; + top_darjeeling_plic_peripheral_t peripheral; + + // (1) First, find which interrupt fired at PLIC by claiming it. + TRY(dif_rv_plic_irq_claim(&rv_plic, kHart, &plic_irq_id)); + + // Check the plic_irq is actually from a DMA peripheral + // This test currently cannot handle any other interrupts, as the logic/ISRs + // are not sufficiently robust. + CHECK(plic_irq_id >= kTopDarjeelingPlicIrqIdDmaDmaDone && + plic_irq_id <= kTopDarjeelingPlicIrqIdDmaDmaError, + "got an irq from a plic_peripheral that is not a DMA!"); + + peripheral = (top_darjeeling_plic_peripheral_t) + top_darjeeling_plic_interrupt_for_peripheral[plic_irq_id]; + + dif_rv_plic_irq_id_t plic_periph_base_irq_id = + kTopDarjeelingPlicIrqIdDmaDmaDone; + + if (peripheral != kTopDarjeelingPlicPeripheralDma) { + CHECK(false, "Invalid plic_irq_id that from a DMA!"); + } + + dma_irq_id = (dif_dma_irq_t)(plic_irq_id - plic_periph_base_irq_id); + + // (2) Handle the peripheral + if (dma_irq_id == kDifDmaIrqDmaDone) { + // Mask the interrupt (also for the next test) + CHECK_DIF_OK(dif_dma_irq_set_enabled(&dma, dma_irq_id, kDifToggleDisabled)); + CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(&rv_plic, plic_irq_id, kHart, + kDifToggleDisabled)); + + } else { + CHECK(false, "Invalid dma_irq_id: %d", dma_irq_id); + } + + // (3) Clear the IRQ at the peripheral and at the PLIC. + // - This section is lifted from the end of the isr_testutils autgenerated + // handler + // - Only the plic_irq_complete() routine matters, since we cannot-yet clear + // the + // INTR_STATE reg at the dma as the event input is still asserted. + + // Acknowledge the IRQ at the peripheral if IRQ is of the event type. + CHECK_DIF_OK(dif_dma_irq_acknowledge(&dma, dma_irq_id)); + + // Complete the IRQ at the PLIC. + CHECK_DIF_OK(dif_rv_plic_irq_complete(&rv_plic, kHart, plic_irq_id)); + + // Set the boolean which allows wfi_flag() to return. + is_finished = true; + + return OK_STATUS(); +} + +static volatile status_t isr_result; +/* This overrides the weak-symbol for ottf_external_isr() */ +void ottf_external_isr(void) { + status_t tmp = external_isr(); + if (status_ok(isr_result)) { + isr_result = tmp; + } +} + +bool test_main(void) { + // Initialize the pinmux. + CHECK_DIF_OK(dif_pinmux_init_from_dt(kDtPinmuxAon, &pinmux)); + pinmux_testutils_init(&pinmux); + + // Initialise DMA. + CHECK_DIF_OK(dif_dma_init_from_dt(kDtDma, &dma)); + + // Initialize the PLIC + CHECK_DIF_OK(dif_rv_core_ibex_init_from_dt(kDtRvCoreIbex, &rv_core_ibex)); + CHECK_DIF_OK(dif_rv_plic_init_from_dt(kDtRvPlic, &rv_plic)); + + // Setup pinmux if required, enable weak pull-up on relevant pads + setup_pads_spi_host0(&pinmux); // direct + + // Setup spi host configuration + CHECK_DIF_OK(dif_spi_host_init_from_dt((dt_spi_host_t)0, &spi_host)); + init_spi_host(&spi_host, (uint32_t)kClockFreqHiSpeedPeripheralHz, + CHUNK_SIZE / 4); + + init_interrupts(); + + // DV sync message + LOG_INFO("spi host configuration complete"); + + // Based on the SHA mode, determine the digest length + uint32_t digest_len; + CHECK_DIF_OK(dif_dma_get_digest_length(kShaMode, &digest_len)); + + setup_spi_dma_transaction(&spi_host, &dma, &received_data[0], CHUNK_SIZE, + TX_SIZE); + + CHECK_DIF_OK(dif_dma_start(&dma, kShaMode)); + + // Loop WFI->ISR->WFI->etc. until 'is_finished' is set true + // Use this to only advance iff our ISR sets it + ATOMIC_WAIT_FOR_INTERRUPT(is_finished); + + dif_dma_status_code_t status; + CHECK_DIF_OK(dif_dma_status_get(&dma, &status)); + + CHECK((status & kDifDmaStatusDone) == kDifDmaStatusDone, + "DMA status done not asserted"); + CHECK((status & kDifDmaStatusSha2DigestValid) == kDifDmaStatusSha2DigestValid, + "DMA status digest valid not asserted"); + + CHECK_DIF_OK(dif_dma_sha2_digest_get(&dma, kShaMode, digest)); + + // Randomize the transfer width, which is possible since we are not using the + // inline hashing mode + dif_dma_transaction_width_t transfer_width = + dma_transfer_widths[rand_testutils_gen32_range( + 0, ARRAYSIZE(dma_transfer_widths) - 1)]; + + dif_dma_transaction_address_t dest_transaction_address; + // Decide where to transfer the second transfer the data to + if (rand_testutils_gen32_range(0, 1) == 0) { + // OT internal memory + dest_transaction_address = (dif_dma_transaction_address_t){ + .address = (uint32_t)&target_ot_internal_data[0], + .asid = kDifDmaOpentitanInternalBus}; + } else { + // CTN memory + dest_transaction_address = (dif_dma_transaction_address_t){ + .address = (uint32_t)&target_ctn_data[0], + .asid = kDifDmaSoCControlRegisterBus}; + } + + // We only check the digest. If thats valid, we assume the correct data to be + // transferred + CHECK_ARRAYS_EQ((uint8_t *)digest, (uint8_t *)kShaDigestExpData, digest_len); + + dif_dma_transaction_t transaction = { + .source = {.address = (uint32_t)&received_data[0], + .asid = kDifDmaOpentitanInternalBus}, + .destination = dest_transaction_address, + .total_size = TX_SIZE, + .chunk_size = TX_SIZE, + .width = transfer_width}; + + CHECK_DIF_OK(dif_dma_handshake_irq_enable(&dma, 0x0)); + CHECK_DIF_OK(dif_dma_configure(&dma, transaction)); + CHECK_DIF_OK(dif_dma_handshake_disable(&dma)); + + CHECK_DIF_OK(dif_dma_start(&dma, kDifDmaCopyOpcode)); + CHECK_DIF_OK(dif_dma_status_poll(&dma, kDifDmaStatusDone)); + + CHECK_ARRAYS_EQ((uint8_t *)received_data, + (uint8_t *)dest_transaction_address.address, TX_SIZE); + + return true; +}