Skip to content

Commit

Permalink
doc: document design.
Browse files Browse the repository at this point in the history
  • Loading branch information
ericonr committed Jan 15, 2025
1 parent 69d8df6 commit 9b5889a
Showing 1 changed file with 78 additions and 0 deletions.
78 changes: 78 additions & 0 deletions documentation/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# μHAL Design

A [primer on the hardware](hardware.md) with which this project interfaces
should be useful in understanding the design choices made.

## Low level interface

The low level interface consists simply of a `struct pcie_bars` object, which
allows its user to read and write from BARs 2 and 4 (`BAR0` is considered an
implementation detail) using the functions defined in `util/pcie.h`.

`BAR4` can also be accessed when `struct pcie_bars` points to a serial port,
which is used when debugging boards outside of a μTCA crate. The serial port
doesn't allow access to `BAR2`, though.

All other functionality of this project depends on this low level interface.

## Basic abstractions

### SDB access

SDB access is provided by the functions in `util/util_sdb.h`. These functions
are higher level abstractions on top of the `libsdbfs` project.

Unlike HALCS, which iterated through the SBD and launched a handler for each
core it found, the only function in this library which iterates over the SDB is
the one which prints its contents. For any other use, users of the library will
search for specific cores (with their index in depth-first traversal) with
`read_sdb()`. In order to know which cores should be available in a given
board, users should consult the build information provided by
`get_synthesis_info()`.

While this implementation might seem less flexible at first, when using this
library on an IOC, it's impossible to escape from the need to know what cores
are available: records for them must be instantiated and have the proper names.
Therefore, this isn't adding any new limitations to an IOC.

### FPGA core access

For the most part, acess to each FPGA core is split into two classes under the
core's namespace (e.g. `afc_timing`): one is the "decoder", `afc_timing::Core`,
the other is the "controller", `afc_timing::Controller`. Usually, the decoder
implements read-only access, while the controller has to be read-write.

The base classes involved in this are `class RegisterDecoderBase`, `class
RegisterDecoder`, `class RegisterController` and `class
RegisterDecoderController`. The implementation for each core is under the
`modules/` directory.

#### Register maps

The register map for each module is encoded as a C struct, which is provided by
the header generated by `cheby`. Headers generated by `wbgen2` don't include a
C struct, so it is necessary to define this struct ourselves.

These register fields are decoded using bitmasks provided by the generated
headers, using the functions from `util/util-bits.h`.

#### RegisterController and RegisterDecoderController

Before `class RegisterDecoderController` was created, controllers were mostly
developed manually, duplicating the correspondence between the register fields
we are interested in and their location in the register map, the mask used to
obtain them, and how to interpret their data. This also made it necessary for
library users to address differently when reading or writing them.

`class RegisterDecoderController` removes this need, requiring only a small
amount of boilerplate to expose writing into register fields under a common
interface.

#### Unconventional controllers

Some FPGA cores couldn't be implemented simply by decoding and encoding
register fields. We list some of them here:

- `acq::Controller`
- `ad9510::Controller`
- `si57x_ctrl::Controller`

0 comments on commit 9b5889a

Please sign in to comment.