Newer
Older
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
//-----------------------------------------------------------------------------
// SoCShute FTDI FT1248 Interface to AXI-Stream Controller
// A joint work commissioned on behalf of SoC Labs, under Arm Academic Access license.
//
// Contributors
//
// David Flynn (d.w.flynn@soton.ac.uk)
//
// Copyright � 2022, SoC Labs (www.soclabs.org)
//-----------------------------------------------------------------------------
module socshute_ft1248_stream #(
parameter integer FT1248_WIDTH = 1, // FTDI Interface 1,2,4 width supported
parameter integer FT1248_CLKON = 1 // FTDI clock always on - else quiet when no access
)(
// IO pad interface - to FT232R configured in 1/2/4/8 mode
output wire ft_clk_o, // SCLK
output wire ft_ssn_o, // SS_N
input wire ft_miso_i, // MISO
output wire [FT1248_WIDTH-1:0] ft_miosio_o, // MIOSIO tristate output when enabled
output wire [FT1248_WIDTH-1:0] ft_miosio_e, // MIOSIO tristate output enable (active hi)
output wire [FT1248_WIDTH-1:0] ft_miosio_z, // MIOSIO tristate output enable (active lo)
input wire [FT1248_WIDTH-1:0] ft_miosio_i, // MIOSIO tristate input
input wire [7:0] ft_clkdiv, // divider prescaler to ensure SCLK <1MHz
input wire clk,
input wire resetn,
// Ports of Axi Master Bus Interface TXD - To ADP Controller
output wire txd_tvalid,
output wire [7:0] txd_tdata,
output wire txd_tlast,
input wire txd_tready,
// Ports of Axi Slave Bus Interface RXD - To
output wire rxd_tready,
input wire [7:0] rxd_tdata,
input wire rxd_tlast,
input wire rxd_tvalid
);
//----------------------------------------------
//-- State Machine encoding
//----------------------------------------------
// Explicit FSM state bit assignment
// bit [0] SCLK
// bit [1] MIO_OE
// bit [2] CMD/W
// bit [3] DAT/R
// bit [4] SSEL
localparam FT_0_IDLE = 5'b00000;
localparam FT_1_IDLE = 5'b00001;
localparam FT_ZCMD_CLKLO = 5'b10100;
localparam FT_CMD_CLKHI = 5'b10111;
localparam FT_CMD_CLKLO = 5'b10110;
localparam FT_ZBT_CLKHI = 5'b10001;
localparam FT_ZBT_CLKLO = 5'b10000;
localparam FT_WD_CLKHI = 5'b11111;
localparam FT_WD_CLKLO = 5'b11110;
localparam FT_ZWD_CLKLO = 5'b11100;
localparam FT_RD_CLKHI = 5'b11001;
localparam FT_RD_CLKLO = 5'b11000;
reg [4:0] ft_state;
// 9- bit shift register to support 8-bit data + 1 sequence control flag
// write data uses bits[7:0], with bit[8] set to flag length for serialized transfers
// read data uses bits[8:1], with bit[0] set to flag continuation for serialized transfers
reg [8:0] ft_reg;
//----------------------------------------------
//-- IO PAD control, parameterized on WIDTH param
//----------------------------------------------
wire bwid8 = (FT1248_WIDTH==8);
wire bwid4 = (FT1248_WIDTH==4);
wire bwid2 = (FT1248_WIDTH==2);
wire bwid1 = (FT1248_WIDTH==1);
wire [7:0] ft_rdmasked;
generate
if (FT1248_WIDTH == 8) begin
assign ft_rdmasked[7:1] = ft_miosio_i[7:1];
assign ft_miosio_o[7:1] = ft_reg[7:1];
assign ft_miosio_e[7:1] = {7{ft_state[1]}};
assign ft_miosio_z[7:1] = ~{7{ft_state[1]}};
end
endgenerate
generate
if (FT1248_WIDTH == 4) begin
assign ft_rdmasked[7:1] = {4'b1111, ft_miosio_i[3:1]};
assign ft_miosio_o[3:1] = ft_reg[3:1];
assign ft_miosio_e[3:1] = {3{ft_state[1]}};
assign ft_miosio_z[3:1] = ~{3{ft_state[1]}};
end
endgenerate
generate
if (FT1248_WIDTH == 2) begin
assign ft_rdmasked[7:1] = {6'b111111, ft_miosio_i[1]};
assign ft_miosio_o[1] = ft_reg[1];
assign ft_miosio_e[1] = ft_state[1];
assign ft_miosio_z[1] = ~ft_state[1];
end
endgenerate
generate
if (FT1248_WIDTH == 1) begin
assign ft_rdmasked[7:1] = 7'b1111111;
end
endgenerate
assign ft_rdmasked[0] = ft_miosio_i[0];
assign ft_miosio_o[0] = ft_reg[0];
assign ft_miosio_e[0] = ft_state[1];
assign ft_miosio_z[0] = ~ft_state[1];
assign ft_clk_o = ft_state[0];
assign ft_ssn_o = !ft_state[4];
// diagnostic decodes
//wire ft_cmd = !ft_state[3] & ft_state[2];
//wire ft_dwr = ft_state[3] & ft_state[2];
//wire ft_drd = ft_state[3] & !ft_state[2];
//----------------------------------------------
//-- Internal clock prescaler
//----------------------------------------------
// clock prescaler, ft_clken enables serial IO engine clocking
reg [7:0] ft_clkcnt_r;
reg ft_clken;
always @(posedge clk or negedge resetn )
begin
if (!resetn) begin
ft_clkcnt_r <= 0;
ft_clken <= 0;
end
else begin
ft_clken <= (ft_clkcnt_r == ft_clkdiv);
ft_clkcnt_r <= (ft_clkcnt_r == ft_clkdiv) ? 0 : (ft_clkcnt_r +1);
end
end
//----------------------------------------------
//-- Internal "synchronizers" (dual stage)
//----------------------------------------------
// synchronizers for channel ready flags when idle
// (treat these signals as synchronous during transfer sequencing)
reg ft_miso_i_sync;
reg ft_miosio_i0_sync;
reg ft_miso_i_sync_1;
reg ft_miosio_i0_sync_1;
always @(posedge clk or negedge resetn )
begin
if (!resetn) begin
ft_miso_i_sync_1 <= 1;
ft_miosio_i0_sync_1 <= 1;
ft_miso_i_sync <= 1;
ft_miosio_i0_sync <= 1;
end
else begin
ft_miso_i_sync_1 <= ft_miso_i;
ft_miosio_i0_sync_1 <= ft_miosio_i[0];
ft_miso_i_sync <= ft_miso_i_sync_1;
ft_miosio_i0_sync <= ft_miosio_i0_sync_1;
end
end
//----------------------------------------------
//-- AXI Stream interface handshakes
//----------------------------------------------
reg ft_txf; // FTDI Transmit channel Full
reg ft_rxe; // FTDO Receive channel Empty
reg ft_wcyc; // read access committed
reg ft_nak; // check for NAK terminate
// TX stream delivers valid FT1248 read data transfer
// 8-bit write port with extra top-bit used as valid qualifer
reg [8:0] txdata;
assign txd_tdata = txdata[7:0];
assign txd_tvalid = txdata[8];
// activate if RX channel data and the stream buffer is not full
wire ft_rxreq = !ft_rxe & !txdata[8];
// RX stream handshakes on valid FT1248 write data transfer
reg rxdone;
reg rxrdy;
assign rxd_tready = rxdone;
// activate if TX channel not full and and the stream buffer data valid
wire ft_txreq = !ft_txf & rxd_tvalid; // & !rxdone; // FTDI TX data ready and rxstream ready
// FTDI1248 commands
wire [3:0] wcmd = 4'b0000; // write request
wire [3:0] rcmd = 4'b0001; // read request
wire [3:0] fcmd = 4'b0100; // write flush request
//wire [3:0] rcmd = 4'b1000; // read request BE bit-pattern
//wire [3:0] fcmd = 4'b0010; // write flush request BE bit-pattern
// and full FT1248 command bit patterns (using top-bits for shift sequencing)
wire [8:0] wcmdpatt = {2'b11, wcmd[0], wcmd[1], 1'b0, wcmd[2], 1'b0, 1'b0, wcmd[3]};
wire [8:0] rcmdpatt = {2'b11, rcmd[0], rcmd[1], 1'b0, rcmd[2], 1'b0, 1'b0, rcmd[3]};
reg ssn_del;
always @(posedge clk or negedge resetn)
if (!resetn)
ssn_del <= 1'b1;
else if (ft_clken)
ssn_del <= ft_ssn_o;
wire ssn_start = ft_ssn_o & ssn_del;
// FTDI1248 state machine
always @(posedge clk or negedge resetn)
if (!resetn) begin
ft_state <= FT_0_IDLE;
ft_reg <= 0;
txdata <= 0;
rxdone <= 0;
ft_wcyc <= 0;
ft_txf <= 1; // ftdi channel TXE# ('1' full)
ft_rxe <= 1; // ftdi channel RXF# ('1' empty)
ft_nak <= 0;
end else begin
ft_txf <= (ft_state==FT_0_IDLE) ? (ft_miosio_i[0] | ft_miosio_i0_sync) : 1'b1; //ft_txf & !( ft_wcyc &(ft_state==FT_ZBT_CLKHI) & ft_miso_i);
ft_rxe <= (ft_state==FT_0_IDLE) ? (ft_miso_i | ft_miso_i_sync) : 1'b1; //ft_rxe & !(!ft_wcyc & (ft_state==FT_ZBT_CLKHI) & ft_miso_i);
txdata[8] <= txdata[8] & !txd_tready; // tx_valid handshake
rxdone <= (ft_clken & (ft_state==FT_ZWD_CLKLO) & !ft_nak) | (rxdone & !rxd_tvalid); // hold until acknowledged
if (ft_clken)
case (ft_state)
FT_0_IDLE: begin // RX req priority
if (ssn_start & ft_rxreq) begin ft_reg <= rcmdpatt; ft_state <= FT_ZCMD_CLKLO; end
else if (ssn_start & ft_txreq) begin ft_reg <= wcmdpatt; ft_state <= FT_ZCMD_CLKLO; ft_wcyc <= 1; end
else ft_state <= (!ft_txf | !ft_rxe | (FT1248_CLKON!=0)) ? FT_1_IDLE : FT_0_IDLE;
end
FT_1_IDLE:
ft_state <= FT_0_IDLE;
FT_ZCMD_CLKLO:
ft_state <= FT_CMD_CLKHI;
FT_CMD_CLKHI:
ft_state <= FT_CMD_CLKLO;
FT_CMD_CLKLO: // 2, 4 or 7 shifts
if (bwid8) begin ft_reg <= FT_ZBT_CLKHI; end
else if (bwid4) begin ft_reg <= {4'b0000,ft_reg[8:4]}; ft_state <= (|ft_reg[8:5]) ? FT_CMD_CLKHI : FT_ZBT_CLKHI; end
else if (bwid2) begin ft_reg <= { 2'b00,ft_reg[8:2]}; ft_state <= (|ft_reg[8:3]) ? FT_CMD_CLKHI : FT_ZBT_CLKHI; end
else begin ft_reg <= { 1'b0,ft_reg[8:1]}; ft_state <= (|ft_reg[8:3]) ? FT_CMD_CLKHI : FT_ZBT_CLKHI; end
FT_ZBT_CLKHI:
ft_state <= FT_ZBT_CLKLO;
FT_ZBT_CLKLO:
if (ft_wcyc) begin ft_reg <= {1'b1,rxd_tdata}; ft_state <= FT_WD_CLKHI; end
else begin ft_reg <= 9'b011111111; ft_state <= FT_RD_CLKHI; end
FT_WD_CLKHI:
if (ft_miso_i & ft_reg[8]) begin ft_nak <= 1'b1; ft_state <= FT_ZWD_CLKLO; end // NAK terminate on first cycle
else if (bwid8) ft_state <= (ft_reg[8]) ? FT_WD_CLKLO : FT_ZWD_CLKLO; // special case repeat on write data
else if (bwid4) ft_state <= (|ft_reg[8:5]) ? FT_WD_CLKLO : FT_ZWD_CLKLO;
else if (bwid2) ft_state <= (|ft_reg[8:3]) ? FT_WD_CLKLO : FT_ZWD_CLKLO;
else ft_state <= (|ft_reg[8:2]) ? FT_WD_CLKLO : FT_ZWD_CLKLO;
FT_WD_CLKLO:
if (bwid8) begin ft_reg <= { 1'b0,ft_reg[7:0]}; ft_state <= FT_WD_CLKHI; end // clear top flag
else if (bwid4) begin ft_reg <= {4'b0000,ft_reg[8:4]}; ft_state <= FT_WD_CLKHI; end // shift 4 bits right
else if (bwid2) begin ft_reg <= { 2'b00,ft_reg[8:2]}; ft_state <= FT_WD_CLKHI; end // shift 2 bits right
else begin ft_reg <= { 1'b0,ft_reg[8:1]}; ft_state <= FT_WD_CLKHI; end // shift 1 bit right
FT_ZWD_CLKLO:
if (ft_nak) begin ft_nak<= 1'b0; ft_state <= FT_0_IDLE; ft_wcyc <= 1'b0; end // terminate without TX handshake
else begin ft_state <= FT_0_IDLE; ft_wcyc <= 1'b0; end
FT_RD_CLKHI: // capture iodata pins end of CLKHI phase
if (ft_miso_i & (&ft_reg[7:0])) begin ft_nak <= 1'b1; ft_state <= FT_RD_CLKLO; end // NAK terminate on first cycle
else if (bwid8) begin ft_reg <= (ft_reg[0]) ? {ft_rdmasked[7:0],1'b1} : {ft_reg[8:1],1'b0}; ft_state <= FT_RD_CLKLO; end // 8-bit read twice
else if (bwid4) begin ft_reg <= {ft_rdmasked[3:0],ft_reg[8:4]}; ft_state <= FT_RD_CLKLO; end
else if (bwid2) begin ft_reg <= {ft_rdmasked[1:0],ft_reg[8:2]}; ft_state <= FT_RD_CLKLO; end
else begin ft_reg <= {ft_rdmasked[ 0],ft_reg[8:1]}; ft_state <= FT_RD_CLKLO; end
FT_RD_CLKLO:
if (ft_nak) begin ft_nak<= 1'b0; ft_state <= FT_0_IDLE; txdata <= 9'b0; end // terminate without TX handshake
else if (ft_reg[0]) begin ft_state <= FT_RD_CLKHI; ft_reg[0] <= !(bwid8); end // loop until all 8 bits shifted in (or 8-bit read repeated)
else begin ft_state <= FT_0_IDLE; txdata <= {1'b1,ft_reg[8:1]}; end
default:
ft_state <= FT_0_IDLE;
endcase
end
endmodule