Skip to content

Commit 047a6f2

Browse files
committed
wip: board configuration
1 parent 9a21033 commit 047a6f2

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

chipflow_lib/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import sys
88
import tomli
99
from pathlib import Path
10+
1011
from pydantic import ValidationError
1112

1213
__version__ = importlib.metadata.version("chipflow_lib")
@@ -64,3 +65,5 @@ def _parse_config_file(config_file):
6465

6566
error_str = "\n".join(error_messages)
6667
raise ChipFlowError(f"Validation error in chipflow.toml:\n{error_str}")
68+
69+

chipflow_lib/boards.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
from dataclasses import dataclass
2+
from typing import Dict, Optional, Callable
3+
4+
from amaranth import *
5+
from amaranth.lib.build import Platform
6+
from amaranth.lib.cdc import ResetSynchronizer
7+
from amaranth.lib.wiring import connect, flipped, Signature, In, Out, Interface
8+
from amaranth.build import Resource, Subsignal, Pins, Attrs
9+
10+
from amaranth_boards.ulx3s import (
11+
_ULX3SPlatform,
12+
ULX3S_12F_Platform,
13+
ULX3S_25F_Platform,
14+
ULX3S_45F_Platform,
15+
ULX3S_85F_Platform
16+
)
17+
18+
19+
PortName = str
20+
WiringFunc = Callable[[Module, Platform, PortName, Interface], None]
21+
"""
22+
A :py:WiringFunc declares a function that is used to wire an :py:`wiring.Interface` to a given named :py:`io.PortLike` on the given :py:`build.Platform`
23+
"""
24+
25+
@dataclass
26+
class BoardPrimitive:
27+
sig: Signature
28+
wire: WiringFunc
29+
30+
@dataclass
31+
class BoardVariant:
32+
"Specification of an FPGA board"
33+
platform: type[Platform]
34+
"The :py:`type` of the :py:`build.Platform` to use for this board."
35+
reset_button: Optional[str] = None
36+
"Resource name to wire to reset"
37+
default_clock: Optional[str] = None
38+
"Default clock to use for this BoardVariant or BoardType"
39+
primitives: Optional[Dict[str, BoardPrimitive]] = None
40+
"A mapping of a Platform Resource name to the primitive that should be used to wire it up"
41+
42+
@dataclass
43+
class BoardType(BoardVariant):
44+
variants: Dict[str, BoardVariant]
45+
"variants overlay the base BoardType"
46+
47+
def _wire_ulx3s_spi_flash(m: Module, platform: _ULX3SPlatform, port_name: str, flash: Interface) -> None:
48+
flash_port = platform.request("spi_flash", dir=dict(cs='-', copi='-', cipo='-', wp='-', hold='-'))
49+
# Flash clock requires a special primitive to access in ECP5
50+
m.submodules.usrmclk = Instance(
51+
"USRMCLK",
52+
i_USRMCLKI=flash.clk.o,
53+
i_USRMCLKTS=ResetSignal(), # tristate in reset for programmer accesss
54+
a_keep=1,
55+
)
56+
57+
# Flash IO buffers
58+
m.submodules += Instance(
59+
"OBZ",
60+
o_O=flash_port.cs.io,
61+
i_I=flash.csn.o,
62+
i_T=ResetSignal(),
63+
)
64+
65+
# Connect flash data pins in order
66+
data_pins = ["copi", "cipo", "wp", "hold"]
67+
for i in range(4):
68+
m.submodules += Instance(
69+
"BB",
70+
io_B=getattr(flash_port, data_pins[i]).io,
71+
i_I=flash.d.o[i],
72+
i_T=~flash.d.oe[i],
73+
o_O=flash.d.i[i]
74+
)
75+
76+
77+
_ulx3s_primitives = {
78+
'spi_flash': BoardPrimitive(
79+
interface_sig=Signature({
80+
"clk": Out(OutputPinSignature(1)),
81+
"csn": Out(OutputPinSignature(1)),
82+
"d": Out(BidirPinSignature(4, all_have_oe=True))
83+
}),
84+
wire=_wire_ulx3s_spi_flash)
85+
}
86+
87+
SUPPORTED_BOARDS = {
88+
'ULX3S': BoardType(
89+
platform=_ULX3SPlatform,
90+
reset_button='button_pwr',
91+
primitives=_ulx3s_primitives,
92+
variants={
93+
'12F': BoardVariant(platform=ULX3S_12F_Platform),
94+
'25F': BoardVariant(platform=ULX3S_25F_Platform),
95+
'45F': BoardVariant(platform=ULX3S_45F_Platform),
96+
'85F': BoardVariant(platform=ULX3S_85F_Platform),
97+
})
98+
}
99+
100+
## Psudocode for board step:
101+
#class _BoardWrapper:
102+
# def elaborate(self, platform):
103+
# m = Module()
104+
# top = top_interfaces()
105+
#
106+
# board = SUPPORTED_BOARDS[self._board_type]
107+
# if self._board_variant:
108+
# board |= board.variants[self._board_variant]
109+
#
110+
# m.domains += ClockDomain("sync")
111+
# m.d.comb += ClockSignal("sync").eq(platform.request().platform.default_clock)
112+
#
113+
# btn_rst = platform.request(board.reset_button)
114+
# m.submodules.rst_sync = ResetSynchronizer(arst=btn_rst.i, domain="sync")
115+
#
116+
# board_conf = config.chipflow.boards.{board_type}
117+
# board_conf |= config.chipflow.boards.{board_type}_{board_variant}
118+
#
119+
# for (portname, interface) in config.chipflow.boards.{board_name}.items():
120+
# if portname in board.primitives:
121+
# check interface matches board.primitives[portname].sig
122+
# board.primitives[portname].wire(m, board.platform, portname, interface)
123+
# else:
124+
# iterate interface, wire up appropriatly to port with comb
125+
# return m
126+
#
127+
#class _BoardStep(BoardStep):
128+
# def __init__(self, config, target):
129+
# _setup_amaranth()
130+
# if target in config.chipflow.boards.keys():
131+
# platform = SUPPORTED_BOARDS[key].platform
132+
# # Also handle board variants,
133+
# self._board_type = target
134+
# self._board_variant = variant
135+
# super().__init__(config, platform)
136+
#
137+
# def build(self):
138+
# self.platform.build(_BoardWrapper(self._board_type, self._board_variant), do_program=False)
139+

chipflow_lib/config_models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ def validate_pad_dicts(cls, v, info: ValidationInfo):
5858
return v
5959

6060

61+
class BoardType(BaseModel):
62+
"""Configuration for silicon in chipflow.toml."""
63+
board_name: Literal["ULX3S"]
64+
board_type: Literal["85F"]
65+
66+
67+
6168
class StepsConfig(BaseModel):
6269
"""Configuration for steps in chipflow.toml."""
6370
silicon: str
@@ -69,6 +76,7 @@ class ChipFlowConfig(BaseModel):
6976
top: Dict[str, Any] = {}
7077
steps: StepsConfig
7178
silicon: SiliconConfig
79+
board: BoardConfig
7280
clocks: Optional[Dict[str, str]] = None
7381
resets: Optional[Dict[str, str]] = None
7482

0 commit comments

Comments
 (0)