fpga: x4xx: Refactor MB CPLD code for future devices
Original-commit: e2a79712a7949ffec88135236c744dc5d8bde217
This commit is contained in:
committed by
Martin Braun
parent
e9a6fe4e2e
commit
ffdcc016cc
@@ -0,0 +1,437 @@
|
||||
#
|
||||
# Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
# Description:
|
||||
#
|
||||
# Common timing constraints for the X4xx's motherboard CPLD.
|
||||
#
|
||||
|
||||
set_time_format -unit ns -decimal_places 3
|
||||
|
||||
#####################################################################
|
||||
# General
|
||||
#####################################################################
|
||||
# For a couple of 3.3V interfaces the buffer SN74AVC4T774RSVR is used to
|
||||
# increase the drive strength. For reuse we define the timings constants here.
|
||||
# For direction A to B and B to A the maximum timing varies by 0.1 ns. Taking
|
||||
# the maximum of both.
|
||||
set buffer_prop_min 0.100
|
||||
set buffer_prop_max 2.400
|
||||
|
||||
#####################################################################
|
||||
# Main Clocks
|
||||
#####################################################################
|
||||
## Input clocks.
|
||||
# Reliable clock: 100.0 MHz
|
||||
set CLK_100_period 10.000
|
||||
create_clock -name CLK_100 -period $CLK_100_period [get_ports CLK_100]
|
||||
|
||||
# internal PLL derived clock
|
||||
derive_pll_clocks
|
||||
# provide name for derived clocks
|
||||
set CLK_250 [get_clocks {*clk[1]}]
|
||||
|
||||
# PLL output pins of the generated 50 MHz clock for internal processing
|
||||
set clk50_period 20.000
|
||||
set pll_clk_out_pin [get_pins {pll_inst|altpll_component|auto_generated|pll1|clk[0]}]
|
||||
|
||||
set clk250_period 4.000
|
||||
|
||||
# PLL reference clock: 64 MHz (maximum)
|
||||
set prc_clock_period 15.625
|
||||
create_clock -name PLL_REF_CLK -period $prc_clock_period [get_ports PLL_REF_CLK]
|
||||
|
||||
#####################################################################
|
||||
# Timing exceptions
|
||||
#####################################################################
|
||||
## SPI slaves
|
||||
# Delay path for all synchronizers is based on the period of the
|
||||
# faster clock domain (50 MHz derived by the PLL from 100 MHz reliable clock).
|
||||
set clk50_period [expr {$CLK_100_period * 2}]
|
||||
|
||||
set_max_delay -to [get_registers *synchronizer_false_path\|value\[0\]\[*\]] \
|
||||
$clk50_period
|
||||
|
||||
# sclk data to CLK_100
|
||||
set_max_delay -from [get_registers *spi_slave_async\|received_word\[*\]] \
|
||||
-to [get_registers *spi_slave_async\|data_out\[*\]] \
|
||||
$clk50_period
|
||||
# PLL driven data to sclk
|
||||
set_max_delay -from [get_clocks {pll_inst*}] \
|
||||
-to [get_registers *spi_slave_async\|transmit_bits\[*\]] \
|
||||
$clk50_period
|
||||
|
||||
#####################################################################
|
||||
# Power supply clocks, LEDs, DIO direction
|
||||
#####################################################################
|
||||
# Change all output signals in this section within one clock period of the
|
||||
# driving clocks.
|
||||
|
||||
# Power supply clocks
|
||||
set power_supply_clocks_outputs [get_ports {PWR_SUPPLY_CLK_*}]
|
||||
set_min_delay -to $power_supply_clocks_outputs 0
|
||||
set_max_delay -to $power_supply_clocks_outputs $CLK_100_period
|
||||
|
||||
# LED signals in db specific sdc
|
||||
|
||||
# DIO direction
|
||||
set dio_outputs [get_ports {DIO_DIRECTION_A[*] DIO_DIRECTION_B[*]}]
|
||||
set_min_delay -to $dio_outputs 0
|
||||
set_max_delay -to $dio_outputs $clk50_period
|
||||
|
||||
# Power control
|
||||
set pwr_ctrl_outputs [get_ports {IPASS_POWER_DISABLE PWR_EN_5V_OSC_100 PWR_EN_5V_OSC_122_88}]
|
||||
set_min_delay -to $pwr_ctrl_outputs 0
|
||||
set_max_delay -to $pwr_ctrl_outputs $clk50_period
|
||||
|
||||
# Power fault inputs
|
||||
# Virtual clocks for constraining inputs. Using an odd clock period to
|
||||
# make sure any uncovered paths will result in timing errors due to short setup
|
||||
# or hold path.
|
||||
set power_fault_inputs [get_ports {IPASS_POWER_EN_FAULT[*]}]
|
||||
create_clock -name virtual_async_in_clk -period 4.567
|
||||
set_input_delay -clock virtual_async_in_clk 0 $power_fault_inputs
|
||||
|
||||
#####################################################################
|
||||
# FPGA <-> MB CPLD PL SPI interface
|
||||
#####################################################################
|
||||
# Create clock for the PL's SPI interface.
|
||||
# PRC at least divided by 2 by the SPI Master on FPGA
|
||||
set pl_sclk_period [expr {2 * $prc_clock_period}]
|
||||
create_clock -name pl_sclk -period $pl_sclk_period [get_registers mb_cpld_sclk]
|
||||
|
||||
# The SPI PL master (on the FPGA) is designed as a system synchronous
|
||||
# interface using PLL_REF_CLK.
|
||||
# The FPGA output constraints are required to calculate the windows
|
||||
# at CPLD of valid data
|
||||
# They are derived iteratively from the FPGA design ensuring a large
|
||||
# valid data period.
|
||||
set pl_spi_fpga_min_out 0.000
|
||||
set pl_spi_fpga_max_out 11.000
|
||||
|
||||
# The longest trace on the PL SPI interface is (sssuming 170.0 ps/in)
|
||||
# Longest trace | Trace length | Trace delay
|
||||
# CS_0 | 7.143 in | 1.215 ns
|
||||
set pl_spi_board_delay 1.215
|
||||
|
||||
# This path also contains a level translator which has a typical
|
||||
# switching time of 2.7 ns. Let's add a margin of 1 ns as worst
|
||||
# case estimation
|
||||
set pl_level_trans_delay 3.700
|
||||
|
||||
# CPLD and FPGA both use PLL reference clock from a common clock chip.
|
||||
# The traces from that clock chip to the ICs are not length matched
|
||||
# Assume a worst case clock difference of 0.5 ns at the IC inputs.
|
||||
# There is no direction defined. The clock can arrive faster or slower
|
||||
# on one IC.
|
||||
set pl_clock_diff 0.500
|
||||
|
||||
set pl_slave_inputs [get_ports {PL_CPLD_SCLK PL_CPLD_MOSI PL_CPLD_CS_N[*]}]
|
||||
# calculate output delays back from capturing edge, add board delay, level translator and clock difference
|
||||
set_input_delay -clock PLL_REF_CLK \
|
||||
-max [expr {$prc_clock_period - $pl_spi_fpga_max_out + $pl_spi_board_delay + $pl_level_trans_delay + $pl_clock_diff}] \
|
||||
$pl_slave_inputs
|
||||
# Assuming data is going without any delay, clock is arriving early at CPLD.
|
||||
# Negate minimum output delay as it is defined from the change to the start clock edge.
|
||||
set_input_delay -clock PLL_REF_CLK \
|
||||
-min [expr {- $pl_spi_fpga_min_out - $pl_clock_diff}] \
|
||||
$pl_slave_inputs
|
||||
|
||||
# ensure large data valid window for the FPGA
|
||||
# those values are used in the FPGA / DB CPLDs
|
||||
# to calculate the input delay
|
||||
# those values are maximum integer values to still meet timing
|
||||
set pl_spi_cpld_min_out -1.000
|
||||
set pl_spi_cpld_max_out 8.000
|
||||
|
||||
set pl_slave_outputs [get_ports {PL_CPLD_MISO}]
|
||||
set_output_delay -clock PLL_REF_CLK -max $pl_spi_cpld_max_out $pl_slave_outputs
|
||||
set_output_delay -clock PLL_REF_CLK -min $pl_spi_cpld_min_out $pl_slave_outputs
|
||||
|
||||
#####################################################################
|
||||
# FPGA <-> MB CPLD PS SPI interface
|
||||
#####################################################################
|
||||
# Assume the PS SPI clock is maximum 5 MHz.
|
||||
# It is driven from another source and provided with the data.
|
||||
set ps_sclk_period 200.000
|
||||
create_clock -name ps_sclk -period $ps_sclk_period [get_ports PS_CPLD_SCLK]
|
||||
|
||||
# The SPI PS master (on the FPGA) is wired through the MIO (Multiplexed I/O)
|
||||
# pins, meaning that the timing characteristics of the interface come from
|
||||
# the controller itself (i.e. no timed routing through PL).
|
||||
# Based on the SPI master controller specification (DS925: Table 48),
|
||||
# one may define the min/max input/output delay constraints.
|
||||
set ps_spi_tco_min -2.000
|
||||
set ps_spi_tco_max 5.000
|
||||
set ps_spi_miso_setup -2.000
|
||||
set ps_spi_miso_hold [expr {0.3 * $ps_sclk_period}]
|
||||
|
||||
# Use the worst-case board propagation delays.
|
||||
# Assuming 170.0 ps/in.
|
||||
# Longest trace | Trace length | Trace delay
|
||||
# CS0_n | 4.735 in | 0.805 ns
|
||||
# --------------------------------------------
|
||||
set ps_spi_board_delay 0.805
|
||||
|
||||
set ps_slave_inputs [get_ports {PS_CPLD_MOSI PS_CPLD_CS_N[*]}]
|
||||
# clock is immediately available, data is taking maximum time
|
||||
# SPI data in CPOL=CPHA=1 is driven on the falling sclk edge
|
||||
set ps_sclk_max_in_delay [expr {$ps_spi_tco_max + $ps_spi_board_delay}]
|
||||
set_input_delay -clock ps_sclk -clock_fall \
|
||||
-max $ps_sclk_max_in_delay \
|
||||
$ps_slave_inputs
|
||||
# fast data and clock delayed (reducing data delay)
|
||||
set_input_delay -clock ps_sclk -clock_fall \
|
||||
-min [expr {$ps_spi_tco_min - $ps_spi_board_delay}] \
|
||||
$ps_slave_inputs
|
||||
|
||||
set ps_slave_outputs [get_ports {PS_CPLD_MISO}]
|
||||
# use only half the frequency because falling edge is driving data
|
||||
set_output_delay -clock ps_sclk \
|
||||
-max [expr {$ps_spi_miso_setup + 2*$ps_spi_board_delay}] \
|
||||
$ps_slave_outputs
|
||||
# use hold requirement only as clock and data propagation further
|
||||
# delay the signal
|
||||
set_output_delay -clock ps_sclk \
|
||||
-min [expr {-$ps_spi_miso_hold}] \
|
||||
$ps_slave_outputs
|
||||
|
||||
# Chip select signals are captured for binary decoding in 250 MHz clock domain.
|
||||
# To be able to specify a maximum delay for the data path only a second set of
|
||||
# input delays is added to the root clock of the 250 MHz domain.
|
||||
set_input_delay -add_delay -clock CLK_100 0 [get_ports {PS_CPLD_CS_N[*]}]
|
||||
# Declare paths between the 2 clock domains as false paths
|
||||
set_false_path -from [get_clocks {CLK_100}] -to [get_ports {PS_CPLD_MISO}]
|
||||
set_false_path -from [get_clocks {ps_sclk}] -to [get_registers {synchronizer:ps_spi_input_sync_inst*}]
|
||||
# Specify maximum data path delay
|
||||
set_max_delay -from [get_ports {PS_CPLD_CS_N[*]}] -to $CLK_250 $clk250_period
|
||||
|
||||
#####################################################################
|
||||
# MB CPLD PS SPI passthrough common
|
||||
#####################################################################
|
||||
###### Binary CS decoding ######
|
||||
# The CS outputs for the external SPI slaves are driven from a 250 MHz clock to
|
||||
# ensure glitch free switching after binary encoding. Additionally those signals
|
||||
# have to meet the setup and hold requirements of the SPI slaves operating at
|
||||
# ps_sclk (5 MHz). CS lines typically are asserted half a clock period of sclk
|
||||
# before any active edge of sclk. The constraints below are using multi-cycle
|
||||
# paths to provide the placer with information about the clock multiplier from
|
||||
# ps_sclk to 250 MHz. Furthermore they incorporate the time required for
|
||||
# decoding by lowering the clock multiplier as shown in the waveform below
|
||||
# (multiplier is not shown correctly).
|
||||
#
|
||||
# ps_sclk -\__________________________________________________/--------
|
||||
# 250 MHz _/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\-
|
||||
# CS @ CPLD input X>--------------- stable ------------------------------------
|
||||
# CS @ CPLD output ---------<XXXXXXX>-------------- stable ---------------------
|
||||
# |<----->| min decoding delay
|
||||
# |<----->| change window
|
||||
# --------------->| SPI slave hold requirement
|
||||
# SPI slave setup requirement |<-------------------------------->|
|
||||
#
|
||||
# Get port to apply the multi-cycle constraint.
|
||||
set binary_cs_ports [get_ports {LMK32_CS_N TPM_CS_N PHASE_DAC_CS_N CLK_DB_CS_N}]
|
||||
# Determine number of full 250 MHz periods within half a period of ps_sclk.
|
||||
set ps_spi_clock_divider [expr {int($ps_sclk_period/$clk250_period/2)}]
|
||||
# Setup multi-cycle accounts for
|
||||
# - one clock cycle data path delay from port to first register stage
|
||||
# - one clock cycle to resolve meta-stability
|
||||
# - up to 3 register stages internally (port to ps_cpld_cs_n_shift3)
|
||||
# - one output register stage (registers on each $binary_cs_ports)
|
||||
# Static timing analysis will take the data path from register to output port
|
||||
# into account.
|
||||
# The number of 250 MHz periods is reduced by a total of 7 clock cycles (listed
|
||||
# above) to match the SPI slave setup requirement time shown in the waveform
|
||||
# above. The slave's setup time in ps_sclk domain is specified below for each
|
||||
# individual slave.
|
||||
set ps_spi_setup_multicycle [expr {$ps_spi_clock_divider - 7}]
|
||||
set_multicycle_path -setup -start -to $binary_cs_ports $ps_spi_setup_multicycle
|
||||
# Hold multicycle accounts for
|
||||
# - min 2 synchronization register stages internally (ps_cpld_cs_n_shift2)
|
||||
# (= min one clock cycle delay as data could arrive just before setup
|
||||
# requirement of first register stages assuming no data delay)
|
||||
# - one output register stage
|
||||
# Static timing analysis will take the data path from register to output port
|
||||
# into account.
|
||||
# As the clock edge for hold analysis is shifted with the setup edge the number
|
||||
# of multi cycles has to be increased by this amount of cycles to get back to
|
||||
# the falling edge of ps_sclk. Furthermore CS lines are released one half
|
||||
# ps_sclk period after the last data transfer. So hold delay is increased by an
|
||||
# additional half clock cycle.
|
||||
set ps_spi_hold_multicycle [expr {$ps_spi_clock_divider + $ps_spi_setup_multicycle - 2}]
|
||||
set_multicycle_path -hold -start -to $binary_cs_ports $ps_spi_hold_multicycle
|
||||
###### local SPI slave ######
|
||||
# The chip select path for the MB CPLD itself is driven in the 250 MHz clock
|
||||
# domain and captured by registers operating at ps_sclk. Therefore setting the
|
||||
# path as false path preventing the placer from adding additional routing delay
|
||||
# to ensure hold timing. The setup path is limited to a maximum extend of one
|
||||
# clock period. As this path crosses clock domains clock propagation is included
|
||||
# in this path during static timing analysis. The TCL analysis in
|
||||
# scripts/ps_cs_analysis.tcl ensures a maximum value for data excluding the
|
||||
# clocking network.
|
||||
set_false_path -from [get_registers {ps_spi_cs_n_decoded[0]}] -hold
|
||||
set_max_delay -from [get_registers {ps_spi_cs_n_decoded[0]}] $clk250_period
|
||||
|
||||
###### LMK04832 ######
|
||||
create_generated_clock -source [get_ports PS_CPLD_SCLK] \
|
||||
-name lmk_spi_sclk [get_ports LMK32_SCLK]
|
||||
|
||||
# Use the worst-case board propagation delays.
|
||||
# Assuming 170.0 ps/in.
|
||||
# Longest trace | Trace length | Trace delay
|
||||
# LMK32_SCLK | 8.259 in | 1.404 ns
|
||||
# --------------------------------------------
|
||||
set lmk_board_delay 1.404
|
||||
|
||||
# setup and hold dominated by CS <-> SCK relationship
|
||||
set lmk_setup 20.000
|
||||
set lmk_hold 20.000
|
||||
set lmk_tco_max 60.000
|
||||
|
||||
set lmk_outputs [get_ports {LMK32_MOSI LMK32_CS_N}]
|
||||
set_output_delay -clock lmk_spi_sclk \
|
||||
-max [expr {$lmk_setup + $lmk_board_delay + $buffer_prop_max}] \
|
||||
$lmk_outputs
|
||||
set_output_delay -clock lmk_spi_sclk \
|
||||
-min [expr {-$lmk_hold - $lmk_board_delay - $buffer_prop_min}] \
|
||||
$lmk_outputs
|
||||
|
||||
set lmk_inputs [get_ports {LMK32_MISO}]
|
||||
set_input_delay -clock lmk_spi_sclk -clock_fall \
|
||||
-max [expr {$lmk_tco_max + 2*$lmk_board_delay + 2*$buffer_prop_max}] \
|
||||
$lmk_inputs
|
||||
set_input_delay -clock lmk_spi_sclk -clock_fall \
|
||||
-min [expr {2*$buffer_prop_min}] \
|
||||
$lmk_inputs
|
||||
|
||||
###### Phase DAC ######
|
||||
create_generated_clock -source [get_ports PS_CPLD_SCLK] \
|
||||
-name phase_dac_spi_sclk [get_ports PHASE_DAC_SCLK]
|
||||
|
||||
# Use the worst-case board propagation delays.
|
||||
# Assuming 170.0 ps/in.
|
||||
# Longest trace | Trace length | Trace delay
|
||||
# SpiDCs3v3_n | 8.322 in | 1.415 ns
|
||||
# --------------------------------------------
|
||||
set phase_dac_board_delay 1.415
|
||||
|
||||
#setup dominated by SYNC signal
|
||||
set phase_dac_setup 13.000
|
||||
set phase_dac_hold 5.000
|
||||
|
||||
# device captures data on falling clock edge (CPOL = 1)
|
||||
# constraining it as it would be like all the other SPI modules
|
||||
# PS SPI master is responsible for changing SPI mode when talking
|
||||
# to this device
|
||||
set phase_dac_outputs [get_ports {PHASE_DAC_MOSI PHASE_DAC_CS_N}]
|
||||
set_output_delay -clock phase_dac_spi_sclk -clock_fall \
|
||||
-max [expr {$phase_dac_setup + $phase_dac_board_delay}] \
|
||||
$phase_dac_outputs
|
||||
set_output_delay -clock phase_dac_spi_sclk -clock_fall \
|
||||
-min [expr {-$phase_dac_hold - $phase_dac_board_delay}] \
|
||||
$phase_dac_outputs
|
||||
|
||||
###### TPM ######
|
||||
create_generated_clock -source [get_ports PS_CPLD_SCLK] \
|
||||
-name tpm_spi_sclk [get_ports TPM_SCLK]
|
||||
|
||||
# Use the worst-case board propagation delays.
|
||||
# Assuming 170.0 ps/in.
|
||||
# Longest trace | Trace length | Trace delay
|
||||
# TPM_CS_n | 1.128 in | 0.196 ns
|
||||
# --------------------------------------------
|
||||
set tpm_board_delay 0.196
|
||||
|
||||
#tco dominated by NSS signal
|
||||
set tpm_setup 5.000
|
||||
set tpm_hold 5.000
|
||||
set tpm_tco_max 25.000
|
||||
|
||||
set tpm_outputs [get_ports {TPM_MOSI TPM_CS_N}]
|
||||
set_output_delay -clock tpm_spi_sclk \
|
||||
-max [expr {$tpm_setup + $tpm_board_delay}] \
|
||||
$tpm_outputs
|
||||
set_output_delay -clock tpm_spi_sclk \
|
||||
-min [expr {-$tpm_hold - $tpm_board_delay}] \
|
||||
$tpm_outputs
|
||||
|
||||
set tpm_inputs [get_ports {TPM_MISO}]
|
||||
set_input_delay -clock tpm_spi_sclk -clock_fall \
|
||||
-max [expr {$tpm_tco_max + 2*$tpm_board_delay}] \
|
||||
$tpm_inputs
|
||||
set_input_delay -clock tpm_spi_sclk -clock_fall \
|
||||
-min 0 \
|
||||
$tpm_inputs
|
||||
|
||||
#### Clocking AUX board SPI interface ####
|
||||
# Rev B clocking aux board uses a LMK05318 connected to this interface
|
||||
# Using its timing for this interface.
|
||||
create_generated_clock -source [get_ports PS_CPLD_SCLK] \
|
||||
-name clk_db_clk_out [get_ports CLK_DB_SCLK]
|
||||
set clk_db_setup 10.000
|
||||
set clk_db_hold 10.000
|
||||
set clk_db_tco_max 20.000
|
||||
# Just a worst case assumption based on 2 times the MB trace length CLK_DB_MOSI.
|
||||
# The multiplier 2 accounts for any traces on the CLK AUX board.
|
||||
set clk_db_board_delay 4.000
|
||||
|
||||
set clk_db_outputs [get_ports {CLK_DB_CS_N CLK_DB_MOSI}]
|
||||
# Output signals have to stable for max setup and propagation time. Clock delay
|
||||
# to device is expected to be 0 in this equation.
|
||||
set_output_delay -clock clk_db_clk_out \
|
||||
-max [expr {$clk_db_setup + $clk_db_board_delay + $buffer_prop_max}] $clk_db_outputs
|
||||
# The min output delay is comprised of:
|
||||
# - device required hold time ($clk_db_hold)
|
||||
# - max clock propagation delay ($clk_db_board_delay)
|
||||
# - min data propagation time (0)
|
||||
# All terms have to be negated as min output delay is defined in opposite
|
||||
# direction (positive into the past).
|
||||
set_output_delay -clock clk_db_clk_out \
|
||||
-min [expr {-$clk_db_hold - $clk_db_board_delay - $buffer_prop_min}] $clk_db_outputs
|
||||
|
||||
set clk_db_inputs [get_ports {CLK_DB_MISO}]
|
||||
# Max delay calculated is based on
|
||||
# - max clock delay ($clk_db_board_delay)
|
||||
# - max clock to out LMK ($clk_db_tco_max)
|
||||
# - max data path delay ($clk_db_board_delay)
|
||||
set_input_delay -clock clk_db_clk_out -clock_fall \
|
||||
-max [expr {$clk_db_tco_max + $clk_db_board_delay*2 + 2*$buffer_prop_max}] $clk_db_inputs
|
||||
# Min delay assumes clock propagates to device and data propagates to CPLD
|
||||
# without any delays.
|
||||
set_input_delay -clock clk_db_clk_out -clock_fall \
|
||||
-min [expr {2*$buffer_prop_min}] $clk_db_inputs
|
||||
|
||||
|
||||
#####################################################################
|
||||
# PCIe signals
|
||||
#####################################################################
|
||||
# I²C bus is operated at 100kHz. Constraints would not improve timing
|
||||
# significantly (typically in the order of nanoseconds, which is negligible
|
||||
# given the SCL period of 10 us).
|
||||
# PCI-Express reset signal is not timing critical as it is received
|
||||
# asynchronously by the FPGA.
|
||||
set_false_path -to [get_ports {IPASS_SDA[0] IPASS_SCL[0] PCIE_RESET}]
|
||||
# I²C inputs are only consumed by synchronizers.
|
||||
# Add exceptions for all known consumers.
|
||||
set_false_path -to [get_registers {PcieCmiWrapper:pcie_cmi_inst|PcieCmi:PcieCmix|UsfCablePort:UsfCablePortx|CablePort:CablePortx|I2cTop:CableI2cx|I2cMonitor:I2cMonitorx|I2cFilter:I2cFilterx|I2cSigFilter:SclFilterx|fSig_ms}]
|
||||
set_false_path -to [get_registers {PcieCmiWrapper:pcie_cmi_inst|PcieCmi:PcieCmix|UsfCablePort:UsfCablePortx|CablePort:CablePortx|I2cTop:CableI2cx|I2cMonitor:I2cMonitorx|I2cFilter:I2cFilterx|I2cSigFilter:SdaFilterx|fSig_ms}]
|
||||
set_false_path -to [get_registers {PcieCmiWrapper:pcie_cmi_inst|PcieCmi:PcieCmix|UsfCablePort:UsfCablePortx|CablePort:CablePortx|StuckBusFixer:StuckBusFixerx|DoubleSyncSlAsyncIn:DoubleSclkx|DoubleSyncAsyncInBase:DoubleSyncAsyncInBasex|DFlopAsync:oSig_msx|lpm_ff:LPM_FFx|dffs[0]}]
|
||||
set_false_path -to [get_registers {PcieCmiWrapper:pcie_cmi_inst|PcieCmi:PcieCmix|UsfCablePort:UsfCablePortx|CablePort:CablePortx|StuckBusFixer:StuckBusFixerx|DoubleSyncSlAsyncIn:DoubleSdax|DoubleSyncAsyncInBase:DoubleSyncAsyncInBasex|DFlopAsync:oSig_msx|lpm_ff:LPM_FFx|dffs[0]}]
|
||||
|
||||
#####################################################################
|
||||
# Known Issue of On-Chip Flash
|
||||
#####################################################################
|
||||
# see https://www.intel.com/content/www/us/en/programmable/support/support-resources/knowledge-base/tools/2016/warning--332060---node---alteraonchipflash-onchipflash-alteraonc.html
|
||||
create_generated_clock -name flash_se_neg_reg \
|
||||
-source [get_pins { on_chip_flash:flash_inst|altera_onchip_flash:onchip_flash_0|altera_onchip_flash_avmm_data_controller:avmm_data_controller|flash_se_neg_reg|clk }] \
|
||||
-divide_by 2 [get_pins { on_chip_flash:flash_inst|altera_onchip_flash:onchip_flash_0|altera_onchip_flash_avmm_data_controller:avmm_data_controller|flash_se_neg_reg|q } ]
|
||||
|
||||
#####################################################################
|
||||
# Clock uncertainty
|
||||
#####################################################################
|
||||
# Assign some uncertainty to all clocks
|
||||
set clock_uncertainty 0.150
|
||||
set_clock_uncertainty -to [get_clocks *] $clock_uncertainty
|
||||
derive_clock_uncertainty
|
||||
@@ -0,0 +1,276 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: ctrlport_to_spi
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// This module wraps a SPI master and provides a ControlPort interface.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// BASE_ADDRESS : Base address for CtrlPort registers.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module ctrlport_to_spi #(
|
||||
parameter BASE_ADDRESS = 0
|
||||
) (
|
||||
//---------------------------------------------------------------
|
||||
// ControlPort Slave
|
||||
//---------------------------------------------------------------
|
||||
|
||||
input wire ctrlport_clk,
|
||||
input wire ctrlport_rst,
|
||||
|
||||
input wire s_ctrlport_req_wr,
|
||||
input wire s_ctrlport_req_rd,
|
||||
input wire [19:0] s_ctrlport_req_addr,
|
||||
input wire [31:0] s_ctrlport_req_data,
|
||||
|
||||
output reg s_ctrlport_resp_ack,
|
||||
output reg [ 1:0] s_ctrlport_resp_status = 0,
|
||||
output reg [31:0] s_ctrlport_resp_data = 0,
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// SPI Signals
|
||||
//---------------------------------------------------------------
|
||||
|
||||
output wire sclk,
|
||||
output wire mosi,
|
||||
output wire [15:0] ss,
|
||||
input wire miso
|
||||
);
|
||||
|
||||
`include "../../../../lib/rfnoc/core/ctrlport.vh"
|
||||
`include "../regmap/spi_regmap_utils.vh"
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Translating CtrlPort <-> Wishbone
|
||||
//---------------------------------------------------------------
|
||||
|
||||
reg wb_cyc_i; // Active bus cycle
|
||||
reg wb_we_i = 1'b0; // Write access
|
||||
reg [ 4:0] wb_adr_i = 5'b0;
|
||||
reg [31:0] wb_dat_i = 32'b0;
|
||||
wire wb_ack_o;
|
||||
wire [31:0] wb_dat_o;
|
||||
wire wb_err_o;
|
||||
|
||||
// Check for address to be in range [base_addr..base_addr+32)
|
||||
localparam NUM_ADDRESSES = 32;
|
||||
wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) &&
|
||||
(s_ctrlport_req_addr < BASE_ADDRESS + NUM_ADDRESSES);
|
||||
|
||||
// Following chapter 3.2.3 (classic standard SINGLE WRITE cycle) of
|
||||
// https://cdn.opencores.org/downloads/wbspec_b4.pdf
|
||||
always @(posedge ctrlport_clk) begin
|
||||
// Reset internal registers and responses
|
||||
if (ctrlport_rst) begin
|
||||
wb_cyc_i <= 1'b0;
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
|
||||
end else begin
|
||||
// Request independent default assignments
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
|
||||
// Wait for ack on active bus transactions
|
||||
if (wb_cyc_i) begin
|
||||
if (wb_ack_o) begin
|
||||
// End bus cycle and generate response
|
||||
wb_cyc_i <= 1'b0;
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_data <= wb_dat_o;
|
||||
|
||||
if (wb_err_o) begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
end else begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_OKAY;
|
||||
end
|
||||
end
|
||||
|
||||
// Write requests
|
||||
end else if (s_ctrlport_req_wr) begin
|
||||
// Assume there is a valid address
|
||||
wb_cyc_i <= 1'b1;
|
||||
wb_we_i <= 1'b1;
|
||||
wb_dat_i <= s_ctrlport_req_data;
|
||||
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + TX_DATA_LOW: begin
|
||||
wb_adr_i <= 5'h00;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + TX_DATA_HIGH: begin
|
||||
wb_adr_i <= 5'h04;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + CONTROL: begin
|
||||
wb_adr_i <= 5'h10;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + CLOCK_DIVIDER: begin
|
||||
wb_adr_i <= 5'h14;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + SLAVE_SELECT: begin
|
||||
wb_adr_i <= 5'h18;
|
||||
end
|
||||
|
||||
// Error on undefined address
|
||||
default: begin
|
||||
wb_cyc_i <= 1'b0;
|
||||
|
||||
if (address_in_range) begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
|
||||
// No response if out of range
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
||||
// Read requests
|
||||
end else if (s_ctrlport_req_rd) begin
|
||||
// Assume there is a valid address
|
||||
wb_cyc_i <= 1'b1;
|
||||
wb_we_i <= 1'b0;
|
||||
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + RX_DATA_LOW: begin
|
||||
wb_adr_i <= 5'h00;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + RX_DATA_HIGH: begin
|
||||
wb_adr_i <= 5'h04;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + CONTROL: begin
|
||||
wb_adr_i <= 5'h10;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + CLOCK_DIVIDER: begin
|
||||
wb_adr_i <= 5'h14;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + SLAVE_SELECT: begin
|
||||
wb_adr_i <= 5'h18;
|
||||
end
|
||||
|
||||
// Error on undefined address
|
||||
default: begin
|
||||
wb_cyc_i <= 1'b0;
|
||||
|
||||
if (address_in_range) begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
|
||||
// No response if out of range
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
||||
// No request
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// SPI Master
|
||||
//---------------------------------------------------------------
|
||||
|
||||
spi_top spi_master (
|
||||
.wb_clk_i (ctrlport_clk),
|
||||
.wb_rst_i (ctrlport_rst),
|
||||
.wb_adr_i (wb_adr_i),
|
||||
.wb_dat_i (wb_dat_i),
|
||||
.wb_dat_o (wb_dat_o),
|
||||
.wb_sel_i (4'hF),
|
||||
.wb_we_i (wb_we_i),
|
||||
.wb_stb_i (wb_cyc_i),
|
||||
.wb_cyc_i (wb_cyc_i),
|
||||
.wb_ack_o (wb_ack_o),
|
||||
.wb_err_o (wb_err_o),
|
||||
.wb_int_o (),
|
||||
.ss_pad_o (ss),
|
||||
.sclk_pad_o (sclk),
|
||||
.mosi_pad_o (mosi),
|
||||
.miso_pad_i (miso)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
|
||||
|
||||
//XmlParse xml_on
|
||||
//<regmap name="SPI_REGMAP" readablestrobes="false" markdown="true" generatevhdl="true" ettusguidelines="true">
|
||||
//
|
||||
// <group name="SPI_REGS">
|
||||
// <info>
|
||||
// This register map is present for each SPI master.
|
||||
//
|
||||
// For information about the register content and the way to interact with the core see the
|
||||
// <a href="https://opencores.org/websvn/filedetails?repname=spi&path=%2Fspi%2Ftrunk%2Fdoc%2Fspi.pdf" target="_blank">documentation</a>
|
||||
// of the SPI master from opencores used internally.
|
||||
//
|
||||
// The core is configured to operate with 16 slave signal signals, up to 128 bits per transmission and 8 bit clock divider.
|
||||
// Only 64 bits of data are available via this register interface.
|
||||
//
|
||||
// For the different SPI modes use the following table to derive the bits in @.CONTROL register. Only option 0 (CPOL=0, CPHA=0) has been tested.
|
||||
//
|
||||
//| CPOL | CPHA | TX_NEG | RX_NEG |
|
||||
//| ------- | -------- | -------- | ------- |
|
||||
//| 0 | 0 | 1 | 0 |
|
||||
//| 0 | 1 | 0 | 1 |
|
||||
//| 1 | 0 | 0 | 1 |
|
||||
//| 1 | 1 | 1 | 0 |
|
||||
// </info>
|
||||
//
|
||||
// <register name="RX_DATA_LOW" offset="0x00" writable="false" size="32">
|
||||
// <info>Lower 32 bits of the received word. (RxWord[31:0])</info>
|
||||
// </register>
|
||||
//
|
||||
// <register name="RX_DATA_HIGH" offset="0x04" writable="false" size="32">
|
||||
// <info>Higher 32 bits of the received word. (RxWord[63:32])</info>
|
||||
// </register>
|
||||
//
|
||||
// <register name="TX_DATA_LOW" offset="0x08" readable="false" size="32">
|
||||
// <info>Lower 32 bits of the received word. (TxWord[31:0])</info>
|
||||
// </register>
|
||||
//
|
||||
// <register name="TX_DATA_HIGH" offset="0x0C" readable="false" size="32">
|
||||
// <info>Higher 32 bits of the received word. (TxWord[63:32])</info>
|
||||
// </register>
|
||||
//
|
||||
// <register name="CONTROL" offset="0x10" size="32">
|
||||
// <info>Control register</info>
|
||||
// </register>
|
||||
|
||||
// <register name="CLOCK_DIVIDER" offset="0x14" size="8">
|
||||
// <bitfield name="Divider" range="7..0">
|
||||
// <info>
|
||||
// Clock Divider.
|
||||
// </info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
// <register name="SLAVE_SELECT" offset="0x18" size="16">
|
||||
// <bitfield name="SS" range="15..0">
|
||||
// <info>
|
||||
// Slave select.
|
||||
// </info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
//
|
||||
// </group>
|
||||
//</regmap>
|
||||
//XmlParse xml_off
|
||||
@@ -0,0 +1,299 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: pl_cpld_regs
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Basic Registers to inform software about version and capabilities.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// BASE_ADDRESS : Base address for CtrlPort registers
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module pl_cpld_regs #(
|
||||
parameter BASE_ADDRESS = 0
|
||||
) (
|
||||
input wire ctrlport_clk,
|
||||
input wire ctrlport_rst,
|
||||
|
||||
// Request
|
||||
input wire s_ctrlport_req_wr,
|
||||
input wire s_ctrlport_req_rd,
|
||||
input wire [19:0] s_ctrlport_req_addr,
|
||||
input wire [31:0] s_ctrlport_req_data,
|
||||
// Response
|
||||
output reg s_ctrlport_resp_ack,
|
||||
output reg [ 1:0] s_ctrlport_resp_status,
|
||||
output reg [31:0] s_ctrlport_resp_data,
|
||||
|
||||
// QSFP LEDs
|
||||
// Port 0
|
||||
output wire [ 3:0] qsfp0_led_active,
|
||||
output wire [ 3:0] qsfp0_led_link,
|
||||
// Port 1
|
||||
output wire [ 3:0] qsfp1_led_active,
|
||||
output wire [ 3:0] qsfp1_led_link,
|
||||
|
||||
// iPass status
|
||||
output wire [ 1:0] ipass_cable_present
|
||||
);
|
||||
|
||||
`ifdef X410
|
||||
`include "../regmap/x410/constants_regmap_utils.vh"
|
||||
`endif
|
||||
`include "../regmap/pl_cpld_base_regmap_utils.vh"
|
||||
`include "../../../../lib/rfnoc/core/ctrlport.vh"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Address Calculation
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
localparam NUM_ADDRESSES = 64;
|
||||
wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) &&
|
||||
(s_ctrlport_req_addr < BASE_ADDRESS + NUM_ADDRESSES);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Internal Registers
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
reg [SCRATCH_REGISTER_SIZE-1:0] scratch_reg;
|
||||
reg [LED_REGISTER_SIZE-1:0] led_reg;
|
||||
reg [CABLE_PRESENT_REG_SIZE-1:0] ipass_reg;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Assign Outputs
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
assign qsfp0_led_active = led_reg[QSFP0_LED_ACTIVE+:QSFP0_LED_ACTIVE_SIZE];
|
||||
assign qsfp0_led_link = led_reg[QSFP0_LED_LINK+:QSFP0_LED_LINK_SIZE];
|
||||
|
||||
assign qsfp1_led_active = led_reg[QSFP1_LED_ACTIVE+:QSFP1_LED_ACTIVE_SIZE];
|
||||
assign qsfp1_led_link = led_reg[QSFP1_LED_LINK+:QSFP1_LED_LINK_SIZE];
|
||||
|
||||
assign ipass_cable_present = ipass_reg;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Handling of ControlPort Requests
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
always @(posedge ctrlport_clk) begin
|
||||
// Reset internal registers and responses
|
||||
if (ctrlport_rst) begin
|
||||
scratch_reg <= 0;
|
||||
led_reg <= 0;
|
||||
ipass_reg <= 0;
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
|
||||
// Write requests
|
||||
end else if (s_ctrlport_req_wr) begin
|
||||
// Always issue an ack and no data
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_data <= {CTRLPORT_DATA_W {1'bx}};
|
||||
s_ctrlport_resp_status <= CTRL_STS_OKAY;
|
||||
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + SCRATCH_REGISTER:
|
||||
scratch_reg <= s_ctrlport_req_data;
|
||||
|
||||
BASE_ADDRESS + LED_REGISTER:
|
||||
led_reg <= s_ctrlport_req_data[LED_REGISTER_SIZE-1:0];
|
||||
|
||||
BASE_ADDRESS + CABLE_PRESENT_REG: begin
|
||||
ipass_reg[0] <= s_ctrlport_req_data[IPASS0_CABLE_PRESENT];
|
||||
ipass_reg[1] <= s_ctrlport_req_data[IPASS1_CABLE_PRESENT];
|
||||
end
|
||||
|
||||
// Error on undefined address
|
||||
default: begin
|
||||
if (address_in_range) begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
|
||||
// No response if out of range
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
||||
// Read request
|
||||
end else if (s_ctrlport_req_rd) begin
|
||||
// Default assumption: valid request
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_status <= CTRL_STS_OKAY;
|
||||
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + SIGNATURE_REGISTER:
|
||||
s_ctrlport_resp_data <= PL_CPLD_SIGNATURE;
|
||||
|
||||
BASE_ADDRESS + REVISION_REGISTER:
|
||||
s_ctrlport_resp_data <= CPLD_REVISION;
|
||||
|
||||
BASE_ADDRESS + OLDEST_COMPATIBLE_REVISION_REGISTER:
|
||||
s_ctrlport_resp_data <= OLDEST_CPLD_REVISION;
|
||||
|
||||
BASE_ADDRESS + SCRATCH_REGISTER:
|
||||
s_ctrlport_resp_data <= scratch_reg;
|
||||
|
||||
BASE_ADDRESS + GIT_HASH_REGISTER:
|
||||
`ifdef GIT_HASH
|
||||
s_ctrlport_resp_data <= `GIT_HASH;
|
||||
`else
|
||||
s_ctrlport_resp_data <= 32'hDEADBEEF;
|
||||
`endif
|
||||
|
||||
BASE_ADDRESS + LED_REGISTER:
|
||||
s_ctrlport_resp_data <= {{(CTRLPORT_DATA_W - LED_REGISTER_SIZE){1'b0}}, led_reg};
|
||||
|
||||
BASE_ADDRESS + CABLE_PRESENT_REG: begin
|
||||
s_ctrlport_resp_data <= {CTRLPORT_DATA_W {1'b0}};
|
||||
s_ctrlport_resp_data[IPASS0_CABLE_PRESENT] <= ipass_reg[0];
|
||||
s_ctrlport_resp_data[IPASS1_CABLE_PRESENT] <= ipass_reg[1];
|
||||
end
|
||||
|
||||
// Error on undefined address
|
||||
default: begin
|
||||
s_ctrlport_resp_data <= {CTRLPORT_DATA_W {1'bx}};
|
||||
if (address_in_range) begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
|
||||
// No response if out of range
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
||||
// No request
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
|
||||
|
||||
//XmlParse xml_on
|
||||
//<regmap name="PL_CPLD_BASE_REGMAP" readablestrobes="false" generatevhdl="true" ettusguidelines="true">
|
||||
// <group name="PL_CPLD_BASE_REGS">
|
||||
// <info>
|
||||
// Basic registers containing version and capabilities information.
|
||||
// </info>
|
||||
//
|
||||
// <register name="SIGNATURE_REGISTER" offset="0x00" writable="false" size="32">
|
||||
// <info>Contains the product's signature.</info>
|
||||
// <bitfield name="PRODUCT_SIGNATURE" range="31..0">
|
||||
// <info>Fixed value PL_CPLD_SIGNATURE of @.CONSTANTS_REGMAP</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
//
|
||||
// <register name="REVISION_REGISTER" offset="0x04" writable="false" size="32">
|
||||
// <info>Contains the CPLD revision (see CPLD_REVISION of @.CONSTANTS_REGMAP)</info>
|
||||
// <bitfield name="REVISION_HH" range="7..0">
|
||||
// <info>Contains revision hour code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="REVISION_DD" range="15..8">
|
||||
// <info>Contains revision day code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="REVISION_MM" range="23..16">
|
||||
// <info>Contains revision month code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="REVISION_YY" range="31..24">
|
||||
// <info>Contains revision year code.</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
//
|
||||
// <register name="OLDEST_COMPATIBLE_REVISION_REGISTER" offset="0x08" writable="false" size="32">
|
||||
// <info>
|
||||
// This register returns (in YYMMDDHH format) the oldest revision
|
||||
// that is still compatible with this one. Compatible means that
|
||||
// registers or register bits may have been added, but not
|
||||
// modified or deleted (see OLDEST_CPLD_REVISION of @.CONSTANTS_REGMAP).
|
||||
// </info>
|
||||
// <bitfield name="OLD_REVISION_HH" range="7..0">
|
||||
// <info>Contains revision hour code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="OLD_REVISION_DD" range="15..8">
|
||||
// <info>Contains revision day code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="OLD_REVISION_MM" range="23..16">
|
||||
// <info>Contains revision month code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="OLD_REVISION_YY" range="31..24">
|
||||
// <info>Contains revision year code.</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
//
|
||||
// <register name="SCRATCH_REGISTER" offset="0x0C" size="32">
|
||||
// <info>Read/write register for general software use.</info>
|
||||
// </register>
|
||||
//
|
||||
// <register name="GIT_HASH_REGISTER" offset="0x10" size="32" writable="false">
|
||||
// <info>
|
||||
// Git hash of commit used to build this image.{br}
|
||||
// Value equals 0xDEADBEEF if the git hash was not used during synthesis.
|
||||
// </info>
|
||||
// <bitfield name="GIT_CLEAN" range="31..28">
|
||||
// <info>
|
||||
// 0x0 in case the git status was clean{br}
|
||||
// 0xF in case there were uncommitted changes
|
||||
// </info>
|
||||
// </bitfield>
|
||||
// <bitfield name="GIT_HASH" range="27..0">
|
||||
// <info>7 hex digit hash code of the commit</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
// </group>
|
||||
//
|
||||
// <group name="MB_CPLD_LED_REGS">
|
||||
// <info>
|
||||
// Register Map to control QSFP LEDs.
|
||||
// </info>
|
||||
// <register name="LED_REGISTER" offset="0x20" size="16">
|
||||
// <info>
|
||||
// Provides to the LEDs of the QSFP ports.
|
||||
// Write access will directly change the LED status.
|
||||
// The LED lights up if the corresponding bit is set.
|
||||
// </info>
|
||||
// <bitfield name="QSFP0_LED_LINK" range="3..0">
|
||||
// <info>Link LEDs of QSFP port 0</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="QSFP0_LED_ACTIVE" range="7..4">
|
||||
// <info>Active LEDs of QSFP port 0</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="QSFP1_LED_LINK" range="11..8">
|
||||
// <info>Link LEDs of QSFP port 1</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="QSFP1_LED_ACTIVE" range="15..12">
|
||||
// <info>Active LEDs of QSFP port 1</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
// </group>
|
||||
//
|
||||
// <group name="PL_CMI_REGS">
|
||||
// <info>
|
||||
// Cable present status register.
|
||||
// </info>
|
||||
// <register name="CABLE_PRESENT_REG" offset="0x30" size="2">
|
||||
// <info>
|
||||
// Information from FPGA about the cable present status.
|
||||
// </info>
|
||||
// <bitfield name="IPASS0_CABLE_PRESENT" range="0">
|
||||
// <info>Set to 1 if cable present in iPass 0 connector.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="IPASS1_CABLE_PRESENT" range="1">
|
||||
// <info>Set to 1 if cable present in iPass 1 connector.</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
// </group>
|
||||
//</regmap>
|
||||
//XmlParse xml_off
|
||||
@@ -0,0 +1,405 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: ps_cpld_regs
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Basic registers to inform software about version and capabilities.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// BASE_ADDRESS : Base address for CtrlPort registers
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module ps_cpld_regs #(
|
||||
parameter BASE_ADDRESS = 0
|
||||
) (
|
||||
input wire ctrlport_clk,
|
||||
input wire ctrlport_rst,
|
||||
|
||||
// Request
|
||||
input wire s_ctrlport_req_wr,
|
||||
input wire s_ctrlport_req_rd,
|
||||
input wire [19:0] s_ctrlport_req_addr,
|
||||
input wire [31:0] s_ctrlport_req_data,
|
||||
// Response
|
||||
output reg s_ctrlport_resp_ack,
|
||||
output reg [ 1:0] s_ctrlport_resp_status,
|
||||
output reg [31:0] s_ctrlport_resp_data,
|
||||
|
||||
// Configuration outputs
|
||||
output reg [ 1:0] db_clk_enable = 2'b00,
|
||||
output reg [ 1:0] db_reset = 2'b11,
|
||||
output reg pll_ref_clk_enable = 1'b0,
|
||||
|
||||
output reg [11:0] dio_direction_a = 12'b0,
|
||||
output reg [11:0] dio_direction_b = 12'b0,
|
||||
|
||||
output reg [39:0] serial_num = 40'b0,
|
||||
output reg cmi_ready = 1'b0,
|
||||
input wire cmi_other_side_detected
|
||||
);
|
||||
`ifdef X410
|
||||
`include "../regmap/x410/constants_regmap_utils.vh"
|
||||
`endif
|
||||
`include "../regmap/ps_cpld_base_regmap_utils.vh"
|
||||
`include "../../../../lib/rfnoc/core/ctrlport.vh"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Address Calculation
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
localparam NUM_ADDRESSES = 64;
|
||||
wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) &&
|
||||
(s_ctrlport_req_addr < BASE_ADDRESS + NUM_ADDRESSES);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Internal Registers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
reg [SCRATCH_REGISTER_SIZE-1:0] scratch_reg;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Handling of ControlPort Requests
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
always @(posedge ctrlport_clk) begin
|
||||
// Reset internal registers and responses
|
||||
if (ctrlport_rst) begin
|
||||
scratch_reg <= 0;
|
||||
db_clk_enable <= 2'b00;
|
||||
db_reset <= 2'b11;
|
||||
pll_ref_clk_enable <= 1'b0;
|
||||
dio_direction_a <= {DIO_DIRECTION_A_SIZE{1'b0}};
|
||||
dio_direction_b <= {DIO_DIRECTION_B_SIZE{1'b0}};
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
s_ctrlport_resp_data <= {CTRLPORT_ADDR_W {1'bx}};
|
||||
s_ctrlport_resp_status <= CTRL_STS_OKAY;
|
||||
|
||||
// Write requests
|
||||
end else begin
|
||||
if (s_ctrlport_req_wr) begin
|
||||
// Always issue an ack and no data
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_data <= {CTRLPORT_ADDR_W {1'bx}};
|
||||
s_ctrlport_resp_status <= CTRL_STS_OKAY;
|
||||
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + SCRATCH_REGISTER:
|
||||
scratch_reg <= s_ctrlport_req_data;
|
||||
|
||||
BASE_ADDRESS + PL_DB_REGISTER: begin
|
||||
if (s_ctrlport_req_data[DISABLE_CLOCK_DB0]) begin
|
||||
db_clk_enable[0] <= 1'b0;
|
||||
end else if (s_ctrlport_req_data[ENABLE_CLOCK_DB0]) begin
|
||||
db_clk_enable[0] <= 1'b1;
|
||||
end
|
||||
if (s_ctrlport_req_data[DISABLE_CLOCK_DB1]) begin
|
||||
db_clk_enable[1] <= 1'b0;
|
||||
end else if (s_ctrlport_req_data[ENABLE_CLOCK_DB1]) begin
|
||||
db_clk_enable[1] <= 1'b1;
|
||||
end
|
||||
if (s_ctrlport_req_data[DISABLE_PLL_REF_CLOCK]) begin
|
||||
pll_ref_clk_enable <= 1'b0;
|
||||
end else if (s_ctrlport_req_data[ENABLE_PLL_REF_CLOCK]) begin
|
||||
pll_ref_clk_enable <= 1'b1;
|
||||
end
|
||||
if (s_ctrlport_req_data[ASSERT_RESET_DB0]) begin
|
||||
db_reset[0] <= 1'b1;
|
||||
end else if (s_ctrlport_req_data[RELEASE_RESET_DB0]) begin
|
||||
db_reset[0] <= 1'b0;
|
||||
end
|
||||
if (s_ctrlport_req_data[ASSERT_RESET_DB1]) begin
|
||||
db_reset[1] <= 1'b1;
|
||||
end else if (s_ctrlport_req_data[RELEASE_RESET_DB1]) begin
|
||||
db_reset[1] <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
BASE_ADDRESS + DIO_DIRECTION_REGISTER: begin
|
||||
dio_direction_a <= s_ctrlport_req_data[DIO_DIRECTION_A_MSB:DIO_DIRECTION_A];
|
||||
dio_direction_b <= s_ctrlport_req_data[DIO_DIRECTION_B_MSB:DIO_DIRECTION_B];
|
||||
end
|
||||
|
||||
BASE_ADDRESS + SERIAL_NUM_LOW_REG: begin
|
||||
serial_num[31:0] <= s_ctrlport_req_data;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + SERIAL_NUM_HIGH_REG: begin
|
||||
serial_num[39:32] <= s_ctrlport_req_data[SERIAL_NUM_HIGH_REG_SIZE-1:0];
|
||||
end
|
||||
|
||||
BASE_ADDRESS + CMI_CONTROL_STATUS: begin
|
||||
cmi_ready <= s_ctrlport_req_data[CMI_READY];
|
||||
end
|
||||
|
||||
// Error on undefined address
|
||||
default: begin
|
||||
if (address_in_range) begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
|
||||
// No response if out of range
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
||||
// Read request
|
||||
end else if (s_ctrlport_req_rd) begin
|
||||
// Default assumption: valid request
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_status <= CTRL_STS_OKAY;
|
||||
s_ctrlport_resp_data <= {CTRLPORT_DATA_W {1'b0}};
|
||||
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + SIGNATURE_REGISTER:
|
||||
s_ctrlport_resp_data <= PS_CPLD_SIGNATURE;
|
||||
|
||||
BASE_ADDRESS + REVISION_REGISTER:
|
||||
s_ctrlport_resp_data <= CPLD_REVISION;
|
||||
|
||||
BASE_ADDRESS + OLDEST_COMPATIBLE_REVISION_REGISTER:
|
||||
s_ctrlport_resp_data <= OLDEST_CPLD_REVISION;
|
||||
|
||||
BASE_ADDRESS + SCRATCH_REGISTER:
|
||||
s_ctrlport_resp_data <= scratch_reg;
|
||||
|
||||
BASE_ADDRESS + GIT_HASH_REGISTER:
|
||||
`ifdef GIT_HASH
|
||||
s_ctrlport_resp_data <= `GIT_HASH;
|
||||
`else
|
||||
s_ctrlport_resp_data <= 32'hDEADBEEF;
|
||||
`endif
|
||||
|
||||
BASE_ADDRESS + PL_DB_REGISTER: begin
|
||||
s_ctrlport_resp_data[DB0_CLOCK_ENABLED] <= db_clk_enable[0];
|
||||
s_ctrlport_resp_data[DB1_CLOCK_ENABLED] <= db_clk_enable[1];
|
||||
s_ctrlport_resp_data[PLL_REF_CLOCK_ENABLED] <= pll_ref_clk_enable;
|
||||
s_ctrlport_resp_data[DB0_RESET_ASSERTED] <= db_reset[0];
|
||||
s_ctrlport_resp_data[DB1_RESET_ASSERTED] <= db_reset[1];
|
||||
end
|
||||
|
||||
BASE_ADDRESS + DIO_DIRECTION_REGISTER: begin
|
||||
s_ctrlport_resp_data[DIO_DIRECTION_A_MSB:DIO_DIRECTION_A] <= dio_direction_a;
|
||||
s_ctrlport_resp_data[DIO_DIRECTION_B_MSB:DIO_DIRECTION_B] <= dio_direction_b;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + SERIAL_NUM_LOW_REG: begin
|
||||
s_ctrlport_resp_data <= serial_num[31:0];
|
||||
end
|
||||
|
||||
BASE_ADDRESS + SERIAL_NUM_HIGH_REG: begin
|
||||
s_ctrlport_resp_data[SERIAL_NUM_HIGH_REG_SIZE-1:0] <= serial_num[39:32];
|
||||
end
|
||||
|
||||
BASE_ADDRESS + CMI_CONTROL_STATUS: begin
|
||||
s_ctrlport_resp_data[CMI_READY] <= cmi_ready;
|
||||
s_ctrlport_resp_data[OTHER_SIDE_DETECTED] <= cmi_other_side_detected;
|
||||
end
|
||||
|
||||
// Error on undefined address
|
||||
default: begin
|
||||
s_ctrlport_resp_data <= {CTRLPORT_DATA_W {1'bx}};
|
||||
if (address_in_range) begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
|
||||
// No response if out of range
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
||||
// No request
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
|
||||
|
||||
//XmlParse xml_on
|
||||
//<regmap name="PS_CPLD_BASE_REGMAP" readablestrobes="false" generatevhdl="true" ettusguidelines="true">
|
||||
// <group name="PS_CPLD_BASE_REGS">
|
||||
// <info>
|
||||
// Basic registers containing version and capabilites information.
|
||||
// </info>
|
||||
//
|
||||
// <register name="SIGNATURE_REGISTER" offset="0x00" writable="false" size="32">
|
||||
// <info>Contains the product's signature.</info>
|
||||
// <bitfield name="PRODUCT_SIGNATURE" range="31..0">
|
||||
// <info>Fixed value PS_CPLD_SIGNATURE of @.CONSTANTS_REGMAP</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
//
|
||||
// <register name="REVISION_REGISTER" offset="0x04" writable="false" size="32">
|
||||
// <info>Contains the CPLD revision (see CPLD_REVISION of @.CONSTANTS_REGMAP).</info>
|
||||
// <bitfield name="REVISION_HH" range="7..0">
|
||||
// <info>Contains revision hour code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="REVISION_DD" range="15..8">
|
||||
// <info>Contains revision day code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="REVISION_MM" range="23..16">
|
||||
// <info>Contains revision month code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="REVISION_YY" range="31..24">
|
||||
// <info>Contains revision year code.</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
//
|
||||
// <register name="OLDEST_COMPATIBLE_REVISION_REGISTER" offset="0x08" writable="false" size="32">
|
||||
// <info>
|
||||
// This register returns (in YYMMDDHH format) the oldest revision
|
||||
// that is still compatible with this one. Compatible means that
|
||||
// registers or register bits may have been added, but not
|
||||
// modified or deleted (see OLDEST_CPLD_REVISION of @.CONSTANTS_REGMAP).
|
||||
// </info>
|
||||
// <bitfield name="OLD_REVISION_HH" range="7..0">
|
||||
// <info>Contains revision hour code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="OLD_REVISION_DD" range="15..8">
|
||||
// <info>Contains revision day code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="OLD_REVISION_MM" range="23..16">
|
||||
// <info>Contains revision month code.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="OLD_REVISION_YY" range="31..24">
|
||||
// <info>Contains revision year code.</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
//
|
||||
// <register name="SCRATCH_REGISTER" offset="0x0C" size="32">
|
||||
// <info>Read/write register for general software use.</info>
|
||||
// </register>
|
||||
//
|
||||
// <register name="GIT_HASH_REGISTER" offset="0x10" size="32" writable="false">
|
||||
// <info>
|
||||
// Git hash of commit used to build this image.{br}
|
||||
// Value equals 0xDEADBEEF if the git hash was not used during synthesis.
|
||||
// </info>
|
||||
// <bitfield name="GIT_CLEAN" range="31..28">
|
||||
// <info>
|
||||
// 0x0 in case the git status was clean{br}
|
||||
// 0xF in case there were uncommitted changes
|
||||
// </info>
|
||||
// </bitfield>
|
||||
// <bitfield name="GIT_HASH" range="27..0">
|
||||
// <info>7 hex digit hash code of the commit</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
// </group>
|
||||
//
|
||||
// <group name="PS_CONTROL_REGS">
|
||||
// <info>
|
||||
// Register Map to control MB CPLD functions.
|
||||
// </info>
|
||||
// <register name="PL_DB_REGISTER" offset="0x20" size="32">
|
||||
// <info>
|
||||
// Register to control the PL part DB SPI connection and reset generation.
|
||||
// The DB connection is clocked with PLL reference clock. Ensure this clock is stable
|
||||
// and enabled before starting any SPI request.
|
||||
// The PLL reference clock can be disabled if both DB connections are disabled or inactive.
|
||||
// To enable the DB connection, enable clock with one write access and release
|
||||
// reset with the next write access.
|
||||
// To disable the DB connection, assert reset with one write access and
|
||||
// disable clocks with the next write access.
|
||||
// </info>
|
||||
// <bitfield name="DB0_CLOCK_ENABLED" range="0" writable="false">
|
||||
// <info>Indicates if a clock is forwarded to DB 0.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="DB1_CLOCK_ENABLED" range="1" writable="false">
|
||||
// <info>Indicates if a clock is forwarded to DB 1.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="PLL_REF_CLOCK_ENABLED" range="2" writable="false">
|
||||
// <info>Indicates if the PLL reference clock for the PL interface is enabled.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="DB0_RESET_ASSERTED" range="4" writable="false">
|
||||
// <info>Indicates that reset is asserted for DB 0.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="DB1_RESET_ASSERTED" range="5" writable="false">
|
||||
// <info>Indicates that reset is asserted for DB 1.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="ENABLE_CLOCK_DB0" range="8" readable="false">
|
||||
// <info>Writing with this flag set enables DB 0 clock forwarding. (may be overwritten by @.DISABLE_CLOCK_DB0)</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="ENABLE_CLOCK_DB1" range="9" readable="false">
|
||||
// <info>Writing with this flag set enables DB 1 clock forwarding. (may be overwritten by @.DISABLE_CLOCK_DB1)</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="ENABLE_PLL_REF_CLOCK" range="10" readable="false">
|
||||
// <info>Writing with this flag set enables the PLL reference clock. Assert this flag after PLL reference clock is stable. (may be overwritten by @.DISABLE_PLL_REF_CLOCK)</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="DISABLE_CLOCK_DB0" range="12" readable="false">
|
||||
// <info>Writing with this flag set disables DB 0 clock forwarding (overrides @.ENABLE_CLOCK_DB0)</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="DISABLE_CLOCK_DB1" range="13" readable="false">
|
||||
// <info>Writing with this flag set disables DB 1 clock forwarding (overrides @.ENABLE_CLOCK_DB1)</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="DISABLE_PLL_REF_CLOCK" range="14" readable="false">
|
||||
// <info>Writing with this flag set disables the PLL reference clock (overrides @.ENABLE_PLL_REF_CLOCK). Assert this flag to reconfigure the clock.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="RELEASE_RESET_DB0" range="16" readable="false">
|
||||
// <info>Writing with this flag set releases DB 0 reset. (may be overwritten by @.ASSERT_RESET_DB0)</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="RELEASE_RESET_DB1" range="17" readable="false">
|
||||
// <info>Writing with this flag set releases DB 1 reset. (may be overwritten by @.ASSERT_RESET_DB1)</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="ASSERT_RESET_DB0" range="20" readable="false">
|
||||
// <info>Writing with this flag set asserts reset for DB 0 (overrides @.RELEASE_RESET_DB0)</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="ASSERT_RESET_DB1" range="21" readable="false">
|
||||
// <info>Writing with this flag set asserts reset for DB 1 (overrides @.RELEASE_RESET_DB1)</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
// </group>
|
||||
//
|
||||
// <group name="DIO_REGS">
|
||||
// <info>
|
||||
// Registers to control the GPIO buffer direction on the DIO board connected to the FPGA.
|
||||
// Make sure the GPIO lines between FPGA and GPIO board are not driven by two drivers.
|
||||
// Set the direction in the FPGA's DIO register appropriately.
|
||||
// </info>
|
||||
// <register name="DIO_DIRECTION_REGISTER" offset="0x30" size="32">
|
||||
// <info>
|
||||
// Set the direction of FPGA buffer connected to DIO ports on the DIO board.{br/}
|
||||
// Each bit represents one signal line. 0 = line is an input to the FPGA, 1 = line is an output driven by the FPGA.
|
||||
// </info>
|
||||
// <bitfield name="DIO_DIRECTION_A" range="11..0" initialvalue="0"/>
|
||||
// <bitfield name="DIO_DIRECTION_B" range="27..16" initialvalue="0"/>
|
||||
// </register>
|
||||
// </group>
|
||||
//
|
||||
// <group name="PS_CMI_REGS">
|
||||
// <info>
|
||||
// Cable present status register.
|
||||
// </info>
|
||||
// <register name="SERIAL_NUM_LOW_REG" offset="0x34" size="32">
|
||||
// <info>Least significant bytes of 5 byte serial number.</info>
|
||||
// </register>
|
||||
// <register name="SERIAL_NUM_HIGH_REG" offset="0x38" size="8">
|
||||
// <info>Most significant byte of 5 byte serial number.</info>
|
||||
// </register>
|
||||
// <register name="CMI_CONTROL_STATUS" offset="0x3C" size="32">
|
||||
// <info>Control CMI communication and delivers information on the CMI link status.</info>
|
||||
// <bitfield name="CMI_READY" range="0">
|
||||
// <info>Set if the device is ready to establish a PCI-Express link (affects CMI_CLP_READY bit).</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="OTHER_SIDE_DETECTED" range="31" writable="false">
|
||||
// <info>1 if an upstream CMI device has been detected.</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
// </group>
|
||||
//</regmap>
|
||||
//XmlParse xml_off
|
||||
@@ -0,0 +1,231 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: ps_power_regs
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Registers to control power supplies on the motherboard.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// BASE_ADDRESS : Base address for CtrlPort registers.
|
||||
// NUM_ADDRESSES : Number of bytes of address space to use.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module ps_power_regs #(
|
||||
parameter BASE_ADDRESS = 0,
|
||||
parameter NUM_ADDRESSES = 32
|
||||
) (
|
||||
input wire ctrlport_clk,
|
||||
input wire ctrlport_rst,
|
||||
|
||||
// Request
|
||||
input wire s_ctrlport_req_wr,
|
||||
input wire s_ctrlport_req_rd,
|
||||
input wire [19:0] s_ctrlport_req_addr,
|
||||
input wire [31:0] s_ctrlport_req_data,
|
||||
// Response
|
||||
output reg s_ctrlport_resp_ack,
|
||||
output reg [ 1:0] s_ctrlport_resp_status,
|
||||
output reg [31:0] s_ctrlport_resp_data,
|
||||
|
||||
// iPass
|
||||
output reg ipass_power_disable = 1'b0,
|
||||
input wire [ 1:0] ipass_power_fault_n,
|
||||
|
||||
// Oscillators
|
||||
output reg osc_100_en,
|
||||
output reg osc_122_88_en
|
||||
);
|
||||
|
||||
`include "../regmap/ps_power_regmap_utils.vh"
|
||||
`include "../../../../lib/rfnoc/core/ctrlport.vh"
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Address Calculation
|
||||
//----------------------------------------------------------
|
||||
|
||||
wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) &&
|
||||
(s_ctrlport_req_addr < BASE_ADDRESS + NUM_ADDRESSES);
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Internal Registers
|
||||
//----------------------------------------------------------
|
||||
|
||||
reg [1:0] ipass_power_sticky = 2'b00;
|
||||
reg [1:0] ipass_clear_sticky = 2'b00;
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Handling of ControlPort Requests
|
||||
//----------------------------------------------------------
|
||||
|
||||
always @(posedge ctrlport_clk) begin
|
||||
// Reset internal registers and responses
|
||||
if (ctrlport_rst) begin
|
||||
ipass_power_disable <= 1'b0;
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
s_ctrlport_resp_status <= CTRL_STS_OKAY;
|
||||
s_ctrlport_resp_data <= {CTRLPORT_ADDR_W {1'bx}};
|
||||
|
||||
osc_100_en <= 1'b0;
|
||||
osc_122_88_en <= 1'b0;
|
||||
|
||||
end else begin
|
||||
// Default assignments
|
||||
ipass_clear_sticky <= 2'b00;
|
||||
|
||||
// Write requests
|
||||
if (s_ctrlport_req_wr) begin
|
||||
// Always issue an ack and no data
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_status <= CTRL_STS_OKAY;
|
||||
s_ctrlport_resp_data <= {CTRLPORT_ADDR_W {1'bx}};
|
||||
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + IPASS_POWER_REG: begin
|
||||
ipass_power_disable <= s_ctrlport_req_data[IPASS_DISABLE_POWER_BIT];
|
||||
ipass_clear_sticky[0] <= s_ctrlport_req_data[IPASS_CLEAR_POWER_FAULT0];
|
||||
ipass_clear_sticky[1] <= s_ctrlport_req_data[IPASS_CLEAR_POWER_FAULT1];
|
||||
end
|
||||
|
||||
BASE_ADDRESS + OSC_POWER_REG: begin
|
||||
osc_100_en <= s_ctrlport_req_data[OSC_100];
|
||||
osc_122_88_en <= s_ctrlport_req_data[OSC_122_88];
|
||||
end
|
||||
|
||||
// Error on undefined address
|
||||
default: begin
|
||||
if (address_in_range) begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
|
||||
// No response if out of range
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
||||
// Read request
|
||||
end else if (s_ctrlport_req_rd) begin
|
||||
// Default assumption: valid request
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_status <= CTRL_STS_OKAY;
|
||||
s_ctrlport_resp_data <= {CTRLPORT_DATA_W {1'b0}};
|
||||
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + IPASS_POWER_REG: begin
|
||||
s_ctrlport_resp_data[IPASS_DISABLE_POWER_BIT] <= ipass_power_disable;
|
||||
s_ctrlport_resp_data[IPASS_POWER_FAULT0] <= ipass_power_sticky[0];
|
||||
s_ctrlport_resp_data[IPASS_POWER_FAULT1] <= ipass_power_sticky[1];
|
||||
end
|
||||
|
||||
BASE_ADDRESS + OSC_POWER_REG: begin
|
||||
s_ctrlport_resp_data[OSC_100] <= osc_100_en;
|
||||
s_ctrlport_resp_data[OSC_122_88] <= osc_122_88_en;
|
||||
end
|
||||
|
||||
// Error on undefined address
|
||||
default: begin
|
||||
s_ctrlport_resp_data <= {CTRLPORT_DATA_W {1'bx}};
|
||||
if (address_in_range) begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
|
||||
// No response if out of range
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
||||
// No request
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Sticky Logic of Power Registers
|
||||
//----------------------------------------------------------
|
||||
|
||||
// Synchronize asynchronous inputs
|
||||
wire [1:0] ipass_power_fault_lcl_n;
|
||||
synchronizer #(
|
||||
.WIDTH (2),
|
||||
.STAGES (2),
|
||||
.INITIAL_VAL (1'b0),
|
||||
.FALSE_PATH_TO_IN (1)
|
||||
) power_fault_sync (
|
||||
.clk (ctrlport_clk),
|
||||
.rst (ctrlport_rst),
|
||||
.in (ipass_power_fault_n),
|
||||
.out (ipass_power_fault_lcl_n)
|
||||
);
|
||||
|
||||
always @(posedge ctrlport_clk) begin
|
||||
if (ctrlport_rst) begin
|
||||
ipass_power_sticky <= 2'b00;
|
||||
end else begin
|
||||
// Keep value if not cleared or set in case of fault
|
||||
ipass_power_sticky <= (ipass_power_sticky & ~ipass_clear_sticky) | ~ipass_power_fault_lcl_n;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
|
||||
|
||||
//XmlParse xml_on
|
||||
//<regmap name="PS_POWER_REGMAP" readablestrobes="false" generatevhdl="true" ettusguidelines="true">
|
||||
// <group name="PS_POWER_REGS">
|
||||
// <info>
|
||||
// Registers to control power supplies on the motherboard.
|
||||
// </info>
|
||||
//
|
||||
// <register name="IPASS_POWER_REG" offset="0x00" size="32">
|
||||
// <info>Controls the power supplies for the iPass connectors.</info>
|
||||
// <bitfield name="IPASS_DISABLE_POWER_BIT" range="0">
|
||||
// <info>Set to 1 to disable power for both iPass connectors.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="IPASS_CLEAR_POWER_FAULT0" range="30" readable="false">
|
||||
// <info>Clear @.IPASS_POWER_FAULT0.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="IPASS_CLEAR_POWER_FAULT1" range="31" readable="false">
|
||||
// <info>Clear @.IPASS_POWER_FAULT1.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="IPASS_POWER_FAULT0" range="30" writable="false">
|
||||
// <info>
|
||||
// Asserted signal indicates a power fault in power switch for iPass
|
||||
// connector 0. Sticky bit. Asserted on occurrence. Reset using
|
||||
// @.IPASS_CLEAR_POWER_FAULT0.
|
||||
// </info>
|
||||
// </bitfield>
|
||||
// <bitfield name="IPASS_POWER_FAULT1" range="31" writable="false">
|
||||
// <info>
|
||||
// Asserted signal indicates a power fault in power switch for iPass
|
||||
// connector 1. Sticky bit. Asserted on occurrence. Reset using
|
||||
// @.IPASS_CLEAR_POWER_FAULT1.
|
||||
// </info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
//
|
||||
// <register name="OSC_POWER_REG" offset="0x04" size="32">
|
||||
// <info>Controls the power supplies for the oscillators.</info>
|
||||
// <bitfield name="OSC_100" range="0">
|
||||
// <info>Enables 5V power switch for the 100 MHz oscillator.</info>
|
||||
// </bitfield>
|
||||
// <bitfield name="OSC_122_88" range="1">
|
||||
// <info>Enables 5V power switch for the 122.88 MHz oscillator.</info>
|
||||
// </bitfield>
|
||||
// </register>
|
||||
// </group>
|
||||
//</regmap>
|
||||
//XmlParse xml_off
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: pwr_supply_clk_gen
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Generates a clock for one motherboard power supply.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// BASE_ADDRESS : Base address for CtrlPort registers.
|
||||
// NUM_ADDRESSES : Number of bytes of address space to use.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module pwr_supply_clk_gen#(
|
||||
parameter SOURCE_CLK_FREQ = 100_000_000,
|
||||
parameter TARGET_CLK_FREQ = 100_000
|
||||
) (
|
||||
// Base clock and reset
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
// Power supply clocks
|
||||
output reg pwr_supply_clk
|
||||
);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Counter Calculation / Definition
|
||||
//-----------------------------------------------------------------------------
|
||||
// Counter to generate the power supply switching clock
|
||||
|
||||
// Assumption: the ratio between the generated clock and the source clock is
|
||||
// even, therefore we can produce a 50% DC clock output.
|
||||
localparam MAX_COUNT = SOURCE_CLK_FREQ / TARGET_CLK_FREQ / 2;
|
||||
localparam COUNTER_W = $clog2(MAX_COUNT);
|
||||
reg [COUNTER_W-1:0] counter = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Clock Generation
|
||||
//-----------------------------------------------------------------------------
|
||||
// This process implements a simple clock divider for the power supply
|
||||
// switcher.
|
||||
|
||||
// SAFE COUNTER START! rst is a synchronous reset generated in the
|
||||
// clk domain; therefore, inherently safe.
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
counter <= 0;
|
||||
pwr_supply_clk <= 1'b0;
|
||||
end
|
||||
else begin
|
||||
// Add one every cycle to the counter
|
||||
counter <= counter + 1'b1;
|
||||
|
||||
// When the counter reaches its mid value, it is reset and the output clock
|
||||
// output is toggled.
|
||||
if (counter == MAX_COUNT-1) begin
|
||||
counter <= 0;
|
||||
pwr_supply_clk <= ~pwr_supply_clk;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
# Module: ps_cs_analysis
|
||||
#
|
||||
# Description:
|
||||
#
|
||||
# Analyze false path in PS SPI logic to ensure an upper delay boundary.
|
||||
#
|
||||
|
||||
# get project to a working state
|
||||
project_open -force "mb_cpld.qpf"
|
||||
create_timing_netlist
|
||||
update_timing_netlist
|
||||
|
||||
# Determine data path delay from MB CPLD chip select signal to MB CPLD internal
|
||||
# SPI slave
|
||||
set paths [report_path -from [get_registers {ps_spi_cs_n_decoded[0]}] -multi_corner]
|
||||
set spiSlaveCsPathDelay [lindex $paths 1]
|
||||
|
||||
# clock period at 250 MHz (clock driving the decoding registers)
|
||||
set maxDelay 4
|
||||
|
||||
# compare path from above with maximum delay
|
||||
if ([expr {$maxDelay < $spiSlaveCsPathDelay}]) {
|
||||
puts "MB CPLD SPI CS line longer than expected."
|
||||
exit 1
|
||||
}
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
|
||||
<cof>
|
||||
<output_filename>output_files/mb_cpld_converted.pof</output_filename>
|
||||
<n_pages>1</n_pages>
|
||||
<width>1</width>
|
||||
<mode>14</mode>
|
||||
<sof_data>
|
||||
<user_name>Page_0</user_name>
|
||||
<page_flags>1</page_flags>
|
||||
<bit0>
|
||||
<sof_filename>output_files/mb_cpld.sof<compress_bitstream>1</compress_bitstream></sof_filename>
|
||||
</bit0>
|
||||
</sof_data>
|
||||
<version>10</version>
|
||||
<create_cvp_file>0</create_cvp_file>
|
||||
<create_hps_iocsr>0</create_hps_iocsr>
|
||||
<auto_create_rpd>1</auto_create_rpd>
|
||||
<rpd_little_endian>1</rpd_little_endian>
|
||||
<options>
|
||||
<map_file>1</map_file>
|
||||
</options>
|
||||
<MAX10_device_options>
|
||||
<por>0</por>
|
||||
<io_pullup>1</io_pullup>
|
||||
<config_from_cfm0_only>0</config_from_cfm0_only>
|
||||
<isp_source>0</isp_source>
|
||||
<verify_protect>0</verify_protect>
|
||||
<epof>0</epof>
|
||||
<ufm_source>0</ufm_source>
|
||||
</MAX10_device_options>
|
||||
<advanced_options>
|
||||
<ignore_epcs_id_check>1</ignore_epcs_id_check>
|
||||
<ignore_condone_check>2</ignore_condone_check>
|
||||
<plc_adjustment>0</plc_adjustment>
|
||||
<post_chain_bitstream_pad_bytes>-1</post_chain_bitstream_pad_bytes>
|
||||
<post_device_bitstream_pad_bytes>-1</post_device_bitstream_pad_bytes>
|
||||
<bitslice_pre_padding>1</bitslice_pre_padding>
|
||||
</advanced_options>
|
||||
</cof>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: reset_generator
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Generates a power-on reset signal that is asserted at startup and stays
|
||||
// asserted for at least CYCLES_IN_RESET clock cycles.
|
||||
//
|
||||
// Internally, it generates a 1-bit synchronous signal (initialize) to safely
|
||||
// initialize the power_on_reset_counter incremental counter to 0's.
|
||||
//
|
||||
// A delayed version of the initializing signal is also generated
|
||||
// (counter_enable) to start counting.
|
||||
//
|
||||
// 1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_ 9_
|
||||
// clk _| |_| |_| |_| |_| |_| |_| |_| |_|
|
||||
// _____________________
|
||||
// initialize _____________|
|
||||
// _________
|
||||
// counter_enable _________________________|
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module reset_generator (
|
||||
input wire clk,
|
||||
output reg power_on_reset = 1'b1
|
||||
);
|
||||
|
||||
wire [0:0] counter_enable;
|
||||
wire [0:0] initialize;
|
||||
|
||||
synchronizer #(
|
||||
.WIDTH (1),
|
||||
.STAGES (3),
|
||||
.INITIAL_VAL (1'b0),
|
||||
.FALSE_PATH_TO_IN (0)
|
||||
) init_sync_inst (
|
||||
.clk (clk),
|
||||
.rst (1'b0),
|
||||
.in (1'b1),
|
||||
.out (initialize)
|
||||
);
|
||||
|
||||
synchronizer #(
|
||||
.WIDTH (1),
|
||||
.STAGES (3),
|
||||
.INITIAL_VAL (1'b0),
|
||||
.FALSE_PATH_TO_IN (0)
|
||||
) counter_en_sync_inst (
|
||||
.clk (clk),
|
||||
.rst (1'b0),
|
||||
.in (initialize),
|
||||
.out (counter_enable)
|
||||
);
|
||||
|
||||
// Internal synchronous reset generator.
|
||||
localparam CYCLES_IN_RESET = 20;
|
||||
reg [7:0] power_on_reset_counter = 8'b0;
|
||||
|
||||
// This block generates a synchronous reset in the clk domain that can be
|
||||
// used by downstream logic.
|
||||
//
|
||||
// power_on_reset_counter is first initialized to 0's upon assertion of
|
||||
// initialize. Some cycles later (3), upon assertion if counter_enable,
|
||||
// power_on_reset_counter starts to increment.
|
||||
//
|
||||
// power_on_reset will remain asserted until power_on_reset_counter reaches
|
||||
// cycles_in_reset, resulting in the deassertion of power_on_reset.
|
||||
always @(posedge clk) begin : power_on_reset_gen
|
||||
if (counter_enable) begin
|
||||
if (power_on_reset_counter == CYCLES_IN_RESET-1) begin
|
||||
power_on_reset <= 1'b0;
|
||||
end else begin
|
||||
power_on_reset_counter <= power_on_reset_counter + 1'b1;
|
||||
power_on_reset <= 1'b1;
|
||||
end
|
||||
end
|
||||
else if (initialize) begin
|
||||
power_on_reset_counter <= 8'b0;
|
||||
power_on_reset <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,288 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: spi_slave
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// SPI slave for configuration CPOL = CPHA = 0.
|
||||
// Transfers 8 bit = 1 byte MSB first. Parallel data has to be
|
||||
// provided and consumed immediately when flags are asserted.
|
||||
//
|
||||
// Limitation: clk frequency <= 2*sclk frequency
|
||||
//
|
||||
// Data request from sclk domain is triggered towards the clk domain ahead of
|
||||
// time. This is due to the clock domain crossing using the synchronizer and
|
||||
// processing pipeline stages.
|
||||
//
|
||||
// The worst case propagation delay of the used synchronizer is:
|
||||
//
|
||||
// 4 'clk' clock cycles:
|
||||
// 1 clock cycle of signal propagation to synchronizer
|
||||
// (data_request_sclk assertion)
|
||||
// 1 clock cycle to capture data with instability in first stage
|
||||
// 1 clock cycle to stabilize first stage
|
||||
// 1 clock cycle to capture data in second stage
|
||||
// (data_request_clk available in 'clk' domain)
|
||||
//
|
||||
// Once synchronized in 'clk' domain, there is one additional clock cycle to
|
||||
// derive data_out_valid and data_in_required. To ensure that transmit data
|
||||
// is registered a 'clk' cycle ahead of the actual transmission we need 2
|
||||
// more 'clk' clock cycles. This ensures that transmit_word has changed and
|
||||
// is stable for at least one 'clk' cycle before 'sclk' asserts again. Any
|
||||
// additional time required externally to respond to the control port
|
||||
// requests should be considered in this crossing as well. This is a total of
|
||||
// 7 clock cycles (+ctrlport response margin) @ clk domain. The minimum
|
||||
// required time in sclk domain to issue the request is calculated based on
|
||||
// the clock frequencies.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// CLK_FREQUENCY : Frequency of "clk"
|
||||
// SPI_FREQUENCY : Frequency of "sclk"
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module spi_slave #(
|
||||
parameter CLK_FREQUENCY = 50000000,
|
||||
parameter SPI_FREQUENCY = 10000000
|
||||
) (
|
||||
//---------------------------------------------------------------
|
||||
// SPI Interface
|
||||
//---------------------------------------------------------------
|
||||
|
||||
input wire sclk,
|
||||
input wire cs_n,
|
||||
input wire mosi,
|
||||
output wire miso,
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Parallel Interface
|
||||
//---------------------------------------------------------------
|
||||
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
output reg data_in_required,
|
||||
input wire data_in_valid,
|
||||
input wire [7:0] data_in,
|
||||
|
||||
output reg data_out_valid,
|
||||
output reg [7:0] data_out,
|
||||
|
||||
output wire active
|
||||
);
|
||||
|
||||
wire [0:0] data_request_clk;
|
||||
wire [0:0] reception_complete_clk;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// SPI Receiver @ sclk
|
||||
//---------------------------------------------------------------
|
||||
|
||||
reg [7:0] receiver_reg;
|
||||
reg [2:0] current_bit_index;
|
||||
reg reception_complete_sclk = 1'b0;
|
||||
reg [7:0] received_word;
|
||||
|
||||
always @(posedge sclk or posedge cs_n) begin
|
||||
// Reset logic on positive cs_n edge = slave idle
|
||||
if (cs_n) begin
|
||||
receiver_reg <= 8'b0;
|
||||
end
|
||||
// Rising edge of sclk
|
||||
else begin
|
||||
// Capture bits into shift register MSBs first
|
||||
receiver_reg <= {receiver_reg[6:0], mosi};
|
||||
end
|
||||
end
|
||||
|
||||
// Reset with cs_n might occur too early during clk sync.
|
||||
// Reset half way through the reception.
|
||||
always @(posedge sclk) begin
|
||||
// Complete word was received
|
||||
if (current_bit_index == 7) begin
|
||||
reception_complete_sclk <= 1'b1;
|
||||
received_word <= {receiver_reg[6:0], mosi};
|
||||
|
||||
// Reset after half transaction
|
||||
end else if (current_bit_index == 3) begin
|
||||
reception_complete_sclk <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Handover of data sclk -> clk
|
||||
//---------------------------------------------------------------
|
||||
|
||||
synchronizer #(
|
||||
.WIDTH (1),
|
||||
.STAGES (2),
|
||||
.INITIAL_VAL (1'b0),
|
||||
.FALSE_PATH_TO_IN (1)
|
||||
) data_sync_inst (
|
||||
.clk (clk),
|
||||
.rst (1'b0),
|
||||
.in (reception_complete_sclk),
|
||||
.out (reception_complete_clk)
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Parallel interface data output @ clk
|
||||
//---------------------------------------------------------------
|
||||
|
||||
reg reception_complete_clk_delayed = 1'b0;
|
||||
|
||||
// Propagate toggling signal without reset to ensure stability on reset
|
||||
always @(posedge clk) begin
|
||||
// Capture last state of reception
|
||||
reception_complete_clk_delayed <= reception_complete_clk;
|
||||
end
|
||||
|
||||
// Derive data and control signal
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
data_out_valid <= 1'b0;
|
||||
data_out <= 8'b0;
|
||||
end
|
||||
else begin
|
||||
// Default assignment
|
||||
data_out_valid <= 1'b0;
|
||||
|
||||
// Provide data to output on rising_edge
|
||||
if (reception_complete_clk & ~reception_complete_clk_delayed) begin
|
||||
// Data can simply be captured as the reception complete signal
|
||||
// indicates stable values in received_word.
|
||||
data_out <= received_word;
|
||||
data_out_valid <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// SPI Transmitter @ sclk
|
||||
//---------------------------------------------------------------
|
||||
|
||||
// Data request calculation:
|
||||
// SCLK_CYCLES_DURING_DATA_REQ = 8 clk period / sclk period
|
||||
// Clock periods are expressed by reciprocal of frequencies.
|
||||
// Term "+CLK_FREQUENCY-1" is used to round up the result in integer logic.
|
||||
localparam SCLK_CYCLES_DURING_DATA_REQ = (8*SPI_FREQUENCY + CLK_FREQUENCY-1)/CLK_FREQUENCY;
|
||||
// subtract from 8 bits per transfer to get target index
|
||||
localparam DATA_REQ_BIT_INDEX = 8 - SCLK_CYCLES_DURING_DATA_REQ;
|
||||
|
||||
reg [7:0] transmit_bits;
|
||||
reg [7:0] transmit_word;
|
||||
reg data_request_sclk = 1'b0;
|
||||
|
||||
always @(negedge sclk or posedge cs_n) begin
|
||||
// Reset logic on positive cs_n edge = slave idle
|
||||
if (cs_n) begin
|
||||
current_bit_index <= 3'b0;
|
||||
data_request_sclk <= 1'b0;
|
||||
transmit_bits <= 8'b0;
|
||||
end
|
||||
// Falling edge of sclk
|
||||
else begin
|
||||
// Fill or move shift register for byte transmissions
|
||||
if (current_bit_index == 7) begin
|
||||
transmit_bits <= transmit_word;
|
||||
end else begin
|
||||
transmit_bits <= {transmit_bits[6:0], 1'b0};
|
||||
end
|
||||
|
||||
// Update bit index
|
||||
current_bit_index <= current_bit_index + 1'b1;
|
||||
|
||||
// Trigger request for new word at start of calculated index
|
||||
if (current_bit_index == DATA_REQ_BIT_INDEX-1) begin
|
||||
data_request_sclk <= 1'b1;
|
||||
// Reset after half the reception in case cs_n is not changed in between
|
||||
// two transactions.
|
||||
end else if (current_bit_index == (DATA_REQ_BIT_INDEX+4-1)%8) begin
|
||||
data_request_sclk <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Drive miso output with data when cs_n low
|
||||
assign miso = cs_n ? 1'bz : transmit_bits[7];
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Handover of Data Request sclk -> clk
|
||||
//---------------------------------------------------------------
|
||||
|
||||
synchronizer #(
|
||||
.WIDTH (1),
|
||||
.STAGES (2),
|
||||
.INITIAL_VAL (1'b0),
|
||||
.FALSE_PATH_TO_IN (1)
|
||||
) request_sync_inst (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.in (data_request_sclk),
|
||||
.out (data_request_clk)
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Parallel Interface Data Input Control
|
||||
//---------------------------------------------------------------
|
||||
|
||||
reg data_request_clk_delayed;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
data_request_clk_delayed <= 1'b0;
|
||||
data_in_required <= 1'b0;
|
||||
transmit_word <= 8'b0;
|
||||
end
|
||||
else begin
|
||||
// Default assignment
|
||||
data_in_required <= 1'b0;
|
||||
|
||||
// Capture last state of data request
|
||||
data_request_clk_delayed <= data_request_clk;
|
||||
|
||||
// Request data from input
|
||||
if (~data_request_clk_delayed & data_request_clk) begin
|
||||
data_in_required <= 1'b1;
|
||||
end
|
||||
|
||||
// Capture new data if valid data available, 0 otherwise.
|
||||
if (data_in_required) begin
|
||||
if (data_in_valid) begin
|
||||
transmit_word <= data_in;
|
||||
end else begin
|
||||
transmit_word <= 8'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Chip Select
|
||||
//---------------------------------------------------------------
|
||||
// Driven as active signal in parallel clock domain
|
||||
|
||||
wire cs_n_clk;
|
||||
assign active = ~cs_n_clk;
|
||||
synchronizer #(
|
||||
.WIDTH (1),
|
||||
.STAGES (2),
|
||||
.INITIAL_VAL (1'b1),
|
||||
.FALSE_PATH_TO_IN (1)
|
||||
) active_sync_inst (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.in (cs_n),
|
||||
.out (cs_n_clk)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,238 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: spi_slave_to_ctrlport_master
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// SPI slave to ContolPort master conversion in order to tunnel control port
|
||||
// request through an SPI bus.
|
||||
//
|
||||
// The request format on SPI is defined as:
|
||||
//
|
||||
// Write request:
|
||||
// 1'b1 = write, 15 bit address, 32 bit data (MOSI), 8 bit processing gap,
|
||||
// 5 bit padding, 1 bit ack, 2 bit status
|
||||
//
|
||||
// Read request:
|
||||
// 1'b0 = read, 15 bit address, 8 bit processing gap, 32 bit data (MISO),
|
||||
// 5 bit padding, 1 bit ack, 2 bit status
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// CLK_FREQUENCY : Frequency of "clk"
|
||||
// SPI_FREQUENCY : Frequency of "sclk"
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module spi_slave_to_ctrlport_master #(
|
||||
parameter CLK_FREQUENCY = 50000000,
|
||||
parameter SPI_FREQUENCY = 10000000
|
||||
) (
|
||||
//---------------------------------------------------------------
|
||||
// ControlPort Master
|
||||
//---------------------------------------------------------------
|
||||
|
||||
input wire ctrlport_clk,
|
||||
input wire ctrlport_rst,
|
||||
|
||||
output wire m_ctrlport_req_wr,
|
||||
output wire m_ctrlport_req_rd,
|
||||
output wire [19:0] m_ctrlport_req_addr,
|
||||
output wire [31:0] m_ctrlport_req_data,
|
||||
|
||||
input wire m_ctrlport_resp_ack,
|
||||
input wire [ 1:0] m_ctrlport_resp_status,
|
||||
input wire [31:0] m_ctrlport_resp_data,
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// SPI Slave
|
||||
//---------------------------------------------------------------
|
||||
|
||||
input wire sclk,
|
||||
input wire cs_n,
|
||||
input wire mosi,
|
||||
output wire miso
|
||||
);
|
||||
|
||||
`include "../../../../lib/rfnoc/core/ctrlport.vh"
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// SPI Slave
|
||||
//---------------------------------------------------------------
|
||||
|
||||
wire [7:0] data_in;
|
||||
wire [7:0] data_out;
|
||||
wire data_in_valid;
|
||||
wire data_out_valid;
|
||||
wire data_in_required;
|
||||
wire spi_slave_active;
|
||||
|
||||
spi_slave #(
|
||||
.CLK_FREQUENCY (CLK_FREQUENCY),
|
||||
.SPI_FREQUENCY (SPI_FREQUENCY)
|
||||
) spi_slave_async (
|
||||
.sclk (sclk),
|
||||
.cs_n (cs_n),
|
||||
.mosi (mosi),
|
||||
.miso (miso),
|
||||
.clk (ctrlport_clk),
|
||||
.rst (ctrlport_rst),
|
||||
.data_in_required (data_in_required),
|
||||
.data_in_valid (data_in_valid),
|
||||
.data_in (data_in),
|
||||
.data_out_valid (data_out_valid),
|
||||
.data_out (data_out),
|
||||
.active (spi_slave_active)
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Reset Generation from SPI Slave
|
||||
//---------------------------------------------------------------
|
||||
|
||||
reg spi_slave_active_delayed = 1'b0;
|
||||
always @(posedge ctrlport_clk) begin
|
||||
if (ctrlport_rst) begin
|
||||
spi_slave_active_delayed <= 1'b0;
|
||||
end
|
||||
else begin
|
||||
spi_slave_active_delayed <= spi_slave_active;
|
||||
end
|
||||
end
|
||||
|
||||
// Trigger reset on falling edge of active signal (rising edge of cs_n)
|
||||
wire spi_slave_reset;
|
||||
assign spi_slave_reset = spi_slave_active_delayed & (~spi_slave_active);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Transfer Constants
|
||||
//---------------------------------------------------------------
|
||||
|
||||
localparam NUM_BYTES_TRANSACTION = 8;
|
||||
localparam NUM_BYTES_WRITE_REQUEST_PAYLOAD = 6;
|
||||
localparam NUM_BYTES_READ_REQUEST_PAYLOAD = 2;
|
||||
localparam MAX_BYTES_RESPONSE_PAYLOAD = 5;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Data Receiver
|
||||
//---------------------------------------------------------------
|
||||
|
||||
reg [3:0] num_bytes_received;
|
||||
reg request_received;
|
||||
reg write_request;
|
||||
reg provide_response;
|
||||
reg [NUM_BYTES_WRITE_REQUEST_PAYLOAD*8-1:0] request_reg = {NUM_BYTES_WRITE_REQUEST_PAYLOAD*8 {1'b0}};
|
||||
|
||||
always @(posedge ctrlport_clk) begin
|
||||
if (ctrlport_rst || spi_slave_reset) begin
|
||||
num_bytes_received <= 4'b0;
|
||||
request_received <= 1'b0;
|
||||
write_request <= 1'b0;
|
||||
provide_response <= 1'b0;
|
||||
end
|
||||
else begin
|
||||
// Counter number of received bytes
|
||||
if (data_out_valid) begin
|
||||
// Increment counter
|
||||
num_bytes_received <= num_bytes_received + 1'b1;
|
||||
|
||||
if (num_bytes_received == NUM_BYTES_TRANSACTION-1) begin
|
||||
num_bytes_received <= 4'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// Check for read / write on first received byte's MSB
|
||||
if (data_out_valid && (num_bytes_received == 0)) begin
|
||||
write_request <= data_out[7];
|
||||
end
|
||||
|
||||
// Detect complete request
|
||||
request_received <= 1'b0;
|
||||
if (data_out_valid) begin
|
||||
if (write_request && (num_bytes_received == NUM_BYTES_WRITE_REQUEST_PAYLOAD-1)) begin
|
||||
request_received <= 1'b1;
|
||||
provide_response <= 1'b1;
|
||||
end else if (~write_request && (num_bytes_received == NUM_BYTES_READ_REQUEST_PAYLOAD-1)) begin
|
||||
request_received <= 1'b1;
|
||||
provide_response <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// Detect end of response on last received byte
|
||||
if (num_bytes_received == NUM_BYTES_TRANSACTION-1) begin
|
||||
provide_response <= 1'b0;
|
||||
end
|
||||
|
||||
// Capture data into shift register
|
||||
if (data_out_valid) begin
|
||||
request_reg <= {request_reg[NUM_BYTES_WRITE_REQUEST_PAYLOAD*8-8-1:0], data_out};
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Drive ControlPort
|
||||
localparam SPI_TRANSFER_ADDRESS_WIDTH = 15;
|
||||
assign m_ctrlport_req_wr = request_received && write_request;
|
||||
assign m_ctrlport_req_rd = request_received && ~write_request;
|
||||
assign m_ctrlport_req_data = request_reg[CTRLPORT_DATA_W-1:0];
|
||||
assign m_ctrlport_req_addr = (write_request) ?
|
||||
{5'b0, request_reg[CTRLPORT_DATA_W+:SPI_TRANSFER_ADDRESS_WIDTH]} :
|
||||
{5'b0, request_reg[0+:SPI_TRANSFER_ADDRESS_WIDTH]};
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Response Handling
|
||||
//---------------------------------------------------------------
|
||||
|
||||
reg [MAX_BYTES_RESPONSE_PAYLOAD*8-1:0] response_reg;
|
||||
reg ready_for_response; // active during processing gap
|
||||
wire write_response_byte;
|
||||
|
||||
always @(posedge ctrlport_clk) begin
|
||||
if (ctrlport_rst || spi_slave_reset) begin
|
||||
response_reg <= {8*MAX_BYTES_RESPONSE_PAYLOAD {1'b0}};
|
||||
ready_for_response <= 1'b0;
|
||||
end
|
||||
else begin
|
||||
// Reset response on new request
|
||||
if (request_received) begin
|
||||
ready_for_response <= 1'b1;
|
||||
if (write_request) begin
|
||||
// Just last byte -> padding, ack flag, CMDERR, padding (data length)
|
||||
response_reg <= {5'b0, 1'b1, CTRL_STS_CMDERR, {CTRLPORT_DATA_W{1'b0}}};
|
||||
end else begin
|
||||
// Last 5 bytes -> data = 0, Padding, ack flag, CMDERR
|
||||
response_reg <= {{CTRLPORT_DATA_W{1'b0}}, 5'b0, 1'b1, CTRL_STS_CMDERR};
|
||||
end
|
||||
|
||||
// Capture response within processing gap, leave default response from above otherwise
|
||||
end else if (m_ctrlport_resp_ack && ready_for_response) begin
|
||||
if (write_request) begin
|
||||
response_reg <= {5'b0, m_ctrlport_resp_ack, m_ctrlport_resp_status, {CTRLPORT_DATA_W{1'b0}}};
|
||||
end else begin
|
||||
response_reg <= {m_ctrlport_resp_data, 5'b0, m_ctrlport_resp_ack, m_ctrlport_resp_status};
|
||||
end
|
||||
end
|
||||
|
||||
// Shift data after writing to slave
|
||||
if (write_response_byte) begin
|
||||
response_reg <= {response_reg[0+:(MAX_BYTES_RESPONSE_PAYLOAD-1)*8], 8'b0};
|
||||
ready_for_response <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Response is written after request part has been transferred
|
||||
assign write_response_byte = data_in_required && provide_response;
|
||||
|
||||
// Assign SPI slave inputs
|
||||
assign data_in = response_reg[(MAX_BYTES_RESPONSE_PAYLOAD-1)*8+:8];
|
||||
assign data_in_valid = write_response_byte;
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
Reference in New Issue
Block a user