#include "FPGA.hpp" #include "delay.hpp" #include "stm.hpp" #include "main.h" #include "FPGA_HAL.hpp" #define LOG_LEVEL LOG_LEVEL_DEBUG #define LOG_MODULE "FPGA" #include "Log.h" static FPGA::HaltedCallback halted_cb; static uint16_t SysCtrlReg = 0x0000; static uint16_t ISRMaskReg = 0x0000; using namespace FPGAHAL; void WriteRegister(FPGA::Reg reg, uint16_t value) { uint16_t cmd[2] = {(uint16_t) (0x8000 | (uint16_t) reg), value}; Low(CS); HAL_SPI_Transmit(&FPGA_SPI, (uint8_t*) cmd, 2, 100); High(CS); } bool FPGA::Configure(Flash *f, uint32_t start_address, uint32_t bitstream_size) { if(!PROGRAM_B.gpio) { LOG_WARN("PROGRAM_B not defined, assuming FPGA configures itself in master configuration"); // wait too allow enough time for FPGA configuration HAL_Delay(2000); return true; } LOG_INFO("Loading bitstream of size %lu...", bitstream_size); Low(PROGRAM_B); while(isHigh(INIT_B)); High(PROGRAM_B); while(!isHigh(INIT_B)); uint8_t buf[256]; while(bitstream_size > 0) { uint16_t size = sizeof(buf); if(size > bitstream_size) { size = bitstream_size; } // TODO this part might be doable with the DMA instead of the buffer // get chunk of bitstream from flash... f->read(start_address, size, buf); // ... and pass it on to FPGA HAL_SPI_Transmit(&CONFIGURATION_SPI, buf, size, 100); bitstream_size -= size; start_address += size; } Delay::ms(1); if(!isHigh(INIT_B)) { LOG_CRIT("INIT_B asserted after configuration, CRC error occurred"); return false; } if(!isHigh(DONE)) { LOG_CRIT("DONE still low after configuration"); return false; } LOG_INFO("...configured"); return true; } bool FPGA::Init(HaltedCallback cb) { halted_cb = cb; SysCtrlReg = 0; ISRMaskReg = 0; // Reset FPGA High(FPGA_RESET); SetMode(Mode::FPGA); Delay::us(1); Low(FPGA_RESET); Delay::ms(10); // Check if FPGA response is as expected uint16_t cmd[2] = {0x4000, 0x0000}; uint16_t recv[2]; Low(CS); HAL_SPI_TransmitReceive(&FPGA_SPI, (uint8_t*) cmd, (uint8_t*) recv, 2, 100); High(CS); if(recv[1] != 0xF0A5) { LOG_ERR("Initialization failed, got 0x%04x instead of 0xF0A5", recv[1]); return false; } LOG_DEBUG("Initialized, status register: 0x%04x", recv[0]); return true; } void FPGA::SetNumberOfPoints(uint16_t npoints) { // register has to be set to number of points - 1 npoints--; WriteRegister(Reg::SweepPoints, npoints); } void FPGA::SetSamplesPerPoint(uint32_t nsamples) { // register has to be set to number of nsamples - 1 nsamples--; // constrain to maximum value nsamples &= 0x1FFFF; // highest bit is located at the system control register SysCtrlReg &= ~0x0001; SysCtrlReg |= nsamples >> 16; WriteRegister(Reg::SystemControl, SysCtrlReg); WriteRegister(Reg::SamplesPerPoint, nsamples & 0xFFFF); } void FPGA::SetSettlingTime(uint16_t us) { if (us > 639) { us = 639; } uint16_t regval = (uint32_t) us * 1024 / 10; WriteRegister(Reg::SettlingTime, regval); } void FPGA::Enable(Periphery p, bool enable) { if (enable) { SysCtrlReg |= (uint16_t) p; WriteRegister(Reg::SystemControl, SysCtrlReg); } else { Disable(p); } } void FPGA::Disable(Periphery p) { SysCtrlReg &= ~(uint16_t) p; WriteRegister(Reg::SystemControl, SysCtrlReg); } void FPGA::EnableInterrupt(Interrupt i) { ISRMaskReg |= (uint16_t) i; WriteRegister(Reg::InterruptMask, ISRMaskReg); } void FPGA::DisableInterrupt(Interrupt i) { ISRMaskReg &= ~(uint16_t) i; WriteRegister(Reg::InterruptMask, ISRMaskReg); } void FPGA::WriteMAX2871Default(uint32_t *DefaultRegs) { WriteRegister(Reg::MAX2871Def0LSB, DefaultRegs[0] & 0xFFFF); WriteRegister(Reg::MAX2871Def0MSB, DefaultRegs[0] >> 16); WriteRegister(Reg::MAX2871Def1LSB, DefaultRegs[1] & 0xFFFF); WriteRegister(Reg::MAX2871Def1MSB, DefaultRegs[1] >> 16); WriteRegister(Reg::MAX2871Def3LSB, DefaultRegs[3] & 0xFFFF); WriteRegister(Reg::MAX2871Def3MSB, DefaultRegs[3] >> 16); WriteRegister(Reg::MAX2871Def4LSB, DefaultRegs[4] & 0xFFFF); WriteRegister(Reg::MAX2871Def4MSB, DefaultRegs[4] >> 16); } void FPGA::WriteSweepConfig(uint16_t pointnum, bool lowband, uint32_t *SourceRegs, uint32_t *LORegs, uint8_t attenuation, uint64_t frequency, bool halt, LowpassFilter filter) { uint16_t send[8]; // select which point this sweep config is for send[0] = pointnum & 0x1FFF; // assemble sweep config from required fields of PLL registers send[1] = (LORegs[4] & 0x00700000) >> 14 | (LORegs[3] & 0xFC000000) >> 26; if (halt) { send[1] |= 0x8000; } if (lowband) { send[1] |= 0x4000; } switch(filter) { case LowpassFilter::Auto: // Select source LP filter if (frequency >= 3500000000) { send[1] |= 0x0600; } else if (frequency >= 1800000000) { send[1] |= 0x0400; } else if (frequency >= 900000000) { send[1] |= 0x0200; } break; case LowpassFilter::M947: break; case LowpassFilter::M1880: send[1] |= 0x0200; break; case LowpassFilter::M3500: send[1] |= 0x0400; break; case LowpassFilter::None: send[1] |= 0x0600; break; } send[2] = (LORegs[1] & 0x00007FF8) << 1 | (LORegs[0] & 0x00007800) >> 11; send[3] = (LORegs[0] & 0x000007F8) << 5 | (LORegs[0] & 0x7F800000) >> 23; send[4] = (LORegs[0] & 0x007F8000) >> 7 | (attenuation & 0x7F) << 1 | (SourceRegs[4] & 0x00400000) >> 22; send[5] = (SourceRegs[4] & 0x00300000) >> 6 | (SourceRegs[3] & 0xFC000000) >> 18 | (SourceRegs[1] & 0x00007F80) >> 7; send[6] = (SourceRegs[1] & 0x00000078) << 9 | (SourceRegs[0] & 0x00007FF8) >> 3; send[7] = (SourceRegs[0] & 0x7FFF8000) >> 15; Low(CS); HAL_SPI_Transmit(&FPGA_SPI, (uint8_t*) send, 8, 100); High(CS); } static inline int64_t sign_extend_64(int64_t x, uint16_t bits) { int64_t m = 1ULL << (bits - 1); return (x ^ m) - m; } static FPGA::ReadCallback callback; static uint16_t raw[18]; static bool halted; bool FPGA::InitiateSampleRead(ReadCallback cb) { callback = cb; uint16_t cmd = 0xC000; uint16_t status; Low(CS); HAL_SPI_TransmitReceive(&FPGA_SPI, (uint8_t*) &cmd, (uint8_t*) &status, 1, 100); if (status & 0x0010) { halted = true; } else { halted = false; } if (!(status & 0x0004)) { // no new data available yet High(CS); if (halted) { if (halted_cb) { halted_cb(); } } else { LOG_WARN("ISR without new data, status: 0x%04x", status); } return false; } // Start data read HAL_SPI_Receive_DMA(&FPGA_SPI, (uint8_t*) raw, 18); return true; } extern "C" { void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { FPGA::SamplingResult result; High(CS); // Assemble data from words result.P1I = sign_extend_64( (uint64_t) raw[17] << 32 | (uint32_t) raw[16] << 16 | raw[15], 48); result.P1Q = sign_extend_64( (uint64_t) raw[14] << 32 | (uint32_t) raw[13] << 16 | raw[12], 48); result.P2I = sign_extend_64( (uint64_t) raw[11] << 32 | (uint32_t) raw[10] << 16 | raw[9], 48); result.P2Q = sign_extend_64( (uint64_t) raw[8] << 32 | (uint32_t) raw[7] << 16 | raw[6], 48); result.RefI = sign_extend_64( (uint64_t) raw[5] << 32 | (uint32_t) raw[4] << 16 | raw[3], 48); result.RefQ = sign_extend_64( (uint64_t) raw[2] << 32 | (uint32_t) raw[1] << 16 | raw[0], 48); if (callback) { callback(result); } if (halted && halted_cb) { halted_cb(); } } } void FPGA::StartSweep() { Low(AUX3); Delay::us(1); High(AUX3); } void FPGA::AbortSweep() { Low(AUX3); } void FPGA::SetMode(Mode mode) { switch(mode) { case Mode::FPGA: // Both AUX1/2 low Low(AUX1); Low(AUX2); Delay::us(1); High(CS); break; case Mode::SourcePLL: Low(CS); Low(AUX2); Delay::us(1); High(AUX1); break; case Mode::LOPLL: Low(CS); Low(AUX1); Delay::us(1); High(AUX2); break; } Delay::us(1); } uint16_t FPGA::GetStatus() { uint16_t cmd = 0x4000; uint16_t status; Low(CS); HAL_SPI_TransmitReceive(&FPGA_SPI, (uint8_t*) &cmd, (uint8_t*) &status, 1, 100); High(CS); return status; } FPGA::ADCLimits FPGA::GetADCLimits() { uint16_t cmd = 0xE000; Low(CS); HAL_SPI_Transmit(&FPGA_SPI, (uint8_t*) &cmd, 1, 100); ADCLimits limits; HAL_SPI_Receive(&FPGA_SPI, (uint8_t*) &limits, 6, 100); High(CS); return limits; } void FPGA::ResetADCLimits() { uint16_t cmd = 0x6000; Low(CS); HAL_SPI_Transmit(&FPGA_SPI, (uint8_t*) &cmd, 1, 100); High(CS); } void FPGA::ResumeHaltedSweep() { uint16_t cmd = 0x2000; Low(CS); HAL_SPI_Transmit(&FPGA_SPI, (uint8_t*) &cmd, 1, 100); High(CS); }