XCORE SDK
XCORE Software Development Kit
qspi_io.h
Go to the documentation of this file.
1 // Copyright 2020-2021 XMOS LIMITED.
2 // This Software is subject to the terms of the XMOS Public Licence: Version 1.
3 #pragma once
4 
9 #include <stdlib.h> /* for size_t */
10 #include <stdint.h>
11 #include <xclib.h> /* for byterev() */
12 #include <xcore/port.h>
13 #include <xcore/clock.h>
14 
25 typedef enum {
29 
33 typedef enum {
37 
41 typedef enum {
45 
51 typedef enum {
55 
77 #define QSPI_IO_BYTE_TO_MOSI(x) ( \
78 ((((x) >> 0) & 1) | 0xE) << (0 * 4) | \
79 ((((x) >> 1) & 1) | 0xE) << (1 * 4) | \
80 ((((x) >> 2) & 1) | 0xE) << (2 * 4) | \
81 ((((x) >> 3) & 1) | 0xE) << (3 * 4) | \
82 ((((x) >> 4) & 1) | 0xE) << (4 * 4) | \
83 ((((x) >> 5) & 1) | 0xE) << (5 * 4) | \
84 ((((x) >> 6) & 1) | 0xE) << (6 * 4) | \
85 ((((x) >> 7) & 1) | 0xE) << (7 * 4) )
86 
87 
93 typedef struct {
100  xclock_t clock_block;
101 
108  port_t cs_port;
109 
116  port_t sclk_port;
117 
124  port_t sio_port;
125 
138 
157 
168 
179 
189 
199 
209 
219 
220  /* The following are used internally and should not be set by the application */
221  size_t transaction_length;
222  uint32_t transaction_start;
223  uint32_t sample_delay;
224  uint32_t sample_edge;
225  uint32_t sio_pad_delay;
226  uint32_t sio_drive;
227 } qspi_io_ctx_t;
228 
240 __attribute__((always_inline))
241 inline uint32_t qspi_io_byte_to_mosi(uint32_t x)
242 {
243  uint32_t ones = 0xFFFFFFFF;
244  x |= 0xFFFFFF00;
245 
246  asm volatile (
247  "zip %0, %1, 0\n"
248  "zip %1, %0, 0\n"
249  "bitrev %0, %0\n"
250  :"+r"(x), "+r"(ones)
251  );
252 
253  return x;
254 }
255 
270 __attribute__((always_inline))
271 inline uint32_t qspi_io_miso_to_byte(uint32_t x)
272 {
273  uint32_t unzipped = 0;
274 
275  asm volatile (
276  "bitrev %1, %1\n"
277  "unzip %0, %1, 0\n"
278  "unzip %0, %1, 0\n"
279  : "+r"(unzipped), "+r"(x)
280  );
281 
282  return unzipped;
283 }
284 
293 __attribute__((always_inline))
294 inline uint32_t qspi_io_nibble_swap(uint32_t word)
295 {
296  uint32_t tmp;
297 
298  /* word = ((word & 0x0F0F0F0F) << 4) | ((word & 0xF0F0F0F0) >> 4) */
299  asm volatile (
300  "{and %0,%0,%2 ; and %1,%0,%3}\n"
301  "{shl %0,%0,4 ; shr %1,%1,4}\n"
302  : "+r"(word), "=r"(tmp)
303  : "r"(0x0F0F0F0F), "r"(0xF0F0F0F0)
304  );
305 
306  return word | tmp;
307 }
308 
309 /* The SETC constant for pad delay is missing from xs2a_user.h */
310 #define QSPI_IO_SETC_PAD_DELAY(n) (0x7007 | ((n) << 3))
311 
312 /* These appear to be missing from the public API of lib_xcore */
313 #define QSPI_IO_RESOURCE_SETCI(res, c) asm volatile( "setc res[%0], %1" :: "r" (res), "n" (c))
314 #define QSPI_IO_RESOURCE_SETC(res, r) asm volatile( "setc res[%0], %1" :: "r" (res), "r" (r))
315 #define QSPI_IO_SETSR(c) asm volatile("setsr %0" : : "n"(c));
316 #define QSPI_IO_CLRSR(c) asm volatile("clrsr %0" : : "n"(c));
317 
350 __attribute__((always_inline))
352  uint32_t first_word,
353  size_t len,
354  qspi_io_transaction_type_t transaction_type)
355 {
356  /* enable fast mode and high priority */
357  QSPI_IO_SETSR(XS1_SR_QUEUE_MASK | XS1_SR_FAST_MASK);
358 
359  ctx->transaction_length = len;
360 
361  port_set_master(ctx->sio_port);
362  port_set_no_ready(ctx->sio_port);
363 
364  if (transaction_type != qspi_io_full_speed) {
365  clock_set_divide(ctx->clock_block, ctx->spi_read_clk_divisor);
366  ctx->sample_delay = ctx->spi_read_sclk_sample_delay;
367  ctx->sample_edge = ctx->spi_read_sclk_sample_edge;
368  ctx->sio_pad_delay = QSPI_IO_SETC_PAD_DELAY(ctx->spi_read_sio_pad_delay);
369  ctx->sio_drive = XS1_SETC_DRIVE_PULL_UP; /* enable pullups during the read */
370  } else {
371  clock_set_divide(ctx->clock_block, ctx->full_speed_clk_divisor);
372  ctx->sample_delay = ctx->full_speed_sclk_sample_delay;
373  ctx->sample_edge = ctx->full_speed_sclk_sample_edge;
374  ctx->sio_pad_delay = QSPI_IO_SETC_PAD_DELAY(ctx->full_speed_sio_pad_delay);
375  ctx->sio_drive = XS1_SETC_DRIVE_DRIVE; /* disable pullups during the read */
376  }
377 
378  first_word = byterev(first_word);
379  first_word = qspi_io_nibble_swap(first_word);
380 
381  /* ensure pullups are disabled during output */
382  QSPI_IO_RESOURCE_SETCI(ctx->sio_port, XS1_SETC_DRIVE_DRIVE);
383 
384  port_out(ctx->sio_port, first_word);
385  port_out(ctx->cs_port, 1);
386  clock_start(ctx->clock_block);
387 }
388 
405 __attribute__((always_inline))
406 inline void qspi_io_bytes_out(const qspi_io_ctx_t *ctx,
407  const qspi_io_transfer_mode_t transfer_mode,
408  const uint8_t *data,
409  size_t len)
410 {
411  const uint32_t *data_words = (const uint32_t *) data;
412  size_t word_len = len / sizeof(uint32_t);
413  uint32_t word;
414  int i;
415 
416  /*
417  * Each iteration of this loop must execute within
418  * no more than eight SCLK cycles.
419  */
420  for (i = 0; i < word_len; i++) {
421  word = data_words[i];
422  if (transfer_mode == qspi_io_transfer_normal) {
423  word = qspi_io_nibble_swap(word);
424  }
425  port_out(ctx->sio_port, word);
426  }
427 
428  len &= sizeof(uint32_t) - 1; /* get the byte remainder */
429 
430  if (len) {
431  word = data_words[i];
432  len *= 8;
433 
434  if (transfer_mode == qspi_io_transfer_normal) {
435  word = qspi_io_nibble_swap(word);
436  }
437  port_set_shift_count(ctx->sio_port, len);
438  port_out(ctx->sio_port, word);
439  }
440 }
441 
456 __attribute__((always_inline))
457 inline void qspi_io_words_out(const qspi_io_ctx_t *ctx,
458  const qspi_io_transfer_mode_t transfer_mode,
459  const uint32_t *data,
460  size_t len)
461 {
462  uint32_t word;
463 
464  /*
465  * Each iteration of this loop must execute within
466  * no more than eight SCLK cycles.
467  */
468  for (int i = 0; i < len; i++) {
469  word = byterev(data[i]);
470  if (transfer_mode == qspi_io_transfer_normal) {
471  word = qspi_io_nibble_swap(word);
472  }
473  port_out(ctx->sio_port, word);
474  }
475 }
476 
490 __attribute__((always_inline))
491 inline void qspi_io_mosi_out(const qspi_io_ctx_t *ctx,
492  const qspi_io_transfer_mode_t transfer_mode,
493  const uint8_t *data,
494  size_t len)
495 {
496  uint32_t word;
497 
498  /*
499  * Each iteration of this loop must execute within
500  * no more than eight SCLK cycles.
501  */
502  for (int i = 0; i < len; i++) {
503  word = data[i];
504  if (transfer_mode != qspi_io_transfer_normal) {
505  word = qspi_io_nibble_swap(word);
506  }
507  word = qspi_io_byte_to_mosi(word);
508  port_out(ctx->sio_port, word);
509  }
510 }
511 
528 __attribute__((always_inline))
530 {
531  /*
532  * If CS has not yet been setup to deassert at the end of
533  * the transaction then do that now. It is happening here
534  * because there should still be time while waiting for the
535  * previous output to complete.
536  *
537  * This cannot be done at the end of a transaction after
538  * the input begins, as it will not be able to complete
539  * in time after the last IN instruction.
540  */
541  if (ctx->transaction_length != 0) {
542  uint32_t cs_start;
543  port_sync(ctx->cs_port);
544  cs_start = port_get_trigger_time(ctx->cs_port);
545  port_out_at_time(ctx->cs_port, cs_start + ctx->transaction_length, 0);
546  ctx->transaction_length = 0;
547  ctx->transaction_start = cs_start + ctx->sample_delay;
548  }
549 
550  if (ctx->sample_delay) {
551  /*
552  * Applying a 5 cycle pad delay to the CS pin when delaying the sample
553  * time by one SCLK cycle helps to ensure that CS is still seen as
554  * active at that time, which is necessary since CS is SIO's ready signal.
555  * This does only work at higher frequencies (~50+ MHz), but these are the
556  * frequencies that require sampling be delayed by a clock cycle.
557  */
558  QSPI_IO_RESOURCE_SETCI(ctx->cs_port, QSPI_IO_SETC_PAD_DELAY(5));
559  }
560 
561  /*
562  * This mess is to ensure that immediately following the sync,
563  * the port is put into buffered mode and the sample edge is
564  * set to falling if required as quickly as possible. Not sure
565  * at this time how to achieve this without using inline assembly.
566  * If the syncr+setc instructions are not grouped together in a
567  * single asm volatile() group, then they get merged, resulting
568  * in a single syncr instruction, followed by checking sample_edge,
569  * then executing the appropriate setc instructions.
570  */
571  if (ctx->sample_edge == qspi_io_sample_edge_falling) {
572  //port_sync(ctx->sio_port);
573  //port_reset(ctx->sio_port);
574  //port_set_sample_falling_edge(ctx->sio_port);
575  //port_set_buffered(ctx->sio_port);
576  asm volatile (
577  "syncr res[%0]\n"
578  "setc res[%0], %1\n"
579  "setc res[%0], %2\n"
580  "setc res[%0], %3\n"
581  : :
582  "r"(ctx->sio_port),
583  "n"(XS1_SETC_INUSE_ON),
584  "n"(XS1_SETC_SDELAY_SDELAY),
585  "n"(XS1_SETC_BUF_BUFFERS)
586  );
587  } else {
588  //port_sync(ctx->sio_port);
589  //port_reset(ctx->sio_port);
590  //port_set_buffered(ctx->sio_port);
591  asm volatile (
592  "syncr res[%0]\n"
593  "setc res[%0], %1\n"
594  "setc res[%0], %2\n"
595  : :
596  "r"(ctx->sio_port),
597  "n"(XS1_SETC_INUSE_ON),
598  "n"(XS1_SETC_BUF_BUFFERS)
599  );
600  }
601 
602  QSPI_IO_RESOURCE_SETC(ctx->sio_port, ctx->sio_pad_delay);
603  QSPI_IO_RESOURCE_SETC(ctx->sio_port, ctx->sio_drive);
604  port_set_transfer_width(ctx->sio_port, 32);
605  port_set_ready_strobed(ctx->sio_port);
606  port_set_slave(ctx->sio_port);
607 }
608 
638 __attribute__((always_inline))
639 inline void qspi_io_bytes_in(const qspi_io_ctx_t *ctx,
640  const qspi_io_transfer_mode_t transfer_mode,
641  uint8_t *data,
642  uint32_t start_time,
643  size_t len)
644 {
645  uint32_t *data_words = (uint32_t *) data;
646  size_t word_len = len / sizeof(uint32_t);
647  uint32_t word;
648  int i;
649 
650  port_set_trigger_time(ctx->sio_port, ctx->transaction_start + start_time);
651 
652  /*
653  * Each iteration of this loop must execute within
654  * no more than eight SCLK cycles.
655  */
656  for (i = 0; i < word_len; i++) {
657  word = port_in(ctx->sio_port);
658  if (transfer_mode == qspi_io_transfer_normal) {
659  word = qspi_io_nibble_swap(word);
660  }
661  data_words[i] = word;
662  }
663 
664  /*
665  * Note: Some of the following code, including the final IN
666  * instruction, may execute well after the data has already
667  * shifted in. This is ok provided this is the end of the
668  * transaction. Also note that the SETPSC and IN instructions
669  * executing after the data has already shifted in only works
670  * correctly when the port is in strobed slave mode and the ready
671  * signal is deasserted immediately following the last byte. This
672  * ensures that the data stops shifting in at the end of the
673  * transaction and that the number of bits shifted in after the last
674  * full word matches the number of remaining bytes to be read in.
675  *
676  * None of this not applies if len is a multiple of four.
677  */
678 
679  len &= sizeof(uint32_t) - 1; /* get the byte remainder */
680 
681  if (len) {
682  if (word_len > 0) {
683  /*
684  * There appears to be a problem (bug?) where if there is
685  * a port time set on a port, and then a port shift
686  * count is set within one or two cycles prior to this
687  * port time, then the subsequent IN instruction hangs
688  * indefinitely. If only either a port time or a port
689  * shift count is used, there appears to be no problem.
690  * So we only set the port shift count here if there
691  * is not an active port time.
692  */
693  port_set_shift_count(ctx->sio_port, len * 8);
694  }
695 
696  word = port_in(ctx->sio_port);
697  word >>= 8 * (4 - len);
698 
699  data = (uint8_t *) &data_words[i];
700  if (transfer_mode == qspi_io_transfer_normal) {
701  word = qspi_io_nibble_swap(word);
702  }
703 
704  for (i = 0; i < len; i++) {
705  *data++ = word & 0xFF;
706  word >>= 8;
707  }
708  }
709 }
710 
728 __attribute__((always_inline))
729 inline void qspi_io_words_in(const qspi_io_ctx_t *ctx,
730  const qspi_io_transfer_mode_t transfer_mode,
731  uint32_t *data,
732  uint32_t start_time,
733  size_t len)
734 {
735  uint32_t word;
736 
737  port_set_trigger_time(ctx->sio_port, ctx->transaction_start + start_time);
738 
739  /*
740  * Each iteration of this loop must execute within
741  * no more than eight SCLK cycles.
742  */
743  for (int i = 0; i < len; i++) {
744  word = port_in(ctx->sio_port);
745  if (transfer_mode == qspi_io_transfer_normal) {
746  word = qspi_io_nibble_swap(word);
747  }
748  data[i] = byterev(word);
749  }
750 }
751 
768 __attribute__((always_inline))
769 inline void qspi_io_miso_in(const qspi_io_ctx_t *ctx,
770  const qspi_io_transfer_mode_t transfer_mode,
771  uint8_t *data,
772  uint32_t start_time,
773  size_t len)
774 {
775  uint32_t word;
776 
777  port_set_trigger_time(ctx->sio_port, ctx->transaction_start + start_time);
778 
779  /*
780  * Each iteration of this loop must execute within
781  * no more than eight SCLK cycles.
782  */
783  for (int i = 0; i < len; i++) {
784  word = qspi_io_miso_to_byte(port_in(ctx->sio_port));
785  if (transfer_mode != qspi_io_transfer_normal) {
786  word = qspi_io_nibble_swap(word);
787  }
788  data[i] = word;
789  }
790 }
791 
808 __attribute__((always_inline))
809 inline void qspi_io_miso_poll(const qspi_io_ctx_t *ctx,
810  const uint8_t mask,
811  const uint8_t val,
812  uint32_t start_time)
813 {
814  uint32_t word;
815 
816  port_set_trigger_time(ctx->sio_port, ctx->transaction_start + start_time);
817 
818  /*
819  * Each iteration of this loop must execute within
820  * no more than eight SCLK cycles.
821  */
822  while (1) {
823  word = qspi_io_miso_to_byte(port_in(ctx->sio_port));
824 
825  if ((word & mask) == val) {
826  break;
827  }
828 
829  port_clear_buffer(ctx->cs_port);
830  port_out_at_time(ctx->cs_port, port_get_trigger_time(ctx->cs_port) + 8, 0);
831  }
832 }
833 
841 __attribute__((always_inline))
842 inline void qspi_io_end_transaction(const qspi_io_ctx_t *ctx)
843 {
844  /*
845  * If the transaction included input, then CS should already
846  * have been setup to deassert at the end. If not, then do it
847  * now as there should still be time while waiting for the
848  * previous output to complete. A sync on CS is necessary in
849  * case this is immediately following a call to
850  * qspi_io_start_transaction().
851  */
852  if (ctx->transaction_length != 0) {
853  uint32_t cs_start;
854  port_sync(ctx->cs_port);
855  cs_start = port_get_trigger_time(ctx->cs_port);
856  port_out_at_time(ctx->cs_port, cs_start + ctx->transaction_length, 0);
857  }
858 
859  port_sync(ctx->cs_port);
860  clock_stop(ctx->clock_block);
861 
862  /*
863  * Ensure the SIO port is back to being sampled on the falling
864  * edge with no pad delay, and that the CS port has no pad delay.
865  */
866  QSPI_IO_RESOURCE_SETCI(ctx->sio_port, QSPI_IO_SETC_PAD_DELAY(0));
867  QSPI_IO_RESOURCE_SETCI(ctx->cs_port, QSPI_IO_SETC_PAD_DELAY(0));
868  port_set_sample_falling_edge(ctx->sio_port);
869 
870  /*
871  * Also enable pull-ups on the SIO lines between transactions. If the
872  * transaction ended with an input, then SIO is still in input mode so
873  * this prevents them from floating.
874  */
875  QSPI_IO_RESOURCE_SETCI(ctx->sio_port, XS1_SETC_DRIVE_PULL_UP);
876 
877  /* disable fast mode and high priority */
878  QSPI_IO_CLRSR(XS1_SR_QUEUE_MASK | XS1_SR_FAST_MASK);
879 }
880 
888 void qspi_io_deinit(const qspi_io_ctx_t *ctx);
889 
901 void qspi_io_init(const qspi_io_ctx_t *ctx,
902  qspi_io_source_clock_t source_clock);
903  // END: addtogroup hil_qspi_io
void qspi_io_init(const qspi_io_ctx_t *ctx, qspi_io_source_clock_t source_clock)
void qspi_io_end_transaction(const qspi_io_ctx_t *ctx)
Definition: qspi_io.h:842
void qspi_io_bytes_out(const qspi_io_ctx_t *ctx, const qspi_io_transfer_mode_t transfer_mode, const uint8_t *data, size_t len)
Definition: qspi_io.h:406
void qspi_io_words_out(const qspi_io_ctx_t *ctx, const qspi_io_transfer_mode_t transfer_mode, const uint32_t *data, size_t len)
Definition: qspi_io.h:457
void qspi_io_miso_in(const qspi_io_ctx_t *ctx, const qspi_io_transfer_mode_t transfer_mode, uint8_t *data, uint32_t start_time, size_t len)
Definition: qspi_io.h:769
qspi_io_transfer_mode_t
Definition: qspi_io.h:41
void qspi_io_miso_poll(const qspi_io_ctx_t *ctx, const uint8_t mask, const uint8_t val, uint32_t start_time)
Definition: qspi_io.h:809
void qspi_io_mosi_out(const qspi_io_ctx_t *ctx, const qspi_io_transfer_mode_t transfer_mode, const uint8_t *data, size_t len)
Definition: qspi_io.h:491
uint32_t qspi_io_nibble_swap(uint32_t word)
Definition: qspi_io.h:294
void qspi_io_start_transaction(qspi_io_ctx_t *ctx, uint32_t first_word, size_t len, qspi_io_transaction_type_t transaction_type)
Definition: qspi_io.h:351
qspi_io_source_clock_t
Definition: qspi_io.h:33
void qspi_io_bytes_in(const qspi_io_ctx_t *ctx, const qspi_io_transfer_mode_t transfer_mode, uint8_t *data, uint32_t start_time, size_t len)
Definition: qspi_io.h:639
qspi_io_transaction_type_t
Definition: qspi_io.h:51
uint32_t qspi_io_byte_to_mosi(uint32_t x)
Definition: qspi_io.h:241
qspi_io_sample_edge_t
Definition: qspi_io.h:25
uint32_t qspi_io_miso_to_byte(uint32_t x)
Definition: qspi_io.h:271
void qspi_io_sio_direction_input(qspi_io_ctx_t *ctx)
Definition: qspi_io.h:529
void qspi_io_deinit(const qspi_io_ctx_t *ctx)
void qspi_io_words_in(const qspi_io_ctx_t *ctx, const qspi_io_transfer_mode_t transfer_mode, uint32_t *data, uint32_t start_time, size_t len)
Definition: qspi_io.h:729
@ qspi_io_transfer_normal
Definition: qspi_io.h:42
@ qspi_io_transfer_nibble_swap
Definition: qspi_io.h:43
@ qspi_io_source_clock_ref
Definition: qspi_io.h:34
@ qspi_io_source_clock_xcore
Definition: qspi_io.h:35
@ qspi_io_full_speed
Definition: qspi_io.h:52
@ qspi_io_spi_read
Definition: qspi_io.h:53
@ qspi_io_sample_edge_falling
Definition: qspi_io.h:27
@ qspi_io_sample_edge_rising
Definition: qspi_io.h:26
Definition: qspi_io.h:93
int spi_read_clk_divisor
Definition: qspi_io.h:156
qspi_io_sample_edge_t full_speed_sclk_sample_edge
Definition: qspi_io.h:188
port_t sio_port
Definition: qspi_io.h:124
uint32_t full_speed_sio_pad_delay
Definition: qspi_io.h:208
qspi_io_sample_edge_t spi_read_sclk_sample_edge
Definition: qspi_io.h:198
port_t sclk_port
Definition: qspi_io.h:116
port_t cs_port
Definition: qspi_io.h:108
uint32_t spi_read_sclk_sample_delay
Definition: qspi_io.h:178
int full_speed_clk_divisor
Definition: qspi_io.h:137
xclock_t clock_block
Definition: qspi_io.h:100
uint32_t spi_read_sio_pad_delay
Definition: qspi_io.h:218
uint32_t full_speed_sclk_sample_delay
Definition: qspi_io.h:167