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.
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.
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;
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.
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.
The table below summarizes the INSEL[3:0] options for all inputs.
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.
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.
CCL.CTRLA = CCL_ENABLE_bm;
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.
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.
The table below summarizes the INSEL[3:0] options for all inputs.
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.
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.
CCL.CTRLA = CCL_ENABLE_bm;
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
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.
The selection of the filter option is done by using the FILTSEL[1:0] bits from the LUTnCTRLA register.
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.
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;
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
- ATmega4809 product page: www.microchip.com/wwwproducts/en/ATMEGA4809
- megaAVR® 0-series Family Data Sheet
- ATmega809/1609/3209/4809 – 48-Pin Data Sheet megaAVR® 0-series
- ATmega4809 Xplained Pro product page: https://www.microchip.com/developmenttools/ProductDetails/atmega4809-xpro
- AVR128DA48 product page: www.microchip.com/wwwproducts/en/AVR128DA48
- AVR128DA48 Curiosity Nano Evaluation Kit product page: https://www.microchip.com/Developmenttools/ProductDetails/DM164151
- AVR128DA28/32/48/64 Data Sheet
- Getting Started with the AVR® DA Family
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) { ; } }