generated from saji/ecp5-template
add rgbview with channel_slice operator
This commit is contained in:
parent
95ca2447cb
commit
8477698d77
|
@ -1,4 +1,4 @@
|
||||||
from amaranth import Array, unsigned
|
from amaranth import unsigned, Cat
|
||||||
from amaranth.lib import wiring, data
|
from amaranth.lib import wiring, data
|
||||||
from amaranth.lib.wiring import Out
|
from amaranth.lib.wiring import Out
|
||||||
|
|
||||||
|
@ -13,6 +13,9 @@ class RGBLayout(data.StructLayout):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __call__(self, value):
|
||||||
|
return RGBView(self, value)
|
||||||
|
|
||||||
|
|
||||||
Rgb888Layout = RGBLayout(8, 8, 8)
|
Rgb888Layout = RGBLayout(8, 8, 8)
|
||||||
Rgb666Layout = RGBLayout(6, 6, 6)
|
Rgb666Layout = RGBLayout(6, 6, 6)
|
||||||
|
@ -20,6 +23,23 @@ Rgb666Layout = RGBLayout(6, 6, 6)
|
||||||
Rgb111Layout = RGBLayout(1, 1, 1)
|
Rgb111Layout = RGBLayout(1, 1, 1)
|
||||||
|
|
||||||
|
|
||||||
|
class RGBView(data.View):
|
||||||
|
|
||||||
|
def channel_size(self) -> int:
|
||||||
|
return self.red.shape()
|
||||||
|
def channel_slice(self, bit: int) -> Rgb111Layout:
|
||||||
|
"""Select bits from each channel and use it to form an Rgb111Layout.
|
||||||
|
This is useful for BCM stuff, since the bits are sliced to form a bitplane.
|
||||||
|
"""
|
||||||
|
return Rgb111Layout(
|
||||||
|
Cat(
|
||||||
|
self.red.bit_select(bit, 1),
|
||||||
|
self.green.bit_select(bit, 1),
|
||||||
|
self.blue.bit_select(bit, 1),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Hub75Stream(wiring.Signature):
|
class Hub75Stream(wiring.Signature):
|
||||||
"""A Hub75E Driver for a single string of panels."""
|
"""A Hub75E Driver for a single string of panels."""
|
||||||
|
|
||||||
|
|
|
@ -122,29 +122,16 @@ class Hub75DataDriver(wiring.Component):
|
||||||
if self.double_fetch:
|
if self.double_fetch:
|
||||||
pixrow = Signal(1)
|
pixrow = Signal(1)
|
||||||
m.d.comb += self.bram.addr.eq(Cat(pixrow, pixnum))
|
m.d.comb += self.bram.addr.eq(Cat(pixrow, pixnum))
|
||||||
ram_rgb_slice = Cat(
|
ram_rgb_slice = self.bram.data.channel_slice(self.bcm_select)
|
||||||
self.bram.data["red"].bit_select(self.bcm_select, 1),
|
|
||||||
self.bram.data["blue"].bit_select(self.bcm_select, 1),
|
|
||||||
self.bram.data["green"].bit_select(self.bcm_select, 1),
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
|
m.d.comb += self.bram.addr.eq(pixnum)
|
||||||
ram_rgb_slice = Array(
|
ram_rgb_slice = Array(
|
||||||
[
|
[
|
||||||
Cat(
|
self.bram.data[0].channel_slice(self.bcm_select),
|
||||||
self.bram.data[0]["red"].bit_select(self.bcm_select, 1),
|
self.bram.data[1].channel_slice(self.bcm_select),
|
||||||
self.bram.data[0]["blue"].bit_select(self.bcm_select, 1),
|
|
||||||
self.bram.data[0]["green"].bit_select(self.bcm_select, 1),
|
|
||||||
),
|
|
||||||
Cat(
|
|
||||||
self.bram.data[1]["red"].bit_select(self.bcm_select, 1),
|
|
||||||
self.bram.data[1]["blue"].bit_select(self.bcm_select, 1),
|
|
||||||
self.bram.data[1]["green"].bit_select(self.bcm_select, 1),
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
m.d.comb += self.bram.addr.eq(pixnum)
|
|
||||||
|
|
||||||
with m.FSM():
|
with m.FSM():
|
||||||
with m.State("init"):
|
with m.State("init"):
|
||||||
m.d.sync += [
|
m.d.sync += [
|
||||||
|
@ -215,7 +202,7 @@ class Hub75StringDriver(wiring.Component):
|
||||||
"bcm_select": In(3),
|
"bcm_select": In(3),
|
||||||
"done": Out(1),
|
"done": Out(1),
|
||||||
"start": In(1),
|
"start": In(1),
|
||||||
"bram_port": In(
|
"bram": In(
|
||||||
ReadPort.Signature(
|
ReadPort.Signature(
|
||||||
addr_width=ceil_log2(panel_length),
|
addr_width=ceil_log2(panel_length),
|
||||||
shape=data.ArrayLayout(Rgb666Layout, 2),
|
shape=data.ArrayLayout(Rgb666Layout, 2),
|
||||||
|
@ -232,16 +219,8 @@ class Hub75StringDriver(wiring.Component):
|
||||||
self._counter = counter = Signal(32) # unused count is optimized out
|
self._counter = counter = Signal(32) # unused count is optimized out
|
||||||
m.d.sync += counter.eq(counter + 1)
|
m.d.sync += counter.eq(counter + 1)
|
||||||
|
|
||||||
ram_rgb0_slice = Cat(
|
ram_rgb0_slice = self.bram.data[0].channel_slice(self.bcm_select)
|
||||||
self.bram_port.data[0]["red"].bit_select(self.bcm_select, 1),
|
ram_rgb1_slice = self.bram.data[1].channel_slice(self.bcm_select)
|
||||||
self.bram_port.data[0]["blue"].bit_select(self.bcm_select, 1),
|
|
||||||
self.bram_port.data[0]["green"].bit_select(self.bcm_select, 1),
|
|
||||||
)
|
|
||||||
ram_rgb1_slice = Cat(
|
|
||||||
self.bram_port.data[1]["red"].bit_select(self.bcm_select, 1),
|
|
||||||
self.bram_port.data[1]["blue"].bit_select(self.bcm_select, 1),
|
|
||||||
self.bram_port.data[1]["green"].bit_select(self.bcm_select, 1),
|
|
||||||
)
|
|
||||||
m.d.comb += [
|
m.d.comb += [
|
||||||
self.display_out.rgb0.eq(ram_rgb0_slice),
|
self.display_out.rgb0.eq(ram_rgb0_slice),
|
||||||
self.display_out.rgb1.eq(ram_rgb1_slice),
|
self.display_out.rgb1.eq(ram_rgb1_slice),
|
||||||
|
@ -249,7 +228,7 @@ class Hub75StringDriver(wiring.Component):
|
||||||
m.d.sync += Assert(self.bcm_select < 6)
|
m.d.sync += Assert(self.bcm_select < 6)
|
||||||
|
|
||||||
pixnum = Signal(range(self.panel_length), init=self.panel_length - 1)
|
pixnum = Signal(range(self.panel_length), init=self.panel_length - 1)
|
||||||
m.d.comb += self.bram_port.addr.eq(pixnum)
|
m.d.comb += self.bram.addr.eq(pixnum)
|
||||||
|
|
||||||
with m.FSM():
|
with m.FSM():
|
||||||
with m.State("init"):
|
with m.State("init"):
|
||||||
|
@ -259,7 +238,7 @@ class Hub75StringDriver(wiring.Component):
|
||||||
pixnum.eq(self.panel_length - 1),
|
pixnum.eq(self.panel_length - 1),
|
||||||
]
|
]
|
||||||
with m.If(self.start == 1):
|
with m.If(self.start == 1):
|
||||||
m.d.sync += self.bram_port.en.eq(1)
|
m.d.sync += self.bram.en.eq(1)
|
||||||
m.next = "writerow"
|
m.next = "writerow"
|
||||||
with m.State("writerow"):
|
with m.State("writerow"):
|
||||||
with m.If(counter[0] == 0):
|
with m.If(counter[0] == 0):
|
||||||
|
@ -272,7 +251,7 @@ class Hub75StringDriver(wiring.Component):
|
||||||
with m.Else():
|
with m.Else():
|
||||||
m.next = "done"
|
m.next = "done"
|
||||||
with m.State("done"):
|
with m.State("done"):
|
||||||
m.d.sync += [self.done.eq(1), self.bram_port.en.eq(0)]
|
m.d.sync += [self.done.eq(1), self.bram.en.eq(0)]
|
||||||
m.next = "init"
|
m.next = "init"
|
||||||
|
|
||||||
return m
|
return m
|
||||||
|
|
23
src/groovylight/tests/test_common.py
Normal file
23
src/groovylight/tests/test_common.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from amaranth import unsigned
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from groovylight.common import Rgb888Layout, Rgb666Layout, RGBView
|
||||||
|
|
||||||
|
|
||||||
|
def test_rgbview():
|
||||||
|
rgb = Rgb888Layout(0xAABBCC)
|
||||||
|
|
||||||
|
assert rgb.channel_size() == unsigned(8)
|
||||||
|
|
||||||
|
rgb18 = Rgb666Layout(0x2DEFD)
|
||||||
|
|
||||||
|
slice = rgb.channel_slice(1)
|
||||||
|
assert isinstance(slice, RGBView), "channel_slice should return another rgbview"
|
||||||
|
|
||||||
|
assert slice.channel_size() == unsigned(1), "channel_slice channel size should be 1"
|
||||||
|
assert isinstance(
|
||||||
|
rgb18.channel_slice(5), RGBView
|
||||||
|
), "channel_slice should return another rgbview"
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="Target of a view is 0 bit"):
|
||||||
|
rgb.channel_slice(8)
|
|
@ -4,9 +4,14 @@ from amaranth.lib.memory import Memory
|
||||||
from amaranth.sim import Simulator
|
from amaranth.sim import Simulator
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from groovylight.common import Rgb888Layout
|
from groovylight.common import Rgb888Layout, Rgb666Layout
|
||||||
|
|
||||||
|
from groovylight.hub75 import (
|
||||||
|
Hub75Coordinator,
|
||||||
|
Hub75DataDriver,
|
||||||
|
Hub75StringDriver,
|
||||||
|
)
|
||||||
|
|
||||||
from ..hub75 import Hub75Coordinator, Hub75DataDriver, Hub75StringDriver, Rgb666Layout
|
|
||||||
|
|
||||||
|
|
||||||
def test_stringdriver():
|
def test_stringdriver():
|
||||||
|
@ -21,7 +26,7 @@ def test_stringdriver():
|
||||||
)
|
)
|
||||||
port = mem.read_port()
|
port = mem.read_port()
|
||||||
|
|
||||||
wiring.connect(m, port, dut.bram_port)
|
wiring.connect(m, port, dut.bram)
|
||||||
|
|
||||||
async def testbench(ctx):
|
async def testbench(ctx):
|
||||||
# select a bit, strobe start, read values, test against known.
|
# select a bit, strobe start, read values, test against known.
|
||||||
|
@ -29,7 +34,7 @@ def test_stringdriver():
|
||||||
ctx.set(dut.start, 1)
|
ctx.set(dut.start, 1)
|
||||||
await ctx.tick()
|
await ctx.tick()
|
||||||
ctx.set(dut.start, 0)
|
ctx.set(dut.start, 0)
|
||||||
assert ctx.get(dut.bram_port.en) == 1
|
assert ctx.get(dut.bram.en) == 1
|
||||||
pass
|
pass
|
||||||
|
|
||||||
sim = Simulator(m)
|
sim = Simulator(m)
|
||||||
|
@ -57,8 +62,7 @@ def test_datadriver():
|
||||||
ctx.set(dut.start, 1)
|
ctx.set(dut.start, 1)
|
||||||
await ctx.tick()
|
await ctx.tick()
|
||||||
ctx.set(dut.start, 0)
|
ctx.set(dut.start, 0)
|
||||||
assert ctx.get(dut.bram_port.en) == 1
|
assert ctx.get(dut.bram.en) == 1
|
||||||
pass
|
|
||||||
|
|
||||||
sim = Simulator(m)
|
sim = Simulator(m)
|
||||||
sim.add_clock(1e-6)
|
sim.add_clock(1e-6)
|
||||||
|
|
Loading…
Reference in a new issue