Soft PLD

Abstract: This article describes how to efficiently implement in software for MCU (Micro-Controller Unit) an emulation of PLD (Programmable Logic Device).


PLD are made to integrate into a single package, the discrete combinational and sequential logic components with which printed circuit boards are often crowded.

When the logic response time is loose, and an MCU is also available, with enough GPIO (General-Purpose Input-Output) pins, programmers usually prefer to integrate the logic in software.

Logical equations, which specify the states of output pins (and of internal logical variables) in function of combinations of the states of input pins (and of internal logical variables), are often naively, and unefficiently, implemented by scalar bit-operations (read or write a single bit, and use tests and conditional jumps to combine bits, as do && and || operators in C).

I describe hereunder a programming technique, inpired from PLD structure, to efficiently implement discrete logic in MCU software using its regular bit-parallel operations (read or write one word, combine two words into a result word with word-wide logical instructions).

PLD Structure

PLD are traditionnally composed of input and output pins, interconnected by a programmable, regular network of multiple-input logic gates and flip-flops.

Each input pin, and each flip-flop, drives a buffer and an inverter, which outputs are usually represented as (complementary pairs of) vertical lines.

Multiple-input AND gates, usually represented as horizontal lines, each take one input per complementary pair of vertical lines, which may be connected to either vertical line of the pair, or to a logic 1 (AND neutral, to ignore the pair), depending on a programmable non-volatile multiplexer.

For each PLD output pin, a multiple-input OR gate takes as inputs a subrange of the multiple-input AND gates, and drives a D-type flip-flop clocked by the PLD clock input pin, and asynchronously initialized by the PLD reset input pin, either high or low depending on a programmable non-volatile multiplexer.

Each PLD output pin is connected to either the output or the input (bypass) of its flip-flop, depending on a programmable non-volatile multiplexer.

Therefore each PLD output pin state is the sum (multiple-input OR gate), either registered (flip-flop) or purely combinational (bypass), of several programmable products (multiple-input AND gates) of the input pins and flip-flops.

Software Mapping of PLD Structure

The PLD clock cycle is mapped onto a periodically executed MCU subroutine hereunder called "PLDcycle".

The PLD input pins are mapped onto an MCU memory word hereunder called PLDin, into which MCU input pins states are copied at the beginning of every PLDcycle, during which they remain stable (whereas MCU input pins states may change).

The PLD output pins are mapped onto another MCU memory word hereunder called PLDout, initialized null at the beginning of every PLDcycle, changed by bit-set instructions during the PLDcycle execution as described hereunder, and copied to the MCU output pins at the end of every PLDcycle.

The PLD flip-flops are mapped onto bits of the PLDin and PLDout memory words: their initial state is setup in PLDout before the first PLDcycle execution; then at the beginning of every PDLcycle, flip-flops "new states" are copied from PLDout into PLDin; then during every PLDcycle, flip-flops current output state is read from PLDin and their current input state is bit-set into PLDout as for PLD output pins.

The PLD multiple-input AND gate is mapped onto the MCU's ALU (Arithmetic and Logic Unit) "zero" status-flag, which is one only when the ALU result bits are all zero.

The programmable input multiplexers for each multiple-input AND gate are implemented with two successive bit-parallel logical instructions:

The output bits of the AND instruction are therefore zero for: and the MCU "zero" status flag resulting from the AND instruction execution, will be in the same state as the equivalent PLD multiple-input AND gate.

The PLD multiple-input OR gate is implemented with, for each input, one conditional bit-set instruction into the PLDout word, executed if the MCU "zero" status flag is one after the AND instruction execution. Therefore, every PLDout bit will remain null (as initialized at the beginning of every PLDcycle) unless set to one by a bit-set instruction executed during the PLDcycle.

PLDcycle Subroutine Coding

Summing up the previous chapter, the PLDcycle subroutine should look in C like:

  void PLDcycle(void) {
    static int  PLDout = INIT;  // INITial value for the flip-flop bits
    int PLDin = PLDout & MASK;  // MASKbit=1:flip-flop, MASKbit=0:input
    PLDin |= readPLDin();       // sample MCU input pins into PLDin
    PLDout = 0;                 // initialize PLD multiple-input OR gates

    // for each PLD multiple-input AND gate, there will be one such line where:
    // the constant POS has a one bit for every non-ignored non-inverted bit,
    // the constant NEG has a one bit for every non-ignored     inverted bit,
    // the constant OUT is the destination bit index in PLDout:
    if((PLDin ^ POS) & (POS | NEG) == 0) { PLDout |= 1<<OUT; }

    writePLDout(PLDout);        // finally update MCU output pins.
The AND-gate lines look better factorized with the help of a few macros:
  #define PLDline(O,P,N) if( (PLDin^(P)) & ((P)|(N)) ==0) {PLDout |= 1<<(O);}
  #define i0 (1<<0)
  #define i1 (1<<1)
  #define i2 (1<<2)
  #define i3 (1<<3)
  #define i4 (1<<4)
Example use of the PLDline macro:
    PLDline(1, i4+i0,   i2);  // o1 =  i4      &~i2      & i0
    PLDline(1, i3,   i4+i1);  //    | ~i4 & i3      &~i1     ;
    PLDline(2, i2+i1,    0);  // o2 =            i2 & i1     ;
It's easy to write, and the resulting code is still efficient because constant expressions are reduced at compile time into single constants, giving for the three above lines, after macro-expansion and reduction:
    if((PLDin ^ 0x11) & 0x15 == 0) { PLDout |= 0x02; }
    if((PLDin ^ 0x08) & 0x1A == 0) { PLDout |= 0x02; }
    if((PLDin ^ 0x06) & 0x06 == 0) { PLDout |= 0x04; }
where each line is usually assembled into five MCU instructions:
  1. bit-parallel-copy PLDin into a work register,
  2. bit-parallel-xor constant into work register,
  3. bit-parallel-and constant into work register,
  4. if non-zero, jump over next instruction,
  5. bit-set constant bit into PLDout.