naps.soc
The naps.soc package contains mainly infrastructure for accessing stuff in the FPGA at runtime via a CPU. That CPU can currently be either the Zynq ARM CPU or a host computer via JTAG. This is especially handy for debugging but also for runtime configuration of the FPGA. There is also a poster motivating and explaining the naps.soc functionality.
Control and Status Registers (CSRs)
The most striking part of the naps.soc package is the CSR infrastructure.
naps chooses a very easy to use approach to CSRs: The special types
ContolSignal
and StatusSignal
beahve like normal Amaranth signals
but are wired up so that their values can be read (StatusSignal
) or written (ControlSignal
)
by the CPU. The CSRs are automatically collected during elaboration,
assigned addresses, and connected to a bus.
If we change our blinky example to use CSRs, we can do the following:
from amaranth import *
from naps import *
class Top(Elaboratable):
runs_on = [Colorlight5a75b70Platform]
def __init__(self):
self.led = ControlSignal()
def elaborate(self, platform):
m = Module()
m.d.comb += platform.request("user_led").o.eq(self.led)
return m
if __name__ == "__main__":
cli(Top)
Thats all we have to do.
“Pydriver”
If we now run our design with the additional --run
flag, we are dropped into a
python shell that runs on the CPU side.
In this shell we can access all CSRs as normal python attributes on the
design
object. For example, we can do:
>>> design.led = 1 # turn on the LED
>>> design.led # read the LED state
1
If we now want to bring back the blinking LED, we can add a method annotated with
@pydriver
to our design. This method could then toggle the LED every second:
from amaranth import *
from naps import *
class Top(Elaboratable):
runs_on = [Colorlight5a75b70Platform]
def __init__(self):
self.led = ControlSignal()
def elaborate(self, platform):
m = Module()
m.d.comb += platform.request("user_led").o.eq(self.led)
return m
@pydriver
def blink(self):
from time import sleep
while True:
self.led = 1
sleep(1)
self.led = 0
sleep(1)
if __name__ == "__main__":
cli(Top)
If we now run our design and enter design.blink()
in the python shell, we can see the LED blink again.
During the elaboration phase, all functions with the @pydriver
decorator are collected and added to the fatbitstream.
Fatbitstreams
These functionalities are enabled by what we call “fatbitstreams”. Fatbitstreams bundle a bitstream for the FPGA with code to program the FPGA and python driver code to interact with the design.