OpenCores
URL https://opencores.org/ocsvn/spicxif/spicxif/trunk

Subversion Repositories spicxif

[/] [spicxif/] [trunk/] [RTL/] [SPIxIF.v] - Rev 2

Compare with Previous | Blame | View Log

////////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2008-2013 by Michael A. Morris, dba M. A. Morris & Associates
//
//  All rights reserved. The source code contained herein is publicly released
//  under the terms and conditions of the GNU Lesser Public License. No part of
//  this source code may be reproduced or transmitted in any form or by any
//  means, electronic or mechanical, including photocopying, recording, or any
//  information storage and retrieval system in violation of the license under
//  which the source code is released.
//
//  The source code contained herein is free; it may be redistributed and/or
//  modified in accordance with the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either version 2.1 of
//  the GNU Lesser General Public License, or any later version.
//
//  The source code contained herein is freely released WITHOUT ANY WARRANTY;
//  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
//  PARTICULAR PURPOSE. (Refer to the GNU Lesser General Public License for
//  more details.)
//
//  A copy of the GNU Lesser General Public License should have been received
//  along with the source code contained herein; if not, a copy can be obtained
//  by writing to:
//
//  Free Software Foundation, Inc.
//  51 Franklin Street, Fifth Floor
//  Boston, MA  02110-1301 USA
//
//  Further, no use of this source code is permitted in any form or means
//  without inclusion of this banner prominently in any derived works.
//
//  Michael A. Morris
//  Huntsville, AL
//
////////////////////////////////////////////////////////////////////////////////
 
`timescale 1ns / 1ps
 
////////////////////////////////////////////////////////////////////////////////
// Company:         M. A. Morris & Associates 
// Engineer:        Michael A. Morris
// 
// Create Date:     20:31:58 03/17/2008 
// Design Name:     Serial Peripheral Interconnect (SPI) Master Interface
// Module Name:     SPIxIF.v 
// Project Name:    VerilogComponentsLib\SPI and SSP Components\SPI Master
// Target Devices:  FPGA
// Tool versions:   ISE 10.1i SP3 
//
// Description:
//
//  This module implements a full-duplex (Master) SPI interface. It is a major
//  revision of the previous implementation which basically implemented the SPI
//  operating modes within a Synchronous Serial Peripheral (SSP) as used in NXP
//  ARM LPC21xx microcontrollers. In an NXP SSP, an SPI-like peripheral is used
//  that has a programmable length shift register which performs serial I/O
//  data transfers in a single cycle: Slave Select (SS) is asserted and de-
//  asserted for each data transfer cycle. The only SPI-like feature of the pre-
//  vious implementation was the ability to program the clock idle state and the
//  data sampling edge, i.e. the SPI operating modes. Because a transfer cycle
//  always deasserted SS, the original implementation would require substan-
//  tial modification in order to be useful with standard SPI-compatible devices
//  such as Serial EEPROMs, serial FRAMs/MRAMs, ADCs/DACs, UARTs, I/O expanders,
//  etc.
//  
//  This module will implement an SPI Master interface that avoids the limita-
//  tions of the previous implementation. It will use a fixed 8-bit interface,
//  but it will support variable length cycles. To operate in this manner, the
//  basic control interface will support an interface that can be easily attach-
//  ed to FIFOs. (The transmit FIFO will use a 9-bit interface, and the receive
//  FIFO will use an 8-bit interface.) An SPI data transfer cycle will start
//  when the transmit FIFO EF indicates that there is data to transmit, and the
//  SPI transfer cycle will terminate when the last bit is shifted out and the
//  transmit FIFO EF indicates that it is empty.
// 
//  The 9th bit of the transmit data will be used to enable the writing of the
//  receive data into the receive FIFO. If the bit is not set, the data shifted
//  in during an 8-bit SPI shift cycle is not captured into the receive FIFO.
//  If the 9th bit is set, then the data shifted into the SPI shift register is
//  captured into the receive FIFO. The 9th bit is expected to be part of the
//  transmit FIFO, so it can be set or cleared for each 8-bit SPI transfer cy-
//  cle. (Note: the 9th bit can be implemented separate from the transmit FIFO,
//  but it would need to be merged into the transmit data bus by the external
//  logic.)
//
//  The explicit read capability provided by the 9th bit is useful when working
//  with SPI devices such as SPI memory devices which do not return any data
//  until a command code and an address have been written. Generally, these
//  devices require several command codes and address bytes to be sent to it
//  before it enables its serial output signal driver and returns the requested
//  data. Therefore, for these devices, the 9th bit is cleared when the command
//  and address bytes are being sent, and the 9th bit set while dummy data is
//  written to and the data received from the device is written into the receive
//  FIFO.
//
//  On the other hand, there are many devices where the data on MISO is consi-
//  dered valid from the onset of a transfer cycle. Devices such as ADCs provide
//  data on MISO that is valid while the conversion command for the next sample
//  is being simultaneously shifted out to the ADC on MOSI. For these devices,
//  the 9th bit is set in the transmit FIFO for each output byte. This causes
//  each received byte to be written to the receive FIFO.
//
//  The SPI interface operates in four modes: Mode 0, 1, 2, and 3. Generally,
//  the mode is selected by two control signals, CPOL and CPHA. CPHA determines
//  the idle state of the SPI clock signal, SCK. The interface shifts data at
//  the beginning of each bit cell. The four operating SPI modes are tabulated
//  in the following table:
//
//  Mode    CPOL    CPHA    :   SCK Idle Level    Sample Edge
//    0       0       0     :         0             Rising
//    1       0       1     :         1             Falling
//    2       1       0     :         0             Falling
//    3       1       1     :         1             Rising
//
//  Examining the table, the sampling edge is set to rising when CPOL and CPHA
//  are the same logic level, and it is set to falling when CPOL and CPHA are
//  complementary logic levels. In other words, the rising edge of SCK occurs in
//  the middle of the bit cell when (CPOL XNOR CPHA) == 1, and the falling edge
//  occurs in the middle of the bit cell when (CPOL XOR CPHA) == 1.
//
//  Two internal signals derived from Mode[1:0] determine the idle state of
//  SCK, SCK_Lvl, and the polarity of SCK used for data sampling, SCK_Inv. A
//  change-of-state (COS) detector is used to dynamically detect changes in the
//  value of SCK_Lvl, and to load the SCK register with the appropriate value
//  when the SPI interface idle, i.e. SS == 0. SCK_Lvl is taken from Mode[0],
//  and SCK_Inv is the XOR of Mode[1] and Mode[0], as indicated in the table
//  above.
//
//  The implementation shifts at one half of the CE frequency. The shift direc-
//  tion is programmable, but MSB first is the default.
//
//  The module contains the SCK generator. A three bit rate select input deter-
//  mines the rate of the SPI clock signal. A 50% duty cycle clock is produced,
//  and two separate clock enables are generated internally for loading and
//  shifting (propagating) the transmit data, and for shifting and writing the
//  receive data. The basic frequency is set by the equation:
//  
//      F(SCK) = Clk / (2**(Rate + 1))
//
//  If rate is set to 0, then the frequency of SCK is one half that of the
//  module's input clock frequency. With Rate set to 7, the SCK frequecy is
//  Clk/256.
//
//  The output shift enable always asserts at the trailing edge of the bit cell,
//  and the input shift enable always asserts in the middle of the bit cell. 
//  These shift enables are independent of the sampling and propagating edges
//  of SCK. The leading edge of the initial output is generated by the output
//  shift register load signal, which is itself generated by a rising edge
//  detector monitoring the DAV signal while slave select is not asserted. Once
//  SS is asserted, the output shift register is synchronously loaded on the TC
//  of the bit counter coincident with output shift register enable signal,
//  CE_OSR. This event also extends SS and reloads the receive enable signal,
//  RdEn. (RdEn was discussed above, and is determined by the 9th of the trans-
//  mit data.) If there is no more data to transmit, SS and RdEn are both de-
//  asserted.
//
//  All of the module control signals are resampled while SS is not asserted.
//  The control signals are held for the duration of a transfer cycle, which is
//  determined by the number of bytes loaded into the external transmit FIFO.
//  To limit the logic complexity, the module does not make prevent the control
//  signals from being changed as the initial transmit data is written. It is
//  necessary for the client of the module to ensure that the control signals
//  signals are stable at least one clock cycle before transmit data is availa-
//  ble.
//
//  With that limitation in mind, the control signals can be changed at any time
//  during a transfer cycle. They will be processed by the module with a one cy-
//  cle delay when SS is not asserted. In this manner, the client logic can dy-
//  namically change the shift direction, SCK operating mode, and SCK operating
//  frequency. This allows the module to be used in situations where the SPI
//  slave devices change modes, rates, and shift directions. For example, a sin-
//  gle SPI interface can be used to support both SPI memory devices (mode 0/3)
//  and SPI ADCs (mode 2) devices.
//
// Dependencies: none
//
// Revision History:
//
//  0.01    08C17   MAM     File Created
//
//  0.02    08C18   MAM     Modified to incorporate SCK_Lvl and a COS detector
//                          on SCK_Lvl to allow the Idle State SCK state to be
//                          dynamically changed.
//
//  0.03    08E09   MAM     Changed comment to reflect that this module is an 
//                          SPI Master Interface.
//
//  1.00    12I07   MAM     Modified to bring into compliance with Verilog 2001.
//                          Modified the interface to use standard control sig-
//                          nals {CPOL, CPHA} and to map the SCK_Lvl and SCK_Inv
//                          signals to the standard SPI modes. To do this, the
//                          standard mode control signals, {CPOL, CPHA}, are run
//                          through a mapping function at the beginning of the
//                          module.
//
//  1.10    12I09   MAM     Restored use of separate transmit and receive shift
//                          registers. Single, combined shift register can't be
//                          used because input data is shifted on the opposite
//                          edge from that used to shift the output data.
//
//  1.20    12I10   MAM     Converted FRE and FWE output signals to FFs. FRE now
//                          pulsed one cycle after the OSR is loaded. FWE now
//                          pulsed one cycle after the last bit is loaded into
//                          the ISR. Converted CE_Cntr from up counter to a down
//                          counter. CE remains a combinatorial signal, but the
//                          CE counter is loaded from a ROM when CE asserts on
//                          basis of the rate captured before SS asserts. CE now
//                          asserts on a fixed value, 0, and the counter is re-
//                          loaded from a ROM. This contrasts with up-counter
//                          implementation which reloaded a fixed value, 0, and
//                          terminated on a variable value. The variable decode
//                          of the counter for CE apparently caused an issue in
//                          simulation that the new down counter resolves.
//
//  1.30    13G06   MAM     Removed code previously commented out. Corrected the
//                          SCK equation. A Rst_SCK was being generated at the
//                          end of every 8 bits. This introduced a discontinuity
//                          in SCK which does not allow frames greater than 8
//                          bits in length to be transmitted. With the removal
//                          the incorrect conditional logic in Rst_SCK, the SSP
//                          Slave and UART modules operate in either SPI Mode 0
//                          or Mode 3.  
//
// Additional Comments:
//
//  The module control signals, LSB, Mode, and Rate, must be set at least one
//  clock cycle before DAV is asserted. Changing these control signals at the
//  same time that DAV is asserted will result in incorrect operation. 
//
////////////////////////////////////////////////////////////////////////////////
 
module SPIxIF (
    input   Rst,                // System Reset (synchronous)
    input   Clk,                // System Clk
//
    input   LSB,                // SPI LSB First Shift Direction
    input   [1:0] Mode,         // SPI Operating Mode
    input   [2:0] Rate,         // SPI Shift Rate Select: SCK = Clk/2**(Rate+1)
//
    input   DAV,                // SPI Transmit Data Available
    output  reg FRE,            // SPI Transmit FIFO Read Enable
    input   [8:0] TD,           // SPI Transmit Data 
//
    output  reg FWE,            // SPI Receive FIFO Write Enable
    output  [7:0] RD,           // SPI Receive Data
//
    output  reg SS,             // SPI Slave Select
    output  reg SCK,            // SPI Shift Clock
    output  MOSI,               // SPI Master Out, Slave In: Serial Data Output
    input   MISO                // SPI Master In, Slave Out: Serial Data In
);
 
////////////////////////////////////////////////////////////////////////////////
//
//  Module Parameters
// 
 
////////////////////////////////////////////////////////////////////////////////    
//
//  Module Declarations
//
 
reg     Dir;                                // Shift Register Shift Direction
 
reg     SCK_Lvl, SCK_Inv, COS_SCK_Lvl;      // SCK level and edge control
 
reg     [2:0] rRate;                        // SPI SCK Rate Select Register
reg     [6:0] CE_Cntr;                      // SPI CE Counter (2x SCK)
wire    CE;                                 // SPI Clock Enable (TC CE_Cntr)
 
wire    CE_SCK, Rst_SCK;                    // SCK generator control signals
 
reg     Ld;                                 // SPI Transfer Cycle Start Pulse
 
wire    CE_OSR, CE_ISR;                     // SPI Shift Register Clock Enables
reg     [7:0] OSR, ISR;                     // SPI Output/Input Shift Registers
reg     RdEn;                               // SPI Read Enable (9th bit in TD)
 
reg     [2:0] BitCnt;                       // SPI Transfer Cycle Length Cntr
wire    TC_BitCnt;                          // SPI Bit Counter Terminal Count
 
////////////////////////////////////////////////////////////////////////////////
//
//  Implementation
//
 
//  Capture the shift direction and hold until end of transfer cycle
 
always @(posedge Clk)
begin
    if(Rst)
        Dir <= #1 0;            // Default to MSB first
    else if(~SS)
        Dir <= #1 LSB;          // Set shift direction for transfer cycle
end
 
//  Assign SCK idle level and invert control signals based on Mode
 
always @(posedge Clk)
begin
    if(Rst) begin
        SCK_Inv <= #1 0;
        SCK_Lvl <= #1 0;
    end else if(~SS) begin
        SCK_Inv <= #1 ^Mode;    // Invert SCK if Mode == 1 or Mode == 2
        SCK_Lvl <= #1 Mode[0];  // Set SCK idle level from LSB of Mode
    end
end
 
//  Generate change of state pulse when SPI clock idle level changes
//      while Slave Select not asserted
 
always @(posedge Clk)
begin
    if(Rst)
        COS_SCK_Lvl <= #1 0;
    else
        COS_SCK_Lvl <= #1 ((~SS) ? (SCK_Lvl ^ Mode[0]) : 0);
end
 
//  Capture SCK rate and hold until the transfer cycle is complete
 
always @(posedge Clk)
begin
    if(Rst)
        rRate <= #1 ~0;             // Default to slowest rate
    else if(~SS)
        rRate <= #1 Rate;
end
 
//
//  SPI Transfer Cycle Load Pulse Generator
//
 
always @(posedge Clk)
begin
    if(Rst)
        Ld <= #1 0;
    else if(~SS)
        Ld <= #1 DAV & ~Ld;
    else if(Ld)
        Ld <= #1 0;
end
 
//
//  Serial SPI Clock Generator
//
 
always @(posedge Clk)
begin
    if(Rst)
        CE_Cntr <= #1 ~0;
    else if(CE)
        case(rRate)
            3'b000  : CE_Cntr <= #1 0;
            3'b001  : CE_Cntr <= #1 1;
            3'b010  : CE_Cntr <= #1 3;
            3'b011  : CE_Cntr <= #1 7;
            3'b100  : CE_Cntr <= #1 15;
            3'b101  : CE_Cntr <= #1 31;
            3'b110  : CE_Cntr <= #1 63;
            3'b111  : CE_Cntr <= #1 127;
        endcase
    else if(SS)
        CE_Cntr <= #1 (CE_Cntr - 1);
end
 
assign CE = (Ld | (~|CE_Cntr));
 
assign CE_SCK  = CE & SS; // Clock starts with Slave Select Strobe
assign Rst_SCK = Rst | Ld | (COS_SCK_Lvl & ~SS) | (TC_BitCnt & CE_OSR & ~DAV);
 
always @(posedge Clk)
begin
    if(Rst_SCK) 
        #1 SCK <= (Ld ? SCK_Inv : SCK_Lvl);
    else if(CE_SCK)
        #1 SCK <= ~SCK;
end
 
//
//  SPI Output Shift Register
//
 
assign CE_OSR = CE_SCK & (SCK_Inv ^ SCK);   
assign Ld_OSR = Ld | (TC_BitCnt & CE_OSR);   
 
always @(posedge Clk)
begin
    if(Rst)
        OSR <= #1 0;
    else if(Ld_OSR)
        OSR <= #1 TD;
    else if(CE_OSR)
        OSR <= #1 ((Dir) ? {SCK_Lvl, OSR[7:1]} : {OSR[6:0], SCK_Lvl});
end
 
assign MOSI = SS & ((Dir) ? OSR[0] : OSR[7]);
 
//
//  SPI Input Shift Register
//
 
assign CE_ISR = CE_SCK & (SCK_Inv ^ ~SCK);   
 
always @(posedge Clk)
begin
    if(Rst)
        ISR <= #1 0;
    else if(Ld)
        ISR <= #1 0;
    else if(CE_ISR)
        ISR <= #1 ((Dir) ? {MISO, ISR[7:1]} : {ISR[6:0], MISO});
end
 
//
//  SPI SR Bit Counter
//
 
assign CE_BitCnt  = CE_OSR & SS;
assign Rst_BitCnt = Rst | Ld | (TC_BitCnt & CE_OSR);
 
always @(posedge Clk)
begin
    if(Rst_BitCnt)
        BitCnt <= #1 7;
    else if(CE_BitCnt)
        BitCnt <= #1 (BitCnt - 1);
end
 
assign TC_BitCnt = ~|BitCnt;
 
//
//  SPI Slave Select Generator
//
 
always @(posedge Clk)
begin
    if(Rst)
        SS <= #1 0;
    else if(Ld_OSR)
        SS <= #1 DAV;
end
 
//
//  SPI MISO Read Enable Register
//
 
always @(posedge Clk)
begin
    if(Rst)
        RdEn <= #1 0;
    else if(Ld_OSR)
        RdEn <= #1 ((DAV) ? TD[8] : 0);
end
 
//
//  SPI Transmit FIFO Read Pulse Generator
//
 
always @(posedge Clk)
begin
    if(Rst)
        FRE <= #1 0;
    else
        FRE <= #1 (Ld | (DAV & (TC_BitCnt & CE_OSR)));
end
 
//
//  SPI Receive FIFO Write Pulse Generator
//
 
always @(posedge Clk)
begin
    if(Rst)
        FWE <= #1 0;
    else
        FWE <= #1 (RdEn & (TC_BitCnt & CE_ISR));
end
 
assign RD = ISR;
 
endmodule
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.