303
index.html
303
index.html
@ -15,7 +15,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
|
||||
<link rel="stylesheet" href="css/reveal.css">
|
||||
<link rel="stylesheet" href="css/theme/lca2019.css" id="theme">
|
||||
<link rel="stylesheet" href="css/theme/lca2020.css" id="theme">
|
||||
|
||||
<!-- Theme used for syntax highlighting of code -->
|
||||
<link rel="stylesheet" href="lib/css/zenburn.css">
|
||||
@ -92,152 +92,205 @@
|
||||
<small>Sean Cross - <a href="https://xobs.io/">https://xobs.io/</a> - @xobs</small>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h1>Hardware is different</h1>
|
||||
<ol>
|
||||
<li>Massively Parallel</li>
|
||||
</ol>
|
||||
<aside class="notes">
|
||||
This is the Open ISA miniconf, which today tends to mean FPGAs. This means that
|
||||
hardware and software are both extensible, and developers will be able to extend
|
||||
the hardware in addition to making modifications to your software package.
|
||||
</aside>
|
||||
</section>
|
||||
This is the Open ISA miniconf, which today tends to mean FPGAs. This means that
|
||||
hardware and software are both extensible, and developers will be able to extend
|
||||
the hardware in addition to making modifications to your software package.
|
||||
|
||||
Undocumented hardware is bad. There are all sorts of quirks, and even if you have
|
||||
the source code, it can be very difficult to read. I'm the primary developer for
|
||||
the Fomu project, and this talk will cover some of the issues I've run into with
|
||||
respect to documentation. It is most directly related to the LiteX and Migen
|
||||
projects, but the concepts will carry over into any other Hardware Description
|
||||
Language you may use.
|
||||
<section>
|
||||
<aside class="notes">
|
||||
Undocumented hardware is bad. There are all sorts of quirks, and even if you have
|
||||
the source code, it can be very difficult to read. I'm the primary developer for
|
||||
the Fomu project, and this talk will cover some of the issues I've run into with
|
||||
respect to documentation. It is most directly related to the LiteX and Migen
|
||||
projects, but the concepts will carry over into any other Hardware Description
|
||||
Language you may use.
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
I'll briefly cover various methods of writing HDL code, then cover the rationale
|
||||
behind the approach we take with lxsocdoc, then give an example of how to use
|
||||
lxsocdoc and how you might apply it to your language. Finally, I'll cover the
|
||||
implications of having documented hardware and how this will help you pay it forward.
|
||||
<section>
|
||||
<aside class="notes">
|
||||
I'll briefly cover various methods of writing HDL code, then cover the rationale
|
||||
behind the approach we take with lxsocdoc, then give an example of how to use
|
||||
lxsocdoc and how you might apply it to your language. Finally, I'll cover the
|
||||
implications of having documented hardware and how this will help you pay it forward.
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
Verilog and VHDL are kind of the C or assembly of the FPGA world. They're universal,
|
||||
but somewhat unwieldy to use. You need to manually set up your address decoders,
|
||||
and documentation is very free-form. Common approaches today involve comments in
|
||||
the HDL and/or C header files. This works, but we can do better. We just need to
|
||||
describe the hardware better.
|
||||
```//Hardware definitions of the SoC. Also is the main repo of documentation for the
|
||||
//programmer-centric view of the hardware.```
|
||||
<section>
|
||||
<aside class="notes">
|
||||
Verilog and VHDL are kind of the C or assembly of the FPGA world. They're universal,
|
||||
but somewhat unwieldy to use. You need to manually set up your address decoders,
|
||||
and documentation is very free-form. Common approaches today involve comments in
|
||||
the HDL and/or C header files. This works, but we can do better. We just need to
|
||||
describe the hardware better.
|
||||
```//Hardware definitions of the SoC. Also is the main repo of documentation for the
|
||||
//programmer-centric view of the hardware.```
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
Fomu uses LiteX, which is related to Migen. This is a hardware description language
|
||||
written in Python. You write Python code and run the program, and it generates
|
||||
a design file -- either Verilog code, or a Yosys netlist. There are many other
|
||||
alternatives such as SpinalHDL or Chisel. By writing in Python as opposed to
|
||||
direct Verilog, we get a lot of nice primitives. The examples from this talk
|
||||
are taken from lxsocdoc and LiteX, but most higher-level hardware description
|
||||
languages can take similar approaches to documentation.
|
||||
<section>
|
||||
<aside class="notes">
|
||||
Fomu uses LiteX, which is related to Migen. This is a hardware description language
|
||||
written in Python. You write Python code and run the program, and it generates
|
||||
a design file -- either Verilog code, or a Yosys netlist. There are many other
|
||||
alternatives such as SpinalHDL or Chisel. By writing in Python as opposed to
|
||||
direct Verilog, we get a lot of nice primitives. The examples from this talk
|
||||
are taken from lxsocdoc and LiteX, but most higher-level hardware description
|
||||
languages can take similar approaches to documentation.
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
In LiteX, two of the primitives used to expose hardware registers to the CPU softcore
|
||||
are CSRStorage and CSRStatus. Instead of manually wiring up a crossbar and decoding
|
||||
the addresses ourselves, we just need to write `self.regname = CSRStatus(8)`,
|
||||
and the build system will wire up 8 bits of read-only memory to the target CPU.
|
||||
Similarly, `self.othername = CSRStorage(8)` will give 8-bits of write-only memory.
|
||||
<section>
|
||||
<aside class="notes">
|
||||
In LiteX, two of the primitives used to expose hardware registers to the CPU softcore
|
||||
are CSRStorage and CSRStatus. Instead of manually wiring up a crossbar and decoding
|
||||
the addresses ourselves, we just need to write `self.regname = CSRStatus(8)`,
|
||||
and the build system will wire up 8 bits of read-only memory to the target CPU.
|
||||
Similarly, `self.othername = CSRStorage(8)` will give 8-bits of write-only memory.
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
This works well, but exposes a new problem: Documentation. As an example, I was
|
||||
working with the SPI Flash block in litex, and wanted to know how the bitbang
|
||||
driver worked. There wasn't any documentation except the source, which looked
|
||||
like this:
|
||||
<section>
|
||||
<h4>SPI Bitbang Module</h4>
|
||||
<pre><code class="python" data-trim>self.bitbang = CSRStorage(4)
|
||||
If(self.bitbang.storage[3],
|
||||
dq.oe.eq(0)
|
||||
).Else(
|
||||
dq.oe.eq(1)
|
||||
),
|
||||
If(self.bitbang.storage[1], # CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
|
||||
self.miso.status.eq(dq.i[1])
|
||||
),
|
||||
dq.o.eq(Cat(self.bitbang.storage[0], Replicate(1, spi_width-1)))</code></pre>
|
||||
<aside class="notes">
|
||||
This works well, but exposes a new problem: Documentation. As an example, I was
|
||||
working with the SPI Flash block in litex, and wanted to know how the bitbang
|
||||
driver worked. There wasn't any documentation except the source, which looked
|
||||
like this.
|
||||
|
||||
self.bitbang = CSRStorage(4)
|
||||
If(self.bitbang.storage[3],
|
||||
dq.oe.eq(0)
|
||||
).Else(
|
||||
dq.oe.eq(1)
|
||||
),
|
||||
If(self.bitbang.storage[1], # CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
|
||||
self.miso.status.eq(dq.i[1])
|
||||
),
|
||||
dq.o.eq(Cat(self.bitbang.storage[0], Replicate(1, spi_width-1)))
|
||||
You can kind of understand it, but it does take a lot of mental power to
|
||||
work through it. I started by creating aliases for the various elements
|
||||
in the storage array, but then I thought: There has to be a better way!
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
You can kind of understand it, but it does take a lot of mental power to
|
||||
work through it. I started by creating aliases for the various elements
|
||||
in the storage array, but then I thought: There has to be a better way!
|
||||
<section>
|
||||
<aside class="notes">
|
||||
As an aside, Python has something called Pydoc and Docstrings. These are
|
||||
comments that go at the top of functions and classes that let you describe
|
||||
what a Python object is and how to use it. This is almost what we want,
|
||||
except once the final SoC is generated we don't really care so much about
|
||||
things like constructor arguments or method properties. Documentation for
|
||||
the end user is different from documentation for the module developer.
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
As an aside, Python has something called Pydoc and Docstrings. These are
|
||||
comments that go at the top of functions and classes that let you describe
|
||||
what a Python object is and how to use it. This is almost what we want,
|
||||
except once the final SoC is generated we don't really care so much about
|
||||
things like constructor arguments or method properties. Documentation for
|
||||
the end user is different from documentation for the module developer.
|
||||
<section>
|
||||
<aside class="notes">
|
||||
This is when I hit upon the idea of `lxsocdoc`. The basic idea is that
|
||||
Python is really good at introspecting Python, so let's add a little bit
|
||||
more information to the CSR objects to make our life easier. And so, after
|
||||
working with the LiteX creator Florent, we refactored the bitbang
|
||||
definition to this:
|
||||
|
||||
This is when I hit upon the idea of `lxsocdoc`. The basic idea is that
|
||||
Python is really good at introspecting Python, so let's add a little bit
|
||||
more information to the CSR objects to make our life easier. And so, after
|
||||
working with the LiteX creator Florent, we refactored the bitbang
|
||||
definition to this:
|
||||
self.bitbang = CSRStorage(4, fields=[
|
||||
CSRField("mosi", description="Output value for MOSI pin, valid whenever ``dir`` is ``0``."),
|
||||
CSRField("clk", description="Output value for SPI CLK pin."),
|
||||
CSRField("cs_n", description="Output value for SPI CSn pin."),
|
||||
CSRField("dir", description="Sets the direction for *ALL* SPI data pins except CLK and CSn.", values=[
|
||||
("0", "OUT", "SPI pins are all output"),
|
||||
("1", "IN", "SPI pins are all input"),
|
||||
])
|
||||
], description="""
|
||||
Bitbang controls for SPI output. Only standard 1x SPI is supported, and as
|
||||
a result all four wires are ganged together. This means that it is only possible
|
||||
to perform half-duplex operations, using this SPI core.
|
||||
""")
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
self.bitbang = CSRStorage(4, fields=[
|
||||
CSRField("mosi", description="Output value for MOSI pin, valid whenever ``dir`` is ``0``."),
|
||||
CSRField("clk", description="Output value for SPI CLK pin."),
|
||||
CSRField("cs_n", description="Output value for SPI CSn pin."),
|
||||
CSRField("dir", description="Sets the direction for *ALL* SPI data pins except CLK and CSn.", values=[
|
||||
("0", "OUT", "SPI pins are all output"),
|
||||
("1", "IN", "SPI pins are all input"),
|
||||
])
|
||||
], description="""
|
||||
Bitbang controls for SPI output. Only standard 1x SPI is supported, and as
|
||||
a result all four wires are ganged together. This means that it is only possible
|
||||
to perform half-duplex operations, using this SPI core.
|
||||
""")
|
||||
<section>
|
||||
<aside class="notes">
|
||||
Now the actual bitbang logic looks like:
|
||||
|
||||
Now the actual bitbang logic looks like:
|
||||
If(self.bitbang.fields.dir,
|
||||
dq.oe.eq(0)
|
||||
).Else(
|
||||
dq.oe.eq(1)
|
||||
),
|
||||
If(self.bitbang.fields.clk, # CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
|
||||
self.miso.status.eq(dq.i[1])
|
||||
),
|
||||
dq.o.eq(Cat(self.bitbang.fields.mosi, Replicate(1, spi_width-1)))
|
||||
|
||||
If(self.bitbang.fields.dir,
|
||||
dq.oe.eq(0)
|
||||
).Else(
|
||||
dq.oe.eq(1)
|
||||
),
|
||||
If(self.bitbang.fields.clk, # CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
|
||||
self.miso.status.eq(dq.i[1])
|
||||
),
|
||||
dq.o.eq(Cat(self.bitbang.fields.mosi, Replicate(1, spi_width-1)))
|
||||
This is a little bit easier to understand -- no longer are we looking at indices
|
||||
in an array to determine what field does what. Instead we get actual named fields.
|
||||
But because Python can introspect Python very easily, this is just the beginning.
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
This is a little bit easier to understand -- no longer are we looking at indices
|
||||
in an array to determine what field does what. Instead we get actual named fields.
|
||||
But because Python can introspect Python very easily, this is just the beginning.
|
||||
<section>
|
||||
<aside class="notes">
|
||||
After the design is elaborated and the output file is generated, we can iterate
|
||||
through the resulting tree and pick out any CSR objects and using any additional
|
||||
information. We can actually generate a full reference manual, just like one you
|
||||
would receive from a SoC Vendor.
|
||||
|
||||
After the design is elaborated and the output file is generated, we can iterate
|
||||
through the resulting tree and pick out any CSR objects and using any additional
|
||||
information. We can actually generate a full reference manual, just like one you
|
||||
would receive from a SoC Vendor.
|
||||
For example, this is what the start of the Fomu SPI Flash documentation looks like:
|
||||
[Register Listing for LXSPI]
|
||||
|
||||
For example, this is what the start of the Fomu SPI Flash documentation looks like:
|
||||
[Register Listing for LXSPI]
|
||||
This is already pretty useful. You can hand this file to someone and show them
|
||||
how your design works. But the title of this talk is called "Paying it Forward",
|
||||
and I can tell you from experience that having such a reference manual for yourself
|
||||
while developing software for your own hardware is still invaluable. Hardware
|
||||
designs are complex things, and not having to decode bitfield offsets in your
|
||||
head or constantly referring to various sections of code to see how it's implemented
|
||||
saves valuable time.
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
This is already pretty useful. You can hand this file to someone and show them
|
||||
how your design works. But the title of this talk is called "Paying it Forward",
|
||||
and I can tell you from experience that having such a reference manual for yourself
|
||||
while developing software for your own hardware is still invaluable. Hardware
|
||||
designs are complex things, and not having to decode bitfield offsets in your
|
||||
head or constantly referring to various sections of code to see how it's implemented
|
||||
saves valuable time.
|
||||
<section>
|
||||
<aside class="notes">
|
||||
So now we have register documentation. Can we do better? Of course we can.
|
||||
SoC reference manuals are more than just register definitions. They also include
|
||||
background information on protocols, as well as more elaboration on how the block
|
||||
works. We can take a cue from CSRs themselves, and add module documentation
|
||||
in a similar fashion.
|
||||
</aside>
|
||||
</section>
|
||||
---ModuleDoc---
|
||||
|
||||
So now we have register documentation. Can we do better? Of course we can.
|
||||
SoC reference manuals are more than just register definitions. They also include
|
||||
background information on protocols, as well as more elaboration on how the block
|
||||
works. We can take a cue from CSRs themselves, and add module documentation
|
||||
in a similar fashion.
|
||||
<section>
|
||||
<aside class="notes">
|
||||
Having documentation for humans is great, but we can go one step further and
|
||||
make documentation for computers. SVD is an XML format defined by ARM that
|
||||
defines various aspects about a chip, including memory layout, interrupt map,
|
||||
and register sets. SVD includes information such as default values and field
|
||||
bits, all information we have thanks to the introspectability of Python.
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
---ModuleDoc---
|
||||
|
||||
Having documentation for humans is great, but we can go one step further and
|
||||
make documentation for computers. SVD is an XML format defined by ARM that
|
||||
defines various aspects about a chip, including memory layout, interrupt map,
|
||||
and register sets. SVD includes information such as default values and field
|
||||
bits, all information we have thanks to the introspectability of Python.
|
||||
|
||||
In addition to generating a reference manual for humans, we can generate an SVD
|
||||
file that's usable in a wide variety of areas. For example, we can turn an SVD
|
||||
file into a Rust Peripheral Access Crate (PAC) using `SVD2Rust`, giving us an
|
||||
easy way to safely access all peripherals on a device.
|
||||
|
||||
We can also import this SVD file into an emulator such as Renode, which will
|
||||
print out fields and flags that get accessed, giving us greater visibility into
|
||||
what a program is doing.
|
||||
<section>
|
||||
<aside class="notes">
|
||||
In addition to generating a reference manual for humans, we can generate an SVD
|
||||
file that's usable in a wide variety of areas. For example, we can turn an SVD
|
||||
file into a Rust Peripheral Access Crate (PAC) using `SVD2Rust`, giving us an
|
||||
easy way to safely access all peripherals on a device.
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<aside class="notes">
|
||||
We can also import this SVD file into an emulator such as Renode, which will
|
||||
print out fields and flags that get accessed, giving us greater visibility into
|
||||
what a program is doing.
|
||||
</aside>
|
||||
</section>
|
||||
lxsocdoc
|
||||
intro to litex/migen
|
||||
concept of mixins
|
||||
|
Reference in New Issue
Block a user