|  | --- | 
|  | title: "The OpenTitan DIF Library" | 
|  | --- | 
|  |  | 
|  | A DIF is a "Device Interface Function". DIFs are low-level routines for | 
|  | accessing the hardware functionality directly, and are agnostic to the | 
|  | particular environment or context they are called from. The intention is that | 
|  | DIFs are high-quality software artifacts which can be used during design | 
|  | verification and early silicon verification. | 
|  |  | 
|  | Although DIFs are high-quality software artifacts, they are not a hardware | 
|  | abstraction layer (HAL), nor do they follow the device driver model of | 
|  | any particular operating system, and as such, **_DIFs are not intended | 
|  | to be used by production firmware_**.  DIFs, in combination with the | 
|  | hardware specification, may be illustrative for writing drivers, but should | 
|  | not be considered drivers themselves. | 
|  |  | 
|  | This subtree provides headers and libraries known collectively as the DIF | 
|  | libraries. | 
|  |  | 
|  | There is one DIF library per hardware IP, and each one contains the DIFs | 
|  | required to actuate all of the specification-required functionality of the | 
|  | hardware they are written for. | 
|  |  | 
|  | Each DIF library contains both auto-generated (which are checked-in to our | 
|  | repository under the `autogen/` subtree), and manually-implemented DIFs (which | 
|  | are not sub-foldered). | 
|  |  | 
|  | ## Developing New DIFs | 
|  |  | 
|  | Developers should use the `util/make_new_dif.py` script to both auto-generate a | 
|  | subset of DIFs, *and* instantiate some initial boilerplate templates that should | 
|  | be subsequently edited. Specifically, the script will create: | 
|  |  | 
|  | 1. auto-generated DIF code, including: | 
|  | * an auto-generated (private) DIF header, `autogen/dif_<ip>_autogen.h`, based | 
|  | on `util/make_new_dif/templates/dif_autogen.h.tpl`, | 
|  | * auto-generated DIF implementations, `autogen/dif_<ip>_autogen.c`, based on | 
|  | `util/make_new_dif/templates/dif_autogen.c.tpl`, and | 
|  | * auto-generated DIF unit tests (that test the auto-generated DIFs), | 
|  | `autogen/dif_<ip>_autogen_unittest.cc`, based on | 
|  | `util/make_new_dif/templates/dif_autogen_unittest.cc.tpl`. | 
|  | 2. boilerplate templates (that should be manually edited/enhanced) for the | 
|  | the portion of the IP DIF library that is manually implemented, including: | 
|  | * a (public) header for the DIF, based on | 
|  | `util/make_new_dif/templates/dif_template.h.tpl`], and | 
|  | * a checklist for the DIF, based on `doc/project/sw_checklist.md.tpl`. | 
|  |  | 
|  | Only the second set of files will need checking and editing, but the templates | 
|  | serve to avoid most of the copy/paste required, while keeping our DIF libraries | 
|  | consistent across IPs. | 
|  |  | 
|  | Further documentation for the script is provided in the script's source. | 
|  | Additionally, please invoke `util/make_new_dif.py --help` for detailed usage. | 
|  |  | 
|  | ## Checklists | 
|  |  | 
|  | This directory also contains checklists for each DIF, in markdown format. They | 
|  | are linked to from the [Hardware Dashboard]({{< relref "hw" >}}), in the | 
|  | Development Stage column. | 
|  |  | 
|  | ## DIF Style Guide | 
|  |  | 
|  | DIFs are very low-level software, so they have a more rigorous coding style than | 
|  | other parts of the codebase. | 
|  |  | 
|  | DIFs should follow the [OpenTitan C/C++ style | 
|  | guide](https://docs.opentitan.org/doc/rm/c_cpp_coding_style/) where it does not | 
|  | contradict with the guidelines below. | 
|  |  | 
|  | The guidelines below apply to writing DIFs, and code should be written in a | 
|  | similar style to the existing DIF libraries in this directory. | 
|  |  | 
|  | ### Definitions | 
|  |  | 
|  | <a name="side-effects"></a> | 
|  | **Side-effects** include (but are not limited to) writing to memory, including | 
|  | memory-mapped hardware, and modifying processor CSRs. | 
|  |  | 
|  | ### DIF Library Guidance | 
|  |  | 
|  | * DIF libraries must be written in C. | 
|  | * DIF libraries can only depend on the following headers (and their associated | 
|  | libraries): | 
|  | * `sw/device/lib/base/bitfield.h` | 
|  | * `sw/device/lib/base/mmio.h` | 
|  | * `sw/device/lib/base/memory.h` | 
|  | * `sw/device/lib/dif/dif_base.h` | 
|  | * DIF libraries must not depend on other DIF libraries. Exercising DIF | 
|  | functionality may require an environment set up using another DIF library, but | 
|  | DIFs must not call DIFs in other DIF libraries. | 
|  | * DIF library headers must be polyglot headers for C and C++. | 
|  | * the public (manually-implemented) DIF header for each DIF library should | 
|  | `#include` the (private) auto-generated header, so that DIF consumers need | 
|  | only `#include` the public DIF header to make use of an IP's DIF library. | 
|  |  | 
|  | ### DIF API Guidance | 
|  |  | 
|  | The following rules specify the basic API that each DIF must conform to. These | 
|  | rules specify the names of types, constants, and functions that each DIF must | 
|  | define for providing certain kinds of non-device-specific functionality (such as | 
|  | initializing handles or managing interrupts). | 
|  |  | 
|  | Notational caveats: | 
|  | * The token `<ip>` is the "short IP name" of the peripheral, | 
|  | in `snake_case` or `PascalCase` as is appropriate. | 
|  | * The parameter name `handle` is not normative, and DIF libraries are free to | 
|  | choose a different, but consistent, name for it. | 
|  | * All functions below are assumed to return `dif_result_t`, a global DIF return | 
|  | type defined in `sw/device/lib/dif/dif_base.h`. | 
|  | * Unless otherwise noted, all symbols mentioned below are required. | 
|  |  | 
|  | #### Hardware Parameterization | 
|  |  | 
|  | Our aim is that a single DIF library can be used with multiple instances of the | 
|  | same IP on the same chip, even when those IPs have been instantiated with | 
|  | different hardware parameters. | 
|  |  | 
|  | At the moment, we have a good approach to being able to address separate | 
|  | hardware instances instantiated at separate addresses, as long as they have the | 
|  | same hardware parameters (see the `base_addr` member in `dif_<ip>_t`). | 
|  | Most other parameters come from the specific IP on a case-by-case basis, and are | 
|  | extracted from the IP's auto-generated register header file, e.g., | 
|  | `<ip>_regs.h`. | 
|  |  | 
|  | #### Base Types | 
|  |  | 
|  | There are two categories of base types: | 
|  | 1. those that are defined once in `sw/device/lib/dif/dif_base.h` and used in all | 
|  | DIF libraries, and | 
|  | 2. those that are expected to be defined separately by all DIF libraries (unless | 
|  | otherwise specified). | 
|  |  | 
|  | The base types defined in `sw/device/lib/dif/dif_base.h` include: | 
|  | * `dif_result_t` -- an enum representing global DIF return codes. | 
|  | * `dif_toggle_t` -- an enum to be used instead of a `bool` when describing | 
|  | enablement states. | 
|  |  | 
|  | The base types that are expected to be defined separately by all DIF libraries | 
|  | include: | 
|  | * `dif_<ip>_t` -- an **auto-generated** type representing a handle to the | 
|  | peripheral. Its first (and only) field is always the base address for the | 
|  | peripheral registers, styled `mmio_region_t base_addr;`. | 
|  | This type is usually passed by `const` pointer, except when it is | 
|  | being initialized (see `dif_<ip>_init()` below). | 
|  | * `dif_<ip>_config_t` -- a **manually-defined** struct representing runtime | 
|  | configuration parameters for the peripheral. It is only present when | 
|  | `dif_<ip>_configure()` is defined. This type is always passed by value. | 
|  |  | 
|  | #### Lifecycle Functions | 
|  | The following functions are the basic functionality for initializing and | 
|  | handling the lifetime of a handle. | 
|  |  | 
|  | * `dif_result_t dif_<ip>_init(mmio_region_t base_addr, dif_<ip>_t *handle);` | 
|  | initializes `handle` with with the base address of the instantiated IP this | 
|  | DIF is targeting to use. This DIF is **auto-generated**. | 
|  |  | 
|  | * `dif_result_t dif_<ip>_configure(const dif_<ip>_t *handle, dif_<ip>_config_t | 
|  | config);` configures the hardware managed by `handle` with runtime parameters | 
|  | in an implementation-defined way. This function should be "one-off": it should | 
|  | only need to be called once for the lifetime of the handle. | 
|  | _If there is no meaningful state to configure, this function may be omitted._ | 
|  | In particular, DIF libraries providing transaction functions will usually have | 
|  | no need for this function at all. This DIF is **manually-implemented**. | 
|  |  | 
|  | #### Transaction Management | 
|  |  | 
|  | The following types and functions are the standard interface for | 
|  | *transaction-oriented* peripherals, in which a client schedules an operation to | 
|  | be completed at some point in the future. All types and functions listed here | 
|  | are **manually-implemented**. | 
|  |  | 
|  | * `dif_<ip>_transaction_t` is a struct representing runtime parameters for | 
|  | starting a hardware transaction. It is only present when `dif_<ip>_start()` is | 
|  | defined. This type is always passed by value. A DIF library my opt to use | 
|  | another pre-existing type instead, when that type provides a more semanticly | 
|  | appropriate meaning. | 
|  | * `dif_<ip>_output_t` is a struct describing how to output a completed | 
|  | transaction. Often, this will be a type like `uint8_t *`. The same caveats | 
|  | about a DIF library providing a different type apply here. | 
|  | * `dif_result_t dif_<ip>_start(const dif_<ip>_t *handle, dif_<ip>_transaction_t | 
|  | transaction);` starts a transaction on a transaction-oriented peripheral. This | 
|  | function may be called multiple times, but each call should be paired with a | 
|  | `dif_<ip>_end()` call. | 
|  | * `dif_result_t dif_<ip>_end(const dif_<ip>_t *handle, dif_<ip>_output_t out);` | 
|  | completes a transaction started with `dif_<ip>_start()`, writing its results | 
|  | to a location specified in `out`. | 
|  |  | 
|  | If a peripheral supports multiple transaction modes with incompatible parameter | 
|  | types, the above names may be duplicated by inserting `mode_<mode>` after `<ip>`. | 
|  | For example, | 
|  | ``` | 
|  | dif_result_t dif_<ip>_mode_<mode>_end(const dif_<ip>_t *handle, | 
|  | dif_<ip>_mode_<mode>_output_t out); | 
|  | ``` | 
|  | There is no requirement that `_start()` and `_end()` share the same set of | 
|  | `<mode>`s; for example, there might be a single `dif_<ip>_start()` but many | 
|  | `dif_<ip>_mode_<mode>_end()`s. This style of API is prefered over using | 
|  | `union`s with `dif_<ip>_transaction_t` and `dif_<ip>_output_t`. | 
|  |  | 
|  | #### Register Locking | 
|  |  | 
|  | The following functions are the standard interface for peripherals that can lock | 
|  | portions of their software-accessible functionality. All types and functions | 
|  | listed here are **manually-implemented**. | 
|  |  | 
|  | * `kDifLocked` is the global return result enum of an operation that can be | 
|  | locked out. DIFs which may fail due to lockout, which is software-detectable, | 
|  | should return this value when possible. | 
|  | * `dif_result_t dif_<ip>_lock(const dif_<ip>_t *handle);` locks out all portions | 
|  | of the peripheral which can be locked. If a peripheral can be locked-out | 
|  | piecewise, `dif_<ip>_lock_<operation>()` functions may be provided alongside | 
|  | or in lieu of `dif_<ip>_lock()`. | 
|  | * `dif_result_t dif_<ip>_is_locked(const dif_<ip>_t *handle, bool *is_locked);` | 
|  | checks whether the peripheral has been locked out. As with `dif_<ip>_lock()`, | 
|  | DIF libraries may provide a piecewise version of this API. | 
|  |  | 
|  | #### Interrupt Management | 
|  |  | 
|  | The following types and functions are the standard interface for peripherals that | 
|  | provide a collection of `INTR_ENABLE`, `INTR_STATE`, and `INTR_TEST` registers | 
|  | for interrupt management. A DIF library for a peripheral providing such | 
|  | registers must provide this interface. To ensure this, all interrupt DIFs, | 
|  | including: headers, (C) implementations, and unit tests, are *auto-generated* from | 
|  | templates and an IP's HJSON configuration file using the `util/make_new_dif.py` | 
|  | tool (decribed above). | 
|  |  | 
|  | If a peripheral is defined with `no_auto_intr_regs: true`, this exact API is not | 
|  | required even if the `INTR_` registers are provided (though DIF libraries are | 
|  | encouraged to follow it where it makes sense). _In these cases, auto-generated | 
|  | interrupt DIFs may not exist._ | 
|  |  | 
|  | * `dif_<ip>_irq_t` is an enum that lists all of the interrupt types for this | 
|  | peripheral. These derrived from the `interrupt_list` attribute within an IP's | 
|  | HJSON file. | 
|  | * `dif_result_t dif_<ip>_irq_get_state(const dif_<ip>_t *handle, | 
|  | dif_<ip>_irq_state_snapshot_t *snapshot, bool *is_pending);` returns a | 
|  | snapshot of the entire interrupt state register, to check the status of all | 
|  | interrupts for a peripheral. | 
|  | * `dif_result_t dif_<ip>_irq_is_pending(const dif_<ip>_t *handle, dif_<ip>_irq_t | 
|  | irq, bool *is_pending);` checks whether a specific interrupt is | 
|  | pending (i.e., if the interrupt has been asserted but not yet cleared). | 
|  | * `dif_result_t dif_<ip>_irq_acknowledge_all(const dif_<ip>_t *handle);` | 
|  | acknowledges all interrupts have been serviced, marking them as | 
|  | complete by clearing all pending bits. This function does nothing and returns | 
|  | `kDifOk` if no interrupts were pending. | 
|  | * `dif_result_t dif_<ip>_irq_acknowledge(const dif_<ip>_t *handle, dif_<ip>_irq_t | 
|  | irq);` acknowledges that an interrupt has been serviced, marking it as | 
|  | complete by clearing its pending bit. This function does nothing and returns | 
|  | `kDifOk` if the interrupt wasn't pending. | 
|  | * `dif_result_t dif_<ip>_irq_get_enabled(const dif_<ip>_t *handle, dif_<ip>_irq_t | 
|  | irq, const dif_<ip>_toggle_t *state);` gets whether an interrupt is | 
|  | enabled (i.e., masked). | 
|  | * `dif_result_t dif_<ip>_irq_set_enabled(const dif_<ip>_t *handle, dif_<ip>_irq_t | 
|  | irq, dif_<ip>_toggle_t state);` sets whether a particular interrupt is | 
|  | enabled (i.e., masked). | 
|  | * `dif_result_t dif_<ip>_irq_force(const dif_<ip>_t *handle, dif_<ip>_irq_t | 
|  | irq);` forcibly asserts a specific interrupt, causing it to be serviced | 
|  | as if hardware had triggered it. | 
|  |  | 
|  | Additionally, the following types allow for batch save/restore operations on | 
|  | the interrupt enable register: | 
|  |  | 
|  | * `dif_<ip>_irq_enable_snapshot_t` is a type that encapsulates restorable | 
|  | interrupt enablement state, to be used with the two functions below. This type | 
|  | should be treated as opaque by clients. | 
|  | * `dif_result_t dif_<ip>_irq_disable_all(const dif_<ip>_t *handle, | 
|  | dif_<ip>_irq_enable_snapshot_t *snapshot);` disables all interrupts associated | 
|  | with the peripheral, saving them to `*snapshot`. `snapshot` may be null, in | 
|  | which case the previous enablement state is not saved. | 
|  | * `dif_result_t dif_<ip>_irq_restore_all(const dif_<ip>_t *handle, | 
|  | const dif_<ip>_irq_enable_snapshot_t *snapshot);` restores an interrupt | 
|  | enablement snapshot produced by the above function. | 
|  |  | 
|  | #### Unit Testing | 
|  |  | 
|  | Each DIF has an associated unit test, written in C++. For auto-generated DIFs, | 
|  | their associated unit tests are also auto-generated. For manually-implemented | 
|  | DIFs, their associated unit tests follow the conventions: | 
|  | * The whole file is wrapped in the `dif_<ip>_unittest` namespace. | 
|  | * There is a base class for all test fixtures, named `<ip>Test`, which derives | 
|  | `testing::Test` and `mock_mmio::MmioTest`. | 
|  | * Each function has an associated test fixture, usually named | 
|  | `<function>Test`, which derives `<ip>Test`. Multiple similar functions may be | 
|  | grouped under one fixture. | 
|  | * Prefer to use expectation macros, like `EXPECT_DIF_OK`, defined in | 
|  | `dif_test_base.h` whenever possible (e.g. do not write | 
|  | `EXPECT_EQ(<long expression>, kDifOk);`). | 
|  |  | 
|  | ### DIF Style Guidance | 
|  |  | 
|  | The following rules must be followed by public DIF functions (those declared in | 
|  | the DIF library's header file). Internal DIF functions (those declared `static` | 
|  | and not declared in the DIF library's header file) should follow these rules but | 
|  | there are some relaxations of these rules for them described at the end. | 
|  |  | 
|  | * DIF declarations must match their definitions exactly. | 
|  | * Scalar arguments must not be declared `const` or `volatile` (cv-qualified) | 
|  | in DIF signatures. | 
|  |  | 
|  | * DIFs must use one of the `dif_result_t` enums (described in | 
|  | `sw/device/lib/dif/dif_base.h`) rather than booleans for reporting errors. If | 
|  | a DIF can either error or instead produce a value, it must return a | 
|  | `dif_result_t`, and use an out-parameter for returning the produced value. | 
|  | * DIFs that return an enum return code must be annotated with | 
|  | `OT_WARN_UNUSED_RESULT`, to help minimize mistakes from | 
|  | failing to check a result. This guidance applies to `static` helper | 
|  | functions that return an error of some kind as well. | 
|  | * DIFs that cannot error and that do not return a value must return `void`. | 
|  |  | 
|  | * DIFs must check their arguments against preconditions using "guard | 
|  | statements". A guard statement is a simple if statement at the start of a | 
|  | function which only returns an error code if the preconditions are not met. | 
|  | Guard statements must cover the following checks: | 
|  | * DIFs must ensure their pointer arguments are non-null, unless that pointer | 
|  | is for an optional out-parameter. Arguments typed `mmio_region_t` are not | 
|  | pointers, and cannot meaningfully be checked for non-nullness. | 
|  | * DIFs must ensure, if they only accept a subset of an enum, that the argument | 
|  | is within that subset. However, DIFs may assume, for checking preconditions, | 
|  | that any enum argument is one of the enum constants. | 
|  | * DIFs must not cause any side-effects before any guard statements. This means | 
|  | returning early from a guard statement must not leave the hardware in an | 
|  | invalid or unrecoverable state. | 
|  |  | 
|  | * Switch statements in DIFs must always have a default case, including when | 
|  | switching on an enum value (an "enum switch"). | 
|  | * The default case of an enum switch must report an error for values that are | 
|  | not a constant from that enum. In the absence of more specific information, | 
|  | this should return `kDifError` or the equivalent return code value from | 
|  | a global DIF return code enum. If the enum switch is part of a guard | 
|  | statement, it may return `kDifBadArg` instead. | 
|  | * Enum switches do not need a `case` for enum constants that are unreachable | 
|  | due to a guard statement. | 
|  |  | 
|  | * DIFs must use `sw/device/lib/base/mmio.h` for accessing memory-mapped | 
|  | hardware. DIFs must not use `sw/device/lib/base/memory.h` for accessing | 
|  | memory-mapped hardware. | 
|  |  | 
|  | * Internal DIF functions, which are not intended to be part of a public DIF | 
|  | library interface, must not be declared in the DIF library header, and must be | 
|  | marked `static`. | 
|  | * `static` DIF functions should not be marked `static inline`. | 
|  | * An internal DIF function does not need to check preconditions, if all the | 
|  | DIF functions that call it have already checked that precondition. |