{"id":4692,"date":"2023-01-08T17:02:53","date_gmt":"2023-01-08T23:02:53","guid":{"rendered":"https:\/\/dev.iachieved.it\/iachievedit\/?p=4692"},"modified":"2023-01-09T02:12:51","modified_gmt":"2023-01-09T08:12:51","slug":"hifive1-rev-b-zephyr-and-spi","status":"publish","type":"post","link":"https:\/\/dev.iachieved.it\/iachievedit\/hifive1-rev-b-zephyr-and-spi\/","title":{"rendered":"HiFive1 Rev B, Zephyr, and SPI"},"content":{"rendered":"<p>A few years back I purchased a SiFive <a href=\"https:\/\/www.sifive.com\/boards\/hifive1-rev-b\">HiFive1 Rev B<\/a> board to join in the RISC-V <a href=\"https:\/\/thenewstack.io\/risc-v-the-next-revolution-in-the-open-hardware-movement\/\">revolution<\/a>.<\/p>\n<p>In this post we&#8217;ll look at using the HiFive to communicate with a Microchip <a href=\"https:\/\/ww1.microchip.com\/downloads\/en\/DeviceDoc\/20005155B.pdf\">23LC512 SRAM<\/a> via SPI.  I&#8217;m fond of these chips because they support up to 5V and are easy to communicate with.  Moreover, writing data and reading it back is a nice way of confirming your communications are working!<\/p>\n<h2>Zephyr<\/h2>\n<p><a href=\"https:\/\/zephyrproject.org\">Zephyr<\/a> is a scalable real-time operating system with permissive licensing (Apache).  It is supported on the HiFive1, alongside <a href=\"https:\/\/sifive.github.io\/freedom-metal-docs\/\">Freedom Metal<\/a> and <a href=\"https:\/\/www.freertos.org\">FreeRTOS<\/a>.  Sure, we could have chosen to use Freedom Metal or FreeRTOS, but Zephyr provides a solid foundation upon which to build RTOS applications on a RISC-V platform.<\/p>\n<h2>Wiring it Up<\/h2>\n<p>SiFive built the HiFive1 Rev B to be &#8220;pin compatible&#8221; with the Arduino, and markets it as such.  If you&#8217;ve used SPI on a board like the Arduino Uno you&#8217;ll recognize the following SPI pin assignments:<\/p>\n<ul>\n<li>CS &#8211; Pin 10<\/li>\n<li>SO &#8211; Pin 11<\/li>\n<li>SI &#8211; Pin 12<\/li>\n<li>CLK &#8211; Pin 13<\/li>\n<\/ul>\n<p>The HiFive1 <a href=\"https:\/\/sifive.cdn.prismic.io\/sifive%2Fa4546ced-0922-4d87-9334-e97c1a9fd9a5_hifive1.b01.schematics.pdf\">schematics<\/a> shows that it uses these same pins for the SPI1 controller.<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/HiFive1RevB_SPI_Pins.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/HiFive1RevB_SPI_Pins.png\" alt=\"\" width=\"295\" height=\"177\" class=\"aligncenter size-full wp-image-4697\" \/><\/a><\/p>\n<h2>PlatformIO and Zephyr<\/h2>\n<p>We&#8217;ve used <a href=\"https:\/\/platformio.org\/\">PlatformIO<\/a> with Visual Studio Code in the past, and will use it here as well.  Create a new project with PlatformIO:<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/platformIONewProject.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/platformIONewProject.png\" alt=\"\" width=\"917\" height=\"292\" class=\"aligncenter size-full wp-image-4726\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/platformIONewProject.png 917w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/platformIONewProject-300x96.png 300w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/platformIONewProject-768x245.png 768w\" sizes=\"(max-width: 917px) 100vw, 917px\" \/><\/a><\/p>\n<p>For our board, we&#8217;ll choose the <strong>HiFive1 Rev B (SiFive)<\/strong> and for the Framework choose <strong>Zephyr RTOS<\/strong>.<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/chooseZephyr.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/chooseZephyr.png\" alt=\"\" width=\"600\" height=\"485\" class=\"aligncenter size-full wp-image-4728\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/chooseZephyr.png 600w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/chooseZephyr-300x243.png 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/a><\/p>\n<p>First, some basic code to ensure we can flash the HiFive1 Rev B and boot into Zephyr.<\/p>\n<pre class=\"theme:vs2012-black lang:default decode:true \" >#include &lt;zephyr.h&gt;\n#include &lt;stdio.h&gt;\n\nvoid main(void) {\n\n  printf(\"Hello, world!\\n\");\n\n}<\/pre>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/helloWorldZephyr.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/helloWorldZephyr.png\" alt=\"\" width=\"366\" height=\"144\" class=\"aligncenter size-full wp-image-4702\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/helloWorldZephyr.png 366w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/helloWorldZephyr-300x118.png 300w\" sizes=\"(max-width: 366px) 100vw, 366px\" \/><\/a><\/p>\n<p>Now, to the fun stuff.<\/p>\n<p>Like Linux, Zephyr uses the <a href=\"https:\/\/docs.zephyrproject.org\/3.2.0\/build\/dts\/index.html\">device tree<\/a> to describe hardware peripherals.  Unlike Linux, obtaining a handle to a device uses a quite sophisticated set of preprocessor macros.  I highly recommend <a href=\"https:\/\/www.youtube.com\/watch?v=w8GgP3h0M8M\">this presentation<\/a> for an explanation on how the macros work.<\/p>\n<p>Armed with this knowledge, let&#8217;s get a SPI device structure.<\/p>\n<pre class=\"theme:vs2012-black lang:default decode:true \" >#include &lt;zephyr.h&gt;\n#include &lt;stdio.h&gt;\n\n#include &lt;device.h&gt;\n#include &lt;devicetree.h&gt;\n#include &lt;drivers\/spi.h&gt;\n\nconst struct device* spi_device;\nstruct spi_config spi_device_config;\n\nint main(void) {\n\n  spi_device = DEVICE_DT_GET(DT_NODELABEL(spi1));\n\n  if (!spi_device) {\n    printf(\"Unable to obtain SPI device\\n\");\n    return -1;\n  }\n\n  return 0;\n\n}\n<\/pre>\n<p>Unfortunately if you tried to compile this as-is you will likely end up with a linker error saying something like <code>undefined reference to __device_dts_ord_30<\/code>.<\/p>\n<p>To fix this error we need to enable SPI support in a file called <code>prj.conf<\/code>.  This file is merged together with Zephy&#8217;s KConfig.  In your PlatformIO project folder, <code>prj.conf<\/code> should go in the <code>zephyr\/<\/code> directory.<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/prjConf.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/prjConf.png\" alt=\"\" width=\"320\" height=\"75\" class=\"aligncenter size-full wp-image-4705\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/prjConf.png 320w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/prjConf-300x70.png 300w\" sizes=\"(max-width: 320px) 100vw, 320px\" \/><\/a><\/p>\n<p>Compile again and upload the firmware to the HiFive1 and you <em>shouldn&#8217;t<\/em> see <code>Unable to obtain SPI device<\/code> because you&#8217;ll have a valid handle.  Let&#8217;s continue and build up our SPI configuration.<\/p>\n<p>[code lang=text]<br \/>\nspi_device_config.frequency = SPI_FREQUENCY; \/\/ 20000000 (20MHz)<br \/>\nspi_device_config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8);<br \/>\n[\/code]<\/p>\n<p>With that completed, the tricky part!  We&#8217;re going to write the function that allows us to write a set of sequential bytes to the SRAM.  To do so requires an understanding of the SPI protocol specific to the 23LC512 chip, and to gain that understanding look at the datasheet.  After doing so, you&#8217;ll know to write a stream of bytes we first transmit the Write command (0x02), followed by a 16-bit address, followed by as many bytes as we want to write.  All the while the chip select line needs to be held low.  Again, this is made clear by the datasheet:<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/sequentialWrite.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/sequentialWrite.png\" alt=\"\" width=\"640\" height=\"336\" class=\"aligncenter size-full wp-image-4709\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/sequentialWrite.png 640w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/sequentialWrite-300x158.png 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<p>Let&#8217;s accomplish this in C with our Zephyr SPI API.  First, the code, and then the explanation.<\/p>\n<pre class=\"theme:vs2012-black lang:default decode:true \" >\n#define WRITE_INS 0x02\nvoid sram_write(uint16_t address, uint8_t* data, uint8_t len) {\n\n  uint8_t addr_h = address &gt;&gt; 8;\n  uint8_t addr_l = address &amp; 0xff;\n  uint8_t writeInst[] = {WRITE_INS, addr_h, addr_l};\n\n  struct spi_buf tx_buffers[] = {\n    {\n      .buf = (void*)writeInst,\n      .len = 3\n    },\n    {\n      .buf = data,\n      .len = len\n    }\n  };\n\n  struct spi_buf_set tx_buffer_set = {\n    .buffers = tx_buffers,\n    .count = 2\n  };\n\n  spi_write(spi_device, &amp;spi_device_config, &amp;tx_buffer_set);\n\n}<\/pre>\n<p>Four C structures are required to write a SPI transaction with Zephyr:<\/p>\n<ul>\n<li><code>struct device<\/code><\/li>\n<li><code>struct spi_config<\/code><\/li>\n<li><code>struct spi_buf<\/code><\/li>\n<li><code>struct spi_buf_set<\/code><\/li>\n<\/ul>\n<p>Our first structure, <code>device<\/code>, is the SPI device itself.  The second, <code>spi_config<\/code>, holds configuration data specific to our SPI transaction.  For example, what frequency are we using, is it MSB-first or LSB-first, etc.<\/p>\n<p>The third and fourth structures package up the data we&#8217;re going to send.  It&#8217;s worth reviewing this a couple of times.  The complete SPI transaction will be in our <em>buffer set<\/em>.  That buffer set is a list of buffers we want to write.<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/spiBufs.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/spiBufs.png\" alt=\"\" width=\"575\" height=\"233\" class=\"aligncenter size-full wp-image-4711\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/spiBufs.png 575w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/spiBufs-300x122.png 300w\" sizes=\"(max-width: 575px) 100vw, 575px\" \/><\/a><\/p>\n<p>Once we&#8217;ve constructed our buffers and buffer set, we can write our data out in a single transaction with <code>spi_write<\/code>.<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/writeInstruction2.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/writeInstruction2.png\" alt=\"\" width=\"1144\" height=\"389\" class=\"aligncenter size-full wp-image-4717\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/writeInstruction2.png 1144w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/writeInstruction2-300x102.png 300w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/writeInstruction2-1024x348.png 1024w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/writeInstruction2-768x261.png 768w\" sizes=\"(max-width: 1144px) 100vw, 1144px\" \/><\/a><\/p>\n<p>If you&#8217;re familiar with SPI you know that when you write data to SI, you&#8217;ll get data back on SO.  This will be important when we attempt to read back in what we&#8217;ve written.<\/p>\n<h2>Generating Random Data<\/h2>\n<p>To test and gain confidence that our write and read functions are working properly we&#8217;ll generate random data to write to the SRAM.  To do so we need to enable a test random number generator.  In your <code>prj.conf<\/code> file add <code>CONFIG_TEST_RANDOM_GENERATOR=y<\/code>, which will allow us to write code like this:<\/p>\n<p>[code lang=text]<br \/>\n#include &lt;random\/rand32.h&gt;<\/p>\n<p>uint32_t r = sys_rand32_get();<br \/>\n[\/code]<\/p>\n<h2>Reading from the SRAM<\/h2>\n<p>Reading from the SRAM requires a bit more complexity in our <code>spi_buf<\/code> and <code>spi_buf_set<\/code> structures.  First, the code, and then the explanation (of why it won&#8217;t work!).<\/p>\n<pre class=\"theme:vs2012-black lang:default decode:true \" >\n#define READ_INS 0x03\nvoid sram_read(uint16_t address, uint8_t* data, uint8_t len) {\n\n  uint8_t addr_h = address >> 8;\n  uint8_t addr_l = address & 0xff;\n  uint8_t readInst[] = {READ_INS, addr_h, addr_l};\n\n  struct spi_buf tx_buffer = {\n    .buf = (void*)readInst,\n    .len = 3\n  };\n    \n  struct spi_buf rx_buffer= {\n    .buf = (void*)data,\n    .len = len\n  };\n\n  const struct spi_buf_set tx_buf_set = {\n    .buffers = (const struct spi_buf*)&tx_buffer,\n    .count   = 1\n  };\n\n  const struct spi_buf_set rx_buf_set = {\n    .buffers = (const struct spi_buf*)&rx_buffer,\n    .count   = 1\n  };\n\n  spi_transceive(spi_device, &spi_device_config, &tx_buf_set, &rx_buf_set);\n\n}\n<\/pre>\n<p>While this looks like it would work, and our logic analyzer is able to interpret the data, here&#8217;s what we see in the console:<\/p>\n<p>[code lang=text]<br \/>\nWrite 0x07c4:  d1 d4 d7 da dd e1 e4 e7 ea ed f0 f3 f7 fa fd 00<br \/>\nRead  0x07c4:  00 00 00 d1 d4 d7 da dd e1 e4 e7 ea ed f0 f3 f7 <\/p>\n<p>Write 0x959d:  a0 a3 a6 aa ad b0 b3 b6 b9 bc c0 c3 c6 c9 cc cf<br \/>\nRead  0x959d:  00 00 00 a0 a3 a6 aa ad b0 b3 b6 b9 bc c0 c3 c6<br \/>\n[\/code]<\/p>\n<p>The first three bytes of our read buffer is zeros, and the last three bytes that were written are missing.  Recall once again that SPI transactions are &#8220;write-a-byte-read-a-byte&#8221;.  To read a byte we must transmit a byte.  In the code above we have a mismatch of transmitting 3 bytes (thereby reading three bytes into our read buffer).  To resolve this we&#8217;ll ensure that our transmit and receive buffers are of the same size.<\/p>\n<pre class=\"theme:vs2012-black lang:default decode:true \" >\n#define READ_INS 0x03\nvoid sram_read(uint16_t address, uint8_t* data, uint8_t len) {\n\n  uint8_t addr_h = address >> 8;\n  uint8_t addr_l = address & 0xff;\n  uint8_t readInst[] = {READ_INS, addr_h, addr_l};\n\n  struct spi_buf tx_buffers[] =  {{\n    .buf = readInst,\n    .len = 3} ,{\n    .buf = NULL,\n    .len = len\n  }};\n    \n  struct spi_buf rx_buffers[] = {{\n    .buf = NULL,\n    .len = 3}, {\n    .buf = (void*)data,\n    .len = len\n  }};\n\n  const struct spi_buf_set tx_buf_set = {\n    .buffers = (const struct spi_buf*)&tx_buffers,\n    .count   = 2\n  };\n\n  const struct spi_buf_set rx_buf_set = {\n    .buffers = (const struct spi_buf*)&rx_buffers,\n    .count   = 2\n  };\n\n  spi_transceive(spi_device, &spi_device_config, &tx_buf_set, &rx_buf_set);\n\n}\n<\/pre>\n<p>We now see:<\/p>\n<p>[code lang=text]<br \/>\nWrite 0xb44e:  51 54 57 5b 5e 61 64 67 6a 6d 71 74 77 7a 7d 80<br \/>\nRead  0xb44e:  51 54 57 5b 5e 61 64 67 6a 6d 71 74 77 7a 7d 80<br \/>\nWrite 0x3883:  86 89 8c 90 93 96 99 9c 9f a2 a5 a9 ac af b2 b5<br \/>\nRead  0x3883:  86 89 8c 90 93 96 99 9c 9f a2 a5 a9 ac af b2 b5<br \/>\n[\/code]<\/p>\n<p>So, what&#8217;s going on here?  Notice that our transmit buffers and receive buffers are now the same size.  <em>Three<\/em> bytes are transmitted while <em>three<\/em> bytes are read in.  It just so happens that the three bytes read in will be discarded, but we then transmit out <em>len<\/em> bytes (of zeros), while reading in <em>len<\/em> bytes of data from the SI line.<\/p>\n<p>An example of what we see with a logic analyzer:<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/readSequence.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/readSequence.png\" alt=\"\" width=\"1200\" height=\"345\" class=\"aligncenter size-full wp-image-4735\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/readSequence.png 1200w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/readSequence-300x86.png 300w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/readSequence-1024x294.png 1024w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/readSequence-768x221.png 768w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/a><\/p>\n<h2>The Code<\/h2>\n<p>Now, for the complete code:<\/p>\n<pre class=\"theme:vs2012-black lang:default decode:true \" >#include &lt;zephyr.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;string.h&gt;\n#include &lt;device.h&gt;\n#include &lt;devicetree.h&gt;\n#include &lt;drivers\/spi.h&gt;\n#include &lt;random\/rand32.h&gt;\n\n#define SPI_FREQUENCY 20000000 \/\/ 20 MHz\n\nconst struct device* spi_device;\nstruct spi_config spi_device_config;\n\n#define WRITE_INS 0x02\nvoid sram_write(uint16_t address, uint8_t* data, uint8_t len) {\n\n  uint8_t addr_h = address &gt;&gt; 8;\n  uint8_t addr_l = address &amp; 0xff;\n  uint8_t writeInst[] = {WRITE_INS, addr_h, addr_l};\n\n  struct spi_buf tx_buffers[] = {\n    {\n      .buf = (void*)writeInst,\n      .len = 3\n    },\n    {\n      .buf = data,\n      .len = len\n    }\n  };\n\n  struct spi_buf_set tx_buffer_set = {\n    .buffers = tx_buffers,\n    .count = 2\n  };\n\n  spi_write(spi_device, &amp;spi_device_config, &amp;tx_buffer_set);\n\n}\n\n#define READ_INS 0x03\nvoid sram_read(uint16_t address, uint8_t* data, uint8_t len) {\n\n  uint8_t addr_h = address &gt;&gt; 8;\n  uint8_t addr_l = address &amp; 0xff;\n  uint8_t readInst[] = {READ_INS, addr_h, addr_l};\n\n  struct spi_buf tx_buffers[] =  {{\n    .buf = readInst,\n    .len = 3} ,{\n    .buf = NULL,\n    .len = len\n  }};\n    \n  struct spi_buf rx_buffers[] = {{\n    .buf = NULL,\n    .len = 3}, {\n    .buf = (void*)data,\n    .len = len\n  }};\n\n  const struct spi_buf_set tx_buf_set = {\n    .buffers = (const struct spi_buf*)&amp;tx_buffers,\n    .count   = 2\n  };\n\n  const struct spi_buf_set rx_buf_set = {\n    .buffers = (const struct spi_buf*)&amp;rx_buffers,\n    .count   = 2\n  };\n\n  spi_transceive(spi_device, &amp;spi_device_config, &amp;tx_buf_set, &amp;rx_buf_set);\n\n}\n\n#define BUFLEN 32\nint main(void) {\n\n  spi_device = DEVICE_DT_GET(DT_NODELABEL(spi1));\n\n  if (!spi_device) {\n    printf(\"Unable to obtain SPI device\\n\");\n    return -1;\n  }\n\n  spi_device_config.frequency = SPI_FREQUENCY;\n  spi_device_config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8);\n  \n  uint8_t tx_buf[BUFLEN];\n  uint8_t rx_buf[BUFLEN];\n\n  for (;;) {\n\n    uint32_t addr = sys_rand32_get() &amp; 0xffff;\n    for (int i = 0; i &lt; BUFLEN; i++) {\n      tx_buf[i] = sys_rand32_get();\n      rx_buf[i] = 0x00;\n    }\n\n    sram_write(addr, tx_buf, BUFLEN);\n    k_msleep(1000);\n\n    sram_read(addr, rx_buf, BUFLEN);\n\n    printf(\"Write 0x%04x:  \", addr);\n    for (int i = 0; i &lt; BUFLEN; i++) {\n      printf(\"%02x \", tx_buf[i]);\n    } printf(\"\\n\");\n    printf(\"Read  0x%04x:  \", addr);\n    for (int i = 0; i &lt; BUFLEN; i++) {\n      printf(\"%02x \", rx_buf[i]);\n    } printf(\"\\n\");\n\n    k_msleep(1000);\n  }\n\n  return 0;\n\n}\n<\/pre>\n<h2>Until Next Time<\/h2>\n<p>If you happen to end up using the 23LC512 SRAM and have a Salaea logic analyzer, check out our 23LC512 High Level Analyzer!  It&#8217;s available in the <strong>Extensions<\/strong> of the Logic 2 software:<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/saleaeMarketplace.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/saleaeMarketplace.png\" alt=\"\" width=\"1200\" height=\"678\" class=\"aligncenter size-full wp-image-4732\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/saleaeMarketplace.png 1200w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/saleaeMarketplace-300x170.png 300w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/saleaeMarketplace-1024x579.png 1024w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/saleaeMarketplace-768x434.png 768w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/a><\/p>\n<p>You can also find the source code for it on <a href=\"https:\/\/github.com\/iachievedit\/23LC512_SPI_Analyzer\">GitHub<\/a>.<\/p>\n<p>One thing I glossed over was the actual speed at which the SPI bus is running with the HiFive1 and Zephyr.  Zooming in with the logic analyzer you can see that the clock frequency is definitely <em>not<\/em> 20 MHz, and is magnitudes of order slower.<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/clockCyle.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/clockCyle.png\" alt=\"\" width=\"427\" height=\"412\" class=\"aligncenter size-full wp-image-4740\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/clockCyle.png 427w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/clockCyle-300x289.png 300w\" sizes=\"(max-width: 427px) 100vw, 427px\" \/><\/a><\/p>\n<p>Reading a scant 16 bytes took 78 milliseconds.<\/p>\n<p>Well, we can &#8220;fix that&#8221; by dropping our SPI frequency down to 100 kHZ.  Yes.<\/p>\n<p>[code lang=text]<br \/>\n#define SPI_FREQUENCY 100000<br \/>\n[\/code]<\/p>\n<p>Observing again a 16-byte transaction:<\/p>\n<p><a href=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/twoTwoMS.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/twoTwoMS.png\" alt=\"\" width=\"1131\" height=\"368\" class=\"aligncenter size-full wp-image-4741\" srcset=\"https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/twoTwoMS.png 1131w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/twoTwoMS-300x98.png 300w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/twoTwoMS-1024x333.png 1024w, https:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2023\/01\/twoTwoMS-768x250.png 768w\" sizes=\"(max-width: 1131px) 100vw, 1131px\" \/><\/a><\/p>\n<p>Ostensibly this slower SPI bus speed is due to the fact that the FE310 chip lacks DMA (directory memory access).  I&#8217;m not certain at this point how to compensate for that to transmit data at faster rates to the SRAM, or if it is possible at all.  Leave a comment if you have an idea!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A few years back I purchased a SiFive HiFive1 Rev B board to join in the RISC-V revolution. In this post we&#8217;ll look at using the HiFive to communicate with a Microchip 23LC512 SRAM via SPI. I&#8217;m fond of these chips because they support up to 5V and are easy to communicate with. Moreover, writing [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":4711,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22,102,101],"tags":[],"class_list":["post-4692","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hacking","category-hifive","category-risc-v"],"_links":{"self":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/4692"}],"collection":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/comments?post=4692"}],"version-history":[{"count":40,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/4692\/revisions"}],"predecessor-version":[{"id":4743,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/4692\/revisions\/4743"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media\/4711"}],"wp:attachment":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media?parent=4692"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/categories?post=4692"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/tags?post=4692"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}