Decoding Serial Busses from Digital Oscilloscope

Introduction

 Using a Digital Oscilloscope was a premium tool in the past; although professional equipment is still a high cost if considered for amateur use, the cost of equipment has fallen the last years allowing more people to get these instruments.

 

In today’s world of microcontrollers and IoT, it is not uncommon to debug serial low-speed buses, like RS232, SPI and TWI (I2C). Hobbyists and professionals need to decode manually each bit knowing the protocol. This is natural to do if you are at the first debugging stages where you need to check electrical integrity on a new design, but it gets tedious if you need to concentrate on the protocol. Purchasing a decoding module for these oscilloscopes increase the cost and many times is not an option for a one off project.

 

The good thing with such instruments is that they usually provide some connectivity with serial, ethernet or USB connection. Usually apart from any web based control you have the option to program the instrument through the SCPI interface. You may use any language you want if you want to access the instrument as long as you have installed the relevant VISA libraries/drivers like National Instrument’s NIVISA, or the Tektronix TekVISA (see below for links).

 

For those unfamiliar with SCPI (Standard Commands for Programmable Instruments), it is a standard protocol to access instruments initially based on IEEE-488 connection. Later on as more instruments used serial or ethernet connection, the required GPIB card has became obsolete and made things easier and more straight forward. The instructions available are mostly universal, but each instrument can complement these with any extensions needed to support the features unique on it.  

 Examples of SCPI commands:

*IDN?
ACQUIRE:STATE STOP

I started programming many instruments that way in the past using C/C++ but after switching to python I forgot about any other language in the host machine. Scripting languages offer an easy go process especially for string manipulation etc. For this reason I wrote a python script to capture signals from the oscilloscope and decode them.

In summary to access the SCPI port of a connected instrument you need the following:

1. Python Interpreter (code is written in 2.7 generation) (https://www.python.org/downloads/ )

2. TekVISA/NIVISA (or any other corresponding VISA library) package (https://www.ni.com/visa/ , http://uk.tek.com/oscilloscope/tds7054-software )

3. PyVISA module (https://pyvisa.readthedocs.io/en/stable/  )

4. The Capture/decode python script.

Now under Windows you may use the executable which is a compiled python script (with GUI2Exe). In this case you will need only the VISA installation and the tool itself (no need for python+pyvisa as these are included in the compiled program).

 

How it works

 The tool captures the signals from the oscilloscopes in a CSV file. As I use an old TDS3012B DPO from Tektronix, I only have 2 available channels to capture. This is fine for capturing RS232 or TWI signals, but for SPI you may need to perform multiple runs to capture 2 data lines and CS along with the clock, with you ending up changing probes for each run, but this is better from nothing.

Oscilloscopes with more channels would of course capture more signals and avoid this hick-up. The script is made and tested on this particular oscilloscope, but it should work in most other instruments maybe with slight modifications. If you need to capture more channels then you still need to modify the script to save the extra channels on the CSV and in addition to add to the SPI decode module the extra traces to decode. But this is why we have open source right?

Keep in mind that the script decodes from the CSV file (and not directly from the instrument). Thus if you have old (or test) captures you may instruct it to use these instead of accessing an instrument. This helps for off-line decoding or testing the script.

 After the signals are captured, the script performs a thresholding according to the set logic standard like TTL, CMOS, LVCMOS etc.

 Then it decodes the signal according to the requested protocol and displays the decoded output on screen.

To keep the script simple, we setup the oscilloscope manually to capture the signals fully (with the required time-span, sampling etc) and we leave it running. Triggering should be to Normal mode (not Auto). When we need to capture the waveform we can either run the script which stops acquisitions or we can stop manually and still use the script to acquire and decode the signals.

 Let’s see some examples.

RS-232 Decoding

Running the command:


DSOCapture.py -b RS232 -l TTL -a 115200 -i CaptureRS422_LVCMOS.csv -n 0

We decode a file with data already acquired to a file.

We need to define the baud rate, the voltage level and polarity (positive or reversed).

RS232 Capture
RS232 Command Prompt Capture Example

The decoder displays the resulted characters along with the Start-Stop and Parity bits. In addition a VCD file is exported that we can view with GTKWave. 

rs232_wave
RS232 Waveform Capture Example

 

TWI Decoding

In TWI mode the parameters are more simple as TWI is clocked so we do not need to define sampling period for each character.


DSOCapture.py -v -b I2C -i CaptureI2C.csv
twi_dos_capture
TWI Command Prompt Capture Example

Again the results are shown in the Command Prompt, showing Start-ACK-Stop conditions.
In addition the VCD file provides a graphical view.

twi_wave01
TWI Waveform Capture Example

 

twi_wave02
TWI Waveform Capture Example Detail

 

SPI Decoding

 
In this example we acquire the signal from the instrument (which is the more common way to do it). Every acquisition will store the .csv file with the data, if you need it. However you will need to copy it since the next acquisition will overwrite it.

DSOCapture.py -v -b SPI -t 3 -visa -i TCPIP::192.168.2.226::INSTR -f capture.csv
spi_dos_capture
SPI Command Prompt Capture Example

 

spi_sd_wave
SPI Waveform Capture Example

 

  

Development Methodology

Now the interesting thing is that I used TDD (Test Driven Development) methods to create this script. To help me in this process I used the pytddmon.py script. So when developing you just need a command prompt or shell in the directory holding the script and run pytddmon.py (should be at the same directory). See picture below. 

PyTDDmon Example
PyTDDmon View

 

Python Test Code
Python Test Code

 

You may download pytddmon from here. Alternatively you may use:

pip install pytddmon

 

Next thing is that I created the test cases for each module. Initially I used real data captured on my oscilloscope or used similar values to test the various cases. For example thresholding the incoming stream is more challenging than just compare and output a logic value. As you pass through the transition phase from 0-1 or 1-0 then some values may trigger multiple transitions on each edge. In this case you need to perform hysteresis ( link ) to avoid it (as the electronics do as well).

 After the signals have been interpreted to logic levels (or edges as well, as TWI uses the edges), then the thresholded outputs are passed to the corresponding serial bus analyzer.

 

Limitations

The script does work well but there are limitations mainly due to the hardware (Digital Oscilloscope) which is worth to mention.

RS232

The problem with RS-232 is that it is an asynchronous transmission. To better state the problem, there is no clock transmitted or derived from the data. It is assumed that the receiver has a similar clock within a certain range to capture and decode the transmission.

If you need to capture a long stream of bytes and the scope does not have big memory you will find yourself limited on what can be captured. As the scope’s memory is not sufficient to hold a longer time period with much detail the decoded data may be scrambled (usually after a few bytes). To overcome this you may need to decrease the time span of the data or use an instrument with sufficient memory capacity to hold accurately the signal transitions.

TWI

This kind of bus uses edge signalling to determine certain protocol states (like Start/Stop conditions). If the sampling of the scope is not sufficient you may get bad decoding of the signals. Although not so severe as RS-232 protocol, this will happen only if you stress the instrument.

SPI

SPI bus is a clocked transmission so sampling is not much an issue as in the other protocols. However because you usually need three lines (SCK, MISO, MOSI), you need extra effort to read all lines with a two channel scope. As this script was built around TDS3012B, it captures only two channels. If you have an instrument with more channels available I would recommend to modify the script and capture the extra channels.

 Summary

Improvements to the script can be performed, but as I am busy doing other things, I currently do not plan to do any additions. In any case any ideas are welcome and at some point may integrate them.

I created this script some time ago, initially for TWI, then I added RS232 and finally the easiest part SPI. I completed the whole script in about a two week time (with full functionality) and it was pretty easy. If I had to work without TDD, I would need to capture all the possible variations on my scope and debug a much larger codebase at the time. 

 You may find a compiled Python executable here

You may find the source tree in BitBucket.

 

COFILOS SD Card Driver

SD Card Driver Development on COFILOS

Last year I completed a major milestone for COFILOS and the Perseus board. I completed a fully TDD written SD Card Driver.

You may check the following video: COFILOS SD Card

I was a big pain to write it, even though I had guide code from the internet and enough relevant information. The outcome is a very readable and organized code.

The initialization phase is very complex (and took the lion’s share of time to implement) is shown here:



/*!
* \brief Device Start (Stub)
* @param pstDriver_ : Pointer to Device structure
* @return 0 (SUCCESS), 1: Fail (Could not open SPI driver)
*/
BOOL f_DriverSD_Start(void *pstDriver_ )
{
type_stDRIVERSD *pst_SD;
INT8U v_error;
INT8U v_PhysDevID;
BOOL v_retval;

pst_SD = (type_stDRIVERSD *) pstDriver_;

if (pst_SD->stDriver.eState != DRIVER_CLOSE) return 1;
if (pst_SD->f_HAL_HookCardPresent == NULL) return 1;
if (pst_SD->f_HAL_HookCardPresent() == 0) return 255;

v_retval = 1; /* default is failure */

f_DriverSDHelper_InitSPI(pst_SD);

f_DriverCoFILOS_Open((PINT8) "SPI.Driver", 0xFF, (DRIVER_STRUCT *) &pst_SD->st_SPI);

Driver_Start((DRIVER_STRUCT *) &pst_SD->st_SPI);

/* Acquire Mutex, we need uninterrupted SPI access */
Driver_Control((DRIVER_STRUCT *) &pst_SD->st_SPI, CMD_SET_SPIMUTEX_ON, NULL);

f_DriverSDHelper_ResetSD(pst_SD);

/* set default value for Unknown Card Type */
/* Assuming initially that all is good */
v_PhysDevID = pst_SD->v_PhyVolumeID;
st_SDPhysical[v_PhysDevID].v_UnknownCard = eSDUn_None;
pst_SD->v_CardType = eSDCardUnknown;

/* set CS, clocked */
f_DriverSDHelper_SelectDevice(pst_SD);

v_error = f_DriverSDHelper_SendCMD0(pst_SD);
if (v_error == 0xFE)
{
  /* Unknown Card */
  /* TODO: Unknown Card */
  f_DriverSDHelper_UnknownDevice(pst_SD->v_PhyVolumeID, eSDUn_CMD0);
}
else
{
  v_error = f_DriverSDHelper_SendCMD8(pst_SD);

  if (v_error == 0) /* SDV2 Byte or Block */
  {
    v_error = f_DriverSDHelper_SendACMD41(pst_SD, 0x40000000);
    v_error = f_DriverSDHelper_SendCMD58(pst_SD);

    if (pst_SD->v_CardType == eSDCardSDv2Byte)
    {
      v_error = f_DriverSDHelper_SendCMD16(pst_SD);
      st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv2Byte;
    }
    else
    {
      st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv2Block;
    }
  }
  else if (v_error == 0xFE) /* SDv1 or MMCv3 or Unknown ? */
 {
   st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv1;
   v_error = f_DriverSDHelper_SendACMD41(pst_SD, 0x00000000);
   if (v_error != 0)
   {
     /* MMCv3 ? */
     v_error = f_DriverSDHelper_SendCMD1(pst_SD);
     if (v_error == 0)
    {
      st_SDPhysical[v_PhysDevID].v_CardType = eSDCardMMCv3;
    }
    else
    {
      f_DriverSDHelper_UnknownDevice(pst_SD->v_PhyVolumeID, eSDUn_CMD1);
    }
   }

   if (v_error == 0)
   {
     v_error = f_DriverSDHelper_SendCMD16(pst_SD);
   }
  }
  else
  {
    /* Unknown card */
    f_DriverSDHelper_UnknownDevice(pst_SD->v_PhyVolumeID, eSDUn_CMD8);
  }
}

/* if Card is SDv1 or better then acquire CSD/CID */
switch(st_SDPhysical[v_PhysDevID].v_CardType)
{
   case eSDCardSDv1:
   case eSDCardSDv2Block:
   case eSDCardSDv2Byte:
     f_DriverSDHelper_SendCMD9(pst_SD);
     f_DriverSDHelper_SendCMD10(pst_SD);
     break;
  default:;
}

/* Deassert CS, clocked */
f_DriverSDHelper_DeSelectDevice(pst_SD);

/* Release Mutex, finished exclusive SPI access */
Driver_Control((DRIVER_STRUCT *) &pst_SD->st_SPI, CMD_SET_SPIMUTEX_OFF, NULL);

if (st_SDPhysical[v_PhysDevID].v_CardType == eSDCardUnknown)
{
  v_retval = 1;
}
else
{
  st_SDPhysical[v_PhysDevID].v_DriverState = eSD_Started;
  v_retval = 0;
  pst_SD->st_SPI.v_ConfigReg = 0xA002; /* increase clock to 24MHz */
}
st_SDPhysical[v_PhysDevID].v_OpenCnt = 0;

return v_retval;

}

After completing the TDD phase on my host (PC) I needed to run my tests on the actual target (ColdFire) microcontroller.

In the next picture you may see the hardware setup along with my programmer. I used a micro-SD of 8GB from Kingston for test.

sd_board

I had already captured the sectors (and image) of the SD card and saved them on my hard-drive. I wanted to be able to compare what the microcontroller would read with the actual data stored.

After having my target run the code, I opened a PuTTY terminal to connect with my virtual COM on my target. Then through the CLI (Command Line Interpreter) I mounted the SD card. I issued the SDInfo command and the data provided the SD card “geometry”.

sd_cli_init

I then read sector zero (or MBR for FAT file system). I compared the data with the image capture to confirm reading of the same piece of information.

sd_cli_rdsec0000

 

The first FAT sector (0x2000) was also read and verified. I had to check it as I needed to run a FAT File System Handler to read the SD Card at a later stage (not very later, though).

sd_cli_rdsec2000

Then the write tests started. My write command in CLI supports a single 32-bit value write currently. This is sufficient at the moment to verify a sector write operation. So I used sector 1 which is unused to write the value 0x1234567

sd_cli_wrsec0001

Next a read on sector 1 is performed to check that the sector was written. And voila! Notice the Big-Endian write (0x78 at byte 0, instead of 0x12).

sd_cli_wrsec0001b

 

At the same time during debugging I used my Tektronix TDS3012B Digital Oscilloscope and a Python script to capture and decode the SD SPI data.

spi_capture

The script outputs also a VCD format file suitable for use with GTKWave. Thus I could see and analyse the data, both analog and digital.

spi_sd

Due to the TDD methods used the debugging phase on the target was very fast. With a few iterations and the help of the scope and the Python scripting tool the SD card driver worked well.

 

 

 

 

 

 

To RTOS or not to RTOS?

To RTOS or not? (to RTOS)

 

Summary

Often in the embedded world the question of using a Real-Time Operating System (RTOS) or not, is the big question amongst engineers. The answers found on-line are usually biased opinions without metrics or scientific support of the argument. They usually state the advantages or disadvantages over the classic round-robin systems. The truth is that engineers prefer and like evidence instead of heuristics. I will try to answer this, as I did for myself. I believe this small guide will help decide if an RTOS is worth the effort or not. 

Task Schedule
Example of Task Schedule in Pre-Emptive System

 

Many times the same question has been asked by embedded systems engineers, regarding whether they should use an RTOS or not.

 

Requirements of Embedded Systems

The embedded systems are often called Real-Time Systems and the terms are used interchangeably although this may not always be the case. Ie. There are embedded systems that may not be real-time. Think for example of a thermometer. There is an embedded processor, but failing to measure the temperature in time does not have an impact. 

One misconception is that Real-Time means very fast. This is a misleading interpretation. Real-Time means deterministic. For example if an event happens, our system needs to respond within a time limit. Depending on the application this time-limit will vary. In addition if multiple events are processed and all have the requirement of real-time then every event should be processed according to its own deadline.

As you may see we stress the property of time. Embedded systems may have other restrictions as well like memory footprint etc. but because here the resource we need to analyze and RTOSes handle it, is time.

 

Round-Robin

Round-Robin systems may come in two varieties. Fixed execution, or scheduled.

Round Robin AVRILOS
Example of Round Robin System

Fixed execution is the system which is hard coded. The main loop calls a list of functions (tasks) which each one process their own events.


Void Main(void)
{
   SystemInit();
   For(;;)
   {
     SerialPort();
     ProcessData();
     SetIO();
     SysTick();
   }
}

Obviously the tasks should not block on waiting, but they should rather return. This might add some complexity but in general this is probably the easiest method to build the system.

 

Scheduler

In this case each function is executed on demand. There is a top scheduler which observes which task has an event to process. Each task has also its deadline. Then the scheduler executes the best possible sequence of the previous tasks, according to their priority. This adds more complexity for the advantage of improving schedulability. Each time a task finishes the scheduler runs and decides which will the next task be.

This simple approach has a problem. What if, one of the tasks needs much processing time that upon returning to the main loop or scheduler, another task may have lost its deadline? Maximum response time in this case is the worst execution (till CPU release) of the set of all tasks. On average you may get better response time than Round-Robin systems but the average value cannot be used for real-time systems.

 

What is RTOS?

RTOS tries to solve the schedulability problem by pre-empting the tasks. This means that each task thinks that runs alone into the system without interruption. The scheduler will pre-empt each task according to some rules and will return back at the same point to continue when all the higher priority events have been processed. Although it might seem too complex, it is not so difficult to be done. Looking at the code of some RTOSes you will understand the logic behind it. Beware thought that many RTOSes are not code friendly. Ie. The code is not well written in respect to reading. For me this is what I consider one of the parameters of choosing an RTOS. Even if the RTOS is not perfect, if you have the source and it is readable you can fix or improve things. If the code is unreadable, then it is far more difficult to change it.

In fact even Round-Robin systems do pre-emptions! Can you guess it? The interrupts. The interrupts do exactly the same; However these are considered hardware priority (which is true) and limited in capacity or numbers. Thus actually RTOSes add one more layer of software pre-emption.

 

Comparison

So why not everyone use an RTOS? There are many reasons.

Example of RTOS Driver
RTOS COFILOS

 

First there is complexity. Using an RTOS may require additional code (like protection mutexes, semaphores, messages etc), that were not needed before.

Second the question of which RTOS to choose is not simple. Some are free, some need licenses and are expensive; others have a large footprint, or no do not provide source code.

Third are resources. What if your memory requirements need a very low footprint system or time constrains prohibit such systems? Maybe an RTOS cannot fit?

And the list goes on.

 

Solution?

And that’s how the question described in the beginning starts. Seems that there is no specific engineering parameter that would pin-point if we really need an RTOS or not. But let’s go back to the principles of decision. What all systems try to do? Share the CPU time resource. Is RTOS better in schedulability in respect to the other non pre-emptive systems?

Actually there is a very good principle that helps us in general with schedulability. This is called Rate-Monotonic Approach (RMA). This method analyzes a system to check if it is possible to schedule its tasks. The inputs are various parameters like period of events, sporadic events, deadlines, etc that help derive mathematically if the system is schedulable. This approach works with fixed-priority schemes and with either pre-emptive or non pre-emptive systems.

Thus the methodology would be to estimate each tasks worst execution time, gather all the deadlines, fill in the matrices and get a result if the specific system is schedulable. Analyzing the round-robin systems first you get the idea if this will work or you stress the system.

RMA proves that a pre-emptive system is better. Thus if you round-robin systems fails to be schedulable, you should try the RTOS. Of course you have to add the context switching time and any other overheads. If the system is schedulable (with a safe margin), then using an RTOS is the solution with the given hardware. It might be the case that neither solution works. In this case you need probably to upgrade the hardware.

Fixed priority is not the best scheduling method, but it is predictable. Earliest Dead Line (EDL) priorities are better, but RMA cannot define how much better. Thus an EDL system will work if the fixed scheduled system is also schedulable.

 Of course there other parameters to consider in this case like costs, memory footprints etc, but the fact is that if you need an RTOS, the question moves from “To RTOS or not to RTOS?” to “Which RTOS?”  which is a whole new story.

 

Examples and Experience

During my carrier I rarely needed an RTOS. Classic round-robin systems would fit the bill very well. And that’s how AVRILOS came along over the years started from 8051 in the ‘90s and then ported in assembly to AVR in 2000. Then after a couple of years AVRILOS was re-written in C for AVR.

 

A case I should have used an RTOS

Looking back, I could see a project where an RTOS was necessary and I failed to see that at that point. The project was a cash register machine. I wrote the OS, which performed dynamic scheduling with Earliest Dead Line first priority but without pre-emption. The main application which we wrote with my colleague had to be split in sub-states and return to the main loop at the 2mS slot interval allocated for every task. This got us a tedious development. The funny thing was that we had implemented a one task pre-emption in case a task was missing its deadline. This was of course a safeguard and was not really used. But the pre-emption mechanism was there. Things got worse when I had to do the 10.4 digits BCD division which itself was longer than 2mS on our Z80 core. The full system could work skipping all these states/substates coding part from our side, if we had chosen to use an RTOS (or build our own). Although we had memory limitations, the extra code to support the splitting of the main application probably would be covered by the RTOS itself with a cleaner implementation.

 

A case that an RTOS would not fit

In another instance we were building the MAC layer of 802.11abg with smart antennas. Our core was an ARM9 with tightly coupled memory (TCM) running at 80MHz. The external bus was running at half the frequency as the connected logic (FPGA) could not go faster. At that point I was wondering if using an RTOS would be beneficial. The system had to respond in 2uS from an event, which means it had to process the received packet and prepare a response. Utilizing some system pipelining we could extend to 4-6uS. When I tried an RTOS to see the overheads, I realized that the penalty was about 2uS. The time cost was way too much, so I used the classic round robin approach which was matching the system needs pretty well.

So you may see from the above examples that the decision to use or not an RTOS was irrelevant of the system complexity or the execution speed, but rather a matter of schedulability.

 

Conclusion

The question to place an RTOS or not can be greatly answered depending on schedulability. If the system can be scheduled without an RTOS safely then you do not need an RTOS. If not, then RTOS is the way to go. Of course there can be other reasons for the decision, like future expansion, ready stacks to use etc. but these goes beyond the basic principles of decision. You may use the RMA method to provide the criteria for your decision.

 

References

[1] Meeting Deadlines in Hard Real-Time Systems

The Rate Monotonic Approach, by Loic P. Briand and Daniel M. Roy, ISBN 0-8186-7406-7,

IEEE Computer Society

 

[2] AVRILOS

AVRILOS: A simple OS for AVR microcontrollers

https://www.codeproject.com/articles/127095/avrilos-a-simple-os-for-avr-microcontrollers

 

FunkOS Driver with Mutex

 

I am currently working on the SD/MMC driver for CoFILOS.
During this time I figured out that there are some issues with the Mutex system, whih I will describe here.

FunkOS uses a driver structure that contains everything needed from the driver level. It mainly contains the standard FunkOS Driver entry point functions (Function Pointer to Init/Read/Write etc), a mutex entry and then custom data which are defined per each driver.

The Driver level is actually a wrapper of the actual driver functions that are called. There the common functionality required by the driver system is implemented. Then the function pointers call the actual work routines of the driver.

FunkOS Driver and Mutex Architecture
What FunkOS Driver does

The mutex function is checked on the Driver level. Now as the mutex is stored on the driver structure, the mutex will work for two tasks if both tasks share the same driver structure. For example accessing the serial port with two tasks that share the serial port (not a very common scenario) means that both tasks should share the same structure. This might be fine for such applications with the drawback that both tasks share the driver structure. Who in this case does the initialization? There are some issues to be solved by the application.

However I do not believe this would be the common case of the drivers. Normally a single task access each device, so this is not a big problem.

Let’s see another example. The SPI interface supports multiple physical devices that may be accessed. The SPI bus itself is a common shared resource. Each SPI device may use different clock speeds and phase settings to work. I would expect in this case that each SPI device has its own Driver structure. Actually this is how the SPI driver is built in CoFILOS.

CoFILOS Driver Requirements
How CoFILOS requires to work

Now the common mutex scheme employed by FunkOS would not work, as the mutexes are inside each structure; This means a mutex lock will lock the specific driver’s mutex. Another task will still access the SPI as the mutex of this driver is not locked. This leads to the probably obvious  solution to keep a driver internal common mutex to properly support mutlitasking.

SPI driver example with internal mutex
CoFILOS SPI Driver Implementation

The new implementation supports two different configuration of access. The first mode is the interleaved mode. There each task may access the SPI interface per transaction (read/write). So two tasks can execute transactions to two different devices. Each transaction though is locked, so we do not scramble the SPI input/output data.

However there are cases where we need to lock a complete transaction sequence. In this case the SPI driver through the Control function supports locking of sequences. In this case CS of this device should be manipulated by the task level itself. This could be modified to be done on the driver as well, but I have not decided if there is a need for handling CS from the task level. This scheme is used for the SD/MMC interfaces where the there is a requirement of series of actions, especially during the initialization phase.

Timeline of two tasks accessing the Driver

TDD cannot completely cover the full testing of this sequence easily, I just created a primitive test to check critical sections and mutex claims, without actually checking the internal sequence. This is a risk I will take in order to reduce the development time, as my main task is the SD/MMC driver interface implementation.

CLI On Target (Perseus-ColdFire)

Before  a couple of weeks the CLI code compiled and downloaded to the target. It took a few hours to do any necessary code porting due to compiler differences (mostly some defines).

I also had to fix some project settings on my Processor Expert (PE) configuration to work correctly on Internal RAM or Internal Flash configurations for the Perseus board.  Now everything is smooth. I prefer to test on Internal RAM (as a program memory) as at this phase I will need to do many iterations (and thus save my flash memory for later). The code is not very big at the moment so It can fit on the 64KB of my MCU.

The first hickup was that my terminal was receiving 2 bytes as a response (ie. 0x30, 0xFF) from my target. I was afraid that my FTDI which was in a QFN package may not be correctly soldered. I already had difficulty to solder it. I used a QFN part by mistake, as I try to avoid them for prototypes which require manual soldering. I had to probe with my Oscilloscope to the Rx/Tx lines. I soon discovered that the stop bit seem to have double width (and thus re-initiating a Start sequence for the UART). I could not control this through Processor Expert (the Parity was grayed out). I then discovered that I used a wrong PE component. I used AsyncroMaster instead of AsyncroSerial. Using the correct component I was ready to go.

I had tested everything on TDD, so I was keen to find out if this would work well the first time. Nahh. Merphy’s law. A few things worked or worked partially a few other did not work at all.

Well it is about specifications. I did test with TDD and I assumed how VT100 would work, but did not test the behavior beforehand. Well assumption is mother of failure, right?

The first problem was <CR> or <LF>. Initially my code could handle either one as an end of line character, but did not work well on PuTTY which sent always <CR>. Thus I modified the code to be robust on this matter and do not care about <CR> or <LF> (or select between the two correctly at compile time).

The majority of the minor issues where easily solved. First I tested the behavior in PuTTY and then replicate that on my TDD testbed. The most difficult part was the <DEL> button.  Actually the key <DEL> sends the backspace character! Well there is a very good explaination here why.

CLI Basic Tests
CLI Basic Tests

In anycase everything sort out very fast, and I apprecieated that my TDD testbed helped do lot’s of functional tests before going to the target. As long as the protocol was understood, it was easy to test all the functionality and make sure nothing was broken in the change process.

CLI Test Example
Running various commands

After the basic line editing worked, I started testing the history function. Ohh, that was easy, worked from the begining… thanks TDD.

The help function had some issues but it was also a quick fix. The help function has to access the top level command table and then get the relevant help strings. Because it has multiple lines to output, the CLI service shall wait the serial port to complete the previous line outputs. Thus a state machine inside the CLI core and the relevant command (in this case Help) to output multiple lines. This is to avoid using extra RAM buffers simply for text outputs.

CLI Help Command Example
CLI Help Command

Did I finish? No I had to test the Flash Read/Write functions, RAM Read and Write, CPU and Version. Everything was set within a few hours.

SPI was another beast, as it did incorporate the newly built driver. This time TDD did its work! Hehe, everything went smooth. I checked the data with the my Osciloscope and tried to access my on-board   Serial Flash. Great, I can see it!

It took me about 40 hours for CLI, another 30 Hours for the SPI drivers, and around 10 hours for debugging these modules. I hope the next sprint would go faster. It seems that before doing anything you need to plan (yeah, I already knew that). The most difficult part is to figure out the architecture. It helped me a lot, to get up to speed when I did the SPI driver.

Conclusion

Another sprint finished. I was so happy, but at the same time I knew I had a long way to reach my first milestone. I still have to access my SD card and add a FatFS module.

 

Funk OS Port for Coldfire on SourceForge

I just uploaded my port of FunkOS for Coldfire microcontroller devices. The code is an Eclipse project with PE, that runs an example I created to test it. The port is not cleaned by commented code, as my time is limited. This is the reason I did not post it earlier. I thought that I need to clean it up from unused content. So time passed and I decided to get along and post it as is.

It uses a modified version of FunkOS R3. The modifications included code fixes that my MISRA checks were complaining on the original FunkOS code, mostly on external packages (like graphics).

The test code included three tasks (Idle, LedAlive, Test), that were going to sleep for a while, did something (LED toggle) and then waited to synchronize by a semaphore with an other task. That created a nice LED blinking with two LEDs.

You may find the project at COFILOS page here.

My post on FUNKOS PORT TO COLDFIRE V2 was based on this code.

I hope someone finds it useful.

 

 

Completed CLI for COFILOS

During the last weeks I finished a long task that I had…

My COFILOS CLI is ready and tested with TDD in my host.
Still I have to test it on my target.

To be honest the main skeleton is ready. Currently only internal commands (ie. compiled) are available. In the future after the addition of the FAT-FS on my SD card I plan to support commands that exists on my SD. This is much more work and is not needed at the moment.

Of course it has some neat things for an embedded system, like line editing and command history.

It took me about a week’s time to implemented it (still the majority of my efford was the design of a scaleable architecture and modularity). However this week time seemd to me too long (1-2 months..), as I work on it when I have time.

I computed myself the memory footprint and it takes about 500-600 bytes of RAM. It seems a lot but this reminded me that strings DO take space. Lots of space in terms of small embedded systems. Fortunetely my Coldfire has 64KB of RAM so this would no be a problem; Even with 32KB I believe it should be fine.

It is very modular. Actually the CLI is a handler not doing any real I/O to UART or Network. It just handles strings, characters and VT100 commands. A thin top layer is used to transfer between the external world and the handler all the required data. In case of COFILOS this is the CLI Service task.

Next step is to implement the drivers for SPI and TWI (I2C), before going forward with FAT-FS….

By then I would complete the RAM Rd/WR, Flash Rd/Wr/Erase, SPI and TWI access functions for the CLI.

FunkOS Port to Coldfire V2

Recently I started working on selecting an RTOS for using in my projects. Given that there are many candidate solutions it was not straight forward to find an applicable RTOS.

I needed a free to use RTOS. I cannot afford to pay anything yet 🙂 If I had to pay I would go for uC/OS-III. I have the book for uC/OS-II and I consider the code, functionality and extensions pretty good.

So I started looking around. I tried FreeRTOS where I know more people are using it. I also looked at picoOS, uexecFemptoOS, XMKrtos . Looking at these I liked picoOS and XMKrtos. Both seemed to be lacking further development.

picoOS was officially declared frozen, given that there was not much contribution by the community. I can understand this as this is an issue with open source in general. You may need to have a significant traction in order for people to contribute back modules or enchancements. I liked its thorough documentation, but the fact that the code was concentrated on a big file(s) that was not easy to follow the structure was something that bothered me.

uexec and FemptoOS had the same philosophy of large files containing the full RTOS and for me the code was again not easily readable (not necessarily from the code structure, but comments or spaces seperation etc). For me it is important to look at the code immediately be able to navigate; It should be a joy to look at the code. When you have a problem it is there were you would look for! This is the spirit of open source… usually you do not expect much support, but you contribute back your improvements. In theory at least.

XMKrtos seemed pretty attractive, but I never actually tried it.

A combination of initial targets architectures (8-bit processors), licensing, code readability and other factors moved me on.

I was not pleased too much with FreeRTOS at the time although I believe it is a good implementation. Then I went through FunkOS.

Normally because of its name, I would not trust such a system for RTOS. Certainly you don’t want to have a funky RTOS, right? Well I started digging into the code and I was shocked. The way it was written was very close to my programming attitude. It was very easy to read and follow. It had also a C++ verion of it as well. And it had also a small manual. Documentation (doxygen) is not as good as picoOS, but I this can be fixed. I plan to add missing parts over time.

So jumped on FunkOS. E, hmmm. Wait a minute I did not have a port for my controller. I use the coldfire V2 family, usually MCF5225x or MCF52233 (I have a demo board), but there are not many ports in general of these controllers. So I started working on my port. Delving on the issue, I discovered that FreeRTOS has better and safer handling on some points (FunkOS was based on FreeRTOS according to its author).

Yesterday I saw another interesting project the ChibiOS/RT. As I have started working with FunkOS, I did not divert to check this one.

Now let’s see some benchmarks. I first run FunkOS on my Demo board with MCF52233. As it was my first attempt to evaluate the RTOS I had only 3 tasks:

  1. Idle Task (Priority 0, Lowest)
  2. LED Alive (Priority 1)
  3. Second LED flash, triggered by a LED Alive semaphore and a timer wait [sleep delay]; (Priority 1)
  4. One IRQ (Systick).
  •  Each task has 256 bytes stack.
  • The core (system) clock runs at 50MHz.
  • Simple stack check: FunkOS original routine. Safer but slow.
  • Fast stack check: Own faster routine. May not accuretely provide always correct information (depending on stack data). Did work well for its purpose on this example.
  • Stack usage: Idle task 68 bytes, other tasks 100 bytes.
  • Flash footprint: 9K
  • RAM footprint: 1K (includes stacks)
  • Systick execution time: 14uS without stack check, 110uS with simple stack check, 22uS with fast stack check.
 

Register Save-Restore
 

1.5uS
 

Task Switch
 

3.2uS
 

SysTick (inc. task switch) with no stack check
 

14uS
 

SysTick (inc. task switch) with simple stack check
 

45us
 

SysTick (inc. task switch) with fast stack check
 

22uS

 

Did the modifications and run the same kernel on my new Perseus development board on MCF52258. My LEDs are flashing…

Happy New Year 2015!!!

 

 

Perseus Prototype

I started working on my first Prototype of Perseus (V0.81) board. A development platform I am building which combines the power of Coldfire MCU along with the capabilities of a Spartan6 FPGA.

Here are my first photos during the board assembly and test.

First assembled the switching power supplies (input 9V-18V ac/dc).

Then added the microcontroller (Coldfire MCF52258).

Perseus_MCU

Then added the FTDI USB to serial converter.

Perseus_USB

The MCU was tested along with a port of FunkOS R3 RTOS.

Assembling all the parts the board was concluded with the FPGA and its SDRAM.

Perseus_FPGA

The live board running the FunkOS flashed the LEDs passing the basic burn in test of the components (no critical errors).

Perseus_Live

Hardware was complete… almost; he he no FPGA code yet.

And more important: much firmware to write for controlling all these peripherals (USB, ethernet, SD card etc).