/*=================================================================*/ /*=== SPI Related Modules ========================================*/ /*=================================================================*/ /* * FILE: SPI.v * PROGRAMMER: William L. Bahn * * CONTENTS: * * OVERVIEW * */ //================================================================= // SPI MASTER PHYSICAL LAYER //================================================================= /* SPI Implementation Logic * * The timing of the SPI events is locked to a freerunning BaseClock * whose frequency is controlled by the clkdiv input to the module. * The value of clkdiv dictates how many additional master clock each * phase of the BaseClock is extended. If the value is zero, then each * phase lasts a single master clock cycle resulting in an SPI clock * of one-half the master clock. Hence, the relationship between the * value of clkdiv and the SPI clock is as follows: * * BaseClock = clk / [2*(clkdiv+1)] * * Events within the SPI controller occur synchronous with either the * rising edge or the falling edge of the BaseClock with one important * exception - changes to the SSN output occur one-hald master clock * cycle after the controller schedules them (the take effect on the * falling edge of the master clock while all other events take effect * on the rising edge) to ensure separation of SSN and SCK transitions. * * In order to simplify the implementation, the core SPI state machine * implementation focuses loading the data and shifting it out the MOSI * line. These events occur in rigid lockstep with the controller and * are independed of the SPI mode being used. The various modes are * implemented by controlling the polarity of the SCK (which is either * the BaseClock or its inverse) and scheduling the SSN edges relative * to the data load and shifts. * * SCK Generation * * The SCK may be either freerunning or gated. If it is gated, then * transitions only occur when the SSN is asserted. If SCK is freerunning, * then it is output as a continuous clock. * * FSM Description * * The FSM normally sits in the IDLE state and, while there, ignores all * inputs with the exception of the execute flag. Upon the first rising * edge of the master clock after detecting the flag, the data and length * values are latched into internal registers and the busy flag is asserted. * This permits the external controller to change these values in anticipation * of the next SPI packet on the next clock cycle. The state machine also * transitions to the COMMIT state and awaits the proper point in the BaseClock * cycle to commence outputting data. * * */ // SPI Control Data Map // | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | // |MISO_INV|MOSI_INV| SSN_INV| SCK_INV|FreeRun | Dir | CPOL | CPHA | module SPI_MASTER_phy(Test8, packet_RX, packet_TX, initiate, length, control, clkdiv, target, busy, MOSI, MISO, SCK, SSN, rst, clk); parameter LO = 1'b0, HI = 1'b1; output [7:0] Test8; // Test Outputs output [15:0] packet_RX; // Packet received input [15:0] packet_TX; // Packet transmitted input initiate; // Initiate SPI transfer input [7:0] length; // packet length in bits input [7:0] control; // SPI Control Register input [7:0] clkdiv; // Clock divisor input [7:0] target; // Slave Target selection output busy; // Packet transmission in progress output MOSI; // Master-Out, Slave-In input MISO; // Master-In, Slave-Out output SCK; // Serial Clock output [7:0] SSN; // Slave Select Not input rst, clk; // Master reset and clock reg busy; wire BaseClk; reg Shift; reg Load; // Internal Signal Lines wire CPOL, CPHA; wire FreeRun, Direction; wire MISO_inv, MOSI_inv, SSN_inv, SCK_inv; assign MISO_inv = control[7]; assign MOSI_inv = control[6]; assign SSN_inv = control[5]; assign SCK_inv = control[4]; assign FreeRun = control[3]; assign Direction = control[2]; assign CPOL = control[1]; assign CPHA = control[0]; //===================================================================== // Generate a series of tick pulses at the SCLK toggle rate. PulseGen8 PULSE0 (.Q(BaseTick), .Period(clkdiv), .en(HI), .rst(rst), .clk(clk)); DFFRen DFF_BCLK (.Q(BaseClk), .D(~BaseClk), .en(BaseTick), .rst(rst), .clk(clk)); wire BaseRise, BaseFall; assign BaseRise = ((BaseTick == HI)&&(BaseClk == LO)); assign BaseFall = ((BaseTick == HI)&&(BaseClk == HI)); //===================================================================== // SSN Generation reg SSN_reg, SSN_set, SSN_reset; DFFRen DFF_SSN0 (.Q(SSN[0]), .D(target[0]&&SSN_reg), .en(HI), .rst(rst), .clk(~clk)); DFFRen DFF_SSN1 (.Q(SSN[1]), .D(target[1]&&SSN_reg), .en(HI), .rst(rst), .clk(~clk)); DFFRen DFF_SSN2 (.Q(SSN[2]), .D(target[2]&&SSN_reg), .en(HI), .rst(rst), .clk(~clk)); DFFRen DFF_SSN3 (.Q(SSN[3]), .D(target[3]&&SSN_reg), .en(HI), .rst(rst), .clk(~clk)); DFFRen DFF_SSN4 (.Q(SSN[4]), .D(target[4]&&SSN_reg), .en(HI), .rst(rst), .clk(~clk)); DFFRen DFF_SSN5 (.Q(SSN[5]), .D(target[5]&&SSN_reg), .en(HI), .rst(rst), .clk(~clk)); DFFRen DFF_SSN6 (.Q(SSN[6]), .D(target[6]&&SSN_reg), .en(HI), .rst(rst), .clk(~clk)); DFFRen DFF_SSN7 (.Q(SSN[7]), .D(target[7]&&SSN_reg), .en(HI), .rst(rst), .clk(~clk)); always @ (posedge clk) begin if ((rst == HI)||(SSN_reset == HI)) SSN_reg <= ~SSN_inv; else if (SSN_set == HI) SSN_reg <= SSN_inv; else SSN_reg <= SSN_reg; end //===================================================================== // SCLK Generation //wire SCK_int; reg SCK_next; //DFFRen DFF_SCLKI (.Q(SCK_int), .D(SCK_next), .en(BaseTick), .rst(rst), .clk(clk)); DFFRen DFF_SCLK (.Q(SCK), .D(SCK_next^SCK_inv), .en(BaseTick), .rst(rst), .clk(clk)); always @ (SSN_reg or FreeRun or BaseClk or CPOL or CPHA) begin if ((SSN_reg == LO)||(FreeRun == HI)) SCK_next = (~BaseClk)^(CPOL^CPHA); else SCK_next = CPOL; end //===================================================================== // Length Counter reg [7:0] count, Next_count; always @ (posedge clk) begin if ((rst == HI)||(Load == HI)) begin count <= length; end else if (BaseFall == HI) count <= count - 1; else count <= count; end //===================================================================== // Arming and Firing Logic wire launch; FireAndAcknowledge FIRE ( .Q(launch), .Fire(initiate), .Ack(busy), .rst(rst), .clk(clk) ); //===================================================================== // Primary FSM parameter S_IDLE = 2'd0, S_SYNC = 2'd1, S_COMMIT = 2'd2, S_RUNNING = 2'd3; reg [1:0] State, Next_State; always @ (posedge clk) begin if (rst == HI) begin State <= S_IDLE; end else begin State <= Next_State; end end // FSM State Transition Definitions always @ (State or launch or length or count or CPHA or BaseRise or BaseFall) begin case (State) S_IDLE: // Capture the initiate flag begin Shift = LO; if ((BaseRise == HI)&&(launch == HI)) begin if (CPHA == HI) SSN_set = HI; else SSN_set = LO; SSN_reset = LO; busy = HI; Load = HI; Next_State = S_COMMIT; end else begin SSN_set = LO; SSN_reset = HI; busy = LO; Load = LO; Next_State = S_IDLE; end end S_COMMIT: // Schedule the SSN assertion begin busy = HI; Shift = LO; SSN_reset = LO; Load = LO; if ((CPHA == LO)&&(BaseFall == HI)) SSN_set = HI; else SSN_set = LO; if (BaseRise == HI) Next_State = S_RUNNING; else Next_State = State; end S_RUNNING: begin Load = LO; SSN_set = LO; Shift = BaseFall; if (count == 8'd0) begin if ((CPHA == LO)&&(BaseFall == HI)) SSN_reset = HI; else if ((CPHA == HI)&&(BaseRise == HI)) SSN_reset = HI; else SSN_reset = LO; if (BaseFall == HI) begin Next_State = S_IDLE; busy = LO; end else begin Next_State = State; busy = HI; end end else begin busy = HI; SSN_reset = LO; Next_State = State; end end default: begin busy = HI; Shift = LO; Load = LO; SSN_set = LO; SSN_reset = LO; Next_State = S_IDLE; end endcase end //===================================================================== // Data Shift Register wire MOSI_int; ShiftReg16DIR SR16 ( .Sout(MOSI_int), .Sin(MISO^MISO_inv), .Q(packet_RX), .D(packet_TX), .Shift(Shift), .Load(Load), .Dir(Direction), .rst(rst), .clk(clk) ); assign MOSI = MOSI_int^MOSI_inv; //===================================================================== // Test Output Assignments assign Test8[7] = BaseTick; assign Test8[6] = BaseClk; assign Test8[5] = SSN_set; assign Test8[4] = SSN_reset; assign Test8[3] = count[0]; assign Test8[2] = launch; assign Test8[1] = State[1]; assign Test8[0] = State[0]; endmodule // ====================================================================== // Block RAM Memory Interface Module // ====================================================================== // ====================================================================== // Instruction Fetch Module // ====================================================================== module INSTRUCTION_FETCH ( Test, // SPI Processor Interface Pointer, Fetch, Instruction, DataValid, // FPGA BlockRAM Interface DPRAM_RA, DPRAM_RD, // Global Clock/Reset clk, rst ); output [ 7:0] Test; // SPI Processor Interface input [15:0] Pointer; input Fetch; output [63:0] Instruction; output DataValid; // FPGA BlockRAM Interface output [15:0] DPRAM_RA; input [63:0] DPRAM_RD; // Global Clock/Reset input clk, rst; parameter LO = 1'b0, HI = 1'b1; // Internal Instruction Data Latch reg [63:0] inst_int; assign Instruction = DPRAM_RD; // FPGA BlockRAM Interface assign DPRAM_RA = Pointer; reg DataValid_int; assign DataValid = DataValid_int; reg State, NextState; parameter S_IDLE = 1'd0, S_FETCH = 1'd1; always @ (posedge clk) begin if (rst == HI) State <= S_IDLE; else State <= NextState; end always @ (State or Fetch) begin case (State) S_IDLE: // Wait for Fetch Request begin DataValid_int <= HI; if (Fetch == HI) NextState <= S_FETCH; else NextState <= State; end S_FETCH: // Latch the Read Request begin DataValid_int <= LO; if (Fetch == LO) NextState <= S_IDLE; else NextState <= State; end default: begin DataValid_int <= LO; NextState <= S_IDLE; end endcase end assign Test[7] = Fetch; assign Test[6] = DataValid; assign Test[5:3] = DPRAM_RA[2:0]; assign Test[2:1] = DPRAM_RD[1:0]; assign Test[0] = State; endmodule // ====================================================================== // SPI Processor // ====================================================================== module SPI_PROCESSOR ( Test, // Loop Controller Interface Time, IP, IP_Load, // Instruction Memory Interface Pointer, Fetch, Instruction, DataValid, // SPI Interface Packet, PacketLength, Target, Initiate, Busy, // Global Clock/Reset clk, rst ); // Test Outputs output [ 7:0] Test; // Loop Controller Interface input [15:0] Time; input [15:0] IP; input IP_Load; // Instruction Fetch Interface output [15:0] Pointer; output Fetch; input [63:0] Instruction; input DataValid; // SPI Interface output [15:0] Packet; output [ 7:0] PacketLength; output [ 7:0] Target; output Initiate; input Busy; // Global Clock/Reset input clk, rst; parameter LO = 1'b0, HI = 1'b1; parameter NULL = 16'h0000; //------------------------------------------------------------- // Instruction Latch //------------------------------------------------------------- reg [63:0] LatchedInstruction; wire INST_Latch; always @ (posedge clk) begin if (INST_Latch == HI) LatchedInstruction <= Instruction; else LatchedInstruction <= LatchedInstruction; end //------------------------------------------------------------- // Instruction Parser //------------------------------------------------------------- wire [15:0] NextInstruction; wire [15:0] TimeStamp; assign NextInstruction = LatchedInstruction[15: 0]; assign TimeStamp = LatchedInstruction[31:16]; assign Packet = LatchedInstruction[47:32]; assign PacketLength = LatchedInstruction[55:48]; assign Target = LatchedInstruction[63:56]; //------------------------------------------------------------- // Instruction Vector Load Latch //------------------------------------------------------------- // Since the IP_Load signal from the Loop Controller lasts only // one clock cycle, it is used to set a Latch that requests a // new vector load. The Latch is reset by the FSM once the request // is acknowledged. reg IP_Load_Latch_Reset; RSFF LOADLATCH ( .Q(IP_Load_Latch), .S(IP_Load), .R(IP_Load_Latch_Reset), .rst(rst), .clk(clk) ); reg [15:0] Pointer_int; Reg16 INST_PTR ( .Q(Pointer), .D(Pointer_int), .en(HI), .rst(rst), .clk(clk) ); //------------------------------------------------------------- // FSM //------------------------------------------------------------- reg INST_Latch_int; reg Initiate_int; reg Fetch_int; assign INST_Latch = INST_Latch_int; assign Initiate = Initiate_int; assign Fetch = Fetch_int; reg [2:0] State, NextState; parameter S_INI = 3'd0, S_VECTOR = 3'd1, S_CAPTURE = 3'd2, S_BUSY = 3'd3, S_LATCH = 3'd4, S_DWELL = 3'd5, S_INITIATE = 3'd6, S_FETCH = 3'd7; always @ (posedge clk) begin if (rst == HI) State <= S_INI; else State <= NextState; end always @ (State or IP_Load_Latch or DataValid or Busy or Pointer or IP or NextInstruction or Time or TimeStamp) begin case (State) S_INI: // Post-Reset - Wait for New Vector Request. begin Pointer_int <= Pointer; Initiate_int <= LO; IP_Load_Latch_Reset <= LO; Fetch_int <= LO; INST_Latch_int <= LO; if (IP_Load_Latch == HI) NextState <= S_VECTOR; else NextState <= State; end S_VECTOR: // Use new IP_Vector from Loop Controller. begin Pointer_int <= IP; Initiate_int <= LO; IP_Load_Latch_Reset <= HI; Fetch_int <= LO; INST_Latch_int <= LO; if (Pointer == NULL) NextState <= S_INI; else NextState <= S_FETCH; end S_FETCH: // Start Instruction Fetch Process. begin Pointer_int <= Pointer; Initiate_int <= LO; IP_Load_Latch_Reset <= LO; Fetch_int <= HI; INST_Latch_int <= LO; if (DataValid == LO) NextState <= S_CAPTURE; else NextState <= State; end S_CAPTURE: // Stall until data from Inst Fetch module is ready. begin Pointer_int <= Pointer; Initiate_int <= LO; IP_Load_Latch_Reset <= LO; Fetch_int <= LO; INST_Latch_int <= LO; if (DataValid == HI) NextState <= S_BUSY; else NextState <= State; end S_BUSY: // Stall while SPI Module is busy. begin Pointer_int <= Pointer; Initiate_int <= LO; IP_Load_Latch_Reset <= LO; Fetch_int <= LO; INST_Latch_int <= LO; if (IP_Load_Latch == HI) NextState <= S_VECTOR; else if (Busy == LO) NextState <= S_LATCH; else NextState <= State; end S_LATCH: // Latch new instruction. begin Pointer_int <= Pointer; Initiate_int <= LO; IP_Load_Latch_Reset <= LO; Fetch_int <= LO; INST_Latch_int <= HI; if (IP_Load_Latch == HI) NextState <= S_VECTOR; else NextState <= S_DWELL; end S_DWELL: // Wait for timestamp to expire. begin Pointer_int <= NextInstruction; Initiate_int <= LO; IP_Load_Latch_Reset <= LO; Fetch_int <= LO; INST_Latch_int <= LO; if (IP_Load_Latch == HI) NextState <= S_VECTOR; else if (Time >= TimeStamp) NextState <= S_INITIATE; else NextState <= State; end S_INITIATE: // Initiate SPI Transfer. begin Pointer_int <= Pointer; Initiate_int <= HI; IP_Load_Latch_Reset <= LO; Fetch_int <= LO; INST_Latch_int <= LO; if (IP_Load_Latch == HI) NextState <= S_VECTOR; else if (Busy == HI) if (Pointer == NULL) NextState <= S_INI; else NextState <= S_FETCH; else NextState <= State; end default: // Post-Reset - Wait for New Vector Request. begin Pointer_int <= Pointer; Initiate_int <= LO; INST_Latch_int <= LO; Fetch_int <= LO; IP_Load_Latch_Reset <= LO; NextState <= S_INI; end endcase end assign Test[7] = Initiate; assign Test[6] = IP_Load_Latch_Reset; assign Test[5] = Fetch; assign Test[4] = INST_Latch; assign Test[3] = IP_Load_Latch; assign Test[2:0] = State[2:0]; endmodule