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 }