mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
- Updated SRAM instance paths in to reflect new hierarchy. - Enhanced to include additional source files and updated parameterized tests for SRAM configurations. - Modified to align with new SRAM structure and added a test for stale macro hierarchy. - Introduced to validate dual-port local-memory protocol and CPU debug/reset functionality, including comprehensive simulation checks.
320 lines
12 KiB
C
320 lines
12 KiB
C
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include <retrosoc/arch/riscv/system_base.h>
|
|
#include <retrosoc/core/archinfo.h>
|
|
#include <retrosoc/core/irq.h>
|
|
#include <retrosoc/core/soc.h>
|
|
#include <retrosoc/generated/irq_metadata.h>
|
|
#include <retrosoc/hal/clint.h>
|
|
#include <retrosoc/hal/dma.h>
|
|
#include <retrosoc/hal/gpio.h>
|
|
#include <retrosoc/hal/i2c.h>
|
|
#include <retrosoc/hal/onchip_sram.h>
|
|
#include <retrosoc/hal/pwm.h>
|
|
#include <retrosoc/hal/rtc.h>
|
|
#include <retrosoc/hal/sysctrl.h>
|
|
#include <retrosoc/hal/timer.h>
|
|
#include <retrosoc/hal/uart.h>
|
|
#include <retrosoc/hal/watchdog.h>
|
|
#include <retrosoc/lib/printf.h>
|
|
|
|
#include "mcu_acceptance.h"
|
|
|
|
static _Alignas(64) uint32_t rs_mcu_source[80];
|
|
static _Alignas(64) uint32_t rs_mcu_destination[80];
|
|
static volatile uint32_t rs_mcu_irq_count;
|
|
static volatile uint32_t rs_mcu_exception_count;
|
|
static volatile bool rs_mcu_handler_failed;
|
|
|
|
static void rs_mcu_finish(bool pass, uint8_t code) {
|
|
printf("Tiny acceptance %s code=%u\n", pass ? "passed" : "failed", code);
|
|
rs_sysctrl_write_test_status(pass, code);
|
|
/* The terminal status must retain the first accepted full-word write. */
|
|
rs_sysctrl_write_test_status(!pass, UINT8_C(255));
|
|
for (;;) {
|
|
}
|
|
}
|
|
|
|
static void rs_mcu_exception(uintptr_t mcause, uintptr_t stack_pointer) {
|
|
volatile uint32_t *const frame = (volatile uint32_t *)stack_pointer;
|
|
|
|
if ((mcause != UINT32_C(2)) && (mcause != UINT32_C(5))) {
|
|
rs_mcu_handler_failed = true;
|
|
}
|
|
++rs_mcu_exception_count;
|
|
/* system_irq.S saves MEPC in slot 11 of the RV32 trap frame. Both test
|
|
* instructions are explicitly 32 bits; normal exceptions do not use this handler. */
|
|
frame[11] += UINT32_C(4);
|
|
}
|
|
|
|
static void rs_mcu_timer_irq(uintptr_t mcause, uintptr_t stack_pointer) {
|
|
(void)mcause;
|
|
(void)stack_pointer;
|
|
++rs_mcu_irq_count;
|
|
if (rs_timer_interrupt_clear(RS_TIMER_0, RS_TIMER_INTERRUPT_ALL) != RS_OK) {
|
|
rs_mcu_handler_failed = true;
|
|
}
|
|
}
|
|
|
|
static bool rs_mcu_dma(void) {
|
|
rs_dma_config_t config = {
|
|
.kind = RS_DMA_KIND_MM_TO_MM,
|
|
.request = RS_DMA_REQUEST_SOFTWARE,
|
|
.source = (uintptr_t)rs_mcu_source,
|
|
.destination = (uintptr_t)rs_mcu_destination,
|
|
.byte_count = UINT32_C(259),
|
|
.width = RS_DMA_WIDTH_32,
|
|
.source_increment = true,
|
|
.destination_increment = true,
|
|
.priority = 0U,
|
|
.burst_beats = 16U,
|
|
};
|
|
|
|
for (uint32_t index = 0U; index < 80U; ++index) {
|
|
rs_mcu_source[index] = UINT32_C(0xa519c300) ^ index;
|
|
rs_mcu_destination[index] = UINT32_C(0xcccccccc);
|
|
}
|
|
if ((rs_dma_configure(4U, &config) != RS_EINVAL) ||
|
|
(rs_dma_configure(RS_DMA_CHANNEL_BULK, &config) != RS_OK) ||
|
|
(rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) ||
|
|
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK)) {
|
|
return false;
|
|
}
|
|
for (uint32_t index = 0U; index < 64U; ++index) {
|
|
if (rs_mcu_source[index] != rs_mcu_destination[index]) {
|
|
return false;
|
|
}
|
|
}
|
|
if (rs_mcu_destination[64] !=
|
|
((rs_mcu_source[64] & UINT32_C(0x00ffffff)) | UINT32_C(0xcc000000))) {
|
|
return false;
|
|
}
|
|
config.kind = RS_DMA_KIND_MM_TO_STREAM;
|
|
config.request = RS_DMA_REQUEST_I2S_TX;
|
|
if (rs_dma_config_validate(0U, &config) == RS_OK) {
|
|
return false;
|
|
}
|
|
/* DMA owns the destination until completion. FENCE.I is executed by the
|
|
* assembly helper before entering either the DMA-written or patched code. */
|
|
rs_mcu_source[0] = UINT32_C(0x02a00513); /* addi a0,zero,42 */
|
|
rs_mcu_source[1] = UINT32_C(0x00008067); /* jalr zero,ra,0 */
|
|
config.kind = RS_DMA_KIND_MM_TO_MM;
|
|
config.request = RS_DMA_REQUEST_SOFTWARE;
|
|
config.byte_count = UINT32_C(8);
|
|
config.burst_beats = 1U;
|
|
if ((rs_dma_configure(RS_DMA_CHANNEL_BULK, &config) != RS_OK) ||
|
|
(rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) ||
|
|
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK) ||
|
|
(rs_mcu_execute_probe((uint32_t)(uintptr_t)rs_mcu_destination) != UINT32_C(42))) {
|
|
return false;
|
|
}
|
|
rs_mcu_destination[0] = UINT32_C(0x02b00513); /* addi a0,zero,43 */
|
|
return rs_mcu_execute_probe((uint32_t)(uintptr_t)rs_mcu_destination) == UINT32_C(43);
|
|
}
|
|
|
|
static bool rs_mcu_interrupts(void) {
|
|
uint32_t timeout = UINT32_C(20000);
|
|
|
|
if ((rs_irq_enable_external(RS_SOC_EXT_IRQ_TIMER0, rs_mcu_timer_irq) != RS_OK) ||
|
|
(rs_timer_interrupt_enable(RS_TIMER_0, RS_TIMER_INTERRUPT_TIMEOUT) != RS_OK) ||
|
|
(rs_timer_interrupt_test(RS_TIMER_0, RS_TIMER_INTERRUPT_TIMEOUT) != RS_OK)) {
|
|
return false;
|
|
}
|
|
__RV_CSR_SET(CSR_MSTATUS, MSTATUS_MIE);
|
|
while ((rs_mcu_irq_count == 0U) && (timeout != 0U)) {
|
|
--timeout;
|
|
}
|
|
__RV_CSR_CLEAR(CSR_MSTATUS, MSTATUS_MIE);
|
|
return (rs_mcu_irq_count == 1U) && !rs_mcu_handler_failed &&
|
|
(rs_irq_disable_external(RS_SOC_EXT_IRQ_TIMER0) == RS_OK);
|
|
}
|
|
|
|
static bool rs_mcu_io(void) {
|
|
const rs_gpio_config_t output = {
|
|
.mode = RS_GPIO_MODE_OUTPUT,
|
|
.pull = RS_GPIO_PULL_NONE,
|
|
.trigger = RS_GPIO_TRIGGER_NONE,
|
|
.output_high = false,
|
|
};
|
|
const rs_gpio_config_t pull_up = {
|
|
.mode = RS_GPIO_MODE_INPUT,
|
|
.pull = RS_GPIO_PULL_UP,
|
|
.trigger = RS_GPIO_TRIGGER_NONE,
|
|
.output_high = false,
|
|
};
|
|
const rs_gpio_config_t pull_down = {
|
|
.mode = RS_GPIO_MODE_INPUT,
|
|
.pull = RS_GPIO_PULL_DOWN,
|
|
.trigger = RS_GPIO_TRIGGER_NONE,
|
|
.output_high = false,
|
|
};
|
|
const rs_uart_config_t loopback = {
|
|
.source_clock_hz = RS_CPU_CLOCK_HZ,
|
|
.baud_rate = UART_BPS,
|
|
.data_bits = 8U,
|
|
.stop_bits = 1U,
|
|
.parity = RS_UART_PARITY_NONE,
|
|
.rx_watermark = 1U,
|
|
.rx_timeout_bits = 16U,
|
|
.tx_enable = true,
|
|
.rx_enable = true,
|
|
.loopback_enable = true,
|
|
};
|
|
const uint32_t transmit[4] = {UINT32_C(0x54), UINT32_C(0x49), UINT32_C(0x4e), UINT32_C(0x59)};
|
|
uint32_t receive[4] = {0U};
|
|
rs_gpio_capabilities_t capabilities;
|
|
rs_uart_timing_t timing;
|
|
bool high;
|
|
uint32_t timeout = UINT32_C(10000);
|
|
uint8_t payload = UINT8_C(0x55);
|
|
const rs_i2c_transfer_t transfer = {
|
|
.address = UINT16_C(0x50),
|
|
.tx_data = &payload,
|
|
.tx_length = 1U,
|
|
};
|
|
rs_i2c_status_t status;
|
|
|
|
if ((rs_gpio_get_capabilities(&capabilities) != RS_OK) ||
|
|
((capabilities.pad_features & (RS_GPIO_PAD_CAP_PULL_UP | RS_GPIO_PAD_CAP_PULL_DOWN)) !=
|
|
((RS_SOC_GPIO_HAS_PULLS != 0U) ? (RS_GPIO_PAD_CAP_PULL_UP | RS_GPIO_PAD_CAP_PULL_DOWN)
|
|
: 0U)) ||
|
|
(rs_gpio_configure(15U, &pull_up) !=
|
|
((RS_SOC_GPIO_HAS_PULLS != 0U) ? RS_OK : RS_ENOTSUP)) ||
|
|
(rs_gpio_configure(15U, &pull_down) !=
|
|
((RS_SOC_GPIO_HAS_PULLS != 0U) ? RS_OK : RS_ENOTSUP)) ||
|
|
(rs_gpio_configure(15U, &output) != RS_OK) || (rs_gpio_write(15U, true) != RS_OK) ||
|
|
(rs_gpio_read(15U, &high) != RS_OK) || !high || (rs_gpio_write(15U, false) != RS_OK) ||
|
|
(rs_gpio_read(15U, &high) != RS_OK) || high) {
|
|
return false;
|
|
}
|
|
if ((rs_uart_configure(&loopback, RS_TIMEOUT_DEFAULT) != RS_OK) ||
|
|
(rs_uart_write_dma(transmit, 4U, RS_TIMEOUT_DEFAULT) != RS_OK) ||
|
|
(rs_uart_read_dma(receive, 4U, RS_TIMEOUT_DEFAULT) != RS_OK)) {
|
|
return false;
|
|
}
|
|
for (uint32_t index = 0U; index < 4U; ++index) {
|
|
if (transmit[index] != receive[index]) {
|
|
return false;
|
|
}
|
|
}
|
|
if ((rs_uart_init(RS_CPU_CLOCK_HZ, UART_BPS) != RS_OK) ||
|
|
(rs_uart_timing_calculate(RS_CPU_CLOCK_HZ, UART_BPS, &timing) != RS_OK)) {
|
|
return false;
|
|
}
|
|
/* UART1 uses the same register ABI; the current shared high-level UART
|
|
* API selects UART0. Exercise UART1 PIO and its internal loopback here. */
|
|
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x00)) = timing.baud_integer;
|
|
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x04)) = timing.baud_fraction;
|
|
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x08)) = UINT32_C(3);
|
|
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x0c)) = UINT32_C(7);
|
|
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x10)) = UINT32_C(0x5a);
|
|
while (((RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x18)) & UINT32_C(0x40)) != 0U) &&
|
|
(timeout != 0U)) {
|
|
--timeout;
|
|
}
|
|
if ((timeout == 0U) || ((RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x14)) &
|
|
UINT32_C(0xff)) != UINT32_C(0x5a))) {
|
|
return false;
|
|
}
|
|
/* There is no slave on the pulled-up acceptance board: a real address
|
|
* transaction must terminate as NACK rather than stick waiting on the bus. */
|
|
for (uint32_t bus = 0U; bus < 2U; ++bus) {
|
|
const rs_i2c_bus_t instance = (bus == 0U) ? RS_I2C_BUS_0 : RS_I2C_BUS_1;
|
|
if ((rs_i2c_init(instance, RS_CPU_CLOCK_HZ, UINT32_C(100000)) != RS_OK) ||
|
|
(rs_i2c_transfer(instance, &transfer, RS_TIMEOUT_DEFAULT) != RS_EIO) ||
|
|
(rs_i2c_get_status(instance, &status) != RS_OK) ||
|
|
((status.errors & RS_I2C_ERROR_ADDR_NACK) == 0U)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool rs_mcu_rtc(void) {
|
|
const rs_rtc_config_t config = {
|
|
.second_cycles = UINT32_C(4096),
|
|
.calibration_ppm = 0,
|
|
.interrupt_enable = 0U,
|
|
.wake_enable = 0U,
|
|
.enable = true,
|
|
};
|
|
const rs_rtc_time_t value = {.seconds = UINT64_C(1234), .subsecond = 0U};
|
|
rs_rtc_time_t observed = {0};
|
|
|
|
return (rs_rtc_probe() == RS_OK) && (rs_rtc_configure(&config, RS_TIMEOUT_DEFAULT) == RS_OK) &&
|
|
(rs_rtc_set_time(&value, RS_TIMEOUT_DEFAULT) == RS_OK) &&
|
|
(rs_rtc_get_time(&observed, RS_TIMEOUT_DEFAULT) == RS_OK) &&
|
|
(observed.seconds >= value.seconds);
|
|
}
|
|
|
|
int main(void) {
|
|
rs_archinfo_t info;
|
|
rs_onchip_sram_info_t memory;
|
|
rs_sysctrl_fault_status_t fault;
|
|
rs_watchdog_status_t watchdog = {0};
|
|
uint64_t before;
|
|
uint64_t after;
|
|
const rs_watchdog_config_t watchdog_config = {
|
|
.prescale_divider = 1U,
|
|
.timeout_ticks = UINT32_C(20000),
|
|
.window_min_ticks = 0U,
|
|
.early_warning_ticks = 0U,
|
|
.window_enable = false,
|
|
.early_warning_enable = false,
|
|
.debug_freeze_enable = true,
|
|
};
|
|
|
|
if (rs_uart_init(RS_CPU_CLOCK_HZ, UART_BPS) != RS_OK) {
|
|
rs_mcu_finish(false, 1U);
|
|
}
|
|
printf("Hello retroSoC! Tiny MCU\n");
|
|
if (rs_watchdog_get_status(&watchdog, RS_TIMEOUT_DEFAULT) != RS_OK) {
|
|
rs_mcu_finish(false, 2U);
|
|
}
|
|
if (watchdog.reset_count != 0U) {
|
|
rs_mcu_finish(true, 0U);
|
|
}
|
|
if ((rs_archinfo_read(&info) != RS_OK) || (rs_archinfo_validate_build(&info) != RS_OK) ||
|
|
(info.soc_id != RS_SOC_ID) || (info.topology != UINT32_C(0x20200001)) ||
|
|
(info.technology != RS_SOC_TECHNOLOGY_ID) ||
|
|
((info.features0 & UINT32_C(1)) != RS_SOC_PLL_PRESENT) ||
|
|
(rs_onchip_sram_probe(&memory) != RS_OK) || (memory.memory_bytes != UINT32_C(131072)) ||
|
|
(memory.data_bytes != 4U) || (memory.bank_count != 32U)) {
|
|
rs_mcu_finish(false, 3U);
|
|
}
|
|
if ((rs_mcu_compressed_probe() != 42U) || ((__RV_CSR_READ(CSR_MISA) & UINT32_C(1)) != 0U) ||
|
|
(rs_irq_register_exception(2U, rs_mcu_exception) != RS_OK) ||
|
|
(rs_irq_register_exception(5U, rs_mcu_exception) != RS_OK)) {
|
|
rs_mcu_finish(false, 4U);
|
|
}
|
|
rs_mcu_atomic_probe(rs_mcu_source);
|
|
rs_mcu_unmapped_probe();
|
|
if ((rs_mcu_exception_count != 2U) || rs_mcu_handler_failed ||
|
|
(rs_sysctrl_get_fault_status(&fault) != RS_OK) || !fault.pending ||
|
|
(fault.address != UINT32_C(0x38000000)) || (rs_sysctrl_clear_fault() != RS_OK)) {
|
|
rs_mcu_finish(false, 5U);
|
|
}
|
|
if (!rs_mcu_dma()) {
|
|
rs_mcu_finish(false, 6U);
|
|
}
|
|
if ((rs_clint_get_time(&before) != RS_OK) || (rs_clint_get_time(&after) != RS_OK) ||
|
|
(after <= before) || !rs_mcu_interrupts()) {
|
|
rs_mcu_finish(false, 7U);
|
|
}
|
|
if (!rs_mcu_rtc() || (rs_pwm_probe() != RS_OK) ||
|
|
(rs_sysctrl_set_hp_release(true) != RS_ENOTSUP)) {
|
|
rs_mcu_finish(false, 8U);
|
|
}
|
|
if (!rs_mcu_io()) {
|
|
rs_mcu_finish(false, 10U);
|
|
}
|
|
printf("Tiny CPU/DMA/IRQ/RTC/I/O passed; exercising watchdog reset\n");
|
|
if ((rs_watchdog_configure(&watchdog_config) != RS_OK) ||
|
|
(rs_watchdog_start(RS_TIMEOUT_DEFAULT) != RS_OK)) {
|
|
rs_mcu_finish(false, 9U);
|
|
}
|
|
for (;;) {
|
|
}
|
|
}
|