forked from M-Labs/artiq
phaser: add trf, duc, interfaces, redo body assembly, use more natrual iq ordering (i lsb)
This commit is contained in:
parent
e5e2392240
commit
c10ac2c92a
|
@ -42,6 +42,13 @@ PHASER_SEL_TRF1 = 1 << 2
|
||||||
PHASER_SEL_ATT0 = 1 << 3
|
PHASER_SEL_ATT0 = 1 << 3
|
||||||
PHASER_SEL_ATT1 = 1 << 4
|
PHASER_SEL_ATT1 = 1 << 4
|
||||||
|
|
||||||
|
PHASER_STA_DAC_ALARM = 1 << 0
|
||||||
|
PHASER_STA_TRF0_LD = 1 << 1
|
||||||
|
PHASER_STA_TRF1_LD = 1 << 2
|
||||||
|
PHASER_STA_TERM0 = 1 << 3
|
||||||
|
PHASER_STA_TERM1 = 1 << 4
|
||||||
|
PHASER_STA_SPI_IDLE = 1 << 5
|
||||||
|
|
||||||
|
|
||||||
class Phaser:
|
class Phaser:
|
||||||
kernel_invariants = {"core", "channel_base", "t_frame"}
|
kernel_invariants = {"core", "channel_base", "t_frame"}
|
||||||
|
@ -57,23 +64,23 @@ class Phaser:
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def init(self):
|
def init(self):
|
||||||
board_id = self.read(PHASER_ADDR_BOARD_ID)
|
board_id = self.read8(PHASER_ADDR_BOARD_ID)
|
||||||
if board_id != PHASER_BOARD_ID:
|
if board_id != PHASER_BOARD_ID:
|
||||||
raise ValueError("invalid board id")
|
raise ValueError("invalid board id")
|
||||||
delay(20*us)
|
delay(20*us)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def write(self, addr, data):
|
def write8(self, addr, data):
|
||||||
"""Write data to a Phaser FPGA register.
|
"""Write data to a Phaser FPGA register.
|
||||||
|
|
||||||
:param addr: Address to write to.
|
:param addr: Address to write to.
|
||||||
:param data: Data to write.
|
:param data: Data to write.
|
||||||
"""
|
"""
|
||||||
rtio_output((self.channel_base << 8) | addr | 0x80, data)
|
rtio_output((self.channel_base << 8) | (addr & 0x7f) | 0x80, data)
|
||||||
delay_mu(int64(self.t_frame))
|
delay_mu(int64(self.t_frame))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def read(self, addr):
|
def read8(self, addr) -> TInt32:
|
||||||
"""Read from Phaser FPGA register.
|
"""Read from Phaser FPGA register.
|
||||||
|
|
||||||
TODO: untested
|
TODO: untested
|
||||||
|
@ -81,40 +88,65 @@ class Phaser:
|
||||||
:param addr: Address to read from.
|
:param addr: Address to read from.
|
||||||
:return: The data read.
|
:return: The data read.
|
||||||
"""
|
"""
|
||||||
rtio_output((self.channel_base << 8) | addr, 0)
|
rtio_output((self.channel_base << 8) | (addr & 0x7f), 0)
|
||||||
response = rtio_input_data(self.channel_base)
|
response = rtio_input_data(self.channel_base)
|
||||||
return response >> self.miso_delay
|
return response >> self.miso_delay
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def write32(self, addr, data: TInt32):
|
||||||
|
for offset in range(4):
|
||||||
|
byte = data >> 24
|
||||||
|
self.write8(addr + offset, byte)
|
||||||
|
data <<= 8
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def read32(self, addr) -> TInt32:
|
||||||
|
data = 0
|
||||||
|
for offset in range(4):
|
||||||
|
data <<= 8
|
||||||
|
data |= self.read8(addr + offset)
|
||||||
|
delay(20*us) # slack
|
||||||
|
return data
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def write16(self, addr, data: TInt32):
|
||||||
|
self.write8(addr, data >> 8)
|
||||||
|
self.write8(addr + 1, data)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def read16(self, addr) -> TInt32:
|
||||||
|
return (self.read8(addr) << 8) | self.read8(addr)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_leds(self, leds):
|
def set_leds(self, leds):
|
||||||
self.write(PHASER_ADDR_LED, leds)
|
self.write8(PHASER_ADDR_LED, leds)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_fan(self, duty):
|
def set_fan(self, duty):
|
||||||
self.write(PHASER_ADDR_FAN, duty)
|
self.write8(PHASER_ADDR_FAN, duty)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_cfg(self, clk_sel=0, dac_resetb=1, dac_sleep=0, dac_txena=1,
|
def set_cfg(self, clk_sel=0, dac_resetb=1, dac_sleep=0, dac_txena=1,
|
||||||
trf0_ps=0, trf1_ps=0, att0_rstn=1, att1_rstn=1):
|
trf0_ps=0, trf1_ps=0, att0_rstn=1, att1_rstn=1):
|
||||||
self.write(PHASER_ADDR_CFG,
|
self.write8(PHASER_ADDR_CFG,
|
||||||
(clk_sel << 0) | (dac_resetb << 1) | (dac_sleep << 2) |
|
(clk_sel << 0) | (dac_resetb << 1) | (dac_sleep << 2) |
|
||||||
(dac_txena << 3) | (trf0_ps << 4) | (trf1_ps << 5) |
|
(dac_txena << 3) | (trf0_ps << 4) | (trf1_ps << 5) |
|
||||||
(att0_rstn << 6) | (att1_rstn << 7))
|
(att0_rstn << 6) | (att1_rstn << 7))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def get_sta(self):
|
def get_sta(self):
|
||||||
return self.read(PHASER_ADDR_STA)
|
return self.read8(PHASER_ADDR_STA)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def get_crc_err(self):
|
def get_crc_err(self):
|
||||||
return self.read(PHASER_ADDR_CRC_ERR)
|
return self.read8(PHASER_ADDR_CRC_ERR)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def get_dac_data(self, ch) -> TInt32:
|
def get_dac_data(self, ch) -> TInt32:
|
||||||
data = 0
|
data = 0
|
||||||
for addr in range(4):
|
for addr in range(4):
|
||||||
data <<= 8
|
data <<= 8
|
||||||
data |= self.read(PHASER_ADDR_DAC0_DATA + (ch << 4) + addr)
|
data |= self.read8(PHASER_ADDR_DAC0_DATA + (ch << 4) + addr)
|
||||||
delay(20*us) # slack
|
delay(20*us) # slack
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@ -122,32 +154,43 @@ class Phaser:
|
||||||
def set_dac_test(self, ch, data: TInt32):
|
def set_dac_test(self, ch, data: TInt32):
|
||||||
for addr in range(4):
|
for addr in range(4):
|
||||||
byte = (data >> 24) & 0xff
|
byte = (data >> 24) & 0xff
|
||||||
self.write(PHASER_ADDR_DAC0_TEST + (ch << 4) + addr, byte)
|
self.write8(PHASER_ADDR_DAC0_TEST + (ch << 4) + addr, byte)
|
||||||
data <<= 8
|
data <<= 8
|
||||||
return data
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_duc_cfg(self, ch, clr=0, clr_once=0, select=0):
|
def set_duc_cfg(self, ch, clr=0, clr_once=0, select=0):
|
||||||
self.write(PHASER_ADDR_DUC0_CFG + (ch << 4),
|
self.write8(PHASER_ADDR_DUC0_CFG + (ch << 4),
|
||||||
(clr << 0) | (clr_once << 1) | (select << 2))
|
(clr << 0) | (clr_once << 1) | (select << 2))
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_duc_frequency_mu(self, ch, ftw):
|
||||||
|
self.write32(PHASER_ADDR_DUC0_F + (ch << 4), ftw)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_duc_phase_mu(self, ch, pow):
|
||||||
|
self.write16(PHASER_ADDR_DUC0_P + (ch << 4), pow)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def duc_stb(self):
|
||||||
|
self.write8(PHASER_ADDR_DUC_STB, 0)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def spi_cfg(self, select, div, end, clk_phase=0, clk_polarity=0,
|
def spi_cfg(self, select, div, end, clk_phase=0, clk_polarity=0,
|
||||||
half_duplex=0, lsb_first=0, offline=0):
|
half_duplex=0, lsb_first=0, offline=0):
|
||||||
self.write(PHASER_ADDR_SPI_SEL, select)
|
self.write8(PHASER_ADDR_SPI_SEL, select)
|
||||||
self.write(PHASER_ADDR_SPI_DIV, div)
|
self.write8(PHASER_ADDR_SPI_DIV, div)
|
||||||
self.write(PHASER_ADDR_SPI_CFG,
|
self.write8(PHASER_ADDR_SPI_CFG,
|
||||||
(offline << 0) | (end << 1) | (clk_phase << 2) |
|
(offline << 0) | (end << 1) | (clk_phase << 2) |
|
||||||
(clk_polarity << 3) | (half_duplex << 4) |
|
(clk_polarity << 3) | (half_duplex << 4) |
|
||||||
(lsb_first << 5))
|
(lsb_first << 5))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def spi_write(self, data):
|
def spi_write(self, data):
|
||||||
self.write(PHASER_ADDR_SPI_DATW, data)
|
self.write8(PHASER_ADDR_SPI_DATW, data)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def spi_read(self):
|
def spi_read(self):
|
||||||
return self.read(PHASER_ADDR_SPI_DATR)
|
return self.read8(PHASER_ADDR_SPI_DATR)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def dac_write(self, addr, data):
|
def dac_write(self, addr, data):
|
||||||
|
@ -194,12 +237,45 @@ class Phaser:
|
||||||
self.spi_write(0)
|
self.spi_write(0)
|
||||||
delay_mu(t_xfer)
|
delay_mu(t_xfer)
|
||||||
data = self.spi_read()
|
data = self.spi_read()
|
||||||
delay(10*us)
|
delay(10*us) # slack
|
||||||
self.spi_cfg(select=PHASER_SEL_ATT0 << ch, div=div, end=1)
|
self.spi_cfg(select=PHASER_SEL_ATT0 << ch, div=div, end=1)
|
||||||
self.spi_write(data)
|
self.spi_write(data)
|
||||||
delay_mu(t_xfer)
|
delay_mu(t_xfer)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def trf_write(self, ch, data, readback=False):
|
||||||
|
div = 30 # 50 ns min period
|
||||||
|
t_xfer = self.core.seconds_to_mu((8 + 1)*(div + 2)*4*ns)
|
||||||
|
read = 0
|
||||||
|
if readback:
|
||||||
|
clk_phase = 1
|
||||||
|
else:
|
||||||
|
clk_phase = 0
|
||||||
|
end = 0
|
||||||
|
for i in range(4):
|
||||||
|
if i == 0 or i == 3:
|
||||||
|
if i == 3:
|
||||||
|
end = 1
|
||||||
|
self.spi_cfg(select=PHASER_SEL_TRF0 << ch, div=div,
|
||||||
|
lsb_first=1, clk_phase=clk_phase, end=end)
|
||||||
|
self.spi_write(data & 0xff)
|
||||||
|
data >>= 8
|
||||||
|
delay_mu(t_xfer)
|
||||||
|
if readback:
|
||||||
|
read >>= 8
|
||||||
|
read |= self.spi_read() << 24
|
||||||
|
delay(10*us) # slack
|
||||||
|
return read
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def trf_read(self, ch, addr, cnt_mux_sel=0) -> TInt32:
|
||||||
|
self.trf_write(ch, 0x80000008 | (addr << 28) | (cnt_mux_sel << 27))
|
||||||
|
# single clk pulse to start readback
|
||||||
|
self.spi_cfg(select=PHASER_SEL_TRF0 << ch, div=30, end=1, clk_polarity=1)
|
||||||
|
self.spi_cfg(select=PHASER_SEL_TRF0 << ch, div=30, end=1, clk_polarity=0)
|
||||||
|
return self.trf_write(ch, 0x00000008, readback=True)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_frequency_mu(self, ch, osc, ftw):
|
def set_frequency_mu(self, ch, osc, ftw):
|
||||||
addr = ((self.channel_base + 1 + ch) << 8) | (osc << 1)
|
addr = ((self.channel_base + 1 + ch) << 8) | (osc << 1)
|
||||||
|
|
|
@ -37,15 +37,13 @@ class Phaser(Module):
|
||||||
n_channels = 2
|
n_channels = 2
|
||||||
n_samples = 8
|
n_samples = 8
|
||||||
n_bits = 14
|
n_bits = 14
|
||||||
body = [Signal(n_channels*2*n_bits, reset_less=True)
|
body = Signal(n_samples*n_channels*2*n_bits, reset_less=True)
|
||||||
for i in range(n_samples)]
|
|
||||||
i_sample = Signal(max=n_samples)
|
|
||||||
self.sync.rio_phy += [
|
self.sync.rio_phy += [
|
||||||
If(self.ch0.dds.valid, # & self.ch1.dds.valid,
|
If(self.ch0.dds.valid, # & self.ch1.dds.valid,
|
||||||
Array(body)[i_sample].eq(Cat(
|
# recent sample, ch0, i first
|
||||||
self.ch0.dds.o.q[2:], self.ch0.dds.o.i[2:],
|
Cat(body).eq(Cat(self.ch0.dds.o.i[2:], self.ch0.dds.o.q[2:],
|
||||||
self.ch1.dds.o.q[2:], self.ch1.dds.o.i[2:])),
|
self.ch1.dds.o.i[2:], self.ch1.dds.o.q[2:],
|
||||||
i_sample.eq(i_sample + 1),
|
body)),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -57,11 +55,6 @@ class Phaser(Module):
|
||||||
Cat(self.intf.data[:-1]).eq(Cat(self.serializer.data[:-1])),
|
Cat(self.intf.data[:-1]).eq(Cat(self.serializer.data[:-1])),
|
||||||
self.serializer.data[-1].eq(self.intf.data[-1]),
|
self.serializer.data[-1].eq(self.intf.data[-1]),
|
||||||
]
|
]
|
||||||
self.sync.rio_phy += [
|
|
||||||
If(self.serializer.stb,
|
|
||||||
i_sample.eq(0),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
header = Record([
|
header = Record([
|
||||||
("we", 1),
|
("we", 1),
|
||||||
|
|
Loading…
Reference in New Issue