Skip to content

Commit

Permalink
applet.radio.sx1276: new applet. (WIP)
Browse files Browse the repository at this point in the history
Co-authored-by: elena <[email protected]>
  • Loading branch information
whitequark and elena-v2 committed Oct 22, 2023
1 parent 0a5ec89 commit 0403785
Show file tree
Hide file tree
Showing 4 changed files with 762 additions and 1 deletion.
2 changes: 1 addition & 1 deletion docs/archive
Submodule archive updated 28 files
+ G00059/ina233.pdf
+ G00060/APX811_812_Rev.2.pdf
+ G00061/CAT24M01-D_Rev.5.PDF
+ G00062/BL24C256A_1.91.pdf
+ G00063/DAC081C08x_SNAS449F_Revision_F.pdf
+ G00064/PCA6408A_Rev.1.pdf
+ G00065/sn74lvc1t45_SCES515L_Revision_L.pdf
+ G00066/tlv733p_SBVS320A_Revision_A.pdf
+ G00067/tpd3s0x4_SLVSCP4B_Revision_B.pdf
+ G00068/TPS731xx_SBVS034N_Revision_N.pdf
+ ...014A_CY7C68015A_CY7C68016A_EZ-USB_FX2LP_USB_MICROCONTROLLER_HIGH_SPEED_USB_PERIPHERAL_CONTROLLER_Rev.AC.pdf
+ G00070/iCE40-LP-HX-Family-Data-Sheet_Revision_3.6.pdf
+ G00071/ICETechnologyLibrary_Version_3.0.pdf
+ G00072/Lattice-PCB-Layout-Recommandations-BGA-Packages_Revision_4.4.pdf
+ G00073/iCE40-Programming-Configuration_Revision_3.2.pdf
+ G00074/iCE40-Hardware-Checklist_Revision_1.8.pdf
+ G00075/Lattice-Package-Diagrams_Revision_6.0.pdf
+ G00076/Memory-Usage-Guide-for-iCE40-Devices_Revision_1.7.pdf
+ G00077/iCE40-sysCLOCK-PLL-Design-Usage-Guide_Revision_1.2.pdf
+ G00078/Lattice-Solder-Reflow-Guide-for-Surface-Mount-Devices_Revision_4.2.pdf
+ G00079/Using_Differential_IOLVDS_Sub-LVDS_in_iCE40_Devices_Revision_1.5.pdf
+ G00080/EZ-USB_TECHNICAL_REFERENCE_MANUAL_Revision_G.pdf
+ G00081/ds055.pdf
+ G00082/ds056.pdf
+ G00083/Sensirion_Environmental_Sensor_Node_SEN5x_Datasheet.pdf
+ G00084/TNxxx - STUSB4500 NVM Description_V1.1.pdf
+ G00085/ieee-standard-for-higher-performance-protocol-for-the-standard-d.pdf
+ G00086/DS_SX1276-7-8-9_W_APP_V7.pdf
141 changes: 141 additions & 0 deletions software/glasgow/applet/radio/sx1276/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Ref: SX1276/77/78/79 - 137 MHz to 1020 MHz Low Power Long Range Transceiver Datasheet
# Ref: https://www.semtech.com/products/wireless-rf/lora-connect/sx1276#documentation
# Accession: G00086

import math
import asyncio
import logging
from amaranth import *

from ....support.logging import *
from ....arch.sx1276 import *
from ...interface.spi_controller import SPIControllerSubtarget, SPIControllerInterface
from ... import *


class RadioSX1276Error(GlasgowAppletError):
pass


class RadioSX1276Subtarget(Elaboratable):
def __init__(self, spi_subtarget, reset_t, dut_reset):
self.spi_subtarget = spi_subtarget
self.reset_t = reset_t
self.dut_reset = dut_reset

def elaborate(self, platform):
m = Module()

m.submodules.spi_subtarget = self.spi_subtarget

m.d.comb += [
self.reset_t.o.eq(~self.dut_reset),
self.reset_t.oe.eq(1),
]

return m


class RadioSX1276Interface:
def __init__(self, interface, logger, device, addr_dut_reset):
self.lower = interface
self._logger = logger
self._level = logging.DEBUG if self._logger.name == __name__ else logging.TRACE
self._device = device
self._addr_dut_reset = addr_dut_reset

def _log(self, message, *args) -> None:
self._logger.log(self._level, "SX1276: " + message, *args)

async def _set_reset(self, reset) -> None:
await self._device.write_register(self._addr_dut_reset, int(reset))

async def reset(self) -> None:
self._log("reset")
await self.lower.synchronize()
await self._set_reset(True)
await self.lower.delay_us(100) # TODO: reference DS
await self.lower.synchronize()
await self._set_reset(False)
await self.lower.delay_us(500) # TODO: reference DS
await self.lower.synchronize()

async def read_register(self, address: int, *, length=1) -> int:
assert address in range(0x7f) and length >= 1
await self.lower.write([(0<<7)|address], hold_ss=True)
value = int.from_bytes(await self.lower.read(length), byteorder="big")
self._log("read register [%02x]=<%.*x>", address, length * 2, value)
return value

async def write_register(self, address: int, value: int, *, length=1) -> None:
assert address in range(0x7f) and isinstance(value, int) and length >= 1
self._log("write register [%02x]=<%.*x>", address, length * 2, value)
await self.lower.write([(1<<7)|address, *value.to_bytes(length)])

async def identify(self):
version = await self.read_register(Addr.RegVersion)
self._log("version=%02x", version)
if version != Version.ProductionRevision:
raise RadioSX1276Error(f"incorrect version {version:#x} returned by device")
return version


class RadioSX1276Applet(GlasgowApplet):
logger = logging.getLogger(__name__)
help = "transmit and receive using SX1276/7/8/9 RF PHYs"
description = """
Transmit and receive packets using the SX1276/7/8/9 RF PHYs. SX1276 is the base device, while
SX1277/8/9 are the lower cost, reduced functionality, interface compatible versions of the same.
[TODO: expand]
"""

__pins = ("cs", "sck", "copi", "cipo", "reset", "dio0")

@classmethod
def add_build_arguments(cls, parser, access):
access.add_build_arguments(parser)

access.add_pin_argument(parser, "cs", required=True, default=True)
access.add_pin_argument(parser, "sck", required=True, default=True)
access.add_pin_argument(parser, "copi", required=True, default=True)
access.add_pin_argument(parser, "cipo", required=True, default=True)
access.add_pin_argument(parser, "reset", required=False, default=True)
access.add_pin_argument(parser, "dio0", required=False, default=True) # currently unused

parser.add_argument(
"-f", "--frequency", metavar="FREQ", type=int, default=1000,
help="set SPI frequency to FREQ kHz (default: %(default)s)")

def build(self, target, args):
dut_reset, self.__addr_dut_reset = target.registers.add_rw(1)

self.mux_interface = iface = target.multiplexer.claim_interface(self, args)
pads = iface.get_pads(args, pins=self.__pins)

spi_subtarget = SPIControllerSubtarget(
pads=pads,
out_fifo=iface.get_out_fifo(),
in_fifo=iface.get_in_fifo(),
period_cyc=math.ceil(target.sys_clk_freq / (args.frequency * 1000)),
delay_cyc=math.ceil(target.sys_clk_freq / 1e6),
sck_idle=0,
sck_edge="rising",
cs_active=0,
)
sx1276_subtarget = RadioSX1276Subtarget(spi_subtarget, pads.reset_t, dut_reset)
return iface.add_subtarget(sx1276_subtarget)

async def run(self, device, args):
iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args)
spi_iface = SPIControllerInterface(iface, self.logger)
sx1276_iface = RadioSX1276Interface(spi_iface, self.logger, device,
self.__addr_dut_reset)
return sx1276_iface

# -------------------------------------------------------------------------------------------------

class RadioSX1276AppletTestCase(GlasgowAppletTestCase, applet=RadioSX1276Applet):
@synthesis_test
def test_build(self):
self.assertBuilds()
Loading

0 comments on commit 0403785

Please sign in to comment.