aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x400/dboards/ctrlport_byte_deserializer.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/top/x400/dboards/ctrlport_byte_deserializer.v')
-rw-r--r--fpga/usrp3/top/x400/dboards/ctrlport_byte_deserializer.v186
1 files changed, 186 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/dboards/ctrlport_byte_deserializer.v b/fpga/usrp3/top/x400/dboards/ctrlport_byte_deserializer.v
new file mode 100644
index 000000000..b8fde0025
--- /dev/null
+++ b/fpga/usrp3/top/x400/dboards/ctrlport_byte_deserializer.v
@@ -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