`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