1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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
|