Showing posts with label 8051. Show all posts
Showing posts with label 8051. Show all posts

Wednesday, May 1, 2019

Improved 8051 DPTR Addressing

Today I was reading more about 8051 microcontrollers, and I realized I could make an improvement to some of the code I posted in 2017 for my BCD Addition Speed Test comparing the 6502 and 8051. As I wrote there, the 8051 has at least one 16 bit register called DPTR, which is the only way to get data to and from external memory. Unlike the 6502, there is no indexing or indirection when accessing external memory, so all addressing has to be done manually. Here is the macro I had in my other post to compare the two types of addressing, with an extra column showing cycle count:

Line6502Cycles8051Cycles
001
002
003
004
005
006
007
008
009
010
011
012
013
014 









...

LDY #Offset

STA (Address), Y 











2

6
IndexDPTR0 MACRO DPTR_copy, Index 
clr C 
mov A, DPTR_copy
add A, Index
mov DP0L, A
mov A, LOW(DPTR_copy)+1
addc A, #0
mov DP0H, A
ENDM
...
push A
IndexDPTR0 Address, #Offset
pop A
movx @DPTR, A

1
1
1
1
1
1
1


2

2
2

The first improvement is to get rid of the clr C on line 002. I'm not sure how I missed something that simple! When I wrote the post, I had mostly worked with the 6502, which needs C to be explicitly cleared. Maybe writing MSP430 assembly, which also has an add without carry instruction, helped me realize that. The second improvement is eliminating the push on line 11 that saves the accumulator. One shortcut is to save that value to DP0H then restore it when the new DP0H is written using the xch instruction. This eliminates the pop on line 13 also. The new version looks like this:

Line6502Cycles8051Cycles
001
002
003
004
005
006
007
008
009
010
011
012









...
LDY #Offset
STA (Address), Y 










2
6
IndexDPTR0 MACRO DPTR_copy, Index 
mov DP0H, A
mov A, DPTR_copy
add A, Index
mov DP0L, A
mov A, LOW(DPTR_copy)+1
addc A, #0
xch A, DP0H
ENDM
...
IndexDPTR0 Address, #Offset
movx @DPTR, A

1
1
1
1
1
1
1



2

The macro is still 7 cycles but it saves the 4 cycles for push/pop or the 2 cycles to save A in a register instead. It also looks much cleaner and will hopefully lead to less headaches.

In addition to movx @DPTR, A and movx A, @DPTR there are also movx @Rn, A and movx A, @Rn instructions that I had not paid much attention to, since using them constrains where objects like BCD numbers can be stored. At the very least, those objects would have to fit into 256 byte pages to use those instructions, and realistically the objects might have to start at the beginning of the page and possibly waste some memory. On the other hand, I have been doing a lot more thinking about how to store those objects to build a graphing calculator. Splitting memory into regular chunks (regardless of the size the object it occupies actually requires) is one strategy to prevent memory fragmentation, so using the faster movx @Rn, A and movx A, @Rn instructions might be a good fit. The R registers are also much more flexible for indexing since it is faster to adjust the address when you don't have to account for crossing page boundaries like the macro does.



Sunday, June 24, 2018

Calculator Processors

Most of the microcontrollers and processors I have looked into using have been for my calculator projects. That's one of the main reasons I did my microcontroller comparison a few years ago. Recently, I have gotten interested in other chips I could use for calculator projects, in addition to the 6502, 6800 family, and 8051 chips I was interested in before.

The main two criteria I have are an external memory bus and through-hole package. Right away, this eliminates most modern microcontrollers, since almost all of them with an external memory bus are SMD only. In theory, I could use a chip without an external bus if it had a few hundred KB of RAM for data and a few hundred more KB of RAM or flash for program space, but I haven't found that in a through-hole package. Another point is that I would like the ability to load programs from an SD card, which is impractical if I need to have the chip reflash itself every time I load a program. Through-hole parts are more convenient to solder, and I would like to do a few more projects with them before I try to start making surface mount boards.

Modern microcontrollers
  • AVR - None of the ATmega series has an external bus. Some of the ATxmega series do, but they don't come in through-hole packages.
  • PIC - For a through-hole part, the PIC32 series is very impressive at 50mhz with 64KB RAM and 256KB of flash, but even this is not enough for what I am planning. The chip requires a separate programmer, and the compiler offers limited optimizations unless you pay, which is absurd in 2018 compared to its competition. It does have something called PMP, which offers a sort of data bus, but it can only address 12 bits, since the DIP part only has 28 pins. 
  • MSP430 - This chip worked well for my RPN Scientific Calculator, but it also lacks an external bus.
  • Propeller - This is an interesting chip but like the others, it lacks an external bus and does not have enough flash or RAM. The one interesting possibility is doing several calculations in parallel, although I don't know how I would use that for a calculator.
  • Zilog - They have some nice chips, including some processors with external buses, but I would like to use something different than what the TI-83 and similar models use.
  • LPC1114 - I really like this chip, and I am using it in my Pocket Calculator, but it also lacks an external bus, and it seems that it is no longer manufactured.

Tuesday, November 21, 2017

BCD Addition Speed Test: 6502 vs 8051

12345.6789 + 9999.99999 packed BCD on 8051
A few years ago I decided to revisit the microcontroller comparison I had done to see how much of a speed up I could get recoding my BCD addition routine in 8051 assembly. At the time I was surprised that it was 3-4 times faster than the C version. That original BCD routine, which I used for my RPN Scientific Calculator, uses unpacked BCD characters arranged with the most significant bytes first. For the calculator I want to build with a 6502, I wrote a new routine that stores the least significant bytes first in packed BCD. This takes advantage of the 6502's decimal mode to quickly add packed BCD bytes and avoids having to shift all the bytes when an extra byte has to be added for the final carry. It turns out that the 8051 has an instruction, DA, to correct the result of BCD additions from their binary result back into BCD, so it is easy to do packed BCD for it as well. To compare the speed of the two chips for use in a calculator, I translated the 6502 packed BCD routine to 8051 assembly and ran both versions in their respective simulators.

Sunday, December 28, 2014

Microcontroller Showdown, Revisited

Back in March I finished the microcontroller comparison of a few chips I was considering using. The 8051 based chips all fared poorly in BCD calculations, which is the main thing they would be used for in another calculator project. Some other 8051 fans pointed out that tests like mine test the compiler more than the chip and the SDCC compiler does a lot less optimizing than GCC. So, I decided to recode the BCD Add routine from the test in assembly. At the beginning I didn't consider this because I thought I would only ever write C for the 8051, but after writing assembly for the 6502, I was excited to try it for the 8051 too.

For this test I used the MCU 8051 IDE simulator. It is really impressive! Being able to see all the RAM and registers helped a lot. It is similar in some ways to what I hoped to accomplish with my 6502 Virtual Trainer. When I translated the original BCD Add function from C, I left out the parts used for subtraction, since they weren't used in the test. Also, some of the values that don't change inside loops are calculated once before the loop and stored for use later. This makes sense because a good C compiler would do this for you. All values are stored in the internal DATA, although this would be far too small for real calculations. The simulator can also work with SDCC, so I was able to simulate the function in C after commenting out the parts for subtraction.

The assembly took a simulated 440us to execute. The C version took between 1365 and 1732us depending on memory model and whether the BCD data was stored in DATA or XDATA. Curiously, storing it in XDATA was always faster. The medium memory model was faster than the large memory model. It seems then that hand-coding the routine in assembly is 3-4 times faster. To be fair, the routines for other architectures might also show a speed up if they were rewritten. Compared to the original numbers of the test, a 3x speedup would make the DS89C450 faster than the MSP430 for this routine. The difference might actually be a little bigger since this test used an upgraded version of SDCC that might optimize better. With that in mind, an 8051-based calculator with external bus (coded in assembly) would outperform an MSP430 with SPI SRAM. That is a bit of a surprise after the results of the first test.

The LPC1114 could also be sped up a little. In the original test the chip was configured to run at 50MHz. Because the flash runs at a maximum 20MHz, 3 wait states are required when executing from flash. However, 3 wait states is a little wasteful since 2.5 would be enough. Running the chip at 40MHz would only require 2 wait states, and would let the flash run at full speed. I'm not sure this would give a whole 16% speedup but I imagine it would offer a little bit of a boost.

Sunday, March 9, 2014

Microcontroller Showdown, Conclusion

The last chip in the comparison is the Z16F2810. When I started the tests I got a Loop overhead time of only 0.64, which made me think the chip might be very fast. When I moved on to the multiplication and division tests, though, some of them completed in only 0.89 seconds. After looking at the assembly generated during compilation I noticed that the arguments of division were only being copied to the register that holds the answer. It seems that even though all the variables were declared as volatile, the compiler decided to optimize the division out anyway. This made me think the compiler was simply ignoring the volatile keyword but the assembly generated when accessing volatiles is slightly different. Loops involving them take about 25% longer to run, so it does have an effect. The only way I could prevent it from optimizing out multiplies and divides while keeping optimizations was to declare the involved variables globally. The User Manual for the chip helped a lot when looking at this problem and I think it is really well done.

For arithmetic operations, the chip did very well. It was always faster than the MSP430 and almost as fast as the LPC1114 for most things. One place it did very well was division where it outperformed all the other chips by a large margin because it has a hardware divider. General GPIO is also pretty fast, although it does not have any special mechanism for speeding it up like the masks on the LPC1114 or the constant generator on the MSP430. BCD Add was about the same as the MSP430. BCD Multiply was faster than on the MSP430 but still much slower than on the LPC1114.

After running all the chips, here are my conclusions:
  • MSP430 Easy to use and reasonably fast. Good choice for small jobs and low-power projects. Held back by low clock speed (16MHz), small memory, and lack of hardware multiplier.
  • LPC1114 The fastest chip by far for everything but division and 8 bit math. Lots of RAM and Flash. Straightforward to use, although there is no real community. Very fast GPIO with masks. Top choice for calculations.
  • DS89C450 Fast for 8 bit math, although slow at shifting. Very fast GPIO since each GPIO pin is mapped to its own byte in memory. Extremely slow at BCD calculations. The only real advantage is the external memory bus.
  • AT89LP6440 Similar to the DS89C450 but lower clock speed and performance. 
  • Z16F2810 Good performance but inferior to the LPC1114 in everything but dividing. Debugger is very useful but the IDE itself is sometimes clunky. Enough RAM and very big Flash (128KB). PLCC package is inconvenient.
In the future I plan to do a few more projects that will need the extra horsepower for the LPC1114. For other smaller jobs I will stick with the MSP430.

Results are after the break.

Monday, March 3, 2014

Microcontroller Showdown, Part 4

The next chip to test is the AT89LP6440. This is a single-cycle 8052 compatible like the DS89C450. It can run from an internal oscillator at 8MHz or an external crystal up to 20MHz like I am using. According to the datasheet, total capacitance for the crystal should not exceed 20pF and the chip itself adds about 10pF of that. The smallest capacitors I can find are 22pF so I ran it without capacitors and it seems to work fine on a breadboard.

As you might imagine, the AT89LP6440 performs similarly to the DS89C450 at 20MHz. The times are not exactly the same, though, because both chips perform some instructions in less cycles than original 8051s. This is apparent, for example, in the multiply times where the AT89LP6440 can do 32 bit multiplication about 30% faster because it does multiplies in 2 cycles instead of the 4 cycles needed by the DS89C450 and standard 8051s. The AT89LP6440 also shifted about 5% faster in the tests, although it was a little slower at BCD calculations. Like the DS89C450, I don't think I will end up using this in a calculator but it may come in handy because it can run at 3 volts and has an external memory interface.

Results are after the break.

Sunday, March 2, 2014

MSP430-Based AT89LP6440 Programmer

Using an FTDI cable to program the AT89LP6440 like I mentioned in my last post turned out to be more difficult than I expected. Configuring the FT232 chip is a real pain because the reset command in the API does not seem to actually reset any of the chip settings to default. After trying some more tweaking I noticed that the uploading itself inexplicably hangs sometimes on longer files. I was not able to finish the next step of the microcontroller comparison. According to another page, FTDI states that "using bitbanging for SPI or I2C is not recommended with the FT232RL part."

The next step was to use an MSP430 to take data in over UART and relay it over SPI to the AT89LP6440. The UART interface of the DS89C450 works really well so I copied this. The MSP430 gives a prompt over UART that can be used to erase the chip, load or verify firmware, and set flags. The challenge of the project was coordinating interrupts and ring buffers for the hardware UART and SPI peripherals so that no data is missed. At 9600 baud it worked reliably but at higher speeds there were checksum errors because sometimes the previous UART byte was not read before the next one came in. This seems to happen at the end of a line of hex when the MSP430 has to send a lot of data out over SPI to prepare the chip for writing. The solution was to send an XOFF character back to the terminal, which stops it from sending any more data bytes until an XON character is sent. Like this I can program at 57.6k and after a few dozen tries I have not had any errors writing to the chip or verifying the firmware. The MSP430 is also connected to the reset line of the AT89LP6440 so that I can put the chip in run mode without having to press any buttons on the breadboard.

The source for the programmer can be found here: AT89 Programmer.


Sunday, February 9, 2014

AT89LP6440 Flashing

For the Microcontroller Comparison I have been trying to get the AT89LP6440 to work. When I sampled the chip I thought it could be programmed over serial with an FTDI cable like the DS89C450, but it turns out that it needs a parallel port. There are adapters and programmers that will simulate a parallel port over USB but I don't want to buy one for a chip that I might not use much in the future.

As a workaround I used the FTDI cable to bitbang the SPI interface. This was tricky because I had to write a whole new loading program instead of relying on the program Atmel supplies. Thankfully, they provide very good documentation of the ISP specification. Bits are sampled on MISO on the falling edge of the clock and MOSI is sampled on the rising edge. My first version of the program worked well but could only upload about 100 bytes of firmware per second. Part of this is due to reading MISO every clock cycle, which is not always necessary. By splitting reading and writing into different functions and sending pin states in groups instead of individually, flashing is much faster. There is a bug, though, that randomly gives a wrong byte when I try to read from the chip to verify the firmware. The wrong byte is always at a different address and sometimes the verification completes, so I know the AT89LP6440 has been programmed properly. I wasted about a week trying to tweak the program and saw that the D2XX drivers supplied by FTDI are unreliable and pretty buggy (at least when used under Windows 7 for bitbanging). This particular problem could be with the chip but several of the other D2XX functions I tried didn't work very well either. The same problem happens with both synchronous and asynchronous modes. There is an open source library called libFTDI but I didn't get very far with it because installing the driver for it would mean losing the virtual COM port you get with the FTDI driver. The next step would be to program the chip using another microcontroller connected to the PC over UART but I don't want to waste any more time on this chip since I am unlikely to use it for anything after the microcontroller comparison.

You can download the program here: AT89Loader. Flashing seems to work with no problems but verification occasionally fails.

Monday, February 3, 2014

DS89C450 High-Baud Flashing

As I mentioned in my last post, I have been using the DS89C450 lately to do some tests. It has a serial program loader in ROM used to program the chip over UART. The baud rate of programming is dependent on the crystal speed. Some crystals, even fast ones, only work with low standard baud rates and some don't work at any of the standard rates. To understand why, we should look at what baud rates the loader accepts. The User's Guide gives an equation for this:

Baud rate = Crystal Frequency / (192 * (256 - Timer Reload))

The loader tries these values for Timer Reload:  FF, FE, FD, FC, FB, FA, F8, F6, F5, F4, F3, F0, EC, EA, E8, E6, E0, DD, D8, D4, D0, CC, C0, BA, B0, A8, A0, 98, 80, 60, 40. The resulting baud rate must match the baud rate used by the programming computer to within 3%. Using this equation we can see that some common crystals such as 12 and 20MHz only fall within the 3% limit for a few of the slower standard baud rates.

This limit can be overcome, however, by using a non-standard baud rate. Neither the MTK program supplied by Maxim nor Hyperterminal can use non-standard baud rates, but some terminal emulators like uCon can. The above equation can be used to calculate the ideal value. In my case, a 20MHz crystal with a Timer Reload of FF gives a baud rate of 104,166. At this speed I could connect to the chip but not program it without errors. Using the next value of FE for Timer Reload gives 52,083 and at this speed I could connect and program the chip successfully. This is big a improvement over the previous maximum baud of only 4,800!

Another great function of uCon is scripting. Scripts can be saved and assigned to a button. The following script lets me erase the DS89C450 and upload new firmware with just one button click (I usually compile programs to "temp.hex" to make scripting easier):

SET PROMPT >
SEND "K"
SEND -n "L"
SET PROMPT G
FSEND "temp.hex"

Sunday, February 2, 2014

Microcontroller Showdown, Part 3

The next chip I compared was the DS89C450 from Maxim. This is a single-cycle 8051 with clock speeds up to 33MHz. The other single-cycle 8051 I wanted to test is the AT89LP6440 but I wasn't able to program it with an FTDI cable. It expects an SPI interface based on a parallel port and I do not know how to get it working with the FTDI cable.

At first I had a lot of trouble getting the DS89C450 running with the flashing software supplied by Maxim. It just would not connect no matter what crystal or capacitor combination I tried. After a little digging I found out that the baud rate depends on the crystal and that 4800 is the max for 12 and 20MHz crystals. Unfortunately, the flashing software only supports 1200 and 9600 or higher bauds. At 1200 baud I was finally able to connect successfully but the chip returned an error when I tried to load firmware. After more digging it seems the problem is caused by using an FTDI cable instead of a real serial port. I can't really hold this against the manufacturer, though, since the chip and software are over 10 years old at this point. Next I tried connecting over Hyperterminal at 4800 baud and this worked great. The interface for communicating with the chip is actually kind of slick. It gives you a prompt you can type commands into and responds back to your input. Loading firmware at 4800 baud, however, was a real pain. If I decide to seriously use this chip, I will definitely track down a crystal that lets me connect at 57600 baud.

The results of the tests are after the break. I ran them with a 20MHz crystal and scaled the results for what they would probably be with a 33MHz crystal. For compiling I used SDCC. This compiler is new to me but it seems pretty simple to use. In place of -O3 I used --opt-code-speed and for -Os I used --opt-code-size. There wasn't really a difference in the results though. As you can see, it adds 8 bit numbers even faster than the LPC1114, 16 bit numbers about as fast as the MSP430, and 32 bit numbers almost twice as fast. It is also much faster at multiplying than the MSP430, which was a surprise to me. Out of curiosity I looked at the 8051 instruction set and was quite surprised to learn that it has 4-cycle multiplication of 8 bit values. The rest of the instruction set and architecture seems very straightforward and I imagine it would be fun to write assembly code for this chip. Division and shifting are pretty slow. GPIO, however, is very fast. Each pin is mapped to its own byte in memory. This seems like a simple feature and I wish more chips had it. With this the chip can bit bang almost as fast as the LPC1114 can with masks. The real test of the chip, though, is BCD calculations and in this it was pretty disappointing, running 2-3 times slower than the MSP430. For these tests all the data was stored in the on-chip external RAM (xdata). This is slower to access but it was necessary to store it here since the internal RAM holds less than 100 bytes. I also tried storing it in the idata section available in 8052 compatible chips like this but that was even slower. It looks like I won't be using this chip for any calculator designs but I will keep it in case I do a project that needs an external memory interface or memory mapped peripherals.