Skip to content

Commit 8086479

Browse files
committed
Adding a Verilog example using PicoSoC
Signed-off-by: gatecat <gatecat@ds0.me>
1 parent 50f6981 commit 8086479

File tree

17 files changed

+1074
-2
lines changed

17 files changed

+1074
-2
lines changed

.github/workflows/main.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ jobs:
2626
runs-on: ubuntu-latest
2727
strategy:
2828
matrix:
29-
design: ['mcu_soc', 'minimal']
29+
design: ['mcu_soc', 'minimal', 'picosoc_verilog']
3030
steps:
3131
- name: Check out source code
3232
uses: actions/checkout@v4
33+
with:
34+
submodules: true
3335

3436
- uses: actions/setup-python@v4
3537
with:
@@ -61,10 +63,12 @@ jobs:
6163
runs-on: ubuntu-latest
6264
strategy:
6365
matrix:
64-
design: ['mcu_soc', 'minimal']
66+
design: ['mcu_soc', 'minimal', 'picosoc_verilog']
6567
steps:
6668
- name: Check out source code
6769
uses: actions/checkout@v4
70+
with:
71+
submodules: true
6872

6973
- name: Set up PDM
7074
uses: pdm-project/setup-pdm@v4

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ __pycache__/
2424
/build
2525
/mcu_soc/build
2626
/minimal/build
27+
/picosoc_verilog/build
2728

2829
# testbenches
2930
*.vcd

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "picosoc_verilog/design/picorv32"]
2+
path = picosoc_verilog/design/picorv32
3+
url = https://github.com/YosysHQ/picorv32

picosoc_verilog/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# PicoSoC (Verilog)
2+
3+
This example design shows how an existing Verilog design (picosoc) can be wrapped in a minimal layer of Amaranth and submitted to the ChipFlow platform.
4+

picosoc_verilog/chipflow.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[chipflow]
2+
project_name = "chipflow-examples-picosoc"
3+
4+
[chipflow.top]
5+
soc = "design.design:MySoC"
6+
7+
[chipflow.steps]
8+
software = "design.steps.software:MySoftwareStep"
9+
10+
[chipflow.silicon]
11+
process = "sky130"
12+
package = "openframe"
13+
14+
[chipflow.test]
15+
event_reference = "design/tests/events_reference.json"

picosoc_verilog/design/design.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import os
2+
3+
from chipflow_lib.platforms.sim import SimPlatform
4+
5+
from amaranth import Module, Instance, ClockSignal, ResetSignal
6+
from amaranth.lib import wiring
7+
from amaranth.lib.wiring import In, Out, flipped, connect
8+
9+
from chipflow_lib.platforms import (
10+
GPIOSignature, UARTSignature, QSPIFlashSignature,
11+
BinaryData, attach_data
12+
)
13+
14+
__all__ = ["MySoC"]
15+
16+
17+
class MySoC(wiring.Component):
18+
def __init__(self):
19+
# Top level interfaces
20+
21+
super().__init__({
22+
"flash": Out(QSPIFlashSignature()),
23+
"uart_0": Out(UARTSignature()),
24+
"gpio_0": Out(GPIOSignature(pin_count=8)),
25+
})
26+
27+
def elaborate(self, platform):
28+
m = Module()
29+
30+
base = os.path.dirname(__file__)
31+
32+
verilog_sources = [
33+
f"{base}/picosoc_asic_top.v",
34+
f"{base}/picorv32/picosoc/spimemio.v",
35+
f"{base}/picorv32/picosoc/simpleuart.v",
36+
f"{base}/picorv32/picosoc/picosoc.v",
37+
f"{base}/picorv32/picorv32.v",
38+
]
39+
40+
if platform is not None:
41+
for verilog_file in verilog_sources:
42+
with open(verilog_file, 'r') as f:
43+
platform.add_file(verilog_file, f)
44+
45+
m.submodules.soc = soc = Instance("picosoc_asic_top",
46+
# Clock and reset
47+
i_clk=ClockSignal(),
48+
i_resetn=~ResetSignal(),
49+
50+
# UART
51+
o_ser_tx=self.uart_0.tx.o,
52+
i_ser_rx=self.uart_0.rx.i,
53+
54+
# SPI flash
55+
o_flash_csb=self.flash.csn.o,
56+
o_flash_clk=self.flash.clk.o,
57+
58+
o_flash_io0_oe=self.flash.d.oe[0],
59+
o_flash_io1_oe=self.flash.d.oe[1],
60+
o_flash_io2_oe=self.flash.d.oe[2],
61+
o_flash_io3_oe=self.flash.d.oe[3],
62+
63+
o_flash_io0_do=self.flash.d.o[0],
64+
o_flash_io1_do=self.flash.d.o[1],
65+
o_flash_io2_do=self.flash.d.o[2],
66+
o_flash_io3_do=self.flash.d.o[3],
67+
68+
i_flash_io0_di=self.flash.d.i[0],
69+
i_flash_io1_di=self.flash.d.i[1],
70+
i_flash_io2_di=self.flash.d.i[2],
71+
i_flash_io3_di=self.flash.d.i[3],
72+
73+
# LEDs
74+
o_leds=self.gpio_0.gpio.o
75+
)
76+
77+
# Hardwire GPIO to output enabled
78+
m.d.comb += self.gpio_0.gpio.oe.eq(0xFF)
79+
80+
attach_data(self.flash, None, BinaryData(filename="software.bin", offset=0x00100000))
81+
82+
return m

picosoc_verilog/design/picorv32

Submodule picorv32 added at 87c89ac
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* PicoSoC - A simple example SoC using PicoRV32
3+
*
4+
* Copyright (C) 2017 Claire Xenia Wolf <claire@yosyshq.com>
5+
* Copyright (C) 2025 Myrtle Shah <gatecat@ds0.me>
6+
*
7+
* Permission to use, copy, modify, and/or distribute this software for any
8+
* purpose with or without fee is hereby granted, provided that the above
9+
* copyright notice and this permission notice appear in all copies.
10+
*
11+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18+
*
19+
*/
20+
21+
`define PICOSOC_MEM picosoc_asic_mem
22+
23+
module picosoc_asic_top (
24+
input clk,
25+
input resetn,
26+
27+
output ser_tx,
28+
input ser_rx,
29+
30+
output [7:0] leds,
31+
32+
output flash_csb,
33+
output flash_clk,
34+
35+
output flash_io0_oe,
36+
output flash_io1_oe,
37+
output flash_io2_oe,
38+
output flash_io3_oe,
39+
40+
output flash_io0_do,
41+
output flash_io1_do,
42+
output flash_io2_do,
43+
output flash_io3_do,
44+
45+
input flash_io0_di,
46+
input flash_io1_di,
47+
input flash_io2_di,
48+
input flash_io3_di
49+
);
50+
51+
wire iomem_valid;
52+
reg iomem_ready;
53+
wire [3:0] iomem_wstrb;
54+
wire [31:0] iomem_addr;
55+
wire [31:0] iomem_wdata;
56+
reg [31:0] iomem_rdata;
57+
58+
reg [31:0] gpio;
59+
assign leds = gpio;
60+
61+
always @(posedge clk) begin
62+
if (!resetn) begin
63+
gpio <= 0;
64+
end else begin
65+
iomem_ready <= 0;
66+
if (iomem_valid && !iomem_ready && iomem_addr[31:24] == 8'h 03) begin
67+
iomem_ready <= 1;
68+
iomem_rdata <= gpio;
69+
if (iomem_wstrb[0]) gpio[ 7: 0] <= iomem_wdata[ 7: 0];
70+
if (iomem_wstrb[1]) gpio[15: 8] <= iomem_wdata[15: 8];
71+
if (iomem_wstrb[2]) gpio[23:16] <= iomem_wdata[23:16];
72+
if (iomem_wstrb[3]) gpio[31:24] <= iomem_wdata[31:24];
73+
end
74+
end
75+
end
76+
77+
picosoc soc (
78+
.clk (clk ),
79+
.resetn (resetn ),
80+
81+
.ser_tx (ser_tx ),
82+
.ser_rx (ser_rx ),
83+
84+
.flash_csb (flash_csb ),
85+
.flash_clk (flash_clk ),
86+
87+
.flash_io0_oe (flash_io0_oe),
88+
.flash_io1_oe (flash_io1_oe),
89+
.flash_io2_oe (flash_io2_oe),
90+
.flash_io3_oe (flash_io3_oe),
91+
92+
.flash_io0_do (flash_io0_do),
93+
.flash_io1_do (flash_io1_do),
94+
.flash_io2_do (flash_io2_do),
95+
.flash_io3_do (flash_io3_do),
96+
97+
.flash_io0_di (flash_io0_di),
98+
.flash_io1_di (flash_io1_di),
99+
.flash_io2_di (flash_io2_di),
100+
.flash_io3_di (flash_io3_di),
101+
102+
.irq_5 (1'b0 ),
103+
.irq_6 (1'b0 ),
104+
.irq_7 (1'b0 ),
105+
106+
.iomem_valid (iomem_valid ),
107+
.iomem_ready (iomem_ready ),
108+
.iomem_wstrb (iomem_wstrb ),
109+
.iomem_addr (iomem_addr ),
110+
.iomem_wdata (iomem_wdata ),
111+
.iomem_rdata (iomem_rdata )
112+
);
113+
endmodule
114+
115+
module picosoc_asic_mem #(
116+
parameter integer WORDS = 256
117+
) (
118+
input clk,
119+
input [3:0] wen,
120+
input [21:0] addr,
121+
input [31:0] wdata,
122+
output reg [31:0] rdata
123+
);
124+
reg [31:0] mem [0:WORDS-1];
125+
126+
always @(posedge clk) begin
127+
if (wen == 4'b0)
128+
rdata <= mem[addr];
129+
if (wen[0]) mem[addr][ 7: 0] <= wdata[ 7: 0];
130+
if (wen[1]) mem[addr][15: 8] <= wdata[15: 8];
131+
if (wen[2]) mem[addr][23:16] <= wdata[23:16];
132+
if (wen[3]) mem[addr][31:24] <= wdata[31:24];
133+
end
134+
endmodule
135+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.o
2+
*.elf
3+
*.bin
4+
generated/
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import os
2+
import sys
3+
from pathlib import Path
4+
import shutil
5+
6+
from doit import create_after
7+
from doit.action import CmdAction
8+
import chipflow_lib.config
9+
10+
11+
BUILD_DIR = "./build/software"
12+
DESIGN_DIR = os.path.dirname(__file__) + "/.."
13+
RISCVCC = f"{sys.executable} -m ziglang cc -target riscv32-freestanding-musl"
14+
CINCLUDES = f"-I. -I{BUILD_DIR} -I{DESIGN_DIR}/software"
15+
LINKER_SCR = f"{DESIGN_DIR}/software/sections.lds"
16+
SOFTWARE_START = f"{DESIGN_DIR}/software/start.s"
17+
CFLAGS = f"-g -mcpu=baseline_rv32-a-c-d -mabi=ilp32 -Wl,--build-id=none,-Bstatic,-T,"
18+
CFLAGS += f"{LINKER_SCR},--strip-debug -static -ffreestanding -nostdlib {CINCLUDES}"
19+
20+
21+
def task_gather_depencencies():
22+
src_files = []
23+
target_files = []
24+
25+
# Project dependencies
26+
rel_paths = _get_source_rel_paths(f"{DESIGN_DIR}/software", ["*.c", "*.h"])
27+
for rel_path in rel_paths:
28+
src_files.append(f"{DESIGN_DIR}/software{rel_path}")
29+
target_files.append(f"{BUILD_DIR}/{rel_path}")
30+
31+
def copy_files():
32+
_create_build_dir()
33+
for i in range(len(src_files)):
34+
shutil.copyfile(src_files[i - 1], target_files[i - 1])
35+
36+
return {
37+
"actions": [(copy_files)],
38+
"file_dep": src_files,
39+
"targets": target_files,
40+
"verbosity": 2
41+
}
42+
43+
44+
@create_after(executed="gather_depencencies", target_regex=".*/software\\.elf")
45+
def task_build_software_elf():
46+
sources = [SOFTWARE_START]
47+
sources += _gather_source_paths(f"{BUILD_DIR}", ["*.c"])
48+
49+
sources_str = " ".join(sources)
50+
51+
return {
52+
"actions": [f"{RISCVCC} {CFLAGS} -o {BUILD_DIR}/software.elf {sources_str}"],
53+
"file_dep": sources + [LINKER_SCR],
54+
"targets": [f"{BUILD_DIR}/software.elf"],
55+
"verbosity": 2
56+
}
57+
58+
59+
@create_after(executed="build_software_elf", target_regex=".*/software\\.bin")
60+
def task_build_software():
61+
return {
62+
"actions": [f"{sys.executable} -m ziglang objcopy -O binary "
63+
f"{BUILD_DIR}/software.elf {BUILD_DIR}/software.bin"],
64+
"file_dep": [f"{BUILD_DIR}/software.elf"],
65+
"targets": [f"{BUILD_DIR}/software.bin"],
66+
}
67+
68+
69+
def _create_build_dir():
70+
Path(f"{BUILD_DIR}").mkdir(parents=True, exist_ok=True)
71+
72+
73+
def _get_source_rel_paths(source_dir, globs):
74+
abs_source_dir = str(Path(source_dir).absolute())
75+
rel_paths = []
76+
for glob in globs:
77+
source_paths = list(Path(abs_source_dir).glob(glob))
78+
for source_path in source_paths:
79+
dst = str(source_path).replace(abs_source_dir, "")
80+
rel_paths.append(dst)
81+
82+
return rel_paths
83+
84+
85+
def _gather_source_paths(source_dir, globs):
86+
sources = []
87+
for glob in globs:
88+
source_paths = list(Path(source_dir).glob(glob))
89+
for source_path in source_paths:
90+
sources.append(f"{source_dir}/" + str(source_path.name))
91+
92+
return sources

0 commit comments

Comments
 (0)