This advanced example extends available RAM via SPI SRAM chips.
It uses a Pi Spigot algorithm to compute up to approximately
decimal digits of the mathematical
constant
.
Along the way, this example implements fascinating memory management mechanisms
including external-RAM-based iterators, containers and algorithms
in modern C++ style.
This example embodies a stunning depiction of computational complexity running in a real-world, real-time system. In addition to maintaining real-time multitasking operation, it simultaneously highlights the quite detailed description of the Pi Spigot algorithm's complexity that can be found in the corresponding book section.
The famous Pi Spigot algorithm is often used to compute
modestly small numbers of decimal digits of the mathematical constant
.
A simple expression of a Spigot algorithm
for the base-10 representation of
is provided in Eq. 6.1, Sect. 6.1,
page 78 of Arndt and Haenel's Pi Unleashed.
This equation has been templated and extended in the Pi Spigot program of our book. Consider, in particular, the code snippet chapter10_08-000_pi_spigot_single.cpp. Compiling and running this program as it stands produces the following output (which may vary regarding timing of your system):
result_digit: 10001
result_test_pi_spigot_single_is_ok: true
elapsed time: 0.265s
operation_count: 19155868
input memory consumption: 137788
result_is_ok: true
In the default release of this program
from the code snippets area running on a PC,
the Pi Spigot algorithm is set up to compute
digits of
.
Play around with the output digit count in the program.
Switch from the default setting of
digits down to
digits or even up to
digits.
Watch the operation count and memory consumption vary with
size of the output digit count.
In this example, the Pi Spigot program has been adapted
to run on our target system with the 8-bit microcontroller.
In order to do so, two external serial SPI SRAM chips
have been used.
The default release on the 8-bit MCU is set up for
decimal digits of
.
The
decimal digit
calculation requires approximately 90s on this particular setup.
Memory allocation schemes and external SRAM iterators/containers are described fully in the book. These have been used to abstract memory access to the off-chip SRAMs and allow the Pi Spigot program to run essentially out of the box in about the same form as found in the code snippets area.
For this particular example adapted to the 8-bit MCU, a state-machine variation of the single-state program is used. The state-machine variation divides the computation time of the individual Pi Spigot operations among the time slices of the idle task of the multitasking scheduler. The internal state variables of the Pi Spigot calculation are stored in static variables along the way as the state machine consequently, iteratively and deliberately works its way through the Pi Spigot calculation.
The program runs continuously, performing successive back-to-back calculations. The subsequent Pi Spigot calculation begins when the previous one finishes. Simultaneously, the well-known blinky application with 1s blinking on/off is handled in the LED task.
A tabulated, known control value containing
decimal digits of
is stored in the constant-valued, ROM-based
array
variable
called local::control::sys_idle_pi_spigot_cntrl
. This control value
is used to check the calculated digits of
on the fly as they are retrieved from each successive iteration
of the calculation.
Progress of the Pi Spigot calculation is expressed as the
duty cycle of a PWM signal on timer_a
output on portb.1
, microcontroller pin 15.
An oscilloscope is required in order to verify the calculation
progress via PWM duty cycle measurement.
A possible program extension
could mount and subsequently implement control for
an additional resistor/LED series combination
or an LCD, as seen in
example Chapter10_08a,
in order to display the calculation progress more obviously.
The duty cycle representing number of digits computed
accelerates toward the end of each calculation,
which mimics the nature of the calculation itself.
Memory extension uses two serial SPI SRAM chips of type Microchip(R) 23LC1024.
Each chip has 1 Mbit (128 kByte) of asynchronous SRAM.
These 8-pin SRAM chips are straightforward to use.
They are controlled with easy-to-understand commands
that execute read/write operations in either single byte sequences
or small page bursts. A lightweight communication class
called mcal::memroy::sram::memory_sram_microchip_23lc1024
that is used to control the SRAM chips can be found
in the file
mcal_memory_sram_microchip_23lc1024.h
The
decimal digit
The
calculation requires approximately 140 kByte RAM.
Thus two 128 kByte chips are required
for the full
digit range intended for this example.
The all-software SPI driver communicates directly with the off-chip SRAM devices. The standard protocol described in the SRAM chip's manual(s) is used.
Pinning in this example is summarized in the table below.
Pin SRAM_0,1 | SRAM Function | MCU Function | MCU Pin |
---|---|---|---|
1 (SRAM_0) | CS_0 | portc.4 |
27 |
6 (SRAM_0) | SCK_0 | portc.3 |
26 |
2 (SRAM_0) | SO_0 | portc.2 |
25 |
5 (SRAM_0) | SI_0 | portc.1 |
24 |
1 (SRAM_1) | CS_1 | portc.5 |
28 |
6 (SRAM_1) | SCK_1 | portc.3 |
shared with SCK_0 |
2 (SRAM_1) | SO_1 | portc.2 |
shared with SO_0 |
5 (SRAM_1) | SI_1 | portc.1 |
shared with SI_0 |
NA | portb.1 /timer_a PWM |
15 |
The hardware setup is pictured in the image below with an oscilloscope measurement in action.
The PWM signal representing calculation progress is shown below. The PWM signal has a frequency of approximately two kilohertz.