Double the pixel clock rate

.. by moving the "assert CLK after each data" into the PIO program,
cutting the amount of PIO data in half.
This commit is contained in:
Jeff Epler 2025-01-24 11:17:44 -06:00
parent a532a9105e
commit 3d6b58f74d
5 changed files with 44 additions and 51 deletions

View file

@ -1,29 +1,26 @@
import sys
from contextlib import contextmanager
import io
import pathlib
from contextlib import redirect_stdout
import adafruit_pioasm
import click
@contextmanager
def temporary_stdout(filename):
old_stdout = sys.stdout
try:
with open(filename, "w", encoding="utf-8") as sys.stdout:
yield sys.stdout
finally:
sys.stdout = old_stdout
@click.command
@click.argument("infile")
@click.argument("outfile")
def main(infile, outfile):
program_name = infile.rpartition("/")[2].partition(".")[0]
print(program_name)
program_name = pathlib.Path(infile).stem
program = adafruit_pioasm.Program.from_file(infile, build_debuginfo=True)
with temporary_stdout(outfile):
c_program = io.StringIO()
with redirect_stdout(c_program):
program.print_c_program(program_name)
with open(outfile, "w", encoding="utf-8") as out:
print("#pragma once", file=out)
print("", file=out)
print(c_program.getvalue().rstrip().replace("True", "true"), file=out)
if __name__ == '__main__':
main()

View file

@ -111,6 +111,8 @@ struct piomatter : piomatter_base {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + protomatter_wrap_target,
offset + protomatter_wrap);
// 1 side-set pin
sm_config_set_sideset(&c, 2, true, false);
sm_config_set_out_shift(&c, /* shift_right= */ false,
/* auto_pull = */ true, 32);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
@ -119,10 +121,11 @@ struct piomatter : piomatter_base {
// frequency is approximately the best sustainable clock with current
// FW & kernel.
constexpr double target_freq =
2700000; // 2.7MHz PIO clock -> 1.35MHz pixel clock
2700000 * 2; // 2.7MHz pixel clock, 2 PIO cycles per pixel
double div = clock_get_hz(clk_sys) / target_freq;
sm_config_set_clkdiv(&c, div);
sm_config_set_out_pins(&c, 0, 28);
sm_config_set_sideset_pins(&c, pinout::PIN_CLK);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);

View file

@ -2,24 +2,27 @@
const int protomatter_wrap = 4;
const int protomatter_wrap_target = 0;
const int protomatter_sideset_pin_count = 0;
const bool protomatter_sideset_enable = 0;
const int protomatter_sideset_pin_count = 1;
const bool protomatter_sideset_enable = true;
const uint16_t protomatter[] = {
// ; data format (out-shift-right):
// ; MSB ... LSB
// ; 0 ddd......ddd: 31-bit delay
// ; 1 ccc......ccc: 31 bit data count
// .side_set 1 opt
// .wrap_target
// top:
0x6021, // out x, 1
0x605f, // out y, 31
0x0025, // jmp !x delay_loop
0x0025, // jmp !x do_delay
// data_loop:
0x6000, // out pins, 32
0x0083, // jmp y--, data_loop
0x1883, // jmp y--, data_loop side 1 ; assert clk bit
// .wrap
// do_delay:
0x6000, // out pins, 32
// delay_loop:
0x0085, // jmp y--, delay_loop
0x0086, // jmp y--, delay_loop
0x0000, // jmp top
// ;; fill program out to 32 instructions so nothing else can load
0xa042, // nop

View file

@ -7,10 +7,10 @@
namespace piomatter {
constexpr unsigned DATA_OVERHEAD = 3;
constexpr unsigned CLOCKS_PER_DATA = 2;
constexpr unsigned DELAY_OVERHEAD = 5;
constexpr unsigned CLOCKS_PER_DELAY = 1;
constexpr int DATA_OVERHEAD = 3;
constexpr int CLOCKS_PER_DATA = 2;
constexpr int DELAY_OVERHEAD = 5;
constexpr int CLOCKS_PER_DELAY = 1;
constexpr uint32_t command_data = 1u << 31;
constexpr uint32_t command_delay = 0;
@ -137,12 +137,12 @@ void protomatter_render_rgb10(std::vector<uint32_t> &result,
int data_count = 0;
auto do_delay = [&](uint32_t delay) {
if (delay == 0)
return;
auto do_data_delay = [&](uint32_t data, int32_t delay) {
delay = std::max((delay / CLOCKS_PER_DELAY) - DELAY_OVERHEAD, 1);
assert(delay < 1000000);
assert(!data_count);
result.push_back(command_delay | (delay ? delay - 1 : 0));
result.push_back(data);
};
auto prep_data = [&data_count, &result](uint32_t n) {
@ -153,27 +153,15 @@ void protomatter_render_rgb10(std::vector<uint32_t> &result,
data_count = n;
};
auto do_data = [&](uint32_t d) {
assert(data_count);
data_count--;
result.push_back(d);
};
int32_t active_time;
auto do_data_active = [&active_time, &do_data](uint32_t d) {
auto do_data_clk_active = [&active_time, &data_count, &result](uint32_t d) {
bool active = active_time > 0;
active_time--;
d |= active ? pinout::oe_active : pinout::oe_inactive;
do_data(d);
};
auto do_data_delay = [&prep_data, &do_data, &do_delay](uint32_t d,
int32_t delay) {
prep_data(1);
do_data(d);
if (delay > 0)
do_delay(delay);
assert(data_count);
data_count--;
result.push_back(d);
};
auto calc_addr_bits = [](int addr) {
@ -195,9 +183,9 @@ void protomatter_render_rgb10(std::vector<uint32_t> &result,
return data;
};
auto add_pixels = [&do_data_active, &result](uint32_t addr_bits, bool r0,
bool g0, bool b0, bool r1,
bool g1, bool b1) {
auto add_pixels = [&do_data_clk_active,
&result](uint32_t addr_bits, bool r0, bool g0, bool b0,
bool r1, bool g1, bool b1) {
uint32_t data = addr_bits;
if (r0)
data |= (1 << pinout::PIN_RGB[0]);
@ -212,8 +200,7 @@ void protomatter_render_rgb10(std::vector<uint32_t> &result,
if (b1)
data |= (1 << pinout::PIN_RGB[5]);
do_data_active(data);
do_data_active(data | pinout::clk_bit);
do_data_clk_active(data);
};
int last_bit = 0;
@ -243,7 +230,7 @@ void protomatter_render_rgb10(std::vector<uint32_t> &result,
active_time = 1 << last_bit;
last_bit = bit;
prep_data(2 * pixels_across);
prep_data(pixels_across);
auto mapiter = matrixmap.map.begin() + 2 * addr * pixels_across;
for (size_t x = 0; x < pixels_across; x++) {
assert(mapiter != matrixmap.map.end());

View file

@ -3,17 +3,20 @@
; 0 ddd......ddd: 31-bit delay
; 1 ccc......ccc: 31 bit data count
.side_set 1 opt
.wrap_target
top:
out x, 1
out y, 31
jmp !x delay_loop
jmp !x do_delay
data_loop:
out pins, 32
jmp y--, data_loop
jmp y--, data_loop side 1 ; assert clk bit
.wrap
do_delay:
out pins, 32
delay_loop:
jmp y--, delay_loop
jmp top