{"id":638,"date":"2015-04-24T10:33:47","date_gmt":"2015-04-24T18:33:47","guid":{"rendered":"http:\/\/www.beyond-circuits.com\/wordpress\/?page_id=638"},"modified":"2026-10-23T13:35:49","modified_gmt":"2026-10-23T21:35:49","slug":"tutorial18","status":"publish","type":"page","link":"https:\/\/www.beyond-circuits.com\/wordpress\/tutorial\/tutorial18\/","title":{"rendered":"Tutorial 18: I2S Receiver"},"content":{"rendered":"<p>In this tutorial, we will develop an I2S audio receiver. The goal of the tutorial is to really give you a chance to write your own RTL code. The tutorial will give you a simulation test bench which you will use to verify your RTL code. The tutorial will proceed in a number of steps, with each step adding more to your design. Let&#8217;s get started.<\/p>\n<h1>I2S Overview<\/h1>\n<p>The I2S protocol is a common standard used to send audio data. It is a serial protocol very similar to SPI, but it is a streaming protocol. That means it is always transmitting data.<\/p>\n<p>I2S sends a stream of stereo audio data. For each audio sample there is a left channel and a right channel value. The values can be any number of bits, although 16, 20, 24, and 32 bit values are the most common. The\u00a0<a href=\"https:\/\/www.nxp.com\/docs\/en\/user-manual\/UM11732.pdf\">I2S Bus Specification<\/a>\u00a0provides a really good overview of the protocol and you should download and peruse that link now.<\/p>\n<p>There are three signals involved in sending audio data. A serial clock <em>SCK<\/em>, a word strobe signal <em>WS<\/em> which indicates if the data is for the left channel or the right channel, and a serial data signal <em>SD<\/em>. The <em>WS<\/em> and <em>SD<\/em> signals are synchronous to the <em>SCK<\/em> signal and change on the <em>falling<\/em> edge of <em>SCK<\/em>. Data is sent most significant bit first. One wrinkle is that the data is one cycle late with respect to the word strobe. The timing diagram illustrates the details.<\/p>\n<h1>Implementing the Receiver<\/h1>\n<div id=\"attachment_786\" style=\"width: 310px\" class=\"wp-caption alignright\"><a href=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_i2s_timing.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-786\" class=\"wp-image-786 size-medium\" src=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_i2s_timing-300x99.png\" alt=\"tutorial18_i2s_timing\" width=\"300\" height=\"99\" srcset=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_i2s_timing-300x99.png 300w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_i2s_timing-500x165.png 500w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_i2s_timing-900x297.png 900w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_i2s_timing.png 916w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-786\" class=\"wp-caption-text\">I2S protocol timing diagram<\/p><\/div>\n<p>In the next few tutorials, we will be using a technique called <em>Test Driven Development<\/em>. With Test Driven Development, you write the test first and then write the RTL code that passes the test. Then you add more to the test, then add more to the RTL to again pass the test. By iterating this way, you get to the point where the test tests everything you want and the RTL performs the correct function. I will be providing the test code and it is your job to write the RTL. I will also give you a block diagram for the RTL so you have a good idea of what you will need to code.<\/p>\n<div id=\"attachment_787\" style=\"width: 310px\" class=\"wp-caption alignright\"><a href=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_receiver1_bd.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-787\" class=\"wp-image-787 size-medium\" src=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_receiver1_bd-300x180.png\" alt=\"tutorial18_receiver1_bd\" width=\"300\" height=\"180\" srcset=\"https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_receiver1_bd-300x180.png 300w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_receiver1_bd-1024x614.png 1024w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_receiver1_bd-500x300.png 500w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_receiver1_bd-900x539.png 900w, https:\/\/www.beyond-circuits.com\/wordpress\/wp-content\/uploads\/2015\/04\/tutorial18_receiver1_bd.png 1160w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-787\" class=\"wp-caption-text\">Basic I2S receiver block diagram<\/p><\/div>\n<p>For our first pass, we will implement a receiver that has a fixed 32-bit word size. The block diagram (almost schematic) is shown to the right. This figure is largely taken from figure 6 of the I2S specification. However, I believe the figure in the specification is actually incorrect. The specification has the polarities of the WSD signals into the AND gates, which enable data from the shift register to be written to the left and right data registers.<\/p>\n<h2>Ideas and Tips<\/h2>\n<p>Here are some ideas and tips about designing this module.<\/p>\n<p>First, remember that an HDL like Verilog describes the hardware. You are not describing a task or a sequence of steps like you would in software. You need to take the block diagram and code each piece of it.<\/p>\n<h3>Registers with enables<\/h3>\n<p>So for example, <em>data_left<\/em> and <em>data_right<\/em> are 32-bit output registers which are clocked off of <em>sclk<\/em> and they each have an enable. How do you code a register with an enable? Here is an example.<\/p>\n<pre><code>always @(posedge clk)\n  if (enable)\n    my_reg &lt;= my_value;\n<\/code><\/pre>\n<p>The <em>enable<\/em> expression can be more complex than a simple value. But, the result of that expression will enable the register <em>my_reg<\/em> to update its value.<\/p>\n<h3>Combinational Logic<\/h3>\n<p>How about the output of a combinational gate? Let&#8217;s say we have values <em>a<\/em> and <em>b<\/em> and we want to create the signal <em>z<\/em> which is the logical OR of <em>a<\/em> and <em>b<\/em>? Well, there are two ways that we can do this in <em>behavioral<\/em> Verilog code. We can declare z to be a wire and use a continuous assignment like this:<\/p>\n<pre><code>wire z;\nassign z = a|b;\n<\/code><\/pre>\n<p>We can even combine the wire declaration and the assign statement into a single line like this:<\/p>\n<pre><code>wire z = a|b; <\/code><\/pre>\n<p>We could also use a combinational procedural block and a reg variable like this:<\/p>\n<pre><code>reg z;\nalways @(*)\n  z = a|b;\n<\/code><\/pre>\n<p>Note a couple of things. First, we need to use a blocking assignment and not a non-blocking assignment. If we were to use a non-blocking assignment, weird interactions with the combinational always block and other sequential blocks could cause incorrect simulation results. Basically, if you were to use a non-blocking assignment and then assign the <em>z<\/em> value into a flip-flop, whether you get the updated value of <em>z<\/em> would depend on simulation ordering of the events. It would lead to the simulation being incorrect and not matching the synthesized design.<\/p>\n<p>Second, it is critical that <em>all<\/em> paths of execution in the always block assign a new value to <em>z<\/em>. If this is not the case, then you are specifying that there may be conditions where <em>z<\/em> does not update and should hold its value. This implies a latch type construct. The only time you should need to use a latch is interfacing with some unusual external logic. I have designed dozens of FPGAs and have only needed a latch once or twice.<\/p>\n<h3>Shift Registers<\/h3>\n<p>There are a number of ways to code a shift register. Say we have a four-bit register called <em>shift<\/em> that shifts a value <em>shift_in<\/em> in the least significant bit.<\/p>\n<p>You could code this the <em>C<\/em> way:<\/p>\n<pre><code>reg [3:0] shift;\nalways @(posedge clk)\n  shift &lt;= (shift&lt;&lt;1)|shift_in;\n<\/code><\/pre>\n<p>You could code this the <em>VHDL<\/em> way:<\/p>\n<pre><code>integer i;\nreg [3:0] shift;\nalways @(posedge clk)\n  begin\n    shift[0] &lt;= shift_in;\n    for (i=1; i&lt;4;i=i+1)\n      shift[i] &lt;= shift[i-1];\n  end\n<\/code><\/pre>\n<p>You could code this the caveman way:<\/p>\n<pre><code>reg [3:0] shift;\n  always @(posedge clk)\n    begin\n      shift[0] &lt;= shift_in;\n      shift[1] &lt;= shift[0];\n      shift[2] &lt;= shift[1];\n      shift[3] &lt;= shift[2];\n     end\n<\/code><\/pre>\n<p>Or the more literate Verilog way:<\/p>\n<pre><code>reg [3:0] shift;\nalways @(posedge clk)\n  begin\n    shift[0] &lt;= shift_in;\n    shift[3:1] &lt;= shift[2:0];\n  end\n<\/code><\/pre>\n<p>Or the more aesthetic Verilog way:<\/p>\n<pre><code>reg [3:0] shift;\nalways @(posedge clk)\n  shift &lt;= {shift,shift_in};\n<\/code><\/pre>\n<p>Some people, and more importantly, some linting tools will complain about the above code. The complaint is that the expression on the right is five bits and the value on the left is four bits and so we are dropping a bit. My rebuttal to this is &#8220;Well, of course we are. We don&#8217;t need that bit any more.&#8221; They would have you write the code like this:<\/p>\n<pre><code>reg [3:0] shift;\nalways @(posedge clk)\n  shift &lt;= {shift[2:0],shift_in};\n<\/code><\/pre>\n<p>I guess that is well and good, but with this new code, in addition to being more verbose, you need to modify two lines of code if the width of the shift register changes. In general, I don&#8217;t think it looks as clean and prefer to just turn off those warnings with the lint tool.<\/p>\n<p>Anyway, hopefully these tips will help you to code the module.<\/p>\n<h2>Files<\/h2>\n<p>Here are the files you will need. The test file uses another module called <em>common_test_util<\/em> for some housekeeping tasks. You will need to have both of these files in your simulation as well as your RTL file.<\/p>\n<ul>\n<li><a href=\"https:\/\/www.beyond-circuits.com\/repos\/tutorial\/trunk\/tests\/i2s_receive1_test.v\">i2s_receive1_test.v<\/a><\/li>\n<li><a href=\"https:\/\/www.beyond-circuits.com\/repos\/tutorial\/trunk\/tbench\/common_test_util.v\">common_test_util.v<\/a><\/li>\n<\/ul>\n<p>Your Verilog RTL file should be called <em>i2s_receive1.v<\/em> and contain a module called <em>i2s_receive1<\/em>. This module should have input ports called <em>sck<\/em>, <em>ws<\/em>, and <em>sd<\/em>, as well as two 32-bit output ports called <em>data_left<\/em> and <em>data_right<\/em>.<\/p>\n<h2>Next Steps<\/h2>\n<p>You can go to the next tutorial where we improve the I2S receiver <a href=\"https:\/\/www.beyond-circuits.com\/wordpress\/tutorial\/tutorial18\/part2\/\">here<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial, we will develop an I2S audio receiver. The goal of the tutorial is to really give you a chance to write your own RTL code. The tutorial will give you a simulation test bench which you will &hellip; <a href=\"https:\/\/www.beyond-circuits.com\/wordpress\/tutorial\/tutorial18\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"parent":226,"menu_order":18,"comment_status":"open","ping_status":"open","template":"","meta":{"footnotes":""},"class_list":["post-638","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/pages\/638","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=638"}],"version-history":[{"count":20,"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/pages\/638\/revisions"}],"predecessor-version":[{"id":1382,"href":"https:\/\/www.beyond-circuits.com\/wordpress\/wp-json\/wp\/v2\/pages\/638\/revisions\/1382"}],"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=638"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}