HCS_C Hardware Users Manual
Version 1.05
(c) Feb 2004 Robert D. Morrison
2/11/04 original
5/5/04 added programming examples, more on booting, generating
downloadable code
1/29/05 added information on breakpoint setting, also how ARM
and FPGA interrupts work, how to use them.
2/5/05 added more details about working with interrupts on
the arm-elf emulation environment. Started an
index
2/11/05 added RTC seconds readback and RTC readback freeze
2/22/05 added stuff for set/clearing int regs, added math regs,
tic timer regs
5/1/05 IO register fixes
Copyright (c) Jan 2005 Robert D. Morrison
INDEX
ARM Interrupt handling on HCS_C
INTRODUCTORY
PRECAUTIONS
This document is a guide for using the functions on HCS_C version A.03.
This is VERY PRELIMINARY, SUBJECT TO CHANGE, AND LIKELY TO HAVE A
ZILLION MISTAKDS :-)
CAUTION!!! READ THIS!!! IF YOU WANT TO KEEP YOUR HCS_C FROM
BEING DAMAGED OR DESTROYED, YOU MUST CONSCIENTOUSLY APPLY
ESD (ELECTROSTATIC DAMAGE) PREVENTION METHODS!!! ESD often
causes cumulative damage. I see many co-workers take a
loaded board from one desk to another, touch or probe boards
without grounding themselves, and carry components in their
hands, use scotch tape to close or otherwise seal components
(think about how scotch tape works!! Pull some scotch tape
off of something in the dark, and you will be enlightened as
to why scotch tape is a non-no anywhere near ESD sensitive
parts!). Styrofoam is just as bad. I couldn't believe how
many times I would receive a CMOS IC plugged into a piece
of styrofoam! Only the black antistatic foam or tubes should
be used to transport parts.
DONT DO DAT!!! This is your project that you have put a lot
of time into--don't take shortcuts, put the board in an
antistatic bag before transporting it.
At the end of this document, I put a list of ESD management
techniques that help minimize the risk of (often silent) damage
from ESD. One effective way that seems to nearly eliminate the
risk, along with anti-static mats, is to work barefoot! I have
more trouble with shoes and socks than anything else.
OK, we got that out of the way, now on to the fun stuff. BUT--
DONT TAKE SHORTCUTS, really, it's not worth the aggravation
and marginal operation. Spend the money, get a good antistatic
mat before you work on HCS_C.
HCS_General
Information
General:
HCS_C is a powerful enhanced version of the original HCS2 design.
By using a faster ARM processor and a fully featured FPGA circuit
design, a great deal of home automation functionality has been
packed into the HCS_C main board. Additional functions such
as networking are planned as plugin daughtercards.
Note that one of the drawbacks of the original HCS2 was the hardware
and firmware dependency on polling many different HCS functions.
The new HCS_C has removed the need to poll functions, replacing
them with on-demand interrupts. Only the local IO inputs need to be
polled for changes. It still is possible for firmware to poll all
devices, for example the UARTs or the PS2 keyboard, but this is
*strongly* discouraged. This is a very inefficient way (and often
doesn't work well under heavy activity load) to use the myriad of
HCS_C devices.
This document provides information on how to configure and use the
HCS_C functions. Knowledge of C language, ARM processor assembly
language, and the Atmel ARM91T40008 processor configuration is
recommended. PLEASE NOTE: THIS DOCUMENT DOES NOT DESCRIBE THE
REGISTERS AND FUNCTIONS IN THE ARM PROCESSOR! The ARM processor
comes with a number of UARTs, IOs, timers, etc. You will need to
look up the ARM91T40008 users guide (see links page) for this
information. This document describes the ADDED features and registers
that
are provided on the HCS_C board.
The HCS_C board has the ARM91T40008 ARM Thumb processor and an Altera
20K100 FPGA, along with 16Mbytes of external Flash Eprom, 1 or 2
Mbytes of external SRAM, and a large number of peripheral devices.
These devices include:
a: 32 buffered and protected inputs (RC plus diodes)
b: 32 buffered and protected outputs (RC plus diodes)
c: Voice chip (WinBond 4003-08MP)
d: Epson RTC-4543A Real Time Clock
e: RS485 interface (x3)
f: RS232 interface (x2)
g Seetron LCD display serial port interface
h. 16 bit SPI interface
i: PS2 keyboard interface
j: daughtercard stack with 4 programmable card selects
k: Relay driver interface and relays (x2)
l: Caller ID serial port
m: X10 Serial interface
n: reset switch, 4 position DIP switch, and four status LEDs
In addition to providing interfaces to each of these peripherals,
the FPGA also adds internal functions as follows:
a: A powerful 256 timer state module. This module allows
interrupt generation at specific times on specified dates.
This module eliminates the need for HCS_C to poll for events
at specific or random times/dates.
b: A firmware timer permits timing program execution, and keeps
track of the minimum and maximum intervals of program execution.
c: A watchdog timer permits HCS_C recovery upon unexpected failure.
This programmable module keeps track of the number of resets
applied. It includes a circuit for software resetting the
ARM processor.
d: math functions to complement the ARM processor, such as
16 bit integer divide, 16 bit modulo, and a ranged pseudorandom
number generator.
e: A powerful onboard state analyzer that operates independently
of the processor (to allow fault observation even if the
processor goes into the weeds). This analyzer monitors
any pair of 128K blocks and can trigger after up to 1024
occurrences of a trigger address. Up to 1024 states can be
stored with a user programmable number before and after the
trigger event. To make maximum use of the state buffer,
an independent collect function can block capture of
unwanted states (for example it is possible to enable
collecting at the start of an interrupt routine and to
stop collecting at the end of the interrupt routine).
---------------------------------------------------------------------
PROGRAMMING ENVIRONMENT FOR HCS_C
HCS_C programming environment:
HCS_C includes a JTAG emulator interface. This can be connected
to a JTAG arm emulator interface buffer board, which plugs via
cable to a PC parallel port. The Gnu C cross compiler and emulator
software tools for the ARM processor can be used to develop firmware
and software for the HCS_C board. The arm-elf-gdb environment is
the package to use. These can be downloaded free from the Gnu
source forge site. You will need to use the Cygwin Unix environment
if you are running a Windows PC, this is also available free on the
internet.
First you must compile your source code. You need a startup
assembly file called crt0.s that defines a stack and jump to
main(). Then you need a C file that has a main() procedure
in it. From here on out, you're on your own (but several
sample files are included to guide you on writing code for
HCS_C. Hopefully there will be several developers doing
work here that you will be able to leverage or use. Next
you need to compile this, I have a Gnu makefile and the
link file that specifies where in memory things get loaded.
Now--a problem--the ARM processor does not power up with
access available to memory. You must configure memory and
then run the infamous remap command that wakes everything
up in the processor. Before remap, you ONLY HAVE ACCESS
to the FPGA boot code (at address 0), the FPGA registers
(at the PREMAP address 0x10000), one sector of Flash (at
the PREMAP_FLASH address 0x20000, read only!) and the ARM
processor internal SRAM (at address 0x300000)! The FPGA boot
code contains the remap command that causes all memory to be
accessable. All fine and good, but this process requires that
the remap command be executed before the emulator can load the
desired code. This is a headache for development since it
means that debugging your code requires that you take it
out of the premap state before loading code. I have a
simple remap macro you can run that does this. I'm sure
the firmware developers will refine this process, but that's
what I have for now.
When you turn on HCS_C, the boot program (currently version 0.20)
examines the left-most DIP switch. If it is on (closed), it
tries to boot from Flash. If it is off (open), it sets up the
console serial port for downloading a program into internal
SRAM. The console port will first emit an OK in either case,
then if in boot-to-flash mode, it will emit an F. If it
successfully finds a boot program in flash, it will run it,
else it will emit a "?" and halt. It looks at the first flash
location, if it is not 0x00000000 or 0xffffffff, it will start
executing at that location. If it is all fs, it will assume
nothing has been programmed, emit the "?" and halt. If it is
all 0s, it will assume a linked boot. It will read the value
at the flash address + 8, and try again to boot at that new
address.
In the download mode, it will emit the OK and sit and wait for
a download to console. The console must be set to 115200 baud
and the download code must be Motorola S records (the Makefile
I have will automatically generate this). The bootloader is
very simple and will not take any records other than type
01 or 02 (so you have to edit the download hex file to remove
the first line, which has the download file title information.
Soon I will fix this in the boot code). After the download
finishes, the bootloader waits until you type one more character
(don't use <CR> since that is two characters) and initiates
execution of the downloaded program in internal SRAM.
Programming code: If you want to debug your program, link your
code to post-remap internal SRAM (0), and test it out. Once
you have it working well enough to load into flash, then
reset HCS_C with the DIP switch in download position, download
the hcs_flash program, and follow its instructions. If you
are overwriting an existing program, you need to clear the
flash chip first. Run the flash program, download any program,
but when it says do you want to program flash, type n. It will
then ask if you want to clear the flash memory. Type "Y" (note
this y is capitalized) here. It then will ask again, are you
sure, type "Y" again. It then will take about 3-4 minutes to
clear the flash. DON'T DO ANYTHING, like cycle power or reset!!!
Just wait. When it finishes, leave the DIP switch in the download
position, redownload hcs_flash, download your desired program,
and this time anser y (not capitalized) to write the flash.
Don't forget to type one more character into the console after the
download to begin execution. Assuming no errors, the flash program
will write the flash memory with your program. You'll want to set
the DIP switch to the run from flash position, hit reset, and
the new program should execute out of flash.
I highly recommend minimizing the number of flash chip clears
since there is a limited number of re-writes possible, and
replacing the SMT flash device is rather a pain. I provided
a linked boot capability that allows repeated overwriting of
the flash into available unprogrammed locations before requiring
a flash clear. This only works if the unprogrammed area is
large enough to accomodate your program (ie, if your program
is bigger than half of the flash device, there wont be sufficient
space to rewrite the program). Do this by erasing the first
instruction (which must be a branch over the boot link value
at start +8), writing the new boot address at start +8, and then
linking your program to the new boot address. The program must
always leave the location start + 8 unprogrammed (0xffffffff)
so you can link to a new location once this version becomes
obsolete.
You can also use the emulator to load your code for testing.
In this mode, you can link it anywhere and run from there. Of
course, linking to flash wont work because you must run the
programming algorithm (hcs_flash) to write to flash. Of course,
you can use the emulator to run from any code location in flash
that is already programmed. Use the link file (eg hcs_rom.lnk)
to control where code and data and stacks are located. The section
text is where your main code will be mapped (see for example the
hcs_test.lnk file). The bss section is stack, and data should
be mapped to sram of course. If your program has string data,
these will get mapped to the rodata section. Be sure to compile
with the ARM thumb mode on, since the bootloader puts it in this
mode (which is much more compact and runs faster). If you use the
emulator to load code, you must run the remap macro to set up
memory, then load your program. This sequence can be put into
the emulator startup file (gdbinit) if desired (just remember to
remove it if testing new boot code for the FPGA since then you
don't want to be in remmapped mode or in thumb mode).
A general bit of wisdom I learned using the emulator--it often
gets in states you would like to recover from, eg, if you push the
reset button while in the emulator, you can recover the connection
by typing mon reset. Type ctl-C if the emulator is in the running
state.
Also note--the set $reg command doesnt work very well. For example,
setting the $sp_irq doesnt do anything, nor does the x $sp_irq
reflect the correct state of the actual sp_irq!! The only way
to set these regs is under program control or by first setting
the cpsr to the IRQ mode (0xd2) and then programming the sp reg.
Even then this sometimes doesn't work because the set $cpsr sometimes
doesn't have any effect. Usually doing it a second time makes it
work, but you can imagine my frustration with this part--everything
else works pretty well on the JTAG emulator, but this part doesnt.
HCS_C will cause a jump to either address 0 or to address 0x400000
depending on the boot select setting of the DIP switch. Be sure
the link file actually puts something at these addresses depending
on the DIP switch setting.
If you want to change the boot code, you must resynthesize the
FPGA. Don't panic, it's very easy but a little time consuming.
You must first test the boot code by overwriting the FPGA boot
memory. First enable Boot memory writes by turning off the boot
lock in the FPGA_TIMER_DATA register bit 2. Now use the emulator
to load your boot program into address 0 (assuming a remap has
not yet occurred--you must reset with the DIP switch in the
download mode, or use the emulator to reset ("mon reset").
If you want to do linked boot from flash (rec
So, do a make command, eg:
make hcs_boot
and then run the emulator (assuming no make errors).
The gdbinit file specifies how to run the emulator
connection, in particular you must set target remote localhost:8888
in
this file.
Use this command to connect to the ARM processor:
OCDLibRemote -c ARM -d WIGGLER -a 1 -s 1;/usr/local/arm/bin/arm-elf-gdb -nw;
The -nw specifies the command line interface. There is a windowed
GUI version but I couldn't get it to work well. I'm sure someone
will figure that out soon, I've got other things to do.
It will first try to create a listening port for the JTAG emulator
port (it will say "Started listening socket on port 8888".
Then it will show a connection to the JTAG buffer board:
"OcdLibRemote ARM: WIGGLER via LPT 1 at speed : 1
(it doesn't seem to matter what speed or packet size is
used).
Now you are connected to the command line version of the
JTAG emulator (if no errors and correctly connected to the
ARM processor on the HCS_C board).
At this point, the emulator is ready to run (the object
code and symbols are automatically loaded for you depending
on what is set in the gdbinit file). But you must do a remap,
the code will not correctly load for debugging since before
the remap you will place the code in the FPGA boot ROM space.
After remap, the boot ROM code disappears and is replaced
with the ARM SRAM (not the external SRAM). Run the remap
command macro that I provided:
source remap
then load the object file and symbols:
load hcs_boot
symbol-file hcs_boot
monitor reset
Now you can type
continue
and this will run from reset.
If you do si, this will step (but there are no symbol entries
for the crt0.s assembly file, so be prepared for a bunch
of
symbol errors until you get past this short file).
Do a control-C to break into the emulator at any time.
Use the onboard FPGA analyzer to display the addresses
executed. For example, run the anly_setup macro to
initialize recording. As soon as you type cont (short
alias for continue), you should see a dump of addresses
on the LCD display port. It will look like this:
000: 000a2 (the start of the trace is at address 0xa2)
001: 000ba (there was a jump or call to address 0xba)
002: 000be
003: 000c0
004: 000c2
005: 000c4
006: 000c6
007: 001e4 (there was a jump or call to address 0x1e4)
To control the display, make
sure a PS2 compatible keyboard is attached to the PS2
port on HCS_C. Then type 'n' to go to the next 8 lines
of the trace buffer, 'p' to go to the previous 8 lines
of the trace buffer, 'N' to skip forward 64 lines of the
trace buffer, 'P' to skip back 64 lines of the trace
buffer, and 't' to return to the start of the trace
buffer. You can type 's' to stop a recording in process
and dump the current state buffer list, and type 'r' to
restart a trace recording. Type 'u' to release the
PS2 keyboard and display back to the HCS_C processor
(a triggered analyzer dump takes over the display and
keyboard to allow viewing of states. Remember that the
analyzer holds a maximum of 1024 states. Note that the
keyboard has auto-repeat which can make it a little
goosey when trying to control the analyzer, you'll
need those Playstation reflexes! Actually it's not
too bad, if I get some time I'll make this a little
easier.
Here are some common commands I use for debugging code:
source FILE
run commands listed in FILE
load FILE
symbol-file FILE
ARM Monitor Commands :
reset
runfrom <addr>
set $<reg> = <value>
(eg, set $pc = 0x100)
b [filename:]<line_number>
note, you must clear the breakpoint ('d') before running
again or singlestepping ('si')
halt
resetrun
endian <big|little>
hbreak set <addr> | clear
reg <name> = <val>
char <addr> = <val>
short <addr> = <val>
long <addr> = <val>
(arm-gdb) help x
Examine memory: x/FMT ADDRESS.
ADDRESS is an expression for the memory address to examine.
FMT is a repeat count followed by a format letter and a size letter.
Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),
t(binary), f(float), a(address), i(instruction), c(char) and s(string).
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).
The specified number of objects of the specified size are printed
according to the format.
Defaults for format and size letters are those previously used.
Default count is 1. Default address is following last thing printed
with this command or "print".
display the current pc on each break or step with:
display
$pc
display the current r0 on each break or step with:
display
$r0
These registers will be displayed on every step or break
step assembler is si (or stepi N)
step C instruction is s (or step N) this seems to work slowly. You
must clear a breakpoint, stepping on a breakpoint seems to cause
a crash
(arm-gdb) help define
Define a new command name. Command name is argument.
Definition appears on following lines, one command per line.
End with a line of just "end".
Use the "document" command to give documentation for the new command.
Commands defined in this way may have up to ten arguments.
Much more on this will be added here soon.
And here is some more.
ARM interrupts: This is complicated. The ARM processor is actually
a core microprocessor inside the AT91R40008 chip, an external
interrupt manager circuit (AIC) is a second layer of hardware that
also has to be set up. So, you find yourself managing the processor
interrupt registers, the AIC interrupt generation registers, the
peripheral interrupt circuit registers, and finally the FPGA interrupt
enables and status, and finally the FPGA peripheral actually generating
the interrupt!! Argggh! But it's all worked out for you unless you
write your own code, then you have quite an adventure. To make matters
worse, the gnu arm-elf interrupt register setting from the command
line does not work! You can set the sp_irq and other registers only
by setting the cpsr register to irq mode, then setting sp--but don't
try to set sp_irq!! In fact even setting cpsr sometimes doesn't work!
If you do it a second time, then it seems to always work, bleah!
Everything else seems to work well, but be forewarned.
Fortunately (except for setting the banked registers such as cpsr and
sp) this all works pretty well once you understand it, so have patience!
There are a lot of registers in the ARM processor, AIC, and FPGA that
all need to be set up to get interrupts to function:
FPGA:
0x800032 interrupt mask (set with 0x800020, clear with 0x800022)
0x800022 interrupt vector
0x800020 prioritized interrupt number
AIC:
0xfffff000-07f specify mode for each interrupt source
0xfffff080-0ff specify vector address for each interrupt source (see
init code snippet below)
0xfffff100 the AIC interrupt vector
0xfffff110 the AIC interrupt mask (set with 0xfffff120, clear with
0xfffff124
0xfffff108 the AIC active interrupt number
0xfffff10c the AIC pending interrupts
ARM processor--you'll want to refer to the ARM7 doc for all the info
on this.
The tricky part with ARM interrupts is trying to get the
ARM processor to actually recognize and vector to an interrupt.
If you are using an ARM pin (eg IRQ0, that the FPGA is driving)
you must make sure it is not in PIO mode (it powers up thinking
it is an IO input pin and wont cause ARM interrupts). Use this
startup code with the ARM_PIO_DISABLE reg instruction clear
(bit 9) to set it to ARM interrupt mode:
// Before doing anything, make sure all interrupts are disabled
lptr = ARM_AIC_IDCR;
*lptr = 0xffffffff; // Init off all interrupts for AIC
*fpga_int_disable_ptr = 0xffff; // Init off all FPGA interrupts
// First, temporarily use the rxptr to set up the interrupt vector
lptr =
ARM_AIC_VECTOR_CONSOLE;
*lptr = (int)console_int_service & 0xfffffffe;
lptr = ARM_AIC_VECTOR_FPGA0;
*lptr = (int)(fpga0_int_service + 0x10) & 0xfffffffe;
lptr = ARM_AIC_VECTOR_FPGA1;
*lptr = (int)(fpga1_int_service + 0x10) & 0xfffffffe;
// ok, now setup the rxptr to be the data channel. This is a int* location
lptr = ARM_PIO_DISABLE;
*lptr = 0x00600200; // Set PIO so console RS232 talks to pins
Normal ARM interrupts will vector to hardcoded address 0x18, where
I store a
lptr = ARM_INT_VECTOR
*lptr = 0xe51fff20; // 18:ldr pc,[pc,# - &0xf20] points to AIC_IVR
If you store your interrupt routine addresses in the appropriate AIC
vector locations (0xfffff080-0xfffff0ff), this instruction will
cause the ARM to vector to the correct service routine when the
corresponding interrupt is asserted. I also load the other exception
instruction locations with jump-to-selfs, for example locations 0x4,
0x8,0xc,0x10,0x14,0x1c. That way if you get an unexpected exception
such as an invalid pointer reference, you wont just go off into
the weeds. Location 0 should have a jump instruction to your
main() entry point.
Notice that I add 0x10 to the interrupt_service routine addresses.
This was a nice way to vector directly into a C routine without
executing the C routine stack setup (your int service routines
may not have local vars, if you need them you need to make a
call from your interrupt service routine). You must put the
following inline assembly to correctly save away your background
registers:
// All interrupts vector to here
void fpga0_int_service()
{
// save regs in the sp_irq location.
asm (" stmdb sp!,{r0,r1,r2,r3,r11,r12}");
Note you must put your interrupt service routines in a separate
file because most of HCS_C runs in thumb mode, but the interrupt
service routines run in ARM mode--so you must compile the service
routines with the -thumb option turned off. When you leave the
service routine, be sure to turn thumb back on: use this exit
sequence:
// OK, here's the interrupt service exit sequence
*aic_iccr_ptr = 0x10000; // Clear the FPGA int in AIC (IRQ0)
*aic_ivr_ptr = 0x0; // Clear the AIC vector in protected mode
*aic_eoicr_ptr = 0x0; // Indicate done servicing interrupts
asm (" mrs r0,SPSR");
asm (" bic r0,r0,#0x80"); // re-enable the interrupt
asm (" msr SPSR,r0");
asm (" add sp,sp,#0x18"); // back up over C sub to r11
asm (" ldmdb sp,{r0,r1,r2,r3,r11,r12}");
asm (" subs pc,r14,#4");
}
Yes, you probably will tell me this is pretty kludgey--but
in between, you can write all the C code you want! No
assembly is needed.
The FPGA interrupts all are routed through IRQ0, so you must
enable ARM to turn on this interrupt. Then turn on all of the
ARM or FPGA interrupt enables you want. Note that I set the
ARM EOICR register--for some reason ARM needs this to start
receiving interrupts (normally you do this at the end of an
interrupt).
// Now enable interrupt sources
lptr = ARM_AIC_IECR;
*lptr = 0x10008; // Turn on interrupts for UART1 console, FPGA
*aic_eoicr_ptr = 0x00; // Indicate done setting up interrupts
Note--if you set vectors (eg at the interrupt location 0x18,
single stepping on the code that writes this location will
cause any locations that have breakpoints on them to NOT be
changed.
Took a while to figure THAT one out!!
HCS_C Register Map
---------------------------------------------------------------------
HCS_C MEMORY MAP (Subject to change!)
Note that you must write 16 bit words to the FPGA, do not write
bytes or longs.
Before Remap:
FPGA BOOT CODE: 0x00000
FPGA REGISTERS: 0x10000
FLASH Eprom boot sector 0x20000
Internal
SRAM 0x300000
After Remap (assuming correctly configured memory map)
Internal ARM SRAM 0x000000
FPGA BOOT CODE 0x810000
FPGA REGISTERS: 0x800000
ARM SRAM at 0x000000 after remap command
FLASH devices are 4Mbytes each (1 of 4). They are selected by
the FPGA_BLOCK_SELECT register special codes for writing
FLASH at 0x400000-5FFFFF
External
SRAM 0x600000
A non-flash routine (eg, sram or boot) sets the flash select
to a special write code cs0=140h, cs1=280h,cs2= 180h, cs3=240h.
This helps prevent accidental flash reprogramming
0x000: n_flash read cs0 device (default)
0x001: n_flash read cs1 device
0x002: n_flash read cs2 device
0x003: n_flash read cs3 device
0x140:
n_flash write cs0 device
0x280: n_flash write cs1 device
0x180: n_flash write cs2 device
0x240: n_flash write cs3 device
SRAM devices are 1/2M each at addresses
External SRAM at 0x600000-7FFFFF
Daughter cards are 64Kbytes each
Daughter card 0 at 0x900000
Daughter card 1 at 0x910000
Daughter card 2 at 0x920000
Daughter card 3 at 0x930000
FPGA
REGISTERS (0x10000 before remap, 0x800000 after remap)
// FPGA registers are 9:1 values (word offsets) within the 0x800000
// base offset. To get the address just double these values
FPGA_MODE 8'h00
// 0x800000
voice_select [1:0]
spi_select [3:2]
x10_tx_mode [4]
rtc_sel <= [7:5] 0=minsec, 1=dowhour, 2=monthdom
3=year
4=matchlow, 5=matchhi, 6=mancompj
FPGA_VOICE_DATA // 0x800002 r/w
FPGA_SPI_DATA // 0x800004
UART r/w
FPGA_RS232_0_DATA // 0x800006
FPGA_RS232_1_DATA // 0x800008
FPGA_RS485_0_DATA // 0x80000a
FPGA_RS485_1_DATA // 0x80000c
FPGA_RS485_2_DATA // 0x80000e
FPGA_KEYBD_DATA // 0x800010 read
FPGA_DISPLAY_DATA // 0x800010 write
FPGA_CID_DATA // 0x800012
FPGA_X10_DATA // 0x800014
FPGA_X10_STAT // 0x800016
FPGA_RELAYS_DATA // 0x800018
FPGA_TIMER_DATA // 0x80001a
read RTC seconds, freeze time
timer_match_clrint [0]
write
timer_1min_clrint [1]
boot_lock [2]
FPGA_RTC_TIME1 // 0x80001a
read RTC val, freeze time
FPGA_RTC_TIME2 // 0x80001c
read RTC val, unfreeze time
FPGA_RTC_DATA // 0x80001c
write (set time)
FPGA_RTC_DATA // 0x80001c read
seconds, freeze time
FPGA_WD_DATA // 0x80001e
FPGA_INT_SET // 0x800020
write set interrupt enable
FPGA_INT_STATUS // 0x800020 read ints:
timer_match_int, // int 15
voice_int, // int 14
daughter_int, // int 13
keybd_rx_int, // int 12
spi_tx_int, // int 11
voice_tx_int, // int 10
x10_tx_int, // int 9
x10_rx_int, // int 8
cid_rx_int, // int 7
rs485_2_int, // int 6
rs485_1_int, // int 5
rs485_0_int, // int 4
rs232_1_int, // int 3
rs232_0_int, // int 2
display_tx_int, // int 1
timer_1sec_int // int 0
FPGA_INT_CLR
// 0x800022 write clear
interrupt enable
FPGA_INT_VECTOR // 0x800022
FPGA_IO_0_DATA // 0x800024
FPGA_IO_1_DATA // 0x800026
FPGA_TRACE_DATA // 0x80002c
FPGA_TRACE_STATE // 0x80002e
trace_state [0]
trace_status [1]
record [2]
collect [3]
triggered [4]
FPGA_VECTOR_DATA // 0x800030
FPGA_INT_ENABLES // 0x800032
FPGA_BLOCK_SELECT // 0x800034
FPGA_ANLY_BLOCK // 0x800036
FPGA_ANLY_TRIGGER // 0x800038
FPGA_ANLY_COUNT // 0x80003a
proc_start_record [10];
proc_stop_record [11];
proc_start_collect [12];
proc_stop_collect [13];
proc_trigger [14];
FPGA_ANLY_STATE // 0x80003c
FPGA_MATH1 // 0x800040
FPGA_MATH2 // 0x800042
FPGA_RANDOM // 0x800044
FPGA_TIC_TIMER1 // 0x800046 write set limit, read random
FPGA_TIC_TIMER2
// 0x800048
---------------------------------------------------------------------
HCS_C FPGA REGISTER DESCRIPTION
HCS_C FPGA Display Port Register
HCS_C FPGA Keyboard Port Register
HCS_C FPGA Interrupt Registers
HCS_C FPGA RealTime Clock Port Registers
HCS_C FPGA Tic Timer Registers
HCS_C FPGA SPI Control Registers
HCS_C FPGA UART Control Registers
HCS_C FPGA X10 Control Registers
HCS_C FPGA Voice Recorder Control Registers
HCS_C FPGA Analyzer Control Registers
HCS_C Reset Control Register
---------------------------------------------------------------------
HCS_C
Mode Register
HCS_C Mode register:
This register controls some general HCS_C system operational
mode selections. For example, a specific voice chip or SPI device
are selected here (HCS_C optionally can have up to four devices
piggybacked). The realtime clock interface has several modes to
choose from (see the RTC register description).
0x800000: FPGA_MODE
write: [8:0]
voice_select[2:0] 0: playback only device 0
1: device 1 (playback or record)
2: device 2 (playback or record)
3: device 3 (playback or record)
4: device 0 (playback or record) (This is the
accidental erase override)
spi_select[4:3]
rtc_sel[7:5] (see the RealTime Clock Registers)
x10_tx_mode[8] 0 specifies 60Hz (US) 1:50Hz (Europe)
default value 0
programming example:
write addr
0x800000 = 0x0004 (set voice to allow recording device 0)
---------------------------------------------------------------------
HCS_C Block Select Register
HCS_C Block select register:
This register controls which Flash Eprom is currently mapped
to address 0x400000--0x5fffff. Special codes are used when
writing to prevent accidental flash programming. Normal read
codes are different:
0x800000: FPGA_BLOCK_SELECT
write: [15:0]
0x0: read Flash device 0
0x1: read Flash device 1
0x2: read Flash device 2
0x3: read Flash device 3
0x140: write Flash device 0
0x280: write Flash device 1
0x180: write Flash device 2
0x240: write Flash device 3
These codes can be combined. For example, to read and write
device 1, use 0x281. To execute from device 2 but write to
device 3 (eg, for executing code from device 2 but writing
logging data to device 3), use 0x242.
programming example:
write addr
0x800000 = 0x0281 (enable second flash device)
---------------------------------------------------------------------
HCS_C Reset Control Register
Reset and the Watchdog timer:
The FPGA generates the board reset. On powerup or when the reset
button is pushed, a one second reset assertion occurs. There is
a watchdog timer that can be turned on with a write of an 8bit
value to the FPGA_WD_DATA register. This value is the number of
seconds before the watchdog will trip the reset line. If the
watchdog is rewritten before the timeout, the reset will not
occur. To perform a manual software reset of the processor, set the
write value to 0 (this will have no effect on the status of the
watchdog timer, nor will it turn it on or off). There is a five
bit status register that indicates how many watchdog resets have
occurred up to 15 (bits 3:0). Bit 4 indicates that a manual
software reset has occurred. Note that this reset does not reset
the FPGA functions, only the processor.
0x80001e: FPGA_WD_DATA
write: [7:0] seconds before timeout, if = 0, software reset
default value 0
read: [4:0] bits 3:0 are number of watchdog resets that have
occurred (this cannot be reset, it will max
out at 0xf (15). Bit 4 indicates if the
manual software reset has occurred, this bit
also cannot be reset.
default value 0
---------------------------------------------------------------------
HCS_C FPGA Interrupt Registers
Interrupt vector generator: This is a simple interrupt vector
scheme that prioritizes the HCS_C interrupts and reads back a
4 bit programmable vector number that can be used to point
directly to a call to an interrupt service routine. The vector
SRAM has been removed since the ARM interrupt structure didn't
really make it that useful.
Note that this interrupt is connected to the ARM int0 pin (external
int 0). You must enable bit 9 the PIO user interface in the ARM processor
as inputs before the interrupt will be recognized. Then you must
enable interrupt bit 9 in the ARM AIC interrupt controller. To make
this work right, write a 0x200 (long) to the PIO disable register
(this enables peripheral, ie interrupt, control rather than PIO
control). Then write the AIC interrupt mode (0xfffff040), service
vector (0xfffff0c0), interrupt enable bit 16, and so on. The interrupt
vector occurs at ARM address 0x18, so putting a "set PC to the AIC_IVR
(address 0xfffff100) register value" (assembly ldr PC,[PC,# - &0F20)
will provide individual interrupt routines for each ARM interrupt.
Once that has occurred, if the FPGA interrupt is the source, you must
then read the FPGA_INT_VECTOR register to get the offset for each FPGA
int source. Sounds complicated, but it's actually kind of neat and
very fast. Within two instructions the processor will be executing
the code specific to the actual FPGA module needing service. Of course,
both the ARM vectors and the FPGA interrupt service vectors must be
preloaded
before interrupts are enabled.
0x800020: FPGA_INT_STATUS
read: [15:0] interrupts pending
int 15: timer_match_int,
int 14: voice_int,
int 13: daughter_int,
int 12: keybd_int,
int 11: spi_tx_int,
int
10: voice_tx_int,
int 9: x10_tx_int,
int 8: x10_rx_int,
int 7: cid_rx_int,
int 6: rs232_rx0_int,
int 5: rs232_rx1_int,
int 4: rs485_rx0_int,
int 3: rs485_rx1_int,
int 2: rs485_rx2_int,
int 1: display_tx_int,
int 0: timer_1sec_int
default value 0
0x800020: FPGA_INT_SET
write: [15:0] Writes a interrupt enable for each active bit.
Any inactive bit does not affect the current interrupt
enable state. This mechanism is the same as the method
used in the ARM processor, and along with FPGA_INT_CLR
allows interrupt enable control without affecting other
interrupt enables.
default value 0
read: [15:0] The interrupt logic prioritizes the interrupts vvv
according to the interrupt number (highest is highest
priority) and supplies the corresponding vector offset
to this register.
default value 0
0x800022:
FPGA_INT_CLR
write: [15:0] Writes a interrupt enable for each active bit.
Any inactive bit does not affect the current interrupt
enable state. This mechanism is the same as the method
used in the ARM processor, and along with FPGA_INT_CLR
allows interrupt enable control without affecting other
interrupt enables.
default value 0
read: [15:0] The interrupt logic prioritizes the interrupts
according to the interrupt number (highest is highest
priority) and supplies the corresponding vector offset
to this register.
default value 0
0x800020:
FPGA_INT_STATUS
read: [15:0] Read back the currently active interrupts. These
interrupts must be cleared within their respective
modules, writing to this register does not clear
interrupts.
default value 0
0x800022: FPGA_INT_VECTOR
read: [15:0] The bits [3:0] indicate which interrupt has been
prioritized. Bits [11:4] indicate which of 256 timer
events occurred (prioritized). The interrupts cannot
be cleared by writing to this register, they must be
cleared with writes to the respective interrupting
modules.
default value 0
0x800032: FPGA_INT_ENABLES
read: read back the current interrupt enable state. Set with
FPGA_INT_SET, clear with FPGA_INT_CLR. Note this register
cannot be written to directly.
default value 0
programming example:
(coming)
---------------------------------------------------------------------
HCS_C FPGA IO Port Registers
IO ports: HCS_C has 32 general purpose buffered inputs and 32 general
purpose buffered outputs.
0x800024: FPGA_IO_0_DATA
write: [15:0] Writes a 16 bit word to the low 16 output pins.
Note, no bit addressability is provided, it is necessary
to maintain a shadow register for this capability. No
readback of these bits are available.
default value NOT SET ON RESET! Use caution if outputs are
connected directly to machinery since the output register
has no power-on clear. The devices do not specify (in the
data sheet) how it will come up from power-up, although
my board always comes up with all bits 0. Nevertheless,
this register should be initialized to 0 or the desired
level immediately upon powerup processor initialization.
read: [15:0] Reads a 16 bit word from the low 16 input pins.
0x800026: FPGA_IO_1_DATA
write: [15:0] Writes a 16 bit word to the high 16 output pins.
Note, no bit addressability is provided, it is necessary
to maintain a shadow register for this capability. No
readback of these bits are available.
default value: NOT SET ON RESET! Use caution if outputs are
connected directly to machinery since the output register
has no power-on clear. The devices do not specify (in the
data sheet) how it will come up from power-up, although
my board always comes up with all bits 0. Nevertheless,
this register should be initialized to 0 or the desired
level immediately upon powerup processor initialization.
read: [15:0] Reads a 16 bit word from the high 16 input pins.
programming example:
write addr
0x800024 = 0x5555 (write data to low IO outputs)
write addr 0x800026 = 0xaaaa (write data to high IO outputs)
read addr 0x800024 = <read IO data low>
read addr 0x800026 = <read IO data high>


---------------------------------------------------------------------
HCS_C FPGA Display Port Register
Display port
0x800010: FPGA_DISPLAY_DATA
write: [7:0] Writes a character to the display UART, permanently
set to 9600 baud. This TTL level port is designed to
connect directly to a SEETRON LCD display 12864, although
it can be connected to any serial port driver and then
to any serial device such as the PC serial port.
Note--
the LCD connector provides +3.3V to the three pin LCD
connector on the right side of the HCS_C board (above the
three pin CID connector and the three three-pin RS485
connectors). If you are using a 5V SEETRON display, you
must supply the display with a separate 5V supply output.
DO NOT connect this +3.3V input pin to the LCD 5V supply,
you will damage the HCS_C board.
programming example:
write addr
0x800010 = 0x0055 (write a "U" to the LCD display)
---------------------------------------------------------------------
HCS_C FPGA Keyboard Port Register
PS2 Keyboard port: The HCS_C PS2 keyboard connector accepts any +3V
PS2 keyboard. Only keystrokes can be read, the ability to drive out
the PS2 keyboard signal (to turn on keyboard LEDs, for example) is not
supported. A mapping function converts the keycode to an ASCII character
which is read on this port. The upper 8 bits of the scan code reside
in bits [15:8] and can be used to detect special key combinations.
The keyboard is automatically connected to the FPGA analyzer module
described below if the analyzer is enabled and has captured a trace.
In this mode, the keyboard can control the analyzer without processor
intervention (thus allowing debugging even if the processor has gone
into the weeds).
0x800010: FPGA_KEYBD_DATA
read: [15:0] The [7:0] bits are the ascii code of the last
key pressed or released. Note that keyboard auto-repeat can cause
rapid reoccurrence of keyboard interrupts.
NOTE: I have noticed that sometimes the PS2 interface logic
in the FPGA doesn't power up right. I am working on this--in
the meantime, an easy work around is to push the reset button.
This seems to cure this problem, which can be detected by
reading this register as always 0x0000 (normally it powers
up with something like 0x14, depending on the attached keyboard).
programming example:
type "U" on the keyboard, then:
read addr 0x800010 = 0x6955 (read back the character typed on the
keyboard, the upper byte will be the actual scan code of the keyboard
for
the letter typed)
---------------------------------------------------------------------
HCS_C FPGA RealTime Clock Port Registers
Real Time Clock and Timer module:
This powerful module includes automatic reading of the RTC chip
time and date into FPGA registers every second. The timer module
monitors this register and constantly compares it against all
entries stored in an event SRAM capable of holding 256 events.
Events may be specified with minute resolution and masks for
year, month, day of the week, day of the month, hour, and minute.
When an event match occurs, a match interrupt is generated and the
specified event number can be read (to find out which event caused
the interrupt). Overlapping events at a particular time are
allowed. The various timer registers are selected by writing
to the FPGA_MODE rtc_sel register, then writing to the FPGA_RTC_DATA
register. You must write these in sequence from 0 to 3, the RTC
update is locked out during the sequence to prevent erroneous
times being preset.
0x80001a: FPGA_RTC_DATA
write: [15:0] writes rtc_sel selected timer register
read: [5:0] packed binary seconds value, also freezes RTC time
readback (release with read of the FPGA_RTC_TIME2 value)
rtc_sel: 0: (locks timer updates)
timer_sec[6:0]
timer_min[14:8]
rtc_sel: 1: timer_hour[5:0]
timer_dow[10:8]
rtc_sel: 2: timer_dom[5:0]
timer_month[12:8]
rtc_sel: 3: (unlocks timer updates)
timer_year[7:0]
rtc_sel: 4: write low half of timer match event
rtc_sel: 5: write high half of timer match event
rtc_sel: 6: force manual update (necessary in case there are
multiple events per time slot). Also must be
done after every match event store.
rtc_sel: 7: No longer selects local timer (it was too inconvenient
to have this share the RTC timer select because of interrupt
management problems. The local timer now has its own
registers.
0x80001a: FPGA_RTC_TIME1
read: [15:0] Packed data and time format, low word, also freezes
RTC time readback (release with read of the FPGA_RTC_TIME2
value)
0x80001c: FPGA_RTC_TIME2
read: [15:0] Packed data and time format, high word. Unfreezes
the RTC time readback value.
packed format is a 32 bit word as follows:
rtc_time = {packed_year[7:0],packed_month[3:0],
packed_dom[4:0],packed_dow[2:0],
packed_hour[5:0],packed_min[5:0]};
To set the time, set the RTC_SEL bits in FPGA_MODE to 0,
which will lock timer updates to ensure no timer rollover
while writing the timer values.
Next write the packed data in sequence with RTC_SEL going
from 0 to 3. A write to 3 (year) will unlock the timer
updates.
programming example:
write addr 0x800000 = 0x0000 (select first RTC parameter)
write addr 0x80001c = 0x0345 (write minutes,seconds)
write addr 0x800000 = 0x0020 (select second RTC parameter)
write addr 0x80001c = 0x32 (write day of week, hour)
write addr 0x800000 = 0x0040 (select third RTC parameter)
write addr 0x80001c = 0x0055 (write month, day of month)
write addr 0x800000 = 0x0055 (select fourth RTC parameter)
write addr 0x80001c = 0x0055 (write year)
read
addr 0x80001a = <low half of time in packed format>
read addr 0x80001c = <high half of time in
packed format>
---------------------------------------------------------------------
The tic timer is used for measuring performance or
time intervals under firmware control. It counts clock
cycles divided by a prescale value (powers of 2, from
no prescale all the way up to 2^15, or 32764). The tic
timer can be started, stopped, or cleared. Readback is
freeze protected, so it will not read back until it is
stopped.
0x800046: FPGA_TIMER_MODE
write: [2:0]
bit 0 clear the timer match interrupt
bit 1 clear the timer minute interrupt
bit 2 inihibit writing to the boot ROM
(debug mode)
0x800048:
FPGA_TIC_TIMER1
write: [7:0]
bit 0 start the tic timer
bit 1 stop the tic timer
bit 2 reset the tic timer value
bit 7:4 prescal the tic timer counter:
0: no prescale
1-15: prescale by 2^n
default: 0
read: [15:0] low half of tic timer value.
0x80001a: FPGA_TIC_TIMER2
read: [15:0] high half of tic timer value.
programming example:
write addr 0x800046 = 0x0005 (reset timer, enable counting)
execute program to be timed
write addr 0x800046 = 0x0002 (stop counting)
read addr
0x800046 = <low half of local timer>
read addr 0x800048 = <high half of local
timer>
---------------------------------------------------------------------
HCS_C
FPGA Math Registers
Divide math registers
0x800040: FPGA_MATH1
write: [15:0] Writes a numerator to be divided. This module
performs a fully aligned unsigned divide. All possible ratios
are correctly computed.
read: [15:0] The integer portion of the divide is returned.
0x800042:
FPGA_MATH1
write: [15:0] Writes a denominator to be divided. This module
performs a fully aligned unsigned divide. All possible ratios
are correctly computed.
read: [15:0] The fractional portion (modulus) of the divide is
returned.
0x800044:
FPGA_RANDOM
write: [15:0] Writes a random value limit. This module
performs a 31 bit pseudorandom number evaluation, with a
repeat period of about 100 seconds. The value read will
be within the range of 0 to the programmed limit.
default: 0 (this value must be programmed before using
the random number register).
read: [15:0] A random number from 0 to the random limit value
is returned.
programming example:
write addr
0x800010 = 0x0055
---------------------------------------------------------------------
HCS_C FPGA Analyzer Control Registers
Analyzer:
A general purpose analyzer is used to trace processor addresses.
It provides realtime tracking of program execution. Since it
resides within the FPGA, it is fairly small--it will capture
1024 address states. It has a simple trigger and a countout
circuit that controls how many states are collected after the
trigger. Once triggered and a buffer of data has been collected,
the LCD display and keyboard operate to control the display of
analysis states. The processor can insert instructions that
control when to collect states, when to start and stop recording,
or even can override the trigger (force state collection). Once
the analyzer is full, it no longer needs the processor for displaying
states (eg, if the processor goes into the weeds).
To set up the analyzer, you must load an anly_