diff --git a/src/groovylight/common.py b/src/groovylight/common.py index c8d07b8..675ca55 100644 --- a/src/groovylight/common.py +++ b/src/groovylight/common.py @@ -1,4 +1,4 @@ -from amaranth import unsigned +from amaranth import Array, unsigned from amaranth.lib import wiring, data from amaranth.lib.wiring import Out @@ -42,13 +42,14 @@ class Hub75Ctrl(wiring.Signature): """ - def __init__(self): + def __init__(self, n_strings: int = 1): super().__init__( { "latch": Out(1), "oe": Out(1), "addr": Out(5), "display_clk": Out(1), + "data": Out(Hub75Data()).array(n_strings), } ) diff --git a/src/groovylight/gamma.py b/src/groovylight/gamma.py index 362d9db..d358c89 100644 --- a/src/groovylight/gamma.py +++ b/src/groovylight/gamma.py @@ -24,8 +24,30 @@ # This file contains code to generate these timing adjustments and # control/quantify them. + +# FIXME: This doesn't work. As much as I wish it did. Because the numbers are still +# added linearly. values that are between the powers of two (which are fitted to the log +# curve) are not even remotely close to matching. +# thankfully a gamma LUT is only 64 elements. we will need 3 muxes per color (1 per channel). + from math import pow +from amaranth import Module, Cat, Mux, ShapeLike, Signal, Assert +from amaranth.build import Platform +from amaranth.lib import wiring, data +from amaranth.lib.wiring import In, Out +from amaranth.lib.memory import Memory, ReadPort, WritePort +from amaranth.utils import ceil_log2 + + +class GammaLUT(wiring.Component): + def __init__(self, n_bits: int, gamma: float = 2.2): + self.gamma = gamma + self.n_bits = n_bits + self.lut = [pow(x, gamma) for x in range(2**n_bits)] + + # TODO: we can combo it maybe with the variable timing method. + super().__init__(wiring.Signature({})) def _gammavec(vals: [float], g: float) -> [float]: return [pow(x, g) for x in vals] diff --git a/src/groovylight/bitslicer.py b/src/groovylight/hub75.py similarity index 99% rename from src/groovylight/bitslicer.py rename to src/groovylight/hub75.py index b04474f..24773a4 100644 --- a/src/groovylight/bitslicer.py +++ b/src/groovylight/hub75.py @@ -163,8 +163,7 @@ class Hub75Coordinator(wiring.Component): self.n_strings = n_strings super().__init__( { - "ctrl": Out(Hub75Ctrl), - "data": data.ArrayLayout(Hub75Data, n_strings), + "hub75": Out(Hub75Ctrl(n_strings)), # TODO: fetching routine? maybe it's passed through. } ) diff --git a/src/groovylight/tests/test_hub75.py b/src/groovylight/tests/test_hub75.py index a0f8050..5b6a2c5 100644 --- a/src/groovylight/tests/test_hub75.py +++ b/src/groovylight/tests/test_hub75.py @@ -5,7 +5,7 @@ from amaranth.lib.wiring import In, Out from amaranth.lib.memory import Memory, WritePort from amaranth.sim import Simulator -from ..bitslicer import Hub75StringDriver, Rgb666Layout +from ..hub75 import Hub75Coordinator, Hub75StringDriver, Rgb666Layout def test_stringdriver(): @@ -36,3 +36,8 @@ def test_stringdriver(): with sim.write_vcd("output.vcd"): sim.run_until(1e-6 * 1000) + + +def test_hub75(): + m = Module() + m.submodules.dut = dut = Hub75Coordinator(1) diff --git a/src/groovylight/tests/test_swapbuffer.py b/src/groovylight/tests/test_swapbuffer.py index e1498a9..e312ba9 100644 --- a/src/groovylight/tests/test_swapbuffer.py +++ b/src/groovylight/tests/test_swapbuffer.py @@ -1,11 +1,6 @@ -from amaranth import Array, Module, Cat, Signal, Assert, unsigned -from amaranth.build import Platform -from amaranth.lib import wiring, data -from amaranth.lib.wiring import In, Out -from amaranth.lib.memory import Memory, WritePort from amaranth.sim import Simulator -from ..bitslicer import Hub75StringDriver, Rgb666Layout, SwapBuffer +from ..hub75 import Hub75StringDriver, Rgb666Layout, SwapBuffer def test_swapbuffer(): diff --git a/tests/test_platform.py b/tests/test_platform.py index 7194d5b..96acb01 100644 --- a/tests/test_platform.py +++ b/tests/test_platform.py @@ -5,6 +5,12 @@ from amaranth.lib.io import Buffer from groovylight.platforms.colorlight_5a75b_v8_2 import Colorlight_5A75B_R82Platform +def progs_exist(programs) -> bool: + for p in programs: + if shutil.which(p) is None: + return False + return True + class Blinky(Elaboratable): def elaborate(self, platform): m = Module() @@ -24,5 +30,8 @@ class Blinky(Elaboratable): def test_platform(): + if not progs_exist(["yosys", "nextpnr-ecp5", "openFPGALoader"]): + pytest.skip("missing toolchain programs") plat = Colorlight_5A75B_R82Platform() + plat.build(Blinky())