OpenFPGA/openfpga_flow/benchmarks/iwls2005/pci/rtl/pci_spoci_ctrl.v

916 lines
26 KiB
Verilog

//////////////////////////////////////////////////////////////////////
//// ////
//// 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