-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathide.v
405 lines (353 loc) · 12.3 KB
/
ide.v
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
392
393
394
395
396
397
398
399
400
401
402
403
404
405
// Copyright 2008, 2009 by Jakub Bednarski
//
// Extracted from Minimig gayle.v
//
// Minimig is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Minimig is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//
//
// -- JB --
//
// 2008-10-06 - initial version
// 2008-10-08 - interrupt controller implemented, kickstart boots
// 2008-10-09 - working identify device command implemented (hdtoolbox detects our drive)
// - read command reads data from hardfile (fixed size and name, only one sector read size supported, workbench sees hardfile partition)
// 2008-10-10 - multiple sector transfer supported: works ok, sequential transfers with direct spi read and 28MHz CPU from 400 to 520 KB/s
// - arm firmare seekfile function very slow: seeking from start to 20MB takes 144 ms (some software improvements required)
// 2008-10-30 - write support added
// 2008-12-31 - added hdd enable
// 2009-05-24 - clean-up & renaming
// 2009-08-11 - hdd_ena enables Master & Slave drives
// 2009-11-18 - changed sector buffer size
// 2010-04-13 - changed sector buffer size
// 2010-08-10 - improved BSY signal handling
// 2022-08-18 - added packet command handling
module ide
(
input clk,
input clk_en,
input reset,
input [2:0] address_in,
input sel_secondary,
input [15:0] data_in,
output [15:0] data_out,
output data_oe,
input rd,
input hwr,
input lwr,
input sel_ide,
output reg [1:0] intreq,
input [1:0] intreq_ack, // interrupt clear
output nrdy, // fifo is not ready for reading
input [1:0] hdd0_ena, // enables Master & Slave drives on primary channel
input [1:0] hdd1_ena, // enables Master & Slave drives on secondary channel
output fifo_rd,
output fifo_wr,
// connection to the IO-Controller
output hdd_cmd_req,
output hdd_dat_req,
input [2:0] hdd_addr,
input [15:0] hdd_data_out,
output [15:0] hdd_data_in,
input hdd_wr,
input hdd_status_wr,
input hdd_data_wr,
input hdd_data_rd
);
localparam VCC = 1'b1;
localparam GND = 1'b0;
/*
0 Data
1 Error | Feature
2 SectorCount
3 SectorNumber
4 CylinderLow
5 CylinderHigh
6 Device/Head
7 Status | Command
command class:
PI (PIO In)
PO (PIO Out)
ND (No Data)
Status:
#6 - DRDY - Drive Ready
#7 - BSY - Busy
#3 - DRQ - Data Request
#0 - ERR - Error
INTRQ - Interrupt Request
*/
// address decoding signals
wire sel_tfr; // HDD task file registers select
wire sel_fifo; // HDD data port select (FIFO buffer)
wire sel_status /* synthesis keep */; // HDD status register select
wire sel_command /* synthesis keep */;// HDD command register select
// internal registers
reg block_mark; // IDE multiple block start flag
reg busy; // busy status (command processing state)
reg pio_in; // pio in command type is being processed
reg pio_out; // pio out command type is being processed
reg error; // error status (command processing failed)
reg [1:0] dev; // drive select (Primary/Secondary, Master/Slave)
wire bsy; // busy
wire drdy; // drive ready
wire drq; // data request
reg drq_d; // data request
wire err; // error
wire [7:0] status; // HDD status
// FIFO control
wire fifo_reset;
wire [15:0] fifo_data_in;
wire [15:0] fifo_data_out;
wire fifo_full;
wire fifo_empty;
wire fifo_last_out; // last word of a sector is being read
wire fifo_last_in; // last word of a sector is being written
// HDD status register
assign status = {bsy,drdy,2'b01,drq,2'b00,err};
// packet states
reg [1:0] packet_state;
localparam PACKET_IDLE = 0;
localparam PACKET_WAITCMD = 1;
localparam PACKET_PROCESSCMD = 2;
wire packet_state_change;
reg [12:0] packet_count;
wire packet_in_last;
wire packet_in;
wire packet_out;
`ifdef IDE_DEBUG
// cmd/status debug
reg [7:0] status_dbg /* synthesis noprune */;
reg [7:0] dbg_ide_cmd /* synthesis noprune */;
reg [2:0] dbg_addr /* synthesis noprune */;
reg dbg_wr /* synthesis noprune */;
reg[15:0] dbg_data_in /* synthesis noprune */;
reg[15:0] dbg_data_out /* synthesis noprune */;
always @(posedge clk) begin
status_dbg <= status;
if (clk_en) begin
dbg_wr <= 0;
if (sel_command) // set when the CPU writes command register
dbg_ide_cmd <= data_in[15:8];
if (sel_ide) begin
dbg_addr <= address_in;
dbg_wr <= hwr | lwr;
if (rd) dbg_data_out <= data_out;
if (hwr | lwr) dbg_data_in <= data_in;
end
end
end
`endif
// HDD status register bits
assign bsy = busy & ~drq;
assign drdy = ~(bsy|drq);
assign err = error;
// address decoding
assign sel_tfr = sel_ide;
assign sel_status = rd && sel_tfr && address_in==3'b111 ? VCC : GND;
assign sel_command = hwr && sel_tfr && address_in==3'b111 ? VCC : GND;
assign sel_fifo = sel_tfr && address_in==3'b000 ? VCC : GND;
//===============================================================================================//
// task file registers
reg [7:0] tfr [7:0];
wire [2:0] tfr_sel;
wire [7:0] tfr_in;
wire [7:0] tfr_out;
wire tfr_we;
reg [8:0] sector_count; // sector counter
wire sector_count_dec_in; // decrease sector counter (reads)
wire sector_count_dec_out; // decrease sector counter (writes)
always @(posedge clk)
if (clk_en) begin
if (hwr && sel_tfr && address_in == 3'b010) begin // sector count register loaded by the host
sector_count <= {1'b0, data_in[15:8]};
if (data_in[15:8] == 0) sector_count <= 9'd256;
end else if (sector_count_dec_in || sector_count_dec_out)
sector_count <= sector_count - 8'd1;
end
reg rd_old;
reg wr_old;
reg sel_fifo_old;
always @(posedge clk)
if (clk_en) begin
rd_old <= rd;
wr_old <= hwr & lwr;
sel_fifo_old <= sel_fifo;
end
assign sector_count_dec_in = pio_in & fifo_last_out & sel_fifo_old & ~rd & rd_old & packet_state == PACKET_IDLE;
assign sector_count_dec_out = pio_out & fifo_last_in & sel_fifo_old & ~hwr & ~lwr & wr_old & packet_state == PACKET_IDLE;
// task file register control
assign tfr_we = packet_in_last ? 1'b1 : bsy ? hdd_wr : sel_tfr & hwr;
assign tfr_sel = packet_in_last ? 3'd2 : bsy ? hdd_addr : address_in;
assign tfr_in = packet_in_last ? 8'h03: bsy ? hdd_data_out[7:0] : data_in[15:8];
// input multiplexer for SPI host
assign hdd_data_in = tfr_sel==0 ? fifo_data_out : {7'h0, dev[1], tfr_out};
// task file registers
always @(posedge clk)
if (clk_en) begin
if (tfr_we)
tfr[tfr_sel] <= tfr_in;
end
assign tfr_out = tfr[tfr_sel];
// master/slave drive select
always @(posedge clk)
if (clk_en) begin
if (reset)
dev <= 0;
else if (sel_tfr && address_in==6 && hwr)
dev <= {sel_secondary, data_in[12]};
end
assign packet_state_change = busy && hdd_status_wr && hdd_data_out[5];
// bytes count in a packet
always @(posedge clk)
if (clk_en) begin
if (reset)
packet_count <= 0;
else if (hdd_wr && hdd_addr == 4)
packet_count[6:0] <= hdd_data_out[7:1];
else if (hdd_wr && hdd_addr == 5)
packet_count[12:7] <= hdd_data_out[5:0];
else if (packet_state_change && packet_state == PACKET_IDLE)
packet_count <= 13'd6; // IDLE->WAITCMD transition, expect 6 words of packet command
end
// status register (write only from SPI host)
// 7 - busy status (write zero to finish command processing: allow host access to task file registers)
// 6
// 5
// 4 - intreq (used for writes only)
// 3 - drq enable for pio in (PI) command type
// 2 - drq enable for pio out (PO) command type
// 1
// 0 - error flag (remember about setting error task file register)
// command busy status
always @(posedge clk)
if (clk_en) begin
if (reset)
busy <= GND;
else if (hdd_status_wr && hdd_data_out[7] || (sector_count_dec_in && sector_count == 9'h01)) // reset by SPI host (by clearing BSY status bit)
busy <= GND;
else if (sel_command) // set when the CPU writes command register
busy <= VCC;
end
// IDE interrupt request register
always @(posedge clk)
if (clk_en) begin
drq_d <= drq;
if (reset) begin
intreq[0] <= GND;
intreq[1] <= GND;
block_mark <= GND;
end else begin
if (busy && hdd_status_wr && hdd_data_out[3])
block_mark <= VCC; // to handle IDENTIFY
if (pio_in) begin // reads
if (hdd_status_wr && hdd_data_out[4])
block_mark <= VCC;
if ((error | (!drq_d & drq)) & block_mark) begin
intreq[dev[1]] <= VCC;
block_mark <= GND;
end
if (packet_in_last) // read the last word from the packet command result
intreq[dev[1]] <= VCC;
end else if (pio_out) begin // writes
if (hdd_status_wr && hdd_data_out[4])
intreq[dev[1]] <= VCC;
end else if (hdd_status_wr && hdd_data_out[7]) // other command types completed
intreq[dev[1]] <= VCC;
else if (packet_state_change && packet_state == PACKET_IDLE) // ready to accept command packet
intreq[dev[1]] <= VCC;
if (intreq_ack[0]) intreq[0] <= GND; // cleared by the CPU
if (intreq_ack[1]) intreq[1] <= GND; // cleared by the CPU
end
end
// pio in command type
always @(posedge clk)
if (clk_en) begin
if (reset)
pio_in <= GND;
else if (drdy) // reset when processing of the current command ends
pio_in <= GND;
else if (busy && hdd_status_wr && hdd_data_out[3]) // set by SPI host
pio_in <= VCC;
end
// pio out command type
always @(posedge clk)
if (clk_en) begin
if (reset)
pio_out <= GND;
else if (busy && hdd_status_wr && hdd_data_out[7]) // reset by SPI host when command processing completes
pio_out <= GND;
else if (busy && hdd_status_wr && hdd_data_out[3]) // pio_in set by SPI host (during PACKET processing)
pio_out <= GND;
else if (busy && hdd_status_wr && hdd_data_out[2]) // set by SPI host
pio_out <= VCC;
end
// packet command state machine
always @(posedge clk)
if (clk_en) begin
if (reset)
packet_state <= PACKET_IDLE;
else if (drdy) // reset when processing of the current command ends
packet_state <= PACKET_IDLE;
else if (packet_state_change) // set by SPI host
packet_state <= packet_state == PACKET_IDLE ? PACKET_WAITCMD :
packet_state == PACKET_WAITCMD ? PACKET_PROCESSCMD : packet_state;
end
assign drq = (fifo_full & pio_in) | (~fifo_full & pio_out & (sector_count != 0 || packet_out)); // HDD data request status bit
// error status
always @(posedge clk)
if (clk_en) begin
if (reset)
error <= GND;
else if (sel_command) // reset by the CPU when command register is written
error <= GND;
else if (busy && hdd_status_wr && hdd_data_out[0]) // set by SPI host
error <= VCC;
end
assign hdd_cmd_req = bsy; // bsy is set when command register is written, tells the SPI host about new command
assign hdd_dat_req = (fifo_full & pio_out); // the FIFO is full so SPI host may read it
// FIFO in/out multiplexer
assign fifo_reset = reset | sel_command | packet_state_change | packet_in_last;
assign fifo_data_in = pio_in ? hdd_data_out : data_in;
assign fifo_rd = pio_out ? hdd_data_rd : sel_fifo & rd;
assign fifo_wr = pio_in ? hdd_data_wr : sel_fifo & hwr & lwr;
assign packet_in = packet_state == PACKET_PROCESSCMD && pio_in;
assign packet_out = packet_state == PACKET_WAITCMD || (packet_state == PACKET_PROCESSCMD && pio_out);
//sector data buffer (FIFO)
ide_fifo SECBUF1
(
.clk(clk),
.clk_en(clk_en),
.reset(fifo_reset),
.data_in(fifo_data_in),
.data_out(fifo_data_out),
.rd(fifo_rd),
.wr(fifo_wr),
.packet_in(packet_in),
.packet_out(packet_out),
.packet_count(packet_count),
.packet_in_last(packet_in_last),
.full(fifo_full),
.empty(fifo_empty),
.last_out(fifo_last_out),
.last_in(fifo_last_in)
);
// fifo is not ready for reading
assign nrdy = pio_in & sel_fifo & fifo_empty;
assign data_oe = (!dev[1] && hdd0_ena[dev[0]]) || (dev[1] && hdd1_ena[dev[0]]);
//data_out multiplexer
assign data_out = sel_fifo && rd ? fifo_data_out :
sel_status ? data_oe ? {status,8'h00} : 16'h00_00 :
sel_tfr && rd ? {tfr_out,8'h00} : 16'h00_00;
//===============================================================================================//
endmodule