// // 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