Using machine-readable datasheets to create datasheets with Cyanobyte and LaTeX
In my spare time I’ve been spending time working on Cyanobyte, a way to simplify the problem of writing peripheral drivers for different embedded platforms. You can think of the project as defining “machine-readable datasheets”.
Letting subject matter experts focus on either the peripheral specification or the embedded platform allows both to come together to develop high-quality drivers like for the Raspberry Pi or Arduino without a great deal of porting code.
This approach is not just useful for code generation. It also can be used to generate Markdown pages that are hosted in the developer documentation as part of the continuous delivery service.
The success of this started getting me thinking about datasheets in general. They are useful reference documents that engineers are already familiar with. Could it be possible to generate PDFs just as easily as generating code?
Peripheral specifications are defined as YAML files with various sections that define varying levels of the peripheral: its metadata, registers, fields, and higher-level functions. In the codegen tool is loads this peripheral spec into a Jinja2 template to output the driver.
As such, it didn’t seem at first like it was possible to do this. Existing Python libraries like PDFKit seemed limited in what they could do, and it did not seem possible to create a PDF Jinja template.
Then I thought about LaTeX, a text-based format which is designed to easily export to PDF files. It would work out pretty well as I could take advantage of existing templates for datasheets. I found an existing template on Overleaf and used that as the basis for my own.
Adapting the template was not too difficult. I replaced some of the graphics with a series of tables providing reference information on each register and field. It wasn’t significantly different from a Python template.
Once I finished it, I was able to use Overleaf to render the results, and I was pretty happy with how it turned out.
The template starts with an Overview with interesting information, register descriptions, then tables. The first table is an overview of all the registers. The next section then goes into more detail about each register if it is made up of smaller fields.
Functions are also displayed in tables, showing the name of the function and the return type.
Functions with multiple returns, in a tuple or array, are displayed as a comma-separated list.
The real power of this approach is that peripherals can be developed wholly independent of the target format. If a new peripheral is created, its generated code and datasheets will work just as well as the ones that exist now. This allows the peripheral engineers to focus on what they do best, and allow the platform integrators to do what they know best.