“Time is not refundable.” — Sir Genius

  • Renvatek Quartz-1 passes all 39 RISC-V ACT4 architectural compliance tests

    Renvatek Quartz-1 passes all 39 RISC-V ACT4 architectural compliance tests

    What’s Renvatek and this core about?

    On this month’s very first day, I bought two .com domains: renvatek.com (canonical) and renvatech.com (redirect). Pretty sure the name “Renvatek” is pronounceable by almost everyone (hopefully). From now on, the entity “Renvatek” will serve as my in-house computational-cores designing powerhouse!

    And the good news is that I have completed the RTL development and the verification for its very first core, the Renvatek Quartz-1! Heads up, this post is going to be about verification and compliance details of this core entirely.

    Renvatek Quartz-1 (QE001001RV32IN0), my single-cycle RV32I core, just passed all 39 RISC-V ACT4 architectural compliance tests. 100% pass rate on the very first shot.

    Here’s what that took, and what actually went wrong along the way.

    What ACT4 tests

    ACT4 is the official RISC-V Architectural Certification Test framework, maintained by the RISC-V Foundation at riscv/riscv-arch-test. For the RV32I base ISA, there are 39 self-checking test programs, one per instruction or instruction group, covering the entire base instruction set: arithmetic, branches, jumps, loads, stores, shifts, and comparisons.

    Each test’s expected result is pre-computed by Sail, the Foundation’s reference model, and baked into the ELF at build time. So no live reference model is running alongside my CPU during simulation. My hardware computes a result, checks it against what Sail already decided the answer should be, and either matches or fails right there.

    I ran this against riscv-arch-test commit 5590e5c64a2ae83a0e4e29a20c3043676c02668f.

    Why this matters

    Architectural compliance means Quartz-1 implements RV32I as the spec says it should, instruction by instruction, and is checked against the same reference model the RISC-V Foundation uses. That’s the baseline any RISC-V core needs before anyone should trust it with real work, and it matters even more for something aimed at defense-grade, radiation-tolerant silicon (future to-do).

    The messy part

    Getting the core to run was one thing. Getting an actual verification flow around it was a different problem.

    I started by wiring the core into Verilator and Cocotb. The first test was deliberately simple: echo_ascii_chars (harnessing the virtual printer @ a print port register (IO): RVMODEL_IO_WRITE_STR macro @ rvmodel_macros.h). Verilator was able to build the whole rv32i_sc design and cocotb started correctly. The test itself passed, but there was already a warning sitting in the output:

    %Warning: imem_program.bin:0: $readmem file not found

    The test still reported PASS, which made the result a little misleading. At that point I was not yet running an architecture test through the instruction memory. I had a simulator that worked, but I did not yet have a useful compliance flow.

    The next problem was getting the RISC-V Architecture Tests framework running. I ended up dealing with the framework’s Python environment, uv, the generated test infrastructure, the Unified Database tooling (udb), and the different branches of riscv-arch-test. There were several attempts at this. At one point I had switched between main, act4 and Jordan Carlin‘s import_module_refactor branch while trying to get the new test-generation code working. The testgen package itself was failing during module discovery because of a circular import involving vector_helpers.py and vv_type.py. I flagged this issue @ #2061, which was fixed by Carlin (PR #2065) and merged to upstream.

    Eventually the framework got far enough to generate the test infrastructure. The output showed 177 test suites being generated, one DUT configuration being prepared, and 195 build targets already up to date. That was the first point where the compliance framework was actually doing something substantial with my core instead of just compiling the simulator.

    Then I had to figure out how to get the generated ELF files into my very specific memory model. The architecture tests produce ELF binaries, while my core expects its instruction memory in a binary format that can be consumed by $readmem. I wrote a runner that finds every generated ELF, extracts the relevant .text sections with riscv32-unknown-elf-objcopy, converts the resulting bytes into the bit ordering expected by my instruction memory, and writes them to imem_program.bin. I also extract .rodata, .data and .bss into a Verilog-format data-memory initialization file. Each test gets its own temporary directory so that the generated memory files do not collide with one another. Refs: link.ld

    That runner had its own problems.

    Because the scripts use set -euo pipefail, an unset PYTHONPATH immediately killed the run with:

    PYTHONPATH: unbound variable

    After fixing that, the simulator started launching, but cocotb could not load libpython3.13.so. The simulator would start, print the cocotb/VPI messages, and then finish, but the actual Python test environment could not be loaded.

    There were also smaller environment problems along the way. Running the compliance script before activating the correct environment produced cocotb-config: No such file or directory. The Python environment being used by the shell and the .venv expected by riscv-arch-test also kept disagreeing, which showed up repeatedly as VIRTUAL_ENV warnings. I had to keep untangling whether a failure was coming from my RTL, cocotb, Verilator, the architecture-test framework, Python, or simply the shell environment.

    That is probably the least glamorous part of building a CPU. At this stage, very little of the debugging was actually about the datapath. I was debugging the machinery around the datapath so that I could eventually trust the tests being run against it.

    The useful part is that the pieces are now separated. The architecture-test framework generates the tests, the ELF files are converted into the memory format my core expects, the runner launches the compiled Verilator model, and cocotb provides the interface between the test environment and the RTL. That gives me a path from an architectural test case all the way down to instructions actually being executed by my core.

    A personal note

    It’s been almost six months of RTL work (started late February this year), and this is the first result that felt real. Not “it compiled.” Not “it ran once and looked okay.” A test suite I didn’t write, checked against a reference model I don’t control, and every single case passed. The confusion at the start wasn’t wasted time. Having to trace the framework by hand is exactly what made the pass mean something.

    Next up are the pipelined variants, and eventually taking this from architectural correctness to radiation-hardened silicon. For today, though, I’m just sitting with this one :-).

    Video proof

    Do you also want to test this core by yourself?

    Third-party verification is the best thing I could ask for. For this, you have to clone the verification repository first.

    Then execute the following commands to verify this core using its simulated binary version:

    cd compliance
    chmod +x main.sh
    ./main.sh

    End note

    Quartz-1 is my first CPU core, and getting it from RTL to a point where someone else can actually run the verification suite was a big part of the process. There were plenty of rough edges along the way, especially around the verification setup, but getting the ACT4 tests passing and packaging the verification environment into something reproducible was a good milestone.

    This is only the beginning for Renvatek and for the cores that come after Quartz-1.

    Thanks for taking the time to read through the process.

  • Creating a unified coding style guide for GitHub and GitLab

    Creating a unified coding style guide for GitHub and GitLab

    I commit a lot to GitHub and my privately hosted GitLab server. To keep my coding style consistent across all my commits and repositories, I created mondal-mondal/coding-guidelines (yes, for that I had to create a GitHub organization for Mondal & Mondal first!).

    You may ask why I had to create an organization for that. Why could not I just host the repository in my personal namespace? It is because my brother also actively engages in the day-to-day research done in our private GitLab server. And for our future team members to be in our fleet pretty soon, I decided to spin off an organization to keep things “shared” instead of “personal.”

    Because many of us work in collaborative teams and projects, especially if the project is open source, people often cannot settle on one coding convention or style that they can cleanly inherit for the entire project without a “style guide.” A “style guide” is nothing but some written rules on preferences while coding to avoid style arguments between a project’s members. That central “style guide” can be this new repository.

    The repository is hosted on my private GitLab server, and a push mirror has been set up from GitLab to GitHub.

    Do not worry if your merged PRs would be overwritten or not, because GitLab does not force push to GitHub as I turned on the “Keep divergent refs” feature! “Keep divergent refs” feature allows you to keep a divergent history, for example, a merged PR, in GitHub without overwriting it using the master copy that is administered by GitLab. This helps the GitHub community to contribute to this repository without their efforts being destructively erased by a force push from GitLab.

    The young phase of this repository supports coding conventions across these languages only for now: Git, C++, Markdown, PlantUML, Python, and SystemVerilog.


    How to contribute to the repository using pull requests (for GitHub first-timers!)

    Any contribution to this repository, even for new languages that are still related and relevant to the RTL and verification industry, is appreciated. Follow the guide below to learn how to create a contribution, specifically a new GitHub pull request (PR):

    Fork this repository from here:

    …or directly fork by clicking on this button:

    Then, clone the forked repository to your local computer:

    git clone https://github.com/<your-github-username>/coding-guidelines

    cd to the cloned-repository directory and create a new branch from which you will create a PR:

    cd coding-guidelines
    
    # Create a new branch.
    git checkout -b <type>/<description>

    Refer to Conventional Branch to learn how to properly name your branch according to the type of the future PR.

    Open the forked repository with your favorite code editor. I will open it with VS Code for a demonstration here:

    code

    Make your necessary changes and do not forget to save them!

    After making your changes, push your changes:

    # Add all the files changed to the commit using the period wildcard.
    git add .
    
    git commit -m "<type>[optional scope]: <description>"
    git push origin <type>/<description>

    Refer to Conventional Commits to learn how to write a clean commit message.

    After pushing your changes, you are now finally ready to open a new PR to the upstream repository:

    1. Open your web browser and go to the original (upstream) repository or your forked repository.
    2. Look for a yellow banner at the top that reads: “<type>/<description> had recent pushes less than a minute ago.”
    3. Click the green “Compare & pull request” button next to it.

    You created your first PR! Now wait for an admin (usually me) to check your commit and approve the changes proposed by your commit(s) if they are valid. Thank you for spending your valuable time to read this quick tutorial and grow this repository.


    Star the repository on GitHub so you do not lose it:


    If you reached here, congratulations! You are one of the few people who actually care to grow the open-source world. Keep it up! See you next weekend.

  • How to start writing HDL, like SystemVerilog, as a total beginner

    A while back, late February this year, I started to build my single-cycle RISC-V (XLEN=32) CPU. The only problem was this: I did not know anything about CPUs, their internals, or where to start or how.

    Let me tell you a very important thing: do not chase any course, paid ones especially, to “learn” how to build your first RTL project. When I started, I did not even know the syntax of SystemVerilog, let alone know how to write RTL files.

    The only way to actually make your project is to see some YouTube videos or playlists and browse through simple RTL repositories.

    After a while, it will eventually click to you that when someone says to “write HDL”, they particularly mean that you have to describe the hardware using code, i.e., the HDL.

    Basically, we have to define the stuff in code that you experimented with low-level gates back in Logisim Evolution. Let me explain this using Logisim Evolution’s analogy.

    Modules and ports are just like the I/O pins (ports) that you use inside Logisim Evolution’s subcircuits (modules).

    module subcircuit (
      input  logic [1:0] pin1_i
      output logic [1:0] pin2_o
    );
    
      // Compute here, i.e., this subcircuit's tasks.
      // You also can declare internal wires that stay inside this subcircuit and never leave it.
      module_contents;
    
    endmodule : subcircuit

    See? This way, you can just “see” someone else’s code, learn the HDL’s syntax, and make your first project by taking theirs as a reference.

  • “amavisd.service failed” error when installing Plesk Email Security extension

    “amavisd.service failed” error when installing Plesk Email Security extension

    I was trying to install Plesk Email Security in my Plesk server, but an error popped up because amavisd.service failed to start. The error was exactly quoted like this:

    Execution systemctl has failed with exit code 1, stdout: , stderr: Job for amavisd.service failed because the control process exited with error code. See "systemctl status amavisd.service" and "journalctl -xeu amavisd.service" for details.

    You can directly use amavisd to verify what caused the error: /usr/sbin/amavisd -c /etc/amavisd/amavisd.conf 2>&1

    If the debug shows something about missing additional required dependencies (here, modules), like:

    ERROR: MISSING REQUIRED ADDITIONAL MODULES:
      DBD::mysql
      DBD::mysql

    Then DBD::mysql Perl module is missing. Simple fix:

    # Install
    sudo dnf install perl-DBD-MySQL -y
    
    # Then start amavisd.
    sudo systemctl start amavisd
    
    # Verify if amavisd started or not.
    sudo systemctl status amavisd

    If it starts cleanly, go back to Plesk and retry installing the email security extension.