generated from saji/ecp5-template
Compare commits
2 commits
1ccc0b3d39
...
d42e227c4d
Author | SHA1 | Date | |
---|---|---|---|
saji | d42e227c4d | ||
saji | bf35262640 |
125
pdm.lock
125
pdm.lock
|
@ -2,10 +2,10 @@
|
|||
# It is not intended for manual editing.
|
||||
|
||||
[metadata]
|
||||
groups = ["default"]
|
||||
groups = ["default", "dev"]
|
||||
strategy = ["inherit_metadata"]
|
||||
lock_version = "4.5.0"
|
||||
content_hash = "sha256:711e8d9f3db580c3fd719dc08025858852c340c17bd0ec68be953ed3a4a18d6d"
|
||||
content_hash = "sha256:7b1a11a64e435bfbced61bd2b5314e78a56b93e0bac7dcb6b043529bc66fc060"
|
||||
|
||||
[[metadata.targets]]
|
||||
requires_python = "==3.12.*"
|
||||
|
@ -27,6 +27,43 @@ files = [
|
|||
{file = "amaranth-0.5.1.tar.gz", hash = "sha256:58b01efcec24c3696a7465e97a6e62f46466afab1f956a6eb00bda4b791c8345"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "basedpyright"
|
||||
version = "1.17.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "static type checking for Python (but based)"
|
||||
groups = ["dev"]
|
||||
dependencies = [
|
||||
"nodejs-wheel-binaries>=20.13.1",
|
||||
]
|
||||
files = [
|
||||
{file = "basedpyright-1.17.0-py3-none-any.whl", hash = "sha256:a7e070be5d3930223df0a435590932fbc114242b7f8d1723c5d717754e7a3b4e"},
|
||||
{file = "basedpyright-1.17.0.tar.gz", hash = "sha256:79e2740156a040fdc68e4372940f40694375ef7e4a0ca70bacf8910af08717e0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
summary = "Cross-platform colored terminal text."
|
||||
groups = ["dev"]
|
||||
marker = "sys_platform == \"win32\""
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "brain-dead simple config-ini parsing"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
|
||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.4"
|
||||
|
@ -75,6 +112,63 @@ files = [
|
|||
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodejs-wheel-binaries"
|
||||
version = "20.17.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "unoffical Node.js package"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "nodejs_wheel_binaries-20.17.0-py2.py3-none-macosx_10_15_x86_64.whl", hash = "sha256:a5eaffb0327d751360e5f01e92f0adeb3b297a9196751bf1dda910b92c9f6f83"},
|
||||
{file = "nodejs_wheel_binaries-20.17.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:a661d2894b11dc886ae8c25727c3cdf1d54db982cdccfcddbb2dfe20dd249f3b"},
|
||||
{file = "nodejs_wheel_binaries-20.17.0-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0144c91e9816cda969d78ba0903acc405c99e0a4bfffbdabac6b48b237335e7"},
|
||||
{file = "nodejs_wheel_binaries-20.17.0-py2.py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87ee7e88bfece2d325949e4f0dedb942b7478c401fc4e6726c5270f5835a02d"},
|
||||
{file = "nodejs_wheel_binaries-20.17.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5b5fb7c51204ad30e9f83ab503f11b17de4bcc1c248314f83ca3302fda24b1a1"},
|
||||
{file = "nodejs_wheel_binaries-20.17.0-py2.py3-none-win_amd64.whl", hash = "sha256:931558d528b976eb67ae1f68253df089e9d7d7267b6980fc580bfd31d67a0f5c"},
|
||||
{file = "nodejs_wheel_binaries-20.17.0.tar.gz", hash = "sha256:827a3297a99764adfaeb0cd1e9e36d8fb79c5b074de916a2a31b8a61b8d208f7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.1"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Core utilities for Python packages"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
|
||||
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.5.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "plugin and hook calling mechanisms for python"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
|
||||
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.3.2"
|
||||
requires_python = ">=3.8"
|
||||
summary = "pytest: simple powerful testing with Python"
|
||||
groups = ["dev"]
|
||||
dependencies = [
|
||||
"colorama; sys_platform == \"win32\"",
|
||||
"exceptiongroup>=1.0.0rc8; python_version < \"3.11\"",
|
||||
"iniconfig",
|
||||
"packaging",
|
||||
"pluggy<2,>=1.5",
|
||||
"tomli>=1; python_version < \"3.11\"",
|
||||
]
|
||||
files = [
|
||||
{file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"},
|
||||
{file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyvcd"
|
||||
version = "0.4.0"
|
||||
|
@ -96,3 +190,30 @@ files = [
|
|||
{file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"},
|
||||
{file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.6.2"
|
||||
requires_python = ">=3.7"
|
||||
summary = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c"},
|
||||
{file = "ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570"},
|
||||
{file = "ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1"},
|
||||
{file = "ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1"},
|
||||
{file = "ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23"},
|
||||
{file = "ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a"},
|
||||
{file = "ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c"},
|
||||
{file = "ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56"},
|
||||
{file = "ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da"},
|
||||
{file = "ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2"},
|
||||
{file = "ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9"},
|
||||
{file = "ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be"},
|
||||
]
|
||||
|
|
|
@ -19,7 +19,8 @@ distribution = false
|
|||
[tool.pdm.dev-dependencies] # or the poetry equivalent
|
||||
dev = [
|
||||
"basedpyright", # you can pin the version here if you want, or just rely on the lockfile
|
||||
"ruff"
|
||||
"ruff",
|
||||
"pytest>=8.3.2",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from amaranth import Module, Cat, Signal, Assert, unsigned
|
||||
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
|
||||
|
||||
|
||||
class RGBLayout(data.StructLayout):
|
||||
|
@ -55,9 +56,10 @@ class Hub75Ctrl(wiring.Signature):
|
|||
|
||||
|
||||
class Hub75Data(wiring.Signature):
|
||||
""" Data lines for HUB75 displays. When combined with Hub75Ctrl, this forms a complete HUB75 interface.
|
||||
"""Data lines for HUB75 displays. When combined with Hub75Ctrl, this forms a complete HUB75 interface.
|
||||
These are kept separate as some devices have a single set of control signals.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
{
|
||||
|
@ -65,31 +67,91 @@ class Hub75Data(wiring.Signature):
|
|||
"rgb1": Out(rgb111_hub75),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
def get_lineram() -> Memory:
|
||||
return Memory(shape=rgb888_layout, depth=512, init=[])
|
||||
|
||||
|
||||
|
||||
class Hub75StringDriver(wiring.Component):
|
||||
""" A data driver for Hub75 panels. This accesses the line memory and feeds out the data
|
||||
"""
|
||||
"""A data driver for Hub75 panels. This accesses the line memory and feeds out the data"""
|
||||
|
||||
bcm_select: In(3)
|
||||
|
||||
|
||||
class Hub75Coordinator(wiring.Component):
|
||||
""" A shared-control hub75 driver"""
|
||||
pass
|
||||
|
||||
class Bitslicer(wiring.Component):
|
||||
start_write: In(1)
|
||||
done: Out(1)
|
||||
bitplane_addr: Out(11)
|
||||
bitplane_wren: Out(1)
|
||||
bitplane_data: Out(6)
|
||||
start: In(1)
|
||||
active_bank: In(1) # the bank to use
|
||||
bram_port: WritePort.Signature(addr_width=9, shape=rgb888_layout)
|
||||
out: Out(Hub75Data())
|
||||
|
||||
|
||||
def elaborate(self, platform: Platform) -> Module:
|
||||
m = Module()
|
||||
|
||||
bitplane_bit = Signal(3)
|
||||
|
||||
# add two memories
|
||||
m.submodules.bram0 = bram0 = Memory(shape=rgb888_layout, depth=512, init=[])
|
||||
m.submodules.bram1 = bram1 = Memory(shape=rgb888_layout, depth=512, init=[])
|
||||
|
||||
|
||||
# We use two brams here so we can swap between each - that way we can update the data
|
||||
# while displaying the other line. Switching is controlled by the coordinator.
|
||||
|
||||
readports = Array([bram0.read_port(), bram1.read_port()])
|
||||
active_readport = readports[self.active_bank]
|
||||
m.d.comb += self.active_readport.eq(readports[self.active_bank])
|
||||
writeports = Array([bram0.write_port(), bram1.write_port()])
|
||||
m.d.comb += self.bram_port.eq(writeports[~self.active_bank])
|
||||
|
||||
counter = Signal(32)
|
||||
m.d.sync += counter.eq(counter + 1)
|
||||
|
||||
ram_rgb_slice = Cat(
|
||||
active_readport.data['red'].bit_select(self.bcm_shift, 1),
|
||||
active_readport.data['blue'].bit_select(self.bcm_shift, 1),
|
||||
active_readport.data['green'].bit_select(self.bcm_shift, 1),
|
||||
)
|
||||
|
||||
pixnum = Signal(8, reset=127)
|
||||
pixrow = Signal(1, reset=0)
|
||||
m.d.comb += self.buf_addr.eq(Cat(pixrow, pixnum))
|
||||
|
||||
with m.FSM():
|
||||
with m.State("init"):
|
||||
m.d.sync += bitplane_bit.eq(0)
|
||||
m.d.sync += self.done.eq(0)
|
||||
m.d.sync += [
|
||||
self.done.eq(0),
|
||||
counter.eq(0),
|
||||
pixnum.eq(127),
|
||||
pixrow.eq(0),
|
||||
]
|
||||
with m.If(self.start == 1):
|
||||
m.d.sync += active_readport.en.eq(1)
|
||||
m.next = "prefetch"
|
||||
with m.State("prefetch"):
|
||||
with m.If(counter == 0):
|
||||
m.d.sync += pixrow.eq(0)
|
||||
with m.Elif(counter == 1):
|
||||
m.d.sync += pixrow.eq(1)
|
||||
with m.Elif(counter == 2):
|
||||
m.d.sync += self.out.rgb0.eq(ram_rgb_slice)
|
||||
with m.Elif(counter == 3):
|
||||
m.d.sync += [self.out.rgb1.eq(ram_rgb_slice), counter.eq(0)]
|
||||
m.next = "writerow"
|
||||
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class Hub75Coordinator(wiring.Component):
|
||||
"""A shared-control hub75 driver"""
|
||||
|
||||
def __init__(self, n_strings=1):
|
||||
self.n_strings = n_strings
|
||||
super().__init__()
|
||||
|
||||
def elaborate(self, platform: Platform) -> Module:
|
||||
m = Module()
|
||||
|
||||
return m
|
||||
|
||||
|
@ -208,3 +270,8 @@ class Hub75EDriver(wiring.Component):
|
|||
m.next = "init"
|
||||
|
||||
return m
|
||||
|
||||
if __name__ == "__main__":
|
||||
m = Hub75EDriver()
|
||||
from amaranth.cli import main
|
||||
main(m)
|
||||
|
|
70
src/groovylight/sdram.py
Normal file
70
src/groovylight/sdram.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
# SDRAM controller for the Colorlight 5a-75b
|
||||
|
||||
|
||||
from amaranth import Module, Cat, Signal, Assert, unsigned
|
||||
from amaranth.build import Platform
|
||||
from amaranth.lib import wiring, data, enum
|
||||
from amaranth.lib.wiring import In, Out
|
||||
|
||||
|
||||
# RAM Specs:
|
||||
# 4 banks, 2048 rows, 256 columns.
|
||||
# 11 row bits, 8 column bits.
|
||||
# word size = 32
|
||||
# 8 megabytes data.
|
||||
|
||||
|
||||
sdram_control_layout = data.StructLayout(
|
||||
{
|
||||
"nCS": 1,
|
||||
"nRAS": 1,
|
||||
"nCAS": 1,
|
||||
"nWE": 1,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
class _WriteBurstLength(enum.Enum, shape=1):
|
||||
"""MRS field"""
|
||||
BURST = 0
|
||||
SINGLE_BIT = 1
|
||||
|
||||
class _TestMode(enum.Enum, shape=2):
|
||||
""" The "test mode" of the sdram. This is always zero pretty much"""
|
||||
MODE_REGISTER_SET = 0
|
||||
RESERVED0 = 1
|
||||
RESERVED1 = 2
|
||||
RESERVED2 = 3
|
||||
|
||||
class _CASLatency(enum.Enum, shape=3):
|
||||
""" How many cycles of latency for the column address select to complete """
|
||||
CYCL2 = 2
|
||||
CYCL3 = 3
|
||||
|
||||
class _BurstType(enum.Enum, shape=1):
|
||||
SEQUENTIAL = 0
|
||||
INTERLEAVED = 1
|
||||
|
||||
class _BurstLength(enum.IntEnum, shape=3):
|
||||
""" The size of the burst """
|
||||
SINGLE = 0
|
||||
DUAL = 1
|
||||
QUAD = 2
|
||||
OCT = 3
|
||||
FULL_PAGE = 7
|
||||
|
||||
|
||||
class _Command(enum.Enum):
|
||||
""" Command set for SDRAM """
|
||||
MRS_WRITE = 0
|
||||
ACTIVATE = 1
|
||||
PRECHARGE = 2
|
||||
WRITE = 3
|
||||
READ = 4
|
||||
CBR = 5 # auto refresh
|
||||
SELF_REFRESH = 6
|
||||
BRST_STOP = 7
|
||||
NOP = 8
|
||||
|
||||
|
Loading…
Reference in a new issue