si_jctrl.v
11 KB
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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
// si_jctrl.v v1 Frank Berndt
// si joychannel controller module;
// :set tabstop=4
module si_jctrl (
sysclk, reset,
write_data, write_enable, read_data,
slave, start, sltx, slreq, busy,
jchan_clk, jchan_in, jchan_ena, jchan_oe
);
// module io ports;
input sysclk; // system clock;
input reset; // controller and system reset;
input [63:0] write_data; // dma write data;
input write_enable; // dma write enable;
output [55:0] read_data; // dma read data;
input slave; // operate as slave;
input start; // start master command or slave receive;
input sltx; // start slave transmit;
output slreq; // slave request status;
output busy; // controller interface busy;
input jchan_clk; // joychannel clock;
input jchan_in; // joychannel input;
output jchan_ena; // enable joychannel input reg;
output jchan_oe; // enable joychannel driver;
// channel data registers;
// dma write data are not modified and can be reused;
// status is cleared on start of read dma;
reg ctrlrst; // controller reset;
reg norsp; // no response;
wire reqerr; // request error;
reg framerr; // frame error;
reg collision; // collision;
wire [4:0] status; // joychannel cmd status;
reg [5:0] tx_size; // # of bytes to transmit, including cmd;
reg [5:0] rx_size; // # of receive bytes;
reg [7:0] cmd; // controller command;
reg [31:0] data; // tx/rx data buffer;
reg size_err; // tx/rx size error;
wire slv_update; // overlay slave rx cmd and size;
reg [5:0] slv_size; // slave rx size;
wire [7:0] slv_cmd; // slave command;
wire [31:0] rx_data; // receive data;
// tx size, rx size and cmd are only set on write dma;
wire bad_size; // bad tx or rx size;
wire tx_gz; // tx_size > 0, start fsm;
wire rx_gz; // rx size > 0, start fsm;
assign bad_size = |write_data[55:54] // tx 64..255;
| (|write_data[47:46]); // rx 64..255;
always @(posedge sysclk)
begin
if(write_enable) begin
tx_size <= write_data[53:48];
rx_size <= write_data[45:40];
size_err <= bad_size;
cmd <= write_data[39:32];
data[31:0] <= write_data[31:0];
end else if(slave & slv_update) begin
rx_size <= slv_size;
cmd <= slv_cmd;
end
end
assign rx_gz = |rx_size;
assign tx_gz = |tx_size;
// return channel bytes as dma read dma;
assign reqerr = size_err;
assign status = { norsp, reqerr, framerr, collision, ctrlrst };
assign read_data = { 2'd0, tx_size, status, rx_size[2:0], cmd, rx_data };
// start jchan controller;
// start signal clears all error status bits;
// done on first error or end of receive;
// controller reset aborts any activity;
wire xstart; // any start;
reg busy; // joychannel controller is busy;
wire errors; // sum of protocol errors;
wire done; // sum of all completion signals;
wire tx_done; // transmitter done;
wire rx_done; // receiver done;
wire rx_rst; // rx bus reset;
wire busy_rst; // aborted when busy;
assign xstart = start | sltx;
assign errors = |status;
assign done = errors | rx_done | ~tx_gz | (slave & (tx_done | ~rx_gz));
assign busy_rst = reset & busy;
always @(posedge sysclk)
begin
busy <= ~reset & ((~done & busy) | xstart);
ctrlrst <= ~xstart & (ctrlrst | busy_rst | rx_rst);
end
// set slave request when rx done;
// clear upon start of response by sltx;
reg slreq; // slave request pending;
always @(posedge sysclk)
begin
slreq <= slave & ~sltx & (slreq | rx_done);
end
// tx/rx state machine;
// shared by tx and rx because of sequential operation;
// tx: [8:6] byte, [5:3] bit, [2:0] phase;
// rx: [5:3] byte, [2:0] bit;
reg [11:0] state; // byte/bit/phase state;
reg tx_start; // start tx fsm;
reg rx_start; // start rx fsm;
wire tx_next; // next tx state;
wire rx_next; // next rx state;
always @(posedge sysclk)
begin
tx_start <= tx_gz & ~reqerr & (slave? sltx : start);
if(tx_start | rx_start)
state <= 12'd0;
else if(tx_next | rx_next)
state <= state + 1;
end
// receiver;
// sample jchan input;
// use io register with clock-enable;
assign jchan_ena = jchan_clk;
reg [1:0] clk_del; // delayed sampling pulse;
reg [1:0] rx_in; // rx sampling register;
wire rx_edge; // detected edge;
wire rx_posedge; // detected posedge;
wire rx_negedge; // detected negedge;
always @(posedge sysclk)
begin
clk_del[0] <= jchan_clk;
clk_del[1] <= clk_del[0];
if(|clk_del)
rx_in <= { rx_in[0], jchan_in };
end
assign rx_edge = (rx_in[1] != rx_in[0]);
assign rx_posedge = (rx_in == 2'b01);
assign rx_negedge = (rx_in == 2'b10);
// pulse width disciminator;
// counter stops at max count of 1984;
// counter is also used by transmitter;
reg [10:0] pw_cnt; // pulse width counter;
wire pw_clr; // zero pw counter;
wire pw_max; // pw counter at max;
assign pw_clr = reset | rx_edge;
assign pw_max = &pw_cnt[10:6];
// discrete count decodes are used to avoid comparators;
// break signal is deprecated and not supported;
// pulse counts:
// 1.. tC, tE (always);
// 1..3 small width, t1L, t0H;
// 4.. large width, t1H, t0L;
// 126.. tS;
// 200.. tRST;
wire pw_large, pw_sep, pw_rst;
reg [2:0] pw_state;
assign pw_large = (pw_cnt == 11'd3); // large pulse width;
assign pw_sep = (pw_cnt == 11'd126); // tS;
assign pw_rst = (pw_cnt == 11'd200); // tRST;
always @(posedge sysclk)
begin
if(pw_clr) begin
pw_cnt <= {11{1'b0}};
pw_state <= 3'b000;
end else if(clk_del[1] & ~pw_max) begin
pw_cnt <= pw_cnt + 1;
pw_state <= pw_state | { pw_rst, pw_sep, pw_large };
end
end
// determine bit value;
// store value at rising edge of signal;
// determine bit at falling edge of signal;
wire rx_bit; // large pulse width;
reg rx_val; // rising edge value;
assign rx_bit = pw_state[0]; // 1 bit;
always @(posedge sysclk)
begin
if(rx_posedge)
rx_val <= rx_bit;
end
// receive state machine;
// started after successful command transmission;
// receive the specified number of bytes;
// must wait for first falling edge of rcv bit;
// align bytes by shifting left after rx_size received;
wire rx_latch; // latch rcv data bit;
wire rx_stop; // stop receiver on reset, errors or last bit;
reg rx_wait; // wait for falling edge of first rcv bit;
reg rx_act; // receiver active;
wire rx_last; // last byte received;
reg rx_align; // shifts until bytes aligned;
wire rx_aligned; // rx bytes are aligned;
wire rx_shift; // shift receive bits;
wire rx_ge45; // state count >= 4 (master), 5 (slave);
wire rx_shift_ena; // enable rx shift of first bytes;
assign rx_latch = rx_act & rx_negedge;
assign rx_stop = reset | rx_last | (rx_wait & errors);
always @(posedge sysclk)
begin
rx_start <= slave? (start & rx_gz) : tx_done;
rx_wait <= ~rx_stop & (rx_wait | rx_start);
rx_act <= ~rx_stop & (rx_act | (rx_wait & rx_negedge));
rx_align <= ~reset & rx_align? ~rx_aligned : rx_last;
end
assign rx_last = rx_act & (state[8:3] == rx_size);
assign rx_ge45 = (state[5:3] >= { 2'b10, slave });
assign rx_aligned = |state[8:6] | rx_ge45;
assign rx_next = rx_latch | (rx_align & ~rx_aligned);
assign rx_done = rx_align & rx_aligned;
assign rx_shift = rx_next;
assign rx_shift_ena = ~rx_aligned;
// frame error is strictly bit timing violation;
// can only be determined at falling edge of signal;
// clear at start of new read dma;
//
// response timeout is independent of bit toggling;
// whenever tS is seen during active receive, which
// includes a possible channel reset;
// when more data is expected than is sent in slave mode;
// clear at start of new read dma;
wire rx_framerr; // frame error;
wire rx_norsp; // response timeout;
assign rx_framerr = (rx_val == rx_bit);
assign rx_norsp = pw_state[1] & (slave? rx_act : rx_wait);
assign rx_rst = rx_wait & |pw_state[2:1] & ~rx_in[1];
always @(posedge sysclk)
begin
if(start)
framerr <= 0;
else if(rx_latch)
framerr <= framerr | rx_framerr;
norsp <= ~start & (norsp | rx_norsp);
end
// tx and rx use single loadable shift register;
// cannot use cmd & data because they cannot be modified;
// register is 5 bytes wide for transmit;
// receiver uses only 4 bytes of it;
// bit order on jchan is msb-first;
// return shift register bits as response data;
// shift left at end of receive to align bytes;
reg [39:0] shift_reg; // tx/rx shift register;
reg load_shift; // load shift register;
wire tx_shift; // register tx shift enable;
wire tx_bit; // bit to transmit;
always @(posedge sysclk)
begin
load_shift <= start | sltx;
if(load_shift)
shift_reg <= slave? { data, data[7:0] } : { cmd, data };
else if(tx_shift)
shift_reg <= { shift_reg[38:0], shift_reg[7] };
else if(rx_shift) begin
if(rx_shift_ena)
shift_reg[39:8] <= shift_reg[38:7];
shift_reg[7:0] <= { shift_reg[6:0], rx_bit };
end
end
assign tx_bit = shift_reg[39];
assign rx_data = shift_reg[31:0];
// decode received slave commands into rx_size;
// use lower 3 bits of command for all reserved commands;
assign slv_cmd = { shift_reg[6:0], rx_bit };
assign slv_update = rx_latch & (state[8:0] == 6'd7);
always @(slv_cmd)
begin
case(slv_cmd)
8'h00: slv_size <= 6'd1; // JCTRL_STATUS;
8'h01: slv_size <= 6'd1; // JCTRL_QUERY;
8'h02: slv_size <= 6'd3; // JCTRL_READ;
8'h03: slv_size <= 6'd35; // JCTRL_WRITE;
8'hff: slv_size <= 6'd1; // JCTRL_RESET;
default: slv_size <= { 3'd0, slv_cmd[2:0] };
endcase
end
// transmitter;
// transmit state machine;
// must drive H for at least tS before transmitting;
// must synchronize activation of transmitter with jchan_clk;
// stop transmitter on reset or collision;
reg tx_pend; // tx pending, awaiting tS;
reg tx_act; // transmitter active;
wire tx_stop; // stop transmitter;
wire ts_exp; // minimum tS expired;
wire tx_coll_stop; // stop at collision during transmit;
assign tx_coll_stop = tx_act & collision;
assign tx_stop = reset | tx_done | tx_coll_stop;
assign ts_exp = slave | (rx_in[1] & pw_state[1]);
assign tx_next = jchan_clk & tx_act;
always @(posedge sysclk)
begin
tx_pend <= ~tx_stop & (tx_pend | tx_start);
tx_act <= ~tx_stop & (tx_act | (tx_pend & ts_exp & jchan_clk));
end
assign tx_shift = tx_next & (state[2:0] == 3'b111);
assign tx_done = tx_act // transmitter is on;
& (state[11:6] == tx_size) // and # of bytes sent;
& (state[2:1] != 2'b00); // and tE sent;
// transmit pulse modulator;
// register before sending to io cell;
// delay tx_0 for collision detection;
wire tx_0; // sum of all 0 outputs;
reg jchan_oe; // drive jbus 0;
reg tx_0del; // delayed tx_0 bit;
assign tx_0 = (state[2:1] == 2'b00) // 0, 1, tE;
| ((state[2:1] == 2'b01) & ~tx_bit) // 1;
| ((state[2:1] == 2'b10) & ~tx_bit); // 1;
always @(posedge sysclk)
begin
jchan_oe <= reset | (tx_act & tx_0);
if(clk_del[0])
tx_0del <= jchan_oe;
end
// collisions can only happen during transmit;
// detect them at end of bit period by comparing
// value on joy channel with transmit value;
wire tx_coll; // collision during transmit;
assign tx_coll = tx_act & (~tx_0del != rx_in[0]);
always @(posedge sysclk)
begin
if(start)
collision <= 0;
else if(clk_del[0])
collision <= collision | tx_coll;
end
endmodule