Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
design: ['mcu_soc', 'minimal']
design: ['mcu_soc', 'minimal', 'picosoc_verilog']
steps:
- name: Check out source code
uses: actions/checkout@v4
with:
submodules: true

- uses: actions/setup-python@v4
with:
Expand Down Expand Up @@ -61,10 +63,12 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
design: ['mcu_soc', 'minimal']
design: ['mcu_soc', 'minimal', 'picosoc_verilog']
steps:
- name: Check out source code
uses: actions/checkout@v4
with:
submodules: true

- name: Set up PDM
uses: pdm-project/setup-pdm@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ __pycache__/
/build
/mcu_soc/build
/minimal/build
/picosoc_verilog/build

# testbenches
*.vcd
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "picosoc_verilog/design/picorv32"]
path = picosoc_verilog/design/picorv32
url = https://github.com/YosysHQ/picorv32
4 changes: 4 additions & 0 deletions picosoc_verilog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# PicoSoC (Verilog)

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.

15 changes: 15 additions & 0 deletions picosoc_verilog/chipflow.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[chipflow]
project_name = "chipflow-examples-picosoc"

[chipflow.top]
soc = "design.design:MySoC"

[chipflow.steps]
software = "design.steps.software:MySoftwareStep"

[chipflow.silicon]
process = "sky130"
package = "openframe"

[chipflow.test]
event_reference = "design/tests/events_reference.json"
82 changes: 82 additions & 0 deletions picosoc_verilog/design/design.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import os

from chipflow_lib.platforms.sim import SimPlatform

from amaranth import Module, Instance, ClockSignal, ResetSignal
from amaranth.lib import wiring
from amaranth.lib.wiring import In, Out, flipped, connect

from chipflow_lib.platforms import (
GPIOSignature, UARTSignature, QSPIFlashSignature,
BinaryData, attach_data
)

__all__ = ["MySoC"]


class MySoC(wiring.Component):
def __init__(self):
# Top level interfaces

super().__init__({
"flash": Out(QSPIFlashSignature()),
"uart_0": Out(UARTSignature()),
"gpio_0": Out(GPIOSignature(pin_count=8)),
})

def elaborate(self, platform):
m = Module()

base = os.path.dirname(__file__)

verilog_sources = [
f"{base}/picosoc_asic_top.v",
f"{base}/picorv32/picosoc/spimemio.v",
f"{base}/picorv32/picosoc/simpleuart.v",
f"{base}/picorv32/picosoc/picosoc.v",
f"{base}/picorv32/picorv32.v",
]

if platform is not None:
for verilog_file in verilog_sources:
with open(verilog_file, 'r') as f:
platform.add_file(verilog_file, f)

m.submodules.soc = soc = Instance("picosoc_asic_top",
# Clock and reset
i_clk=ClockSignal(),
i_resetn=~ResetSignal(),

# UART
o_ser_tx=self.uart_0.tx.o,
i_ser_rx=self.uart_0.rx.i,

# SPI flash
o_flash_csb=self.flash.csn.o,
o_flash_clk=self.flash.clk.o,

o_flash_io0_oe=self.flash.d.oe[0],
o_flash_io1_oe=self.flash.d.oe[1],
o_flash_io2_oe=self.flash.d.oe[2],
o_flash_io3_oe=self.flash.d.oe[3],

o_flash_io0_do=self.flash.d.o[0],
o_flash_io1_do=self.flash.d.o[1],
o_flash_io2_do=self.flash.d.o[2],
o_flash_io3_do=self.flash.d.o[3],

i_flash_io0_di=self.flash.d.i[0],
i_flash_io1_di=self.flash.d.i[1],
i_flash_io2_di=self.flash.d.i[2],
i_flash_io3_di=self.flash.d.i[3],

# LEDs
o_leds=self.gpio_0.gpio.o
Comment on lines +46 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, might be something worth putting in a toml description?

)

# Hardwire GPIO to output enabled
m.d.comb += self.gpio_0.gpio.oe.eq(0xFF)

attach_data(self.flash, None, BinaryData(filename="software.bin", offset=0x00100000))

return m
1 change: 1 addition & 0 deletions picosoc_verilog/design/picorv32
Submodule picorv32 added at 87c89a
135 changes: 135 additions & 0 deletions picosoc_verilog/design/picosoc_asic_top.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 Claire Xenia Wolf <[email protected]>
* Copyright (C) 2025 Myrtle Shah <[email protected]>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/

`define PICOSOC_MEM picosoc_asic_mem

module picosoc_asic_top (
input clk,
input resetn,

output ser_tx,
input ser_rx,

output [7:0] leds,

output flash_csb,
output flash_clk,

output flash_io0_oe,
output flash_io1_oe,
output flash_io2_oe,
output flash_io3_oe,

output flash_io0_do,
output flash_io1_do,
output flash_io2_do,
output flash_io3_do,

input flash_io0_di,
input flash_io1_di,
input flash_io2_di,
input flash_io3_di
);

wire iomem_valid;
reg iomem_ready;
wire [3:0] iomem_wstrb;
wire [31:0] iomem_addr;
wire [31:0] iomem_wdata;
reg [31:0] iomem_rdata;

reg [31:0] gpio;
assign leds = gpio;

always @(posedge clk) begin
if (!resetn) begin
gpio <= 0;
end else begin
iomem_ready <= 0;
if (iomem_valid && !iomem_ready && iomem_addr[31:24] == 8'h 03) begin
iomem_ready <= 1;
iomem_rdata <= gpio;
if (iomem_wstrb[0]) gpio[ 7: 0] <= iomem_wdata[ 7: 0];
if (iomem_wstrb[1]) gpio[15: 8] <= iomem_wdata[15: 8];
if (iomem_wstrb[2]) gpio[23:16] <= iomem_wdata[23:16];
if (iomem_wstrb[3]) gpio[31:24] <= iomem_wdata[31:24];
end
end
end

picosoc soc (
.clk (clk ),
.resetn (resetn ),

.ser_tx (ser_tx ),
.ser_rx (ser_rx ),

.flash_csb (flash_csb ),
.flash_clk (flash_clk ),

.flash_io0_oe (flash_io0_oe),
.flash_io1_oe (flash_io1_oe),
.flash_io2_oe (flash_io2_oe),
.flash_io3_oe (flash_io3_oe),

.flash_io0_do (flash_io0_do),
.flash_io1_do (flash_io1_do),
.flash_io2_do (flash_io2_do),
.flash_io3_do (flash_io3_do),

.flash_io0_di (flash_io0_di),
.flash_io1_di (flash_io1_di),
.flash_io2_di (flash_io2_di),
.flash_io3_di (flash_io3_di),

.irq_5 (1'b0 ),
.irq_6 (1'b0 ),
.irq_7 (1'b0 ),

.iomem_valid (iomem_valid ),
.iomem_ready (iomem_ready ),
.iomem_wstrb (iomem_wstrb ),
.iomem_addr (iomem_addr ),
.iomem_wdata (iomem_wdata ),
.iomem_rdata (iomem_rdata )
);
endmodule

module picosoc_asic_mem #(
parameter integer WORDS = 256
) (
input clk,
input [3:0] wen,
input [21:0] addr,
input [31:0] wdata,
output reg [31:0] rdata
);
reg [31:0] mem [0:WORDS-1];

always @(posedge clk) begin
if (wen == 4'b0)
rdata <= mem[addr];
if (wen[0]) mem[addr][ 7: 0] <= wdata[ 7: 0];
if (wen[1]) mem[addr][15: 8] <= wdata[15: 8];
if (wen[2]) mem[addr][23:16] <= wdata[23:16];
if (wen[3]) mem[addr][31:24] <= wdata[31:24];
end
endmodule

4 changes: 4 additions & 0 deletions picosoc_verilog/design/software/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.o
*.elf
*.bin
generated/
92 changes: 92 additions & 0 deletions picosoc_verilog/design/software/doit_build.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't think this should be necessary now?

if the chipflow-lib impl doesnt do something you need, lets fix that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be a ton of work to get software building working with Verilog, and I don't think everyone is even going to want this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough, see other comment..

Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import os
import sys
from pathlib import Path
import shutil

from doit import create_after
from doit.action import CmdAction
import chipflow_lib.config


BUILD_DIR = "./build/software"
DESIGN_DIR = os.path.dirname(__file__) + "/.."
RISCVCC = f"{sys.executable} -m ziglang cc -target riscv32-freestanding-musl"
CINCLUDES = f"-I. -I{BUILD_DIR} -I{DESIGN_DIR}/software"
LINKER_SCR = f"{DESIGN_DIR}/software/sections.lds"
SOFTWARE_START = f"{DESIGN_DIR}/software/start.s"
CFLAGS = f"-g -mcpu=baseline_rv32-a-c-d -mabi=ilp32 -Wl,--build-id=none,-Bstatic,-T,"
CFLAGS += f"{LINKER_SCR},--strip-debug -static -ffreestanding -nostdlib {CINCLUDES}"


def task_gather_depencencies():
src_files = []
target_files = []

# Project dependencies
rel_paths = _get_source_rel_paths(f"{DESIGN_DIR}/software", ["*.c", "*.h"])
for rel_path in rel_paths:
src_files.append(f"{DESIGN_DIR}/software{rel_path}")
target_files.append(f"{BUILD_DIR}/{rel_path}")

def copy_files():
_create_build_dir()
for i in range(len(src_files)):
shutil.copyfile(src_files[i - 1], target_files[i - 1])

return {
"actions": [(copy_files)],
"file_dep": src_files,
"targets": target_files,
"verbosity": 2
}


@create_after(executed="gather_depencencies", target_regex=".*/software\\.elf")
def task_build_software_elf():
sources = [SOFTWARE_START]
sources += _gather_source_paths(f"{BUILD_DIR}", ["*.c"])

sources_str = " ".join(sources)

return {
"actions": [f"{RISCVCC} {CFLAGS} -o {BUILD_DIR}/software.elf {sources_str}"],
"file_dep": sources + [LINKER_SCR],
"targets": [f"{BUILD_DIR}/software.elf"],
"verbosity": 2
}


@create_after(executed="build_software_elf", target_regex=".*/software\\.bin")
def task_build_software():
return {
"actions": [f"{sys.executable} -m ziglang objcopy -O binary "
f"{BUILD_DIR}/software.elf {BUILD_DIR}/software.bin"],
"file_dep": [f"{BUILD_DIR}/software.elf"],
"targets": [f"{BUILD_DIR}/software.bin"],
}


def _create_build_dir():
Path(f"{BUILD_DIR}").mkdir(parents=True, exist_ok=True)


def _get_source_rel_paths(source_dir, globs):
abs_source_dir = str(Path(source_dir).absolute())
rel_paths = []
for glob in globs:
source_paths = list(Path(abs_source_dir).glob(glob))
for source_path in source_paths:
dst = str(source_path).replace(abs_source_dir, "")
rel_paths.append(dst)

return rel_paths


def _gather_source_paths(source_dir, globs):
sources = []
for glob in globs:
source_paths = list(Path(source_dir).glob(glob))
for source_path in source_paths:
sources.append(f"{source_dir}/" + str(source_path.name))

return sources
Loading
Loading