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.

