Merge pull request #1676 from mndza/rearrange-gateware-regs
This commit is contained in:
commit
a8661eef4a
20 changed files with 299 additions and 192 deletions
|
|
@ -3,6 +3,7 @@ set -e
|
|||
git submodule init
|
||||
git submodule update
|
||||
cd firmware/hackrf_usb
|
||||
cmake -B build
|
||||
rm -rf build
|
||||
cmake -DBOARD=HACKRF_ONE -B build
|
||||
cmake --build build
|
||||
cd ../..
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ set -e
|
|||
git submodule init
|
||||
git submodule update
|
||||
cd firmware/hackrf_usb
|
||||
rm -rf build
|
||||
cmake -DBOARD=PRALINE -B build
|
||||
cmake --build build
|
||||
cd ../..
|
||||
|
|
|
|||
|
|
@ -719,8 +719,7 @@ class HackRF:
|
|||
log(receive.stdout + receive.stderr)
|
||||
fail(220 + self.unit_number)
|
||||
|
||||
# confirm that PLL locked to new source
|
||||
expected_value = "0x01" if (enable and self.partner.revision != "r9") else "0x51"
|
||||
# confirm that PLL A locked to new source
|
||||
timeout = time.time() + 1
|
||||
while time.time() < timeout:
|
||||
debug = subprocess.run([self.partner.bin_dir + "/hackrf_debug", "-d",
|
||||
|
|
@ -730,9 +729,17 @@ class HackRF:
|
|||
if debug.returncode != 0:
|
||||
log(debug.stderr)
|
||||
fail(230 + self.unit_number)
|
||||
if expected_value in debug.stdout:
|
||||
output = str(debug.stdout).split()
|
||||
try:
|
||||
val = int(output[3], 0)
|
||||
except:
|
||||
log(debug.stderr)
|
||||
log(traceback.format_exc())
|
||||
fail(230 + self.unit_number)
|
||||
# Check LOL_A
|
||||
if not (val >> 5) & 1:
|
||||
break
|
||||
if expected_value not in debug.stdout:
|
||||
if (val >> 5) & 1:
|
||||
fail(232 + self.unit_number)
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -595,8 +595,7 @@ class HackRF:
|
|||
log(receive.stdout + receive.stderr)
|
||||
fail(220 + self.unit_number)
|
||||
|
||||
# confirm that PLL locked to new source
|
||||
expected_value = "0x01" if (enable and self.partner.revision != "r9") else "0x51"
|
||||
# confirm that PLL A locked to new source
|
||||
timeout = time.time() + 1
|
||||
while time.time() < timeout:
|
||||
debug = subprocess.run([self.partner.bin_dir + "hackrf_debug", "-d",
|
||||
|
|
@ -606,9 +605,17 @@ class HackRF:
|
|||
if debug.returncode != 0:
|
||||
log(debug.stderr)
|
||||
fail(230 + self.unit_number)
|
||||
if expected_value in debug.stdout:
|
||||
output = str(debug.stdout).split()
|
||||
try:
|
||||
val = int(output[3], 0)
|
||||
except:
|
||||
log(debug.stderr)
|
||||
log(traceback.format_exc())
|
||||
fail(230 + self.unit_number)
|
||||
# Check LOL_A
|
||||
if not (val >> 5) & 1:
|
||||
break
|
||||
if expected_value not in debug.stdout:
|
||||
if (val >> 5) & 1:
|
||||
fail(232 + self.unit_number)
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
|
|
@ -1351,7 +1358,7 @@ def main():
|
|||
eut.clkout_connected = True
|
||||
tester.clkout_connected = True
|
||||
|
||||
if count > 0 and not args.fw_update:
|
||||
if count > 0 and not args.fwupdate:
|
||||
eut.activate_leds(False)
|
||||
|
||||
eut.test_serial()
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@ void fpga_init(fpga_driver_t* const drv)
|
|||
{
|
||||
// Standard bitstream default register values.
|
||||
set_FPGA_STANDARD_CTRL_DC_BLOCK(drv, true);
|
||||
set_FPGA_STANDARD_CTRL_QUARTER_SHIFT_EN(drv, false);
|
||||
set_FPGA_STANDARD_CTRL_QUARTER_SHIFT_UP(drv, false);
|
||||
set_FPGA_STANDARD_RX_PSTEP(drv, 0);
|
||||
set_FPGA_STANDARD_CTRL_PRBS(drv, false);
|
||||
set_FPGA_STANDARD_CTRL_TRIGGER_EN(drv, false);
|
||||
set_FPGA_STANDARD_TX_CTRL(drv, 0);
|
||||
|
|
@ -105,8 +104,7 @@ void fpga_set_rx_quarter_shift_mode(
|
|||
fpga_driver_t* const drv,
|
||||
const fpga_quarter_shift_mode_t mode)
|
||||
{
|
||||
set_FPGA_STANDARD_CTRL_QUARTER_SHIFT_EN(drv, (mode >> 0) & 0b1);
|
||||
set_FPGA_STANDARD_CTRL_QUARTER_SHIFT_UP(drv, (mode >> 1) & 0b1);
|
||||
set_FPGA_STANDARD_RX_PSTEP(drv, (mode & 0b11) << 6);
|
||||
fpga_regs_commit(drv);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@
|
|||
#include <stdbool.h>
|
||||
#include "ice40_spi.h"
|
||||
|
||||
/* Up to 6 registers, each containing up to 8 bits of data */
|
||||
#define FPGA_NUM_REGS 6
|
||||
/* Up to 7 registers, each containing up to 8 bits of data */
|
||||
#define FPGA_NUM_REGS 7
|
||||
#define FPGA_DATA_REGS_MAX_VALUE 255
|
||||
|
||||
typedef enum {
|
||||
|
|
|
|||
|
|
@ -53,22 +53,23 @@ const uint8_t n = r;
|
|||
/* REG 01 (1): CTRL */
|
||||
__MREG__(FPGA_STANDARD_CTRL, 1, 0, 8)
|
||||
__MREG__(FPGA_STANDARD_CTRL_DC_BLOCK, 1, 0, 1)
|
||||
__MREG__(FPGA_STANDARD_CTRL_QUARTER_SHIFT_EN, 1, 1, 1)
|
||||
__MREG__(FPGA_STANDARD_CTRL_QUARTER_SHIFT_UP, 1, 2, 1)
|
||||
__MREG__(FPGA_STANDARD_CTRL_PRBS, 1, 6, 1)
|
||||
__MREG__(FPGA_STANDARD_CTRL_TRIGGER_EN, 1, 7, 1)
|
||||
|
||||
/* REG 02 (2): RX_DECIM */
|
||||
__MREG__(FPGA_STANDARD_RX_DECIM, 2, 0, 3)
|
||||
|
||||
/* REG 03 (3): TX_CTRL */
|
||||
__MREG__(FPGA_STANDARD_TX_CTRL, 3, 0, 1)
|
||||
__MREG__(FPGA_STANDARD_TX_CTRL_NCO_EN, 3, 0, 1)
|
||||
/* REG 03 (3): RX_PSTEP */
|
||||
__MREG__(FPGA_STANDARD_RX_PSTEP, 3, 0, 8)
|
||||
|
||||
/* REG 04 (4): TX_INTRP */
|
||||
__MREG__(FPGA_STANDARD_TX_INTRP, 4, 0, 3)
|
||||
/* REG 04 (4): TX_CTRL */
|
||||
__MREG__(FPGA_STANDARD_TX_CTRL, 4, 0, 1)
|
||||
__MREG__(FPGA_STANDARD_TX_CTRL_NCO_EN, 4, 0, 1)
|
||||
|
||||
/* REG 05 (5): TX_PSTEP */
|
||||
__MREG__(FPGA_STANDARD_TX_PSTEP, 5, 0, 8)
|
||||
/* REG 05 (5): TX_INTRP */
|
||||
__MREG__(FPGA_STANDARD_TX_INTRP, 5, 0, 3)
|
||||
|
||||
/* REG 06 (6): TX_PSTEP */
|
||||
__MREG__(FPGA_STANDARD_TX_PSTEP, 6, 0, 8)
|
||||
|
||||
#endif // __FPGA_REGS_DEF
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ bool fpga_spi_selftest(void)
|
|||
}
|
||||
|
||||
// Test writing a register and reading it back.
|
||||
uint8_t reg = 5;
|
||||
uint8_t reg = 6;
|
||||
uint8_t write_value = 0xA5;
|
||||
ssp1_set_mode_ice40();
|
||||
ice40_spi_write(&ice40, reg, write_value);
|
||||
|
|
|
|||
|
|
@ -266,6 +266,7 @@ fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program)
|
|||
sgpio_cpld_stream_disable(&sgpio_config);
|
||||
}
|
||||
|
||||
#ifndef PRALINE
|
||||
/* Integer mode can be enabled if p1 is even and p2 is zero. */
|
||||
if (p1 & 0x1 || p2) {
|
||||
si5351c_set_int_mode(&clock_gen, 0, 0);
|
||||
|
|
@ -273,7 +274,6 @@ fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program)
|
|||
si5351c_set_int_mode(&clock_gen, 0, 1);
|
||||
}
|
||||
|
||||
#ifndef PRALINE
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
/*
|
||||
* On HackRF One r9 all sample clocks are externally derived
|
||||
|
|
@ -295,8 +295,31 @@ fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program)
|
|||
si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); //p1 doesn't matter
|
||||
}
|
||||
#else
|
||||
/* MS0/CLK0 is the source for the MAX5864/FPGA (AFE_CLK). */
|
||||
/* MS0/CLK0 is the source for the MAX5864 (AFE_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1);
|
||||
|
||||
/* MS1/CLK1 is the source for the FPGA (FPGA_CLK and SCT_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 1);
|
||||
|
||||
/* Delay FPGA_CLK relative to AFE_CLK. */
|
||||
uint8_t phase_offset = 0;
|
||||
if (p1 < 2100) {
|
||||
phase_offset = (p1 >> 4) - 6;
|
||||
}
|
||||
si5351c_set_phase(&clock_gen, 1, phase_offset);
|
||||
|
||||
if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) {
|
||||
/*
|
||||
* On older boards FPGA_CLK is on CLK2 while SCT_CLK is on
|
||||
* CLK1. We configure both so that behavior is consistent with
|
||||
* newer boards that use CLK1 for both FPGA_CLK and SCT_CLK.
|
||||
*/
|
||||
si5351c_configure_multisynth(&clock_gen, 2, p1, p2, p3, 1);
|
||||
si5351c_set_phase(&clock_gen, 2, phase_offset);
|
||||
}
|
||||
|
||||
/* Reset PLL to synchronize output clock phase. */
|
||||
si5351c_reset_pll(&clock_gen);
|
||||
#endif
|
||||
|
||||
if (streaming) {
|
||||
|
|
@ -528,8 +551,6 @@ void clock_gen_init(void)
|
|||
si5351c_power_down_all_clocks(&clock_gen);
|
||||
si5351c_set_crystal_configuration(&clock_gen);
|
||||
si5351c_enable_xo_and_ms_fanout(&clock_gen);
|
||||
si5351c_configure_pll_sources(&clock_gen);
|
||||
si5351c_configure_pll_multisynth(&clock_gen);
|
||||
|
||||
/*
|
||||
* Clocks on HackRF One r9:
|
||||
|
|
@ -592,6 +613,7 @@ void clock_gen_init(void)
|
|||
/* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */
|
||||
sample_rate_set(10ULL * FP_ONE_MHZ, true);
|
||||
|
||||
si5351c_configure_clock_control(&clock_gen);
|
||||
si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL);
|
||||
// soft reset
|
||||
si5351c_reset_pll(&clock_gen);
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
|
|||
switch (opmode) {
|
||||
case TRANSCEIVER_MODE_TX:
|
||||
case TRANSCEIVER_MODE_SS:
|
||||
n = compute_resample_log(rate / FP_ONE_HZ, requested_n);
|
||||
if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]) {
|
||||
#ifdef PRALINE
|
||||
fpga_set_tx_interpolation_ratio(&fpga, n);
|
||||
|
|
@ -196,10 +197,6 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
|
|||
}
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* Resampling is enabled only in RX mode to work around a
|
||||
* spectrum inversion bug with TX interpolation.
|
||||
*/
|
||||
n = compute_resample_log(rate / FP_ONE_HZ, requested_n);
|
||||
if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]) {
|
||||
#ifdef PRALINE
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "platform_scu.h"
|
||||
#include "hackrf_core.h"
|
||||
#include "selftest.h"
|
||||
#include "delay.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
|
|
@ -116,34 +117,46 @@ void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv)
|
|||
/*
|
||||
* Register 15: PLL Input Source
|
||||
* CLKIN_DIV=0 (Divide by 1)
|
||||
* PLLA_SRC=0 (XTAL)
|
||||
* PLLB_SRC=1 (CLKIN)
|
||||
* Set both PLLA_SRC and PLLB_SRC
|
||||
*/
|
||||
void si5351c_configure_pll_sources(si5351c_driver_t* const drv)
|
||||
void si5351c_configure_pll_sources(
|
||||
si5351c_driver_t* const drv,
|
||||
const enum pll_sources source)
|
||||
{
|
||||
uint8_t data[] = {15, 0x08};
|
||||
uint8_t data[] = {15, 0x00};
|
||||
|
||||
if (source == PLL_SOURCE_CLKIN) {
|
||||
data[1] = 0x0c;
|
||||
}
|
||||
si5351c_write(drv, data, sizeof(data));
|
||||
}
|
||||
|
||||
/* MultiSynth NA (PLLA) and NB (PLLB) */
|
||||
void si5351c_configure_pll_multisynth(si5351c_driver_t* const drv)
|
||||
void si5351c_configure_pll_multisynth(
|
||||
si5351c_driver_t* const drv,
|
||||
const enum pll_sources source)
|
||||
{
|
||||
/*PLLA: 25MHz XTAL * (0x0e00+512)/128 = 800mhz -> int mode */
|
||||
/* XTAL: 25 MHz * (0x0e00 + 512) / 128 = 800 MHz, integer mode */
|
||||
uint8_t data[] = {26, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00};
|
||||
if (source == PLL_SOURCE_CLKIN) {
|
||||
/* CLKIN: 10 MHz * (0x2600 + 512) / 128 = 800 MHz, integer mode */
|
||||
data[4] = 0x26;
|
||||
}
|
||||
si5351c_write(drv, data, sizeof(data));
|
||||
|
||||
/*PLLB: 10MHz CLKIN * (0x2600+512)/128 = 800mhz */
|
||||
/* Apply same configuration to PLL B. */
|
||||
data[0] = 34;
|
||||
data[4] = 0x26;
|
||||
si5351c_write(drv, data, sizeof(data));
|
||||
}
|
||||
|
||||
void si5351c_reset_pll(si5351c_driver_t* const drv)
|
||||
{
|
||||
si5351c_disable_all_outputs(drv);
|
||||
/* reset PLLA and PLLB */
|
||||
uint8_t data[] = {177, 0xA0};
|
||||
si5351c_write(drv, data, sizeof(data));
|
||||
delay_us_at_mhz(2000, 204);
|
||||
si5351c_enable_clock_outputs(drv);
|
||||
}
|
||||
|
||||
void si5351c_configure_multisynth(
|
||||
|
|
@ -180,43 +193,11 @@ void si5351c_configure_multisynth(
|
|||
si5351c_write(drv, data, sizeof(data));
|
||||
}
|
||||
|
||||
void si5351c_configure_clock_control(
|
||||
si5351c_driver_t* const drv,
|
||||
const enum pll_sources source)
|
||||
void si5351c_configure_clock_control(si5351c_driver_t* const drv)
|
||||
{
|
||||
uint8_t pll;
|
||||
const uint8_t pll = SI5351C_CLK_PLL_SRC_A;
|
||||
uint8_t clkout_ctrl;
|
||||
|
||||
#ifdef RAD1O
|
||||
(void) source;
|
||||
/* PLLA on XTAL */
|
||||
pll = SI5351C_CLK_PLL_SRC_A;
|
||||
#endif
|
||||
|
||||
#if (defined JAWBREAKER || defined HACKRF_ONE || defined PRALINE)
|
||||
if (source == PLL_SOURCE_CLKIN) {
|
||||
/* PLLB on CLKIN */
|
||||
pll = SI5351C_CLK_PLL_SRC_B;
|
||||
#if defined(HACKRF_ONE)
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
/*
|
||||
* HackRF One r9 always uses PLL A on the XTAL input
|
||||
* but externally switches that input to CLKIN.
|
||||
*/
|
||||
pll = SI5351C_CLK_PLL_SRC_A;
|
||||
gpio_set(platform_gpio()->h1r9_clkin_en);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/* PLLA on XTAL */
|
||||
pll = SI5351C_CLK_PLL_SRC_A;
|
||||
#if defined(HACKRF_ONE)
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
gpio_clear(platform_gpio()->h1r9_clkin_en);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (clkout_enabled) {
|
||||
clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) |
|
||||
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
|
||||
|
|
@ -263,13 +244,27 @@ void si5351c_configure_clock_control(
|
|||
data[6] = SI5351C_CLK_POWERDOWN;
|
||||
}
|
||||
#ifdef PRALINE
|
||||
/* CLK0: AFE_CLK */
|
||||
data[1] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(pll) |
|
||||
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
|
||||
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA);
|
||||
data[3] = clkout_ctrl;
|
||||
/* CLK1: SCT_CLK and FPGA_CLK */
|
||||
data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(pll) |
|
||||
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
|
||||
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA);
|
||||
/* CLK4: XCVR_CLK */
|
||||
data[5] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(pll) |
|
||||
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
|
||||
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA) | SI5351C_CLK_INV;
|
||||
if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) {
|
||||
/* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */
|
||||
data[3] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(pll) |
|
||||
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
|
||||
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA);
|
||||
} else {
|
||||
/* CLK2: MCU_CLK */
|
||||
data[3] = SI5351C_CLK_POWERDOWN;
|
||||
}
|
||||
#endif
|
||||
si5351c_write(drv, data, sizeof(data));
|
||||
}
|
||||
|
|
@ -281,7 +276,7 @@ void si5351c_configure_clock_control(
|
|||
void si5351c_enable_clock_outputs(si5351c_driver_t* const drv)
|
||||
{
|
||||
/* Enable CLK outputs 0, 1, 2, 4, 5 only. */
|
||||
/* Praline: enable 0, 4, 5 only. */
|
||||
/* Praline: enable 0, 1, 4, 5 only. */
|
||||
/* 7: Clock to CPU is deactivated as it is not used and creates noise */
|
||||
/* 3: External clock output is deactivated by default */
|
||||
|
||||
|
|
@ -291,8 +286,14 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv)
|
|||
SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7);
|
||||
#else
|
||||
uint8_t value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) |
|
||||
SI5351C_CLK_DISABLE(2) | SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) |
|
||||
SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7);
|
||||
SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) | SI5351C_CLK_DISABLE(6) |
|
||||
SI5351C_CLK_DISABLE(7);
|
||||
if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) {
|
||||
/* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */
|
||||
value |= SI5351C_CLK_ENABLE(2);
|
||||
} else {
|
||||
value |= SI5351C_CLK_DISABLE(2);
|
||||
}
|
||||
#endif
|
||||
uint8_t clkout = 3;
|
||||
|
||||
|
|
@ -348,18 +349,26 @@ void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_source
|
|||
if (source == active_clock_source) {
|
||||
return;
|
||||
}
|
||||
si5351c_configure_clock_control(drv, source);
|
||||
active_clock_source = source;
|
||||
si5351c_disable_all_outputs(drv);
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
/* 25MHz XTAL * (0x0e00+512)/128 = 800mhz -> int mode */
|
||||
uint8_t pll_data[] = {26, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00};
|
||||
#if defined(HACKRF_ONE)
|
||||
/*
|
||||
* HackRF One r9 always uses PLL A on the XTAL input
|
||||
* but externally switches that input to CLKIN.
|
||||
*/
|
||||
si5351c_configure_pll_sources(drv, PLL_SOURCE_XTAL);
|
||||
if (source == PLL_SOURCE_CLKIN) {
|
||||
/* 10MHz CLKIN * (0x2600+512)/128 = 800mhz */
|
||||
pll_data[4] = 0x26;
|
||||
gpio_set(platform_gpio()->h1r9_clkin_en);
|
||||
} else {
|
||||
gpio_clear(platform_gpio()->h1r9_clkin_en);
|
||||
}
|
||||
si5351c_write(drv, pll_data, sizeof(pll_data));
|
||||
si5351c_reset_pll(drv);
|
||||
#endif
|
||||
} else {
|
||||
si5351c_configure_pll_sources(drv, source);
|
||||
}
|
||||
si5351c_configure_pll_multisynth(drv, source);
|
||||
active_clock_source = source;
|
||||
si5351c_reset_pll(drv);
|
||||
}
|
||||
|
||||
bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv)
|
||||
|
|
@ -385,7 +394,7 @@ void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable)
|
|||
/* Configure clock to 10MHz */
|
||||
si5351c_configure_multisynth(drv, clkout, 80 * 128 - 512, 0, 1, 0);
|
||||
|
||||
si5351c_configure_clock_control(drv, active_clock_source);
|
||||
si5351c_configure_clock_control(drv);
|
||||
si5351c_enable_clock_outputs(drv);
|
||||
}
|
||||
|
||||
|
|
@ -434,3 +443,21 @@ void si5351c_init(si5351c_driver_t* const drv)
|
|||
#endif
|
||||
(void) drv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set initial phase offset of output multisynth. AN619 associates this setting
|
||||
* with outputs, but it seems to really be a multisynth setting.
|
||||
*
|
||||
* After changing this setting, you must call si5351c_reset_pll() to
|
||||
* synchronize outputs with the new phase offset.
|
||||
*/
|
||||
void si5351c_set_phase(
|
||||
si5351c_driver_t* const drv,
|
||||
const uint8_t ms_number,
|
||||
const uint8_t offset)
|
||||
{
|
||||
const uint8_t address = 165 + ms_number;
|
||||
if (ms_number < 8) {
|
||||
si5351c_write_single(drv, address, offset & 0x7f);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,8 +75,12 @@ void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv);
|
|||
void si5351c_power_down_all_clocks(si5351c_driver_t* const drv);
|
||||
void si5351c_set_crystal_configuration(si5351c_driver_t* const drv);
|
||||
void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv);
|
||||
void si5351c_configure_pll_sources(si5351c_driver_t* const drv);
|
||||
void si5351c_configure_pll_multisynth(si5351c_driver_t* const drv);
|
||||
void si5351c_configure_pll_sources(
|
||||
si5351c_driver_t* const drv,
|
||||
const enum pll_sources source);
|
||||
void si5351c_configure_pll_multisynth(
|
||||
si5351c_driver_t* const drv,
|
||||
const enum pll_sources source);
|
||||
void si5351c_reset_pll(si5351c_driver_t* const drv);
|
||||
void si5351c_configure_multisynth(
|
||||
si5351c_driver_t* const drv,
|
||||
|
|
@ -85,9 +89,7 @@ void si5351c_configure_multisynth(
|
|||
const uint32_t p2,
|
||||
const uint32_t p3,
|
||||
const uint_fast8_t r_div);
|
||||
void si5351c_configure_clock_control(
|
||||
si5351c_driver_t* const drv,
|
||||
const enum pll_sources source);
|
||||
void si5351c_configure_clock_control(si5351c_driver_t* const drv);
|
||||
void si5351c_enable_clock_outputs(si5351c_driver_t* const drv);
|
||||
void si5351c_set_int_mode(
|
||||
si5351c_driver_t* const drv,
|
||||
|
|
@ -104,6 +106,10 @@ void si5351c_write(
|
|||
const size_t data_count);
|
||||
void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable);
|
||||
void si5351c_init(si5351c_driver_t* const drv);
|
||||
void si5351c_set_phase(
|
||||
si5351c_driver_t* const drv,
|
||||
const uint8_t ms_number,
|
||||
const uint8_t offset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# Copyright (c) 2024 Great Scott Gadgets <info@greatscottgadgets.com>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from amaranth import Elaboratable, Signal, Instance, Module, ClockDomain
|
||||
from amaranth import Elaboratable, Signal, Const, Instance, Module, ClockDomain
|
||||
from amaranth.build import Resource, Pins, Clock, Attrs
|
||||
from amaranth.vendor import LatticeICE40Platform
|
||||
from amaranth_boards.resources import SPIResource
|
||||
|
|
@ -21,8 +21,8 @@ class PralinePlatform(LatticeICE40Platform):
|
|||
hfosc_div = 0 # Do not divide
|
||||
|
||||
resources = [
|
||||
Resource("fpga_clk", 0, Pins("39", dir="i"),
|
||||
Attrs(GLOBAL=True, IO_STANDARD="SB_LVCMOS")),
|
||||
Resource("fpga_clk", 0, Pins("47", dir="i"),
|
||||
Attrs(IO_STANDARD="SB_LVCMOS")),
|
||||
|
||||
# ADC/DAC interfaces.
|
||||
Resource("afe_clk", 0, Pins("35", dir="i"),
|
||||
|
|
@ -65,15 +65,23 @@ class PralinePlatform(LatticeICE40Platform):
|
|||
class ClockDomainGenerator(Elaboratable):
|
||||
|
||||
@staticmethod
|
||||
def lut_delay(m, signal, *, depth):
|
||||
def lut_delay(m, signal, *, depth, invert=False, bel=None):
|
||||
# Each LUT introduces a minimum propagation delay of 9ns (best case).
|
||||
signal_out = signal
|
||||
for i in range(depth):
|
||||
signal_in = signal_out
|
||||
signal_out = Signal()
|
||||
signal_out = Signal(attrs={"keep": True}) # avoid LUT optimization
|
||||
other_opts = {}
|
||||
if bel is not None:
|
||||
other_opts["a_BEL"] = f"{bel}/lc{i}"
|
||||
m.submodules += Instance("SB_LUT4",
|
||||
p_LUT_INIT=0xAAAA, # Buffer configuration
|
||||
p_LUT_INIT=Const(0b01 if invert else 0b10, 16),
|
||||
i_I0=signal_in,
|
||||
i_I1=Const(0),
|
||||
i_I2=Const(0),
|
||||
i_I3=Const(0),
|
||||
o_O=signal_out,
|
||||
**other_opts,
|
||||
)
|
||||
return signal_out
|
||||
|
||||
|
|
@ -81,12 +89,17 @@ class ClockDomainGenerator(Elaboratable):
|
|||
m = Module()
|
||||
|
||||
# Define clock domains.
|
||||
m.domains.gck1 = cd_gck1 = ClockDomain(name="gck1", reset_less=True) # analog front-end clock.
|
||||
m.domains.adclk = cd_adclk = ClockDomain(name="adclk", reset_less=True)
|
||||
m.domains.daclk = cd_daclk = ClockDomain(name="daclk", reset_less=True)
|
||||
|
||||
# We need to delay `gck1` clock by at least 8ns, not possible with the PLL alone.
|
||||
# Each LUT introduces a minimum propagation delay of 9ns (best case).
|
||||
delayed_gck1 = self.lut_delay(m, platform.request("afe_clk").i, depth=2)
|
||||
m.d.comb += cd_gck1.clk.eq(delayed_gck1)
|
||||
platform.add_clock_constraint(delayed_gck1, 40e6)
|
||||
adclk_ref = platform.request("afe_clk").i
|
||||
fpgaclk_ref = platform.request("fpga_clk").i
|
||||
|
||||
delayed_adclk = self.lut_delay(m, adclk_ref, depth=1, bel="X12/Y30") # delay `afe_clk` clock by at least 8ns
|
||||
m.d.comb += cd_adclk.clk.eq(delayed_adclk)
|
||||
platform.add_clock_constraint(delayed_adclk, 40e6)
|
||||
|
||||
m.d.comb += cd_daclk.clk.eq(fpgaclk_ref)
|
||||
platform.add_clock_constraint(cd_daclk.clk, 40e6)
|
||||
|
||||
return m
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -24,7 +24,7 @@ class ShiftAddMCM(wiring.Component):
|
|||
"output": Out(stream.Signature(
|
||||
data.ArrayLayout(
|
||||
data.StructLayout({
|
||||
f"{i}": signed(width + bits_for(term)) for i, term in enumerate(terms)
|
||||
f"{i}": signed(width + bits_for(abs(term))) for i, term in enumerate(terms)
|
||||
}), num_channels), always_ready=always_ready)),
|
||||
})
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ class ShiftAddMCM(wiring.Component):
|
|||
result += shifted_n
|
||||
|
||||
# A single register can feed multiple outputs.
|
||||
result_q = Signal(signed(self.width+bits_for(term-1)), name=f"mul_{term}_{c}")
|
||||
result_q = Signal(signed(self.width+bits_for(abs(term))), name=f"mul_{term}_{c}")
|
||||
with m.If(self.input.ready & self.input.valid):
|
||||
m.d.sync += result_q.eq(result)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# Copyright (c) 2025 Great Scott Gadgets <info@greatscottgadgets.com>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from amaranth import Module, Signal, C, Cat
|
||||
from amaranth import Module, Signal, C, Cat, Mux
|
||||
from amaranth.lib import io, stream, wiring
|
||||
from amaranth.lib.wiring import Out, In
|
||||
|
||||
|
|
@ -15,9 +15,10 @@ class MAX586xInterface(wiring.Component):
|
|||
dac_stream: In(stream.Signature(IQSample(8), always_ready=True))
|
||||
q_invert: In(1)
|
||||
|
||||
def __init__(self, bb_domain):
|
||||
def __init__(self, adc_domain, dac_domain):
|
||||
super().__init__()
|
||||
self._bb_domain = bb_domain
|
||||
self._adc_domain = adc_domain
|
||||
self._dac_domain = dac_domain
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
|
@ -25,30 +26,27 @@ class MAX586xInterface(wiring.Component):
|
|||
dac_stream = self.dac_stream
|
||||
|
||||
# Generate masks for inverting the Q component based on the q_invert signal.
|
||||
q_invert = Signal()
|
||||
q_invert_rx = Signal()
|
||||
q_invert_tx = Signal()
|
||||
rx_q_mask = Signal(8)
|
||||
tx_q_mask = Signal(10)
|
||||
m.d[self._bb_domain] += q_invert.eq(self.q_invert)
|
||||
with m.If(q_invert):
|
||||
m.d.comb += [
|
||||
rx_q_mask.eq(0x80),
|
||||
tx_q_mask.eq(0x1FF),
|
||||
]
|
||||
with m.Else():
|
||||
m.d.comb += [
|
||||
rx_q_mask.eq(0x7F),
|
||||
tx_q_mask.eq(0x200),
|
||||
]
|
||||
m.d[self._adc_domain] += q_invert_rx.eq(self.q_invert)
|
||||
m.d[self._dac_domain] += q_invert_tx.eq(self.q_invert)
|
||||
|
||||
m.d.comb += [
|
||||
rx_q_mask.eq(Mux(q_invert_rx, 0x80, 0x7F)),
|
||||
tx_q_mask.eq(Mux(q_invert_tx, 0x1FF, 0x200)),
|
||||
]
|
||||
|
||||
# Capture the ADC signals using a DDR input buffer.
|
||||
m.submodules.adc_in = adc_in = io.DDRBuffer("i", platform.request("da", dir="-"), i_domain=self._bb_domain)
|
||||
m.submodules.adc_in = adc_in = io.DDRBuffer("i", platform.request("da", dir="-"), i_domain=self._adc_domain)
|
||||
m.d.comb += [
|
||||
adc_stream.p.i .eq(adc_in.i[0] ^ 0x80), # I: non-inverted between MAX2837 and MAX5864.
|
||||
adc_stream.p.q .eq(adc_in.i[1] ^ rx_q_mask), # Q: inverted between MAX2837 and MAX5864.
|
||||
]
|
||||
|
||||
# Output to the DAC using a DDR output buffer.
|
||||
m.submodules.dac_out = dac_out = io.DDRBuffer("o", platform.request("dd", dir="-"), o_domain=self._bb_domain)
|
||||
m.submodules.dac_out = dac_out = io.DDRBuffer("o", platform.request("dd", dir="-"), o_domain=self._dac_domain)
|
||||
with m.If(dac_stream.valid):
|
||||
m.d.comb += [
|
||||
dac_out.o[0] .eq(Cat(C(0, 2), dac_stream.p.i) ^ 0x200),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
# Copyright (c) 2025 Great Scott Gadgets <info@greatscottgadgets.com>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from amaranth import Elaboratable, Module, Cat, DomainRenamer
|
||||
from amaranth import Elaboratable, Module, Cat, DomainRenamer, Signal
|
||||
from amaranth.lib.wiring import connect
|
||||
|
||||
from amaranth_future import fixed
|
||||
|
|
@ -25,22 +25,24 @@ class Top(Elaboratable):
|
|||
m = Module()
|
||||
|
||||
m.submodules.clkgen = ClockDomainGenerator()
|
||||
adc_clk = "adclk"
|
||||
dac_clk = "daclk"
|
||||
|
||||
# Submodules.
|
||||
m.submodules.adcdac_intf = adcdac_intf = MAX586xInterface(bb_domain="gck1")
|
||||
m.submodules.adcdac_intf = adcdac_intf = MAX586xInterface(adc_domain=adc_clk, dac_domain=dac_clk)
|
||||
m.submodules.mcu_intf = mcu_intf = SGPIOInterface(
|
||||
sample_width=24,
|
||||
rx_assignments=[
|
||||
lambda w: Cat(w[8:12], w[11].replicate(4)),
|
||||
lambda w: w[0:8],
|
||||
lambda w: Cat(w[20:24], w[23].replicate(4)),
|
||||
lambda w: Cat(w[8:12], w[11].replicate(4)),
|
||||
lambda w: w[12:20],
|
||||
lambda w: Cat(w[20:24], w[23].replicate(4)),
|
||||
],
|
||||
tx_assignments=[
|
||||
lambda w, v: w[8:12].eq(v),
|
||||
lambda w, v: w[0:8].eq(v),
|
||||
lambda w, v: w[20:24].eq(v),
|
||||
lambda w, v: w[8:12].eq(v),
|
||||
lambda w, v: w[12:20].eq(v),
|
||||
lambda w, v: w[20:24].eq(v),
|
||||
],
|
||||
domain="sync"
|
||||
)
|
||||
|
|
@ -56,19 +58,19 @@ class Top(Elaboratable):
|
|||
|
||||
rx_chain = {
|
||||
# DC block and quarter shift.
|
||||
"dc_block": DCBlock(width=8, num_channels=2, domain="gck1"),
|
||||
"quarter_shift": DomainRenamer("gck1")(QuarterShift()),
|
||||
"dc_block": DCBlock(width=8, num_channels=2, domain=adc_clk),
|
||||
"quarter_shift": DomainRenamer(adc_clk)(QuarterShift()),
|
||||
|
||||
# CIC mandatory first stage with compensator.
|
||||
"cic": CICDecimator(2, 4, (4,8,16,32), width_in=8, width_out=12, num_channels=2, always_ready=True, domain="gck1"),
|
||||
"cic_comp": DomainRenamer("gck1")(FIRFilter([-0.125, 0, 0.75, 0, -0.125], shape=fixed.SQ(11), shape_out=fixed.SQ(11), always_ready=True, num_channels=2)),
|
||||
"cic": CICDecimator(2, 4, (4,8,16,32), width_in=8, width_out=12, num_channels=2, always_ready=True, domain=adc_clk),
|
||||
"cic_comp": DomainRenamer(adc_clk)(FIRFilter([-0.125, 0, 0.75, 0, -0.125], shape=fixed.SQ(11), shape_out=fixed.SQ(11), always_ready=True, num_channels=2)),
|
||||
|
||||
# Final half-band decimator stages.
|
||||
"hbfir1": HalfBandDecimatorMAC16(taps_hb1, data_shape=fixed.SQ(11), overclock_rate=4, always_ready=True, domain="gck1"),
|
||||
"hbfir2": HalfBandDecimatorMAC16(taps_hb2, data_shape=fixed.SQ(11), overclock_rate=8, always_ready=True, domain="gck1"),
|
||||
"hbfir1": HalfBandDecimatorMAC16(taps_hb1, data_shape=fixed.SQ(11), overclock_rate=4, always_ready=True, domain=adc_clk),
|
||||
"hbfir2": HalfBandDecimatorMAC16(taps_hb2, data_shape=fixed.SQ(11), overclock_rate=8, always_ready=True, domain=adc_clk),
|
||||
|
||||
# Clock domain conversion.
|
||||
"clkconv": ClockConverter(IQSample(12), 8, "gck1", "sync", always_ready=True),
|
||||
"clkconv": ClockConverter(IQSample(12), 8, adc_clk, "sync", always_ready=True),
|
||||
}
|
||||
for k,v in rx_chain.items():
|
||||
m.submodules[f"rx_{k}"] = v
|
||||
|
|
@ -85,9 +87,11 @@ class Top(Elaboratable):
|
|||
m.submodules.spi_regs = spi_regs = SPIRegisterInterface(spi_port)
|
||||
|
||||
# Add control registers.
|
||||
ctrl = spi_regs.add_register(0x01, init=0)
|
||||
rx_decim = spi_regs.add_register(0x02, init=0, size=3)
|
||||
#tx_intrp = spi_regs.add_register(0x04, init=0, size=3)
|
||||
ctrl = spi_regs.add_register(0x01, init=0)
|
||||
rx_decim = Signal(3, init=2)
|
||||
rx_decim_new = Signal(3)
|
||||
rx_decim_stb = Signal()
|
||||
spi_regs.add_sfr(0x02, read=rx_decim, write_signal=rx_decim_new, write_strobe=rx_decim_stb)
|
||||
|
||||
m.d.comb += [
|
||||
# Trigger enable.
|
||||
|
|
@ -99,8 +103,13 @@ class Top(Elaboratable):
|
|||
rx_chain["quarter_shift"].up .eq(ctrl[2]),
|
||||
|
||||
# RX decimation rate.
|
||||
rx_chain["cic"].factor .eq(rx_decim+2),
|
||||
rx_chain["cic"].factor .eq(rx_decim),
|
||||
]
|
||||
with m.If(rx_decim_stb):
|
||||
with m.If(rx_decim_new < 2):
|
||||
m.d.sync += rx_decim.eq(2)
|
||||
with m.Else():
|
||||
m.d.sync += rx_decim.eq(rx_decim_new)
|
||||
|
||||
return m
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
# Copyright (c) 2025 Great Scott Gadgets <info@greatscottgadgets.com>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from amaranth import Elaboratable, Module, Cat, DomainRenamer
|
||||
from amaranth import Elaboratable, Module, Cat, DomainRenamer, Signal
|
||||
from amaranth.lib import cdc
|
||||
from amaranth.lib.wiring import connect
|
||||
|
||||
from amaranth_future import fixed
|
||||
|
|
@ -23,22 +24,24 @@ class Top(Elaboratable):
|
|||
m = Module()
|
||||
|
||||
m.submodules.clkgen = ClockDomainGenerator()
|
||||
adc_clk = "adclk"
|
||||
dac_clk = "daclk"
|
||||
|
||||
# Submodules.
|
||||
m.submodules.adcdac_intf = adcdac_intf = MAX586xInterface(bb_domain="gck1")
|
||||
m.submodules.adcdac_intf = adcdac_intf = MAX586xInterface(adc_domain=adc_clk, dac_domain=dac_clk)
|
||||
m.submodules.mcu_intf = mcu_intf = SGPIOInterface(
|
||||
sample_width=24,
|
||||
rx_assignments=[
|
||||
lambda w: Cat(w[8:12], w[11].replicate(4)),
|
||||
lambda w: w[0:8],
|
||||
lambda w: Cat(w[20:24], w[23].replicate(4)),
|
||||
lambda w: Cat(w[8:12], w[11].replicate(4)),
|
||||
lambda w: w[12:20],
|
||||
lambda w: Cat(w[20:24], w[23].replicate(4)),
|
||||
],
|
||||
tx_assignments=[
|
||||
lambda w, v: w[8:12].eq(v),
|
||||
lambda w, v: w[0:8].eq(v),
|
||||
lambda w, v: w[20:24].eq(v),
|
||||
lambda w, v: w[8:12].eq(v),
|
||||
lambda w, v: w[12:20].eq(v),
|
||||
lambda w, v: w[20:24].eq(v),
|
||||
],
|
||||
domain="sync"
|
||||
)
|
||||
|
|
@ -55,20 +58,20 @@ class Top(Elaboratable):
|
|||
|
||||
tx_chain = {
|
||||
# Clock domain conversion.
|
||||
"clkconv": ClockConverter(IQSample(12), 8, "sync", "gck1", always_ready=False),
|
||||
"clkconv": ClockConverter(IQSample(12), 8, "sync", dac_clk, always_ready=False),
|
||||
|
||||
# Half-band interpolation stages (+ skid buffers for timing closure).
|
||||
"hbfir1": HalfBandInterpolatorMAC16(taps_hb1, data_shape=fixed.SQ(11),
|
||||
overclock_rate=8, num_channels=2, always_ready=False, domain="gck1"),
|
||||
"skid1": DomainRenamer("gck1")(StreamSkidBuffer(IQSample(12), always_ready=False)),
|
||||
overclock_rate=8, num_channels=2, always_ready=False, domain=dac_clk),
|
||||
"skid1": DomainRenamer(dac_clk)(StreamSkidBuffer(IQSample(12), always_ready=False)),
|
||||
"hbfir2": HalfBandInterpolatorMAC16(taps_hb2, data_shape=fixed.SQ(11),
|
||||
overclock_rate=4, num_channels=2, always_ready=False, domain="gck1"),
|
||||
"skid2": DomainRenamer("gck1")(StreamSkidBuffer(IQSample(12), always_ready=False)),
|
||||
overclock_rate=4, num_channels=2, always_ready=False, domain=dac_clk),
|
||||
"skid2": DomainRenamer(dac_clk)(StreamSkidBuffer(IQSample(12), always_ready=False)),
|
||||
|
||||
# CIC interpolation stage.
|
||||
"cic_comp": DomainRenamer("gck1")(FIRFilter([-0.125, 0, 0.75, 0, -0.125], shape=fixed.SQ(11), shape_out=fixed.SQ(11), always_ready=False, num_channels=2)),
|
||||
"cic_comp": DomainRenamer(dac_clk)(FIRFilter([-0.125, 0, 0.75, 0, -0.125], shape=fixed.SQ(11), shape_out=fixed.SQ(11), always_ready=False, num_channels=2)),
|
||||
"cic_interpolator": CICInterpolator(2, 4, (4, 8, 16, 32), 12, 8, num_channels=2,
|
||||
always_ready=False, domain="gck1"),
|
||||
always_ready=False, domain=dac_clk),
|
||||
}
|
||||
for k,v in tx_chain.items():
|
||||
m.submodules[f"tx_{k}"] = v
|
||||
|
|
@ -86,16 +89,24 @@ class Top(Elaboratable):
|
|||
m.submodules.spi_regs = spi_regs = SPIRegisterInterface(spi_port)
|
||||
|
||||
# Add control registers.
|
||||
ctrl = spi_regs.add_register(0x01, init=0)
|
||||
tx_intrp = spi_regs.add_register(0x02, init=0, size=3)
|
||||
ctrl = spi_regs.add_register(0x01, init=0)
|
||||
tx_intrp = Signal(3, init=2)
|
||||
tx_intrp_new = Signal(3)
|
||||
tx_intrp_stb = Signal()
|
||||
spi_regs.add_sfr(0x05, read=tx_intrp, write_signal=tx_intrp_new, write_strobe=tx_intrp_stb)
|
||||
|
||||
m.d.comb += [
|
||||
# Trigger enable.
|
||||
mcu_intf.trigger_en .eq(ctrl[7]),
|
||||
|
||||
# TX interpolation rate.
|
||||
tx_chain["cic_interpolator"].factor .eq(tx_intrp + 2),
|
||||
]
|
||||
# TX interpolation rate.
|
||||
m.submodules.rx_decim_cdc = cdc.FFSynchronizer(tx_intrp, tx_chain["cic_interpolator"].factor, o_domain=dac_clk)
|
||||
|
||||
with m.If(tx_intrp_stb):
|
||||
with m.If(tx_intrp_new < 2):
|
||||
m.d.sync += tx_intrp.eq(2)
|
||||
with m.Else():
|
||||
m.d.sync += tx_intrp.eq(tx_intrp_new)
|
||||
|
||||
return m
|
||||
|
||||
|
|
|
|||
|
|
@ -54,17 +54,19 @@ class Top(Elaboratable):
|
|||
m = Module()
|
||||
|
||||
m.submodules.clkgen = ClockDomainGenerator()
|
||||
adc_clk = "adclk"
|
||||
dac_clk = "daclk"
|
||||
|
||||
# Submodules.
|
||||
m.submodules.adcdac_intf = adcdac_intf = MAX586xInterface(bb_domain="gck1")
|
||||
m.submodules.adcdac_intf = adcdac_intf = MAX586xInterface(adc_domain=adc_clk, dac_domain=dac_clk)
|
||||
m.submodules.mcu_intf = mcu_intf = SGPIOInterface(sample_width=8, domain="sync")
|
||||
|
||||
m.d.comb += adcdac_intf.q_invert.eq(platform.request("q_invert").i)
|
||||
|
||||
rx_chain = {
|
||||
"dc_block": DCBlock(width=8, num_channels=2, domain="gck1"),
|
||||
"half_prec": DomainRenamer("gck1")(IQHalfPrecisionConverter()),
|
||||
"clkconv": ClockConverter(IQSample(4), 16, "gck1", "sync"),
|
||||
"dc_block": DCBlock(width=8, num_channels=2, domain=adc_clk),
|
||||
"half_prec": DomainRenamer(adc_clk)(IQHalfPrecisionConverter()),
|
||||
"clkconv": ClockConverter(IQSample(4), 16, adc_clk, "sync"),
|
||||
}
|
||||
for k,v in rx_chain.items():
|
||||
m.submodules[f"rx_{k}"] = v
|
||||
|
|
@ -78,8 +80,8 @@ class Top(Elaboratable):
|
|||
|
||||
|
||||
tx_chain = {
|
||||
"clkconv": ClockConverter(IQSample(4), 16, "sync", "gck1", always_ready=False),
|
||||
"half_prec": DomainRenamer("gck1")(IQHalfPrecisionConverterInv()),
|
||||
"clkconv": ClockConverter(IQSample(4), 16, "sync", dac_clk, always_ready=False),
|
||||
"half_prec": DomainRenamer(dac_clk)(IQHalfPrecisionConverterInv()),
|
||||
}
|
||||
for k,v in tx_chain.items():
|
||||
m.submodules[f"tx_{k}"] = v
|
||||
|
|
|
|||
|
|
@ -26,9 +26,11 @@ class Top(Elaboratable):
|
|||
m = Module()
|
||||
|
||||
m.submodules.clkgen = ClockDomainGenerator()
|
||||
adc_clk = "adclk"
|
||||
dac_clk = "daclk"
|
||||
|
||||
# Submodules.
|
||||
m.submodules.adcdac_intf = adcdac_intf = MAX586xInterface(bb_domain="gck1")
|
||||
m.submodules.adcdac_intf = adcdac_intf = MAX586xInterface(adc_domain=adc_clk, dac_domain=dac_clk)
|
||||
m.submodules.mcu_intf = mcu_intf = SGPIOInterface(sample_width=16, domain="sync")
|
||||
|
||||
m.d.comb += adcdac_intf.q_invert.eq(platform.request("q_invert").i)
|
||||
|
|
@ -52,13 +54,13 @@ class Top(Elaboratable):
|
|||
common_rx_filter_opts = dict(
|
||||
data_shape=fixed.SQ(7),
|
||||
always_ready=True,
|
||||
domain="gck1",
|
||||
domain=adc_clk,
|
||||
)
|
||||
|
||||
rx_chain = {
|
||||
# DC block and quarter shift.
|
||||
"dc_block": DCBlock(width=8, num_channels=2, domain="gck1"),
|
||||
"quarter_shift": DomainRenamer("gck1")(QuarterShift()),
|
||||
"dc_block": DCBlock(width=8, num_channels=2, domain=adc_clk),
|
||||
"quarter_shift": DomainRenamer(adc_clk)(QuarterShift()),
|
||||
|
||||
# Half-band decimation stages.
|
||||
"hbfir5": HalfBandDecimator(taps5, **common_rx_filter_opts),
|
||||
|
|
@ -68,7 +70,7 @@ class Top(Elaboratable):
|
|||
"hbfir1": HalfBandDecimator(taps, **common_rx_filter_opts),
|
||||
|
||||
# Clock domain conversion.
|
||||
"clkconv": ClockConverter(IQSample(8), 8, "gck1", "sync"),
|
||||
"clkconv": ClockConverter(IQSample(8), 8, adc_clk, "sync"),
|
||||
}
|
||||
for k,v in rx_chain.items():
|
||||
m.submodules[f"rx_{k}"] = v
|
||||
|
|
@ -82,20 +84,20 @@ class Top(Elaboratable):
|
|||
|
||||
tx_chain = {
|
||||
# Clock domain conversion.
|
||||
"clkconv": ClockConverter(IQSample(8), 8, "sync", "gck1", always_ready=False),
|
||||
"clkconv": ClockConverter(IQSample(8), 8, "sync", dac_clk, always_ready=False),
|
||||
|
||||
# Half-band interpolation stages (+ skid buffers for timing closure).
|
||||
"hbfir1": HalfBandInterpolator(taps, data_shape=fixed.SQ(7),
|
||||
num_channels=2, always_ready=False, domain="gck1"),
|
||||
"skid2": DomainRenamer("gck1")(StreamSkidBuffer(IQSample(8), always_ready=False)),
|
||||
num_channels=2, always_ready=False, domain=dac_clk),
|
||||
"skid2": DomainRenamer(dac_clk)(StreamSkidBuffer(IQSample(8), always_ready=False)),
|
||||
"hbfir2": HalfBandInterpolator(taps2, data_shape=fixed.SQ(7),
|
||||
num_channels=2, always_ready=False, domain="gck1"),
|
||||
"skid3": DomainRenamer("gck1")(StreamSkidBuffer(IQSample(8), always_ready=False)),
|
||||
num_channels=2, always_ready=False, domain=dac_clk),
|
||||
"skid3": DomainRenamer(dac_clk)(StreamSkidBuffer(IQSample(8), always_ready=False)),
|
||||
|
||||
# CIC interpolation stage.
|
||||
"cic_interpolator": CICInterpolator(1, 3, (1, 2, 4, 8), 8, 8, num_channels=2,
|
||||
always_ready=False, domain="gck1"),
|
||||
"skid4": DomainRenamer("gck1")(StreamSkidBuffer(IQSample(8), always_ready=False)),
|
||||
always_ready=False, domain=dac_clk),
|
||||
"skid4": DomainRenamer(dac_clk)(StreamSkidBuffer(IQSample(8), always_ready=False)),
|
||||
}
|
||||
for k,v in tx_chain.items():
|
||||
m.submodules[f"tx_{k}"] = v
|
||||
|
|
@ -106,7 +108,7 @@ class Top(Elaboratable):
|
|||
connect(m, last, block.input)
|
||||
last = block.output
|
||||
# DAC can also be driven with an internal NCO.
|
||||
m.submodules.nco = nco = DomainRenamer("gck1")(NCO(phase_width=16, output_width=8))
|
||||
m.submodules.nco = nco = DomainRenamer(dac_clk)(NCO(phase_width=16, output_width=8))
|
||||
with m.If(nco.en):
|
||||
m.d.comb += [
|
||||
adcdac_intf.dac_stream.p.eq(nco.output),
|
||||
|
|
@ -123,9 +125,10 @@ class Top(Elaboratable):
|
|||
# Add control registers.
|
||||
ctrl = spi_regs.add_register(0x01, init=0)
|
||||
rx_decim = spi_regs.add_register(0x02, init=0, size=3)
|
||||
tx_ctrl = spi_regs.add_register(0x03, init=0, size=1)
|
||||
tx_intrp = spi_regs.add_register(0x04, init=0, size=3)
|
||||
tx_pstep = spi_regs.add_register(0x05, init=0)
|
||||
rx_pstep = spi_regs.add_register(0x03, init=0)
|
||||
tx_ctrl = spi_regs.add_register(0x04, init=0, size=1)
|
||||
tx_intrp = spi_regs.add_register(0x05, init=0, size=3)
|
||||
tx_pstep = spi_regs.add_register(0x06, init=0)
|
||||
|
||||
m.d.sync += [
|
||||
# Trigger enable.
|
||||
|
|
@ -136,8 +139,8 @@ class Top(Elaboratable):
|
|||
|
||||
# RX settings.
|
||||
rx_chain["dc_block"].enable .eq(ctrl[0]),
|
||||
rx_chain["quarter_shift"].enable .eq(ctrl[1]),
|
||||
rx_chain["quarter_shift"].up .eq(ctrl[2]),
|
||||
rx_chain["quarter_shift"].enable .eq(rx_pstep[-2]),
|
||||
rx_chain["quarter_shift"].up .eq(rx_pstep[-1]),
|
||||
|
||||
# RX decimation rate.
|
||||
rx_chain["hbfir5"].enable .eq(rx_decim > 4),
|
||||
|
|
@ -145,19 +148,23 @@ class Top(Elaboratable):
|
|||
rx_chain["hbfir3"].enable .eq(rx_decim > 2),
|
||||
rx_chain["hbfir2"].enable .eq(rx_decim > 1),
|
||||
rx_chain["hbfir1"].enable .eq(rx_decim > 0),
|
||||
]
|
||||
|
||||
# TX interpolation rate.
|
||||
tx_chain["cic_interpolator"].factor .eq(Mux(tx_intrp > 2, tx_intrp - 2, 0)),
|
||||
tx_chain["hbfir1"].enable .eq(tx_intrp > 0),
|
||||
tx_chain["hbfir2"].enable .eq(tx_intrp > 1),
|
||||
# TX interpolation rate.
|
||||
tx_intrp_dacclk = Signal.like(tx_intrp)
|
||||
m.submodules.tx_intrp_cdc = cdc.FFSynchronizer(tx_intrp, tx_intrp_dacclk, o_domain=dac_clk)
|
||||
m.d.comb += [
|
||||
tx_chain["cic_interpolator"].factor .eq(Mux(tx_intrp_dacclk > 2, tx_intrp_dacclk - 2, 0)),
|
||||
tx_chain["hbfir1"].enable .eq(tx_intrp_dacclk > 0),
|
||||
tx_chain["hbfir2"].enable .eq(tx_intrp_dacclk > 1),
|
||||
]
|
||||
|
||||
# TX NCO control.
|
||||
tx_pstep_gck1 = Signal(8)
|
||||
m.submodules.nco_phase_cdc = cdc.FFSynchronizer(tx_pstep, tx_pstep_gck1, o_domain="gck1")
|
||||
m.d.gck1 += [
|
||||
tx_pstep_dacclk = Signal(8)
|
||||
m.submodules.nco_phase_cdc = cdc.FFSynchronizer(tx_pstep, tx_pstep_dacclk, o_domain=dac_clk)
|
||||
m.d[dac_clk] += [
|
||||
nco.en .eq(tx_ctrl[0]),
|
||||
nco.phase .eq(nco.phase + (tx_pstep_gck1 << 6)),
|
||||
nco.phase .eq(nco.phase + (tx_pstep_dacclk << 6)),
|
||||
]
|
||||
|
||||
return m
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue