////////////////////////////////////////////////////////////////////// //// //// //// File name: pci_spoci_ctrl //// //// //// //// This file is part of the "PCI bridge" project //// //// http://www.opencores.org/cores/pci/ //// //// //// //// Author(s): //// //// - Miha Dolenc (mihad@opencores.org) //// //// //// //// //// ////////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2004 Miha Dolenc, mihad@opencores.org //// //// //// //// This source file may be used and distributed without //// //// restriction provided that this copyright statement is not //// //// removed from the file and that any derivative work contains //// //// the original copyright notice and the associated disclaimer. //// //// //// //// This source file is free; you can redistribute it //// //// and/or modify it under the terms of the GNU Lesser General //// //// Public License as published by the Free Software Foundation; //// //// either version 2.1 of the License, or (at your option) any //// //// later version. //// //// //// //// This source 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 Lesser General Public License for more //// //// details. //// //// //// //// You should have received a copy of the GNU Lesser General //// //// Public License along with this source; if not, download it //// //// from http://www.opencores.org/lgpl.shtml //// //// //// ////////////////////////////////////////////////////////////////////// // // CVS Revision History // // $Log: pci_spoci_ctrl.v,v $ // Revision 1.1 2004/01/24 11:54:18 mihad // Update! SPOCI Implemented! // // `include "pci_constants.v" // synopsys translate_off `include "timescale.v" // synopsys translate_on module pci_spoci_ctrl ( reset_i , clk_i , do_rnd_read_i , do_seq_read_i , do_write_i , write_done_o , dat_rdy_o , no_ack_o , adr_i , dat_i , dat_o , pci_spoci_sda_i , pci_spoci_sda_oe_o , pci_spoci_scl_oe_o ); parameter tx_rx_state_width = 9 ; parameter tx_rx_idle = 'h1 ; parameter tx_rx_start = 'h2 ; parameter tx_rx_restart = 'h4 ; parameter tx_rx_send_bits = 'h8 ; parameter tx_rx_rec_bits = 'h10 ; parameter tx_rx_send_ack = 'h20 ; parameter tx_rx_rec_ack = 'h40 ; parameter tx_rx_send_nack = 'h80 ; parameter tx_rx_stop = 'h100 ; parameter rw_seq_state_width = 5 ; parameter rw_seq_idle = 'h1 ; parameter rw_seq_tx_ctrl = 'h2 ; parameter rw_seq_tx_adr = 'h4 ; parameter rw_seq_tx_byte = 'h8 ; parameter rw_seq_rx_byte = 'h10 ; `ifdef PCI33 parameter cnt_width = 9 ; parameter period_cnt = 334 ; `endif `ifdef PCI66 parameter cnt_width = 10 ; parameter period_cnt = 667 ; `endif input reset_i , clk_i ; input do_rnd_read_i , do_seq_read_i , do_write_i ; output write_done_o , dat_rdy_o , no_ack_o ; input [10: 0] adr_i ; input [ 7: 0] dat_i ; output [ 7: 0] dat_o ; input pci_spoci_sda_i ; output pci_spoci_sda_oe_o , pci_spoci_scl_oe_o ; reg write_done_o , dat_rdy_o , no_ack_o ; reg [ 7: 0] dat_o ; reg pci_spoci_sda_oe_o , pci_spoci_scl_oe_o ; reg clk_gen_cnt_en ; reg clk_gen_cnt_clr ; reg [cnt_width - 1:0] clk_gen_cnt ; reg [tx_rx_state_width - 1:0] tx_rx_state ; reg [tx_rx_state_width - 1:0] tx_rx_next_state ; reg tx_rx_sm_idle ; reg scl_oe ; reg scl_oe_en ; reg sda_oe ; reg sda_oe_en ; reg sda_i_reg_en ; reg sda_i_reg ; always@(posedge clk_i or posedge reset_i) begin if (reset_i) begin clk_gen_cnt <= 'h0 ; tx_rx_state <= tx_rx_idle ; `ifdef ACTIVE_LOW_OE pci_spoci_sda_oe_o <= 1'b1 ; pci_spoci_scl_oe_o <= 1'b1 ; `endif `ifdef ACTIVE_HIGH_OE pci_spoci_sda_oe_o <= 1'b0 ; pci_spoci_scl_oe_o <= 1'b0 ; `endif sda_i_reg <= 1'b1 ; end else begin tx_rx_state <= tx_rx_next_state ; if (clk_gen_cnt_clr) clk_gen_cnt <= 'h0 ; else if (clk_gen_cnt_en) clk_gen_cnt <= clk_gen_cnt + 1'b1 ; if (sda_oe_en) begin `ifdef ACTIVE_LOW_OE pci_spoci_sda_oe_o <= ~sda_oe ; `endif `ifdef ACTIVE_HIGH_OE pci_spoci_sda_oe_o <= sda_oe ; `endif end if (scl_oe_en) begin `ifdef ACTIVE_LOW_OE pci_spoci_scl_oe_o <= ~scl_oe ; `endif `ifdef ACTIVE_HIGH_OE pci_spoci_scl_oe_o <= scl_oe ; `endif end if (sda_i_reg_en) sda_i_reg <= pci_spoci_sda_i ; end end reg [ 7: 0] tx_shift_reg ; reg send_start ; reg start_sent ; reg send_bit ; reg bit_sent ; reg rec_bit ; reg bit_rec ; reg rec_ack ; reg ack_rec ; reg nack_rec ; reg send_ack ; reg ack_sent ; reg send_nack ; reg nack_sent ; reg send_stop ; reg stop_sent ; always@ ( tx_rx_state or clk_gen_cnt or send_start or send_bit or tx_shift_reg or send_stop or rec_ack or sda_i_reg or rec_bit or send_ack or send_nack ) begin clk_gen_cnt_clr = 1'b0 ; clk_gen_cnt_en = 1'b0 ; tx_rx_next_state = tx_rx_state ; tx_rx_sm_idle = 1'b0 ; scl_oe = 1'b0 ; sda_oe = 1'b0 ; scl_oe_en = 1'b0 ; sda_oe_en = 1'b0 ; start_sent = 1'b0 ; bit_sent = 1'b0 ; ack_rec = 1'b0 ; nack_rec = 1'b0 ; sda_i_reg_en = 1'b0 ; stop_sent = 1'b0 ; bit_rec = 1'b0 ; ack_sent = 1'b0 ; nack_sent = 1'b0 ; case (tx_rx_state) tx_rx_idle: begin tx_rx_sm_idle = 1'b1 ; // from idle state, the only transition can be to the send start bit if (send_start) begin tx_rx_next_state = tx_rx_start ; clk_gen_cnt_clr = 1'b1 ; end end tx_rx_start: begin clk_gen_cnt_en = 1'b1 ; sda_oe = 1'b1 ; // start bit is sent by transmiting 0 on the sda line if (clk_gen_cnt == (period_cnt >> 1)) begin start_sent = 1'b1 ; sda_oe_en = 1'b1 ; end // after half clock period of driving the sda low, the only possible // transition is to send state. // if send bit is not active, stop the procedure - undrive sda if (clk_gen_cnt == period_cnt) begin clk_gen_cnt_clr = 1'b1 ; if (send_bit) begin tx_rx_next_state = tx_rx_send_bits ; end else begin sda_oe = 1'b0 ; sda_oe_en = 1'b1 ; tx_rx_next_state = tx_rx_idle ; end end end tx_rx_send_bits: begin clk_gen_cnt_en = 1'b1 ; // generate high to low transition on the scl line immediately if (clk_gen_cnt == 'h0) begin scl_oe = 1'b1 ; scl_oe_en = 1'b1 ; end // after half of clock low time, load new value for sda oe, depending on the // msb bit in the shift register if (clk_gen_cnt == (period_cnt >> 2)) begin sda_oe = ~tx_shift_reg[7] ; sda_oe_en = 1'b1 ; bit_sent = 1'b1 ; end // after clock low time, generate low to high transition on the scl line if (clk_gen_cnt == (period_cnt >> 1)) begin scl_oe = 1'b0 ; scl_oe_en = 1'b1 ; end // after clock high time, check what to do next if (clk_gen_cnt == (period_cnt)) begin clk_gen_cnt_clr = 1'b1 ; if (~send_bit) begin // after transmiting all the bits, the only possible transition is to the state // that checks the eprom acknowledge if (rec_ack) tx_rx_next_state = tx_rx_rec_ack ; else begin sda_oe = 1'b0 ; sda_oe_en = 1'b1 ; tx_rx_next_state = tx_rx_idle ; end end end end tx_rx_rec_bits: begin clk_gen_cnt_en = 1'b1 ; sda_i_reg_en = 1'b1 ; // generate high to low transition on the scl line immediately if (clk_gen_cnt == 'h0) begin scl_oe = 1'b1 ; scl_oe_en = 1'b1 ; end // after half of clock low time, disable sda driver if (clk_gen_cnt == (period_cnt >> 2)) begin sda_oe = 1'b0 ; sda_oe_en = 1'b1 ; end // after clock low time, generate low to high transition on the scl line if (clk_gen_cnt == (period_cnt >> 1)) begin scl_oe = 1'b0 ; scl_oe_en = 1'b1 ; end // after half of clock high time, report received bit if (clk_gen_cnt == ((period_cnt >> 1) + (period_cnt >> 2)) ) begin bit_rec = 1'b1 ; end // after clock period is finished, check the next operation if (clk_gen_cnt == (period_cnt)) begin clk_gen_cnt_clr = 1'b1 ; if (~rec_bit) begin // when all bits are received, only nack or ack next states are possible if (send_ack) tx_rx_next_state = tx_rx_send_ack ; else if (send_nack) tx_rx_next_state = tx_rx_send_nack ; else begin tx_rx_next_state = tx_rx_idle ; end end end end tx_rx_send_ack: begin clk_gen_cnt_en = 1'b1 ; // generate high to low transition on the scl line if (clk_gen_cnt == 'h0) begin scl_oe = 1'b1 ; scl_oe_en = 1'b1 ; end // after half of clock low time, enable the sda driver if (clk_gen_cnt == (period_cnt >> 2)) begin sda_oe = 1'b1 ; sda_oe_en = 1'b1 ; ack_sent = 1'b1 ; end // after clock low time, disable the scl driver - generate low to high transition on the scl line if (clk_gen_cnt == (period_cnt >> 1)) begin scl_oe = 1'b0 ; scl_oe_en = 1'b1 ; end // after clock period time expires, check what to do next if (clk_gen_cnt == period_cnt) begin clk_gen_cnt_clr = 1'b1 ; // after the byte is acknowledged, the only possible next state is receive bits // state if (rec_bit) tx_rx_next_state = tx_rx_rec_bits ; else begin // this should never happen sda_oe = 1'b0 ; sda_oe_en = 1'b1 ; tx_rx_next_state = tx_rx_idle ; end end end tx_rx_rec_ack: begin clk_gen_cnt_en = 1'b1 ; sda_i_reg_en = 1'b1 ; // generate high to low transition on the scl line if (clk_gen_cnt == 'h0) begin scl_oe = 1'b1 ; scl_oe_en = 1'b1 ; end // after half of clock low time, disable the sda driver if (clk_gen_cnt == (period_cnt >> 2)) begin sda_oe = 1'b0 ; sda_oe_en = 1'b1 ; end // after clock low time, disable the scl driver - generate low to high transition on the scl line if (clk_gen_cnt == (period_cnt >> 1)) begin scl_oe = 1'b0 ; scl_oe_en = 1'b1 ; end // after 1/2 clock high time, report ack or nack condition, depending on the sda input state if (clk_gen_cnt == ((period_cnt >> 1) + (period_cnt >> 2)) ) begin ack_rec = ~sda_i_reg ; nack_rec = sda_i_reg ; end // after clock period time expires, check what to do next if (clk_gen_cnt == period_cnt) begin clk_gen_cnt_clr = 1'b1 ; if (send_bit) tx_rx_next_state = tx_rx_send_bits ; else if (rec_bit) tx_rx_next_state = tx_rx_rec_bits ; else if (send_stop) tx_rx_next_state = tx_rx_stop ; else if (send_start) tx_rx_next_state = tx_rx_restart ; else begin // this should never happen tx_rx_next_state = tx_rx_idle ; end end end tx_rx_send_nack: begin clk_gen_cnt_en = 1'b1 ; // generate high to low transition on the scl line if (clk_gen_cnt == 'h0) begin scl_oe = 1'b1 ; scl_oe_en = 1'b1 ; end // after half of clock low time, disable the sda driver if (clk_gen_cnt == (period_cnt >> 2)) begin sda_oe = 1'b0 ; sda_oe_en = 1'b1 ; nack_sent = 1'b1 ; end // after clock low time, disable the scl driver - generate low to high transition on the scl line if (clk_gen_cnt == (period_cnt >> 1)) begin scl_oe = 1'b0 ; scl_oe_en = 1'b1 ; end // after clock period time expires, check what to do next if (clk_gen_cnt == period_cnt) begin clk_gen_cnt_clr = 1'b1 ; // after the no acknowledge is sent, the only possible next state is stop // state if (send_stop) tx_rx_next_state = tx_rx_stop ; else begin // this should never happen tx_rx_next_state = tx_rx_idle ; end end end tx_rx_restart: begin clk_gen_cnt_en = 1'b1 ; // generate high to low transition if (clk_gen_cnt == 'h0) begin scl_oe = 1'b1 ; scl_oe_en = 1'b1 ; end // after half of clock low time, release sda line if (clk_gen_cnt == (period_cnt >> 2)) begin sda_oe = 1'b0 ; sda_oe_en = 1'b1 ; end // generate low to high transition if (clk_gen_cnt == (period_cnt >> 1)) begin clk_gen_cnt_clr = 1'b1 ; scl_oe = 1'b0 ; scl_oe_en = 1'b1 ; if (send_start) tx_rx_next_state = tx_rx_start ; else tx_rx_next_state = tx_rx_idle ; end end tx_rx_stop: begin clk_gen_cnt_en = 1'b1 ; // generate high to low transition if (clk_gen_cnt == 'h0) begin scl_oe = 1'b1 ; scl_oe_en = 1'b1 ; end // after half of clock low time, drive sda line low if (clk_gen_cnt == (period_cnt >> 2)) begin sda_oe = 1'b1 ; sda_oe_en = 1'b1 ; end // generate low to high transition if (clk_gen_cnt == (period_cnt >> 1)) begin scl_oe = 1'b0 ; scl_oe_en = 1'b1 ; end // after full clock period, release the sda line if (clk_gen_cnt == period_cnt) begin sda_oe = 1'b0 ; sda_oe_en = 1'b1 ; stop_sent = 1'b1 ; tx_rx_next_state = tx_rx_idle ; end end endcase end reg [rw_seq_state_width - 1:0] rw_seq_state ; reg doing_read , doing_write , doing_seq_read , adr_set ; reg [ 3: 0] bits_transfered ; always@(posedge clk_i or posedge reset_i) begin if (reset_i) begin rw_seq_state <= rw_seq_idle ; adr_set <= 1'b0 ; doing_read <= 1'b0 ; doing_write <= 1'b0 ; doing_seq_read <= 1'b0 ; dat_o <= 'h0 ; tx_shift_reg <= 'h0 ; send_start <= 'h0 ; send_stop <= 'h0 ; send_bit <= 'h0 ; send_nack <= 'h0 ; rec_ack <= 'h0 ; no_ack_o <= 'h0 ; bits_transfered <= 'h0 ; write_done_o <= 'h0 ; dat_rdy_o <= 'h0 ; send_ack <= 'h0 ; rec_bit <= 'h0 ; end else begin case (rw_seq_state) rw_seq_idle: begin tx_shift_reg <= {4'b1010, adr_i[10: 8], 1'b0} ; adr_set <= 1'b0 ; if ( tx_rx_sm_idle & ~(doing_write | doing_read | doing_seq_read) ) begin if (do_write_i | do_rnd_read_i | do_seq_read_i) begin rw_seq_state <= rw_seq_tx_ctrl ; send_start <= 1'b1 ; end if (do_write_i) doing_write <= 1'b1 ; else if (do_rnd_read_i) doing_read <= 1'b1 ; else if (do_seq_read_i) doing_seq_read <= 1'b1 ; end else begin doing_write <= 1'b0 ; doing_read <= 1'b0 ; doing_seq_read <= 1'b0 ; end end rw_seq_tx_ctrl: begin if (send_start) begin bits_transfered <= 'h0 ; if (start_sent) begin send_start <= 1'b0 ; send_bit <= 1'b1 ; end end else if (send_bit) begin if (bit_sent) begin bits_transfered <= bits_transfered + 1'b1 ; tx_shift_reg <= {tx_shift_reg[6:0], tx_shift_reg[0]} ; end if (bits_transfered == 'h8) begin send_bit <= 1'b0 ; rec_ack <= 1'b1 ; end end else if (rec_ack) begin bits_transfered <= 'h0 ; if (ack_rec | nack_rec) rec_ack <= 1'b0 ; if (ack_rec) begin if (doing_write | ~adr_set) begin rw_seq_state <= rw_seq_tx_adr ; tx_shift_reg <= adr_i[ 7: 0] ; send_bit <= 1'b1 ; end else begin rw_seq_state <= rw_seq_rx_byte ; rec_bit <= 1'b1 ; end end else if (nack_rec) begin no_ack_o <= 1'b1 ; send_stop <= 1'b1 ; end end else if (send_stop) begin no_ack_o <= 1'b0 ; if (stop_sent) begin send_stop <= 1'b0 ; rw_seq_state <= rw_seq_idle ; end end end rw_seq_tx_adr: begin if (send_bit) begin if (bit_sent) begin bits_transfered <= bits_transfered + 1'b1 ; tx_shift_reg <= {tx_shift_reg[6:0], tx_shift_reg[0]} ; end if (bits_transfered == 'h8) begin send_bit <= 1'b0 ; rec_ack <= 1'b1 ; end end else if (rec_ack) begin bits_transfered <= 'h0 ; if (ack_rec | nack_rec) rec_ack <= 1'b0 ; if (ack_rec) begin adr_set <= 1'b1 ; if (doing_write) begin send_bit <= 1'b1 ; rw_seq_state <= rw_seq_tx_byte ; tx_shift_reg <= dat_i ; end else if (doing_read | doing_seq_read) begin send_start <= 1'b1 ; rw_seq_state <= rw_seq_tx_ctrl ; tx_shift_reg <= 8'b10100001 ; end end else if (nack_rec) begin no_ack_o <= 1'b1 ; send_stop <= 1'b1 ; end end else if (send_stop) begin no_ack_o <= 1'b0 ; if (stop_sent) begin send_stop <= 1'b0 ; rw_seq_state <= rw_seq_idle ; end end end rw_seq_tx_byte: begin if (send_bit) begin if (bit_sent) begin bits_transfered <= bits_transfered + 1'b1 ; tx_shift_reg <= {tx_shift_reg[6:0], tx_shift_reg[0]} ; end if (bits_transfered == 'h8) begin send_bit <= 1'b0 ; rec_ack <= 1'b1 ; end end else if (rec_ack) begin bits_transfered <= 'h0 ; if (ack_rec | nack_rec) begin rec_ack <= 1'b0 ; send_stop <= 1'b1 ; end if (nack_rec) no_ack_o <= 1'b1 ; if (ack_rec) write_done_o <= 1'b1 ; end else if (send_stop) begin no_ack_o <= 1'b0 ; write_done_o <= 1'b0 ; if (stop_sent) begin send_stop <= 1'b0 ; rw_seq_state <= rw_seq_idle ; end end end rw_seq_rx_byte: begin if (rec_bit) begin if (bit_rec) begin bits_transfered <= bits_transfered + 1'b1 ; dat_o <= {dat_o[6:0], sda_i_reg} ; end if (bits_transfered == 'h8) begin rec_bit <= 1'b0 ; dat_rdy_o <= 1'b1 ; if (doing_read) send_nack <= 1'b1 ; else send_ack <= 1'b1 ; end end else if (send_nack) begin dat_rdy_o <= 1'b0 ; bits_transfered <= 'h0 ; if (nack_sent) begin send_stop <= 1'b1 ; send_nack <= 1'b0 ; end end else if (send_ack) begin dat_rdy_o <= 1'b0 ; bits_transfered <= 'h0 ; if (~do_seq_read_i) begin send_ack <= 1'b0 ; send_nack <= 1'b1 ; end else if (ack_sent) begin send_ack <= 1'b0 ; rec_bit <= 1'b1 ; end end else if (send_stop) begin if (stop_sent) begin send_stop <= 1'b0 ; rw_seq_state <= rw_seq_idle ; end end end endcase end end endmodule // pci_spoci_ctrl