Bericht: Testkonzept fast fertig

This commit is contained in:
Matthias Biermann
2025-02-16 15:34:02 +01:00
parent f2d179a6f2
commit b221edf9f4
7 changed files with 151 additions and 62 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.
+150 -8
View File
@@ -255,21 +255,22 @@ Sobald alle Datenpakete verarbeitet und wieder in den Speicher geschrieben wurde
Um das CRC-DMA-IP in ein Gesamtsystem einzubinden, müssen die AXI-Lite und Full-AXI Schnittstellen mit dem Processing System (PS) des Zynq-SoC verbunden werden.
Im hier erstellten Gesamtsystem wurde die Full-AXI-Master Schnittstelle mit der ACP-Schnittstelle verbunden.
Da Speicherzugriffe auf das SDRAM mit der ACP-Schnittstelle über den Cache erfolgen, sind die Daten so stets mit der CPU snychronisiert.
Da Speicherzugriffe auf das SDRAM mit der ACP-Schnittstelle über den Cache erfolgen, sind die Daten so stets mit der CPU synchronisiert.
Die GP-Master-AXI Schnittstelle ist mit der AXIL-Slave Schnittstelle des IPs verbunden.
Damit ist die CPU in der Lage, die Konfigurationsregister des IPs zu schreiben und das IP zu starten.
Die Interuptleitung des IPs ist mit dem Interrupt-Controller des Zynq-SoC verbunden.
Die Interruptleitung des IPs ist mit dem Interrupt-Controller des Zynq-SoC verbunden.
Der System ILA dient zur genaueren Betrachung der Full-AXI-Bursts IPs auf der Hardware.
Dies wird in Abschnitt~\ref{sec:VerifikationGesamtsystem} genauer beschrieben.
Dieses Gesamtsystem kann nun synthetisiert und implementiert werden.
\newpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Konzept und Umsetzung der Software}
\section{Software - Treiberfunktion}
\newpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -285,13 +286,154 @@ Daher liegt ein besonderes Augenmerk auf der Verifikation dieser Komponente.
\subsubsection{Referenzprogramm}
Zur Erstellung der
Zunächst wurde das C-Programm \emph{crc.c} geschrieben, welches CRC-Prüfsummen in Software berechnet.
Der CRC-Algorithmus wurde hauptsächlich mithilfe von Sunshine2k.de \cite{SunshineCrcErklärung} erstellt.
Parallel wurden die Ergebnisse mit anderen CRC-Online Rechnern verglichen, um die Korrektheit der Berechnung zu überprüfen \cite{crcCalc}.
Kern dieses Programms ist die Funktion \texttt{uint32\_t calcCRC32(...)}, welche die CRC-Prüfsumme über ein Datenpaket berechnet.
Mithilfe dieser Funktion können CRC-Prüfsummen für beliebige Datenpakete und für eine beliebige Kombination an CRC-Parametern berechnet werden.
Neben dem testweise Berechnen von Prüfsummen werden in der \texttt{main}-Funktion auch die CRC-Prüfsummen für die Testbench der CRC-Berechnungskomponente berechnet.
Dies geschieht in der Funktion \texttt{void calc\_axis\_crc\_tb()}.
% \begin{lstlisting}[caption={Starten und Stoppen der Übertragung}, label={code:OW_start_transmission}]
% uint32_t calcCRC32(
% uint8_t* inBytes,
% size_t size,
% uint32_t polynomial,
% uint32_t initialValue,
% uint32_t finalXOR,
% uint8_t inputReflected,
% uint8_t outputReflected
% ) {
% uint32_t crc = initialValue;
% uint8_t byte;
% for (size_t i = 0; i < size; i++) {
% byte = inBytes[i];
% if (inputReflected != 0) {
% uint8_t reflected = 0;
% for (int b = 0; b < 8; b++) {
% if ((byte & (1<<b)) != 0) {
% reflected |= (uint8_t) (1<<(7-b));
% }
% }
% byte = reflected;
% }
% crc ^= (uint32_t) (byte << 24);
% for (int bit = 0; bit < 8; bit++) {
% if ((crc & (uint32_t)(1<<31)) != 0) {
% crc = (uint32_t) (crc << 1) ^ polynomial;
% } else {
% crc <<= 1;
% }
% }
% }
% if (outputReflected != 0) {
% uint32_t reflected = 0;
% for (int i = 0; i < 32; i++) {
% if ((crc & (uint32_t) (1<<i)) != 0) {
% reflected |= (uint32_t) (1<<(31-i));
% }
% }
% crc = reflected;
% }
% return crc ^ finalXOR;
% }
% \end{lstlisting}
\subsubsection{Simulation / Testbench}
\subsection{Verifikation des CRC-DMA-IPs}
Um die Korrektheit der CRC-Berechnungskomponente \emph{axis\_crc} zu gewährleisten, wurde eine Testbench erstellt.
In dieser Testbench werden von der CRC-Berechnungskomponente insgesamt 24 Prüfsummen über dasselbe Datenpaket von drei 32-Bit-Worten mit verschiedenen Parametern berechnet.
Da auch nur ein kleinster Fehler in der Implementierung des CRC-Algorithmus völlig andere Ergebnisse zur Folge hätte, genug es zunächst nur ein Datenpaket für die Verifikation zu nutzen.
\subsection{Verifikation des Gesamtsystems}
Die Testbench wurde in VHDL geschrieben und überprüft automatisch die Korrektheit der berechneten CRC-Prüfsummen sowie das korrekte Ausgeben der Daten.
\begin{figure}[H]
\centering
\includegraphics[width=1.0\textwidth]{wave_axis_crc.png}
\caption{Wave-Diagramm der \emph{axis\_crc} Testbench}
\label{fig:AxisCrcTestbenchWave}
\end{figure}
In Abbildung~\ref{fig:AxisCrcTestbenchWave} ist ein Ausschnitt des Wave-Diagramms der Testbench dargestellt.
Falls einer der 24 Testfälle fehlschlägt, das heißt, wenn entweder die zugeführten Daten nicht wieder korrekt ausgegeben werden oder wenn die CRC-Prüfsumme falsch ist, wird eine Fehlermeldung ausgegeben.
Somit kann die korrekte Funktionalität der CRC-Berechnungskomponente gewährleistet werden.
Die in dieser Arbeit erstellte CRC-Berechnungskomponente wurde mithilfe dieser Testbench erfolgriech verfiziert.
\subsection{Simulation des CRC-DMA-IPs}
\begin{figure}[H]
\centering
\includegraphics[width=1.0\textwidth]{Blockschaltbilder/axi_crc_dma_sim_default_view.png}
\caption{Simulation des CRC-DMA-IPs}
\label{fig:BlockschaltbildCRC_DMA_IP_Simulation}
\end{figure}
Zur Simulation des gesamten CRC-DMA-IPs wird das Block-Design \emph{axi\_crc\_dma\_sim\_01} verwendet, welches in Abbildung~\ref{fig:BlockschaltbildCRC_DMA_IP_Simulation} dargestellt ist.
Das IP \emph{axil\_master\_with\_rom} ist ein AXIL-Master, der die nötige Konfiguration des CRC-DMA-IPs durchführt und den Vorgang anschließend startet.
Er stammt aus dem IP-Verzeichnis dieser Vorlesung.
Die AXIL-Schreibbefehle für den AXIL-Master sind im Stimuli-Script \emph{axi\_crc\_dma\_sim.stm} definiert.
Das IP \emph{axi3\_slave\_verif} ist ein Baustein, der als Full-AXI-Slave fungiert.
Er akzeptiert Schreibbursts und gibt bei Lesebursts selbst erzeugte Daten wieder.
Dabei hat diese Komponente jedoch keinen internen Speicher, sondern dient nur dazu, korrekte Reaktionen auf Lese- und Schreibbursts zu erzeugen.
Die Adressen und die eigentlichen Daten werden von dieser Komponente nicht verarbeitet.
Das Grundgerüst dieser Komponente wurde mithilfe von ChatGPT erstellt und anschließend in der Simulation etwas angepasst, sodass das Verhalten möglichst dem korrekten Verhalten einer Full-AXI-Slave Schnittstelle entspricht.
In diesem Simulations-Block-Design wird zum einen die Funktionsweise der AXIL-Schnittstelle überprüft.
Das heißt, dass die AXIL-Register richtig geschrieben und gelesen werden können und dass der CRC-DMA-Vorgang über das Kontrollregister gestartet werden kann.
Zum anderen wird vornehmlich das richtige DMA-Verhalten des IPs überprüft.
Dabei sind untern anderem folgende Punkte von Bedeutung:
\begin{itemize}
\item Werden die Valid/Ready-Handshakes korrekt durchgeführt?
\item Werden die korrekten Adressen übergeben?
\item Werden die korrekten Daten übergeben?
\item Wird die korrekte Menge an Daten entsprechend der Konfiguration gelesen?
\item Wird die korrekte Menge an Daten entsprechend der Konfiguration geschrieben?
\item Werden stets maximal mögliche Burstlängen verwendet?
\item Werden 4kB-Adressgrenzen korrekt behandelt?
\item Wird Interruptleitung nach dem Vorgang korrekt gesetzt?
\end{itemize}
Da es sich um eine manuelle Testbench handelt, werden all diese Punkte durch das Betrachten des Wave-Diagramms überprüft.
Zusätzlich wurden dabei verschiedene Konfigurationen des IPs getstest, um auch die Funktionalität bei unterschiedlichen Parametern zu überprüfen und Edge-Cases mit abzudecken.
\begin{figure}[H]
\centering
\includegraphics[width=1.0\textwidth]{wave_axi_dma_sim_1_ausschnitt.png}
\caption{Exemplarisches Wave-Diagramm der Simulation des CRC-DMA-IPs}
\label{fig:CRC_DMA_IP_SimulationWave}
\end{figure}
\subsection{Verifikation des Gesamtsystems} \label{sec:VerifikationGesamtsystem}
Die Verifikation des Gesamtsystems erfolgt auf der Hardware.
Für die finale Verifikation des CRC-DMA-IPs bzw. des Gesamtsystems wurde das Testprogramm \texttt{CRC\_DMA.cpp} erstellt, welches im Kontext eines Linux Betriebssystems ausgeführt wird.
Das Testprogramm ist in mehrere Testläufe unterteilt.
Der Ablauf eines Testlaufs ist wie folgt:
\begin{enumerate}
\item Auswählen eines neuen CRC-Parameter-Satzes
\item Zufällige Datenpakete erzeugen mit einer zufälligen Paketanzahl und -größe
\item Konfiguration der CRC-DMA-IPs
\item Starten des CRC-DMA-IPs
\item Berechnen der CRC-Prüfsummen in Software zur Verifikation
\item Warten auf den Interrupt des IPs
\item Überprüfen der vom IP geschriebenen Daten und CRC-Prüfsummen
\end{enumerate}
Die Anzahl der Testläufe wird dem Programm als Argument beim Ausführen übergeben.
Die Testergebnisse werden in einer Log-Datei gespeichert.
\newpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+1 -1
View File
@@ -45,7 +45,7 @@
breaklines=true, % Automatischer Zeilenumbruch
frame=single, % Rahmen um den Codeblock
captionpos=b, % Position der Beschriftung
morekeywords={uint32_t, uint16_t, uint8_t, TIM_HandleTypeDef},
morekeywords={uint32_t, uint16_t, uint8_t, size_t},
}
%Zum Zusammenfügen von Spalten in tabular
-53
View File
@@ -3,14 +3,6 @@
#include <stdint.h>
#include <stdlib.h>
// PC speichert mit LITTLE ENDIAN
// Berechnen einer 8 Bit CRC-Pruefsumme
uint8_t calcCRC8(uint8_t* data, size_t size);
// Berechnen einer 16 Bit CRC-Pruefsumme
uint16_t calcCRC16(uint8_t* inBytes, size_t size);
// Berechnen einer 32 Bit CRC-Pruefsumme mit allen Parametern
uint32_t calcCRC32(
uint8_t* inBytes,
@@ -51,51 +43,6 @@ int main()
return 0;
}
uint8_t calcCRC8(uint8_t* inBytes, size_t size)
{
const uint8_t polynomial = 0xD5;
uint8_t crc = 0x0; // initial value
for (size_t i = 0; i < size; i++) {
crc ^= inBytes[i];
for (int bit = 0; bit < 8; bit++) {
if ((crc & 0x80) != 0) {
crc = (uint8_t) ((crc << 1) ^ polynomial);
}
else {
crc = (uint8_t)(crc << 1);
}
}
}
return crc;
}
uint16_t calcCRC16(uint8_t* inBytes, size_t size)
{
const uint16_t polynomial = 0x1021;
uint16_t crc = 0;
for (size_t b = 0; b < size; b++) {
crc ^= (uint16_t) (inBytes[b] << 8);
for (int i = 0; i < 8; i++) {
if ((crc & 0x8000) != 0) {
crc = (uint16_t) (crc << 1) ^ polynomial;
} else {
crc = (uint16_t)(crc << 1);
}
}
}
return crc;
}
uint32_t calcCRC32(
uint8_t* inBytes,
size_t size,