Forum | Documentation | Website | Blog

Skip to content
Snippets Groups Projects
Commit 29f25596 authored by Krishna  Narayanan's avatar Krishna Narayanan Committed by Deepak Khatri
Browse files

Updated simpPRU Documentation

parent 778302a0
Branches
Tags
1 merge request!17Updated simpPRU Documentation
Showing
with 167 additions and 351 deletions
Sending state of button using RPMSG
===================================
!!! info “Schematic” === “Pocket Beagle” |image0|
::
=== "BeagleBone Black / Beagle Bone Black Wireless"
![](images/led_button_beagle_bone_black.png)
.. image:: images/led_button_pocket_beagle.png
:width: 598
:align: center
:height: 400
:alt: Sending state of button using RPMSG
Code
----
......
Delay example
=============
!!! info “Schematic” === “Pocket Beagle” |image0|
.. image:: images/led_pocket_beagle.png
:width: 598
:align: center
:height: 400
:alt: Delay example
::
=== "BeagleBone Black / Beagle Bone Black Wireless"
![](images/led_beagle_bone_black.png)
Code
----
......@@ -30,4 +30,3 @@ using the ``delay`` call, after that it writes LOW to header pin P1_31,
then again waits for 5000ms using the ``delay`` call, and finally writes
HIGH to header pin P1_31.
.. |image0| image:: images/led_pocket_beagle.png
\ No newline at end of file
Digital read example
====================
!!! info “Schematic” === “Pocket Beagle” |image0|
::
=== "BeagleBone Black / Beagle Bone Black Wireless"
![](images/led_button_beagle_bone_black.png)
.. image:: images/led_button_pocket_beagle.png
:width: 598
:align: center
:height: 400
:alt: Digital read example
Code
----
......@@ -33,4 +32,3 @@ This code runs a never ending loop, since it is ``while : true``. Inside
P1_29 is HIGH, header pin P1_31 is set to LOW, and if header pin P1_29
is LOW, header pin P1_31 is set to HIGH.
.. |image0| image:: images/led_button_pocket_beagle.png
\ No newline at end of file
Digital write example
=====================
!!! info “Schematic” === “Pocket Beagle” |image0|
::
=== "BeagleBone Black / Beagle Bone Black Wireless"
![](images/led_button_beagle_bone_black.png)
.. image:: images/led_pocket_beagle.png
:width: 598
:align: center
:height: 400
:alt: Digital write example
Code
----
......@@ -26,4 +25,3 @@ Explaination
This code runs a never ending loop, since it is ``while : true``. Inside
``while`` it sets header pin P1_31 to HIGH.
.. |image0| image:: images/led_pocket_beagle.png
HCSR04 Distance Sensor example (sending distance data to ARM using RPMSG)
=========================================================================
!!! info “Schematic” === “Pocket Beagle” |image0|
::
=== "BeagleBone Black / Beagle Bone Black Wireless"
![](images/hcsr04_beagle_bone_black.png)
.. image:: images/hcsr04_pocket_beagle.png
:width: 598
:align: center
:height: 400
:alt: HCSR04 Distance Sensor example
Code
----
......
Ultrasonic range sensor example
===============================
!!! info “Schematic” === “Pocket Beagle” |image0|
::
=== "BeagleBone Black / Beagle Bone Black Wireless"
![](images/hcsr04_beagle_bone_black.png)
.. image:: images/hcsr04_pocket_beagle.png
:width: 598
:align: center
:height: 400
:alt: Ultrasonic range sensor example
Code
----
......
simppru-examples/images/simpPRU.png

32.4 KiB

......@@ -3,53 +3,32 @@
simpPRU Examples
##############
The PRU is a dual core micro-controller system present on the AM335x SoC
which powers the BeagleBone. It is meant to be used for high speed
jitter free IO control. Being independent from the linux scheduler and
having direct access to the IO pins of the BeagleBone Black, the PRU is
ideal for offloading IO intensive tasks.
Programming the PRU is a uphill task for a beginner, since it involves
several steps, writing the firmware for the PRU, writing a loader
program. This can be a easy task for a experienced developer, but it
keeps many creative developers away. So, I propose to implement a easy
to understand language for the PRU, hiding away all the low level stuff
and providing a clean interface to program PRU.
This can be achieved by implementing a language on top of PRU C. It will
directly compile down to PRU C. This could also be solved by
implementing a bytecode engine on the PRU, but this will result in waste
of already limited resources on PRU. With this approach, both PRU cores
can be run independent of each other.
These are the examples which have been tested on simpPRU.These examples will serve as a guide for the users to implement.
.. image:: images/simpPRU.png
:width: 398
:align: center
:height: 200
:alt: simpPRU
What is simpPRU
---------------
- simpPRU is a procedural programming language.
- It is a statically typed language. Variables and functions must be
assigned data types during compilation.
- It is typesafe, and data types of variables are decided during
compilation.
- simPRU codes have a ``.sim`` extension.
- simpPRU provides a console app to use Remoteproc functionality.
:alt: simpPRU Examples
.. toctree::
:maxdepth: 1
build.rst
install.rst
language.rst
io.rst
usage-simppru.rst
usage-simppru-console.rst
delay.rst
digital_read.rst
digital_write.rst
hcsr04_example_rpmsg.rst
hcsr04_sensor.rst
button_click_rpmsg.rst
led_blink_button.rst
led_blink_for.rst
led_blink_while.rst
led_blink.rst
read_counter.rst
rpmsg_example.rst
rpmsg_pru_calculator.rst
LED blink example
=================
!!! info “Schematic” === “Pocket Beagle” |image0|
::
=== "BeagleBone Black / Beagle Bone Black Wireless"
![](images/led_beagle_bone_black.png)
.. image:: images/led_pocket_beagle.png
:width: 598
:align: center
:height: 400
:alt: LED blink example
Code
----
......@@ -31,4 +30,3 @@ This code runs a never ending loop, since it is ``while : true``. Inside
header pin P1_31 to LOW, then again it waits for 1000ms. This loop runs
endlessly, so we get a Blinking output if one connects a LED
.. |image0| image:: images/led_pocket_beagle.png
LED blink on button press example
=================================
!!! info “Schematic” === “Pocket Beagle” |image0|
::
=== "BeagleBone Black / Beagle Bone Black Wireless"
![](images/led_button_beagle_bone_black.png)
.. image:: images/led_button_pocket_beagle.png
:width: 598
:align: center
:height: 400
:alt: LED blink on button press example
Code
----
......@@ -34,5 +33,3 @@ HIGH, waits for 1000ms, then sets header pin P1_31 to LOW, then again it
waits for 1000ms. This loop runs endlessly as long as header pin P1_29
is HIGH, so we get a Blinking output if one connects a LED to output
pin.
.. |image0| image:: images/led_button_pocket_beagle.png
LED blink using hardware counter
================================
!!! info “Schematic” === “Pocket Beagle” |image0|
::
=== "BeagleBone Black / Beagle Bone Black Wireless"
![](images/led_beagle_bone_black.png)
.. image:: images/led_pocket_beagle.png
:width: 598
:align: center
:height: 400
:alt: LED blink using hardware counter
Code
----
......@@ -49,5 +48,3 @@ loop. Here, we check if ``read_counter`` is less than 200000000, as
counter takes exactly 1 second to count this much cycles, so basically
the LED is turned on for 1 second, and then turned off for 1 second.
Thus if a LED is connected to the pin, we get a endlessly blinking LED.
.. |image0| image:: images/led_pocket_beagle.png
LED blink using for loop example
================================
!!! info “Schematic” === “Pocket Beagle” |image0|
::
=== "BeagleBone Black / Beagle Bone Black Wireless"
![](images/led_beagle_bone_bl
.. image:: images/led_pocket_beagle.png
:width: 598
:align: center
:height: 400
:alt: LED blink using hardware counter
Code
----
......@@ -33,4 +31,3 @@ to LOW, then again it waits for 1000ms. This loop runs endlessly, so we
get a Blinking output if one connects a LED. So LED will blink 10 times
with this code.
.. |image0| image:: images/led_pocket_beagle.png
LED blink using while loop example
==================================
!!! info “Schematic” === “Pocket Beagle” |image0|
::
=== "BeagleBone Black / Beagle Bone Black Wireless"
![](images/led_beagle_bone_black.png)
.. image:: images/led_pocket_beagle.png
:width: 598
:align: center
:height: 400
:alt: LED blink using while loop example
Code
----
......@@ -31,4 +30,3 @@ Inside ``while`` it sets header pin P1_31 to HIGH, waits for 1000ms,
then sets header pin P1_31 to LOW, then again it waits for 1000ms. This
loop runs endlessly, so we get a Blinking output if one connects a LED
.. |image0| image:: images/led_pocket_beagle.png
......@@ -37,7 +37,7 @@ What is simpPRU
assigned data types during compilation.
- It is typesafe, and data types of variables are decided during
compilation.
- simPRU codes have a ``.sim`` extension.
- simpPRU codes have a ``.sim`` extension.
- simpPRU provides a console app to use Remoteproc functionality.
......
IO Functions
============
PRU<->Header pin mappings
-------------------------
=== “PocketBeagle” === “PRU0” \| R30 Register bit (Output) \| R31
Register bit (Input) \| Header pin \| \| :————–: \| :————–: \| :——–: \|
\| - \| 16 \| P1_20 \| \| 7 \| 7 \| P1_29 \| \| 4 \| 4 \| P1_31 \| \| 1
\| 1 \| P1_33 \| \| 0 \| 0 \| P1_36 \| \| - \| 15 \| P2_18 \| \| 15 \| -
\| P2_33 \| \| - \| 14 \| P2_22 \| \| 14 \| - \| P2_24 \| \| 6 \| 6 \|
P2_28 \| \| 3 \| 3 \| P2_30 \| \| 2 \| 2 \| P2_32 \| \| 5 \| 5 \| P2_34
\|
::
=== "PRU1"
| R30 Register bit (Output) | R31 Register bit (Input) | Header pin |
| :--------------: | :--------------: | :--------: |
| 9 | 9 | P1_02 |
| 11 | 11 | P1_04 |
| 15 | 15 | P1_30 |
| 14 | 14 | P1_32 |
| 10 | 10 | P1_35 |
| - | 16 | P2_31 |
| 8 | 8 | P2_35 |
=== “BeagleBone Black” === “PRU0” \| R30 Register bit (Output) \| R31
Register bit (Input) \| Header pin \| \| :————–: \| :————–: \| :——–: \|
\| - \| 15 \| P8_15 \| \| 15 \| - \| P8_11 \| \| - \| 14 \| P8_16 \| \|
14 \| - \| P8_12 \| \| 7 \| 7 \| P9_25 \| \| 5 \| 5 \| P9_27 \| \| 3 \|
3 \| P9_28 \| \| 1 \| 1 \| P9_29 \| \| 2 \| 2 \| P9_30 \| \| 0 \| 0 \|
P9_31 \| \| 6 \| 6 \| P9_41 \| \| 4 \| 4 \| P9_42 \|
::
=== "PRU1"
| R30 Register bit (Output) | R31 Register bit (Input) | Header pin |
| :--------------: | :--------------: | :--------: |
| 13 | 13 | P8_20 |
| 12 | 12 | P8_21 |
| 8 | 8 | **P8_27 |
| 10 | 10 | **P8_28 |
| 9 | 9 | **P8_29 |
| 6 | 6 | **P8_39 |
| 7 | 7 | **P8_40 |
| 4 | 4 | **P8_41 |
| 5 | 5 | **P8_42 |
| 2 | 2 | **P8_43 |
| 3 | 3 | **P8_44 |
| 0 | 0 | **P8_45 |
| 1 | 1 | **P8_46 |
** Before using these pins, you need to disable HDMI functionality.
You can read how to do this [here](https://archive.vn/D8Bzy)
=== “BeagleBone Black Wireless” === “PRU0” \| R30 Register bit (Output)
\| R31 Register bit (Input) \| Header pin \| \| :————–: \| :————–: \|
:——–: \| \| - \| 15 \| P8_15 \| \| 15 \| - \| P8_11 \| \| - \| 14 \|
P8_16 \| \| 14 \| - \| P8_12 \| \| 7 \| 7 \| P9_25 \| \| 5 \| 5 \| P9_27
\| \| 3 \| 3 \| P9_28 \| \| 1 \| 1 \| P9_29 \| \| 2 \| 2 \| P9_30 \| \|
0 \| 0 \| P9_31 \| \| 6 \| 6 \| P9_41 \| \| 4 \| 4 \| P9_42 \|
::/home/krishna/docs.beagleboard.io/simppru/usage/images
=== "PRU1"
| R30 Register bit (Output) | R31 Register bit (Input) | Header pin |
| :--------------: | :--------------: | :--------: |
| 13 | 13 | P8_20 |
| 12 | 12 | P8_21 |
| 8 | 8 | **P8_27 |
| 10 | 10 | **P8_28 |
| 9 | 9 | **P8_29 |
| 6 | 6 | **P8_39 |
| 7 | 7 | **P8_40 |
| 4 | 4 | **P8_41 |
| 5 | 5 | **P8_42 |
| 2 | 2 | **P8_43 |
| 3 | 3 | **P8_44 |
| 0 | 0 | **P8_45 |
| 1 | 1 | **P8_46 |
** Before using these pins, you need to disable HDMI functionality.
You can read how to do this [here](https://archive.vn/D8Bzy)
=== “BeagleBone AI” === “PRU0” \| R30 Register bit (Output) \| R31
Register bit (Input) \| Header pin \| \| :————–: \| :————–: \| :——–: \|
\| 3 \| 3 \| P8_12 \| \| 4 \| 4 \| P8_11 \| \| 5 \| 5 \| P9_15 \| \| 17
\| 17 \| P9_26 \|
::
=== "PRU1"
| R30 Register bit (Output) | R31 Register bit (Input) | Header pin |
| :--------------: | :--------------: | :--------: |
| 1 | 1 | P9_20 |
| 2 | 2 | P9_19 |
| 3 | 3 | P9_41 |
| 5 | 5 | P8_18 |
| 6 | 6 | P8_19 |
| 7 | 7 | P8_13 |
| 9 | 9 | P8_14 |
| 10 | 10 | P9_42 |
| 11 | 11 | P9_27 |
| 14 | 14 | P9_14 |
| 15 | 15 | P9_16 |
| 16 | 16 | P8_15 |
| 17 | 17 | P8_26 |
| 18 | 18 | P8_16 |
=== "PRU2"
| R30 Register bit (Output) | R31 Register bit (Input) | Header pin |
| :--------------: | :--------------: | :--------: |
| 10 | 10 | P8_33 |
| 11 | 11 | P8_31 |
| 6 | 6 | P8_38 |
| 7 | 7 | P8_36 |
| 20 | 20 | P8_08 |
| 15 | 15 | P9_13 |
| 3 | 3 | P8_39 |
| 2 | 2 | P8_42 |
| 9 | 9 | P8_35 |
| 8 | 8 | P8_34 |
| 5 | 5 | P8_37 |
| 4 | 4 | P8_40 |
| 17 | 17 | P8_28 |
| 18 | 18 | P8_29 |
| 19 | 19 | P8_30 |
| 1 | 1 | P8_41 |
| 0 | 0 | P8_44 |
| 14 | 14 | P9_11 |
=== "PRU3"
| R30 Register bit (Output) | R31 Register bit (Input) | Header pin |
| :--------------: | :--------------: | :--------: |
| 0 | 0 | P8_32 |
| 5 | 5 | P9_25 |
| 6 | 6 | P8_09 |
| 10 | 10 | P9_31 |
| 8 | 8 | P9_18 |
| 16 | 16 | P8_07 |
| 15 | 15 | P8_10 |
| 17 | 17 | P8_27 |
| 20 | 20 | P8_43 |
| 18 | 18 | P8_45 |
| 19 | 19 | P8_46 |
| 9 | 9 | P9_17 |
| 13 | 13 | P9_28 |
| 11 | 11 | P9_29 |
| 12 | 12 | P9_30 |
** Before using these pins, you need to disable HDMI functionality.
You can read how to do this [here](https://archive.vn/D8Bzy)
- All Header pins are ``constant integer variable`` by default, with
its value equal to respective R30/R31 register bit
......
......@@ -14,8 +14,7 @@ Datatypes
- ``int`` - Integer datatype
- ``bool`` - Boolean datatype
- ``char`` / ``uint8`` - Character / Unsigned 8 bit integer datatype
- ``void`` - Void datatype, can only be used a return type for
functions
- ``void`` - Void datatype, can only be used a return type for functions
Constants
---------
......@@ -27,8 +26,7 @@ Constants
- ``true`` - Boolean constant (True)
- ``false`` - Boolean constant (False)
- ``Px_yz`` - Pin mapping constants are Integer constant, where x is
1,2 or 8,9 and yz are the header pin numbers. For further details
refer to `this <io.md>`__
1,2 or 8,9 and yz are the header pin numbers.
Operators
---------
......@@ -81,8 +79,7 @@ Variable declaration
``boolean`` by default.
- Variables can be assigned other variables of same datatype.
``int``\ s and ``char``\ s can be assigned to each other.
- Variables can be assigned expressions whose output is of same
datatype.
- Variables can be assigned expressions whose output is of same datatype.
Declaration
~~~~~~~~~~~
......@@ -147,8 +144,7 @@ Indexing:
- Arrays are zero-indexed.
- The index can be either a char or an int or an expression involving
chars and ints.
- The index can be either a char or an int or an expression involving chars and ints.
- Accessing elements of an array:
......@@ -269,8 +265,7 @@ Boolean expressions
=> 'a' < 'b';
true
!!! Note Expressions are evaluated following the `operator
precedence <#operators>`__
- **Note** : Expressions are evaluated following the `operator precedence <#operators>`
If-else statement
-----------------
......@@ -373,9 +368,7 @@ Syntax
....
}
!!! Note **var** is a **integer**, and **start, stop, increment** can be
**arithmetic expression, integer or character variable, or integer or
character constant**.
- **Note** : **var** is a **integer**, and **start, stop, increment** can be **arithmetic expression, integer or character variable, or integer or character constant**.
.. _examples-1:
......@@ -410,8 +403,7 @@ Examples
While-loop statement
--------------------
While loop statement repeatedly executes a target statement as long as a
given condition is true.
While loop statement repeatedly executes a target statement as long as a given condition is true.
.. _syntax-2:
......@@ -453,8 +445,7 @@ Examples
Control statements
------------------
!!! Note ``break`` and ``continue`` can only be used inside looping
statements
- **Note** : ``break`` and ``continue`` can only be used inside looping statements
break
~~~~~
......@@ -525,7 +516,7 @@ declaration tells the compiler about a function’s name, return type, and
parameters. A function definition provides the actual body of the
function.
!!! warning Function must be defined before calling it.
- **Warning** : Function must be defined before calling it.
.. _syntax-5:
......@@ -542,27 +533,36 @@ Syntax
return <data_type>;
}
!!! Note If return data type is void, then return statement is not
- **Note** : If return data type is void, then return statement is not
needed, and if still it is added, it must be return nothing, i.e.,
something like this ``return ;``
!!! Warning ``return`` can only be present in the body of the function
- **Warning** : ``return`` can only be present in the body of the function
only once, that too at the end of the function, not inside any compound
statements.
!!! fail “Wrong” \* ``return`` inside a compound statement, this syntax
is not allowed.
``python def test : int : int a { if : a < 4 { return a; } }``
- **Wrong** : ``return`` inside a compound statement, this syntax is not allowed.
!!! done “Correct” \* ``return`` is not inside compound statments, It
should be placed only at the end of function definition \```python def
test : int : int a { int gf := 8; if : a < 4 { gf := 4; }
.. code:: python
def test : int : int a {
if : a < 4 {
return a;
}
}
::
- **Correct** : ``return`` is not inside compound statments, It should be placed only at the end of function definition
return gf;
.. code:: python
def test : int : int a {
int gf := 8;
if : a < 4
{
gf := 4;
}
return gf;
}
```
.. _examples-5:
......@@ -571,52 +571,52 @@ Examples
Examples according to return types
=== “Integer” \```python def test_func : int : int a, int b { int aa :=
a + 5;
- **Integer**
::
.. code:: python
if : aa < 3 {
def test_func : int : int a, int b
{
int aa := a + 5;
if : aa < 3 {
aa : = 0;
}
}
return aa + b;
return aa + b;
}
```
=== “Character” \```python def next_char : char : char ch, int inc {
char chinc := ch + inc;
- **Character**
::
.. code:: python
return chinc;
def next_char : char : char ch, int inc {
char chinc := ch + inc;
return chinc;
}
```
=== “Boolean” \```python def compare : bool : int val { bool ret :=
false;
::
- **Boolean**
if : val < 0 {
ret := true;
}
.. code:: python
return ret;
}
```
def compare : bool : int val {
bool ret :=false;
if : val < 0 {
ret := true;
}
return ret;
}
=== “Void” \```python def example_func : void : bool qu { if : qu and
true { … do something … } }
- **Void**
::
.. code:: python
def example_func_v : void : {
int temp := 90;
return;
}
```
Function call
~~~~~~~~~~~~~
......@@ -639,24 +639,19 @@ Syntax
Examples
^^^^^^^^
We will consider functions defined in earlier
`subsection <#examples_5>`__
=== “Integer”
``python int a := 55; int ret_val := test_func(4, a);``
- **Integer** ``int a := 55; int ret_val := test_func(4, a);``
=== “Character”
``python char a := 'a'; char b := next_char(a, 1);``
- **Character** ``char a := 'a'; char b := next_char(a, 1);``
=== “Boolean ``python bool val := compare(22); compare(-2);``
- **Boolean** ``bool val := compare(22); compare(-2);``
=== “Void” ``python example_func(false); example_func_v();``
- **Void** ``example_func(false); example_func_v();``
Testing or Debugging
~~~~~~~~~~~~~~~~~~~~
For testing or debugging code, use the test or -t flag to enable print,
println and stub functions. Use preprocess to stop after generating the
For testing or debugging code, use the --test or -t flag to enable print,
println and stub functions. Use --preprocess to stop after generating the
C code only. Then run the generated C code (at /tmp/temp.c) using
``gcc``.
......@@ -683,4 +678,4 @@ Stub functions
^^^^^^^^^^^^^^
PRU specific functions will be replaced by stub functions which print
function_name called with arguments arg_name when called.
**function_name called with arguments arg_name** when called.
Usage
Usage(simppru-console)
=====
simppru-console is a console app, it can be used to send/receive message
......@@ -6,10 +6,13 @@ to the PRU using RPMSG, and also start/stop the PRU. It is built to
facilitate easier way to use rpmsg and remoteproc API's to control and
communicate with the PRU
!!! Warning Make sure to stop PRU before exiting. Press ``ctrl+c`` to
exit
- **Warning** : Make sure to stop PRU before exiting. Press ``ctrl+c`` to exit
|image0|
.. image:: images/main_screen.png
:width: 598
:align: center
:height: 400
:alt: Main Screen
Features
--------
......@@ -22,7 +25,11 @@ Start/stop buttons
Use these button to start/stop the selected PRU. If PRU is already
running, on starting simppru-console, it is automatically stopped.
|image1|
.. image:: images/stop_screen.png
:width: 598
:align: center
:height: 400
:alt: Start/stop buttons
Send message to PRU
~~~~~~~~~~~~~~~~~~~
......@@ -32,7 +39,11 @@ supported. On pressing enter, the typed message is sent.
PRU0 is running echo program, whatever is sent is echoed back.
|image2|
.. image:: images/send_screen.png
:width: 598
:align: center
:height: 400
:alt: Start/stop buttons
Receive message from PRU
~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -42,26 +53,30 @@ using a for loop, which checks if new message is arrived every 10 ms.
- PRU is running echo program, whatever is sent is echoed back.
|image3| |image4|
.. image:: images/receive_screen_2.png
:width: 598
:align: center
:height: 400
:alt: Echoing program
- PRU is running countup program, it sends a increasing count every 1
second, which starts from 0
|image5| |image6|
.. image:: images/receive_counter_2.png
:width: 598
:align: center
:height: 400
:alt: Echoing program
Change PRU id
Change PRU ID
~~~~~~~~~~~~~
Using the radio box in the upper right corner, one can change the PRU
id, i.e. if one wants to use the features for PRU0 or PRU1
|image7|
.. image:: images/select_pru_id_screen.png
:width: 598
:align: center
:height: 400
:alt: Change the PRU ID
.. |image0| image:: images/main_screen.png
.. |image1| image:: images/stop_screen.png
.. |image2| image:: images/send_screen.png
.. |image3| image:: images/receive_screen.png
.. |image4| image:: images/receive_screen_2.png
.. |image5| image:: images/receive_counter.png
.. |image6| image:: images/receive_counter_2.png
.. |image7| image:: images/select_pru_id_screen.png
Usage
Usage(simppru)
=====
.. code:: bash
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment