Source code for gateware.pdq2

# Copyright 2013-2015 Robert Jordens <jordens@gmail.com>
#
# This file is part of pdq2.
#
# pdq2 is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pdq2 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pdq2.  If not, see <http://www.gnu.org/licenses/>.

from migen.fhdl.std import *
from migen.genlib.record import Record
from migen.genlib.resetsync import AsyncResetSynchronizer

from .dac import Dac
from .comm import Comm
from .ft245r import Ft245r_rx, SimFt245r_rx, SimReader


[docs]class Pdq2Base(Module): """PDQ2 Base configuration. Used both in functional simulation and final gateware. Holds the three :mod:`gateware.dac.Dac` and the communication handler :mod:`gateware.comm.Comm`. Args: ctrl_pads (Record): Control pads for :mod:`gateware.comm.Comm`. mem_depth (list[int]): Memory depths for the DAC channels. Attributes: dacs (list): List of :mod:`gateware.dac.Dac`. comm (Module): :mod:`gateware.comm.Comm`. """ def __init__(self, ctrl_pads, mem_depths=(1 << 13, 1 << 13, 1 << 12)): # (1 << 13, (1 << 12) + (1 << 11), (1 << 12) + (1 << 11)) self.dacs = [] for i, depth in enumerate(mem_depths): dac = Dac(mem_depth=depth) setattr(self.submodules, "dac{}".format(i), dac) self.dacs.append(dac) self.submodules.comm = Comm(ctrl_pads, self.dacs)
class Pdq2Sim(Module): ctrl_layout = [ ("adr", 4), ("aux", 1), ("frame", 3), ("trigger", 1), ("reset", 1), ("go2_in", 1), ("go2_out", 1), ] def __init__(self, mem, skip_ft245r=True): self.ctrl_pads = Record(self.ctrl_layout) self.ctrl_pads.adr.reset = 0b1111 self.ctrl_pads.trigger.reset = 1 self.ctrl_pads.frame.reset = 0b000 self.submodules.dut = ResetInserter(["sys"])(Pdq2Base(self.ctrl_pads)) self.comb += self.dut.reset_sys.eq(self.dut.comm.ctrl.reset) if skip_ft245r: reader = SimReader(mem) else: reader = SimFt245r_rx(mem) self.submodules.reader = ResetInserter(["sys"])(reader) self.comb += self.reader.reset_sys.eq(self.dut.comm.ctrl.reset) self.comb += self.dut.comm.sink.connect(self.reader.source) # override high-ack during reset draining the reader self.comb += self.reader.source.ack.eq(self.dut.comm.sink.ack & ~self.dut.comm.ctrl.reset) self.outputs = [] def do_simulation(self, selfp): self.outputs.append([dac.out.data for dac in selfp.dut.dacs]) do_simulation.passive = True
[docs]class CRG(Module): """PDQ2 Clock and Reset generator. Args: platform (Platform): PDQ2 Platform. Attributes: rst (Signal): Reset input. clk_p (Signal): Positive clock output. clk_n (Signal): Negative clock output. dcm_sel (Signal): Select doubled clock. Input. dcm_locked (Signal): DCM locked. Output. cd_sys (ClockDomain): System clock domain driven. """ def __init__(self, platform): self.clock_domains.cd_sys = ClockDomain() self.clk_p = self.cd_sys.clk self.clk_n = Signal() self.rst = Signal() self.dcm_locked = Signal() self.dcm_sel = Signal() clkin = platform.request("clk50") clkin_period = 20. clkin_sdr = Signal() self.specials += Instance("IBUFG", i_I=clkin, o_O=clkin_sdr) dcm_clk2x = Signal() dcm_clk2x180 = Signal() self.specials += Instance("DCM_SP", p_CLKDV_DIVIDE=2, p_CLKFX_DIVIDE=1, p_CLKFX_MULTIPLY=2, p_CLKIN_DIVIDE_BY_2="FALSE", p_CLKIN_PERIOD=clkin_period, #p_CLK_FEEDBACK="2X", p_CLK_FEEDBACK="NONE", p_DLL_FREQUENCY_MODE="LOW", p_DFS_FREQUENCY_MODE="LOW", p_STARTUP_WAIT="TRUE", p_CLKOUT_PHASE_SHIFT="NONE", p_PHASE_SHIFT=0, p_DUTY_CYCLE_CORRECTION="TRUE", i_RST=0, i_PSEN=0, i_PSINCDEC=0, i_PSCLK=0, i_CLKIN=clkin_sdr, o_LOCKED=self.dcm_locked, #o_CLK2X=dcm_clk2x, #o_CLK2X180=dcm_clk2x180, o_CLKFX=dcm_clk2x, o_CLKFX180=dcm_clk2x180, #i_CLKFB=clk_p, i_CLKFB=0, ) self.specials += Instance("BUFGMUX", i_I0=clkin_sdr, i_I1=dcm_clk2x, i_S=self.dcm_sel, o_O=self.clk_p) self.specials += Instance("BUFGMUX", i_I0=~clkin_sdr, i_I1=dcm_clk2x180, i_S=self.dcm_sel, o_O=self.clk_n) self.specials += AsyncResetSynchronizer( self.cd_sys, ~self.dcm_locked | self.rst)
[docs]class Pdq2(Pdq2Base): """PDQ2 Top module. Wires up USB FIFO reader :mod:`gateware.ft245r.Ft345r_rx`, clock and reset generator :mod:`CRG`, and the DAC output signals. Delegates the wiring of the remaining modules to :mod:`Pdq2Base`. ``pads.go2_out`` is assigned the DCM locked signal. Args: platform (Platform): PDQ2 platform. """ def __init__(self, platform): ctrl_pads = platform.request("ctrl") Pdq2Base.__init__(self, ctrl_pads) self.submodules.crg = CRG(platform) comm_pads = platform.request("comm") self.submodules.reader = Ft245r_rx(comm_pads) self.comb += [ self.comm.sink.connect(self.reader.source), self.crg.rst.eq(self.comm.ctrl.reset), ctrl_pads.go2_out.eq(self.crg.dcm_locked), self.crg.dcm_sel.eq(self.comm.ctrl.dcm_sel) ] for i, dac in enumerate(self.dacs): pads = platform.request("dac", i) # inverted clocks ensure setup and hold times of data ce = Signal() d = Signal.like(dac.out.data) self.comb += [ ce.eq(~dac.out.silence), d.eq(~dac.out.data), # pcb inversion ] self.specials += Instance("ODDR2", i_C0=self.crg.clk_p, i_C1=self.crg.clk_n, i_CE=ce, i_D0=0, i_D1=1, i_R=0, i_S=0, o_Q=pads.clk_p) self.specials += Instance("ODDR2", i_C0=self.crg.clk_p, i_C1=self.crg.clk_n, i_CE=ce, i_D0=1, i_D1=0, i_R=0, i_S=0, o_Q=pads.clk_n) dclk = Signal() self.specials += Instance("ODDR2", i_C0=self.crg.clk_p, i_C1=self.crg.clk_n, i_CE=ce, i_D0=0, i_D1=1, i_R=0, i_S=0, o_Q=dclk) self.specials += Instance("OBUFDS", i_I=dclk, o_O=pads.data_clk_p, o_OB=pads.data_clk_n) for i in range(16): self.specials += Instance("OBUFDS", i_I=d[i], o_O=pads.data_p[i], o_OB=pads.data_n[i])