Creating a New Physical Device - LEAP-FPGA/leap-documentation GitHub Wiki

Physical devices are those latency-insensitive modules which, in addition to having latency-insensitive channel I/O also have physical wires, typically to an external chip. There are two methods of incorporating a new device into LEAP: the old "physical platform" method and the new, experimental "user-level device" method.

Physical Platform

At its lowest level, LEAP views the FPGA as a collection of physical device: PCI-e, DDR, SERDES, RF, etc.

An example "physical platform":https://github.com/LEAP-Core/leap-platforms/tree/master/modules/bluespec/common/fpgaenv/physical-platform/pci-express-generic is shown here:

// PHYSICAL_DRIVERS

// This represents the collection of all platform capabilities which the
// rest of the FPGA uses to interact with the outside world.
// We use other modules to actually do the work.

interface PHYSICAL_DRIVERS;
    interface CLOCKS_DRIVER                        clocksDriver;
    interface PCIE_DRIVER                          pcieDriver;
    interface DDR_DRIVER                           ddrDriver;
    interface AURORA_COMPLEX_DRIVERS               auroraDriver;
endinterface

// TOP_LEVEL_WIRES

// The TOP_LEVEL_WIRES is the datatype which gets passed to the top level
// and output as input/output wires. These wires are then connected to
// physical pins on the FPGA as specified in the accompanying UCF file.
// These wires are defined in the individual devices.

interface TOP_LEVEL_WIRES;
    // wires from devices
    interface CLOCKS_WIRES                        clocksWires;
    interface PCIE_WIRES                          pcieWires;
    interface DDR_WIRES                           ddrWires;
    interface AURORA_COMPLEX_WIRES                auroraWires;
endinterface

// PHYSICAL_PLATFORM

// The platform is the aggregation of wires and drivers.

interface PHYSICAL_PLATFORM;
    interface PHYSICAL_DRIVERS physicalDrivers;
    interface TOP_LEVEL_WIRES  topLevelWires;
endinterface

// mkPhysicalPlatform

// This is a convenient way for the outside world to instantiate all the devices
// and an aggregation of all the wires.

module [CONNECTED_MODULE] mkPhysicalPlatform
    //interface: 
    (PHYSICAL_PLATFORM);
    
    // The Platform is instantiated inside a NULL clock domain. Our first course of
    // action should be to instantiate the Clocks Physical Device and obtain interfaces
    // to clock and reset the other devices with.
    
    CLOCKS_DEVICE clocks <- mkClocksDevice();
    
    Clock clk = clocks.driver.clock;
    Reset rst = clocks.driver.reset;

    // There is a strong assumption that the clock for this module is the 200MHz
    // differential clock.
    let ddrConfig = defaultValue;
    ddrConfig.internalClock = clocks.driver.rawClock;
    ddrConfig.internalReset = clocks.driver.rawReset;
    ddrConfig.modelResetNeedsFanout = True;
    
    case (\`DRAM_CLOCK_MECHANISM) matches
        "InternalUnbuffered":   ddrConfig.clockArchitecture = CLOCK_INTERNAL_UNBUFFERED;   
        "ExternalDifferential": ddrConfig.clockArchitecture = CLOCK_EXTERNAL_DIFFERENTIAL;   
        default:                ddrConfig.clockArchitecture = CLOCK_INTERNAL_BUFFERED;   
    endcase

    // Set the ddr clock source by parameter. 
    DDR_DEVICE sdram <- mkDDRDevice(ddrConfig,
                                    clocked_by clk,
                                    reset_by clocks.driver.baseReset);

    // Next, create the physical device that can trigger a soft reset. Pass along the
    // interface to the trigger module that the clocks device has given us.
    let pcieRst <- mkResetFanout(clocks.driver.baseReset, clocked_by clk);
    PCIE_DEVICE pcie <- mkPCIEDevice(clocks.driver.rawClock,
                                     clocks.driver.rawReset,
                                     clocked_by clk,
                                     reset_by pcieRst);

    //
    // Pass reset from PCIe to the model.  The host holds reset long enough that
    // a crossing wire to the model clock domain is sufficient.
    //
    Reg#(Bool) pcieInReset <- mkReg(True,
                                    clocked_by pcie.driver.clock,
                                    reset_by pcie.driver.reset);
    ReadOnly#(Bool) assertModelReset <-
        mkNullCrossingWire(clocks.driver.clock,
                           pcieInReset,
                           clocked_by pcie.driver.clock,
                           reset_by pcie.driver.reset);
    
    (* fire_when_enabled, no_implicit_conditions *)
    rule exitResetPCIe (pcieInReset);
        pcieInReset <= False;
    endrule

    (* fire_when_enabled *)
    rule triggerModelReset (assertModelReset);
        clocks.softResetTrigger.reset();
    endrule

    AURORA_COMPLEX aurora_device <- mkAuroraDevice(clocks.driver.rawClock,
                                                   clocks.driver.rawReset,
                                                   clocked_by clk, reset_by rst);

    //
    // Aggregate the drivers
    //
    interface PHYSICAL_DRIVERS physicalDrivers;
        interface clocksDriver = clocks.driver;
        interface pcieDriver   = pcie.driver;
        interface ddrDriver    = sdram.driver;
        interface auroraDriver = aurora_device.drivers;
    endinterface
    
    //
    // Aggregate the wires
    //
    interface TOP_LEVEL_WIRES topLevelWires;
        interface clocksWires = clocks.wires;
        interface pcieWires   = pcie.wires;
        interface ddrWires    = sdram.wires;
        interface auroraWires = aurora_device.wires;
    endinterface
               
endmodule