diff --git a/rtl/archimedes_top.v b/rtl/archimedes_top.v index 5c7fa9b..816a1b0 100644 --- a/rtl/archimedes_top.v +++ b/rtl/archimedes_top.v @@ -344,7 +344,7 @@ wire floppy_density; wire floppy_reset; wire fdc_sel = cpu_stb & cpu_cyc & floppy_en; -fdc1772 #(CLKCPU) FDC1772 ( +fdc1772 #(.EXT_MOTOR(1'b1)) FDC1772 ( .clkcpu ( CLKCPU_I ), .clk8m_en ( ioc_clk8m_en ), @@ -370,7 +370,7 @@ fdc1772 #(CLKCPU) FDC1772 ( .sd_dout_strobe ( sd_buff_wr ), .floppy_drive ( floppy_drive ), -// .floppy_motor ( floppy_motor ), + .floppy_motor ( !floppy_motor ), // .floppy_inuse ( floppy_inuse ), .floppy_side ( floppy_side ), // .floppy_density ( floppy_density ), diff --git a/rtl/fdc/fdc.qip b/rtl/fdc/fdc.qip index c5a3500..d8b084b 100644 --- a/rtl/fdc/fdc.qip +++ b/rtl/fdc/fdc.qip @@ -1,2 +1,2 @@ set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) floppy.v ] -set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) fdc1772.v ] +set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) fdc1772.sv ] diff --git a/rtl/fdc/fdc1772.v b/rtl/fdc/fdc1772.sv similarity index 66% rename from rtl/fdc/fdc1772.v rename to rtl/fdc/fdc1772.sv index 302c623..07e886c 100644 --- a/rtl/fdc/fdc1772.v +++ b/rtl/fdc/fdc1772.sv @@ -26,9 +26,12 @@ module fdc1772 ( input clk8m_en, // external set signals - input [3:0] floppy_drive, + input [W:0] floppy_drive, input floppy_side, input floppy_reset, + output floppy_step, + input floppy_motor, + output floppy_ready, // interrupts output reg irq, @@ -41,13 +44,13 @@ module fdc1772 ( output reg [7:0] cpu_dout, // place any signals that need to be passed up to the top after here. - input [1:0] img_mounted, // signaling that new image has been mounted - input [1:0] img_wp, // write protect + input [W:0] img_mounted, // signaling that new image has been mounted + input [W:0] img_wp, // write protect input img_ds, // double-sided image (for BBC Micro only) input [31:0] img_size, // size of image in bytes output reg[31:0] sd_lba, - output reg [1:0] sd_rd, - output reg [1:0] sd_wr, + output reg [W:0] sd_rd, + output reg [W:0] sd_wr, input sd_ack, input [7:0] sd_buff_addr, input [15:0] sd_dout, @@ -55,43 +58,34 @@ module fdc1772 ( input sd_dout_strobe ); -parameter CLK = 32000000; -parameter CLK_EN = 16'd8000; // in kHz -parameter SECTOR_SIZE_CODE = 2'd3; // sec size 0=128, 1=256, 2=512, 3=1024 -parameter SECTOR_BASE = 1'b0; // number of first sector on track (archie 0, dos 1) -localparam SECTOR_SIZE = 11'd128 << SECTOR_SIZE_CODE; +parameter CLK_EN = 16'd8000; // in kHz +parameter FD_NUM = 2; // number of supported floppies +parameter MODEL = 2; // 0 - wd1770, 1 - fd1771, 2 - wd1772, 3 = wd1773/fd1793 +parameter EXT_MOTOR = 1'b0; // != 0 if motor is controlled externally by floppy_motor +parameter INVERT_HEAD_RA = 1'b0; // != 0 - invert head in READ_ADDRESS reply + +localparam IMG_ARCHIE = 0; +localparam IMG_ST = 1; +localparam IMG_BBC = 2; // SSD, DSD formats +localparam IMG_TI99 = 3; // V9T9 format + +parameter IMG_TYPE = IMG_ARCHIE; + +localparam W = FD_NUM - 1; +localparam WIDX = $clog2(FD_NUM); // ------------------------------------------------------------------------- // --------------------- IO controller image handling ---------------------- // ------------------------------------------------------------------------- -always @(*) begin - case (SECTOR_SIZE_CODE) - // archie - 3: sd_lba = {(16'd0 + (fd_spt*track[6:0]) << fd_doubleside) + (floppy_side ? 5'd0 : fd_spt) + sector[4:0], s_odd }; - // st - 2: sd_lba = ((fd_spt*track[6:0]) << fd_doubleside) + (floppy_side ? 5'd0 : fd_spt) + sector[4:0] - 1'd1; - // bbc micro - 1: sd_lba = (((fd_spt*track[6:0]) << fd_doubleside) + (floppy_side ? 5'd0 : fd_spt) + sector[4:0]) >> 1; - default: sd_lba = 0; - endcase -end - -reg [1:0] floppy_ready = 0; - -wire floppy_present = (floppy_drive == 4'b1110)?floppy_ready[0]: - (floppy_drive == 4'b1101)?floppy_ready[1]:1'b0; - -wire floppy_write_protected = (floppy_drive == 4'b1110)?img_wp[0]: - (floppy_drive == 4'b1101)?img_wp[1]:1'b1; - -reg [10:0] sector_len[2]; -reg [4:0] spt[2]; // sectors/track -reg [9:0] gap_len[2]; // gap len/sector -reg [1:0] doubleside; -reg [1:0] hd; -reg fm; +reg [10:0] fdn_sector_len[FD_NUM]; +reg [4:0] fdn_spt[FD_NUM]; // sectors/track +reg [9:0] fdn_gap_len[FD_NUM]; // gap len/sector +reg fdn_doubleside[FD_NUM]; +reg fdn_hd[FD_NUM]; +reg fdn_fm[FD_NUM]; +reg fdn_present[FD_NUM]; reg [11:0] image_sectors; reg [11:0] image_sps; // sectors/side @@ -99,20 +93,34 @@ reg [4:0] image_spt; // sectors/track reg [9:0] image_gap_len; reg image_doubleside; wire image_hd = img_size[20]; +reg image_fm; + +reg [1:0] sector_size_code; // sec size 0=128, 1=256, 2=512, 3=1024 +reg [10:0] sector_size; +reg sector_base; // number of first sector on track (archie 0, dos 1) always @(*) begin - case (SECTOR_SIZE_CODE) - 3: begin + case (IMG_TYPE) + IMG_ARCHIE: begin // archie, 1024 bytes/sector - fm = 0; + sector_size_code = 2'd3; + sector_base = 0; + sd_lba = {(16'd0 + (fd_spt*track[6:0]) << fd_doubleside) + (floppy_side ? 5'd0 : fd_spt) + sector[4:0], s_odd }; + + image_fm = 0; image_sectors = img_size[21:10]; image_doubleside = 1'b1; image_spt = image_hd ? 5'd10 : 5'd5; image_gap_len = 10'd220; + end - 2: begin + IMG_ST: begin // this block is valid for the .st format (or similar arrangement), 512 bytes/sector - fm = 0; + sector_size_code = 2'd2; + sector_base = 1; + sd_lba = ((fd_spt*track[6:0]) << fd_doubleside) + (floppy_side ? 5'd0 : fd_spt) + sector[4:0] - 1'd1; + + image_fm = 0; image_sectors = img_size[20:9]; image_doubleside = 1'b0; image_sps = image_sectors; @@ -140,16 +148,32 @@ always @(*) begin default : image_gap_len = 10'd2; endcase; end - 1: begin - // 256 bytes/sector single density (BBC SSD) - fm = 1; + IMG_BBC, IMG_TI99: begin + // 256 bytes/sector single density (BBC SSD/DSD, TI99/4A) + sector_size_code = 2'd1; + sector_base = 0; + if (IMG_TYPE == IMG_BBC) begin + sd_lba = (((fd_spt*track[6:0]) << fd_doubleside) + (floppy_side ? 5'd0 : fd_spt) + sector[4:0]) >> 1; + image_spt = 10; + end else begin + sd_lba = (fd_spt*(floppy_side ? track[5:0] : 79-track[5:0]) + sector[4:0]) >> 1; + image_spt = 9; + end + + image_fm = 1; image_sectors = img_size[19:8]; image_doubleside = img_ds; - image_spt = 5'd10; + if (img_ds) + image_sps = image_sectors >> 1'b1; + else + image_sps = image_sectors; image_gap_len = 10'd50; end default: begin - fm = 0; + sector_size_code = 2'd0; + sector_base = 0; + sd_lba = 0; + image_fm = 0; image_sectors = 0; image_doubleside = 0; image_spt = 0; @@ -157,27 +181,25 @@ always @(*) begin end endcase + + sector_size = 11'd128 << sector_size_code; end always @(posedge clkcpu) begin - reg [1:0] img_mountedD; - + reg [W:0] img_mountedD; + integer i; img_mountedD <= img_mounted; - if (~img_mountedD[0] && img_mounted[0]) begin - floppy_ready[0] <= |img_size; - sector_len[0] <= SECTOR_SIZE; - spt[0] <= image_spt; - gap_len[0] <= image_gap_len; - doubleside[0] <= image_doubleside; - hd[0] <= image_hd; - end - if (~img_mountedD[1] && img_mounted[1]) begin - floppy_ready[1] <= |img_size; - sector_len[1] <= SECTOR_SIZE; - spt[1] <= image_spt; - gap_len[1] <= image_gap_len; - doubleside[1] <= image_doubleside; - hd[1] <= image_hd; + + for(i = 0; i < FD_NUM; i = i+1'd1) begin + if (~img_mountedD[i] && img_mounted[i]) begin + fdn_present[i] <= |img_size; + fdn_sector_len[i] <= sector_size; + fdn_spt[i] <= image_spt; + fdn_gap_len[i] <= image_gap_len; + fdn_doubleside[i] <= image_doubleside; + fdn_hd[i] <= image_hd; + fdn_fm[i] <= image_fm; + end end end @@ -224,208 +246,78 @@ end // -------------------- virtual floppy drive mechanics --------------------- // ------------------------------------------------------------------------- -// ------------------------------------------------------------------------- -// ------------------------------- floppy 0 -------------------------------- -// ------------------------------------------------------------------------- -wire fd0_index; -wire fd0_ready; -wire [6:0] fd0_track; -wire [4:0] fd0_sector; -wire fd0_sector_hdr; -wire fd0_sector_data; -wire fd0_dclk; +wire fdn_index[FD_NUM]; +wire fdn_ready[FD_NUM]; +wire [6:0] fdn_track[FD_NUM]; +wire [4:0] fdn_sector[FD_NUM]; +wire fdn_sector_hdr[FD_NUM]; +wire fdn_sector_data[FD_NUM]; +wire fdn_dclk[FD_NUM]; -floppy #(.SYS_CLK(CLK)) floppy0 ( - .clk ( clkcpu ), +generate + genvar i; + + for(i=0; i < FD_NUM; i = i+1) begin :fdd - // control signals into floppy - .select (!floppy_drive[0] ), - .motor_on ( motor_on ), - .step_in ( step_in ), - .step_out ( step_out ), + floppy #(.CLK_EN(CLK_EN)) floppy + ( + .clk ( clkcpu ), + .clk8m_en ( clk8m_en ), - // physical parameters - .sector_len ( sector_len[0] ), - .spt ( spt[0] ), - .sector_gap_len ( gap_len[0] ), - .sector_base ( SECTOR_BASE ), - .hd ( hd[0] ), - .fm ( fm ), + // control signals into floppy + .select ( fd_any && fdn == i ), + .motor_on ( fd_motor ), + .step_in ( step_in ), + .step_out ( step_out ), - // status signals generated by floppy - .dclk_en ( fd0_dclk ), - .track ( fd0_track ), - .sector ( fd0_sector ), - .sector_hdr ( fd0_sector_hdr ), - .sector_data ( fd0_sector_data ), - .ready ( fd0_ready ), - .index ( fd0_index ) -); + // physical parameters + .sector_len ( fdn_sector_len[i] ), + .spt ( fdn_spt[i] ), + .sector_gap_len ( fdn_gap_len[i] ), + .sector_base ( sector_base ), + .hd ( fdn_hd[i] ), + .fm ( fdn_fm[i] ), -// ------------------------------------------------------------------------- -// ------------------------------- floppy 1 -------------------------------- -// ------------------------------------------------------------------------- -wire fd1_index; -wire fd1_ready; -wire [6:0] fd1_track; -wire [4:0] fd1_sector; -wire fd1_sector_hdr; -wire fd1_sector_data; -wire fd1_dclk; - -floppy #(.SYS_CLK(CLK)) floppy1 ( - .clk ( clkcpu ), - - // control signals into floppy - .select (!floppy_drive[1] ), - .motor_on ( motor_on ), - .step_in ( step_in ), - .step_out ( step_out ), - - // physical parameters - .sector_len ( sector_len[1] ), - .spt ( spt[1] ), - .sector_gap_len ( gap_len[1] ), - .sector_base ( SECTOR_BASE ), - .hd ( hd[1] ), - .fm ( fm ), - - // status signals generated by floppy - .dclk_en ( fd1_dclk ), - .track ( fd1_track ), - .sector ( fd1_sector ), - .sector_hdr ( fd1_sector_hdr ), - .sector_data ( fd1_sector_data ), - .ready ( fd1_ready ), - .index ( fd1_index ) -); - -// ------------------------------------------------------------------------- -// ------------------------------- floppy 2 -------------------------------- -// ------------------------------------------------------------------------- -wire fd2_index; -wire fd2_ready; -wire [6:0] fd2_track; -wire [4:0] fd2_sector; -wire fd2_sector_hdr; -wire fd2_sector_data; -wire fd2_dclk; - -floppy #(.SYS_CLK(CLK)) floppy2 ( - .clk ( clkcpu ), - - // control signals into floppy - .select (!floppy_drive[2] ), - .motor_on ( motor_on ), - .step_in ( step_in ), - .step_out ( step_out ), - - // physical parameters - .sector_len ( ), - .spt ( ), - .sector_gap_len ( ), - .sector_base ( SECTOR_BASE ), - .hd ( ), - .fm ( fm ), - - // status signals generated by floppy - .dclk_en ( fd2_dclk ), - .track ( fd2_track ), - .sector ( fd2_sector ), - .sector_hdr ( fd2_sector_hdr ), - .sector_data ( fd2_sector_data ), - .ready ( fd2_ready ), - .index ( fd2_index ) -); - -// ------------------------------------------------------------------------- -// ------------------------------- floppy 3 -------------------------------- -// ------------------------------------------------------------------------- -wire fd3_index; -wire fd3_ready; -wire [6:0] fd3_track; -wire [4:0] fd3_sector; -wire fd3_sector_hdr; -wire fd3_sector_data; -wire fd3_dclk; - -floppy #(.SYS_CLK(CLK)) floppy3 ( - .clk ( clkcpu ), - - // control signals into floppy - .select (!floppy_drive[3] ), - .motor_on ( motor_on ), - .step_in ( step_in ), - .step_out ( step_out ), - - // physical parameters - .sector_len ( ), - .spt ( ), - .sector_gap_len ( ), - .sector_base ( SECTOR_BASE ), - .hd ( ), - .fm ( fm ), - - // status signals generated by floppy - .dclk_en ( fd3_dclk ), - .track ( fd3_track ), - .sector ( fd3_sector ), - .sector_hdr ( fd3_sector_hdr ), - .sector_data ( fd3_sector_data ), - .ready ( fd3_ready ), - .index ( fd3_index ) -); + // status signals generated by floppy + .dclk_en ( fdn_dclk[i] ), + .track ( fdn_track[i] ), + .sector ( fdn_sector[i] ), + .sector_hdr ( fdn_sector_hdr[i] ), + .sector_data ( fdn_sector_data[i] ), + .ready ( fdn_ready[i] ), + .index ( fdn_index[i] ) + ); + end +endgenerate // ------------------------------------------------------------------------- // ----------------------------- floppy demux ------------------------------ // ------------------------------------------------------------------------- -wire fd_index = (!floppy_drive[0])?fd0_index: - (!floppy_drive[1])?fd1_index: - (!floppy_drive[2])?fd2_index: - (!floppy_drive[3])?fd3_index: - 1'b0; +reg [WIDX:0] fdn; +always begin + integer i; + + fdn = 0; + for(i = FD_NUM-1; i >= 0; i = i - 1) if(!floppy_drive[i]) fdn = i[WIDX:0]; +end -wire fd_ready = (!floppy_drive[0])?fd0_ready: - (!floppy_drive[1])?fd1_ready: - (!floppy_drive[2])?fd2_ready: - (!floppy_drive[3])?fd3_ready: - 1'b0; +wire fd_any = ~&floppy_drive; -wire [6:0] fd_track = (!floppy_drive[0])?fd0_track: - (!floppy_drive[1])?fd1_track: - (!floppy_drive[2])?fd2_track: - (!floppy_drive[3])?fd3_track: - 7'd0; +wire fd_index = fd_any ? fdn_index[fdn] : 1'b0; +wire fd_ready = fd_any ? fdn_ready[fdn] : 1'b0; +wire [6:0] fd_track = fd_any ? fdn_track[fdn] : 7'd0; +wire [4:0] fd_sector = fd_any ? fdn_sector[fdn] : 5'd0; +wire fd_sector_hdr = fd_any ? fdn_sector_hdr[fdn] : 1'b0; +//wire fd_sector_data = fd_any ? fdn_sector_data[fdn] : 1'b0; +wire fd_dclk_en = fd_any ? fdn_dclk[fdn] : 1'b0; +wire fd_present = fd_any ? fdn_present[fdn] : 1'b0; +wire fd_writeprot = fd_any ? img_wp[fdn] : 1'b1; -wire [4:0] fd_sector = (!floppy_drive[0])?fd0_sector: - (!floppy_drive[1])?fd1_sector: - (!floppy_drive[2])?fd2_sector: - (!floppy_drive[3])?fd3_sector: - 4'd0; +wire fd_doubleside = fdn_doubleside[fdn]; +wire [4:0] fd_spt = fdn_spt[fdn]; -wire fd_sector_hdr = (!floppy_drive[0])?fd0_sector_hdr: - (!floppy_drive[1])?fd1_sector_hdr: - (!floppy_drive[2])?fd2_sector_hdr: - (!floppy_drive[3])?fd3_sector_hdr: - 1'b0; -/* -wire fd_sector_data = (!floppy_drive[0])?fd0_sector_data: - (!floppy_drive[1])?fd1_sector_data: - (!floppy_drive[2])?fd2_sector_data: - (!floppy_drive[3])?fd3_sector_data: - 1'b0; -*/ -wire fd_dclk_en = (!floppy_drive[0])?fd0_dclk: - (!floppy_drive[1])?fd1_dclk: - (!floppy_drive[2])?fd2_dclk: - (!floppy_drive[3])?fd3_dclk: - 1'b0; - -wire fd_doubleside = (!floppy_drive[0])?doubleside[0]:doubleside[1]; -wire [4:0] fd_spt = (!floppy_drive[0])?spt[0]:spt[1]; - -wire fd_track0 = (fd_track == 0); +assign floppy_ready = fd_ready && fd_present; // ------------------------------------------------------------------------- // ----------------------- internal state machines ------------------------- @@ -445,6 +337,8 @@ reg busy /* verilator public */; reg step_in, step_out; reg [3:0] motor_spin_up_sequence /* verilator public */; +wire fd_motor = EXT_MOTOR ? floppy_motor : motor_on; + // consider spin up done either if the motor is not supposed to spin at all or // if it's supposed to run and has left the spin up sequence wire motor_spin_up_done = (!motor_on) || (motor_on && (motor_spin_up_sequence == 0)); @@ -457,18 +351,24 @@ reg [15:0] step_pulse_cnt; // the step rate is only valid for command type I wire [15:0] step_rate_clk = - (cmd[1:0]==2'b00)?(16'd6*CLK_EN-1'd1): // 6ms - (cmd[1:0]==2'b01)?(16'd12*CLK_EN-1'd1): // 12ms - (cmd[1:0]==2'b10)?(16'd2*CLK_EN-1'd1): // 2ms - (16'd3*CLK_EN-1'd1); // 3ms + (cmd[1:0]==2'b00) ? (16'd6 *CLK_EN-1'd1): // 6ms + (cmd[1:0]==2'b01) ? (16'd12*CLK_EN-1'd1): // 12ms + (MODEL == 2 && cmd[1:0]==2'b10) ? (16'd2 *CLK_EN-1'd1): // 2ms + (cmd[1:0]==2'b10) ? (16'd20*CLK_EN-1'd1): // 20ms + (MODEL == 2) ? (16'd3 *CLK_EN-1'd1): // 3ms + (16'd30*CLK_EN-1'd1); // 30ms reg [15:0] step_rate_cnt; reg [23:0] delay_cnt; +assign floppy_step = step_in | step_out; + // flag indicating that a "step" is in progress wire step_busy = (step_rate_cnt != 0); wire delaying = (delay_cnt != 0); +wire fd_track0 = (fd_track == 0); + reg [7:0] step_to; reg RNF; reg sector_inc_strobe; @@ -554,7 +454,7 @@ always @(posedge clkcpu) begin // ------------------------ TYPE I ------------------------- if(cmd_type_1) begin - if(!floppy_present) begin + if(!fd_present) begin // no image selected -> send irq after 6 ms if (!notready_wait) begin delay_cnt <= 16'd6*CLK_EN; @@ -562,7 +462,6 @@ always @(posedge clkcpu) begin end else begin RNF <= 1'b1; busy <= 1'b0; - motor_timeout_index <= MOTOR_IDLE_COUNTER - 1'd1; irq_set <= 1'b1; // emit irq when command done end end else @@ -636,7 +535,6 @@ always @(posedge clkcpu) begin // finish 3: begin busy <= 1'b0; - motor_timeout_index <= MOTOR_IDLE_COUNTER - 1'd1; irq_set <= 1'b1; // emit irq when command done seek_state <= 0; end @@ -645,7 +543,7 @@ always @(posedge clkcpu) begin // ------------------------ TYPE II ------------------------- if(cmd_type_2) begin - if(!floppy_present) begin + if(!fd_present) begin // no image selected -> send irq after 6 ms if (!notready_wait) begin delay_cnt <= 16'd6*CLK_EN; @@ -653,12 +551,10 @@ always @(posedge clkcpu) begin end else begin RNF <= 1'b1; busy <= 1'b0; - motor_timeout_index <= MOTOR_IDLE_COUNTER - 1'd1; irq_set <= 1'b1; // emit irq when command done end end else if (sector_not_found) begin busy <= 1'b0; - motor_timeout_index <= MOTOR_IDLE_COUNTER - 1'd1; irq_set <= 1'b1; // emit irq when command done RNF <= 1'b1; end else if (cmd[2] && !notready_wait) begin @@ -668,7 +564,7 @@ always @(posedge clkcpu) begin // read sector end else begin if(cmd[7:5] == 3'b100) begin - if ((sector - SECTOR_BASE) >= fd_spt) begin + if ((sector - sector_base) >= fd_spt) begin // wait 5 rotations (1 sec) before setting RNF sector_not_found <= 1'b1; delay_cnt <= 24'd1000 * CLK_EN; @@ -693,7 +589,6 @@ always @(posedge clkcpu) begin if (cmd[4]) sector_inc_strobe <= 1'b1; // multiple sector transfer else begin busy <= 1'b0; - motor_timeout_index <= MOTOR_IDLE_COUNTER - 1'd1; irq_set <= 1'b1; // emit irq when command done end end @@ -707,7 +602,7 @@ always @(posedge clkcpu) begin // write sector if(cmd[7:5] == 3'b101) begin - if ((sector - SECTOR_BASE) >= fd_spt) begin + if ((sector - sector_base) >= fd_spt) begin // wait 5 rotations (1 sec) before setting RNF sector_not_found <= 1'b1; delay_cnt <= 24'd1000 * CLK_EN; @@ -715,12 +610,12 @@ always @(posedge clkcpu) begin case (data_transfer_state) 2'b00: begin // pre-read phase - if (SECTOR_SIZE_CODE < 2) sd_card_read <= 1; + if (sector_size_code < 2) sd_card_read <= 1; data_transfer_state <= 2'b10; end 2'b10: begin // CPU phase - if (fifo_cpuptr == 0) data_transfer_start <= 1'b1; + if (fifo_cpuptr == 0 && fd_ready && fd_sector_hdr && (fd_sector == sector)) data_transfer_start <= 1'b1; if (data_transfer_done) begin sd_card_write <= 1; data_transfer_state <= 2'b11; @@ -733,7 +628,6 @@ always @(posedge clkcpu) begin if (cmd[4]) sector_inc_strobe <= 1'b1; // multiple sector transfer else begin busy <= 1'b0; - motor_timeout_index <= MOTOR_IDLE_COUNTER - 1'd1; irq_set <= 1'b1; // emit irq when command done end end @@ -748,24 +642,21 @@ always @(posedge clkcpu) begin // ------------------------ TYPE III ------------------------- if(cmd_type_3) begin - if(!floppy_present) begin + if(!fd_present) begin // no image selected -> send irq immediately RNF <= 1'b1; busy <= 1'b0; - motor_timeout_index <= MOTOR_IDLE_COUNTER - 1'd1; irq_set <= 1'b1; // emit irq when command done end else begin // read track TODO: fake if(cmd[7:4] == 4'b1110) begin busy <= 1'b0; - motor_timeout_index <= MOTOR_IDLE_COUNTER - 1'd1; irq_set <= 1'b1; // emit irq when command done end // write track TODO: fake if(cmd[7:4] == 4'b1111) begin busy <= 1'b0; - motor_timeout_index <= MOTOR_IDLE_COUNTER - 1'd1; irq_set <= 1'b1; // emit irq when command done end @@ -777,7 +668,6 @@ always @(posedge clkcpu) begin if(data_transfer_done) begin busy <= 1'b0; - motor_timeout_index <= MOTOR_IDLE_COUNTER - 1'd1; irq_set <= 1'b1; // emit irq when command done end end @@ -791,17 +681,21 @@ always @(posedge clkcpu) begin irq_at_index <= 1'b0; if (irq_at_index) irq_set <= 1'b1; - // led motor timeout run once fdc is not busy anymore - if(!busy) begin + // let motor timeout run once fdc is not busy anymore + if(!busy && motor_spin_up_done) begin if(motor_timeout_index != 0) motor_timeout_index <= motor_timeout_index - 4'd1; - else + else if(motor_on) + motor_timeout_index <= MOTOR_IDLE_COUNTER; + + if(motor_timeout_index == 1) motor_on <= 1'b0; end if(motor_spin_up_sequence != 0) motor_spin_up_sequence <= motor_spin_up_sequence - 4'd1; end + if(busy) motor_timeout_index <= 0; end end @@ -818,21 +712,21 @@ reg [10:0] fifo_cpuptr; reg [9:0] fifo_cpuptr_adj; wire [7:0] fifo_q; reg s_odd; //odd sector -reg [8:0] fifo_sdptr; +reg [9:0] fifo_sdptr; always @(*) begin - if (SECTOR_SIZE_CODE == 3) + if (sector_size_code == 3) fifo_sdptr = { s_odd, sd_buff_addr }; else fifo_sdptr = { 1'b0, sd_buff_addr }; - if (SECTOR_SIZE_CODE == 1) - fifo_cpuptr_adj = { 1'b0, sector[0], fifo_cpuptr[7:0] }; + if (sector_size_code == 1) + fifo_cpuptr_adj = { 1'b0, (fd_spt[0] & (track[0] ^ !floppy_side)) ^ sector[0], fifo_cpuptr[7:0] }; else fifo_cpuptr_adj = fifo_cpuptr[9:0]; end -dpram_dif #(9,16,10,8) fifo +fdc1772_dpram #(10) fifo ( .clock(clkcpu), @@ -871,32 +765,32 @@ always @(posedge clkcpu) begin begin s_odd <= 1'b0; if (~sd_card_readD & sd_card_read) begin - sd_rd <= ~{ floppy_drive[1], floppy_drive[0] }; + sd_rd[fdn] <= 1; sd_state <= SD_READ; end else if (~sd_card_writeD & sd_card_write) begin - sd_wr <= ~{ floppy_drive[1], floppy_drive[0] }; + sd_wr[fdn] <= 1; sd_state <= SD_WRITE; end end SD_READ: if (sd_ackD & ~sd_ack) begin - if (s_odd || SECTOR_SIZE_CODE != 3) begin + if (s_odd || sector_size_code != 3) begin sd_state <= SD_IDLE; end else begin s_odd <= 1; - sd_rd <= ~{ floppy_drive[1], floppy_drive[0] }; + sd_rd[fdn] <= 1; end end SD_WRITE: if (sd_ackD & ~sd_ack) begin - if (s_odd || SECTOR_SIZE_CODE != 3) begin + if (s_odd || sector_size_code != 3) begin sd_state <= SD_IDLE; end else begin s_odd <= 1; - sd_wr <= ~{ floppy_drive[1], floppy_drive[0] }; + sd_wr[fdn] <= 1; end end @@ -905,16 +799,44 @@ always @(posedge clkcpu) begin end // -------------------- CPU data read/write ----------------------- +reg data_in_strobe; +reg data_in_valid; + +function [15:0] crc; + input [15:0] curcrc; + input [7:0] val; + reg [3:0] i; + begin + crc = {curcrc[15:8] ^ val, 8'h00}; + for (i = 0; i < 8; i=i+1'd1) begin + if(crc[15]) begin + crc = crc << 1; + crc = crc ^ 16'h1021; + end + else crc = crc << 1; + end + crc = {curcrc[7:0] ^ crc[15:8], crc[7:0]}; + end +endfunction always @(posedge clkcpu) begin reg data_transfer_startD; reg [10:0] data_transfer_cnt; + reg [15:0] crcval; + reg crc_en; - if (data_in_strobe) data_out <= data_in; + crc_en <= 0; + if(crc_en) crcval <= crc(crcval, data_out); + + if (cpu_we && cpu_addr == FDC_REG_DATA) begin + data_out <= data_in; + data_in_valid <= 1; + end // reset fifo read pointer on reception of a new command or // when multi-sector transfer increments the sector number if(cmd_rx || sector_inc_strobe) begin + data_in_valid <= 0; data_transfer_cnt <= 0; fifo_cpuptr <= 0; end @@ -926,15 +848,21 @@ always @(posedge clkcpu) begin if(~data_transfer_startD & data_transfer_start) begin // read_address command has 6 data bytes - if(cmd[7:4] == 4'b1100) + if(cmd[7:4] == 4'b1100) begin + crcval <= 16'hB230; data_transfer_cnt <= 11'd6+11'd1; + end // read/write sector has SECTOR_SIZE data bytes if(cmd[7:6] == 2'b10) - data_transfer_cnt <= SECTOR_SIZE + 1'd1; + data_transfer_cnt <= sector_size + 1'd1; + + // write sector asserts drq earlier to fill up the data register in time + if(cmd[7:5] == 3'b101) drq_set <= !data_in_valid; end - // write sector data arrived from CPU + // advance fifo pointer when the write sector data consumed + data_in_strobe <= 1'b0; if(cmd[7:5] == 3'b101 && data_in_strobe) fifo_cpuptr <= fifo_cpuptr + 1'd1; if(fd_dclk_en) begin @@ -942,27 +870,32 @@ always @(posedge clkcpu) begin if(data_transfer_cnt != 1) begin data_lost <= 1'b0; if (drq) data_lost <= 1'b1; - drq_set <= 1'b1; + // raise drq, except when the last byte is already taken from the CPU for write + if (cmd[7:5] != 3'b101 || data_transfer_cnt != 2) drq_set <= 1'b1; // read_address if(cmd[7:4] == 4'b1100) begin case(data_transfer_cnt) - 7: data_out <= fd_track; - 6: data_out <= { 7'b0000000, floppy_side }; - 5: data_out <= fd_sector; - 4: data_out <= SECTOR_SIZE_CODE; // TODO: sec size 0=128, 1=256, 2=512, 3=1024 - 3: data_out <= 8'ha5; - 2: data_out <= 8'h5a; + 7: begin data_out <= fd_track; crc_en <= 1; end + 6: begin data_out <= { 7'b0000000, (INVERT_HEAD_RA != 0) ^ floppy_side }; crc_en <= 1; end + 5: begin data_out <= fd_sector; crc_en <= 1; end + 4: begin data_out <= sector_size_code[1:0]; crc_en <= 1; end // TODO: sec size 0=128, 1=256, 2=512, 3=1024 + 3: data_out <= crcval[15:8]; + 2: data_out <= crcval[7:0]; endcase // case (data_read_cnt) end // read sector - if(cmd[7:5] == 3'b100) begin - if(fifo_cpuptr != SECTOR_SIZE) begin - data_out <= fifo_q; - fifo_cpuptr <= fifo_cpuptr + 1'd1; - end + if(cmd[7:5] == 3'b100 && fifo_cpuptr != sector_size) begin + data_out <= fifo_q; + fifo_cpuptr <= fifo_cpuptr + 1'd1; end + // write sector + if(cmd[7:5] == 3'b101 && fifo_cpuptr != sector_size) begin + data_in_strobe <= 1; + data_in_valid <= 0; + end + end // count down and stop after last byte @@ -974,8 +907,8 @@ always @(posedge clkcpu) begin end // the status byte -wire [7:0] status = { motor_on, - floppy_write_protected, // wrprot +wire [7:0] status = { (MODEL == 1 || MODEL == 3) ? !floppy_ready : motor_on, + (cmd[7:5] == 3'b101 || cmd[7:4] == 4'b1111 || cmd_type_1) && fd_writeprot, // wrprot (only for write!) cmd_type_1?motor_spin_up_done:1'b0, // data mark RNF, // seek error/record not found 1'b0, // crc error @@ -1021,7 +954,6 @@ end // cpu register write reg cmd_rx /* verilator public */; reg cmd_rx_i; -reg data_in_strobe; always @(posedge clkcpu) begin if(!floppy_reset) begin @@ -1033,9 +965,7 @@ always @(posedge clkcpu) begin // reset state machines and counters cmd_rx_i <= 1'b0; cmd_rx <= 1'b0; - data_in_strobe <= 0; end else begin - data_in_strobe <= 0; // cmd_rx is delayed to make sure all signals (the cmd!) are stable when // cmd_rx is evaluated @@ -1097,7 +1027,6 @@ always @(posedge clkcpu) begin sector <= cpu_din; if(cpu_addr == FDC_REG_DATA) begin // data register - data_in_strobe <= 1; data_in <= cpu_din; end end @@ -1110,3 +1039,42 @@ always @(posedge clkcpu) begin end endmodule + +module fdc1772_dpram #(ADDRWIDTH=9) +( + input clock, + + input [ADDRWIDTH-2:0] address_a, + input [15:0] data_a, + input wren_a, + output reg [15:0] q_a, + + input [ADDRWIDTH-1:0] address_b, + input [7:0] data_b, + input wren_b, + output reg [7:0] q_b +); + +logic [1:0][7:0] ram[0:(1<<(ADDRWIDTH-1))-1]; + +always@(posedge clock) begin + if(wren_a) begin + ram[address_a] <= data_a; + q_a <= data_a; + end + else begin + q_a <= ram[address_a]; + end +end + +always@(posedge clock) begin + if(wren_b) begin + ram[address_b[ADDRWIDTH-1:1]][address_b[0]] <= data_b; + q_b <= data_b; + end + else begin + q_b <= ram[address_b[ADDRWIDTH-1:1]][address_b[0]]; + end +end + +endmodule diff --git a/rtl/fdc/floppy.v b/rtl/fdc/floppy.v index 024130f..26ee342 100644 --- a/rtl/fdc/floppy.v +++ b/rtl/fdc/floppy.v @@ -19,11 +19,13 @@ module floppy ( // main clock - input clk, - input select, - input motor_on, - input step_in, - input step_out, + input clk, + input clk8m_en, + + input select, + input motor_on, + input step_in, + input step_out, input [10:0] sector_len, input sector_base, // number of first sector on track (archie 0, dos 1) @@ -32,38 +34,38 @@ module floppy ( input hd, input fm, - output dclk_en, // data clock enable + output dclk_en, // data clock enable output [6:0] track, // number of track under head output [4:0] sector, // number of sector under head, 0 = no sector - output sector_hdr, // valid sector header under head - output sector_data, // valid sector data under head + output sector_hdr, // valid sector header under head + output sector_data, // valid sector data under head - output ready, // drive is ready, data can be read + output ready, // drive is ready, data can be read output reg index ); // The sysclock is the value all floppy timings are derived from. // Default: 8 MHz -parameter SYS_CLK = 8000000; +parameter CLK_EN = 8000; assign sector_hdr = (sec_state == SECTOR_STATE_HDR); assign sector_data = (sec_state == SECTOR_STATE_DATA); // a standard DD floppy has a data rate of 250kBit/s and rotates at 300RPM -localparam RATESD = 20'd125000; -localparam RATEDD = 20'd250000; -localparam RATEHD = 20'd500000; -localparam RPM = 10'd300; -localparam STEPBUSY = 8'd18; // 18ms after step data can be read -localparam SPINUP = 10'd500; // drive spins up in up to 800ms -localparam SPINDOWN = 10'd300; // GUESSED: drive spins down in 300ms -localparam INDEX_PULSE_LEN = 4'd5; // fd1036 data sheet says 1~8ms -localparam SECTOR_HDR_LEN = 4'd6; // GUESSED: Sector header is 6 bytes -localparam TRACKS = 8'd85; // max allowed track +localparam RATESD = 125000; +localparam RATEDD = 250000; +localparam RATEHD = 500000; +localparam RPM = 300; +localparam STEPBUSY = 18; // 18ms after step data can be read +localparam SPINUP = 500; // drive spins up in up to 800ms +localparam SPINDOWN = 300; // GUESSED: drive spins down in 300ms +localparam INDEX_PULSE_LEN = 5; // fd1036 data sheet says 1~8ms +localparam SECTOR_HDR_LEN = 6; // GUESSED: Sector header is 6 bytes +localparam TRACKS = 85; // max allowed track // Archimedes specific values -//localparam SECTOR_LEN = 11'd1024 // Default sector size is 1024 on Archie -//localparam SECTOR_LEN = 11'd512; // Default sector size is 512 on ST ... +//localparam SECTOR_LEN = 11'd1024 // Default sector size is 1024 on Archie +//localparam SECTOR_LEN = 11'd512; // Default sector size is 512 on ST ... //localparam SPT = 4'd10; // ... with 5 sectors per track //localparam SECTOR_BASE = 4'd1; // number of first sector on track (archie 0, dos 1) @@ -72,8 +74,8 @@ localparam BPTSD = RATESD*60/(8*RPM); localparam BPTDD = RATEDD*60/(8*RPM); localparam BPTHD = RATEHD*60/(8*RPM); -// report disk ready if it spins at full speed and head is not moving -assign ready = select && (rate == (fm ? RATESD : hd ? RATEHD : RATEDD)) && (step_busy == 0); +// report disk ready if it spins at full speed +assign ready = select && (rate == (fm ? RATESD : hd ? RATEHD : RATEDD)); // ================================================================ // ========================= INDEX PULSE ========================== @@ -81,9 +83,9 @@ assign ready = select && (rate == (fm ? RATESD : hd ? RATEHD : RATEDD)) && (step // Index pulse generation. Pulse starts with the begin of index_pulse_start // and lasts INDEX_PULSE_CYCLES system clock cycles -localparam INDEX_PULSE_CYCLES = INDEX_PULSE_LEN * SYS_CLK / 1000; +localparam INDEX_PULSE_CYCLES = INDEX_PULSE_LEN * CLK_EN; reg [18:0] index_pulse_cnt; -always @(posedge clk) begin +always @(posedge clk) if(clk8m_en) begin if(index_pulse_start && (index_pulse_cnt == INDEX_PULSE_CYCLES-1)) begin index <= 1'b0; index_pulse_cnt <= 19'd0; @@ -97,7 +99,7 @@ end // ======================= track handling ========================= // ================================================================ -localparam STEP_BUSY_CLKS = (SYS_CLK/1000)*STEPBUSY; // steprate is in ms +localparam STEP_BUSY_CLKS = CLK_EN*STEPBUSY; // steprate is in ms assign track = current_track; reg [6:0] current_track /* verilator public */ = 7'd0; @@ -110,7 +112,7 @@ always @(posedge clk) begin step_inD <= step_in; step_outD <= step_out; - if(step_busy != 0) + if(clk8m_en && step_busy != 0) step_busy <= step_busy - 18'd1; if(select) begin @@ -159,7 +161,7 @@ always @(posedge clk) begin case(sec_state) SECTOR_STATE_GAP: begin sec_state <= SECTOR_STATE_HDR; - sec_byte_cnt <= SECTOR_HDR_LEN-1'd1; + sec_byte_cnt <= SECTOR_HDR_LEN[9:0]-1'd1; end SECTOR_STATE_HDR: begin @@ -225,8 +227,8 @@ end // number of system clock cycles after which disk has reached // full speed -localparam SPIN_UP_CLKS = SYS_CLK/1000*SPINUP; -localparam SPIN_DOWN_CLKS = SYS_CLK/1000*SPINDOWN; +localparam SPIN_UP_CLKS = CLK_EN*SPINUP; +localparam SPIN_DOWN_CLKS = CLK_EN*SPINDOWN; reg [31:0] spin_up_counter; // internal motor on signal that is only true if the drive is selected @@ -242,7 +244,7 @@ always @(posedge clk) begin // reset spin_up counter whenever motor state changes if(motor_onD != motor_on_sel) spin_up_counter <= 32'd0; - else begin + else if(clk8m_en) begin spin_up_counter <= spin_up_counter + (fm ? RATESD : hd ? RATEHD : RATEDD); if(motor_on_sel) begin @@ -271,12 +273,14 @@ reg data_clk_en; reg [31:0] clk_cnt; always @(posedge clk) begin data_clk_en <= 0; - if(clk_cnt + rate > SYS_CLK/2) begin - clk_cnt <= clk_cnt - (SYS_CLK/2 - rate); - data_clk <= !data_clk; - if (~data_clk) data_clk_en <= 1; - end else - clk_cnt <= clk_cnt + rate; + if(clk8m_en) begin + if(clk_cnt + rate > CLK_EN*1000/2) begin + clk_cnt <= clk_cnt - (CLK_EN*1000/2 - rate); + data_clk <= !data_clk; + if (~data_clk) data_clk_en <= 1; + end else + clk_cnt <= clk_cnt + rate; + end end endmodule