(chp:makesys)= # The Build System **Gundolf Kiefer, 2025** ``` ************************************************** This chapter is under construction! ************************************************** ``` (sec:makesys:intro)= ## Overview The *PicoNut* build system is based on GNU make and provides the following features: - support for hardware and (RISC-V) software - support for hardware simulation and synthesis - support for alternative synthesis toolchains - support for internal builds and externally built systems by means of a *build context* - support for automated verification, CI/CD - support for integrated documentation (`help` target) - modular, only necessary parts rebuilt - highly parallel operation, dependency-driven for fast build cycles - prepared for automatic rule checking, e.g. to identify installation conflicts, missing *verify* targets Improvements over the previous build system (in use until 09/2025) include: - Support for external modules and fast builds using prebuilt libraries - Cleaned up directory structure - Avoidance of unnecessary dependencies and duplicated code - Naming conventions: Prefix PN_/pn_ for anything related to PicoNut - fewer make targets - fewer make variables to learn, explicit rules - brief outputs - fewer .mk files - configuration: fewer & clearly documented locations - Sanity: Explicit check for anything leading to non-obvious errors (sec:makesys:concepts)= ## General Concepts ### Modules The *PicoNut* project is divided into modules. A *module* is a unit with its own Makefile that can be built by itself. A *module* may be *internal* as part of the *PicoNut* repository or *external* as part of a user project which uses *PicoNut*. External modules are not part of the *PicoNut* project. Each module is identified by a unique identifier, which is available as a variable `PN_MODULE_ID` in any *PicoNut* Makfile. For internal modules, the ID is equal to the relative path inside the *PicoNut* source tree (`PN_SOURCE_DIR`). For external modules, the ID may be provided by the user or be set automatically to the absolute path inside the user's file system (without a leading "/"). The term *module name* (variable `PN_MODULE_NAME`) reflects the last component of the module ID, which is also the exact name of the containing directory. Modules may have *submodules*. These are modules with their own Makefile. Submodules may be subcomponents (e.g. the ALU of a CPU), *alternatives* (e.g. *Nucleus* variants as submodules as in `hw/cpu/nucleus` or `hw/cpu/membrana` ) or have any other meaning at the responsibilty of the base module developer. In any case, submodules are reflected by sudirectories with the same name as the respective submodule. Subdirectories can also be freely used at the responsibilty of the module developer. It is not required that subdirectories represent submodules. ### Target Technologies (Techs) Hardware modules may be built for simulation or for synthesis for a certain FPGA or ASIC technology. These different general targets are referred to as *target technologies* or - shortened - *techs* (build argument `TECHS`). Presently, the following techs are defined: * `sim`: Simulation * `syn`: Synthesis for various physical (FPGA) technologies In the future, more techs may be added. ### Directories The build system operates relies on a clearly defined directory structure and trees as defined by the following subsections. #### The *Source Tree* (`PN_SOURCE_DIR`) This is the directory tree obtained after cloning the *PicoNut* repository, and it contains all source files. The source tree is to be considered read-only, in particular, no build artefacts are allowed to be written to it. Exceptions must have a good reason and must be documented. Otherwise, writing into the source tree is considered to be a bug. The subdirectory `build` is reserved, since some users may want to use `$(PN_SOURCE_DIR)/build` as the build tree directory (see below). **Note:** The presence of a source tree is optional. In particular, externally built systems may use an installation directory instead. #### The *Build Tree* (`PN_BUILD_DIR`) The *build tree* contains all files (final or temporary) generated during the build process. For each module, the build system assigns a unique, module-specific subdirectory to the module by means of the variable `PN_MODULE_BUILD_DIR`. Any outputs must be directed to `PN_MODULE_BUILD_DIR` or a module-specific subdirectory. A module can use its build directory freely and, for example, create and use subdirectories as adequate. The only restriction to this is that the subdirectories must not have the same name as eventual submodules. If the module has submodules, the build directory of each submodule will be a `$(PN_MODULE_BUILD_DIR)/`. This may be used by the main module, e.g. to access header files or object files of a submodule directly without the necessity to export these files globally. The module build directory is specific to a certain context (e.g. system or global, see below). Hence, builds for different systems with potentially different configurations do not interfere with each other. #### The *Installation Tree* (`PN_INSTALLED_DIR`) The *installation directory* is the place where pre-compiled libraries or pre-synthesized modules together with the exported header files are placed. Purpose of a *PicoNut* installation is to allow building systems without a *PicoNut* source tree (external builds). In addition, builds of internal systems can be accelerated with the help of an installation. **Note:** The existence of an *installation directory* is optional. The location is given by the user by the environment variable `PICONUT`. Inside the build system, the installation directory must be accessed by the variable `PN_INSTALLED_DIR`, which is a normalized absolute path to the same directory. If no installation exists, `PN_INSTALLED_DIR` is empty. Recommended locations are: * `PICONUT = ~/.piconut` for a user-specific installation * `PICONUT = /opt/piconut` for a computer-wide installation #### The *System Directory* (`PN_SYSTEM_DIR`) This is the root of a system project using *PicoNut*. It must contain the following files: * `Makefile`: A *PicoNut*-compliant Makfile for the whole system. * `piconut-config.mk`: System-specific configuration The final outputs of the build process (target `stage`) are then written into: * `piconut/`: All *PicoNut* hardware and software outputs. #### The *Stage Tree* (`PN_STAGE_DIR`) The *stage tree* is a directory inside the build tree or the system directory with the same structure as an installation tree, which is used to collect all exported outputs during a recursive multi-module build process. All `install-*` targets defined in Makefiles actually write into the *stage tree*. Only the main `install` target actually writes into a real installation directory as specified by `PREFIX`. And it does so by updating the stage tree and then copying to `$(PREFIX)` For global builds (without a system context), the stage directory is contained in the build tree (`$(PN_BUILD_DIR/pub`). For system context, the stage directory is `(PN_SYSTEM_DIR)/piconut`. ### Build Context The build system may be used for very different use cases: * developing systems using *PicoNut* * developing *PicoNut* components as a *PicoNut* developer * developing on the *PicoNut* infrastructure as a *PicoNut* core developer Systems may have custom configurations. *PicoNut* developers typically work inside the *PicoNut* source tree. System developers may not. The *build context* refers to the system currently built for, or it may be *global*, if not system is defined. The build system provides a separation of build contexts by means context-specific module build directories (`PN_MODULE_BUILD_DIR`). Also, the behaviour of some targets may depend on the context in order to facilitate the typical use cases. For example, for systems, the stage directory is set to the system's `piconut` directory to have all outputs immediately available for the system. ### Layers and Dependencies To keep the growing ecosystem around the *PicoNut* project maintainable, strict modularity with clearly-defined dependencies between modules must be maintained. Each module is assigned to one of the following *layers*: #### Layers The *PicoNut* ecosystem is divided into layers, and any module is assigned to a specific layers. The following subsection describe the layers informally. Specific information on rules and Makefile considerations are given in the [section on writing Makefiles](sec:makesys:makefiles). ##### Layer 0: Configuration This layer comprises all auto-generated headers containing configuration parameter definitions. The configuration is updated automatically by means of make targets from layer 1. Normal modules (layer 2 and 3) do not need to take care of it. **Note:** The outputs contain variables to identify the version. Whenever the version changes, `make config-update` should be executed. ##### Layer 1: Common + Tools These are header files or elementary software libraries commonly used in any other modules. Common modules ensure that all build tools (from `tools`) are available and that `#include ` works properly. Common code is located inside `hw/common`, `sw/common` and `tools` of the *PicoNut* source tree. ##### Layer 2: Regular Components This layer comprises all regular components, typically hardware modules together with their software drivers. This includes: - `hw/cpu` (the CPU with all *Nucleus* and *Membrana* variants) - `hw/peripherals` (hardware models + software drivers) - GUI or other simulator components ##### Layer 3: Systems and Software Modules at this layer may be: a) Systems b) Software: libraries, applications, operating systems Both systems and software applications may also be external, i.e. not part of the *PicoNut* source tree. In general, layer-3 modules (systems or software) never access other components directly through the source tree or by targets in their *Makefiles*. Instead, they use *PicoNut* modules via an installation in a wider sense. This may either be a global installation (`$PICONUT`) or system-specific installation maintained inside `$(PN_SYSTEM_DIR)/piconut`. #### Dependency Rules *Dependencies* are allowed as specified by the following rules: 1. **A modules may depend on its own submodules.** The build system ensures that submodules are *staged* before the module itself is built. 2. **A module may depend on any modules of any lower layer.** The build system can be used to ensure that modules of layers 0 through 2 are prebuilt and staged before the module's submodules and the module itself are built. The section on [writing Makefiles](sec:makesys:makefiles) details on how to specify such dependencies for a module. 3. **Systems, software libraries and software applications located in layer 3 may depend arbitrarily on software libraries or applications.** The developer of a new or changed module is responsible to maintain the buildability of the whole project for all possible configurations. In particular, circular dependencies must be avoided. In general, dependencies between software modules should be kept minimal. In the future, a layer concept for the software infrastructure itself may be defined. 4. **Besides the cases mentioned so far, a module must not depend on any other modules.** 5. **Sub-targets for a certain technology (`TECHS`) must not depend on sub-targets for other technologies - neither of the same module nor of any (legally) dependent module.** In particular, synthesis techs require certain, sometimes complex (vendor-specific) tools. This rule is to avoid dependencies on such tool suites if a user does not really want to use it. 6. **Software modules of layer 3 are not allowed to use hardware-specific configuration variables (`PN_CFG_*`, layer 0).** Such software must be portable. Any hardware-specific software must be encapsulated in a driver (layer 2) or a common module (layer 1). #### Examples 1. **(Hardware) Components like CPU components or peripheral hardware including their software drivers** are assigned to layer 2. They may use anything from `hw/common` or `sw/common` (layer 1). 2. **Systems** (layer 3) may use any hardware components of layer 2. Their software may be part of the system itself, but may also be a standard application located in `sw/applications`. Hence, the system may depend on software libraries and applications. 3. **Software projects** (layer 3) may use a smaller or larger set of software libraries. Hence, irregular dependencies are allowed for hardware-independent software. 4. For **common modules** (layer 0) special rules apply. They comprise only a small part of the code base, and their build times and memory footprints should be kept minimal. Developer working on such modules should carefully inform themselves about the rules that apply. Also, they should be aware that even small changes may quickly break the whole build system. (sec:makesys:using)= ## Using the Build System ### Overview Each module directory contains a Makefile capable of building, verifying and installing the module (and its submodules). They may also implement additional, module-specific targets. The set of targets and command-line variables can be viewed by running ``` $ make help ``` in the module source directory. ### When is it necessary to clean? The built system automatically detects changes in pure source code files and rebuilts parts as necessary. However, the following changes generally require a manual cleanup (`make clean`) of the relevant module(s): - Makefile is modified, e.g. new/removed source files - other meta files are changed - config/build parameters are changed (exception: `PN_CFG_*`) - command line parameters (e.g. `DEBUG`) of the make invocation are changed (sec:makesys:makefiles)= ## Writing Makefiles This section describes the general structure of a module Makefile, variables, rules and targets provided by the build system and the targets that may or must be defined by the Makefile. (sec:makesys:templates)= ### Starting Point for New Modules In many cases, an existing Makefile can just be reused and adapted for a new module. The following modules have well-documented Makefiles that can be used as templates to start with: * `hw/peripherals/uart` for a peripheral module * `hw/cpu` for a hardware module with submodules * `hw/cpu/membrana/membrana_hw` for a hardware module with parts written in Verilog * `sw/applications/hello_piconut` for a software module (sec:makesys:makefiles:structure)= ### General Structure of a Makefile Each Makefile has the following general structure: ``` # Rules for tech A (e.g. simulation) ... ... # Rules for tech B (e.g. synthesis, optional) ... ... # Rules for software A (optional) ... ... # Global targets ... ``` The following subsections give details on the parts mentioned above. #### Build Configuration Refers to: `` The header section ... 1. may set some variables that define general properties of the module (the pre-configuration), 2. then includes the *PicoNut* build system, 3. then sets some general variables and rules affecting the build system in general (the post-configuration) Inclusion of the *PicoNut* build system happens with a single line like: `include ../../piconut.mk` (relative path to `piconut.mk` in the source root directory) for internal modules. External modules not part of the *PicoNut* repository include the file by: `include $(PICONUT)/piconut.mk` where PICONUT is an environment variable pointing to the *PicoNut* installation. In the **pre-configuration** section, the following optional variables may be set: * `PN_SUBMODULES`: list of relative paths to submodules. These will be built by the build system before their respective parent modules. Submodules are contained in subdirectories of the module. However, not every subdirectory must represent a submodule. * `PN_BUILD_COMMON`: build the common and config part first. This defaults to 1, which is adequate for all layer-2 modules as well as internal systems and software of layer 3. Common modules must set it to 0. External modules should set it to 0 to not depend on the *PicoNut* source tree. (Default: 1) * `PN_BUILD_HW`: If set (= 1), automatic rules for building hardware are activated. (Default: 1) * `PN_BUILD_SW`: If set (= 1), automatic rules for building software are activated. (Default: 1) By default, C++ source files (.cpp) are assumed to by hardware (SystemC) code, and C sources are treated as software (for RISC-V). Software modules using C++ may set `PN_BUILD_HW = 0` to clarify that C++ sources are not meant to be build for the host system. In the **post-configuration** section, the automatic `build-prepare:` target may be supplied with additional dependencies or a recipe to be executed before the build process starts. This is used [to declare dependencies on modules at lower layers](sec:makesys:makefiles:layers). Also, any user-specific variables may be set in the post-configuration section. (sec:makesys:makefiles:layers)= #### Layer-Specific Options ``` ************************************************** This section is under construction! ************************************************** ``` TBD: Introduce PN_BUILD_COMMON and the default setting towards layer 2- 4. Any changes to the configuration or a Makefile requires a cleanup before the next build (`make clean`) ##### Layer 0: Configuration This layer comprises all auto-generated headers containing configuration parameter definitions. The configuration is updated automatically by means of make targets from layer 1. Normal modules (layer 2 and 3) do not need to take care of it. Internal target(s): - `config-build` (equivalent to `config-install` to `PN_STAGE_DIR`) - `config-install` Outputs: - `(hw|sw)/include/piconut-config.h` Depends on (as available): - `$(PN_SOURCE_DIR)/piconut-config.mk` - `$(PN_INSTALLED_DIR)/piconut-config.mk` - `$(PN_SYSTEM_DIR)/piconut-config.mk` **Note:** The outputs contain variables to identify the version. Whenever the version changes, `make config-update` should be executed. ##### Layer 1: Common + Tools These are header files or elementary software libraries commonly used in any other modules. Common modules ensure that all build tools (from `tools`) are available outside and that `#include ` works properly. Common code must be located inside - `hw/common`, - `sw/common` and - `tools`, respectively. For common code, the following rules apply: - `#include ` is not allowed. Instead, `#include "[/]piconut_base.h"`may be used to have the basic subset of definitions and the configuration variables available. - It is strongly discouraged to use configutation variables to avoid frequent rebuilds A layer-1 module (common + tools) should add the following rule to its Makefile to ensure that layer 0 is up-to-date in the current build tree: ``` build-prepare: prepare-config ``` Internal target(s) (for developers): - `common-build` - `common-install` ##### Layer 2: Regular Components This layer comprises all regular components, typically hardware modules together with their software drivers. This includes: - `hw/cpu` (the CPU with all *Nucleus* and *Membrana* variants) - `hw/peripherals` (hardware models + software drivers) - GUI or other simulator components A layer-2 module (*PicoNut* component) should add the following rule to its Makefile to ensure that layers 0 and 1 are up-to-date in the current build tree: ``` build-prepare: prepare-common ``` This line should be added to any Makefile of a layer-2 module, including submodules. The allows to (re)build submodules independently. To avoid that the common parts are built multiple times during recursive make invocations (i.e. for submodules), the above rule only performs a build in the top-most recursion layer (`$(MAKELEVEL) == 0`. If this does not work properly, for example, if the *PicoNut* build system is invoked as a recursive build from a third-party Makefile, the option `PN_BUILD_COMMON=1` may be added to the command line. ##### Layer 3: Systems and Software Modules at this layer may be: a) Systems - may have own configuration - have their own tree inside `PN_BUILD_DIR` - target 'install' installs into the system directory by default: PREFIX ?= $(PN_SYSTEM_DIR)/piconut b) Software: libraries, applications, operating systems - must not depend on a system - may use drivers from layer 2 (components) - must not access configuration variables (may be accessed by driver functions) - may have irregular dependencies on other libraries (or apps): these may be expressed by make rules Both systems and software applications may also be external, i.e. not part of the *PicoNut* source tree. In general, layer-3 modules (systems or software) never access other components directly through the source tree. Instead, they are accessed via an installation, either globally (`$PICONUT`) or maintained inside the system directory (`$(PN_SYSTEM_DIR)/piconut`). For a system or software, there are multiple options, depending on the setup of the system/software developer: ###### a) *PicoNut* installation exists, no non-default compile-time options selected This is a typical case for external development of a system or software, which is not part of the *PicoNut* repository, and the developer only uses *PicoNut*. In this case, no rule needs be added to the Makefile, but the developer is responsible for providing a *PicoNut* installation. ###### b) No *PicoNut* installation exists or non-default compile-time options are set In this case, all or a subset of *PicoNut* modules are built together with the system in its own build tree. To this end, the following rule must be added to the respective system Makefile: ``` build-prepare: $(PN_PREBUILD) MODULES= ``` If no installation exists, *all* modules used by the system must be added to the `MODULES` argument. **Note:** The user is responsible to carefully select all components used by the system and affected by the custom configuration. Missing out modules may lead to hard to detect problems. If the system has submodules, the above lines should be added to all (sub)modules, where the `MODULES` arguments only cover those modules directly required by the respective (sub)module ###### c) Software Applications or Libraries To allow local builds, software modules may add a rule to require the common modules: ``` build-prepare: prepare-common $(PN_PREBUILD) MODULES= ``` #### Automatic Recipes for Compilation and Linking Refers to: `` ##### For Hardware (simulation) For **compiling** hardware source files, the following rule is provided by the build system: - C++: `%.cpp` -> `$(PN_MODULE_BUILD_DIR)/sim/%.o` Input files are detected by their suffix. Outputs are automatically written into `$(PN_MODULE_BUILD_DIR)/sim`. The list of all object files (for linking/archiving) can be obtained from the list of source by an assignment like: ``` MODULE_OBJ := $(MODULE_SRC:%.cpp=$(PN_MODULE_BUILD_DIR)/sim/%.o) ``` Build dependencies are maintained automatically in `.d` files that must be included by a line like: ``` -include $(MODULE_OBJ:%.o=%.d) ``` For **linking** hardware object files to an executable, automatic recipes are defined for the following suffixes: * Testbench executable: `$(PN_MODULE_BUILD_DIR)/sim/%_tb` * Simulator: `$(PN_MODULE_BUILD_DIR)/sim/%_sim` To activate the recipe, a rule declaring the underlying object files must be declared, for example: ``` $(PN_MODULE_BUILD_DIR)/sim/mymodule_tb: $(MYMODULE_OBJ) ``` Similarly, an automatic recipe exists for building an **archive /library)** based on the following name template: * Archive: `$(PN_MODULE_BUILD_DIR)/sim/%.a` Usage example: ``` $(PN_MODULE_BUILD_DIR)/sim/libmymodule.a: $(MYMODULE_OBJ) ``` **Custom flags** for compilation or linking can be added by: ``` PN_HW_CFLAGS += PN_HW_LDFLAGS += ``` In general, all search paths are set by the build system, and the same holds for optimization options (based on the global `DEBUG` parameter). Hence, custom flags should be avoided. However, used libraries must always be declared explicitly by extending `PN_HW_LDFLAGS`. **Note:** All libraries used by the a module - including those generated by submodules - must be declared explicitly by extending `PN_HW_LDFLAGS`. For example (in module `hw/cpu`): ``` PN_HW_LDFLAGS += -lnucleus_ref -lmembrana_soft ``` ##### For Hardware (synthesis) TBD ##### For Software (RISC-V Cross-Compilation) For **compiling** software (RISC-V) source files, the following rules are provided by the build system: - C: `%.cpp` -> `$(PN_MODULE_BUILD_DIR)/%.o` - Assembler: `$.S` -> `$(PN_MODULE_BUILD_DIR)/%.o` Input files are detected by their suffix. Outputs are automatically written into `$(PN_MODULE_BUILD_DIR)`. The list of all object files (for linking/archiving) can be obtained from the list of source by an assignment like: ``` MODULE_OBJ := $(MODULE_SRC:%.cpp=$(PN_MODULE_BUILD_DIR)/%.o) ``` Build dependencies are maintained automatically in `.d` files that must be included by a line like: ``` -include $(MODULE_OBJ:%.o=%.d) ``` For **linking** object files to an executable, an automatic recipe is defined for the `.elf` suffix: * RISC-V executable: `$(PN_MODULE_BUILD_DIR)/%.elf` To activate the recipe, a rule declaring the underlying object files must be declared, for example: ``` $(PN_MODULE_BUILD_DIR)/myapp.elf: $(MYAPP_OBJ) ``` Similarly, an automatic recipe exists for building an **archive /library)** based on the following name template: * Archive: `$(PN_MODULE_BUILD_DIR)/%.a` Usage example: ``` $(PN_MODULE_BUILD_DIR)/libmyapp.a: $(MYAPP_OBJ) ``` **Custom flags** for compilation or linking can be added by: ``` PN_SW_CFLAGS += PN_SW_LDFLAGS += ``` In general, all search paths are set by the build system, and the same holds for optimization options (based on the global `DEBUG` parameter). Hence, custom flags should be avoided. However, used libraries must always be declared explicitly by extending `PN_SW_LDFLAGS`. **Note:** All libraries used by the a module - including those generated by submodules - must be declared explicitly by extending `PN_SW_LDFLAGS`. ##### Search Order The automatic recipes make sure that header files, libraries and synthesis artefacts are searched for in the following location in this order of precedence: 1. `PN_BUILD_DIR/pub` 2. `PN_SYSTEM_DIR` 3. `PN_INSTALLED_DIR` Inside each of these locations, the following sub-paths are used: * `hw/include` : headers for hardware modules * `hw/lib` : libraries for hardware simulation * `hw/syn` : synthesis artefacts for hardware modules * `sw/include` : headers for software (RISC-V) modules * `sw/lib` : libraries for software (RISC-V) modules * `sw/lib` : software binaries (for the RISC-V architecture) #### Global Module Targets Refers to: `` The following targets may be provided by the Makefile. They will be picked up and used by the main targets `build`, `stage`, `verify` and `install`, which are defined and implemented in the build system. Unless specified otherwise, the targets are optional. If missing, they do not produce any outputs. * `build-all` : Build everything which is not dependent on a certain TECHS argument, e.g. RISC-V software. * `build-sim` : Build all artefacts for simulation, typically the library and testbench executable. * `build-syn` : Build all synthesis artefacts. * `verify-sim` / `verify-sim` / `verify-syn` : Verify the outputs of the respective `build-*` target. * `install-all` : Stage everything which does not depend on a certain TECHS argument. In the case of hardware, header files should be installed here. * `install-sim` : Stage all simulation artefacts. * `install-syn` : Stage all synthesis artefacts. #### Installation Macros Inside the `install-*` recipes (see previous section), the following commands *must* be used. It is not allowed to write into the target directory in another way. They make sure that the files are installed into the right directory with the correct permissions: * `$(PN_INSTALL_HW_INCLUDE) ` : Install hardware header(s) * `$(PN_INSTALL_HW_LIB) ` : Install hardware simulation library/libraries * `$(PN_INSTALL_HW_BIN) ` : Install a simulator * `$(PN_INSTALL_HW_SYN) ` : Install a synthesis artefact * `$(PN_INSTALL_SW_INCLUDE) ` : Install software headers * `$(PN_INSTALL_SW_LIB) ` : Install a software library/libraries * `$(PN_INSTALL_SW_BIN) ` : Install a software binary * `$(PN_INSTALL_TOOL_BIN) ` : Install a tool (from `tools/*` only) * `$(PN_INSTALL_TOOL_LIB) ` : Install a tool library (from `tools/*` only) ### Variables ``` ************************************************** This section is under construction! ************************************************** ``` #### Module Properties * `$(PN_MODULE_BUILD_DIR)` : Build directory for the current module. All generated files must go there! * `$(PN_MODULE_ID)` : Module ID. For an internal module, this is the relative path below `$(PN_SOURCE_DIR)`. For external module, this is the absolute path in the file system without a leading `/`. * `$(PN_MODULE_NAME)` : Last path component of `$(PN_MODULE_ID)` #### Directories * `$(PN_SOURCE_DIR)` : Source directory root (or empty, if not defined) * `$(PN_BUILD_DIR)` : Build directory root * `$(PN_SYSTEM_DIR)` : Root of System directory (or empty, if no system context is used) * `$(PN_INSTALLED_DIR)` : Root of an existing *PicoNut* installation (or empty, if not available) #### TBD ### How the build system works #### Automatic rules - `build`, `verify` and `install` automatically call the same target for their direct submodules first. - Only `build` depends on targets of layers <= 1. Installation and verification is controlled by the main Makefile. - `verify-(sim|syn)` and `install-(sim|syn)` automatically depend on their respective `build-*` targets, but not on anything of a lower layer. #### Manual rules To ensure that all modules of lower layers are built correctly, manual dependencies for `build-prepare` as described in the [section on dependency layers](sec:makesys:deplayers). ### Internal notes (TBD) - `build-prepare`: Phony target, by default empty, all dependencies are resolved before any of the modules's `build-submodules` and `build-` targets are invoked - components (layer 2) have dependency `build-prepare: build-common` Layer build preparation: - How to prepare PN_BUILD_DIR for a build process in layer n. - layer 0: (nothing to do) - layer 1: depend on `prepare-config` (== `config`) - layer 2: build and export layer 1 by recursive make invocations, target: `prepare-common` -> `build-prepare` depends on `build-common`, which does it all - layer 3: in MAKELEVEL == 0: build parts of layer 2 (system/software makefile must name the modules explicitly) - -> PN_REBUILD_MODULES (sec:makesys:config)= ## Configuration parameters ``` ************************************************** This section is under construction! ************************************************** ``` ### General considerations 1. All configuration parameters should be visible in hardware and software. 2. **A configuration parameter always relates to** - a) **a module** (identified by its relative directory path) - b) **a group of modules**, e.g. all Nuclei or all Membranas (-> identified by their common containing directory, e.g. `hw/cpus/nuclei`) - c) **a system** (e.g. memory layout, clock frequency) 3. Configuration parameters are typically **set in (just) three places**: - a) **inside the respective module itself** (= default for common use) - b) **in the build system** (= default for common use) - c) **inside the system** for system-secific settings 5. Configuration parameters mainly control the hardware. Software may just want to read/know them, mainly for compile-time optimizations/variations. 6. As far as possible, hardware parameters should remain variable at runtime (simulation) or synthesis time (synthesis). 7. As far as possible, hardware parameters should be accessible to software at runtime, e.g. by means of CSRs (CPU) or version/feature registers (peripherals). ### Rules and conventions #### a) Module-Level 1. Module-related configuration parameters **are defined and documented in the respective module sources, typically inside C/C++ header files (.h)**. The documentation should be done via *Doxygen* as a section "Configuration Options" inside the module documentation. 2. All configuration variables must adhere to the naming conventions (see section on naming conventions, i.e. start with `PN_CFG_` ). 3. All definitions inside the module must allow them to be predefined by enclosing them with `#ifndef PN_CFG_...` and `#endif`. 4. Before using any configuration file in C/C++ code, the *PicoNut* header file must be included: ``` #include ``` **Notes:** 1. The way to define module-specific parameters ist not language-independent. In the future, this may be overcome by introducing an optional, per-module `piconut-config.mk` file. #### b) Global configuration 1. Configuration parameters not related to a particular module (typically system-level parameters) **are defined and documented with the build system**. At present, this happens in the file `$PN_SOURCE_DIR/piconut-config.mk`. 2. A global C header file containing all parameters set at installation time is auto-generated and included by `#include ` from any hardware or software source file. 3. Header/package files for other languages (e.g. VHDL) may follow in the future. They will also be auto-generated from `$PN_SOURCE_DIR/piconut-config.mk`. #### c) System configuration 1. Any configuration parameter may also be set/modified in a system project using the file `PN_SYSTEM_DIR/piconut-config.mk`. A standard makefile rule for systems will generate a C/C++ header inside `piconut/`. 2. The system makefile is responsible for (re-)building any *PicoNut* modules with system-specific parameters that do not match the global defaults. Such modules must be built and installed as follows: - `$ make build PN_SYSTEM=` - `$ make install PN_SYSTEM= PREFIX=/piconut` ### Naming conventions - All configuration parameters start with `PN_CFG_`. - `PN__`: Each module must have a unique name and name its parameters according to it. - `PN_CFG_CPU_` : Parameters related to a *PicoNut* core - `PN_CFG_SYS_`: System parameters (e.g. memory layout, system clock frequency) ### Related files * `$PN_SOURCE_DIR/piconut-config.mk`: Global configuration settings (human-readable, well-documented) * `$PN_SYSTEM_DIR/piconut-config.mk`: System-specific configuration settings (human-readable, well-documented), precedes over global configuration * `$PN_SOURCE_DIR/sw/common/piconut.h`: includes everything required for *PicoNut* software. Includes `` from the build directory. * `$PN_BUILD_DIR/(sw|hw)/include/piconut-config.h`: Build-related configuration, includes version. Is auto-created based on `$PN_SYSTEM_DIR/piconut-config.mk` (if present) and `$PN_SOURCE_DIR/piconut-config.mk` (sec:makesys:synthesis)= ## Synthesis ``` ************************************************** This section is under construction! ************************************************** ``` ### Possible synthesis outputs & installation exports - `.sv`: RTL SystemVerilog, hollow (without submodules, file may include .v/.sv files of submodules) - `.v`: RTL Verilog, solid (includes complete subtree) - `..json` / `..v` `..dcp`: Gate-level, technology-mapped netlist (board-specific) - `..bit` / `.dfu` : Bitfile for FPGA programming, module is a system **NOTE:** At present, the target hardware and technology is defined by `PN_BOARD` only. There are no variables to specify the device family, device or toolchain. All the three of them are implicitely defined by `PN_BOARD`. ### Phony targets **The following targets are provided by the build system. ** - `build-rtl`: Synthesize SystemC code (ICSC), translate everything into clean Verilog or VHDL - `build-netlist`: Run RTL synthesize to obtain a gate-level netlist - board-specific: depends on PN_CFG_BOARD - `build-layout`: Run place & route and, in case of FPGA technology, build a bitfile - system-specific: depends on PN_BOARD_CONTRAINTS - board-specific: depends on PN_BOARD **The following targets are recognized by the build system and should be defined in modules.** - `build-syn`: Build all synthesis targets ### Types of modules 1. **Simple Module** - no exchangable submodules - not reused in presynthesized way - Recommended Makefile section: - build-syn: (nothing) - verify-syn: build-netlist - run testbench - install-syn: (build-syn) - headers 2. **Reusable Module** - no exchangable submodules - reusable in presynthesized way - e.g.: nucleus, membrana, peripheral - Recommended Makefile section: - build-syn: build-netlist - verify-syn: build-netlist - run testbench - install-syn: (build-syn) - headers - .v - ..(v|json|dcp) 3. **Super-Module / Hollow Module** - with exchangable submodules ("blackbox") - not reused in presynthesized way - e.g. cpu, system - Recommended Makefile section: - build-syn: build-rtl - verify-syn: build-netlist - run testbench with a default set of submodules - install-syn: (build-syn) - headers - .sv for the default config: - - .v - ..(v|json|dcp) (sec:makesys:naming)= ## Naming and Coding Conventions ``` ************************************************** This section is under construction! ************************************************** ``` 1. All symbols that may go into `PN_INSTALLED_DIR` must be prefixed by `PN_` or `pn_`. Legal exceptions are: * purely internal symbols (e.g. internal modules of a Nucleus) * Makefile symbols that directly affect the build process and nothing else, i.e. are not visible and used in the source code: PREFIX, VERBOSE, CHECK (but *not* PN_DEBUG, which is visible in the source code) 2. ... (sec:makesys:directories)= ## Directory layouts ``` ************************************************** This section is under construction! ************************************************** ``` ### The *Source Tree* (`PN_SOURCE_DIR`) ### The *Build Tree* (`PN_BUILD_DIR`) #### General Considerations - The build directory `PN_BUILD_DIR` reflects the directory hierarchy of the source tree (`PN_SOURCE_DIR`). - All build artefacts must go into `PN_MODULE_BUILD_DIR`. The module is responsible and free to use this directory as needed. - The subdirectory `pub/` is reserved; build artefacts of submodules are "installed" here during the build process. The internal organization of `pub/` is the same as in `PN_INSTALLED_DIR` - (OBSOLETE) The following directory names are reserved, they contain installation artefacts of the `sw/common` and `hw/common` modules: - `sw/include`, `sw/lib` - `hw/include`, `hw/lib`, (`hw/syn`) ### The *Installation Tree* (`PN_INSTALLED_DIR`) #### General Considerations - The directory `PN_INSTALLED_DIR` is expected to contain all build artefacts used by a) other modules, b) (external) systems or c) software applications. - The build system never writes to `PN_INSTALLED_DIR`. The installation target for "install" targets is given by commonly used variable `PREFIX`. - The directory structure is flat compared to the source tree to simplify search paths for libraries and headers. All modules contained in the *PicoNut* source tree are responsible to avoid naming conflicts. - The same layout is used for a global installation and per-system modules. The system project directory `piconut/` can be regarded as an overlay of the installation directory. - **All modules of the *PicoNut* repo except systems must be buildable without an installed directory and without a system. Systems may rely on an existing install dir.** #### Layout ``` VERSION # Version string sw/ include/ # RISC-V headers (.h) (flat directory hierarchy) lib/ # RISC-V libraries (.a) (flat directory hierarchy) bin/ # RISC-V binaries hw/ include/ # SystemC headers (.h, .H) for hardware modules (flat hierarchy) / # headers specific to a certain module (e.g. peripheral or Nucleus/Membrana variant) / # headers specific to a certain peripheral .h # main header to use a certain peripheral lib/ # Hardware simulation libraries (.a) (flat hierarchy) syn/ # Hardware synthesis artefacts (.v, .vhdl, ...) (flat hierarchy) Possible types: .sv: System verilog: current module without submodules (typically generated by ICSC) .v: Verilog modules (manually written or generated by ICSC ... bin/ # Tools src/ # exact copy of the source tree from which this was installed (without .git* and other metafiles); TBD: Headers may primarily be found here then instead of sw/include or hw/include -> remove them here or in */include?. ``` ### The *System Directory* (`PN_SYSTEM_DIR`) ``` piconut/ # - Same sub-directories as $PN_INSTALL_DIR without 'src/'. # - Directory can and should be removed by "clean" target of the user's project # - Modules with non-default parameters must be built and installed using # PREFIX=$PN_SYSTEM_DIR/piconut # - The makefile (piconut.mk) makes sure that headers and # libraries are first searched in the system directory, then in $PICONUT. piconut-config.mk # system-specific configuration ``` (sec:makesys:more)= ## Further Information For the case that some information is missing or appears to be outdated, more and up-to-date information can be found in * [the sample Makefiles mentioned in the section on writing new Makefiles](sec:makesys:templates), * as comments in the file `piconut.mk`.