// // ide_disk.v // single block (512 byte/256 work) IDE disk read/write // module ide_disk(clk, reset, ide_lba, ide_read_req, ide_write_req, ide_error, ide_done, buffer_addr, buffer_rd, buffer_wr, buffer_in, buffer_out, ide_data_bus, ide_dior, ide_diow, ide_cs, ide_da); input clk; input reset; input [23:0] ide_lba; input ide_read_req; input ide_write_req; output ide_error; output ide_done; output reg [7:0] buffer_addr; output reg buffer_rd; output reg buffer_wr; output reg [11:0] buffer_out; input [11:0] buffer_in; parameter [4:0] ready = 5'd0, init0 = 5'd1, init1 = 5'd2, init2 = 5'd3, init3 = 5'd4, init4 = 5'd5, init5 = 5'd6, init6 = 5'd7, init7 = 5'd8, init8 = 5'd9, init9 = 5'd10, init10 = 5'd11, init11 = 5'd12, read0 = 5'd13, read1 = 5'd14, write0 = 5'd15, write1 = 5'd16, last0 = 5'd17, last1 = 5'd18, last2 = 5'd19, last3 = 5'd20, wait0 = 5'd21, wait1 = 5'd22; parameter ATA_ALTER = 5'b01110; parameter ATA_DEVCTRL = 5'b01110; /* bit [2] is a nIEN */ parameter ATA_DATA = 5'b10000; parameter ATA_ERROR = 5'b10001; parameter ATA_FEATURE = 5'b10001; parameter ATA_SECCNT = 5'b10010; parameter ATA_SECNUM = 5'b10011; /* LBA[7:0] */ parameter ATA_CYLLOW = 5'b10100; /* LBA[15:8] */ parameter ATA_CYLHIGH = 5'b10101; /* LBA[23:16] */ parameter ATA_DRVHEAD = 5'b10110; /* LBA + DRV + LBA[27:24] */ parameter ATA_STATUS = 5'b10111; parameter ATA_COMMAND = 5'b10111; parameter IDE_STATUS_BSY = 7; parameter IDE_STATUS_DRDY = 6; parameter IDE_STATUS_DWF = 5; parameter IDE_STATUS_DSC = 4; parameter IDE_STATUS_DRQ = 3; parameter IDE_STATUS_CORR = 2; parameter IDE_STATUS_IDX = 1; parameter IDE_STATUS_ERR = 0; parameter ATA_CMD_READ = 16'h0020; parameter ATA_CMD_WRITE = 16'h0030; reg ata_rd; reg ata_wr; reg [4:0] ata_addr; reg [15:0] ata_in; wire [15:0] ata_out; wire ata_done; inout [15:0] ide_data_bus; output ide_dior; output ide_diow; output [1:0] ide_cs; output [2:0] ide_da; // reg [4:0] ide_state; reg [4:0] ide_state_next; reg [7:0] offset; reg [7:0] wc; reg err; reg done; reg set_done, clear_done; reg set_err, clear_err; reg inc_offset; // ide ide1(.clk(clk), .reset(reset), .ata_rd(ata_rd), .ata_wr(ata_wr), .ata_addr(ata_addr), .ata_in(ata_in), .ata_out(ata_out), .ata_done(ata_done), .ide_data_bus(ide_data_bus), .ide_dior(ide_dior), .ide_diow(ide_diow), .ide_cs(ide_cs), .ide_da(ide_da)); // wire [23:0] lba; wire start; assign lba = ide_lba; assign start = ide_read_req | ide_write_req; assign ide_done = done; // always @(posedge clk) if (reset) begin err <= 1'b0; done <= 1'b0; offset <= 8'b0; wc <= 8'b0; end else begin if (set_err) err <= 1'b1; else if (clear_err) err <= 1'b0; if (set_done) done <= 1'b1; else if (clear_done) done <= 1'b0; if (inc_offset) begin offset <= offset + 8'h01; wc <= wc + 8'h01; end end // // ide state machine // always @(posedge clk) if (reset) ide_state <= ready; else begin ide_state <= ide_state_next; //if (ide_state_next != ready) //$display("ide_state %d", ide_state_next); end always @(ide_state or lba or start or ata_done or ata_out) begin ide_state_next = ide_state; set_err = 0; clear_err = 0; set_done = 0; clear_done = 0; inc_offset = 0; ata_rd = 0; ata_wr = 0; ata_addr = 0; ata_in = 0; buffer_rd = 0; buffer_wr = 0; buffer_addr = 0; buffer_out = 0; case (ide_state) ready: begin if (start) begin ide_state_next = init0; clear_done = 1; $display("ide_disk: XXX go!"); end end init0: begin ata_addr = ATA_STATUS; ata_rd = 1; if (ata_done && ~ata_out[IDE_STATUS_BSY] && ata_out[IDE_STATUS_DRDY]) ide_state_next = init1; end init1: begin ata_wr = 1; ata_addr = ATA_DRVHEAD; ata_in = 16'h0040; if (ata_done) ide_state_next = wait0; end wait0: begin // cnt = 1; // if (cnt_rdy) ide_state_next = init2; end init2: begin ata_addr = ATA_STATUS; ata_rd = 1; if (ata_done && ~ata_out[IDE_STATUS_BSY] && ata_out[IDE_STATUS_DRDY]) ide_state_next = init3; end init3: begin ata_wr = 1; ata_addr = ATA_DEVCTRL; ata_in = 16'h0002; // nIEN if (ata_done) ide_state_next = init4; end init4: begin ata_wr = 1; ata_addr = ATA_SECCNT; ata_in = { 8'b0, 8'd1 }; if (ata_done) ide_state_next = init5; end init5: begin ata_wr = 1; ata_addr = ATA_SECNUM; ata_in = {8'b0, lba[7:0]}; // LBA[7:0] if (ata_done) ide_state_next = init6; end init6: begin ata_wr = 1; ata_addr = ATA_CYLLOW; ata_in = {8'b0, lba[15:8]}; // LBA[15:8] if (ata_done) ide_state_next = init7; end init7: begin ata_wr = 1; ata_addr = ATA_CYLHIGH; ata_in = lba[23:16]; // LBA[23:16] if (ata_done) ide_state_next = init8; end init8: begin ata_wr = 1; ata_addr = ATA_DRVHEAD; ata_in = 16'h0040; // LBA[27:24] + LBA if (ata_done) ide_state_next = init9; end init9: begin ata_wr = 1; ata_addr = ATA_COMMAND; ata_in = ide_write_req ? ATA_CMD_WRITE : ide_read_req ? ATA_CMD_READ : 16'b0; if (ata_done) ide_state_next = wait1; end wait1: begin // cnt = 1; // if (cnt_rdy) ide_state_next = init10; end init10: begin ata_rd = 1; ata_addr = ATA_ALTER; if (ata_done) ide_state_next = init11; end init11: begin ata_rd = 1; ata_addr = ATA_STATUS; //if (ata_done) $display("ide_disk: XXX init11 ata_out %x", ata_out); if (ata_done && ~ata_out[IDE_STATUS_BSY] && ata_out[IDE_STATUS_DRQ]) begin if (ide_write_req) ide_state_next = write0; else if (ide_read_req) ide_state_next = read0; end if (ata_out[IDE_STATUS_ERR]) set_err = 1; end read0: begin ata_rd = 1; ata_addr = ATA_DATA; if (ata_done) ide_state_next = read1; end read1: begin //buffer write buffer_addr = offset; buffer_out = ata_out[11:0]; if (0) $display("ide_disk: buffer_addr %o, buffer_out %o", buffer_addr, buffer_out); buffer_wr = 1; inc_offset = 1; if (wc == 8'hff) ide_state_next = last0; else // if (wc == 16'hff00) // ide_state_next = init10; // else ide_state_next = read0; end write0: begin //buffer read buffer_addr = offset; buffer_rd = 1; ata_in = {4'b0, buffer_in}; inc_offset = 1; ide_state_next = write1; end write1: begin if (wc == 8'hff) ide_state_next = last0; else // if (wc == 16'hff00) // ide_state_next = init10; // else ide_state_next = write0; end last0: begin ata_rd = 1; ata_addr = ATA_ALTER; if (ata_done) ide_state_next = last1; end last1: begin ata_rd = 1; ata_addr = ATA_STATUS; if (ata_done) ide_state_next = last2; end last2: begin clear_err = 1; set_done = 1; ide_state_next = last3; end last3: begin clear_done = 1; ide_state_next = ready; $display("ide_disk: XXX last3, done"); end default: begin end endcase end endmodule