Skip to content

A project to demonstrate the BeagleBone Black sampling a pulse width modulator signal using the PRUSS

License

Notifications You must be signed in to change notification settings

ttreker/PRUSS-EndToEnd-PWM

Repository files navigation

PRUSS-EndToEnd-PWM Version 0.60
(C) Copyright 2016 by Christopher C. Mills:

This program is distributed under the terms of the GNU General Public
License v3 (GPLv3). A copy of this lisence is included with this archive.

THIS PROGRAM HAS VERY LIMITED TESTING - USE AT YOUR OWN RISK.

INTRODUCTION
--------------------------------------------------------------------------------
This is a demonstration program of the PRU Subsystem (PRUSS) on the BeagleBone
Black Rev C (BBB). It is inspired by an interest in the BBB, the PRUSS, and in
particular by Derek Molloy's excellent introduction to the BBB: "Exploring
BeagleBone." Hopefully, this project is a useful supplement to the examples
found in this book. As such a supplement, it assumes the following of the user:

   1) Introductory competence with coding and manipulating the BBB. This could
      possibly be through the excellent book "Exploring BeagleBone" by Derek
      Molloy (as was the case of myself) and its accompanying web site.
   2) Experience compiling and loading device tree overlays.
   3) Competence in the C language.
   4) Comfort with learning and developing in the PRUSS assembly language
      using the PASM assembler.
   5) Some Linux experience (the more the better, but the Molloy intro is
      adequate).
   6) An introductory understanding of the uio_pruss kernel module, including
      how to access its external memory buffer (DDR) parameters via the /sys
      filesystem. (See Molloy and his book's web page for his chapter 13.)
   7) Access to a PWM source device that can produce a 3.3V pulse. (More on this
      below)
   8) An interest in understanding and coding the PRUSS on the BBB.

USEFUL RESOURCES
--------------------------------------------------------------------------------

   - "Exploring BeagleBone" by Derek Molloy (and its accompanying web site)
   - Texas Instruments "AM335X Sitara Processors" (Rev. L)
   - Texas Instruments "AM335X PRU-ICSS Reference Guide" (Rev. A)
   - The am335x-pru-package on Github (source code and docs a great reference)
   - prudebug-bbb on Github (A PRUSS debugger authored by Steven Anderson and
     improved by myself, Christopher C. Mills-- improvements ongoing)

DESCRIPTION
--------------------------------------------------------------------------------
This is a demonstration of the PRUSS operating in the framework of an end-to-end
application. The intent is to go one step beyond a simple demonstation of the
PRUSS by using a TCP/IP client to download data via a server running on the
BeagleBone Black that also manages the PRUSS. It intentionally doesn't attempt
to do anything too fancy so as to remain reasonably easy to understand. The
PRUSS captures data from an input pin attached to a pulse width modulator (PWM)
source. A sample by the PRUSS consists of a pair of 32 bit words: one holding a
high count, and the other holding a low count of a single pulse.

IMPORTANT: To run the program, it is necessary to have access to a PWM
           generator of some sort, configured for a BBB *safe* input voltage
           (3.3V). There is a function generator kit called the "FG085 miniDDS
           Function Generator" available from multiple sources (needs to be
           built and has a maximum frequency for PWM of around 10KHz). However,
           another BBB, a microcontroller, or a user-built circuit could
           provide the source. These latter approaches could have the added
           advantage of running at a much higher frequency (10MHz or greater).

           This PWM source is assumed to exist for this code and further
           details about configuring this source is beyond the scope of the
           information in this demo.

There are a couple of useful things demonstrated in this project:

   - PRU-to-PRU interrupts
   - Use of the PRUSS Scratchpad register bank on the broadside bus to rapidly
     exchange data between PRUs, circumventing the need to use slow Shared
     Memory.

The use of PRU-to-PRU interrupts and the scratchpad are not documented in
Molloy. Both of these may be useful in extracting every ounce of speed out of
the PRUSS. This project is a good starting point to test PRUSS throughput.

Device Tree Overlay:

   The device tree overlay file, PWM-PRU-ETE.dts, used on this project is
   derived from the one Molloy uses in the Chapter 13 ADC example (on the
   web). It has two added outputs from that example; one was added to PRU 1
   for debugging its operation with a scope. However, only the one input on
   PRU 0 is used in this project. The other outputs were left available to
   be used for future expansion or for debugging with a scope.

   Also, note that HDMI needs to be disabled for this example. See Molloy
   for details on how to go about doing this. Check out his web site for
   updated details on this topic.

In addition to a Makefile, a PASM header file, and a device tree overlay and
its build script, this demonstration consists primarily of four modules:

   pruss-ete.c  (server and PRUSS manager)
   client-ete.c (client compilable on any system including the BBB)
   data-pru0.p  (PRU 0 code for PWM sample capturing)
   ddr-pru1.p   (PRU 1 code for dumping samples to DDR) 

There is also included a Windows client written under the .NET framework in C#.
This can be used in place of the client above:

   win-client-ete.cs (requires a platform running .NET)

The DDR buffer is setup by the uio_pruss kernel module. This module is loaded by
default upon BBB boot and this buffer (called External RAM by uio_pruss
nomenclature) should be of adequate size by default for this demo. Molloy
describes how to expand this on his Chapter 13 web page if that is desired.

The following describes the role in a bit more detail of each of the above
four modules:

pruss-ete:
   1) Determines DDR buffer size and calculates sample count based on this.
   2) Determines DDR buffer physical address.
   3) Configures the PRUSS.
   4) Writes the DDR buffer parameters into PRUSS Shared Memory.
   5) Starts the PRUSS (PRU 1, then PRU 0).
   6) Waits for PRUSS sampling to complete.

PRU 1:
   1) Waits for an interrupt from PRU 0 signalling that a
      pulse-width sample is ready in the scratchpad.
   2) When interrupt is received, transfers scratchpad
      data (the sample) to its register file.
   3) Writes the sample from its registers to the next location in the DDR
      buffer.
   4) Decrements the sample count and halts if 0, after triggering a host
      interrupt signalling completion.
   5) Otherwise, it goes back to (1) above and repeats.

PRU 0:
   1) Syncs itself to the first high-going pulse.
   2) Counts from 0 (count tick duration undetermined) until pulse goes
      low.
   3) Counts from 0 until pulse goes high.
   4) Passes counts into scratchpad bank.
   5) Triggers interrupt to PRU 1.
   6) Decrements the sample count and halts if 0.

pruss-ete (continues):
   7) Receives interrupt from PRU 1 signaling completion.
   8) Shuts down the PRUSS.
   9) Sets itself up as a TCP server and waits for a connection. The
      port was passed on the command line.
   10) Immediately upon connection established by the client (client-ete) it
       sends the DDR buffer size to the client.
   11) Waits for a "GO" string from the client.
   12) Upon receipt of the "GO" string, dumps the DDR buffer to the
       TCP stream.
   13) It then shuts down.

client-ete or win-client-ete:
   1) Connects to the server: Its port was passed on its command line.
   2) Immediately reads the stream for the sample data size.
   3) Upon receipt of the size, sends a "GO" string to the server.
   4) Reads the resulting stream of data from the server-- formatting it
      and outputting it to a file.
   5) It then shuts down.

Things to note about this operation:

   1) PRU 0 counts its samples by counting pairs of high/low transitions on its
      input pin. No input, no count and so the PRUSS operation never completes.
   2) Likewise, PRU 1 decrements its sample count by counting interrupts from
      PRU 0, so no input to PRU 0 means it will hang also, never interrupting
      the pruss-ete module, which will then also hang.
   3) The sample count used is derived from how big the DDR buffer is: the
      bigger the buffer, the greater the sample count:

         sample count = buffer size/8

   4) Beware: how long it takes to complete not only depends on the number of
      samples but the frequency of the PWM. If a slow frequency PWM is used the
      sampling could take a long time.
   5) The fastest PWM frequency is undetermined as of this writing, but in
      theory this code could support a pretty high frequency PWM (say 10Mhz or
      more). This module is designed to test this throughput. For initial
      testing, I used a 38 micro secs (~26KHz) pulse width. (As of this
      writing a test to determine highest throughput of this code is planned,
      but has not yet been performed.)
   6) A quirk of operation is that the client should *not* be started until
      the server has finished the PRUSS operation and has configured itself as
      a TCP server waiting on a port. The reason for this annoyance was to
      keep the handshaking between client and server to a minimum to keep
      the code reasonably easy to follow.

Practical Use:

   Although not intended as such by any stretch of the imagination, this code
   could actually be useful to gather stats on a PWM device. This could be
   useful, for instance, to determine the severity of jitter. You could:

   1) Run the server on a PWM source.
   2) Run the client on a Windows PC and connect to the server to dump the data.
   3) Analyze the data in a spreadsheet for variance or graph the deviations
      against time.

BUILD
--------------------------------------------------------------------------------

1) Compile and install the device tree overlay supplied (PWM-PRU-ETE) with the
   following command in the project directory:

      ./build-pwm-dts

   This will result in the file

      PWM-PRU-ETE-00A0.dtbo

   which can then be installed in the usual manner. Installation of this dto
   as well as the unloading of any competing dtos is beyond the scope of
   these instructions.

2) Be certain to test that this device tree overlay is installed and that the
   pins are configured as expected. (See Molloy for further description)
3) Run:

      make

   in the project directory to create both the client and server executables.
   Note: the client can be compiled on another system if desired. Its gcc
   parameters can be determined from the Makefile.

The make will produce the following two executables:

   pruss-ete (the server and PRUSS manager)
   client-ete (the downloading client)

It will also produce the PRU binaries:

   data-pru0.bin (PRU 0 PWM sampling code)
   ddr-pru1.bin  (PRU 1 DDR buffering code)

Note: This module must be linked to the prussdrv library which is installed
      on the BBB via the am335x_pru_package. Because of this, no action
      probably needs to be taken to ensure the project's Makefile runs. If
      there is a problem, load the package via apt-get. This package is also
      available for download on Github.


WINDOWS CLIENT BUILD
--------------------------------------------------------------------------------
If you prefer to run the client from a Windows PC, you can try out the C# .NET
Console Application provided. It has been compiled under Visual Studio 2010
Professional as well as Visual Studio Commumity 2015 using Target framework
3.5. or 4.5. 


INSTALLATION
--------------------------------------------------------------------------------
Other than the device tree overlay described above, no install is required

USAGE
--------------------------------------------------------------------------------
Note: The server (pruss-ete) must be run with root privileges.

1) Safely configure a PWM source and connect it to P9.28 (double check the pin
   configuration and source voltage if you care about your BBB!)
2) Run:

     pruss-ete <port number>

   where <port number> is any desired unused port (maybe arbitrarily 51717).
   The server will print a series of informational and status messages.
3) Wait for the PRUSS work to complete and the TCP server to start. This will
   be indicated by the message:

      Starting the server...

4) When the server has indicated that it has started, start the client with:

     client-ete <hostname or ip> <port number>

   where the port number is the same as for the server. If using the Windows
   client, it can be started similarly with:

     win-client-ete <hostname or ip> <port number>

5) The client and server should quickly complete and shutdown, again, after
   displaying a series of informational and status messages.
6) Check for the file

      tcpcap.dat

   in the client's startup folder.
    

TESTING
--------------------------------------------------------------------------------
Initial development was done using the FG085 miniDDS Function Generator. It was
run at a speed on the order of 10KHz.

The following is an account of a more robust test setup used to test the code
after development had been completed:

For the test set up a simple pulse generator, using a ChipKit MAX32
Microcontroller board and a mixture of C and assembly to exploit the high
speed I/O toggling capability of the Microchip PIC32MX795F512H (80MHz), was
used. This allowed generating pulses as short as 60 nanoSeconds. The limits of
the system were close to being tested. 

The table below shows the results of capturing pulse trains using a variety of
values:

               |             | Sample
               |  (Duty Cyc) | Counts
Pulse Duration | (Frequency) |(in Hex)
---------------------------------------
               |             |
(-ve) 3960 nS  |      (33%)  | 0000018C
(+ve) 1950 nS  |  (169 KHz)  | 000000C5 
               |             |
(-ve) 465 nS   |      (42%)  | 0000002E
(+ve) 335 nS   | (1.25 MHz)  | 0000001E 
               |             |
(-ve) 61.5 nS  |      (50%)  | 00000006
(+ve) 61.5 nS  |  (8.1 MHz)  | 00000003 


DISCUSSION
--------------------------------------------------------------------------------
The aim of this project was to keep the code from being obscure, while
demonstrating a "complete" system involving the PRUSS that may have a hint of
being practical. It is probably possible to finetune the PRUSS code to be
faster or more accurate. For instance, the high counts have a systematic error
that reduces their counts (likely by a fixed amount) as a result of all the
testing overhead, and data transfer. The BeagleLogic project claims operating
speeds of up to 100MHz, 2 orders of magnitude faster than the above results.

Addressing these issues to improve performance is likely to complicate the code
in a manner that would make it less digestable. Consequently, such
optimizations were avoided on this project. Instead, the principle idea has
been to provide a starting point for investigating these kinds of
optimizations. It is up to the user to explore further efficiency
possibilities. Some of these may include:

   -- Cycle counting blocks of code using the PRUSS resources supporting this.
      (See the PRUSS documentation for use of the cycle and stall count
       registers.)
   -- Exploring alternative algorithms.
   -- Using scratchpad PRU-to-PRU signalling rather than an interrupt to
      combine signalling and data transfer in one step via the single cycle
      broadside bus instructions (XIN, XOUT).

In addition, on the Linux/C side improvements in buffering could be achieved by
writing a custom kernel module which, among other things, could dramatically
improve buffer size. (Derek Molloy is in the process of completing a four part
series on the web for doing just this.)

CONTRIBUTORS
--------------------------------------------------------------------------------
Christopher C. Mills ---- Linux C and PRUSS  source modules; design
Ram Radhakrishnan ------- PWM circuit, Windows C client; design

About

A project to demonstrate the BeagleBone Black sampling a pulse width modulator signal using the PRUSS

Resources

License

Stars

Watchers

Forks

Packages

No packages published