<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>PRBS23</title>
    <link>https://www.prbs23.com/blog/</link>
    <description>Recent content on PRBS23</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>prbs23@prbs23.com (prbs23)</managingEditor>
    <webMaster>prbs23@prbs23.com (prbs23)</webMaster>
    <lastBuildDate>Thu, 13 Aug 2026 21:05:23 -0600</lastBuildDate><atom:link href="https://www.prbs23.com/blog/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Reverse Engineering the Gravity 800 Smoker Controller</title>
      <link>https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/</link>
      <pubDate>Thu, 13 Aug 2026 21:05:23 -0600</pubDate>
      <author>prbs23@prbs23.com (prbs23)</author>
      <guid>https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/</guid>
      <description>&lt;p&gt;I recently released &lt;a href=&#34;https://www.prbs23.com/blog/posts/announcement-freefall-800/&#34;&gt;FreeFall 800, open source firmware for the Gravity 800&lt;/a&gt; smoker/grill from Masterbuilt. The project started because I needed to open the controller to fix a broken wire. That is when I noticed that inside was a standard ESP32 module, with a plainly visible programming header. This made me very surprised to find that there were little to no details online about the controller&amp;rsquo;s internals. In this post I aim to document everything I managed to reverse engineer about the Gravity 800 controller board, and will discuss some of the very strange choices the designers made.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/board_back.jpg&#34; alt=&#34;Gravity 800 Controller Board. (I apologize for the image quality in this post, I thought my phone camera was better)&#34; style=&#34;max-height:450px&#34;/&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/board_back.jpg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Gravity 800 Controller Board. (I apologize for the image quality in this post, I thought my phone camera was better)
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;At a high level the Gravity 800 controller board has the following major components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ESP32 WiFi microcontroller module&lt;/li&gt;
&lt;li&gt;Four character, 16 segment LCD display with backlight&lt;/li&gt;
&lt;li&gt;Five button and knob encoder physical inputs&lt;/li&gt;
&lt;li&gt;Piezo buzzer for temperature and timer alarms&lt;/li&gt;
&lt;li&gt;Grill lid open switch&lt;/li&gt;
&lt;li&gt;Thermocouple ADC for smoker chamber temperature, with thermistor for cold junction measurement&lt;/li&gt;
&lt;li&gt;Four thermistor temperature probe ADC inputs&lt;/li&gt;
&lt;li&gt;12-volt blower fan motor driver&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately on first investigation, other than the MCU which is specifically an &lt;a href=&#34;https://documentation.espressif.com/esp32-wroom-32e_esp32-wroom-32ue_datasheet_en.html&#34;&gt;ESP32-WROOM-32UE&lt;/a&gt; module, and the LCD driver which appears to be a &lt;a href=&#34;https://ms.i-coreic.com/storage/uploads/pdf/202508/27/CS1621-DATASHEET-.pdf&#34;&gt;CS1621&lt;/a&gt;, the rest of the ICs are either unmarked or do not have specific enough markings for me to find data sheets or documentation. This meant that not only did I need to reverse the PCB connectivity to build FreeFall 800, I also ended up having to reverse engineer the serial protocols, at least enough to emulate them.&lt;/p&gt;
&lt;p&gt;I will go through the reverse engineering process in detail below, but in the end I did end up with a full schematic of the Gravity 800 controller board. Some component values are missing (mainly unmarked capacitors and inductors, since I didn&amp;rsquo;t want to desolder them), but references should be accurate. A KiCAD project with the schematic is published in my reverse engineering repository here: &lt;a href=&#34;https://gitlab.com/prbs23/gravity-800-reverse-engineering&#34;&gt;prbs23/gravity-800-reverse-engineering&lt;/a&gt;.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/controller_reversed.svg&#34; alt=&#34;Full schematic of Gravity 800 controller board&#34; style=&#34;max-height:500px&#34;/&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/controller_reversed.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Full schematic of Gravity 800 controller board
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h2 id=&#34;power-supply&#34;&gt;Power Supply&lt;/h2&gt;
&lt;p&gt;The easiest place to start reversing the board was the power input. Not surprisingly, there does not appear to be anything too interesting going on with the power supply.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_power_supply.svg&#34; alt=&#34;Power Supply Schematic&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_power_supply.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Power Supply Schematic
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;On the input side we have a reverse voltage protection diode and a couple of capacitors to produce the 12 volt rail that both feeds the 3.3 volt regulator and is used directly for the blower fan and buzzer. I was not able to identify what specific voltage regulator is being used, but it appears to be a pretty standard adjustable switching regulator configured to output 3.3V.&lt;/p&gt;
&lt;h2 id=&#34;microcontroller&#34;&gt;Microcontroller&lt;/h2&gt;
&lt;p&gt;After tracing the power supply, I moved on to the other part of the board that looked familiar: the ESP32 microcontroller. Specifically the controller uses the 8MB flash variant of the ESP32-WROOM-32UE module. In addition to the integrated WiFi and Bluetooth radios, the module has 32 GPIO that can also be used with various UART, SPI, PWM, and ADC peripherals.&lt;/p&gt;
&lt;p&gt;Obviously the first thing to do was to connect to the programming header and dump the firmware. The programming header was conveniently labeled &amp;ldquo;Programming&amp;rdquo; and had the following pin out:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Pin&lt;/th&gt;
          &lt;th&gt;Description&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;RX&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;2&lt;/td&gt;
          &lt;td&gt;TX&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;3&lt;/td&gt;
          &lt;td&gt;GPIO0/BOOT&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;4&lt;/td&gt;
          &lt;td&gt;GND&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;5&lt;/td&gt;
          &lt;td&gt;3.3V&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Like all ESP32s the programming header does double duty as a serial debug interface while the processor is running. You can boot into the programming boot loader by tying &lt;code&gt;GPIO0/BOOT&lt;/code&gt; low while applying power to the controller. In the first (of several) annoying decisions made by the designers, the reset (&lt;code&gt;EN&lt;/code&gt;) of the ESP32 is simply pulled high on the board, and not exposed on the programming header.  This means every time we want to program the part, it&amp;rsquo;s necessary to power cycle the board. That must have been annoying for their firmware developers.&lt;/p&gt;
&lt;p&gt;Dumping the firmware was as simple as installing &lt;code&gt;espflash&lt;/code&gt; and running:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;espflash read-flash --port /dev/ttyUSB0 --baud 115200 --chip esp32 0 0x800000 firmware_dump.bin
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is no flash protection or encryption enabled on the controller, so I now had the full flash image. I did end up doing some basic decompilation and tracing of the firmware in &lt;a href=&#34;https://github.com/nationalsecurityagency/ghidra&#34;&gt;Ghidra&lt;/a&gt; and Claude. However, other than some specific thermocouple calibration functions (discussed more later), I did not find much else in the firmware that was worth the effort of tracing in detail.&lt;/p&gt;
&lt;p&gt;The serial debug output did not have anything particularly helpful for reverse engineering. However it does expose some details of the infrastructure the mobile app is built on top of. Once connected to a WiFi network, this is a snippet of the (hopefully anonymized) messages that were logged:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:300px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;54
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;55
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;56
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;57
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;58
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;59
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;60
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;61
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;62
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;63
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;64
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;65
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;66
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;67
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;68
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;69
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;70
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;71
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;72
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;73
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;74
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;75
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;76
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;77
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;78
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;79
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;80
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;81
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;82
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;83
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;84
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;85
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(4890) wifi:new:&amp;lt;11,0&amp;gt;, old:&amp;lt;1,0&amp;gt;, ap:&amp;lt;255,255&amp;gt;, sta:&amp;lt;11,0&amp;gt;, prof:1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(5690) wifi:state: init -&amp;gt; auth (b0)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(5700) wifi:state: auth -&amp;gt; assoc (0)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(5700) wifi:state: assoc -&amp;gt; run (10)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(5740) wifi:connected with XXXX_SSID_XXXX, aid = 7, channel 11, BW20, bssid = xx:xx:xx:xx:xx:xx
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(5740) wifi:security: WPA2-PSK, phy: bgn, rssi: -72
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(5750) wifi:pm start, type: 2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;event_info.disconnected:11,event_id=4
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(5840) wifi:AP&amp;#39;s beacon interval = 102400 us, DTIM period = 2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;F_Rd_AdRegt[4]: 6291455
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(7270) BH: b5 80 00 00 00 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(7360) Main: DRAM:101540
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(8660) esp_netif_handlers: sta ip: ddd.ddd.ddd.ddd, mask: 255.255.254.0, gw: ddd.ddd.ddd.ddd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;event_info.disconnected:63,event_id=7
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(8660) WiFi: SYSTEM_EVENT_STA_GOT_IP
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(8670) WiFi: Got IP: ddd.ddd.ddd.ddd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(8670) AWS: start aws
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;keyAD=4092,coseblue=0,upStep=0,showback=0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(8960) Main: wifi_shown_contr=0,b4byte3bit1=0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(9280) BH: b5 86 00 00 00 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(9670) AWS: Connecting to AWS...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rssi:-73
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(10360) Main: DRAM:59132
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;F_Rd_AdRegt[4]: 8388607
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(11280) BH: b5 86 00 00 00 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(12810) AWS: Connected to AWS ok
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(12810) AWS: SubscrTopic:$aws/things/&amp;lt;XXX_DEVICE_ID_XXX&amp;gt;/jobs/get/+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(13280) BH: b5 8e 00 00 00 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(13330) AWS: SubscrTopic:$aws/things/&amp;lt;XXX_DEVICE_ID_XXX&amp;gt;/shadow/get/rejected
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(13360) Main: DRAM:59392
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;keyAD=4093,coseblue=0,upStep=0,showback=0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(13960) Main: wifi_shown_contr=0,b4byte3bit1=0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;F_Rd_AdRegt[4]: 8388607
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(15280) BH: b5 8e 00 00 00 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(15480) AWS: SubscrTopic:$aws/things/&amp;lt;XXX_DEVICE_ID_XXX&amp;gt;/shadow/get/accepted
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(15990) AWS: SubscrTopic:$aws/things/&amp;lt;XXX_DEVICE_ID_XXX&amp;gt;/shadow/update/document
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rssi:-75
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(16360) Main: DRAM:61320
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(16500) AWS: SubscrTopic:$aws/things/&amp;lt;XXX_DEVICE_ID_XXX&amp;gt;/shadow/update/rejected
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(16810) AWS: SubscrTopic:$aws/things/&amp;lt;XXX_DEVICE_ID_XXX&amp;gt;/shadow/update/delta
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(17280) BH: b5 9e 00 00 00 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;F_Rd_AdRegt[4]: 8388607
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(17920) AWS: desired------
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(17930) AWS: Update Status:$aws/things/&amp;lt;XXX_DEVICE_ID_XXX&amp;gt;/shadow/update   Msg:{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&amp;#34;state&amp;#34;:	{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&amp;#34;reported&amp;#34;:	{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;model&amp;#34;:	&amp;#34;C:G:P26:1:D&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;vers&amp;#34;:	&amp;#34;3.0.01&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;update&amp;#34;:	0,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;trigger&amp;#34;:	0,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;mac&amp;#34;:	&amp;#34;xxxxxxxxxxxx&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;pwrOn&amp;#34;:	false,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;fah&amp;#34;:	true,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;engaged&amp;#34;:	true,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;doorOpn&amp;#34;:	true,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;mainTemp&amp;#34;:	999,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;heat&amp;#34;:	{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;				&amp;#34;t2&amp;#34;:	{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;					&amp;#34;heating&amp;#34;:	false,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;					&amp;#34;intensity&amp;#34;:	0,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;					&amp;#34;trgt&amp;#34;:	225,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;					&amp;#34;max&amp;#34;:	700,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;					&amp;#34;min&amp;#34;:	150
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;				}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			},
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;probes&amp;#34;:	null,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;errors&amp;#34;:	[0, 0, 0, 0, 0],
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;timers&amp;#34;:	null,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;ssid&amp;#34;:	&amp;#34;XXXX_SSID_XXXX&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&amp;#34;RSSI&amp;#34;:	-75
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		},
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&amp;#34;desired&amp;#34;:	null
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(17970) AWS: Update Status length=507
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(18000) AWS: IoT_data_tsend 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(18000) AWS: Request Get:$aws/things/&amp;lt;XXX_DEVICE_ID_XXX&amp;gt;/shadow/get
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;(18220) AWS: get accept callback
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.... Repeat from keyAD line
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;

  
    &lt;figcaption&gt;&lt;span&gt;Serial log output from factory Gravity 800 firmware&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;The most obvious thing we can see from this log is that they are using the &lt;a href=&#34;https://aws.amazon.com/iot-core/&#34;&gt;AWS IoT Core&lt;/a&gt; managed MQTT message broker. Both from the log messages, and also some light decompiling it appears they are likely using the &lt;a href=&#34;https://github.com/espressif/esp-aws-iot&#34;&gt;Espressif esp-aws-iot&lt;/a&gt; library for doing this. Interestingly, this trace was captured while the controller was in the &amp;ldquo;off&amp;rdquo; state. So even when &amp;ldquo;off&amp;rdquo; the controller is still sending the full device state to AWS and Masterbuilt, including your WiFi router SSID and RSSI.&lt;/p&gt;
&lt;h3 id=&#34;pinout&#34;&gt;Pinout&lt;/h3&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_mcu.svg&#34; alt=&#34;MCU Schematic&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_mcu.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            MCU Schematic
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;While this was interesting, it was not particularly helpful for my reverse engineering effort. The next step was tracing all the pins of the ESP32 module to find what they were connected to. This is the full list of pin connections I ended up with after fully reverse engineering:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Pin&lt;/th&gt;
          &lt;th&gt;Name&lt;/th&gt;
          &lt;th&gt;Direction&lt;/th&gt;
          &lt;th&gt;Destination&lt;/th&gt;
          &lt;th&gt;Function&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;25&lt;/td&gt;
          &lt;td&gt;GPIO0&lt;/td&gt;
          &lt;td&gt;Input&lt;/td&gt;
          &lt;td&gt;Programming Header&lt;/td&gt;
          &lt;td&gt;Boot mode&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;34&lt;/td&gt;
          &lt;td&gt;U0RXD&lt;/td&gt;
          &lt;td&gt;Input&lt;/td&gt;
          &lt;td&gt;Programming Header&lt;/td&gt;
          &lt;td&gt;UART RX&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;35&lt;/td&gt;
          &lt;td&gt;U0TXD&lt;/td&gt;
          &lt;td&gt;Output&lt;/td&gt;
          &lt;td&gt;Programming Header&lt;/td&gt;
          &lt;td&gt;UART TX&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;10&lt;/td&gt;
          &lt;td&gt;GPIO25&lt;/td&gt;
          &lt;td&gt;Input&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#lid-switch&#34;&gt;Lid switch&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Lid switch (closed=low)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;27&lt;/td&gt;
          &lt;td&gt;GPIO16&lt;/td&gt;
          &lt;td&gt;Output&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#buzzer&#34;&gt;Buzzer&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Buzzer pulse (4kHz)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;28&lt;/td&gt;
          &lt;td&gt;GPIO17&lt;/td&gt;
          &lt;td&gt;Output&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#fan-driver&#34;&gt;Fan Driver&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Fan enable (active high)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;30&lt;/td&gt;
          &lt;td&gt;GPIO18&lt;/td&gt;
          &lt;td&gt;Input&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#knob-encoder&#34;&gt;Knob Encoder&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Encoder A&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;29&lt;/td&gt;
          &lt;td&gt;GPIO5&lt;/td&gt;
          &lt;td&gt;Input&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#knob-encoder&#34;&gt;Knob Encoder&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Encoder B&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;31&lt;/td&gt;
          &lt;td&gt;GPIO19&lt;/td&gt;
          &lt;td&gt;Input&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#knob-encoder&#34;&gt;Knob Encoder&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Encoder Push Button (unused)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;16&lt;/td&gt;
          &lt;td&gt;GPIO13&lt;/td&gt;
          &lt;td&gt;Output&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#display&#34;&gt;Display Controller&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Backlight Enable (active low)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;33&lt;/td&gt;
          &lt;td&gt;GPIO21&lt;/td&gt;
          &lt;td&gt;Output&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#display&#34;&gt;Display Controller&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Display CS (active low)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;36&lt;/td&gt;
          &lt;td&gt;GPIO22&lt;/td&gt;
          &lt;td&gt;Output&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#display&#34;&gt;Display Controller&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Display &lt;span style=&#34;text-decoration: overline&#34;&gt;WR&lt;/span&gt; (SCK)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;37&lt;/td&gt;
          &lt;td&gt;GPIO23&lt;/td&gt;
          &lt;td&gt;Output&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#display&#34;&gt;Display Controller&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Display SDA&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;12&lt;/td&gt;
          &lt;td&gt;GPIO27&lt;/td&gt;
          &lt;td&gt;Output&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#thermocouple-adc&#34;&gt;Thermocouple ADC&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Thermocouple ADC SCK&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;26&lt;/td&gt;
          &lt;td&gt;GPIO4&lt;/td&gt;
          &lt;td&gt;Bidir&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#thermocouple-adc&#34;&gt;Thermocouple ADC&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;Thermocouple ADC SDA&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;8&lt;/td&gt;
          &lt;td&gt;GPIO32&lt;/td&gt;
          &lt;td&gt;Input&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#uart-adc-subsystem&#34;&gt;UART ADC&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;ADC UART RX&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;7&lt;/td&gt;
          &lt;td&gt;GPIO35&lt;/td&gt;
          &lt;td&gt;Output&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;#uart-adc-subsystem&#34;&gt;UART ADC&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;ADC UART TX&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;One thing that I was surprised &lt;em&gt;NOT&lt;/em&gt; to find while probing the ESP32 pins was any of the control push buttons. Given the number of free pins I was expecting them to be directly connected, but after some tracing I found that the push buttons appeared to be multiplexed, and connected to what I have been calling the UART ADC.&lt;/p&gt;
&lt;h2 id=&#34;uart-adc-subsystem&#34;&gt;UART ADC Subsystem&lt;/h2&gt;
&lt;p&gt;Tracing the push buttons I ended up looking at the &lt;code&gt;U3&lt;/code&gt; chip on the controller, an unmarked 16-pin SOIC. This chip is connected to the ESP32, through what appeared to be a 9600 baud UART interface. While both TX and RX are connected between ICs, the ESP32 factory firmware never appears to send anything, and &lt;code&gt;U3&lt;/code&gt; automatically starts sending out a 16 byte message every 36 milliseconds when power is applied.&lt;/p&gt;
&lt;p&gt;Other than power and some decoupling, the remaining pins of &lt;code&gt;U3&lt;/code&gt; are connected to: The four temperature probe thermistor inputs, a board mounted NTC thermistor (which I eventually determined was used for the cold thermocouple cold junction temperature), and&amp;hellip; the push buttons for the controls. This all points towards &lt;code&gt;U3&lt;/code&gt; being an ADC; however, it is pretty rare for an ADC to use 9600 baud UART instead of SPI or I&lt;sup&gt;2&lt;/sup&gt;C. Of the few UART ADCs I could find, none matched the description of &lt;code&gt;U3&lt;/code&gt;. My best guess is that it&amp;rsquo;s a cheap MCU programmed to behave as an ADC for the ESP32. Why would they not just use a SPI or I&lt;sup&gt;2&lt;/sup&gt;C ADC? Why not just use the ADC inputs of the ESP32?&lt;/p&gt;
&lt;p&gt;Weird design decisions aside, I captured a couple of trace samples of the 16 byte messages the &amp;ldquo;UART ADC&amp;rdquo; was sending out:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:;&#34;&gt;
  &lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Byte #&lt;/th&gt;
          &lt;th&gt;0&lt;/th&gt;
          &lt;th&gt;1&lt;/th&gt;
          &lt;th&gt;2&lt;/th&gt;
          &lt;th&gt;3&lt;/th&gt;
          &lt;th&gt;4&lt;/th&gt;
          &lt;th&gt;5&lt;/th&gt;
          &lt;th&gt;6&lt;/th&gt;
          &lt;th&gt;7&lt;/th&gt;
          &lt;th&gt;8&lt;/th&gt;
          &lt;th&gt;9&lt;/th&gt;
          &lt;th&gt;10&lt;/th&gt;
          &lt;th&gt;11&lt;/th&gt;
          &lt;th&gt;12&lt;/th&gt;
          &lt;th&gt;13&lt;/th&gt;
          &lt;th&gt;14&lt;/th&gt;
          &lt;th&gt;15&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Frame 1&lt;/td&gt;
          &lt;td&gt;E1&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FD&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FA&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FB&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FB&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FA&lt;/td&gt;
          &lt;td&gt;08&lt;/td&gt;
          &lt;td&gt;03&lt;/td&gt;
          &lt;td&gt;00&lt;/td&gt;
          &lt;td&gt;00&lt;/td&gt;
          &lt;td&gt;1E&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Frame 2&lt;/td&gt;
          &lt;td&gt;E1&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FC&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FB&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FB&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FB&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FB&lt;/td&gt;
          &lt;td&gt;08&lt;/td&gt;
          &lt;td&gt;03&lt;/td&gt;
          &lt;td&gt;00&lt;/td&gt;
          &lt;td&gt;00&lt;/td&gt;
          &lt;td&gt;1F&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Frame 3&lt;/td&gt;
          &lt;td&gt;E1&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FC&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FB&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FC&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FA&lt;/td&gt;
          &lt;td&gt;0F&lt;/td&gt;
          &lt;td&gt;FB&lt;/td&gt;
          &lt;td&gt;08&lt;/td&gt;
          &lt;td&gt;03&lt;/td&gt;
          &lt;td&gt;00&lt;/td&gt;
          &lt;td&gt;00&lt;/td&gt;
          &lt;td&gt;1F&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

  
    &lt;figcaption&gt;&lt;span&gt;Three captured frames from the UART ADC&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Every message starts with a &lt;code&gt;0xE1&lt;/code&gt; start byte, followed by what appears to be 7 (odd?) two byte ADC sample values, and ending with a check byte. Bits &lt;code&gt;[15:12]&lt;/code&gt; of each sample value are zero, so everything is pointing towards this being a 12 bit ADC. The check byte appears to be calculated as: &lt;code&gt;check_byte = sum(frame[0:15]) % 256&lt;/code&gt;. This would make the frame format look something like:&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/uart_adc_protocol.svg&#34; alt=&#34;UART ADC Frame Format&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/uart_adc_protocol.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            UART ADC Frame Format
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The seven inputs of the ADC are mapped as follows:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Pin&lt;/th&gt;
          &lt;th&gt;Channel&lt;/th&gt;
          &lt;th&gt;Input&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;15&lt;/td&gt;
          &lt;td&gt;CH0&lt;/td&gt;
          &lt;td&gt;Multiplexed push buttons&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;14&lt;/td&gt;
          &lt;td&gt;CH1&lt;/td&gt;
          &lt;td&gt;Thermistor probe 1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;13&lt;/td&gt;
          &lt;td&gt;CH2&lt;/td&gt;
          &lt;td&gt;Thermistor probe 2&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;12&lt;/td&gt;
          &lt;td&gt;CH3&lt;/td&gt;
          &lt;td&gt;Thermistor probe 3&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;11&lt;/td&gt;
          &lt;td&gt;CH4&lt;/td&gt;
          &lt;td&gt;Thermistor probe 4&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;10&lt;/td&gt;
          &lt;td&gt;CH5&lt;/td&gt;
          &lt;td&gt;Thermocouple cold junction thermistor&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;9&lt;/td&gt;
          &lt;td&gt;CH6&lt;/td&gt;
          &lt;td&gt;Unused, tied to GND&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This schematic shows the whole UART ADC subsystem, with button multiplexing and thermistor conditioning.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_uart_adc.svg&#34; alt=&#34;UART ADC Subsystem Schematic&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_uart_adc.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            UART ADC Subsystem Schematic
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h3 id=&#34;multiplexed-push-buttons&#34;&gt;Multiplexed Push Buttons&lt;/h3&gt;
&lt;p&gt;Despite having plenty of free IO on the ESP32, and there only being 5 push buttons on the board, the designers chose to multiplex all the buttons through a single ADC input on the UART ADC. The ADC input is pulled up with a 7.5kΩ resistor, and then all 5 buttons pull the input down with a different strength resistor. The table below lists the pull down strengths, expected output voltage, as well as ADC thresholds I ended up using.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Button&lt;/th&gt;
          &lt;th&gt;Pulldown&lt;/th&gt;
          &lt;th&gt;Nominal Voltage&lt;/th&gt;
          &lt;th&gt;ADC Value&lt;/th&gt;
          &lt;th&gt;Positive Threshold&lt;/th&gt;
          &lt;th&gt;Negative Threshold&lt;/th&gt;
          &lt;th&gt;Window Size&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;None&lt;/td&gt;
          &lt;td&gt;N/A&lt;/td&gt;
          &lt;td&gt;3.3V&lt;/td&gt;
          &lt;td&gt;4095&lt;/td&gt;
          &lt;td&gt;N/A&lt;/td&gt;
          &lt;td&gt;3971&lt;/td&gt;
          &lt;td&gt;124&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Timer&lt;/td&gt;
          &lt;td&gt;100kΩ&lt;/td&gt;
          &lt;td&gt;3.069V&lt;/td&gt;
          &lt;td&gt;3810&lt;/td&gt;
          &lt;td&gt;3931&lt;/td&gt;
          &lt;td&gt;3452&lt;/td&gt;
          &lt;td&gt;379&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Up&lt;/td&gt;
          &lt;td&gt;22kΩ&lt;/td&gt;
          &lt;td&gt;2.461V&lt;/td&gt;
          &lt;td&gt;3054&lt;/td&gt;
          &lt;td&gt;3412&lt;/td&gt;
          &lt;td&gt;2375&lt;/td&gt;
          &lt;td&gt;1037&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Temp&lt;/td&gt;
          &lt;td&gt;5.1kΩ&lt;/td&gt;
          &lt;td&gt;1.336V&lt;/td&gt;
          &lt;td&gt;1657&lt;/td&gt;
          &lt;td&gt;2335&lt;/td&gt;
          &lt;td&gt;1312&lt;/td&gt;
          &lt;td&gt;1023&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Enter&lt;/td&gt;
          &lt;td&gt;2.2kΩ&lt;/td&gt;
          &lt;td&gt;0.748V&lt;/td&gt;
          &lt;td&gt;928&lt;/td&gt;
          &lt;td&gt;1272&lt;/td&gt;
          &lt;td&gt;624&lt;/td&gt;
          &lt;td&gt;648&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Down&lt;/td&gt;
          &lt;td&gt;510Ω&lt;/td&gt;
          &lt;td&gt;0.210V&lt;/td&gt;
          &lt;td&gt;260&lt;/td&gt;
          &lt;td&gt;574&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;594&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;To prevent button confusion, in my FreeFall 800 firmware, I chose my thresholds such that there is a 40 ADC code window between each button&amp;rsquo;s window where I do not detect any button. Due to the structure used, detecting multiple buttons pushed at once is sort of possible, but supporting it would narrow the ADC value windows for single button presses. I have not done the math to figure out which combinations are uniquely detectable.&lt;/p&gt;
&lt;h3 id=&#34;thermistors&#34;&gt;Thermistors&lt;/h3&gt;
&lt;p&gt;The other five (used) inputs to the UART ADC all come from NTC thermistors, which use the same structure: A voltage divider with fixed pullup and the thermistor as the pull down, followed by an RC low pass filter.&lt;/p&gt;
&lt;p&gt;The probe inputs use a 47kΩ pull up resistor, and the thermistors have a nominal 230kΩ resistance at 25°C. From the decompiled factory firmware, it appears to use a fixed β value of 4331, so the temperature can be resolved using the following formula:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:;&#34;&gt;
  &lt;div&gt;
$$
T_0 = 25.0{\degree}\text{C}, R_0 = 230k\Omega, \beta = 4331, R_{high} = 47\text{k}\Omega
$$
$$
R_{probe} = -\frac{R_{high} \cdot {adc\_value}}{{adc\_value} - 4096}
$$
$$
T_{probe} = \frac{1}{\frac{1}{T_0 + 273.15} + \frac{1}{\beta} * \log(\frac{R_{probe}}{R_0})} - 273.15
$$
&lt;/div&gt;

  
    &lt;figcaption&gt;&lt;span&gt;Equation for calculating probe temperature from raw ADC value&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;The final thermistor input is a board mounted NTC thermistor that is used as a cold junction temperature measurement for the thermocouple temperature calculation. This thermistor has a nominal 10kΩ resistance at 25°C, and uses the same input structure with a 10kΩ pull up resistor. The β value used by the factory firmware in this case is 4104. Because the same input structure is used, the same equation can be used to calculate temperature.&lt;/p&gt;
&lt;p&gt;From my testing, the physical implementation of this cold junction thermistor has some design problems. The most obvious is that it is not located anywhere near the actual thermistor junction. A bad design decision, but not the biggest issue. The bigger problem is they used a tiny 0603 SMT thermistor, which, with a nominal 165μA passing through it, self heats to about 5-10°C above the board nominal temperature. This error then gets inferred into the thermocouple measurement, giving a constant offset.&lt;/p&gt;
&lt;h2 id=&#34;thermocouple-adc&#34;&gt;Thermocouple ADC&lt;/h2&gt;
&lt;p&gt;Unlike the temperature probes, the Gravity 800 uses a thermocouple to measure the smoker chamber temperature. This is handled by an independent single input differential ADC IC that has the reference &lt;code&gt;IC4&lt;/code&gt; on the controller board. Much like the UART ADC this chip did not have any useful identifying markings, and also had a weird serial interface.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_thermocouple_adc.svg&#34; alt=&#34;Thermocouple ADC Schematic&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_thermocouple_adc.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Thermocouple ADC Schematic
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The thermocouple itself is connected to the ADC with a fairly standard 1MΩ biasing and low pass filter circuit. On first inspection, it appeared that the serial interface to the ESP32 was a unidirectional SPI-like interface, where the ESP32 would periodically clock a block of data out of the ADC. But after trying some initial testing under this hypothesis, the serial data line proved to use more complex bidirectional signaling.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/thermocouple_adc_trace1.jpg&#34; alt=&#34;Oscilloscope trace capture of single thermocouple ADC transaction. Green trace ESP32 side of R105, blue trace ADC side of R105.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/thermocouple_adc_trace1.jpg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Oscilloscope trace capture of single thermocouple ADC transaction. Green trace ESP32 side of R105, blue trace ADC side of R105.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;By probing the SDA signal on either side of resistor &lt;code&gt;R105&lt;/code&gt; and looking at the edge rate on either side, I was able to determine which side of the interface was driving SDA. From that observation the sequence appears to be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ADC pulls SDA low&lt;/li&gt;
&lt;li&gt;ESP32 sends 27 clocks, during which the ADC shifts out 27 bits of sample data&lt;/li&gt;
&lt;li&gt;On the next clock cycle the ADC stops driving SDA and the ESP32 starts driving&lt;/li&gt;
&lt;li&gt;ESP32 drives 8 clock cycle value of &lt;code&gt;0x56&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;One cycle interface turn around where ESP stops and ADC starts driving SDA again.&lt;/li&gt;
&lt;li&gt;ESP32 clocks 9 more cycles of status data out of ADC.&lt;/li&gt;
&lt;li&gt;Wait for ADC to pull SDA low again.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;From my analysis this is the most accurate representation of the protocol:


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/thermocouple_adc_protocol.svg&#34; alt=&#34;Thermocouple ADC serial protocol&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/thermocouple_adc_protocol.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Thermocouple ADC serial protocol
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;However, on the first transaction after coming out of reset, the factory does something slightly different. Presumably this is initializing something in the ADC. Instead of sending a fixed &lt;code&gt;0x56&lt;/code&gt; command, it sends a 16 bit value of: &lt;code&gt;0xcb 0x04&lt;/code&gt;&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/thermocouple_adc_protocol_first.svg&#34; alt=&#34;First Thermocouple ADC serial transaction after reset&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/thermocouple_adc_protocol_first.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            First Thermocouple ADC serial transaction after reset
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Once I was able to get a 22-bit value out of the ADC, that appeared to change with temperature, I just needed to figure out how to map it to a temperature. This is where I needed to reverse engineer the factory firmware, because I had no real information on how the ADC measurements mapped to temperatures. Most of the firmware I did not fully annotate or trace, but I did identify one function which appeared to handle the calibration and correction of the thermocouple measurement.&lt;/p&gt;
&lt;p&gt;The factory firmware appears to convert the ADC value into degrees fahrenheit with a linear scaling factor (0.00390543072717 °F/LSB), adds the cold junction temperature from the board mounted NTC, and then applies a piecewise linear correction factor. The correction function as well as a plot of its correction is shown below. Because the factory firmware does all its calculations in fahrenheit, I am showing both their coefficients in fahrenheit, and the coefficients used by FreeFall 800, which does its math in celsius.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:;&#34;&gt;
  &lt;div&gt;
$$
T_{corF}(T_{uncorF}) = T_{uncorF} + \begin{cases}
0                                                        &amp; \text{if } T_{uncorF} \leq 50{\degree}F \\
30 \times \frac {T_{uncorF} - 50} {250 - 50}             &amp; \text{if } 50{\degree}F \leq T_{uncorF} \leq 250{\degree}F \\
30                                                       &amp; \text{if } 250{\degree}F \leq T_{uncorF} \leq 400{\degree}F \\
30 + (41-30) \times \frac {T_{uncorF} - 400} {600 - 400} &amp; \text{if } 400{\degree}F \leq T_{uncorF} \leq 600{\degree}F \\
41                                                       &amp; \text{if } 600{\degree}F \leq T_{uncorF} \\
\end{cases}
$$
$$
T_{corC}(T_{uncorC}) = T_{uncorC} + \begin{cases}
0                                                                    &amp; \text{if } T_{uncorC} \leq 10{\degree}C \\
16.667 \times \frac {T_{uncorC} - 10} {107 - 10        }             &amp; \text{if } 10{\degree}C \leq T_{uncorC} \leq 107{\degree}C \\
16.667                                                               &amp; \text{if } 107{\degree}C \leq T_{uncorC} \leq 204{\degree}C \\
16.667 + (22.778-16.667) \times \frac {T_{uncorC} - 204} {316 - 204} &amp; \text{if } 204{\degree}C \leq T_{uncorC} \leq 316{\degree}C \\
22.778                                                               &amp; \text{if } 316{\degree}C \leq T_{uncorC} \\
\end{cases}
$$
&lt;/div&gt;

  
    &lt;figcaption&gt;&lt;span&gt;Piecewise linear correction function extracted from factory firmware.&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;



&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/thermocouple_correction.svg&#34; alt=&#34;Thermocouple correction curve&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/thermocouple_correction.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Thermocouple correction curve
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;It is unclear where this correction curve comes from, it does not match any thermocouple calibration curve I was able to find. So I have to assume this is correcting for some non-linearity in the sampling circuitry, not the thermocouple itself. Additionally, since this is using a relatively coarse linear piecewise function, I suspect it is possible to get more accurate temperatures by fitting a continuous function to calibration data. However, I cannot quantify if this is worth it, since I have not yet collected the required data to test it.&lt;/p&gt;
&lt;p&gt;This correction function is applied to the scaled and offset ADC value. The full thermocouple conversion function is shown below:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:;&#34;&gt;
  &lt;div&gt;
$$ T_{scaleF} = 0.00390543072717{\degree}F $$
$$ T_{scaleC} = 0.00216968373732{\degree}C $$
$$ T_{offset} = 0 $$
$$ T = T_{cor}(({ADC Sample} * T_{scale}) + T_{coldJ} + T_{offset}) $$
&lt;/div&gt;

  
    &lt;figcaption&gt;&lt;span&gt;Full thermocouple temperature conversion from factory firmware.&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Absent better calibration and correction data, this is also temperature calculation that I implemented in FreeFall 800.&lt;/p&gt;
&lt;h2 id=&#34;display&#34;&gt;Display&lt;/h2&gt;
&lt;p&gt;The last major component of the controller board is the LCD display and controller (&lt;code&gt;U31&lt;/code&gt;). Unlike most of the other components, I was actually able to find a datasheet for the LCD controller that matched its pin out: &lt;a href=&#34;https://ms.i-coreic.com/storage/uploads/pdf/202508/27/CS1621-DATASHEET-.pdf&#34;&gt;CS1621&lt;/a&gt;. The English CS1621 datasheet is missing some serial protocol details, but I found that the &lt;a href=&#34;https://www.holtek.com/webapi/116711/HT1621_21Gv340.pdf&#34;&gt;HT1621&lt;/a&gt; appears equivalent and has a better datasheet. This made reverse engineering this part much easier than the ADCs, the main thing to figure out was the mapping of segment buffer bits to specific LCD segments.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_display.svg&#34; alt=&#34;LCD display and controller schematic&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_display.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            LCD display and controller schematic
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The serial interface on the CS1621 is almost SPI, and fortunately it is possible to use the SPI peripheral in the ESP32 for the way it is used on this board. Technically the interface is made up of chip select (pin 9), bidirectional serial data (pin 12), and then pins 10 and 11 are inverted read and write enables. To write a data bit to the controller the &lt;span style=&#34;text-decoration: overline&#34;&gt;WR&lt;/span&gt; pin is pulled low, and then data is latched on the rising edge. To read a bit from the controller the same is done on the &lt;span style=&#34;text-decoration: overline&#34;&gt;RD&lt;/span&gt; pin. However if you only need to write to the controller, you can treat the &lt;span style=&#34;text-decoration: overline&#34;&gt;WR&lt;/span&gt; pin as a SPI clock signal. Since the &lt;span style=&#34;text-decoration: overline&#34;&gt;RD&lt;/span&gt; pin is not connected to the ESP32, clearly this is what the designers did. (I will note they probably should have a pull up on &lt;span style=&#34;text-decoration: overline&#34;&gt;RD&lt;/span&gt;, but it seems to work okay un-driven)&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/lcd_protocol.svg&#34; alt=&#34;Excerpt from CS1621 datasheet showing data and command transaction formats&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/lcd_protocol.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Excerpt from CS1621 datasheet showing data and command transaction formats
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h3 id=&#34;segment-mapping&#34;&gt;Segment Mapping&lt;/h3&gt;
&lt;p&gt;The display content is updated by using the &amp;ldquo;WRITE mode&amp;rdquo; command to update the display buffer. The factory firmware actually writes the display buffer twice in a single command, first it sends 16 bytes of 0s to clear the buffer, and then 16 bytes of actual segment state data. I am not sure this is required now that I have the datasheet, but I wrote the FreeFall 800 LCD driver before I found an English version of the datasheet so I just replicated what the factory firmware did. The table below shows the full mapping of segments:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;COM0&lt;/th&gt;
          &lt;th&gt;COM1&lt;/th&gt;
          &lt;th&gt;COM2&lt;/th&gt;
          &lt;th&gt;COM3&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG0&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH3_E&lt;/td&gt;
          &lt;td&gt;CH3_F&lt;/td&gt;
          &lt;td&gt;CH3_H&lt;/td&gt;
          &lt;td&gt;CH3_A1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG1&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH3_D1&lt;/td&gt;
          &lt;td&gt;CH3_K&lt;/td&gt;
          &lt;td&gt;CH3_G1&lt;/td&gt;
          &lt;td&gt;CH3_I&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG2&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH3_L&lt;/td&gt;
          &lt;td&gt;CH3_G2&lt;/td&gt;
          &lt;td&gt;CH3_J&lt;/td&gt;
          &lt;td&gt;CH3_A2&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG3&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH2_D2&lt;/td&gt;
          &lt;td&gt;CH2_M&lt;/td&gt;
          &lt;td&gt;CH2_C&lt;/td&gt;
          &lt;td&gt;CH2_B&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG4&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH2_L&lt;/td&gt;
          &lt;td&gt;CH2_G2&lt;/td&gt;
          &lt;td&gt;CH2_J&lt;/td&gt;
          &lt;td&gt;CH2_A2&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG5&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH2_D1&lt;/td&gt;
          &lt;td&gt;CH2_K&lt;/td&gt;
          &lt;td&gt;CH2_G1&lt;/td&gt;
          &lt;td&gt;CH2_I&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG6&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH2_E&lt;/td&gt;
          &lt;td&gt;CH2_F&lt;/td&gt;
          &lt;td&gt;CH2_H&lt;/td&gt;
          &lt;td&gt;CH2_A1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG7&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH3_D2&lt;/td&gt;
          &lt;td&gt;CH3_M&lt;/td&gt;
          &lt;td&gt;CH3_C&lt;/td&gt;
          &lt;td&gt;CH3_B&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG8&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH1_E&lt;/td&gt;
          &lt;td&gt;CH1_F&lt;/td&gt;
          &lt;td&gt;CH1_H&lt;/td&gt;
          &lt;td&gt;CH1_A1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG9&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH1_D1&lt;/td&gt;
          &lt;td&gt;CH1_K&lt;/td&gt;
          &lt;td&gt;CH1_G1&lt;/td&gt;
          &lt;td&gt;CH1_I&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG10&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH1_L&lt;/td&gt;
          &lt;td&gt;CH1_G2&lt;/td&gt;
          &lt;td&gt;CH1_J&lt;/td&gt;
          &lt;td&gt;CH1_A2&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG11&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH1_D2&lt;/td&gt;
          &lt;td&gt;CH1_M&lt;/td&gt;
          &lt;td&gt;CH1_C&lt;/td&gt;
          &lt;td&gt;CH1_B&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG12&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH0_E&lt;/td&gt;
          &lt;td&gt;CH0_F&lt;/td&gt;
          &lt;td&gt;CH0_H&lt;/td&gt;
          &lt;td&gt;CH0_A1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG13&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH0_D1&lt;/td&gt;
          &lt;td&gt;CH0_K&lt;/td&gt;
          &lt;td&gt;CH0_G1&lt;/td&gt;
          &lt;td&gt;CH0_I&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG14&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH0_L&lt;/td&gt;
          &lt;td&gt;CH0_G2&lt;/td&gt;
          &lt;td&gt;CH0_J&lt;/td&gt;
          &lt;td&gt;CH0_A2&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG15&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;CH0_D2&lt;/td&gt;
          &lt;td&gt;CH0_M&lt;/td&gt;
          &lt;td&gt;CH0_C&lt;/td&gt;
          &lt;td&gt;CH0_B&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG16&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;BT&lt;/td&gt;
          &lt;td&gt;HT2&lt;/td&gt;
          &lt;td&gt;SMK&lt;/td&gt;
          &lt;td&gt;HT1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG17&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;MP1&lt;/td&gt;
          &lt;td&gt;MP2&lt;/td&gt;
          &lt;td&gt;MP3&lt;/td&gt;
          &lt;td&gt;MP4&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;SEG18&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;TEMP&lt;/td&gt;
          &lt;td&gt;TIME&lt;/td&gt;
          &lt;td&gt;RLD&lt;/td&gt;
          &lt;td&gt;COL&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The segment names map the the screen layout like the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;  RLD        TIME        TEMP        MP1        MP2        MP3        MP4

 --A1-- --A2--      --A1-- --A2--         --A1-- --A2--      --A1-- --A2--     HT1
| \    |   /  |    | \    |   /  |       | \    |   /  |    | \    |   /  |
F  H   I  J   B    F  H   I  J   B  COL  F  H   I  J   B    F  H   I  J   B
|   \  | /    |    |   \  | /    |       |   \  | /    |    |   \  | /    |    SMK
 --G1-- --G2--      --G1-- --G2--         --G1-- --G2--      --G1-- --G2--
|   /  | \    |    |   /  | \    |       |   /  | \    |    |   /  | \    |
E  K   L  M   C    E  K   L  M   C  COL  E  K   L  M   C    E  K   L  M   C    HT2
| /    |   \  |    | /    |   \  |       | /    |   \  |    | /    |   \  |
 --D1-- --D2--      --D1-- --D2--         --D1--  -D2--      --D1-- --D2--     BT
.             .    .             .       .             .    .             .
+---- CH3 ----+    +---- CH2 ----+       +---- CH1 ----+    +---- CH0 ----+
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;initialization-and-configuration&#34;&gt;Initialization and Configuration&lt;/h3&gt;
&lt;p&gt;When the controller comes out of reset it needs to be configured for the connected LCD. This is done with a sequence command of 12 bit command writes, where each command starts with a &lt;code&gt;0b100&lt;/code&gt; command prefix. The configuration command sequence that the Gravity 800 factory firmware (and thus FreeFall 800) uses is:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;Command Word&lt;/th&gt;
          &lt;th&gt;Function&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;1.&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;0x852&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Configure bias generator for 1/3 bias voltage with 4 commons.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;2.&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;0x830&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Enable internal RC oscillator with 250Khz frequency.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;3.&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;0x800&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Disable oscillator and bias generator.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;4.&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;0x80a&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Disable watchdog timer.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;5.&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;0x802&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Enable oscillator generator.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;6.&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;0x806&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Enable bias generator&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;backlight&#34;&gt;Backlight&lt;/h3&gt;
&lt;p&gt;The LCD backlight is driven through a high side P-channel MOSFET, connected to GPIO13 (module pin 16) of the ESP32. Because of the high side switch circuit used the backlight is enabled when GPIO13 is pulled low, and disabled when high. Annoyingly they did not include a pull up resistor on the MOSFET gate, so until the ESP32 powers on and configures GPIO13 as an output the backlight will partially turn on.&lt;/p&gt;
&lt;h2 id=&#34;miscellaneous&#34;&gt;Miscellaneous&lt;/h2&gt;
&lt;p&gt;The rest of the controller is a variety of miscellaneous passive circuits&lt;/p&gt;
&lt;h3 id=&#34;knob-encoder&#34;&gt;Knob Encoder&lt;/h3&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_knob_encoder.svg&#34; alt=&#34;Encoder Knob Schematic&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_knob_encoder.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Encoder Knob Schematic
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The knob is connected to a pretty standard 2 switch mechanical rotary encoder, with pull up and series resistors driving pins GPIO18 and GPIO5. Weirdly, the board has the pads and resistors to support a push button encoder, with the push button connected to GPIO19, but the actual mounted encoder is not pushable. In theory you could swap the encoder for a pushable one and have an extra user input option.&lt;/p&gt;
&lt;h3 id=&#34;lid-switch&#34;&gt;Lid Switch&lt;/h3&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_lid_switch.svg&#34; alt=&#34;Lid Switch Schematic&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_lid_switch.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Lid Switch Schematic
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The grill lid switch uses the same pull up plus series resistor input structure for driving GPIO25.&lt;/p&gt;
&lt;h3 id=&#34;fan-driver&#34;&gt;Fan Driver&lt;/h3&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_fan_driver.svg&#34; alt=&#34;Fan Driver Schematic&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_fan_driver.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Fan Driver Schematic
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The blower fan is driven from the 12 volt supply using a high side &lt;a href=&#34;https://www.vbsemi.com/Package/TO252/20P03-VB.pdf&#34;&gt;20P03&lt;/a&gt; 30V/20A P-Channel MOSFET. The gate of the 20P03 is driven from a second low side N-Channel MOSFET, so that the 3.3V ESP32 can switch the 12V motor using GPIO17. The motor output itself has a LC filter and clamp diode to protect the rest of the controller.&lt;/p&gt;
&lt;h3 id=&#34;buzzer&#34;&gt;Buzzer&lt;/h3&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_buzzer.svg&#34; alt=&#34;Buzzer Schematic&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/reverse-engineering-gravity-800/schematic_buzzer.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Buzzer Schematic
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Finally, the piezo buzzer is also driven directly from the 12 volt supply, and is connected to GPIO16 of the ESP32 through a low side N-Channel MOSFET. The factory firmware always drives the buzzer with a 4kHz frequency. Presumably you could use other frequencies, but for the time being I have kept with the same configuration.&lt;/p&gt;
&lt;h2 id=&#34;future-work&#34;&gt;Future Work&lt;/h2&gt;
&lt;p&gt;At this point pretty much everything on the Gravity 800 controller board is fully understood. There are a few things I still want to do in this area though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Reverse engineer more of the Gravity series controller boards. When I published the FreeFall 800 repository, one common piece of feedback I got was people wanting to run it on the Gravity XT controller. This is a substantially different controller with a full graphical LCD; but from the FCC filings, it appears to still use a (newer) ESP32 module. If I can get my hands on an XT, or any other model, gravity series controller I would be interested in reverse engineering them too.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;More information on the thermocouple or UART ADCs. I have enough information to get data out of these ADCs, but if anyone has any more information about these parts, or parts with similar looking interfaces I would love to know!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There is still more work to do on FreeFall 800! I am currently looking into adding MQTT support, improved configuration and calibration, among other things to FreeFall 800. Additionally, in the process of writing this post and doing some deeper research into the LCD controller, I now suspect that there&amp;rsquo;s a more efficient way I could be updating the LCD.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Announcement: FreeFall 800 (v0.1.0)</title>
      <link>https://www.prbs23.com/blog/posts/announcement-freefall-800/</link>
      <pubDate>Sat, 25 Jul 2026 14:20:53 -0600</pubDate>
      <author>prbs23@prbs23.com (prbs23)</author>
      <guid>https://www.prbs23.com/blog/posts/announcement-freefall-800/</guid>
      <description>&lt;p&gt;Today I&amp;rsquo;m publicly releasing a project I&amp;rsquo;ve been working on for a while: FreeFall 800. &lt;a href=&#34;https://gitlab.com/prbs23/freefall_800&#34;&gt;FreeFall 800&lt;/a&gt; is a local first, open source firmware project, that replaces the factory firmware that comes on the Masterbuilt Gravity 800 smoker grill.&lt;/p&gt;
&lt;p&gt;A year ago I was in the market for a new grill, and ended up purchasing a &lt;a href=&#34;https://www.masterbuilt.com/products/gravity-series-800-digital-charcoal-griddle-grill-smoker&#34;&gt;Masterbuilt Gravity 800&lt;/a&gt; charcoal smoker grill. The grill itself has been great. However, like most &amp;ldquo;smart&amp;rdquo; devices, it has one fatal flaw: Without the internet, manufacturer&amp;rsquo;s servers, and their mobile app, you cannot make use of any of the &amp;ldquo;smart&amp;rdquo; remote control or monitoring features.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://gitlab.com/prbs23/freefall_800&#34;&gt;FreeFall 800&lt;/a&gt; aims to fix that. Once you replace the factory firmware with FreeFall 800 the grill will locally serve its own web app you can access from any phone or computer. This web app gives you essentially all the same remote control and monitoring features you get from the Masterbuilt app, but without any dependency on the internet or someone else&amp;rsquo;s servers. FreeFall 800 currently supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Monitoring the chamber and probe temperatures, with live plotting of temperatures and fan speed, and downloadable history&lt;/li&gt;
&lt;li&gt;Settable timer and probe target temperatures, with audible alarm&lt;/li&gt;
&lt;li&gt;PID-controlled set temperature, with tunable gains&lt;/li&gt;
&lt;li&gt;Option to disable the lid switch interlock that normally stops the fan when the lid is open&lt;/li&gt;
&lt;li&gt;Thermocouple and thermistor calibration, initially migrated from factory firmware calibration&lt;/li&gt;
&lt;li&gt;Supports both standalone WiFi access point mode and joining your home network, with mDNS for automatic host name resolution&lt;/li&gt;
&lt;li&gt;Over-the-air firmware updates&lt;/li&gt;
&lt;/ul&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/announcement-freefall-800/header_image.jpg&#34; alt=&#34;Masterbuilt Gravity 800 Smoker Grill&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/announcement-freefall-800/header_image.jpg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Masterbuilt Gravity 800 Smoker Grill
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;I started this project when I needed to repair the power cord, which had been pulled out of the controller. Taking apart the controller, I quickly found that not only was it using the popular ESP32-WROOM-32UE wireless MCU module, but the programming header for it was easily accessible on the back of the controller board. That was the point at which I knew I needed to reverse engineer this thing and write my own firmware. I will go into the reverse engineering in a separate post, but after a couple months of tinkering my custom Rust based firmware provides almost all the features of the original firmware, and I am releasing the whole thing under the GPL v3 license.&lt;/p&gt;
&lt;h2 id=&#34;getting-started-with-freefall-800&#34;&gt;Getting Started With FreeFall 800&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;First, a few warnings:&lt;/strong&gt; It is possible the FreeFall 800 firmware will damage your controller or grill. Only install this on &lt;em&gt;your&lt;/em&gt; Gravity 800 if you are willing to take the risk. &lt;strong&gt;Always&lt;/strong&gt; back up your factory firmware image, in case you need it for recovery.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This project is definitely still in an &amp;ldquo;alpha&amp;rdquo; quality state. Expect that you will likely encounter bugs. If you are willing to, please report them.&lt;/li&gt;
&lt;li&gt;This firmware was developed specifically on a controller with hardware revision &lt;code&gt;ZH-PCB-331802-C-V09&lt;/code&gt;, and factory firmware version &lt;code&gt;3.0.01&lt;/code&gt;. If you run this on a different hardware revision, no promises are given that it will work, but if it does I would love to know.&lt;/li&gt;
&lt;li&gt;No thought has yet been put into security or authentication in this firmware. Anyone who can connect to your device can control it, so whatever you do, don&amp;rsquo;t expose it to the internet.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;building-firmware&#34;&gt;Building Firmware&lt;/h3&gt;
&lt;p&gt;If you are one of those people that like to build everything from source, you can do just that with the following commands:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install Rust via &lt;a href=&#34;https://rustup.rs/&#34;&gt;rustup&lt;/a&gt; (this provides &lt;code&gt;cargo&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Install espup ESP32 Rust toolchain installer:
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cargo install espup --locked
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Install the Espressif Rust toolchain:
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;espup install
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Source additional environment variables:
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;source ~/export-esp.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Build firmware:
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git clone https://gitlab.com/prbs23/freefall_800.git freefall_800
cd freefall_800
cargo build --release
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you don&amp;rsquo;t want to bother with building the firmware yourself, you can just download it from the repository release page: &lt;a href=&#34;https://gitlab.com/prbs23/freefall_800/-/releases&#34;&gt;FreeFall 800 Releases&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;flashing-firmware&#34;&gt;Flashing Firmware&lt;/h3&gt;
&lt;p&gt;To load FreeFall 800 onto your Gravity 800, you will need a &lt;a href=&#34;https://www.sparkfun.com/sparkfun-ftdi-basic-breakout-3-3v.html&#34;&gt;3.3 volt USB-Serial adapter&lt;/a&gt; and a couple of jumper wires.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt; Using a 5V or other voltage serial adapter could damage your controller.&lt;/strong&gt; Some adapters are configurable, make sure you are using 3.3 volts.&lt;/p&gt;
&lt;p&gt;To get access to the programming header, remove the controller from your grill, and remove the back cover. You should find a programming header shown in the image below. The pinout for that header is the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;RXD&lt;/code&gt; - USART RX&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TXD&lt;/code&gt; - USART TX&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BOOT&lt;/code&gt; - Boot/Flash Mode, IO0 - Pull low for boot loader&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GND&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+3.3V&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Connect &lt;code&gt;RXD&lt;/code&gt;, &lt;code&gt;TXD&lt;/code&gt; and &lt;code&gt;GND&lt;/code&gt; to a 3.3V USB-Serial adapter. You can connect &lt;code&gt;BOOT&lt;/code&gt; directly to ground if you just want to program the controller, if you want serial debug while firmware is running, connect to &lt;code&gt;GND&lt;/code&gt; with a switch.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/announcement-freefall-800/programming_setup.png&#34; alt=&#34;Programming header connection setup.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/announcement-freefall-800/programming_setup.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Programming header connection setup.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Once connected you can run the following commands to flash FreeFall 800 onto your controller:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install espflash:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cargo install espflash --locked
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Back up current firmware:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;espflash read-flash --port /dev/ttyUSB0 --baud 115200 --chip esp32 0 0x800000 firmware_dump.bin
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tie &amp;ldquo;Boot Mode&amp;rdquo; pin (pin 4) on programming header to ground, then power up the controller from its normal power supply (the serial adapter does not power the board). Screen should stay blank.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Load firmware:&lt;/p&gt;
&lt;p&gt;If you have already built the firmware from source, you can just run:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cargo run --release
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you are not building from source, download the latest &amp;ldquo;Firmware Image&amp;rdquo; from the &lt;a href=&#34;https://gitlab.com/prbs23/freefall_800/-/releases&#34;&gt;repository release page&lt;/a&gt;. And then run:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;espflash flash --erase-parts otadata --monitor --chip esp32 --port /dev/ttyUSB0 freefall_800_fw
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Release the &lt;code&gt;boot&lt;/code&gt; pin, and power controller off and back on. You should see the firmware booting in the console. You can now unplug programming header and device should run on its own.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once you have initially flashed your controller with FreeFall 800, future firmware updates can be done &lt;a href=&#34;https://gitlab.com/prbs23/freefall_800#ota-over-the-air-firmware-updates&#34;&gt;over the network&lt;/a&gt; using the OTA firmware images, also available from the &lt;a href=&#34;https://gitlab.com/prbs23/freefall_800/-/releases&#34;&gt;release page&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;connecting&#34;&gt;Connecting&lt;/h3&gt;
&lt;p&gt;If your controller had previously been connected to your network then FreeFall 800 should automatically reconnect to the same network when it starts up for the first time. However, if your controller was not connected to a network, or was connected to a network that is not present anymore, it will fall back to operating as its own WiFi access point, creating its own network with the name &lt;code&gt;freefall_800_XXXXXX&lt;/code&gt; (where the Xs come from the device MAC address), and the password &lt;code&gt;Gravity800&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once your computer or phone is connected to the same network as the controller, point your browser at &lt;a href=&#34;http://freefall800.local&#34;&gt;&lt;code&gt;freefall800.local&lt;/code&gt;&lt;/a&gt; and it should load the FreeFall 800 web app:&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/announcement-freefall-800/web_app.png&#34; alt=&#34;FreeFall 800 Web App&#34; style=&#34;max-height:500px&#34;/&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/announcement-freefall-800/web_app.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            FreeFall 800 Web App
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;From here you can fully control the grill, monitor temperatures, set alarms, and configure the controller.&lt;/p&gt;
&lt;p&gt;Of course the physical front panel controls still work as well. Not all the configurability available through the web app is possible through the front panel, but you can set the target and probe alarm temperatures and control the timer without opening the app.&lt;/p&gt;
&lt;h2 id=&#34;limitations&#34;&gt;Limitations&lt;/h2&gt;
&lt;p&gt;In its current v0.1.0 state, FreeFall 800 provides almost all of the functionality provided by the Masterbuilt app. But there are a couple of limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;FreeFall 800 cannot send notifications to connected devices when the timer expires or probe targets are hit. This is primarily a limitation of the web interface being a web-app, not a full mobile app. In theory web sites/applications can send notifications, but on most browsers this is limited to applications served over HTTPS, which is not really practical for this use case. To generate notifications I will likely need to create a real mobile app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Because FreeFall 800 operates fully locally, you cannot access your grill from outside your local network, without extra effort. The Masterbuilt servers act as an intermediary for the factory firmware, so you can transparently control your grill from anywhere. There are of course options to tunnel traffic to your local network to work around this, but that is outside the scope of this project.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;future-work&#34;&gt;Future Work&lt;/h2&gt;
&lt;p&gt;There are, of course, so many things to be fixed and improved on this project going forward. Other than bug fixes and smaller improvements, these are some larger areas of improvement I would like to see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Develop native mobile applications to connect with FreeFall 800 controllers. Mainly this is to allow for notifications to be generated for alarms, which I do miss having from the factory firmware. All control over FreeFall 800 is available through a JSON API, so no firmware changes would even be required to implement a native Android or iOS app. However, maybe the simplest solution would be to wrap the existing web app with some extensions in a simple mobile app shell.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;More advanced temperature programming. The factory firmware only allows you to set a static temperature target, and this is what is implemented in FreeFall 800 as well. But now that the basics are implemented, it may be interesting to be able to program a temperature sequence, or even temperature ramps from the web app. I believe the temperature control PID loop could be made stable enough to allow for at least slow temperature changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Support for other Gravity series or Masterbuilt grills/smokers that use a similar controller. This one will need some community support, since I can&amp;rsquo;t justify actually buying the hardware myself. However, if anyone has other devices with similar model controllers and would like to help develop support for them, I would love a &lt;a href=&#34;https://gitlab.com/prbs23/freefall_800/-/merge_requests&#34;&gt;merge request&lt;/a&gt;!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;contributing&#34;&gt;Contributing&lt;/h2&gt;
&lt;p&gt;All source code for the project is available on the &lt;a href=&#34;https://gitlab.com/prbs23/freefall_800/&#34;&gt;FreeFall 800 GitLab page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you try out FreeFall 800 and find bugs, have feature requests, or any other feedback. Please create an issue describing the bug or feature in the &lt;a href=&#34;https://gitlab.com/prbs23/freefall_800/-/work_items&#34;&gt;GitLab issue tracker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Feel free to fork the repository and play around with the firmware! If you make improvements you think would be useful to the project as a whole, please open a &lt;a href=&#34;https://gitlab.com/prbs23/freefall_800/-/merge_requests&#34;&gt;merge request&lt;/a&gt;. I would love to see what others can do with this starting point.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Getting Started With a Private LoRaWAN Network Using ChirpStack v4 (Updated)</title>
      <link>https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/</link>
      <pubDate>Tue, 23 Jul 2024 20:31:41 -0600</pubDate>
      <author>prbs23@prbs23.com (prbs23)</author>
      <guid>https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/</guid>
      <description>&lt;p&gt;I have recently started experimenting with LoRa radios, and the LoRaWAN network protocol. Interestingly it seems that the LoRa ecosystem is simultaneously surprisingly open (both in specifications and implementations), while also having very little definitive documentation on how to actually get started as an indvidual hacker. As such I found the process of learning enough of the protocol to set up my own network surprisingly difficult.&lt;/p&gt;
&lt;p&gt;This post will walk through the process of setting up my own private LoRaWAN network, from low cost hardware all the way up to the application level. While I would never claim to be an expert in LoRa or LoRaWAN, I hope someone finds this a useful reference.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;note&#34;&gt;Note:&lt;/h4&gt;
&lt;p&gt;This is an updated republication of my original 2022 &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/&#34;&gt;Getting Started With a Private LoRaWAN Network&lt;/a&gt; post. Shortly after my last post I had to put the project on hold for a long time, and when I finally got back to it, I found ChirpStack had released a new architecturally incompatible version 4 of their LoRaWAN network framework. Since the configuration files and setup process have changed so significantly, the process described in my old post is no longer valid. I have rewritten the ChirpStack configuration section of this post to be compatible with the new ChirpStack v4.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;lora-and-lorawan-basics&#34;&gt;LoRa and LoRaWAN Basics&lt;/h2&gt;
&lt;p&gt;LoRa (not to be confused with LoRaWAN) is a proprietary modulation and physical layer standard for low power wireless communication, originally developed in 2010 by Cycleo, which was later acquired by Semtech in 2012. The LoRa physical layer uses a &amp;ldquo;Chirp Spread Spectrum&amp;rdquo; (CSS) modulation technique to spread the signal over either 125, 250, or 500MHz bands. The use of CSS modulation, instead of the more common &amp;ldquo;Direct Sequence Spread Spectrum&amp;rdquo; (DSSS) allows LoRa to transmit over longer ranges and be more robust to many common types of interference.&lt;/p&gt;
&lt;p&gt;The advertised range for LoRa transceivers tend to be in the 1km to 10km range, though from my research it realistically is more often on the lower end of that range. I have not tested what range I can get from my system yet, so I don&amp;rsquo;t have my own empirical data. More information about LoRa modulation and the physical layer can be found in this &lt;a href=&#34;https://semtech--c.na98.content.force.com/sfc/dist/version/download/?oid=00DE0000000JelG&amp;amp;ids=0682R000005PU6SQAW&amp;amp;d=%252Fa%252F2R0000001OJk%252FyDEcfAkD9qEz6oG3PJryoHKas3UMsMDa3TFqz1UQOkM&#34;&gt;Application Note&lt;/a&gt; from Semtech, and this excellent &lt;a href=&#34;http://www.sghoslya.com/&#34;&gt;series of blog posts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;LoRaWAN, on the other hand, is a &amp;ldquo;Low Power Wide Area Network&amp;rdquo; (LPWAN) specification, built on top of the LoRa physical layer. LoRaWAN defines a network architecture, MAC layer, and link layer protocols, primarily targeted at building low power and bandwidth sensor networks. The LoRaWAN standard is defined by the &lt;a href=&#34;https://lora-alliance.org&#34;&gt;LoRa Alliance&lt;/a&gt;, and is published as an open standard in two primary parts: The &lt;a href=&#34;https://resources.lora-alliance.org/technical-specifications/ts001-1-0-4-lorawan-l2-1-0-4-specification&#34;&gt;LoRaWAN L2 Specification&lt;/a&gt; which defines everything except specific physical layer parameters, and the &lt;a href=&#34;https://resources.lora-alliance.org/technical-specifications/rp2-1-0-3-lorawan-regional-parameters&#34;&gt;LoRaWAN Regional Parameters&lt;/a&gt; which defines physical layer parameters, such as frequency bands, bandwidth, etc. for specific regions of the world, in order to be compliant with local regulations.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/lorawan_network_architecture.png&#34; alt=&#34;Basic LoRaWAN Network Architecture.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/lorawan_network_architecture.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Basic LoRaWAN Network Architecture.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The basic architecture of a LoRaWAN network is shown above. Each LoRaWAN &amp;ldquo;End-Device&amp;rdquo; communicates with one or more &amp;ldquo;Gateway&amp;rdquo; devices over the LoRa physical layer. These gateways forward messages between the end-devices and a &amp;ldquo;Network Server&amp;rdquo; over standard TCP/IP networking. The network server manages all devices connected to one or more gateways, including device activation (joining a network), congestion control, etc. Device authentication is handled by a separate &amp;ldquo;Join Server&amp;rdquo; that interfaces with one or more network servers, and finally any non-management messages from a device are sent from the network server to an application server.&lt;/p&gt;
&lt;h2 id=&#34;getting-started&#34;&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;The simplest way to start using LoRaWAN, is to buy some LoRaWAN devices, connect them to a public network such as &lt;a href=&#34;https://www.thethingsnetwork.org/&#34;&gt;The Things Network&lt;/a&gt;, and you are good to go. While this is simple because all the network and server management is handled by someone else, I wanted to set up my own network. This was partially just to understand how all the pieces of the LoRaWAN network work, but also because I don&amp;rsquo;t have coverage from any of these networks where I live anyway.&lt;/p&gt;
&lt;p&gt;To set up your own network you need essentially four things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;End device hardware, generally a LoRa transceiver and a microcontroller.&lt;/li&gt;
&lt;li&gt;Firmware network stack to run on the end device.&lt;/li&gt;
&lt;li&gt;Gateway radio for the end devices to connect to.&lt;/li&gt;
&lt;li&gt;Gateway client and network/join server software.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;end-devices&#34;&gt;End-Devices&lt;/h3&gt;
&lt;h4 id=&#34;hardware&#34;&gt;Hardware&lt;/h4&gt;
&lt;p&gt;For end-device hardware, there are many different options, from &lt;a href=&#34;https://lora-alliance.org/showcase/search/?_sfm_lorawan_certified_device=certified&amp;amp;_sft_product_categories=end-node-sensors-or-other-devices&#34;&gt;off the shelf LoRaWAN connected sensors and devices&lt;/a&gt;, to bare &lt;a href=&#34;https://www.semtech.com/lora/lora-products&#34;&gt;LoRa transceiver chips&lt;/a&gt;. For my first foray into LoRaWAN I chose to use an integrated LoRa radio/microcontroller module, since I believe it provides the most direct development path to building my own custom device PCBs in the future, while also allowing complete programmability of the device. There are several manufactures that produce these modules with different specifications, but I chose the &lt;a href=&#34;https://www.murata.com/en-us/products/connectivitymodule/lpwa/overview/lineup/type-abz&#34;&gt;CMWX1ZZABZ&lt;/a&gt; module from muRata as it was a fairly common, low cost option.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/b-l072z-lrwan1_development_board.png&#34; alt=&#34;B-L072Z-LRWAN1 board for CMWX1ZZABZ module used as end-device for testing network.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/b-l072z-lrwan1_development_board.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            B-L072Z-LRWAN1 board for CMWX1ZZABZ module used as end-device for testing network.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The CMWX1ZZABZ module integrates a Semtech SX1276 LoRa transceiver, RF switch, and STM32L microcontroller into single surface mount RF module that is easily integrated onto a custom PCB. The integration of a STM32 MCU is convenient, as this is a microcontroller platform that I am already familiar with. Additionally, ST Microelectronics even sells the &lt;a href=&#34;https://www.st.com/en/evaluation-tools/b-l072z-lrwan1.html&#34;&gt;B-L072Z-LRWAN1&lt;/a&gt; development board that included everything I needed to get started building a LoRaWAN end-device.&lt;/p&gt;
&lt;h4 id=&#34;firmware-networking-stack&#34;&gt;Firmware Networking Stack&lt;/h4&gt;
&lt;p&gt;In addition to the end-device hardware, I also needed firmware that implemented the end-device side of the LoRaWAN protocol. Fortunately, Semtech have published a BSD licensed &lt;a href=&#34;https://github.com/Lora-net/LoRaMac-node&#34;&gt;LoRaWAN end-device networking stack&lt;/a&gt; that works with many of their LoRa transceiver chips. Even better, STMicroelectronics, provide a &lt;a href=&#34;https://www.st.com/en/embedded-software/i-cube-lrwan.html&#34;&gt;packaged version of this library&lt;/a&gt; that is already integrated with their STM32Cube embedded software stack. This makes it very simple to get some demo applications running on the B-L072Z-LRWAN1 development board with very little effort.&lt;/p&gt;
&lt;p&gt;One of the demo applications provided as part of the STM32Cube LoRaWAN expansion makes the module behave as a &lt;a href=&#34;https://www.st.com/resource/en/application_note/an4967-examples-of-at-commands-on-icubelrwan-stmicroelectronics.pdf&#34;&gt;fully featured LoRaWAN modem that accepts AT style commands&lt;/a&gt; over a UART port. This AT modem application firmware is what I ended up using to test and debug, as it gave me interactive control over my end-device with known working firmware, so I didn&amp;rsquo;t need to debug my own firmware at the same time as my network configuration.&lt;/p&gt;
&lt;h3 id=&#34;gateway&#34;&gt;Gateway&lt;/h3&gt;
&lt;h4 id=&#34;hardware-1&#34;&gt;Hardware&lt;/h4&gt;
&lt;p&gt;Unlike the end-devices, the hardware for a Gateway turns out to be a bit more complicated. Primarily this is because while the end-device only needs to transmit or receive on one channel at a time, gateways generally must be able to receive packets from multiple end-devices on multiple channels simultaneously&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. Additionally, not only must the gateway be able to receive packets on typically several channels at once, in order to support the class B beacon protocol the gateway must use &lt;a href=&#34;https://content.cdntwrk.com/files/aT0xNDI4Mzk1JnY9MSZpc3N1ZU5hbWU9dHMwMDEtMS0wLTQtbG9yYXdhbi1sMi0xLTAtNC1zcGVjaWZpY2F0aW9uJmNtZD1kJnNpZz0xYWMyYjRiZjM3YTExNWMzZDU2NzBiYTM4OTAyY2E5ZA%253D%253D#%5B%7B%22num%22%3A166%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C69%2C733%2C0%5D&#34;&gt;GPS to get precise time and location information&lt;/a&gt;, and also has to have a full TCP/IP networking stack to communicate with the network server.&lt;/p&gt;
&lt;p&gt;Now, there &lt;a href=&#34;https://lora-alliance.org/showcase/search/?_sft_product_categories=gateway&#34;&gt;&lt;em&gt;are&lt;/em&gt; full off the shelf LoRaWAN gateways for sale&lt;/a&gt;, but most don&amp;rsquo;t even publish a price, and as the adage goes &amp;ldquo;if you have to ask the price you can&amp;rsquo;t afford it&amp;rdquo;. Even the few gateway devices that you can easily purchase are usually north of $400, which was a little out of &lt;em&gt;my&lt;/em&gt; budget. After some research, it turns out that most LoRaWAN gateways are built off the same Semtech SX130x (baseband)/SX125x (front-end) chipset, connected to something like a fairly beefy ARM processor running Linux. The general structure of one of these gateways is shown in the block diagram below, from the Semtech LoRaWAN gateway &amp;ldquo;Corecell&amp;rdquo; reference design.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/lorawan_corecell_block_diagram.png&#34; alt=&#34;Semtech Block Diagram of &amp;#34;Corecell&amp;#34; gateway reference design&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/lorawan_corecell_block_diagram.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Semtech Block Diagram of &amp;#34;Corecell&amp;#34; gateway reference design
             (&lt;a href=&#34;https://semtech--c.na98.content.force.com/sfc/dist/version/download/?oid=00DE0000000JelG&amp;amp;ids=0682R000006GEO0QAO&amp;amp;d=%2Fa%2F2R000000HV3G%2Fan5Oh4KfiY7tAO1MO7RiPOj08WUpOTjPHSF57_.NlJ0&amp;amp;operationContext=DELIVERY&amp;amp;asPdf=true&amp;amp;viewId=05H2R000002Z67aUAC&amp;amp;dpt=#page=6&#34;&gt;Semtech Corecell Reference Design Users Guide&lt;/a&gt;)
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Since I was trying to avoid spending hundreds of dollars if I could, I did briefly consider building my own gateway PCB based on this architecture. However due to 2022 &amp;ldquo;Chipageddon&amp;rdquo;, I couldn&amp;rsquo;t actually get my hands on bare SX1308 chips. In the end I found that the &lt;a href=&#34;https://pycom.io/&#34;&gt;Pycom&lt;/a&gt; makes an expansion board for their &lt;a href=&#34;https://docs.pycom.io/datasheets/development/wipy3/&#34;&gt;Wipy microcontroller modules&lt;/a&gt; that turns them into a LoRaWAN gateway. Now, I wasn&amp;rsquo;t interested in actually using a Wipy or their associated software, but the &lt;a href=&#34;https://docs.pycom.io/datasheets/expansionboards/pygate/&#34;&gt;&amp;ldquo;Pygate&amp;rdquo; expansion module&lt;/a&gt; is essentially a copy of the Semtech reference design above that I could pick one up for &lt;a href=&#34;https://www.digikey.com/en/products/detail/pycom-ltd/604565285973/12354323&#34;&gt;$52 from Mouser&lt;/a&gt;.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/lorawan_basestation_wiring.png&#34; alt=&#34;Connections between Raspberry PI, Pygate module, and GPS.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/lorawan_basestation_wiring.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Connections between Raspberry PI, Pygate module, and GPS.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;To make a full gateway device, I connected the Pygate module to an old Raspberry Pi (Model B rev. 2) and a &lt;a href=&#34;https://www.sparkfun.com/products/13670&#34;&gt;GP-736&lt;/a&gt; GPS module that I had laying around. The GPS module unfortunately does not have a 1pps output that can be used for very precise timing. But even it&amp;rsquo;s limitations this it is sufficient to build an equivalent system to the Corecell reference design, and more importantly, works with the Basic Station software from Semtech.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/lorawan_basestation_system.png&#34; alt=&#34;Hardware setup for my low cost LoRaWAN gateway.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/lorawan_basestation_system.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Hardware setup for my low cost LoRaWAN gateway.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h4 id=&#34;gateway-software&#34;&gt;Gateway Software&lt;/h4&gt;
&lt;p&gt;To go along with Semtech&amp;rsquo;s Corecell reference design, they also provide the source for an packet forwarder application they call &lt;a href=&#34;https://github.com/lorabasics/basicstation&#34;&gt;LoRa Basics Station&lt;/a&gt;. This software contains the code to drive the SX1308 and SX1257 chips on the Pygate board, and read timing and location information from a standard NEMA GPS data stream and. This application connects a LoRa network server to forward packets between end devices and the LoRaWAN network.&lt;/p&gt;
&lt;p&gt;Building the Basics Station software turned out to be fairly straight forward. In fact Semtech have already put in the hard work to get it building on Raspberry Pi, so it&amp;rsquo;s as simple as:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/lorabasics/basicstation.git basicstation
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; basicstation
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make &lt;span class=&#34;nv&#34;&gt;platform&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;rpi &lt;span class=&#34;nv&#34;&gt;variant&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;std
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;We will get into configuring and running the software a bit later, as that turned out to be the tricky bit, and requires some understanding of the rest of the network servers.&lt;/p&gt;
&lt;p&gt;While the Basic Station software was already set up to drive the whole SX1308/SX1257 chipset at runtime, it didn&amp;rsquo;t handle everything for me. Specifically, the Pygate board has four input pins that must be driven correctly by the Raspberry Pi before it will work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;RFPWR_EN:&lt;/strong&gt; Enable signal to voltage regulator for RF power rail. Needs to be driven high to power RF front-end.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FEM_EN&lt;/strong&gt;: Enable signal to RF front-end. Must be driven high before enable front end circuits.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SX1308_RST:&lt;/strong&gt; Active high reset for SX1308 chip. Can be driven high to reset SX1308 chip, must be low at runtime.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SX1257_RST:&lt;/strong&gt; Active high reset for both SX1257 chip. Can be driven high to reset the SX1257 chips, must be low during operation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While these possibly could have been tied high and low respectively, I connected these signals to Raspberry Pi GPIO pins and used the following shell script to power up the Pygate board and bring the chips out of reset:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:300px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;sx1308_rst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;sx1257_rst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;27&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;fem_en&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;rfpwr_en&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Export IOs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$sx1308_rst&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/export&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$sx1257_rst&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/export&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fem_en&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;     &amp;gt; /sys/class/gpio/export&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$rfpwr_en&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;   &amp;gt; /sys/class/gpio/export&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Make IOs outputs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;out&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1308_rst&lt;/span&gt;/direction&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;out&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1257_rst&lt;/span&gt;/direction&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;out&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$rfpwr_en&lt;/span&gt;/direction&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;out&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$fem_en&lt;/span&gt;/direction&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Enable power rails and assert resets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1308_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1257_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$rfpwr_en&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$fem_en&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Deassert resets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1308_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1257_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;pygate_setup.sh&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;One advantage of driving these enables/resets from GPIOs is that I can also reset or power down the whole LoRa chipset remotely using another shell script:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:300px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;sx1308_rst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;sx1257_rst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;27&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;fem_en&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;rfpwr_en&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Assert resets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1308_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1257_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Disable power rails&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$fem_en&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$rfpwr_en&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;pygate_shutdown.sh&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;h3 id=&#34;servers&#34;&gt;Servers&lt;/h3&gt;
&lt;p&gt;With the network hardware figured out, I just just needed the core LoRaWAN servers and an application back-end to send data to. For the LoRaWAN core servers ended up choosing to use &lt;a href=&#34;https://www.chirpstack.io/&#34;&gt;ChirpStack&lt;/a&gt;. It provides an integrated solution for the full LoRaWAN network core that is simple to self-host, has many back-end integrations out of the box, and is open source. For my back-end application I&amp;rsquo;m starting with logging messages to a PostgreSQL database for plotting with &lt;a href=&#34;https://grafana.com/&#34;&gt;Grafana&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All the details of the ChirpStack architecture, can be found in their &lt;a href=&#34;https://www.chirpstack.io/docs/architecture.html&#34;&gt;architecture documentation&lt;/a&gt;. Depending on the size of the network, and applications being run over it, ChirpStack can be deployed in different ways, but for my initial setup, this is what I architecture ended up landing on:&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_deployment.png&#34; alt=&#34;ChirpStack server deployment.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_deployment.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            ChirpStack server deployment.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;You will note that compared to my &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/&#34;&gt;previous version of this post&lt;/a&gt;, there are two fewer server processes. In ChirpStack v4, the network server and application server functionality were merged into a single process. Additionally, I am no longer using InfluxDB, partially this is because I have since learned that PostgreSQL is likely more than capable of handling the amount of time-series I would be generating ,data without the complexity of a separate database. But additionally the latest version of InfluxDB can no longer be self-hosted, so not appropriate for a standalone network.&lt;/p&gt;
&lt;h4 id=&#34;chirpstack-on-the-gateway&#34;&gt;ChirpStack On the Gateway&lt;/h4&gt;
&lt;p&gt;To connect the Semtech Basic Station packet forwarder into the ChirpStack infrastructure we need to use the ChirpStack Gateway Bridge. This acts as a shim between the Semtech LNS protocol used by the Basic Station packet forwarder and the MQTT protocol and API used by ChirpStack&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. We have the choice of running the Gateway Bridge either on the Raspberry Pi gateway or on the server with the ChirpStack Network Server, I chose to run it on the Raspberry Pi so that all the external network communication would be happening within the domain of ChirpStack protocols.&lt;/p&gt;
&lt;p&gt;Unlike the other ChirpStack components I ended up needing to build the gateway bridge from source, this was because my Raspberry Pi board was old enough that the CPU was not supported by the pre-built ARM binaries. Fortunately this is not too difficult, I just needed to install the Go compiler and run make:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt-get intall golang-go
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/chirpstack/chirpstack-gateway-bridge chirpstack-gateway-bridge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; chirpstack-gateway-bridge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;h4 id=&#34;chirpstack-in-docker-compose&#34;&gt;ChirpStack in Docker Compose&lt;/h4&gt;
&lt;p&gt;The rest of the chirpstack components and my back-end database I am running on a standard x86 computer running linux as docker containers, managed with docker-compose. ChirpStack conveniently publishes an example docker-compose configuration on this GitHub page: &lt;a href=&#34;https://github.com/chirpstack/chirpstack-docker&#34;&gt;chirpstack/chirpstack-docker&lt;/a&gt;. This gave me a good starting place but the configuration did not match the server setup I was looking for, and did not include the Grafana for plotting received data. My final docker-compose and other configurations can be found in the &lt;a href=&#34;https://gitlab.com/prbs23/chirpstack-docker-prbs23&#34;&gt;prbs23/chirpstack-docker-prbs23 GitLab repository&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:400px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;54
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;55
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;56
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;57
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;58
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;59
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;60
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;61
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;chirpstack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;chirpstack/chirpstack:4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-&lt;span class=&#34;l&#34;&gt;c /etc/chirpstack&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;unless-stopped&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./configuration/chirpstack:/etc/chirpstack&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./lorawan-devices:/opt/lorawan-devices&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;depends_on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;mosquitto&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;redis&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;MQTT_BROKER_HOST=mosquitto&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;REDIS_HOST=redis&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;POSTGRESQL_HOST=postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;7070&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;7070&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres:14-alpine&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;unless-stopped&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./configuration/postgresql/initdb:/docker-entrypoint-initdb.d&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;postgresqldata:/var/lib/postgresql/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;POSTGRES_PASSWORD=ruwkUXDzkGYr8Y&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;redis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;redis:7-alpine&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;unless-stopped&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;redis-server --save 300 1 --save 60 100 --appendonly no&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;redisdata:/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;mosquitto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;eclipse-mosquitto:2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;unless-stopped&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;7071&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;7071&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./configuration/mosquitto/config/:/mosquitto/config/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;grafana&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;grafana/grafana-oss:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s1&#34;&gt;&amp;#39;7072:3000&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;grafana-storage:/var/lib/grafana&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./configuration/grafana/provisioning:/etc/grafana/provisioning&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./configuration/grafana/dashboards:/etc/dashboards&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;depends_on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GF_SECURITY_ADMIN_USER=admin&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GF_SECURITY_ADMIN_PASSWORD=A37NJSGmLVLCue&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;postgresqldata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;redisdata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;grafana-storage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;docker-compose.yml&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;There are a couple main things that I customized from ChirpStack&amp;rsquo;s default docker-compose file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Removed the ChirpStack Gateway Bridge container, because I am running the gateway bridge on my gateway device itself.&lt;/li&gt;
&lt;li&gt;Removed the ChirpStack REST API server, as this is only neccesary for integrating with application software built for ChirpStack v3.&lt;/li&gt;
&lt;li&gt;Changed the external facing ports. I have other applications running on this server that already used the default ports.&lt;/li&gt;
&lt;li&gt;Configured PostgreSQL integration to log decoded payload received from end device sensors.&lt;/li&gt;
&lt;li&gt;Added Grafana container to the configuration. Because my first application for this network will be data logging, Grafana can be used to plot data stored in the ChirpStack PostgreSQL integration database.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;configuring-the-network&#34;&gt;Configuring the Network&lt;/h2&gt;
&lt;p&gt;Now that I have all the hardware and software pieces I need for a private LoRaWAN network, I just needed to configure them to work together. This turned out to be more complicated than I expected, and also poorly documented, so I&amp;rsquo;ll walk through the important configurations for each component in the system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The following configuration is set up as a test network. There are multiple authentication and security features available in ChirpStack which I have not configured for this application, but you should be using for a real network.&lt;/p&gt;
&lt;h3 id=&#34;chirpstack-network-server&#34;&gt;ChirpStack Network Server&lt;/h3&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:400px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Logging.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;logging&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# Options are: trace, debug, info, warn error.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;level&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;info&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# PostgreSQL configuration.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;postgresql&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# PostgreSQL DSN.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;dsn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;postgres://chirpstack:&amp;lt;POSTGRESQL_CHIRPSTACK_PASSWORD&amp;gt;@$POSTGRESQL_HOST/chirpstack?sslmode=disable&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# Connection Options&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;max_open_connections&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;min_idle_connections&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Redis configuration.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;redis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# Server address or addresses.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;servers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;redis://$REDIS_HOST/&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# TLS enabled.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;tls_enabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# Redis Cluster.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;cluster&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Network related configuration.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;network&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# Network identifier (NetID, 3 bytes) encoded as HEX (e.g. 010203).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;net_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;000000&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# Enabled regions.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;enabled_regions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;us915_0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# API interface configuration.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# interface:port to bind the API interface to.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;bind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;0.0.0.0:7070&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# API Secret. (Generate with openssl rand -base64 32)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;secret&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;API_SECRET&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;integration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;enabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;mqtt&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;postgresql&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;integration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mqtt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tcp://$MQTT_BROKER_HOST:7071/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;integration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;postgresql&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;dsn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;postgres://chirpstack_integration:&amp;lt;POSTGRES_CHIRPSTACK_INTEGRATION_PASSWORD&amp;gt;@$POSTGRESQL_HOST/chirpstack_integration?sslmode=disable&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;configuration/chirpstack/chirpstack.toml&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;In ChirpStack v4, the functionality of the v3 Application Server (API, and web UI) were merged into the ChirpStack network server. This does simplify the configuration some, in that there is now a single ChirpStack configuration file &lt;code&gt;configuration/chirpstack/chirpstack.toml&lt;/code&gt;. The file now contains all the ChirpStack server configuration parameters, including LoRaWAN network settings and regions, authentication secrets, and application integration options. There are however two main LoRaWAN setting to call out.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;net_id&lt;/code&gt;: Specifies the LoRaWAN network ID for the network server. The network ID is used to uniquely identify LoRaWAN networks when routing between network servers when supporting network roaming. If you wanted to support connecting end devices through other device&amp;rsquo;s gateways, then it is necessary to acquire a unique network ID from the LoRa Alliance. However, network IDs &lt;code&gt;0x000000&lt;/code&gt; and &lt;code&gt;0x000001&lt;/code&gt; are reserved for experimental networks (I consider this experimental), and networks that are not using roaming (which I am not), so this can just be set to &lt;code&gt;0x000000&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enabled_regions&lt;/code&gt;: Defines which LoRaWAN regions and channel plan configurations will be supported by the network server. In ChirpStack v4, the network server now simultaneously supports multiple channel plan configurations, each entry in the &lt;code&gt;enabled_regions&lt;/code&gt; configuration list corresponds to a configuration file named &lt;code&gt;region_&amp;lt;region&amp;gt;.toml&lt;/code&gt; in the &lt;code&gt;configuration/chirpstack&lt;/code&gt; directory. The &lt;a href=&#34;https://github.com/chirpstack/chirpstack-docker&#34;&gt;chirpstack/chirpstack-docker&lt;/a&gt; repository comes with a large set of standard channel plans for all regions. I chose the &amp;ldquo;us915_0&amp;rdquo; configuration for my network, since it is in the US, and matches the channel plan I used when setting up ChirpStack v3. More details on channel plans below.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to the LoRaWAN network settings, the remainder of the settings that needed to be configured were the connection parameters to the other docker containers in the system.&lt;/p&gt;
&lt;h3 id=&#34;gateway-bridge&#34;&gt;Gateway Bridge&lt;/h3&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:400px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;integration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mqtt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;auth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;generic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;servers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tcp://&amp;lt;NETWORK_SERVER_HOST&amp;gt;:7071&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;integration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mqtt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;event_topic_template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;us915_0/gateway/{{ .GatewayID }}/event/{{ .EventType }}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;state_topic_template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;us915_0/gateway/{{ .GatewayID }}/state/{{ .StateType }}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;command_topic_template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;us915_0/gateway/{{ .GatewayID }}/command/#&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;basic_station&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;basic_station&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;bind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;:3001&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;US915&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;frequency_min&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;923000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;frequency_max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;938000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;basic_station&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;concentrators&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;basic_station&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;concentrators&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;multi_sf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;frequencies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;mi&#34;&gt;902300000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;mi&#34;&gt;902500000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;mi&#34;&gt;902700000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;mi&#34;&gt;902900000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;mi&#34;&gt;903100000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;mi&#34;&gt;903300000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;mi&#34;&gt;903500000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;mi&#34;&gt;903700000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;basic_station&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;concentrators&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lora_std&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;frequency&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;903000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;bandwidth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;500000&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;spreading_factor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;configuration/chirpstack-gateway-bridge/chirpstack-gateway-bridge.toml&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Other than the MQTT broker host and port, the primary purpose of this configuration is to set up the channel plan that this gateway will use. This needs to match with one of the &lt;code&gt;enabled_regions&lt;/code&gt; configurations listed in the network server configuration above. To understand how to configure the LoRaWAN channel plan should be configured we need to understand two things:&lt;/p&gt;
&lt;p&gt;First, we need to know how the regional parameters specification defines the LoRaWAN channels for our region. In my case the &lt;a href=&#34;https://content.cdntwrk.com/files/aT0xNDI4MzI2JnY9MSZpc3N1ZU5hbWU9cnAyLTEtMC0zLWxvcmF3YW4tcmVnaW9uYWwtcGFyYW1ldGVycyZjbWQ9ZCZzaWc9ZDI4NDMxYzU2NTVjZjZjZDRiYzJiM2ExZGEwMTAzMmM%253D#page=30&#34;&gt;US915 LoRaWAN Regional Parameters&lt;/a&gt; specification defines 72 uplink channels. The first 64 channels are defined as 125khz wide channels spaced every 200khz starting 902.3Mhz. The remaining 8 channels are 500khz wide spaced every 1.6Mhz starting at 902.3Mhz&lt;/p&gt;
&lt;p&gt;The second thing to understand what is supported by the SX1308 radio baseband chip. The SX1308 has 10 independent receive demodulation data paths that operate in parallel. Channels 0-7 are fixed 125khz LoRa demodulators with independently controlled frequencies, which can demodulate all LoRa spreading factors simultaneously. Channel 8 has a configurable LoRa demodulator that supports 125khz, 250khz, and 500khz channel bandwidth and a configurable frequency and spreading factor, but can only demodulate a single data rate at a time. Finally channel 9 is a (G)FSK demodulator that is used to support the LR-FHSS channels, which use frequency hopping spread spectrum modulation instead of the LoRa chirp spread spectrum modulation used by the other channels.&lt;/p&gt;
&lt;p&gt;Given this, these are the important settings from the gateway configuration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;region&lt;/code&gt;, &lt;code&gt;frequency_min&lt;/code&gt;, &lt;code&gt;frequency_max&lt;/code&gt;: These simply define what LoRa region that the gateway is operating in, which for me is the US915 band, which operates between 902Mhz and 928Mhz.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;backend.basic_station.concentrators.multi_sf&lt;/code&gt;: This specifies the channel frequencies for the first eight channels of the SX1308. Note that since the regional specification defies more 125Mhz channels than the SX1308 has, we have to pick a subset of the defined channels. I am simply using the first 8 128Khz LoRaWAN channels.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;backend.basic_station.concentrators.lora_std&lt;/code&gt;: The &lt;code&gt;frequency&lt;/code&gt;, &lt;code&gt;bandwidth&lt;/code&gt;, and &lt;code&gt;spreading_factor&lt;/code&gt; values in this group configure demodulation channel 8 in the SX1308. Since we already have 8 125Khz channels configured, I am using this channel for one of the 500khz channels, specifically LoRaWAN channel 64.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;integration.mqtt.*_topic_template&lt;/code&gt;: In the ChirpStack v4 architecture, the MQTT topics the gateway bridge uses to communicate with the network server need to be prefixed with the channel plan name used by the network server. In this case this is &amp;ldquo;us915_0&amp;rdquo;, as that is the name of the region/channel plan configuration we used in chirpstack.toml.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;gateway-packet-forwarder&#34;&gt;Gateway Packet Forwarder&lt;/h3&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:200px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;SX1301_conf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;                  &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Actual&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;channel&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;plan&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;controlled&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;by&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;(ChirpStack&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Gateway&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Bridge)&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;device&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/dev/spidev0.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;   &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Default&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;SPI&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;device&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;lorawan_public&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;      &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Use&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;sync&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;header,&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;must&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;be&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;coordinated&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;all&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;devices&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;ahead&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;of&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;clksrc&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;                  &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;radio_1&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;provides&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;clock&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;to&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;concentrator&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;radio_0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;SX1257&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;rssi_offset&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;-166.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;tx_enable&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;        &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;First&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;SX1257&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;device&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;used&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;transmitter&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;antenna_gain&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;radio_1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;SX1257&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;rssi_offset&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;-166.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;tx_enable&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;        &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Second&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;SX&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1257&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;connected&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;to&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;transmit&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;pps&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;                  &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;My&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;GPS&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;doesn&amp;#39;t&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;have&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;pps&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;so&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;don&amp;#39;t&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;expect&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;station_conf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;log_file&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;stderr&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;DEBUG&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;XDEBUG,DEBUG,VERBOSE,INFO,NOTICE,WARNING,ERROR,CRITICAL&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;log_size&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;10000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;log_rotate&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;CUPS_RESYNC_INTV&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;gps&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/dev/serial0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;        &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;GPS&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Connected&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;to&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;serial0&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;device&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;pps&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;gps&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;   
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;station.conf&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Finally the configuration for the Basic Station packet forwarder application just needs to be configured based on the hardware setup of our gateway. The important settings here are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lorawan_public&lt;/code&gt;: Setting this option to &lt;code&gt;false&lt;/code&gt; which changes the LoRa sync header byte from the specification&amp;rsquo;s &lt;code&gt;0x34&lt;/code&gt; to &lt;code&gt;0x12&lt;/code&gt;. Since the sync header is used to identify the start of a packet, changing the sync header prevents packets on our private network from interfering with any public LoRaWAN network.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clksrc&lt;/code&gt;: This indicates which of the two SX1257 radio front-ends generates the 32Mhz radio clock for the SX1308. On the Pygate module this is the second SX1257.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;radio0&lt;/code&gt;/&lt;code&gt;radio1&lt;/code&gt;: These groups configure the two SX1257 front-ends connected to the SX1308. The main difference between them is that on the Pygate module, the &amp;ldquo;radio0&amp;rdquo; transmit path is hooked up, and it is not for &amp;ldquo;radio1&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pps&lt;/code&gt;: Since my GPS module does not have a PPS output that I can connect to that SX1308 chip this gets set to false.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;configuring-an-application-through-the-chirpstack-ui&#34;&gt;Configuring an Application Through the ChirpStack UI&lt;/h2&gt;
&lt;p&gt;Once these configurations are set up we can start the servers, but before we can start sending packets we need to configure an application that we can associate with our gateway and devices. This is done through the ChirpStack network server&amp;rsquo;s web interface.&lt;/p&gt;
&lt;p&gt;Fortunately this is another area where ChirpStack v4 has made things simpler. Previously it was necessary to configure an &amp;ldquo;Network Server&amp;rdquo;, &amp;ldquo;Organization&amp;rdquo;, &amp;ldquo;Network&amp;rdquo;, and &amp;ldquo;Service Srofile&amp;rdquo; before setting up an application. None of that is required anymore: Version 4 comes with a default &amp;ldquo;Tenant&amp;rdquo; (somewhat equivalent to an organization in v3), and since the network and application servers have been merged, there&amp;rsquo;s not a need to define the network server connection or configure a network through the UI anymore. This means we can jump right into configuring the application.&lt;/p&gt;
&lt;p&gt;The first thing to set up is a device profile by selecting the &amp;ldquo;Device Profiles&amp;rdquo; section on the sidebar, and clicking &amp;ldquo;Add device profile&amp;rdquo;. This configures common network parameters used by one or more types of devices connected to our application. The important settings to configure are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;General→Region and Region Configuration: Set &lt;code&gt;US915&lt;/code&gt; and &lt;code&gt;US915 (channels 0-7 + 64)&lt;/code&gt;, these will be the only options available, because these are the only channel plans we configured for the network server.&lt;/li&gt;
&lt;li&gt;General→MAC version: I am using &lt;code&gt;LoRaWAN 1.0.4&lt;/code&gt; because it the demo firmware I am using does not support the latest LoRaWAN 1.1.0 version.&lt;/li&gt;
&lt;li&gt;General→Regional parameters revision: Version &lt;code&gt;RP002-1.0.4&lt;/code&gt; is the latest specification, and is supported by my demo firmware.&lt;/li&gt;
&lt;li&gt;General→ADR algorithm: Make sure that the &lt;code&gt;LoRa only&lt;/code&gt; option is selected, as the gateway is not configured for LR-FHSS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To simply logging data into the PostgreSQL integration database, I chose to enable &amp;ldquo;Cayenne LPP&amp;rdquo; codec under the &amp;ldquo;Codec&amp;rdquo; tab of the device-profile settings. When enabled, the ChirpStack application server will assume uplink packets from devices with this profile are encoded with the &lt;a href=&#34;https://developers.mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload&#34;&gt;Cayenne LPP&lt;/a&gt; format, and automatically decoded them. This makes it simple encode basic binary values on the end-device and have them logged into the database in a meaningful way.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_create_device_profile.png&#34; alt=&#34;Creating a device profile.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_create_device_profile.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Creating a device profile.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The next step is to connect our gateway to the network server. To do this, under &amp;ldquo;Gateways&amp;rdquo; we create a new gateway with a name, gateway ID. The gateway ID is defined by the Basic Station packet forwarder application and by default is set to the MAC address of the primary network interface on the gateway. The network server and service profiles should be configured to the ones we have just created.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_add_gateway.png&#34; alt=&#34;Add gateway.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_add_gateway.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Add gateway.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Now we can up the application that runs on top of our LoRaWAN network. To do this we just create a new application with a name and a description. Since the PostgreSQL integration has been configured in &lt;code&gt;chirpstack.toml&lt;/code&gt; we don&amp;rsquo;t need to set up the additional integration settings for decoded data from received packets into the database.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_add_application.png&#34; alt=&#34;Create application.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_add_application.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Create application.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Finally we can add a our end-device to the application under the &amp;ldquo;Devices&amp;rdquo; tab, by selecting create and entering a device name, device EUI, and the previously created device profile. The Device EUI is a globally unique IEEE EUI64 identifier for the end-device, the CMWX1ZZABZ transceiver module comes pre-programmed with a Device EUI which will be used by firmware when sending join requests.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_add_device.png&#34; alt=&#34;Addng end-device to application.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_add_device.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Addng end-device to application.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Once the device has been created ChirpStack brings us to a page to enter or create an &amp;ldquo;Application Key&amp;rdquo;. This is a shared 128-bit key used by the end-device and network to derive encryption keys for all data sent across the network. It should be set to something random, and communicated to the end-device out of band.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_set_device_key.png&#34; alt=&#34;Add end-device to application.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/chirpstack_set_device_key.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Add end-device to application.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;And that&amp;rsquo;s it!&lt;/strong&gt; Our network and applications are set up and ready to go!&lt;/p&gt;
&lt;h2 id=&#34;sending-a-packet&#34;&gt;Sending a Packet&lt;/h2&gt;
&lt;p&gt;As previously mentioned, to test out the network I am running an example application which turns the CMWX1ZZABZ module into an AT style modem. I&amp;rsquo;m connecting to the modem firmware over the USB-Serial interface provided by the ST-Discovery development board I&amp;rsquo;m using.&lt;/p&gt;
&lt;p&gt;When the module comes out of reset we get the following output:
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;APP_VERSION:        V1.1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;MW_LORAWAN_VERSION: V2.3.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;MW_RADIO_VERSION:   V1.1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### OTAA ######
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### AppKey:      2B:7E:15:16:28:AE:D2:A6:AB:F7:15:88:09:CF:4F:3C
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### NwkKey:      2B:7E:15:16:28:AE:D2:A6:AB:F7:15:88:09:CF:4F:3C
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### ABP  ######
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### AppSKey:     2B:7E:15:16:28:AE:D2:A6:AB:F7:15:88:09:CF:4F:3C
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### NwkSKey:     2B:7E:15:16:28:AE:D2:A6:AB:F7:15:88:09:CF:4F:3C
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### DevEui:  XX:XX:XX:XX:XX:XX:XX:XX
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### AppEui:  01:01:01:01:01:01:01:01
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### DevAddr: XX:XX:XX:XX
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ATtention command interface
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AT? to list all available functions
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;From here we can send the commands to have our end-device join the network. First we need to set the &lt;code&gt;NwkKey&lt;/code&gt; to the &amp;ldquo;Application Key&amp;rdquo; we configured in the ChirpStack UI previously, then send the join command:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AT+NWKKEY=XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AT+JOIN=1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;When everything works correctly I get an output like the following, indicating that we have joined the network.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OK
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;299s464:TX on freq 903100000 Hz at DR 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OK
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;299s895:MAC txDone
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;304s884:RX_1 on freq 925700000 Hz at DR 10
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;305s018:MAC rxDone
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+EVT:JOINED
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+EVT:RX_1, DR 0, RSSI -80, SNR 8
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;To send a packet we use the &lt;code&gt;AT+SEND&lt;/code&gt; command which takes three arguments: The LoRaWAN port, which can be anything other than 0 (port zero indicates network management packet), a 1 or 0 indicating if we want the network to confirm receipt of the packet, and finally the data to send as a hex string. Since I have configured ChirpStack to decode packets using the Cayenne LPP protocol, the message I&amp;rsquo;m sending is formatted using Cayenne LPP. The following command will send a packet with a value of 4.00 for the &amp;ldquo;Analog Input 3&amp;rdquo; value on port 100.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AT+SEND=100:1:03020190
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;In response the firmware gives us this output indicating that it sent our message and it was confirmed by the network:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;685s981:TX on freq 902500000 Hz at DR 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OK
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;686s333:MAC txDone
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;687s323:RX_1 on freq 923900000 Hz at DR 10
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;687s434:MAC rxDone
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+EVT:SEND_CONFIRMED
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+EVT:RX_1, DR 10, RSSI -75, SNR 8
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;After sending a few of these messages, we can check the &lt;code&gt;event_up&lt;/code&gt; table in the PostgreSQL integration database through Grafana and see that each packet we sent was stored into the database, with the decoded payload in the &lt;code&gt;jsonb&lt;/code&gt; type &lt;code&gt;object&lt;/code&gt; column.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/grafana_example_data.png&#34; alt=&#34;Data from packets logged into PostgreSQL database.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/grafana_example_data.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Data from packets logged into PostgreSQL database.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Success!&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;next-steps&#34;&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;Now that I have a working LoRaWAN network, there are several areas that I will be investigating in the future.&lt;/p&gt;
&lt;p&gt;To start with I have just been using the built in PostgreSQL integration and Cayenne LPP packet format built into ChirpStack to log basic data. Of course the LoRaWAN network supports sending any data with any protocol, so now that I can reliably send data there are a lot of options of both custom and built in protocols that can be layered on top of LoRaWAN and ChirpStack.&lt;/p&gt;
&lt;p&gt;However as a proof of concept I modified the one of the other STM32 LoRaWAN examples to use the internal temperature sensor inside the STM32L0 to log data using Cayenne LPP (source code here &lt;a href=&#34;https://gitlab.com/prbs23/lorawan_env_sensor_fw&#34;&gt;prbs23/lorawan_env_sensor_fw&lt;/a&gt;. Then using a Grafana dashboard I was able to make a nice dashboard that shows historical temperatures.


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/grafana_temperature_data.png&#34; alt=&#34;Simple temperature history dashboard in Grafana.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/grafana_temperature_data.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Simple temperature history dashboard in Grafana.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;This is really a proof of concept for a more custom built data collection network I am looking to build on top of LoRaWAN. I have already started building some custom firmware from scratch using the &lt;a href=&#34;https://github.com/Lora-net/LoRaMac-node&#34;&gt;LoRaMAC&lt;/a&gt; library, but there is still much to be done in terms of defining protocols, device commissioning, and custom sensor support.&lt;/p&gt;
&lt;p&gt;Finally, as previously mentioned I have not configured all the authentication and authorization features supported by ChirpStack. I need to spend some more time investigating and learning how to configure these to better secure my network.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Technically in some regions such as US915 it is valid to only support a single channel, in which case a single transceiver could act as a gateway. A proof of concept implementation for this can be found in this &amp;ldquo;&lt;a href=&#34;https://github.com/sealj553/lorawan-gateway&#34;&gt;Single Channel LoRaWAN Gateway&lt;/a&gt;&amp;rdquo; project on Github. However in other regions such as EU868 region, for example, a &lt;a href=&#34;https://content.cdntwrk.com/files/aT0xNDI4MzI2JnY9MSZpc3N1ZU5hbWU9cnAyLTEtMC0zLWxvcmF3YW4tcmVnaW9uYWwtcGFyYW1ldGVycyZjbWQ9ZCZzaWc9ZDI4NDMxYzU2NTVjZjZjZDRiYzJiM2ExZGEwMTAzMmM%253D#page=25&#34;&gt;gateway is &lt;em&gt;required&lt;/em&gt; to support&lt;/a&gt; at a minimum of three specific channels.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Theoretically ChirpStack also includes its own equivalent to the Basics Station application, in the form of &lt;a href=&#34;https://www.chirpstack.io/docs/chirpstack-concentratord/index.html&#34;&gt;Concentratord&lt;/a&gt;. However at the time, the integration between the Concentratord and the Gateway Bridge was &lt;a href=&#34;https://www.chirpstack.io/gateway-bridge/backends/concentratord/&#34;&gt;listed as &amp;ldquo;experimental&amp;rdquo;&lt;/a&gt;, so I have not actually tried this out.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Combining Dist Constarints in SystemVerilog</title>
      <link>https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/</link>
      <pubDate>Thu, 25 May 2023 23:32:04 -0600</pubDate>
      <author>prbs23@prbs23.com (prbs23)</author>
      <guid>https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/</guid>
      <description>&lt;h2 id=&#34;basic-distribution-constraints&#34;&gt;Basic Distribution Constraints&lt;/h2&gt;
&lt;p&gt;In SystemVerilog, almost all constraints are some form of boolean expression. The constraint solver&amp;rsquo;s job is to satisfy all these boolean expression when choosing random values. The two exceptions to this are &lt;code&gt;solve before&lt;/code&gt; constraints, which control the order of value resolution, and &lt;code&gt;dist&lt;/code&gt; constraints, witch control the distribution of values chosen. This latter type of constraint is what we are looking at in this post.&lt;/p&gt;
&lt;p&gt;At a high level the distribution constraint provides a syntax for defining the &lt;a href=&#34;https://en.wikipedia.org/wiki/Probability_mass_function&#34;&gt;probability mass function&lt;/a&gt; for a random variable over a given range of discrete values. Below are two examples of &lt;code&gt;dist&lt;/code&gt; constraints and the resulting distribution of values over 100k randomizations. The first specifies linearly increasing probability over four values, while the second shows exponentially increasing probability for the same values.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Linear Distribution Function&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Exponential Distribution Function&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;constraint&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;linear_dist_c&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;dist&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;4&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;
&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;constraint&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;linear_dist_c&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;dist&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;4&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mh&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;128&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;
&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;

&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/linear_dist_8.png&#34; alt=&#34;Distribution of values with linear `dist` constraint&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/linear_dist_8.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Distribution of values with linear `dist` constraint
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;

&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/exp_dist_8.png&#34; alt=&#34;Distribution of values with exponential `dist` constraint&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/exp_dist_8.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Distribution of values with exponential `dist` constraint
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In both these cases the probability of the constraint solver choosing a given value is the value&amp;rsquo;s weight divided by the sum of weights. For example the probability of selecting the value 0 in our linear distribution is &lt;code&gt;1/(1+2+...+8) = 1/38 = 0.02631&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Obviously we can make these distributions more complicated, and the language has various syntax variations for specifying distributions. However, the question in this post is what happens if we have multiple different distribution constraints defined for the same variable?&lt;/p&gt;
&lt;h2 id=&#34;applying-multiple-dist-constraints&#34;&gt;Applying Multiple &lt;code&gt;dist&lt;/code&gt; constraints&lt;/h2&gt;
&lt;p&gt;Our first question might be, what does the SystemVerilog LRM say about combinations of distribution constraints? Unfortunately it is completely unspecified. The only real specification about &lt;em&gt;how&lt;/em&gt; constraint solvers should behave with regard to distribution constraints is as follows.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Absent any other constraints, the probability that the expression matches any value in the list is proportional
to its specified weight. If there are constraints on some expressions that cause the distribution weights on
these expressions to be not satisfiable, implementations are only required to satisfy the constraints. An
exception to this rule is a weight of zero, which is treated as a constraint.&lt;/p&gt;
&lt;p&gt;&amp;ndash; IEEE Std 1800-2017 &amp;ldquo;IEEE Standard for SystemVerilog&amp;ndash;Unified Hardware Design, Specification, and Verification Language&amp;rdquo; section 18.5.4&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&amp;rsquo;s not very helpful. So what happens if we just try it? The figure below shows various combinations of three different distribution constraints, and linear and exponentially increasing distributions from above, along with a uniform distribution where all values are given the same probability. For each combination of distributions I randomized a value 100k times, using three different simulators, to see how various SystemVerilog implementations compared.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/simple_dist_combinations.png&#34; alt=&#34;Distribution of values with various combinations of distribution constraints in three simulators.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/simple_dist_combinations.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Distribution of values with various combinations of distribution constraints in three simulators.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;See the following EDAPlayground for how I generated the data these plots: &lt;a href=&#34;https://edaplayground.com/x/wVNy&#34;&gt;Multiple Dist Constraints&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;From this experiment we can see that all three simulators combine the distribution constraints together in some way to create a new distribution (ie. they don&amp;rsquo;t discard the previous dist constraint). However, there appear to be two different methods used, as the combined distributions for Xcelium and VCS are very similar (though maybe not exactly the same), and Questa seems to do something entirely different. Let&amp;rsquo;s see if we can figure out what the tools are doing.&lt;/p&gt;
&lt;h3 id=&#34;combining-distributions-in-xcelium-and-vcs&#34;&gt;Combining Distributions in Xcelium and VCS&lt;/h3&gt;
&lt;p&gt;As far as I could tell, none of the simulators document how they handle combinations of distribution constraints. However, with some experimentation it wasn&amp;rsquo;t hard to come up with a function that appears to behave similarly to Xcelium and VCS. It appears in the case of both Xcelium and VCS, the probability of a given value being selected when there are multiple distribution constraints is simply the average of probabilities from all distribution constraints. Additionally, from the LRM we know that the probability of a given weight for a single &lt;code&gt;dist&lt;/code&gt; constraint is the weight for that value divided by all weights. Therefore, more formally we can say, given &lt;em&gt;N&lt;/em&gt; distribution constraints where the weight for a value &lt;em&gt;x&lt;/em&gt; is given by the function &lt;em&gt;weight&lt;sub&gt;n&lt;/sub&gt;(x)&lt;/em&gt;, the resulting probability mass function is:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;xcelium_vcs_simple_combination_function.svg&#34; alt=&#34;p(x) = \frac{\sum_{n=1}^{N} \frac{weight_n(x)}{\sum_{m=-\infty}^{\infty} weight(m)}}{N}&#34;&gt;&lt;/p&gt;
&lt;p&gt;The figure below shows a comparison of using this formula to compute the expected distributions vs the observed distribution from Xcelium and VCS.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/computed_xcelium_vcs_dist.png&#34; alt=&#34;Comparison of Xcelium and VCS distributions to the average of probabilities.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/computed_xcelium_vcs_dist.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Comparison of Xcelium and VCS distributions to the average of probabilities.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;But wait! The SystemVerilog spec says that a weight of zero in a &lt;code&gt;dist&lt;/code&gt; constraint is considered as a hard constraint keeping the variable not equal to the value. If they were just averaging the probabilities together the average of a 0 and non-zero probability is going to be a non-zero value. If we actually test this by running the same experiment with the same sets of constraints, except that the values 1 and 6 are given a weight of 0 in the uniform &lt;code&gt;dist&lt;/code&gt; constraint we get the results in the figure below. Note that the simple average of probabilities doesn&amp;rsquo;t match the actual measured distributions.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/computed_xcelium_vcs_partial_dist.png&#34; alt=&#34;Comparison of Xcelium and VCS distributions to the simple average of probabilities and zero-filtered average of probabilities.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/computed_xcelium_vcs_partial_dist.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Comparison of Xcelium and VCS distributions to the simple average of probabilities and zero-filtered average of probabilities.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;We can reproduce the same distribution we observe from Xcelium and VCS with a small modification to the average of probabilities combination technique. The averaging of probabilities appears to function the same, however when computing the probability of selecting a value for each individual distribution constraint, the weight for a value is considered as 0 if &lt;em&gt;any&lt;/em&gt; other &lt;code&gt;dist&lt;/code&gt; constraint has a weight of 0. Therefore a more accurate equation for the final probability mass function for a combination of &lt;em&gt;N&lt;/em&gt; distribution constraints would be:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;xcelium_vcs_zero_filtered_combination_function.svg&#34; alt=&#34;p(x) = \frac{\sum_{n=1}^{N} \frac{weight_n(x) \times (\prod_{i=1}^{N} weight_n(i) &amp;gt; 0)}{\sum_{m=-\infty}^{\infty} (weight(m) \times (\prod_{i=1}^{N} weight_n(i) &amp;gt; 0))}}{N}&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;combining-distributions-in-questa&#34;&gt;Combining Distributions in Questa&lt;/h3&gt;
&lt;p&gt;While Xcelium and VCS appear to use a similar function for combining distributions, the Questa simulator does something significantly different. From some experimentation, whatever they are doing appears equivalent to multiplying the weights (not probabilities) from all &lt;code&gt;dist&lt;/code&gt; constraints for each value together, and then computing the probability from these weights. Formally we could write this probability mass function for the combination of N constraints given the weight functions &lt;em&gt;weight&lt;sub&gt;n&lt;/sub&gt;(x)&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;questa_combination_function.svg&#34; alt=&#34;p(x) = \frac{\prod_{n=1}^{N} weight_n(x)}{\sum_{m=-\infty}^{\infty} (\prod_{n=1}^{N} weight_n(x))}&#34;&gt;&lt;/p&gt;
&lt;p&gt;Comparing this probability formulas to the actual results from Questa we get effective the same distributions.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/computed_questa_dist.png&#34; alt=&#34;Comparison of Questa distributions to the normalized product of probabilities.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/computed_questa_dist.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Comparison of Questa distributions to the normalized product of probabilities.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The nice thing about this way of combining distributions is that we get for free that a weight of zero from one constraint will always result in a final probably being 0 as well. If we do a similar test as we did for the other simulators with a partial uniform distribution constraint where some values have a zero probability, we see our formula still matches the results from Questa.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/computed_questa_partial_dist.png&#34; alt=&#34;Comparison of Questa distributions to the product of weights.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/combining-dist-constraints-in-system-verilog/computed_questa_partial_dist.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Comparison of Questa distributions to the product of weights.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h2 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;While all three simulators do something I would consider reasonable, I&amp;rsquo;m not sure either behavior is necessarily what I would want in any given application. Additionally, since there is no specification defined behavior, there&amp;rsquo;s no guarantees about how your constraint solver will handle any given set of constraints.&lt;/p&gt;
&lt;p&gt;Overall, this investigation has made me wary of ever apply two distribution constraints to the same variable. In general I think I will be putting my distribution constraints inside their own &lt;code&gt;constraint&lt;/code&gt; blocks, so that they can be disabled individually, and replaced, instead of combined with another constraint. It&amp;rsquo;s helpful to now know exactly what a given simulator will do, but I wouldn&amp;rsquo;t rely on it.&lt;/p&gt;
&lt;p&gt;Finally, I have only looked into how multiple distribution constraints interact when applied to a single variable, but there&amp;rsquo;s other questions too&amp;hellip; What if you had two distribution constraints on different variables, but a hard constraint keeping the variables equal? How do distribution constraints interact with other types of hard constraints? One day, maybe I will investigate these too.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Getting Started With a Private LoRaWAN Network</title>
      <link>https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/</link>
      <pubDate>Thu, 07 Jul 2022 22:01:39 -0600</pubDate>
      <author>prbs23@prbs23.com (prbs23)</author>
      <guid>https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/</guid>
      <description>&lt;h4 id=&#34;note&#34;&gt;Note:&lt;/h4&gt;
&lt;p&gt;Since publishing this post, ChirpStack released their version 4 LoRaWAN network server, which significantly changes the architecture and configuration. This post goes through setting up a network with ChirpStack v3, for an updated version of this post using ChirpStack v4, see &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network-updated/&#34;&gt;Getting Started With a Private LoRaWAN Network Using ChirpStack v4&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I have recently started experimenting with LoRa radios, and the LoRaWAN network protocol. Interestingly it seems that the LoRa ecosystem is simultaneously surprisingly open (both in specifications and implementations), while also having very little definitive documentation on how to actually get started as an indvidual hacker. As such I found the process of learning enough of the protocol to set up my own network surprisingly difficult.&lt;/p&gt;
&lt;p&gt;This post will walk through the process of setting up my own private LoRaWAN network, from low cost hardware all the way up to the application level. While I would never claim to be an expert in LoRa or LoRaWAN, I hope someone finds this a useful reference.&lt;/p&gt;
&lt;h2 id=&#34;lora-and-lorawan-basics&#34;&gt;LoRa and LoRaWAN Basics&lt;/h2&gt;
&lt;p&gt;LoRa (not to be confused with LoRaWAN) is a proprietary modulation and physical layer standard for low power wireless communication, originally developed in 2010 by Cycleo, which was later acquired by Semtech in 2012. The LoRa physical layer uses a &amp;ldquo;Chirp Spread Spectrum&amp;rdquo; (CSS) modulation technique to spread the signal over either 125, 250, or 500MHz bands. The use of CSS modulation, instead of the more common &amp;ldquo;Direct Sequence Spread Spectrum&amp;rdquo; (DSSS) allows LoRa to transmit over longer ranges and be more robust to many common types of interference.&lt;/p&gt;
&lt;p&gt;The advertised range for LoRa transceivers tend to be in the 1km to 10km range, though from my research it realistically is more often on the lower end of that range. I have not tested what range I can get from my system yet, so I don&amp;rsquo;t have my own empirical data. More information about LoRa modulation and the physical layer can be found in this &lt;a href=&#34;https://semtech--c.na98.content.force.com/sfc/dist/version/download/?oid=00DE0000000JelG&amp;amp;ids=0682R000005PU6SQAW&amp;amp;d=%252Fa%252F2R0000001OJk%252FyDEcfAkD9qEz6oG3PJryoHKas3UMsMDa3TFqz1UQOkM&#34;&gt;Application Note&lt;/a&gt; from Semtech, and this excellent &lt;a href=&#34;http://www.sghoslya.com/&#34;&gt;series of blog posts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;LoRaWAN, on the other hand, is a &amp;ldquo;Low Power Wide Area Network&amp;rdquo; (LPWAN) specification, built on top of the LoRa physical layer. LoRaWAN defines a network architecture, MAC layer, and link layer protocols, primarily targeted at building low power and bandwidth sensor networks. The LoRaWAN standard is defined by the &lt;a href=&#34;https://lora-alliance.org&#34;&gt;LoRa Alliance&lt;/a&gt;, and is published as an open standard in two primary parts: The &lt;a href=&#34;https://resources.lora-alliance.org/technical-specifications/ts001-1-0-4-lorawan-l2-1-0-4-specification&#34;&gt;LoRaWAN L2 Specification&lt;/a&gt; which defines everything except specific physical layer parameters, and the &lt;a href=&#34;https://resources.lora-alliance.org/technical-specifications/rp2-1-0-3-lorawan-regional-parameters&#34;&gt;LoRaWAN Regional Parameters&lt;/a&gt; which defines physical layer parameters, such as frequency bands, bandwidth, etc. for specific regions of the world, in order to be compliant with local regulations.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/lorawan_network_architecture.png&#34; alt=&#34;Basic LoRaWAN Network Architecture.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/lorawan_network_architecture.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Basic LoRaWAN Network Architecture.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The basic architecture of a LoRaWAN network is shown above. Each LoRaWAN &amp;ldquo;End-Device&amp;rdquo; communicates with one or more &amp;ldquo;Gateway&amp;rdquo; devices over the LoRa physical layer. These gateways forward messages between the end-devices and a &amp;ldquo;Network Server&amp;rdquo; over standard TCP/IP networking. The network server manages all devices connected to one or more gateways, including device activation (joining a network), congestion control, etc. Device authentication is handled by a separate &amp;ldquo;Join Server&amp;rdquo; that interfaces with one or more network servers, and finally any non-management messages from a device are sent from the network server to an application server.&lt;/p&gt;
&lt;h2 id=&#34;getting-started&#34;&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;The simplest way to start using LoRaWAN, is to buy some LoRaWAN devices, connect them to a public network such as &lt;a href=&#34;https://www.thethingsnetwork.org/&#34;&gt;The Things Network&lt;/a&gt;, and you are good to go. While this is simple because all the network and server management is handled by someone else, I wanted to set up my own network. This was partially just to understand how all the pieces of the LoRaWAN network work, but also because I don&amp;rsquo;t have coverage from any of these networks where I live anyway.&lt;/p&gt;
&lt;p&gt;To set up your own network you need essentially four things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;End device hardware, generally a LoRa transceiver and a microcontroller.&lt;/li&gt;
&lt;li&gt;Firmware network stack to run on the end device.&lt;/li&gt;
&lt;li&gt;Gateway radio for the end devices to connect to.&lt;/li&gt;
&lt;li&gt;Gateway client and network/join server software.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;end-devices&#34;&gt;End-Devices&lt;/h3&gt;
&lt;h4 id=&#34;hardware&#34;&gt;Hardware&lt;/h4&gt;
&lt;p&gt;For end-device hardware, there are many different options, from &lt;a href=&#34;https://lora-alliance.org/showcase/search/?_sfm_lorawan_certified_device=certified&amp;amp;_sft_product_categories=end-node-sensors-or-other-devices&#34;&gt;off the shelf LoRaWAN connected sensors and devices&lt;/a&gt;, to bare &lt;a href=&#34;https://www.semtech.com/lora/lora-products&#34;&gt;LoRa transceiver chips&lt;/a&gt;. For my first foray into LoRaWAN I chose to use an integrated LoRa radio/microcontroller module, since I believe it provides the most direct development path to building my own custom device PCBs in the future, while also allowing complete programmability of the device. There are several manufactures that produce these modules with different specifications, but I chose the &lt;a href=&#34;https://www.murata.com/en-us/products/connectivitymodule/lpwa/overview/lineup/type-abz&#34;&gt;CMWX1ZZABZ&lt;/a&gt; module from muRata as it was a fairly common, low cost option.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/b-l072z-lrwan1_development_board.png&#34; alt=&#34;B-L072Z-LRWAN1 board for CMWX1ZZABZ module used as end-device for testing network.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/b-l072z-lrwan1_development_board.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            B-L072Z-LRWAN1 board for CMWX1ZZABZ module used as end-device for testing network.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The CMWX1ZZABZ module integrates a Semtech SX1276 LoRa transceiver, RF switch, and STM32L microcontroller into single surface mount RF module that is easily integrated onto a custom PCB. The integration of a STM32 MCU is convenient, as this is a microcontroller platform that I am already familiar with. Additionally, ST Microelectronics even sells the &lt;a href=&#34;https://www.st.com/en/evaluation-tools/b-l072z-lrwan1.html&#34;&gt;B-L072Z-LRWAN1&lt;/a&gt; development board that included everything I needed to get started building a LoRaWAN end-device.&lt;/p&gt;
&lt;h4 id=&#34;firmware-networking-stack&#34;&gt;Firmware Networking Stack&lt;/h4&gt;
&lt;p&gt;In addition to the end-device hardware, I also needed firmware that implemented the end-device side of the LoRaWAN protocol. Fortunately, Semtech have published a BSD licensed &lt;a href=&#34;https://github.com/Lora-net/LoRaMac-node&#34;&gt;LoRaWAN end-device networking stack&lt;/a&gt; that works with many of their LoRa transceiver chips. Even better, STMicroelectronics, provide a &lt;a href=&#34;https://www.st.com/en/embedded-software/i-cube-lrwan.html&#34;&gt;packaged version of this library&lt;/a&gt; that is already integrated with their STM32Cube embedded software stack. This makes it very simple to get some demo applications running on the B-L072Z-LRWAN1 development board with very little effort.&lt;/p&gt;
&lt;p&gt;One of the demo applications provided as part of the STM32Cube LoRaWAN expansion makes the module behave as a &lt;a href=&#34;https://www.st.com/resource/en/application_note/an4967-examples-of-at-commands-on-icubelrwan-stmicroelectronics.pdf&#34;&gt;fully featured LoRaWAN modem that accepts AT style commands&lt;/a&gt; over a UART port. This AT modem application firmware is what I ended up using to test and debug, as it gave me interactive control over my end-device with known working firmware, so I didn&amp;rsquo;t need to debug my own firmware at the same time as my network configuration.&lt;/p&gt;
&lt;h3 id=&#34;gateway&#34;&gt;Gateway&lt;/h3&gt;
&lt;h4 id=&#34;hardware-1&#34;&gt;Hardware&lt;/h4&gt;
&lt;p&gt;Unlike the end-devices, the hardware for a Gateway turns out to be a bit more complicated. Primarily this is because while the end-device only needs to transmit or receive on one channel at a time, gateways generally must be able to receive packets from multiple end-devices on multiple channels simultaneously&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. Additionally, not only must the gateway be able to receive packets on typically several channels at once, in order to support the class B beacon protocol the gateway must use &lt;a href=&#34;https://content.cdntwrk.com/files/aT0xNDI4Mzk1JnY9MSZpc3N1ZU5hbWU9dHMwMDEtMS0wLTQtbG9yYXdhbi1sMi0xLTAtNC1zcGVjaWZpY2F0aW9uJmNtZD1kJnNpZz0xYWMyYjRiZjM3YTExNWMzZDU2NzBiYTM4OTAyY2E5ZA%253D%253D#%5B%7B%22num%22%3A166%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C69%2C733%2C0%5D&#34;&gt;GPS to get precise time and location information&lt;/a&gt;, and also has to have a full TCP/IP networking stack to communicate with the network server.&lt;/p&gt;
&lt;p&gt;Now, there &lt;a href=&#34;https://lora-alliance.org/showcase/search/?_sft_product_categories=gateway&#34;&gt;&lt;em&gt;are&lt;/em&gt; full off the shelf LoRaWAN gateways for sale&lt;/a&gt;, but most don&amp;rsquo;t even publish a price, and as the adage goes &amp;ldquo;if you have to ask the price you can&amp;rsquo;t afford it&amp;rdquo;. Even the few gateway devices that you can easily purchase are usually north of $400, which was a little out of &lt;em&gt;my&lt;/em&gt; budget. After some research, it turns out that most LoRaWAN gateways are built off the same Semtech SX130x (baseband)/SX125x (front-end) chipset, connected to something like a fairly beefy ARM processor running Linux. The general structure of one of these gateways is shown in the block diagram below, from the Semtech LoRaWAN gateway &amp;ldquo;Corecell&amp;rdquo; reference design.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/lorawan_corecell_block_diagram.png&#34; alt=&#34;Semtech Block Diagram of &amp;#34;Corecell&amp;#34; gateway reference design&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/lorawan_corecell_block_diagram.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Semtech Block Diagram of &amp;#34;Corecell&amp;#34; gateway reference design
             (&lt;a href=&#34;https://semtech--c.na98.content.force.com/sfc/dist/version/download/?oid=00DE0000000JelG&amp;amp;ids=0682R000006GEO0QAO&amp;amp;d=%2Fa%2F2R000000HV3G%2Fan5Oh4KfiY7tAO1MO7RiPOj08WUpOTjPHSF57_.NlJ0&amp;amp;operationContext=DELIVERY&amp;amp;asPdf=true&amp;amp;viewId=05H2R000002Z67aUAC&amp;amp;dpt=#page=6&#34;&gt;Semtech Corecell Reference Design Users Guide&lt;/a&gt;)
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Since I was trying to avoid spending hundreds of dollars if I could, I did briefly consider building my own gateway PCB based on this architecture. However due to the ongoing &amp;ldquo;Chipageddon&amp;rdquo;, I couldn&amp;rsquo;t actually get my hands on bare SX1308 chips. In the end I found that the &lt;a href=&#34;https://pycom.io/&#34;&gt;Pycom&lt;/a&gt; makes an expansion board for their &lt;a href=&#34;https://docs.pycom.io/datasheets/development/wipy3/&#34;&gt;Wipy microcontroller modules&lt;/a&gt; that turns them into a LoRaWAN gateway. Now, I wasn&amp;rsquo;t interested in actually using a Wipy or their associated software, but the &lt;a href=&#34;https://docs.pycom.io/datasheets/expansionboards/pygate/&#34;&gt;&amp;ldquo;Pygate&amp;rdquo; expansion module&lt;/a&gt; is essentially a copy of the Semtech reference design above that I could pick one up for &lt;a href=&#34;https://www.digikey.com/en/products/detail/pycom-ltd/604565285973/12354323&#34;&gt;$52 from Mouser&lt;/a&gt;.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/lorawan_basestation_wiring.png&#34; alt=&#34;Connections between Raspberry PI, Pygate module, and GPS.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/lorawan_basestation_wiring.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Connections between Raspberry PI, Pygate module, and GPS.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;To make a full gateway device, I connected the Pygate module to an old Raspberry Pi (Model B rev. 2) and a &lt;a href=&#34;https://www.sparkfun.com/products/13670&#34;&gt;GP-736&lt;/a&gt; GPS module that I had laying around. The GPS module unfortunately does not have a 1pps output that can be used for very precise timing. But even it&amp;rsquo;s limitations this it is sufficient to build an equivalent system to the Corecell reference design, and more importantly, works with the Basic Station software from Semtech.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/lorawan_basestation_system.png&#34; alt=&#34;Hardware setup for my low cost LoRaWAN gateway.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/lorawan_basestation_system.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Hardware setup for my low cost LoRaWAN gateway.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h4 id=&#34;gateway-software&#34;&gt;Gateway Software&lt;/h4&gt;
&lt;p&gt;To go along with Semtech&amp;rsquo;s Corecell reference design, they also provide the source for an packet forwarder application they call &lt;a href=&#34;https://github.com/lorabasics/basicstation&#34;&gt;LoRa Basics Station&lt;/a&gt;. This software contains the code to drive the SX1308 and SX1257 chips on the Pygate board, and read timing and location information from a standard NEMA GPS data stream and. This application connects a LoRa network server to forward packets between end devices and the LoRaWAN network.&lt;/p&gt;
&lt;p&gt;Building the Basics Station software turned out to be fairly straight forward. In fact Semtech have already put in the hard work to get it building on Raspberry Pi, so it&amp;rsquo;s as simple as:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/lorabasics/basicstation.git basicstation
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; basicstation
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make &lt;span class=&#34;nv&#34;&gt;platform&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;rpi &lt;span class=&#34;nv&#34;&gt;variant&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;std
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;We will get into configuring and running the software a bit later, as that turned out to be the tricky bit, and requires some understanding of the rest of the network servers.&lt;/p&gt;
&lt;p&gt;While the Basic Station software was already set up to drive the whole SX1308/SX1257 chipset at runtime, it didn&amp;rsquo;t handle everything for me. Specifically, the Pygate board has four input pins that must be driven correctly by the Raspberry Pi before it will work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;RFPWR_EN:&lt;/strong&gt; Enable signal to voltage regulator for RF power rail. Needs to be driven high to power RF front-end.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FEM_EN&lt;/strong&gt;: Enable signal to RF front-end. Must be driven high before enable front end circuits.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SX1308_RST:&lt;/strong&gt; Active high reset for SX1308 chip. Can be driven high to reset SX1308 chip, must be low at runtime.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SX1257_RST:&lt;/strong&gt; Active high reset for both SX1257 chip. Can be driven high to reset the SX1257 chips, must be low during operation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While these possibly could have been tied high and low respectively, I connected these signals to Raspberry Pi GPIO pins and used the following shell script to power up the Pygate board and bring the chips out of reset:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:300px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;sx1308_rst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;sx1257_rst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;27&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;fem_en&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;rfpwr_en&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Export IOs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$sx1308_rst&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/export&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$sx1257_rst&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/export&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fem_en&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;     &amp;gt; /sys/class/gpio/export&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$rfpwr_en&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;   &amp;gt; /sys/class/gpio/export&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Make IOs outputs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;out&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1308_rst&lt;/span&gt;/direction&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;out&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1257_rst&lt;/span&gt;/direction&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;out&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$rfpwr_en&lt;/span&gt;/direction&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;out&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$fem_en&lt;/span&gt;/direction&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Enable power rails and assert resets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1308_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1257_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$rfpwr_en&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$fem_en&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Deassert resets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1308_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1257_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;pygate_setup.sh&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;One advantage of driving these enables/resets from GPIOs is that I can also reset or power down the whole LoRa chipset remotely using another shell script:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:300px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;sx1308_rst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;sx1257_rst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;27&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;fem_en&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;rfpwr_en&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Assert resets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1308_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$sx1257_rst&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Disable power rails&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$fem_en&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &amp;gt; /sys/class/gpio/gpio&lt;span class=&#34;nv&#34;&gt;$rfpwr_en&lt;/span&gt;/value&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;pygate_shutdown.sh&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;h3 id=&#34;servers&#34;&gt;Servers&lt;/h3&gt;
&lt;p&gt;With the network hardware figured out, I just just needed the core LoRaWAN servers and an application back-end to send data to. For the LoRaWAN core servers ended up choosing to use &lt;a href=&#34;https://www.chirpstack.io/&#34;&gt;ChirpStack&lt;/a&gt;. It provides an integrated solution for the full LoRaWAN network core that is simple to self-host, has many back-end integrations out of the box, and is open source. For my back-end application I&amp;rsquo;m starting with logging messages to an InfluxDB time series database.&lt;/p&gt;
&lt;p&gt;All the details of the ChirpStack architecture, can be found in their &lt;a href=&#34;https://www.chirpstack.io/project/architecture/&#34;&gt;architecture documentation&lt;/a&gt;. Depending on the size of the network, and applications being run over it, ChirpStack can be deployed in different ways, but for my initial setup, this is what I architecture ended up landing on:&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_deployment.png&#34; alt=&#34;ChirpStack server deployment.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_deployment.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            ChirpStack server deployment.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h4 id=&#34;chirpstack-on-the-gateway&#34;&gt;ChirpStack On the Gateway&lt;/h4&gt;
&lt;p&gt;To connect the Semtech Basic Station packet forwarder into the ChirpStack infrastructure we need to use the ChirpStack Gateway Bridge. This acts as a shim between the Semtech LNS protocol used by the Basic Station packet forwarder and the MQTT protocol and API used by ChirpStack&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. We have the choice of running the Gateway Bridge either on the Raspberry Pi gateway or on the server with the ChirpStack Network Server, I chose to run it on the Raspberry Pi so that all the external network communication would be happening within the domain of ChirpStack protocols.&lt;/p&gt;
&lt;p&gt;Unlike the other ChirpStack components I ended up needing to build the gateway bridge from source, this was because my Raspberry Pi board was old enough that the CPU was not supported by the pre-built ARM binaries. Fortunately this is not too difficult, I just needed to install the Go compiler and run make:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt-get intall golang-go
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/brocaar/chirpstack-gateway-bridge.git chirpstack-gateway-bridge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; chirpstack-gateway-bridge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;h4 id=&#34;chirpstack-in-docker-compose&#34;&gt;ChirpStack in Docker Compose&lt;/h4&gt;
&lt;p&gt;The rest of the chirpstack components and my back-end database I am running on a standard x86 computer running linux as docker containers, managed with docker-compose. ChirpStack conveniently publishes an example docker-compose configuration on this GitHub page: &lt;a href=&#34;https://github.com/brocaar/chirpstack-docker&#34;&gt;brocaar/chirpstack-docker&lt;/a&gt;. This gave me a good starting place but the configuration did not match the server setup I was looking for, and did not include my back-end InfluxDB database. My final docker-compose and other configurations can be found in the &lt;a href=&#34;https://gitlab.com/prbs23/chirpstack-docker&#34;&gt;prbs23/chirpstack-docker GitLab repository&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:400px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;54
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;55
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;56
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;57
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;58
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;59
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;60
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;61
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;62
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;63
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;64
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;65
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;3&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;chirpstack-network-server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;chirpstack/chirpstack-network-server:3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./configuration/chirpstack-network-server:/etc/chirpstack-network-server&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;depends_on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;postgresql&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;mosquitto&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;chirpstack-application-server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;chirpstack/chirpstack-application-server:3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;7070&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;7070&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./configuration/chirpstack-application-server:/etc/chirpstack-application-server&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;depends_on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;chirpstack-network-server&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;postgresql&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres:9.6-alpine&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;POSTGRES_PASSWORD=&amp;lt;POSTGRESS_ROOT_PASSWORD&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./configuration/postgresql/initdb:/docker-entrypoint-initdb.d&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;postgresqldata:/var/lib/postgresql/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;redis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;redis:5-alpine&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;redisdata:/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;mosquitto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;eclipse-mosquitto:2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;m&#34;&gt;7072&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;7072&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./configuration/eclipse-mosquitto/mosquitto.conf:/mosquitto/config/mosquitto.conf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;influxdb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;influxdb:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# Mount for influxdb data directory and configuration&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./influxdb2:/var/lib/influxdb2:rw&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;7074:8086&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;grafana&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;grafana/grafana-oss:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s1&#34;&gt;&amp;#39;7075:3000&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;grafana-storage:/var/lib/grafana&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./grafana-provisioning/:/etc/grafana/provisioning&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;depends_on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;influxdb&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GF_SECURITY_ADMIN_USER=admin&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;GF_SECURITY_ADMIN_PASSWORD=&amp;lt;GRAFANA_ADMIN_PASSWORD&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;postgresqldata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;redisdata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;grafana-storage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;docker-compose.yml&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;There are a couple main things that I customized from ChirpStack&amp;rsquo;s default docker-compose file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Removed the ChirpStack Gateway Bridge container, because I am running the gateway bridge on my gateway device itself.&lt;/li&gt;
&lt;li&gt;Changed the external facing ports. I have other applications running on this server that already used the default ports.&lt;/li&gt;
&lt;li&gt;Added InfluxDB time series database and Grafana containers to the configuration. Because my first application for this network will be data logging, I&amp;rsquo;m using the InfluxDB integration with ChirpStack as an easy time series data store, and Grafana to plot the collected data.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;configuring-the-network&#34;&gt;Configuring the Network&lt;/h2&gt;
&lt;p&gt;Now that I have all the hardware and software pieces I need for a private LoRaWAN network, I just needed to configure them to work together. This turned out to be more complicated than I expected, and also poorly documented, so I&amp;rsquo;ll walk through the important configurations for each component in the system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The following configuration is set up as a test network. There are multiple authentication and security features available in ChirpStack which I have not configured for this application, but you should be using for a real network.&lt;/p&gt;
&lt;h3 id=&#34;network-server&#34;&gt;Network Server&lt;/h3&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:200px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;postgresql&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;dsn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;postgres://chirpstack_ns:&amp;lt;POSTGRES_NS_PASSWORD&amp;gt;@postgresql/chirpstack_ns?sslmode=disable&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;redis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;redis://redis:6379&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;network_server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;net_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;000000&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;network_server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;band&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;US915&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;network_server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;network_settings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;enabled_uplink_channels&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;network_server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;gateway&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mqtt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tcp://mosquitto:7072&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;join_server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;http://chirpstack-application-server:8003&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;chirpstack-network-server.toml&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;The network server configuration is located in a file named by default &lt;code&gt;chirpstack-network-server.toml&lt;/code&gt;, and primarily configures the connectivity and credentials between the network server and the other components in the ChirpStack architecture. There are however a couple interesting LoRaWAN setting to call out.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;net_id&lt;/code&gt;: Specifies the LoRaWAN network ID for the network server. The network ID is used to uniquely identify LoRaWAN networks when routing between network servers when supporting network roaming. If you wanted to support connecting end devices through other device&amp;rsquo;s gateways, then it is necessary to acquire a unique network ID from the LoRa Alliance. However, network IDs &lt;code&gt;0x000000&lt;/code&gt; and &lt;code&gt;0x000001&lt;/code&gt; are reserved for experimental networks (I consider this experimental), and networks that are not using roaming (which I am not), so this can just be set to &lt;code&gt;0x000000&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;band&lt;/code&gt;: Defines which region from the LoRaWAN Regional Parameters specification is being used for this network. Since my network is located in the US, I have to use &amp;ldquo;US915&amp;rdquo; as my band.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enabled_uplink_channels&lt;/code&gt;: Declares the subset of channels (as defined by the regional parameters specification for the US915 band) are to be used on the network. For reasons we will get into later, only a subset of 9 channels will be enabled.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;application-server&#34;&gt;Application Server&lt;/h3&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:200px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;postgresql&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;dsn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;postgres://chirpstack_as:&amp;lt;POSTGRES_AS_PASSWORD&amp;gt;@postgresql/chirpstack_as?sslmode=disable&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;redis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;redis://redis:6379&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;application_server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;integration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mqtt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tcp://mosquitto:7072&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;application_server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;public_host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;chirpstack-application-server:8001&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;application_server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;external_api&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;bind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;0.0.0.0:7070&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;jwt_secret&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;JWT_SECRET&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;chirpstack-application-server.toml&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Since the application server is not directly interfacing with the LoRaWAN network, the configuration here is pretty simple. Really it just needs to configure the connections between it and the databases and MQTT server docker containers.&lt;/p&gt;
&lt;h3 id=&#34;gateway-bridge&#34;&gt;Gateway Bridge&lt;/h3&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:200px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Gateway backend configuration.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;basic_station&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# Basic Station backend.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;basic_station&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;bind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;localhost:3001&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c&#34;&gt;# Channel Configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;US915&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;frequency_min&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;902000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;frequency_max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;938000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;basic_station&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;concentrators&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;basic_station&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;concentrators&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;multi_sf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;frequencies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;mi&#34;&gt;902300000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	    &lt;span class=&#34;mi&#34;&gt;902500000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	    &lt;span class=&#34;mi&#34;&gt;902700000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	    &lt;span class=&#34;mi&#34;&gt;902900000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	    &lt;span class=&#34;mi&#34;&gt;903100000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	    &lt;span class=&#34;mi&#34;&gt;903300000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	    &lt;span class=&#34;mi&#34;&gt;903500000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	    &lt;span class=&#34;mi&#34;&gt;903700000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;basic_station&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;concentrators&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lora_std&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;frequency&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;903000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;bandwidth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;500000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;spreading_factor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Network server configuration.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;integration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mqtt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;auth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;generic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;servers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;tcp://&amp;lt;network_server_host&amp;gt;:7072&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;chirpstack-gateway-bridge.toml&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;In addition to setting up hosts and ports for the connections to the network server and packet forwarding application, this configuration is where we define the receive channels that will be used by the gateway. To understand how these settings should be configured we need to understand two things:&lt;/p&gt;
&lt;p&gt;First, we need to know how the regional parameters specification defines the LoRaWAN channels for our region. In my case the &lt;a href=&#34;https://content.cdntwrk.com/files/aT0xNDI4MzI2JnY9MSZpc3N1ZU5hbWU9cnAyLTEtMC0zLWxvcmF3YW4tcmVnaW9uYWwtcGFyYW1ldGVycyZjbWQ9ZCZzaWc9ZDI4NDMxYzU2NTVjZjZjZDRiYzJiM2ExZGEwMTAzMmM%253D#page=30&#34;&gt;US915 LoRaWAN Regional Parameters&lt;/a&gt; specification defines 72 uplink channels. The first 64 channels are defined as 125khz wide channels spaced every 200khz starting 902.3Mhz. The remaining 8 channels are 500khz wide spaced every 1.6Mhz starting at 902.3Mhz&lt;/p&gt;
&lt;p&gt;The second thing to understand what is supported by the SX1308 radio baseband chip. The SX1308 has 10 independent receive demodulation data paths that operate in parallel. Channels 0-7 are fixed 125khz LoRa demodulators with independently controlled frequencies, which can demodulate all LoRa spreading factors simultaneously. Channel 8 has a configurable LoRa demodulator that supports 125khz, 250khz, and 500khz channel bandwidth and a configurable frequency and spreading factor, but can only demodulate a single data rate at a time. Finally channel 9 is a (G)FSK demodulator that is used to support the LR-FHSS channels, which use frequency hopping spread spectrum modulation instead of the LoRa chirp spread spectrum modulation used by the other channels.&lt;/p&gt;
&lt;p&gt;Given this, these are the important settings from the gateway configuration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;region&lt;/code&gt;, &lt;code&gt;frequency_min&lt;/code&gt;, &lt;code&gt;frequency_max&lt;/code&gt;: These simply define what LoRa region that the gateway is operating in, which for me is the US915 band, which operates between 902Mhz and 928Mhz.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;backend.basic_station.concentrators.multi_sf&lt;/code&gt;: This specifies the channel frequencies for the first eight channels of the SX1308. Note that since the regional specification defies more 125Mhz channels than the SX1308 has, we have to pick a subset of the defined channels. I am simply using the first 8 128Khz LoRaWAN channels.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;backend.basic_station.concentrators.lora_std&lt;/code&gt;: The &lt;code&gt;frequency&lt;/code&gt;, &lt;code&gt;bandwidth&lt;/code&gt;, and &lt;code&gt;spreading_factor&lt;/code&gt; values in this group configure demodulation channel 8 in the SX1308. Since we already have 8 125Khz channels configured, I am using this channel for one of the 500khz channels, specifically LoRaWAN channel 64.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;gateway-packet-forwarder&#34;&gt;Gateway Packet Forwarder&lt;/h3&gt;
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:200px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;SX1301_conf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;                  &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Actual&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;channel&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;plan&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;controlled&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;by&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;(ChirpStack&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Gateway&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Bridge)&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;device&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/dev/spidev0.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;   &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Default&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;SPI&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;device&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;lorawan_public&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;      &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Use&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;sync&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;header,&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;must&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;be&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;coordinated&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;all&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;devices&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;ahead&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;of&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;clksrc&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;                  &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;radio_1&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;provides&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;clock&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;to&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;concentrator&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;radio_0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;SX1257&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;rssi_offset&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;-166.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;tx_enable&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;        &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;First&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;SX1257&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;device&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;used&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;transmitter&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;antenna_gain&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;radio_1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;SX1257&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;rssi_offset&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;-166.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;tx_enable&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;        &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Second&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;SX&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1257&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;connected&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;to&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;transmit&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;pps&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;                  &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;My&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;GPS&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;doesn&amp;#39;t&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;have&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;pps&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;so&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;don&amp;#39;t&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;expect&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;station_conf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;log_file&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;stderr&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;DEBUG&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;XDEBUG,DEBUG,VERBOSE,INFO,NOTICE,WARNING,ERROR,CRITICAL&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;log_size&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;10000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;log_rotate&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;CUPS_RESYNC_INTV&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;gps&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/dev/serial0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;        &lt;span class=&#34;err&#34;&gt;/*&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;GPS&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;Connected&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;to&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;serial0&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;device&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;pps&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;gps&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;   
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;station.conf&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Finally the configuration for the Basic Station packet forwarder application just needs to be configured based on the hardware setup of our gateway. The important settings here are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lorawan_public&lt;/code&gt;: Setting this option to &lt;code&gt;false&lt;/code&gt; which changes the LoRa sync header byte from the specification&amp;rsquo;s &lt;code&gt;0x34&lt;/code&gt; to &lt;code&gt;0x12&lt;/code&gt;. Since the sync header is used to identify the start of a packet, changing the sync header prevents packets on our private network from interfering with any public LoRaWAN network.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clksrc&lt;/code&gt;: This indicates which of the two SX1257 radio front-ends generates the 32Mhz radio clock for the SX1308. On the Pygate module this is the second SX1257.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;radio0&lt;/code&gt;/&lt;code&gt;radio1&lt;/code&gt;: These groups configure the two SX1257 front-ends connected to the SX1308. The main difference between them is that on the Pygate module, the &amp;ldquo;radio0&amp;rdquo; transmit path is hooked up, and it is not for &amp;ldquo;radio1&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pps&lt;/code&gt;: Since my GPS module does not have a PPS output that I can connect to that SX1308 chip this gets set to false.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;setting-up-an-organization-and-application&#34;&gt;Setting Up an Organization and Application&lt;/h2&gt;
&lt;p&gt;Even once all these configurations are set up, I found I wasn&amp;rsquo;t done. We need to configure the ChirpStack application server to set up the logical network and applications. This is done through the application server&amp;rsquo;s web interface.&lt;/p&gt;
&lt;p&gt;Once logged into the ChirpStack application sever, we first need to setup the connection to the network server. In &amp;ldquo;Network-servers&amp;rdquo;, we add a new network server with a logical name for the network, and the host:port for network server. The host and port should be &amp;ldquo;chirpstack-network-server:8000&amp;rdquo;. This is also where we could set up TLS authentication and encryption between network and application servers.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_connect_network_server.png&#34; alt=&#34;Adding network server connection to the application server.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_connect_network_server.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Adding network server connection to the application server.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Next we must create an &amp;ldquo;organization&amp;rdquo; for our application network to run in. ChirpStack appears to be built from a network administrator perspective, and so provides &amp;ldquo;organizations&amp;rdquo; as a way to manage groups of users and applications. For a private LoRaWAN network this isn&amp;rsquo;t particularly useful, but we still need to set one up. Under &amp;ldquo;Organizations&amp;rdquo; we create a new organization a name, display name and permissions. For the permissions I selected &amp;ldquo;Organization can have gateways&amp;rdquo;.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_create_organization.png&#34; alt=&#34;Creating an organization.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_create_organization.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Creating an organization.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Now that we have an organization, we need to configure the service and device profiles. Each gateway and application is associated with a service profile that defines how the application/gateway can consume network resources. Each device is associated with a device profile which defines network and protocol parameters that are applied to a set of devices.&lt;/p&gt;
&lt;p&gt;First we create a new network service profile under &amp;ldquo;Service-profiles&amp;rdquo;. The profile has a name, an associated network server (we only have one option here), and other network settings. The ChirpStack defaults have the maximum allowed data rate set to DR0, which is the slowest LoRaWAN data rate. For our gateway configuration, the highest supported data rate is DR4, so setting the maximum allowed data-rate setting to 4 allows us to get the most per-device bandwidth out of the network.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_create_service_profile.png&#34; alt=&#34;Creating a service profile.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_create_service_profile.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Creating a service profile.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Next we create a device profile by under the &amp;ldquo;Device-profiles&amp;rdquo; sections. As with the service profile we need to give it a name and an associated network. Additionally we need to set the LoRaWAN protocol and regional parameters specification versions that will be used by the device. I&amp;rsquo;m using protocol spec version 1.0.4 because it is what was supported in the end-device firmware network stack, and regional parameters version RP002-1.0.3, since that is the latest version. Finally we need to configure the automatic data rate algorithm to &amp;ldquo;LoRa Only&amp;rdquo;, Since our gateway isn&amp;rsquo;t configured for LR-FHSS. On the &amp;ldquo;Join (OTAA/APB)&amp;rdquo; tab I also set &amp;ldquo;Device supports OTAA&amp;rdquo; to enable over the air activation protocol which allows the end-device and network server to negotiate network parameters without needing them to be configured out of band.&lt;/p&gt;
&lt;p&gt;To simplify logging data into my InfluxDB backend, I chose to enable the &amp;ldquo;Cayenne LPP&amp;rdquo; codec under the &amp;ldquo;Codec&amp;rdquo; tab of the device-profile settings. When enabled, the ChirpStack application server will assume uplink packets from devices with this profile are encoded with the &lt;a href=&#34;https://developers.mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload&#34;&gt;Cayenne LPP&lt;/a&gt; format, and automatically decoded them. This makes it simple encode basic binary values on the end-device and have them logged into the InfluxDB database in a meaningful way.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_create_device_profile.png&#34; alt=&#34;Creating a device profile.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_create_device_profile.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Creating a device profile.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;With the profiles created we can now add a gateway to the organization. To do this, under &amp;ldquo;Gateways&amp;rdquo; we create a new gateway with a name and description, gateway ID, network server, and finally a service profile. The gateway ID is defined by the Basic Station packet forwarder application and by default is set to the MAC address of the primary network interface on the gateway. The network server and service profiles should be configured to the ones we have just created.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_add_gateway.png&#34; alt=&#34;Add gateway to organization.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_add_gateway.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Add gateway to organization.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Next we will finally set up the application that runs on top of our LoRaWAN network. To do this we just create a new application with a name and the service profile that we created previously.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_add_application.png&#34; alt=&#34;Create application.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_add_application.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Create application.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Once we have an application we need to tell ChirpStack what to do with packets once they are received, by choosing one or more &amp;ldquo;Integrations&amp;rdquo;. My goal for this initial setup was simply to log data into an InfluxDB time series database, conveniently ChirpStack has an integration with InfluxDB which stores data values from each uplink packet as decoded using the Cayenne LPP protocol. To enable this, under &amp;ldquo;Applications&amp;rdquo;-&amp;gt;&amp;ldquo;Integrations&amp;rdquo; we find the &amp;ldquo;InfluxDB&amp;rdquo; integration, add it to the application, then in the integration settings we will select InfluxDB 2.x for the protocol, &lt;code&gt;http://influxdb:8086/api/v2/write&lt;/code&gt; for the API InfluxDB endpoint (this is the docker internal hostname and port), and specify which bucket in the database it should put received packets into.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_configure_influxdb_integration.png&#34; alt=&#34;Configuring influxdb integration.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_configure_influxdb_integration.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Configuring influxdb integration.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Now we can finally add a our end-device to the application under the &amp;ldquo;Devices&amp;rdquo; tab, by selecting create and entering a device name, description, device EUI, and the previously created device profile. The Device EUI is a globally unique IEEE EUI64 identifier for the end-device, the CMWX1ZZABZ transceiver module comes pre-programmed with a Device EUI which will be used by firmware when sending join requests.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_add_device.png&#34; alt=&#34;Addng end-device to application.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_add_device.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Addng end-device to application.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Once the device has been created ChirpStack brings us to a page to enter or create an &amp;ldquo;Application Key&amp;rdquo;. This is a shared 128-bit key used by the end-device and network to derive encryption keys for all data sent across the network. It should be set to something random, and communicated to the end-device out of band.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_set_device_key.png&#34; alt=&#34;Add end-device to application.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/chirpstack_set_device_key.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Add end-device to application.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;And that&amp;rsquo;s it!&lt;/strong&gt; Our network and applications are set up and ready to go!&lt;/p&gt;
&lt;h2 id=&#34;sending-a-packet&#34;&gt;Sending a Packet&lt;/h2&gt;
&lt;p&gt;As previously mentioned, to test out the network I am running an example application which turns the CMWX1ZZABZ module into an AT style modem. I&amp;rsquo;m connecting to the modem firmware over the USB-Serial interface provided by the ST-Discovery development board I&amp;rsquo;m using.&lt;/p&gt;
&lt;p&gt;When the module comes out of reset we get the following output:
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;APP_VERSION:        V1.1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;MW_LORAWAN_VERSION: V2.3.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;MW_RADIO_VERSION:   V1.1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### OTAA ######
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### AppKey:      2B:7E:15:16:28:AE:D2:A6:AB:F7:15:88:09:CF:4F:3C
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### NwkKey:      2B:7E:15:16:28:AE:D2:A6:AB:F7:15:88:09:CF:4F:3C
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### ABP  ######
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### AppSKey:     2B:7E:15:16:28:AE:D2:A6:AB:F7:15:88:09:CF:4F:3C
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### NwkSKey:     2B:7E:15:16:28:AE:D2:A6:AB:F7:15:88:09:CF:4F:3C
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### DevEui:  XX:XX:XX:XX:XX:XX:XX:XX
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### AppEui:  01:01:01:01:01:01:01:01
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;###### DevAddr: XX:XX:XX:XX
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ATtention command interface
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AT? to list all available functions
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;From here we can send the commands to have our end-device join the network. First we need to set the &lt;code&gt;NwkKey&lt;/code&gt; to the &amp;ldquo;Application Key&amp;rdquo; we configured in the ChirpStack UI previously, then send the join command:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AT+NWKKEY=XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AT+JOIN=1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;When everything works correctly I get an output like the following, indicating that we have joined the network.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OK
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;299s464:TX on freq 903100000 Hz at DR 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OK
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;299s895:MAC txDone
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;304s884:RX_1 on freq 925700000 Hz at DR 10
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;305s018:MAC rxDone
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+EVT:JOINED
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+EVT:RX_1, DR 0, RSSI -80, SNR 8
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;To send a packet we use the &lt;code&gt;AT+SEND&lt;/code&gt; command which takes three arguments: The LoRaWAN port, which can be anything other than 0 (port zero indicates network management packet), a 1 or 0 indicating if we want the network to confirm receipt of the packet, and finally the data to send as a hex string. Since I have configured ChirpStack to decode packets using the Cayenne LPP protocol, the message I&amp;rsquo;m sending is formatted using Cayenne LPP. The following command will send a packet with a value of 4.00 for the &amp;ldquo;Analog Input 3&amp;rdquo; value on port 100.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AT+SEND=100:1:03020190
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;In response the firmware gives us this output indicating that it sent our message and it was confirmed by the network:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;685s981:TX on freq 902500000 Hz at DR 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OK
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;686s333:MAC txDone
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;687s323:RX_1 on freq 923900000 Hz at DR 10
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;687s434:MAC rxDone
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+EVT:SEND_CONFIRMED
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+EVT:RX_1, DR 10, RSSI -75, SNR 8
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;After sending a few of these messages, looking in the InfluxDB bucket I now have the data values from each packet I sent logged into the database, along with a little bit of metadata about it.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/influxdb_example_data.png&#34; alt=&#34;Data from packets logged into InfluxDB database.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/getting-started-with-a-private-lorawan-network/influxdb_example_data.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Data from packets logged into InfluxDB database.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Success!&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;next-steps&#34;&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;Now that I have a working LoRaWAN network, there are several areas that I will be investigating in the future.&lt;/p&gt;
&lt;p&gt;To start with I have just been using the built in InfluxDB integration and Cayenne LPP packet format built into ChirpStack to log basic data. Of course the LoRaWAN network supports sending any data with any protocol, so now that I can reliably send data there are a lot of options of both custom and built in protocols that can be layered on top of LoRaWAN and ChirpStack.&lt;/p&gt;
&lt;p&gt;Additionally, I have just been using the CMWX1ZZABZ as a dumb modem to manually transmit packets/data over the network. For a real application of my LoRaWAN network I need to develop firmware for the CMWX1ZZABZ to actual interface with sensors and send the data to the network.&lt;/p&gt;
&lt;p&gt;Finally, as previously mentioned I have not configured all the authentication and authorization features in the ChirpStack architecture. I need to spend some more time investigating and learning how to configure these to better secure my network.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Technically in some regions such as US915 it is valid to only support a single channel, in which case a single transceiver could act as a gateway. A proof of concept implementation for this can be found in this &amp;ldquo;&lt;a href=&#34;https://github.com/sealj553/lorawan-gateway&#34;&gt;Single Channel LoRaWAN Gateway&lt;/a&gt;&amp;rdquo; project on Github. However in other regions such as EU868 region, for example, a &lt;a href=&#34;https://content.cdntwrk.com/files/aT0xNDI4MzI2JnY9MSZpc3N1ZU5hbWU9cnAyLTEtMC0zLWxvcmF3YW4tcmVnaW9uYWwtcGFyYW1ldGVycyZjbWQ9ZCZzaWc9ZDI4NDMxYzU2NTVjZjZjZDRiYzJiM2ExZGEwMTAzMmM%253D#page=25&#34;&gt;gateway is &lt;em&gt;required&lt;/em&gt; to support&lt;/a&gt; at a minimum of three specific channels.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Theoretically ChirpStack also includes its own equivalent to the Basics Station application, in the form of &lt;a href=&#34;https://www.chirpstack.io/concentratord/&#34;&gt;Concentratord&lt;/a&gt;. However on the integration between the Concentratord and the Gateway Bridge is &lt;a href=&#34;https://www.chirpstack.io/gateway-bridge/backends/concentratord/&#34;&gt;currently listed as &amp;ldquo;experimental&amp;rdquo;&lt;/a&gt;, so I have not actually tried this out.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Don&#39;t Wait For Me</title>
      <link>https://www.prbs23.com/blog/posts/systemverilog-wait-fork/</link>
      <pubDate>Thu, 17 Feb 2022 22:22:26 -0700</pubDate>
      <author>prbs23@prbs23.com (prbs23)</author>
      <guid>https://www.prbs23.com/blog/posts/systemverilog-wait-fork/</guid>
      <description>&lt;p&gt;From the perspective your more traditional software programming languages, multi-tasking in SystemVerilog is weird. On one hand, the language includes a special syntax (&lt;code&gt;fork&lt;/code&gt; and &lt;code&gt;join&lt;/code&gt;) that make it really easy to write code that &amp;ldquo;runs&amp;rdquo; in parallel. On the other hand, SystemVerilog &amp;ldquo;processes&amp;rdquo; aren&amp;rsquo;t really processes, or even threads, in the traditional sense. Even if you &amp;ldquo;get&amp;rdquo; the SystemVerilog model of cooperative &lt;a href=&#34;https://en.wikipedia.org/wiki/Cooperative_multitasking&#34;&gt;cooperative multi-tasking&lt;/a&gt;, there are a number of pitfalls you can run into. One such gotcha that I recently ran into was caused by what appeared to be an innocuous &lt;code&gt;wait fork&lt;/code&gt; statement.&lt;/p&gt;
&lt;h2 id=&#34;an-unexpected-infinite-wait&#34;&gt;An Unexpected Infinite Wait&lt;/h2&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;initial&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;bit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;31&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;forever&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;c1&#34;&gt;// Monitor some signals for correct behavior.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;join_none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// Do some other stuff. Then...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;do_read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;32&amp;#39;hdead&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_beef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Done&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// &amp;lt;--- This print never happens
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;The initial problem code structure&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;The issue was in an old (I mean &lt;em&gt;really&lt;/em&gt; old, pre-UVM) test case that hadn&amp;rsquo;t been run in a long time, and appeared to hang in a task that issued an bus read transaction. From inspecting the simulation waves and logs, I could tell that the bus transaction was completing without issue, but the task was still never returning. The basic structure of this the test code is shown above, a process is forked off, then the &lt;code&gt;do_read()&lt;/code&gt; task is called. This obviously calls the content of &lt;code&gt;do_read()&lt;/code&gt; into question, after digging through a few translation layers (did I mention this code was old), I found the code that actually did the bus transaction, which I&amp;rsquo;ll call &lt;code&gt;do_read_internal()&lt;/code&gt;, looked something like this:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;task&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;do_read_internal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;bit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;31&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;byte_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;output&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;bit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;31&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flags_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NONE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;c1&#34;&gt;// Wiggle bus wires and pass time as required do drive transaction
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;join_none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BLOCKING_TRANSACTION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;wait&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endtask&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;do_read_internal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;do_read_internal() task called by test case through do_read() task&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Now, if you know what you&amp;rsquo;re looking for you may already see the issue. But before I give it all away, suffice it to say, the problem here is the use of the &lt;code&gt;wait fork&lt;/code&gt; statement. So let&amp;rsquo;s talk about what that last &lt;code&gt;wait fork&lt;/code&gt; statement is actually doing.&lt;/p&gt;
&lt;h2 id=&#34;a-not-so-brief-introduction-to-wait-fork&#34;&gt;A Not So Brief Introduction to &lt;code&gt;wait fork&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Lets start in the obvious place, what does the SystsemVerilog LRM (Language Reference Manual) say about &lt;code&gt;wait fork&lt;/code&gt;? This is summary of the statement that we can find in section &lt;strong&gt;9.6.1 Wait fork statement&lt;/strong&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;wait fork&lt;/code&gt; statement blocks process execution flow until all immediate child subprocesses (processes created by the current process, excluding their descendants) have completed their execution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This seems fairly simple, it is a statement that makes a process wait until all sub-processes that were started from the current process have finished. The problem with this construct is that it&amp;rsquo;s not always obvious which processes &amp;ldquo;all immediate child subprocesses&amp;rdquo; refer to. Lets start with a simple example:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;initial&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;main_process&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t: Hello from subprocess_1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;50&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t: Hello from subprocess_2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;join_none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wait&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t: Done&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;main_process&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;Trivial wait fork example&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;In this case the &lt;code&gt;fork&lt;/code&gt;/&lt;code&gt;join_none&lt;/code&gt; block is going to spawn two subprocesses which wait for a period of time and then print a message. Because a &lt;code&gt;join_none&lt;/code&gt; was used, execution of the &lt;code&gt;main_process&lt;/code&gt; block will not wait for these subprocesses to finish before continuing. Then the &lt;code&gt;wait fork&lt;/code&gt; statement will wait for both the two subprocesses running the &lt;code&gt;subprocess_1&lt;/code&gt; and &lt;code&gt;subprocess_2&lt;/code&gt; blocks to finish, and then print &amp;ldquo;Done&amp;rdquo;. Easy enough. Now, what if we make it a bit more complicated and move the &lt;code&gt;wait fork&lt;/code&gt; into another task.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;initial&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;main_process&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t: Hello from subprocess_1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;50&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t: Hello from subprocess_2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;join_none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;task_1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t: Done&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;main_process&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;task&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;task_1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;25&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t: Hello from subprocess_3&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;75&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t: Hello from subprocess_4&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;join_none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wait&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endtask&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;task_1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;What happens when we move the wait fork into a task?&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;In this case we start subprocesses &lt;code&gt;subprocess_1&lt;/code&gt; and &lt;code&gt;subprocess_2&lt;/code&gt;, and don&amp;rsquo;t wait for them to finish, just like the previous example. However we now call a task &lt;code&gt;task_1()&lt;/code&gt; which also spawns two new processes &lt;code&gt;subprocess_3&lt;/code&gt; and &lt;code&gt;subprocess_4&lt;/code&gt;, then executes a &lt;code&gt;wait fork&lt;/code&gt; statement, before returning and printing &amp;ldquo;Done&amp;rdquo;. If &lt;code&gt;wait fork&lt;/code&gt; followed normal scoping rules like one might naively expect it to wait for subprocesses 3 and 4 only, but if we look at the output from this example, we see this is not the case.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;xcelium&amp;gt; run
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                  25: Hello from subprocess_3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                  50: Hello from subprocess_2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                  75: Hello from subprocess_4
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 100: Hello from subprocess_1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 100: Done
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;The reason that the &lt;code&gt;wait fork&lt;/code&gt; waits for all four of the spawned subprocess to finish, is that simply that they are all subprocesses of the process from which &lt;code&gt;wait fork&lt;/code&gt; was called, the one started from the &lt;code&gt;main_process&lt;/code&gt; initial block. This is because calling a task or another SystemVerilog scope doesn&amp;rsquo;t start a new process, in fact the only way that a new process is spawned (from an existing one) is through a fork statement. Because the code running in &lt;code&gt;task_1&lt;/code&gt; is still part of the same process, &amp;ldquo;all immediate subprocesses&amp;rdquo; includes &lt;code&gt;subprocess_1&lt;/code&gt; and &lt;code&gt;subprocess_2&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;initial&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;main_process&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;task_1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;task_2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t: Done&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;main_process&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;task&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;task_1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;infinite_subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;forever&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t Hello again from infinite_subprocess&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;infinite_subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;infinite_sub_subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;k&#34;&gt;forever&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;75&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t Hello again from infinite_sub_subprocess&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;infinite_sub_subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;join_none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;join_none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endtask&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;task_1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;task&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;task_2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;25&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t: Hello from subprocess_2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;75&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;$display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%t: Hello from subprocess_3&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;$time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;join_none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wait&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endtask&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;task_2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;A wait fork example that will hang forever&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;This final example demonstrates, in a more obvious way, the bug in my problem code above. In this case, the task &lt;code&gt;task_1&lt;/code&gt; is first called from &lt;code&gt;main_process&lt;/code&gt; which starts two subprocesses &lt;code&gt;infinite_subprocess&lt;/code&gt; and &lt;code&gt;subprocess_1&lt;/code&gt;, then immediately returns because the block is terminated with &lt;code&gt;join_none&lt;/code&gt;. Then &lt;code&gt;task_2&lt;/code&gt; is executed, which forks two more processes (&lt;code&gt;subprocess_2&lt;/code&gt; and &lt;code&gt;subprocess_3&lt;/code&gt;) and finally executes a &lt;code&gt;wait fork&lt;/code&gt; statement. In this case the &lt;code&gt;wait fork&lt;/code&gt; statement is &lt;em&gt;never&lt;/em&gt; actually going to finish, because it has to wait for the conspicuously named &lt;code&gt;infinite_subprocess&lt;/code&gt; (which never ends), even though it was started from a different task.&lt;/p&gt;
&lt;p&gt;Finally, note that the &lt;code&gt;wait fork&lt;/code&gt; in &lt;code&gt;task_2&lt;/code&gt; does &lt;em&gt;not&lt;/em&gt; have to wait for the process &lt;code&gt;infinite_sub_subprocess&lt;/code&gt; because it is not a &lt;em&gt;direct&lt;/em&gt; subprocess of the &lt;code&gt;main_process&lt;/code&gt;. The &lt;code&gt;infinite_sub_subprocess&lt;/code&gt; process is forked from the &lt;code&gt;subprocess_1&lt;/code&gt; process, but because it uses &lt;code&gt;join_none&lt;/code&gt;, the process does not wait for &lt;code&gt;infinite_sub_subprocess&lt;/code&gt;, and leaves the sub-subprocess running, unattached, from any parent process.&lt;/p&gt;
&lt;h2 id=&#34;what-went-wrong&#34;&gt;What Went Wrong?&lt;/h2&gt;
&lt;p&gt;Going back to my hanging test case. Now that we&amp;rsquo;ve looked at some examples of how &lt;code&gt;wait fork&lt;/code&gt; works, it&amp;rsquo;s pretty obvious if you know where to look. The main test code starts an infinite process, and then calls a task which start it&amp;rsquo;s own process and then uses &lt;code&gt;wait fork&lt;/code&gt; to wait for it&amp;rsquo;s process, but unintentionally ends up waiting on an infinite loop to end. The &lt;code&gt;do_read_internal&lt;/code&gt; task has no visibility to know that an infinite process has even been spawned from it&amp;rsquo;s process, but &lt;code&gt;wait fork&lt;/code&gt; will wait for it anyway.&lt;/p&gt;
&lt;p&gt;The thing I find devious about this bug is that it demonstrates how starting of a subprocess in one place can cause a completely unrelated piece of code to hang, potentially forever. In fact it is entirely possible that the code that starts the infinite loop subprocess is in one library and the &lt;code&gt;wait fork&lt;/code&gt; statement is in another library. All that is required to cause havoc is that they are called from the same process. When I realized this it made me reconsider the safety of using &lt;code&gt;wait fork&lt;/code&gt;, and at this point it seems like it&amp;rsquo;s only really safe to use in very specific applications.&lt;/p&gt;
&lt;h2 id=&#34;remediation&#34;&gt;Remediation&lt;/h2&gt;
&lt;p&gt;Unfortunately, there is not a version of &lt;code&gt;wait fork&lt;/code&gt; with a more limited scope (such as waiting for subprocess of the current task or scope). This leaves us with three, not great, alternatives to implement the functionality that our &lt;code&gt;do_read_internal&lt;/code&gt; was going for: starting a non-block process, and then conditionally waiting for it to finish.&lt;/p&gt;
&lt;p&gt;The first, and in my opinion best, option in this case is to simply declare an event that gets emitted by the started subprocess on completion. Then to wait for the subprocess to end, the main process can just wait on the event, instead of using &lt;code&gt;wait fork&lt;/code&gt;. Aside from needing to declare a new event variable, this works fairly well if there is only one subprocess to wait on. This implementation would look something like the following:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;event&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;done_event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// Wiggle bus wires and pass time as required do drive transaction
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;done_event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;join_none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BLOCKING_TRANSACTION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;done_event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Wait or forked subprocess to report it has ended.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;do_read_internal() task which uses a done event for waiting.&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Another option is to use the SystemVerilog &amp;ldquo;fine-grain process control&amp;rdquo; features (see section 9.7 of the LRM). Essentially this feature provides an API to get a reference to a &amp;ldquo;process&amp;rdquo; object associated with the current process, which can then be used to wait on or otherwise manage the process. In this case, we can have the subprocess store it&amp;rsquo;s &amp;ldquo;process&amp;rdquo; object in a variable, and then use the &lt;code&gt;await()&lt;/code&gt; task of the process object to wait for the subprocess to end. Something like the following:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;process&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subprocess_handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;subprocess_handle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Get process handle object for subprocess.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// Wiggle bus wires and pass time as required do drive transaction
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;join_none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BLOCKING_TRANSACTION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;                        &lt;span class=&#34;c1&#34;&gt;// Ensure that the subprocess has a chance to execute, so that the subprocess_handle will not be null.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;subprocess_handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Wait for the subprocess to finish
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;do_read_internal() task using fine-grain process control to wait for subprocess&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;The two options above work fine when you are starting a single subprocess, and then want to conditionally wait for it to finish, but what if you are starting an indeterminate number of processes and want to wait for them to all finish? In this case the behavior of &lt;code&gt;wait fork&lt;/code&gt; to wait for all subprocesses is a really convenient feature. However as we have seen, when calling &lt;code&gt;wait fork&lt;/code&gt; you need to ensure you know which subprocesses it will actually wait for. The only way to really do this for sure, is to fork a new process within which we carefully control which processes are started. This does have the overhead of creating an extra another process, but this could be reasonable trade-off in some cases. An example of this implementation is shown below.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Start a new process for the content of the task
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;c1&#34;&gt;// Wiggle bus wires and pass time as required do drive transaction
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;join_none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BLOCKING_TRANSACTION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;wait&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fork&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// This call is now safe, because we are gaurenteed there is only one subprocess.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;join&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Block until this wrapper process finishes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;do_read_internal() task using an extra subprocess to control the scope of wait fork&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;h2 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;In the end, I chose to take the first option and replace the &lt;code&gt;wait fork&lt;/code&gt; with a wait on a &lt;code&gt;done&lt;/code&gt; event, and everything works fine. However, I am also now taking a much more critical look at all other uses of &lt;code&gt;wait fork&lt;/code&gt; in our repository, as I now have a better appreciation for some of the pitfalls of this statement.&lt;/p&gt;
&lt;p&gt;There are certainly places where it is mostly safe. Use of &lt;code&gt;wait fork&lt;/code&gt; directly within a fork/join block is generally safe because it&amp;rsquo;s harder to get an unexpected subprocess. Likewise use within a UVM sequence &lt;code&gt;body()&lt;/code&gt; task also appears to be fairly safe, because sequence bodies are always started in a new process. However, even in these fairly well controlled uses it is important to remember that any task or function that is called &lt;em&gt;could&lt;/em&gt; start an unexpected infinite subprocess, and then we&amp;rsquo;re right back to the same problem.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not saying don&amp;rsquo;t use &lt;code&gt;wait fork&lt;/code&gt;, but I do think it needs to be used with caution. It is easy to forget, or not be aware of, what all it will wait for.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Considering SystemVerilog Tagged Unions</title>
      <link>https://www.prbs23.com/blog/posts/considering-systemverilog-tagged-unions/</link>
      <pubDate>Sat, 22 Jan 2022 11:42:00 -0700</pubDate>
      <author>prbs23@prbs23.com (prbs23)</author>
      <guid>https://www.prbs23.com/blog/posts/considering-systemverilog-tagged-unions/</guid>
      <description>&lt;p&gt;I have recently been thinking a lot about tagged unions in SystemVerilog, since I discovered them a few months ago. In this post I present some of the ideal use cases for tagged unions, and why I think no one actually uses them.&lt;/p&gt;
&lt;p&gt;Despite being &lt;a href=&#34;https://accellera.org/images/eda/sv-bc/att-1352/01-2003-12-09_TaggedUnions_Proposal.pdf&#34;&gt;initially proposed in 2003&lt;/a&gt;, and officially part of the language since the &lt;a href=&#34;https://ieeexplore.ieee.org/document/1560791&#34;&gt;SystemVerilog 1800-2005 standard&lt;/a&gt; was released, at this point tagged unions appear to be a forgotten language feature. I think this is a shame, because since I accidentally ran across the tagged union section of the LRM, I keep thinking of new applications where I would like to make use of them. Don&amp;rsquo;t get me wrong, there are plenty of reasons that you don&amp;rsquo;t see tagged unions used in the wild today (language ergonomics, tool support, etc.) but that doesn&amp;rsquo;t mean they couldn&amp;rsquo;t have been useful.&lt;/p&gt;
&lt;h2 id=&#34;quick-introduction-to-sum-types&#34;&gt;Quick Introduction to Sum Types&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Tagged_union&#34;&gt;Tagged unions&lt;/a&gt;, commonly also referred to as &amp;ldquo;sum types&amp;rdquo;, at least in SystemVerilog, I like to think of a combination of two more commonly used data types, unions, and enums. Like an enum, the primary value (tag) of a tagged union variable can be one of several values, as defined by the type declaration. However, unlike a normal enum, along with the &amp;ldquo;tag&amp;rdquo;, the tagged union variable can store another value of a type defined by the tag. Each tag value can define a different type of value held by the union when the variable has that tag value. The syntax of tagged unions allow the compiler to enforce that the type of value assigned to the tagged union matches the type as defined by the tag, and it is always possible to know the type of value stored in the variable by looking at the tag, unlike a regular union.&lt;/p&gt;
&lt;p&gt;By way of example, in the following code, we declare a tagged union type &lt;code&gt;int_or_float_t&lt;/code&gt;, when assigned the tag &lt;code&gt;INT_VAL&lt;/code&gt; it is guaranteed by the compiler to hold an integer value, and when assigned the tag &lt;code&gt;FLOAT_VAL&lt;/code&gt; always a float value.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;tagged&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;int&lt;/span&gt;   &lt;span class=&#34;n&#34;&gt;INT_VAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;float&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FLOAT_VAL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;int_or_float_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;Simple tagged union example.&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Unlike a normal union, if you assign a &lt;code&gt;int_or_float_t&lt;/code&gt; variable to &lt;code&gt;INT_VAL&lt;/code&gt; with an associate integer value, the language will not allow us to refer to the integer value as a float. It will either be a compile time or runtime error, depending on the syntax used.&lt;/p&gt;
&lt;h2 id=&#34;why-not-to-use-tagged-unions&#34;&gt;Why not to use tagged unions&lt;/h2&gt;
&lt;p&gt;Before getting into why I think tagged unions should be used more, why have they been essentially forgotten in SystemVerilog? I believe this mainly comes down to two issues: lack of support from major simulator vendors and language ergonomics.&lt;/p&gt;
&lt;h3 id=&#34;lacking-support&#34;&gt;Lacking Support&lt;/h3&gt;
&lt;p&gt;Of the three major commercial simulators I recently tested, one (Cadence Xcelium) didn&amp;rsquo;t support tagged unions at all, and the other two (Mentor Questa and Synopsys VCS) have varying degrees of partial support. In my opinion this is the biggest reason we don&amp;rsquo;t see tagged unions used, if a feature isn&amp;rsquo;t supported by all of the big three simulators then it can&amp;rsquo;t be used in any portable VIP or standard libraries. Even in the two simulators that did have support, sadly neither supported constraining or randomizing tagged union variables. Being able to randomize both the tag and values of a tagged union would provide some interesting capabilities as I will discuss later.&lt;/p&gt;
&lt;h3 id=&#34;ergonomics&#34;&gt;Ergonomics&lt;/h3&gt;
&lt;p&gt;The other problem with tagged unions is that the syntax for using them is in some ways&amp;hellip; clunky at best. The pattern matching &lt;code&gt;case&lt;/code&gt; and &lt;code&gt;if&lt;/code&gt; syntax, which is (pretty much) required to check the tag and access values of the tagged union are both fairly verbose with lots of additional &lt;code&gt;tagged&lt;/code&gt;, &lt;code&gt;matches&lt;/code&gt; kind of keywords. Even assignments to the union value (tag) require an additional &lt;code&gt;tagged&lt;/code&gt; keyword. Additionally, unlike other language which have an equivalent tagged union or sum type, SystemVerilog doesn&amp;rsquo;t provide a way to &amp;ldquo;parameterize&amp;rdquo; or &amp;ldquo;template&amp;rdquo; the types held by a tagged union.&lt;/p&gt;
&lt;p&gt;Between lack of support, and generally feeling like a feature that was added to the language without a huge amount of thought, I can&amp;rsquo;t really blame anyone for not using tagged unions. However I am, none the less, disappointed that I can&amp;rsquo;t use them because they are not supported by the tools I use every day. After having used sum types in other languages, I think they could bring a lot of value to verification code written in SystemVerilog.&lt;/p&gt;
&lt;h2 id=&#34;uses-of-tagged-unions&#34;&gt;Uses of Tagged Unions&lt;/h2&gt;
&lt;p&gt;Now if we ignore the legitimate issues with tagged unions discussed above, what could they be used for? Note that the proposals below are pretty much all currently theoretical. I haven&amp;rsquo;t tried to implement these, and some are even impossible today due to missing support in current tools (especially around randomization and constraints). Nevertheless, I think these ideas are worth more investigation, and would love to hear from anyone who has done anything similar in SV.&lt;/p&gt;
&lt;h3 id=&#34;error-handling-using-result-types&#34;&gt;Error Handling using &amp;ldquo;Result&amp;rdquo; Types&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s face it, error handling in most SystemVerilog code is bad. The language itself does not provide &amp;ldquo;exceptions&amp;rdquo; or any other architected error handling pattern, so most code just doesn&amp;rsquo;t handle errors. When an error &lt;em&gt;does&lt;/em&gt; happen, most code will either log an error message then give up, or it will log an error and carry on as if nothing happened. From a software engineering perspective, this first off just bad practice, but also can lead to unpredictable behavior following the first error in a simulation.&lt;/p&gt;
&lt;p&gt;We can do better though! Another language that doesn&amp;rsquo;t have exceptions, but does have sum types is &lt;a href=&#34;https://www.rust-lang.org/&#34;&gt;Rust&lt;/a&gt;. In Rust, the error handling problem is implemented using a &lt;code&gt;Result&lt;/code&gt; tagged union type (called &lt;code&gt;enum&lt;/code&gt; in rust). Any function that &lt;em&gt;could&lt;/em&gt; fail returns a &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt; value, if the function succeeds then an &lt;code&gt;Ok&lt;/code&gt; value is returned with a return value of type &lt;code&gt;T&lt;/code&gt;. If the function fails for some reason, then an &lt;code&gt;Err&lt;/code&gt; value is returned, along with an &lt;code&gt;E&lt;/code&gt; typed value representing the kind of error that occurred.&lt;/p&gt;
&lt;p&gt;The nice thing about this error handling technique is that it requires no additional features from the language (such as exceptions), but provides a consistent way for functions to return errors to their caller in a way &lt;strong&gt;that cannot be ignored&lt;/strong&gt;. The compiler doesn&amp;rsquo;t allow you to use the return value from the function without either handling errors, or at least explicitly ignoring them. In my experience, this pattern works well in Rust, and I think it could be a good pattern for error handling in SystemVerilog.&lt;/p&gt;
&lt;p&gt;Below I show a &amp;ldquo;Result&amp;rdquo; type implemented in SystemVerilog with tagged unions. Because SystemVerilog doesn&amp;rsquo;t support parametrizing tagged union types, we can&amp;rsquo;t create a generic &lt;code&gt;Result&amp;lt;T,E&amp;gt;&lt;/code&gt;type, but a simple macro could be used to automate the creation of result types. In this case, I am declaring the handling code for deserializing a byte stream into a packet where it is necessary to check packet length and a packet CRC, either of which could result in an error.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;val1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;val2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;packet_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// Define a packet type
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SIZE_ERR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CRC_ERR&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;error_e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;// Define a error type
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// The Result tagged union type
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;tagged&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;packet_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;error_e&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;packet_result_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Deserialize function, returning packet_result_t type
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;packet_result_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;deserialize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;byte&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;packet_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pkt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// Check for errors, returning an error enum value if there is one.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;tagged&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Err&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SIZE_ERR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;compute_crc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;taged&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Err&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CRC_ERR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// Extract the values
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;pkt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;val1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]}};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;pkt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;val2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]}};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// Return the packet value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;tagged&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Ok&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pkt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endfunction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;&amp;#34;Result&amp;#34; tagged union example.&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;The other half of this is how do you actually use the returned result type value. This is done using the &amp;ldquo;pattern matching conditional&amp;rdquo; syntax, as described in section 12.6 of the LRM. There are two forms of pattern matching conditionals, one for case statements, and one for if statements. This example shows using both to handle the &amp;ldquo;result&amp;rdquo; from the previously defined &lt;code&gt;deserialize()&lt;/code&gt; function.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;packet_result_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;deserialize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// If pattern matching syntax. In this case errors are being ignored.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;matches&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;tagged&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Ok&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pkt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// Use the packet value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// Inside this context, the `pkt` name can be used to refer to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// the packet_t typed &amp;#34;Ok&amp;#34; value.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Case matching syntax. This time we will actually do something with
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//  the errors.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;matches&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;tagged&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Ok&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;pkt:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// Use the packet. Once again `pkt` can be used to refer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// to the packet_t value.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;tagged&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;SIZE_ERR:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// Handle specifically the packet size error condition.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;tagged&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;e:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// Handle any other error value. Once again the `e` value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// can be used to determine the error type.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endcase&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;Example showing how the result of the deserialize() function can be used.&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;h3 id=&#34;transaction-packet-or-opcode-subtypes&#34;&gt;Transaction, Packet, or Opcode subtypes&lt;/h3&gt;
&lt;p&gt;In design verification a common thing to do is to declare a class that represents a packet, opcode, or some other kind of &amp;ldquo;transaction&amp;rdquo;. These transaction classes contain variables that hold data from fields of the packet, or operands of the opcode, but what do you do when the transaction can have a different set of data values, depending on the type of transaction. In my experience I have seen two approaches used, either the transaction class contains a superset of all possible fields for the transaction, or sub-classes of the primary transaction type are declared for each transaction format, containing only the valid fields. The problem with including the super-set of fields is the user has to know which fields are valid in a transaction at any given time, and there is nothing to stop you from using an invalid field by mistake. On the other hand, declaring sub-classes can be very verbose, and requires a lot of explicit casts to work with values of the transaction.&lt;/p&gt;
&lt;p&gt;However, tagged unions give us another option. As an example (inspired by one of the tagged union examples the SystemVerilog LRM), say we wanted a transaction that represents a CPU instruction where each instruction format has a different set of operands. We could define the instruction using a class that contains an anonymous tagged union as shown below:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Instruction&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uvm_sequence_item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// Main opcode definition
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;tagged&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// Binary arethmatic instruction format supports add, subtract, multiply, and divde
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// opcodes, with three operands, two source registers and one destination register
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ADD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SUB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MUL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DIV&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opcode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;bit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rdest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BIN_FMT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// Jump format instruction has a single destination literal operand with three
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// different opcodes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;JMP&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;JEQ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;JNE&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opcode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;bit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;JMP_FMT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// Additional metadata fields can still be added to the transaction class.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endclass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Instruction&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;Example of using tagged unions to express transacion with different field formats.&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;With this construction, the &lt;code&gt;Instruction&lt;/code&gt; transaction class can hold all the operands for all instruction formats, but the compiler will only allow us to reference the fields for the instruction format actually used by the current value of the &lt;code&gt;Instruction&lt;/code&gt; transaction. This sounds similar to the sub-class approach, however where with a sub-class implementation you would need to cast the &lt;code&gt;Instruction&lt;/code&gt; to a &lt;code&gt;BinFmtInstruction&lt;/code&gt; to access the &lt;code&gt;r1&lt;/code&gt; field (for example), in this implementation it is possible to refer to any field without casts as long as the format is correct. For example if we have already checked that &lt;code&gt;format&lt;/code&gt; is &lt;code&gt;BIN_FMT&lt;/code&gt; the expression &lt;code&gt;inst.BIN_FMT.rdest&lt;/code&gt; will reference the &lt;code&gt;rdest&lt;/code&gt; value of the instruction with no casts.&lt;/p&gt;
&lt;p&gt;Is this actually better than the alternatives though? I believe it is a significant improvement over defining a transaction with a super-set of fields (the most common approach), since it ensures correctness when referencing the transaction fields. I am not sure it&amp;rsquo;s that much better than defining class sub-types for each packet format, since both require that you explicitly check the transaction format before you can reference values. However, I think the tagged union implementation would make the code easier to read with fewer &lt;code&gt;$cast&lt;/code&gt; calls and variables of different subtypes hanging around.&lt;/p&gt;
&lt;h3 id=&#34;configuration&#34;&gt;Configuration&lt;/h3&gt;
&lt;p&gt;The final application where there could be real benefits from using tagged unions is in configuring sequences and components. Particularly where there is some mode setting where each mode has a different set of options that can be configured. Take the example of a sequence which generates transactions with a configurable traffic pattern. This might normally be implemented like the following:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PERIODIC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RAND&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BURST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;traffic_pattern_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;traffic_seq&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;uvm_sequence&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;traffic_pattern_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pattern&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;rand&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;periodic_interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;rand&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rand_min&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;rand&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rand_max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;rand&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;burst_interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;rand&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;burst_duration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endclass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;traffic_seq&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;Traditional implementation of a configurable traffic sequence.&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;In this implementation, the &lt;code&gt;periodic_interval&lt;/code&gt; variable is only valid when &lt;code&gt;pattern == PERIODIC&lt;/code&gt;. Likewise, the value of &lt;code&gt;rand_min&lt;/code&gt; and &lt;code&gt;rand_max&lt;/code&gt; are only used when &lt;code&gt;pattern == RAND&lt;/code&gt;. At best it is confusing that you can set &lt;code&gt;periodic_interval&lt;/code&gt; when pattern is set to &lt;code&gt;BURST&lt;/code&gt;. A better way you could implement the same concept with tagged unions would look like the following:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;traffic_seq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;tagged&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PERIODIC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;      &lt;span class=&#34;k&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RAND&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;duration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BURST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pattern&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endclass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;traffic_seq&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;Configuration traffic pattern sequence using tagged unions.&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;With the tagged union implementation the &lt;code&gt;min&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt; values only exist (from the compiler&amp;rsquo;s perspective) when &lt;code&gt;pattern == RAND&lt;/code&gt;. Additionally you cannot set pattern &lt;em&gt;without&lt;/em&gt; setting it&amp;rsquo;s parameters. As an to set the pattern to &lt;code&gt;BURST&lt;/code&gt; you would use an expression like:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;seq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pattern&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;tagged&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BURST&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#39;{&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;interval:&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;duration:&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  
&lt;/figure&gt;

&lt;p&gt;I like this tagged union approach because not only is the assignment expression fairly concise and explicit, but it eliminates a whole class of errors and confusions. The configuration parameters for given traffic pattern type can only be accessed within the context of that pattern. There is no way to accidentally set &lt;code&gt;periodic_internval&lt;/code&gt; instead of &lt;code&gt;burst_interval&lt;/code&gt;. This tagged union design pattern could be applied to any use case where there is a setting where each option has it&amp;rsquo;s own parameters. For example you might use this for a configuration that controls a random distribution modes (think normal, uniform, geometric, etc.) where each distribution has it&amp;rsquo;s own unique arguments.&lt;/p&gt;
&lt;h2 id=&#34;wrap-up&#34;&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;As I mentioned at the beginning, the potential uses for tagged unions in SystemVerilog has been something I couldn&amp;rsquo;t get out of my mind for a few months. Although I have not been able to actually do a lot of work with them (due to Cadence lacking any kind of support), I think there are several good applications for tagged unions which would improve the readability and correctness of verification code, through syntax and compile time checks. Unfortunately, while tagged unions appear to have a lot of advantages, lacking tool support and poor syntax ergonomics sadly make it pretty much a non-starter from a practical perspective.&lt;/p&gt;
&lt;p&gt;If anyone has actually used SystemVerilog tagged unions, I would love to know how you used them and how they worked out.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Building A Picture Frame From Mars</title>
      <link>https://www.prbs23.com/blog/posts/picture-frame-from-mars/</link>
      <pubDate>Sun, 14 Nov 2021 13:16:20 -0700</pubDate>
      <author>prbs23@prbs23.com (prbs23)</author>
      <guid>https://www.prbs23.com/blog/posts/picture-frame-from-mars/</guid>
      <description>&lt;p&gt;This whole project started when I happened across the &lt;a href=&#34;https://mars.nasa.gov/mars2020/multimedia/raw-images/&#34;&gt;NASA Perseverance Raw Image Library&lt;/a&gt; one day. Something about the constantly varying combination of images: some with scientific purposes, some for navigation, and some of the rover itself, along with the fact that they were &lt;em&gt;FROM MARS&lt;/em&gt; kept me coming back to the page to see what Curiosity had been up to lately. This eventually lead to the idea of having a digital picture frame which would cycle through the latest raw images from the Perseverance rover. So that&amp;rsquo;s what I set out to build.&lt;/p&gt;


&lt;div class=&#34;box &#34; style=&#34;max-width:75%&#34;&gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/picture-frame-from-mars/completed_photo_frame.jpg&#34; alt=&#34;Finished Perseverance Digital Picture Frame&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/picture-frame-from-mars/completed_photo_frame.jpg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Finished Perseverance Digital Picture Frame
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h2 id=&#34;the-hardware&#34;&gt;The Hardware&lt;/h2&gt;
&lt;p&gt;To start with, I would need a digital picture frame with internet access. There were two main directions I could have taken here; build a picture frame from scratch, or somehow modify a commercial frame. On one hand I could connect a Raspberry Pi to a large LCD panel and stick it in a picture frame, this would give me a very customizable platform to develop on, at the cost of some extra effort and being a less integrated solution. On the other hand, I could just buy an off the shelf Wi-Fi enabled digital picture frame and figure out how to write my own software/firmware for it. This would be easier from a hardware perspective, but I depending on what hardware and software ran on the picture frame it could make programming for it more difficult.&lt;/p&gt;
&lt;p&gt;In the end I decided to use an off the shelf Wi-Fi photo frame, mainly because of cost, I was able to buy an full integrated picture frame for less than the cost of a just the LCD panel required to build my own. When I was comparing the Wi-Fi picture frames on the market I found something interesting, almost all of them used one of two platforms to enable the user to upload pictures to the frame: &lt;a href=&#34;https://play.google.com/store/apps/details?id=com.idwell.ourphoto&amp;amp;hl=en_US&amp;amp;gl=US&#34;&gt;OurPhoto&lt;/a&gt; or &lt;a href=&#34;https://frameo.net/&#34;&gt;Frameo&lt;/a&gt;. From what I could learn, both these platforms provide smart picture frame firmware to various manufactures which integrate with their platforms. While I was unable to find any useful information about the content of this firmware, the fact that it was designed to run across many different hardware models gave me some confidence that I wasn&amp;rsquo;t going to to find a highly specialized or low level firmware in these devices. I ended up buying an &lt;a href=&#34;https://www.amazon.com/dp/B083SH697H&#34;&gt;AKImart 10.1 Inch Smart WiFi Digital Photo Frame&lt;/a&gt; for a little over $100, as it struck a decent balance between size and being cheap enough I was okay with potentially bricking it.&lt;/p&gt;
&lt;h3 id=&#34;its-an-android-system&#34;&gt;&amp;ldquo;It&amp;rsquo;s an ANDROID system&amp;rdquo;&lt;/h3&gt;
&lt;p&gt;

&lt;div class=&#34;box floatright&#34; style=&#34;max-width:50%&#34;&gt;
  &lt;figure class=&#34;floatright&#34; itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/picture-frame-from-mars/android_about_tablet.jpg&#34; alt=&#34;My digital picture frame turned out to be running Android 6.0.1&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/picture-frame-from-mars/android_about_tablet.jpg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            My digital picture frame turned out to be running Android 6.0.1
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

Overall there was very little information online about what hardware I could expect to find in the picture frame, so I didn&amp;rsquo;t really know what I was getting myself into. My best guess was I would find some kind of ARM single board computer, running either a customized Linux OS or a specialized Android ROM. It didn&amp;rsquo;t take long to figure out though, because when I first turned on my picture frame, the first thing I was greeted with was an Android setup wizard. This could either of been a really good thing (lots of development resources) or a bad thing (Android can be locked down well if properly configured).&lt;/p&gt;
&lt;p&gt;The &amp;ldquo;Digital Picture Frame&amp;rdquo; that I bought turned out to essentially be a stripped down tablet without a battery running Android 6.0.1. While the OS was configured such one couldn&amp;rsquo;t escape the Frameo &amp;ldquo;Frame&amp;rdquo; app and use it as a normal Android device, fortunately the &lt;a href=&#34;https://developer.android.com/studio/command-line/adb&#34;&gt;ADB USB debugging interface&lt;/a&gt; was left enabled, and even had root permissions. From the ADB shell I could install and launch any app I wanted, so all I needed now was an Android app that would show the latest pictures from Perseverance.&lt;/p&gt;
&lt;h2 id=&#34;perseverance-raw-image-api&#34;&gt;Perseverance Raw Image API&lt;/h2&gt;
&lt;p&gt;Now that I knew I should be able to write my own software for picture frame, I needed to figure out how to actually get the latest pictures from the raw image library. Initially, I was hoping to find public API of some kind to retrieve the pictures, maybe a simple RSS would be nice. NASA does in fact have several &lt;a href=&#34;https://api.nasa.gov/&#34;&gt;open APIs&lt;/a&gt; for various programs, they even have a &lt;a href=&#34;https://api.nasa.gov#mars-rover-photos&#34;&gt;Mars Rover Photos API&lt;/a&gt;. Unfortunately though this API does includes images from many previous mars rover missions, including Curiosity rover mission, it does not provide access to the Perseverance images.  While there was no public API for me to use in the end, I did eventually find that the &lt;a href=&#34;https://mars.nasa.gov/mars2020/multimedia/raw-images/&#34;&gt;Perseverance Raw Image Library&lt;/a&gt; webpage makes requests to an undocumented but open API to retrieve a list of images to display.&lt;/p&gt;
&lt;p&gt;Fortunately, the API used by the raw image library page was fairly simple to reverse engineer, at least as far as was required for my purposes. All requests were sent to &lt;code&gt;https://mars.nasa.gov/rss/api/&lt;/code&gt; with various query parameters that controlled the type of request and content returned, which in all cases was a JSON object. The first step was to understand how the various query parameters worked. There were some query parameters that were fixed, in all the requests I could observe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;feed=raw_images&lt;/code&gt;&lt;br&gt;
Presumably there are multiple data feeds that could be served through this API.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feedtype=json&lt;/code&gt;&lt;br&gt;
The data returned by the API was json encoded, so this makes sense, but even an obvious alternative such as &lt;code&gt;feedtype=xml&lt;/code&gt; resulted in a 404.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ver=1.2&lt;/code&gt;&lt;br&gt;
Likely the API version number.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;category=mars2020,ingenuity&lt;/code&gt;&lt;br&gt;
The existence of this argument implies there could be other raw image feeds, if only we knew the right string. Once again, I have yet to find other valid categories, and no way of listing the available categories.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Presumably there are alternate valid values for these parameters, but I was never able to find any that did not return a 404 error code. In addition to these fixed parameters, there were two parameters that controlled the format of JSON object, resulting in three different message formats, as indicated by the &lt;code&gt;type&lt;/code&gt; field in the response.&lt;/p&gt;
&lt;h3 id=&#34;latest-images-summary&#34;&gt;Latest Images Summary&lt;/h3&gt;
&lt;p&gt;When &lt;code&gt;latest=true&lt;/code&gt; is included in the request query parameters, a latest image summary object is returned, as indicated by a &lt;code&gt;mars2020_ingenuity-latest-images-1.2&lt;/code&gt; value in the &lt;code&gt;type&lt;/code&gt; field:
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;mars2020_ingenuity-latest-images-1.2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;total&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;161829&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;new_count&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;389&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;latest_sol&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;258&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;latest&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2021-11-11T15:49:19Z&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;sol_count&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;55&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;latest_sols&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mi&#34;&gt;258&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mi&#34;&gt;257&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mi&#34;&gt;256&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;mi&#34;&gt;208&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;https://mars.nasa.gov/rss/api/?feed=raw_images&amp;amp;category=mars2020,ingenuity&amp;amp;feedtype=json&amp;amp;ver=1.2&amp;amp;latest=true&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The content of this response message does not appear to change with any other query parameters. The content of the message gives a few useful pieces of information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;total&lt;/code&gt;&lt;br&gt;
Appears to be a total number of images available through this API. It does not reflect the total number images in the Perseverance/Ingenuity category.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new_count&lt;/code&gt;&lt;br&gt;
Number of &amp;ldquo;new&amp;rdquo; images, which appears to be the number of images published in the last 24 hours.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;latest_sol&lt;/code&gt;&lt;br&gt;
The most recent &amp;ldquo;sol&amp;rdquo; (number of mars solar days for the rover since the beginning of it&amp;rsquo;s mission) for which images have been published.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;latest&lt;/code&gt;&lt;br&gt;
UTC time that the image was received on earth from the rover. Interestingly it appears that images can be buffered for a surprisingly long time before being received, I have seen images with almost two (earth) months between being captured and received.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sol_count&lt;/code&gt;&lt;br&gt;
Based on the name and values returned, this appears to be a count of mars solar days, but it&amp;rsquo;s not clear what it represents.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;latest_sols&lt;/code&gt;&lt;br&gt;
List of &amp;ldquo;sols&amp;rdquo; for which pictures have been published in the last 24 hours.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;image-detail-response&#34;&gt;Image Detail Response&lt;/h3&gt;
&lt;p&gt;When an &lt;code&gt;id=&lt;/code&gt; query parameter is included in the request, an image detail response object is returned, corresponding to the image with the given unique id. This response format for this response is indicated by the &lt;code&gt;type&lt;/code&gt; value of &lt;code&gt;mars2020-imagedetail-1.2&lt;/code&gt;.
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:300px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;image&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;extended&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;mastAz&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;21.5952&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;mastEl&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;24.5965&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;sclk&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;689864742.333&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;scaleFactor&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;xyz&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(0.0,0.0,0.0)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;subframeRect&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(1,1,5120,3840)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;dimension&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(1280,960)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;link_related_sol&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020/multimedia/raw-images/?sol=258&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;sol&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;258&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;attitude&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(0.81634,-0.0556483,-0.000426989,0.574885)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;json_link_related_sol&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/rss/api/?feed=raw_images&amp;amp;category=mars2020,ingenuity&amp;amp;feedtype=json&amp;amp;sol=258&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;image_files&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;medium&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020-raw-images/pub/ods/surface/sol/00258/ids/edr/browse/ncam/NLF_0258_0689864724_675ECM_N0080000NCAM00514_01_295J01_800.jpg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;small&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020-raw-images/pub/ods/surface/sol/00258/ids/edr/browse/ncam/NLF_0258_0689864724_675ECM_N0080000NCAM00514_01_295J01_320.jpg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;full_res&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020-raw-images/pub/ods/surface/sol/00258/ids/edr/browse/ncam/NLF_0258_0689864724_675ECM_N0080000NCAM00514_01_295J01.png&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;large&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020-raw-images/pub/ods/surface/sol/00258/ids/edr/browse/ncam/NLF_0258_0689864724_675ECM_N0080000NCAM00514_01_295J01_1200.jpg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;imageid&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NLF_0258_0689864724_675ECM_N0080000NCAM00514_01_295J&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;camera&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;filter_name&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;UNK&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;camera_vector&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(0.8452750616932372,0.3358633696321663,-0.41557895401333894)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;camera_model_component_list&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(0.972477,0.396478,-2.02505);(0.843847,0.347228,-0.40911);(265.584,909.072,-265.014);(689.368,283.332,476.213);(0.843346,0.348554,-0.409015);(1.736e-06,0.0501396,-0.0171254);(-8e-09,1e-08,-2.9e-08);2.0;0.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;camera_position&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(0.972477,0.396478,-2.02505)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;instrument&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NAVCAM_LEFT&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;camera_model_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;CAHVORE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;caption&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NASA&amp;#39;s Mars Perseverance rover acquired this image using its onboard Left Navigation Camera (Navcam). The camera is located high on the rover&amp;#39;s mast and aids in driving. \n\nThis image was acquired on Nov. 11, 2021 (Sol 258) at the local mean solar time of 18:02:21.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;sample_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Full&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;date_taken_mars&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Sol-00258M18:02:21.470&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;credit&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NASA/JPL-Caltech&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;date_taken_utc&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2021-11-11T01:10:13.211&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;link&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020/multimedia/raw-images/?id=NLF_0258_0689864724_675ECM_N0080000NCAM00514_01_295J&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;link_related_camera&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020/multimedia/raw-images/?camera=NAVCAM_LEFT&amp;amp;sol=258&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;drive&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Mars Perseverance Sol 258: Left Navigation Camera (Navcam)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;site&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;date_received&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2021-11-11T08:34:42Z&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;mars2020_ingenuity-imagedetail-1.2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;mission&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;mars2020,ingenuity&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;https://mars.nasa.gov/rss/api/?feed=raw_images&amp;amp;category=mars2020,ingenuity&amp;amp;ver=1.2&amp;amp;feedtype=json&amp;amp;id=NLF_0258_0689864724_675ECM_N0080000NCAM00514_01_295J&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;image&lt;/code&gt; object contains a lot of detailed information about the specific image. For my purpose of just displaying images the images I found the following images useful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;image_files&lt;/code&gt;&lt;br&gt;
Object containing URLs to four differently scaled versions of the image.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sol&lt;/code&gt;&lt;br&gt;
Mission solar day number when the image was captured.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;date_taken_utc&lt;/code&gt;&lt;br&gt;
The UTC time-stamp of when the image was captured.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;date_received&lt;/code&gt;&lt;br&gt;
UTC time-stamp of when the image was received by NASA.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;title&lt;/code&gt;&lt;br&gt;
Short title for the image. This usually contains the sol that the image was captured and the name of the camera used to capture the image.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;caption&lt;/code&gt;&lt;br&gt;
Long form auto-generated caption describing the image.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;images-list-response&#34;&gt;Images List Response&lt;/h3&gt;
&lt;p&gt;Finally if neither &lt;code&gt;latest=true&lt;/code&gt; or &lt;code&gt;id=&lt;/code&gt; query parameters are included in the request an images list response, as indicated by the &lt;code&gt;mars2020_ingenuity-images-list-1.2&lt;/code&gt; &lt;code&gt;type&lt;/code&gt; value is returned. I found that this type of request responded to a few different query parameters, to either control the amount of data returned or filter the list of returned images. Below are the parameters that I was able to reverse engineer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;num=&amp;lt;Integer&amp;gt;&lt;/code&gt;&lt;br&gt;
Number of images to return in this request. There appears to be hard maximum of 100 image per request.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;page=&amp;lt;Integer&amp;gt;&lt;/code&gt;&lt;br&gt;
Page number of results to return. By default (&lt;code&gt;page=0&lt;/code&gt;) the returned image list contains the first &lt;code&gt;num&lt;/code&gt; images that match the given filters. For non-zero values of &lt;code&gt;page&lt;/code&gt; the API returns &lt;code&gt;num&lt;/code&gt; pictures starting at offset &lt;code&gt;num * page&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;order=&amp;lt;String&amp;gt;&lt;/code&gt;&lt;br&gt;
String value controlling the order of returned images. The only valid values I have found are &lt;code&gt;sol desc&lt;/code&gt; (ordered from newest to oldest by mission solar day number), and &lt;code&gt;sol asc&lt;/code&gt; (ordered from oldest to newest by sol number).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sol=&amp;lt;Integer&amp;gt;&lt;/code&gt;&lt;br&gt;
Filter returned image list to a single mars solar day number.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;search=&amp;lt;String&amp;gt;&lt;/code&gt;&lt;br&gt;
Filters the returned image list based on the camera used to capture the image. This is string of pipe (&lt;code&gt;|&lt;/code&gt;) separated camera identifiers. The response to this query includes a data structure that lists the valid camera identifiers, but the current list appears to be: &lt;code&gt;HELI_NAV&lt;/code&gt; &lt;code&gt;HELI_RTE&lt;/code&gt;, &lt;code&gt;NAVCAM_LEFT&lt;/code&gt;, &lt;code&gt;NAVCAM_RIGHT&lt;/code&gt;, &lt;code&gt;FRONT_HAZCAM_LEFT_A&lt;/code&gt;, &lt;code&gt;FRONT_HAZCAM_LEFT_B&lt;/code&gt;, &lt;code&gt;FRONT_HAZCAM_RIGHT_A&lt;/code&gt;, &lt;code&gt;FRONT_HAZCAM_RIGHT_B&lt;/code&gt;, &lt;code&gt;REAR_HAZCAM_LEFT&lt;/code&gt;, &lt;code&gt;REAR_HAZCAM_RIGHT&lt;/code&gt;, &lt;code&gt;CACHECAM&lt;/code&gt;, &lt;code&gt;MCZ_LEFT&lt;/code&gt;, &lt;code&gt;MCZ_RIGHT&lt;/code&gt;, &lt;code&gt;SKYCAM&lt;/code&gt;, &lt;code&gt;PIXL_MCC&lt;/code&gt;, &lt;code&gt;SHERLOC_WATSON&lt;/code&gt;, &lt;code&gt;SHERLOC_ACI&lt;/code&gt;, &lt;code&gt;SUPERCAM_RMI&lt;/code&gt;, &lt;code&gt;EDL_PUCAM1&lt;/code&gt;, &lt;code&gt;EDL_PUCAM2&lt;/code&gt;, &lt;code&gt;EDL_DDCAM&lt;/code&gt;, &lt;code&gt;EDL_RUCAM&lt;/code&gt;, &lt;code&gt;LCAM&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;condition_2=&amp;lt;String&amp;gt;&lt;/code&gt;&lt;br&gt;
Generally a starting date filter. This field can either filter based on day the image was taken using the form  &lt;code&gt;&amp;lt;yyyy-MM-dd&amp;gt;:date_taken:gte&lt;/code&gt;, or filter based on the time that the image was received using the form &lt;code&gt;&amp;lt;yyyy-MM-dd&#39;T&#39;HH:mm:ss.SSSSSSZ&amp;gt;:date_received:gte&lt;/code&gt; when used in conjunction with a &lt;code&gt;condition_3&lt;/code&gt; &lt;code&gt;sol&lt;/code&gt; filter parameter.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;condition_3=&amp;lt;String&amp;gt;&lt;/code&gt;&lt;br&gt;
Either an end date or sol number filter. With no &lt;code&gt;condition_2&lt;/code&gt; parameter specified, or when &lt;code&gt;condition_2&lt;/code&gt; uses the &amp;ldquo;date_taken&amp;rdquo; format, this parameter is an end date filter using the form &lt;code&gt;&amp;lt;yyyy-MM-dd&amp;gt;:date_take:lt&lt;/code&gt;. When &lt;code&gt;condition_2&lt;/code&gt; is a &amp;ldquo;date_received&amp;rdquo; filter, this parameter can be a sol number filter of the form: &lt;code&gt;&amp;lt;comma separated list of sol numbers&amp;gt;:sol:in&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While it would seem that the &lt;code&gt;condition_2&lt;/code&gt; and &lt;code&gt;condition_3&lt;/code&gt; parameters appear as if they could provide more generic filtering features, they appear to be locked down to only these use cases on the server side. The response to this type of request looks like the following (lists abbreviated):
&lt;figure class=&#34;no-photoswipe&#34; style=&#34;--max-figure-height:300px;&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;54
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;55
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;56
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;57
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;nav&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;checkboxes&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;s2&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;HELI_NAV&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;s2&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Navigation Camera&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;order&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Mars Helicopter Tech Demo Cameras&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;about_text&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Cameras onboard the &amp;lt;a href=&amp;#39;https://mars.nasa.gov/technology/helicopter/&amp;#39;&amp;gt;Ingenuity Mars Helicopter&amp;lt;/a&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;images&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;extended&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;mastAz&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;UNK&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;mastEl&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;UNK&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;sclk&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;666952979.177&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;scaleFactor&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;xyz&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(0.0,0.0,0.0)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;subframeRect&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(1,1,1280,960)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;dimension&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(1280,960)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;sol&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;attitude&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(1.0,0.0,0.0,0.0)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;image_files&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;medium&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020-raw-images/pub/ods/surface/sol/00000/ids/edr/browse/fcam/FLR_0000_0666952977_663ECM_N0010044AUT_04096_00_2I3J02_800.jpg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;small&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020-raw-images/pub/ods/surface/sol/00000/ids/edr/browse/fcam/FLR_0000_0666952977_663ECM_N0010044AUT_04096_00_2I3J02_320.jpg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;full_res&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020-raw-images/pub/ods/surface/sol/00000/ids/edr/browse/fcam/FLR_0000_0666952977_663ECM_N0010044AUT_04096_00_2I3J02.png&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;large&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020-raw-images/pub/ods/surface/sol/00000/ids/edr/browse/fcam/FLR_0000_0666952977_663ECM_N0010044AUT_04096_00_2I3J02_1200.jpg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;imageid&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;FLR_0000_0666952977_663ECM_N0010044AUT_04096_00_2I3J02&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;camera&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;filter_name&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;UNK&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;camera_vector&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(0.6926460219832632,-0.7044446294451989,-0.15491692056253062)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;camera_model_component_list&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(1.10353,-0.008945,-0.729124);(0.884935,-0.17181,0.432865);(651.291,418.924,318.224);(186.623,-81.6605,689.135);(0.885293,-0.171227,0.432363);(1e-06,0.00889,-0.006754);(-0.006127,0.010389,0.004541);2.0;0.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;camera_position&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(1.10353,-0.008945,-0.729124)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;instrument&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;FRONT_HAZCAM_LEFT_A&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;camera_model_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;CAHVORE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;caption&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NASA&amp;#39;s Mars Perseverance rover acquired this image of the area in front of it using its onboard Front Left Hazard Avoidance Camera A. \n\nThis image was acquired on Feb. 18, 2021 (Sol 0) at the local mean solar time of 15:53:58.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;sample_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Full&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;date_taken_mars&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Sol-00000M15:53:58.302&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;credit&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NASA/JPL-Caltech&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;date_taken_utc&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2021-02-18T20:44:29.175&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;json_link&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/rss/api/?feed=raw_images&amp;amp;category=mars2020,ingenuity&amp;amp;feedtype=json&amp;amp;id=FLR_0000_0666952977_663ECM_N0010044AUT_04096_00_2I3J02&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;link&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://mars.nasa.gov/mars2020/multimedia/raw-images/?id=FLR_0000_0666952977_663ECM_N0010044AUT_04096_00_2I3J02&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;drive&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;44&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Mars Perseverance Sol 0: Front Left Hazard Avoidance Camera (Hazcam)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;site&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;date_received&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2021-02-22T20:06:53Z&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;per_page&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;3&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;total_results&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;67201&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;mars2020_ingenuity-images-list-1.2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;page&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;mission&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;mars2020,ingenuity&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;total_images&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;162120&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;https://mars.nasa.gov/rss/api/?feed=raw_images&amp;amp;category=mars2020,ingenuity&amp;amp;feedtype=json&amp;amp;ver=1.2&amp;amp;num=3&amp;amp;page=0&amp;amp;&amp;amp;order=sol&amp;#43;asc&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

Depending on how many images are requested, these responses can be large, but the main fields of interest are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nav&lt;/code&gt;&lt;br&gt;
Structure containing the valid camera identifiers for the search query parameter. Since this structure appears to be used to render the filter UI on the raw image library webpage, it not only includes the camera identifiers, it also includes a human readable name for the camera.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;images&lt;/code&gt;&lt;br&gt;
List of image details objects for images matching the query filters. This uses the same data structure as the &lt;code&gt;image&lt;/code&gt; object in the image details response.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;per_page&lt;/code&gt;&lt;br&gt;
Number of images actually returned per &amp;ldquo;page&amp;rdquo; by this request. Should match the &lt;code&gt;num&lt;/code&gt; query parameter, unless it&amp;rsquo;s greater than the hard limit of 100.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;page&lt;/code&gt;&lt;br&gt;
The &amp;ldquo;page&amp;rdquo; number of images returned in this request. This should be the same as the &lt;code&gt;page&lt;/code&gt; query parameter, if specified.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;total_results&lt;/code&gt;&lt;br&gt;
Total number of images that match the filters given in the request.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-customized-picture-frame-app&#34;&gt;The Customized Picture Frame App&lt;/h2&gt;
&lt;p&gt;Once I determined that the picture frame was running Android with an open ADB shell, I knew it should be possible to write a custom app that would query the NASA raw images API and displays them fullscreen on the frame. The only problem was that I had never written an Android app before, but there&amp;rsquo;s a first time for everything. Once I reverse engineered the API used by the NASA raw image library webpage, and learned a little bit of Android development, the actual implementation of the app turned out to be fairly simple. While I don&amp;rsquo;t recommend anyone use my code for as any kind of reference, I have published all the code on my Gitlab project here: &lt;a href=&#34;https://gitlab.com/prbs23/mars-photo-stream&#34;&gt;Mars Photo Stream&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;

&lt;div class=&#34;box floatright&#34; style=&#34;max-width:50%&#34;&gt;
  &lt;figure class=&#34;floatright&#34; itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/picture-frame-from-mars/photo_frame_w_overlay.jpg&#34; alt=&#34;My custom Mars Photo Stream app running on the picture frame, displaying information overlay.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/picture-frame-from-mars/photo_frame_w_overlay.jpg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            My custom Mars Photo Stream app running on the picture frame, displaying information overlay.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

The app mainly consists of two periodic looper handlers. One handler is responsible for updating a list of the latest 300 images by querying the NASA API every hour. The other handler is responsible for updating the image being displayed. It randomly selects one image from the list of latest images, downloads it, and then updates an ImageSwitcher widget with the new image. Other than these periodic handlers, I just needed a few buttons to handle pausing the automatic image advancement, skipping to a new image, and managing settings..&lt;/p&gt;
&lt;p&gt;Overall the app is functionally at a point where everything working well enough that I am happy to just have it on the wall. It&amp;rsquo;s stable, does what I set out to accomplish, and has enough configurability for daily use. Of course there are still some issues and areas for potential future improvements that I may pick up at a later time.&lt;/p&gt;
&lt;h2 id=&#34;next-steps&#34;&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;The main issue with the picture frame is that somehow Frameo configuration the custom Android ROM to completely disable the soft navigation buttons (most importantly the back button). This is not a problem when operating inside my app, but when navigating the Android settings UI, this can be problematic. To close the Android settings app and get back to the picture frame app I currently need to &amp;ldquo;force stop&amp;rdquo; the settings app&amp;hellip; not exactly convenient. I have tried many suggestions on how to re-enable the navigation bar, but so far nothing I have tried worked. If anyone is experienced with the configuration of the Android SystemUI package and has ideas on how to re-enable it, &lt;a href=&#34;mailto:prbs23@prbs23.com&#34;&gt;get in touch&lt;/a&gt; because this really the only major issue I have.&lt;/p&gt;
&lt;p&gt;There are also two features new features that I would like to implement, but haven&amp;rsquo;t gotten around to yet. While I find most of the raw images have something interesting about them, occasionally the app will select an image that is just &amp;ldquo;static&amp;rdquo;, or a featureless sky, or something equally uninteresting. I would like to be able to have my app somehow automatically filter these images out before showing them. It seems possible that some kind of simple contrast analysis of the images should be able to identify these &amp;ldquo;uninteresting&amp;rdquo; images. Maybe I will tackle this problem when I want to start learning about image processing.&lt;/p&gt;
&lt;p&gt;Conversely, there will also sometimes be a really interesting image shows up that I would like to be able to save for later. My initial idea was to add a button which would send an email containing a link to the current image to a pre-configured address. Unfortunately, I wasn&amp;rsquo;t able to find any simple APIs for sending an email in the background, so I haven&amp;rsquo;t bothered to figure this out yet. I would still like to have the feature, but I&amp;rsquo;m no longer sure that email is the correct tool, maybe there&amp;rsquo;s a better alternative.&lt;/p&gt;
&lt;h2 id=&#34;making-your-own&#34;&gt;Making Your Own&lt;/h2&gt;
&lt;p&gt;Now I know what everyone is saying, how do I make one of these for myself? Overall this project turned out to be a lot less complicated than I initially expected, and should be easy to reproduce. The following steps should be all it takes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Get yourself a Wi-Fi digital picture frame. If you want to play it safe you can get the same &lt;a href=&#34;https://www.amazon.com/dp/B083SH697H&#34;&gt;AKImart 10.1 Inch Smart WiFi Digital Photo Frame&lt;/a&gt; that I used. I would expect that at least pretty much any Frameo Wi-Fi digital picture frame would also work, but I don&amp;rsquo;t know that they will all be unlocked in the same way the AKImart one was.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Either build or download the APK for my &amp;ldquo;Mars Photo Stream&amp;rdquo; app. The &lt;a href=&#34;https://gitlab.com/prbs23/mars-photo-stream/-/releases&#34;&gt;Releases&lt;/a&gt; should always have an APK for the latest release of the app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the Mars Photo Stream APK onto the picture frame using the ADB tool using the following command:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ adb install com.prbs23.marsphotostream-v0.1.0.apk&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you don&amp;rsquo;t want to ever use the picture frame using the intended Frameo application, then you can disable it. If you don&amp;rsquo;t disable the Frameo &amp;ldquo;frame&amp;rdquo; app, then every time the picture frame boots up it will ask if you want to launch the Frameo app or Mars Photo Stream. Depending on your use case you may or may not want this.&lt;br&gt;
If you want to disable the Frameo app so it always boots into the Mars Photo Stream app, then run the following commands at the ADB shell:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ adb shell
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;shell@ZN-DP1007$ su root
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@ZN-DP1007$ pm disable net.frameo.frame
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@ZN-DP1007$ pm hide net.frameo.frame&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure the Wi-Fi setting for the picture frame so that it can access the internet.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If everything has goes as expected, the Mars Photo Stream app should now be running on your picture frame, and will automatically start displaying raw images from the Perseverance rover and Ingenuity helicopter.&lt;/p&gt;
&lt;p&gt;In theory the Mars Photo Stream app could also be installed onto any Android phone or tablet and used as a picture frame, though I haven&amp;rsquo;t tested this. In this case you can directly download the &lt;a href=&#34;https://gitlab.com/prbs23/mars-photo-stream/-/releases&#34;&gt;latest APK file&lt;/a&gt; onto the device and install it. If there is enough interest I could likely get the app on the Google Play Store, but for a personal project I didn&amp;rsquo;t bother figuring out how to do this.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Bidirectional Delay in SystemVerilog</title>
      <link>https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/</link>
      <pubDate>Fri, 22 Oct 2021 07:07:39 -0600</pubDate>
      <author>prbs23@prbs23.com (prbs23)</author>
      <guid>https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/</guid>
      <description>&lt;p&gt;I recently wrote about &lt;a href=&#34;https://www.prbs23.com/blog/posts/drive-strength-detection-in-system-verilog/&#34;&gt;detecting driver strength in SystemVerilog&lt;/a&gt;. That work actually came out of solving a larger problem: How do you build a bidirectional digital wire model that includes propagation delay?&lt;/p&gt;
&lt;h2 id=&#34;motivation&#34;&gt;Motivation&lt;/h2&gt;
&lt;p&gt;The problem I had was how to model, in a digital simulation, the behavior of a &amp;ldquo;channel&amp;rdquo; (wire) for a high speed SerDes link. At the symbol rates used for modern high speed SerDes links, we start running into the physical limitations of copper as a conductor: the attenuation, frequency response, and speed of signals propagating through the channel are all factors that cannot be ignored as they might usually be. An additional requirement that my channel model had, was that it needed to be bidirectional. For my gate models to function, I not only needed to model the propagation delay of signals through the channel, but I needed drive strength resolution to resolve the value of the channel with drivers on both ends.&lt;/p&gt;
&lt;p&gt;The protocol for the PHY I was modeling specifies that the transmitter should only be enabled when it detects that there is a receiver connected to it. The way this detection is done is by having the transmitter sense the presence of the termination resistor in the receiver. This is convenient because it lets the transmitter do endpoint detection without any extra wires, but it does complicate the channel modeling, because a lot of SystemVerilog constructs do not resolve drive strengths in both directions.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/serdes_with_endpoint_detection.svg&#34; alt=&#34;Basic schematic diagram of system.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/serdes_with_endpoint_detection.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Basic schematic diagram of system.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h2 id=&#34;a-first-attempt&#34;&gt;A First Attempt&lt;/h2&gt;
&lt;p&gt;While constructing a model that could inject bit flip errors and remain bidirectional was a little tricky, its not too difficult with the right set up of &lt;code&gt;tranif&lt;/code&gt; gates. What turned out to be the tricky part was modeling propagation delay, because as far as I could find, all SystemVerilog constructs which introduced a delay, also behave as unidirectional drivers. So, how can I make a module where a driver change on either port will appear, after a delay, on the other?&lt;/p&gt;
&lt;h3 id=&#34;breaking-down-the-problem&#34;&gt;Breaking Down The Problem&lt;/h3&gt;
&lt;p&gt;An important thing to remember is that in SystemVerilog a net value is really a combination of two components, a value (&lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;z&lt;/code&gt;, or &lt;code&gt;x&lt;/code&gt;), and a strength (&lt;code&gt;highz&lt;/code&gt;, &lt;code&gt;weak&lt;/code&gt;, &lt;code&gt;pull&lt;/code&gt;, &lt;code&gt;strong&lt;/code&gt;, or &lt;code&gt;supply&lt;/code&gt;). It&amp;rsquo;s easy to to delay the value part, but in SystemVerilog this always loses the strength part. Considering this, I hypothesized that if I could decompose the net value into two separate signals (value and strength), then recompose them on the other side of the delay, I could make a strength preserving delay module. If that works, then I could put two of those circuits back to back to make a truly bidirectional delay module.&lt;/p&gt;
&lt;p&gt;The basic idea was to first build a strength maintaining delay element. To do this I needed three pieces: To decompose the net value I needed a strength detector which would take in a net, a output a value proportional to the strength of the net&amp;rsquo;s driver. To recompose the net value I needed a variable strength driver which would take in two &amp;ldquo;values&amp;rdquo; a net value and a net strength, and would drive an output net with the specified strength and value. Finally I would need a controllable delay element so that the delay could be controlled by my testbench. Once I had the unidirectional delay, I could put two of these back to back, as shown in the system schematic below, to create the bidirectional delay I needed.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/bidir_delay_element_schem_initial.png&#34; alt=&#34;Schematic showing structure of first bidirectional delay module concept.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/bidir_delay_element_schem_initial.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Schematic showing structure of first bidirectional delay module concept.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;First, I needed a way to convert the strength of a driver on a net to a SystemVerilog value. This turned out to be a deep hole all on it&amp;rsquo;s own, and I&amp;rsquo;ve already written about it at length &lt;a href=&#34;https://www.prbs23.com/blog/posts/drive-strength-detection-in-system-verilog/&#34;&gt;in my last blog post&lt;/a&gt;. There are a couple ways of doing this doing this with different trade-offs, but for the purposes of this model I used the Verilog only &amp;ldquo;sense net&amp;rdquo; strength detection method because it&amp;rsquo;s a nice fast standalone implementation. It detects &lt;code&gt;supply&lt;/code&gt; strength drivers as &lt;code&gt;strong&lt;/code&gt; but that was okay for my use case. The code for that is shown below, for details on how it works, that can be found in my previous post.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_net_strength_detect&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;output&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;reg&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wire&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strong_sense&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wire&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pull_sense&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wire&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weak_sense&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;strong1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;strong0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strong_sense&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;pull1&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;pull0&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pull_sense&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;weak1&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;weak0&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weak_sense&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;cmos&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strong_gate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strong_sense&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;cmos&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pull_gate&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pull_sense&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;cmos&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weak_gate&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;weak_sense&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pull_sense&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strong_sense&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;weak_sense&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pull_sense&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endmodule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;sense_net_strength_detect.sv&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;The next piece I needed was a module that has &amp;ldquo;value&amp;rdquo; and &amp;ldquo;strength&amp;rdquo; inputs, and will drive an output node high or low with the given strength level. There turns out to be a few ways to do this, but the cleanest approach I found was to use two assign statements for each strength level: One which can only drive a &lt;code&gt;&#39;1&lt;/code&gt; value with the given strength, and one which can only drive a &lt;code&gt;&#39;0&lt;/code&gt; value with the given strength. Then the value of the assign statement is the &amp;ldquo;value&amp;rdquo; input ORed or ANDed with a strength value comparison so that only the correct assign statement is active for the given value of the &amp;ldquo;strength&amp;rdquo; input. The code for this module looks like the following:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;multi_strength_driver&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;input&lt;/span&gt;       &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// highz = 0, weak = 1, pull = 2, strong = 3, supply = 4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;supply0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;highz1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;highz0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;supply1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;strong0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;highz1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;highz0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;strong1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;pull0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;   &lt;span class=&#34;k&#34;&gt;highz1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;highz0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;pull1&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;weak0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;   &lt;span class=&#34;k&#34;&gt;highz1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;highz0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;weak1&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endmodule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;multi_strength_driver.sv&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Finally, the programmable delays could be simple assign statements with a delay value. Since I&amp;rsquo;m breaking up the strength and value of the signal, and providing independent data paths in each direction, there is no need to try to preserve drive strength in these assignments or use bidirectional gates.&lt;/p&gt;
&lt;h3 id=&#34;putting-it-all-together-for-the-first-time&#34;&gt;Putting It All Together &lt;em&gt;(For The First Time)&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;Now that I had all the pieces, I just needed to stick them together and try it out. For each of the two &lt;code&gt;inout&lt;/code&gt;, I created a &amp;ldquo;sense_strength&amp;rdquo; signal that was the value representation of that net&amp;rsquo;s drive strength, and two &lt;code&gt;reg&lt;/code&gt; signals to hold the delayed value and strength values for the multi_strength_driver, from the other side of the link.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bidir_delay_initial&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;parameter&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DELAY_WIDTH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;32&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DELAY_WIDTH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wire&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;reg&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;reg&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wire&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;reg&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;drive_value_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;reg&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;sense_net_strength_detect&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;sense_net_strength_detect&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;multi_strength_driver&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;drive_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;multi_strength_driver&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;drive_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;drive_value_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;initial&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;&amp;#39;d0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;drive_strength_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;&amp;#39;d0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;drive_value_b&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mb&#34;&gt;&amp;#39;b0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;drive_strength_b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;&amp;#39;d0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;drive_value_b&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endmodule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;bidir_delay_initial.sv&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;To test my solution I created a basic little testbench. On one side (side A) of my delay module I put another instance of my multi-strength driver module, so I could dynamically control the strength of the driver on that side, and to the other (side B), I placed &lt;code&gt;weak&lt;/code&gt; strength driver to 0&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/bidir_delay_tb.png&#34; alt=&#34;Simple bidirectional delay test bench.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/bidir_delay_tb.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Simple bidirectional delay test bench.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;For initial turn on I used a sequence that would start by driving a &lt;code&gt;strong1&lt;/code&gt;, decreasing strength down to &lt;code&gt;highz&lt;/code&gt;, and then increase the strength back to &lt;code&gt;strong&lt;/code&gt; with a value of 0 on the net &lt;code&gt;a&lt;/code&gt;. Between each step I used a 10ns delay, and the delay element was set with a fixed 2ns delay.&lt;/p&gt;
&lt;p&gt;Unfortunately, my initial results were not what I was intending, I expected that the &lt;code&gt;b&lt;/code&gt; net would be initially high, transition to &lt;code&gt;1&#39;bx&lt;/code&gt; 2ns after the &lt;code&gt;a&lt;/code&gt; driver changes strength to &lt;code&gt;weak1&lt;/code&gt;, and then transition low 2ns after the &lt;code&gt;a&lt;/code&gt; driver transitioned to &lt;code&gt;highz&lt;/code&gt;. The &lt;code&gt;a&lt;/code&gt; net should have been high until 4ns after the &lt;code&gt;a&lt;/code&gt; driver strength goes to 1, when it should to go to a &lt;code&gt;1&#39;bx&lt;/code&gt;. After the &lt;code&gt;a&lt;/code&gt; driver goes to a strength of zero it should be pulled low by the pulldown on net &lt;code&gt;b&lt;/code&gt;. What the simulation results, as seen below, actually showed was both nets &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; being driven high (with a &lt;code&gt;strong&lt;/code&gt; driver) until the driver on side &lt;code&gt;a&lt;/code&gt; drove a &lt;code&gt;strong0&lt;/code&gt;, when both sides go to &lt;code&gt;strongX&lt;/code&gt;.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/bidir_delay_latchup.png&#34; alt=&#34;Simulation waveforms from initial bidirectional delay module showing positive feedback latch behavior.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/bidir_delay_latchup.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Simulation waveforms from initial bidirectional delay module showing positive feedback latch behavior.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;While not the intended result, an astute observer may have predicted this behavior simply from looking at the initial simplified schematic. By putting two of the same strength preserving delays back to back, I created a positive feedback loop in the net strength. After 2x the delay duration from time 0, the variable strength driver inside the delay module wll be driving the same value and strength as the strongest external driver on either side of the delay. If the external driver changes value or strength the internal driver will continue to drive the old value for 2x the delay duration. Unless the net is externally driven with a larger strength than it was before, the net will continue to hold the old value. This is somewhat akin to the latching behavior you get from a unity gain amplifier with positive feedback.&lt;/p&gt;
&lt;h2 id=&#34;breaking-the-feedback-loop-a-more-correct-delay&#34;&gt;Breaking the Feedback Loop &lt;em&gt;(a more correct delay)&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;To actually make this delay element to behave correctly, I needed to break the positive feedback loop caused when one of the variable-strength drivers in the delay element is the strongest driver on it&amp;rsquo;s net. I found that this is possible by only enabling the driver on port &lt;code&gt;b&lt;/code&gt; when the driver on port &lt;code&gt;a&lt;/code&gt; is not driving the net, and vice versa. While there isn&amp;rsquo;t a (native) way to know which driver is driving a net, a close enough approximation is if the detected value and strength of the net matches the value and strength inputs to the variable-strength driver on that net. If the strength and value match, then the driver on the opposite port is disabled, which breaks the feedback loop.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/bidir_delay_element_schem.png&#34; alt=&#34;Schematic showing structure of bidirectional delay module with broken feedback loop.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/bidir_delay_element_schem.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Schematic showing structure of bidirectional delay module with broken feedback loop.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;While this is actually a conceptually simple way to break up the feedback loop, the trick turned out to be expressing it in SystemVerilog such that it wouldn&amp;rsquo;t cause zero-time loops in the simulator when resolving the bidirectional net values. After a decent amount of experimentation, the solution turned out to be correct partitioning of &lt;code&gt;always&lt;/code&gt; blocks and some well placed &lt;code&gt;#0&lt;/code&gt; delays to force net resolution order. Note that there has to be some additional special handling for &lt;code&gt;1&#39;bx&lt;/code&gt; values, because they can be the result of multiple drivers on one net. The code for my final bidirectional delay module is shown below.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bidir_delay&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;parameter&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DELAY_WIDTH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;32&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DELAY_WIDTH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wire&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;reg&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;reg&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;sense_value_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_value_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;reg&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;reg&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;driving_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;driving_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;sense_net_strength_detect&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;sense_net_strength_detect&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;multi_strength_driver&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;drive_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;multi_strength_driver&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;drive_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;drive_value_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;initial&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;&amp;#39;d0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;drive_strength_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;&amp;#39;d0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;drive_value_b&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mb&#34;&gt;&amp;#39;b0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;drive_strength_b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;&amp;#39;d0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;driving_a&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;&amp;#39;d0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;driving_b&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;&amp;#39;d0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;driving_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;driving_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_value_a_delay&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;driving_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;driving_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_value_a_delay&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_value_a_delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_value_b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_value_a_delay&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_value_a_delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_value_b_delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_value_b_delay&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_value_b_delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;driving_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;driving_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;driving_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;driving_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;posedge&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;driving_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mb&#34;&gt;&amp;#39;b0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;posedge&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;driving_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;#(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;mb&#34;&gt;&amp;#39;b0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Evaluate this after all the inputs have settled to prevent extra delta cycles.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;driving_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;drive_value_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_strength_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_value_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Evaluate this after all the inputs have settled to prevent extra delta cycles.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;driving_b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig_b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_value_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;drive_value_b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sense_strength_b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive_strength_b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endmodule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;bidir_delay.sv&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Using the same testbench as before, simulating this modified delay module resulted in the waves shown below. With the positive feedback loop broken, we now get the expected behavior on the bidriectional nets. The &lt;code&gt;a&lt;/code&gt; node gets pulled down by the pulldown on net &lt;code&gt;b&lt;/code&gt;after the 2x delay period, instead of latching the &lt;code&gt;strong&lt;/code&gt; value, as the old implementation did.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/bidir_delay_waves.png&#34; alt=&#34;Simulation results of bidirectional delay module showing expected behavior, after breaking feedback loop.&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/bidirectional-delay-in-systemverilog/bidir_delay_waves.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Simulation results of bidirectional delay module showing expected behavior, after breaking feedback loop.
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h3 id=&#34;remaining-issues&#34;&gt;Remaining Issues&lt;/h3&gt;
&lt;p&gt;The most significant remaining issue with this design is that if the &lt;code&gt;delay&lt;/code&gt; port is set to 0, it can cause an infinite zero-time loop in the simulator. For my use case, the I was able to externally ensure that the delay input never was set to 0, but if this was not possible, one could also add &lt;code&gt;tranif&lt;/code&gt; gates to disconnect and bypass the drivers when the delay input was set to zero. The other significant issue is that it cannot handle &lt;code&gt;supply&lt;/code&gt; strength net values, mainly because of the approach used to sense the net drive strength. If this was a significant issue, the DPI drive strength detection implementation from my previous blog post could be used in place of the sense-net one I used.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;While there are still some corner cases with this bidirectional delay implementation that don&amp;rsquo;t quite match with reality, it has proven to be fairly solid in production. The fact that it was possible to implement in pure SystemVerilog is both convenient, and means that it should be portable to any simulator. In the end, this project gave me the opportunity to really dig into the details of how net resolution, strength modeling, as well as gate primetives actually work in SystemVerilog.&lt;/p&gt;
&lt;p&gt;If you would like to play around with my bidirectional delay module, you can find an implementation on EDA Playgound here: &lt;a href=&#34;https://www.edaplayground.com/x/2PdE&#34;&gt;https://www.edaplayground.com/x/2PdE&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Drive Strength Detection in SystemVerilog</title>
      <link>https://www.prbs23.com/blog/posts/drive-strength-detection-in-system-verilog/</link>
      <pubDate>Tue, 21 Sep 2021 22:44:59 -0600</pubDate>
      <author>prbs23@prbs23.com (prbs23)</author>
      <guid>https://www.prbs23.com/blog/posts/drive-strength-detection-in-system-verilog/</guid>
      <description>&lt;p&gt;From time to time I will run across small problems or challenges in my day job which seem as though they should be simple enough but get stuck in my mind until they are fully investigated. This is one such issue I ran into a while ago: How can a verilog model determine the drive strength of a net value in SystemVerilog?&lt;/p&gt;
&lt;p&gt;When I initially ran into this problem, I thought it would require a quick search through the LRM and I&amp;rsquo;d be on my way. However, after some searching and not finding any useful constructs in the language that would directly provide visibility into a net&amp;rsquo;s drive strength, I ended up on google, and all the recommendations were similar to this post from stack overflow: &lt;a href=&#34;https://stackoverflow.com/questions/28402691/how-to-check-signal-drive-strength&#34;&gt;How to check signal drive strength?&lt;/a&gt;. It seemed the recommended solution was to format the net value using the &lt;code&gt;&amp;quot;%v&amp;quot;&lt;/code&gt; format string, and then do string comparisons on the formatted value.&lt;/p&gt;
&lt;p&gt;Having run into my fair share of string and string formatting performance issues in SystemVerilog in the past, the suggested &amp;ldquo;solution&amp;rdquo; was instantly ringing some alarm bells. This is the problem that latched into my mind, and I needed to know if there was a better way&amp;hellip; but first, a quick refresher on the drive strength modeling features in SystemVerilog.&lt;/p&gt;
&lt;h2 id=&#34;drive-strength-modeling&#34;&gt;Drive Strength Modeling&lt;/h2&gt;
&lt;p&gt;In SystemVerilog (and Verilog before it), a &amp;ldquo;net&amp;rdquo; is a special type of variable construct which is used to structurally model a wire in a circuit. The value of the &amp;ldquo;net&amp;rdquo; variable is a combination of a 4-state logical value (&lt;code&gt;0&lt;/code&gt;,&lt;code&gt;1&lt;/code&gt;,&lt;code&gt;z&lt;/code&gt;,&lt;code&gt;x&lt;/code&gt;), and a strength value (&lt;code&gt;highz&lt;/code&gt;, &lt;code&gt;weak&lt;/code&gt;, &lt;code&gt;pull&lt;/code&gt;, &lt;code&gt;strong&lt;/code&gt;, or &lt;code&gt;supply&lt;/code&gt;). The value of a net is determined by &amp;ldquo;driver&amp;rdquo; elements connected to the net, which can be either continuous assignment expressions, or gate level modeling primitives. If semantically a SystemVerilog net represents a wire in a circuit, and the logical value represents the &lt;em&gt;voltage&lt;/em&gt; on the wire, then the strength of the net represents how much &lt;em&gt;current&lt;/em&gt; can that wire can source (or said another way, the impedance of the driver).&lt;/p&gt;
&lt;p&gt;For every delta cycle of a simulation, the simulator will determine the logical and strength values of each net, by looking at the value and strength of all driver expressions on the net. If a net has no active drivers, then the value of the net is &amp;ldquo;high impedance&amp;rdquo; represented as &lt;code&gt;&#39;z&lt;/code&gt; in SystemVerilog 4-state values. If a net has one driver expression or primitive, then the value and strength of the net are equal to that of the driver. However, if there are more than one driver connected to a single net, the logical value of the net is determined by comparing the strength of each driver on the net. If there is one driver on the net that has a strength lager than the rest, or there are multiple drivers that all have the same largest strength value, and the same logical value, then the logical value of the net is determined by the logical value of the driver with the greatest strength. If there are multiple drivers that all have the same greatest strength on the net, but have different values, then the value of the net is &amp;ldquo;unknown&amp;rdquo; or &lt;code&gt;&#39;x&lt;/code&gt; with the strength of the strongest drivers.&lt;/p&gt;


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/posts/drive-strength-detection-in-system-verilog/sv_drive_strengths.svg&#34; alt=&#34;Extracted figures from SystemVerilog LRM showing combinations of different strength drivers (left) and resulting net value (right)&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/posts/drive-strength-detection-in-system-verilog/sv_drive_strengths.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Extracted figures from SystemVerilog LRM showing combinations of different strength drivers (left) and resulting net value (right)
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;If the strength value of a net is determined by the relative strength values of the drivers on the net, then there must be a way to define the strength of a driving expression. In SystemVerilog, by default all driver expressions have a &lt;code&gt;strong&lt;/code&gt; strength value, which for a lot of use cases, is all you would ever need. However, the strength of logical 1 and 0 values can be individually set for both continuous assignment expressions and gate primitives by including a token of the form &lt;code&gt;(&amp;lt;strength&amp;gt;1, &amp;lt;strength&amp;gt;0)&lt;/code&gt; after the &lt;code&gt;assign&lt;/code&gt; keyword or primitive name. For example the expression &lt;code&gt;assign (strong1, weak0) foo = ~bar;&lt;/code&gt; will drive the net &lt;code&gt;foo&lt;/code&gt; with a logical value of 1 and strength of &lt;code&gt;strong&lt;/code&gt;, when &lt;code&gt;bar&lt;/code&gt; is a logical 0, and drive a logical value of 0 with strength value &lt;code&gt;weak&lt;/code&gt; when &lt;code&gt;bar&lt;/code&gt; is 1.&lt;/p&gt;
&lt;p&gt;As an example, one might use drive strength modeling when modeling an I&lt;sup&gt;2&lt;/sup&gt;C bus, which uses pull-up resistors to pull the bus high, and open drain drivers in each device to pull it low. In such a model, the pull up resistors on the bus can be simply modeled as a &lt;code&gt;pull1&lt;/code&gt; continuous assignments with a value of &lt;code&gt;1&#39;b1&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;pull1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;highz0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;mb&#34;&gt;&amp;#39;b1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Additionally, the multiple I&lt;sup&gt;2&lt;/sup&gt;C IO drivers can be modeled as driving the bus using a continuous assignment expressions with a &lt;code&gt;(highz1, strong0)&lt;/code&gt; strength expression:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;highz1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;strong0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;device0_scl_value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;highz1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;strong0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;device1_scl_value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This is is not just nice because it&amp;rsquo;s a concise way of having the simulator figure out the interactions between devices on the bus, but it does so in a way that structurally mirrors how the circuits work. So if the simulator can do all the work of understanding the strengths of different drivers on a net, and resolving the net to the correct value, why would you need to be able to determine the strength of a net? There&amp;rsquo;s a few use cases I have actually run into, but continuing the I&lt;sup&gt;2&lt;/sup&gt;C example, one use case would be if you wanted to write an assertion to check the bus is never driven high with a &lt;code&gt;strong&lt;/code&gt; driver, since according to the specification, this should never happen.&lt;/p&gt;
&lt;h2 id=&#34;three-ways-of-detecting-drive-strength&#34;&gt;Three ways of detecting drive strength&lt;/h2&gt;
&lt;p&gt;As part of this investigation I came up with two new ways for SystemVerilog code to detect or observe the strength of a net&amp;rsquo;s driver, in addition to the initial format string based solution from Stack Overflow that kicked all this off. For all three of these approaches I will be using them to implement a generic strength detection module, that accepts the target net to detect the strength of as an inout port, and outputs a value from 0-4 indicating the drive strength of the net.&lt;/p&gt;
&lt;p&gt;The idea being if you wanted to write an assertion to check that an I&lt;sup&gt;2&lt;/sup&gt;C bus was never driven high with a strong driver, you could use the module to get the drive strength, and then put a simple assertion on that value, as shown below:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;wire&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scl_strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;strength_detect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scl_strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;property&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;not&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scl_strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;the-string-formatting-approach-google-me-up-some-code&#34;&gt;The String Formatting Approach: &lt;em&gt;Google me up some code&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;As already mentioned, if you start looking into how to detect the drive strength of a net in SystemVerilog, all the recommendations I could find on the first page of Google results suggested to use the &lt;code&gt;$sformatf()&lt;/code&gt; function with the &lt;code&gt;&amp;quot;%v&amp;quot;&lt;/code&gt; format string, which creates a string representation of the net value, including the drive strength. For example a net being driven to a value of 1 with a strong driver, when passed to the &lt;code&gt;&amp;quot;%v&amp;quot;&lt;/code&gt; format string results in the string &amp;ldquo;St1&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Generalizing this approach into a module might look something like the following code. Every time the &lt;code&gt;inout&lt;/code&gt; signal &lt;code&gt;sig&lt;/code&gt; changes, we format the value of &lt;code&gt;sig&lt;/code&gt; to a string, and then do a bunch of string comparisons to figure out the drive strength of the net.&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;str_strength_detect&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;output&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;logic&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stren_str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;stren_str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;$sformatf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stren_str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s&#34;&gt;&amp;#34;HiZ&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s&#34;&gt;&amp;#34;We0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;We1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;WeZ&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s&#34;&gt;&amp;#34;Pu0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Pu1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;PuZ&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s&#34;&gt;&amp;#34;St0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;St1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;StZ&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s&#34;&gt;&amp;#34;Su0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Su1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;SuZ&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;endcase&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endmodule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;str_strength_detect.sv&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Now, this does work, but I had performance concerns. SystemVerilog is good at some things, but string manipulation is not one of it&amp;rsquo;s strong suits, and I have seen simulations crawl to a halt because of particularly large or complex string formatting and manipulation tasks. We will discuss the performance of this later, but suffice it to say, when I saw this as the recommended approach to detect the strength of a net&amp;rsquo;s driver, I couldn&amp;rsquo;t help but think: There has to be a better way!&lt;/p&gt;
&lt;h3 id=&#34;sense-net-approach-using-the-simulator-against-itself&#34;&gt;&amp;ldquo;Sense Net&amp;rdquo; Approach: &lt;em&gt;Using the simulator against itself&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;After re-reading most of the LRM a few times, I eventually convinced myself that within the confines of the SystemVerilog language, there is &lt;em&gt;no&lt;/em&gt; syntax or API that returns an integer value with a net&amp;rsquo;s drive strength. However, while we can&amp;rsquo;t directly query the drive strength, we &lt;em&gt;can&lt;/em&gt; observe the effect that the strength of a driver has on net value resolution! The basic idea is to connect drivers with various strengths and opposite value to the net which we are measuring the strength of, and observe if the net value changes. The simulator&amp;rsquo;s drive strength resolution will tell us if strength of the net is stronger, equal, or weaker than the applied driver based on if the value changes, goes to &lt;code&gt;&#39;x&lt;/code&gt;, or remains the same.&lt;/p&gt;
&lt;p&gt;The trick with this, of course, is we need to do this without actually ever changing the value of the net, because that would change the result of the simulation. It turns out we can get around this by using the &lt;code&gt;cmos&lt;/code&gt; gate primitive, which is a unidirectional model element which (when enabled) will drive it&amp;rsquo;s output net with the same strength and logical value as it&amp;rsquo;s input net. This allows us to essentially create a &amp;ldquo;duplicate&amp;rdquo; net with the same driver values (logical and strength) as that of our target net, but which we can add additional drivers to without effect the real target net.&lt;/p&gt;
&lt;p&gt;After some optimization, I ended up up with with the following module with the same ports as the &lt;code&gt;str_strength_detect&lt;/code&gt; module above, but doesn&amp;rsquo;t use any strings:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sense_net_strength_detect&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;output&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wire&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strong_sense&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wire&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pull_sense&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;wire&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weak_sense&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;strong1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;strong0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strong_sense&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;pull1&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;pull0&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pull_sense&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;weak1&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;weak0&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weak_sense&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;cmos&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strong_gate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strong_sense&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;cmos&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pull_gate&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pull_sense&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;cmos&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weak_gate&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;weak_sense&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;assign&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pull_sense&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strong_sense&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;weak_sense&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pull_sense&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endmodule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;sense_net_strength_detect.sv&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Now, while this solution is almost perfect, it has one limitation: It cannot observe the difference between &lt;code&gt;supply&lt;/code&gt; and &lt;code&gt;strong&lt;/code&gt; strength values. The reason for this is that the &lt;code&gt;cmos&lt;/code&gt; primitive has one special case where the strength value is not passed through, if the input drive strength is &lt;code&gt;supply&lt;/code&gt; the output drive strength is only &lt;code&gt;strong&lt;/code&gt;. There&amp;rsquo;s a lot of cases where this limitation doesn&amp;rsquo;t matter (including the problem that got this whole thing started), and in those cases I actually really liked this solution because it&amp;rsquo;s simple and pure SystemVerilog. However, I wanted to know if there was a way around this limitation&amp;hellip;&lt;/p&gt;
&lt;h3 id=&#34;dpi-c-api-method-going-even-deeper&#34;&gt;DPI-C API Method: &lt;em&gt;Going even deeper&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;Now, as already mentioned, I believe SystemVerilog has no way to directly query the strength of a driver, however, what it does have is a well defined C interface API specification. The &amp;ldquo;Direct Programming Interface&amp;rdquo; chapters of the SystemVerilog specification defines a C API that in theory allows C code to directly interact with any SystemVerilog code running in any (complaint) simulator. One thing this API provides is a method to query the value of a net, including strength value information, using the &lt;code&gt;vpi_get_value()&lt;/code&gt; function. Since you cannot call the DPI C directly from SystemVerilog (even though you can call other linked C functions), this approach was initially less interesting to me because I would need to add yet another language to my code stack, but I was curious how this solution would look.&lt;/p&gt;
&lt;p&gt;While the SystemVerilog specification may define the DPI, it&amp;rsquo;s far from clear how the API is intended to be used. After more experimentation than I would like to admit, I ended up with the following solution: Define two C helper methods that my SystemVerilog code can call, one to get a &lt;code&gt;chandle&lt;/code&gt; (pointer) to the net of interest, and one to return an integer representation of the net&amp;rsquo;s drive strength from the DPI call:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;vpiHandle&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;get_handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;vpi_handle_by_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;get_strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vpiHandle&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;s_vpi_value&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vpiStrengthVal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;vpi_get_value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;s0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;vpiSupplyDrive&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;vpiStrongDrive&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;vpiPullDrive&lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;vpiWeakDrive&lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;vpiHiZ&lt;/span&gt;         &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;             &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;dpi_strength_detect.c&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

Note that the &lt;code&gt;vpi_get_value()&lt;/code&gt; function actually returns two strength values for a net: The strongest driver on the net driving a 1 value, and the strongest value on the net driving a 0 value. To get the actual drive strength that the simulator will resolve the net to, we need to return the larger strength of &lt;code&gt;s1&lt;/code&gt; and &lt;code&gt;s0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Using these two helper C methods, we can make a simple module that calls the &lt;code&gt;get_strength()&lt;/code&gt; C function every time the net value changes as shown below:&lt;/p&gt;
&lt;figure class=&#34;no-photoswipe&#34;&gt;
  &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-systemverilog&#34; data-lang=&#34;systemverilog&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dpi_strength_detect&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;output&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;logic&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;DPI-C&amp;#34;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_handle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;chandle&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;DPI-C&amp;#34;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;chandle&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;chandle&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;initial&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;$sformatf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%m.sig&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;always&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;@(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;strength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_strength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;endmodule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
  
    &lt;figcaption&gt;&lt;span&gt;dpi_strength_detect.sv&lt;/span&gt;&lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;This solution works as intended, and can even differentiate between &lt;code&gt;supply&lt;/code&gt; and &lt;code&gt;strong&lt;/code&gt; drivers, but does have the complexity downside of needing to deal with and compile in C code. Most simulators provide relatively simple ways of compiling in DPI-C code, but it&amp;rsquo;s not as clean of a solution as I would have liked. If I could find a way of directly calling the DPI-C API functions from SystemVerilog code, this solution would have been more appealing.&lt;/p&gt;
&lt;h2 id=&#34;performance&#34;&gt;Performance&lt;/h2&gt;
&lt;p&gt;I started digging into this issue because I had a hunch that the string formatting approach would have too much overhead, so does this really matter? Is there really any benefit to avoiding strings?&lt;/p&gt;
&lt;p&gt;I built a very simple test bench which would cycle through all possible drive strengths with both 0 and 1 logical values 5 million times. Without a strength detection module in the model, only the multi-strength driver use to generate the signal, 5 million iterations took 14 seconds to simulate with the Xcellium Cadence simulator. The table below shows the simulation time when one strength detection module is added to the simulation for each approach I have discussed.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;No Strength Detection&lt;/th&gt;
          &lt;th&gt;Format String Method&lt;/th&gt;
          &lt;th&gt;Sense Net Method&lt;/th&gt;
          &lt;th&gt;DPI-C Method&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Simulation Duration&lt;/td&gt;
          &lt;td&gt;14 Seconds&lt;/td&gt;
          &lt;td&gt;44 Seconds&lt;/td&gt;
          &lt;td&gt;25.6 Seconds&lt;/td&gt;
          &lt;td&gt;32 Seconds&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Simulation Throughput&lt;/td&gt;
          &lt;td&gt;8.571 ms/s&lt;/td&gt;
          &lt;td&gt;2.737 ms/s&lt;/td&gt;
          &lt;td&gt;4.675 ms/s&lt;/td&gt;
          &lt;td&gt;3.75 ms/s&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Relative Slowdown&lt;/td&gt;
          &lt;td&gt;-&lt;/td&gt;
          &lt;td&gt;3.14x&lt;/td&gt;
          &lt;td&gt;1.83x&lt;/td&gt;
          &lt;td&gt;2.28x&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The data does indeed prove out my initial expectation that string manipulation is slow. However, I was quite surprised to see that the DPI-C method was slower than my initial SystemVerilog only approach. I would have thought that the additional nets and value resolution work the simulator would have to do for the sense net approach would have out-weighed any overhead in calling out to a C library.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Overall this was an interesting deep dive into a little explored part of the SystemVerilog language. If you can get away with not needing to differentiate &lt;code&gt;strong&lt;/code&gt; from &lt;code&gt;supply&lt;/code&gt; strength values, then I would definitely recommend the &amp;ldquo;sense net&amp;rdquo; detection approach as it&amp;rsquo;s not only the fastest, but also pure SystemVerilog. If you really need to detect &lt;code&gt;supply&lt;/code&gt; strengths and the addition of two DPI-C functions isn&amp;rsquo;t problematic, then the DPI-C approach is a good choice. Either way, is better than the commonly recommended approach of using formatting the net value as a string.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>About Me</title>
      <link>https://www.prbs23.com/blog/about/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <author>prbs23@prbs23.com (prbs23)</author>
      <guid>https://www.prbs23.com/blog/about/</guid>
      <description>&lt;p&gt;I am a full time ASIC verification engineer, and part time electronics and software tinkerer. This blog is a place for me to write about pretty much any topic that piques my interest. Usually this ends up being a deep dive into a niche topic related to my work in ASIC design. However, I will be trying to post some content related to my non-professional software, firmware, and hardware projects. My goal here is to be informative, and I hope that someone learns something from my mistakes and the rat-holes I go down.&lt;/p&gt;
&lt;h4 id=&#34;what-is-prbs23&#34;&gt;What is PRBS23:&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Pseudorandom_binary_sequence&#34;&gt;Pseudorandom Binary Sequences&lt;/a&gt; are deterministic bit sequences with random-like statistical distributions, commonly generated by &lt;a href=&#34;https://en.wikipedia.org/wiki/Linear-feedback_shift_register&#34;&gt;linear-feedback shift registers&lt;/a&gt; (LFSR). PRBS patterns are useful for many data integrity validation techniques, because they are usually random enough to be used in place of arbitrary data, but the whole pattern can be recreated from a single short seed. There are many different possible PRBS sequences, typically defined by length and polynomial representation, but one commonly used in high speed SERDES like PCIe, Ethernet, etc. is PRBS23. It is 2&lt;sup&gt;23&lt;/sup&gt; bit (~1 MBit) long sequence generated from the following polynomial: x&lt;sup&gt;23&lt;/sup&gt; + x&lt;sup&gt;28&lt;/sup&gt; + 1, and can be generated using the following LFSR circuit:


&lt;div class=&#34;box &#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34;&gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://www.prbs23.com/blog/about/prbs23_lfsr.svg&#34; alt=&#34;Fibonacci LFSR implementation of PRBS23 Generator&#34; /&gt;
      &lt;a href=&#34;https://www.prbs23.com/blog/about/prbs23_lfsr.svg&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
    &lt;/div&gt;
      &lt;figcaption&gt;
          &lt;p&gt;
            Fibonacci LFSR implementation of PRBS23 Generator
            
          &lt;/p&gt;
      &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;As far as it&amp;rsquo;s relation to this blog, the name is just as random as a PRBS23. Many things here are related, but not in an obvious way&amp;hellip;&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>

