coredevice/spi2: port to NAC3

This commit is contained in:
Sebastien Bourdeauducq 2021-11-10 17:23:44 +08:00
parent b7313ddc32
commit 31955d0c7a
1 changed files with 36 additions and 26 deletions

View File

@ -7,8 +7,10 @@ Output event replacement is not supported and issuing commands at the same
time is an error. time is an error.
""" """
from artiq.language.core import syscall, kernel, portable, delay_mu from numpy import int32, int64
from artiq.language.types import TInt32, TNone
from artiq.language.core import nac3, KernelInvariant, kernel, portable, extern
from artiq.coredevice.core import Core
from artiq.coredevice.rtio import rtio_output, rtio_input_data from artiq.coredevice.rtio import rtio_output, rtio_input_data
@ -33,6 +35,7 @@ SPI_LSB_FIRST = 0x40
SPI_HALF_DUPLEX = 0x80 SPI_HALF_DUPLEX = 0x80
@nac3
class SPIMaster: class SPIMaster:
"""Core device Serial Peripheral Interface (SPI) bus master. """Core device Serial Peripheral Interface (SPI) bus master.
@ -62,23 +65,25 @@ class SPIMaster:
:meth:`update_xfer_duration_mu` :meth:`update_xfer_duration_mu`
:param core_device: Core device name :param core_device: Core device name
""" """
kernel_invariants = {"core", "ref_period_mu", "channel"} core: KernelInvariant[Core]
ref_period_mu: KernelInvariant[int64]
channel: KernelInvariant[int32]
xfer_duration_mu: int64
def __init__(self, dmgr, channel, div=0, length=0, core_device="core"): def __init__(self, dmgr, channel, div=0, length=0, core_device="core"):
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
self.ref_period_mu = self.core.seconds_to_mu( self.ref_period_mu = self.core.seconds_to_mu(self.core.coarse_ref_period)
self.core.coarse_ref_period)
assert self.ref_period_mu == self.core.ref_multiplier assert self.ref_period_mu == self.core.ref_multiplier
self.channel = channel self.channel = channel
self.update_xfer_duration_mu(div, length) self.update_xfer_duration_mu(div, length)
@portable @portable
def frequency_to_div(self, f): def frequency_to_div(self, f: float) -> int32:
"""Convert a SPI clock frequency to the closest SPI clock divider.""" """Convert a SPI clock frequency to the closest SPI clock divider."""
return int(round(1/(f*self.core.mu_to_seconds(self.ref_period_mu)))) return round(1./(f*self.core.mu_to_seconds(self.ref_period_mu)))
@kernel @kernel
def set_config(self, flags, length, freq, cs): def set_config(self, flags: int32, length: int32, freq: float, cs: int32):
"""Set the configuration register. """Set the configuration register.
* If ``SPI_CS_POLARITY`` is cleared (``cs`` active low, the default), * If ``SPI_CS_POLARITY`` is cleared (``cs`` active low, the default),
@ -145,7 +150,7 @@ class SPIMaster:
self.set_config_mu(flags, length, self.frequency_to_div(freq), cs) self.set_config_mu(flags, length, self.frequency_to_div(freq), cs)
@kernel @kernel
def set_config_mu(self, flags, length, div, cs): def set_config_mu(self, flags: int32, length: int32, div: int32, cs: int32):
"""Set the ``config`` register (in SPI bus machine units). """Set the ``config`` register (in SPI bus machine units).
.. seealso:: :meth:`set_config` .. seealso:: :meth:`set_config`
@ -162,17 +167,18 @@ class SPIMaster:
Or number of the chip select to assert if ``cs`` is decoded Or number of the chip select to assert if ``cs`` is decoded
downstream. (reset=0) downstream. (reset=0)
""" """
if length > 32 or length < 1: # NAC3TODO
raise ValueError("Invalid SPI transfer length") #if length > 32 or length < 1:
if div > 257 or div < 2: # raise ValueError("Invalid SPI transfer length")
raise ValueError("Invalid SPI clock divider") #if div > 257 or div < 2:
# raise ValueError("Invalid SPI clock divider")
rtio_output((self.channel << 8) | SPI_CONFIG_ADDR, flags | rtio_output((self.channel << 8) | SPI_CONFIG_ADDR, flags |
((length - 1) << 8) | ((div - 2) << 16) | (cs << 24)) ((length - 1) << 8) | ((div - 2) << 16) | (cs << 24))
self.update_xfer_duration_mu(div, length) self.update_xfer_duration_mu(div, length)
delay_mu(self.ref_period_mu) delay_mu(self.ref_period_mu)
@portable @portable
def update_xfer_duration_mu(self, div, length): def update_xfer_duration_mu(self, div: int32, length: int32):
"""Calculate and set the transfer duration. """Calculate and set the transfer duration.
This method updates the SPI transfer duration which is used This method updates the SPI transfer duration which is used
@ -195,10 +201,10 @@ class SPIMaster:
:param div: SPI clock divider (see: :meth:`set_config_mu`) :param div: SPI clock divider (see: :meth:`set_config_mu`)
:param length: SPI transfer length (see: :meth:`set_config_mu`) :param length: SPI transfer length (see: :meth:`set_config_mu`)
""" """
self.xfer_duration_mu = ((length + 1)*div + 1)*self.ref_period_mu self.xfer_duration_mu = int64((length + 1)*div + 1)*self.ref_period_mu
@kernel @kernel
def write(self, data): def write(self, data: int32):
"""Write SPI data to shift register register and start transfer. """Write SPI data to shift register register and start transfer.
* The ``data`` register and the shift register are 32 bits wide. * The ``data`` register and the shift register are 32 bits wide.
@ -226,7 +232,7 @@ class SPIMaster:
delay_mu(self.xfer_duration_mu) delay_mu(self.xfer_duration_mu)
@kernel @kernel
def read(self): def read(self) -> int32:
"""Read SPI data submitted by the SPI core. """Read SPI data submitted by the SPI core.
For bit alignment and bit ordering see :meth:`set_config`. For bit alignment and bit ordering see :meth:`set_config`.
@ -238,21 +244,22 @@ class SPIMaster:
return rtio_input_data(self.channel) return rtio_input_data(self.channel)
@syscall(flags={"nounwind", "nowrite"}) @extern
def spi_set_config(busno: TInt32, flags: TInt32, length: TInt32, div: TInt32, cs: TInt32) -> TNone: def spi_set_config(busno: int32, flags: int32, length: int32, div: int32, cs: int32):
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"}) @extern
def spi_write(busno: TInt32, data: TInt32) -> TNone: def spi_write(busno: int32, data: int32):
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"}) @extern
def spi_read(busno: TInt32) -> TInt32: def spi_read(busno: int32) -> int32:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@nac3
class NRTSPIMaster: class NRTSPIMaster:
"""Core device non-realtime Serial Peripheral Interface (SPI) bus master. """Core device non-realtime Serial Peripheral Interface (SPI) bus master.
Owns one non-realtime SPI bus. Owns one non-realtime SPI bus.
@ -265,12 +272,15 @@ class NRTSPIMaster:
See :class:`SPIMaster` for a description of the methods. See :class:`SPIMaster` for a description of the methods.
""" """
core: KernelInvariant[Core]
busno: KernelInvariant[int32]
def __init__(self, dmgr, busno=0, core_device="core"): def __init__(self, dmgr, busno=0, core_device="core"):
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
self.busno = busno self.busno = busno
@kernel @kernel
def set_config_mu(self, flags=0, length=8, div=6, cs=1): def set_config_mu(self, flags: int32 = 0, length: int32 = 8, div: int32 = 6, cs: int32 = 1):
"""Set the ``config`` register. """Set the ``config`` register.
Note that the non-realtime SPI cores are usually clocked by the system Note that the non-realtime SPI cores are usually clocked by the system
@ -280,9 +290,9 @@ class NRTSPIMaster:
spi_set_config(self.busno, flags, length, div, cs) spi_set_config(self.busno, flags, length, div, cs)
@kernel @kernel
def write(self, data=0): def write(self, data: int32 = 0):
spi_write(self.busno, data) spi_write(self.busno, data)
@kernel @kernel
def read(self): def read(self) -> int32:
return spi_read(self.busno) return spi_read(self.busno)