Nice name for the entry seeing as though it is already ten days after I started writing this post. This entry is going to be mostly on bringing up an embedded processor from scratch.
So I started working with the PSoC 4200 over the last couple of weeks. After going down a good number of rabbit holes, and not being able to figure out if certain tools would work together, I decided to simply suck it up and use the Cypress Creator software to start. The bonus of that is that within a couple of hours I was able to download the example project, and burn it onto the board and run it. I then made a small change, recompiled and threw that down onto the board to make certain that I was actually programming the board successfully.
When I bring up a processor from scratch, I like to follow a pretty rigorous path. It basically moves from the absolutely easiest stuff to the more difficult stuff. Here is the order that I tend to tackle the projects, or embedded programming 101:
- Blink an LED. Almost every board has an LED on it, and if not throw down a resistor and an LED on one of the output pins. The first incarnation uses a loop and a counter to blink the LED on and off. I then modify the counter in a loop to make it blink either faster or slower to prove to myself that I am altering the code successfully. (At this point, I hook up a debugger and make sure that I can view and step through the code to make sure that I have the debugger set up properly. Currently, I don’t have a debugger, but I’m hoping to grab one in the next few weeks).
- Blink the LED using a timer to change between the LED on and the LED off. This proves that I understand configuring the timers, and understand the clocks within the chip. It is very easy to accidentally miss clock divider, or be off by a little bit. This step also requires understanding clock routing within the chip.
- Use the timer to cause an interrupt and change the LED on/off state in an interrupt. Requires understanding of interrupts, interrupt vector table, and how to clear the interrupt sources.
- Transmit words continuously on the UART interface. Make sure that I have the baud rate set up properly for the UART, and can see the data coming back on a PC.
- Echo received characters on the UART interface, back to the transmit interface. When a type an ‘x’ on the keyboard, I should see that echoed back.
After finishing those five simple steps I’m usually familiar enough with the processor, that every thing else is simply reading documentation and digging through registers. Doing the above steps forces the programmer to read and understand the documentation since every company documents their chip in very different ways. Some companies have a single 2000 page programming reference guide, while other companies break each hardware subsection into a different document.
So one of the absurd things with the Cypress Creator IDE is that it tries to force the programmer into using their canned components as opposed to actually programming the processor. If a UART is needed, drag and drop it into the fake processor schematic and fill in a couple of fields. No need to understand the registers that actually are underlying the component. Same thing with I/O pins, SPI buses, etc. The down side to that is the code becomes very large, because it requires all of these generic components where most of the functionality is not being used.
Here is the actual code C code that blinks the LED on the board:
typedef volatile unsigned long R32;
typedef unsigned long U32;
#define GPIO_PRT1_DR 0x40040100
#define GPIO_PRT1_PS 0x40040104
#define GPIO_PRT1_PC 0x40040108
#define GPIO_PRT1_INTR_CFG 0x4004010c
#define GPIO_PRT1_INTR 0x40040110
#define GPIO_PRT1_PC2 0x40040114
#define HSIOM_PORT_SEL1 0x40010004
int main()
{
/* Initialization code */
U32 count = 0;
*((R32 *)GPIO_PRT1_DR) = 0xff;
*((R32 *)GPIO_PRT1_PC) = 0x00180000;
*((R32 *)GPIO_PRT1_INTR_CFG) = 0;
*((R32 *)GPIO_PRT1_PC2) = 0;
/* Send the GPIO bit to the hardware pin */
*((R32 *)HSIOM_PORT_SEL1) &= ~0x0f000000;
for(;;)
{
/* Place your application code here. */
if (count == 0x10000)
{
*((R32 *)GPIO_PRT1_DR) = 0x00;
}
if (count >= 0x20000)
{
*((R32 *)GPIO_PRT1_DR) = 0xff;
count = 0;
}
count++;
}
}
Looking at the above code, it is a grand total of five initialization statements, and then a simple loop with a counter changing the LED bit on and off. Compiling the code, takes maybe two or three seconds. It produces 100 bytes of object code. Using the Creator tool, and a PWM to blink the LED on and off, it takes a little over twenty or thirty seconds to compile, and generates a couple K of functions that I may or may not use. The long compile time is caused by auto generating code, routing and configuring clocks inside the chip, and running the FPGA style generator. I’m not really using any of those resources currently, so it is simply a waste of my time.
The other issue is that Cypress has allowed their documentation to take a back seat to their code generation tool. The documentation is not only poor, but it is incomplete. I tried to find the address of certain registers, and it is not located in any of the documents. I had to dig through their generated code to figure out the addresses of the registers. I would say that their documentation is very poor, even below Microchip if that is possible. Right now Freescale and ST Micro have very good and very complete documentation. Microchip, and Cypress don’t compare.
They provide a bootloader. That is fabulous especially if it is written well. The only problem is that it seems to have been written by a first or second year co-op student. The bootloader size should be as small as possible. The Microchip bootloaders I wrote were either 768 or 1024 bytes depending on the processor. The Freescale bootloader was 1024 bytes. (It was actually about 530 bytes, but because of the flash protection scheme, code could only be protected in 512 byte blocks, so I had to move to two blocks). Their bootloader component is 6400 bytes, or 1/5 of the 32K of code in the processor. What the heck are they doing in there? Maybe they are running the SETI program with the unused cycles.
A second issue with the bootloader is that it requires RAM for the interrupt vector table. Admittedly, that is the easiest way to write a bootloader, but it means that 196 bytes of RAM are sucked up by that table. A better, but more complex method is to create a jump table in the low flash, and move the exception vector table into the application code and not rely on . While it will incur a couple extra clock cycles of delay during an interrupt, the RAM is preserved for use by the application code. That would have been a show stopper with the old processor that only had 8K of Flash and 512 bytes of RAM. Since this processor has 32K of Flash, and 4K or RAM, it is less of an issue.
Right now I don’t have a debugger. I’m gonna grab a Pioneer board (costs about $25) which includes a debugger and a PSOC 4200 processor. Having a debugger will really accelerate the speed of developing the code.