This is a walkthrough of the Microchip repo for building an FPGA image for the BeagleV-Fire Polarfire fabric.

Environment Setup Link to heading

In repo, there is a script to export some environment variables used by other scripts:

  • Libero
  • SoftConsole (SC)
  • License server
#!/bin/bash

#===============================================================================
# Edit the following section with the location where the following tools are
# installed:
#   - SoftConsole (SC_INSTALL_DIR)
#   - Libero (LIBERO_INSTALL_DIR)
#   - Licensing daemon for Libero (LICENSE_DAEMON_DIR)
#===============================================================================
export SC_INSTALL_DIR=/home/$USER/Microchip/SoftConsole-v2022.2-RISC-V-747
export LIBERO_INSTALL_DIR=/home/$USER/Microchip/Libero_SoC_v2023.2
export LICENSE_DAEMON_DIR=/home/$USER/Microchip/Linux_Licensing_Daemon
export LICENSE_FILE_DIR=/home/$USER/Microchip/license

#===============================================================================
# The following was tested on Ubuntu 20.04 with:
#   - Libero 2023.2
#   - SoftConsole 2022.2
#===============================================================================

#
# SoftConsole
#
export PATH=$PATH:$SC_INSTALL_DIR/riscv-unknown-elf-gcc/bin
export FPGENPROG=$LIBERO_INSTALL_DIR/Libero/bin64/fpgenprog

#
# Libero
#
export PATH=$PATH:$LIBERO_INSTALL_DIR/Libero/bin:$LIBERO_INSTALL_DIR/Libero/bin64
export PATH=$PATH:$LIBERO_INSTALL_DIR/Synplify/bin
export PATH=$PATH:$LIBERO_INSTALL_DIR/Model/modeltech/linuxacoem
export LOCALE=C
export LD_LIBRARY_PATH=/usr/lib/i386-linux-gnu:$LD_LIBRARY_PATH

#
# Libero License daemon
#
export LM_LICENSE_FILE=1702@localhost
export SNPSLMD_LICENSE_FILE=1702@localhost

$LICENSE_DAEMON_DIR/lmgrd -c $LICENSE_FILE_DIR/License.dat -l $LICENSE_FILE_DIR/license.log

Software Link to heading

In repo, the top script build-bitstream.py calls the flow to build both software then bitstream

virtualenv .venv
source .venv/bin/activate
python requests gitpython  pyyaml
python build-bitstream.py ./build-options/default.yaml

I will try to break down the steps done in the each of scripts

default.yaml Link to heading

Starting with the default.yaml passed to top script, where it has some configuration.

---
HSS:
    type: git
    link: https://openbeagle.org/beaglev-fire/hart-software-services.git
    branch: main-beaglev-fire
    board: bvf
gateware:
    type: sources
    unique-design-version: 2.0.4

MSS Link to heading

MSS is the microprocess sub-system in polarfire with riscv. mss_configurator is passed MSS_Configuration.cfg to generate memory and peripherals enabled in the design.

def make_mss_config(mss_configurator, config_file, output_dir):
    print("================================================================================")
    print("                          Generating MSS configuration")
    print("================================================================================\r\n", flush=True)
    cmd = mss_configurator + ' -GENERATE -CONFIGURATION_FILE:' + config_file + ' -OUTPUT_DIR:' + output_dir
    exe_sys_cmd(cmd)
    mss_config_file_path = os.path.join(gateware_top_dir, "sources", "MSS_Configuration", "MSS_Configuration.cfg")
    work_mss_dir = os.path.join("work", "MSS")
    make_mss_config(mss_configurator, mss_config_file_path, os.path.join(os.getcwd(), work_mss_dir))

A snippet from MSS_Configuration.cfg looks something as follows:

BANK2_VOLTAGE                                       3.3
BANK4_VOLTAGE                                       1.8
BANK5_VOLTAGE                                       3.3
CAN_0                                               FABRIC
CAN_0_TX_EBL_N                                      FABRIC
CAN_1                                               FABRIC
CAN_1_TX_EBL_N                                      FABRIC
CAN_CLK_FREQ                                        8
CAN_CLK_SOURCE                                      MSS_PLL
CORE_UP                                             UNUSED
CRYPTO                                              UNUSED
CRYPTO_DLL_JITTER_TOLERANCE                         MEDIUM_LOW
CRYPTO_ENABLE_ALARM                                 false
CRYPTO_ENABLE_BUSERROR                              false
CRYPTO_ENABLE_BUSY                                  true
...
...

HSS Link to heading

HSS is the bootloader (Firmware to boot MSS) to init peripherals and start up payload. make_hss eventually generates hex files

    make_hss(sources["HSS"], yaml_input_file)
# Builds the HSS using a pre-defined config file using SoftConsole in headless mode
def make_hss(hss_source, yaml_input_file):
    print("================================================================================")
    print("                       Build Hart Software Services (HSS)")
    print("================================================================================\r\n", flush=True)

    cwd = os.getcwd()
    print("debug: cwd : ", cwd)

    # Retrieve build target info from YAML file
    with open(yaml_input_file) as f:  # open the yaml file passed as an arg
        data = yaml.load(f, Loader=yaml.FullLoader)
        try:
            target_board = data.get("HSS").get("board")
        except:
            target_board = "bvf"
        f.close()
    print("Target board: " + target_board)

    # Update XML in HSS project
    XML_file = "boards/" + target_board + "/soc_fpga_design/xml/PF_SOC_MSS_mss_cfg.xml"
    XML_file_abs_path = os.path.join(hss_source, XML_file)
    try:
        os.remove(XML_file_abs_path)
    except:
        print("HSS target board does not have a default MSS XML configuration - not a problem.", flush=True)

    shutil.copyfile("./work/MSS/PF_SOC_MSS_mss_cfg.xml", XML_file_abs_path)

    # Select HSS configuration to build
    def_config_file = os.path.join(hss_source, "boards/" + target_board + "/def_config")
    shutil.copyfile(def_config_file, os.path.join(hss_source, "./.config"))

    # Call HSS makefile
    initial_directory = os.getcwd()
    os.chdir(hss_source)
    make_command = "make BOARD=" + target_board
    exe_sys_cmd(make_command)
    os.chdir(initial_directory)

    # Check build was successful and copy the build artifact to the output directory
    generated_hex_file = "./sources/HSS/Default/bootmode1/hss-envm-wrapper-bm1-p0.hex"
    if os.path.isfile(generated_hex_file):
        shutil.copyfile(generated_hex_file, "./work/HSS/hss-envm-wrapper-bm1-p0.hex")
    else:
        print("!!! Error: Hart Soft Service build failed !!!", flush=True)
        exit()

Libero Link to heading

This call to libero passing the tcl file and the hex generated from HSS.

    fpga_design_sources_path = os.path.join(gateware_top_dir, "sources", "FPGA-design")
    generate_libero_project(libero, yaml_input_file, fpga_design_sources_path, build_dir)
def generate_libero_project(libero, yaml_input_file, fpga_design_sources_path, build_dir_path):
    print("================================================================================")
    print("                            Generate Libero project")
    print("================================================================================\r\n", flush=True)
    # Execute the Libero TCL script used to create the Libero design
    initial_directory = os.getcwd()
    os.chdir(fpga_design_sources_path)
    project_location = os.path.join("..", "..", build_dir_path, "work", "libero")
    script = "BUILD_BVF_GATEWARE.tcl"

    script_args = get_libero_script_args(yaml_input_file)
    design_version = get_design_version(yaml_input_file)

    hss_image_location = os.path.join("..", "..", "work", "HSS", "hss-envm-wrapper-bm1-p0.hex")
    prog_export_path = os.path.join("..", "..", build_dir_path)

    top_level_name = get_top_level_name()
    print("top level name: ", top_level_name)

    call_libero(libero, script, script_args, project_location, hss_image_location, prog_export_path, top_level_name, design_version)
    os.chdir(initial_directory)

Hardware Link to heading

The next steps describe the important file (including verilog) to build fpga bitstream.

Specifications Link to heading

PolarFire SoC MSS Technical Reference Manual 9

How MSS and Fabric Talk Link to heading

In MSS simulation guide 8, Describes Fabric interface controller (FIC) as a way to address peripherals and memory in polarfire.

blinky Link to heading

Starting with template directory with gateware directory that can be used to build the fpga bitstream.

./sources/FPGA-design/script_support/components/CAPE/ROBOTICS/HDL/apb_rotary_enc.v
./sources/FPGA-design/script_support/components/CAPE/ROBOTICS/HDL/debounce.v
./sources/FPGA-design/script_support/components/CAPE/ROBOTICS/HDL/rotary_encoder.v
./sources/FPGA-design/script_support/components/CAPE/ROBOTICS/HDL/servos.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TEMPLATE/HDL/apb_ctrl_status.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TEMPLATE/HDL/CAPE.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TEMPLATE/HDL/P8_IOPADS.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TEMPLATE/HDL/P9_11_18_IOPADS.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TEMPLATE/HDL/P9_21_31_IOPADS.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TEMPLATE/HDL/P9_41_42_IOPADS.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TUTORIAL/HDL/apb_ctrl_status.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TUTORIAL/HDL/blinky.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TUTORIAL/HDL/CAPE.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TUTORIAL/HDL/P8_IOPADS.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TUTORIAL/HDL/P9_11_18_IOPADS.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TUTORIAL/HDL/P9_21_31_IOPADS.v
./sources/FPGA-design/script_support/components/CAPE/VERILOG_TUTORIAL/HDL/P9_41_42_IOPADS.v
./sources/FPGA-design/script_support/HDL/APB_arbiter/apb_arbiter.v
./sources/FPGA-design/script_support/HDL/AXI4_address_shim/AXI4_address_shim.v
./sources/FPGA-design/script_support/HDL/MIV_IHC/MIV_IHCC/miv_ihcc_ctrl.v
./sources/FPGA-design/script_support/HDL/MIV_IHC/MIV_IHCC/miv_ihcc_irqs.v
./sources/FPGA-design/script_support/HDL/MIV_IHC/MIV_IHCC/miv_ihcc_mem.v
./sources/FPGA-design/script_support/HDL/MIV_IHC/MIV_IHCC/miv_ihcc.v
./sources/FPGA-design/script_support/HDL/MIV_IHC/MIV_IHCIA/miv_ihcia.v
./sources/FPGA-design/script_support/HDL/XCVR_LOOPBACK/pattern_chk.v
./sources/FPGA-design/script_support/HDL/XCVR_LOOPBACK/pattern_gen.v
./sources/FPGA-design/script_support/HDL/XCVR_LOOPBACK/startup.v

The interesting one is blinky as it has top level user logic

repos/gateware/sources/FPGA-design/script_support/components/CAPE/VERILOG_TUTORIAL/HDL/blinky.v

`timescale 1ns/100ps
module blinky(
   input    clk,
   input    resetn,
   output   blink
   );

   
   reg [22:0] counter;
   
   assign blink = counter[22];

   always@(posedge clk or negedge resetn)
   begin
      if(~resetn)
         begin
            counter <= 16'h0000;
         end
      else
         begin
            counter <= counter + 1;
         end
   end
endmodule

The CAPE.v is the top level for bitstream that instantiates blinky module with pads to conntect it to external pins.

gateware/sources/FPGA-design/script_support/components/CAPE/VERILOG_TUTORIAL/HDL/CAPE.v

blinky blinky_0(
        .clk     ( PCLK ),
        .resetn  ( PRESETN ),
        .blink   ( BLINK )
        );
   
   assign GPIO_OUT_net_0 = { 16'h0000 , GPIO_OUT[27:6], BLINK, GPIO_OUT[4:0] };

P8_IOPADS P8_IOPADS_0(
        // Inputs
        .GPIO_OE  ( GPIO_OE_net_0 ),
        .GPIO_OUT ( GPIO_OUT_net_0 ),
        // Outputs
        .GPIO_IN  ( GPIO_IN_net_2 ),
        // Inouts
        .P8_3     ( P8_3 ),
        .P8_4     ( P8_4 ),
        .P8_5     ( P8_5 ),
        .P8_6     ( P8_6 ),
        .P8_7     ( P8_7 ),
        .P8_8     ( P8_8 ),

There was apb controller to interface with processing unit.

apb_ctrl_status apb_ctrl_status_0(
        // Inputs
        .presetn ( PRESETN ),
        .pclk    ( PCLK ),
        .psel    ( APB_SLAVE_SLAVE_PSEL ),
        .penable ( APB_SLAVE_SLAVE_PENABLE ),
        .pwrite  ( APB_SLAVE_SLAVE_PWRITE ),
        .paddr   ( APB_SLAVE_SLAVE_PADDR_0 ),
        .pwdata  ( APB_SLAVE_SLAVE_PWDATA ),
        .status  ( apb_ctrl_status_0_control ),
        // Outputs
        .prdata  ( APB_SLAVE_PRDATA ),
        .control ( apb_ctrl_status_0_control ) 
        );

Logging into the apb_ctrl_status where it has register read/write logic.

repos/gateware/sources/FPGA-design/script_support/components/CAPE/VERILOG_TEMPLATE/HDL/apb_ctrl_status.v

   reg [31:0] control_value;
   
   wire rd_enable;
   wire wr_enable;  

   assign wr_enable = (penable && pwrite && psel);
   assign rd_enable = (!pwrite && psel);

   always@(posedge pclk or negedge presetn)
   begin
      if(~presetn)
         begin
            prdata <= 'b0;
            control_value <= 32'h00000000;
            control <= 32'h00000000;
         end
      else
         begin
            case(paddr[7:0])
               STATUS:
                  begin
                    if (rd_enable)
                        begin
                           prdata <= status;
                        end
                  end
               CONTROL_0:
                  begin
                    if (rd_enable)
                        begin
                           prdata <= 32'hdeadbeef;
                           control <= 32'hdeadbeef;
                        end
                  end
                CONTROL_1:
                    begin
                      if (rd_enable)
                        begin
                            prdata <= control_value;
                            control <= control_value;
                        end
                      else if (wr_enable)
                        begin
                            control_value <= pwdata;
                        end
                    end  
               default:
                  begin
                     prdata <= 32'b0;
                  end
            endcase
         end
   end
endmodule

TCL Link to heading

To link this that to the libero tcl files, ADD_CAPE.tcl is sourced based on cape_option repos/gateware/sources/FPGA-design/script_support/components/CAPE/VERILOG_TUTORIAL/ADD_CAPE.tcl

create_hdl_core -file $project_dir/hdl/CAPE.v -module {CAPE} -library {work} -package {}

hdl_core_add_bif -hdl_core_name {CAPE} -bif_definition {APB:AMBA:AMBA2:slave} -bif_name {BIF_1} -signal_map {} 
hdl_core_assign_bif_signal -hdl_core_name {CAPE} -bif_name {BIF_1} -bif_signal_name {PADDR} -core_signal_name {APB_SLAVE_SLAVE_PADDR} 
hdl_core_assign_bif_signal -hdl_core_name {CAPE} -bif_name {BIF_1} -bif_signal_name {PSELx} -core_signal_name {APB_SLAVE_SLAVE_PSEL} 
hdl_core_assign_bif_signal -hdl_core_name {CAPE} -bif_name {BIF_1} -bif_signal_name {PENABLE} -core_signal_name {APB_SLAVE_SLAVE_PENABLE} 
hdl_core_assign_bif_signal -hdl_core_name {CAPE} -bif_name {BIF_1} -bif_signal_name {PWRITE} -core_signal_name {APB_SLAVE_SLAVE_PWRITE} 
hdl_core_assign_bif_signal -hdl_core_name {CAPE} -bif_name {BIF_1} -bif_signal_name {PRDATA} -core_signal_name {APB_SLAVE_SLAVE_PRDATA} 
hdl_core_assign_bif_signal -hdl_core_name {CAPE} -bif_name {BIF_1} -bif_signal_name {PWDATA} -core_signal_name {APB_SLAVE_SLAVE_PWDATA} 
hdl_core_rename_bif -hdl_core_name {CAPE} -current_bif_name {BIF_1} -new_bif_name {APB_TARGET} 

The file is parsed from the yaml file. In this case, it’s VERILOG_TUTORIAL

repos/gateware/sources/FPGA-design/script_support/components/BVF_GATEWARE.tcl

source script_support/components/CAPE/$cape_option/ADD_CAPE.tcl 

From repos/gateware/custom-fpga-design/my_custom_fpga_design.yaml

---
HSS:
    type: git
    link: https://openbeagle.org/beaglev-fire/hart-software-services.git
    branch: main-beaglev-fire
    board: bvf
gateware:
    type: sources
    build-args: "M2_OPTION:NONE CAPE_OPTION:VERILOG_TUTORIAL"

From the top, repos/gateware/sources/FPGA-design/BUILD_BVF_GATEWARE.tcl

safe_source ./script_support/B_V_F_recursive.tcl

repos/gateware/sources/FPGA-design/script_support/B_V_F_recursive.tcl

::safe_source script_support/components/BVF_GATEWARE.tcl