// ARM transports FFT results computed in PL. It performs no FFT or power calculation.
#include <stdint.h>
#define REG32(address) (*(volatile uint32_t *)(address))
#define GPIO_BASE 0x41200000u
#define UART_BASE 0xe0001000u

static void pause_cycles(uint32_t count) {
    while (count--) __asm__ volatile ("nop");
}
static void put_char(char c) {
    while (REG32(UART_BASE + 0x2c) & 0x10u) {}
    REG32(UART_BASE + 0x30) = (uint32_t)c;
}
static void put_text(const char *s) { while (*s) put_char(*s++); }
static void put_unsigned(uint32_t n) {
    char digits[10];
    unsigned count = 0;
    do { digits[count++] = (char)('0' + n % 10u); n /= 10u; } while (n);
    while (count) put_char(digits[--count]);
}
static void put_signed(int32_t n) {
    if (n < 0) { put_char('-'); put_unsigned((uint32_t)(-n)); }
    else put_unsigned((uint32_t)n);
}
static uint32_t start_toggle;
static uint32_t read_field(unsigned selector, unsigned bin) {
    REG32(GPIO_BASE) = start_toggle | (selector << 16) | bin;
    __asm__ volatile ("dsb sy" ::: "memory");
    // GPIO selection + synchronous spectrum RAM read + result register.
    pause_cycles(1000);
    return REG32(GPIO_BASE + 8);
}
int main(void) {
    // ps7_init.tcl sets the actual clock and 115200-baud divisors.
    REG32(UART_BASE + 0x00) = 0x28u; // Disable TX/RX while setting 8N1.
    REG32(UART_BASE + 0x04) = 0x20u; // 8 bits, no parity, one stop bit.
    REG32(UART_BASE + 0x00) = 0x03u; // Reset TX and RX FIFOs.
    while (REG32(UART_BASE + 0x00) & 0x03u) {}
    REG32(UART_BASE + 0x00) = 0x14u; // Enable TX and RX.
    put_text("FFT512_START\r\n");
    for (;;) {
        unsigned poll;
        for (poll = 0; poll < 100000; ++poll)
            if (read_field(0, 0) & 1u) break;
        if (poll == 100000) {
            put_text("ERROR,FFT_TIMEOUT\r\n");
            for (;;) {}
        }
        uint32_t frame = read_field(9, 0);
        put_text("RANGE,"); put_unsigned(frame);
        put_char(','); put_unsigned(read_field(10, 0));
        const unsigned fields[] = {1, 2, 3, 4, 5};
        for (unsigned n = 0; n < 5; ++n) {
            put_char(','); put_unsigned(read_field(fields[n], 0));
        }
        put_text("\r\n");
        for (unsigned bin = 0; bin < 512; ++bin) {
            uint32_t iq = read_field(6, bin);
            uint32_t power = read_field(7, bin);
            put_text("BIN,"); put_unsigned(bin); put_char(',');
            put_signed((int16_t)(iq & 65535u)); put_char(',');
            put_signed((int16_t)(iq >> 16)); put_char(',');
            put_unsigned(power); put_text("\r\n");
        }
        put_text("END,"); put_unsigned(frame); put_text("\r\n");
        pause_cycles(20000000u);
        start_toggle ^= 0xc0000000u; // Toggle request AND alternate the single-target case.
        (void)read_field(0, 0);
        // Allow the start request to clear DONE before the next poll.
        pause_cycles(1000);
    }
}
