fix(pre-bringup): resolve P0 + quick-win P1 findings from 2026-04-19 audit
Addresses findings from docs/DEVELOP_AUDIT_2026-04-19.md: P0 source-level: - F-4.3 ADAR1000_Manager::adarSetTxPhase now writes REG_LOAD_WORKING with LD_WRK_REGS_LDTX_OVERRIDE (0x02) instead of 0x01. Previous value toggled the LDRX latch on a TX-phase write, so host TX phase updates never reached the working registers. - F-6.1 DDC mixer_saturation / filter_overflow / diagnostics were deleted at the receiver boundary. Now plumbed to new outputs on radar_receiver_final (ddc_overflow_any, ddc_saturation_count) and aggregated into gpio_dig5 in radar_system_top. Added mark_debug attributes for ILA visibility. Test/debug inputs tied low explicitly. - F-0.8 adc_clk_mmcm.xdc set_clock_uncertainty: removed invalid -add flag (Vivado silently rejected it, applying zero guardband). Now uses absolute 0.150 ns which covers 53 ps jitter + ~100 ps PVT margin. P1: - F-4.2 adarSetBit / adarResetBit reject broadcast=ON — the RMW sampled a single device but wrote to all four, clobbering the other three's state. - F-4.4 initializeSingleDevice returns false and leaves initialized=false when scratchpad verification fails; previously marked the device initialized anyway so downstream PA enable could drive a dead bus. - F-6.2 FIR I/Q filter_overflow ports, previously unconnected, now OR'd into the module-level filter_overflow output. - F-6.3 mti_canceller exposes 8-bit saturation counter. Saturation was previously invisible and produces spurious Doppler harmonics. Verification: - 27/27 iverilog testbenches pass - 228/228 pytest pass (cross-layer contract + cosim) - MCU unit tests 51/51 + 24/24 pass - Remote Vivado 2025.2 build: bitstream writes; 400 MHz mixer pipeline now shows WNS -0.109 ns which MATCHES the audit's F-0.9 prediction that the design only closed because F-0.8's guardband was silently dropped. ft_clkout F-0.9 remains a show-stopper (requires MRCC pin move), tracked separately. Not addressed in this PR (larger scope, follow-up tickets): F-0.4, F-0.5, F-0.6, F-0.7, F-0.9, F-1.1, F-1.2, F-2.2, F-3.2, F-4.1, F-4.7, F-6.4, F-6.5.
This commit is contained in:
@@ -10,15 +10,15 @@ extern SPI_HandleTypeDef hspi1;
|
||||
extern UART_HandleTypeDef huart3;
|
||||
|
||||
// Chip Select GPIO definitions
|
||||
static const struct {
|
||||
GPIO_TypeDef* port;
|
||||
uint16_t pin;
|
||||
} CHIP_SELECTS[4] = {
|
||||
{ADAR_1_CS_3V3_GPIO_Port, ADAR_1_CS_3V3_Pin}, // ADAR1000 #1
|
||||
{ADAR_2_CS_3V3_GPIO_Port, ADAR_2_CS_3V3_Pin}, // ADAR1000 #2
|
||||
{ADAR_3_CS_3V3_GPIO_Port, ADAR_3_CS_3V3_Pin}, // ADAR1000 #3
|
||||
{ADAR_4_CS_3V3_GPIO_Port, ADAR_4_CS_3V3_Pin} // ADAR1000 #4
|
||||
};
|
||||
static const struct {
|
||||
GPIO_TypeDef* port;
|
||||
uint16_t pin;
|
||||
} CHIP_SELECTS[4] = {
|
||||
{ADAR_1_CS_3V3_GPIO_Port, ADAR_1_CS_3V3_Pin}, // ADAR1000 #1
|
||||
{ADAR_2_CS_3V3_GPIO_Port, ADAR_2_CS_3V3_Pin}, // ADAR1000 #2
|
||||
{ADAR_3_CS_3V3_GPIO_Port, ADAR_3_CS_3V3_Pin}, // ADAR1000 #3
|
||||
{ADAR_4_CS_3V3_GPIO_Port, ADAR_4_CS_3V3_Pin} // ADAR1000 #4
|
||||
};
|
||||
|
||||
// ADAR1000 Vector Modulator lookup tables (128-state phase grid, 2.8125 deg step).
|
||||
//
|
||||
@@ -255,15 +255,15 @@ bool ADAR1000Manager::setBeamAngle(float angle_degrees, BeamDirection direction)
|
||||
|
||||
for (uint8_t dev = 0; dev < devices_.size(); ++dev) {
|
||||
for (uint8_t ch = 0; ch < 4; ++ch) {
|
||||
if (direction == BeamDirection::TX) {
|
||||
adarSetTxPhase(dev, ch + 1, phase_settings[ch], BROADCAST_OFF);
|
||||
adarSetTxVgaGain(dev, ch + 1, kDefaultTxVgaGain, BROADCAST_OFF);
|
||||
} else {
|
||||
adarSetRxPhase(dev, ch + 1, phase_settings[ch], BROADCAST_OFF);
|
||||
adarSetRxVgaGain(dev, ch + 1, kDefaultRxVgaGain, BROADCAST_OFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (direction == BeamDirection::TX) {
|
||||
adarSetTxPhase(dev, ch + 1, phase_settings[ch], BROADCAST_OFF);
|
||||
adarSetTxVgaGain(dev, ch + 1, kDefaultTxVgaGain, BROADCAST_OFF);
|
||||
} else {
|
||||
adarSetRxPhase(dev, ch + 1, phase_settings[ch], BROADCAST_OFF);
|
||||
adarSetRxVgaGain(dev, ch + 1, kDefaultRxVgaGain, BROADCAST_OFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -433,10 +433,15 @@ bool ADAR1000Manager::initializeSingleDevice(uint8_t deviceIndex) {
|
||||
adarWrite(deviceIndex, REG_ADC_CONTROL, ADAR1000_ADC_2MHZ_CLK | ADAR1000_ADC_EN, BROADCAST_OFF);
|
||||
|
||||
// Verify communication with scratchpad test
|
||||
// Audit F-4.4: on SPI failure, previously marked the device initialized
|
||||
// anyway, so downstream (e.g. PA enable) could drive PA gates out-of-spec
|
||||
// on a dead bus. Now propagate the failure so initializeAllDevices aborts.
|
||||
DIAG("BF", " dev[%u] verifying SPI communication...", deviceIndex);
|
||||
bool comms_ok = verifyDeviceCommunication(deviceIndex);
|
||||
if (!comms_ok) {
|
||||
DIAG_WARN("BF", " dev[%u] scratchpad verify FAILED but marking initialized anyway", deviceIndex);
|
||||
DIAG_ERR("BF", " dev[%u] scratchpad verify FAILED -- device NOT marked initialized", deviceIndex);
|
||||
devices_[deviceIndex]->initialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
devices_[deviceIndex]->initialized = true;
|
||||
@@ -522,7 +527,7 @@ bool ADAR1000Manager::initializeADTR1107Sequence() {
|
||||
HAL_UART_Transmit(&huart3, success, sizeof(success) - 1, 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ADAR1000Manager::setAllDevicesTXMode() {
|
||||
DIAG("BF", "setAllDevicesTXMode(): ADTR1107 -> TX, then configure ADAR1000s");
|
||||
@@ -648,7 +653,7 @@ void ADAR1000Manager::setADTR1107Mode(BeamDirection direction) {
|
||||
}
|
||||
DIAG("BF", " ADTR1107 RX mode complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ADAR1000Manager::setADTR1107Control(bool tx_mode) {
|
||||
DIAG("BF", "setADTR1107Control(%s): setting TR switch on all %u devices, settling %lu us",
|
||||
@@ -727,7 +732,7 @@ void ADAR1000Manager::setLNABias(bool enable) {
|
||||
for (uint8_t dev = 0; dev < devices_.size(); ++dev) {
|
||||
adarWrite(dev, REG_LNA_BIAS_ON, lna_bias, BROADCAST_OFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ADAR1000Manager::delayUs(uint32_t microseconds) {
|
||||
// Simple implementation - for F7 @ 216MHz, each loop ~7 cycles ≈ 0.032us
|
||||
@@ -835,12 +840,26 @@ uint8_t ADAR1000Manager::adarRead(uint8_t deviceIndex, uint32_t mem_addr) {
|
||||
}
|
||||
|
||||
void ADAR1000Manager::adarSetBit(uint8_t deviceIndex, uint32_t mem_addr, uint8_t bit, uint8_t broadcast) {
|
||||
// Audit F-4.2: broadcast-RMW is unsafe. The read samples a single device
|
||||
// but the write fans out to all four, overwriting the other three with
|
||||
// deviceIndex's state. Reject and surface the mistake.
|
||||
if (broadcast == BROADCAST_ON) {
|
||||
DIAG_ERR("BF", "adarSetBit: broadcast RMW is unsafe, ignored (dev=%u addr=0x%03lX bit=%u)",
|
||||
deviceIndex, (unsigned long)mem_addr, bit);
|
||||
return;
|
||||
}
|
||||
uint8_t temp = adarRead(deviceIndex, mem_addr);
|
||||
uint8_t data = temp | (1 << bit);
|
||||
adarWrite(deviceIndex, mem_addr, data, broadcast);
|
||||
}
|
||||
|
||||
void ADAR1000Manager::adarResetBit(uint8_t deviceIndex, uint32_t mem_addr, uint8_t bit, uint8_t broadcast) {
|
||||
// Audit F-4.2: see adarSetBit.
|
||||
if (broadcast == BROADCAST_ON) {
|
||||
DIAG_ERR("BF", "adarResetBit: broadcast RMW is unsafe, ignored (dev=%u addr=0x%03lX bit=%u)",
|
||||
deviceIndex, (unsigned long)mem_addr, bit);
|
||||
return;
|
||||
}
|
||||
uint8_t temp = adarRead(deviceIndex, mem_addr);
|
||||
uint8_t data = temp & ~(1 << bit);
|
||||
adarWrite(deviceIndex, mem_addr, data, broadcast);
|
||||
@@ -904,7 +923,7 @@ void ADAR1000Manager::adarSetTxPhase(uint8_t deviceIndex, uint8_t channel, uint8
|
||||
|
||||
adarWrite(deviceIndex, mem_addr_i, i_val, broadcast);
|
||||
adarWrite(deviceIndex, mem_addr_q, q_val, broadcast);
|
||||
adarWrite(deviceIndex, REG_LOAD_WORKING, 0x1, broadcast);
|
||||
adarWrite(deviceIndex, REG_LOAD_WORKING, LD_WRK_REGS_LDTX_OVERRIDE, broadcast);
|
||||
}
|
||||
|
||||
void ADAR1000Manager::adarSetRxVgaGain(uint8_t deviceIndex, uint8_t channel, uint8_t gain, uint8_t broadcast) {
|
||||
@@ -929,11 +948,11 @@ void ADAR1000Manager::adarSetTxVgaGain(uint8_t deviceIndex, uint8_t channel, uin
|
||||
adarWrite(deviceIndex, REG_LOAD_WORKING, LD_WRK_REGS_LDTX_OVERRIDE, broadcast);
|
||||
}
|
||||
|
||||
void ADAR1000Manager::adarSetTxBias(uint8_t deviceIndex, uint8_t broadcast) {
|
||||
adarWrite(deviceIndex, REG_BIAS_CURRENT_TX, kTxBiasCurrent, broadcast);
|
||||
adarWrite(deviceIndex, REG_BIAS_CURRENT_TX_DRV, kTxDriverBiasCurrent, broadcast);
|
||||
adarWrite(deviceIndex, REG_LOAD_WORKING, 0x2, broadcast);
|
||||
}
|
||||
void ADAR1000Manager::adarSetTxBias(uint8_t deviceIndex, uint8_t broadcast) {
|
||||
adarWrite(deviceIndex, REG_BIAS_CURRENT_TX, kTxBiasCurrent, broadcast);
|
||||
adarWrite(deviceIndex, REG_BIAS_CURRENT_TX_DRV, kTxDriverBiasCurrent, broadcast);
|
||||
adarWrite(deviceIndex, REG_LOAD_WORKING, 0x2, broadcast);
|
||||
}
|
||||
|
||||
uint8_t ADAR1000Manager::adarAdcRead(uint8_t deviceIndex, uint8_t broadcast) {
|
||||
adarWrite(deviceIndex, REG_ADC_CONTROL, ADAR1000_ADC_ST_CONV, broadcast);
|
||||
|
||||
Reference in New Issue
Block a user