{"id":2192,"date":"2022-10-18T14:00:53","date_gmt":"2022-10-18T13:00:53","guid":{"rendered":"http:\/\/www.fabienm.eu\/flf\/?p=2192"},"modified":"2022-11-10T15:58:27","modified_gmt":"2022-11-10T14:58:27","slug":"simulons-la-fft-de-xilinx","status":"publish","type":"post","link":"http:\/\/www.fabienm.eu\/flf\/simulons-la-fft-de-xilinx\/","title":{"rendered":"Simulons la FFT de Xilinx"},"content":{"rendered":"\n<p><a href=\"http:\/\/www.fabienm.eu\/flf\/traitement-numerique-du-signal-prise-de-notes\/\" data-type=\"post\" data-id=\"2060\">Apr\u00e8s avoir simul\u00e9 des FFT avec Python et pylab,<\/a> voyons comment les int\u00e9grer dans un FPGA r\u00e9el de Xilinx.<\/p>\n\n\n\n<p>Faire une FFT&nbsp;dans un FPGA est quelque chose qui n&rsquo;est pas trivial. L&rsquo;avantage, suivant la taille du FPGA, est de pouvoir en faire tourner plusieurs en parall\u00e8le pour acc\u00e9l\u00e9rer le traitement. L\u2019inconv\u00e9nient \u00e9tant le temps de d\u00e9veloppement qui est d\u00e9cupl\u00e9 par rapport \u00e0 une solution embarqu\u00e9e sur les habituels DSP&nbsp;ou microcontr\u00f4leurs.<\/p>\n\n\n\n<p>Pour acc\u00e9l\u00e9rer le d\u00e9veloppement, l&rsquo;utilisation de modules fournis par les constructeurs est tr\u00e8s tentante. Bien s\u00fbr, si on utilise la FFT&nbsp;d&rsquo;un constructeur X, elle ne sera pas utilisable sur le FPGA&nbsp;du constructeur Y&#8230; Mais c&rsquo;est de bonne guerre.<\/p>\n\n\n\n<p>Plus g\u00eanant est la difficult\u00e9 de simuler le module sur son PC pour valider l&rsquo;algorithme que l&rsquo;on souhaite mettre en \u0153uvre.<\/p>\n\n\n\n<p>C&rsquo;est pour cela que <a href=\"https:\/\/docs.xilinx.com\/r\/en-US\/pg109-xfft\/C-Model\">Xilinx fournit un mod\u00e8le C de sa FFT<\/a>. Mod\u00e8le que l&rsquo;on peut utiliser gratuitement avec GCC.<\/p>\n\n\n\n<p>Voyons voir comment mettre tout \u00e7a en \u0153uvre.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installation du mod\u00e8le<\/h2>\n\n\n\n<p>Pour compiler le mod\u00e8le il faut d&rsquo;abord le g\u00e9n\u00e9rer \u00e0 partir d&rsquo;un projet Vivado. On cr\u00e9e donc un projet Vivado avec un FPGA cible et on instancie le bloc \u00abFast Fourrier Transform\u00bb dans l&rsquo;\u00abIP designer\u00bb. Pour pouvoir g\u00e9n\u00e9rer le mod\u00e8le il faut que les entr\u00e9es\/sorties soient connect\u00e9es \u00e0 quelques chose, dans notre cas nous nous contenterons d&rsquo;exporter les ports.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/Screenshot-from-2022-10-11-15-21-34.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"327\" src=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/Screenshot-from-2022-10-11-15-21-34-1024x327.png\" alt=\"\" class=\"wp-image-2196\" srcset=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/Screenshot-from-2022-10-11-15-21-34-1024x327.png 1024w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/Screenshot-from-2022-10-11-15-21-34-300x96.png 300w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/Screenshot-from-2022-10-11-15-21-34-768x246.png 768w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/Screenshot-from-2022-10-11-15-21-34.png 1317w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Le bloc FFT de Vivado<\/figcaption><\/figure>\n\n\n\n<p>L&rsquo;archive au format zip est g\u00e9n\u00e9r\u00e9e dans le r\u00e9pertoire suivant :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>test_fft\/test_fft.gen\/sources_1\/bd\/fft_test_design\/ip\/fft_test_design_xfft_0_0\/cmodel\/xfft_v9_1_bitacc_cmodel_lin64.zip<\/code><\/pre>\n\n\n\n<p>Archive que l&rsquo;on d\u00e9zippera dans le r\u00e9pertoire de son choix :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ unzip xfft_v9_1_bitacc_cmodel_lin64.zip\n$ ls -l\ngmp.h\nlibgmp.so.11\nlibIp_xfft_v9_1_bitacc_cmodel.so\nmake_xfft_v9_1_mex.m\nrun_bitacc_cmodel.c\nrun_xfft_v9_1_mex.m\nxfft_v9_1_bitacc_cmodel.h\nxfft_v9_1_bitacc_mex.cpp<\/code><\/pre>\n\n\n\n<p>et \u00e0 laquelle nous ajouterons un fichier main() et un Makefile. Par contre ne r\u00eavez pas, il n&rsquo;y a pas les sources du mod\u00e8le \ud83d\ude09 le mod\u00e8le se trouve dans le fichier binaire de librairie   <code>libIp_xfft_v9_1_bitacc_cmodel.so<\/code><\/p>\n\n\n\n<p>L&rsquo;explication pour la compilation est donn\u00e9e sur le<a href=\"https:\/\/docs.xilinx.com\/r\/en-US\/pg109-xfft\/Linux\"> site officiel<\/a>. Avec<code> g++<\/code> \u00e7a donne :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ g++ -std=c++11 -I. -L. -lgmp -Wl,-rpath,. run_bitacc_cmodel.c -o run_fft -lIp_xfft_v9_1_bitacc_cmodel<\/code><\/pre>\n\n\n\n<p>La compilation g\u00e9n\u00e8re un binaire nomm\u00e9 run_fft qu&rsquo;il faut lancer en int\u00e9grant les librairies du r\u00e9pertoire courant pour le lien dynamique :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$  LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:. .\/run_fft\nRunning the C model...\nSimulation completed successfully\nOutputs from simulation are correct\n$ <\/code><\/pre>\n\n\n\n<p>Le r\u00e9sultat est relativement frustrant: certes il n&rsquo;y a pas d&rsquo;erreur, mais enfin bon &#8230; on n&rsquo;est pas super avanc\u00e9. On aimerait bien avoir de belles courbes et pouvoir admirer le r\u00e9sultat spectral de cette FFT !<\/p>\n\n\n\n<p>Pour cela il va falloir se plonger dans le code \u00abmain()\u00bb et injecter son propre signal.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Plong\u00e9e dans le code<\/h2>\n\n\n\n<p>Pour avoir la documentation du mod\u00e8le on pourra bien s\u00fbr se r\u00e9f\u00e9rer \u00e0 la <a href=\"https:\/\/docs.xilinx.com\/r\/en-US\/pg109-xfft\/C-Model\">documentation officiel<\/a>, mais on peut \u00e9galement se plonger dans le <code>header xfft_v9_1_bitacc_cmodel.h<\/code>  qui est bien comment\u00e9.<\/p>\n\n\n\n<p>Le calcul est lanc\u00e9 avec la fonction xilinx_ip_xfft_v9_1_bitacc_simulate d\u00e9clar\u00e9e ainsi :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\/**\n * Simulate this bit-accurate C-Model.\n *\n * @param     state      Internal state of this C-Model. State\n *                       may span multiple simulations.\n * @param     inputs     Inputs to this C-Model.\n * @param     outputs    Outputs from this C-Model.\n *\n * @returns   Exit code   Zero for SUCCESS, Non-zero otherwise.\n *\/\nIp_xilinx_ip_xfft_v9_1_DLL\nint xilinx_ip_xfft_v9_1_bitacc_simulate\n(\n struct xilinx_ip_xfft_v9_1_state*   state,\n struct xilinx_ip_xfft_v9_1_inputs   inputs,\n struct xilinx_ip_xfft_v9_1_outputs* outputs\n );<\/code><\/pre>\n\n\n\n<p>L&rsquo;\u00e9tat est cr\u00e9\u00e9 avec la fonction <code>xilinx_ip_xfft_v9_1_create_state()<\/code> et la structure d&rsquo;entr\u00e9e (inputs) poss\u00e8de un tableau de double pour la partie imaginaire et un tableau de double pour la partie r\u00e9elle. La taille de la FFT \u00e9tant donn\u00e9e en 2^n par l&rsquo;attribut <code>nfft<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>struct xilinx_ip_xfft_v9_1_inputs\n{\n  int      nfft;              \/\/@- log2(point size)\n\n  double*  xn_re;             \/\/@- Input data (real)\n  int      xn_re_size;\n\n  double*  xn_im;             \/\/@- Input data (imaginary)\n  int      xn_im_size;\n\n  int*     scaling_sch;       \/\/@- Scaling schedule\n  int      scaling_sch_size;\n\n  int      direction;         \/\/@- Transform direction\n}; \/\/ end xilinx_ip_xfft_v9_1_inputs<\/code><\/pre>\n\n\n\n<p>La structure de sortie est encore plus simple :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>struct xilinx_ip_xfft_v9_1_outputs\n{\n  double*  xk_re;          \/\/@- Output data (real)\n  int      xk_re_size;\n\n  double*  xk_im;          \/\/@- Output data (imaginary)\n  int      xk_im_size;\n\n  int      blk_exp;        \/\/@- Block exponent\n\n  int      overflow;       \/\/@- Overflow occurred\n}; \/\/ xilinx_ip_xfft_v9_1_outputs<\/code><\/pre>\n\n\n\n<p>Dans l&rsquo;exemple donn\u00e9e, la partie imaginaire est fix\u00e9e \u00e0 0 sur les 1024&nbsp;\u00e9chantillons et la partie r\u00e9el \u00e0 0.5.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/\/ Create input data frame: constant data\n    double constant_input = 0.5;\n    int i;\n    for (i=0; i&lt;samples; i++) {\n      xn_re&#91;i] = constant_input;\n      xn_im&#91;i] = 0.0;\n    }<\/code><\/pre>\n\n\n\n<p>Si le signal est constant, en toute logique seule la fr\u00e9quence continue (0Hz) doit \u00eatre diff\u00e9rente de 0. C&rsquo;est ce qui est v\u00e9rifi\u00e9 apr\u00e8s avoir effectu\u00e9 le calcul :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/\/ Check xk_re data: only xk_re&#91;0] should be non-zero\n    double expected_xk_re_0;\n    if (C_HAS_SCALING == 0) {\n      expected_xk_re_0 = constant_input * (1 &lt;&lt; C_NFFT_MAX);\n    } else {\n      expected_xk_re_0 = constant_input;\n    }\n    if (xk_re&#91;0] != expected_xk_re_0) {\n      cerr &lt;&lt; \"ERROR:\" &lt;&lt; channel_text &lt;&lt; \" xk_re&#91;0] is incorrect: expected \" &lt;&lt; expected_xk_re_0 &lt;&lt; \", actual \" &lt;&lt; xk_re&#91;0] &lt;&lt; endl;\n      ok = false;\n    }\n    for (i=1; i&lt;samples; i++) {\n      if (xk_re&#91;i] != 0.0) {\n        cerr &lt;&lt; \"ERROR:\" &lt;&lt; channel_text &lt;&lt; \" xk_re&#91;\" &lt;&lt; i &lt;&lt; \"] is incorrect: expected \" &lt;&lt; 0.0 &lt;&lt; \", actual \" &lt;&lt; xk_re&#91;i] &lt;&lt; endl;\n        ok = false;\n      }\n    }\n\n    \/\/ Check xk_im data: all values should be zero\n    for (i=1; i&lt;samples; i++) {\n      if (xk_im&#91;i] != 0.0) {\n        cerr &lt;&lt; \"ERROR:\" &lt;&lt; channel_text &lt;&lt; \" xk_im&#91;\" &lt;&lt; i &lt;&lt; \"] is incorrect: expected \" &lt;&lt; 0.0 &lt;&lt; \", actual \" &lt;&lt; xk_im&#91;i] &lt;&lt; endl;\n        ok = false;\n      }\n    }<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Transform\u00e9e de wavelet<\/h2>\n\n\n\n<p>Tout ceci n&rsquo;est pas tr\u00e8s parlant pour le moment, testons maintenant le mod\u00e8le sur la \u00abwavelet\u00bb g\u00e9n\u00e9r\u00e9e \u00e0 partir d&rsquo;un script python. Le <a href=\"https:\/\/github.com\/Martoni\/traitement_numerique_du_signal\/blob\/main\/cmodel\/xil_gen_sig.py\">script permettant de g\u00e9n\u00e9rer le signal <\/a>et de l&rsquo;\u00e9crire dans un fichier *.txt se trouve dans le r\u00e9pertoire <a href=\"https:\/\/github.com\/Martoni\/traitement_numerique_du_signal\/tree\/main\/cmodel\">cmodel<\/a> du d\u00e9pot github.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/Screenshot-from-2022-10-18-14-40-29.png\"><img loading=\"lazy\" decoding=\"async\" width=\"745\" height=\"839\" src=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/Screenshot-from-2022-10-18-14-40-29.png\" alt=\"\" class=\"wp-image-2214\" srcset=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/Screenshot-from-2022-10-18-14-40-29.png 745w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/Screenshot-from-2022-10-18-14-40-29-266x300.png 266w\" sizes=\"auto, (max-width: 745px) 100vw, 745px\" \/><\/a><figcaption class=\"wp-element-caption\">Le signal que l&rsquo;on va injecter dans le mod\u00e8le FFT de xilinx<\/figcaption><\/figure>\n\n\n\n<p>Le script g\u00e9n\u00e8re un fichier ysig.txt avec toutes les valeurs flottantes \u00e9crites en ASCII. On va ensuite relire le fichier <a href=\"https:\/\/github.com\/Martoni\/traitement_numerique_du_signal\/blob\/main\/cmodel\/real_sig_test.c#L85\">avec le programme C++<\/a> :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/\/ Read input data from file ysig.txt\n    std::ifstream yfile; yfile.open(\"ysig.txt\");\n    if(!yfile.is_open()){\n        perror(\"Open error\");\n        exit(EXIT_FAILURE);\n    }\n    string line;\n    int i=0; \n    while(getline(yfile, line)){\n        xn_re&#91;i] = stof(line);\n        cout &lt;&lt; stof(line) &lt;&lt; endl;\n        xn_im&#91;i] = 0.0;\n        i++; \n    }<\/code><\/pre>\n\n\n\n<p>Le programme \u00e9crira le r\u00e9sultat sous dans le fichier xfft_out.txt une fois le r\u00e9sultat calcul\u00e9:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n    \/\/ save outputs in xfft_out.txt\n    std::ofstream outfile; outfile.open(\"xfft_out.txt\");\n    if(outputs.xk_re_size != outputs.xk_im_size){\n        printf(\"Error imaginary part size is not equal to real part\");\n    }\n    for(int i=0; i &lt; outputs.xk_re_size; i++){\n        outfile &lt;&lt; outputs.xk_re&#91;i] &lt;&lt; \", \" &lt;&lt; outputs.xk_im&#91;i] &lt;&lt; endl;\n    }\n    <\/code><\/pre>\n\n\n\n<p>Fichier que l&rsquo;on relira pour l&rsquo;afficher au moyen du script python <a href=\"https:\/\/github.com\/Martoni\/traitement_numerique_du_signal\/blob\/main\/cmodel\/plot_fft.py\">plot_fft.py<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/fourrier_xfft.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"530\" src=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/fourrier_xfft-1024x530.png\" alt=\"\" class=\"wp-image-2217\" srcset=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/fourrier_xfft-1024x530.png 1024w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/fourrier_xfft-300x155.png 300w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/fourrier_xfft-768x398.png 768w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/fourrier_xfft-1536x795.png 1536w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2022\/10\/fourrier_xfft.png 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">R\u00e9sultat plut\u00f4t concluant puisque identique au calcul de la fonction magnitude_spectrum de pylab.<\/figcaption><\/figure>\n\n\n\n<p>Et nous avons la bonne surprise d&rsquo;obtenir le m\u00eame spectre du module qu&rsquo;avec la fonction de pylab.<\/p>\n\n\n\n<p>On peut maintenant jouer avec les param\u00e8tres du module Xilinx et affiner notre mod\u00e8le de simulation avant de le synth\u00e9tiser dans un FPGA (de chez Xilinx \u00e9videment \ud83d\ude09<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Apr\u00e8s avoir simul\u00e9 des FFT avec Python et pylab, voyons comment les int\u00e9grer dans un FPGA r\u00e9el de Xilinx. Faire une FFT&nbsp;dans un FPGA est quelque chose qui n&rsquo;est pas trivial. L&rsquo;avantage, suivant la taille du FPGA, est de pouvoir en faire tourner plusieurs en parall\u00e8le pour acc\u00e9l\u00e9rer le traitement. L\u2019inconv\u00e9nient \u00e9tant le temps de &hellip; <a href=\"http:\/\/www.fabienm.eu\/flf\/simulons-la-fft-de-xilinx\/\" class=\"more-link\">Continuer la lecture de <span class=\"screen-reader-text\">Simulons la FFT de Xilinx<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","footnotes":""},"categories":[1],"tags":[228,13,225,226,56,227,17],"class_list":["post-2192","post","type-post","status-publish","format-standard","hentry","category-non-classe","tag-c","tag-compilation","tag-dsp","tag-fft","tag-python","tag-signal","tag-xilinx"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"post-thumbnail":false},"uagb_author_info":{"display_name":"Fabien Marteau","author_link":"http:\/\/www.fabienm.eu\/flf\/author\/admin\/"},"uagb_comment_info":0,"uagb_excerpt":"Apr\u00e8s avoir simul\u00e9 des FFT avec Python et pylab, voyons comment les int\u00e9grer dans un FPGA r\u00e9el de Xilinx. Faire une FFT&nbsp;dans un FPGA est quelque chose qui n&rsquo;est pas trivial. L&rsquo;avantage, suivant la taille du FPGA, est de pouvoir en faire tourner plusieurs en parall\u00e8le pour acc\u00e9l\u00e9rer le traitement. L\u2019inconv\u00e9nient \u00e9tant le temps de\u2026","_links":{"self":[{"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/2192","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/comments?post=2192"}],"version-history":[{"count":23,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/2192\/revisions"}],"predecessor-version":[{"id":2248,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/2192\/revisions\/2248"}],"wp:attachment":[{"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/media?parent=2192"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/categories?post=2192"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/tags?post=2192"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}