Skip to content

Commit c799636

Browse files
committed
[rp] PIO with compile-time assembler
1 parent 85b8770 commit c799636

10 files changed

Lines changed: 1531 additions & 0 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) 2026, Andrey Kunitsyn
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#include <modm/board.hpp>
13+
#include "ws2812.pio.hpp"
14+
using namespace Board;
15+
16+
17+
struct HW
18+
{
19+
using PIO = modm::platform::Pio0;
20+
using PIO_SM = PIO::StateMachine<0>;
21+
using DataGpio = modm::platform::GpioOutput16;
22+
};
23+
24+
25+
int
26+
main()
27+
{
28+
29+
30+
Board::initialize();
31+
32+
HW::DataGpio::setOutput(Gpio::OutputType::PushPull, Gpio::SlewRate::Fast);
33+
HW::DataGpio::setDriveStrength(Gpio::DriveStrength::mA_12);
34+
35+
auto pio_program_offset = HW::PIO::addProgram(ws2812::program);
36+
37+
ws2812::init<Board::SystemClock,HW::PIO,HW::PIO_SM,HW::DataGpio>(pio_program_offset);
38+
39+
constexpr auto delay_val = 5ms;
40+
41+
while (true)
42+
{
43+
uint32_t clr = 0;
44+
while (clr!=0xff0000) {
45+
clr = clr + 0x010000;
46+
HW::PIO_SM::writeBlocking(clr << 8);
47+
modm::delay(delay_val);
48+
}
49+
while (clr!=0x000000) {
50+
clr = clr - 0x010000;
51+
HW::PIO_SM::writeBlocking(clr << 8);
52+
modm::delay(delay_val);
53+
}
54+
while (clr!=0x00ff00) {
55+
clr = clr + 0x000100;
56+
HW::PIO_SM::writeBlocking(clr << 8);
57+
modm::delay(delay_val);
58+
}
59+
while (clr!=0x000000) {
60+
clr = clr - 0x000100;
61+
HW::PIO_SM::writeBlocking(clr << 8);
62+
modm::delay(delay_val);
63+
}
64+
while (clr!=0x0000ff) {
65+
clr = clr + 0x000001;
66+
HW::PIO_SM::writeBlocking(clr << 8);
67+
modm::delay(delay_val);
68+
}
69+
while (clr!=0x000000) {
70+
clr = clr - 0x000001;
71+
HW::PIO_SM::writeBlocking(clr << 8);
72+
modm::delay(delay_val);
73+
}
74+
75+
}
76+
77+
return 0;
78+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<library>
2+
<extends>modm:rp-pico</extends>
3+
<options>
4+
<option name="modm:build:build.path">../../../build/rp_pico/pio_ws2812</option>
5+
</options>
6+
<modules>
7+
<module>modm:build:scons</module>
8+
<module>modm:platform:pio:0</module>
9+
</modules>
10+
</library>
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (c) 2026, Andrey Kunitsyn
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#pragma once
13+
14+
#include <modm/board.hpp>
15+
16+
/* from https://github.com/raspberrypi/pico-examples/blob/master/pio/ws2812/ws2812.pio */
17+
18+
/* .program ws2812 */
19+
namespace ws2812
20+
{
21+
using namespace modm::platform::pio;
22+
23+
// constants WS2812
24+
static constexpr uint32_t T0H = 350; // ns
25+
static constexpr uint32_t T0L = 800;
26+
static constexpr uint32_t T1H = 700;
27+
static constexpr uint32_t T1L = 600;
28+
29+
// constants WS2812B
30+
// static constexpr uint32_t T0H = 400; // ns
31+
// static constexpr uint32_t T0L = 850;
32+
// static constexpr uint32_t T1H = 850;
33+
// static constexpr uint32_t T1L = 400;
34+
35+
static constexpr uint32_t TimeScale = 50;
36+
37+
static constexpr uint32_t TH_Common = T0H/TimeScale;
38+
static constexpr uint32_t TH_Add = (T1H-T0H)/TimeScale;
39+
static constexpr uint32_t TL_Common = T1L/TimeScale;
40+
static constexpr uint32_t TL_Add = (T0L-T1L)/TimeScale;
41+
42+
// labels
43+
struct bitloop {};
44+
struct do_one {};
45+
struct do_zero {};
46+
47+
/*
48+
.side_set 1
49+
.wrap_target
50+
bitloop:
51+
out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
52+
jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
53+
do_one:
54+
jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
55+
do_zero:
56+
nop side 0 [T2 - 1] ; Or drive low, for a short pulse
57+
.wrap
58+
*/
59+
60+
// PIO program
61+
static constexpr auto program = PIOProgram::begin()
62+
.sideset<1>
63+
.wrapTarget<>
64+
.label<bitloop>
65+
.instr(pio::Out.x<1> .side<0>.delay<TL_Common-1>)
66+
.instr(pio::Jmp.not_x.to<do_zero> .side<1>.delay<TH_Common-1>)
67+
.label<do_one>
68+
.instr(pio::Jmp.to<bitloop> .side<1>.delay<TH_Add-1>)
69+
.label<do_zero>
70+
.instr(pio::Nop .side<0>.delay<TL_Add-1>)
71+
.wrap<>
72+
.end();
73+
74+
/*
75+
pio_gpio_init(pio, pin);
76+
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
77+
78+
pio_sm_config c = ws2812_program_get_default_config(offset);
79+
sm_config_set_sideset_pins(&c, pin);
80+
sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
81+
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
82+
83+
int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
84+
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
85+
sm_config_set_clkdiv(&c, div);
86+
87+
pio_sm_init(pio, sm, offset, &c);
88+
pio_sm_set_enabled(pio, sm, true);
89+
*/
90+
// initialization code
91+
template <typename SystemClock,typename PIO,typename SM,typename DataGpio>
92+
static constexpr void init(uint16_t offset) {
93+
PIO::template connect<typename DataGpio::Pad>();
94+
SM::template addOutput<DataGpio>();
95+
96+
SM::config(offset,program)
97+
.template setSidesetPins<DataGpio>()
98+
.template setOutShift<false,true,24>()
99+
.setFifoJoinTx()
100+
.template setFrequency<SystemClock,1000000000/TimeScale>()
101+
.init(offset+program.getOffset<bitloop>());
102+
SM::setEnabled(true);
103+
}
104+
105+
}

src/modm/platform/pio/rp/module.lb

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2024, Andrey Kunitsyn
5+
#
6+
# This file is part of the modm project.
7+
#
8+
# This Source Code Form is subject to the terms of the Mozilla Public
9+
# License, v. 2.0. If a copy of the MPL was not distributed with this
10+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
11+
# -----------------------------------------------------------------------------
12+
13+
14+
class Instance(Module):
15+
def __init__(self, instance):
16+
self.instance = instance
17+
18+
def init(self, module):
19+
module.name = str(self.instance)
20+
module.description = "PIO {} instance".format(self.instance)
21+
22+
def prepare(self, module, options):
23+
return True
24+
25+
def build(self, env):
26+
properties = {
27+
"id": self.instance,
28+
}
29+
env.substitutions = properties
30+
env.outbasepath = "modm/src/modm/platform/pio"
31+
32+
env.template("pio.hpp.in", "pio_{}.hpp".format(self.instance))
33+
env.template("pio.cpp.in", "pio_{}.cpp".format(self.instance))
34+
35+
36+
37+
def init(module):
38+
module.name = ":platform:pio"
39+
module.description = "Programmable IO block (PIO)"
40+
41+
def prepare(module, options):
42+
device = options[":target"]
43+
if not device.has_driver("pio:rp*"):
44+
return False
45+
46+
module.depends(
47+
":platform:gpio",
48+
":platform:clockgen",
49+
":architecture:interrupt",
50+
":architecture:fiber")
51+
52+
for instance in listify(device.get_driver("pio")["instance"]):
53+
module.add_submodule(Instance(instance))
54+
55+
return True
56+
57+
def build(env):
58+
env.outbasepath = "modm/src/modm/platform/pio"
59+
env.copy("pio_asm.hpp")
60+
env.copy("pio_program.hpp")
61+
env.copy("pio_sm.hpp")
62+
pass
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2022, Andrey Kunitsyn
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#include <modm/architecture/driver/atomic/queue.hpp>
13+
#include <modm/architecture/interface/atomic_lock.hpp>
14+
#include <modm/architecture/interface/interrupt.hpp>
15+
#include <modm/platform/core/resets.hpp>
16+
17+
#include "../device.hpp"
18+
19+
#include "pio_{{ id }}.hpp"
20+
21+
// ----------------------------------------------------------------------------
22+
23+
namespace modm::platform
24+
{
25+
uint32_t Pio{{ id }}::used_instruction_space = 0;
26+
}
27+
28+
29+
30+
// ----------------------------------------------------------------------------
31+
MODM_ISR(PIO{{ id }}_IRQ_0)
32+
{
33+
34+
}
35+
36+
MODM_ISR(PIO{{ id }}_IRQ_1)
37+
{
38+
39+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright (c) 2022, Andrey Kunitsyn
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#pragma once
13+
14+
#include <modm/architecture/interface/peripheral.hpp>
15+
#include <modm/platform/core/peripherals.hpp>
16+
#include <modm/platform/gpio/connector.hpp>
17+
#include <modm/math/algorithm/prescaler.hpp>
18+
19+
#include "pio_program.hpp"
20+
#include "pio_sm.hpp"
21+
#include <hardware/structs/pio.h>
22+
23+
namespace modm::platform
24+
{
25+
26+
27+
/**
28+
* Programmable IO block (PIO)
29+
*
30+
* @ingroup modm_platform_pio
31+
* @author Andrey Kunitsyn
32+
*/
33+
class Pio{{ id }} : public ::modm::PeripheralDriver
34+
{
35+
static inline pio_hw_t& pio() { return *pio{{ id }}_hw; }
36+
37+
static uint32_t used_instruction_space;
38+
static int findOffset(const PIOProgram& prg) {
39+
uint32_t program_mask = (1u << prg.length) - 1;
40+
if (prg.origin >= 0) {
41+
if (prg.origin > 32 - prg.length) return -1;
42+
return used_instruction_space & (program_mask << prg.origin) ? -1 : prg.origin;
43+
} else {
44+
// work down from the top always
45+
for (int i = 32 - prg.length; i >= 0; i--) {
46+
if ((used_instruction_space & (program_mask << static_cast<unsigned int>(i)))==0) {
47+
return i;
48+
}
49+
}
50+
return -1;
51+
}
52+
}
53+
static void addProgramAtOffset(size_t offset,const PIOProgram& prg) {
54+
for (uint8_t i = 0; i < prg.length; ++i) {
55+
auto& instr = prg.instructions[i];
56+
pio().instr_mem[offset + i] = instr.store(offset);
57+
}
58+
uint32_t program_mask = (1u << prg.length) - 1;
59+
used_instruction_space |= program_mask << offset;
60+
}
61+
62+
template <typename Pio,size_t SM>
63+
friend class pio::implementation::StateMachine;
64+
65+
public:
66+
static constexpr Peripheral peripherial = Peripheral::Pio{{ id }};
67+
template <size_t SM>
68+
class StateMachine : public modm::platform::pio::implementation::StateMachine<Pio{{ id }},SM> {};
69+
70+
template< class... Signals >
71+
static void
72+
connect()
73+
{
74+
using Connector = GpioConnector<peripherial, Signals...>;
75+
Connector::connect();
76+
}
77+
78+
template< class Pin >
79+
static void connectPin() {
80+
using Connector = GpioConnector<peripherial, typename Pin::Pad>;
81+
Connector::connect();
82+
}
83+
84+
template <typename Program>
85+
static size_t addProgram(const Program& prg_data) {
86+
auto prg = PIOProgram::get(prg_data);
87+
auto offset = findOffset(prg);
88+
if (offset < 0) {
89+
return offset;
90+
}
91+
auto res = static_cast<size_t>(offset);
92+
addProgramAtOffset(res,prg);
93+
return res;
94+
}
95+
};
96+
97+
} // namespace modm::platform
98+

0 commit comments

Comments
 (0)