Skip to content

Commit

Permalink
Improve SPI controller accuracy
Browse files Browse the repository at this point in the history
  • Loading branch information
calc84maniac committed Sep 14, 2024
1 parent 2fb0768 commit d4cb839
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 65 deletions.
2 changes: 1 addition & 1 deletion core/emu.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#include <emscripten.h>
#endif

#define IMAGE_VERSION 0xCECE0017
#define IMAGE_VERSION 0xCECE0018

void EMSCRIPTEN_KEEPALIVE emu_exit(void) {
cpu_set_signal(CPU_SIGNAL_EXIT);
Expand Down
6 changes: 5 additions & 1 deletion core/panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1316,7 +1316,11 @@ static void panel_write_param(uint8_t value) {
}
}

uint8_t panel_spi_select(uint32_t* rxData) {
void panel_spi_select(bool low) {
(void)low;
}

uint8_t panel_spi_peek(uint32_t *rxData) {
(void)rxData;
/* The first transfer frame is always 9 bits */
return 9;
Expand Down
4 changes: 2 additions & 2 deletions core/panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -611,9 +611,9 @@ void panel_vsync(void);
void panel_clock_porch(uint32_t clocks);
void panel_scan_until(uint32_t currTick);

uint8_t panel_spi_select(uint32_t* rxData);
void panel_spi_select(bool low);
uint8_t panel_spi_peek(uint32_t* rxData);
uint8_t panel_spi_transfer(uint32_t txData, uint32_t* rxData);
void panel_spi_deselect(void);

#ifdef __cplusplus
}
Expand Down
133 changes: 75 additions & 58 deletions core/spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,30 @@

spi_state_t spi;

static uint8_t null_spi_select(uint32_t* rxData) {
static void null_spi_select(bool low) {
(void)low;
}

static uint8_t null_spi_peek(uint32_t *rxData) {
/* Hack to make OS 5.7.0 happy without a coprocessor */
*rxData = 0xC3;
return 8;
}

static uint8_t null_spi_transfer(uint32_t txData, uint32_t* rxData) {
static uint8_t null_spi_transfer(uint32_t txData, uint32_t *rxData) {
(void)txData;
return null_spi_select(rxData);
}

static void null_spi_deselect(void) {
return null_spi_peek(rxData);
}

static void spi_set_device_funcs(void) {
if (spi.arm) {
spi.device_select = null_spi_select;
spi.device_peek = null_spi_peek;
spi.device_transfer = null_spi_transfer;
spi.device_deselect = null_spi_deselect;
} else {
spi.device_select = panel_spi_select;
spi.device_peek = panel_spi_peek;
spi.device_transfer = panel_spi_transfer;
spi.device_deselect = panel_spi_deselect;
}
}

Expand All @@ -58,8 +59,13 @@ static void spi_update_thresholds(void) {

static uint32_t spi_next_transfer(void) {
if (spi.transferBits == 0) {
bool txAvailable = (spi.cr2 >> 8 & 1) && spi.tfve != 0;
bool txEnabled = (spi.cr2 >> 8 & 1);
bool txAvailable = txEnabled && spi.tfve != 0;
if (unlikely(spi.cr2 >> 7 & 1)) {
/* If FLASH bit is reset and TX is enabled, only receive when the TX FIFO is non-empty */
if (!(spi.cr0 >> 11 & 1) && txEnabled && spi.tfve == 0) {
return 0;
}
/* Odd RX behavior, allow transfer after 15 entries only if TX FIFO is non-empty */
if (spi.rfve >= SPI_RXFIFO_DEPTH - (spi.tfve == 0)) {
return 0;
Expand All @@ -70,52 +76,57 @@ static uint32_t spi_next_transfer(void) {
}

spi.transferBits = (spi.cr1 >> 16 & 0x1F) + 1;
spi.txFrame = spi.txFifo[spi.tfvi & (SPI_TXFIFO_DEPTH - 1)] << (32 - spi.transferBits);
if (likely(txAvailable)) {
spi.txFrame = spi.txFifo[spi.tfvi++ & (SPI_TXFIFO_DEPTH - 1)] << (32 - spi.transferBits);
spi.tfvi++;
spi.tfve--;
spi_update_thresholds();
} else {
/* For now, just send 0 bits when no TX data available */
spi.txFrame = 0;
if (spi.cr2 >> 8 & 1) {
/* Set TX underflow if TX enabled */
spi.intStatus |= 1 << 1;
intrpt_set(INT_SPI, spi.intCtrl & spi.intStatus);
}
} else if (unlikely(txEnabled)) {
/* Set TX underflow if TX enabled */
spi.intStatus |= 1 << 1;
intrpt_set(INT_SPI, spi.intCtrl & spi.intStatus);
}
}

if (unlikely(spi.deviceBits == 0)) {
uint32_t rxData = 0;
spi.deviceBits = spi.device_peek(&rxData);
spi.deviceFrame = rxData << (32 - spi.deviceBits);
}

uint8_t bitCount = spi.transferBits < spi.deviceBits ? spi.transferBits : spi.deviceBits;
return bitCount * ((spi.cr1 & 0xFFFF) + 1);
}

static void spi_event(enum sched_item_id id) {
uint8_t bitCount = spi.transferBits < spi.deviceBits ? spi.transferBits : spi.deviceBits;
spi.rxFrame <<= bitCount;
/* Handle loopback */
if (unlikely(spi.cr0 >> 7 & 1)) {
spi.rxFrame |= spi.txFrame >> (32 - bitCount);
}
/* For now, allow only receives from coprocessor */
else if (spi.arm) {
spi.rxFrame |= spi.deviceFrame >> (32 - bitCount);
}
spi.deviceFrame <<= bitCount;
spi.deviceFrame |= spi.txFrame >> (32 - bitCount);
spi.txFrame <<= bitCount;
if (likely(spi.transferBits != 0)) {
uint8_t bitCount = spi.transferBits < spi.deviceBits ? spi.transferBits : spi.deviceBits;
spi.rxFrame <<= bitCount;
/* Handle loopback */
if (unlikely(spi.cr0 >> 7 & 1)) {
spi.rxFrame |= spi.txFrame >> (32 - bitCount);
}
/* For now, allow only receives from coprocessor */
else if (spi.arm) {
spi.rxFrame |= spi.deviceFrame >> (32 - bitCount);
}
spi.deviceFrame <<= bitCount;
spi.deviceFrame |= spi.txFrame >> (32 - bitCount);
spi.txFrame <<= bitCount;

spi.deviceBits -= bitCount;
if (spi.deviceBits == 0) {
uint32_t rxData = 0;
spi.deviceBits = spi.device_transfer(spi.deviceFrame, &rxData);
spi.deviceFrame = rxData << (32 - spi.deviceBits);
}
spi.deviceBits -= bitCount;
if (spi.deviceBits == 0) {
uint32_t rxData = 0;
spi.deviceBits = spi.device_transfer(spi.deviceFrame, &rxData);
spi.deviceFrame = rxData << (32 - spi.deviceBits);
}

spi.transferBits -= bitCount;
if (spi.transferBits == 0 && unlikely(spi.cr2 >> 7 & 1)) {
/* Note: rfve bound check was performed when starting the transfer */
spi.rxFifo[(spi.rfvi + spi.rfve++) & (SPI_RXFIFO_DEPTH - 1)] = spi.rxFrame;
spi_update_thresholds();
spi.transferBits -= bitCount;
if (spi.transferBits == 0 && unlikely(spi.cr2 >> 7 & 1)) {
/* Note: rfve bound check was performed when starting the transfer */
spi.rxFifo[(spi.rfvi + spi.rfve++) & (SPI_RXFIFO_DEPTH - 1)] = spi.rxFrame;
spi_update_thresholds();
}
}

uint32_t ticks = spi_next_transfer();
Expand All @@ -127,22 +138,23 @@ static void spi_event(enum sched_item_id id) {
static void spi_update(void) {
spi_update_thresholds();
if (!(spi.cr2 & 1)) {
if (spi.deviceBits != 0) {
spi.device_deselect();
if (spi.deviceBits != 0 || sched_active(SCHED_SPI)) {
spi.device_select(false);
spi.deviceFrame = spi.deviceBits = 0;
spi.txFrame = spi.transferBits = 0;
sched_clear(SCHED_SPI);
}
} else if (spi.transferBits == 0) {
} else if (!sched_active(SCHED_SPI)) {
assert(spi.transferBits == 0);
if (spi.deviceBits == 0) {
uint32_t rxData = 0;
spi.deviceBits = spi.device_select(&rxData);
spi.deviceFrame = rxData << (32 - spi.deviceBits);
}

uint32_t ticks = spi_next_transfer();
if (ticks) {
sched_set(SCHED_SPI, ticks);
spi.device_select(true);
/* Delay one bit length after enable and before first transfer */
sched_set(SCHED_SPI, (spi.cr1 & 0xFFFF) + 1);
} else {
uint32_t ticks = spi_next_transfer();
if (ticks) {
sched_set(SCHED_SPI, ticks);
}
}
}
}
Expand Down Expand Up @@ -208,6 +220,9 @@ static void spi_write(uint16_t addr, uint8_t byte, bool poke) {
bool stateChanged = false;
switch (addr >> 2) {
case 0x00 >> 2: // CR0
if ((spi.cr0 ^ value) & ~mask & (1 << 11)) {
stateChanged = true;
}
value &= 0xFFFF;
spi.cr0 &= mask;
spi.cr0 |= value;
Expand Down Expand Up @@ -241,14 +256,16 @@ static void spi_write(uint16_t addr, uint8_t byte, bool poke) {
spi.intStatus |= spi_get_threshold_status();
intrpt_set(INT_SPI, spi.intCtrl & spi.intStatus);
break;
case 0x18 >> 2: // DATA
spi.dtr &= mask;
spi.dtr |= value;
case 0x18 >> 2: { // DATA
uint32_t *fifoEntry = &spi.txFifo[(spi.tfvi + spi.tfve) & (SPI_TXFIFO_DEPTH - 1)];
*fifoEntry &= mask;
*fifoEntry |= value;
if (!shift && spi.tfve != SPI_TXFIFO_DEPTH) {
spi.txFifo[(spi.tfvi + spi.tfve++) & (SPI_TXFIFO_DEPTH - 1)] = spi.dtr;
spi.tfve++;
stateChanged = true;
}
break;
}
}
if (stateChanged) {
spi_update();
Expand All @@ -272,7 +289,7 @@ void spi_reset(void) {

void spi_device_select(bool arm) {
if (spi.arm != arm) {
spi.device_deselect();
spi.device_select(false);
spi.arm = arm;
spi_set_device_funcs();
spi_update();
Expand Down
6 changes: 3 additions & 3 deletions core/spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ extern "C" {
#define SPI_FEATURES 0xE

typedef struct spi_state {
uint32_t cr0, cr1, cr2, intCtrl, dtr;
uint32_t cr0, cr1, cr2, intCtrl;
uint32_t rxFrame, txFrame, deviceFrame;
uint8_t transferBits, deviceBits;
uint8_t rfvi, rfve, tfvi, tfve;
uint8_t intStatus;
bool arm;
uint32_t rxFifo[SPI_RXFIFO_DEPTH], txFifo[SPI_TXFIFO_DEPTH];

uint8_t (*device_select)(uint32_t*);
void (*device_select)(bool);
uint8_t (*device_peek)(uint32_t*);
uint8_t (*device_transfer)(uint32_t, uint32_t*);
void (*device_deselect)(void);
} spi_state_t;

extern spi_state_t spi;
Expand Down

0 comments on commit d4cb839

Please sign in to comment.