// part of NeoGS project (c) 2007-2008 NedoPC // // SPI mode 0 8-bit master module // // short diagram for speed=0 (Fclk/Fspi=2, no rdy shown) // // clk: ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ (positive edges) // counter: 00|00|00|10|11|12|13|14|15|16|17|18|19|1A|1B|1C|1D|1E|1F|00|00|00 // internal! // sck: ___________/``\__/``\__/``\__/``\__/``\__/``\__/``\__/``\_______ // sdo: --------< do7 | do6 | do5 | do4 | do3 | do2 | do1 | do0 >------- // sdi: --------< di7 | di6 | di5 | di4 | di3 | di2 | di1 | di0 >------- // bsync: ________/`````\_________________________________________________ // start: _____/``\_______________________________________________________ // din: ------------------------------------------------------------ // dout: old old old old old old old old old old old old old | new new new // // data on sdo must be latched by slave on rising sck edge. data on sdo changes on falling edge of sck // // data from sdi is latched by master on positive edge of sck, while slave changes it on falling edge. // WARNING: slave must emit valid di7 bit BEFORE first pulse on sck! // // start is synchronous pulse, which starts all transfer and also latches din data on the same clk edge // as it is registered high. start can be given anytime (only when speed=0), // so it is functioning then as synchronous reset. when speed!=0, there is global enable for majority of // flipflops in the module, so start can't be accepted at any time // // dout updates with freshly received data at the clk edge in which sck goes high for the last time, thus // latching last bit on sdi. // // sdo emits last bit shifted out after the transfer end module spi ( // SPI wires input clk, // system clock output sck, // SCK output reg sdo, // MOSI input sdi, // MISO // DMA interface input dma_req, input [7:0] dma_din, // Z80 interface input cpu_req, input [7:0] cpu_din, // output output start, // start strobe, 1 clock length output reg [7:0] dout ); assign sck = counter[0]; assign start = req && rdy; wire [7:0] din = dma_req ? dma_din : cpu_din; wire rdy = counter[4]; // 0 - transmission in progress wire req = cpu_req || dma_req; reg [4:0] counter; always @(posedge clk) begin reg [7:0] shift; if (start) begin counter <= 5'b0; sdo <= din[7]; shift[7:1] <= din[6:0]; end else if (!rdy) begin counter <= counter + 5'd1; // shift in (rising edge of SCK) if (!sck) begin shift[0] <= sdi; if (&counter[3:1]) dout <= {shift[7:1], sdi}; end // shift out (falling edge of sck) if (sck) begin sdo <= shift[7]; shift[7:1] <= shift[6:0]; // last bit remains after end of exchange end end end endmodule