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
229 lines
8.0 KiB
Verilog
229 lines
8.0 KiB
Verilog
//
|
|
// Copyright 2021 Ettus Research, a National Instruments Brand
|
|
//
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
//
|
|
// Module: ctrlport_byte_serializer
|
|
//
|
|
// Description:
|
|
// Serializes CtrlPort requests into a byte stream.
|
|
//
|
|
// The serialized data is similar to an AXI4-Streaming interface with one byte
|
|
// per clock cycle and a valid signal. Direction controls the current transmission
|
|
// direction. 0 = Master to Slave, 1 = Slave to Master, where this module is the
|
|
// master. Direction is always present in direction master to slave where the
|
|
// other signals valid and data can be shared on a tri-state bus.
|
|
//
|
|
// The transmission is defined as described below. The bytes are transmitted MSB
|
|
// first.
|
|
// Write request:
|
|
// 1'b1 = write, 15 bit address, 32 bit data (MOSI) = 6 bytes request
|
|
// 5 bit padding, 1 bit ack, 2 bit status = 1 byte response
|
|
// Read request:
|
|
// 1'b0 = read, 15 bit address = 2 bytes request
|
|
// 32 bit data, 5 bit padding, 1 bit ack, 2 bit status = 5 bytes response
|
|
//
|
|
// When sharing valid and data signal lines between master and slave the
|
|
// timing is defined as:
|
|
//
|
|
// clk __/--\__/--\__/--\__/--\__/--\__/--\__/--\__/--\__/--\__/--\__/--\__
|
|
// direction _______________________/-----------------\____________
|
|
// master output enable _____/-----------------\______________________________
|
|
// slave output enable _____________________________/-----------------\______
|
|
// valid & data zzzzz| Master driven | zzz | Slave driven | zzzzz
|
|
// transaction <--- Request ---><--- Response --->
|
|
//
|
|
// The slave should use the direction signal to derive it's own output enable
|
|
// leaving the master the option to terminate the transaction.
|
|
// On switch from slave to master the direction has to be changed at least
|
|
// one clock cycle before enabling the master output enable to avoid driving the
|
|
// bus from two sources.
|
|
//
|
|
|
|
`default_nettype none
|
|
|
|
module ctrlport_byte_serializer (
|
|
// Clock and Reset
|
|
input wire ctrlport_clk,
|
|
input wire ctrlport_rst,
|
|
|
|
// Request
|
|
input wire s_ctrlport_req_wr,
|
|
input wire s_ctrlport_req_rd,
|
|
input wire [19:0] s_ctrlport_req_addr,
|
|
input wire [31:0] s_ctrlport_req_data,
|
|
|
|
// Response
|
|
output reg s_ctrlport_resp_ack = 1'b0,
|
|
output reg [ 1:0] s_ctrlport_resp_status = 2'b0,
|
|
output reg [31:0] s_ctrlport_resp_data = 32'b0,
|
|
|
|
// GPIO interface
|
|
input wire [ 7:0] bytestream_data_in,
|
|
input wire bytestream_valid_in,
|
|
output reg [ 7:0] bytestream_data_out = 8'b0,
|
|
output reg bytestream_valid_out = 1'b0,
|
|
output reg bytestream_direction = 1'b0,
|
|
output reg bytestream_output_enable = 1'b1
|
|
);
|
|
|
|
`include "../../../lib/rfnoc/core/ctrlport.vh"
|
|
|
|
//---------------------------------------------------------------
|
|
// transfer constants
|
|
//---------------------------------------------------------------
|
|
// derived from the transaction format (see description above)
|
|
localparam NUM_BYTES_TX_READ = 2;
|
|
localparam NUM_BYTES_RX_READ = 5;
|
|
localparam NUM_BYTES_TX_WRITE = 6;
|
|
localparam NUM_BYTES_RX_WRITE = 1;
|
|
|
|
localparam TIMEOUT_COUNTER_WIDTH = 6;
|
|
|
|
//----------------------------------------------------------
|
|
// FSM to handle transfers
|
|
//----------------------------------------------------------
|
|
localparam IDLE = 3'd0;
|
|
localparam SENDING = 3'd1;
|
|
localparam INIT_RX = 3'd2;
|
|
localparam DIR_SWITCH = 3'd3;
|
|
localparam DIR_SWITCH_DLY = 3'd4;
|
|
localparam RECEIVING = 3'd5;
|
|
localparam ACK = 3'd6;
|
|
localparam TIMEOUT = 3'd7;
|
|
|
|
// input registers to relax input timing
|
|
reg [7:0] bytestream_data_in_reg = 8'b0;
|
|
reg bytestream_valid_in_reg = 1'b0;
|
|
always @ (posedge ctrlport_clk) begin
|
|
bytestream_data_in_reg <= bytestream_data_in;
|
|
bytestream_valid_in_reg <= bytestream_valid_in;
|
|
end
|
|
|
|
// internal registers
|
|
reg [ 2:0] state = IDLE;
|
|
reg [NUM_BYTES_TX_WRITE*8-1:0] request_cache = {NUM_BYTES_TX_WRITE*8{1'b0}};
|
|
reg [ NUM_BYTES_RX_READ*8-1:0] response_cache = {NUM_BYTES_RX_READ*8{1'b0}};
|
|
reg [ 2:0] byte_counter = 3'b0;
|
|
reg write_transfer = 1'b0;
|
|
reg [TIMEOUT_COUNTER_WIDTH-1:0] timeout_counter = {TIMEOUT_COUNTER_WIDTH {1'b0}};
|
|
|
|
always @ (posedge ctrlport_clk) begin
|
|
if (ctrlport_rst) begin
|
|
state <= IDLE;
|
|
|
|
bytestream_valid_out <= 1'b0;
|
|
byte_counter <= 3'b0;
|
|
bytestream_direction <= 1'b0;
|
|
bytestream_output_enable <= 1'b1;
|
|
|
|
s_ctrlport_resp_ack <= 1'b0;
|
|
end else begin
|
|
case (state)
|
|
IDLE: begin
|
|
// reset values from previous states
|
|
s_ctrlport_resp_ack <= 1'b0;
|
|
bytestream_valid_out <= 1'b0;
|
|
bytestream_output_enable <= 1'b1;
|
|
byte_counter <= 3'b0;
|
|
timeout_counter <= {TIMEOUT_COUNTER_WIDTH {1'b0}};
|
|
|
|
// start transmission on read or write
|
|
if (s_ctrlport_req_rd || s_ctrlport_req_wr) begin
|
|
state <= SENDING;
|
|
request_cache <= {s_ctrlport_req_wr, s_ctrlport_req_addr[14:0], s_ctrlport_req_data};
|
|
write_transfer <= s_ctrlport_req_wr;
|
|
end
|
|
end
|
|
|
|
// send as many bytes as required for read / write
|
|
SENDING: begin
|
|
bytestream_data_out <= request_cache[NUM_BYTES_TX_WRITE*8-8+:8];
|
|
request_cache <= {request_cache[NUM_BYTES_TX_WRITE*8-9:0], 8'b0};
|
|
bytestream_valid_out <= 1'b1;
|
|
byte_counter <= byte_counter + 1'b1;
|
|
|
|
if ((write_transfer && byte_counter == NUM_BYTES_TX_WRITE-1) ||
|
|
(~write_transfer && byte_counter == NUM_BYTES_TX_READ-1)) begin
|
|
state <= INIT_RX;
|
|
end
|
|
end
|
|
|
|
// first cycle for switching to make sure valid signal is driven
|
|
// from slave when being in RECEIVING state
|
|
INIT_RX: begin
|
|
state <= DIR_SWITCH;
|
|
|
|
bytestream_direction <= 1'b1;
|
|
bytestream_output_enable <= 1'b0;
|
|
bytestream_valid_out <= 1'b0;
|
|
|
|
byte_counter <= 3'b0;
|
|
end
|
|
|
|
// second switching cycle to let CPLD load the lines based on direction
|
|
DIR_SWITCH: begin
|
|
state <= DIR_SWITCH_DLY;
|
|
end
|
|
|
|
// third switching cycle to compensate data input register
|
|
DIR_SWITCH_DLY: begin
|
|
state <= RECEIVING;
|
|
end
|
|
|
|
// wait for response to be received
|
|
// immediately change direction after successful reception to have one
|
|
// clock cycle of pause to avoid double driving the bus
|
|
RECEIVING: begin
|
|
timeout_counter <= timeout_counter + 1;
|
|
|
|
if (bytestream_valid_in_reg) begin
|
|
byte_counter <= byte_counter + 1'b1;
|
|
response_cache = {response_cache[NUM_BYTES_RX_READ*8-9:0], bytestream_data_in_reg};
|
|
|
|
if ((write_transfer && byte_counter == NUM_BYTES_RX_WRITE-1) ||
|
|
(~write_transfer && byte_counter == NUM_BYTES_RX_READ-1)) begin
|
|
state <= ACK;
|
|
bytestream_direction <= 1'b0;
|
|
end
|
|
end
|
|
|
|
if (timeout_counter == {TIMEOUT_COUNTER_WIDTH {1'b1}}) begin
|
|
state <= TIMEOUT;
|
|
bytestream_direction <= 1'b0;
|
|
end
|
|
end
|
|
|
|
// issue ctrlport response
|
|
ACK: begin
|
|
state <= IDLE;
|
|
|
|
s_ctrlport_resp_ack <= 1'b1;
|
|
// status based on received ack
|
|
s_ctrlport_resp_status <= response_cache[2] ? response_cache[1:0] : CTRL_STS_CMDERR;
|
|
if (write_transfer) begin
|
|
s_ctrlport_resp_data <= 32'b0;
|
|
end else begin
|
|
s_ctrlport_resp_data <= response_cache[39:8];
|
|
end
|
|
end
|
|
|
|
TIMEOUT: begin
|
|
state <= IDLE;
|
|
|
|
s_ctrlport_resp_ack <= 1'b1;
|
|
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
|
s_ctrlport_resp_data <= 32'b0;
|
|
end
|
|
|
|
default: begin
|
|
state <= IDLE;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
endmodule
|
|
|
|
`default_nettype wire
|