Accelerator Functional Unit Developer Guide: Intel FPGA Programmable Acceleration Card N3000 Variants

ID 683190
Date 7/15/2022
Public
Document Table of Contents

4.5.2.2. Hello AFU Example (pll)

A new PLL can be instantiated to provide additional clocks in your design. These steps are performed to add an Intel® Arria® 10 IOPLL to the hello_afu design:
  1. Create IOPLL in using IP Catalog and set PLL to desired settings.
  2. Instantiate PLL in hello_afu with ccip_async_shim to perform clock boundary crossing.
  3. Edit ccip_std_afu.sv to connect G_CLK100 to AFU.
  4. Update the *.qsf and *.sdc files.
The updated hello_afu module is listed below:
import ccip_if_pkg::*;
module hello_afu_pll
   (
    input  pClk,    // Core clock. CCI interface is synchronous to this clock.
    input  pClk_reset,  // CCI interface ACTIVE HIGH reset.
	
	input G_CLK100, //100 MHz Global clock for PLL

    // CCI-P signals
    input  t_if_ccip_Rx pClk_cp2af_sRxPort,
    output t_if_ccip_Tx pClk_af2cp_sTxPort
    );

	`define AFU_ACCEL_UUID 128'h850adcc2_6ceb_4b22_9722_d43375b61c66
    // The AFU must respond with its AFU ID in response to MMIO reads of
    // the CCI-P device feature header (DFH).  The AFU ID is a unique ID
    // for a given program.  Here we generated one with the "uuidgen"
    // program and stored it in the AFU's JSON file.  ASE and synthesis
    // setup scripts automatically invoke the OPAE afu_json_mgr script
    // to extract the UUID into afu_json_info.vh.
    logic [127:0] afu_id = `AFU_ACCEL_UUID;

    logic [63:0] scratch_reg;

pll_50Mhz u0 (
		.rst      (pClk_reset),      //   input,  width = 1,   reset.reset
		.refclk   (G_CLK100),   //   input,  width = 1,  refclk.clk
		.locked   (),   //  output,  width = 1,  locked.export
		.outclk_0 (uClk_50)  //  output,  width = 1, outclk0.clk
	);

	
	//uClk_usr domain CCIP signals
	t_if_ccip_Tx af2cp_sTxPort;
	t_if_ccip_Rx cp2af_sRxPort;
	
	ccip_async_shim ccip_async_shim (
				    .bb_softreset    (pClk_reset),
				    .bb_clk          (pClk),
				    .bb_tx           (pClk_af2cp_sTxPort),
				    .bb_rx           (pClk_cp2af_sRxPort),
				    .afu_softreset   (reset),
				    .afu_clk         (uClk_50),
				    .afu_tx          (af2cp_sTxPort),
				    .afu_rx          (cp2af_sRxPort)
				    );
   

    // The c0 header is normally used for memory read responses.
    // The header must be interpreted as an MMIO response when
    // c0 mmmioRdValid or mmioWrValid is set.  In these cases the
    // c0 header is cast into a ReqMmioHdr.
    t_ccip_c0_ReqMmioHdr mmioHdr;
    assign mmioHdr = t_ccip_c0_ReqMmioHdr'(cp2af_sRxPort.c0.hdr);

    //
    // Receive MMIO writes
    //
    always_ff @(posedge uClk_50)
    begin
        if (reset)
        begin
            scratch_reg <= '0;
        end
        else
        begin
            // set the registers on MMIO write request
            // these are user-defined AFU registers at offset 0x40.
            if (cp2af_sRxPort.c0.mmioWrValid == 1)
            begin
                case (mmioHdr.address)
                    16'h0020: scratch_reg <= cp2af_sRxPort.c0.data[63:0];
                endcase
            end
        end
    end

    //
    // Handle MMIO reads.
    //
    always_ff @(posedge uClk_50)
    begin
        if (reset)
        begin
            af2cp_sTxPort.c1.hdr <= '0;
            af2cp_sTxPort.c1.valid <= '0;
            af2cp_sTxPort.c0.hdr <= '0;
            af2cp_sTxPort.c0.valid <= '0;
            af2cp_sTxPort.c2.hdr <= '0;
            af2cp_sTxPort.c2.mmioRdValid <= '0;
        end
        else
        begin
            // Clear read response flag in case there was a response last cycle.
            af2cp_sTxPort.c2.mmioRdValid <= 0;

            // serve MMIO read requests
            if (cp2af_sRxPort.c0.mmioRdValid == 1'b1)
            begin
                // Copy TID, which the host needs to map the response to the request
                af2cp_sTxPort.c2.hdr.tid <= mmioHdr.tid;

                // Post response
                af2cp_sTxPort.c2.mmioRdValid <= 1;

                case (mmioHdr.address)
                    // AFU header
                    16'h0000: af2cp_sTxPort.c2.data <= {
                        4'b0001, // Feature type = AFU
                        8'b0,    // reserved
                        4'b0,    // afu minor revision = 0
                        7'b0,    // reserved
                        1'b1,    // end of DFH list = 1
                        24'b0,   // next DFH offset = 0
                        4'b0,    // afu major revision = 0
                        12'b0    // feature ID = 0
                        };

                    // AFU_ID_L
                    16'h0002: af2cp_sTxPort.c2.data <= afu_id[63:0];

                    // AFU_ID_H
                    16'h0004: af2cp_sTxPort.c2.data <= afu_id[127:64];

                    // DFH_RSVD0 and DFH_RSVD1
                    16'h0006: af2cp_sTxPort.c2.data <= 64'h0;
                    16'h0008: af2cp_sTxPort.c2.data <= 64'h0;

                    // Scratch Register.  Return the last value written
                    // to this MMIO address.
                    16'h0020: af2cp_sTxPort.c2.data <= scratch_reg;

                    default:  af2cp_sTxPort.c2.data <= 64'h0;
                endcase
            end
        end
    end
endmodule
The afu.qsf is updated as shown below:
# CCI-P async shim
source $AFU_SRC_ROOT/rtl/BBB_ccip_async/hw/par/ccip_async_addenda.qsf

set_global_assignment -name IP_FILE             $AFU_SRC_ROOT/rtl/pll/pll_50Mhz.ip

set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to G_CLK100 -entity pac_top
Compile the design with make as shown below:
$ make 2x2x25G INCLUDE_DIAGNOSTICS=0 INCLUDE_MEMORY=0 INCLUDE_AFU_PCIE1=0 GUI=1
Note: You may need to modify the afu.sdc file based on the new clock added.