index: add lots more sections
Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
e98a3193cd
commit
04d15f7970
124
index.html
124
index.html
@ -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 -->
|
||||
|
Loading…
Reference in New Issue
Block a user