Line 7... |
Line 7... |
The core is split in three main modules:
|
The core is split in three main modules:
|
|
|
\begin{enumerate}
|
\begin{enumerate}
|
\item The CPU (mips\_cpu.vhdl).
|
\item The CPU (mips\_cpu.vhdl).
|
\item The cache+memory controller (mips\_cache.vhdl).
|
\item The cache+memory controller (mips\_cache.vhdl).
|
\item An 'MCU' entity which combines CPU+Cache (mips\_mpu.vhdl).
|
\item A 'SoC' entity which combines CPU+Cache (mips\_soc.vhdl).
|
\end{enumerate}
|
\end{enumerate}
|
|
|
The entity you should use in your projects is the MCU module. The project
|
The entity you should use in your projects is the SoC module. The project
|
includes a 'hardware demo' built around this module (see section
|
includes a 'hardware demo' built around this module (see section
|
~\ref{pregenerated_demo}) which can be used as an usage example.\\
|
~\ref{pregenerated_demo}) which is meant as an usage example.\\
|
|
|
The main modules are briefly described in the following subsections.
|
|
|
|
|
\section{Bootstrap Code}
|
|
\label{bootstrap_code}
|
|
|
\section{MCU Module}
|
Though the core is meant to run mostly from off-chip memory, the current version
|
\label{mcu_module}
|
of the SoC module includes a small ROM implemented as FPGA BRAM and called
|
|
'bootstrap BRAM'. In the current version of the core, this BRAM can be loaded
|
|
with arbitrary code and its size can be configured by using generics, but it
|
|
can't be removed from the SoC. Even though the memory map can be modified to
|
|
boot from external FLASH and not use a BRAM at all, a BRAM will still be
|
|
inferred within the SoC -- subsequent versions will fix this.
|
|
|
|
As can be seen in table~\ref{tab_soc_memory_map}, the internal BRAM is mirrored
|
|
all over a wide area starting at \texttt{0xb8000000}. In practice, this means
|
|
the BRAM will be mapped at the CPU reset address (\texttt{0xbfc00000}) and thus
|
|
the bootstrap code should be placed there.
|
|
Unless the bootstrap BRAM is very small, it will span over the interrupt vector
|
|
address too (\texttt{0xbfc00180}).
|
|
|
|
For example, the 'Adventure' demo included with the project uses bootstrap
|
|
code included in file \texttt{/src/common/bootstrap.s}. This bootstrap code
|
|
is fairly incomplete (interrupt response code is mostly a stub) yet it's enough
|
|
to boot most applications.
|
|
Note that the C startup code, which deals with things like initializing the
|
|
static variables on the data segment, etc. is not part of this bootstrap code.
|
|
It can be found in file \texttt{/src/common/c\_startup.s}
|
|
|
|
So, in short, the code loaded onto the startup BRAM should include the most
|
|
basic system initialization (cache initialization at least) and the entry point
|
|
for the interrupt response code; plus a jump to the main program entry address.
|
|
|
|
Anyone trying to build some application on this core is advised to use the code
|
|
samples as starting points, specially the makefiles.
|
|
|
|
|
|
\subsection{Loading Bootstrap Code on the SoC Module}
|
|
\label{loading_bootstrap_code}
|
|
|
|
Once the code that is to be loaded on the bootstrap BRAM has been built, you
|
|
need to load it onto the bootstrap BRAM within the FPGA.
|
|
|
|
As you probably already know, there are several possible ways to deal with this
|
|
and most of them involve using \emph{'Memory Initialization Files'} of
|
|
some sort. This project is different.
|
|
|
|
So far, this project does not include any support for using IMF
|
|
files of any kind. Instead, the bootstrap BRAM is inferred and initialized
|
|
using regular VHDL constructs and a constant passed to the SoC module as a
|
|
generic.
|
|
|
|
This scheme has a big drawback: every time the object code within the FPGA
|
|
changes, the whole synthesis needs to be re-run. This drawback is manageable
|
|
as long as the core is not used in any big project or if the bootstrap code
|
|
does not change often.
|
|
|
The MCU module main purpose is to encapsulate the somewhat complex
|
On the other hand, I see some big advantages in using regular BRAM inference in
|
interconnection between the CPU and the Cache module.
|
this stage of the project:
|
|
|
If some project demands that some piece of hardware be directly connected to the
|
|
CPU, bypassing the cache, this is where it should be -- an MMU comes to mind.
|
|
|
|
Any peripherals deemed common enough that they will be present in all projects
|
|
might be placed in the MCU module too -- after all, the MCU name has been chosen
|
|
to imply that 'bundling together' of a CPU and a bunch of peripherals.
|
|
|
|
In the current version of the MCU module, there is only a peripheral included in
|
|
it -- a hardwired UART module. There is no penalty for placing peripherals
|
|
ouside the MCU module, so there is no incentive to place them inside, thus
|
|
making the interface more complex. This is an implementation option of yours.\\
|
|
|
|
\subsection{MCU Ports}
|
|
\label{mcu_ports}
|
|
|
|
\begin{figure}[h]
|
|
\makebox[\textwidth]{\framebox[9cm]{\rule{0pt}{9cm}
|
|
\includegraphics[width=8cm]{img/mpu_symbol.png}}}
|
|
\caption{MPU module interface\label{mpu_symbol}}
|
|
\end{figure}
|
|
|
|
\begin{table}[h]
|
|
\caption{MCU module interface ports}
|
|
\begin{tabularx}{\textwidth}{ lll|X }
|
|
\toprule
|
|
Name & Type & Width & Description \\
|
|
\midrule
|
|
clk & in & 1 & Clock input, active rising edge. \\
|
|
reset & in & 1 & Synchronous global reset. \\
|
|
\midrule
|
|
sram\_address & out & 16 & Memory word address (bit 0 absent). \\
|
|
sram\_data\_wr & out & 16 & Memory write data. Only valid when one of the \\
|
|
& & & memory byte write enable outputs is active.\\
|
|
sram\_data\_rd & in & 16 & Memory read data. Latched when xxx. \\
|
|
sram\_byte\_we\_n & out & 2 & Memory byte write enable, active low. \\
|
|
& & & (0) enables the low byte (7 downto 0) \\
|
|
& & & (1) enables the high byte (15 downto 8). \\
|
|
\midrule
|
|
io\_rd\_addr & out & 30 & I/O port read address (bits 1..0 absent). \\
|
|
& & & Only valid when io\_rd\_vma is high. \\
|
|
io\_wr\_addr & out & 30 & I/O port write address (bits 1..0 absent). \\
|
|
io\_wr\_data & out & 32 & I/O write data. Only valid when one of the \\
|
|
& & & i/o byte write enable outputs is active.\\
|
|
io\_rd\_data & in & 32 & I/O read data. Latched when xxx. \\
|
|
io\_byte\_we & out & 4 & I/O byte write enable, active high. \\
|
|
& & & (0) enables the low byte (7 downto 0) \\
|
|
& & & (3) enables the high byte (31 downto 24). \\
|
|
io\_rd\_vma & out & 1 & Active high on i/o read cycles. \\
|
|
\midrule
|
|
uart\_rxd & in & 1 & RxD input to internal UART. \\
|
|
uart\_txd & out & 1 & TxD output from internal UART. \\
|
|
\midrule
|
|
interrupt & in & 8 & Interrupt request inputs, active high. \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
\end{table}
|
|
|
|
As you can see in figure~\ref{mpu_symbol} (symbol generated by Xilinx ISE),
|
|
the MCU has the following interfaces:
|
|
|
|
\begin{enumerate}
|
\begin{enumerate}
|
\item Interface to external static asynchronous memory (SRAM, FLASH...).
|
\item The whole scheme is totally vendor agnostic.
|
\item Interface to on-chip peripherals.
|
\item Object code embedded on VHDL constants can very easily be used in both simulation and synthesis.
|
\item Interrupt inputs.
|
|
\end{enumerate}
|
\end{enumerate}
|
|
|
These interfaces will be explained in the following subsections. The top module
|
So, whatever object code is to be used to initialize the SoC bootstrap BRAM has
|
for the demo supplied with the project (c2sb\_demo.vhdl) will be used for
|
to be passed to the SoC module instance as a generic constant (see section
|
illustration.
|
~\ref{soc_generics}). The constant must be of type \texttt{t\_obj\_code}, which
|
|
is defined in package \emph{mips\_pkg}.
|
\emph{NOTE}: This section needs a lot of elaboration -- ideally this should be
|
|
equivalent to
|
|
a datasheet in thoroughness and detail. This work, like many other parts of this
|
\subsection{Building the Bootstrap Initialization Constant}
|
project, will have to wait.
|
\label{boot_code_conversion}
|
|
|
\subsection{MCU interface to static memory}
|
The project includes a python script (\texttt{/tools/build\_pkg/build\_pkg.py})
|
\label{mcu_if_sram}
|
whose purpose is to build an VHDL \texttt{t\_obj\_code} constant out of a
|
|
\emph{binary} object code file.
|
The interface to external memory in the MCU module is essentially that of the
|
|
internal cache/memory controller. Its timing is described in section
|
This script will read one or more big-endian, binary object files and will
|
~\ref{cache_state_machine}.\\
|
produce a VHDL package file that will contain initialization constants for
|
|
the bootstrap BRAM and for some other memories that are only used in the
|
The MCU inputs are meant to be connected straight to the FPGA i/o pins. The only
|
simulation test bench.
|
trick is the bidirectional memory data bus: as you can see, the MCU data buses
|
The package can optionally include too some simulation and synthesis
|
are unidirectional and thus you will need to provide an interconnection
|
configuration constants -- such as the size of the bootstrap BRAM.
|
external to this module. This interconnection shall include the requisite
|
|
3-state buffers:
|
The makefiles included in the code samples invoke this script twice: once
|
|
to generate a package called \emph{sim\_params\_pkg} and used in the
|
\begin{verbatim}
|
simulation test bench; and once to build a package called
|
sram_databus <= sram_data_wr when sram_byte_we_n/="11" else (others => 'Z');
|
\emph{bootstrap\_code\_pkg} used for synthesis.
|
\end{verbatim}
|
|
|
|
The top level module can be used as a fully tested example of how to use this
|
|
interface to connect to a common SRAM chip (ISSI IS61LV25616).
|
|
|
|
In reviewing the top module source, note that I had to adapt the dual
|
|
byte-write-enable outputs to the SRAM
|
|
configuration of a single write-enable plus dual byte-enable inputs.
|
|
|
|
Note too that the static memory bus is used to access both the 16-bit wide SRAM
|
|
and an 8-bit wide FLASH. These chips are connected to separate buses on the
|
|
target board, so the top module needs to conflate both buses before connecting
|
|
them to the MPU. This is why a multiplexor is used in the mpu\_sram\_data\_rd
|
|
bus. A real-world board would probably have the SRAM and the FLASH connected
|
|
to the same bus, simplifying the interface logic.
|
|
|
|
|
|
\subsection{MCU interface to peripherals}
|
|
\label{mcu_if_io}
|
|
|
|
TODO Documentation to be done
|
|
|
|
\subsection{MCU interrupt inputs}
|
Please refer to the makefiles for usage examples, and read the script source
|
\label{mcu_irqs}
|
for more detailed usage instructions.
|
|
|
TODO Documentation to be done
|
|
|
|
No newline at end of file
|
No newline at end of file
|