forked from BelaPlatform/Bela
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpru_rtaudio.p
1165 lines (1030 loc) · 42.9 KB
/
pru_rtaudio.p
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
.origin 0
.entrypoint START
#define DBOX_CAPE // Define this to use new cape hardware
#define CLOCK_BASE 0x44E00000
#define CLOCK_SPI0 0x4C
#define CLOCK_SPI1 0x50
#define CLOCK_L4LS 0x60
#define SPI0_BASE 0x48030100
#define SPI1_BASE 0x481A0100
#define SPI_BASE SPI0_BASE
#define SPI_SYSCONFIG 0x10
#define SPI_SYSSTATUS 0x14
#define SPI_MODULCTRL 0x28
#define SPI_CH0CONF 0x2C
#define SPI_CH0STAT 0x30
#define SPI_CH0CTRL 0x34
#define SPI_CH0TX 0x38
#define SPI_CH0RX 0x3C
#define SPI_CH1CONF 0x40
#define SPI_CH1STAT 0x44
#define SPI_CH1CTRL 0x48
#define SPI_CH1TX 0x4C
#define SPI_CH1RX 0x50
#define GPIO0 0x44E07000
#define GPIO1 0x4804C000
#define GPIO_CLEARDATAOUT 0x190
#define GPIO_SETDATAOUT 0x194
#define PRU0_ARM_INTERRUPT 19 // Interrupt signalling we're done
#define PRU1_ARM_INTERRUPT 20 // Interrupt signalling a block is ready
#define C_ADC_DAC_MEM C24 // PRU0 mem
#ifdef DBOX_CAPE
#define DAC_GPIO GPIO0
#define DAC_CS_PIN (1<<5) // GPIO0:5 = P9 pin 17
#else
#define DAC_GPIO GPIO1
#define DAC_CS_PIN (1<<16) // GPIO1:16 = P9 pin 15
#endif
#define DAC_TRM 0 // SPI transmit and receive
#define DAC_WL 32 // Word length
#define DAC_CLK_MODE 1 // SPI mode
#define DAC_CLK_DIV 1 // Clock divider (48MHz / 2^n)
#define DAC_DPE 1 // d0 = receive, d1 = transmit
#define AD5668_COMMAND_OFFSET 24
#define AD5668_ADDRESS_OFFSET 20
#define AD5668_DATA_OFFSET 4
#define AD5668_REF_OFFSET 0
#ifdef DBOX_CAPE
#define ADC_GPIO GPIO1
#define ADC_CS_PIN (1<<16) // GPIO1:16 = P9 pin 15
#else
#define ADC_GPIO GPIO1
#define ADC_CS_PIN (1<<17) // GPIO1:17 = P9 pin 23
#endif
#define ADC_TRM 0 // SPI transmit and receive
#define ADC_WL 16 // Word length
#define ADC_CLK_MODE 0 // SPI mode
#define ADC_CLK_DIV 1 // Clock divider (48MHz / 2^n)
#define ADC_DPE 1 // d0 = receive, d1 = transmit
#define AD7699_CFG_MASK 0xF120 // Mask for config update, unipolar, full BW
#define AD7699_CHANNEL_OFFSET 9 // 7 bits offset of a 14-bit left-justified word
#define AD7699_SEQ_OFFSET 3 // sequencer (0 = disable, 3 = scan all)
#define SHARED_COMM_MEM_BASE 0x00010000 // Location where comm flags are written
#define COMM_SHOULD_STOP 0 // Set to be nonzero when loop should stop
#define COMM_CURRENT_BUFFER 4 // Which buffer we are on
#define COMM_BUFFER_FRAMES 8 // How many frames per buffer
#define COMM_SHOULD_SYNC 12 // Whether to synchronise to an external clock
#define COMM_SYNC_ADDRESS 16 // Which memory address to find the GPIO on
#define COMM_SYNC_PIN_MASK 20 // Which pin to read for the sync
#define COMM_LED_ADDRESS 24 // Which memory address to find the status LED on
#define COMM_LED_PIN_MASK 28 // Which pin to write to change LED
#define COMM_FRAME_COUNT 32 // How many frames have elapse since beginning
#define COMM_USE_SPI 36 // Whether or not to use SPI ADC and DAC
#define COMM_NUM_CHANNELS 40 // Low 2 bits indicate 8 [0x3], 4 [0x1] or 2 [0x0] channels
#define COMM_USE_DIGITAL 44 // Whether or not to use DIGITAL
#define COMM_PRU_NUMBER 48 // Which PRU this code is running on
#define COMM_MUX_CONFIG 52 // Whether to use the mux capelet, and how many channels
#define COMM_MUX_END_CHANNEL 56 // Which mux channel the last buffer ended on
// General constants for McASP peripherals (used for audio codec)
#define MCASP0_BASE 0x48038000
#define MCASP1_BASE 0x4803C000
#define MCASP_PWRIDLESYSCONFIG 0x04
#define MCASP_PFUNC 0x10
#define MCASP_PDIR 0x14
#define MCASP_PDOUT 0x18
#define MCASP_PDSET 0x1C
#define MCASP_PDIN 0x1C
#define MCASP_PDCLR 0x20
#define MCASP_GBLCTL 0x44
#define MCASP_AMUTE 0x48
#define MCASP_DLBCTL 0x4C
#define MCASP_DITCTL 0x50
#define MCASP_RGBLCTL 0x60
#define MCASP_RMASK 0x64
#define MCASP_RFMT 0x68
#define MCASP_AFSRCTL 0x6C
#define MCASP_ACLKRCTL 0x70
#define MCASP_AHCLKRCTL 0x74
#define MCASP_RTDM 0x78
#define MCASP_RINTCTL 0x7C
#define MCASP_RSTAT 0x80
#define MCASP_RSLOT 0x84
#define MCASP_RCLKCHK 0x88
#define MCASP_REVTCTL 0x8C
#define MCASP_XGBLCTL 0xA0
#define MCASP_XMASK 0xA4
#define MCASP_XFMT 0xA8
#define MCASP_AFSXCTL 0xAC
#define MCASP_ACLKXCTL 0xB0
#define MCASP_AHCLKXCTL 0xB4
#define MCASP_XTDM 0xB8
#define MCASP_XINTCTL 0xBC
#define MCASP_XSTAT 0xC0
#define MCASP_XSLOT 0xC4
#define MCASP_XCLKCHK 0xC8
#define MCASP_XEVTCTL 0xCC
#define MCASP_SRCTL0 0x180
#define MCASP_SRCTL1 0x184
#define MCASP_SRCTL2 0x188
#define MCASP_SRCTL3 0x18C
#define MCASP_SRCTL4 0x190
#define MCASP_SRCTL5 0x194
#define MCASP_XBUF0 0x200
#define MCASP_XBUF1 0x204
#define MCASP_XBUF2 0x208
#define MCASP_XBUF3 0x20C
#define MCASP_XBUF4 0x210
#define MCASP_XBUF5 0x214
#define MCASP_RBUF0 0x280
#define MCASP_RBUF1 0x284
#define MCASP_RBUF2 0x288
#define MCASP_RBUF3 0x28C
#define MCASP_RBUF4 0x290
#define MCASP_RBUF5 0x294
#define MCASP_WFIFOCTL 0x1000
#define MCASP_WFIFOSTS 0x1004
#define MCASP_RFIFOCTL 0x1008
#define MCASP_RFIFOSTS 0x100C
#define MCASP_XSTAT_XUNDRN_BIT 0 // Bit to test if there was an underrun
#define MCASP_XSTAT_XDATA_BIT 5 // Bit to test for transmit ready
#define MCASP_RSTAT_RDATA_BIT 5 // Bit to test for receive ready
// Constants used for this particular audio setup
#define MCASP_BASE MCASP0_BASE
#ifdef DBOX_CAPE
#define MCASP_SRCTL_X MCASP_SRCTL2 // Ser. 2 is transmitter
#define MCASP_SRCTL_R MCASP_SRCTL0 // Ser. 0 is receiver
#define MCASP_XBUF MCASP_XBUF2
#define MCASP_RBUF MCASP_RBUF0
#else
#define MCASP_SRCTL_X MCASP_SRCTL3 // Ser. 3 is transmitter
#define MCASP_SRCTL_R MCASP_SRCTL2 // Ser. 2 is receiver
#define MCASP_XBUF MCASP_XBUF3
#define MCASP_RBUF MCASP_RBUF2
#endif
#define MCASP_PIN_AFSX (1 << 28)
#define MCASP_PIN_AHCLKX (1 << 27)
#define MCASP_PIN_ACLKX (1 << 26)
#define MCASP_PIN_AMUTE (1 << 25) // Also, 0 to 3 are XFR0 to XFR3
#ifdef DBOX_CAPE
#define MCASP_OUTPUT_PINS MCASP_PIN_AHCLKX | (1 << 2) // AHCLKX and AXR2 outputs
#else
#define MCASP_OUTPUT_PINS (1 << 3) // Which pins are outputs
#endif
#define MCASP_DATA_MASK 0xFFFF // 16 bit data
#define MCASP_DATA_FORMAT 0x807C // MSB first, 0 bit delay, 16 bits, CFG bus, ROR 16bits
#define C_MCASP_MEM C28 // Shared PRU mem
// Flags for the flags register
#define FLAG_BIT_BUFFER1 0
#define FLAG_BIT_USE_SPI 1
#define FLAG_BIT_MCASP_HWORD 2 // Whether we are on the high word for McASP transmission
#define FLAG_BIT_USE_DIGITAL 3
#define FLAG_BIT_MUX_CONFIG0 8 // Mux capelet configuration:
#define FLAG_BIT_MUX_CONFIG1 9 // 00 = off, 01 = 2 ch., 10 = 4 ch., 11 = 8 ch.
#define FLAG_MASK_MUX_CONFIG 0x0300
// Registers used throughout
// r1, r2, r3 are used for temporary storage
#define MEM_DIGITAL_BASE 0x11000 //Base address for DIGITAL : Shared RAM + 0x400
#define MEM_DIGITAL_BUFFER1_OFFSET 0x400 //Start pointer to DIGITAL_BUFFER1, which is 256 words after.
// 256 is the maximum number of frames allowed
#define reg_digital_current r6 // Pointer to current storage location of DIGITAL
#define reg_num_channels r9 // Number of SPI ADC/DAC channels to use
#define reg_frame_current r10 // Current frame count in SPI ADC/DAC transfer
#define reg_frame_total r11 // Total frame count for SPI ADC/DAC
#define reg_dac_data r12 // Current dword for SPI DAC
#define reg_adc_data r13 // Current dword for SPI ADC
#define reg_mcasp_dac_data r14 // Current dword for McASP DAC
#define reg_mcasp_adc_data r15 // Current dword for McASP ADC
#define reg_dac_buf0 r16 // Start pointer to SPI DAC buffer 0
#define reg_dac_buf1 r17 // Start pointer to SPI DAC buffer 1
#define reg_dac_current r18 // Pointer to current storage location of SPI DAC
#define reg_adc_current r19 // Pointer to current storage location of SPI ADC
#define reg_mcasp_buf0 r20 // Start pointer to McASP DAC buffer 0
#define reg_mcasp_buf1 r21 // Start pointer to McASP DAC buffer 1
#define reg_mcasp_dac_current r22 // Pointer to current storage location of McASP DAC
#define reg_mcasp_adc_current r23 // Pointer to current storage location of McASP ADC
#define reg_flags r24 // Buffer ID (0 and 1) and other flags
#define reg_comm_addr r25 // Memory address for communicating with ARM
#define reg_spi_addr r26 // Base address for SPI
// r27, r28 used in macros
#define reg_mcasp_addr r29 // Base address for McASP
#define reg_pru1_mux_pins r30 // Register mapped directly to P8 pins (PRU1 only)
//0 P8_07 36 0x890/090 66 gpio2[2]
//1 P8_08 37 0x894/094 67 gpio2[3]
//2 P8_09 39 0x89c/09c 69 gpio2[5]
//3 P8_10 38 0x898/098 68 gpio2[4]
//4 P8_11 13 0x834/034 45 gpio1[13]
//5 P8_12 12 0x830/030 44 gpio1[12]
//6 P9_12 30 0x878/078 60 gpio1[28]
//7 P9_14 18 0x848/048 50 gpio1[18]
//8 P8_15 15 0x83c/03c 47 gpio1[15]
//9 P8_16 14 0x838/038 46 gpio1[14]
//10 P9_16 19 0x84c/04c 51 gpio1[19]
//11 P8_18 35 0x88c/08c 65 gpio2[1]
//12 P8_27 56 0x8e0/0e0 86 gpio2[22]
//13 P8_28 58 0x8e8/0e8 88 gpio2[24]
//14 P8_29 57 0x8e4/0e4 87 gpio2[23]
//15 P8_30 59 0x8ec/0ec 89 gpio2[25]
//generic GPIOs constants
//#define GPIO1 0x4804c000
#define GPIO2 0x481ac000
//#define GPIO_CLEARDATAOUT 0x190 //SETDATAOUT is CLEARDATAOUT+4
#define GPIO_OE 0x134
#define GPIO_DATAIN 0x138
.macro READ_GPIO_BITS
.mparam gpio_data, gpio_num_bit, digital_bit, digital
QBBC DONE, digital, digital_bit //if the pin is set as an output, nothing to do here
QBBC CLEAR, gpio_data, gpio_num_bit
SET digital, digital_bit+16
QBA DONE
CLEAR:
CLR digital, digital_bit+16
QBA DONE
DONE:
.endm
.macro SET_GPIO_BITS
.mparam gpio_oe, gpio_setdataout, gpio_cleardataout, gpio_num_bit, digital_bit, digital //sets the bits in GPIO_OE, GPIO_SETDATAOUT and GPIO_CLEARDATAOUT
//Remember that the GPIO_OE Output data enable register behaves as follows for each bit:
//0 = The corresponding GPIO pin is configured as an output.
//1 = The corresponding GPIO pin is configured as an input.
QBBS SETINPUT, digital, digital_bit
CLR gpio_oe, gpio_num_bit //if it is an output, configure pin as output
QBBC CLEARDATAOUT, digital, digital_bit+16 // check the output value. If it is 0, branch
SET gpio_setdataout, gpio_num_bit //if it is 1, set output to high
QBA DONE
CLEARDATAOUT:
SET gpio_cleardataout, gpio_num_bit // set output to low
QBA DONE
SETINPUT: //if it is an input, set the relevant bit
SET gpio_oe, gpio_num_bit
QBA DONE
DONE:
.endm
QBA START // when first starting, go to START, skipping this section.
DIGITAL:
//IMPORTANT: do NOT use r28 in this macro, as it contains the return address for JAL
//r27 is now the input word passed in render(), one word per frame
//[31:16]: data(1=high, 0=low), [15:0]: direction (0=output, 1=input) )
//Preparing the gpio_oe, gpio_cleardataout and gpio_setdataout for each module
//r2 will hold GPIO1_OE
//load current status of GPIO_OE in r2
MOV r2, GPIO1 | GPIO_OE
//it takes 190ns to go through the next instruction
LBBO r2, r2, 0, 4
//GPIO1-start
//process oe and datain and prepare dataout for GPIO1
//r7 will contain GPIO1_CLEARDATAOUT
//r8 will contain GPIO1_SETDATAOUT
MOV r8, 0
MOV r7, 0
//map GPIO_ANALOG to gpio1 pins,
//r2 is gpio1_oe, r8 is gpio1_setdataout, r7 is gpio1_cleardataout, r27 is the input word
//the following operations will read from r27 and update r2,r7,r8
SET_GPIO_BITS r2, r8, r7, 13, 4, r27
SET_GPIO_BITS r2, r8, r7, 12, 5, r27
SET_GPIO_BITS r2, r8, r7, 28, 6, r27
SET_GPIO_BITS r2, r8, r7, 18, 7, r27
SET_GPIO_BITS r2, r8, r7, 15, 8, r27
SET_GPIO_BITS r2, r8, r7, 14, 9, r27
SET_GPIO_BITS r2, r8, r7, 19, 10, r27
//set the output enable register for gpio1.
MOV r3, GPIO1 | GPIO_OE //use r3 as a temp register
SBBO r2, r3, 0, 4 //takes two cycles (10ns)
//GPIO1-end
// r2 is now unused
//GPIO2-start
//r3 will hold GPIO1_OE
//load current status of GPIO_OE in r3
MOV r3, GPIO2 | GPIO_OE
//it takes 200ns to go through the next instructions
LBBO r3, r3, 0, 4
//process oe and datain and prepare dataout for GPIO2
//r4 will contain GPIO2_CLEARDATAOUT
//r5 will contain GPIO2_SETDATAOUT
MOV r5, 0
MOV r4, 0
//map GPIO_ANALOG to gpio2 pins
//r3 is gpio2_oe, r5 is gpio2_setdataout, r4 is gpio2_cleardataout, r27 is the input word
//the following operations will read from r27 and update r3,r4,r5
SET_GPIO_BITS r3, r5, r4, 2, 0, r27
SET_GPIO_BITS r3, r5, r4, 3, 1, r27
SET_GPIO_BITS r3, r5, r4, 5, 2, r27
SET_GPIO_BITS r3, r5, r4, 4, 3, r27
SET_GPIO_BITS r3, r5, r4, 1, 11, r27
SET_GPIO_BITS r3, r5, r4, 22, 12, r27
SET_GPIO_BITS r3, r5, r4, 24, 13, r27
SET_GPIO_BITS r3, r5, r4, 23, 14, r27
SET_GPIO_BITS r3, r5, r4, 25, 15, r27
//set the output enable register for gpio2.
MOV r2, GPIO2 | GPIO_OE //use r2 as a temp registerp
SBBO r3, r2, 0, 4 //takes two cycles (10ns)
//GPIO2-end
//r3 is now unused
//load current inputs in r2, r3
//r2 will contain GPIO1_DATAIN
//r3 will contain GPIO2_DATAIN
//load the memory locations
MOV r2, GPIO1 | GPIO_DATAIN
MOV r3, GPIO2 | GPIO_DATAIN
//takes 375 nns to go through the next two instructions
//read the datain
LBBO r2, r2, 0, 4
LBBO r3, r3, 0, 4
//now read from r2 and r3 only the channels that are set as input in the lower word of r27
// and set their value in the high word of r27
//GPIO1
READ_GPIO_BITS r2, 13, 4, r27
READ_GPIO_BITS r2, 12, 5, r27
READ_GPIO_BITS r2, 28, 6, r27
READ_GPIO_BITS r2, 18, 7, r27
READ_GPIO_BITS r2, 15, 8, r27
READ_GPIO_BITS r2, 14, 9, r27
READ_GPIO_BITS r2, 19, 10, r27
//GPIO2
READ_GPIO_BITS r3, 2, 0, r27
READ_GPIO_BITS r3, 3, 1, r27
READ_GPIO_BITS r3, 5, 2, r27
READ_GPIO_BITS r3, 4, 3, r27
READ_GPIO_BITS r3, 1, 11, r27
READ_GPIO_BITS r3, 22, 12, r27
READ_GPIO_BITS r3, 24, 13, r27
READ_GPIO_BITS r3, 23, 14, r27
READ_GPIO_BITS r3, 25, 15, r27
//r2, r3 are now unused
//now all the setdataout and cleardataout are ready to be written to the GPIO register.
//CLEARDATAOUT and SETDATAOUT are consecutive positions in memory, so we just write 8 bytes to CLEARDATAOUT.
//We can do this because we chose cleardata and setdata registers for a given GPIO to be consecutive
//load the memory addresses to be written to
MOV r2, GPIO1 | GPIO_CLEARDATAOUT //use r2 as a temp register
MOV r3, GPIO2 | GPIO_CLEARDATAOUT //use r3 as a temp register
//write 8 bytes for each GPIO
//takes 30ns in total to go through the following two instructions
SBBO r7, r2, 0, 8 //store r7 and r8 in GPIO1_CLEARDATAOUT and GPIO1_SETDATAOUT
//takes 145ns to be effective when going low, 185ns when going high
SBBO r4, r3, 0, 8 //store r4 and r5 in GPIO2_CLEARDATAOUT and GPIO2_SETDATAOUT
//takes 95ns to be effective when going low, 130ns when going high
//reversing the order of the two lines above will swap the performances between the GPIO modules
//i.e.: the first line will always take 145ns/185ns and the second one will always take 95ns/130ns,
//regardless of whether the order is gpio1-gpio2 or gpio2-gpio1
JMP r28.w0 // go back to ADC_WRITE_AND_PROCESS_GPIO
.macro HANG //useful for debugging
DALOOP:
set r30.t14
clr r30.t14
QBA DALOOP
.endm
// Bring CS line low to write to DAC
.macro DAC_CS_ASSERT
MOV r27, DAC_CS_PIN
MOV r28, DAC_GPIO + GPIO_CLEARDATAOUT
SBBO r27, r28, 0, 4
.endm
// Bring CS line high at end of DAC transaction
.macro DAC_CS_UNASSERT
MOV r27, DAC_CS_PIN
MOV r28, DAC_GPIO + GPIO_SETDATAOUT
SBBO r27, r28, 0, 4
.endm
// Write to DAC TX register
.macro DAC_TX
.mparam data
SBBO data, reg_spi_addr, SPI_CH0TX, 4
.endm
// Wait for SPI to finish (uses RXS indicator)
.macro DAC_WAIT_FOR_FINISH
LOOP:
LBBO r27, reg_spi_addr, SPI_CH0STAT, 4
QBBC LOOP, r27, 0
.endm
// Read the RX word to clear
.macro DAC_DISCARD_RX
LBBO r27, reg_spi_addr, SPI_CH0RX, 4
.endm
// Complete DAC write with chip select
.macro DAC_WRITE
.mparam reg
DAC_CS_ASSERT
DAC_TX reg
DAC_WAIT_FOR_FINISH
DAC_CS_UNASSERT
DAC_DISCARD_RX
.endm
// Transform channel order on DAC
// (in) 01234567 --> (out) 64201357
// This is to make the pin order on the Bela cape
// make sense
.macro DAC_CHANNEL_REORDER
.mparam out, in
QBBS DAC_CHANNEL_REORDER_HIGH, in, 2
// Input channels 0,1,2,3 --> 6,4,2,0
// out = (3 - in) << 1
XOR out, in, 0x03
LSL out, out, 1
QBA DAC_CHANNEL_REORDER_DONE
DAC_CHANNEL_REORDER_HIGH:
// Input channels 4,5,6,7 --> 1,3,5,7
// out = ((in & 0x03) << 1) + 1
AND out, in, 0x03
LSL out, out, 1
ADD out, out, 1
DAC_CHANNEL_REORDER_DONE:
.endm
// Bring CS line low to write to ADC
.macro ADC_CS_ASSERT
MOV r27, ADC_CS_PIN
MOV r28, ADC_GPIO + GPIO_CLEARDATAOUT
SBBO r27, r28, 0, 4
.endm
// Bring CS line high at end of ADC transaction
.macro ADC_CS_UNASSERT
MOV r27, ADC_CS_PIN
MOV r28, ADC_GPIO + GPIO_SETDATAOUT
SBBO r27, r28, 0, 4
.endm
// Write to ADC TX register
.macro ADC_TX
.mparam data
SBBO data, reg_spi_addr, SPI_CH1TX, 4
.endm
// Wait for SPI to finish (uses RXS indicator)
.macro ADC_WAIT_FOR_FINISH
LOOP:
LBBO r27, reg_spi_addr, SPI_CH1STAT, 4
QBBC LOOP, r27, 0
.endm
// Read the RX word to clear; store output
.macro ADC_RX
.mparam data
LBBO data, reg_spi_addr, SPI_CH1RX, 4
.endm
// Complete ADC write+read with chip select
.macro ADC_WRITE
.mparam in, out
ADC_CS_ASSERT
ADC_TX in
ADC_WAIT_FOR_FINISH
ADC_RX out
ADC_CS_UNASSERT
.endm
// Complete ADC write+read with chip select and also performs IO for digital
.macro ADC_WRITE_GPIO
.mparam in, out, do_gpio
ADC_CS_ASSERT
ADC_TX in
QBBC GPIO_DONE, reg_flags, FLAG_BIT_USE_DIGITAL //skip if DIGITAL is disabled
QBLT CASE_4_OR_8_CHANNELS, reg_num_channels, 2
CASE_2_CHANNELS:
AND r27, reg_frame_current, 0x1
QBNE GPIO_DONE, r27, 0
JMP DO_GPIO
CASE_4_OR_8_CHANNELS:
AND r27, do_gpio, 0x3 // only do a DIGITAL every 2 SPI I/O
QBNE GPIO_DONE, r27, 0
DO_GPIO:
//from here to GPIO_DONE takes 1.8us, while usually ADC_WAIT_FOR_FINISH only waits for 1.14us.
//TODO: it would be better to split the DIGITAL stuff in two parts:
//- one taking place during DAC_WRITE which sets the GPIO_OE
//- and the other during ADC_WRITE which actually reads DATAIN and writes CLEAR/SET DATAOUT
//r27 is actually r27, so do not use r27 from here to ...
LBBO r27, reg_digital_current, 0, 4
JAL r28.w0, DIGITAL // note that this is not called as a macro, but with JAL. r28 will contain the return address
SBBO r27, reg_digital_current, 0, 4
//..here you can start using r27 again
ADD reg_digital_current, reg_digital_current, 4 //increment pointer
GPIO_DONE:
ADC_WAIT_FOR_FINISH
ADC_RX out
ADC_CS_UNASSERT
.endm
// Write a McASP register
.macro MCASP_REG_WRITE
.mparam reg, value
MOV r27, value
SBBO r27, reg_mcasp_addr, reg, 4
.endm
// Write a McASP register beyond the 0xFF boundary
.macro MCASP_REG_WRITE_EXT
.mparam reg, value
MOV r27, value
MOV r28, reg
ADD r28, reg_mcasp_addr, r28
SBBO r27, r28, 0, 4
.endm
// Read a McASP register
.macro MCASP_REG_READ
.mparam reg, value
LBBO value, reg_mcasp_addr, reg, 4
.endm
// Read a McASP register beyond the 0xFF boundary
.macro MCASP_REG_READ_EXT
.mparam reg, value
MOV r28, reg
ADD r28, reg_mcasp_addr, r28
LBBO value, r28, 0, 4
.endm
// Set a bit and wait for it to come up
.macro MCASP_REG_SET_BIT_AND_POLL
.mparam reg, mask
MOV r27, mask
LBBO r28, reg_mcasp_addr, reg, 4
OR r28, r28, r27
SBBO r28, reg_mcasp_addr, reg, 4
POLL:
LBBO r28, reg_mcasp_addr, reg, 4
AND r28, r28, r27
QBEQ POLL, r28, 0
.endm
// Multiplexer Capelet: Increment channel on muxes 0-3
.macro MUX_INCREMENT_0_TO_3
MOV r28, FLAG_MASK_MUX_CONFIG
AND r28, reg_flags, r28 // Check flags
QBEQ DONE, r28, 0 // Skip if disabled
LSR r28, r28, FLAG_BIT_MUX_CONFIG0
AND r27, reg_pru1_mux_pins, 0x07 // Current mux channel in r30 bits 2-0
ADD r27, r27, 1 // Increment channel
AND r27, r27, 0x07 // Mask to 8 channels
QBEQ UPDATE, r28, 0x03
AND r27, r27, 0x03 // Mask to 4 channels
QBEQ UPDATE, r28, 0x02
AND r27, r27, 0x01 // Mask to 2 channels
UPDATE:
MOV r28, 0xFFFFFFF8
AND r28, reg_pru1_mux_pins, r28 // Mask out low 3 bits of r30
OR r28, r28, r27 // Combine with new value
MOV reg_pru1_mux_pins, r28 // Move back to r30 to propagate to pins
DONE:
.endm
// Multiplexer Capelet: Increment channel on muxes 4-7
.macro MUX_INCREMENT_4_TO_7
MOV r28, FLAG_MASK_MUX_CONFIG
AND r28, reg_flags, r28 // Check flags
QBEQ DONE, r28, 0 // Skip if disabled
LSR r28, r28, FLAG_BIT_MUX_CONFIG0
AND r27, reg_pru1_mux_pins, 0x38 // Current mux channel in r30 bits 5-3
ADD r27, r27, 8 // Increment channel (+1 LSB starting at bit 3)
AND r27, r27, 0x38 // Mask to 8 channels
QBEQ UPDATE, r28, 0x03
AND r27, r27, 0x18 // Mask to 4 channels
QBEQ UPDATE, r28, 0x02
AND r27, r27, 0x08 // Mask to 2 channels
UPDATE:
MOV r28, 0xFFFFFFC7
AND r28, reg_pru1_mux_pins, r28 // Mask out bits 5-3 of r30
OR r28, r28, r27 // Combine with new value
MOV reg_pru1_mux_pins, r28 // Move back to r30 to propagate to pins
DONE:
.endm
START:
// Load useful registers for addressing SPI
MOV reg_comm_addr, SHARED_COMM_MEM_BASE
MOV reg_spi_addr, SPI_BASE
MOV reg_mcasp_addr, MCASP_BASE
// Find out which PRU we are running on
// This affects the following offsets
MOV r0, 0x24000 // PRU1 control register offset
LBBO r2, reg_comm_addr, COMM_PRU_NUMBER, 4
QBEQ PRU_NUMBER_CHECK_DONE, r2, 1
MOV r0, 0x22000 // PRU0 control register offset
PRU_NUMBER_CHECK_DONE:
// Set up c24 and c25 offsets with CTBIR register
// Thus C24 points to start of PRU0 RAM
OR r3, r0, 0x20 // CTBIR0
MOV r2, 0
SBBO r2, r3, 0, 4
// Set up c28 pointer offset for shared PRU RAM
OR r3, r0, 0x28 // CTPPR0
MOV r2, 0x00000120 // To get address 0x00012000
SBBO r2, r3, 0, 4
// Set ARM such that PRU can write to registers
LBCO r0, C4, 4, 4
CLR r0, r0, 4
SBCO r0, C4, 4, 4
// Clear flags
MOV reg_flags, 0
// Default number of channels in case SPI disabled
LDI reg_num_channels, 8
// Find out whether we should use DIGITAL
LBBO r2, reg_comm_addr, COMM_USE_DIGITAL, 4
QBEQ DIGITAL_INIT_DONE, r2, 0 // if we use digital
SET reg_flags, reg_flags, FLAG_BIT_USE_DIGITAL
/* This block of code is not really needed, as the memory is initialized by ARM before the PRU is started.
Will leave it here for future reference
DIGITAL_INIT: //set the digital buffer to 0x0000ffff (all inputs), to prevent unwanted high outputs
//the loop is unrolled by a factor of four just to take advantage of the speed of SBBO on larger byte bursts, but there is no real need for it
MOV r2, 0x0000ffff //value to store. 0x0000ffff means all inputs
MOV r3, MEM_DIGITAL_BASE //start of the digital buffer
MOV r4, MEM_DIGITAL_BASE+2*MEM_DIGITAL_BUFFER1_OFFSET //end of the digital buffer
DIGITAL_INIT_BUFFER_LOOP:
SBBO r2, r3, 0, 4
ADD r3, r3, 4 //increment pointer
QBGT DIGITAL_INIT_BUFFER_LOOP, r3, r4 //loop until we reach the end of the buffer
*/
DIGITAL_INIT_DONE:
// Check if we should use an external multiplexer capelet
// The valid values are 0 (off), 1 (2 ch), 2 (4 ch), 3 (8 ch)
// This can only happen on PRU1 because of how the pins are mapped
LBBO r2, reg_comm_addr, COMM_PRU_NUMBER, 4
QBNE MUX_INIT_DONE, r2, 1
LBBO r2, reg_comm_addr, COMM_MUX_CONFIG, 4
AND r2, r2, 0x03
QBEQ MUX_INIT_DONE, r2, 0
// If we get here, we are using the mux. Prepare flags and initial state.
LSL r2, r2, FLAG_BIT_MUX_CONFIG0
OR reg_flags, reg_flags, r2
// Clear lower 6 bits of r30 which controls the mux pins
MOV r2, 0xFFFFFFC0
AND reg_pru1_mux_pins, reg_pru1_mux_pins, r2
MUX_INIT_DONE:
// Find out whether we should use SPI ADC and DAC
LBBO r2, reg_comm_addr, COMM_USE_SPI, 4
QBEQ SPI_FLAG_CHECK_DONE, r2, 0
SET reg_flags, reg_flags, FLAG_BIT_USE_SPI
SPI_FLAG_CHECK_DONE:
// If we don't use SPI, then skip all this init
QBBC SPI_INIT_DONE, reg_flags, FLAG_BIT_USE_SPI
// Load the number of channels: valid values are 8, 4 or 2
LBBO reg_num_channels, reg_comm_addr, COMM_NUM_CHANNELS, 4
QBGT SPI_NUM_CHANNELS_LT8, reg_num_channels, 8 // 8 > num_channels ?
LDI reg_num_channels, 8 // If N >= 8, N = 8
QBA SPI_NUM_CHANNELS_DONE
SPI_NUM_CHANNELS_LT8:
QBGT SPI_NUM_CHANNELS_LT4, reg_num_channels, 4 // 4 > num_channels ?
LDI reg_num_channels, 4 // If N >= 4, N = 4
QBA SPI_NUM_CHANNELS_DONE
SPI_NUM_CHANNELS_LT4:
LDI reg_num_channels, 2 // else N = 2
SPI_NUM_CHANNELS_DONE:
// Init SPI clock
MOV r2, 0x02
MOV r3, CLOCK_BASE + CLOCK_SPI0
SBBO r2, r3, 0, 4
// Reset SPI and wait for finish
MOV r2, 0x02
SBBO r2, reg_spi_addr, SPI_SYSCONFIG, 4
SPI_WAIT_RESET:
LBBO r2, reg_spi_addr, SPI_SYSSTATUS, 4
QBBC SPI_WAIT_RESET, r2, 0
// Turn off SPI channels
MOV r2, 0
SBBO r2, reg_spi_addr, SPI_CH0CTRL, 4
SBBO r2, reg_spi_addr, SPI_CH1CTRL, 4
// Set to master; chip select lines enabled (CS0 used for DAC)
MOV r2, 0x00
SBBO r2, reg_spi_addr, SPI_MODULCTRL, 4
// Configure CH0 for DAC
MOV r2, (3 << 27) | (DAC_DPE << 16) | (DAC_TRM << 12) | ((DAC_WL - 1) << 7) | (DAC_CLK_DIV << 2) | DAC_CLK_MODE | (1 << 6)
SBBO r2, reg_spi_addr, SPI_CH0CONF, 4
// Configure CH1 for ADC
MOV r2, (3 << 27) | (ADC_DPE << 16) | (ADC_TRM << 12) | ((ADC_WL - 1) << 7) | (ADC_CLK_DIV << 2) | ADC_CLK_MODE
SBBO r2, reg_spi_addr, SPI_CH1CONF, 4
// Turn on SPI channels
MOV r2, 0x01
SBBO r2, reg_spi_addr, SPI_CH0CTRL, 4
SBBO r2, reg_spi_addr, SPI_CH1CTRL, 4
// DAC power-on reset sequence
MOV r2, (0x07 << AD5668_COMMAND_OFFSET)
DAC_WRITE r2
// Initialise ADC
MOV r2, AD7699_CFG_MASK | (0 << AD7699_CHANNEL_OFFSET) | (0 << AD7699_SEQ_OFFSET)
ADC_WRITE r2, r2
// Enable DAC internal reference
MOV r2, (0x08 << AD5668_COMMAND_OFFSET) | (0x01 << AD5668_REF_OFFSET)
DAC_WRITE r2
// Read ADC ch0 and ch1: result is always 2 samples behind so start here
MOV r2, AD7699_CFG_MASK | (0x00 << AD7699_CHANNEL_OFFSET)
ADC_WRITE r2, r2
MOV r2, AD7699_CFG_MASK | (0x01 << AD7699_CHANNEL_OFFSET)
ADC_WRITE r2, r2
SPI_INIT_DONE:
// Prepare McASP0 for audio
MCASP_REG_WRITE MCASP_GBLCTL, 0 // Disable McASP
MCASP_REG_WRITE_EXT MCASP_SRCTL0, 0 // All serialisers off
MCASP_REG_WRITE_EXT MCASP_SRCTL1, 0
MCASP_REG_WRITE_EXT MCASP_SRCTL2, 0
MCASP_REG_WRITE_EXT MCASP_SRCTL3, 0
MCASP_REG_WRITE_EXT MCASP_SRCTL4, 0
MCASP_REG_WRITE_EXT MCASP_SRCTL5, 0
MCASP_REG_WRITE MCASP_PWRIDLESYSCONFIG, 0x02 // Power on
MCASP_REG_WRITE MCASP_PFUNC, 0x00 // All pins are McASP
MCASP_REG_WRITE MCASP_PDIR, MCASP_OUTPUT_PINS // Set pin direction
MCASP_REG_WRITE MCASP_DLBCTL, 0x00
MCASP_REG_WRITE MCASP_DITCTL, 0x00
MCASP_REG_WRITE MCASP_RMASK, MCASP_DATA_MASK // 16 bit data receive
MCASP_REG_WRITE MCASP_RFMT, MCASP_DATA_FORMAT // Set data format
MCASP_REG_WRITE MCASP_AFSRCTL, 0x100 // I2S mode
MCASP_REG_WRITE MCASP_ACLKRCTL, 0x80 // Sample on rising edge
MCASP_REG_WRITE MCASP_AHCLKRCTL, 0x8001 // Internal clock, not inv, /2; irrelevant?
MCASP_REG_WRITE MCASP_RTDM, 0x03 // Enable TDM slots 0 and 1
MCASP_REG_WRITE MCASP_RINTCTL, 0x00 // No interrupts
MCASP_REG_WRITE MCASP_XMASK, MCASP_DATA_MASK // 16 bit data transmit
MCASP_REG_WRITE MCASP_XFMT, MCASP_DATA_FORMAT // Set data format
MCASP_REG_WRITE MCASP_AFSXCTL, 0x100 // I2S mode
MCASP_REG_WRITE MCASP_ACLKXCTL, 0x00 // Transmit on rising edge, sync. xmit and recv
MCASP_REG_WRITE MCASP_AHCLKXCTL, 0x8001 // External clock from AHCLKX
MCASP_REG_WRITE MCASP_XTDM, 0x03 // Enable TDM slots 0 and 1
MCASP_REG_WRITE MCASP_XINTCTL, 0x00 // No interrupts
MCASP_REG_WRITE_EXT MCASP_SRCTL_R, 0x02 // Set up receive serialiser
MCASP_REG_WRITE_EXT MCASP_SRCTL_X, 0x01 // Set up transmit serialiser
MCASP_REG_WRITE_EXT MCASP_WFIFOCTL, 0x00 // Disable FIFOs
MCASP_REG_WRITE_EXT MCASP_RFIFOCTL, 0x00
MCASP_REG_WRITE MCASP_XSTAT, 0xFF // Clear transmit errors
MCASP_REG_WRITE MCASP_RSTAT, 0xFF // Clear receive errors
MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 1) // Set RHCLKRST
MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 9) // Set XHCLKRST
// The above write sequence will have temporarily changed the AHCLKX frequency
// The PLL needs time to settle or the sample rate will be unstable and possibly
// cause an underrun. Give it ~1ms before going on.
// 10ns per loop iteration = 10^-8s --> 10^5 iterations needed
MOV r2, 1 << 28
MOV r3, GPIO1 + GPIO_SETDATAOUT
SBBO r2, r3, 0, 4
MOV r2, 100000
MCASP_INIT_WAIT:
SUB r2, r2, 1
QBNE MCASP_INIT_WAIT, r2, 0
MOV r2, 1 << 28
MOV r3, GPIO1 + GPIO_CLEARDATAOUT
SBBO r2, r3, 0, 4
MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 0) // Set RCLKRST
MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 8) // Set XCLKRST
MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 2) // Set RSRCLR
MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 10) // Set XSRCLR
MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 3) // Set RSMRST
MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 11) // Set XSMRST
MCASP_REG_WRITE_EXT MCASP_XBUF, 0x00 // Write to the transmit buffer to prevent underflow
MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 4) // Set RFRST
MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 12) // Set XFRST
// Initialisation
LBBO reg_frame_total, reg_comm_addr, COMM_BUFFER_FRAMES, 4 // Total frame count (SPI; 0.5x-2x for McASP)
MOV reg_dac_buf0, 0 // DAC buffer 0 start pointer
LSL reg_dac_buf1, reg_frame_total, 1 // DAC buffer 1 start pointer = N[ch]*2[bytes]*bufsize
LMBD r2, reg_num_channels, 1 // Returns 1, 2 or 3 depending on the number of channels
LSL reg_dac_buf1, reg_dac_buf1, r2 // Multiply by 2, 4 or 8 to get the N[ch] scaling above
MOV reg_mcasp_buf0, 0 // McASP DAC buffer 0 start pointer
LSL reg_mcasp_buf1, reg_frame_total, r2 // McASP DAC buffer 1 start pointer = 2[ch]*2[bytes]*(N/4)[samples/spi]*bufsize
CLR reg_flags, reg_flags, FLAG_BIT_BUFFER1 // Bit 0 holds which buffer we are on
MOV r2, 0
SBBO r2, reg_comm_addr, COMM_FRAME_COUNT, 4 // Start with frame count of 0
/* This block of code is not really needed, as the memory is initialized by ARM before the PRU is started.
Will leave it here for future reference
//Initialise all SPI and audio buffers (DAC0, DAC1, ADC0, ADC1) to zero.
//This is useful for analog outs so they do not have spikes during the first buffer.
//This is not very useful for audio, as you still hear the initial "tumpf" when the converter starts
//and each sample in the DAC buffer is reset to 0 after it is written to the DAC.
QBBC SPI_INIT_BUFFER_DONE, reg_flags, FLAG_BIT_USE_SPI
//Initialize SPI buffers
//compute the memory offset of the end of the audio buffer and store it in r4
SUB r4, reg_dac_buf1, reg_dac_buf0 // length of the buffer, assumes reg_dac_buf1>ref_dac_buf0
LSL r4, r4, 2 //length of four buffers (DAC0, DAC1, ADC0, ADC1)
ADD r4, reg_dac_buf0, r4 //total offset
MOV r2, 0// value to store
MOV r3, 0 // offset counter
SPI_INIT_BUFFER_LOOP:
SBCO r2, C_ADC_DAC_MEM, r3, 4
ADD r3, r3, 4
QBGT SPI_INIT_BUFFER_LOOP, r3, r4
SPI_INIT_BUFFER_DONE:
//Initialize audio buffers
//compute the memory offset of the end of the audio buffer and store it in r4
SUB r4, reg_mcasp_buf1, reg_mcasp_buf0 // length of the buffer, assumes reg_mcasp_buf1>ref_mcasp_buf0
LSL r4, r4, 2 //length of four buffers (DAC0, DAC1, ADC0, ADC1)
ADD r4, reg_mcasp_buf0, r4 //total offset
MOV r2, 0 // value to store
MOV r3, 0 // offset counter
MCASP_INIT_BUFFER_LOOP:
SBCO r2, C_MCASP_MEM, r3, 4
ADD r3, r3, 4
QBGT MCASP_INIT_BUFFER_LOOP, r3, r4
*/
// Here we are out of sync by one TDM slot since the 0 word transmitted above will have occupied
// the first output slot. Send one more word before jumping into the loop.
MCASP_DAC_WAIT_BEFORE_LOOP:
LBBO r2, reg_mcasp_addr, MCASP_XSTAT, 4
QBBC MCASP_DAC_WAIT_BEFORE_LOOP, r2, MCASP_XSTAT_XDATA_BIT
MCASP_REG_WRITE_EXT MCASP_XBUF, 0x00
// Likewise, read and discard the first sample we get back from the ADC. This keeps the DAC and ADC
// in sync in terms of which TDM slot we are reading (empirically found that we should throw this away
// rather than keep it and invert the phase)
MCASP_ADC_WAIT_BEFORE_LOOP:
LBBO r2, reg_mcasp_addr, MCASP_RSTAT, 4
QBBC MCASP_ADC_WAIT_BEFORE_LOOP, r2, MCASP_RSTAT_RDATA_BIT
MCASP_REG_READ_EXT MCASP_RBUF, r2
WRITE_ONE_BUFFER:
// Write a single buffer of DAC samples and read a buffer of ADC samples
// Load starting positions
MOV reg_dac_current, reg_dac_buf0 // DAC: reg_dac_current is current pointer
LMBD r2, reg_num_channels, 1 // 1, 2 or 3 for 2, 4 or 8 channels
LSL reg_adc_current, reg_frame_total, r2
LSL reg_adc_current, reg_adc_current, 2 // N * 2 * 2 * bufsize
ADD reg_adc_current, reg_adc_current, reg_dac_current // ADC: starts N * 2 * 2 * bufsize beyond DAC
MOV reg_mcasp_dac_current, reg_mcasp_buf0 // McASP: set current DAC pointer
LSL reg_mcasp_adc_current, reg_frame_total, r2 // McASP ADC: starts (N/2)*2*2*bufsize beyond DAC
LSL reg_mcasp_adc_current, reg_mcasp_adc_current, 1
ADC reg_mcasp_adc_current, reg_mcasp_adc_current, reg_mcasp_dac_current
MOV reg_frame_current, 0
QBBS DIGITAL_BASE_CHECK_SET, reg_flags, FLAG_BIT_BUFFER1 //check which buffer we are using for DIGITAL
// if we are here, we are using buffer0
MOV reg_digital_current, MEM_DIGITAL_BASE
QBA DIGITAL_BASE_CHECK_DONE
DIGITAL_BASE_CHECK_SET: //if we are here, we are using buffer1
MOV reg_digital_current, MEM_DIGITAL_BASE+MEM_DIGITAL_BUFFER1_OFFSET //so adjust offset appropriately
DIGITAL_BASE_CHECK_DONE:
WRITE_LOOP:
// Write N channels to DAC from successive values in memory
// At the same time, read N channels from ADC
// Unrolled by a factor of 2 to get high and low words
MOV r1, 0
ADC_DAC_LOOP:
QBBC SPI_DAC_LOAD_DONE, reg_flags, FLAG_BIT_USE_SPI
// Load next 2 SPI DAC samples and store zero in their place
LBCO reg_dac_data, C_ADC_DAC_MEM, reg_dac_current, 4
MOV r2, 0
SBCO r2, C_ADC_DAC_MEM, reg_dac_current, 4
ADD reg_dac_current, reg_dac_current, 4
SPI_DAC_LOAD_DONE:
// On even iterations, load two more samples and choose the first one
// On odd iterations, transmit the second of the samples already loaded
// QBBS MCASP_DAC_HIGH_WORD, r1, 1
QBBS MCASP_DAC_HIGH_WORD, reg_flags, FLAG_BIT_MCASP_HWORD
MCASP_DAC_LOW_WORD:
// Load next 2 Audio DAC samples and store zero in their place
LBCO reg_mcasp_dac_data, C_MCASP_MEM, reg_mcasp_dac_current, 4
MOV r2, 0
SBCO r2, C_MCASP_MEM, reg_mcasp_dac_current, 4
ADD reg_mcasp_dac_current, reg_mcasp_dac_current, 4
// Mask out the low word (first in little endian)
MOV r2, 0xFFFF
AND r7, reg_mcasp_dac_data, r2
QBA MCASP_WAIT_XSTAT
MCASP_DAC_HIGH_WORD:
// Take the high word of the previously loaded data
LSR r7, reg_mcasp_dac_data, 16
// Every 2 channels we send one audio sample; this loop already
// sends exactly two SPI channels.
// Wait for McASP XSTAT[XDATA] to set indicating we can write more data
MCASP_WAIT_XSTAT:
LBBO r2, reg_mcasp_addr, MCASP_XSTAT, 4
QBBS START, r2, MCASP_XSTAT_XUNDRN_BIT // if underrun occurred, reset the PRU
QBBC MCASP_WAIT_XSTAT, r2, MCASP_XSTAT_XDATA_BIT
MCASP_REG_WRITE_EXT MCASP_XBUF, r7
// Same idea with ADC: even iterations, load the sample into the low word, odd
// iterations, load the sample into the high word and store
// QBBS MCASP_ADC_HIGH_WORD, r1, 1
QBBS MCASP_ADC_HIGH_WORD, reg_flags, FLAG_BIT_MCASP_HWORD
MCASP_ADC_LOW_WORD:
// Start ADC data at 0
LDI reg_mcasp_adc_data, 0
// Now wait for a received word to become available from the audio ADC
MCASP_WAIT_RSTAT_LOW:
LBBO r2, reg_mcasp_addr, MCASP_RSTAT, 4
QBBC MCASP_WAIT_RSTAT_LOW, r2, MCASP_RSTAT_RDATA_BIT
// Mask low word and store in ADC data register
MCASP_REG_READ_EXT MCASP_RBUF, r3
MOV r2, 0xFFFF
AND reg_mcasp_adc_data, r3, r2
QBA MCASP_ADC_DONE
MCASP_ADC_HIGH_WORD:
// Wait for a received word to become available from the audio ADC
MCASP_WAIT_RSTAT_HIGH:
LBBO r2, reg_mcasp_addr, MCASP_RSTAT, 4
QBBC MCASP_WAIT_RSTAT_HIGH, r2, MCASP_RSTAT_RDATA_BIT
// Read data and shift 16 bits to the left (into the high word)
MCASP_REG_READ_EXT MCASP_RBUF, r3
LSL r3, r3, 16
OR reg_mcasp_adc_data, reg_mcasp_adc_data, r3
// Now store the result and increment the pointer
SBCO reg_mcasp_adc_data, C_MCASP_MEM, reg_mcasp_adc_current, 4
ADD reg_mcasp_adc_current, reg_mcasp_adc_current, 4
MCASP_ADC_DONE:
QBBC SPI_SKIP_WRITE, reg_flags, FLAG_BIT_USE_SPI