index: add lots more sections

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2020-01-02 16:33:07 +08:00
parent e98a3193cd
commit 04d15f7970
1 changed files with 99 additions and 25 deletions

View File

@ -134,6 +134,12 @@
<section>
<h2>Talk Outline</h2>
<ol>
<li>How to write HDL Code</li>
<li>Rationale behind <tt>lxsocdoc</tt></li>
<li>Examples of <tt>lxsocdoc</tt></li>
<li>Benefits of this approach</li>
</ol>
<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
@ -144,14 +150,27 @@
<section>
<h2>Motivation</h2>
<aside class="notes">
<pre><code class="hljs cpp">// Hardware definitions of the SoC. Also is the main repo of
// documentation for the programmer-centric view
// of the hardware.
/* Start of memory range for the UART peripheral */
#define UART_OFFSET 0x10000000
/* Offset of the data register for the debug UART. A write
here will send the data out of the UART. A write when a
send is going on will halt the processor until the send is
completed. A read will receive any byte that was received
by the UART since the last read, or 0xFFFFFFFF when none
was. There is no receive buffer, so it's possible to miss
data if you don't poll frequently enough.
The debug UART is always configured as 8N1. */
#define UART_DATA_REG 0x00</code></pre>
<p><tt>mach_defines.h</tt>, Hackaday 2019 Con Badge</p>
<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>
@ -169,7 +188,11 @@
</section>
<section>
<h2>LiteX Primitives</h2>
<h2>LiteX Primitives</h2>
<pre><code class="python" data-trim>class GPIOOut(Module, AutoCSR):
def __init__(self, signal):
self._out = CSRStorage(len(signal))
self.comb += signal.eq(self._out.storage)</code></pre>
<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
@ -183,16 +206,16 @@
<h4>SPI Bitbang Module</h4>
<pre><code class="python" data-trim>self.bitbang = CSRStorage(4)
If(self.bitbang.storage[3],
dq.oe.eq(0)
dq.oe.eq(0)
).Else(
dq.oe.eq(1)
dq.oe.eq(1)
),
# CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
If(self.bitbang.storage[1],
self.miso.status.eq(dq.i[1])
self.miso.status.eq(dq.i[1])
),
dq.o.eq(
Cat(self.bitbang.storage[0], Replicate(1, spi_width-1))
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
@ -221,18 +244,18 @@ dq.o.eq(
<section>
<h4>New Register Definition</h4>
<pre><code class="python" data-trim>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.
""")</code></pre>
CSRField("mosi", description="Output value for MOSI..."
CSRField("clk", description="Output value for SPI CLK..."
CSRField("cs_n", description="Output value for SPI C..."
CSRField("dir", description="Sets the dir...", 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.""")</code></pre>
<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
@ -245,16 +268,16 @@ dq.o.eq(
<section>
<h4>Refactored SPI Bitbang</h4>
<pre><code class="python" data-trim>If(self.bitbang.fields.dir,
dq.oe.eq(0)
dq.oe.eq(0)
).Else(
dq.oe.eq(1)
dq.oe.eq(1)
),
# CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
If(self.bitbang.fields.clk,
self.miso.status.eq(dq.i[1])
self.miso.status.eq(dq.i[1])
),
dq.o.eq(
Cat(self.bitbang.fields.mosi, Replicate(1, spi_width-1))
Cat(self.bitbang.fields.mosi, Replicate(1, spi_width-1))
)</code></pre>
<aside class="notes">
Now the actual bitbang logic looks like this.
@ -266,7 +289,8 @@ dq.o.eq(
</section>
<section>
<h2>Generating a Manual</h2>
<h2>Generating a Manual</h2>
<img data-src="img/lxspi_bitbang.png">
<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
@ -286,8 +310,27 @@ dq.o.eq(
</aside>
</section>
<section>
<h2>Undocumented Fields</h2>
<aside class="notes">
It turns out that there is enough information that we can extract that
even undocumented fields are somewhat useful.
</aside>
</section>
<section>
<h2>Interrupts</h2>
<img data-src="img/interrupts.png">
<aside class="notes">
We can even extract interrupt information, including which bits inside an
interrupt register map to which event, and which interrupt number is assigned
to a given module.
</aside>
</section>
<section>
<h2>More Documentation: ModuleDoc</h2>
<img data-src="img/timer0-doc.png">
<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
@ -299,6 +342,7 @@ dq.o.eq(
<section>
<h2>Protocol Documentation</h2>
<img data-src="img/usb-wishbone.png">
<aside class="notes">
We can add additional documentation such as protocol waveforms. Here
we use WaveDrom to define the protocol of Wishbone-over-SPI. There
@ -336,6 +380,36 @@ dq.o.eq(
what a program is doing.
</aside>
</section>
<section>
<h2>Benefits of Higher Level Languages</h2>
<aside class="notes">
By using a higher level language, we are able to describe the hardware
in greater detail than if we used Verilog or VHDL. We can add additional
fields to our register definition fields to provide nice, human-readable
documentation. This also allows us to generate machine-readable formats
such as SVD, which opens up a whole world of software.
</aside>
</section>
<section>
<h2>Documentation helps you</h2>
<h2>Documentation helps others</h2>
<aside class="notes">
Documenting your hardware is important because it is necessary for you to
write software that interfaces with it today, and it helps you work with
others when it comes time to share your design with the world. By
properly documenting various fields within your module, you make it easier
on yourself to interact with today, and you make it easier to let others
get up to speed in the future. By documenting your hardware, you're helping
to pay it forward.
</aside>
</section>
<section>
<h2>Thank you</h2>
<h3>Questions</h3>
</section>
</div>
</div> <!-- class="reveal" -->
<!-- End of main presentation -->