fix(spi): repair session readiness and lifecycle semantics

Three defects found by SPI-P4 display bring-up in the committed SPI-P3
integration and HAL, each root-caused with log-level evidence:

- retrosoc_tiny.sv wired the SPI gpio_ready_i to the GPIO APB bridge's
  transaction-level ready, which drops for the whole CDC round trip of every
  GPIO register access. The frozen D/C contract requires GPIO30 writes
  between segments during a session, so the guard latched fault 2 and closed
  the session on the first D/C change. The readiness scope per the frozen
  text is GPIO clock/reset availability; the expression is now
  s_gpio_rst_n && !s_actual_gate[1], stable during a session.
- rs_spi_acquire returned after GPIO_READY alone, before the enable gap
  reached IDLE; the first ARM could then be rejected. It now also waits
  BUS_IDLE, so an acquired session is immediately usable.
- rs_spi_recover waited for ownership to clear, but RECOVER retains the
  session per the ABI; it now waits for wire-state Disabled. rs_spi_release
  unconditionally issued DISABLE, legal only from IDLE; from the
  post-recover Disabled state it now goes straight to pin restore and
  RELEASE.

Validation: SPI-P3 acceptance re-run passes after the HAL changes
(SIM_TEST_PASS Tiny cycles=1037899); the SPI-P4 display acceptance passes on
both profiles and both simulators with these fixes in place
(build/ics55-tiny-2026-10-23-01-37-106e35060a8e/,
build/ihp130-tiny-2026-10-23-01-38-616e0604657d/ and the Icarus QUICK
variants); full Pytest 2102 passed, 0 failed; C format/policy/host gates
clean. No ABI or Mini change.
This commit is contained in:
maksyuki committed 2026-10-23 08:04:15 +08:00
1 parent 95c243b709
commit 3d464bb228
2 files changed
+18 -7

No files matched your search

+17 -6
View File
@@ -63,8 +63,13 @@ rs_status_t rs_spi_configure(const rs_spi_config_t *config) {
rs_status_t rs_spi_acquire(rs_timeout_t timeout) {
#ifdef RS_SPI_TARGET_PRESENT
rs_status_t result;
RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_ENABLE;
return rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_GPIO_READY, true, timeout);
result = rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_GPIO_READY, true, timeout);
if (result != RS_OK) {
return result;
}
return rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_BUS_IDLE, true, timeout);
#else
(void)timeout;
return RS_ENOTSUP;
@@ -77,10 +82,16 @@ rs_status_t rs_spi_release(rs_timeout_t timeout) {
rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_SEGMENT_CLEAN, true, timeout);
if (result != RS_OK)
return result;
RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_DISABLE;
result = rs_spi_wait_status(UINT32_C(0xF), false, timeout);
if (result != RS_OK)
return result;
rs_spi_status_t status;
if (rs_spi_get_status(&status) != RS_OK) {
return RS_EIO;
}
if ((status.status & UINT32_C(0xF)) != UINT32_C(0x0)) {
RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_DISABLE;
result = rs_spi_wait_status(UINT32_C(0xF), false, timeout);
if (result != RS_OK)
return result;
}
const rs_gpio_config_t input = {
.mode = RS_GPIO_MODE_INPUT,
.pull = RS_GPIO_PULL_NONE,
@@ -154,7 +165,7 @@ rs_status_t rs_spi_abort(rs_timeout_t timeout) {
rs_status_t rs_spi_recover(rs_timeout_t timeout) {
#ifdef RS_SPI_TARGET_PRESENT
RS_SPI_REG(RS_SPI_ABI_COMMAND) = UINT32_C(1) << RS_SPI_ABI_RECOVER;
return rs_spi_wait_status(UINT32_C(1) << RS_SPI_ABI_STATUS_SESSION_OWNED, false, timeout);
return rs_spi_wait_status(UINT32_C(0xF), false, timeout);
#else
(void)timeout;
return RS_ENOTSUP;
+1 -1
View File
@@ -954,7 +954,7 @@ axi4_error_slave #(
.alt_select_i (s_gpio_alt_select),
.output_enable_i (s_gpio_output_enable),
.open_drain_i (s_gpio_open_drain),
.gpio_ready_i (s_gpio_rst_n && s_apb_cdc_ready[0]),
.gpio_ready_i (s_gpio_rst_n && !s_actual_gate[1]),
.miso_i (s_spi_miso),
.sck_o (s_spi_sck),
.mosi_o (s_spi_mosi),