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>
|
<section>
|
||||||
<h2>Talk Outline</h2>
|
<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">
|
<aside class="notes">
|
||||||
I'll briefly cover various methods of writing HDL code, then cover the rationale
|
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
|
behind the approach we take with lxsocdoc, then give an example of how to use
|
||||||
@ -144,14 +150,27 @@
|
|||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Motivation</h2>
|
<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,
|
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,
|
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
|
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
|
the HDL and/or C header files. This works, but we can do better. We just need to
|
||||||
describe the hardware better.
|
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>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@ -169,7 +188,11 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<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">
|
<aside class="notes">
|
||||||
In LiteX, two of the primitives used to expose hardware registers to the CPU softcore
|
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
|
are CSRStorage and CSRStatus. Instead of manually wiring up a crossbar and decoding
|
||||||
@ -183,16 +206,16 @@
|
|||||||
<h4>SPI Bitbang Module</h4>
|
<h4>SPI Bitbang Module</h4>
|
||||||
<pre><code class="python" data-trim>self.bitbang = CSRStorage(4)
|
<pre><code class="python" data-trim>self.bitbang = CSRStorage(4)
|
||||||
If(self.bitbang.storage[3],
|
If(self.bitbang.storage[3],
|
||||||
dq.oe.eq(0)
|
dq.oe.eq(0)
|
||||||
).Else(
|
).Else(
|
||||||
dq.oe.eq(1)
|
dq.oe.eq(1)
|
||||||
),
|
),
|
||||||
# CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
|
# CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
|
||||||
If(self.bitbang.storage[1],
|
If(self.bitbang.storage[1],
|
||||||
self.miso.status.eq(dq.i[1])
|
self.miso.status.eq(dq.i[1])
|
||||||
),
|
),
|
||||||
dq.o.eq(
|
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>
|
)</code></pre>
|
||||||
<aside class="notes">
|
<aside class="notes">
|
||||||
This works well, but exposes a new problem: Documentation. As an example, I was
|
This works well, but exposes a new problem: Documentation. As an example, I was
|
||||||
@ -221,18 +244,18 @@ dq.o.eq(
|
|||||||
<section>
|
<section>
|
||||||
<h4>New Register Definition</h4>
|
<h4>New Register Definition</h4>
|
||||||
<pre><code class="python" data-trim>self.bitbang = CSRStorage(4, fields=[
|
<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("mosi", description="Output value for MOSI..."
|
||||||
CSRField("clk", description="Output value for SPI CLK pin."),
|
CSRField("clk", description="Output value for SPI CLK..."
|
||||||
CSRField("cs_n", description="Output value for SPI CSn pin."),
|
CSRField("cs_n", description="Output value for SPI C..."
|
||||||
CSRField("dir", description="Sets the direction for *ALL* SPI data pins except CLK and CSn.", values=[
|
CSRField("dir", description="Sets the dir...", values=[
|
||||||
("0", "OUT", "SPI pins are all output"),
|
("0", "OUT", "SPI pins are all output"),
|
||||||
("1", "IN", "SPI pins are all input"),
|
("1", "IN", "SPI pins are all input"),
|
||||||
])
|
])
|
||||||
], description="""
|
], description="""Bitbang controls for SPI output. Only
|
||||||
Bitbang controls for SPI output. Only standard 1x SPI is supported, and as
|
standard 1x SPI is supported, and as a result all
|
||||||
a result all four wires are ganged together. This means that it is only possible
|
four wires are ganged together. This means that it
|
||||||
to perform half-duplex operations, using this SPI core.
|
is only possible to perform half-duplex operations,
|
||||||
""")</code></pre>
|
using this SPI core.""")</code></pre>
|
||||||
<aside class="notes">
|
<aside class="notes">
|
||||||
This is when I hit upon the idea of `lxsocdoc`. The basic idea is that
|
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
|
Python is really good at introspecting Python, so let's add a little bit
|
||||||
@ -245,16 +268,16 @@ dq.o.eq(
|
|||||||
<section>
|
<section>
|
||||||
<h4>Refactored SPI Bitbang</h4>
|
<h4>Refactored SPI Bitbang</h4>
|
||||||
<pre><code class="python" data-trim>If(self.bitbang.fields.dir,
|
<pre><code class="python" data-trim>If(self.bitbang.fields.dir,
|
||||||
dq.oe.eq(0)
|
dq.oe.eq(0)
|
||||||
).Else(
|
).Else(
|
||||||
dq.oe.eq(1)
|
dq.oe.eq(1)
|
||||||
),
|
),
|
||||||
# CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
|
# CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
|
||||||
If(self.bitbang.fields.clk,
|
If(self.bitbang.fields.clk,
|
||||||
self.miso.status.eq(dq.i[1])
|
self.miso.status.eq(dq.i[1])
|
||||||
),
|
),
|
||||||
dq.o.eq(
|
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>
|
)</code></pre>
|
||||||
<aside class="notes">
|
<aside class="notes">
|
||||||
Now the actual bitbang logic looks like this.
|
Now the actual bitbang logic looks like this.
|
||||||
@ -266,7 +289,8 @@ dq.o.eq(
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Generating a Manual</h2>
|
<h2>Generating a Manual</h2>
|
||||||
|
<img data-src="img/lxspi_bitbang.png">
|
||||||
<aside class="notes">
|
<aside class="notes">
|
||||||
After the design is elaborated and the output file is generated, we can iterate
|
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
|
through the resulting tree and pick out any CSR objects and using any additional
|
||||||
@ -286,8 +310,27 @@ dq.o.eq(
|
|||||||
</aside>
|
</aside>
|
||||||
</section>
|
</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>
|
<section>
|
||||||
<h2>More Documentation: ModuleDoc</h2>
|
<h2>More Documentation: ModuleDoc</h2>
|
||||||
|
<img data-src="img/timer0-doc.png">
|
||||||
<aside class="notes">
|
<aside class="notes">
|
||||||
So now we have register documentation. Can we do better? Of course we can.
|
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
|
SoC reference manuals are more than just register definitions. They also include
|
||||||
@ -299,6 +342,7 @@ dq.o.eq(
|
|||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Protocol Documentation</h2>
|
<h2>Protocol Documentation</h2>
|
||||||
|
<img data-src="img/usb-wishbone.png">
|
||||||
<aside class="notes">
|
<aside class="notes">
|
||||||
We can add additional documentation such as protocol waveforms. Here
|
We can add additional documentation such as protocol waveforms. Here
|
||||||
we use WaveDrom to define the protocol of Wishbone-over-SPI. There
|
we use WaveDrom to define the protocol of Wishbone-over-SPI. There
|
||||||
@ -336,6 +380,36 @@ dq.o.eq(
|
|||||||
what a program is doing.
|
what a program is doing.
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</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>
|
||||||
</div> <!-- class="reveal" -->
|
</div> <!-- class="reveal" -->
|
||||||
<!-- End of main presentation -->
|
<!-- End of main presentation -->
|
||||||
|
Loading…
Reference in New Issue
Block a user