Page 108 - The Art of Designing Embedded Systems
P. 108

Firmware Musings  95

                    tinguish memory reads from fetches when the processor generates a fetch
                    signal for every instruction byte. Some processors (e.g., the 280) produce
                    a fetch only on the read of the first byte of a multiple byte opcode; subse-
                    quent ones all look the same as any data read. Forget trying to split the
                    memory space if cycle types are not truly unique.
                         When such a space-splitting scheme is impossible, then build an ex-
                    ternal mapper that translates address lines. However, avoid the temptation
                    to simply latch upper address lines. Though it’s easy to store A16, A17,
                    et al. in an output port, every time the latch changes the entire program gets
                    mapped out. Though there are awkward ways to write code to deal with
                    this, add a bit more hardware to ease the software team’s job.
                         Design a circuit that maps just portions of the logical space in and
                    out. Look at software requirements first to see what hardware configura-
                    tion makes sense.
                         Every program needs access to a data area that holds the stack and
                    miscellaneous variables. The stack, for sure, must always be visible to the
                    processor so calls and returns function. Some amount of  “common” pro-
                    gram storage should always be mapped in. The remapping code, at least,
                    should be stored here so that it doesn’t disappear during a bank switch. De-
                    sign the hardware so these regions are always available.
                         Is the address space limitation due to an excess of code or of data?
                    Perhaps  the  code  is  tiny,  but  a  gigantic  array  requires  tons  of  RAM.
                    Clearly, you’ll be mapping RAM in and out, leaving one area of ROM-
                    enough to store the entire program-always   in view. An obese program
                    yields just the opposite design. In either of these cases a logical address
                    space split into three sections makes the most sense: common code (always
                    visible, containing runtime routines called by a compiler and the mapping
                    code), mapped code or data, and common RAM (stack and other critical
                    variables needed all the time).
                         For example, perhaps oo00 to 03FFF is common code. 4000 to 7FFF
                    might be banked code: depending on the setting of a port it could map to
                    almost any physical address. 8000 to FFFF is then common RAM.
                         Sure, you can use heroic programming to simplify the hardware. I
                    think it’s a mistake, as the incremental parts cost is minuscule compared to
                    the increased bug rate implicit in any complicated bit of code. It is possi-
                    ble-and   reasonable-to  remove one bank by copying the common code
                    to RAM and executing it there, using one bank for both common code and
                    data.
                         It’s easy to implement a three-bank design.  Suppose addresses are
                    arranged as in the previous example. A0 to A14 go to the RAM, which is
                    selected when A15 = 1.
   103   104   105   106   107   108   109   110   111   112   113