Getting started with Programmable Logic ArraY (PLAY)

1. Introduction

This article provides a getting started guide for the PLAY – Programmable Logic ArraY peripheral. It explains the main concepts, the required tools, and the basic steps to build, program, and run PLAY examples. This article is attached to the PLAY application note AN6401 and should be read in conjunction with it for detailed technical information and background. After reading this page, you should be able to:

  • understand what PLAY is and what you can do with it,
  • set up the HW and SW environment,
  • design and validate logic using Logisim evolution,
  • configure and use PLAY from STM32CubeMX,
  • run your first PLAY example on an STM32H5 board.

2. What is PLAY?

PLAY (Programmable Logic ArraY) is a new peripheral that allows you to add custom logical functions or state machines on existing signals, without requiring an external FPGA or additional logic ICs.

PLAY can:

  • take internal or external peripheral signals as inputs,
  • drive internal or external peripheral signals as outputs,
  • implement combinatorial logic, synchronous (registered) logic with a dedicated clock, and feedback paths between logic blocks,
  • operate even in Stop mode,
  • connect to EXTI for wakeup capability.

This section provides only a high-level introduction to PLAY. For a complete functional description, please refer to the PLAY application note AN6401 and the reference manual RM0517, which serve as the main reference documents for the peripheral architecture and features

3. Ecosystem

  1. Logisim evolution is used to design and simulate the digital logic, and to derive the truth table values that will configure the PLAY LUTs.
  2. STM32CubeMX is then used to configure the PLAY peripheral (inputs, outputs, LUTs) and to generate the corresponding initialization code.
  3. Finally, the generated project is opened in a toolchain / IDE such as IAR Embedded Workbench, Keil MDK‑ARM, STM32CubeIDE, or another supported environment (for example Eclipse‑based or VS Code), where the application code is completed, compiled, flashed and debugged.

3.1. Logisim evolution

Logisim evolution is a graphical tool used to design and simulate digital logic circuits. In the PLAY ecosystem, it is mainly used to prepare the configuration of the LUTs.

Logisim evolution is used as a front‑end tool for logic design and validation:

  • Design and simulate digital logic circuits using an intuitive graphical interface.
  • Build circuits starting from truth tables using the combinational analysis module.
  • Available on Linux, macOS, and Windows.

For PLAY, Logisim evolution helps to:

  • Design and validate the Boolean logic of the function implemented in the LUT
  • Simulate the behavior of the logic to verify correct operation before implementation
  • Generate the truth table required for the LUT configuration
  • Derive the corresponding hexadecimal value used to program the LUT (e.g. 0xFFFE, 0xF888) from the truth table
Logisim

Logisim evolution can be downloaded from its official project page:

3.2. STM32CubeMX integration

In the PLAY ecosystem, STM32CubeMX is the central tool used to configure the PLAY peripheral and to generate the initialization code.

The configuration in STM32CubeMX is split into two main parts: Mode and Configuration.

1. Mode The Mode section allows you to:

  • activate the PLAY peripheral in the project, and
  • define which resources in the pinout are used as PLAY inputs and outputs.

Enabling PLAY in this view ensures that the peripheral is instantiated in the project and that the corresponding GPIO pins are reserved for PLAY usage.

2. Configuration The Configuration section exposes the complete configuration of the peripheral. It is organized into several tabs, including:

  • Input MUX Settings – selection of the input signals from the input matrix I/O,
  • Output MUX Settings – selection of the output signals routed to GPIO pins or internal peripherals,
  • Logic Element Settings – LUT configuration, including selection of LUT input/output signals and the programming of LUT truth table values (for example the 16‑bit values obtained from Logisim evolution).
Getting started with PLAY programmable Logic ArraY 1770808336299.png

Together, these views allow you to map PLAY to the desired MCU signals, apply the logic functions defined in your design, and automatically generate the C initialization code that will be compiled in your application project.

STM32CubeMX can be obtained from ST’s website:https://www.st.com/en/development-tools/stm32cubemx.html#get-software

4. PLAY examples

4.1. PLAY – Custom logic example

This example demonstrates how to use the Programmable Logic Array block to implement small custom logic functions directly inside the device. By configuring the PLAY instead of adding external logic ICs, you can create and modify Boolean functions (AND/OR/XOR/NOT combinations) through simple configuration.

This example shows how to map logic equations or truth tables into PLAY, connect them to on‑chip signals (such as GPIOs or peripheral outputs), and use the result to simplify glue logic and reduce external components.

  • Create and simulate a custom logic function through Logisim evolution
  • Configure PLAY using STM32CubeMX
4.1.1. Logisim evolution

This custom logic circuit has 4 inputs and 1 output:

  • The two upper inputs are combined by an AND gate.
  • The two lower inputs are combined by an OR gate.
  • The outputs of these two gates are then fed into a final AND gate, which drives the single output.

To obtain the logic equation and truth table in Logisim‑evolution:

To obtain the logic equation and truth table in Logisim‑evolution:

  1. In the top menu bar, click “Project”.
  2. In the drop‑down menu, select “Analyze Circuit”.
    example1 2.png

This opens the Combinational Analysis window, where the 4 inputs and 1 output of your circuit are automatically detected and you can view/edit the truth table and the resulting Boolean expression.

example1 3.png

example1 4.png


To configure PLAY in STM32CubeMX, you must take the truth table from Logisim evolution and map it to the PLA configuration.

The truth table value in hexadecimal is: 0xE000.


4.1.2. PLAY configuration – STM32CubeMx
  • Go to File > New Project in the main window.
  • Select the STM32H5E5ZJTx in the MCU Selector tab and click Start project.

If you haven't downloaded the STM32CubeH5 library yet, it will now be downloaded automatically. This might take some time.

  • Under Logic Array select PLAY1.
  • Activate PLAY1
4.1.2.1. Inputs
  1. Under Inputs Activated, enable PLAY1_IN0, PLAY1_IN1, PLAY1_IN2 and PLAY1_IN3. These signals are mapped to GPIO pins, as they are used as external inputs to the PLAY.
  2. Open the Input MUX Settings tab.
  3. For MUX0…MUX3, select the corresponding source:
    • MUX0 → PLAY1_IN0
    • MUX1 → PLAY1_IN1
    • MUX2 → PLAY1_IN2
    • MUX3 → PLAY1_IN3
Getting started with PLAY programmable Logic ArraY 1770820127003.png
4.1.2.2. Output
  1. Under Outputs Activated, enable PLAY1_OUT0 .
  2. In the Output MUX Settings tab, select PLAY1 LUT0 output direct as the source for MUX0 so that the LUT result is driven to PLAY1_OUT0, which is mapped to a GPIO pin and used as an external output of the PLAY.
4.1.2.3. LUT settings
  1. Go to the Look Up Table Settings tab and select Look Up Table 0.
  2. Enable Input 0…Input 3 and select Output of IN MUX as the signal type for each, with signal names IN MUX0…IN MUX3 (mapped to PLAY1_IN0…3).
  3. Enter this value in the LUT truth table field 0xE000.(from your Logisim evolution)

With all PLAY inputs, outputs, MUXes and the LUT truth table configured, the peripheral setup is complete in STM32CubeMX. At this stage, we can simply generate the project code, and the custom logic implemented in Logisim evolution will be synthesized in hardware by the PLAY peripheral.

4.1.2.4. Code configuration
  • Open main.c using the Project Explorer: myproject / Src / main.c.
Info white.png Information
Insert your code between /* USER CODE BEGIN 2 */ and /* USER CODE END 2 */ tags
  /* In this example, edge triggers are not used */
  edge_trigger_config.lut_out_falling_mask = 0U;
  edge_trigger_config.lut_out_rising_mask = 0U;

  /* Start PLAY module */
  if (HAL_PLAY_Start(&hplay1, &edge_trigger_config) != HAL_OK)
  {
    Error_Handler();
  }
4.1.3. Compile and flash
  • Click Debug button to run step by step
  • Click Resume to continue execution Resume button.png
  • Click Suspend to stop execution Suspend button.png

4.2. PLAY – Signal Routing example

From Internal Signal to the External World

In this PLAY example, we focus on how an internal signal is routed to the external world through GPIOs.

For the first time in an STM32 device, an internal signal can be directly routed to the outside world, and this example illustrates how simple and powerful this capability is when used through PLAY.

The goal is to show, in a clear and practical way, how a signal generated or processed inside the device (internal signal) can be selected, routed, and made available outside the chip.

Through this example, we will:

  • Identify the internal signal source (e.g. a timer, ADC result, audio path, or internal bus).
  • Show how this signal is mapped and routed to an external interface (GPIO, dedicated output, or communication channel).
  • Highlight the configuration steps required to make the internal signal visible outside.
4.2.1. PLAY configuration – STM32CubeMx
  • Go to File > New Project in the main window.
  • Select the STM32H5E5ZJTx in the MCU Selector tab and click Start project.

If you haven't downloaded the STM32CubeH5 library yet, it will now be downloaded automatically. This might take some time.

  1. Under Logic Array select PLAY1.
  2. Activate PLAY1
4.2.1.1. Input
  1. Open the Input MUX Settings tab.
  2. For MUX0, select the corresponding source:
    • MUX0 → TIM2_TRGO


The Logic Array inputs are fed through multiplexers (MUXes). A MUX selects one signal among several internal sources and routes it to the Logic Array.

The TIM2_TRGO signal is connected to MUX0, which is why MUX0 must be used here.

The complete list of which signals are available on each MUX is given in the Reference Manual RM0517 (Table 195. PLAY logic array input n signal selection ).

4.2.1.2. Output
  1. Under Outputs Activated, enable PLAY1_OUT0 .
  2. In the Output MUX Settings tab, select PLAY1 LUT0 output direct as the source for MUX0 so that the LUT result is driven to PLAY1_OUT0, which is mapped to a GPIO pin and used as an external output of the PLAY.

The direct option means the LUT result is sent to the output without an extra register stage (no additional clock cycle of latency).

4.2.1.3. LUT settings
  1. Go to the Look Up Table Settings tab and select Look Up Table 0.
  2. Enable Input 0 and select Output of IN MUX as the signal type, with signal names IN MUX0 (mapped to PLAY1_IN0).
  3. Enter this value in the LUT truth table field 0xAAAA

The LUT is configured with only one input (Input 0).The 16‑bit value 0xAAAA (1010 1010 1010 1010₂) sets:

  • output = 0 when input = 0
  • output = 1 when input = 1

So, for a 1‑input LUT, 0xAAAA makes the LUT output equal to Input 0 (identity). We use this value just to route the Input 0 signal through the LUT without changing it.

With all PLAY inputs, outputs, MUXes and the LUT truth table configured, the peripheral setup is complete in STM32CubeMX. At this stage, we can simply generate the project code, and the logic will be synthesized in hardware by the PLAY peripheral.

4.2.1.4. Code configuration
  • Open main.c using the Project Explorer: myproject / Src / main.c.
Info white.png Information
Insert your code between /* USER CODE BEGIN 2 */ and /* USER CODE END 2 */ tags
  /* In this example, edge triggers are not used */
  edge_trigger_config.lut_out_falling_mask = 0U;
  edge_trigger_config.lut_out_rising_mask = 0U;

  /* Start PLAY module */
  if (HAL_PLAY_Start(&hplay1, &edge_trigger_config) != HAL_OK)
  {
    Error_Handler();
  }

Compile and flash

  • Click Debug button to run step by step
  • Click Resume to continue execution Resume button.png
  • Click Suspend to stop execution Suspend button.png

4.3. PLAY – 3-bit counter example

This example uses the Programmable Logic Array block to implement a simple 3‑bit up‑counter as a state machine.

The counter:

  • Uses 3 state bits to count from 000 to 111 (0 to 7).
  • user_button: a push‑button connected to a GPIO input .
  • Q0, Q1, Q2: three counter bits driving three LEDs connected to GPIO outputs.

We first describe and verify the counter behavior in Logisim evolution, then load the corresponding truth table into PLAY using STM32CubeMX so the counter runs directly in hardware without CPU intervention.

4.3.1. Logisim evolution

This circuit implements a 3‑bit up‑counter state machine.

  • Inputs
    • user_button: user push‑button used as a count request.
    • play_clk: clock signal that, in the PLAY, is selected as the Clock Gate source for the LUT internal registers.
  • Three PLAY LUTs (LUT1, LUT2, LUT3)
    • Each LUT is configured as a small PLA that computes the next‑state bit of the counter:
      • LUT1 → next value of Q0 (LSB)
      • LUT2 → next value of Q1
      • LUT3 → next value of Q2 (MSB)
    • Their inputs are the current state bits (Q0, Q1, Q2) and the enable pulse from the edge detector.
    • The direct outputs of the LUTs are not used; the registered outputs form the actual state bits.
  • Outputs

In Logisim, explicit flip‑flops are drawn after the LUTs to represent these registered outputs.

Their outputs are the counter bits Q0, Q1, Q2 visible on the right.

Overall behavior

On every rising edge of play_clk,

  • If a button edge was detected, the LUTs compute the next counter value and the registered outputs update to the new Q0, Q1, Q2.
  • If no edge was detected, the LUTs are configured so that next state = current state, and the counter holds its value.
3-bit counter.png

Simulation

4.3.2. PLAY configuration – STM32CubeMx
  • Go to File > New Project in the main window.
  • Select the STM32H5E5ZJTx in the MCU Selector tab and click Start project.

If you haven't downloaded the STM32CubeH5 library yet, it will now be downloaded automatically. This might take some time.

  1. Under Logic Array select PLAY1.
  2. Activate PLAY1
4.3.2.1. Inputs
  1. Under Inputs Activated, enable PLAY1_IN12. This signal is mapped to GPIO pin (used as external input to the PLAY).
  2. Open the Input MUX Settings tab.
  3. For MUX12, select the corresponding source:
    • MUX12 → PLAY1_IN12

This signal is mapped to a GPIO pin and used as the external button input to the PLAY.

Getting started with PLAY programmable Logic ArraY 1770894995251.png
4.3.2.2. Outputs
  1. Under Outputs Activated, enable PLAY1_OUT0, PLAY1_OUT13 and PLAY1_OUT14 .
  2. In the Output MUX Settings tab, select PLAY1 LUT0, PLAY1_OUT13 and PLAY1_OUT14 as output registered, so that the LUT result is driven to PLAY1_OUT0 , PLAY1_OUT13 and PLAY1_OUT14 , which is mapped to a GPIO pins and used as an external outputs of the PLAY.

These PLAY outputs are mapped to GPIO pins that, on this board, are connected to LEDs, so the 3‑bit counter value can be seen directly on the LEDs.

Getting started with PLAY programmable Logic ArraY 1770895087462.png
4.3.2.3. LUTs settings

In the Look Up Table Settings tab, three LUTs are used to build the 3‑bit binary counter driven by the button signal (clock‑gated on IN MUX12):

  • LUT0 (Q0 – LSB)
    • Input: its own registered output (LUT0_OUT_REGISTERED).
    • Truth table: 0x5555.
    • Effect: Q0 toggles on every valid button pulse (each time the LUT register is clocked via IN MUX12).
  • LUT1 (Q1)
    • Inputs: Q1 (LUT1_OUT_REGISTERED) and Q0 (LUT0_OUT_REGISTERED).
    • Truth table: 0x6666.
    • Effect: implements the function Q1 ← Q1 ⊕ Q0. Q1 toggles only when Q0 is 1.
  • LUT2 (Q2 – MSB)
    • Inputs: Q2 (LUT2_OUT_REGISTERED), Q1 (LUT1_OUT_REGISTERED) and Q0 (LUT0_OUT_REGISTERED).
    • Truth table: 0x6A6A.
    • Effect: Q2 toggles only when Q1 and Q0 are both 1.

Taken together, LUT0, LUT1 and LUT2 implement a standard 3‑bit synchronous binary up‑counter. Each valid button edge generates a clock pulse on IN MUX12, which clocks the LUT registers and advances the counter from 000 to 111, then wraps around.

Getting started with PLAY programmable Logic ArraY 1770900741370.png


NVIC settings

In the NVIC Settings tab, enable PLAY1 interrupt, as shown in the image below.

Getting started with PLAY programmable Logic ArraY 1770902644071.png

With all PLAY inputs, outputs, MUXes and the LUTs configured, the peripheral setup is complete in STM32CubeMX. At this stage, we can simply generate the project code.

4.3.2.4. Code configuration
  • Open main.c using the Project Explorer: myproject / Src / main.c

 

Info white.png Information
Insert your code between /* USER CODE BEGIN 1 */ and /* USER CODE END 1 */ tags
/* USER CODE BEGIN 1 */

  HAL_PLAY_EdgeTriggerConfTypeDef edge_trigger_config;

/* USER CODE END 1 */
Info white.png Information
Insert your code between /* USER CODE BEGIN 2 */ and /* USER CODE END 2 */ tags
  /* Register the PLAY LUT output falling edge callbacks */
  if (HAL_PLAY_RegisterLUTOutputCallback(&hplay1, HAL_PLAY_LUT_OUTPUT_FALLING_CB_ID,
                                         lut_output_falling_callback) != HAL_OK)
  {
    Error_Handler();
  }

  /* Enable LUT2 registered output falling edge trigger */
  edge_trigger_config.lut_out_falling_mask = HAL_PLAY_LUT2_OUT_REGISTERED;
  edge_trigger_config.lut_out_rising_mask = 0U;

  /* Start PLAY module */
  if (HAL_PLAY_Start(&hplay1, &edge_trigger_config) != HAL_OK)
  {
    Error_Handler();
  }

  if (HAL_PLAY_LUT_EnableIT(&hplay1, HAL_PLAY_LUT2_OUT_REGISTERED) != HAL_OK)
  {
    Error_Handler();
  }

Compile and flash

  • Click Debug button to run step by step
  • Click Resume to continue execution Resume button.png
  • Click Suspend to stop execution Suspend button.png

5. References

  • [AN6401]: Introduction to programmable logic array (PLAY) for STM32 MCUs
  • Youtube: Getting started with Programmable Logic Array (PLAY)
  • RM0517: STM32H5E4/H5F4 and STM32H5E5/H5F5 reference manual
  • DS14972: STM32H5Fxxx datasheet
  • DS14973: STM32H5Exxx datasheet