Line 24... |
Line 24... |
|
|
You should have received a copy of the GNU General Public License along
|
You should have received a copy of the GNU General Public License along
|
with this program. If not, see \texttt{http://www.gnu.org/licenses/} for a copy.
|
with this program. If not, see \texttt{http://www.gnu.org/licenses/} for a copy.
|
\end{license}
|
\end{license}
|
\begin{revisionhistory}
|
\begin{revisionhistory}
|
|
0.2 & 5/14/2016 & Gisselquist & Updated Draft, still not complete \\\hline
|
0.1 & 4/22/2016 & Gisselquist & First Draft \\\hline
|
0.1 & 4/22/2016 & Gisselquist & First Draft \\\hline
|
\end{revisionhistory}
|
\end{revisionhistory}
|
% Revision History
|
% Revision History
|
% Table of Contents, named Contents
|
% Table of Contents, named Contents
|
\tableofcontents
|
\tableofcontents
|
Line 48... |
Line 49... |
to change without notice.
|
to change without notice.
|
|
|
This project comes from my desire to demonstrate the Zip CPU's utility in a
|
This project comes from my desire to demonstrate the Zip CPU's utility in a
|
challenging environment. The CMod~S6 board fits this role nicely.
|
challenging environment. The CMod~S6 board fits this role nicely.
|
\begin{enumerate}
|
\begin{enumerate}
|
\item The Spartan--6 LX4 FPGA is very limited in it's resources:
|
\item The Spartan--6 LX4 FPGA is very limited in its resources:
|
It only has 2,400 look--up tables (LUTs), and can only support
|
It only has 2,400 look--up tables (LUTs), and can only support
|
a 4,096~Word RAM memory (16 kB).
|
a 4,096~Word RAM memory (16 kB).
|
\item With only 4kW RAM, the majority of any program will need to be placed into
|
\item With only 4kW RAM, the majority of any program will need to be placed into
|
and run from flash. (The chip will actually support more, just not
|
and run from flash. (The chip will actually support more, just not
|
8k RAM.)
|
8k RAM.)
|
Line 103... |
Line 104... |
\end{center}\end{figure}
|
\end{center}\end{figure}
|
Using this alternate architecture, it should be possible to test the peripherals
|
Using this alternate architecture, it should be possible to test the peripherals
|
and program the flash memory. Both architectures may be loaded into the flash,
|
and program the flash memory. Both architectures may be loaded into the flash,
|
together with the programming code for the Zip CPU.
|
together with the programming code for the Zip CPU.
|
|
|
The basic approach is simple: up and until the software works, the S6 will
|
The basic approach to loading the board is actually quite simple. Using the
|
power up into the alternate architecture of Fig.~\ref{fig:altarchitecture}.
|
Digilent ADEPT JTAG configuration program, {\tt djtgcfg}, the alternate
|
While in this state, the flash may be examined and programmed. Once complete,
|
configuration may be written directly to the device. Once this alternate
|
a UART command to the ICAPE port will tell the S6 to load the (primary)
|
configuration has been loaded, the flash may be examined and programmed.
|
FPGA configuration from an alternate flash location. This alternate location
|
This includes programming a primary and alternate configuration into the
|
will contain a configuration image containing the CPU. The CPU will then begin
|
configuration section of the flash. Once complete, the system may then be
|
following the instructions given to it from the flash.
|
reloaded with the primary configuration file which will contain an image of
|
|
the CPU. The CPU will then begin following the instructions found in flash
|
|
memory.
|
|
|
|
|
|
\chapter{Software}
|
|
\section{Directory Structure}
|
|
\begin{itemize}
|
|
\item[{\tt trunk/bench}] Contains software for emulating the S6 without the S6
|
|
present.
|
|
\begin{itemize}
|
|
\item[{\tt trunk/bench/cpp}] All of the bench testing software is
|
|
written in C++, so it is found in this directory. Primary
|
|
among these programs is the {\tt zip\_sim} program which will
|
|
simulate the ZipCPU within the S6--Soc. Specifically, it
|
|
simulates everything at or below the {\tt busmaster.v} level.
|
|
|
|
Some, although not all, of the peripherals have been simulated
|
|
and made a part of this simulation. These include the
|
|
Quad--SPI flash, the UART, and the LED's.
|
|
|
|
\end{itemize}
|
|
\item[{\tt trunk/doc}] All of the documentation for the S6SoC project may be
|
|
found in this documentation directory. Specifically, I would commend
|
|
your attention to anything with a {\tt .pdf} extension, as these
|
|
are the completed documents. Among these you should find a copy of the
|
|
GPL copyright under which this software is released, as well as a
|
|
pre--built copy of this document.
|
|
\begin{itemize}
|
|
\item[{\tt trunk/doc/gfx}] Here is where the graphics are located in
|
|
support of this specification document.
|
|
\item[{\tt trunk/doc/src}] And here is where the \LaTeX files are
|
|
kept that were used in building both this document as well as
|
|
the GPL copyright.
|
|
\end{itemize}
|
|
\item[{\tt trunk/rtl}] Verilog files
|
|
\begin{itemize}
|
|
\item[{\tt trunk/rtl/cpu}] Verilog files containing the ZipCPU
|
|
core and peripherals. The toplevel file here is the
|
|
{\tt zipbones.v} file, although some of the peripherals, such
|
|
as the {\tt ziptimer.v} are referenced independently.
|
|
\end{itemize}
|
|
\item[{\tt trunk/sw}] The main software directory, primarily a repository
|
|
for software subdirectories beneath it.
|
|
\begin{itemize}
|
|
\item[{\tt trunk/sw/dev}] This directory holds a variety of
|
|
simple programs for the ZipCPU, such as {\tt helloworld},
|
|
{\tt doorbell} and {\tt doorbell2}, as well as software drivers
|
|
for various peripherals, such as the real--time clock simulator,
|
|
and the keypad and display device drivers.
|
|
\item[{\tt trunk/sw/host}] This directory holds support software which
|
|
can be built on and run on the host machine. Building this
|
|
software will involve adjusting the Makefile so that it knows
|
|
where your local ADEPT installation directory is.\footnote{Many
|
|
of the programs also depend upon the serial number of my CMod
|
|
S6 device. This will need to be adjusted in any new install.}
|
|
Once built, you will find a variety of very useful programs
|
|
within here.
|
|
\item[{\tt trunk/sw/zipos}] This directory contains the source code for
|
|
a rudimentary, very basic, operating system that I
|
|
call the ZipOS.
|
|
\end{itemize}
|
|
\end{itemize}
|
|
|
|
\section{ZipCPU Tool Chain}
|
|
To build programs for the ZipCPU, you will need the ZipCPU toolchain. You
|
|
can find this as part of the ZipCPU project, available at OpenCores. Building
|
|
the ZipCPU project should result in a set of binaries in the
|
|
\hbox{\tt trunk/sw/install/cross-tools/bin} directory. Make this directory
|
|
a part of your path, and you should be able to build the CMod S6 ZipCPU
|
|
software. Specifically, you will need to use {\tt zip-gcc}, {\tt zip-as}
|
|
{\tt zip-ld}, and {\tt zip-cpp}. Other tools, such as {\tt zip-objdump} and
|
|
{\tt zip-readelf}, may also prove to be very useful when trying to figure out
|
|
what is going on within the SoC.
|
|
|
|
\section{Bench Test Software}
|
|
|
|
Bench testing software currently consists of the {\tt zip\_sim} program found
|
|
within {\tt trunk/bench/cpp}. This program requires Verilator to run, and
|
|
simulates in a cycle accurate fashion, the entire S6SoC from {\tt busmaster.v}
|
|
on down. Further, the external Quad--SPI flash memory, UART, and LED's are
|
|
also simulated, although the 2--line display, audio, and keypad are not.
|
|
|
|
\section{Host Software}
|
|
These include:
|
|
\begin{itemize}
|
|
\item {\tt dumpuart}: My current approach to debugging involves
|
|
dumping the state of the registers and memory to the
|
|
UART upon reboot. The dumpuart command found here is
|
|
designed to make certain that the UART is first set
|
|
up correctly at 9600~Baud, and that second everything
|
|
read from the UART is directly sent to both a file and
|
|
to the screen. In this fashion, it is similar to the
|
|
UNIX {\tt tee} program, save for its serial port
|
|
attachment.
|
|
\item {\tt readflash}: As I am loathe to remove anything from
|
|
a device that came factory installed, the
|
|
{\tt readflash} program reads the original installed
|
|
configuration from the flash and dumps it to a file.
|
|
\item {\tt wbregs}
|
|
\item {\tt zipload}: This is the primary program you will need
|
|
to get your software loaded on the CMod. It takes three
|
|
arguments. The first is the name of the primary
|
|
Xilinx configuration file, the second is the name
|
|
of the alternate Xilinx configuration file, and the
|
|
third is the name of the ZipCPU program you wish to
|
|
write to Flash memory.
|
|
|
|
Each of these arguments is optional. For example, if
|
|
only one configuration file is given, the loader will
|
|
load the primary configuration. If only one ZipCPU
|
|
program is given, the program will be loaded into
|
|
the program memory area and the configuration file areas
|
|
will be left untouched.
|
|
\end{itemize}
|
|
\section{ZipCPU Programs}
|
|
\begin{itemize}
|
|
\item {\tt helloworld}: The first program any programmer should build,
|
|
``Hello, world!'' This program sends the string, ``Hello, world!''
|
|
over the UART connection once per second. It is a very valuable
|
|
program because, if you can get this program running, you know you have
|
|
a lot of things working and working correctly. For example, running
|
|
this program means you can run the {\tt zip-gcc} compiler, load
|
|
the auxiliar configuration, load the program info flash memory, load
|
|
the primary configuration, and read from the UART port. It also means
|
|
that you must have the UART port properly configured and wired to your
|
|
CMod board.
|
|
\item {\tt doorbell}: This annoying program verifies the functionality of the
|
|
audio device by playing a doorbell sound to the audio port. It will
|
|
then wait ten seconds, and play the doorbell sound again (and again,
|
|
and again). (It gets old after a while ...)
|
|
\item {\tt doorbell2}: This adds to the functionality of the {\tt doorbell}
|
|
program a wait for keypress, and a display of the current time on the
|
|
2--line display. While almost our fully functional program, this
|
|
does not include any menus to configure the device or set time, since
|
|
it doesn't include any keypad functionality.
|
|
\item {\tt kptest}: A test of whether or not they keypad driver works. When
|
|
run, anytime a key is pressed, the key's value (in hex) will be
|
|
sent to the UART. Further, pressing an `F' on the keypad will also
|
|
send a newline over the UART, in case you wish to keep your lines from
|
|
getting too long.
|
|
\end{itemize}
|
|
\section{ZipOS}
|
|
This operating system is pre--emptive and multitasking, although with many
|
|
limitations. Those familiar with the internals of the Linux kernel may laugh
|
|
that I call this an Operating System at all: it has no memory management unit,
|
|
no paging, no virtual memory, no file I/O access, no network stack, no ability
|
|
to dynamically add or remove tasks, indeed it hardly has any of the things
|
|
most often associated with an Operating System. It does, however, handle
|
|
interrupts, support multiple pre--emptive tasks in a multitasking, timesharing
|
|
fashion, and it supports some very basic and rudimentary system calls. In a
|
|
similar fashion, it does contain just about all of the functionality necessary
|
|
for a multi--tasking microcontroller built around a do--forever loop. For its
|
|
size, I consider it an impressive achievement. You are welcome to disagree
|
|
with me, however.
|
|
|
|
This version of the ZipOS starts in the resetdump.s code, so that upon
|
|
any startup the ZipOS will dump register contents, the BusError register, and
|
|
any scope contents to the UART. This can take some time, so you may wish to
|
|
configure what you really wish to send--if anything. If desired, this will
|
|
also dump the entire memory as well. All of this is quite useful in case the
|
|
ZipCPU encounters a bus error or other sort of error that causes it to hang,
|
|
stall, or reboot, as these registers are very carefully not touched prior to
|
|
being sent to the UART output port.
|
|
|
|
{\tt resetdump.s} also calls a rudimentary bootloader, to load the parts of
|
|
the ZipOS that need to run faster into Block RAM. The choice of what parts
|
|
to load into Block RAM is made on a file by file basis, and found within
|
|
the linker script, {\tt cmodram.ld}.
|
|
|
|
Upon completion, {\tt resetdump.s} calls the entry routine for the O/S,
|
|
{\tt kernel\_entry()} found in {\tt kernel.c}. This is the main task loop for
|
|
the entire O/S, and worth studying if you are interested in understanding how
|
|
the O/S works.
|
|
|
|
The user tasks are found (mostly) within {\tt doorbell.c}, also found in the
|
|
ZipOS directory. This file contains two kernel entry points, {\tt kntasks()},
|
|
which returns the number of tasks the kernel needs to know about, and
|
|
{\tt kinit()}, which builds each of the tasks and connects their file
|
|
descriptors to the various devices they will be referencing.
|
|
\subsection{Traps}
|
|
The ZipCPU supports a variety of traps, listed here:
|
|
\begin{itemize}
|
|
\item WAIT: Halts the execution of a process until an event takes place, or
|
|
a timeout has been reached. The events that can take place are a
|
|
bitmask of the various interrupts the CPU supports, together with a
|
|
bitmask of the values found in {\tt swint.h}.
|
|
|
|
The timeout value can either be zero, to return immediately with the
|
|
list of events that have taken place, negative, to wait indefinitely,
|
|
or a positive number of milliseconds in order to wait at least that
|
|
time for the event of interest to take place.
|
|
|
|
This also allows a process to sleep for any number of milliseconds.
|
|
|
|
When wait returns, any events returned by the wait have been cleared.
|
|
|
|
The other thing to be aware of is that events may accumulate before the
|
|
wait system call. They will only be returned and cleared, though, if
|
|
the wait call indicates an interest in those events.
|
|
\item CLEAR: This system call works closely with the wait system call.
|
|
Indeed, it is very similar to a wait system call with a zero timeout.
|
|
It clears any of the requested events which may be pending. If given
|
|
a timeout (in milliseconds), it will start a timer and generate a
|
|
{\tt SWINT\_TIMEOUT} event to be sent to this process when the timer
|
|
completes.
|
|
\item POST: Certain devices, such as the real--time clock and the doorbell
|
|
reader, need the ability of being able to post events to any listener
|
|
within the O/S. The POST system call allows them to POST events in
|
|
this manner.
|
|
\item YIELD: This is simply a way of being nice to other processes. This system
|
|
call takes no arguments and simply asks the scheduler to schedule the
|
|
next process. It does not take this process off of the ready to run
|
|
list, so the next process may be this one. However, since the scheduler
|
|
is a round--robin scheduler, it will only return to this process if
|
|
nothing else is available to run.
|
|
\item READ: This is roughly the same system call as the POSIX read() system
|
|
call. It reads some number of memory addresses (words, not octets),
|
|
to the given file descriptor. If the memory requested is unavailable,
|
|
the read will wait until it is available, possibly indefinitely.
|
|
\item WRITE: This is roughly the same system call as the POSIX write() system
|
|
call. It writes some number of memory addresses (words, not octets),
|
|
to the given file descriptor. If nothing is reading from the device,
|
|
the write stall the task forever, otherwise it will only stall the task
|
|
until the data is either written to the receiving task, or copied into
|
|
a memory buffer.
|
|
\item TIME: Returns the number of seconds since startup. Eventually, this will
|
|
return the number of seconds since January 1, 1970, and be identical
|
|
to the UNIX system time() command, but that may not happen on this
|
|
project.
|
|
% \item SEMGET
|
|
% \item SEMPUT
|
|
\item MALLOC: Allocates memory from the system/kernel heap. This is a very
|
|
low overhead memory allocator that, while it does allocate memory,
|
|
cannot free it later. It is nearly 100\% efficient since only
|
|
one memory address, the top of the heap, is used to determine what
|
|
memory has been allocated.
|
|
\item FREE: Not currently implemented.
|
|
\end{itemize}
|
|
\subsection{Scheduler}
|
|
The ZipCPU currently supports only a round--robin scheduler. Tasks are executed
|
|
in the order they were created, as long as they are available to be executed.
|
|
If no tasks are available to be run, the Scheduler will run the idle task which
|
|
puts the CPU to sleep while waiting for an interrupt.
|
|
|
\chapter{Operation}
|
\chapter{Operation}
|
|
|
\chapter{Registers}
|
\chapter{Registers}
|
There are several address regions on the S6~SoC, as shown in
|
There are several address regions on the S6~SoC, as shown in
|
Line 139... |
Line 381... |
This isn't quite so true with the other address regions. Accessing the I/O
|
This isn't quite so true with the other address regions. Accessing the I/O
|
region, while it may be read/write, may have side-effects. For example, reading
|
region, while it may be read/write, may have side-effects. For example, reading
|
from the debugging scope device's data port will read a word from the scope's
|
from the debugging scope device's data port will read a word from the scope's
|
buffer and advance the buffer pointer.
|
buffer and advance the buffer pointer.
|
|
|
\section{Debugging Scope}
|
\section{Peripheral I/O Control}
|
The debugging scope consists of two registers, a control register and a data
|
|
register. It needs to be internally wired to 32--wires, internal to the S6
|
|
SoC, that will be of interest later. For further details on how to configure
|
|
and use this scope, please see the {\tt WBSCOPE} project on OpenCores.
|
|
|
|
\section{Internal Configuration Access Port}
|
|
The Internal Configuration Access Port (ICAP) provides access to the internal
|
|
configuration details of the FPGA. This access was designed so as to provide
|
|
the CPU with the capability to command a different FPGA load. In particular,
|
|
the code in Fig.~\ref{fig:reload} should reconfigure the FPGA from any given
|
|
Quad SPI {\tt address}.\footnote{According to Xilinx's technical support, this
|
|
will only work if the JTAG port is not busy.}
|
|
\begin{figure}\begin{center}\begin{tabbing}
|
|
{\tt warmboot(uint32 address) \{} \\
|
|
\hbox to 0.25in{}\={\tt uint32\_t *icape6 = (volatile uint32\_t *)0x{\em <ICAPE port address>};}\\
|
|
\>{\tt icape6[13] = (address<<2)\&0x0ffff;}\\
|
|
\>{\tt icape6[14] = ((address>>14)\&0x0ff)|((0x03)<<8);}\\
|
|
\>{\tt icape6[4] = 14;}\\
|
|
\>{\em // The CMod~S6 is now reconfiguring itself from the new address.}\\
|
|
\>{\em // If all goes well, this routine will never return.}\\
|
|
{\tt \}}
|
|
\end{tabbing}
|
|
\caption{Spartan--6 ICAPE Usage}\label{fig:reload}
|
|
\end{center}\end{figure}
|
|
|
|
For further details, please see either the {\tt WBICAPETWO} project on OpenCores
|
|
as well as Xilinx's ``Spartan-6 FPGA Configuration User Guide''.
|
|
|
|
\section{Real--Time Clock}
|
|
The Real Time Clock will be included if there is enough area to support it.
|
|
The four registers correspond to a clock, a timer, a stopwatch, and an alarm.
|
|
If space is tight, the timer and stopwatch, or indeed the entire clock, may be
|
|
removed from the design. For further details regarding how to set and use this
|
|
clock, please see the {\tt RTCCLOCK} project on OpenCores.
|
|
|
|
\section{I/O Peripherals}
|
|
Tbl.~\ref{tbl:ioregs}
|
Tbl.~\ref{tbl:ioregs}
|
\begin{table}[htbp]
|
\begin{table}[htbp]
|
\begin{center}\begin{reglist}
|
\begin{center}\begin{reglist}
|
PIC &\scalebox{0.8}{\tt 0x0100} & 32 & R/W & Interrupt Controller \\\hline
|
PIC &\scalebox{0.8}{\tt 0x0100} & 32 & R/W & Interrupt Controller \\\hline
|
BUSERR &\scalebox{0.8}{\tt 0x0101} & 32 & R & Last Bus Error Address\\\hline
|
BUSERR &\scalebox{0.8}{\tt 0x0101} & 32 & R & Last Bus Error Address\\\hline
|
TIMA &\scalebox{0.8}{\tt 0x0102} & 32 & R/W & ZipTimer A\\\hline
|
TIMA &\scalebox{0.8}{\tt 0x0102} & 32 & R/W & ZipTimer A\\\hline
|
TIMB &\scalebox{0.8}{\tt 0x0103} & 32 & R/W & ZipTimer B\\\hline
|
TIMB &\scalebox{0.8}{\tt 0x0103} & 32 & R/W & ZipTimer B\\\hline
|
PWM &\scalebox{0.8}{\tt 0x0104} & 32 & R/W & PWM Audio Controller\\\hline
|
PWM &\scalebox{0.8}{\tt 0x0104} & 32 & R/W & PWM Audio Controller\\\hline
|
KYPAD &\scalebox{0.8}{\tt 0x0105} & 32 & R/W & Special Purpose I/O, Keypad, LED Controller \\\hline
|
SPIO &\scalebox{0.8}{\tt 0x0105} & 32 & R/W & Special Purpose I/O, Keypad, LED Controller \\\hline
|
GPIO &\scalebox{0.8}{\tt 0x0106} & 32 & R/W & GPIO Controller \\\hline
|
GPIO &\scalebox{0.8}{\tt 0x0106} & 32 & R/W & GPIO Controller \\\hline
|
UART &\scalebox{0.8}{\tt 0x0107} & 32 & R/W & UART data\\\hline
|
UART &\scalebox{0.8}{\tt 0x0107} & 32 & R/W & UART data\\\hline
|
\end{reglist}
|
\end{reglist}
|
\caption{I/O Peripheral Registers}\label{tbl:ioregs}
|
\caption{I/O Peripheral Registers}\label{tbl:ioregs}
|
\end{center}\end{table}
|
\end{center}\end{table}
|
shows the addresses of various I/O peripherals included as part of the SoC.
|
shows the addresses of various I/O peripherals included as part of the SoC.
|
|
|
|
\subsection{Interrupt Controller}
|
The interrupt controller is identical to the one found with the ZipSystem.
|
The interrupt controller is identical to the one found with the ZipSystem.
|
Please read the ZipSystem documentation for how to control this.
|
The layout of the PIC bits is shown in Fig.~\ref{fig:picreg}.
|
|
\begin{figure}\begin{center}
|
|
\begin{bytefield}[endianness=big]{32}
|
|
\bitheader{0-31} \\
|
|
\bitbox{1}{E}
|
|
\bitbox{15}{Enabled}
|
|
\bitbox{1}{A}
|
|
\bitbox{15}{ACTIVE}
|
|
\\
|
|
\end{bytefield}
|
|
\caption{Programmable Interrupt Control (PIC) Register}\label{fig:picreg}
|
|
\end{center}\end{figure}
|
|
This controller supports up to fifteen interrupts, however only twelve are
|
|
defined within the SoC. If any interrupt line is active, the PIC controller
|
|
will have that bit set among it's active set. Once set, the bit and hence the
|
|
interrupt can only be cleared by writing to the controller. Interrupts can
|
|
also be enabled as well. The enabled bit mask controls which interrupt lines
|
|
are permitted to interrupt the CPU. Hence, just because an interrupt is active
|
|
doesn't mean it will interrupt the CPU--the enabled line must be set as well.
|
|
Finally, then {\tt A} or {\tt ANY} bit will be high if any interrupts are both
|
|
enabled and active, whereas the {\tt E} or global interrupt enable bit can be
|
|
set to allow the PIC to interrupt the CPU or cleared to disable all interrupts.
|
|
|
|
To keep operations on this register atomic, most of the bits of this register
|
|
have special meanings upon write. The one exception to this is the global
|
|
interrupt enable bit. On any write, interrupts will be globally enabled or
|
|
disabled based upon the value of this bit. Further, the {\tt ANY} bit is a
|
|
read only bit, so writes to it have no effect.
|
|
|
|
Enabling specific interrupts, via writes to the enable lines, are different.
|
|
To enable a specific interrupt, enable all interrupts and
|
|
set the wire high associated with the specific interrupt you wish to enable.
|
|
Hence writing a {\tt 0x80010000} will enable interrupt line zero, while also
|
|
enabling all previously enabled interrupts.
|
|
To disable a specific interrupt, disable all interrupts and write a one to the
|
|
enable line of the interrupt you wish to disable. In this fashion, writing a
|
|
{\tt 0x00010000} will disable all interrupts and leave interrupt line zero
|
|
disabled when the interrupts are re--enabled later, whereas {\tt 0x07fff0000}
|
|
will disable all specific interrupts.
|
|
|
|
Interrupts are acknowledged in a fashion similar to enabling interrupts. By
|
|
writing a `1' to the active bit mask, the interrupt will be acknowledged and
|
|
reset, whereas writing a `0' leaves the interrupt untouched. In this fashion,
|
|
as individual interrupts are handled, a `1' may be written to this bottom mask
|
|
to clear the interrupt. Be aware, however, that any interrupt acknowledgement
|
|
may also globally enable or disable interrupts.
|
|
|
|
\subsection{Last Bus Error Address}
|
The Bus Error peripheral simply records the address of the last bus error.
|
The Bus Error peripheral simply records the address of the last bus error.
|
This can be useful when debugging. While the peripheral may only be read,
|
This can be useful when debugging. While the peripheral may only be read,
|
setting it is really as easy as creating a bus error and trapping the result.
|
setting it is really as easy as creating a bus error and trapping the result.
|
|
Another use for this is upon any reboot, it is possible to read the address
|
|
of the last bus error and perhaps learn something of what caused the CPU to
|
|
restart.
|
|
|
|
\subsection{ZipTimer}
|
|
The S6 Soc contains two ZipTimers, available for the CPU to use. These are
|
|
countdown timers. Writing any non--zero value to them will cause them to
|
|
immediately start counting down from that value towards zero, and to interrupt
|
|
the CPU when they reach zero. Writing a new value while the timer is running
|
|
will cause that new value to automatically load into the CPU and start counting
|
|
from there. Writing a zero to the timer disables the timer, and causes it to
|
|
stop.
|
|
|
|
ZipTimer A can be set to auto reload. When set, the timer will automatically
|
|
load it's last set value upon reaching zero and interrupting the CPU. This
|
|
effectively turns it into an interrupt timer if desired. To set this feature,
|
|
write to the timer the number of clock ticks before an interrupt, but also set
|
|
the high order bit. In this fashion, writing a {\tt 0x80013880} will interrupt
|
|
the CPU every millisecond, starting one millisecond after the write takes place
|
|
(assuming an 80~MHz system clock).
|
|
|
|
ZipTimer B has been wired for a different purpose. ZipTimer B does not support
|
|
auto reload, nor will it interrupt the CPU. Instead, ZipTimer B has been wired
|
|
as a watchdog timer. When this timer reaches zero, the CPU will be rebooted.
|
|
One way to use this timer would be in conjunction with the ZipTimer A, and to
|
|
write a number to it upon any entry to the interrupt service routine. If given
|
|
enough time, this would cause the CPU to reboot if for any reason it locked up.
|
|
|
The two ZipTimer's are ZipSystem timer's, placed onto this peripheral bus.
|
\subsection{PWM Audio Controller}
|
They are available for the CPU to use. Common uses might include I2C or SPI
|
The bit fields of the PWM Audio controller are shown in Fig.~\ref{fig:pwmreg}.
|
speed control, or multi--tasking task-swap control. For further details, please
|
\begin{figure}\begin{center}
|
see the ZipSystem documentation.
|
\begin{bytefield}[endianness=big]{32}
|
|
\bitheader{0-31} \\
|
|
\bitbox{10}{Unused}
|
|
\bitbox{1}{S}
|
|
\bitbox{1}{G}
|
|
\bitbox{3}{}
|
|
\bitbox{1}{E}
|
|
\bitbox{16}{Sample}
|
|
\\
|
|
\end{bytefield}
|
|
\caption{PWM Audio Controller Bitfields}\label{fig:pwmreg}
|
|
\end{center}\end{figure}
|
|
This controller has been designed for easy writing. To send a sample to the
|
|
PWM audio controller, simply write the sample to the controller and clear the
|
|
PWM audio interrupt. When the audio interrupts the CPU again, it is ready
|
|
for the next sample.
|
|
|
|
The audio sample rate has been fixed at 8~kHz. While changing this rate is
|
|
easy to do within {\tt busmaster.v}, the rate itself takes some work to keep
|
|
up with, so I wouldn't recommend going much (any) faster.
|
|
|
|
The audio controller supports two additional functionalities, however. The
|
|
first is that the {\tt E} bit will be set upon any read when or if the audio
|
|
controller is ready for another sample. Equivalently, the audio interrupt
|
|
will be asserted.
|
|
|
|
The second functionality has to do with the two auxiliary control bits present
|
|
in the PModAMP2 audio device. These are the gain and shutdown bits. To set
|
|
these bits, write a sample to the controller while also setting the {\tt E}
|
|
bit. When the {\tt E} bit is set upon any write, the shutdown and gain bits
|
|
will also be set. (Be aware, the shutdown bit is negative logic.) Hence, one
|
|
may start this interface by writing a {\tt 0x0310000} to the device, and later
|
|
shut it back off by writing a {\tt 0x010000}.
|
|
|
Audio Controller
|
\subsection{Special Purpose I/O}
|
|
|
Register {\tt KYPAD}, as shown in Fig.~\ref{fig:spioreg},
|
Register {\tt SPIO}, as shown in Fig.~\ref{fig:spioreg},
|
\begin{figure}\begin{center}
|
\begin{figure}\begin{center}
|
\begin{bytefield}[endianness=big]{32}
|
\begin{bytefield}[endianness=big]{32}
|
\bitheader{0-31} \\
|
\bitheader{0-31} \\
|
\begin{leftwordgroup}{Read}\bitbox[lrt]{16}{Zeros}
|
\begin{leftwordgroup}{Read}\bitbox[lrt]{16}{Zeros}
|
\bitbox[lrt]{4}{Kpad}
|
\bitbox[lrt]{4}{Kpad}
|
Line 245... |
Line 557... |
Specifically, to change an LED, write the new value as well as a `1' to the
|
Specifically, to change an LED, write the new value as well as a `1' to the
|
corresponding LED change enable bit. The same goes for the keypad column
|
corresponding LED change enable bit. The same goes for the keypad column
|
output, a `1' needs to be written to the change enable bit in order for a
|
output, a `1' needs to be written to the change enable bit in order for a
|
new value to be accepted.
|
new value to be accepted.
|
|
|
|
As examples, writing a {\tt 0x0ff} to the {\tt SPIO} register will turn all
|
|
LED's on, {\tt 0x0f0} will turn all LED's off, and {\tt 0x011} and {\tt 0x010}
|
|
will turn LED0 on and then off again respectively.
|
|
|
The controller will generate a keypad interrupt whenever any row input is
|
The controller will generate a keypad interrupt whenever any row input is
|
zero, and a button interrupt whenever any button value is a one.
|
zero, and a button interrupt whenever any button value is a one. This also
|
|
means that, once generated, the interrupt must be disabled until the key or
|
|
button is released.
|
|
|
|
\subsection{General Purpose I/O}
|
The General Purpose Input and Output (GPIO) control register, shown in
|
The General Purpose Input and Output (GPIO) control register, shown in
|
Fig.~\ref{fig:gpioreg},
|
Fig.~\ref{fig:gpioreg},
|
\begin{figure}\begin{center}
|
\begin{figure}\begin{center}
|
\begin{bytefield}[endianness=big]{32}
|
\begin{bytefield}[endianness=big]{32}
|
\bitheader{0-31} \\
|
\bitheader{0-31} \\
|
Line 261... |
Line 580... |
\caption{GPIO Control Register}\label{fig:gpioreg}
|
\caption{GPIO Control Register}\label{fig:gpioreg}
|
\end{center}\end{figure}
|
\end{center}\end{figure}
|
is quite simple to use: when read, the top 16--bits indicate
|
is quite simple to use: when read, the top 16--bits indicate
|
the value of the 16--input GPIO pins, whereas the bottom 16--bits indicate
|
the value of the 16--input GPIO pins, whereas the bottom 16--bits indicate
|
the value being placed on the 16--output GPIO pins. To change a GPIO pin,
|
the value being placed on the 16--output GPIO pins. To change a GPIO pin,
|
write the new pins value to this register, together with setting the
|
write the new pin's value to this register, together with setting the
|
corresponding pin in the upper 16--bits. For example, to set output pin 0,
|
corresponding pin in the upper 16--bits. For example, to set output pin 0,
|
write a {\tt 0x010001} to the GPIO device. To clear output pin 0, write a
|
write a {\tt 0x010001} to the GPIO device. To clear output pin 0, write a
|
{\tt 0x010000}. This makes it possible to adjust some output pins independent
|
{\tt 0x010000}. This makes it possible to adjust some output pins independent
|
of the others.
|
of the others.
|
|
|
The GPIO controller, like the keypad or SPIO controller, will also generate
|
The GPIO controller, like the keypad or SPIO controller, will also generate
|
an interrupt. The GPIO interrupt is generated whenever a GPIO input line
|
an interrupt. The GPIO interrupt is generated whenever a GPIO input line
|
changes.
|
changes. The interrupt is not selective: if any line changes, a GPIO interrupt
|
|
will be generated. There are no do not care lines.
|
|
|
Of the 16 GPIO inputs and the 16 GPIO outputs, two lines have been taken for
|
Of the 16 GPIO inputs and the 16 GPIO outputs, two lines have been taken for
|
I2C support. GPIO line zero, for both input and output, is an I2C data line,
|
I2C support. GPIO line zero, for both input and output, is an I2C data line,
|
and GPIO line one is an I2C clock line. If the output of either of these
|
{\tt io\_sda}, and GPIO line one is an I2C clock line, {\tt io\_scl}. If the
|
|
output of either of these
|
lines is set to zero, the GPIO controller will drive the line. Otherwise,
|
lines is set to zero, the GPIO controller will drive the line. Otherwise,
|
the line is pulled up with a weak resistor so that other devices may
|
the line is pulled up with a weak resistor so that other devices may
|
pull it low. If either line is low, when the output control bit is high,
|
pull it low. If either line is low, when the output control bit is high,
|
it is an indicator that another device is sending data across these wires.
|
it is an indicator that another device is sending data across these wires.
|
|
|
|
\subsection{UART Data Register}
|
Moving on to the UART \ldots
|
Moving on to the UART \ldots
|
although the UART module within the S6~SoC is highly configurable, as built
|
although the UART module within the S6~SoC is highly configurable, as built
|
the UART can only handle 9600~Baud, 8--data bits, no parity, and one stop bit.
|
the UART can only handle 9600~Baud, 8--data bits, no parity, and one stop bit.
|
There is a single byte data buffer, so reading from the port has a real--time
|
Changing this involves changing the constant {\tt uart\_setup} within
|
requirement associated with it.
|
{\tt busmaster.v}. Further, the UART has only a single byte data buffer, so
|
|
reading from the port has a real--time requirement associated with it--the
|
|
data buffer must be emptied before the next value is read.
|
Attempts to read from this port will either return an 8--bit data value from
|
Attempts to read from this port will either return an 8--bit data value from
|
the port, or if no values are available it will return an {\tt 0x0100}
|
the port, or if no values are available it will return an {\tt 0x0100}
|
indicating that fact. In a similar fashion, writes to this port will send
|
indicating that fact. In general, reading from the UART port involves first
|
the lower 8--bits of the write out the serial port. If the port is already
|
waiting for the interrupt to be ready, second reading from the port itself,
|
busy, a single byte will be buffered.
|
and then third immediately clearing the interrupt. (The interrupt cannot
|
|
be cleared while data is waiting.) Writing to the UART port is done in a
|
|
similar fashion. First, wait until the UART transmit interrupt is asserted,
|
|
second write to the UART port, and then third clear the interrupt. As with
|
|
the read interrupt, clearing the interrupt prior to writing to the port will
|
|
have no effect.
|
|
|
|
\section{Debugging Scope}
|
|
The debugging scope consists of two registers, a control register and a data
|
|
register. It needs to be internally wired to 32--wires, internal to the S6
|
|
SoC, that will be of interest later. For further details on how to configure
|
|
and use this scope, please see the {\tt WBSCOPE} project on OpenCores.
|
|
|
|
\section{Internal Configuration Access Port}
|
|
The Internal Configuration Access Port (ICAP) provides access to the internal
|
|
configuration details of the FPGA. This access was designed so as to provide
|
|
the CPU with the capability to command a different FPGA load. In particular,
|
|
the code in Fig.~\ref{fig:reload} should reconfigure the FPGA from any given
|
|
Quad SPI {\tt address}.\footnote{According to Xilinx's technical support, this
|
|
will only work if the JTAG port is not busy.}
|
|
\begin{figure}\begin{center}\begin{tabbing}
|
|
{\tt warmboot(uint32 address) \{} \\
|
|
\hbox to 0.25in{}\={\tt uint32\_t *icape6 = (volatile uint32\_t *)0x{\em <ICAPE port address>};}\\
|
|
\>{\tt icape6[13] = (address<<2)\&0x0ffff;}\\
|
|
\>{\tt icape6[14] = ((address>>14)\&0x0ff)|((0x03)<<8);}\\
|
|
\>{\tt icape6[4] = 14;}\\
|
|
\>{\em // The CMod~S6 is now reconfiguring itself from the new address.}\\
|
|
\>{\em // If all goes well, this routine will never return.}\\
|
|
{\tt \}}
|
|
\end{tabbing}
|
|
\caption{Spartan--6 ICAPE Usage}\label{fig:reload}
|
|
\end{center}\end{figure}
|
|
|
|
One subtle problem with this port is that it will not work if the CMod is
|
|
plugged in to the USB JTAG port. It will only work if the CMod has been
|
|
provided with an independent power supply, leaving the USB JTAG unplugged.
|
|
|
|
For further details, please see either the {\tt WBICAPETWO} project on OpenCores
|
|
as well as Xilinx's ``Spartan-6 FPGA Configuration User Guide''.
|
|
|
|
\section{Real--Time Clock}
|
|
The Real Time Clock will be included if there is enough area to support it.
|
|
The four registers correspond to a clock, a timer, a stopwatch, and an alarm.
|
|
If space is tight, the timer and stopwatch, or indeed the entire clock, may be
|
|
removed from the design. For further details regarding how to set and use this
|
|
clock, please see the {\tt RTCCLOCK} project on OpenCores.
|
|
|
|
There is currently not enough area on the chip to support the Real--Time Clock
|
|
together with all of the other peripherals listed here. You can adjust whether
|
|
the clock is included or not by adjusting the {\tt `define} lines at the top
|
|
of {\tt busmaster.v}. For example, it may be possible to get the RTC back by
|
|
disabling the ICAPE2 interface.
|
|
|
|
\section{On-Chip Block RAM}
|
|
|
|
The block RAM is the fastest memory available to the processor. It is also
|
|
the {\em only} writeable memory available to the processor. Hence all
|
|
non-constant program data {\em must} be placed into block RAM. The ZipCPU
|
|
can also run instructions from the block RAM if extra speed is desired. When
|
|
runnning from block RAM, the ZipCPU will nominally take 8~clocks per
|
|
instruction, for an effective rate of 8~MIPS. Loads or stores to block RAM
|
|
will take one instruction longer.
|
|
|
|
\section{Flash Memory}
|
|
The flash memory has been arbitrarily sectioned into three sections, one for
|
|
a primary configuration, a second section for an alternate configuration file,
|
|
and the third section for any program and data. These regions are shown in
|
|
Tbl.~\ref{tbl:flash-addresses}.
|
|
\begin{table}[htbp]
|
|
\begin{center}\begin{tabular}{|p{0.75in}|p{0.75in}|p{0.5in}|p{3.0in}|}\hline
|
|
\rowcolor[gray]{0.85} Start & End & & Purpose \\\hline\hline
|
|
\scalebox{0.9}{\tt 0x400000} & \scalebox{0.9}{\tt 0x43ffff} & R & Primary configuration space\\\hline
|
|
\scalebox{0.9}{\tt 0x440000} & \scalebox{0.9}{\tt 0x47ffff} & R & Alternate configuration space\\\hline
|
|
\scalebox{0.9}{\tt 0x480000} & \scalebox{0.9}{\tt 0x7fffff} & R & ZipCPU program memory\\\hline
|
|
\end{tabular}
|
|
\caption{Flash Address Regions}\label{tbl:flash-addresses}
|
|
\end{center}\end{table}
|
|
The host program {\tt zipload} can be used to load a ZipCPU program and
|
|
configuration files into this address space. To use it, first load the
|
|
alternate configuration into the FPGA. Then pass it, as arguments, the
|
|
primary, and alternate if desired, configuration files followed by the ZipCPU
|
|
program file. Then, when the primary configuration is loaded again, perhaps
|
|
upon power up, the ZipCPU will automatically start running from it's
|
|
{\tt RESET\_ADDRESS}, {\tt 0x480000}.
|
|
|
|
When running from Flash memory, the ZipCPU will nominally take 52~clocks per
|
|
instruction, for an effective speed of about 1.5~MIPS.
|
|
|
\chapter{Clocks}
|
\chapter{Clocks}
|
|
|
The S6~SoC is designed to run off of one master clock. This clock is derived
|
The S6~SoC is designed to run off of one master clock. This clock is derived
|
from the 8~MHz input clock on the board, by multiplying it up to 80~MHz.
|
from the 8~MHz input clock on the board, by multiplying it up to 80~MHz.
|