Basic FPGA Structure

This is the minimal test structure that could ever be loaded into the FPGA. When loaded into the FPGA, everything related to the FPGA (NAND, SD card, RTC etc.) will be gone forever, so care must be taken on how the development platform is built.

The structure implement just the most trivial devices, if I may call them like that, implemented in the original FPGA - the board ID and the 1us 32-bit counter.

The PCI target implemented is quite minimal, but tests showed it is working perfectly with what the on-board SoC implements from the whole PCI standard. Also it proven itself functional at a PCI clock of 60MHz without extra precautions.

Remember also to correctly set the required pins in the Device Planner tool or use the preference file (.lpf) listed further down.

You also need to create a PLL with 25MHz input and 50MHz output and name it !SysClockGen. This will be used for generating the 50MHz PCI clock from the 25MHz input reference.

Verilog File

module Main( RefClk,
             PCI_CLK, PCI_AD, PCI_CBE, PCI_FRAME, PCI_IRDY, PCI_TRDY, PCI_DEVSEL, PCI_PAR,
             PCI_GNT, PCI_STOP );

    input       RefClk;
    output      PCI_CLK;
    inout   [31:0]  PCI_AD;
        inout   [3:0]   PCI_CBE;
        inout       PCI_FRAME;
    inout       PCI_IRDY;
    inout       PCI_TRDY;
    inout       PCI_DEVSEL;
    inout       PCI_PAR;
    input       PCI_GNT;
    inout       PCI_STOP;

*/*///////////////////////////////////////////////////////////////////////////

/*
  PCI_T_CmdStart signals the first cycle of a PCI transaction so the PCI target
 can save whatever data is needed for the rest of the transaction. For this, the
 previous PCI_FRAME signal is needed to determine it's status change. As an
 extra condition, a 32-bit aligned address condition is added to the expression
 in order to fully use the LUT.
  PCI_T_CmdEnd signals the last cycle in the PCI transaction.

*/
    reg     PCI_FRAME_1T;
    wire        PCI_T_CmdStart,PCI_T_CmdEnd;

always @(posedge PCI_CLK) PCI_FRAME_1T <= PCI_FRAME;

assign PCI_T_CmdStart = PCI_FRAME_1T && (!PCI_FRAME) && !PCI_AD[1] && !PCI_AD[0];
assign PCI_T_CmdEnd = PCI_FRAME && !PCI_TRDY;

*/*///////////////////////////////////////////////////////////////////////////

/*
  PCI_T_CmdRd_ signals a read command has been detected on the PCI C/BE lines.
  PCI_T_CmdWr_ signals a write command has been detected on the PCI C/BE lines.

*/
    wire        PCI_T_CmdRd_,PCI_T_CmdWr_;

assign PCI_T_CmdRd_ = PCI_CBE[2] && ~PCI_CBE[0] && (PCI_CBE[1] | PCI_CBE[3]);
assign PCI_T_CmdWr_ = PCI_CBE[2] && PCI_CBE[1] && PCI_CBE[0];

*/*///////////////////////////////////////////////////////////////////////////

/*
  PCI_T_AddrSel_* signals that the address range to which the FPGA should have
 any reaction at all has been detected. The bulk of the address decoding insures
 that the top 19 bits indicate 0xE8000*** for TS specific devices and 0xE8001***
 for user devices. That keeps the whole mapping in just two address pages and
 simplifies the overall code.
  Specific device address selection is also evaluated at this level. The address
 bit separating TS devices from user devices may be ignored in order to allow
 accessing a device from both address spaces. For some devices this might be
 a necessity in order to map the minimum amount of address space and avoid
 mapping twice the TS devices address range.
  Also 32-bit aligned addresses are recognized only.

  Because of a nasty surprise from the Lattice tools implementing the equation
 with more than 4 levels of LUTs instead of just 3 that were actually needed,
 plus the rather strange order of pins in the TS-7800 board, specialized
 implementation is required for best results in the time domain.

*/
    wire        PCI_T_AddrSel;
    wire        PCI_T_AddrSel_BoardID;
    wire        PCI_T_AddrSel_uSecCnt;

//assign PCI_T_AddrSel          = (PCI_AD[31:12]&20'hFFFFE)==20'hE8000;

    wire    [3:0]   PCI_T_AddrSel_E800xxxx_;
    wire        PCI_T_AddrSel_E800xxxx;

ORCALUT4 PCI_T_AddrSel_E800xxxx_0 ( .A(PCI_AD[16]), .B(PCI_AD[17]), .C(PCI_AD[19]), .D(PCI_AD[20]),
                                    .Z(PCI_T_AddrSel_E800xxxx_[0]) );
  defparam PCI_T_AddrSel_E800xxxx_0.init=16'h0001;  // xxxx xxxx xxx0 0x00 xxxx xxxx xxxx xxxx
ORCALUT4 PCI_T_AddrSel_E800xxxx_1 ( .A(PCI_AD[18]), .B(PCI_AD[23]), .C(PCI_AD[25]), .D(PCI_AD[27]),
                                    .Z(PCI_T_AddrSel_E800xxxx_[1]) );
  defparam PCI_T_AddrSel_E800xxxx_1.init=16'h0100;  // xxxx 1x0x 0xxx x0xx xxxx xxxx xxxx xxxx
ORCALUT4 PCI_T_AddrSel_E800xxxx_2 ( .A(PCI_AD[26]), .B(PCI_AD[29]), .C(PCI_AD[30]), .D(PCI_AD[31]),
                                    .Z(PCI_T_AddrSel_E800xxxx_[2]) );
  defparam PCI_T_AddrSel_E800xxxx_2.init=16'h4000;  // 111x x0xx xxxx xxxx xxxx xxxx xxxx xxxx
ORCALUT4 PCI_T_AddrSel_E800xxxx_3 ( .A(PCI_AD[21]), .B(PCI_AD[22]), .C(PCI_AD[24]), .D(PCI_AD[28]),
                                    .Z(PCI_T_AddrSel_E800xxxx_[3]) );
  defparam PCI_T_AddrSel_E800xxxx_3.init=16'h0001;  // xxx0 xxx0 x00x xxxx xxxx xxxx xxxx xxxx
ORCALUT4 PCI_T_AddrSel_E800xxxx_03 ( .A(PCI_T_AddrSel_E800xxxx_[0]), .B(PCI_T_AddrSel_E800xxxx_[1]),
                                     .C(PCI_T_AddrSel_E800xxxx_[2]), .D(PCI_T_AddrSel_E800xxxx_[3]),
                                     .Z(PCI_T_AddrSel_E800xxxx) );
  defparam PCI_T_AddrSel_E800xxxx_03.init=16'h8000;
assign PCI_T_AddrSel          = PCI_T_AddrSel_E800xxxx && (PCI_AD[15:12]&4'hE)==4'h0;

assign PCI_T_AddrSel_BoardID  = PCI_AD[11:2]==10'h000 /*&& !PCI_AD[12]*/;
assign PCI_T_AddrSel_uSecCnt  = PCI_AD[11:2]==10'h010 /*&& !PCI_AD[12]*/;

*/*///////////////////////////////////////////////////////////////////////////

/*
  CmdRd_* signals are active high read commands for the implemented devices.
  CmdWr_* signals are active high write commands for the implemented devices.
  In order to keep the design as clean as possible and to insure maximum speed
 can be achieved, per device read and write commands are detected and saved for
 the whole PCI cycle.

*/

    reg     CmdRd_BoardID;
    reg     CmdRd_uSecCnt;

always @(posedge PCI_CLK)
if (PCI_T_CmdEnd)
  begin
    CmdRd_BoardID  <= 0;
    CmdRd_uSecCnt  <= 0;
  end
else if (PCI_T_CmdStart)
  begin
    CmdRd_BoardID  <= PCI_T_AddrSel && PCI_T_CmdRd_ && PCI_T_AddrSel_BoardID;
    CmdRd_uSecCnt  <= PCI_T_AddrSel && PCI_T_CmdRd_ && PCI_T_AddrSel_uSecCnt;
  end

*/*///////////////////////////////////////////////////////////////////////////

/*
  PCI_DEVSEL_* signals indicate that the corresponding devices have detected
 they are accessed for the required transaction. All the PCI_DEVSEL_* signals
 are combined with an AND function to give the global DEVSEL signal for the
 target PCI devices. When the signal is asserted and one clock after it is
 deasserted, it's value is sent to the DEVSEL pin for output.
  Alternatively, CmdRd_* and CmdWr_* signals for specific devices can be used
 in order to avoid unnecessary wires defining.

*/
    wire        PCI_T_DevSel;
    reg     PCI_T_DevSel_;

assign PCI_T_DevSel = !CmdRd_BoardID && !CmdRd_uSecCnt;

always @(posedge PCI_CLK) PCI_T_DevSel_ <= PCI_T_DevSel;

assign PCI_DEVSEL = (!PCI_T_DevSel | !PCI_T_DevSel_) ? PCI_T_DevSel : 1'bZ;

*/*///////////////////////////////////////////////////////////////////////////

/*
  PCI_TRDY_* signals are indicating that the devices are ready for transfer.
 They should be indicating a device is ready only when it is both accessed and
 ready for the transaction. All the PCI_TRDY_* signals are combined with an AND
 function to give the global TRDY signal for the target PCI devices. In case a
 device is always ready for transactions, usually with simple and very fast
 devices, the associated PCI_DEVSEL_* signal can be used instead.
  0 - "ready for transfer"
  1 - "busy"
  When one of the PCI target devices is active, the resulting signal value is
 sent to the TRDY pin for output.
  Alternatively, CmdRd_* and CmdWr_* signals for specific devices can be used
 in order to avoid unnecessary wires defining.

*/
    wire        PCI_T_TRDY;

assign PCI_T_TRDY = !CmdRd_BoardID && !CmdRd_uSecCnt;

assign PCI_TRDY = (!PCI_T_DevSel | !PCI_T_DevSel_) ? PCI_T_TRDY : 1'bZ;

*/*///////////////////////////////////////////////////////////////////////////

/*
  PCI_STOP signal must be driven high whenever the FPGA devices are accessed to
 avoid bus errors because unstable values on the line.

*/
assign PCI_STOP = (!PCI_T_DevSel | !PCI_T_DevSel_) ? 1'b1 : 1'bZ;

*/*///////////////////////////////////////////////////////////////////////////

/*
  This is a read-only value counting the number of microseconds since the FPGA
 has been programmed. In order to avoid reading it while the value changes, it
 is implemented as a PCI_CLK synchronous structure, with changes occuring only
 on positive edges.
  The normal address of the register is 0xE8000040 and this gives an internal
 address of 0x010.
  While this is a really unnecessary device, it is implemented here just because
 it exists in the standard TS-7800 FPGA.

*/
    reg [31:0]  uSecCnt;
    reg [5:0]   uSecCnt_Prescaler;
    wire        uSecCnt_PrescalerEnd;

always @(posedge PCI_CLK)
if (uSecCnt_PrescalerEnd) uSecCnt_Prescaler <= 0;
else uSecCnt_Prescaler <= uSecCnt_Prescaler+1;

// 50MHz end of prescaler rule
assign uSecCnt_PrescalerEnd = uSecCnt_Prescaler[5] && uSecCnt_Prescaler[4] && uSecCnt_Prescaler[1];
// 60MHz end of prescaler rule
/*assign uSecCnt_PrescalerEnd = uSecCnt_Prescaler[5] && uSecCnt_Prescaler[4] &&
                                uSecCnt_Prescaler[3] && uSecCnt_Prescaler[2];*/

always @(posedge PCI_CLK) if (uSecCnt_PrescalerEnd) uSecCnt <= uSecCnt+1;

*/*///////////////////////////////////////////////////////////////////////////

/*
  PCI_AD_Out_* busses are used for reading data from PCI target devices. They
 are all combined by the bus access module depending on the currently selected
 device. Based on PCI_DEVSEL_* signals, they are all combined in a global
 PCI_AD_Out bus used for output on the PCI bus.

*/
    wire    [31:0]  PCI_AD_Out;

assign PCI_AD_Out = ( CmdRd_BoardID                    ? 32'hFFFFFF01 : 32'h00000000) |
                    ( CmdRd_uSecCnt                    ? uSecCnt      : 32'h00000000);

    wire        PCI_AD_OutEn;

assign PCI_AD_OutEn = CmdRd_BoardID | CmdRd_uSecCnt;

assign PCI_AD = PCI_AD_OutEn ? PCI_AD_Out : 32'hZZZZZZZZ;

*/*///////////////////////////////////////////////////////////////////////////

/*
  The parity signal must be generated whenever data is pushed on the PCI data
 lines. This happens when a PCI target device is read and when a PCI master
 device is writing the adress or some data values. The parity output value is
 delayed with a PCI clock cycle.
  Due to the specific of the board, the parity is not verified for the data
 put on the PCI bus by the Orion chip. It is just generated for the data put on
 the PCI bus by the FPGA structure. But the CBE lines must be used always from
 the bus to cover both situations.
  As seen in some Lattice tools examples, best approach is to latch the first
 set of results on 4-input lines and then do a parity on the resulting 9
 signals.

  Because of a nasty surprise from the Lattice tools implementing the equation
 with 4 levels of LUTs instead of just 3 that were actually needed, plus the
 rather strange order of pins in the TS-7800 board, specialized implementation
 is required for best results in the time domain.

*/
    reg     PCI_PAR_OutEn;
    reg [8:0]   PCI_PAR_1;

/*always @(posedge PCI_CLK)
  begin
    PCI_PAR_1[0] <= ^PCI_AD_Out[31:28];
    PCI_PAR_1[1] <= ^PCI_AD_Out[27:24];
    PCI_PAR_1[2] <= ^PCI_AD_Out[23:20];
    PCI_PAR_1[3] <= ^PCI_AD_Out[19:16];
    PCI_PAR_1[4] <= ^PCI_AD_Out[15:12];
    PCI_PAR_1[5] <= ^PCI_AD_Out[11:10];
    PCI_PAR_1[6] <= ^PCI_AD_Out[ 7: 4];
    PCI_PAR_1[7] <= ^PCI_AD_Out[ 3: 0];
    PCI_PAR_1[8] <= ^PCI_CBE[3:0];
  end

*/

always @(posedge PCI_CLK)
  begin
    PCI_PAR_1[0] <= PCI_AD_Out[21] ^ PCI_AD_Out[24] ^ PCI_AD_Out[28] ^ PCI_AD_Out[22];
    PCI_PAR_1[1] <= PCI_AD_Out[29] ^ PCI_AD_Out[31] ^ PCI_AD_Out[30] ^ PCI_AD_Out[26];
    PCI_PAR_1[2] <= PCI_AD_Out[18] ^ PCI_AD_Out[23] ^ PCI_AD_Out[27] ^ PCI_AD_Out[25];
    PCI_PAR_1[3] <= PCI_AD_Out[16] ^ PCI_AD_Out[20] ^ PCI_AD_Out[17] ^ PCI_AD_Out[19];
    PCI_PAR_1[4] <= PCI_AD_Out[9]  ^ PCI_AD_Out[13] ^ PCI_AD_Out[14] ^ PCI_AD_Out[15];
    PCI_PAR_1[5] <= PCI_AD_Out[7]  ^ PCI_AD_Out[11] ^ PCI_AD_Out[10] ^ PCI_AD_Out[12];
    PCI_PAR_1[6] <= PCI_AD_Out[6]  ^ PCI_AD_Out[4]  ^ PCI_AD_Out[8]  ^ PCI_AD_Out[3];
    PCI_PAR_1[7] <= PCI_AD_Out[0]  ^ PCI_AD_Out[1]  ^ PCI_AD_Out[2]  ^ PCI_AD_Out[5];
    PCI_PAR_1[8] <= ^PCI_CBE[3:0];
  end

always @(posedge PCI_CLK) PCI_PAR_OutEn <= PCI_AD_OutEn;

assign PCI_PAR = PCI_PAR_OutEn ? ( ^PCI_PAR_1[8:0] ) : 1'bZ;

*/*///////////////////////////////////////////////////////////////////////////

/*
  PCI bus clock generator from 25MHz external crystal reference.

*/
SysClockGen ClkGen ( .CLK(RefClk), .CLKOP(PCI_CLK), .LOCK() );

endmodule

Preference File

COMMERCIAL;
BLOCK RESETPATHS;
BLOCK ASYNCPATHS;
IOBUF ALLPORTS IO_TYPE=LVCMOS33 ;
IOBUF PORT "RefClk" IO_TYPE=LVCMOS25 ;
LOCATE COMP "RefClk" SITE "R4" ;
LOCATE COMP "PCI_CLK" SITE "R8" ;
LOCATE COMP "PCI_AD_0" SITE "B3" ;
LOCATE COMP "PCI_AD_1" SITE "B2" ;
LOCATE COMP "PCI_AD_2" SITE "C3" ;
LOCATE COMP "PCI_AD_3" SITE "B1" ;
LOCATE COMP "PCI_AD_4" SITE "D3" ;
LOCATE COMP "PCI_AD_5" SITE "C2" ;
LOCATE COMP "PCI_AD_6" SITE "E4" ;
LOCATE COMP "PCI_AD_7" SITE "C1" ;
LOCATE COMP "PCI_AD_8" SITE "D2" ;
LOCATE COMP "PCI_AD_9" SITE "F3" ;
LOCATE COMP "PCI_AD_10" SITE "D1" ;
LOCATE COMP "PCI_AD_11" SITE "G4" ;
LOCATE COMP "PCI_AD_12" SITE "E1" ;
LOCATE COMP "PCI_AD_13" SITE "F2" ;
LOCATE COMP "PCI_AD_14" SITE "F1" ;
LOCATE COMP "PCI_AD_15" SITE "J4" ;
LOCATE COMP "PCI_AD_16" SITE "J5" ;
LOCATE COMP "PCI_AD_17" SITE "K2" ;
LOCATE COMP "PCI_AD_18" SITE "L4" ;
LOCATE COMP "PCI_AD_19" SITE "K1" ;
LOCATE COMP "PCI_AD_20" SITE "L3" ;
LOCATE COMP "PCI_AD_21" SITE "N5" ;
LOCATE COMP "PCI_AD_22" SITE "L6" ;
LOCATE COMP "PCI_AD_23" SITE "L1" ;
LOCATE COMP "PCI_AD_24" SITE "N6" ;
LOCATE COMP "PCI_AD_25" SITE "N2" ;
LOCATE COMP "PCI_AD_26" SITE "P4" ;
LOCATE COMP "PCI_AD_27" SITE "M1" ;
LOCATE COMP "PCI_AD_28" SITE "T2" ;
LOCATE COMP "PCI_AD_29" SITE "P2" ;
LOCATE COMP "PCI_AD_30" SITE "R3" ;
LOCATE COMP "PCI_AD_31" SITE "P1" ;
LOCATE COMP "PCI_CBE_0" SITE "F4" ;
LOCATE COMP "PCI_CBE_1" SITE "G1" ;
LOCATE COMP "PCI_CBE_2" SITE "J2" ;
LOCATE COMP "PCI_CBE_3" SITE "L2" ;
LOCATE COMP "PCI_FRAME" SITE "L5" ;
LOCATE COMP "PCI_DEVSEL" SITE "H2" ;
LOCATE COMP "PCI_TRDY" SITE "K4" ;
LOCATE COMP "PCI_PAR" SITE "G3" ;
LOCATE COMP "PCI_IRDY" SITE "J1" ;
LOCATE COMP "PCI_GNT" SITE "R2" ;
LOCATE COMP "PCI_REQ" SITE "N1" ;
LOCATE COMP "PCI_STOP" SITE "K3" ;