`timescale 1ns / 1ps
//
//	 PCI interface. Most of this code came from http://www.ben.com/minipci/verilog.php
//  There seems to be some issues with burst writes. Get the analyzer working to figure it out
//
//
//  Device IDs can be found here http://pci-ids.ucw.cz/  
//
//


module pci_interface(AD, CBE, PCI_CLK, PCI_GNT_n, PCI_REQ_n, PME_n, IDSEL,
							FRAME_n, IRDY_n, TRDY_n, DEVSEL_n, STOP_n, LOCK_n, PERR_n,
							SERR_n, PAR, ACK64_n, REQ64_n, PCI_RESET_n, INTA_n,

							ib_req, ib_gnt, ib_write_out, ib_read_out, ib_write_in, 
							ib_read_in, ib_data, ib_addr, ib_clock);

//PCI bus sugnals that are actually used
inout [31:0] AD;
inout [3:0] CBE;
input PCI_CLK;
input IDSEL;
inout FRAME_n;
inout IRDY_n;
inout TRDY_n;
output DEVSEL_n;

//sometimes necessary but unused signals
inout STOP_n;
inout LOCK_n;
inout PCI_GNT_n;
inout PCI_REQ_n;
input PCI_RESET_n;
output INTA_n;
inout PERR_n;
output SERR_n;
inout PAR;

//totally unecessary signals
inout ACK64_n;
input REQ64_n;
input PME_n;


output ib_req;
input ib_gnt;
output ib_write_out;
output ib_read_out;
input ib_write_in;
input ib_read_in;
inout [15:0] ib_data;
inout [10:0] ib_addr;
input ib_clock;

 
//these lines are only outputs for bus masters
assign CBE = 4'bz;
assign FRAME_n = 1'bz;
assign IRDY_n = 1'bz;

assign PAR= 1'bz;  //PC mboards generally don't do parity checking 
assign PCI_GNT_n = 1'bz; //a lot of PCI features aren't implemented
assign PCI_REQ_n = 1'bz;
assign STOP_n = 1'bz;
assign LOCK_n = 1'bz;
assign PERR_n = 1'bz;
assign SERR_n = 1'b1;
assign ACK64_n = 1'bz;
assign INTA_n = 1'bz;






parameter DEVICE_ID = 16'h0bad;  
parameter VENDOR_ID = 16'hf00d;		
parameter DEVICE_CLASS = 24'hFF0000;	// Misc
parameter DEVICE_REV = 8'h01;
parameter SUBSYSTEM_ID = 16'h0001;	// Card identifier
parameter SUBSYSTEM_VENDOR_ID = 16'hf001; // Card identifier
parameter DEVSEL_TIMING = 2'b00;	// Fast!

parameter ST_IDLE = 3'b000;
parameter ST_BUSY = 3'b010;
parameter ST_MEMREAD = 3'b100;
parameter ST_MEMWRITE = 3'b101;
parameter ST_CFGREAD = 3'b110;
parameter ST_CFGWRITE = 3'b111;

parameter MEMREAD = 4'b0110;
parameter MEMWRITE = 4'b0111;
parameter CFGREAD = 4'b1010;
parameter CFGWRITE = 4'b1011;

reg memen; //Enable memory access (after config)
reg [2:0] state;
reg [31:0] data;
reg [1:0] enable;

reg [18:0] baseaddr;
reg [10:0] address;
///  base address   internal address   00
//		31:13	 				12:2				1:0


reg ib_req;
wire ib_write_out = ib_gnt & (state == ST_MEMWRITE);
wire ib_read_out = ib_gnt & (state == ST_MEMREAD);
wire [10:0] ib_addr = ib_gnt & ((state == ST_MEMWRITE) | (state == ST_MEMREAD)) ? address[10:0] : 11'bz;
wire [15:0] ib_data = ib_gnt & (state == ST_MEMWRITE) ? AD[15:0] : 16'bz;

parameter EN_NONE = 0;
parameter EN_RD = 1;
parameter EN_WR = 2;
parameter EN_TR = 3;

//there is a 1 clock delay on data read from the internal bus, so assign it directly to the AD lines
//when reading from it. address 0 can't be on the internal bus because it requests and reports bus access.
wire ext_read;
assign ext_read = (state == ST_MEMREAD) & (address != 'h0);
assign AD = (enable == EN_RD) ? (ext_read ? {16'h0,ib_data} : data) : 32'bZ;
assign TRDY_n = (enable == EN_NONE) ? 'bZ : (enable == EN_TR ? 1 : 0);
assign PAR = (enable == EN_RD) ? 0 : 'bZ;
reg DEVSEL_n;

wire cfg_hit = ((CBE == CFGREAD || CBE == CFGWRITE) && IDSEL && AD[1:0] == 2'b00);
wire addr_hit = ((CBE == MEMREAD || CBE == MEMWRITE) && memen && AD[31:13] == {baseaddr});
wire hit = cfg_hit | addr_hit;


//allow the USB interface to read PCI configuration. 
//For real cleverness, get it to write the configuration back 
//so no reboot is needed after reprogramming- either that or store a copy of the 
//base address in external SRAM and refresh it on command
reg [10:0] ib_addr_latch;
reg [15:0] ib_data_latch;
always @ (posedge ib_clock or negedge PCI_RESET_n)
	if(~PCI_RESET_n) begin
		ib_addr_latch <= 0;
		ib_data_latch <= 0;
	end
	else begin
		if(ib_addr == 11'hb) ib_data_latch <= {13'b0,memen,ib_gnt,ib_req};
		if(ib_addr == 11'hc) ib_data_latch <= baseaddr[18:3];
		if(ib_addr == 11'hd) ib_data_latch <= {baseaddr[2:0],13'b0};

		ib_addr_latch <= ib_addr;
	end
assign ib_data = (ib_read_in && ((ib_addr_latch >= 11'hb) && (ib_addr_latch <= 11'hd))) 
				? ib_data_latch : 16'bz;

always @(posedge PCI_CLK or negedge PCI_RESET_n)
begin
    if (~PCI_RESET_n) begin
        state <= ST_IDLE;
        enable <= EN_NONE;
        baseaddr <= 0;
        DEVSEL_n <= 'bZ;
        memen <= 0;
		  ib_req <= 0;
    end
    else    begin
                
    case (state)
        ST_IDLE: begin
            enable <= EN_NONE;
            DEVSEL_n <= 'bZ;
            if (~FRAME_n) begin
                address <= AD[12:2];
                if (hit) begin
                    state <= {1'b1, CBE[3], CBE[0]};
                    DEVSEL_n <= 0;
                    // pipeline the write enable
                    if (CBE[0])
                        enable <= EN_WR;
                end
                else begin
                    state <= ST_BUSY;
                    enable <= EN_NONE;
                end
            end
        end

        ST_BUSY: begin
            DEVSEL_n <= 'bZ;
            enable <= EN_NONE;
            if (FRAME_n)
                state <= ST_IDLE;
        end

        ST_CFGREAD: begin
            enable <= EN_RD;
            if (~IRDY_n || TRDY_n) begin
                case (address[6:0])
                    0: data <= { DEVICE_ID, VENDOR_ID };
                    1: data <= { 5'b0, DEVSEL_TIMING, 9'b0,  14'b0, memen, 1'b0};
                    2: data <= { DEVICE_CLASS, DEVICE_REV };
                    4: data <= { baseaddr, 9'b0, 4'b0000 }; // baseaddr + request 16Kb /4Kw mem anywhere
                    11: data <= {SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID };
                    16: data <= {  baseaddr,13'b0 };
                    default: data <= (32'h0);
                endcase
					 address <= address + 1;
            end
            if (FRAME_n && ~IRDY_n && ~TRDY_n) begin
                DEVSEL_n <= 1;
                state <= ST_IDLE;
                enable <= EN_TR;
            end
        end

        ST_CFGWRITE: begin
            enable <= EN_WR;
            if (~IRDY_n) begin
                case (address[6:0])
                    4: baseaddr <= AD[31:13];  // XXX examine cbe
                    1: memen <= AD[1];
                    default: ;
                endcase
                address <= address + 1;
                if (FRAME_n) begin
                    DEVSEL_n <= 1;
                    state <= ST_IDLE;
                    enable <= EN_TR;
                end
            end
        end

        ST_MEMREAD: begin
            enable <= EN_RD;
            if (~IRDY_n || TRDY_n) begin
					if(address == 11'h0)
						data <= {30'b0,ib_gnt,ib_req};
					address <= address + 1;
            end
            if (FRAME_n && ~IRDY_n && ~TRDY_n) begin
                DEVSEL_n <= 1;
                state <= ST_IDLE;
                enable <= EN_TR;
            end
        end

        ST_MEMWRITE: begin
            enable <= EN_WR;
            if (~IRDY_n) begin
					if(address == 11'h0)
						ib_req <= AD[0];
            	address <= address + 1;
               if (FRAME_n) begin
                    DEVSEL_n <= 1;
                    state <= ST_IDLE;
                    enable <= EN_TR;
               end
            end
        end

    endcase
    end
end
	 



endmodule

<div align="center"><br /><script type="text/javascript"><!--
google_ad_client = "pub-7293844627074885";
//468x60, Created at 07. 11. 25
google_ad_slot = "8619794253";
google_ad_width = 468;
google_ad_height = 60;
//--></script>
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script><br />&nbsp;</div>