Introduction

There were a number of issues related to the Two Digit Timer project that need to be reviewd. Some of the issues relate to the differences between writing code for hardware compared to writing software, and some have to do with problems caused by how I told you to structure the code.

Clock Management

In the past I have designed projects for this course so they would be implemented as monolithic pieces of code, with the use of a library for the delay procedures being a new feature in the course last semester. For this project, I had you divide the application into four modules, with a main program that does nothing other than to start the other three modules running. I wanted to make the three manager modules work somewhat like generic library code and in that spirit I had you pass the clock rate as a parameter to each module’s run routine, much as we did when implementing the msecDelay() procedure.

The problem (I think!) is that I didn’t understand Handel-C’s scoping rules for macro expressions. The clock rate gets set as a value for the preprocessor symbol PAL_ACTUAL_CLOCK_RATE, which in turn got passed either literally or using a macro expr to the manager macro procedures and from there to the msecDelay() macro procedure. Most of the time this approach works just fine. But in certain cases it reliably causes the compiler to emit an error message and to stop compilation. From what the error message says (a constant, always one less than the clock rate) is too big to fit in the counter variable defined in msecDelay(). On the surface, it looks like a bug in the compiler, but it is arguably just a poorly worded error message that is generated when the clock rate variable (preprocessor symbol or macro expression) gets defined in multiple places in the code. Whether it is a compiler bug or not, we need a way to work around the problem.

My workaround is to include the following header file (and no others) in each of the four modules:

#ifndef __TWO_DIGIT_CLOCK__ #define __TWO_DIGIT_CLOCK__ #include <stdlib.hch> #include <pal_master.hch> #include <delay_procs.hch> extern unsigned 1 button1, button2; extern unsigned 4 tens, units; extern macro expr run; extern macro expr fast; extern macro expr ClockRate; #endif

The key item at this point is the second to last line, which declares ClockRate as an external macro proc. Then in the main module I put:

#ifdef USE_SIM #define PAL_TARGET_CLOCK_RATE 10000 #else #define PAL_TARGET_CLOCK_RATE 50000000 #endif #include "two_digit_clock.hch" macro expr ClockRate = PAL_ACTUAL_CLOCK_RATE; unsigned 1 button1, button2; unsigned 4 tens = 0, units = 0; macro expr run = button1; macro expr fast = button2;

If you are not familiar with traditional C language rules it might look strange to declare variables and macros to be extern in the header file and then to immediately define them. In C a symbol may be external in any number of modules but must be defined in exactly one. The language specifically allows a symbol to be declared both extern and to be defined in the same module just so the same header file can be included in all modules.

The next step in the workaround was to eliminate the parameter being passed to each manager’s run procedure. Each module is now sharing the single global definition of ClockRate given in the main module. They can all pass ClockRate as the first argument for all their calls to msecDelay() and neither the compiler nor the linker ever complains.

The Workaround Is Now Perfect!

Updated

A previous version of this web page reported a problem with multiple clocks being defined, requiring you to “select one to follow” when starting a simulation run. With a student’s help (thank you!) I found that it is necessary not to #define either PAL_TARGET_CLOCK_RATE or PAL_TARGET_CLOCK_RATE anywhere except in the main module. So you can think of it as the main module sets up the clock (once) and makes it available to the other modules as a global macro expression, ClockRate. (All global variables must be defined exactly once, and we are using the main module as the place for defining this global macro expression as well as the other four variables that are declared extern in the header file.)

An arguable inelegance remains: two_digit_clock.hch has a #include for pal_master.hch, which is not actually needed in the Timer Module and a #include for delay_procs.hch, which is not actually needed in either the main module or in the display manager module. The argument would be that it would somehow be more proper to include only those header files in two_digit_clock.hch that all the .hcc files in the project actually need, and to have the individual .hcc files include any additional header files they need. But this inelegance is offset by the fact that it saves us having to write a separate lines to include pal_master.hch in three out of four files in the project, and to write lines that include delay_procs.hch in the two files that need it. This design choice does not effect what actually gets produced when the project is built, but it does add some (small) amount to the time it takes to do a build because the C preprocessor is sometimes processing files that are not actually needed. But it makes the overall structure of the project less delicate: all modules get all the “standard” header files for the project and there is no need to think about exactly which one is needed in each module.

Coding Issues

This is a good chance to point out some common coding issues that arise as a result of not yet fully understanding how Handel-C and FPGA hardware work.