5 Getting Started with CCL

5 Introduction

Author: Cristian Pop, Microchip Technology Inc.

The Configurable Custom Logic (CCL) is a programmable logic peripheral that allows the on-chip creation of the logic functions for Microchip tinyAVR® 0- and 1-series, megaAVR® 0-series, and AVR® DA devices. The CCL provides programmable, combinational and sequential logic that operates independently of the CPU execution. It can be connected to a wide range of internal and external inputs such as device pins, events, or other internal peripherals and can serve as a “glue logic” between the device peripherals and external devices.

This technical brief explains how to use the CCL to implement the following functions:

  • Logic AND Gate:

    Uses CCL to implement a simple logic AND gate.

  • State Decoder:

    Shows how to use CCL combinational logic to detect a specific state of the external signals.

  • SR Latch:

    Uses internal CCL sequential logic to create an SR latch.

Note: For each of the use cases described in this document, there are two code examples: One bare metal developed on ATmega4809, and one generated with MPLAB® Code Configurator (MCC) developed on AVR128DA48.

Note: In addition to the use cases mentioned above, this document provides advanced examples for the CCL peripheral, as described in Advanced Examples. These are generated with MCC and developed on AVR128DA48.

5.1 Overview

The CCL peripheral has one pair of Look-Up Tables (LUTs). Each LUT consists of three inputs with a Truth table, a synchronizer, a filter, and an edge detector. Each LUT can generate an output as a user programmable logic expression with three inputs; any device with CCL will have a minimum of two LUTs available. These inputs can be individually masked. The output can be generated from the combinatorial inputs and be filtered to remove spikes. An optional sequential logic module can be enabled. The inputs to the sequential module are individually controlled by two independent, adjacent LUT outputs (LUT0/LUT1), enabling complex waveform generation.

Truth Table

It is possible to create simple logical blocks (AND, OR, NAND, NOR, XOR) or custom ones using the truth table up to three inputs on each of the LUTs. When more than three inputs are required, multiple connected LUTs are used to create logical gates.

To define a combinational specific logic function, the CCL module uses truth tables. A truth table shows how the logic circuit responds to various combinations of three inputs. Each combination of the Input bits (IN[2:0]) corresponds to one bit in the respective TRUTHn register.

Below are some examples on how to create some common logical gates using three inputs.

Figure 5-46. AND Logic

To get a HIGH(1) output from an AND gate, all inputs must be HIGH(1). Looking at the truth table above, only TRUTH[7] fulfills this requirement if all three inputs are used. This means that TRUTH[7] must be HIGH(1) and the rest must be LOW(0), resulting in the hex value of 0x80 to be used in the TRUTHn register.

CCL.TRUTHn = 0x80; 
Figure 5-47. XOR Gate

To get a HIGH(1) output from an XOR gate, the number of HIGH(1) inputs must be odd. Looking at the truth table above, TRUTH[1], TRUTH[2], TRUTH[4], and TRUTH[7] fulfill this requirement. This means that these bits must be HIGH(1) and the rest must be LOW(0), resulting in the hex value of 0x96 to be used in the TRUTHn register.

CCL.TRUTHn = 0x96;

When any of the three inputs are not needed, the unused input will be masked (tied low). Only the TRUTH bits where the masked input is ‘0’ can be used when looking at the truth table to determine how the bits should be set to get the wanted logic. Below are some examples of where various inputs are masked.

Figure 5-48. Two-Input AND Gate, IN[0] Masked
Figure 5-49. Two-Input XOR Gate, IN[2] Masked

Some applications require more than three logic inputs. The CCL module provides the option to link internally the next LUTs direct output to a LUT input. For example, if LUT0 and LUT1 are used to create a logic function, the LUT1 output can be connected to the LUT0 input internally.

Using the CCL eliminates the need for external logic, reduces Bill of Materials (BOM) cost and enables the CPU to handle time critical parts of the application more efficient.

5.2 Logic AND Gate

The following example shows how to configure and use CCL LUT1 to implement an AND gate with three inputs.

Figure 5-50 5-56. Using CCL as Logic AND Gate
The first step is to select the I/O pins as inputs using the INSELx[3:0] bits from the LUT Control (LUTnCTRLB and LUTnCTRLC) registers.
Figure 5-51 5-57. LUTn Control B Register
Figure 5-52 5-58. LUTn Control C Register

The table below summarizes the INSEL[3:0] options for all inputs.

Figure 5-53 5-59. CCL Input Selection Options

This translates to the following code.

CCL.LUT1CTRLB = CCL_INSEL0_IO_gc | CCL_INSEL1_IO_gc;

CCL.LUT1CTRLC = CCL_INSEL2_IO_gc;

The next step is to configure the truth tables for LUT1 to generate the right combinational logic to implement an AND gate on the selected pins. Thus, the truth table will have a value of 0x80.

CCL.TRUTH1 = 0x80;

The next step is to configure the output of the decoder, specifically, the I/O Port pin (PC3) in this example. This is done by setting the OUTEN bit on the LUT0CTRLA register.

Figure 5-54 5-60. LUTn Control A Register

This translates to the following code:

CCL.LUT1CTRLA = CCL_OUTEN_bm;

By enabling the LUTn output on the I/O pin, the settings for the corresponding pin are overwritten. To enable the decoding of the input sequence, the CCL and the used LUTs need to be enabled. That is done using the ENABLE bit from the LUTnCTRLA register.

CCL.LUT1CTRLA |= CCL_ENABLE_bm;

To complete the setup, the CCL module needs to be enabled using a CCL Global Enable bit from the CTRLA register.

Figure 5-55 5-61. CCL Control A Register
CCL.CTRLA = CCL_ENABLE_bm;
Tip: The full code example is also available in the Appendix section.

An MCC generated code example for AVR128DA48, with the same functionality as the one described in this section, can be found here:

5.3 State Decoder

The application may need to detect when a specific combination of signals (pattern) appears on the pins. By combining logic gates, a simple state decoder for external signals can be implemented without involving the CPU.

Figure 5-50 5-56. Using the AVR® Microcontroller as a State Decoder

In this example, the CCL module will be used to decode the presence of the b’10110 pattern on the input pins. LUT0 and LUT1, connected to the corresponding input pins, will be used.

The input selection from different input options is done using the INSELx[3:0] bits from the LUT Control (LUTnCTRLB and LUTnCTRLC) registers, as shown in the following figures.

Figure 5-51 5-57. LUTn Control B Register
Figure 5-52 5-58. LUTn Control C Register

The table below summarizes the INSEL[3:0] options for all inputs.

Figure 5-53 5-59. CCL Input Selection Options

For this example, two adjacent LUTs (LUT0 and LUT1) will be used, with the output of LUT1 connected to the LUT0 input (linked).

CCL.LUT0CTRLC = CCL_INSEL2_LINK_gc;

The other two inputs of LUT0 and all three inputs of LUT1 are connected to the I/O pins.

CCL.LUT0CTRLB = CCL_INSEL0_IO_gc | CCL_INSEL1_IO_gc;
CCL.LUT1CTRLB = CCL_INSEL0_IO_gc | CCL_INSEL1_IO_gc;

CCL.LUT1CTRLC = CCL_INSEL2_IO_gc;

The following step is to configure the truth tables for LUT0 and LUT1 to generate the right combinational logic to detect b’10110 on the selected pins. The TRUTH1 table is used to decode the pattern for the Most Significant three bits (b’10110).

CCL.TRUTH1 = 0x20;

LUT0 has as inputs two Least Significant bits from the input pattern (b’10110) and the decoded output of LUT1 on the third input, resulting in binary sequence b’110 to be decoded. The value of the truth table, in this case, will be 0x40.

CCL.TRUTH0 = 0x40;

The next step is to configure the output of the decoder, specifically, the I/O PORT pin PA3 in this example. This is done by setting the OUTEN bit on the LUT0CTRLA register.

Figure 5-54 5-60. LUTn Control A Register

This translates to the following code.

CCL.LUT0CTRLA = CCL_OUTEN_bm;

By enabling the LUTn output on the I/O pin, the settings for the corresponding pin are overwritten. To complete the setup and the start decoding of the input sequence, the CCL and used LUTs need to be enabled. That is done using the ENABLE bit from the LUTnCTRLA register.

CCL.LUT1CTRLA = CCL_ENABLE_bm;
CCL.LUT0CTRLA |= CCL_ENABLE_bm;

To complete the setup, the CCL module needs to be enabled using a CCL Global Enable bit from the CTRLA register.

Figure 5-55 5-61. CCL Control A Register
CCL.CTRLA = CCL_ENABLE_bm;
Tip: The full code example is also available in the Appendix section.

An MCC generated code example for AVR128DA48, with the same functionality as the one described in this section, can be found here:

5.4 SR Latch

This section describes an application example that uses CCL combinational and sequential logic to implement an SR latch. This functionality can be created using two adjacent LUTs (LUT0 and LUT1) connected through a sequential logic block.
Figure 5-62. Using CCL to Implement an SR Latch

For Set and Reset signals, two pins are used as inputs for LUTs (I/O PORT pin PA1 and I/O PORT pin PC1). That translates to the following code.

CCL.LUT0CTRLB = CCL_INSEL0_MASK_gc | CCL_INSEL1_IO_gc;	
CCL.LUT0CTRLC = CCL_INSEL2_MASK_gc;
CCL.LUT1CTRLB = CCL_INSEL0_MASK_gc | CCL_INSEL1_IO_gc;	
CCL.LUT0CTRLC = CCL_INSEL2_MASK_gc;

In this case, only the input selected for the Input signal needs to be considered when configuring the Truth register for each LUT. For instance, if the signal is active-high and available on LUTn_IN[1], the Truth register will be set to 0x02. If the input signal is active-low, which is the case for many evaluation kits, the Truth register will be set to 0x01. For the selected example, the input signals are active-low, so the Truth register will be set to 0x01 for both LUTs.

CCL.TRUTH0 = 0x01;
CCL.TRUTH1 = 0x01;

The truth table output is a combinatorial function of the inputs. This may cause some short glitches when the inputs change value. These glitches may not cause any problems, but if the LUT output is set to trigger an event, used as input on a timer or similar, an unwanted glitch may trigger unwanted events and peripheral action. In removing these glitches by clocking through the filters, the user will only get the intended output. Each Look-up Table (LUT) in the CCL includes a filter that can be used to synchronize or filter the LUT output.

Figure 5-63. CCL Filter

The selection of the filter option is done by using the FILTSEL[1:0] bits from the LUTnCTRLA register.

Figure 5-64. CCL Filter Options
CCL.LUT0CTRLA = CCL_FILTSEL_FILTER_gc;
CCL.LUT1CTRLA = CCL_FILTSEL_FILTER_gc;

The next step is to connect the LUTs through a sequential logic to create SR latch functionality. The bits in SEQSEL0[3:0] from the Sequential Control (SEQCTRL0) register select the sequential configuration for LUT0 and LUT1.

Figure 5-65. Sequential 1 Control 0 Register

This translates to the following code:

CCL.SEQCTRL0 = CCL_SEQSEL0_RS_gc;

To complete the setup and enable the LUT0 output on the LUT0OUT pin (PA3), the used LUTs and CCL need to be enabled.

CCL.LUT1CTRLA |= CCL_ENABLE_bm;
CCL.LUT0CTRLA |= CCL_ENABLE_bm | CCL_OUTEN_bm;
CCL.CTRLA = CCL_ENABLE_bm;
Tip: The full code example is also available in the Appendix section.

An MCC generated code example for AVR128DA48, with the same functionality as the one described in this section, can be found here:

5.5 Advanced Examples

This section consists of advanced use cases that use the CCL peripheral combined with other several peripherals. These use cases are generated using MCC and are developed on AVR128DA48.

Manchester Encoder

This use case is an implementation of a Manchester encoder using Core Independent Peripherals (CIPs) by following the interaction between CCL, USART and Event System peripherals. The raw data are received via serial communication, encoded using a circuit composed of the CIPs mentioned above, and sent further through a single data wire.

Manchester Decoder

This use case is an implementation of a Manchester decoder using CIPs by following the interaction between CCL, Event System, TCB and SPI peripherals. The encoded data are received through a single data wire. The NRZ (Non-Return-to-Zero) signal and clock signal are recovered using the circuit composed of the CIPs mentioned above. The resulting signals are routed to the SPI peripheral which reads the data. The decoded data are transmitted further via serial communication.

Bi-Phase Encoder

This project is an implementation of a Bi-phase encoder using CIPs by following the interaction between CCL, Event System, SPI and USART peripherals. The raw data are received via serial communication, encoded using the circuit composed of the CIPs mentioned above, and sent further through a single data wire.

Bi-Phase Decoder

This project is an implementation of a Bi-phase decoder using CIPs by following the interaction between CCL, TCA, TCB, USART, Event System and SPI peripherals. The encoded data are received through a single data wire. The NRZ (Non-return-to-zero) signal and clock signal are recovered using the circuit composed of the CIPs mentioned above. The resulting signals are routed to the SPI peripheral which reads the data. The decoded data is transmitted further via serial communication.

Driving a Metronome

The use case consists of a circuit composed of CIPs, which is capable of creating the signals that drive a Switec stepper motor as a metronome. It also adjusts the number of beats per minute of the metronome by reading an input value provided by the user.

SOS Sequence Generator

This use case consists of a circuit composed of CIPs, which operates with the involvement of the core only in the initialization part and generates an SOS sequence signal.

RGB Lighting with WS2812

This use case consists of a circuit composed of CIPs for interfacing the CCL and SPI peripherals with the WS2812 LED.

5.6 References

5.7 Appendix

Logic AND Gate Code Example

#include <avr/io.h>

void PORT_init (void);
void CCL_init(void);

/**
 * \brief Initialize ports
 */
void PORT_init (void)
{
    PORTC.DIR &= ~PIN0_bm;         //PC0 - LUT1 IN[0]
    PORTC.DIR &= ~PIN1_bm;         //PC1 - LUT1 IN[1]
    PORTC.DIR &= ~PIN2_bm;         //PC2 - LUT1 IN[2]

    PORTC.DIR |= PIN3_bm;		   //PC3 - LUT1 output
}

/**
 * \brief Initialize CCL peripheral
 */
void CCL_init(void)
{

//configure inputs for used LUTs
    CCL.LUT1CTRLB = CCL_INSEL0_IO_gc    /* IO pin LUTn-IN0 input source */
                    | CCL_INSEL1_IO_gc; /* IO pin LUTn-IN1 input source */
    CCL.LUT1CTRLC = CCL_INSEL2_IO_gc;   /* IO pin LUTn-IN2 input source */
	
//Configure Truth Table
    CCL.TRUTH1 = 0x80; /* Truth 1: 128 */
	
//Enable LUT0 output on IO pin
    CCL.LUT1CTRLA = CCL_OUTEN_bm;     /* Output Enable: enabled */

//Enable LUTs
    CCL.LUT1CTRLA |= CCL_ENABLE_bm;    /* LUT Enable: enabled */
	
//Enable CCL module
    CCL.CTRLA = CCL_ENABLE_bm;         /* Enable: enabled */
}


int main(void)
{
    PORT_init();
    CCL_init();
    while (1) 
    {
        ;		
    }
}

State Decoder Code Example

#include <avr/io.h>

void PORT_init (void);
void CCL_init(void);

/**
 * \brief Initialize ports
 */
void PORT_init (void)
{

    PORTA.DIR &= ~PIN0_bm;         //PA0 - LUT0 IN[0]
    PORTA.DIR &= ~PIN1_bm;         //PA1 - LUT0 IN[1]
    PORTC.DIR &= ~PIN0_bm;         //PC0 - LUT1 IN[0]
    PORTC.DIR &= ~PIN1_bm;         //PC0 - LUT1 IN[1]
    PORTC.DIR &= ~PIN2_bm;         //PC0 - LUT1 IN[2]

    PORTA.DIR |= PIN3_bm;	        //PA3 - LUT0 output
 
}

/**
 * \brief Initialize CCL peripheral
 */
void CCL_init(void)
{

//configure inputs for used LUTs
    CCL.LUT0CTRLB = CCL_INSEL0_IO_gc    /* IO pin LUTn-IN0 input source */
                    | CCL_INSEL1_IO_gc; /* IO pin LUTn-IN1 input source */
    CCL.LUT0CTRLC = CCL_INSEL2_LINK_gc; /* Linked LUT input source */

    CCL.LUT1CTRLB = CCL_INSEL0_IO_gc    /* IO pin LUTn-IN0 input source */
                    | CCL_INSEL1_IO_gc; /* IO pin LUTn-IN1 input source */
    CCL.LUT1CTRLC = CCL_INSEL2_IO_gc;   /* IO pin LUTn-IN2 input source */
	
//Configure Truth Tables
    CCL.TRUTH0 = 0x40; /* Truth 0: 64 */
    CCL.TRUTH1 = 0x20; /* Truth 1: 32 */
	
//Enable LUT0 output on IO pin
    CCL.LUT0CTRLA = CCL_OUTEN_bm;     /* Output Enable: enabled */

//Enable LUTs
    CCL.LUT0CTRLA |= CCL_ENABLE_bm;    /* LUT Enable: enabled */
    CCL.LUT1CTRLA = CCL_ENABLE_bm;    /* LUT Enable: enabled */
	
//Enable CCL module
    CCL.CTRLA = CCL_ENABLE_bm;         /* Enable: enabled */
}

int main(void)
{
    PORT_init();
    CCL_init();
    while (1) 
    {
        ;		
    }
}

SR Latch Code Example

#include <avr/io.h>

void PORT_init (void);
void CCL_init(void);

/**
 * \brief Initialize ports
 */
void PORT_init (void)
{

    PORTA.DIR &= ~PIN1_bm;          //PA1 - LUT0 IN[1]
    PORTC.DIR &= ~PIN1_bm;          //PC1 - LUT1 IN[1]
	
    PORTA.DIR |= PIN3_bm;	        //PA3 - LUT0 output

}

/**
 * \brief Initialize CCL peripheral
 */
void CCL_init(void)
{

//configure inputs for used LUTs
    CCL.LUT0CTRLB = CCL_INSEL0_MASK_gc	/* LUTn-IN0 input masked */
                    | CCL_INSEL1_IO_gc; /* IO pin LUTn-IN1 input source */
    CCL.LUT0CTRLC = CCL_INSEL2_MASK_gc; /* LUTn-IN2 input masked */

    CCL.LUT1CTRLB = CCL_INSEL0_MASK_gc	/* LUTn-IN0 input masked */
                    | CCL_INSEL1_IO_gc; /* IO pin LUTn-IN1 input source */
    CCL.LUT1CTRLC = CCL_INSEL2_MASK_gc; /* LUTn-IN2 input masked */

//Configure Truth Tables
    CCL.TRUTH0 = 0x01; /* Truth 0: 1 */
    CCL.TRUTH1 = 0x01; /* Truth 1: 1 */

// Configure filter
    CCL.LUT0CTRLA = CCL_FILTSEL_FILTER_gc;      /* Enable filter*/
    CCL.LUT1CTRLA = CCL_FILTSEL_FILTER_gc;      /* Enable filter*/

//Enable sequential logic for LUT0 and LUT1
    CCL.SEQCTRL0 = CCL_SEQSEL0_RS_gc;
		
//Enable LUT0 output on IO pin
    CCL.LUT0CTRLA |= CCL_OUTEN_bm;     /* Output Enable: enabled */
	
//Enable LUTs
    CCL.LUT0CTRLA |= CCL_ENABLE_bm;    /* LUT Enable: enabled */
    CCL.LUT1CTRLA |= CCL_ENABLE_bm;    /* LUT Enable: enabled */
	
//Enable CCL module
    CCL.CTRLA = CCL_ENABLE_bm;         /* Enable: enabled */
}


int main(void)
{
    PORT_init();
    CCL_init();
    /* Replace with your application code */
    while (1) 
    {
        ;		
    }
}