Return of the HMCU…

Well! It turns out I’m coming to the end of my work placement (booo!) but boy have I learnt a lot. A lot of my work consisted of writing VHDL, along with testing its functionality in a simulation environment. I’ve learnt about text file parsing, different methods of structuring your VHDL code, along with good coding practice.

So what better way to test my new skills than to redo an old project? Exactly! Except this time, I’m going to write it in a proper environment with a proper test bench. Let me list the current feature set of this CPU:

  1. Improved instruction set, 11 instructions in total with room for more.
  2. Efficient minimal assembler, written in C++. Outputs text files to be used in a simulation environment though operand writing function can be changed to output raw hex to a data file, for use at synthesis of ROM for example.
  3. Increased data width! By splitting the instruction and operand data across two 32bit words, a full 32bit signed processor can be implemented with the minor downside of larger instruction space consumption.
  4. Added peripherals, as of yet I’ve written the code for a timer, 32bit GPIO and I’m undergoing the code for an SPI module. The GPIO features atomic registers, allowing for bits to be set, reset or toggled with a single register write. All peripherals are memory mapped meaning accessing them can be done using standard memory manipulation instructions. All peripherals feature a moveable base address and a memory arbiter does the muxing of address, data ready and data request lines.
  5. ROM and RAM abstraction layers. The microcontroller can make memory read and write requests to an intermediate layer. The intermediate layer can then introduce wait states dependent on the type of memory attached to these interfaces. If I get round to implementing this microcontroller in hardware, I’ll likely be using a Microchip Serial SRAM for the RAM, interfaced through SPI and a Microchip Serial EEPROM for the ROM, also interfaced through SPI. These memories won’t be running as fast as the master clock, hence the requirement for wait states. It also allows me to plaster an SDRAM interface to the RAM abstractor for all that RAM that I’ll need! Memory is addressable to 31bits meaning I can access up to 2GB.
  6. Memory fetch, decode and execute takes 7 cycles with no memory wait states. All instructions are 1 cycle apart from memory reads and writes. Memory reads and writes introduce an additional 3 clock cycles.

As of yet, I’ve simulated the core in ModelSim (the main reason for resurrecting this project!) and I’ve made a few little assembly programs to test out the functionality of the peripherals and the core.

Test 1: Software GPIO Toggle

The GPIO I’ve designed is actually relatively functional when it comes to GPIOs, featuring 6 registers: Data direction register, Input data register, Output data register, Atomic set register, Atomic reset register and an Atomic toggle register.

Doing a software GPIO toggle is pretty easy, it basically consists of initializing the GPIO, initializing a register to set a pin high, writing that register to the GPIO output register, reading back the GPIO output register, inverting that 1 bit (using an XOR operation), rewriting, and looping. In C, this would loop like:

GPIO_DIRECTION = 0x01
RA = 1;
while(1){
GPIO_OUTPUT = RA;
RA = GPIO_OUTPUT ^ 1;
}

In assembler however, it is more like:

‘Initialize GPIO Outputs
STR RA 255
STR RB 1
WRR RA 384
‘Write GPIO to 1
STR RA 1
WRR RA 385
MOV RA 385
‘Invert bottom bit
LGC XOR RA RA RB
JMP 8

Where above, it can be seen that a seperate register is used to invert the required bit. Obviously with the use of atomic registers, this software toggling isn’t required, it does however stress nearly all parts of the CPU: The memory interface (RAM and ROM), the ALU and the ability to jump.

ModelSIm1
As can be seen, the top signal is the GPIO output and after X clock cycles, “Regs(0)” (Register A), gets written to the output register, causing that one pin to toggle.

I’m initializing all the pins as outputs, just to demonstrate that only the bottom pin is being toggled. If I initialize RB as 2 instead of 1, the second pin toggles instead and so on. With this software toggling method, the speed of the pin toggling is 735kHz with a 50Mhz clock… Not that amazing sadly!

Test 2: GPIO Toggling using atomic registers

Atomic registers are a really cool thing in the fact that they latch the value written to them for one clock cycle, affect the register they’re meant to affect then reset to zero. This is really useful for things like setting a pin high, or setting a pin low, or in this case toggling a pin! By writing directly to the atomic register, the GPIO pins can generally be driven much faster than reading the current value of the output register, modifying that within the register, then writing it back to the output register.

The C equivalent program for this would be:

GPIO_DIRECTION = 0x01
RA = 1;
while(1){
GPIO_TOGGLE_AT = RA; //Atomic toggle register
}

And in Assembly:

‘Initialize GPIO Outputs
STR RA 255
WRR RA 384
‘Write RA to GPIO toggle atomic register
STR RA 1
WRR RA 388
JMP 4

Using atomic registers bumps this up to a faster 1.041MHz, getting better!

ModelSIm2
Using atomic registers gives a pretty big speed improvement!

Test 3: Writing the GPIO high and low as fast as possible

Another test I generally do with microcontrollers is see how fast a pin can be toggled by writing it high then low. This is once again pretty simple using atomic registers as you can write a ‘1’ to the atomic set register and a ‘1’ again to the atomic reset register.

The C code for this once again is pretty simple:

GPIO_DIRECTION = 0x01
RA = 1;
while(1){
GPIO_SET_AT = RA; //Atomic set register
GPIO_RESET_AT = RA; //Atomic reset register
}

This pretty much maps directly to assembly:

‘Initialize GPIO Outputs
STR RA 255
WRR RA 384
‘Write RA to GPIO set and reset atomic registers
STR RA 1
WRR RA 386
WRR RA 387
JMP 6

Note, the addresses 38x have an offset of 384 added because this is where the peripherals start (above the RAM). It may also seem illogical that I’m jumping to 6 which should be the line after JMP 6! The reason for this is the jump instruction works in 32bit lengths. Each instruction word is 64bits, therefore, I need to jump to the line I want x 2 to get to the right location. My expectations for this code is that the pin will be lower longer than it will be higher due to the fact the jump instruction happen after the pin is reset.

ModelSIm3
Voila! Unequal duty cycle, as expected 😉

With a duty cycle of 37%, a frequency of 1.85MHz was managed. Not the fastest GPIO toggling you can get on a microcontroller but for such an inefficient architecture, not terrible!

Test 4: Timers!

Timers are one of the most vital parts of a microcontroller! They’re used to create PWMs, create an internal timebase, generate constant interrupts, amongst much more cool features! In my current implementation, the timers are very simple. They have three registers: Current count register, Prescaler register and Control register.

Both the counter and the prescaler are 32bit variables allowing for tremendous lengthed timers! With a clock of 50Mhz and a maximum prescaler, the timers would overflow every 2-3 minutes, much longer than some of the microcontrollers you see nowadays. The control register as of yet only has two effective bits. Reset and Enable. In reset, regardless of the enable bit, the timer doesn’t count and both count and internal prescale counter are kept at zero. Once the reset is deasserted, the timer is essentially idle, not counting until the enable bit it set but also not forcing the counter and internal prescale counter to be zero. This allows the timer to be paused without losing the current count due to a reset.

Initializing the timer as ever is quite easy. The C code would look like so:

TIMER_COUNTER = 0;
TIMER_PRESCALER = 100;
TIMER_CONTROL = 2; //Reset timer
TIMER_CONTROL = 1; //Enable timer
while(1){
RA = TIMER_COUNTER;
}

And in assembler:

‘Reset timer and disable
STR RA 2
WRR RA 389
‘Write prescaler value
STR RA 100
WRR RA 390
‘Enable timer!
STR RA 1
WRR RA 389
‘Get current count
MOV RA 391
JMP 12

ModelSIm4
The timer increments! Yay.

Well! We can now read the timer, along with write the required registers.

Test 5: Software PWM

The final of my tests! Generating a software PWM script. This will make use of conditional jumps, along with both the GPIO and the TIMER peripheral.

One again, the software implementation is pretty easy and in C would look like:

GPIO_DIRECTION = 0x01;
TIMER_COUNTER = 0;
TIMER_PRESCALER = 1;
TIMER_CONTROL = 2; //Reset timer
TIMER_CONTROL = 1; //Enable timer
RA = 0x01;
RC = 10; //Compare value
RD = 15; //Rollover value for RB
while(1){
RB = TIMER_COUNTER;
if(RB>RC) GPIO_RESET_AT = RA;
else GPIO_SET_AT = RA;
RB = RB and RD;
}

In assembly however is where we start to see the divide between the two languages with the former being much less complex than an assembly equivalent.

‘Reset timer and disable
STR RA 2
WRR RA 389
‘Write prescaler value
STR RA 50
WRR RA 390
‘Initialize GPIO
STR RA 1
WRR RA 384
‘Setup PWM Parameters
STR RC 10
STR RD 15
‘Enable timer!
STR RA 1
WRR RA 389
‘Get current count
MOV RB 391
‘AND RB with RD
LGC AND RB RB RD
‘Compare RB to RC
JPC > RB RC 30
‘Reset GPIO on fallthrough
WRR RA 387
JMP 20
‘Jump to GPIO Set
WRR RA 386
JMP 20

ModelSIm5
Software PWM! 

The duty cycle can be calculated by RC/(RD+1) in this case, giving a theoretical duty cycle of 62.5% (RC = 10, RD = 15). Measuring the actual duty cycle gives a value of 63.2% at 64kHz. The main source of discrepancy is the instructions between setting the GPIO output and reading back the timer output, along with all the jumps etc. Not bad for… 27 lines of assembly… oh.

ModelSIm6
Setting RC to 1 gives for a really small duty cycle!

So! Not bad progress for two nights worth of programming. The source isn’t available as of yet though it should be soon after I’ve got round to improving it (I’m using a load of variables and potential latches, shock horror)!

Keep tuned for more updates!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s