From cd9590503cab25b66084caad7bb615a84f01849a Mon Sep 17 00:00:00 2001 From: Florian Agbuya Date: Wed, 28 Feb 2024 14:47:38 +0800 Subject: [PATCH] add fast-servo gateware support files --- fast-servo/linien-gateware/README.md | 5 + fast-servo/linien-gateware/__init__.py | 3 + fast-servo/linien-gateware/cores/__init__.py | 0 fast-servo/linien-gateware/cores/adc.py | 179 ++++++++ fast-servo/linien-gateware/cores/dac.py | 84 ++++ fast-servo/linien-gateware/cores/pitaya_ps.py | 385 ++++++++++++++++++ fast-servo/linien-gateware/cores/ps7.py | 193 +++++++++ fast-servo/linien-gateware/cores/spi_phy.py | 141 +++++++ .../linien-gateware/fast_servo_platform.py | 336 +++++++++++++++ fast-servo/linien-gateware/fast_servo_soc.py | 290 +++++++++++++ fast-servo/linien-gateware/verilog/LTC2195.v | 162 ++++++++ .../linien-gateware/verilog/axi_slave.v | 265 ++++++++++++ .../linien-gateware/verilog/bus_clk_bridge.v | 128 ++++++ .../linien-gateware/verilog/spi2threewire.v | 111 +++++ 14 files changed, 2282 insertions(+) create mode 100644 fast-servo/linien-gateware/README.md create mode 100644 fast-servo/linien-gateware/__init__.py create mode 100644 fast-servo/linien-gateware/cores/__init__.py create mode 100644 fast-servo/linien-gateware/cores/adc.py create mode 100644 fast-servo/linien-gateware/cores/dac.py create mode 100644 fast-servo/linien-gateware/cores/pitaya_ps.py create mode 100644 fast-servo/linien-gateware/cores/ps7.py create mode 100644 fast-servo/linien-gateware/cores/spi_phy.py create mode 100644 fast-servo/linien-gateware/fast_servo_platform.py create mode 100644 fast-servo/linien-gateware/fast_servo_soc.py create mode 100644 fast-servo/linien-gateware/verilog/LTC2195.v create mode 100644 fast-servo/linien-gateware/verilog/axi_slave.v create mode 100644 fast-servo/linien-gateware/verilog/bus_clk_bridge.v create mode 100644 fast-servo/linien-gateware/verilog/spi2threewire.v diff --git a/fast-servo/linien-gateware/README.md b/fast-servo/linien-gateware/README.md new file mode 100644 index 0000000..99afeb0 --- /dev/null +++ b/fast-servo/linien-gateware/README.md @@ -0,0 +1,5 @@ +## Source Repository +Files in this directory were copied from [elhep/Fast-Servo-Frimware](https://github.com/elhep/Fast-Servo-Firmware/tree/master/fast_servo/gateware). + +## Commit ID +The files were copied from commit ID [7fae40c](https://github.com/elhep/Fast-Servo-Firmware/commit/7fae40c0f872a91218be378f8289b98b1e366729). \ No newline at end of file diff --git a/fast-servo/linien-gateware/__init__.py b/fast-servo/linien-gateware/__init__.py new file mode 100644 index 0000000..4e9697a --- /dev/null +++ b/fast-servo/linien-gateware/__init__.py @@ -0,0 +1,3 @@ +import os + +verilog_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "verilog") diff --git a/fast-servo/linien-gateware/cores/__init__.py b/fast-servo/linien-gateware/cores/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fast-servo/linien-gateware/cores/adc.py b/fast-servo/linien-gateware/cores/adc.py new file mode 100644 index 0000000..75d7837 --- /dev/null +++ b/fast-servo/linien-gateware/cores/adc.py @@ -0,0 +1,179 @@ +# This file is part of Fast Servo Software Package. +# +# Copyright (C) 2023 Jakub Matyas +# Warsaw University of Technology +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program 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. +# +# This program 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 this program. If not, see . + +from migen import * +from migen.genlib.cdc import MultiReg +from misoc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage + + +class _CRG(Module): + def __init__(self, platform, dco_clk, dco_freq=200e6): + self.clock_domains.cd_dco = ClockDomain() + self.clock_domains.cd_dco2x = ClockDomain() + self.clock_domains.cd_dco2d = ClockDomain() + dco_clk_p, dco_clk_n = dco_clk + + dco_clk_buf = Signal() + self.specials += Instance( + "IBUFGDS", i_I=dco_clk_p, i_IB=dco_clk_n, o_O=dco_clk_buf + ) + + # # # + clk_feedback = Signal() + clk_feedback_buf = Signal() + + clk_dco = Signal() + clk_dco2x = Signal() + clk_dco2d = Signal() + self.locked = Signal() + + platform.add_period_constraint(dco_clk_p, 1e9 / dco_freq) + self.specials += [ + Instance( + "PLLE2_BASE", + p_BANDWIDTH="OPTIMIZED", + p_DIVCLK_DIVIDE=1, + p_CLKFBOUT_PHASE=0.0, + p_CLKFBOUT_MULT=4, # VCO @ 800 MHz + p_CLKIN1_PERIOD=(1e9 / dco_freq), + p_REF_JITTER1=0.01, + p_STARTUP_WAIT="FALSE", + i_CLKIN1=dco_clk_buf, + i_PWRDWN=0, + i_RST=ResetSignal("sys"), + i_CLKFBIN=clk_feedback_buf, + o_CLKFBOUT=clk_feedback, + p_CLKOUT0_DIVIDE=4, + p_CLKOUT0_PHASE=0.0, + p_CLKOUT0_DUTY_CYCLE=0.5, + o_CLKOUT0=clk_dco, # 200 MHz <- dco_clk + p_CLKOUT1_DIVIDE=2, + p_CLKOUT1_PHASE=0.0, + p_CLKOUT1_DUTY_CYCLE=0.5, + o_CLKOUT1=clk_dco2x, # 400 MHZ <- 2 * dco_clk = 2*200 MHz + p_CLKOUT2_DIVIDE=8, + p_CLKOUT2_PHASE=0.0, + p_CLKOUT2_DUTY_CYCLE=0.5, + o_CLKOUT2=clk_dco2d, # 100 MHz <- dco_clk / 2 = 200 MHz / 2 + o_LOCKED=self.locked, + ) + ] + + self.specials += Instance("BUFG", i_I=clk_feedback, o_O=clk_feedback_buf) + self.specials += Instance("BUFG", i_I=clk_dco, o_O=self.cd_dco.clk) + self.specials += Instance("BUFG", i_I=clk_dco2d, o_O=self.cd_dco2d.clk) + self.specials += Instance("BUFG", i_I=clk_dco2x, o_O=self.cd_dco2x.clk) + + +class ADC(Module, AutoCSR): + def __init__(self, platform, dco_freq=200e6): + adc_pads = platform.request("adc") + afe_pads = platform.request("adc_afe") + + self.frame_csr = CSRStatus(4) + self.data_ch0 = CSRStatus(16) + self.data_ch1 = CSRStatus(16) + + self.tap_delay = CSRStorage(5) + self.bitslip_csr = CSRStorage(1) + + self.afe_ctrl = CSRStorage(4) + + tap_delay_val = Signal(5) + bitslip = Signal() + bitslip_re_dco_2d = Signal() + + ch1_gain_x10 = Signal() + ch2_gain_x10 = Signal() + ch1_shdn = Signal() + ch2_shdn = Signal() + + self.data_out = [Signal(16, reset_less=True), Signal(16, reset_less=True)] + self.s_frame = Signal(4) + + ### + + # DCO clock coming from LTC2195 + # dco_clk = Record([("p", 1), ("n", 1)]) + dco_clk =(adc_pads.dco_p, adc_pads.dco_n) + self.comb += [ + # dco_clk.p.eq(adc_pads.dco_p), + # dco_clk.n.eq(adc_pads.dco_n), + tap_delay_val.eq(self.tap_delay.storage), + Cat(ch1_gain_x10, ch2_gain_x10, ch1_shdn, ch2_shdn).eq( + self.afe_ctrl.storage + ), + ] + + self.submodules._crg = _CRG(platform, dco_clk, dco_freq) + + self.specials += MultiReg(self.bitslip_csr.re, bitslip_re_dco_2d, "dco2d") + self.sync.dco2d += [ + bitslip.eq(Mux(bitslip_re_dco_2d, self.bitslip_csr.storage, 0)) + ] + + self.comb += [ + self.frame_csr.status.eq(self.s_frame), + self.data_ch0.status.eq(self.data_out[0]), + self.data_ch1.status.eq(self.data_out[1]), + ] + + self.comb += [ + afe_pads.ch1_gain.eq(ch1_gain_x10), + afe_pads.ch2_gain.eq(ch2_gain_x10), + afe_pads.nshdn_ch1.eq(~ch1_shdn), + afe_pads.nshdn_ch2.eq(~ch2_shdn), + ] + + dummy = Signal(8) + dummy_idelay_rdy = Signal() + + self.specials += Instance( + "LTC2195", + i_rst_in=ResetSignal("sys"), + i_clk200=ClockSignal("idelay"), + i_DCO=ClockSignal("dco"), + i_DCO_2D=ClockSignal("dco2d"), + i_FR_in_p=adc_pads.frame_p, + i_FR_in_n=adc_pads.frame_n, + i_D0_in_p=adc_pads.data0_p, + i_D0_in_n=adc_pads.data0_n, + i_D1_in_p=adc_pads.data1_p, + i_D1_in_n=adc_pads.data1_n, + i_bitslip=bitslip, + i_delay_val=tap_delay_val, + o_ADC0_out=self.data_out[1], # LANES swapped on hardware + o_ADC1_out=self.data_out[0], + o_FR_out=self.s_frame, + o_o_data_from_pins=dummy, + o_idelay_rdy=dummy_idelay_rdy, + ) + + +class AUX_ADC_CTRL(Module, AutoCSR): + def __init__(self, platform): + adc_aux_pads = platform.request("aux_adc") + + self.adc_aux_ctrl = CSRStorage(5) + + self.comb += [ + adc_aux_pads.diff_n.eq(~self.adc_aux_ctrl.storage[0]), + adc_aux_pads.a.eq(self.adc_aux_ctrl.storage[1:4]), + adc_aux_pads.range.eq(self.adc_aux_ctrl.storage[4]), + ] diff --git a/fast-servo/linien-gateware/cores/dac.py b/fast-servo/linien-gateware/cores/dac.py new file mode 100644 index 0000000..9090ad4 --- /dev/null +++ b/fast-servo/linien-gateware/cores/dac.py @@ -0,0 +1,84 @@ +# This file is part of Fast Servo Software Package. +# +# Copyright (C) 2023 Jakub Matyas +# Warsaw University of Technology +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program 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. +# +# This program 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 this program. If not, see . + +from migen import * +from misoc.interconnect.csr import AutoCSR, CSRStorage +from migen.genlib.io import DDROutput + + +class DAC(Module, AutoCSR): + def __init__(self, platform): + dac_pads = platform.request("dac") + dac_afe_pads = platform.request("dac_afe") + self.dac_ctrl = CSRStorage(3) + self.output_value_ch0 = CSRStorage(14) + self.output_value_ch1 = CSRStorage(14) + + manual_override = Signal() + ch0_pd = Signal() + ch1_pd = Signal() + + output_data_ch0 = Signal(14) + output_data_ch1 = Signal(14) + + self.data_in = [Signal(14, reset_less=True), Signal(14, reset_less=True)] + platform.add_period_constraint(dac_pads.dclkio, 10.0) + + self.comb += [ + Cat(manual_override, ch0_pd, ch1_pd).eq(self.dac_ctrl.storage), + dac_pads.rst.eq(ResetSignal("sys")), + dac_afe_pads.ch1_pd_n.eq(~ch0_pd), + dac_afe_pads.ch2_pd_n.eq(~ch1_pd), + output_data_ch0.eq( + Mux(manual_override, self.output_value_ch0.storage, self.data_in[0]) + ), + output_data_ch1.eq( + Mux(manual_override, self.output_value_ch1.storage, self.data_in[1]) + ), + ] + + # data + for lane in range(14): + self.specials += DDROutput( + i1 = output_data_ch0[lane], + i2 = output_data_ch1[lane], + o = dac_pads.data[lane], + clk = ClockSignal("dco2d") + ) + + # clock forwarding + self.specials += DDROutput( + i1 = 0b0, + i2 = 0b1, + o = dac_pads.dclkio, + clk = ClockSignal("dco2d"), + ) + + +class AUX_DAC_CTRL(Module, AutoCSR): + def __init__(self, platform): + dac_aux_pads = platform.request("aux_dac") + + self.dac_aux_ctrl = CSRStorage(3) + + self.comb += [ + dac_aux_pads.nclr.eq(~self.dac_aux_ctrl.storage[0]), + dac_aux_pads.bin.eq(self.dac_aux_ctrl.storage[1]), + dac_aux_pads.nldac.eq(~self.dac_aux_ctrl.storage[2]), + ] diff --git a/fast-servo/linien-gateware/cores/pitaya_ps.py b/fast-servo/linien-gateware/cores/pitaya_ps.py new file mode 100644 index 0000000..33b91f6 --- /dev/null +++ b/fast-servo/linien-gateware/cores/pitaya_ps.py @@ -0,0 +1,385 @@ +# Copyright 2014-2015 Robert Jördens +# Copyright 2018-2022 Benjamin Wiegand +# Copyright 2021-2022 Bastian Leykauf +# Copyright 2023 Jakub Matyas +# Warsaw University of Technology +# +# This file is part of Fast Servo Software Package and is based on Linien. +# +# Linien 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. +# +# Linien 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 Linien. If not, see . + +from functools import reduce +from operator import or_ + +from migen import ( + DIR_M_TO_S, + DIR_S_TO_M, + Cat, + ClockSignal, + If, + Instance, + Module, + Record, + Replicate, + ResetSignal, + Signal, +) +from misoc.interconnect import csr_bus, wishbone + +sys_layout = [ + ("rstn", 1, DIR_M_TO_S), + ("clk", 1, DIR_M_TO_S), + ("addr", 32, DIR_M_TO_S), + ("wdata", 32, DIR_M_TO_S), + ("sel", 4, DIR_M_TO_S), + ("wen", 1, DIR_M_TO_S), + ("ren", 1, DIR_M_TO_S), + ("rdata", 32, DIR_S_TO_M), + ("err", 1, DIR_S_TO_M), + ("ack", 1, DIR_S_TO_M), +] + + +axi_layout = [ + ("arvalid", 1), + ("awvalid", 1), + ("bready", 1), + ("rready", 1), + ("wlast", 1), + ("wvalid", 1), + ("arid", 12), + ("awid", 12), + ("wid", 12), + ("arburst", 2), + ("arlock", 2), + ("arsize", 3), + ("awburst", 2), + ("awlock", 2), + ("awsize", 3), + ("arprot", 3), + ("awprot", 3), + ("araddr", 32), + ("awaddr", 32), + ("wdata", 32), + ("arcache", 4), + ("arlen", 4), + ("arqos", 4), + ("awcache", 4), + ("awlen", 4), + ("awqos", 4), + ("wstrb", 4), + ("aclk", 1), + ("arready", 1), + ("awready", 1), + ("bvalid", 1), + ("rlast", 1), + ("rvalid", 1), + ("wready", 1), + ("bid", 12), + ("rid", 12), + ("bresp", 2), + ("rresp", 2), + ("rdata", 32), + ("arstn", 1), +] + + +class PitayaPS(Module): + def __init__(self, cpu): + self.fclk = Signal(4) + self.frstn = Signal(4) + + ### + + self.submodules.axi = Axi2Sys() + axi = self.axi.axi + self.comb += [ + axi.aclk.eq(self.fclk[0]), + axi.arstn.eq(self.frstn[0]), + ] + + self.specials += Instance( + "system_processing_system7_0_0", + io_MIO=cpu.mio, + io_PS_CLK=cpu.ps_clk, + io_PS_PORB=cpu.ps_porb, + io_PS_SRSTB=cpu.ps_srstb, + io_DDR_Addr=cpu.DDR_addr, + io_DDR_BankAddr=cpu.DDR_ba, + io_DDR_CAS_n=cpu.DDR_cas_n, + io_DDR_Clk_n=cpu.DDR_ck_n, + io_DDR_Clk=cpu.DDR_ck_p, + io_DDR_CKE=cpu.DDR_cke, + io_DDR_CS_n=cpu.DDR_cs_n, + io_DDR_DM=cpu.DDR_dm, + io_DDR_DQ=cpu.DDR_dq, + io_DDR_DQS_n=cpu.DDR_dqs_n, + io_DDR_DQS=cpu.DDR_dqs_p, + io_DDR_ODT=cpu.DDR_odt, + io_DDR_RAS_n=cpu.DDR_ras_n, + io_DDR_DRSTB=cpu.DDR_reset_n, + io_DDR_WEB=cpu.DDR_we_n, + io_DDR_VRN=cpu.ddr_vrn, + io_DDR_VRP=cpu.ddr_vrp, + o_FCLK_CLK0=self.fclk[0], + o_FCLK_CLK1=self.fclk[1], + o_FCLK_CLK2=self.fclk[2], + o_FCLK_CLK3=self.fclk[3], + o_FCLK_RESET0_N=self.frstn[0], + o_FCLK_RESET1_N=self.frstn[1], + o_FCLK_RESET2_N=self.frstn[2], + o_FCLK_RESET3_N=self.frstn[3], + i_M_AXI_GP0_ACLK=axi.aclk, + o_M_AXI_GP0_ARVALID=axi.arvalid, + o_M_AXI_GP0_AWVALID=axi.awvalid, + o_M_AXI_GP0_BREADY=axi.bready, + o_M_AXI_GP0_RREADY=axi.rready, + o_M_AXI_GP0_WLAST=axi.wlast, + o_M_AXI_GP0_WVALID=axi.wvalid, + o_M_AXI_GP0_ARID=axi.arid, + o_M_AXI_GP0_AWID=axi.awid, + o_M_AXI_GP0_WID=axi.wid, + o_M_AXI_GP0_ARBURST=axi.arburst, + o_M_AXI_GP0_ARLOCK=axi.arlock, + o_M_AXI_GP0_ARSIZE=axi.arsize, + o_M_AXI_GP0_AWBURST=axi.awburst, + o_M_AXI_GP0_AWLOCK=axi.awlock, + o_M_AXI_GP0_AWSIZE=axi.awsize, + o_M_AXI_GP0_ARPROT=axi.arprot, + o_M_AXI_GP0_AWPROT=axi.awprot, + o_M_AXI_GP0_ARADDR=axi.araddr, + o_M_AXI_GP0_AWADDR=axi.awaddr, + o_M_AXI_GP0_WDATA=axi.wdata, + o_M_AXI_GP0_ARCACHE=axi.arcache, + o_M_AXI_GP0_ARLEN=axi.arlen, + o_M_AXI_GP0_ARQOS=axi.arqos, + o_M_AXI_GP0_AWCACHE=axi.awcache, + o_M_AXI_GP0_AWLEN=axi.awlen, + o_M_AXI_GP0_AWQOS=axi.awqos, + o_M_AXI_GP0_WSTRB=axi.wstrb, + i_M_AXI_GP0_ARREADY=axi.arready, + i_M_AXI_GP0_AWREADY=axi.awready, + i_M_AXI_GP0_BVALID=axi.bvalid, + i_M_AXI_GP0_RLAST=axi.rlast, + i_M_AXI_GP0_RVALID=axi.rvalid, + i_M_AXI_GP0_WREADY=axi.wready, + i_M_AXI_GP0_BID=axi.bid, + i_M_AXI_GP0_RID=axi.rid, + i_M_AXI_GP0_BRESP=axi.bresp, + i_M_AXI_GP0_RRESP=axi.rresp, + i_M_AXI_GP0_RDATA=axi.rdata, + i_SPI0_SS_I=0, + # i_SPI0_SS_I=spi.ss_i, + # o_SPI0_SS_O=spi.ss_o, + # o_SPI0_SS_T=spi.ss_t, + # o_SPI0_SS1_O=spi.ss1_o, + # o_SPI0_SS2_O=spi.ss2_o, + i_SPI0_SCLK_I=0, + # i_SPI0_SCLK_I=spi.sclk_i, + # o_SPI0_SCLK_O=spi.sclk_o, + # o_SPI0_SCLK_T=spi.sclk_t, + i_SPI0_MOSI_I=0, + # i_SPI0_MOSI_I=spi.mosi_i, + # o_SPI0_MOSI_O=spi.mosi_o, + # o_SPI0_MOSI_T=spi.mosi_t, + i_SPI0_MISO_I=0, + # i_SPI0_MISO_I=spi.miso_i, + # o_SPI0_MISO_O=spi.miso_o, + # o_SPI0_MISO_T=spi.miso_t, + i_USB0_VBUS_PWRFAULT=0, + ) + + +class Axi2Sys(Module): + def __init__(self): + self.sys = Record(sys_layout) + self.axi = Record(axi_layout) + + ### + + self.comb += [self.sys.clk.eq(self.axi.aclk), self.sys.rstn.eq(self.axi.arstn)] + + self.specials += Instance( + "axi_slave", + p_AXI_DW=32, + p_AXI_AW=32, + p_AXI_IW=12, + i_axi_clk_i=self.axi.aclk, + i_axi_rstn_i=self.axi.arstn, + i_axi_awid_i=self.axi.awid, + i_axi_awaddr_i=self.axi.awaddr, + i_axi_awlen_i=self.axi.awlen, + i_axi_awsize_i=self.axi.awsize, + i_axi_awburst_i=self.axi.awburst, + i_axi_awlock_i=self.axi.awlock, + i_axi_awcache_i=self.axi.awcache, + i_axi_awprot_i=self.axi.awprot, + i_axi_awvalid_i=self.axi.awvalid, + o_axi_awready_o=self.axi.awready, + i_axi_wid_i=self.axi.wid, + i_axi_wdata_i=self.axi.wdata, + i_axi_wstrb_i=self.axi.wstrb, + i_axi_wlast_i=self.axi.wlast, + i_axi_wvalid_i=self.axi.wvalid, + o_axi_wready_o=self.axi.wready, + o_axi_bid_o=self.axi.bid, + o_axi_bresp_o=self.axi.bresp, + o_axi_bvalid_o=self.axi.bvalid, + i_axi_bready_i=self.axi.bready, + i_axi_arid_i=self.axi.arid, + i_axi_araddr_i=self.axi.araddr, + i_axi_arlen_i=self.axi.arlen, + i_axi_arsize_i=self.axi.arsize, + i_axi_arburst_i=self.axi.arburst, + i_axi_arlock_i=self.axi.arlock, + i_axi_arcache_i=self.axi.arcache, + i_axi_arprot_i=self.axi.arprot, + i_axi_arvalid_i=self.axi.arvalid, + o_axi_arready_o=self.axi.arready, + o_axi_rid_o=self.axi.rid, + o_axi_rdata_o=self.axi.rdata, + o_axi_rresp_o=self.axi.rresp, + o_axi_rlast_o=self.axi.rlast, + o_axi_rvalid_o=self.axi.rvalid, + i_axi_rready_i=self.axi.rready, + o_sys_addr_o=self.sys.addr, + o_sys_wdata_o=self.sys.wdata, + o_sys_sel_o=self.sys.sel, + o_sys_wen_o=self.sys.wen, + o_sys_ren_o=self.sys.ren, + i_sys_rdata_i=self.sys.rdata, + i_sys_err_i=self.sys.err, + i_sys_ack_i=self.sys.ack, + ) + + +class SysInterconnect(Module): + def __init__(self, master, *slaves): + cs = Signal(max=len(slaves)) if len(slaves) != 1 else Signal() + self.comb += cs.eq(master.addr[20:23]) + rets = [] + for i, s in enumerate(slaves): + sel = Signal() + self.comb += [ + sel.eq(cs == i), + s.clk.eq(master.clk), + s.rstn.eq(master.rstn), + s.addr.eq(master.addr), + s.wdata.eq(master.wdata), + s.sel.eq(master.sel), + s.wen.eq(sel & master.wen), + s.ren.eq(sel & master.ren), + ] + ret = Cat(s.err, s.ack, s.rdata) + rets.append(Replicate(sel, len(ret)) & ret) + self.comb += Cat(master.err, master.ack, master.rdata).eq(reduce(or_, rets)) + + +class Sys2Wishbone(Module): + def __init__(self): + self.wishbone = wb = wishbone.Interface() + self.sys = sys = Record(sys_layout) + + ### + + sys2 = Record(sys_layout) + + self.specials += Instance( + "bus_clk_bridge", + i_sys_clk_i=sys.clk, + i_sys_rstn_i=sys.rstn, + i_sys_addr_i=sys.addr, + i_sys_wdata_i=sys.wdata, + i_sys_sel_i=sys.sel, + i_sys_wen_i=sys.wen, + i_sys_ren_i=sys.ren, + o_sys_rdata_o=sys.rdata, + o_sys_err_o=sys.err, + o_sys_ack_o=sys.ack, + i_clk_i=ClockSignal(), + i_rstn_i=~ResetSignal(), + o_addr_o=sys2.addr, + o_wen_o=sys2.wen, + o_ren_o=sys2.ren, + o_wdata_o=sys2.wdata, + i_rdata_i=sys2.rdata, + i_err_i=sys2.err, + i_ack_i=sys2.ack, + ) + self.sync += [ + If( + sys2.ren | sys2.wen, + wb.cyc.eq(1), + wb.adr.eq(sys2.addr[2:]), + wb.we.eq(sys2.wen), + wb.dat_w.eq(sys2.wdata), + ).Elif(wb.ack, wb.cyc.eq(0)) + ] + self.comb += [ + wb.stb.eq(wb.cyc), + sys2.rdata.eq(wb.dat_r), + sys2.ack.eq(wb.ack), + sys2.err.eq(wb.err), + ] + + +class SysCDC(Module): + def __init__(self, cd_target="sys"): + self.source = Record(sys_layout) + self.target = Record(sys_layout) + + self.specials += Instance( + "bus_clk_bridge", + i_sys_clk_i=self.source.clk, + i_sys_rstn_i=self.source.rstn, + i_sys_addr_i=self.source.addr, + i_sys_wdata_i=self.source.wdata, + i_sys_sel_i=self.source.sel, + i_sys_wen_i=self.source.wen, + i_sys_ren_i=self.source.ren, + o_sys_rdata_o=self.source.rdata, + o_sys_err_o=self.source.err, + o_sys_ack_o=self.source.ack, + i_clk_i=self.target.clk, + i_rstn_i=self.target.rstn, + o_addr_o=self.target.addr, + o_wdata_o=self.target.wdata, + o_wen_o=self.target.wen, + o_ren_o=self.target.ren, + i_rdata_i=self.target.rdata, + i_err_i=self.target.err, + i_ack_i=self.target.ack, + ) + self.comb += [ + self.target.clk.eq(ClockSignal(cd_target)), + self.target.rstn.eq(~ResetSignal(cd_target)), + ] + + +class Sys2CSR(Module): + def __init__(self): + self.csr = csr_bus.Interface() + self.sys = Record(sys_layout) + + ### + + stb = Signal() + self.sync += [ + stb.eq(self.sys.wen | self.sys.ren), + self.csr.adr.eq(self.sys.addr[2:]), + self.csr.we.eq(self.sys.wen), + self.csr.dat_w.eq(self.sys.wdata), + self.sys.ack.eq(stb), + self.sys.rdata.eq(self.csr.dat_r), + ] diff --git a/fast-servo/linien-gateware/cores/ps7.py b/fast-servo/linien-gateware/cores/ps7.py new file mode 100644 index 0000000..95e0aa4 --- /dev/null +++ b/fast-servo/linien-gateware/cores/ps7.py @@ -0,0 +1,193 @@ +# This file is part of Fast Servo Software Package. +# +# Copyright (C) 2023 Jakub Matyas +# Warsaw University of Technology +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program 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. +# +# This program 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 this program. If not, see . + +from migen import * + + +class PS7(Module): + def __init__(self, platform, ps7_name=None): + self.frstn = Signal() + self.ps7_tcl = [] + self.platform = platform + + self.ps7_name = "Zynq" if ps7_name is None else ps7_name + + ps7_clk = platform.request("ps7_clk") + ps7_porb = platform.request("ps7_porb") + ps7_srstb = platform.request("ps7_srstb") + ps7_mio = platform.request("ps7_mio") + ps7_ddram = platform.request("ps7_ddram") + + self.cpu_params = dict( + # Clk / Rst. + io_PS_CLK = ps7_clk, + io_PS_PORB = ps7_porb, + io_PS_SRSTB = ps7_srstb, + + # MIO. + io_MIO = ps7_mio, + + # DDRAM. + io_DDR_Addr = ps7_ddram.addr, + io_DDR_BankAddr = ps7_ddram.ba, + io_DDR_CAS_n = ps7_ddram.cas_n, + io_DDR_Clk_n = ps7_ddram.ck_n, + io_DDR_Clk = ps7_ddram.ck_p, + io_DDR_CKE = ps7_ddram.cke, + io_DDR_CS_n = ps7_ddram.cs_n, + io_DDR_DM = ps7_ddram.dm, + io_DDR_DQ = ps7_ddram.dq, + io_DDR_DQS_n = ps7_ddram.dqs_n, + io_DDR_DQS = ps7_ddram.dqs_p, + io_DDR_ODT = ps7_ddram.odt, + io_DDR_RAS_n = ps7_ddram.ras_n, + io_DDR_DRSTB = ps7_ddram.reset_n, + io_DDR_WEB = ps7_ddram.we_n, + io_DDR_VRN = ps7_ddram.vrn, + io_DDR_VRP = ps7_ddram.vrp, + + # USB0. + # i_USB0_VBUS_PWRFAULT = 0, + + # Fabric Clk / Rst. + # o_FCLK_CLK0 = ClockSignal("ps7"), + o_FCLK_RESET0_N = self.frstn + ) + + self.set_ps7(name=self.ps7_name, + config={ + **self.platform.ps7_config + }) + + def add_ps7_config(self, config): + # Config must be provided as a config, value dict. + assert isinstance(config, dict) + # Add configs to PS7. + self.ps7_tcl.append("set_property -dict [list \\") + for config, value in config.items(): + self.ps7_tcl.append("CONFIG.{} {} \\".format(config, '{{' + str(value) + '}}')) + self.ps7_tcl.append(f"] [get_ips {self.ps7_name}]") + + def set_ps7(self, name=None, config=None, preset=None): + self.ps7_name = name + self.ps7_tcl.append(f"set ps7 [create_ip -vendor xilinx.com -name processing_system7 -module_name {self.ps7_name}]") + self.add_ps7_config(config) + + def add_axi_gp_master(self, interface): + # assert type(interface) is Axi2Sys + + axi_interface = interface + + self.cpu_params.update({ + # AXI GP clk. + f"i_M_AXI_GP0_ACLK" : axi_interface.aclk, + + # AXI GP aw. + f"o_M_AXI_GP0_AWVALID" : axi_interface.awvalid, + f"i_M_AXI_GP0_AWREADY" : axi_interface.awready, + f"o_M_AXI_GP0_AWADDR" : axi_interface.awaddr, + f"o_M_AXI_GP0_AWBURST" : axi_interface.awburst, + f"o_M_AXI_GP0_AWLEN" : axi_interface.awlen, + f"o_M_AXI_GP0_AWSIZE" : axi_interface.awsize, + f"o_M_AXI_GP0_AWID" : axi_interface.awid, + f"o_M_AXI_GP0_AWLOCK" : axi_interface.awlock, + f"o_M_AXI_GP0_AWPROT" : axi_interface.awprot, + f"o_M_AXI_GP0_AWCACHE" : axi_interface.awcache, + f"o_M_AXI_GP0_AWQOS" : axi_interface.awqos, + + # AXI GP w. + f"o_M_AXI_GP0_WVALID" : axi_interface.wvalid, + f"o_M_AXI_GP0_WLAST" : axi_interface.wlast, + f"i_M_AXI_GP0_WREADY" : axi_interface.wready, + f"o_M_AXI_GP0_WID" : axi_interface.wid, + f"o_M_AXI_GP0_WDATA" : axi_interface.wdata, + f"o_M_AXI_GP0_WSTRB" : axi_interface.wstrb, + + # AXI GP b. + f"i_M_AXI_GP0_BVALID" : axi_interface.bvalid, + f"o_M_AXI_GP0_BREADY" : axi_interface.bready, + f"i_M_AXI_GP0_BID" : axi_interface.bid, + f"i_M_AXI_GP0_BRESP" : axi_interface.bresp, + + # AXI GP ar. + f"o_M_AXI_GP0_ARVALID" : axi_interface.arvalid, + f"i_M_AXI_GP0_ARREADY" : axi_interface.arready, + f"o_M_AXI_GP0_ARADDR" : axi_interface.araddr, + f"o_M_AXI_GP0_ARBURST" : axi_interface.arburst, + f"o_M_AXI_GP0_ARLEN" : axi_interface.arlen, + f"o_M_AXI_GP0_ARID" : axi_interface.arid, + f"o_M_AXI_GP0_ARLOCK" : axi_interface.arlock, + f"o_M_AXI_GP0_ARSIZE" : axi_interface.arsize, + f"o_M_AXI_GP0_ARPROT" : axi_interface.arprot, + f"o_M_AXI_GP0_ARCACHE" : axi_interface.arcache, + f"o_M_AXI_GP0_ARQOS" : axi_interface.arqos, + + # AXI GP r. + f"i_M_AXI_GP0_RVALID" : axi_interface.rvalid, + f"o_M_AXI_GP0_RREADY" : axi_interface.rready, + f"i_M_AXI_GP0_RLAST" : axi_interface.rlast, + f"i_M_AXI_GP0_RID" : axi_interface.rid, + f"i_M_AXI_GP0_RRESP" : axi_interface.rresp, + f"i_M_AXI_GP0_RDATA" : axi_interface.rdata, + }) + + def add_i2c_emio(self, platform, i2c_name, i2c_number): + ps7_i2c_pads = platform.request(i2c_name, i2c_number) #, loose=True) + if ps7_i2c_pads is not None: + o_i2c_scl = Signal() + i_i2c_scl = Signal() + t_i2c_scl = Signal() + o_i2c_sda = Signal() + i_i2c_sda = Signal() + t_i2c_sda = Signal() + + self.specials += Instance("IOBUF", + i_T = t_i2c_sda, + i_I = o_i2c_sda, + io_IO = ps7_i2c_pads.sda, + o_O = i_i2c_sda, + ) + + self.specials += Instance("IOBUF", + i_T = t_i2c_scl, + i_I = o_i2c_scl, + io_IO = ps7_i2c_pads.scl, + o_O = i_i2c_scl, + ) + + self.cpu_params.update({ + f"i_I2C{i2c_number}_SCL_I" : i_i2c_scl, + f"o_I2C{i2c_number}_SCL_T" : t_i2c_scl, + f"o_I2C{i2c_number}_SCL_O" : o_i2c_scl, + + f"i_I2C{i2c_number}_SDA_I" : i_i2c_sda, + f"o_I2C{i2c_number}_SDA_T" : t_i2c_sda, + f"o_I2C{i2c_number}_SDA_O" : o_i2c_sda, + }) + + def do_finalize(self): + if len(self.ps7_tcl): + self.ps7_tcl += [ + f"upgrade_ip [get_ips {self.ps7_name}]", + f"generate_target all [get_ips {self.ps7_name}]", + f"synth_ip [get_ips {self.ps7_name}]" + ] + self.platform.toolchain.pre_synthesis_commands += self.ps7_tcl + + self.specials += Instance(self.ps7_name, **self.cpu_params) diff --git a/fast-servo/linien-gateware/cores/spi_phy.py b/fast-servo/linien-gateware/cores/spi_phy.py new file mode 100644 index 0000000..7b095a3 --- /dev/null +++ b/fast-servo/linien-gateware/cores/spi_phy.py @@ -0,0 +1,141 @@ +# This file is part of Fast Servo Software Package. +# +# Copyright (C) 2023 Jakub Matyas +# Warsaw University of Technology +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program 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. +# +# This program 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 this program. If not, see . + +from enum import Enum + +from migen import * + + +class SpiInterface(Enum): + ADC = 0 + DAC = 1 + + +class SpiPhy(Module): + def __init__(self, spi_type, ps7_spi_pads): + assert isinstance(spi_type, SpiInterface) + + self.ps_ss = Signal(3, reset_less=True) + + self.ps_miso_o = Signal() + self.ps_miso_t = Signal() + self.ps_mosi_o = Signal() + self.ps_mosi_t = Signal() + self.ps_sclk_o = Signal() + self.ps_sclk_t = Signal() + self.ps_sclk_i = Signal() + self.ps_mosi_i = Signal() + self.ps_miso_i = Signal() + self.ps_ss_i = Signal() + self.ps_ss_t = Signal() + + if spi_type.name == "ADC": + + cs_translated = Signal(4, reset_less=True) + + main_adc_miso = Signal(reset_less=True) + aux_adc_miso_a = Signal(reset_less=True) + aux_adc_miso_b = Signal(reset_less=True) + aux_dac_miso = Signal(reset_less=True) + + miso_signals = [main_adc_miso, aux_adc_miso_a, aux_adc_miso_b, aux_dac_miso] + + # MAIN ADC + self.specials += Instance("OBUFT", i_I=self.ps_sclk_o, i_T=self.ps_sclk_t, o_O=ps7_spi_pads.sclk) + self.specials += Instance("OBUFT", i_I=self.ps_mosi_o, i_T=self.ps_mosi_t, o_O=ps7_spi_pads.mosi) + self.specials += Instance("IBUF", i_I=ps7_spi_pads.miso, o_O=main_adc_miso) + + # AUX ADC + self.specials += Instance("OBUFT", i_I=self.ps_sclk_o, i_T=self.ps_sclk_t, o_O=ps7_spi_pads.aux_adc_sclk) + self.specials += Instance("IBUF", i_I=ps7_spi_pads.aux_adc_miso_a, o_O=aux_adc_miso_a) + self.specials += Instance("IBUF", i_I=ps7_spi_pads.aux_adc_miso_b, o_O=aux_adc_miso_b) + + # AUX DAC + self.specials += Instance("OBUFT", i_I=self.ps_sclk_o, i_T=self.ps_sclk_t, o_O=ps7_spi_pads.aux_dac_sclk) + self.specials += Instance("OBUFT", i_I=self.ps_mosi_o, i_T=self.ps_mosi_t, o_O=ps7_spi_pads.aux_dac_mosi) + self.specials += Instance("IBUF", i_I=ps7_spi_pads.aux_dac_miso, o_O=aux_dac_miso) + + cases = {} + # for i in range(4): + # case_mask = ~(1 << i) & 0xF + # print(f"Case mask: 0b{case_mask:04b}") + # cases[i + 1] = [ + # cs_translated.eq(case_mask), + # self.ps_miso_i.eq(miso_signals[i]) + # ] + + cases[0b001] = [ + cs_translated.eq(0b1110), + self.ps_miso_i.eq(main_adc_miso), + ] + + cases[0b010] = [ + cs_translated.eq(0b1101), + self.ps_miso_i.eq(aux_adc_miso_a), + ] + + cases[0b011] = [ + cs_translated.eq(0b1011), + self.ps_miso_i.eq(aux_adc_miso_b), + ] + cases[0b100] = [ + cs_translated.eq(0b0111), + self.ps_miso_i.eq(aux_dac_miso), + ] + + cases["default"] = [ + cs_translated.eq(0b1111), + self.ps_miso_i.eq(0b1), + ] + + self.comb += [ + self.ps_ss_i.eq(1), + Case(self.ps_ss, cases), + + ps7_spi_pads.cs.eq(cs_translated[0]), + + # only one CS line physically available to AUX ADC + ps7_spi_pads.aux_adc_cs.eq(cs_translated[1] & cs_translated[2]), + ps7_spi_pads.aux_dac_miso.eq(cs_translated[3]), + + ] + else: + self.specials += Instance( + "spi2threewire", + o_ps_sclk_i = self.ps_sclk_i, + i_ps_sclk_o = self.ps_sclk_o, + i_ps_sclk_t = self.ps_sclk_t, + + o_ps_mosi_i = self.ps_mosi_i, + i_ps_mosi_o = self.ps_mosi_o, + i_ps_mosi_t = self.ps_mosi_t, + + o_ps_miso_i = self.ps_miso_i, + i_ps_miso_o = self.ps_miso_o, + i_ps_miso_t = self.ps_miso_t, + + o_ps_ss_i = self.ps_ss_i, + i_ps_ss = self.ps_ss, + i_ps_ss_t = self.ps_ss_t, + + o_o_ss = ps7_spi_pads.cs, + o_o_sclk = ps7_spi_pads.sclk, + io_sdio = ps7_spi_pads.sdio, + ) + diff --git a/fast-servo/linien-gateware/fast_servo_platform.py b/fast-servo/linien-gateware/fast_servo_platform.py new file mode 100644 index 0000000..13b4aa3 --- /dev/null +++ b/fast-servo/linien-gateware/fast_servo_platform.py @@ -0,0 +1,336 @@ +# This file is part of Fast Servo Software Package. +# +# Copyright (C) 2022-2023 Jakub Matyas +# Warsaw University of Technology +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program 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. +# +# This program 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 this program. If not, see . + +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform + +import os +from fast_servo.gateware import verilog_dir + + +# IOs ---------------------------------------------------------------------------------------------- + + +_io = [ + # Front panel LEDs + ("fp_led", 0, Pins("G7"), IOStandard("LVCMOS25")), + ("fp_led", 1, Pins("G8"), IOStandard("LVCMOS25")), + ("fp_led", 2, Pins("G2"), IOStandard("LVCMOS25")), + ("fp_led", 3, Pins("G3"), IOStandard("LVCMOS25")), + + # LEDs + ("user_led", 0, Pins("AB16"), IOStandard("LVCMOS33")), + ("user_led", 1, Pins("AA14"), IOStandard("LVCMOS33")), + ("user_led", 2, Pins("AA15"), IOStandard("LVCMOS33")), + + # ETH LEDs + ("eth_led", 0, Pins("V11"), IOStandard("LVCMOS33")), + ("eth_led", 1, Pins("U13"), IOStandard("LVCMOS33")), + + ("from_eth_phy", 0, Pins("J3"), IOStandard("LVCMOS18")), + ("from_eth_phy", 1, Pins("K8"), IOStandard("LVCMOS18")), + + + # CLK 100 MHz from oscillator + ("clk100", 0, Pins("Y14"), IOStandard("LVCMOS33")), + + # MGT clk from on-board Si5338A + ("mgt_clk1", 0, + Subsignal("p", Pins("U5")), + Subsignal("n", Pins("V5")), + IOStandard("LVDS_25"), + ), + + # FCLK125 from on-board Si5338A + ("fclk125", 0, Pins("K2"), IOStandard("LVCMOS18")), + + # # ADC CLK + # ("fpga_clk", 0, + # Subsignal("p", Pins("C6")), + # Subsignal("n", Pins("C5")), + # IOStandard("LVDS_25"), + # ), + + # DAC CLK + ("fpga_clk", 1, + Subsignal("p", Pins("J7")), + Subsignal("n", Pins("J6")), + IOStandard("LVDS_25"), + ), + + # MAIN ADC LT2195 + ("adc", 0, + Subsignal("data0_p", Pins("F7 B7 E4 D1"), IOStandard("LVDS_25")), + Subsignal("data0_n", Pins("E7 B6 E3 C1"), IOStandard("LVDS_25")), + Subsignal("data1_p", Pins("A2 D5 F2 D7"), IOStandard("LVDS_25")), + Subsignal("data1_n", Pins("A1 C4 F1 D6"), IOStandard("LVDS_25")), + Subsignal("dco_p", Pins("B4"), IOStandard("LVDS_25")), + Subsignal("dco_n", Pins("B3"), IOStandard("LVDS_25")), + Subsignal("frame_p", Pins("B2"), IOStandard("LVDS_25")), + Subsignal("frame_n", Pins("B1"), IOStandard("LVDS_25")) + ), + + # ADC AFE + ("adc_afe", 0, + Subsignal("ch1_gain", Pins("AA12"), IOStandard("LVCMOS33")), + Subsignal("ch2_gain", Pins("AB11"), IOStandard("LVCMOS33")), + Subsignal("nshdn_ch1", Pins("G4"), IOStandard("LVCMOS25")), + Subsignal("nshdn_ch2", Pins("F4"), IOStandard("LVCMOS25")), + ), + + # P5 R5 N1 N4 L2 P6 N3 R4 P1 L1 M3 M4 U2 T2 + # MAIN DAC AD9117 + ("dac", 0, + Subsignal("data", + Pins("T2 U2 M4 M3 L1 P1 R4 N3 P6 L2 N4 N1 R5 P5"), + IOStandard("LVCMOS18")), + Subsignal("dclkio", Pins("U1"), IOStandard("LVCMOS18")), + Subsignal("rst", Pins("T1"), IOStandard("LVCMOS18")), + ), + + # DAC AFE + ("dac_afe", 0, + Subsignal("ch1_pd_n", Pins("L5")), + Subsignal("ch2_pd_n", Pins("L4")), + IOStandard("LVCMOS18"), + ), + + # AUXILIARY ADC + ("aux_adc", 0, + Subsignal("diff_n", Pins("W18")), + Subsignal("a", Pins("AB18 AB19 W17")), + Subsignal("range", Pins("U19")), + IOStandard("LVCMOS33") + ), + + # AUXILIARY DAC + ("aux_dac", 0, + Subsignal("nclr", Pins("V19")), + Subsignal("bin", Pins("AA19")), + Subsignal("nldac", Pins("AB21")), + IOStandard("LVCMOS33") + ), + + # ADC SPI + ("spi", 0, + Subsignal("sclk", Pins("J1"), IOStandard("LVCMOS18")), + Subsignal("mosi", Pins("J5"), IOStandard("LVCMOS18")), + Subsignal("miso", Pins("K5"), IOStandard("LVCMOS18")), + Subsignal("cs", Pins("J2"), IOStandard("LVCMOS18")), + Subsignal("aux_adc_sclk", Pins("AB22"), IOStandard("LVCMOS33")), + Subsignal("aux_adc_miso_a", Pins("Y18"), IOStandard("LVCMOS33")), + Subsignal("aux_adc_miso_b", Pins("AA16"), IOStandard("LVCMOS33")), + Subsignal("aux_adc_cs", Pins("Y19"), IOStandard("LVCMOS33")), + Subsignal("aux_dac_sclk", Pins("V18"), IOStandard("LVCMOS33")), + Subsignal("aux_dac_mosi", Pins("U17"), IOStandard("LVCMOS33")), + Subsignal("aux_dac_miso", Pins("U18"), IOStandard("LVCMOS33")), + Subsignal("aux_dac_cs", Pins("AA20"), IOStandard("LVCMOS33")), + ), + + # DAC SPI + ("spi", 1, + Subsignal("sclk", Pins("K3"), IOStandard("LVCMOS18")), + Subsignal("sdio", Pins("R2"), IOStandard("LVCMOS18")), + Subsignal("cs", Pins("R3"), IOStandard("LVCMOS18")), + # Subsignal("sclk_aux", Pins("V18"), IOStandard("LVCMOS33")), + # Subsignal("mosi_aux", Pins("U17"), IOStandard("LVCMOS33")), + # Subsignal("miso_aux", Pins("U18"), IOStandard("LVCMOS33")), + # Subsignal("cs_aux", Pins("AA20"), IOStandard("LVCMOS33")), + ), + #Pins("Y15 AB14 AB13 V13 V14 V15 U12 W13 W12 R17 W15 AB17 Y12 Y13 V16 W16 AA17 Y17"), + ("gpio", 0, + Subsignal("n", Pins("Y15 AB14 AB13 V13 V14 V15 U12 W13"), IOStandard("LVCMOS33")), + Subsignal("p", Pins("W12 R17 W15 AB17 Y12 Y13 V16 W16"), IOStandard("LVCMOS33")), + ), + + # PS7 + ("ps7_clk", 0, Pins("_" * 1)), + ("ps7_porb", 0, Pins("_" * 1)), + ("ps7_srstb", 0, Pins("_" * 1)), + ("ps7_mio", 0, Pins("_" * 54)), + ("ps7_ddram", 0, + Subsignal("addr", Pins("_" * 15)), + Subsignal("ba", Pins("_" * 3)), + Subsignal("cas_n", Pins("_" * 1)), + Subsignal("ck_n", Pins("_" * 1)), + Subsignal("ck_p", Pins("_" * 1)), + Subsignal("cke", Pins("_" * 1)), + Subsignal("cs_n", Pins("_" * 1)), + Subsignal("dm", Pins("_" * 4)), + Subsignal("dq", Pins("_" * 32)), + Subsignal("dqs_n", Pins("_" * 4)), + Subsignal("dqs_p", Pins("_" * 4)), + Subsignal("odt", Pins("_" * 1)), + Subsignal("ras_n", Pins("_" * 1)), + Subsignal("reset_n", Pins("_" * 1)), + Subsignal("we_n", Pins("_" * 1)), + Subsignal("vrn", Pins("_" * 1)), + Subsignal("vrp", Pins("_" * 1)), + + ), + + # I2C0 to Si5340 on Fast Servo + ("ps7_i2c", 0, + Subsignal("sda", Pins("M2")), + Subsignal("scl", Pins("M1")), + IOStandard("LVCMOS18") + ), + + # Si540 nRST + ("nrst", 0, Pins("M7"), IOStandard("LVCMOS18")), + +] + + + +_connector_eem = [ + ("eem", { + "d0_cc_n": "C3", + "d0_cc_p": "D3", + "d1_n": "A4", + "d1_p": "A5", + "d2_n": "F6", + "d2_p": "G6", + "d3_n": "A6", + "d3_p": "A7", + "d4_n": "E5", + "d4_p": "F5", + "d5_n": "H3", + "d5_p": "H4", + "d6_n": "B8", + "d6_p": "C8", + "d7_n": "D8", + "d7_p": "E8", + }), +] + +_connector_gpio = [ + ("gpio", { + "qspi_io3": "Y15", + "qspi_io2": "AB14", + "qspi_io1": "AB13", + "qspi_io0": "V13", + "qspi_clk": "V14", + "qspi_ncs": "V15", + "hrtim_che1": "U12", + "hrtim_che2": "W13", + "hrtim_cha1": "W12", + "hrtim_cha2": "R17", + "lptim2_out": "W15", + "uart4_tx": "AB17", + "spi1_mosi": "Y12", + "spi1_miso": "Y13", + "spi1_nss": "V16", + "spi1_sck": "W16", + "i2c1_sda": "AA17", + "i2c1_scl": "Y17", + }) +] + +# PS7 config --------------------------------------------------------------------------------------- + +ps7_config_board_preset = { + "PCW_PRESET_BANK0_VOLTAGE" : "LVCMOS 3.3V", + "PCW_PRESET_BANK1_VOLTAGE" : "LVCMOS 1.8V", + "PCW_CRYSTAL_PERIPHERAL_FREQMHZ" : "33.333333", + "PCW_QSPI_PERIPHERAL_ENABLE" : "1", + "PCW_QSPI_GRP_SINGLE_SS_ENABLE" : "1", + "PCW_SINGLE_QSPI_DATA_MODE" : "x4", + "PCW_QSPI_QSPI_IO" : "MIO 1 .. 6", + "PCW_QSPI_GRP_FBCLK_ENABLE" : "1", + + # SD Card + "PCW_SD0_PERIPHERAL_ENABLE" : "1", + "PCW_SD0_SD0_IO" : "MIO 40 .. 45", + "PCW_MIO_40_PULLUP" : "disabled", + "PCW_MIO_41_PULLUP" : "disabled", + "PCW_MIO_42_PULLUP" : "disabled", + "PCW_MIO_43_PULLUP" : "disabled", + "PCW_MIO_44_PULLUP" : "disabled", + "PCW_MIO_45_PULLUP" : "disabled", + + # UART + "PCW_UART0_PERIPHERAL_ENABLE" : "1", + "PCW_UART0_UART0_IO" : "MIO 10 .. 11", + + # ETHERNET + "PCW_ACT_ENET0_PERIPHERAL_FREQMHZ" : "125", + "PCW_ENET0_PERIPHERAL_CLKSRC" : "ARM PLL", + "PCW_ENET0_PERIPHERAL_ENABLE" : "1", + "PCW_ENET0_ENET0_IO" : "MIO 16 .. 27", + "PCW_ENET0_GRP_MDIO_ENABLE" : "1", + "PCW_ENET0_GRP_MDIO_IO" : "MIO 52 .. 53", + "PCW_ENET_RESET_ENABLE" : "1", + "PCW_ENET0_RESET_ENABLE" : "1", + "PCW_ENET0_RESET_IO" : "MIO 50", + + # USB + "PCW_USB0_PERIPHERAL_ENABLE" : "1", + "PCW_USB0_USB0_IO" : "MIO 28 .. 39", + "PCW_USB_RESET_ENABLE" : "1", + "PCW_USB0_RESET_ENABLE" : "1", + "PCW_USB0_RESET_IO" : "MIO 51", + + "PCW_WDT_PERIPHERAL_ENABLE" : "1", + "PCW_TTC0_PERIPHERAL_ENABLE" : "1", # TTC0 required for Linux + + # I2C + "PCW_I2C_RESET_ENABLE" : "0", + "PCW_I2C_RESET_POLARITY" : "Active Low", + + # I2C0 + "PCW_I2C0_I2C0_IO" : "EMIO", + "PCW_I2C0_PERIPHERAL_ENABLE" : "1", + "PCW_I2C0_GRP_INT_ENABLE" : "1", + "PCW_I2C0_GRP_INT_IO" : "EMIO", + + # I2C1 + "PCW_I2C1_PERIPHERAL_ENABLE" : "1", + "PCW_I2C1_I2C1_IO" : "MIO 48 .. 49", + "PCW_I2C1_I2C1_IO" : "MIO 12 .. 13", + "PCW_I2C1_PERIPHERAL_ENABLE" : "1", + + "PCW_UIPARAM_DDR_MEMORY_TYPE" : "DDR 3 (Low Voltage)", + "PCW_UIPARAM_DDR_PARTNO" : "MT41J256M16 RE-125", + "PCW_GPIO_MIO_GPIO_ENABLE" : "1", + + "PCW_FPGA0_PERIPHERAL_FREQMHZ" : "100", + "PCW_EN_CLK0_PORT" : "0", # dont use FCLK0 +} + +class Platform(XilinxPlatform): + default_clk_name = "clk100" + default_clk_period = 10.0 + + def __init__(self): + XilinxPlatform.__init__(self, "xc7z015-clg485-1", _io, _connector_gpio + _connector_eem, toolchain="vivado") + ps7_config = ps7_config_board_preset + self.ps7_config = ps7_config + + verilog_sources = os.listdir(verilog_dir) + self.add_sources(verilog_dir, *verilog_sources) + + def do_finalize(self, fragment): + try: + XilinxPlatform.do_finalize(self, fragment) + self.add_period_constraint(self.lookup_request(self.default_clk_name, loose=True), self.default_clk_period) + except ValueError: + pass + except ConstraintError: + pass diff --git a/fast-servo/linien-gateware/fast_servo_soc.py b/fast-servo/linien-gateware/fast_servo_soc.py new file mode 100644 index 0000000..02128f5 --- /dev/null +++ b/fast-servo/linien-gateware/fast_servo_soc.py @@ -0,0 +1,290 @@ +# This file is part of Fast Servo Software Package. +# +# Copyright (C) 2023 Jakub Matyas +# Warsaw University of Technology +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program 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. +# +# This program 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 this program. If not, see . + +import os + +from migen import * +from misoc.interconnect import csr_bus +from misoc.interconnect.csr import AutoCSR, CSRStorage + +from fast_servo.gateware.cores.adc import ADC, AUX_ADC_CTRL +from fast_servo.gateware.cores.dac import AUX_DAC_CTRL, DAC +from fast_servo.gateware.cores.pitaya_ps import Axi2Sys, Sys2CSR, SysCDC, SysInterconnect +from fast_servo.gateware.cores.ps7 import PS7 +from fast_servo.gateware.cores.spi_phy import SpiInterface, SpiPhy + + +class CRG(Module): + def __init__(self, platform): + self.ps_rst = Signal() + self.locked = Signal() + self.clock_domains.cd_sys = ClockDomain() + self.clock_domains.cd_sys_double = ClockDomain() + self.clock_domains.cd_idelay = ClockDomain() + + # # # + + # Clk. + clk100 = platform.request("clk100") + platform.add_period_constraint(clk100, 10.0) + self.clkin = clk100 + clk100_buf = Signal() + self.specials += Instance("IBUFG", i_I=clk100, o_O=clk100_buf) + + clk_feedback = Signal() + clk_feedback_buf = Signal() + + clk_sys = Signal() + clk_idelay = Signal() + + self.specials += [ + Instance( + "PLLE2_BASE", + p_BANDWIDTH="OPTIMIZED", + p_DIVCLK_DIVIDE=1, + p_CLKFBOUT_PHASE=0.0, + p_CLKFBOUT_MULT=10, + p_CLKIN1_PERIOD=10.0, + p_REF_JITTER1=0.01, + p_STARTUP_WAIT="FALSE", + i_CLKIN1=clk100_buf, + i_PWRDWN=0, + i_RST=self.ps_rst, + i_CLKFBIN=clk_feedback_buf, + o_CLKFBOUT=clk_feedback, + p_CLKOUT0_DIVIDE=10, + p_CLKOUT0_PHASE=0.0, + p_CLKOUT0_DUTY_CYCLE=0.5, + o_CLKOUT0=clk_sys, # 100 MHz <- sys_clk + p_CLKOUT1_DIVIDE=5, + p_CLKOUT1_PHASE=0.0, + p_CLKOUT1_DUTY_CYCLE=0.5, + o_CLKOUT1=clk_idelay, # 200 MHZ <- 2 * sys_clk = 2*100 MHz + o_LOCKED=self.locked, + ) + ] + + self.specials += Instance("BUFG", i_I=clk_feedback, o_O=clk_feedback_buf) + self.specials += Instance("BUFG", i_I=clk_sys, o_O=self.cd_sys.clk) + self.specials += Instance("BUFG", i_I=clk_idelay, o_O=self.cd_idelay.clk) + self.specials += Instance("BUFG", i_I=clk_idelay, o_O=self.cd_sys_double.clk) + + + # Ignore sys_clk to pll clkin path created by SoC's rst. + platform.add_false_path_constraints(self.cd_sys.clk, self.clkin) + + self.specials += Instance("FD", p_INIT=1, i_D=~self.locked, i_C=self.cd_sys.clk, o_Q=self.cd_sys.rst) + + +class BaseSoC(PS7, AutoCSR): + def __init__(self, platform, passthrouh=False): + PS7.__init__(self, platform) + + # TODO: + # LINIEN SPECIFIC csr_map - in the future should be moved + # to csr_devices list + self.csr_map = { + "adc": 9, + "fp_led0": 10, + "fp_led1": 11, + "fp_led2": 12, + "fp_led3": 13, + "dac": 14, + "adc_aux_ctrl": 15, + "dac_aux_ctrl": 16, + } + self.soc_name = "FastServo" + self.interconnect_slaves = [] + self.csr_devices = [] + + self.platform = platform + + self.submodules.crg = CRG(platform) + + # # # AXI to system bus bridge + self.submodules.axi2sys = Axi2Sys() + self.submodules.sys2csr = Sys2CSR() + self.submodules.syscdc = SysCDC() + self.add_axi_gp_master(self.axi2sys.axi) + + self.comb += [ + self.axi2sys.axi.aclk.eq(ClockSignal("sys")), + self.axi2sys.axi.arstn.eq(self.frstn), + self.syscdc.target.connect(self.sys2csr.sys), + ] + + # ETH LEDS + self.comb += [ + platform.request("eth_led", 0).eq(platform.request("from_eth_phy", 0)), + platform.request("eth_led", 1).eq(platform.request("from_eth_phy", 1)), + ] + + # I2C0 to Si5340 on Fast Servo + self.add_i2c_emio(platform, "ps7_i2c", 0) + + # SPI0 - interface to main ADC and auxiliary ADC + self.add_spi_interface(platform, SpiInterface.ADC) + + # SPI1 - interface to main DAC and auxiliary DAC + self.add_spi_interface(platform, SpiInterface.DAC) + + # self.add_main_adc(platform) + self.submodules.adc = ADC(platform) + self.csr_devices.append("adc") + + # self.add_main_dac(platform) + self.submodules.dac = DAC(platform) + self.csr_devices.append("dac") + + # DEBUG + if passthrouh: + DAC_DATA_WIDTH = 14 + for ch in range(2): + saturate = Signal() + adc_signal = self.adc.data_out[ch] + self.comb += [ + saturate.eq(adc_signal[-3:] != Replicate(adc_signal[-1], 3)), + self.dac.data_in[ch].eq(Mux(saturate, + Cat(Replicate(~adc_signal[-1], DAC_DATA_WIDTH-1), adc_signal[-1]), + adc_signal[:-2])) + ] + + si_5340_nrst = platform.request("nrst") + self.comb += si_5340_nrst.eq(1) + + for i in range(4): + led_pin = platform.request("fp_led", i) + setattr(self.submodules, f"fp_led{i}", LED(led_pin)) + self.csr_devices.append(f"fp_led{i}") + + self.submodules.adc_aux_ctrl = AUX_ADC_CTRL(platform) + self.csr_devices.append("adc_aux_ctrl") + + self.submodules.dac_aux_ctrl = AUX_DAC_CTRL(platform) + self.csr_devices.append("dac_aux_ctrl") + + def add_spi_interface(self, platform, spi_type): + assert isinstance(spi_type, SpiInterface) + n = spi_type.value + + ps7_spi_pads = platform.request("spi", spi_type.value) + spi_phy = SpiPhy(spi_type, ps7_spi_pads) + self.submodules += spi_phy + + ps7_config_spi = { + f"PCW_SPI{n}_GRP_SS0_ENABLE" : "1", + f"PCW_SPI{n}_GRP_SS0_IO" : "EMIO", + f"PCW_SPI{n}_GRP_SS1_ENABLE" : "1", + f"PCW_SPI{n}_GRP_SS1_IO" : "EMIO", + f"PCW_SPI{n}_GRP_SS2_ENABLE" : "1", + f"PCW_SPI{n}_GRP_SS2_IO" : "EMIO", + f"PCW_SPI{n}_PERIPHERAL_ENABLE" : "1", + f"PCW_SPI{n}_SPI{n}_IO" : "EMIO", + f"PCW_SPI_PERIPHERAL_CLKSRC" : "IO PLL", + f"PCW_SPI_PERIPHERAL_DIVISOR0" : "10", + f"PCW_SPI_PERIPHERAL_FREQMHZ" : "166.666666", + } + self.add_ps7_config(ps7_config_spi) + + self.cpu_params.update({ + f"o_SPI{n}_MISO_O" : spi_phy.ps_miso_o, + f"o_SPI{n}_MISO_T" : spi_phy.ps_miso_t, + f"o_SPI{n}_MOSI_O" : spi_phy.ps_mosi_o, + f"o_SPI{n}_MOSI_T" : spi_phy.ps_mosi_t, + f"o_SPI{n}_SCLK_O" : spi_phy.ps_sclk_o, + f"o_SPI{n}_SCLK_T" : spi_phy.ps_sclk_t, + + f"i_SPI{n}_SCLK_I" : spi_phy.ps_sclk_i, + f"i_SPI{n}_MOSI_I" : spi_phy.ps_mosi_i, + f"i_SPI{n}_MISO_I" : spi_phy.ps_miso_i, + f"i_SPI{n}_SS_I" : spi_phy.ps_ss_i, + f"o_SPI{n}_SS_O" : spi_phy.ps_ss[0], + f"o_SPI{n}_SS1_O" : spi_phy.ps_ss[1], + f"o_SPI{n}_SS2_O" : spi_phy.ps_ss[2], + f"o_SPI{n}_SS_T" : spi_phy.ps_ss_t, + }) + + def add_interconnect_slave(self, slave): + self.interconnect_slaves.append(slave) + + def get_csr_dev_address(self, name, memory): + # TODO: switch to MiSoC-like address retriving from + # the list of CSR devices + if memory is not None: + name = name + "_" + memory.name_override + try: + return self.csr_map[name] + except KeyError: + return None + + def soc_finalize(self): + # Overload this method to customize SystemInterconnect + # and csrbanks - especially useful in Linien + self.add_interconnect_slave(self.syscdc.source) + self.submodules.interconnect = SysInterconnect( + self.axi2sys.sys, + *self.interconnect_slaves + ) + self.submodules.csrbanks = csr_bus.CSRBankArray(self, + self.get_csr_dev_address) + self.submodules.csrcon = csr_bus.Interconnect( + self.sys2csr.csr, self.csrbanks.get_buses() + ) + + def do_finalize(self): + self.soc_finalize() + PS7.do_finalize(self) + + def build(self, *args, **kwargs): + self.platform.build(self, *args, **kwargs) + +class LED(Module, AutoCSR): + def __init__(self, led): + + self.led_out = CSRStorage(1) + + self.comb += led.eq(self.led_out.storage) + + +if __name__ == "__main__": + import subprocess + from fast_servo.gateware.fast_servo_platform import Platform + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", default=False, help="Hardwire ADC data to DAC") + args = parser.parse_args() + + + root_path = os.getcwd() + platform = Platform() + fast_servo = BaseSoC(platform, passthrouh=args.debug) + + build_dir = "builds/fast_servo_gw_debug" if args.debug else"builds/fast_servo_gw" + build_name = "top" + fast_servo.build(build_dir=build_dir, build_name=build_name, run=True) + os.chdir(os.path.join(root_path, build_dir)) + with open(f"{build_name}.bif", "w") as f: + f.write(f"all:\n{{\n\t{build_name}.bit\n}}") + + cmd = f"bootgen -image {build_name}.bif -arch zynq -process_bitstream bin -w on".split(" ") + subprocess.run(cmd) + + + diff --git a/fast-servo/linien-gateware/verilog/LTC2195.v b/fast-servo/linien-gateware/verilog/LTC2195.v new file mode 100644 index 0000000..31309f1 --- /dev/null +++ b/fast-servo/linien-gateware/verilog/LTC2195.v @@ -0,0 +1,162 @@ +/////////////////////////////////////////////////////////////////////////////// +// LTC2195.v +// +// 8/03/21 +// Jakub Matyas +// +// LTC2195 controller. +// +// +/////////////////////////////////////////////////////////////////////////////// + +// `include "timescale.v" +`timescale 1ns/1ps // this was in the SelectIO design + +module LTC2195( + input rst_in, + input clk200, + input DCO, + input DCO_2D, + input FR_in_p, + input FR_in_n, + input [3:0] D0_in_p, + input [3:0] D0_in_n, + input [3:0] D1_in_p, + input [3:0] D1_in_n, + input bitslip, + input [4:0] delay_val, + + output reg [15:0] ADC0_out, + output reg [15:0] ADC1_out, + output reg [3:0] FR_out, + output wire [8:0] o_data_from_pins, + output idelay_rdy +); + +// /////////////////////////////////////////////////////////////////////////////// +// // LVDS inputs + +localparam N_BITS = 4; +localparam N_LANES = 9; // for each channel 4 lanes + 1 lane for FRAME +wire [N_LANES-1:0] data_in_p, data_in_n, data_in_from_pins, data_in_from_pins_delay; +assign data_in_p = {FR_in_p, D1_in_p, D0_in_p}; +assign data_in_n = {FR_in_n, D1_in_n, D0_in_n}; +assign o_data_from_pins = data_in_from_pins_delay; + + +wire [N_LANES*4 -1:0] data_out; +wire [35:0 ]data_out_mod; + + +assign data_out_mod = {~data_out[35:24], data_out[23:20], ~data_out[19:16], data_out[15:0]}; + + +always @(posedge DCO_2D) begin + ADC0_out <= { + data_out_mod[0], data_out_mod[4], data_out_mod[1], data_out_mod[5], + data_out_mod[2], data_out_mod[6], data_out_mod[3], data_out_mod[7], + data_out_mod[8], data_out_mod[12], data_out_mod[9], data_out_mod[13], + data_out_mod[10], data_out_mod[14], data_out_mod[11], data_out_mod[15] + }; + + ADC1_out <= { + data_out_mod[16 + 0], data_out_mod[16 + 4], data_out_mod[16 + 1], data_out_mod[16 + 5], + data_out_mod[16 + 2], data_out_mod[16 + 6], data_out_mod[16 + 3], data_out_mod[16 + 7], + data_out_mod[16 + 8], data_out_mod[16 + 12], data_out_mod[16 + 9], data_out_mod[16 + 13], + data_out_mod[16 + 10], data_out_mod[16 + 14], data_out_mod[16 + 11], data_out_mod[16 + 15] + }; + FR_out <= {data_out_mod[32], data_out_mod[33], data_out_mod[34], data_out_mod[35]}; // value that arrived first is LSB, therefore reversing order +end + +wire s_idelay_rdy; +IDELAYCTRL IDELAYCTRL_inst ( + .RDY(s_idelay_rdy), // 1-bit output: Ready output + .REFCLK(clk200), // 1-bit input: Reference clock input + .RST(s_rst) // 1-bit input: Active high reset input +); + +assign idelay_rdy = s_idelay_rdy; + + +reg s_rst; +reg [5:0] rst_cnt; +wire serdes_o; + +always @(posedge DCO_2D) begin + if (rst_in) begin + s_rst <= 1'b1; + rst_cnt <= 'b0; + end else begin + if (rst_cnt == 22) + s_rst <= 'b0; + else + rst_cnt <= rst_cnt + 1; + end +end + +genvar lane; +generate for (lane=0; lane | WR | + * WR DATA ----> | | ----------- + * WR RESPONSE <---- | CH | | + * \------/ /--------\ + * | SIMPLE | ---> WR/RD ADDRRESS + * AXI | | ---> WR DATA + * | RP | <--- RD DATA + * | BUS | <--- ACKNOWLEDGE + * /------\ \--------/ + * RD ADDRESS ----> | RD | | + * RD DATA <---- | CH | ----------- + * \------/ + * + * + * Because AXI bus is quite complex simplier bus was created. + * + * It combines write and read channel, where write has bigger priority. Command + * is then send forward to red pitaya bus. When wite or read acknowledge is + * received AXI response is created and new AXI is accepted. + * + * To prevent AXI lockups because no response is received, this slave creates its + * own after 32 cycles (ack_cnt). + * + */ + + + + +module axi_slave +#( +parameter AXI_DW = 64 , // data width (8,16,...,1024) +parameter AXI_AW = 32 , // address width +parameter AXI_IW = 8 , // ID width +parameter AXI_SW = AXI_DW >> 3 // strobe width - 1 bit for every data byte +) +( + // global signals + input axi_clk_i , //!< AXI global clock + input axi_rstn_i , //!< AXI global reset + + // axi write address channel + input [ AXI_IW-1: 0] axi_awid_i , //!< AXI write address ID + input [ AXI_AW-1: 0] axi_awaddr_i , //!< AXI write address + input [ 4-1: 0] axi_awlen_i , //!< AXI write burst length + input [ 3-1: 0] axi_awsize_i , //!< AXI write burst size + input [ 2-1: 0] axi_awburst_i , //!< AXI write burst type + input [ 2-1: 0] axi_awlock_i , //!< AXI write lock type + input [ 4-1: 0] axi_awcache_i , //!< AXI write cache type + input [ 3-1: 0] axi_awprot_i , //!< AXI write protection type + input axi_awvalid_i , //!< AXI write address valid + output axi_awready_o , //!< AXI write ready + + // axi write data channel + input [ AXI_IW-1: 0] axi_wid_i , //!< AXI write data ID + input [ AXI_DW-1: 0] axi_wdata_i , //!< AXI write data + input [ AXI_SW-1: 0] axi_wstrb_i , //!< AXI write strobes + input axi_wlast_i , //!< AXI write last + input axi_wvalid_i , //!< AXI write valid + output axi_wready_o , //!< AXI write ready + + // axi write response channel + output [ AXI_IW-1: 0] axi_bid_o , //!< AXI write response ID + output reg [ 2-1: 0] axi_bresp_o , //!< AXI write response + output reg axi_bvalid_o , //!< AXI write response valid + input axi_bready_i , //!< AXI write response ready + + // axi read address channel + input [ AXI_IW-1: 0] axi_arid_i , //!< AXI read address ID + input [ AXI_AW-1: 0] axi_araddr_i , //!< AXI read address + input [ 4-1: 0] axi_arlen_i , //!< AXI read burst length + input [ 3-1: 0] axi_arsize_i , //!< AXI read burst size + input [ 2-1: 0] axi_arburst_i , //!< AXI read burst type + input [ 2-1: 0] axi_arlock_i , //!< AXI read lock type + input [ 4-1: 0] axi_arcache_i , //!< AXI read cache type + input [ 3-1: 0] axi_arprot_i , //!< AXI read protection type + input axi_arvalid_i , //!< AXI read address valid + output axi_arready_o , //!< AXI read address ready + + // axi read data channel + output [ AXI_IW-1: 0] axi_rid_o , //!< AXI read response ID + output reg [ AXI_DW-1: 0] axi_rdata_o , //!< AXI read data + output reg [ 2-1: 0] axi_rresp_o , //!< AXI read response + output reg axi_rlast_o , //!< AXI read last + output reg axi_rvalid_o , //!< AXI read response valid + input axi_rready_i , //!< AXI read response ready + + // RP system read/write channel + output [ AXI_AW-1: 0] sys_addr_o , //!< system bus read/write address. + output [ AXI_DW-1: 0] sys_wdata_o , //!< system bus write data. + output reg [ AXI_SW-1: 0] sys_sel_o , //!< system bus write byte select. + output reg sys_wen_o , //!< system bus write enable. + output reg sys_ren_o , //!< system bus read enable. + input [ AXI_DW-1: 0] sys_rdata_i , //!< system bus read data. + input sys_err_i , //!< system bus error indicator. + input sys_ack_i //!< system bus acknowledge signal. +); + + +//--------------------------------------------------------------------------------- +// +// AXI slave Module +wire ack ; +reg [ 6-1: 0] ack_cnt ; + +reg rd_do ; +reg [ AXI_IW-1: 0] rd_arid ; +reg [ AXI_AW-1: 0] rd_araddr ; +reg rd_error ; +wire rd_errorw ; + +reg wr_do ; +reg [ AXI_IW-1: 0] wr_awid ; +reg [ AXI_AW-1: 0] wr_awaddr ; +reg [ AXI_IW-1: 0] wr_wid ; +reg [ AXI_DW-1: 0] wr_wdata ; +reg wr_error ; +wire wr_errorw ; + +assign wr_errorw = (axi_awlen_i != 4'h0) || (axi_awsize_i != 3'b010); // error if write burst and more/less than 4B transfer +assign rd_errorw = (axi_arlen_i != 4'h0) || (axi_arsize_i != 3'b010); // error if read burst and more/less than 4B transfer + +always @(posedge axi_clk_i) begin + if (axi_rstn_i == 1'b0) begin + rd_do <= 1'b0 ; + rd_error <= 1'b0 ; + end + else begin + if (axi_arvalid_i && !rd_do && !axi_awvalid_i && !wr_do) // accept just one read request - write has priority + rd_do <= 1'b1 ; + else if (axi_rready_i && rd_do && ack) + rd_do <= 1'b0 ; + + if (axi_arvalid_i && axi_arready_o) begin // latch ID and address + rd_arid <= axi_arid_i ; + rd_araddr <= axi_araddr_i ; + rd_error <= rd_errorw ; + end + end +end + + +always @(posedge axi_clk_i) begin + if (axi_rstn_i == 1'b0) begin + wr_do <= 1'b0 ; + wr_error <= 1'b0 ; + end + else begin + if (axi_awvalid_i && !wr_do && !rd_do) // accept just one write request - if idle + wr_do <= 1'b1 ; + else if (axi_bready_i && wr_do && ack) + wr_do <= 1'b0 ; + + if (axi_awvalid_i && axi_awready_o) begin // latch ID and address + wr_awid <= axi_awid_i ; + wr_awaddr <= axi_awaddr_i ; + wr_error <= wr_errorw ; + end + + if (axi_wvalid_i && wr_do) begin // latch ID and write data + wr_wid <= axi_wid_i ; + wr_wdata <= axi_wdata_i ; + end + end +end + + + + + +assign axi_awready_o = !wr_do && !rd_do ; +assign axi_wready_o = (wr_do && axi_wvalid_i) || (wr_errorw && axi_wvalid_i) ; +assign axi_bid_o = wr_awid ; +//assign axi_bresp_o = {wr_error,1'b0} ; // 2'b10 SLVERR +//assign axi_bvalid_o = (sys_wen_o && axi_bready_i) || (wr_error && axi_bready_i) ; + +assign axi_arready_o = !rd_do && !wr_do && !axi_awvalid_i ; +assign axi_rid_o = rd_arid ; +//assign axi_rdata_o = sys_rdata_i ; + +always @(posedge axi_clk_i) begin + if (axi_rstn_i == 1'b0) begin + axi_bvalid_o <= 1'b0 ; + axi_bresp_o <= 2'h0 ; + axi_rlast_o <= 1'b0 ; + axi_rvalid_o <= 1'b0 ; + axi_rresp_o <= 2'h0 ; + end + else begin + axi_bvalid_o <= wr_do && ack ; + axi_bresp_o <= {(wr_error || ack_cnt[5]),1'b0} ; // 2'b10 SLVERR 2'b00 OK + axi_rlast_o <= rd_do && ack ; + axi_rvalid_o <= rd_do && ack ; + axi_rresp_o <= {(rd_error || ack_cnt[5]),1'b0} ; // 2'b10 SLVERR 2'b00 OK + axi_rdata_o <= sys_rdata_i ; + end +end + +// acknowledge protection +always @(posedge axi_clk_i) begin + if (axi_rstn_i == 1'b0) begin + ack_cnt <= 6'h0 ; + end + else begin + if ((axi_arvalid_i && axi_arready_o) || (axi_awvalid_i && axi_awready_o)) // rd || wr request + ack_cnt <= 6'h1 ; + else if (ack) + ack_cnt <= 6'h0 ; + else if (|ack_cnt) + ack_cnt <= ack_cnt + 6'h1 ; + end +end + +assign ack = sys_ack_i || ack_cnt[5] || (rd_do && rd_errorw) || (wr_do && wr_errorw); // bus acknowledge or timeout or error + + + + + +//------------------------------------------ +// Simple slave interface + +always @(posedge axi_clk_i) begin + if (axi_rstn_i == 1'b0) begin + sys_wen_o <= 1'b0 ; + sys_ren_o <= 1'b0 ; + sys_sel_o <= {AXI_SW{1'b0}} ; + end + else begin + sys_wen_o <= wr_do && axi_wvalid_i && !wr_errorw ; + sys_ren_o <= axi_arvalid_i && axi_arready_o && !rd_errorw ; + sys_sel_o <= {AXI_SW{1'b1}} ; + end +end + +assign sys_addr_o = rd_do ? rd_araddr : wr_awaddr ; +assign sys_wdata_o = wr_wdata ; + + + + + +endmodule diff --git a/fast-servo/linien-gateware/verilog/bus_clk_bridge.v b/fast-servo/linien-gateware/verilog/bus_clk_bridge.v new file mode 100644 index 0000000..af2ee69 --- /dev/null +++ b/fast-servo/linien-gateware/verilog/bus_clk_bridge.v @@ -0,0 +1,128 @@ +/** + * $Id: bus_clk_bridge.v 961 2014-01-21 11:40:39Z matej.oblak $ + * + * @brief Red Pitaya system bus clock crossing bridge. + * + * @Author Matej Oblak + * + * (c) Red Pitaya http://www.redpitaya.com + * + * This part of code is written in Verilog hardware description language (HDL). + * Please visit http://en.wikipedia.org/wiki/Verilog + * for more details on the language used herein. + */ + + + +/** + * GENERAL DESCRIPTION: + * + * Clock domain bridge for system bus. + * + * + * /------\ + * SYSTEM | | PROCESSING + * BUS <-----> | SYNC | <-----> BUS + * | | + * \------/ + * + * + * System bus runs on one clock domain while processing runs on separate. To + * simplify transition of writing and reading data this bridge was created. + * + */ + + + + +module bus_clk_bridge +( + // system bus + input sys_clk_i , //!< bus clock + input sys_rstn_i , //!< bus reset - active low + input [ 32-1: 0] sys_addr_i , //!< bus address + input [ 32-1: 0] sys_wdata_i , //!< bus write data + input [ 4-1: 0] sys_sel_i , //!< bus write byte select + input sys_wen_i , //!< bus write enable + input sys_ren_i , //!< bus read enable + output [ 32-1: 0] sys_rdata_o , //!< bus read data + output sys_err_o , //!< bus error indicator + output sys_ack_o , //!< bus acknowledge signal + + + // Destination bus + input clk_i , //!< clock + input rstn_i , //!< reset - active low + output reg [ 32-1: 0] addr_o , //!< address + output reg [ 32-1: 0] wdata_o , //!< write data + output wen_o , //!< write enable + output ren_o , //!< read enable + input [ 32-1: 0] rdata_i , //!< read data + input err_i , //!< error indicator + input ack_i //!< acknowledge signal +); + + + +//--------------------------------------------------------------------------------- +// Synchronize signals between clock domains + +reg sys_rd ; +reg sys_wr ; +reg sys_do ; +reg [ 2-1: 0] sys_sync ; +reg sys_done ; +reg dst_do ; +reg [ 2-1: 0] dst_sync ; +reg dst_done ; + +always @(posedge sys_clk_i) begin + if (sys_rstn_i == 1'b0) begin + sys_rd <= 1'b0 ; + sys_wr <= 1'b0 ; + sys_do <= 1'b0 ; + sys_sync <= 2'h0 ; + sys_done <= 1'b0 ; + end + else begin + + if ((sys_do == sys_done) && (sys_wen_i || sys_ren_i)) begin + addr_o <= sys_addr_i ; + wdata_o <= sys_wdata_i ; + sys_rd <= sys_ren_i ; + sys_wr <= sys_wen_i ; + sys_do <= !sys_do ; + end + + sys_sync <= {sys_sync[0], dst_done}; + sys_done <= sys_sync[1]; + end +end + + +always @(posedge clk_i) begin + if (rstn_i == 1'b0) begin + dst_do <= 1'b0 ; + dst_sync <= 2'h0 ; + dst_done <= 1'b0 ; + end + else begin + dst_sync <= {dst_sync[0], sys_do}; + dst_do <= dst_sync[1]; + + if (ack_i && (dst_do != dst_done)) + dst_done <= dst_do; + end +end + +assign ren_o = sys_rd && (dst_sync[1]^dst_do); +assign wen_o = sys_wr && (dst_sync[1]^dst_do); + + +assign sys_rdata_o = rdata_i ; +assign sys_err_o = err_i ; +assign sys_ack_o = sys_done ^ sys_sync[1] ; + + + +endmodule diff --git a/fast-servo/linien-gateware/verilog/spi2threewire.v b/fast-servo/linien-gateware/verilog/spi2threewire.v new file mode 100644 index 0000000..37043c8 --- /dev/null +++ b/fast-servo/linien-gateware/verilog/spi2threewire.v @@ -0,0 +1,111 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: WUT +// Engineer: Jakub Matyas +// +// Create Date: 03/02/2023 01:39:08 PM +// Design Name: +// Module Name: spi2threewire +// Project Name: +// Target Devices: +// Tool Versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// + +module spi2threewire ( + output ps_sclk_i, + input ps_sclk_o, + input ps_sclk_t, + + output ps_mosi_i, + input ps_mosi_o, + input ps_mosi_t, + + output ps_miso_i, + input ps_miso_o, + input ps_miso_t, + + output ps_ss_i, + input [2:0] ps_ss, + input ps_ss_t, + + output o_ss, + output o_sclk, + inout sdio + +); + +assign ps_sclk_i = 1'b0; +assign ps_mosi_i = 1'b0; +assign ps_ss_i = 1'b1; + + +reg [3:0] bit_count = 'd0; + +// rd_wr_n - whether it will be READ transaction or WRITE +// first bit of the command +reg rd_wr_n = 'd0; +reg sdio_buffer_direction = 'd0; + +wire s_sclk; +wire s_csn; +wire s_sdio_buffer_direction; + +assign s_sclk = ps_sclk_o; +assign s_csn = ps_ss[0]; +assign o_ss = s_csn; + +// 1 if there is a SPI tranmsission going on (CS_N is LOW) +// AND transmission is of READ type +// 0 otherwise +assign s_sdio_buffer_direction = sdio_buffer_direction & ~s_csn; + +always @(posedge s_sclk or posedge s_csn) begin + if (s_csn == 1'b1) begin + // after the transmission, fill bit counter with ZEROS + // and zero transmission type (rd_wr_n) + bit_count <= 4'd0; + rd_wr_n <= 1'b0; + end else begin + // on every rising edge increment bit counter + // sample first bit to get the knowledge of the transmission type + bit_count <= (bit_count < 4'd15) ? bit_count + 1'b1 : bit_count; + if (bit_count == 4'b0) + rd_wr_n <= ps_mosi_o; + end +end + +always @(negedge s_sclk or posedge s_csn) begin + if (s_csn == 1'b1) + sdio_buffer_direction <= 1'b0; + else begin + if (bit_count == 4'd8) + // after the 8th bit, on falling edge, + // set the SDIO buffer direction + // accordingly to the transmission type + sdio_buffer_direction <= rd_wr_n; + end +end + + +IOBUF IOBUF_inst ( + .O(ps_miso_i), + .IO(sdio), + .I(ps_mosi_o), + .T(s_sdio_buffer_direction) // 3-state enable input, high=input (from ext), low=output +); + +OBUFT sclk_buf ( + .O(o_sclk), + .I(ps_sclk_o), + .T('b0) +); + +endmodule \ No newline at end of file