{"id":823,"date":"2015-06-05T10:13:33","date_gmt":"2015-06-05T18:13:33","guid":{"rendered":"http:\/\/www.beyond-circuits.com\/wordpress\/?page_id=823"},"modified":"2016-09-26T12:46:15","modified_gmt":"2016-09-26T20:46:15","slug":"tutorial21","status":"publish","type":"page","link":"https:\/\/www.beyond-circuits.com\/wordpress\/tutorial\/tutorial21\/","title":{"rendered":"Tutorial 21: Having some fun"},"content":{"rendered":"<p>In this tutorial, let&#8217;s\u00a0have a little fun. Now that we can receive audio data and send it back out, we might as well do something with it. In this tutorial, we&#8217;re going to make a speech jammer.<\/p>\n<p>What&#8217;s a speech jammer? We&#8217;re going to take audio input from a microphone and delay it about 450 milliseconds and then play it back out. Most people find it really difficult to talk with the delayed audio feedback. Curiously, it doesn&#8217;t seem to affect me much. I think people that know me might not find that so strange. Here&#8217;s a lovely reading of Dr. Seuss&#8217; book <a href=\"https:\/\/www.youtube.com\/watch?v=dK2ylXWn_v4\">One Fish Two Fish<\/a>.<\/p>\n<h1>Overview<\/h1>\n<p>To get something basic working, all we need is a module which receives the AXI4 audio stream and delays it, which then produces an AXI4 audio stream out. The block design looks something like this:<\/p>\n<p><a href=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_fig1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-824\" src=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_fig1.png\" alt=\"tutorial21_fig1\" width=\"1228\" height=\"448\" srcset=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_fig1.png 1228w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_fig1-300x109.png 300w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_fig1-1024x374.png 1024w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_fig1-500x182.png 500w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_fig1-900x328.png 900w\" sizes=\"auto, (max-width: 1228px) 100vw, 1228px\" \/><\/a><\/p>\n<p>All we do is insert the <em>axi4_stream_delay<\/em> module between the I2S receiver and I2S transmitter. There&#8217;s only one gotcha: the <em>axi4_stream_delay<\/em> module doesn&#8217;t exist, and we need to write it.<\/p>\n<h1>The AXI4 stream delay module<\/h1>\n<p>The <em>axi4_stream_delay<\/em> module accepts audio data on an AXI4 streaming slave interface, stores it in a buffer, and then sends it out an AXI4 streaming master interface. To keep things simple, our module will make a few assumptions. The first is that it is receiving audio data so that the packet size is 8 bytes. The second is that we don&#8217;t need to keep all the bits in the audio data, we may want to truncate some of the data to save on memory requirements. The third thing is that the maximum delay is fixed as a parameter to the module, but there is a delay input which is able to\u00a0specify how many samples we want to delay. Your module declaration should look something like this. We&#8217;ll also be including a <em>scale<\/em> input that\u00a0specifies a scaling value so we can turn up the volume on the audio as it passes though the block.<\/p>\n<pre>module axi4_stream_delay #\r\n(\r\nparameter DATA_WIDTH = 32,\r\nparameter SAMPLE_WIDTH = 16,\r\nparameter MAX_DELAY = 8192\r\n)\r\n(\r\ninput ACLK,\r\ninput ARESETN,\r\ninput [$clog2(MAX_DELAY)-1:0] delay,\r\ninput [$clog2(SAMPLE_WIDTH)-1:0] scale,\r\ninput M_AXIS_TREADY,\r\noutput reg M_AXIS_TVALID,\r\noutput reg M_AXIS_TLAST,\r\noutput reg [DATA_WIDTH-1:0] M_AXIS_TDATA,\r\n\r\noutput reg S_AXIS_TREADY,\r\ninput S_AXIS_TVALID,\r\ninput S_AXIS_TLAST,\r\ninput [DATA_WIDTH-1:0] S_AXIS_TDATA\r\n);\r\n<\/pre>\n<p>The <em>DATA_WIDTH<\/em> parameter specifies the width of the <em>TDATA<\/em> bus. The <em>SAMPLE_WIDTH<\/em> parameter specifies the amount of audio data we want to store for each sample. So the module is going to\u00a0only store bits <em>DATA_WIDTH<\/em>-1 through <em>DATA_WIDTH<\/em>&#8211;<em>SAMPLE_WIDTH<\/em> in the buffer. The lower bits will be thrown away. Memory space within the FPGA is pretty precious, so it&#8217;s a good idea to not waste it. The final parameter <em>MAX_DEPTH<\/em> specifies the maximum amount of delay. To calculate the time of the delay, recall that our audio data is arriving at 48kHz, or one sample every 20.833\u00b5s. The default value of 8192 will produce a maximum delay of 171ms.<\/p>\n<h2>The buffer<\/h2>\n<p>Flip-flops in an FPGA are extremely flexible. They can be updated at pretty much any time, and their outputs can be used whenever you want\u00a0by as many consumers as you want. This flexibility comes at a high price, though, in terms of area and power. Sometimes you have a need to store a lot of information and this information doesn&#8217;t need to be accessed all the time like a flip-flop. The FPGA contains a large number of small RAM memories. These are much denser than the flip-flops and use much less power. There are limitations about how you can access them, though.<\/p>\n<p>To use a memory in Verilog, you need to declare a <strong>reg<\/strong> array. The array will have a width and a depth. Here&#8217;s the declaration for the memory in our module:<\/p>\n<pre> reg [2*SAMPLE_WIDTH-1:0] delay_mem[MAX_DELAY-1:0];<\/pre>\n<div id=\"attachment_828\" style=\"width: 310px\" class=\"wp-caption alignright\"><a href=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_bd1.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-828\" class=\"wp-image-828 size-medium\" src=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_bd1-300x240.png\" alt=\"A circular buffer\" width=\"300\" height=\"240\" srcset=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_bd1-300x240.png 300w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_bd1-374x300.png 374w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/06\/tutorial21_bd1.png 504w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-828\" class=\"wp-caption-text\">A circular buffer<\/p><\/div>\n<p>Each element of the array can hold two samples. The array has <em>MAX_DELAY<\/em> elements indexed between 0 and <em>MAX_DELAY<\/em>-1.<\/p>\n<p>In order to use the buffer, we need a pointer which indicates the address that we want to write to and read from in the buffer.<\/p>\n<p>&nbsp;<\/p>\n<p>Here is a declaration for the pointer:<\/p>\n<pre> reg [$clog2(MAX_DELAY)-1:0] ptr;<\/pre>\n<p>We want our buffer to be circular. That just means that when the pointer has the value of <em>delay<\/em>-1\u00a0and we increment it, it just wraps back to zero. Here is the code to manage <em>ptr<\/em>:<\/p>\n<pre>  always @(posedge ACLK)\r\n    if (!ARESETN)\r\n      ptr &lt;= 0;\r\n    else if (<em>buffer enable condition<\/em>)\r\n      if (ptr+1 == delay)\r\n\tptr &lt;= 0;\r\n      else\r\n\tptr &lt;= ptr+1;\r\n<\/pre>\n<p>When you code a flip-flop, you are using a Verilog construct which the synthesis tool recognizes as a template for a flip-flop. Likewise, you need to code a memory using a proper memory template which the synthesis tool can then recognize. Also, the memories inside the FPGA are <em>synchronous<\/em>, which means they need to use a clock signal. If you have used SRAM parts before that were asynchronous, these may be a little different. The code below assumes the value being written to the RAM is called <em>in_reg<\/em> and the value being read from the RAM is stored in <em>out_reg<\/em>.<\/p>\n<pre>  always @(posedge ACLK)\r\n    if (!S_AXIS_TREADY &amp;&amp; !M_AXIS_TVALID)\r\n      begin\r\n\tdelay_mem[ptr] &lt;= in_reg;\r\n\tout_reg &lt;= delay_mem[ptr];\r\n      end\r\n<\/pre>\n<p>There are a number of different templates for RAMs depending on what type of RAM you want the tool to use. The RAMs used in the FPGA are dual port. Each port can have its own clock, address, write port and read port. For more information on inferring RAMs in your design, consult the <a href=\"http:\/\/www.xilinx.com\/support\/answers\/46515.html\">Xilinx Answer Record 46515<\/a>.<\/p>\n<h1>Tasks<\/h1>\n<p>I have given you the code that reads and writes the circular buffer and the module declaration. You need to provide code which implements an AXI4 slave interface which stores the left and right sample data in <em>in_reg<\/em>, code which takes data in <em>out_reg<\/em> and writes it out the AXI4 streaming master interface, and code which decides when to write and read from the memory.<\/p>\n<p>Since we read and write to the RAM at the same time, the slave and master interfaces need to be synchronized. You enable the RAM when the slave input buffer <em>in_reg<\/em> has valid data in it, and the master interface has sent the data it had in <em>out_reg<\/em>.<\/p>\n<p>When you transfer the data from out_reg to M_AXIS_TDATA you need to align the value such that the MSB of M_AXIS_TDATA is lined up with the MSB of <em>out_reg<\/em>. You will need to shift the <em>out_reg<\/em> value left by <em>DATA_WIDTH<\/em>&#8211;<em>SAMPLE_WIDTH<\/em>. We also want to scale the data by the value specified by the <em>scale<\/em> input. To do that, just shift left by\u00a0<em>DATA_WIDTH<\/em>&#8211;<em>SAMPLE_WIDTH<\/em>+<em>scale<\/em>.<\/p>\n<h1>Test<\/h1>\n<p>A test <a href=\"\/repos\/tutorial\/trunk\/tests\/axi4_stream_delay_test.v\">axi4_stream_delay_test.v<\/a> is attached, which can be used to verify your design. This test makes use of the common_test_util and queue modules found in the other tutorials, as well as the AXI4 stream master and slave bus functional models.<\/p>\n<p>Note that the test does not vary the <em>depth<\/em> or <em>scale<\/em> inputs during the test. It is possible to modify those parameters for the test, but in this case, they remain static throughout.<\/p>\n<h1>Building the FPGA<\/h1>\n<p>Now build your FPGA using a block design like the one shown above. You should modify the delay parameter on the AXI4 stream delay module to 16384. That gives us about 342ms of delay.<\/p>\n<h1>Software<\/h1>\n<p>We have to make some changes to the software that initializes the ADAU1761 audio chip. In previous tutorials, we just wanted to receive audio from the line in and send it out to the headphones. In this tutorial, we&#8217;ll need to take audio data from the microphone input. Also, I found it helpful to increase the gain on the audio, so we&#8217;re going to do that as well.<\/p>\n<p>The changes are that we increase the input gain on the left and write channels. The mixer control registers are at address 0x400a and 0x400c. We write a value of 0xf there to un-mute the microphone input and increase the input gain to 6dB. We also set the left and right mixer gain (0x400b, 0x400d) to 6dB (0x7). Finally we need to turn on the mic bias by setting register 0x4010 to 0x5. Use <a href=\"\/wordpress\/wp-content\/uploads\/tutorials\/tutorial21\/main.c\">main.c<\/a> in your SDK project for this tutorial.<\/p>\n<h1>Using the speech jammer<\/h1>\n<p>Now you just need to find some headphones and a microphone. You want to minimize the sound that a person hears directly when they speak, so over-ear headphones work the best. Alternately,\u00a0you can use ear buds and then over-ear hearing protectors. This is actually the best setup. You&#8217;re also going to need a mic. It is important that the mic gain be very high, and you&#8217;re really going to want to speak directly into the mic.<\/p>\n<p>It can also be helpful to increase the gain of the audio signal so that the speech is louder in the ear of the speaker. You can do that in the axi4_stream_delay module by scaling the data as it passes through. I found that a factor of eight worked best for my mic and headphones.<\/p>\n<p>Now try it out on your friends, and see what happens.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial, let&#8217;s\u00a0have a little fun. Now that we can receive audio data and send it back out, we might as well do something with it. In this tutorial, we&#8217;re going to make a speech jammer. What&#8217;s a speech &hellip; <a href=\"https:\/\/www.beyond-circuits.com\/wordpress\/tutorial\/tutorial21\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"parent":226,"menu_order":21,"comment_status":"open","ping_status":"open","template":"","meta":{"footnotes":""},"class_list":["post-823","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/pages\/823","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/comments?post=823"}],"version-history":[{"count":18,"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/pages\/823\/revisions"}],"predecessor-version":[{"id":1103,"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/pages\/823\/revisions\/1103"}],"up":[{"embeddable":true,"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/pages\/226"}],"wp:attachment":[{"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/media?parent=823"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}