Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
bieganski committed Aug 17, 2024
2 parents d949a61 + 64f5867 commit 6e13211
Show file tree
Hide file tree
Showing 27 changed files with 748 additions and 251 deletions.
31 changes: 23 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,39 @@ on:

jobs:
build:
name: Build and test on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
# Specify here the list of images to use for the machine
os: [ubuntu-22.04]
name: Build/simulate/synthesisze mtkcpu
runs-on: ubuntu-22.04
steps:
- name: Checkout the project
uses: actions/checkout@v2

- name: Build the Docker image
shell: bash
run: make build-docker

- name: Run unit tests
shell: bash
run: make unit-test-docker
run: ./docker_run.py --cmd 'poetry run pytest -n15 ./mtkcpu/'

- name: Run simulation with UART on stdout
shell: bash
run: |
./docker_run.py --cmd 'poetry run mtkcpu/cli/top.py gen_linker_script'
./docker_run.py --cmd 'make -B -C sw/uart_tx'
bash -c 'timeout 30 ./docker_run.py --cmd "poetry run ./mtkcpu/cli/top.py sim -e sw/uart_tx/build/uart_tx.elf" || true' | tee log.txt
grep -q "Hello from mtkCPU" log.txt
- name: Run OCD tests
shell: bash
run: make test-ocd-gdb-docker
run: ./docker_run.py --cmd 'poetry run python3 mtkcpu/tests/test_jtag.py && cat ckpt.log'

- name: Run (external) riscv-tests tests
shell: bash
run: |
make fetch-riscv-tests-isa # 'isa' directory will be created locally.
mv ./isa sw/
./docker_run.py --cmd 'poetry run python3 mtkcpu/tests/test_external_riscv_tests.py /toolchain/sw/isa' # NOTE issues with paths, on host-docker boundary.
- name: Synthesisze SoC
shell: bash
run: |
Expand Down
8 changes: 4 additions & 4 deletions Dockerfile.riscv-fpga
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ RUN apt-get install -y libboost-all-dev libeigen3-dev

# By default yosys and iceprog are installed in /usr/local/bin/, and for nextpnr we specify it explicitly.

RUN git clone https://github.com/YosysHQ/icestorm && cd icestorm && make -j15 && make install
RUN git clone https://github.com/YosysHQ/yosys && cd yosys && git submodule update --init --recursive && make -j15 && make install
RUN git clone https://github.com/YosysHQ/nextpnr && cd nextpnr && cmake . -DARCH=ice40 -DICESTORM_INSTALL_PREFIX=/usr/local/ && make -j15 && make install
RUN git clone https://github.com/YosysHQ/icestorm && cd icestorm && make -j$(nproc) && make install
RUN git clone https://github.com/YosysHQ/yosys && cd yosys && git submodule update --init --recursive && make -j$(nproc) && make install
RUN git clone https://github.com/YosysHQ/nextpnr && cd nextpnr && cmake . -DARCH=ice40 -DICESTORM_INSTALL_PREFIX=/usr/local/ && make -j$(nproc) && make install

# Make sure all synthesis binaries are in PATH.
RUN which iceprog yosys nextpnr-ice40

# Install RISC-V OpenOCD.
RUN git clone https://github.com/riscv/riscv-openocd
RUN cd riscv-openocd && ./bootstrap && ./configure && make -j15 && make install
RUN cd riscv-openocd && ./bootstrap && ./configure && make -j$(nproc) && make install
RUN which openocd

# Cleanup the container.
Expand Down
21 changes: 21 additions & 0 deletions Dockerfile.riscv-tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from bieganski/riscv-fpga:16-07-2024

# ARG DEBIAN_FRONTEND=noninteractive

# RUN apt-get update
# RUN apt-get install -y build-essential git automake gcc-riscv64-unknown-elf

WORKDIR /

RUN git clone https://github.com/riscv-software-src/riscv-tests

ENV PATH="/root/.local/xPacks/@xpack-dev-tools/riscv-none-elf-gcc/13.2.0-1.2/.content/bin:$PATH"

WORKDIR riscv-tests
# RUN which riscv64-none-elf-gcc

RUN git submodule update --init --recursive
RUN autoconf
RUN ./configure --with-xlen=32

RUN cd isa && make -j12 RISCV_PREFIX=riscv-none-elf- XLEN=32
310 changes: 282 additions & 28 deletions LICENSE.txt

Large diffs are not rendered by default.

20 changes: 9 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,15 @@ build:
build-docker:
bash ./build_docker_image.sh

DRUN := docker run -v $(MAKEFILE_DIR)/sw:/toolchain/sw $(DOCKER_IMAGE_NAME)
DRUN_IT := docker run -v $(MAKEFILE_DIR)/sw:/toolchain/sw -v /dev:/dev -it $(DOCKER_IMAGE_NAME)

unit-test-docker:
$(DRUN) poetry run mtkcpu tests cpu

test-ocd-gdb-docker:
$(DRUN) sh -c 'poetry run python3 mtkcpu/tests/test_jtag.py && cat ckpt.log'

run-docker-it:
$(DRUN_IT) bash
build-docker-riscv-tests:
docker build -f Dockerfile.riscv-tests -t riscv-tests .

fetch-riscv-tests-isa: build-docker-riscv-tests
@rm -rf isa
@docker rm -f dummy
docker create --name dummy riscv-tests
docker cp dummy:/riscv-tests/isa .
docker rm -f dummy

fetch-gcc: export id := $(shell docker create $(DOCKER_IMAGE_NAME))
fetch-gcc: export temp := $(shell mktemp -p .)
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ For more information about how tests work, please refer to [that file](./doc/tes
* [Robert Baruch's refreshed RiscV playlist](https://www.youtube.com/playlist?list=PLEeZWGE3PwbZTypHq00G-yEX8TEI95lw4)


### Acknowledgements
### License, acknowledgements

`mtkCPU` is licensed under GPLv2 terms. Some parts of `mtkCPU` come from [minerva CPU](https://github.com/lambdaconcept/minerva) (LambdaConcept's property, released under [license](doc/MINERVA_LICENSE.txt)).

Some parts of `mtkCPU` were inspired by [minerva CPU](https://github.com/lambdaconcept/minerva) parts (LambdaConcept's property).
29 changes: 29 additions & 0 deletions doc/MINERVA_LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Copyright (C) Jean-François Nguyen <[email protected]>
Copyright (C) 2018-2021 LambdaConcept

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


Other authors retain ownership of their contributions. If a submission can
reasonably be considered independently copyrightable, it's yours and we
encourage you to claim it with appropriate copyright notices. This submission
then falls under the "otherwise noted" category. All submissions are strongly
encouraged to use the two-clause BSD license reproduced above.
4 changes: 3 additions & 1 deletion doc/gdb.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,10 @@ Info : Listening on port 3333 for gdb connections

## GDB

BUG: To find suitable GDB version, see https://github.com/bieganski/mtkcpu/issues/69

```bash
riscv-none-elf-gdb -x board/breakpoint_example.gdb sw/blink_led/build/blink_led.elf
gdb-multiarch -x board/breakpoint_example.gdb sw/blink_led/build/blink_led.elf
```

Expected output contains following:
Expand Down
4 changes: 3 additions & 1 deletion docker_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,16 @@ def main(cmd : Optional[str]):

command = f"""
docker run \
--init --tty \
--net host \
{construct_groups_params()} \
-v {Path(__file__).parent}/sw:/toolchain/sw \
{interactive} {container_name} {cmd}
"""
print(command)

subprocess.run(command, shell=True)
completed_process = subprocess.run(command, shell=True)
exit(completed_process.returncode)


if __name__ == "__main__":
Expand Down
17 changes: 0 additions & 17 deletions mtkcpu/cli/main.py

This file was deleted.

17 changes: 0 additions & 17 deletions mtkcpu/cli/tests.py

This file was deleted.

107 changes: 86 additions & 21 deletions mtkcpu/cli/top.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
import logging
from pathlib import Path
from typing import Optional
from typing import Optional, Callable
import os

from amaranth.sim import Simulator
from amaranth.build.plat import Platform
from amaranth.hdl import Module

Expand All @@ -15,28 +15,38 @@
from mtkcpu.units.memory_interface import AddressManager
from mtkcpu.utils.linker import write_linker_script
from mtkcpu.cpu.cpu import CPU_Config
from mtkcpu.utils.tests.dmi_utils import monitor_pc_and_main_fsm

import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__file__)

def get_board_cpu(elf_path : Optional[Path], cpu_config: CPU_Config):
def get_board_cpu(elf_path : Optional[Path], cpu_config: CPU_Config, num_bytes: Optional[int] = 1024):
"""
If 'num_bytes' is None, it will automatically adjust memory size so that the ELF fits. Useful for simulation.
"""
if elf_path:
mem = read_elf(elf_path, verbose=False)
# logger.info(f"ELF {elf_path} memory content: {mem}")
logger.info(f"== read elf: {len(mem)}*4 ()= {len(mem) * 4}) bytes")
max_offset = max(mem.keys()) - CODE_START_ADDR
logger.info(f"== read elf: {len(mem)}*4 ()= {len(mem) * 4}) non-bss bytes, max_offset: {hex(max_offset)}")
num_bytes = num_bytes or (max_offset + 4)

if num_bytes > 1_000_000:
logging.warning(f"Huge CPU memory size: {num_bytes}")

if elf_path:
mem_config = EBRMemConfig.from_mem_dict(
simulate=False,
simulate=True,
start_addr=CODE_START_ADDR,
num_bytes=1024,
num_bytes=num_bytes,
mem_dict=MemoryContents(mem)
)
else:
mem_config = EBRMemConfig(
mem_size_words=10, # TODO should we allow empty memory?
mem_size_words=num_bytes // 4,
mem_content_words=None,
mem_addr=CODE_START_ADDR,
simulate=False
simulate=True,
)
return MtkCpu(mem_config=mem_config, cpu_config=cpu_config)

Expand Down Expand Up @@ -71,14 +81,55 @@ def get_platform() -> Platform:

return platform

def uart_process(cpu: MtkCpu):
def aux():
from mtkcpu.units.mmio.uart import UartTX
uart_block_matches = [block for (block, _) in cpu.arbiter.mmio_cfg if isinstance(block, UartTX)]
if len(uart_block_matches) != 1:
raise ValueError(f"Could not determine UART block! Was expecting one match, got {len(uart_block_matches)} instead! {uart_block_matches}")
uart = uart_block_matches[0]
bus = uart._wb_slave_bus.wb_bus
prev_bus_cyc = 0

print ("starting UART tx..")
while True:
bus_cyc = yield bus.cyc
if bus_cyc and not prev_bus_cyc:
# transaction initiated
adr = yield bus.adr
mask = yield bus.we
# origin of those magic constants is 'handle_transaction' method of UartTX block.
if (adr == 0x8) and (mask & 1):
tx_byte = (yield bus.dat_w) & 0xff
print(chr(tx_byte), end="")
prev_bus_cyc = bus_cyc
yield
return aux

def sim(cpu: MtkCpu, verbose: bool, with_uart: bool, user_processes: list[Callable] = [], regs_verbose: list[int] = []):
sim = Simulator(cpu)
sim.add_clock(1e-6)

if with_uart:
sim.add_sync_process(uart_process(cpu=cpu))

if verbose:
sim.add_sync_process(monitor_pc_and_main_fsm(cpu=cpu, wait_for_first_haltreq=False, log_fn=print, regs_verbose=regs_verbose))

for p in user_processes:
# user-defined processes. could be both passive or active.
sim.add_sync_process(p)

with sim.write_vcd("uart.vcd"):
sim.run()

def build(
elf_path : Optional[Path],
do_program: bool,
cpu_config: CPU_Config):
platform = get_platform()
m = get_board_cpu(elf_path=elf_path, cpu_config=cpu_config)
platform.build(m, do_program=do_program)
platform.build(m, do_program=do_program, nextpnr_opts="--timing-allow-fail")
logger.info(f"OK, Design was built successfully, printing out some stats..")
timing_report = Path("build/top.tim")
if not timing_report.exists():
Expand Down Expand Up @@ -149,33 +200,47 @@ def generate_bsp():
def main():
from argparse import ArgumentParser
parser = ArgumentParser()
sp = parser.add_subparsers(required=True, dest="command")

build_p = sp.add_parser("build", help="Build the IceBreaker bitstream containing full SoC.")
build_p.add_argument("--no_dm", action="store_true")
build_p.add_argument("--dev_mode", action="store_true")
build_p.add_argument("--with_virtual_memory", action="store_true")
build_p.add_argument("-e", "--elf", type=Path, help="Path to an .elf file to initialize Block RAM with.")
build_p.add_argument("-p", "--program", action="store_true")
subparsers = parser.add_subparsers(required=True, dest="command")

sp.add_parser("gen_bsp", help="Generate bsp .c and .h sources, based on SoC address space.")
sp.add_parser("gen_linker_script", help="Generate linker script, based on SoC address space.")
build_parser = subparsers.add_parser("build", help="Build the IceBreaker bitstream containing full SoC.")
sim_parser = subparsers.add_parser("sim", help="Simulate mtkcpu with given ELF. The UART is printed to stdout.")
_ = subparsers.add_parser("gen_bsp", help="Generate bsp .c and .h sources, based on SoC address space.")
_ = subparsers.add_parser("gen_linker_script", help="Generate linker script, based on SoC address space.")

for p in [build_parser, sim_parser]:
p.add_argument("--no_dm", action="store_true")
p.add_argument("--dev_mode", action="store_true")
p.add_argument("--with_virtual_memory", action="store_true")
p.add_argument("-e", "--elf", type=Path, required=(parser is sim_parser), help="Path to an .elf file to initialize Block RAM with.")

sim_parser.add_argument("-v", "--verbose", action="store_true")

build_parser.add_argument("-p", "--program", action="store_true")

args = parser.parse_args()


if args.command == "build":
if args.command in ["build", "sim"]:
cpu_config = CPU_Config(
with_debug=(not args.no_dm),
dev_mode=args.dev_mode,
pc_reset_value=CODE_START_ADDR,
with_virtual_memory=args.with_virtual_memory,
)

if args.command == "build":
build(
elf_path=args.elf,
do_program=args.program,
cpu_config=cpu_config,
)
elif args.command == "sim":
cpu = get_board_cpu(elf_path=args.elf, cpu_config=cpu_config, num_bytes=None)
sim(
cpu=cpu,
with_uart=True,
verbose=args.verbose,
)
elif args.command == "gen_bsp":
generate_bsp()
elif args.command == "gen_linker_script":
Expand Down
Loading

0 comments on commit 6e13211

Please sign in to comment.