Subversion Repositories pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

`include "../include/tune.v"

// PentEvo project (c) NedoPC 2008-2009
//
// vg93 interface
//
// #1F - vg93 command/state reg {0,0} - not here!
// #3F - vg93 track register    {0,1} - not here!
// #5F - vg93 sector register   {1,0} - not here!
// #7F - vg93 data register     {1,1} - not here!
// #FF - output "system" reg/input (DRQ+IRQ) reg
//   output: d6 - FM/MFM               - NOT USED ANYWHERE! -> skipped
//           d4 - disk side            - inverted out
//           d3 - head load            - for HRDY pin of vg93
//           d2 - /RESET for vg93      - must be zero at system reset
//           d1:d0 - disk drive select - to the 74138
//    input: d7 - /INTRQ - resynced at CPU clock
//           d6 - /DRQ   - .....................
//
// current limitations:
//  1. read clock regenerator is made of simple counter, as in pentagon128
//  1. write precompensation is based only on SL/SR/TR43 signals

module vg93(

        input zclk, // Z80 cpu clock
        input rst_n,
        input fclk, // fpga 28 MHz clock

        output vg_clk,
        output reg vg_res_n,

        input [7:0] din,  // data input from CPU
        output intrq,drq, // output signals for the read #FF (not here)
        input vg_wrFF,    // when TRDOS port #FF written - positive strobe


        output reg vg_hrdy,
        output reg vg_rclk,
        output reg vg_rawr,
        output reg [1:0] vg_a, // disk drive selection
        output reg vg_wrd,
        output reg vg_side,

        input step, // step signal from VG93
        input vg_sl,vg_sr,vg_tr43,
        input rdat_n,
        input vg_wf_de,
        input vg_drq,
        input vg_irq,
        input vg_wd
);


        localparam WRDELAY_OUTER_LEFT  = 4'd4;
        localparam WRDELAY_OUTER_RIGHT = 4'd11;
        localparam WRDELAY_INNER_LEFT  = 4'd0;  // minimal delay is for maximum shift left
        localparam WRDELAY_INNER_RIGHT = 4'd14; // maximal delay is for maximum shift right
        localparam WRDELAY_STANDARD    = 4'd7;  // no-shift




        reg [2:0] vgclk_div7;
        wire vgclk_strobe7;
        reg [1:0] vgclk_div4;

        reg [2:0] step_pulse;
        reg [2:0] drq_pulse;
        wire step_pospulse;
        wire  drq_pospulse;
        reg turbo_state;

        reg [1:0] intrq_sync;
        reg [1:0] drq_sync;

        reg [1:0] sl_sync,sr_sync,tr43_sync;
        reg [2:0] wd_sync;
        wire sl,sr,tr43,wd;

        reg [3:0] wrdelay_cnt;
        wire delay_end;

        reg [3:0] wrwidth_cnt;
        wire wrwidth_ena;


        reg [4:0] rdat_sync;
        reg rdat_edge1, rdat_edge2;
        wire rdat;

        reg [3:0] rwidth_cnt;
        wire rwidth_ena;
        reg [5:0] rclk_cnt;
        wire rclk_strobe;



        // VG93 clocking and turbo-mode

        always @(posedge fclk)
        begin
                step_pulse[2:0] <= { step_pulse[1:0], step};
                 drq_pulse[2:0] <= {  drq_pulse[1:0], vg_drq};
        end

        assign step_pospulse = ( step_pulse[1] & (~step_pulse[2]) );
        assign  drq_pospulse = (  drq_pulse[1] & ( ~drq_pulse[2]) );

        always @(posedge fclk,negedge rst_n)
        begin
                if( !rst_n )
                        turbo_state <= 1'b0;
                else
                begin
                        if( drq_pospulse )
                                turbo_state <= 1'b0;
                        else if( step_pospulse )
                                turbo_state <= 1'b1;
                end
        end



        assign vgclk_strobe7 = (vgclk_div7[2:1] == 2'b11); // 28/7=4MHz freq strobe

        always @(posedge fclk)
        begin
                if( vgclk_strobe7 )
                        vgclk_div7 <= 3'd0;
                else
                        vgclk_div7 <= vgclk_div7 + 3'd1;
        end

        always @(posedge fclk)
        begin
                if( vgclk_strobe7 )
                begin
                        vgclk_div4[0] <= ~vgclk_div4[1];

                        if( turbo_state )
                                vgclk_div4[1] <= ~vgclk_div4[1];
                        else
                                vgclk_div4[1] <= vgclk_div4[0];
                end
        end

        assign vg_clk = vgclk_div4[1];


        // input/output for TR-DOS port #FF

        always @(posedge zclk, negedge rst_n) // CHANGE IF GO TO THE positive/negative strobes instead of zclk!
        begin
                if( !rst_n )
                        vg_res_n <= 1'b0;
                else if( vg_wrFF )
                        { vg_side, vg_hrdy, vg_res_n, vg_a } <= { (~din[4]),din[3],din[2],din[1:0] };
        end

        always @(posedge zclk)
        begin
                intrq_sync[1:0] <= {intrq_sync[0],vg_irq};
                drq_sync[1:0] <= {drq_sync[0],vg_drq};
        end

        assign intrq = intrq_sync[1];
        assign drq   =   drq_sync[1];




        // write precompensation
        // delay times are as in WRDELAY_* parameters, vg_wrd width is always 7 clocks

        always @(posedge fclk)
        begin
                  sl_sync[1:0] <= {   sl_sync[0],   vg_sl   };
                  sr_sync[1:0] <= {   sr_sync[0],   vg_sr   };
                tr43_sync[1:0] <= { tr43_sync[0],   vg_tr43 };
                  wd_sync[2:0] <= {   wd_sync[1:0], vg_wd   };
        end

        assign   sl =   sl_sync[1]; // just state signals
        assign   sr =   sr_sync[1]; //
        assign tr43 = tr43_sync[1]; //

        assign   wd =   wd_sync[1] & (~wd_sync[2]); // strobe: beginning of vg_wd


        // make delay
        always @(posedge fclk)
        begin
                if( wd )
                        case( {sl, tr43, sr} )
                        3'b100:  // shift left, outer tracks
                                wrdelay_cnt <= WRDELAY_OUTER_LEFT;
                        3'b001:  // shift right, outer tracks
                                wrdelay_cnt <= WRDELAY_OUTER_RIGHT;
                        3'b110:  // shift left, inner tracks
                                wrdelay_cnt <= WRDELAY_INNER_LEFT;
                        3'b011:  // shift right, inner tracks
                                wrdelay_cnt <= WRDELAY_INNER_RIGHT;
                        default: // no shift
                                wrdelay_cnt <= WRDELAY_STANDARD;
                        endcase
                else if( !delay_end )
                        wrdelay_cnt <= wrdelay_cnt - 4'd1;
        end

        assign delay_end = (wrdelay_cnt==4'd0);


        // make vg_wdr impulse after a delay

        always @(posedge fclk)
                if( wrwidth_ena )
                begin
                        if( wd )
                                wrwidth_cnt <= 4'd0;
                        else
                                wrwidth_cnt <= wrwidth_cnt + 4'd1;
                end

        assign wrwidth_ena = wd | ( delay_end & (~wrwidth_cnt[3]) );

        always @(posedge fclk)
                vg_wrd <= | wrwidth_cnt[2:0]; // only 7 clocks is the lendth of vg_wrd





        // RCLK/RAWR restore
        // currently simplest counter method, no PLL whatsoever now
        //
        // RCLK period must be 112 clocks (@28 MHz), or 56 clocks for each state
        // RAWR on time is 4 clocks

        // digital filter - removing glitches
        always @(posedge fclk)
                rdat_sync[4:0] <= { rdat_sync[3:0], (~rdat_n) };



        always @(posedge fclk)
        begin
                if( rdat_sync[4:1]==4'b1111 ) // filter beginning of strobe
                        rdat_edge1 <= 1'b1;
                else if( rclk_strobe ) // filter any more strobes during same strobe half-perion
                        rdat_edge1 <= 1'b0;
//              else if( rdat_sync[4:1]==4'b0000 )
//                      rdat_edge1 <= 1'b0;

                rdat_edge2 <= rdat_edge1;
        end

        assign rdat = rdat_edge1 & (~rdat_edge2);

//      assign rdat = rdat_sync[1] & (~rdat_sync[2]);



        always @(posedge fclk)
                if( rwidth_ena )
                begin
                        if( rdat )
                                rwidth_cnt <= 4'd0;
                        else
                                rwidth_cnt <= rwidth_cnt + 4'd1;
                end

        assign rwidth_ena = rdat | (~rwidth_cnt[2]); // [2] - 140ns, [3] - 280ns


        assign rclk_strobe = (rclk_cnt==6'd0);

        always @(posedge fclk)
        begin
                if( rdat )
                        rclk_cnt <= 6'd29; // (56/2)-1 plus halfwidth of RAWR
                else if( rclk_strobe )
                        rclk_cnt <= 6'd55; // period is 56 clocks
                else
                        rclk_cnt <= rclk_cnt - 6'd1;
        end

        reg [5:0] counter = 0;                     
        wire[5:0] delta = 27 - counter; 
        wire[5:0] shift = { delta[5], delta[5], delta[4:1] }; // sign div
        wire[5:0] inc   = rawr_sr[1:0] == 2'b10 ? shift : 1;

        reg [3:0] rawr_sr;

        always @ (posedge clk)
        begin
                rawr_sr <= { rawr_sr[2:0], rdat_n };
                vg_rawr <= !(rawr_sr[3] && !rawr_sr[0] ); // rawr 100ns
        end

        always @ (posedge fclk)
        begin               
                if (counter < 55)
                        counter <= counter + inc;   
                else           
                begin             
                        counter <= 0;
                        vg_rclk <= ~vg_rclk;  
                end    
        end



endmodule