Update FDC.

This commit is contained in:
sorgelig
2021-07-22 07:16:21 +08:00
parent e1000fb687
commit 8b1b4c9c5f
4 changed files with 321 additions and 349 deletions

View File

@@ -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 ),

View File

@@ -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 ]

View File

@@ -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

View File

@@ -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