fpga: x4xx: Refactor MB CPLD code for future devices

Original-commit: e2a79712a7949ffec88135236c744dc5d8bde217
This commit is contained in:
Javier Valenzuela
2023-05-23 09:05:27 +02:00
committed by Martin Braun
parent e9a6fe4e2e
commit ffdcc016cc
33 changed files with 5895 additions and 14 deletions
+437
View File
@@ -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
+276
View File
@@ -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
+299
View File
@@ -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
+405
View File
@@ -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
+231
View File
@@ -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
+73
View File
@@ -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
+93
View File
@@ -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
+288
View File
@@ -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