fpga: x400: Add support for X410 motherboard FPGA
Co-authored-by: Andrew Moch <Andrew.Moch@ni.com> Co-authored-by: Daniel Jepson <daniel.jepson@ni.com> Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com> Co-authored-by: Joerg Hofrichter <joerg.hofrichter@ni.com> Co-authored-by: Kumaran Subramoniam <kumaran.subramoniam@ni.com> Co-authored-by: Max Köhler <max.koehler@ni.com> Co-authored-by: Michael Auchter <michael.auchter@ni.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Wade Fife <wade.fife@ettus.com> Co-authored-by: Hector Rubio <hrubio@ni.com> Original-commit: 6d3765605262016a80f71e36357f749ea35cbe5a
This commit is contained in:
committed by
Aaron Rossetto
co-authored by
Andrew Moch
Daniel Jepson
Javier Valenzuela
Joerg Hofrichter
Kumaran Subramoniam
Max Köhler
Michael Auchter
Paul Butler
Hector Rubio
parent
bfef20ea45
commit
61782b02d7
@@ -0,0 +1,186 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: ctrlport_byte_deserializer
|
||||
//
|
||||
// Description:
|
||||
// Slave interface of CtrlPort interface serialized as byte stream.
|
||||
// See description in ctrlport_byte_serializer module for more details.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module ctrlport_byte_deserializer (
|
||||
input wire ctrlport_clk,
|
||||
input wire ctrlport_rst,
|
||||
|
||||
// Request
|
||||
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,
|
||||
|
||||
// Response
|
||||
input wire m_ctrlport_resp_ack,
|
||||
input wire [ 1:0] m_ctrlport_resp_status,
|
||||
input wire [31:0] m_ctrlport_resp_data,
|
||||
|
||||
// byte interface
|
||||
input wire [ 7:0] bytestream_data_in,
|
||||
input wire bytestream_valid_in,
|
||||
input wire bytestream_direction,
|
||||
output reg [ 7:0] bytestream_data_out = 8'b0,
|
||||
output reg bytestream_valid_out = 1'b0,
|
||||
output reg bytestream_output_enable = 1'b0
|
||||
);
|
||||
|
||||
`include "../../../lib/rfnoc/core/ctrlport.vh"
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// transfer constants
|
||||
//---------------------------------------------------------------
|
||||
// derived from transaction specification
|
||||
localparam NUM_BYTES_RX_READ = 2;
|
||||
localparam NUM_BYTES_TX_READ = 5;
|
||||
localparam NUM_BYTES_RX_WRITE = 6;
|
||||
localparam NUM_BYTES_TX_WRITE = 1;
|
||||
|
||||
localparam SPI_TRANSFER_ADDRESS_WIDTH = 15;
|
||||
|
||||
//----------------------------------------------------------
|
||||
// handle transfer
|
||||
//----------------------------------------------------------
|
||||
localparam INIT_RX = 2'd0;
|
||||
localparam RECEIVE = 2'd1;
|
||||
localparam WAIT_RESPONSE = 2'd2;
|
||||
localparam SENDING = 2'd3;
|
||||
|
||||
// internal registers
|
||||
reg [ 1:0] state = INIT_RX;
|
||||
reg [NUM_BYTES_RX_WRITE*8-1:0] request_cache = {NUM_BYTES_RX_WRITE*8 {1'b0}};
|
||||
reg [ NUM_BYTES_TX_READ*8-1:0] response_cache = {NUM_BYTES_TX_READ*8 {1'b0}};
|
||||
reg [ 2:0] byte_counter = 3'b0;
|
||||
reg transfer_complete = 1'b0;
|
||||
reg write_transfer = 1'b0;
|
||||
|
||||
// input registers to relax input timing
|
||||
reg [7:0] bytestream_data_in_reg = 8'b0;
|
||||
reg bytestream_valid_in_reg = 1'b0;
|
||||
reg bytestream_direction_reg = 1'b0;
|
||||
always @ (posedge ctrlport_clk) begin
|
||||
bytestream_data_in_reg <= bytestream_data_in;
|
||||
bytestream_valid_in_reg <= bytestream_valid_in;
|
||||
bytestream_direction_reg <= bytestream_direction;
|
||||
end
|
||||
|
||||
// state machine
|
||||
always @ (posedge ctrlport_clk) begin
|
||||
if (ctrlport_rst) begin
|
||||
state <= INIT_RX;
|
||||
byte_counter <= 3'b0;
|
||||
transfer_complete <= 1'b0;
|
||||
bytestream_output_enable <= 1'b0;
|
||||
|
||||
end else begin
|
||||
// default assignments
|
||||
transfer_complete <= 1'b0;
|
||||
// direction defined by master
|
||||
bytestream_output_enable <= bytestream_direction;
|
||||
bytestream_valid_out <= 1'b0;
|
||||
|
||||
case (state)
|
||||
// additional cycle for switching to make sure valid signal is driven
|
||||
// from master when being in RECEIVE state
|
||||
INIT_RX: begin
|
||||
byte_counter <= 3'b0;
|
||||
if (bytestream_direction_reg == 0) begin
|
||||
state <= RECEIVE;
|
||||
end
|
||||
end
|
||||
|
||||
// wait for reception of request from master
|
||||
RECEIVE: begin
|
||||
if (bytestream_valid_in_reg) begin
|
||||
byte_counter <= byte_counter + 1'b1;
|
||||
request_cache <= {request_cache[NUM_BYTES_RX_WRITE*8-9:0], bytestream_data_in_reg};
|
||||
|
||||
// capture write or read
|
||||
if (byte_counter == 0) begin
|
||||
write_transfer <= bytestream_data_in_reg[7];
|
||||
end
|
||||
|
||||
// wait until request completes
|
||||
if ((write_transfer && byte_counter == NUM_BYTES_RX_WRITE-1) ||
|
||||
(~write_transfer && byte_counter == NUM_BYTES_RX_READ-1)) begin
|
||||
transfer_complete <= 1'b1;
|
||||
state <= WAIT_RESPONSE;
|
||||
end
|
||||
end
|
||||
|
||||
// Workaround for missing pull down resistor:
|
||||
// Use pull up and schmitt trigger to detect FPGA reload by line going high unexpectedly
|
||||
if (bytestream_direction_reg == 1) begin
|
||||
state <= INIT_RX;
|
||||
end
|
||||
end
|
||||
|
||||
WAIT_RESPONSE: begin
|
||||
byte_counter <= 3'b0;
|
||||
if (m_ctrlport_resp_ack) begin
|
||||
state <= SENDING;
|
||||
|
||||
if (write_transfer) begin
|
||||
response_cache <= {5'b0, 1'b1, m_ctrlport_resp_status, 32'b0};
|
||||
end else begin
|
||||
response_cache <= {m_ctrlport_resp_data, 5'b0, 1'b1, m_ctrlport_resp_status};
|
||||
end
|
||||
end
|
||||
|
||||
//abort by host
|
||||
if (bytestream_direction_reg == 0) begin
|
||||
state <= INIT_RX;
|
||||
end
|
||||
end
|
||||
|
||||
SENDING: begin
|
||||
bytestream_valid_out <= 1'b1;
|
||||
bytestream_data_out <= response_cache[NUM_BYTES_TX_READ*8-8+:8];
|
||||
response_cache <= {response_cache[NUM_BYTES_TX_READ*8-9:0], 8'b0};
|
||||
byte_counter <= byte_counter + 1'b1;
|
||||
|
||||
// wait until request completes
|
||||
if ((write_transfer && byte_counter == NUM_BYTES_TX_WRITE-1) ||
|
||||
(~write_transfer && byte_counter == NUM_BYTES_TX_READ-1)) begin
|
||||
state <= INIT_RX;
|
||||
end
|
||||
|
||||
//abort by host
|
||||
if (bytestream_direction_reg == 0) begin
|
||||
state <= INIT_RX;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
state <= INIT_RX;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
//----------------------------------------------------------
|
||||
// assign request to ctrlport
|
||||
//----------------------------------------------------------
|
||||
assign m_ctrlport_req_wr = write_transfer & transfer_complete;
|
||||
assign m_ctrlport_req_rd = ~write_transfer & transfer_complete;
|
||||
assign m_ctrlport_req_data = request_cache[0+:CTRLPORT_DATA_W];
|
||||
assign m_ctrlport_req_addr = (write_transfer) ?
|
||||
// Skipping data in LSBs to get to the address for writes.
|
||||
{5'b0, request_cache[CTRLPORT_DATA_W+:SPI_TRANSFER_ADDRESS_WIDTH]} :
|
||||
// Full request = address of 2 bytes in LSBs.
|
||||
{5'b0, request_cache[0+:SPI_TRANSFER_ADDRESS_WIDTH]};
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
Reference in New Issue
Block a user