diff options
author | Max Köhler <max.koehler@ni.com> | 2021-02-05 13:14:41 -0600 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2021-06-10 11:56:58 -0500 |
commit | 7015f5ed2d495f3908773b7c7d74864d0cc3871a (patch) | |
tree | e9c9f7d95f5c35c089bfc9534707934bfe41344a /fpga/usrp3/top/x400/cpld/spi_slave.v | |
parent | 6d3765605262016a80f71e36357f749ea35cbe5a (diff) | |
download | uhd-7015f5ed2d495f3908773b7c7d74864d0cc3871a.tar.gz uhd-7015f5ed2d495f3908773b7c7d74864d0cc3871a.tar.bz2 uhd-7015f5ed2d495f3908773b7c7d74864d0cc3871a.zip |
fpga: x400: cpld: Add support for X410 motherboard CPLD
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com>
Diffstat (limited to 'fpga/usrp3/top/x400/cpld/spi_slave.v')
-rw-r--r-- | fpga/usrp3/top/x400/cpld/spi_slave.v | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/cpld/spi_slave.v b/fpga/usrp3/top/x400/cpld/spi_slave.v new file mode 100644 index 000000000..ab2f16fea --- /dev/null +++ b/fpga/usrp3/top/x400/cpld/spi_slave.v @@ -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 |