Java 2 Ada - Tag Ethernet2016-12-04T23:01:00+00:00Stephane Carrezurn:md5:d12e23c53b2436d6becce3d51ddbdf38AWASimple UDP Echo Server on STM32F746urn:md5:7aad616c93da6cc270743613ab55c89a2016-12-04T23:01:00+00:002016-12-04T23:01:00+00:00Stephane CarrezSTM32EthernetAdaUDPDHCP
<div class="post-text"><h3>Overview</h3><p>The Echo server listens to the UDP port 7 on the Ethernet network and it sends back the received packet to the sender: this is the <a href="https://tools.ietf.org/html/rfc862">RFC 862 Echo protocol</a>. Our application follows that RFC but it also maintains a list of the last 10 messages that have been received. The list is then displayed on the STM32 display so that we get a visual feedback of the received messages.</p><p>The Echo server uses the DHCP client to get and IPv4 address and the default gateway. We will see how that DHCP client is integrated in the application.</p><p>The application has two tasks. The main task loops to manage the refresh of the STM32 display and also to perform some network housekeeping such as the DHCP client management and ARP table management. The second task is responsible for waiting Ethernet packets, analyzing them to handle ARP, ICMP and UDP packets.</p><p>Through this article, you will see:</p><ol><li>How the STM32 board and network stack are initialized,</li><li>How the board gets an IPv4 address using DHCP,</li><li>How to implement the UDP echo server,</li><li>How to build and test the echo server.</li></ol><h3>Initialization</h3><h5>STM32 Board Initialization</h5><p>First of all, the STM32 board must be initialized. There is no random generator available in the <a href="https://en.wikipedia.org/wiki/Ravenscar_profile">Ada Ravenscar profile</a> and we need one for the DHCP protocol for the XID generation. The STM32 provides a hardware random generator that we are going to use. The <code>Initialize_RNG</code> must be called once during the startup and before any network operation is called.</p><p>We will use the display to list the messages that we have received. The <code>Display</code> instance must be initialized and the layer configured.</p><pre><code class="lang-ada">with HAL.Bitmap;
with STM32.RNG.Interrupts;
with STM32.Board;
...
STM32.RNG.Interrupts.Initialize_RNG;
STM32.Board.Display.Initialize;
STM32.Board.Display.Initialize_Layer (1, HAL.Bitmap.ARGB_1555);
</code></pre><h5>Network stack initialization</h5><p>The network stack will need some memory to receive and send network packets. As described in <a href="http://blog.vacs.fr/vacs/blogs/post.html?post=2016/09/29/Using-the-Ada-Embedded-Network-STM32-Ethernet-Driver">Using the Ada Embedded Network STM32 Ethernet Driver</a>, we allocate the memory by using the <code>SDRAM.Reserve</code> function and the <code>Add_Region</code> procedure to configure the network buffers that will be available.</p><p>An instance of the STM32 Ethernet driver must be declared in a package. The instance must be <code>aliased</code> because the network stack will need to get an access to it.</p><pre><code class="lang-ada">with Interfaces;
with Net.Buffers;
with Net.Interfaces.STM32;
with STM32.SDRAM;
...
NET_BUFFER_SIZE : constant Interfaces.Unsigned_32 := Net.Buffers.NET_ALLOC_SIZE * 256;
Ifnet : aliased Net.Interfaces.STM32.STM32_Ifnet;
</code></pre><p>The Ethernet driver is initialized by calling the <code>Initialize</code> procedure. By doing so, the Ethernet receive and transmit rings are configured and we are ready to receive and transmit packets. On its side the Ethernet driver will also reserve some memory by using the <code>Reserve</code> and <code>Add_Region</code> operations. The buffers allocated will be used for the Ethernet receive ring.</p><pre><code class="lang-ada"> Net.Buffers.Add_Region (STM32.SDRAM.Reserve (Amount => NET_BUFFER_SIZE), NET_BUFFER_SIZE);
Ifnet.Initialize;
</code></pre><p>The Ethernet driver configures the MII transceiver and enables interrupts for the receive and transmit rings.</p><h3>Getting the IPv4 address with DHCP</h3><p>At this stage, the network stack is almost ready but it does not have any IPv4 address. We are going to use the DHCP protocol to automatically get an IPv4 address, get the default gateway and other network configuration such as the DNS server. The DHCP client uses a UDP socket on port 68 to send and receive DHCP messages. Such DHCP client is provided by the <code>Net.DHCP</code> package and we need to declare an instance of it. The DHCP client is based on the UDP socket support that we are going to use for the echo server. The DHCP client instance must be declared <code>aliased</code> because the UDP socket layer need to get an access to it to propagate the DHCP packets that are received.</p><pre><code class="lang-ada">with Net.DHCP;
...
Dhcp : aliased Net.DHCP.Client;
</code></pre><p>The DHCP client instance must be initialized and the Ethernet driver interface must be passed as parameter to correctly configure and bind the UDP socket. After the <code>Initialize</code> procedure is called, the DHCP state machine is ready to enter into action. We don't have an IPv4 address after the procedure returns.</p><pre><code class="lang-ada"> Dhcp.Initialize (Ifnet'Access);
</code></pre><p>The DHCP client is using an asynchronous implementation to maintain the client state according to <a href="https://tools.ietf.org/html/rfc2131">RFC 2131</a>. For this it has two important operations that are called by tasks in different contexts. First the <code>Process</code> procedure is responsible for sending requests to the DHCP server and to manage the timeouts used for the retransmissions, renewal and lease expiration. The <code>Process</code> procedure sends the <code>DHCPDISCOVER</code> and <code>DHCPREQUEST</code> messages. On the other hand, the <code>Receive</code> procedure is called by the network stack to handle the DHCP packets sent by the DHCP server. The <code>Receive</code> procedure gets the <code>DHCPOFFER</code> and <code>DHCPACK</code> messages.</p><p>Getting an IPv4 address with the DHCP protocol can take some time and must be repeated continuously due to the DHCP lease expiration. This is why the DHCP client must not be stopped and should continue forever.</p><p>Refer to the <a href="https://github.com/stcarrez/ada-enet/wiki/Net_DHCP">DHCP</a> documentation to learn more about this process.</p><h3>UDP Echo Server</h3><h5>Logger protected type</h5><p>The echo server will record the message that are received. The message is inserted in the list by the receive task and it is read by the main task. We use the an <a href="https://en.wikibooks.org/wiki/Ada_Programming/Tasking#Protected_types">Ada protected type</a> to protect the list from concurrent accesses.</p><p>Each message is represented by the <code>Message</code> record which has an identifier that is unique and incremented each time a message is received. To avoid dynamic memory allocation the list of message is fixed and is represented by the <code>Message_List</code> array. The list itself is managed by the <code>Logger</code> protected type.</p><pre><code class="lang-ada">type Message is record
Id : Natural := 0;
Content : String (1 .. 80) := (others => ' ');
end record;
type Message_List is array (1 .. 10) of Message;
protected type Logger is
procedure Echo (Content : in Message);
function Get return Message_List;
private
Id : Natural := 0;
List : Message_List;
end Logger;
</code></pre><p>The <code>Logger</code> <a href="https://en.wikibooks.org/wiki/Ada_Programming/Tasking#Protected_types">protected type</a> provides the <code>Echo</code> procedure to insert a message to the list and the <code>Get</code> function to retrieve the list of messages.</p><h5>Server Declaration</h5><p>The UDP Echo Server uses the UDP socket support provided by the <code>Net.Sockets.UDP</code> package. The UDP package defines the <code>Socket</code> abstract type which represents the UDP endpoint. The <code>Socket</code> type is abstract because it defines the <code>Receive</code> procedure that must be implemented. The <code>Receive</code> procedure will be called by the network stack when a UDP packet for the socket is received.</p><p>The declaration of our echo server is the following:</p><pre><code class="lang-ada">with Net.Buffers;
with Net.Sockets;
...
type Echo_Server is new Net.Sockets.UDP.Socket with record
Count : Natural := 0;
Messages : Logger;
end record;
</code></pre><p>It holds a counter of message as well as the messages in the <code>Logger</code> protected type.</p><p>The echo server must implement the <code>Receive</code> procedure:</p><pre><code class="lang-ada">overriding
procedure Receive (Endpoint : in out Echo_Server;
From : in Net.Sockets.Sockaddr_In;
Packet : in out Net.Buffers.Buffer_Type);
</code></pre><p>The network stack will call the <code>Receive</code> procedure each time a UDP packet for the socket is received. The <code>From</code> parameter will contain the IPv4 address and UDP port of the client that sent the UDP packet. The <code>Packet</code> parameter contains the received UDP packet.</p><h5>Server Implementation</h5><p>Implementing the server is very easy because we only have to implement the <code>Receive</code> procedure (we will leave the <code>Logger</code> protected type implementation as an exercise to the reader).</p><p>First we use the <code>Get_Data_Size</code> function to get the size of our packet. The function is able to return different sizes to take into account one or several protocol headers. We want to know the size of our UDP packet, excluding the UDP header. We tell <code>Get_Data_Size</code> we want to get the <code>UDP_PACKET</code> size. This size represents the size of the echo message sent by the client.</p><pre><code class="lang-ada"> Msg : Message;
Size : constant Net.Uint16 := Packet.Get_Data_Size (Net.Buffers.UDP_PACKET);
Len : constant Natural
:= (if Size > Msg.Content'Length then Msg.Content'Length else Natural (Size));
</code></pre><p>Having the size we truncate it so that we get a string that fits in our message. We then use the <code>Get_String</code> procedure to retrieve the echo message in a string. This procedure gets from the packet a number of characters that corresponds to the string length passed as parameter.</p><pre><code class="lang-ada"> Packet.Get_String (Msg.Content (1 .. Len));
</code></pre><p>The <code>Buffer_Type</code> provides other <code>Get</code> operations to extract data from the packet. It maintains a position in the buffer that tells the <code>Get</code> operation the location to read in the packet and each <code>Get</code> updates the position according to what was actually read. There are also several <code>Put</code> operations intended to be used to write and build the packet before sending it. We are not going to use them because the echo server has to return the original packet as is. Instead, we have to tell what is the size of the packet that we are going to send. This is done by the <code>Set_Data_Size</code> procedure:</p><pre><code class="lang-ada"> Packet.Set_Data_Size (Size);
</code></pre><p>Here we want to give the orignal size so that we return the full packet.</p><p>Now we can use the <code>Send</code> procedure to send the packet back to the client. We use the client IPv4 address and UDP port represented by <code>From</code> as the destination address. The <code>Send</code> procedure returns a status that tells whether the packet was successfully sent or queued.</p><pre><code class="lang-ada">Status : Net.Error_Code;
...
Endpoint.Send (To => From, Packet => Packet, Status => Status);
</code></pre><h5>Server Initialization</h5><p>Now that the <code>Echo_Server</code> type is implemented, we have to make a global instance of it and bind it to the UDP port 7 that corresponds to the UDP echo protocol. The port number must be defined in network byte order (as in Unix Socket API) and this is why it is converted using the <code>To_Network</code> function. We don't know our IPv4 address and by using 0 we tell the UDP stack to use the IPv4 address that is configured on the Ethernet interface.</p><pre><code class="lang-ada">Server : aliased Echo_Server;
...
Server.Bind (Ifnet'Access, (Port => Net.Headers.To_Network (7),
Addr => (others => 0)));
</code></pre><h3>Main loop and receive task</h3><p>As explained in the overview, we need several tasks to handle the display, network housekeeping and reception of Ethernet packets. To make it simple the display, ARP table management and DHCP client management will be handled by the main task. The reception of Ethernet packet will be handled by a second task. It is possible to use a specific task for the ARP management and another one for the DHCP but there is no real benefit in doing so for our simple echo server.</p><p>The main loop repeats calls to the ARP <code>Timeout</code> procedure and the DHCP <code>Process</code> procedure. The <code>Process</code> procedure returns a delay that we are supposed to wait but we are not going to use it for this example. The main loop simply looks as follows:</p><pre><code class="lang-ada">Dhcp_Timeout : Ada.Real_Time.Time_Span;
...
loop
Net.Protos.Arp.Timeout (Ifnet);
Dhcp.Process (Dhcp_Timeout);
...
delay until Ada.Real_Time.Clock + Ada.Real_Time.Milliseconds (500);
end loop;
</code></pre><p>The receive task was described in the previous article <a href="http://blog.vacs.fr/vacs/blogs/post.html?post=2016/09/29/Using-the-Ada-Embedded-Network-STM32-Ethernet-Driver">Using the Ada Embedded Network STM32 Ethernet Driver</a>. The task is declared at package level as follows:</p><pre><code class="lang-ada"> task Controller with
Storage_Size => (16 * 1024),
Priority => System.Default_Priority;
</code></pre><p>And the implementation loops to receive packets from the Ethernet driver and calls either the ARP <code>Receive</code> procedure, the ICMP <code>Receive</code> procedure or the UDP <code>Input</code> procedure. The complete implementation can be found in the <a href="https://github.com/stcarrez/ada-enet/blob/master/demos/echo/receive.adb">receive.adb</a> file.</p><h3>Building and testing the server</h3><p>To build the UDP echo server and have it run on the STM32 board is a three step process:</p><ol><li>First, you will use the <code>arm-eabi-gnatmake</code> command with the</li></ol><p><code>echo</code> GNAT project. After successful build, you will get the <code>echo</code> ELF binary image in obj/stm32f746disco/echo.</p><ol><li>Then, the ELF image must be converted to binary by extracting the ELF sections that must be put on</li></ol><p>the flash. This is done by running the <code>arm-eabi-objcopy</code> command.</p><ol><li>Finaly, the binary image produced by <code>arm-eabi-objcopy</code> must be put on the flash using the <code>st-util</code> utility. You may have to press the reset button on the board so that the <code>st-util</code> is able to take control of the board; then release the reset button to let <code>st-util</code> the flas</li></ol></div> Ethernet Traffic Monitor on a STM32F746 urn:md5:a7a9cac7cf353401f9cd074c244581ca2016-09-30T19:19:00+00:002016-09-30T19:19:00+00:00Stephane CarrezSTM32EthernetAda
<div class="post-text"><p>The application is completely written in Ada 2012 with:</p><ul><li>The <a href="https://github.com/AdaCore/embedded-runtimes">GNAT ARM embedded runtimes</a> is the Ada 2012 ravenscar runtime that provides support for interrupts, tasks, protected objects and other Ada features.</li><li>The <a href="https://github.com/stcarrez/ada-enet">Ada Embedded Network Stack</a> is the small network library that provides network buffer management with an Ethernet driver for the <a href="http://www.st.com/content/st_com/en/products/microcontrollers/stm32-32-bit-arm-cortex-mcus/stm32f7-series/stm32f7x6/stm32f746ng.html">STM32F746</a> board.</li><li>The <a href="https://github.com/stcarrez/etherscope">EtherScope</a> application which performs the analysis and displays the information.</li></ul><h3>Traffic Analyzer</h3><p>The traffic analyzer inspects the received packet and tries to find interesting information about it. The analyzer is able to recognize several protocols. New protocols may easily be added in the future. The first version supports:</p><ul><li>Analysis of Ethernet frame to identify the devices that are part of the network with their associated IP address and network utilization.</li><li>Analysis of IPv4 packet to identify the main IPv4 protocols including ICMP, IGMP, UDP and TCP.</li><li>Analysis of IGMP with discovery of subscribed multicast groups and monitoring of the associated UDP traffic.</li><li>Analysis of TCP with the identification of some well known protocols such as http, https, ssh and others.</li></ul><p>Each analyser collects the information and is able to report the number of bytes, number of packets and network bandwidth utilization. Some information is also collected in different graph tables so that we can provide some visual graph about the network bandwidth usage.</p><h3>Network setup to use EtherScope</h3><p>To use EtherScope, you will connect the <a href="http://www.st.com/content/st_com/en/products/microcontrollers/stm32-32-bit-arm-cortex-mcus/stm32f7-series/stm32f7x6/stm32f746ng.html">STM32F746</a> board to an Ethernet switch that you insert or have on your network. By default, the switch will isolate the different ports (as opposite to a hub) and unicast traffic is directed only to the concerned port. In other words, EtherScope will only see broadcast and multi-cast traffic. In order to see the interesting traffic (TCP for example), you will need to configure the switch to do port mirroring. By doing so, you tell the switch to mirror all the traffic of a selected port to the mirror port. You will connect EtherScope to that mirror port and it will see all the mirrored traffic.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/net-monitoring.png" longdesc="net-monitoring.png" alt="net-monitoring.png"></img></div></div></p><h3>EtherScope in action</h3><p>The following 4 minutes video shows the EtherScope in action.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/zEtA-S5jvfY" frameborder="0" allowfullscreen></iframe>
<h3>EtherScope Internal Design</h3><p>The EtherScope has several functional layers:</p><ul><li>The display layer manages the user interaction through the touch panel. It displays the information that was analyzed and manages the refresh of the display with its graphs.</li><li>The packet analyzer inspects the traffic.</li><li>The Ethernet network driver configures the Ethernet receive ring, handles interrupts and manages the reception of packets (the transmission part is not used for this project).</li><li>The Ada Drivers Library provides a number of utility packages from their samples to manage the display and draw text as well as some geometric forms.</li><li>The GNAT ARM ravenscar runtime provides low level support for the STM32 board configuration, interrupt and task management. It also brings a number of important drivers to control the touch panel, the button, SPI, I2C and other hardware components.</li></ul><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/etheroscope-design.png" longdesc="etheroscope-design.png" alt="etheroscope-design.png"></img></div></div></p><p>The <code>EtherScope.Receiver</code> is the package that has the receiver task that loops to receive a packet from the Ethernet driver and analyzer it through the analyzer. Because the result of the analysis is shared between two tasks, it is protected by the <code>DB</code> protected object.</p><p>The <code>EtherScope.Display</code> provides several operations to display the analysis in various forms depending on the user selection. Its operations are called repeatedly by the etherscope main loop. The display operation fetch the analysis from the <code>DB</code> protected object and format the result through the <code>UI.Graphs</code> or text presentations.</p><h3>Conclusion</h3><p>You can get the EtherScope sources at: <a href="https://github.com/stcarrez/etherscope">https://github.com/stcarrez/etherscope</a> Feel free to fork EtherScope, hack it and add new protocol analyzers.</p><p>The following analyzers could be implemented in the future:</p><ul><li>A DNS analyzer that shows which DNS requests are made,</li><li>A DHCP analyzer to track and show IP allocation,</li><li>A FTP analyzer to reconcile the ftp-data stream to the ftp flow,</li><li>An IPv6 analyzer</li></ul></div> Using the Ada Embedded Network STM32 Ethernet Driverurn:md5:ae78a17d69f26023da00adcf471a68292016-09-29T19:19:00+00:002016-09-29T19:19:00+00:00Stephane CarrezAdaSTM32Ethernet
<div class="post-text"><p>In any network stack, the buffer management is key to obtain good performance. Let's see how it is modeled.</p><h3>Net.Buffers</h3><p>The <code>Net.Buffers</code> package provides support for network buffer management. A network buffer can hold a single packet frame so that it is limited to 1500 bytes of payload with 14 or 16 bytes for the Ethernet header. The network buffers are allocated by the Ethernet driver during the initialization to setup the Ethernet receive queue. The allocation of network buffers for the transmission is under the responsibility of the application.</p><p>Before receiving a packet, the application also has to allocate a network buffer. Upon successful reception of a packet by the <code>Receive</code> procedure, the allocated network buffer will be given to the Ethernet receive queue and the application will get back the received buffer. There is no memory copy.</p><p>The package defines two important types: <code>Buffer_Type</code> and <code>Buffer_List</code>. These two types are limited types to forbid copies and force a strict design to applications. The <code>Buffer_Type</code> describes the packet frame and it provides various operations to access the buffer. The <code>Buffer_List</code> defines a list of buffers.</p><p>The network buffers are kept within a single linked list managed by a protected object. Because interrupt handlers can release a buffer, that protected object has the priority <code>System.Max_Interrupt_Priority</code>. The protected operations are very basic and are in O(1) complexity so that their execution is bounded in time whatever the arguments.</p><p>Before anything, the network buffers have to be allocated. The application can do this by reserving some memory region (using <code>STM32.SDRAM.Reserve</code>) and adding the region with the <code>Add_Region</code> procedure. The region must be a multiple of <code>NET_ALLOC_SIZE</code> constant. To allocate 32 buffers, you can do the following:</p><pre><code class="lang-ada"> NET_BUFFER_SIZE : constant Interfaces.Unsigned_32 := Net.Buffers.NET_ALLOC_SIZE * 32;
...
Net.Buffers.Add_Region (STM32.SDRAM.Reserve (Amount => NET_BUFFER_SIZE), NET_BUFFER_SIZE);
</code></pre><p>An application will allocate a buffer by using the <code>Allocate</code> operation and this is as easy as:</p><pre><code class="lang-ada"> Packet : Net.Buffers.Buffer_Type;
...
Net.Buffers.Allocate (Packet);
</code></pre><p>What happens if there is no available buffer? No exception is raised because the networks stack is intended to be used in embedded systems where exceptions are not available. You have to check if the allocation succeeded by using the <code>Is_Null</code> function:</p><pre><code> if Packet.Is_Null then
null; -- Oops
end if;
</code></pre><h3>Net.Interfaces</h3><p>The <code>Net.Interfaces</code> package represents the low level network driver that is capable of sending and receiving packets. The package defines the <code>Ifnet_Type</code> abstract type which defines the three important operations:</p><ul><li><code>Initialize</code> to configure and setup the network interface,</li><li><code>Send</code> to send a packet on the network.</li><li><code>Receive</code> to wait for a packet and get it from the network.</li></ul><h4>STM32 Ethernet Driver</h4><p>The STM32 Ethernet driver implements the three important operations required by the <code>Ifnet_Type</code> abstraction. The <code>Initialize</code> procedure performs the STM32 Ethernet initialization, configures the receive and transmit rings and setup to accept interrupts. This operation must be called prior to any other.</p><h5>Sending a packet</h5><p>The STM32 Ethernet driver has a transmit queue to manage the Ethernet hardware transmit ring and send packets over the network. The transmit queue is a protected object so that concurrent accesses between application task and the Ethernet interrupt are safe. To transmit a packet, the driver adds the packet to the next available transmit descriptor. The packet buffer ownership is transferred to the transmit ring so that there is no memory copy. Once the packet is queued, the application has lost the buffer ownership. The buffer being owned by the DMA, it will be released by the transmit interrupt, as soon as the packet is sent (<a href="3">3</a>).</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/ada-driver-send.png" longdesc="ada-driver-send.png" alt="ada-driver-send.png"></img></div></div></p><p>When the transmit queue is full, the application is blocked until a transmit descriptor becomes available.</p><h5>Receiving a packet</h5><p>The SMT32 Ethernet driver has a receive queue which is a second protected object, separate from the transmit queue. The receive queue is used by the Ethernet hardware to control the Ethernet receive ring and by the application to pick received packets. Each receive descriptor is assigned a packet buffer that is owned by default to the DMA. When a packet is available and the application calls the <code>Wait_Packet</code> operation, the packet buffer ownership is transferred to the application to avoid any memory copy. To avoid having a ring descriptor loosing its buffer, the application gives a new buffer that is used for the ring descriptor. This is why the application has first to allocate the buffer (<a href="1">1</a>), call the <code>Receive</code> operation (<a href="2">2</a>) to get back the packet in a new buffer and finally release the buffer when it has done with it (<a href="3">3</a>).</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/ada-driver-receive.png" longdesc="ada-driver-receive.png" alt="ada-driver-receive.png"></img></div></div></p><h4>Receive loop example</h4><p>Below is an example of a task that loops to receive Ethernet packets and process them. This is the main receiver task used by the <a href="https://github.com/stcarrez/etherscope">EtherScope</a> monitoring tool.</p><p>The Ifnet driver initialization is done in the main EtherScope task. We must not use the driver before it is full initialized. This is why the task starts to loop for the Ifnet driver to be ready.</p><pre><code class="lang-ada"> task body Controller is
use type Ada.Real_Time.Time;
Packet : Net.Buffers.Buffer_Type;
begin
while not Ifnet.Is_Ready loop
delay until Ada.Real_Time.Clock + Ada.Real_Time.Seconds (1);
end loop;
Net.Buffers.Allocate (Packet);
loop
Ifnet.Receive (Packet);
EtherScope.Analyzer.Base.Analyze (Packet);
end loop;
end Controller;
</code></pre><p>Then, we allocate a packet buffer and enter in the main loop to continuously receive a packet and do some processing. The careful reader will note that there is no buffer release. We don't need that because the <code>Receive</code> driver operation will pick our buffer for its ring and it will give us a buffer that holds the received packet. We will give him back that buffer at the next loop. In this application, the number of buffers needed by the buffer pool is the size of the Ethernet Rx ring plus one.</p><p>The complete source is available in <a href="https://github.com/stcarrez/etherscope/blob/master/src/etherscope-receiver.adb">etherscope-receiver.adb</a>.</p><p>Using this design and implementation, the EtherScope application has shown that it can sustain more than 95Mb of traffic for analysis. Quite nice for 216 Mhz ARM Cortex-M7!</p></div>