Skip to content

Commit

Permalink
[rp] PIO with compile-time assembler
Browse files Browse the repository at this point in the history
  • Loading branch information
andryblack committed Oct 23, 2024
1 parent cedb3aa commit 2a28199
Show file tree
Hide file tree
Showing 8 changed files with 1,178 additions and 0 deletions.
148 changes: 148 additions & 0 deletions examples/rp_pico/pio_ws2812/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2016, Sascha Schade
* Copyright (c) 2017, Niklas Hauser
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#include <modm/board.hpp>
using namespace Board;

/* from https://github.com/raspberrypi/pico-examples/blob/master/pio/ws2812/ws2812.pio */

// constants WS2812
// static constexpr uint32_t T0H = 350; // ns
// static constexpr uint32_t T0L = 800;
// static constexpr uint32_t T1H = 700;
// static constexpr uint32_t T1L = 600;

// constants WS2812B
static constexpr uint32_t T0H = 400; // ns
static constexpr uint32_t T0L = 850;
static constexpr uint32_t T1H = 850;
static constexpr uint32_t T1L = 400;

static constexpr uint32_t TimeScale = 50;

static constexpr uint32_t TH_Common = T0H/TimeScale;
static constexpr uint32_t TH_Add = (T1H-T0H)/TimeScale;
static constexpr uint32_t TL_Common = T1L/TimeScale;
static constexpr uint32_t TL_Add = (T0L-T1L)/TimeScale;

static constexpr uint32_t T3 = 12;//

// labels
struct bitloop {};
struct do_one {};
struct do_zero {};


/*
.wrap_target
bitloop:
out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
do_one:
jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
do_zero:
nop side 0 [T2 - 1] ; Or drive low, for a short pulse
.wrap
*/
// PIO program
static constexpr auto pio_prog = modm::platform::PIOProgram::begin()
.sideset<1>()
.wrapTarget()
.label<bitloop>()
.instr(pio::Out().x<1>() .side<0>().delay<TL_Common-1>())
.instr(pio::Jmp().not_x().to<do_zero>() .side<1>().delay<TH_Common-1>())
.label<do_one>()
.instr(pio::Jmp().to<bitloop>() .side<1>().delay<TH_Add-1>())
.label<do_zero>()
.instr(pio::Nop() .side<0>().delay<TL_Add-1>())
.wrap()
.end();


struct HW
{
using PIO = modm::platform::Pio0;
using PIO_SM = PIO::StateMachine<0>;
using DataGpio = modm::platform::GpioOutput23;
};

static inline void write(uint32_t d) {
while (HW::PIO_SM::txFifoFull()) {__NOP();}
HW::PIO_SM::write(d << 8);
}

int
main()
{


Board::initialize();

HW::DataGpio::setOutput(Gpio::OutputType::PushPull, Gpio::SlewRate::Fast);
HW::DataGpio::setDriveStrength(Gpio::DriveStrength::mA_12);

auto pio_prog_offset = HW::PIO::addProgram(pio_prog);

HW::PIO::connect<HW::DataGpio::Pad>();

HW::PIO_SM::addOutput<HW::DataGpio>();

HW::PIO_SM::config()
.setup(pio_prog_offset,pio_prog)
.setSidesetPins<HW::DataGpio,1,false,false>()
.setFifoJoinTx()
.setOutShift<false,true,24>()
.setFrequency<Board::SystemClock,1000000000/TimeScale>()
.init(pio_prog_offset+pio_prog.getOffset<bitloop>());

HW::PIO_SM::setEnabled(true);

constexpr auto delay_val = 5ms;

while (true)
{
uint32_t clr = 0;
while (clr!=0xff0000) {
clr = clr + 0x010000;
write(clr);
modm::delay(delay_val);
}
while (clr!=0x000000) {
clr = clr - 0x010000;
write(clr);
modm::delay(delay_val);
}
while (clr!=0x00ff00) {
clr = clr + 0x000100;
write(clr);
modm::delay(delay_val);
}
while (clr!=0x000000) {
clr = clr - 0x000100;
write(clr);
modm::delay(delay_val);
}
while (clr!=0x0000ff) {
clr = clr + 0x000001;
write(clr);
modm::delay(delay_val);
}
while (clr!=0x000000) {
clr = clr - 0x000001;
write(clr);
modm::delay(delay_val);
}

}

return 0;
}
10 changes: 10 additions & 0 deletions examples/rp_pico/pio_ws2812/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<library>
<extends>modm:rp-pico</extends>
<options>
<option name="modm:build:build.path">../../../build/rp_pico/pio_ws2812</option>
</options>
<modules>
<module>modm:build:scons</module>
<module>modm:platform:pio:0</module>
</modules>
</library>
61 changes: 61 additions & 0 deletions src/modm/platform/pio/rp/module.lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2024, Andrey Kunitsyn
#
# This file is part of the modm project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# -----------------------------------------------------------------------------


class Instance(Module):
def __init__(self, instance):
self.instance = instance

def init(self, module):
module.name = str(self.instance)
module.description = "PIO {} instance".format(self.instance)

def prepare(self, module, options):
return True

def build(self, env):
properties = {
"id": self.instance,
}
env.substitutions = properties
env.outbasepath = "modm/src/modm/platform/pio"

env.template("pio.hpp.in", "pio_{}.hpp".format(self.instance))
env.template("pio.cpp.in", "pio_{}.cpp".format(self.instance))



def init(module):
module.name = ":platform:pio"
module.description = "Programmable IO block (PIO)"

def prepare(module, options):
device = options[":target"]
if not device.has_driver("pio:rp*"):
return False

module.depends(
":platform:gpio",
":platform:clockgen",
":architecture:interrupt")

for instance in listify(device.get_driver("pio")["instance"]):
module.add_submodule(Instance(instance))

return True

def build(env):
env.outbasepath = "modm/src/modm/platform/pio"
env.copy("pio_asm.hpp")
env.copy("pio_program.hpp")
env.copy("pio_sm.hpp")
pass
39 changes: 39 additions & 0 deletions src/modm/platform/pio/rp/pio.cpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2022, Andrey Kunitsyn
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#include <modm/architecture/driver/atomic/queue.hpp>
#include <modm/architecture/interface/atomic_lock.hpp>
#include <modm/architecture/interface/interrupt.hpp>
#include <modm/platform/core/resets.hpp>

#include "../device.hpp"

#include "pio_{{ id }}.hpp"

// ----------------------------------------------------------------------------

namespace modm::platform
{
uint32_t Pio{{ id }}::used_instruction_space = 0;
}



// ----------------------------------------------------------------------------
MODM_ISR(PIO{{ id }}_IRQ_0)
{

}

MODM_ISR(PIO{{ id }}_IRQ_1)
{

}
98 changes: 98 additions & 0 deletions src/modm/platform/pio/rp/pio.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2022, Andrey Kunitsyn
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#pragma once

#include <modm/architecture/interface/peripheral.hpp>
#include <modm/platform/core/peripherals.hpp>
#include <modm/platform/gpio/connector.hpp>
#include <modm/math/algorithm/prescaler.hpp>

#include "pio_program.hpp"
#include "pio_sm.hpp"
#include <hardware/structs/pio.h>

namespace modm::platform
{


/**
* Programmable IO block (PIO)
*
* @ingroup modm_platform_pio
* @author Andrey Kunitsyn
*/
class Pio{{ id }} : public ::modm::PeripheralDriver
{
static inline pio_hw_t& pio() { return *pio{{ id }}_hw; }

static uint32_t used_instruction_space;
static int findOffset(const PIOProgram& prg) {
uint32_t program_mask = (1u << prg.length) - 1;
if (prg.origin >= 0) {
if (prg.origin > 32 - prg.length) return -1;
return used_instruction_space & (program_mask << prg.origin) ? -1 : prg.origin;
} else {
// work down from the top always
for (int i = 32 - prg.length; i >= 0; i--) {
if ((used_instruction_space & (program_mask << static_cast<unsigned int>(i)))==0) {
return i;
}
}
return -1;
}
}
static void addProgramAtOffset(size_t offset,const PIOProgram& prg) {
for (uint8_t i = 0; i < prg.length; ++i) {
auto& instr = prg.instructions[i];
pio().instr_mem[offset + i] = instr.store(offset);
}
uint32_t program_mask = (1u << prg.length) - 1;
used_instruction_space |= program_mask << offset;
}

template <typename Pio,size_t SM>
friend class pio::StateMachine;

public:
static constexpr Peripheral peripherial = Peripheral::Pio{{ id }};
template <size_t SM>
class StateMachine : public modm::platform::pio::StateMachine<Pio{{ id }},SM> {};

template< class... Signals >
static void
connect()
{
using Connector = GpioConnector<peripherial, Signals...>;
Connector::connect();
}

template< class Pin >
static void connectPin() {
using Connector = GpioConnector<peripherial, typename Pin::Pad>;
Connector::connect();
}

template <typename Program>
static size_t addProgram(const Program& prg_data) {
auto prg = PIOProgram::get(prg_data);
auto offset = findOffset(prg);
if (offset < 0) {
return offset;
}
auto res = static_cast<size_t>(offset);
addProgramAtOffset(res,prg);
return res;
}
};

} // namespace modm::platform

Loading

0 comments on commit 2a28199

Please sign in to comment.