The Lost Art of Assembly Language Programming

Cypress introduced it’s first mass market microcontroller in 2001. It used a Cypress designed 8 bit CISC processor running at 24 MHz, with as little as 4 KB Flash and 256 bytes RAM. Wrapped around that was a neat array of programmable analog and digital blocks. This may not sound like much, but with a creative mindset you could get these parts to do amazing things. For instance, I once implemented a complete ultrasonic ranging sensor with full wave analog demodulation in a single PSOC1 as shown below.

PSOC1 Ultrasonic Ranging

With CPU resources at a premium, you had to write tight, efficient code to get the most out of PSOC1. A single C library could consume the entire Flash. Consequently, I wrote a lot of assembly code. That’s not so bad, since I actually enjoy it more than C. There’s a certain elegance to well written, fully commented machine code. In the case of PSOC1, here’s what you had to work with: 5 registers, some RAM and Flash. That’s it. Real Men Write In Assembly.

M8C Architecture

 

We’ll start with simple machine code instruction to make the CPU do something. You can reference the M8C assembly language user guide here for more details. To get the M8C to execute 2+3=5 we write:

mov A,2       ;Load A with 2
add A,3       ;Add 3 to A. Result=5 is in A

We can get fancy by using variables. Let’s add R=P+Q. Assume P is at RAM location 0x20 and Q is at location 0x21, and R is at 0x22

;Initialize variables
mov [0x20],2  ;Load P with 2
mov [0x21],3  ;Load Q with 3

;Add variables
mov X,[0x20]  ;X <- P
mov A,[0x21]  ;A <- Q
adc [X],A     ;X <- P + Q
mov [0x22],X  ;R <- X

The fun thing about assembly is you can always dream up cool ways of doing things in less operations based on the machine’s instruction set. For example, we can simplify the above code as follows:

;Add variables
mov [0x20],[0x22]   ;R <- P
adc [0x22],[0x21]   ;R <- P + Q

In my experience, a good programmer with expert knowledge of the instruction set and CPU resources can always write better code than a compiler. There’s a certain human creativity that algorithms can’t match.

All that being said, I had not seen a good “machine code 101” tutorial for writing assembly in PSOC Creator on modern ARM M0 processors. So let’s walk through one now. We’ll use a CY8CKIT-145 and blink the LED. It’s just what happens to be laying around on the lab bench. Any PSOC4 kit will do.

CY8CKIT-145-40XX

We’ll start by creating a standard project in PSOC Creator, drop a Digital Output pin on the schematic and call it “LED”

Then open the .CYDWR file and drag pin LED to P2[5], since that’s where it is on the LED board. Yours may be in a different place on whatever board you are using.

Now under “Source Files” in the workspace directory you will delete main.c and replace with main.s

Now right clock on “Source Files”, select “Add New File” and select “GNU ARM Assembly File” in the dialog. Rename the file from GNUArmAssembly01.s to main.s

Your workspace ends up looking like this:

So far, so good. Now open main.s, delete everything if it’s not empty and add the following code. This sets up the IDE for M0 assembly architecture

// ==============================================
// ARM M0 Assembly Tutorial
//
// 01 – Blink LED
// ==============================================
.syntax unified
.text
.thumb

Next we need to include register definitions for the chip we are using. These are all from the PSOC4 Technical Reference Manual (TRM)

// ==============================================
// Includes
// ==============================================
.include “cydevicegnu_trm.inc”

Then we are going to do some .equ statements, same as #define in C. This identifies the Port 2 GPIO data register plus bits for the LED pin in on and off state

// ==============================================
// Defines
// ==============================================
.equ LED_DR,CYREG_GPIO_PRT2_DR          // LED data reg address
.equ LED_PIN,5                          // P2.5
.equ LED_OFF,1<<led_pin                 // 0010 0000
.equ LED_ON,~LED_OFF                    // 1101 1111

Now you add the right syntax to set up main()

// ==============================================
// main
// ==============================================
.global main
.func main, main
.type main, %function
.thumb_func

Finally we add the code for main, which is pretty simple:

main:
ldr r5,=LED_DR      // Load GPIO port addr to r5

loop0:
ldr r6,=LED_ON      // Move led data to r6
str r6,[r5]         // Write r6 data to r5 addr

ldr r0,=0xFFFFFF    // Argument passed in r0
bl CyDelayCycles    // Delay for N cycles

ldr r6,=LED_OFF     // Move led data to r6
str r6,[r5]         // Write r6 data to r5 addr

ldr r0,=0xFFFFFF    // Argument passed in r0
bl CyDelayCycles    // Delay for N cycles

b loop0             // Branch loop0

.endfunc            // End of main
.end                // End of code

One thing to note: The function CyDelayCycles is defined CyBootAsmGnu.s. Any function in assembly gets its arguments passed by the first 4 registers r0,r1,r2 and r3. Before calling the function you simply load r0 with the argument then do a bl (branch with link). This is also why I avoided the first 4 registers when messing with LED data. If you’re interested in doing more with ARM assembly, definitely read the Cortex M0+ Technical Reference Manual. It’s a great primer for the M0+ instruction set.

That’s it. End result is a blinking LED. Cool thing is you can use PSOC Creator with all it’s nice features, but sill access the power of machine code.

You can get the project ZIP file here.

Regards
Darrin Vallis

Serial Wire View with PSOC4

I use PSOC4 to invent all kinds of unique solutions for customers. Usually, they want them field upgradeable to deploy new features or fix bugs. Fortunately Cypress has a great I2C boot loader to meet this need, so I use the heck out of it.

Cypress has a great debugger built into PSOC Creator which fully supports all the ARM Serial Wire Debug protocols such as breakpoints, single step, memory, register viewing etc. However, when you are running a boot loader the debugger does not work! Why not? Because with a boot loader there are two applications resident in PSOC4: The boot loader and application. This is not supported by Cypress implementation of SWD.

Where does this leave you, the intrepid code developer, when debugging a boot loader project? Personally, I have used all kinds of methods: debug UART interface, debug I2C interface, bang out states on pins, debug Bluetooth interface … and on and on. You get the idea. All these methods burn a communications interface and require extra pins on the chip. Sometimes that’s not possible.

The issue recently came to a head when a customer very nearly in production experienced a boot loader failure. One system out of a few thousand was “bricked” when they tried to field  update in the lab. Their pinout is frozen, they can’t add new hardware so how do we look inside PSOC4 and see what’s going on?

I woke up at 2 AM and thought “Ah Ha! SWV!” (Yes, I Am A Geek) Serial Wire View is an ARM native debug protocol that let’s you XRAY the insides of any ARM MCU with the right interface. SWV is a protocol which runs on the SWD pins (clock and data) but also needs the Serial Wire Output (SWO) pin. Cypress left the SWO pin and associated IP off of PSOC4 to save die cost, foiling my great idea. Brief interlude to drink and bang head on desk.

Fortunately, I don’t give up easily. At least my subconscious does not. Woke up the next night thinking “Ah Ha!” again. Wife was mildly annoyed, but tolerates my idiosyncrasies.

Cypress has a nice software UART transmitter implementation. I shamelessly stole it, modified for my purposes and created a custom component. (It’s pretty easy to do this by the way) Baud rate was modified to 230 KBps and the output pin forced to a specific pin with a control file.

Once the component is in place, you can use its _DView_Printf( ) API call to display any debug data. Here is an example:

More about that output pin. Cypress sells a tool for programming and debugging PSOC called CY8CKIT-002, aka MiniProg3. The programming connector consists of VDD, GND, reset, SWD clock and SWD data as shown below.

Since we can’t use SWD protocol for debugging anyway, we can change the pins from SWD to normal GPIO. The pins still function for programming. By default they are in SWD mode as shown.

Going to the system tab of the .CYDWR file, we can change them to GPIO.

Once we do that, the pins look like this. Here’s the trick. We now assign the TX output of our DTView component to pin 3[2], which is available  on the SWD programming header, pin 5.

Can you see where we are going with this? Printf( ) data is now coming out of PSOC4 on pin 3[2], easily accessible on our debug header. This is where MiniProg3 comes in. It can actually receive data as a 230 KBps RX UART on its XRES pin. Weird, right? By building a simple interface cable we can get the data from your debug header into MiniProg3.

MiniProg3 XRES —— SWD HEADER pin 5

MiniProg3   GND —— SWD HEADER pin 2

However, MiniProg3 does not show up as a COM port on your PC, so how do we the data? It needs to be accessed by a host application running the PP_COM API. This is documented under PSOC Programmer Component Object Model COM Interface Guide, Cypress specification 001-45209. If you installed PSOC Creator or Programmer, this document is actually on your PC under C:\Program Files (x86)\Cypress\Programmer\Documents. Engineers don’t like to read instructions. Amazing what you can find when you do.

I wrote a simple  console application which opens MiniProg3 using PP_COM, retrieves data from the serial RX pin via USB and displays it like a simple terminal program. Voila! You now have a serial debugger that works for any PSOC4 project using MiniProg3 as your USB to serial dongle.

Customer was really happy with this. We were able to immediately see his problem and fixed it in about 5 minutes.

Finally, here are all the source files

DTView Firmware : PSOC Creator example project and DTView component

DTViewer Binary : Installer for DTViewer console

ViewerSource : Complete source code for DTViewer console (Requires Visual Studio 2015)

That’s all. Have fun with the new debugging tool.

DTV