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

           Introductory Precautions

       HCS_C General Information

       HCS_C Feature List

       Programming Environment 

       Writing HCS_C Code

       JTAG Emulator commands

       ARM Interrupt handling on HCS_C

       HCS_C Register Address Map

       HCS_C FPGA Register Description

         Analyzer Setup Example

         Appendix A: ESD management

 

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.

 

HCS_C FEATURE LIST


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.

 

WRITING HCS_C CODE

 

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

 

 

HCS_C JTAG EMULATOR COMMANDS

 

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.

 

ARM7 INTERRUPT INFORMATION

 

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

 

    FPGA Mode Register

    HCS_C Block Select Register

    HCS_C FPGA Display Port Register

    HCS_C FPGA Keyboard Port Register

    HCS_C FPGA Interrupt Registers

    HCS_C FPGA IO Port 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 Math 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>

 

---------------------------------------------------------------------

HCS_C FPGA Tic Timer

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_