C Forth Equivalent

This page lists two equivalent actions by the following programming languages and comments on what they mean:

  • Forth

  • C

Forth

$160  constant  TA0CTL  \ Timer0_A3 Control

: TA0CTL_MC<< ( x -- x ) 4 lshift ; \ Timer A mode control 1
    0 constant MC_0      \ Timer A mode control: 0 - Stop
    1 constant MC_1      \ Timer A mode control: 1 - Up to CCR0
    2 constant MC_2      \ Timer A mode control: 2 - Continous up
    3 constant MC_3      \ Timer A mode control: 3 - Up/Down
: TA0CTL_TASSEL<< ( x -- x ) 8 lshift ; \ Timer A clock source select 1
    0 constant TASSEL_0      \ Timer A clock source select: 0 - TACLK
    1 constant TASSEL_1      \ Timer A clock source select: 1 - ACLK
    2 constant TASSEL_2      \ Timer A clock source select: 2 - SMCLK
    3 constant TASSEL_3      \ Timer A clock source select: 3 - INCLK


 MC_2 TA0CTL_MC<<
     TASSEL_1 TA0CTL_TASSEL<<
        + TA0CTL bis!

Lets break this down ?

Some knowledge of Forth is required when reading this.

Name

Description

MC_2, TASSEL_1

input parameter constants

TA0CTL_MC<<

Word, expects one input parameter and returns the same data after it is shifted left by the number of bits listed in the Word definition above

TA0CTL_TASSEL<<

Word, expects one input parameter and returns the same data after it is shifted left by the number of bits listed in the Word definition above

+

TA0CTL_MC<< + TA0CTL_TASSEL<<

TA0CTL

Timer_A control register, address constant

bis!

Takes two input parameters, data and address. High bits in the data are stored at the address. Alternately bic! CLEARS any High bits in the data at the address.

Forth Source Values

I value the following attributes in my source code, stated in order of importance, and I feel that my source above satisfies many of these ?

  • Readability

  • Maintainability

  • Traceability

  • Serviceability

Attribute

Description

Readability

No clever code, no magic numbers. This readability is for technicians like me.

Maintainability

10 years later, when the device is broken in some way, it needs to be fixed, the code has to make this part easy, not harder.

Traceability

Register names have to be MCU manufacturer technical manual compliant so hours don’t have to be wasted decoding ‘magic numbers’ and what they mean.

Serviceability

The source should have built in Test Words, such as make a stepper motor rotate once, clockwise, just to test it isn’t jammed.

The C Programming Language

Taken from https://vivonomicon.com/2018/11/28/basic-low-power-design-sleepy-pwm-on-the-msp430/ who writes a great article!

TA0CTL  |= (TASSEL_2 | MC_1);

Lets break this down ?

TASSEL_2 + MC_1 are Binary ORed with each other, the result is then Bitwise inclusive ORed with the data from reading register TA0CTL and the new result is then saved back into TA0CTL

In the accompanying text, the author explains:

The MC_1 setting in TA0CTL tells the timer to count up to whatever value is stored in channel 0 before “ticking over”, so this channel will control the base frequency of our PWM signal. The TASSEL_2 setting just tells the timer to use the core system clock for its base frequency.

If he hadn’t, you wouldn’t have a clue what the source code actually does without the accompanying documentation. Even so, you have no idea what other options are available.

What Lurks Below ?

However, C’s ease comes at the cost of hidden complexity. As the Author says:

The msp430.h header file is included with the toolchain, and it is similar to the STM32 device header files. It defines almost-legible names for the chip’s register addresses and settings; P1DIR is not exactly easy to read, but it is much nicer than (volatile uint16_t *)0x0022

Have you looked in a .h or .include file before ?

You may be in for a shock because all the complexity of making embedded C appear simple, is hidden in these files.

stm32l051xx.h

Do you have any idea what the code below does ?

As a technician, my care factor is zero. Personally I’d rather read the timer technical manual and understand the hardware, then provide my own abstractions as my Forth code moves from configuration of hardware to the much higher level Problem Definition Language (PDL). Think of our classic washing machine “on fill wash empty spin off”.

/******************* TIM Instances : output(s) available **********************/
#define IS_TIM_CCX_INSTANCE(INSTANCE, CHANNEL) \
    ((((INSTANCE) == TIM2) &&                  \
     (((CHANNEL) == TIM_CHANNEL_1) ||          \
     ((CHANNEL) == TIM_CHANNEL_2) ||          \
     ((CHANNEL) == TIM_CHANNEL_3) ||          \
     ((CHANNEL) == TIM_CHANNEL_4)))           \
    ||                                        \
    (((INSTANCE) == TIM21) &&                 \
     (((CHANNEL) == TIM_CHANNEL_1) ||         \
      ((CHANNEL) == TIM_CHANNEL_2)))          \
    ||                                        \
    (((INSTANCE) == TIM22) &&                 \
     (((CHANNEL) == TIM_CHANNEL_1) ||         \
      ((CHANNEL) == TIM_CHANNEL_2))))

The things we do for a painless life ?

/******************************************************************************/
/*  For a painless codes migration between the STM32L0xx device product       */
/*  lines, the aliases defined below are put in place to overcome the         */
/*  differences in the interrupt handlers and IRQn definitions.               */
/*  No need to update developed interrupt code when moving across             */
/*  product lines within the same STM32L0 Family                              */
/******************************************************************************/

/* Aliases for __IRQn */

#define RNG_LPUART1_IRQn               LPUART1_IRQn
#define AES_LPUART1_IRQn               LPUART1_IRQn
#define AES_RNG_LPUART1_IRQn           LPUART1_IRQn
#define TIM6_DAC_IRQn                  TIM6_IRQn
#define RCC_CRS_IRQn                   RCC_IRQn
#define DMA1_Channel4_5_IRQn           DMA1_Channel4_5_6_7_IRQn
#define ADC1_IRQn                      ADC1_COMP_IRQn

/* Aliases for __IRQHandler */
#define RNG_LPUART1_IRQHandler         LPUART1_IRQHandler
#define AES_LPUART1_IRQHandler         LPUART1_IRQHandler
#define AES_RNG_LPUART1_IRQHandler     LPUART1_IRQHandler
#define TIM6_DAC_IRQHandler            TIM6_IRQHandler
#define RCC_CRS_IRQHandler             RCC_IRQHandler
#define DMA1_Channel4_5_IRQHandler     DMA1_Channel4_5_6_7_IRQHandler
#define ADC1_IRQHandler                ADC1_COMP_IRQHandler

Summary

Both languages will produce the same machine code in the end, after all they’re controlling the same microprocessor.

C

C language appears simple and short but hides a lot of complexity. Literate programming is difficult and doesn’t look good with a lot of text explaining the purpose. Automated documentation like Doxygen doesn’t really add any more clarity to embedded C.

So authors also have to write a accompanying text document, and if they don’t do that for any reason (hate writing docs or don’t have time) the embedded source is always incomplete for analysis.

Forth

My Forth examples contain all the information in the source, what’s more it is automatically generated from the SVD. This process guarantees