// ZX-Evo Base Configuration (c) NedoPC 2008,2009,2010,2011,2012,2013,2014,2019
 
//
 
// fpga SPI slave device -- AVR controlled.
 
 
 
/*
 
    This file is part of ZX-Evo Base Configuration firmware.
 
 
 
    ZX-Evo Base Configuration firmware 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.
 
 
 
    ZX-Evo Base Configuration firmware 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 ZX-Evo Base Configuration firmware.
 
    If not, see <http://www.gnu.org/licenses/>.
 
*/
 
 
 
`include "../include/tune.v"
 
 
 
module slavespi(
 
 
 
        input  wire fclk,
 
        input  wire rst_n,
 
 
 
        input  wire spics_n, // avr SPI iface
 
        output wire spidi,   //
 
        input  wire spido,   //
 
        input  wire spick,   //
 
 
 
        input  wire [ 7:0] status_in, // status bits to be shown to avr
 
 
 
 
 
        output wire [39:0] kbd_out,
 
        output wire        kbd_stb,
 
 
 
        output wire [ 7:0] mus_out,
 
        output wire        mus_xstb,
 
        output wire        mus_ystb,
 
        output wire        mus_btnstb,
 
        output wire        kj_stb,
 
 
 
 
 
        input  wire [ 7:0] gluclock_addr,
 
        input  wire [ 2:0] comport_addr,
 
 
 
        input  wire [ 7:0] wait_write,
 
        output wire [ 7:0] wait_read,
 
        input  wire        wait_rnw,
 
 
 
        output wire        wait_end,
 
 
 
        output wire [ 7:0] config0, // config bits for overall system
 
        output wire [ 7:0] config1, //
 
 
 
        output wire        genrst, // positive pulse, causes Z80 reset
 
 
 
        output wire        sd_lock_out, // SDcard control iface
 
        input  wire        sd_lock_in,  //
 
        output wire        sd_cs_n,     //
 
        output wire        sd_start,    //
 
        output wire [ 7:0] sd_datain,   //
 
        input  wire [ 7:0] sd_dataout   //
 
);
 
 
 
`ifdef SIMULATE
 
        initial
 
        begin
 
                force wait_read = 8'h00;
 
        end
 
`endif
 
 
 
 
 
        // re-synchronize SPI
 
        //
 
        reg [2:0] spics_n_sync;
 
        reg [1:0] spido_sync;
 
        reg [2:0] spick_sync;
 
        //
 
        always @(posedge fclk)
 
        begin
 
                spics_n_sync[2:0] <= { spics_n_sync[1:0], spics_n };
 
                spido_sync  [1:0] <= { spido_sync    [0], spido   };
 
                spick_sync  [2:0] <= { spick_sync  [1:0], spick   };
 
        end
 
        //
 
        wire scs_n = spics_n_sync[1]; // scs_n - synchronized CS_N
 
        wire sdo   = spido_sync[1];
 
        wire sck   = spick_sync[1];
 
        //
 
        wire scs_n_01 = (~spics_n_sync[2]) &   spics_n_sync[1] ;
 
        wire scs_n_10 =   spics_n_sync[2]  & (~spics_n_sync[1]);
 
        //
 
        wire sck_01 = (~spick_sync[2]) &   spick_sync[1] ;
 
        wire sck_10 =   spick_sync[2]  & (~spick_sync[1]);
 
 
 
 
 
        reg [7:0] regnum; // register number holder
 
 
 
        reg [7:0] shift_out;
 
 
 
        reg [7:0] data_in;
 
 
 
 
 
 
 
        // register selectors
 
        wire sel_kbdreg, sel_kbdstb, sel_musxcr, sel_musycr, sel_musbtn, sel_kj,
 
             sel_rstreg, sel_waitreg, sel_gluadr, sel_comadr, sel_cfg0, sel_cfg1,
 
             sel_sddata, sel_sdctrl;
 
 
 
        // keyboard register
 
        reg [39:0] kbd_reg;
 
 
 
 
 
        // common single-byte shift-in register
 
        reg [7:0] common_reg;
 
 
 
 
 
        // wait data out register
 
        reg [7:0] wait_reg;
 
 
 
        //
 
        reg [7:0] cfg0_reg_out;
 
        reg [7:0] cfg1_reg_out;
 
 
 
 
 
        // SDcard access registers
 
        reg [7:0] sddata; // output to SDcard
 
        reg [1:0] sdctrl; // SDcard control (CS_n and lock)
 
 
 
 
 
`ifdef SIMULATE
 
        initial
 
        begin
 
                cfg0_reg_out[7:0] = 8'd0;
 
        end
 
`endif
 
 
 
 
 
        // register number
 
        //
 
        always @(posedge fclk)
 
        begin
 
                if( scs_n_01 )
 
                begin
 
                        regnum <= 8'd0;
 
                end
 
                else if( scs_n && sck_01 )
 
                begin
 
                        regnum[7:0] <= { sdo, regnum[7:1] };
 
                end
 
        end
 
 
 
 
 
        // send data to avr
 
        //
 
        always @*
 
        case(1'b1)
 
                sel_waitreg: data_in = wait_write;
 
                sel_gluadr:  data_in = gluclock_addr;
 
                sel_comadr:  data_in = comport_addr;
 
                sel_sddata:  data_in = sd_dataout;
 
                sel_sdctrl:  data_in = { sd_lock_in, 7'bXXX_XXXX };
 
                default:     data_in = 8'bXXXX_XXXX;
 
        endcase
 
        //
 
        always @(posedge fclk)
 
        begin
 
                if( scs_n_01 || scs_n_10 ) // both edges
 
                begin
 
                        shift_out <= scs_n ? status_in : data_in;
 
                end
 
                else if( sck_01 )
 
                begin
 
                        shift_out[7:0] <= { 1'b0, shift_out[7:1] };
 
                end
 
        end
 
        //
 
        assign spidi = shift_out[0];
 
 
 
 
 
        // reg number decoding
 
        //
 
        assign sel_kbdreg = ( (regnum[7:4]==4'h1) && !regnum[0] ); // $10
 
        assign sel_kbdstb = ( (regnum[7:4]==4'h1) &&  regnum[0] ); // $11
 
        //
 
        assign sel_musxcr = ( (regnum[7:4]==4'h2) && !regnum[1] && !regnum[0] ); // $20
 
        assign sel_musycr = ( (regnum[7:4]==4'h2) && !regnum[1] &&  regnum[0] ); // $21
 
        assign sel_musbtn = ( (regnum[7:4]==4'h2) &&  regnum[1] && !regnum[0] ); // $22
 
        assign sel_kj     = ( (regnum[7:4]==4'h2) &&  regnum[1] &&  regnum[0] ); // $23
 
        //
 
        assign sel_rstreg = ( (regnum[7:4]==4'h3) ) ; // $30
 
        //
 
        assign sel_waitreg = ( (regnum[7:4]==4'h4) && (regnum[1:0]==2'b00) ); // $40
 
        assign sel_gluadr  = ( (regnum[7:4]==4'h4) && (regnum[1:0]==2'b01) ); // $41
 
        assign sel_comadr  = ( (regnum[7:4]==4'h4) && (regnum[1:0]==2'b10) ); // $42
 
        //
 
        assign sel_cfg0 = (regnum[7:4]==4'h5 && regnum[0]==1'b0); // $50
 
        assign sel_cfg1 = (regnum[7:4]==4'h5 && regnum[0]==1'b1); // $51
 
        //
 
        assign sel_sddata = ( (regnum[7:4]==4'h6) && !regnum[0] ); // $60
 
        assign sel_sdctrl = ( (regnum[7:4]==4'h6) &&  regnum[0] ); // $61
 
 
 
 
 
        // registers data-in
 
        //
 
        always @(posedge fclk)
 
        begin
 
                // kbd data shift-in
 
                if( !scs_n && sel_kbdreg && sck_01 )
 
                        kbd_reg[39:0] <= { sdo, kbd_reg[39:1] };
 
 
 
                // mouse data shift-in
 
                if( !scs_n && (sel_musxcr || sel_musycr || sel_musbtn || sel_kj) && sck_01 )
 
                        common_reg[7:0] <= { sdo, common_reg[7:1] };
 
 
 
                // wait read data shift-in
 
                if( !scs_n && sel_waitreg && sck_01 )
 
                        wait_reg[7:0] <= { sdo, wait_reg[7:1] };
 
 
 
                // config shift-in
 
                if( !scs_n && (sel_cfg0 || sel_cfg1) && sck_01 )
 
                        common_reg[7:0] <= { sdo, common_reg[7:1] };
 
 
 
                // config output
 
                if( scs_n_01 && sel_cfg0 )
 
                        cfg0_reg_out <= common_reg;
 
                if( scs_n_01 && sel_cfg1 )
 
                        cfg1_reg_out <= common_reg;
 
 
 
                // SD data shift-in
 
                if( !scs_n && sel_sddata && sck_01 )
 
                        common_reg[7:0] <= { sdo, common_reg[7:1] };
 
 
 
                // SD control shift-in
 
                if( !scs_n && sel_sdctrl && sck_01 )
 
                        common_reg[7:0] <= { sdo, common_reg[7:1] };
 
        end
 
        //
 
        // SD control output
 
        always @(posedge fclk, negedge rst_n)
 
        if( !rst_n )
 
                sdctrl <= 2'b01;
 
        else // posedge clk
 
        begin
 
                if( scs_n_01 && sel_sdctrl )
 
                        sdctrl <= { common_reg[7], common_reg[0] };
 
        end
 
 
 
 
 
        // output data
 
        assign kbd_out = kbd_reg;
 
        assign kbd_stb = sel_kbdstb && scs_n_01;
 
 
 
        assign mus_out    = common_reg;
 
        assign mus_xstb   = sel_musxcr && scs_n_01;
 
        assign mus_ystb   = sel_musycr && scs_n_01;
 
        assign mus_btnstb = sel_musbtn && scs_n_01;
 
        assign kj_stb     = sel_kj     && scs_n_01;
 
 
 
        assign genrst = sel_rstreg && scs_n_01;
 
 
 
        assign wait_read = wait_reg;
 
        assign wait_end = sel_waitreg && scs_n_01;
 
 
 
        assign config0 = cfg0_reg_out;
 
        assign config1 = cfg1_reg_out;
 
 
 
 
 
 
 
        // SD control output
 
        assign sd_lock_out = sdctrl[1];
 
        assign sd_cs_n     = sdctrl[0];
 
 
 
        // SD data output
 
        assign sd_datain = common_reg;
 
        // SD start strobe
 
        assign sd_start = sel_sddata && scs_n_01;
 
 
 
 
 
endmodule