|
| 1 | +# μHAL Design |
| 2 | + |
| 3 | +A [primer on the hardware](hardware.md) with which this project interfaces |
| 4 | +should be useful in understanding the design choices made. |
| 5 | + |
| 6 | +## Low level interface |
| 7 | + |
| 8 | +The low level interface consists simply of a `struct pcie_bars` object, which |
| 9 | +allows its user to read and write from BARs 2 and 4 (`BAR0` is considered an |
| 10 | +implementation detail) using the functions defined in `util/pcie.h`. |
| 11 | + |
| 12 | +`BAR4` can also be accessed when `struct pcie_bars` points to a serial port, |
| 13 | +which is used when debugging boards outside of a μTCA crate. The serial port |
| 14 | +doesn't allow access to `BAR2`, though. |
| 15 | + |
| 16 | +All other functionality of this project depends on this low level interface. |
| 17 | + |
| 18 | +## Basic abstractions |
| 19 | + |
| 20 | +### SDB access |
| 21 | + |
| 22 | +SDB access is provided by the functions in `util/util_sdb.h`. These functions |
| 23 | +are higher level abstractions on top of the `libsdbfs` project. |
| 24 | + |
| 25 | +Unlike HALCS, which iterated through the SBD and launched a handler for each |
| 26 | +core it found, the only function in this library which iterates over the SDB is |
| 27 | +the one which prints its contents. For any other use, users of the library will |
| 28 | +search for specific cores (with their index in depth-first traversal) with |
| 29 | +`read_sdb()`. In order to know which cores should be available in a given |
| 30 | +board, users should consult the build information provided by |
| 31 | +`get_synthesis_info()`. |
| 32 | + |
| 33 | +While this implementation might seem less flexible at first, it's impossible to |
| 34 | +escape from the need to know what cores are available when using this library |
| 35 | +on an IOC: records for them must be instantiated and have the proper names. |
| 36 | +Therefore, this isn't adding any new limitations to an IOC. |
| 37 | + |
| 38 | +### FPGA core access |
| 39 | + |
| 40 | +For the most part, acess to each FPGA core is split into two classes under the |
| 41 | +core's namespace (e.g. `afc_timing`): one is the "decoder", `afc_timing::Core`, |
| 42 | +the other is the "controller", `afc_timing::Controller`. Usually, the decoder |
| 43 | +implements read-only access, while the controller has to be read-write. |
| 44 | + |
| 45 | +The base classes involved in this are `class RegisterDecoderBase`, `class |
| 46 | +RegisterDecoder`, `class RegisterController` and `class |
| 47 | +RegisterDecoderController`. The implementation for each core is under the |
| 48 | +`modules/` directory. |
| 49 | + |
| 50 | +#### Register maps |
| 51 | + |
| 52 | +The register map for each module is encoded as a C struct, which is provided by |
| 53 | +the header generated by `cheby`. Headers generated by `wbgen2` don't include a |
| 54 | +C struct, so it is necessary to define this struct ourselves. |
| 55 | + |
| 56 | +These register fields are decoded using bitmasks — and only bitmasks, we don't |
| 57 | +use shift macros and instead obtain the shift directly from the mask, in order |
| 58 | +to simplify boilerplate code and avoid mismatches — provided by the generated |
| 59 | +headers, using the functions from `util/util-bits.h`. |
| 60 | + |
| 61 | +#### RegisterController and RegisterDecoderController |
| 62 | + |
| 63 | +Before `class RegisterDecoderController` was created, controllers were mostly |
| 64 | +developed manually, duplicating the correspondence between the register fields |
| 65 | +we are interested in and their location in the register map, the mask used to |
| 66 | +obtain them, and how to interpret their data. This also made it necessary for |
| 67 | +library users to address fields differently when reading or writing them. |
| 68 | + |
| 69 | +`class RegisterDecoderController` removes this need, requiring only a small |
| 70 | +amount of boilerplate to expose writing into register fields under a common |
| 71 | +interface. |
| 72 | + |
| 73 | +#### Unconventional controllers |
| 74 | + |
| 75 | +Some FPGA cores couldn't be implemented simply by decoding and encoding |
| 76 | +register fields. Some of them are listed here: |
| 77 | + |
| 78 | +- `acq::Controller` |
| 79 | +- `ad9510::Controller` |
| 80 | +- `si57x_ctrl::Controller` |
0 commit comments