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,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
|
||||
Reference in New Issue
Block a user