tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/ws2812/ws2812_xtensa.go (about)

     1  //go:build xtensa
     2  
     3  package ws2812
     4  
     5  import (
     6  	"device"
     7  	"machine"
     8  	"runtime/interrupt"
     9  	"unsafe"
    10  )
    11  
    12  func (d Device) WriteByte(c byte) error {
    13  	portSet, maskSet := d.Pin.PortMaskSet()
    14  	portClear, maskClear := d.Pin.PortMaskClear()
    15  	mask := interrupt.Disable()
    16  
    17  	switch machine.CPUFrequency() {
    18  	case 160e6: // 160MHz
    19  		// See:
    20  		// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
    21  		// Because I do not know the exact instruction timings, I'm going to
    22  		// assume that every instruction executes in one cycle. Branches and
    23  		// load/stores will probably be slower than that, but as long as all
    24  		// timings are only increased a little bit this should not be a problem
    25  		// (see above post).
    26  		// T0H: 40  cycles or  333.3ns
    27  		// T0L: 131 cycles or 1091.7ns
    28  		//   +: 171 cycles or 1425.0ns
    29  		// T1H: 95  cycles or  791.7ns
    30  		// T1L: 75  cycles or  625.0ns
    31  		//   +: 170 cycles or 1416.7ns
    32  		// Some documentation:
    33  		// http://cholla.mmto.org/esp8266/xtensa.html
    34  		// https://0x04.net/~mwk/doc/xtensa.pdf
    35  		device.AsmFull(`
    36  		1: // send_bit
    37  			s32i  {maskSet}, {portSet}, 0     // [1]  T0H and T1H start here
    38  			nop                               // [37]
    39  			nop
    40  			nop
    41  			nop
    42  			nop
    43  			nop
    44  			nop
    45  			nop
    46  			nop
    47  			nop
    48  			nop
    49  			nop
    50  			nop
    51  			nop
    52  			nop
    53  			nop
    54  			nop
    55  			nop
    56  			nop
    57  			nop
    58  			nop
    59  			nop
    60  			nop
    61  			nop
    62  			nop
    63  			nop
    64  			nop
    65  			nop
    66  			nop
    67  			nop
    68  			nop
    69  			nop
    70  			nop
    71  			nop
    72  			nop
    73  			nop
    74  			nop
    75  			slli  {value}, {value}, 1         // [1]  shift {value} to the left by 1
    76  			bbsi  {value}, 8, 2f              // [1]  branch to skip_store if bit 8 is set
    77  			s32i  {maskClear}, {portClear}, 0 // [1]  T0H -> T0L transition
    78  		2: // skip_store
    79  			nop                               // [55]
    80  			nop
    81  			nop
    82  			nop
    83  			nop
    84  			nop
    85  			nop
    86  			nop
    87  			nop
    88  			nop
    89  			nop
    90  			nop
    91  			nop
    92  			nop
    93  			nop
    94  			nop
    95  			nop
    96  			nop
    97  			nop
    98  			nop
    99  			nop
   100  			nop
   101  			nop
   102  			nop
   103  			nop
   104  			nop
   105  			nop
   106  			nop
   107  			nop
   108  			nop
   109  			nop
   110  			nop
   111  			nop
   112  			nop
   113  			nop
   114  			nop
   115  			nop
   116  			nop
   117  			nop
   118  			nop
   119  			nop
   120  			nop
   121  			nop
   122  			nop
   123  			nop
   124  			nop
   125  			nop
   126  			nop
   127  			nop
   128  			nop
   129  			nop
   130  			nop
   131  			nop
   132  			nop
   133  			nop
   134  			s32i  {maskClear}, {portClear}, 0 // [1]  T1H -> T1L transition
   135  			nop                               // [72]
   136  			nop
   137  			nop
   138  			nop
   139  			nop
   140  			nop
   141  			nop
   142  			nop
   143  			nop
   144  			nop
   145  			nop
   146  			nop
   147  			nop
   148  			nop
   149  			nop
   150  			nop
   151  			nop
   152  			nop
   153  			nop
   154  			nop
   155  			nop
   156  			nop
   157  			nop
   158  			nop
   159  			nop
   160  			nop
   161  			nop
   162  			nop
   163  			nop
   164  			nop
   165  			nop
   166  			nop
   167  			nop
   168  			nop
   169  			nop
   170  			nop
   171  			nop
   172  			nop
   173  			nop
   174  			nop
   175  			nop
   176  			nop
   177  			nop
   178  			nop
   179  			nop
   180  			nop
   181  			nop
   182  			nop
   183  			nop
   184  			nop
   185  			nop
   186  			nop
   187  			nop
   188  			nop
   189  			nop
   190  			nop
   191  			nop
   192  			nop
   193  			nop
   194  			nop
   195  			nop
   196  			nop
   197  			nop
   198  			nop
   199  			nop
   200  			nop
   201  			nop
   202  			nop
   203  			nop
   204  			nop
   205  			nop
   206  			nop
   207  			addi  {i}, {i}, -1                // [1]
   208  			bnez {i}, 1b                      // [1]  send_bit, T1H and T1L end here
   209  
   210  			// Restore original values after modifying them in the inline
   211  			// assembly. Not doing that would result in undefined behavior as
   212  			// the compiler doesn't know we're modifying these values.
   213  			movi.n {i}, 8
   214  			slli  {value}, {value}, 8
   215  		`, map[string]interface{}{
   216  			// Note: casting pointers to uintptr here because of what might be
   217  			// an Xtensa backend bug with inline assembly.
   218  			"value":     uint32(c),
   219  			"i":         8,
   220  			"maskSet":   maskSet,
   221  			"portSet":   uintptr(unsafe.Pointer(portSet)),
   222  			"maskClear": maskClear,
   223  			"portClear": uintptr(unsafe.Pointer(portClear)),
   224  		})
   225  		interrupt.Restore(mask)
   226  		return nil
   227  	case 80e6: // 80MHz
   228  		// See docs for 160MHz.
   229  		// T0H: 21 cycles or  262.5ns
   230  		// T0L: 67 cycles or  837.5ns
   231  		//   +: 88 cycles or 1100.0ns
   232  		// T1H: 47 cycles or  587.5ns
   233  		// T1L: 39 cycles or  487.5ns
   234  		//   +: 86 cycles or 1075.0ns
   235  		device.AsmFull(`
   236  		1: // send_bit
   237  			s32i  {maskSet}, {portSet}, 0     // [1]  T0H and T1H start here
   238  			nop                               // [18]
   239  			nop
   240  			nop
   241  			nop
   242  			nop
   243  			nop
   244  			nop
   245  			nop
   246  			nop
   247  			nop
   248  			nop
   249  			nop
   250  			nop
   251  			nop
   252  			nop
   253  			nop
   254  			nop
   255  			nop
   256  			slli  {value}, {value}, 1         // [1]  shift {value} to the left by 1
   257  			bbsi  {value}, 8, 2f              // [1]  branch to skip_store if bit 8 is set
   258  			s32i  {maskClear}, {portClear}, 0 // [1]  T0H -> T0L transition
   259  		2: // skip_store
   260  			nop                               // [27]
   261  			nop
   262  			nop
   263  			nop
   264  			nop
   265  			nop
   266  			nop
   267  			nop
   268  			nop
   269  			nop
   270  			nop
   271  			nop
   272  			nop
   273  			nop
   274  			nop
   275  			nop
   276  			nop
   277  			nop
   278  			nop
   279  			nop
   280  			nop
   281  			nop
   282  			nop
   283  			nop
   284  			nop
   285  			nop
   286  			nop
   287  			s32i  {maskClear}, {portClear}, 0 // [1]  T1H -> T1L transition
   288  			nop                               // [36]
   289  			nop
   290  			nop
   291  			nop
   292  			nop
   293  			nop
   294  			nop
   295  			nop
   296  			nop
   297  			nop
   298  			nop
   299  			nop
   300  			nop
   301  			nop
   302  			nop
   303  			nop
   304  			nop
   305  			nop
   306  			nop
   307  			nop
   308  			nop
   309  			nop
   310  			nop
   311  			nop
   312  			nop
   313  			nop
   314  			nop
   315  			nop
   316  			nop
   317  			nop
   318  			nop
   319  			nop
   320  			nop
   321  			nop
   322  			nop
   323  			nop
   324  			addi  {i}, {i}, -1                // [1]
   325  			bnez {i}, 1b                      // [1]  send_bit, T1H and T1L end here
   326  
   327  			// Restore original values after modifying them in the inline
   328  			// assembly. Not doing that would result in undefined behavior as
   329  			// the compiler doesn't know we're modifying these values.
   330  			movi.n {i}, 8
   331  			slli  {value}, {value}, 8
   332  		`, map[string]interface{}{
   333  			// Note: casting pointers to uintptr here because of what might be
   334  			// an Xtensa backend bug with inline assembly.
   335  			"value":     uint32(c),
   336  			"i":         8,
   337  			"maskSet":   maskSet,
   338  			"portSet":   uintptr(unsafe.Pointer(portSet)),
   339  			"maskClear": maskClear,
   340  			"portClear": uintptr(unsafe.Pointer(portClear)),
   341  		})
   342  		interrupt.Restore(mask)
   343  		return nil
   344  	default:
   345  		interrupt.Restore(mask)
   346  		return errUnknownClockSpeed
   347  	}
   348  }