diff --git a/src/firmware/src/main.rs b/src/firmware/src/main.rs index d8bb24d..f9ca7e5 100644 --- a/src/firmware/src/main.rs +++ b/src/firmware/src/main.rs @@ -93,7 +93,8 @@ pub fn main_core0() { let busy = pl::csr::adc::busy_read(); info!("started {}", busy); while pl::csr::adc::busy_read() != 0 {} - info!("done, bus_error={}", pl::csr::adc::bus_error_read()); + info!("done, bus_error={}, overflow={}", + pl::csr::adc::bus_error_read(), pl::csr::adc::overflow_read()); cache::dcci_slice(&BUFFER.data); for i in 0..BUFFER_SIZE { info!("{:02x}", BUFFER.data[i]); diff --git a/src/gateware/dma.py b/src/gateware/dma.py index 8142ad7..97afd12 100644 --- a/src/gateware/dma.py +++ b/src/gateware/dma.py @@ -1,32 +1,34 @@ from migen import * from migen.genlib.fsm import FSM +from migen.genlib.fifo import AsyncFIFOBuffered +from migen.genlib.cdc import MultiReg, PulseSynchronizer from migen_axi.interconnect import axi from misoc.interconnect.csr import * +AXI_ADDRESS_WIDTH = 32 +AXI_DATA_WIDTH = 64 AXI_BURST_LEN = 16 +AXI_ALIGNMENT_BITS = log2_int(AXI_BURST_LEN*AXI_DATA_WIDTH//8) +AXI_ALIGNED_ADDRESS_WIDTH = AXI_ADDRESS_WIDTH - AXI_ALIGNMENT_BITS class BlockAddressGenerator(Module): def __init__(self, membus_stream, data_width): - address_width = len(membus_stream.addr) - alignment_bits = log2_int(AXI_BURST_LEN*data_width//8) - aligned_address_width = address_width - alignment_bits - - self.base_address = Signal(aligned_address_width) - self.length = Signal(aligned_address_width) + self.base_address = Signal(AXI_ALIGNED_ADDRESS_WIDTH) + self.length = Signal(AXI_ALIGNED_ADDRESS_WIDTH) self.start = Signal() self.busy = Signal() - current_address = Signal(aligned_address_width) - remaining = Signal(aligned_address_width) + current_address = Signal(AXI_ALIGNED_ADDRESS_WIDTH) + remaining = Signal(AXI_ALIGNED_ADDRESS_WIDTH) self.comb += [ - membus_stream.addr.eq(Cat(C(0, alignment_bits), current_address)), + membus_stream.addr.eq(Cat(C(0, AXI_ALIGNMENT_BITS), current_address)), membus_stream.id.eq(0), # Same ID for all transactions to forbid reordering. membus_stream.burst.eq(axi.Burst.incr.value), membus_stream.len.eq(AXI_BURST_LEN-1), # Number of transfers in burst (0->1 transfer, 1->2 transfers...). - membus_stream.size.eq(log2_int(data_width//8)), # Width of burst: 3 = 8 bytes = 64 bits. + membus_stream.size.eq(log2_int(AXI_DATA_WIDTH//8)), # Width of burst: 3 = 8 bytes = 64 bits. membus_stream.cache.eq(0xf), ] @@ -52,16 +54,80 @@ class BlockAddressGenerator(Module): ) +def convert_signal_endianness(signal): + assert len(signal) % 8 == 0 + nbytes = len(signal)//8 + signal_bytes = [] + for i in range(nbytes): + signal_bytes.append(signal[8*i:8*(i+1)]) + return Cat(*reversed(signal_bytes)) + + class ADCWriter(Module): - def __init__(self, membus_stream, data_width, pads): + def __init__(self, membus_stream, pads): + self.length = Signal(AXI_ALIGNED_ADDRESS_WIDTH) # in AXI bursts + self.start = Signal() + self.overflow = Signal() self.busy = Signal() + fifo = ClockDomainsRenamer({"write": "adc", "read": "sys"})(AsyncFIFOBuffered(64, 512)) + self.submodules += fifo + + # FIFO write + adc_a = Signal(16) + adc_b = Signal(16) + self.sync.adc += [ + #adc_a.eq(pads.data_a), + #adc_b.eq(pads.data_b), + adc_a.eq(0x7842), + adc_b.eq(0xdf86), + ] + + fifo_inbuf = Signal(64) + fifo_inbuf_sel = Signal() + self.sync.adc += [ + fifo_inbuf_sel.eq(~fifo_inbuf_sel), + If(fifo_inbuf_sel, + fifo_inbuf[32:].eq(Cat(adc_a, adc_b)), + ).Else( + fifo_inbuf[:32].eq(Cat(adc_a, adc_b)), + ) + ] + self.comb += fifo.din.eq(fifo_inbuf), + + length = Signal(AXI_ALIGNED_ADDRESS_WIDTH) + start = Signal() + + assert AXI_DATA_WIDTH == len(fifo_inbuf) + remaining = Signal(AXI_ADDRESS_WIDTH - log2_int(AXI_DATA_WIDTH//8)) # in AXI_DATA_WIDTH words + self.sync.adc += [ + If(remaining != 0, remaining.eq(remaining - 1)), + If(start, remaining.eq(length << log2_int(AXI_BURST_LEN))), + ] + self.comb += fifo.we.eq((remaining != 0) & ~fifo_inbuf_sel) + + overflow = Signal() + self.comb += overflow.eq(fifo.we & ~fifo.writable) + + # control CDC + self.specials += MultiReg(self.length, length, "adc") + ps_start = PulseSynchronizer("sys", "adc") + ps_overflow = PulseSynchronizer("adc", "sys") + self.submodules += ps_start, ps_overflow + self.comb += [ + ps_start.i.eq(self.start), + start.eq(ps_start.o), + ps_overflow.i.eq(overflow), + self.overflow.eq(ps_overflow.o) + ] + + # FIFO read self.comb += [ membus_stream.id.eq(0), - membus_stream.valid.eq(1), - #self.sink.ack.eq(membus.w.ready), - membus_stream.data.eq(0x12345678deadbeef), - membus_stream.strb.eq(2**(data_width//8)-1), + membus_stream.valid.eq(fifo.readable), + fifo.re.eq(membus_stream.ready), + membus_stream.data.eq(convert_signal_endianness(fifo.dout)), + membus_stream.strb.eq(2**(AXI_DATA_WIDTH//8)-1), ] beat_count = Signal(max=AXI_BURST_LEN) self.sync += [ @@ -76,20 +142,25 @@ class ADCWriter(Module): ) ] + # Busy generation + remaining_sys = Signal(AXI_ADDRESS_WIDTH - log2_int(AXI_DATA_WIDTH//8)) + self.sync += [ + If(self.start, remaining_sys.eq(self.length << log2_int(AXI_BURST_LEN))), + If(fifo.readable & fifo.re, remaining_sys.eq(remaining_sys - 1)) + ] + self.comb += self.busy.eq(remaining_sys != 0) + class ADC(Module, AutoCSR): def __init__(self, membus, pads): - data_width = len(membus.w.data) - address_width = len(membus.aw.addr) - alignment_bits = log2_int(AXI_BURST_LEN*data_width//8) - - self.base_address = CSRStorage(address_width, alignment_bits=alignment_bits) - self.length = CSRStorage(address_width, alignment_bits=alignment_bits) + self.base_address = CSRStorage(AXI_ADDRESS_WIDTH, alignment_bits=AXI_ALIGNMENT_BITS) + self.length = CSRStorage(AXI_ADDRESS_WIDTH, alignment_bits=AXI_ALIGNMENT_BITS) self.start = CSR() self.busy = CSRStatus() self.bus_error = CSRStatus() - - address_generator = BlockAddressGenerator(membus.aw, data_width) + self.overflow = CSRStatus() + + address_generator = BlockAddressGenerator(membus.aw, AXI_DATA_WIDTH) self.submodules += address_generator self.comb += [ address_generator.base_address.eq(self.base_address.storage), @@ -97,8 +168,16 @@ class ADC(Module, AutoCSR): address_generator.start.eq(self.start.re) ] - adc_writer = ADCWriter(membus.w, data_width, pads) + adc_writer = ADCWriter(membus.w, pads) self.submodules += adc_writer + self.comb += [ + adc_writer.length.eq(self.length.storage), + adc_writer.start.eq(self.start.re) + ] + self.sync += [ + If(self.start.re, self.overflow.status.eq(0)), + If(adc_writer.overflow, self.overflow.status.eq(1)) + ] self.comb += self.busy.status.eq(address_generator.busy | adc_writer.busy) diff --git a/src/gateware/rust-pitaya.py b/src/gateware/rust-pitaya.py index 4d0bb4c..8444e3b 100755 --- a/src/gateware/rust-pitaya.py +++ b/src/gateware/rust-pitaya.py @@ -5,6 +5,7 @@ from operator import itemgetter from migen import * from migen.build.generic_platform import * +from migen.genlib.cdc import AsyncResetSynchronizer from migen_axi.integration.soc_core import SoCCore from migen_axi.platforms import redpitaya from misoc.integration import cpu_interface @@ -24,8 +25,25 @@ class RustPitaya(SoCCore): platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]") platform.add_platform_command("set_input_jitter clk_fpga_0 0.24") + # ADC clocking + clk125_pads = platform.request("clk125") + platform.add_platform_command("create_clock -name clk_adc -period 8 [get_ports {port}]", port=clk125_pads.p) + self.clock_domains.cd_adc = ClockDomain() + self.specials += [ + Instance("IBUFGDS", i_I=clk125_pads.p, i_IB=clk125_pads.n, + o_O=self.cd_adc.clk), + AsyncResetSynchronizer(self.cd_adc, ResetSignal()), + ] adc_pads = platform.request("adc") + self.specials += [ + Instance("ODDR", o_Q=adc_pads.clk[0], i_D1=1, i_D2=0, i_CE=1, i_C=self.cd_adc.clk), + Instance("ODDR", o_Q=adc_pads.clk[1], i_D1=0, i_D2=1, i_CE=1, i_C=self.cd_adc.clk), + ] + self.comb += adc_pads.cdcs.eq(1) + for port in adc_pads.data_a, adc_pads.data_b: + platform.add_platform_command("set_input_delay -max 3.400 -clock clk_adc [get_ports {port}[*]]", port=port) + # ADC DMA self.submodules.adc = dma.ADC(self.ps7.s_axi_hp0, adc_pads) self.csr_devices.append("adc")