/ src / main.cpp
main.cpp
  1  #include <iostream>
  2  #include <pybind11/pybind11.h>
  3  
  4  #include "piolib.h"
  5  #include "utils/piolib/examples/ws2812.pio.h"
  6  
  7  #define STRINGIFY(x) #x
  8  #define MACRO_STRINGIFY(x) STRINGIFY(x)
  9  
 10  namespace py = pybind11;
 11  
 12  namespace {
 13  
 14  typedef struct { uint32_t value;
 15  } pio_pinmask_t;
 16  typedef uint32_t pio_pinmask_value_t;
 17  #define PIO_PINMASK_C(c) UINT32_C(c)
 18  #define PIO_PINMASK_BIT (32)
 19  #define PIO_PINMASK(i) (UINT32_C(1) << (i))
 20  #define PIO_PINMASK_PRINT(p) mp_printf(&mp_plat_print, "%s:%d: %s = %08x\n", \
 21      __FILE__, __LINE__, #p, \
 22      (uint32_t)(PIO_PINMASK_VALUE(p)));
 23  #define PIO_PINMASK_ALL PIO_PINMASK_FROM_VALUE(~UINT32_C(0))
 24  
 25  #define PIO_PINMASK_VALUE(p) ((p).value)
 26  #define PIO_PINMASK_FROM_VALUE(v) ((pio_pinmask_t) {(v)})
 27  #define PIO_PINMASK_FROM_PIN(i) ((pio_pinmask_t) {(PIO_PINMASK(i))})
 28  #define PIO_PINMASK_NONE PIO_PINMASK_FROM_VALUE(0)
 29  #define PIO_PINMASK_SET(p, i) ((p).value |= PIO_PINMASK(i))
 30  #define PIO_PINMASK_CLEAR(p, i) ((p).value &= ~PIO_PINMASK(i))
 31  #define PIO_PINMASK_IS_SET(p, i) (((p).value & PIO_PINMASK(i)) != 0)
 32  #define PIO_PINMASK_BINOP(op, p, q) PIO_PINMASK_FROM_VALUE((p).value op(q).value)
 33  #define PIO_PINMASK_BINOP_ASSIGN(op, p, q) ((p).value op(q).value)
 34  #define PIO_PINMASK_EQUAL(p, q) ((p).value == (q).value)
 35  #define PIO_PINMASK_AND(p, q) PIO_PINMASK_BINOP(&, (p), (q))
 36  #define PIO_PINMASK_AND_NOT(p, q) PIO_PINMASK_BINOP(&~, (p), (q))
 37  #define PIO_PINMASK_OR(p, q) PIO_PINMASK_BINOP(|, (p), (q))
 38  #define PIO_PINMASK_OR3(p, q, r) PIO_PINMASK_OR((p), PIO_PINMASK_OR((q), (r)))
 39  #define PIO_PINMASK_INTERSECT(p, q) PIO_PINMASK_BINOP_ASSIGN( &=, (p), (q))
 40  #define PIO_PINMASK_DIFFERENCE(p, q) PIO_PINMASK_BINOP_ASSIGN( &= ~, (p), (q))
 41  #define PIO_PINMASK_MERGE(p, q) PIO_PINMASK_BINOP_ASSIGN( |=, (p), (q))
 42  
 43  #define PIO_PINMASK_CONSECUTIVE_PINS(start, count) PIO_PINMASK_FROM_VALUE(((PIO_PINMASK_C(1) << count) - 1) << start)
 44  #define PIO_PINMASK_SHIFTED(p, count) PIO_PINMASK_FROM_VALUE(PIO_PINMASK_FROM_VALUE(p) << count)
 45  
 46  PIO pio_open_check() {
 47      if (pio_init()) {
 48          throw std::runtime_error("PIO not available");
 49      }
 50  
 51      PIO pio = pio_open(0);
 52      if(PIO_IS_ERR(pio)) {
 53          throw std::runtime_error(
 54              py::str("Failed to open PIO device (error {})").attr("format")(PIO_ERR_VAL(pio)).cast<std::string>());
 55      }
 56      return pio;
 57  }
 58  
 59  int pio_sm_claim_unused_sm_check(PIO pio) {
 60      int sm = pio_claim_unused_sm(pio, false);
 61      if (sm < 0) {
 62          throw std::runtime_error("No state machine available");
 63      }
 64      return sm;
 65  }
 66  
 67  int pio_add_program_check(PIO pio, const struct pio_program *program) {
 68      int offset = pio_add_program(pio, program);
 69      if (offset < 0) {
 70          throw std::runtime_error("Could not load program");
 71      }
 72      return offset;
 73  }
 74  
 75  int get_pin_number(py::object gpio_obj) {
 76      return py::getattr(gpio_obj, "_pin", gpio_obj).attr("id").cast<int>();
 77  }
 78  
 79  #ifndef NUM_BANK0_GPIOS
 80  #define NUM_BANK0_GPIOS (28)
 81  #endif
 82  
 83  static void rp2pio_statemachine_set_pull(pio_pinmask_t pull_pin_up, pio_pinmask_t pull_pin_down, pio_pinmask_t pins_we_use) {
 84      for (size_t i = 0; i < NUM_BANK0_GPIOS; i++) {
 85          bool used = PIO_PINMASK_IS_SET(pins_we_use, i);
 86          if (used) {
 87              bool pull_up = PIO_PINMASK_IS_SET(pull_pin_up, i);
 88              bool pull_down = PIO_PINMASK_IS_SET(pull_pin_down, i);
 89              gpio_set_pulls(i, pull_up, pull_down);
 90          }
 91      }
 92  }
 93  
 94  template<class T>
 95  int get_default(py::object o, T default_value) {
 96      if (o.is_none()) { return default_value; }
 97      return o.cast<T>();
 98  }
 99  
100  class StateMachine {
101      PIO pio{};
102      int sm{-1};
103      int offset{-1};
104      double frequency;
105  
106      void check_for_deinit() {
107          if(PIO_IS_ERR(pio)) {
108              throw std::runtime_error("StateMachine object has been deinitialized");
109          }
110      }
111  public:
112      StateMachine(py::buffer assembled,
113              double frequency_in,
114              int8_t offset,
115              py::buffer init,
116              py::object first_sideset_pin,
117              int sideset_pin_count,
118              bool sideset_enable,
119              py::object first_in_pin,
120              int in_pin_count,
121              uint32_t pull_in_pin_up,
122              uint32_t pull_in_pin_down,
123              bool auto_pull,
124              bool out_shift_right,
125              int pull_threshold,
126              bool auto_push,
127              bool in_shift_right,
128              int push_threshold,
129              int wrap,
130              int wrap_target) {
131          pio = pio_open_check();
132          sm = pio_sm_claim_unused_sm_check(pio);
133          py::buffer_info info = assembled.request();
134          if (info.itemsize != 2) {
135              throw py::value_error("assembled: Expected array of type `H`");
136          }
137          if (info.size >= 32) {
138              throw py::value_error("assembled: Exceeds maximum program length (32)");
139          }
140          if (offset < -1 || offset > 31) {
141              throw py::value_error("offset exceeds valid range of -1 to 31 inclusive");
142          }
143          if (!init.is_none()) {
144              py::buffer_info init_info = init.request();
145              if (info.itemsize != 2) {
146                  throw py::value_error("init: Expected array of type `H`");
147              }
148          }
149  
150          ssize_t program_len = info.size;
151          if(wrap == -1)  {
152              wrap = program_len - 1;
153          }
154  
155          if(wrap < 0 || wrap >= program_len) {
156              throw py::value_error("wrap out of range");
157          }
158  
159          if(wrap_target < 0 || wrap >= program_len) {
160              throw py::value_error("wrap_target out of range");
161          }
162  
163          pio_pinmask_t pindirs = PIO_PINMASK_NONE;
164          pio_pinmask_t pins_we_use = PIO_PINMASK_NONE;
165          pio_pinmask_t pin_pull_up = PIO_PINMASK_NONE;
166          pio_pinmask_t pin_pull_down = PIO_PINMASK_NONE;
167  
168          struct pio_program program = {
169              .instructions = reinterpret_cast<uint16_t*>(info.ptr),
170              .length = static_cast<uint8_t>(info.size),
171              .origin = offset,
172          };
173  
174          offset = pio_add_program_check(pio, &program);
175          wrap += offset;
176          wrap_target += offset;
177  
178          pio_sm_config c = pio_get_default_sm_config();
179          sm_config_set_wrap(&c, wrap_target, wrap);
180  
181          if (!first_sideset_pin.is_none()) {
182              auto first_sideset_pin_number = get_pin_number(first_sideset_pin);
183              if (sideset_pin_count < 1 || (sideset_enable + sideset_pin_count) > 5 || first_sideset_pin_number + sideset_pin_count > NUM_BANK0_GPIOS) {
184                  throw py::value_error("sideset_pin_count out of range");
185              }
186  
187              PIO_PINMASK_MERGE(pindirs, PIO_PINMASK_CONSECUTIVE_PINS(sideset_pin_count, first_sideset_pin_number));
188              PIO_PINMASK_MERGE(pins_we_use, PIO_PINMASK_CONSECUTIVE_PINS(sideset_pin_count, first_sideset_pin_number));
189  
190              for(int i=0; i<sideset_pin_count; i++) {
191                  pio_gpio_init(pio, first_sideset_pin_number + i);
192              }
193              sm_config_set_sideset(&c, sideset_pin_count + sideset_enable, /* optional */ sideset_enable, /* pindirs */ false);
194              sm_config_set_sideset_pins(&c, first_sideset_pin_number);
195          }
196  
197          if (!first_in_pin.is_none()) {
198              auto first_in_pin_number = get_pin_number(first_in_pin);
199              if (in_pin_count < 1 || first_in_pin_number + in_pin_count > NUM_BANK0_GPIOS) {
200                  throw py::value_error("sideset_pin_count out of range");
201              }
202              for(int i=0; i<in_pin_count; i++) {
203                  pio_gpio_init(pio, first_in_pin_number + i);
204              }
205              sm_config_set_in_pins(&c, first_in_pin_number);
206              PIO_PINMASK_MERGE(pin_pull_up, PIO_PINMASK_FROM_VALUE(pull_in_pin_up << first_in_pin_number));
207              PIO_PINMASK_MERGE(pin_pull_down, PIO_PINMASK_FROM_VALUE(pull_in_pin_down << first_in_pin_number));
208              PIO_PINMASK_MERGE(pins_we_use, PIO_PINMASK_CONSECUTIVE_PINS(in_pin_count, first_in_pin_number));
209          }
210  
211          pio_sm_set_pindirs_with_mask(pio, sm, PIO_PINMASK_VALUE(pindirs), PIO_PINMASK_VALUE(pins_we_use));
212          rp2pio_statemachine_set_pull(pin_pull_up, pin_pull_down, pins_we_use);
213  
214          if (!init.is_none()) {
215              run(init);
216          }
217  
218          sm_config_set_out_shift(&c, out_shift_right, auto_pull, pull_threshold);
219          sm_config_set_in_shift(&c, in_shift_right, auto_push, push_threshold);
220  
221          double div = frequency_in ? clock_get_hz(clk_sys) / frequency_in : 1.0;
222          int div_int = (int) div;
223          int div_frac = (int) ((div_int- div) * 256);
224          sm_config_set_clkdiv_int_frac(&c, div_int, div_frac);
225          frequency = clock_get_hz(clk_sys) / (div_int + div_frac / 256.);
226  
227          pio_sm_init(pio, sm, offset, &c);
228          pio_sm_set_enabled(pio, sm, true);
229  
230          if (pio_sm_config_xfer(pio, sm, PIO_DIR_TO_SM, 65532, 2)) {
231              throw std::runtime_error("pio_sm_config_xfer(PIO_DIR_TO_SM) failed");
232          }
233          if (pio_sm_config_xfer(pio, sm, PIO_DIR_FROM_SM, 65532, 2)) {
234              throw std::runtime_error("pio_sm_config_xfer(PIO_DIR_FROM_SM) failed");
235          }
236      }
237  
238      void run(py::buffer instructions) {
239          check_for_deinit();
240          py::buffer_info info = instructions.request();
241          if (info.itemsize != 2) {
242              throw py::value_error("instructions: Expected array of type `H`");
243          }
244          auto raw_instructions = reinterpret_cast<const uint16_t *>(info.ptr);
245          for (ssize_t i = 0; i < info.size; i++) {
246              pio_sm_exec(pio, sm, raw_instructions[i]);
247          }
248      }
249  
250      void deinit() {
251          if(!PIO_IS_ERR(pio)) pio_close(pio);
252          pio = nullptr;
253      }
254  
255      ~StateMachine() {
256          deinit();
257      }
258  
259  
260      void readinto(py::buffer b) {
261          check_for_deinit();
262  
263          py::buffer_info info = b.request();
264          uint32_t *info_ptr32 = reinterpret_cast<uint32_t*>(info.ptr);
265          uint32_t *ptr = info_ptr32;
266  
267          if (info.readonly) {
268              throw py::type_error("read-only buffer");
269          }
270  
271          std::vector<uint32_t> vec;
272          // the DMA controller doesn't replicate 8- and 16-bit values like on rp2, so we have to do it ourselves
273          if (info.itemsize != 4) {
274              vec.reserve(info.size);
275              switch(info.itemsize) {
276                  case 1:
277                      break;
278                  case 2:
279                      break;
280                  default:
281                      throw py::value_error("buffer must contain items of 1, 2, or 4 bytes");
282              }
283              ptr = &vec[0];
284          }
285  
286          size_t size = info.size * sizeof(uint32_t);
287          if (pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, size, ptr)) {
288              throw std::runtime_error("pio_sm_xfer_data() failed");
289          }
290  
291          switch(info.itemsize) {
292              case 1:
293                  {
294                      uint8_t *info_ptr8 = reinterpret_cast<uint8_t*>(info.ptr);
295                      for(ssize_t i=0; i<info.size; i++) {
296                          info_ptr8[i] = vec[i];
297                      }
298                  }
299                  break;
300              case 2:
301                  {
302                      uint16_t *info_ptr16 = reinterpret_cast<uint16_t*>(info.ptr);
303                      for(ssize_t i=0; i<info.size; i++) {
304                          info_ptr16[i] = vec[i];
305                      }
306                  }
307                  break;
308          }
309      }
310  
311      void write(py::buffer b) {
312          check_for_deinit();
313  
314          py::buffer_info info = b.request();
315          uint32_t *ptr = reinterpret_cast<uint32_t*>(info.ptr);
316          std::vector<uint32_t> vec;
317          // the DMA controller doesn't replicate 8- and 16-bit values like on rp2, so we have to do it ourselves
318          if (info.itemsize != 4) {
319              vec.reserve(info.size);
320              switch(info.itemsize) {
321                  case 1:
322                  {
323                      auto *buf = reinterpret_cast<uint8_t*>(info.ptr);
324                      for(pybind11::ssize_t i=0; i<info.size; i++) {
325                          vec.push_back(buf[i] * 0x01010101);
326                      }
327                      break;
328                  }
329                  case 2:
330                  {
331                      auto *buf = reinterpret_cast<uint16_t*>(info.ptr);
332                      for(pybind11::ssize_t i=0; i<info.size; i++) {
333                          vec.push_back(buf[i] * 0x00010001);
334                      }
335                  }
336                      break;
337                  default:
338                      throw py::value_error("buffer must contain items of 1, 2, or 4 bytes");
339              }
340              ptr = &vec[0];
341          }
342          size_t size = info.size * sizeof(uint32_t);
343          if (pio_sm_xfer_data(pio, sm, PIO_DIR_TO_SM, size, ptr)) {
344              throw std::runtime_error("pio_sm_xfer_data() failed");
345          }
346      }
347  
348      double get_frequency() const {
349          return frequency;
350      }
351  };
352  
353  PYBIND11_MODULE(adafruit_rp1pio, m) {
354      m.doc() = R"pbdoc(
355          Hardware interface to RP1 series’ programmable IO (PIO) peripheral.
356          -------------------------------------------------------------------
357  
358          Except as noted, this is intended to be a subset of the functionality
359          in CircuitPython's ``rp2pio`` module.
360  
361          .. currentmodule:: adafruit_rp1pio
362  
363          .. autosummary::
364             :toctree: _generate
365  
366             StateMachine
367      )pbdoc";
368  
369  
370      py::class_<StateMachine>(m, "StateMachine", "A single PIO StateMachine")
371          .def(py::init<py::buffer /* assembled */,
372                  double /* frequency */,
373                  int8_t /* offset */,
374                  py::buffer /* init */,
375                  py::object /* first_sideset_pin */,
376                  int /* sideset_pin_count */,
377                  bool /* sideset_enable */,
378                  py::object /* first_in_pin */,
379                  int /* in_pin_count */,
380                  uint32_t /* pin_in_pull_up */,
381                  uint32_t /* pin_in_pull_down */,
382                  bool /* auto_pull */,
383                  bool /* out_shift_right */,
384                  int /* pull_threshold */,
385                  bool /* auto_push */,
386                  bool /* in_shift_right */,
387                  int /* push_threshold */,
388                  int /* wrap */,
389                  int /* wrap_target */ >(),
390              "Construct a StateMachine",
391              py::arg("assembled"),
392              py::arg("frequency"),
393              py::kw_only(),
394              py::arg("offset") = -1,
395              py::arg("init") = py::none(),
396              py::arg("first_sideset_pin") = py::none(),
397              py::arg("sideset_pin_count") = 1,
398              py::arg("sideset_enable") = false,
399              py::arg("first_in_pin") = py::none(),
400              py::arg("in_pin_count") = 1,
401              py::arg("pull_in_pin_up") = 0,
402              py::arg("pull_in_pin_down") = 0,
403              py::arg("auto_pull") = false,
404              py::arg("out_shift_right") = true,
405              py::arg("pull_threshold") = 32,
406              py::arg("auto_push") = false,
407              py::arg("in_shift_right") = true,
408              py::arg("push_threshold") = 32,
409              py::arg("wrap") = -1,
410              py::arg("wrap_target") = 0
411              )
412          .def("write", &StateMachine::write, "Write the data contained in buffer to the state machine", py::arg("buffer"))
413          .def("readinto", &StateMachine::readinto, "Read data from the state machine into a buffer", py::arg("buffer"))
414          .def("run", &StateMachine::run, "Execute instructions on the state machine", py::arg("instructions"))
415          .def("deinit", &StateMachine::deinit, "Releases the resources associated with the state machine.")
416          .def("__enter__", [](StateMachine &m) { return m; },
417              "Enables using a StateMachine object in a `with` statement.")
418          .def("__exit__", [](StateMachine &m, py::object unused1, py::object unused2, py::object unused3) { m.deinit(); },
419              """Release the hardware resources associated with this object""",
420              py::arg("exc_type"),
421              py::arg("exc_value"),
422              py::arg("exc_traceback"))
423          .def_property_readonly("frequency", &StateMachine::get_frequency, "The state machine's actual frequency");
424  #ifdef VERSION_INFO
425      m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO);
426  #else
427      m.attr("__version__") = "dev";
428  #endif
429  }
430  }