blob: e87537f6289fc9289acf5b848ba43ee5ef0fc90d [file] [log] [blame]
/*
* Copyright 2023 Google LLC
* Copyright lowRISC contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// SMC application that exercises the I2S Receive features.
#include "hw/top_matcha/ip/i2s/data/i2s_regs.h"
#include "hw/top_matcha/sw/autogen/top_matcha.h"
#include "sw/device/lib/dif/dif_i2s.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/runtime/print.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_test_config.h"
#include "sw/device/lib/testing/test_framework/status.h"
#include "sw/device/lib/testing/test_framework/test_util.h"
OTTF_DEFINE_TEST_CONFIG();
static dif_i2s_t i2s;
static dif_rv_plic_t plic_smc;
static dif_uart_t smc_uart;
volatile bool i2s_rx_watermark_seen = false;
volatile bool i2s_rx_overflow_seen = false;
void ottf_external_isr(void) {
dif_rv_plic_irq_id_t interrupt_id;
CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic_smc, kTopMatchaPlicTargetIbex0Smc,
&interrupt_id));
top_matcha_plic_peripheral_smc_t peripheral_id =
top_matcha_plic_interrupt_for_peripheral_smc[interrupt_id];
CHECK(peripheral_id == kTopMatchaPlicPeripheralI2s0,
"ISR interrupted peripheral is not I2S!");
switch (peripheral_id) {
case kTopMatchaPlicPeripheralI2s0: {
switch (interrupt_id) {
case kTopMatchaPlicIrqIdI2s0RxWatermark:
// Acknowledge and disable the interrupt.
// It will be re-enabled after the RX FIFO is drained.
CHECK_DIF_OK(dif_i2s_irq_acknowledge(&i2s, kDifI2sIrqRxWatermark));
CHECK_DIF_OK(dif_i2s_irq_set_enabled(&i2s, kDifI2sIrqRxWatermark,
kDifToggleDisabled));
i2s_rx_watermark_seen = true;
break;
case kTopMatchaPlicIrqIdI2s0RxOverflow:
// Acknowlege and disable the interrupt.
CHECK_DIF_OK(dif_i2s_irq_acknowledge(&i2s, kDifI2sIrqRxOverflow));
CHECK_DIF_OK(dif_i2s_irq_set_enabled(&i2s, kDifI2sIrqRxWatermark,
kDifToggleDisabled));
i2s_rx_overflow_seen = true;
break;
default:
LOG_FATAL("Interrupt is not an I2S interrupt");
}
break;
}
default:
LOG_FATAL("Peripheral is not implemented!");
}
// Complete the IRQ by writing the IRQ source to the Ibex specific CC
// register.
CHECK_DIF_OK(dif_rv_plic_irq_complete(&plic_smc, kTopMatchaPlicTargetIbex0Smc,
interrupt_id));
}
#define kSamples (6)
uint32_t samples[kSamples];
void _ottf_main(void) {
uint32_t reg_val;
test_status_set(kTestStatusInTest);
// Initialize the SMC UART to enable logging for non-DV simulation platforms.
if (kDeviceType != kDeviceSimDV) {
init_uart(TOP_MATCHA_SMC_UART_BASE_ADDR, &smc_uart);
}
CHECK_DIF_OK(dif_rv_plic_init(
mmio_region_from_addr(TOP_MATCHA_RV_PLIC_SMC_BASE_ADDR), &plic_smc));
CHECK_DIF_OK(dif_rv_plic_irq_set_priority(
&plic_smc, kTopMatchaPlicIrqIdI2s0RxWatermark, kDifRvPlicMaxPriority));
CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
&plic_smc, kTopMatchaPlicIrqIdI2s0RxWatermark,
kTopMatchaPlicTargetIbex0Smc, kDifToggleEnabled));
CHECK_DIF_OK(dif_rv_plic_irq_set_priority(
&plic_smc, kTopMatchaPlicIrqIdI2s0RxOverflow, kDifRvPlicMaxPriority));
CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
&plic_smc, kTopMatchaPlicIrqIdI2s0RxOverflow,
kTopMatchaPlicTargetIbex0Smc, kDifToggleEnabled));
irq_global_ctrl(true);
irq_external_ctrl(true);
memset(samples, 0xa5, ARRAYSIZE(samples));
CHECK_DIF_OK(
dif_i2s_init(mmio_region_from_addr(TOP_MATCHA_I2S0_BASE_ADDR), &i2s));
// Clear RX FIFO
reg_val = mmio_region_read32(i2s.base_addr, I2S_FIFO_CTRL_REG_OFFSET);
reg_val = bitfield_bit32_write(reg_val, I2S_FIFO_CTRL_RXRST_BIT, 1);
mmio_region_write32(i2s.base_addr, I2S_FIFO_CTRL_REG_OFFSET, reg_val);
int sample = 0;
CHECK_DIF_OK(dif_i2s_irq_acknowledge_all(&i2s));
CHECK_DIF_OK(
dif_i2s_irq_set_enabled(&i2s, kDifI2sIrqRxOverflow, kDifToggleEnabled));
reg_val = mmio_region_read32(i2s.base_addr, I2S_CTRL_REG_OFFSET);
reg_val = bitfield_bit32_write(reg_val, I2S_CTRL_RX_BIT, 1);
reg_val = bitfield_field32_write(reg_val, I2S_CTRL_NCO_RX_FIELD,
11); // divide by 22
mmio_region_write32(i2s.base_addr, I2S_CTRL_REG_OFFSET, reg_val);
while (sample < ARRAYSIZE(samples)) {
CHECK_DIF_OK(dif_i2s_irq_set_enabled(&i2s, kDifI2sIrqRxWatermark,
kDifToggleEnabled));
while (!i2s_rx_watermark_seen) {
wait_for_interrupt();
}
i2s_rx_watermark_seen = false;
bool empty;
CHECK_DIF_OK(dif_i2s_rxfifo_empty(&i2s, &empty));
while (!empty) {
reg_val = mmio_region_read32(i2s.base_addr, I2S_RDATA_REG_OFFSET);
samples[sample++] = reg_val;
CHECK_DIF_OK(dif_i2s_rxfifo_empty(&i2s, &empty));
if (sample == ARRAYSIZE(samples)) {
break;
}
}
}
reg_val = mmio_region_read32(i2s.base_addr, I2S_CTRL_REG_OFFSET);
reg_val = bitfield_bit32_write(reg_val, I2S_CTRL_RX_BIT, 0);
mmio_region_write32(i2s.base_addr, I2S_CTRL_REG_OFFSET, reg_val);
if (kDeviceType != kDeviceSimVerilator) {
for (int i = 0; i < ARRAYSIZE(samples); ++i) {
uint16_t left = samples[i] >> 16;
uint16_t right = samples[i] & 0xFFFF;
LOG_INFO("Sample %d - Left: 0x%x Right: 0x%x", i, left, right);
}
}
// Note: passing a test in the SMC ends the simulation, even if the secure
// core is in the middle of a test.
if (i2s_rx_overflow_seen) {
test_status_set(kTestStatusFailed);
} else {
test_status_set(kTestStatusPassed);
}
}