Building a DSP board, Part Eight: Initializing the Hardware
This is the eighth in a series on how I went about building a dual Motorola DSP56000 sampling board.
We left off with a program bootstrapped into memory from the serial port and all ready to run. But before we begin execution, we must make sure that the DSP registers and ports are full initialized.
At this point, I will insert a file that I include at the top of all of my programs called SETUP.ASM. I will be adding my comments to the file. The extra stuff will be set off by the `#' character.
; setup.asm
; setup the SRAMS
; setup the ssi port to talk to the 5805s
; setup the sci port to talk to host
# The first thing we must do is make sure that no interrupts are running.
# Actually, at this point, the interrupts should already be masked, but
# I never take any chances.
; set up mode register
; LF ** TM ** S1 S0 I1 I0
; 0 0 0 0 0 0 1 1
; No loop flag (LF)
# ^ This should be obvious
; No trace mode (TR)
# ^ Ever the cautious one!
; No scaling (Sn)
# ^ Don't want automatic scaling done.
# (Mainly used for floating point.)
; Mask Interrupts (In)
# ^ Set bits for masking.
ori #$03,mr
# If you think about it, the above is pointless because of the `or' except
# for the masking of the interrrupts. I really should put
# andi #$03,mr
# before it.
# Now we must set the wait states for the SRAMS. Remember that we need
# only one wait state. Also, we can have a max of 15 wait states (4 bits
# per type of memory).
; set bus control register for one wait state for X and Y mem (SRAMS)
; XMEM YMEM PMEM EXIO
; $1 $1 $0 $0
; Number of wait states for each type of memory
movep #$1100,x:m_bcr
# Now we must assign priority levels to the types of interrupts.
# Remember that we hooked up the serial ADCs and DACs to the
# synchronous serial ports. We want to start processing audio data
# the nanosecond it gets in. Therefore, we'll assign it the highest
# priority. As far as the asynchronous serial port goes, we won't
# generally be using while we are doing DSP work. However, it is
# handy to be able to send changes to the chip on the fly. But, do
# we really want to use interrupts to read the asynch serial port?
# Interrupts will already be servicing the audio data, so the main
# loop of the processing programs will simply be an infinite loop.
# Why not have the main loop of the program be on the lookout for
# asynch serial data? I think it's a good idea, so I don't assign
# any interrupts to the asynch port.
; set interrupt priority levels
; SCL1 SCL0 SSL1 SSL0 HPL1 HPL0 **** ****
; 0 0 1 1 0 0 0 0
; **** **** IBL2 IBL1 IBL0 IAL2 IAL1 IAL0
; 0 0 0 0 0 0 0 0
; No SCI interrupts {we'll poll} (SCLn=0)
; Priority 2 for SSI (SSLn=1)
; No Host interrrupts (HPLn=0)
# ^ Not using the host port here. If
# you build a card into the IBMPC, you
# might want to read from host port
# instead of asynch port.
; No IRQx interrups (IxLn=0)
movep #$3000,x:m_ipr
# One of the wonderful things about the synchronous serial port is that
# you can set it to accept word lengths of 24, 16, 12, and 8 bits. If
# you hook into the data stream of a CD player, you'll need to use 24
# bits. (Surprise! This is the standard, even though the bottom 8 bits
# are zeros. I don't think these are actually encoded on the disc, just
# inserted by the circuitry.) Remember, we are using an external clock,
# so no scaling is required.
; initialize ssi port
; setup Control Register A (CRA)
; PSR WL1 WL0 DC4 DC3 DC2 DC1 DC0 PM7 PM6 PM5 PM4 PM3 PM2 PM1 PM0
; 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
; No prescaler (PMn)
# ^ We use external clock.
; No frame rate (DCn)
# ^ We use external clock.
; Word length = 16 (WL1=1, WL0=0)
; No prescaler range (PSR)
# ^ We use external clock.
movep #$4000,x:m_cra
# There are a lot of fancy ways to set up external and internal data
# clocks for the SSI. I just put zeros here 'cause I'm not using them.
; setup Control Register B (CRB)
; RIE TIE RE TE MOD GCK SYN FSL *** *** SCKD SCD2 SCD1 SCD0 OF1 OF0
; 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0
; No output flags (OFn)
; SCn and SCK are all inputs (SCxx=0)
# SC0 is our L/R clock
# SC2 is data fram clock
; Frame length = data word (FSL=0)
# Use Word length, not Bit length
; Synchronous (SYN=1)
# We're all running off the same
# clock, so better use synchronous
# input and output frames.
; No gated glock (GCK=0)
# That's gated CLOCK! The clock is
# continously running, not gated.
; Normal Mode (MOD=0)
# We're not using network mode.
; Transmit on Frame Sync (TE=1)
; Receive on Frame Sync (RE=1)
# ^ Sync with frames.
; No transmit interrupt (TIE=0)
# We don't need this, as we know
# it will be clear when we are
# ready to write to it. This is
# because we are not generating
# more data than we are provided with.
; No receive interrupt, yet (RIE=0)
# Hey! No interrupts yet! Later!
movep #$3200,x:m_crb
# Now we set up the asynchronous serial port. We'll do standard N81
# and 4800 baud (because 9600 doesn't divide down from 20MHz by integers
# very well.
; initialize SCI port
; setup SCI Interface Control Register (SCR)
; **** **** TMIE TIE RIE ILIE TE RE
; 0 0 0 0 0 0 1 1
; WOMS RWU WAKE SBK **** WDS2 WDS1 WDS0
; 0 0 0 0 0 0 1 0
; 1 start-8 data-1 stop (WDS2,WDS1,WDS0=0,1,0)
; No send break (SBK=0)
; No wake-up (WAKE,RWU=0)
# ??? Something to do will idle line?
; No open-collector output (WOMS=0)
; Enable receiver (RE=1)
; Enable transmitter (TE=1)
; No idle line interrupt (ILIE=0)
# ??? See above?
; No receive interrupt {we'll poll} (RIE=0)
; No transmit interrupt (TIE=0)
# We'll make sure it's ready
; No timer interrupt (TMIE=0)
movep #$0302,x:m_scr
; setup SCI Clock Control Register (SCCR)
; TCM RCM SCP COD CD11 CD10 CD9 CD8
; 0 0 0 0 0 0 0 0
; CD7 CD6 CD5 CD4 CD3 CD2 CD1 CD0
; 0 1 0 0 0 0 0 1
; We need a baud rate of 4800
# For fun, let's try 9600
; Divider = 20,000,000 / (2*1*2*16*4800) = 65
# 9600 = 32 or 33
; Baud rate really is 4808 (< 0.2% error)
# 9766 (1.7% err) or 9470 (1.4% err)
; CD11-CD0 = 000001000001
# This is 65 binary
; No clock output (COD=0)
# We need no output clock
; No prescaler (SCP=0)
# This puts a 1 instead of 8 in
# the divider equation
; Use internal clock for receive (RCM=0)
; Use internal clock for transmit (TCM=0)
movep #$0041,x:m_sccr
# Now we have to enable all of the input and output pins for
# our serial interfaces.
; enable SSI and SCI pins (Port C Control Register - PCC)
; CC8 CC7 CC6 CC5 CC4 CC3 CC2 CC1 CC0
; 1 1 1 1 0 1 0 1 1
; STD enable (CC8=1)
# Asynch transmit data
; SRD enable (CC7=1)
# Asynch receive data
; SCK enable (CC6=1)
# Synch bit clock
; SC2 enable (CC5=1)
# Synch word clock
; No SC1 (CC4=0)
; SC0 enable (CC3=1)
# Synch L/R clock
; No SCLK (CC2=0)
# No clock output
; TXD enable (CC1=1)
# Synch transmit data
; RXD enable (CC0=1)
# Synch receive data
movep #$01EB,x:m_pcc
Now, to use the above file, you'd put this into the top of your program:
include 'defs.inc'
include 'ioequ.inc'
include 'intequ.inc'
org p:pgmram
include 'setup.asm'
The above top three files are available via my archive server and include all the definitions used in 'setup.asm'.
Then, you do all your initialization and add
; start interrupts
movep #$b200,x:m_crb
andi #$00,mr
The first line starts enables the synchronous serial interrupts and the second line enables all interrupts.