239 lines
9.3 KiB
TeX
239 lines
9.3 KiB
TeX
\documentclass[12pt]{article}
|
|
\usepackage[margin=2cm]{geometry}
|
|
\usepackage[utf8]{inputenc}
|
|
\usepackage{hyperref}
|
|
\usepackage{graphicx}\documentclass[12pt]{article}
|
|
\usepackage[margin=2cm]{geometry}
|
|
\usepackage[utf8]{inputenc}
|
|
\usepackage{hyperref}
|
|
\usepackage{graphicx}
|
|
\usepackage{minted}
|
|
\usepackage{caption}
|
|
|
|
\title{Laboratory 2 report}
|
|
\author{Cédric Hölzl \and Antoine Brunner}
|
|
\date{March 2020}
|
|
|
|
\begin{document}
|
|
\maketitle
|
|
|
|
\section{VHDL system design}
|
|
In the first part of the laboratory, we had to implement the statistic computations, the level adjuster in VHDL.
|
|
|
|
For the statistics computation, we didn't do anything incredible ;). In the component, there are three registers: one for the maximum, one for the minimum, and one for the sum. Those registers are updated with the value from \emph{pix\_data} when the signal \emph{valid} is 1. They are reset when the signal \emph{pix\_sof} is 1, so that the statistics for the new frame can be computed.
|
|
|
|
The level adjuster was just a matter of finding a formula that does what we want. The component takes as input a 14-bit value that is in the range $[raw\_min, raw\_max]$, and we would like to remap those values to the full range $[0, 2^{14}-1]$. If we were able to use floating point arithmetic, what we would do the following:
|
|
\[x \mapsto \frac{x - min}{max - min} \cdot (2^{14} - 1)\]
|
|
|
|
That is, we would first divide $x - min$ by $max - min$ to have a value between 0 and 1. Then, we would multiply that by $2^{14} - 1$ to get the result. But since we are using integer arithmetic, we cannot perform the first division in this way. The trick is to first multiply by $2^{14} - 1$, which results in a 27-bit value, and then make an integer division by $max - min$.
|
|
|
|
Once we realized that little trick, the rest was a matter of translating it to VHDL, which was fairly easy, since we had already been given the division component.
|
|
|
|
\section{C application design}
|
|
As for the first lab, the C wasn't too extensive, we used IORD/IOWR to interact with the right registers (or bits) for the different function: in one case writing a value, in another one checking a bit and the last one looping while a bit is 1. We also completed the main loop of the application, with it working as follows: While no error occurs capture and wait.
|
|
|
|
\section{QSys system integration}
|
|
The second part of the lab consisted of connecting all the hardware component together using \emph{QSys}. We didn't encounter any major problems in that part. Some small issues that we had was that we were setting wrong directions for some signals, but that wasn't really hard to fix.
|
|
Note that we used the automatic memory mapping from QSys to let him decide where our components were mapped in memory. The memory mapping was then exported to the software part through the file system.h. Figure~\ref{fig:memory_mapping} shows how that mapping was chosen by QSys and exported to the file \emph{system.h}.
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics[width=\textwidth]{qsys_1.png}
|
|
\includegraphics[width=\textwidth]{horizontal.png}
|
|
\caption{At the top, the QSys editor. At the bottom, the \emph{system.h} file that was automatically generated. It can be seen that the memory mapping was taken by QSys.}
|
|
\label{fig:memory_mapping}
|
|
\end{figure}
|
|
|
|
Another problem that we had was that we forgot to instantiate the components of the system in the VHDL entity that \emph{QSys} generated, because we initialy thought it was also done automaticly.
|
|
|
|
\section*{Results}
|
|
In this section we just present a few images that we captured using the thermal camera, in Figure~\ref{fig:thermal_images}
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics{output_face.png}
|
|
\includegraphics{output_bottles.png}
|
|
\includegraphics{output_computer.png}
|
|
\caption{The images that we captured using the thermal camera. From left to right: Face with glasses, Glass Bottles, Inside a Computer with CPU and GPU (where we can see on the left a column of chokes and capacitors and on the right a crystal both of them being a major source of heat. We can also see on the bottom right an SSD).}
|
|
\label{fig:thermal_images}
|
|
\end{figure}
|
|
|
|
\newpage
|
|
\section{Appendix: Code}
|
|
In this appendix, we have put the code that implements what was described in the previous sections, if you prefer to read from the PDF. In order not to make the report too long, we have only included the changes that we made, not the full files.
|
|
|
|
\begin{minted}[breaklines]{VHDL}
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
entity lepton_stats is
|
|
port(
|
|
clk : in std_logic;
|
|
reset : in std_logic;
|
|
pix_data : in std_logic_vector(13 downto 0);
|
|
pix_valid : in std_logic;
|
|
pix_sof : in std_logic;
|
|
pix_eof : in std_logic;
|
|
stat_min : out std_logic_vector(13 downto 0);
|
|
stat_max : out std_logic_vector(13 downto 0);
|
|
stat_sum : out std_logic_vector(26 downto 0);
|
|
stat_valid : out std_logic);
|
|
end lepton_stats;
|
|
|
|
architecture rtl of lepton_stats is
|
|
|
|
-- The accumulated sum, min and max of the pixel values
|
|
signal curr_min : unsigned(13 downto 0);
|
|
signal curr_max : unsigned(13 downto 0);
|
|
signal curr_sum : unsigned(26 downto 0);
|
|
|
|
-- The next value of the registers
|
|
signal next_min : unsigned(13 downto 0);
|
|
signal next_max : unsigned(13 downto 0);
|
|
signal next_sum : unsigned(26 downto 0);
|
|
|
|
begin
|
|
|
|
-- This is the synchronous transition logic
|
|
transition_logic : process(clk, reset)
|
|
begin
|
|
if reset = '1' then
|
|
curr_sum <= (others => '0');
|
|
curr_min <= (others => '0');
|
|
curr_max <= (others => '0');
|
|
elsif rising_edge(clk) then
|
|
curr_min <= next_min;
|
|
curr_max <= next_max;
|
|
curr_sum <= next_sum;
|
|
end if;
|
|
end process;
|
|
|
|
-- This is the combinatorial transition logic
|
|
next_min <=
|
|
curr_min when pix_valid = '0' else
|
|
unsigned(pix_data) when pix_sof = '1' else
|
|
curr_min when unsigned(pix_data) >= curr_min else
|
|
unsigned(pix_data);
|
|
|
|
next_max <=
|
|
curr_max when pix_valid = '0' else
|
|
unsigned(pix_data) when pix_sof = '1' else
|
|
curr_max when unsigned(pix_data) <= curr_max else
|
|
unsigned(pix_data);
|
|
|
|
next_sum <=
|
|
curr_sum when pix_valid = '0' else
|
|
unsigned((26 downto 14 => '0') & pix_data) when pix_sof = '1' else
|
|
curr_sum + unsigned((26 downto 14 => '0') & pix_data);
|
|
|
|
-- This is the synchronous output logic
|
|
output_logic : process(clk, reset)
|
|
begin
|
|
if rising_edge(clk) then
|
|
stat_valid <= pix_eof;
|
|
end if;
|
|
end process;
|
|
|
|
-- This is the combinatorial output logic
|
|
stat_min <= std_logic_vector(curr_min);
|
|
stat_max <= std_logic_vector(curr_max);
|
|
stat_sum <= std_logic_vector(curr_sum);
|
|
|
|
end rtl;
|
|
\end{minted}
|
|
\captionof{listing}{
|
|
The code written in lepton\_stats.vhd
|
|
\label{code:lepton_stats.vhd}
|
|
}
|
|
|
|
|
|
\begin{minted}[breaklines]{VHDL}
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
entity level_adjuster is
|
|
port(
|
|
clk : in std_logic;
|
|
raw_pixel : in std_logic_vector(13 downto 0);
|
|
raw_max : in std_logic_vector(13 downto 0);
|
|
raw_min : in std_logic_vector(13 downto 0);
|
|
raw_sum : in std_logic_vector(26 downto 0);
|
|
adjusted_pixel : out std_logic_vector(13 downto 0));
|
|
end level_adjuster;
|
|
|
|
architecture rtl of level_adjuster is
|
|
component lpm_divider
|
|
port(
|
|
clock : in std_logic;
|
|
denom : in std_logic_vector(13 downto 0);
|
|
numer : in std_logic_vector(27 downto 0);
|
|
quotient : out std_logic_vector(27 downto 0);
|
|
remain : out std_logic_vector(13 downto 0));
|
|
end component;
|
|
|
|
-- Intermediate signals needed by the divider
|
|
signal numer : std_logic_vector(27 downto 0);
|
|
signal denom : std_logic_vector(13 downto 0);
|
|
signal quot : std_logic_vector(27 downto 0);
|
|
|
|
begin
|
|
|
|
-- Computation of the intermediate signals
|
|
numer <= std_logic_vector((13 downto 0 => '1') * (unsigned(raw_pixel) - unsigned(raw_min)));
|
|
denom <= std_logic_vector(unsigned(raw_max) - unsigned(raw_min));
|
|
|
|
-- We compute the remaineder of (x - min) / (max - min)
|
|
divider : lpm_divider port map(
|
|
clock => clk,
|
|
numer => numer,
|
|
denom => denom,
|
|
quotient => quot,
|
|
remain => open
|
|
);
|
|
|
|
-- And we only keep the LSB of the quotient (we know the MSB must be 0)
|
|
adjusted_pixel <=
|
|
(adjusted_pixel'range => '0') when denom = (denom'range => '0') else
|
|
quot(13 downto 0);
|
|
|
|
end rtl;
|
|
\end{minted}
|
|
\captionof{listing}{
|
|
The code written in level\_adjuster.vhd
|
|
\label{code:level_adjuster.vhd}
|
|
}
|
|
|
|
\begin{minted}[breaklines]{C}
|
|
do{
|
|
lepton_start_capture(&lepton);
|
|
lepton_wait_until_eof(&lepton);
|
|
}while(lepton_error_check(&lepton));
|
|
\end{minted}
|
|
\captionof{listing}{
|
|
The code written in app.c
|
|
\label{code:app.c}
|
|
}
|
|
|
|
\begin{minted}[breaklines]{C}
|
|
void lepton_start_capture(lepton_dev *dev) {
|
|
IOWR_16DIRECT(dev->base, LEPTON_REGS_COMMAND_OFST, 0x1);
|
|
}
|
|
|
|
bool lepton_error_check(lepton_dev *dev) {
|
|
return (IORD_16DIRECT(dev->base, LEPTON_REGS_STATUS_OFST) & 0x2) != 0;
|
|
}
|
|
|
|
void lepton_wait_until_eof(lepton_dev *dev) {
|
|
while(IORD_16DIRECT(dev->base, LEPTON_REGS_STATUS_OFST) & 0x1);
|
|
}
|
|
\end{minted}
|
|
\captionof{listing}{
|
|
The code written in lepton.c
|
|
\label{code:lepton.c}
|
|
}
|
|
|
|
\end{document}
|
|
|