2245: add dio_spi examples
This commit is contained in:
parent
8ff606888c
commit
9920661516
197
2245.tex
197
2245.tex
|
@ -20,6 +20,9 @@
|
|||
\usetikzlibrary{calc}
|
||||
\usetikzlibrary{fit,backgrounds}
|
||||
|
||||
\usepackage{tikz-timing}
|
||||
\usetikztiminglibrary{counters}
|
||||
|
||||
\title{2245 LVDS-TTL}
|
||||
\author{M-Labs Limited}
|
||||
\date{January 2022}
|
||||
|
@ -67,9 +70,9 @@ Only shielded Ethernet Cat-6 cables should be connected.
|
|||
|
||||
\newcommand*{\MyLabel}[3][2cm]{\parbox{#1}{\centering #2 \\ #3}}
|
||||
\newcommand*{\MymyLabel}[3][4cm]{\parbox{#1}{\centering #2 \\ #3}}
|
||||
\newcommand{\inputcolorboxminted}[2]{%
|
||||
\newcommand{\inputcolorboxminted}[3][4]{%
|
||||
\begin{tcolorbox}[colback=white]
|
||||
\inputminted[#1, gobble=4]{python}{#2}
|
||||
\inputminted[#2, gobble=#1]{python}{#3}
|
||||
\end{tcolorbox}
|
||||
}
|
||||
|
||||
|
@ -489,6 +492,196 @@ If the gateware counter is enabled on the TTL channel, it can typically count up
|
|||
One channel needs to be configured as input, and the other as output.
|
||||
\inputcolorboxminted{firstline=74,lastline=80}{examples/ttl.py}
|
||||
|
||||
\newcommand{\wrapspacer}[1]% #1 = special text
|
||||
{\bgroup
|
||||
\sbox0{\begin{minipage}{\linewidth}\hrule height0pt
|
||||
#1\hrule height0pt
|
||||
\end{minipage}}%
|
||||
\dimen0=\dimexpr \ht0+\dp0\relax
|
||||
\loop\ifdim\dimen0>\baselineskip
|
||||
\strut\vspace{-\baselineskip}\newline
|
||||
\advance\dimen0 by -\baselineskip
|
||||
\repeat
|
||||
\noindent\strut\usebox0\par
|
||||
\egroup}
|
||||
|
||||
\newpage
|
||||
\subsection{SPI Master Device}
|
||||
If a EEM port is configured as \texttt{dio\char`_spi} instead of \texttt{dio}, its associated TTL channels can be configured as SPI master devices.
|
||||
Invocation of an SPI transfer follows this pattern:
|
||||
\begin{enumerate}
|
||||
% The config register can be set using set_config.
|
||||
% However, the only difference between these 2 methods is that set_config accepts an arbitrary
|
||||
% frequency, then translate into the rough frequency divisor for set_config_mu.
|
||||
% It doesn't guarantee such frequency would be set as the SPI frequency
|
||||
|
||||
% In addition, finding clock division is quite easy. set_config_mu seems to be a more
|
||||
% straight-forward & representative of the actual implementation.
|
||||
\item Set the \texttt{config} register (e.g. using \texttt{set\char`_config\char`_mu()}).
|
||||
\item Start the SPI transfer by writing the \texttt{data} register using \texttt{write()}.
|
||||
\item If the data from the SPI slave is to be read (i.e. \texttt{SPI\char`_INPUT} was set in \texttt{config}), invoke \texttt{read()} to read.
|
||||
|
||||
\end{enumerate}
|
||||
|
||||
The list of configurations supported in the gateware are listed as below:
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{|c|l|}
|
||||
\hline
|
||||
Flag & Description \\ \hline
|
||||
\texttt{SPI\char`_OFFLINE} & Switch all pins to high impedance mode. \\ \hline
|
||||
\texttt{SPI\char`_END} & Next SPI transfer is the last of the transcation. \\ \hline
|
||||
\texttt{SPI\char`_INPUT} & Submit SPI read data as RTIO input event when the transfer is complete. \\ \hline
|
||||
\texttt{SPI\char`_CS\char`_POLARITY} & Active level of chip select (CS) \\ \hline
|
||||
\texttt{SPI\char`_CLK\char`_POLARITY} & Idle level of serial clock (SCK) \\ \hline
|
||||
\texttt{SPI\char`_CLK\char`_PHASE} & Data is sampled on falling edge \& shifted out on rising edge. \\ \hline
|
||||
\texttt{SPI\char`_LSB\char`_FIRST} & LSB is the first on bit on the wire. \\ \hline
|
||||
\texttt{SPI\char`_HALF\char`_DUPLEX} & Use 3-wire SPI, where MOSI is both input \& output capable. \\ \hline
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
The following ARTIQ example demonstrates the flow of an SPI transcation with a typical SPI setup with 3 homogeneous slaves.
|
||||
The direction switches on the LVDS-TTL card should be set to the correct IO direction for all relevant channels before powering on.
|
||||
\begin{center}
|
||||
\begin{circuitikz}[european, scale=1, every label/.append style={align=center}]
|
||||
% SPI master
|
||||
\draw (0, 1.8) node[twoportshape, t={}, circuitikz/bipoles/twoport/width=2.8, circuitikz/bipoles/twoport/height=2, scale=1] (master) {};
|
||||
\node [label={center:\large{SPI Master}}] at (-0.6, 2.05) {};
|
||||
\node [label={center:\large{(LVDS-TTL)}}] at (-0.6, 1.55) {};
|
||||
\node [label=left:{SCK}] at (2, 2.8) {};
|
||||
\node [label=left:{MOSI}] at (2, 2.4) {};
|
||||
\node [label=left:{MISO}] at (2, 2.0) {};
|
||||
\node [label=left:{CS0}] at (2, 1.6) {};
|
||||
\node [label=left:{CS1}] at (2, 1.2) {};
|
||||
\node [label=left:{CS2}] at (2, 0.8) {};
|
||||
|
||||
% SPI slaves
|
||||
% The top one will have its SCK, MOSI, MISO aligned with the master, for wiring simplicity
|
||||
\draw (7, 2.2) node[twoportshape, t={}, circuitikz/bipoles/twoport/width=2.8, circuitikz/bipoles/twoport/height=1.4, scale=1] (slave0) {};
|
||||
\node [label={center:\large{SPI Slave 0}}] at (7.6, 2.2) {};
|
||||
\node [label=right:{SCK}] at (5, 2.8) {};
|
||||
\node [label=right:{MOSI}] at (5, 2.4) {};
|
||||
\node [label=right:{MISO}] at (5, 2.0) {};
|
||||
\node [label=right:{$\mathrm{\overline{CS}}$}] at (5, 1.6) {};
|
||||
|
||||
% The top one will have its SCK, MOSI, MISO aligned with the master, for wiring simplicity
|
||||
\draw (7, 0) node[twoportshape, t={}, circuitikz/bipoles/twoport/width=2.8, circuitikz/bipoles/twoport/height=1.4, scale=1] (slave1) {};
|
||||
\node [label={center:\large{SPI Slave 1}}] at (7.6, 0) {};
|
||||
\node [label=right:{SCK}] at (5, 0.6) {};
|
||||
\node [label=right:{MOSI}] at (5, 0.2) {};
|
||||
\node [label=right:{MISO}] at (5, -0.2) {};
|
||||
\node [label=right:{$\mathrm{\overline{CS}}$}] at (5, -0.6) {};
|
||||
|
||||
% The top one will have its SCK, MOSI, MISO aligned with the master, for wiring simplicity
|
||||
\draw (7, -2.2) node[twoportshape, t={}, circuitikz/bipoles/twoport/width=2.8, circuitikz/bipoles/twoport/height=1.4, scale=1] (slave2) {};
|
||||
\node [label={center:\large{SPI Slave 2}}] at (7.6, -2.2) {};
|
||||
\node [label=right:{SCK}] at (5, -1.6) {};
|
||||
\node [label=right:{MOSI}] at (5, -2.0) {};
|
||||
\node [label=right:{MISO}] at (5, -2.4) {};
|
||||
\node [label=right:{$\mathrm{\overline{CS}}$}] at (5, -2.8) {};
|
||||
|
||||
% Connect the master to slave 0
|
||||
\draw [-latexslim] (1.95, 2.8) -- (5.05, 2.8);
|
||||
\draw [-latexslim] (1.95, 2.4) -- (5.05, 2.4);
|
||||
\draw [latexslim-] (1.95, 2.0) -- (5.05, 2.0);
|
||||
\draw [-latexslim] (1.95, 1.6) -- (5.05, 1.6);
|
||||
|
||||
% Connect slave 1
|
||||
\draw [-latexslim] (4.2, 2.8) -- (4.2, 0.6) -- (5.05, 0.6);
|
||||
\draw [-latexslim] (3.8, 2.4) -- (3.8, 0.2) -- (5.05, 0.2);
|
||||
\draw [-] (3.4, 2.0) -- (3.4, -0.2) -- (5.05, -0.2);
|
||||
\draw [-latexslim] (1.95, 1.2) -- (3.0, 1.2) -- (3.0, -0.6) -- (5.05, -0.6);
|
||||
|
||||
% Connect slave 2
|
||||
\draw [-latexslim] (4.2, 0.6) -- (4.2, -1.6) -- (5.05, -1.6);
|
||||
\draw [-latexslim] (3.8, 0.2) -- (3.8, -2.0) -- (5.05, -2.0);
|
||||
\draw [-] (3.4, -0.2) -- (3.4, -2.4) -- (5.05, -2.4);
|
||||
\draw [-latexslim] (1.95, 0.8) -- (2.6, 0.8) -- (2.6, -2.8) -- (5.05, -2.8);
|
||||
|
||||
% Add dot to intersection to distinguish from overlaps
|
||||
\node at (4.2, 2.8)[circle,fill,inner sep=0.7pt]{};
|
||||
\node at (3.8, 2.4)[circle,fill,inner sep=0.7pt]{};
|
||||
\node at (3.4, 2.0)[circle,fill,inner sep=0.7pt]{};
|
||||
\node at (4.2, 0.6)[circle,fill,inner sep=0.7pt]{};
|
||||
\node at (3.8, 0.2)[circle,fill,inner sep=0.7pt]{};
|
||||
\node at (3.4, -0.2)[circle,fill,inner sep=0.7pt]{};
|
||||
|
||||
\end{circuitikz}
|
||||
\end{center}
|
||||
|
||||
\newpage
|
||||
\subsubsection{SPI Configuration}
|
||||
The following examples will assume the SPI communication has the following properties:
|
||||
\begin{itemize}
|
||||
\item Chip select (CS) is active low
|
||||
\item Serial clock (SCK) idle level is low
|
||||
\item Data is sampled on rising edge of SCK \& shifted out on falling edge of SCK
|
||||
\item Most significant bit (MSB) first
|
||||
\item Full duplex
|
||||
\end{itemize}
|
||||
The base line configuration for an \texttt{SPIMaster} instance can be defined as such:
|
||||
\inputcolorboxminted[0]{firstline=105,lastline=110}{examples/ttl.py}
|
||||
The \texttt{SPI\char`_END} \& \texttt{SPI\char`_INPUT} flags will be modified during runtime in the following example.
|
||||
|
||||
\subsubsection{SPI frequency}
|
||||
Frequency of the SPI clock must be the result of RTIO clock frequency divided by an integer factor from [2, 257].
|
||||
In the folowing examples, the SPI frequency will be set to 1 MHz by dividing the RTIO frequency (125 MHz) by 125.
|
||||
\inputcolorboxminted[0]{firstline=112,lastline=112}{examples/ttl.py}
|
||||
|
||||
\subsubsection{SPI write}
|
||||
Typically, an SPI write operation involves sending an instruction and data to the SPI slaves.
|
||||
Suppose the instruction and data are 8 bits and 32 bits respectively.
|
||||
The timing diagram of such write operation is shown in the following.
|
||||
|
||||
\begin{center}
|
||||
\begin{tikztimingtable}
|
||||
[
|
||||
timing/d/background/.style={fill=white},
|
||||
timing/lslope=0.2
|
||||
]
|
||||
$\mathrm{\overline{CS}}$ & H51{L}H \\
|
||||
SCK & LL31{T}; 2{[dotted] T}; 17{T} L \\
|
||||
% The better approach is to use pass the counter (\tikztimingcounter{Q}) to a macro,
|
||||
% then print the label from macro. But it turns out tikz-timing will print
|
||||
% the counter value separately, even with an additional macro.
|
||||
% Therefore, it does not work properly.
|
||||
MOSI & [timing/counter/new={char=I, reset char=J, reset type=arg, increment=-1, text format=I}, timing/counter/new={char=A, reset char=R, reset type=arg, increment=-1, text format=D}]
|
||||
UJ{7}8{2I}R{31}8{2A}; [dotted] D [dotted] D{}; R{7}8{2A}2U \\
|
||||
MOSI & 53U \\
|
||||
\end{tikztimingtable}%
|
||||
\end{center}
|
||||
|
||||
\newpage
|
||||
Suppose the instruction is \texttt{0x13}, while the data is \texttt{0xDEADBEEF}. In addition, both slave 1 \& 2 are selected. This SPI transcation can be performed by the following code.
|
||||
\inputcolorboxminted{firstline=119,lastline=128}{examples/ttl.py}
|
||||
|
||||
\subsubsection{SPI read}
|
||||
A 32-bits read is represented by the following timing diagram.
|
||||
|
||||
\begin{center}
|
||||
\begin{tikztimingtable}
|
||||
[
|
||||
timing/d/background/.style={fill=white},
|
||||
timing/lslope=0.2
|
||||
]
|
||||
$\mathrm{\overline{CS}}$ & H51{L}H \\
|
||||
SCK & LL31{T}; 2{[dotted] T}; 17{T} L \\
|
||||
% The better approach is to use pass the counter (\tikztimingcounter{Q}) to a macro,
|
||||
% then print the label from macro. But it turns out tikz-timing will print
|
||||
% the counter value separately, even with an additional macro.
|
||||
% Therefore, it does not work properly.
|
||||
MOSI & [timing/counter/new={char=I, reset char=J, reset type=arg, increment=-1, text format=I}]
|
||||
UJ{7}8{2I}36U \\
|
||||
MOSI & [timing/counter/new={char=A, reset char=R, reset type=arg, increment=-1, text format=D}]
|
||||
17UR{31}8{2A}; [dotted] D [dotted] D{}; R{7}8{2A}2U \\
|
||||
\end{tikztimingtable}%
|
||||
\end{center}
|
||||
|
||||
Suppose the instruction is \texttt{0x81}, where only slave 0 is selected. This SPI transcation can be performed by the following code.
|
||||
\inputcolorboxminted{firstline=136,lastline=150}{examples/ttl.py}
|
||||
|
||||
\newpage
|
||||
\section{Ordering Information}
|
||||
To order, please visit \url{https://m-labs.hk} and select the 2245 LVDS-TTL in the ARTIQ Sinara crate configuration tool. The card may also be ordered separately by writing to \url{mailto:sales@m-labs.hk}.
|
||||
|
||||
|
|
|
@ -101,3 +101,50 @@ class ClockGen(EnvExperiment):
|
|||
def run(self):
|
||||
self.core.reset()
|
||||
self.ttl0.set(62.5*MHz)
|
||||
|
||||
from artiq.coredevice import spi2 as spi
|
||||
|
||||
SPI_CONFIG = (0 * spi.SPI_OFFLINE | 0 * spi.SPI_END |
|
||||
0 * spi.SPI_INPUT | 0 * spi.SPI_CS_POLARITY |
|
||||
0 * spi.SPI_CLK_POLARITY | 0 * spi.SPI_CLK_PHASE |
|
||||
0 * spi.SPI_LSB_FIRST | 0 * spi.SPI_HALF_DUPLEX)
|
||||
|
||||
CLK_DIV = 125
|
||||
|
||||
class SPIWrite(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.spi = self.get_device("dio_spi0")
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.spi.set_config_mu(SPI_CONFIG, 8, CLK_DIV, 0b110)
|
||||
self.spi.write(0x13 << 24) # Shift the bits to the MSBs.
|
||||
# Since SPI_LSB_FIRST is NOT set,
|
||||
# SPI Machine will shift out bits from
|
||||
# the MSB of the `data` register.`
|
||||
self.spi.set_config_mu(SPI_CONFIG | spi.SPI_END, 32, CLK_DIV, 0b110)
|
||||
self.spi.write(0xDEADBEEF)
|
||||
|
||||
|
||||
class SPIRead(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.spi = self.get_device("dio_spi0")
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.spi.set_config_mu(SPI_CONFIG, 8, CLK_DIV, 0b001)
|
||||
self.spi.write(0x81 << 24) # Shift the bits to the MSBs.
|
||||
# Since SPI_LSB_FIRST is NOT set,
|
||||
# SPI Machine will shift out bits from
|
||||
# the MSB of the `data` register.`
|
||||
self.spi.set_config_mu(SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT,
|
||||
32, CLK_DIV, 0b001)
|
||||
self.spi.write(0) # write() performs the SPI transfer.
|
||||
# As suggested by the timing diagram,
|
||||
# the exact value of this argument
|
||||
# does not matter.
|
||||
print(self.spi.read())
|
||||
|
|
Loading…
Reference in New Issue