Compare commits

..

No commits in common. "main" and "v1.0" have entirely different histories.
main ... v1.0

229 changed files with 3740 additions and 9579 deletions

View file

@ -1,52 +0,0 @@
name: Run compliance test suite
on: [push, pull_request]
jobs:
compliance:
name: RISC-V Compliance Test
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
path: serv
- name: install fusesoc, verilator, gcc and riscof
run: |
sudo apt-get install -y python3-setuptools verilator
pip3 install fusesoc
pip3 install riscof
wget -qO- https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2023.07.05/riscv64-elf-ubuntu-22.04-gcc-nightly-2023.07.05-nightly.tar.gz | tar xz
echo $GITHUB_WORKSPACE/riscv/bin >> $GITHUB_PATH
- name: set root and SERV directory
run: |
echo "SERV=$GITHUB_WORKSPACE/serv" >> $GITHUB_ENV
- name: setup workspace
run: fusesoc library add serv $SERV
- name: Setup SAIL-RISCV Model
run: |
tar -xzf $SERV/verif/bin/sail-riscv.tar.gz
echo $GITHUB_WORKSPACE/sail-riscv >> $GITHUB_PATH
- name: Init arch tests
run: riscof arch-test --clone
- name: Run RV32I compliance tests
run: riscof run --config=$SERV/verif/config.ini --suite=riscv-arch-test/riscv-test-suite/rv32i_m/I --env=riscv-arch-test/riscv-test-suite/env --no-browser
- name: Run RV32IM compliance tests
run: riscof run --config=$SERV/verif/config.ini --suite=riscv-arch-test/riscv-test-suite/rv32i_m/M --env=riscv-arch-test/riscv-test-suite/env --no-browser
- name: Run RV32IC compliance tests
run: riscof run --config=$SERV/verif/config.ini --suite=riscv-arch-test/riscv-test-suite/rv32i_m/C --env=riscv-arch-test/riscv-test-suite/env --no-browser
- name: Run RV32I Zifencei compliance tests
run: riscof run --config=$SERV/verif/config.ini --suite=riscv-arch-test/riscv-test-suite/rv32i_m/Zifencei --env=riscv-arch-test/riscv-test-suite/env --no-browser
- name: Run RV32I Privilege compliance tests
run: riscof run --config=$SERV/verif/config.ini --suite=riscv-arch-test/riscv-test-suite/rv32i_m/privilege --env=riscv-arch-test/riscv-test-suite/env --no-browser

View file

@ -1,67 +0,0 @@
name: Formal verification
on: [push]
jobs:
formal:
name: Run RISCV-formal verification suite
runs-on: ubuntu-latest
steps:
- name: Checkout riscv-formal
uses: actions/checkout@v4
with:
repository: YosysHQ/riscv-formal
- name: Checkout SERV
uses: actions/checkout@v4
with:
path: cores/serv/serv-src
- uses: YosysHQ/setup-oss-cad-suite@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare formal tests
run: |
cd cores/serv
python3 ../../checks/genchecks.py
# Skip non-instruction tests for now
# - run: make -C cores/serv/checks causal_ch0
# - run: make -C cores/serv/checks liveness_ch0
# - run: make -C cores/serv/checks pc_bwd_ch0
# - run: make -C cores/serv/checks pc_fwd_ch0
# - run: make -C cores/serv/checks reg_ch0
# - run: make -C cores/serv/checks unique_ch0
- run: make -C cores/serv/checks insn_add_ch0
- run: make -C cores/serv/checks insn_addi_ch0
- run: make -C cores/serv/checks insn_and_ch0
- run: make -C cores/serv/checks insn_andi_ch0
- run: make -C cores/serv/checks insn_auipc_ch0
- run: make -C cores/serv/checks insn_beq_ch0
- run: make -C cores/serv/checks insn_bge_ch0
- run: make -C cores/serv/checks insn_bgeu_ch0
- run: make -C cores/serv/checks insn_blt_ch0
- run: make -C cores/serv/checks insn_bltu_ch0
- run: make -C cores/serv/checks insn_bne_ch0
- run: make -C cores/serv/checks insn_jal_ch0
- run: make -C cores/serv/checks insn_jalr_ch0
- run: make -C cores/serv/checks insn_lb_ch0
- run: make -C cores/serv/checks insn_lbu_ch0
- run: make -C cores/serv/checks insn_lh_ch0
- run: make -C cores/serv/checks insn_lhu_ch0
- run: make -C cores/serv/checks insn_lui_ch0
- run: make -C cores/serv/checks insn_lw_ch0
- run: make -C cores/serv/checks insn_or_ch0
- run: make -C cores/serv/checks insn_ori_ch0
- run: make -C cores/serv/checks insn_sb_ch0
- run: make -C cores/serv/checks insn_sh_ch0
- run: make -C cores/serv/checks insn_sll_ch0
- run: make -C cores/serv/checks insn_slli_ch0
- run: make -C cores/serv/checks insn_slt_ch0
- run: make -C cores/serv/checks insn_slti_ch0
- run: make -C cores/serv/checks insn_sltiu_ch0
- run: make -C cores/serv/checks insn_sltu_ch0
- run: make -C cores/serv/checks insn_sra_ch0
- run: make -C cores/serv/checks insn_srai_ch0
- run: make -C cores/serv/checks insn_srl_ch0
- run: make -C cores/serv/checks insn_srli_ch0
- run: make -C cores/serv/checks insn_sub_ch0
- run: make -C cores/serv/checks insn_sw_ch0
- run: make -C cores/serv/checks insn_xor_ch0
- run: make -C cores/serv/checks insn_xori_ch0

View file

@ -1,22 +0,0 @@
name: Run linter
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
name: Linter
env:
REPO : serv
VLNV : serv
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
path: serv
- run: sudo apt install verilator
- run: pip3 install fusesoc
- run: fusesoc library add $REPO $GITHUB_WORKSPACE/$REPO
- run: fusesoc run --target=lint $VLNV
- run: fusesoc run --target=lint servant
- run: fusesoc run --target=lint serving

View file

@ -1,24 +0,0 @@
name: Build GDS using OpenLANE and sky130 PDK
on: [push]
jobs:
build-openlane-sky130:
runs-on: ubuntu-latest
env:
REPO : serv
VLNV : serv
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
path: serv
- run: echo "EDALIZE_LAUNCHER=el_docker" >> $GITHUB_ENV
- run: pip3 install fusesoc
- run: fusesoc library add $REPO $GITHUB_WORKSPACE/$REPO
- run: fusesoc run --target=sky130 $VLNV
- run: find -name *.gds
- name: Store artifacts
uses: actions/upload-artifact@v4
with:
name: serv.gds
path: /home/runner/work/serv/serv/serv/build/serv_1.3.0/sky130-openlane/runs/serv_synth_wrapper/results/final/gds/serv_synth_wrapper.gds

View file

@ -1,28 +0,0 @@
name: Build documentation
on:
push:
branches:
- master
jobs:
docs:
name: Build documentation
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v1
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
pip install -r doc/requirements.txt
- name: Build sphinx documentation
run: |
make -C doc html
- name: Deploy to gh-pages
uses: JamesIves/github-pages-deploy-action@4.1.1
with:
branch: gh-pages # The branch the action should deploy to.
folder: doc/_build/html # The folder the action should deploy.

4
.gitmodules vendored
View file

@ -0,0 +1,4 @@
[submodule "zephyr"]
path = zephyr
url = https://github.com/olofk/zephyr
branch = serv

View file

@ -1,16 +0,0 @@
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
version: 2
sphinx:
configuration: doc/conf.py
python:
install:
- requirements: doc/requirements.txt
build:
os: "ubuntu-22.04"
tools:
python: "3.11"

38
NEWS
View file

@ -1,38 +0,0 @@
1.3.0 2024-07-05 Olof Kindgren
======================================================
* Zephyr BSP: Port to Zephyr 3.5.0 + support tickless timer
* Make RF RAM IF work with single-port RAM
* Add PC tracing
* Make most modules width-independent
* Avoid releasing trap signal too early
* Improve timer wraparound behavior
* Overhaul documentation
* Add Servile convenience wrapper component
* Base Serving and Serving on Servile
* Add simulation cycle counter to testbench
* Add Hello world ASM example for Servant
* New Servant ports: Arty S7-50, PolarFire Splash Kit, Machdyne Kolibri, GMM-7550, Alchistry AU, ECP5 Evaluation board, Terasic DE1 SoC
1.2.1 2022-12-25 Olof Kindgren
======================================================
* Guarantee at least 2 cycles of o_rst after ice40 PLL is locked
* New Servant ports: ICE-V Wireless
* Add reset input for Arty A7
* Add Servant documentation
* Updated RISC-V Compliance support from 2.7.4 to 3.x
1.2.0 2022-07-25 Olof Kindgren
======================================================
* New Servant ports: EBAZ4205, Chameleon96, Nexys2, Alinx AX309
* Support for M ISA extension
* Support for C ISA extension
* Fix occasionally wrong sign on immediates
* Support for producing GDS with OpenLANE
* Fix Model/QuestaSim compatibility
* Add ViDBo support
* Improved documentation
* Less resource usage
* Updated RISC-V Compliance support from 1.0 to 2.7.4

213
README.md
View file

@ -2,139 +2,113 @@
# SERV
[![Join the chat at https://gitter.im/librecores/serv](https://badges.gitter.im/librecores/serv.svg)](https://gitter.im/librecores/serv?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Compliance tests](https://github.com/olofk/serv/actions/workflows/ci.yml/badge.svg)](https://github.com/olofk/serv/actions/workflows/ci.yml)
[![Documentation Status](https://readthedocs.org/projects/serv/badge/?version=latest)](https://serv.readthedocs.io/en/latest/?badge=latest)
SERV is an award-winning bit-serial RISC-V core
In fact, the award-winning SERV is the world's smallest RISC-V CPU. It's the perfect companion whenever you need a bit of computation and silicon real estate is at a premium.
## Prerequisites
How small is it then? Synthesizing the latest version of SERV in its most minimal form, yields the following results for some popular FPGA architectures and a typical CMOS process.
Create a directory to keep all the different parts of the project together. We
will refer to this directory as `$SERV` from now on.
| Lattice iCE40 | Intel Cyclone 10LP | AMD Artix-7 | CMOS |
| ------------- | ------------------ | ----------- | ------ |
| 198 LUT | 239 LUT | 125 LUT | 2.1kGE |
| 164 FF | 164 FF | 164 FF | |
Download the main serv repo
`cd $SERV && git clone https://github.com/olofk/serv`
If you want to know more about SERV, what a bit-serial CPU is and what it's good for, I recommend starting out by watching the fantastic short SERV movies
* [introduction to SERV](https://www.award-winning.me/serv-introduction/)
* [SERV : RISC-V for a fistful of gates](https://www.award-winning.me/serv-for-a-fistful-of-gates/)
* [SERV: 32-bit is the New 8-bit](https://www.award-winning.me/serv-32-bit-is-the-new-8-bit/)
* [Bit by bit - How to fit 8 RISC V cores in a $38 FPGA board (presentation from the Zürich 2019 RISC-V workshop)](https://www.youtube.com/watch?v=xjIxORBRaeQ)
Install FuseSoC
All SERV videos and more can also be found [here](https://www.award-winning.me/videos/).
`pip install fusesoc`
Apart from being the world's smallest RISC-V CPU, SERV also aims at being the best documented RISC-V CPU. For this there is an official [SERV user manual](https://serv.readthedocs.io/en/latest/#) with block diagrams that are correct to the gate-level, cycle-accurate timing diagrams and an in-depth description of how things work.
Initialize the FuseSoC standard libraries
## Systems using SERV
`fusesoc init`
SERV can be easily integrated into any design, but if you are looking at just quickly trying it out, here is a list of some systems that are already using SERV:
Create a workspace directory for FuseSoC
[Servant](https://serv.readthedocs.io/en/latest/servant.html) is the reference platform for SERV. It is a very basic SoC that contains just enough runs Zephyr RTOS. Servant is intended for FPGAs and has been ported to around 20 different FPGA boards. It is also used to run the RISC-V regression test suite.
`mkdir $SERV/workspace`
[CoreScore](https://corescore.store/) is an award-giving benchmark for FPGAs and their synthesis/P&R tools. It tests how many SERV cores that can be put into a particular FPGA.
Register the serv repo as a core library
[Observer](https://github.com/olofk/observer) is a configurable and software-programmable sensor aggregation platform for heterogeneous sensors.
`cd $SERV/workspace && fusesoc library add serv $SERV`
[Subservient](https://github.com/olofk/subservient/) is a small technology-independent SERV-based SoC intended for ASIC implementations together with a single-port SRAM.
Check that the CPU passes the linter
[Litex](https://github.com/enjoy-digital/litex) is a Python-based framework for creating FPGA SoCs. SERV is one of the 30+ supported cores. A Litex-generated SoC has been used to run DooM on SERV.
`cd $SERV/workspace && fusesoc run --target=lint serv`
## Getting started
:o: Create a root directory to keep all the different parts of the project together. We
will refer to this directory as `$WORKSPACE` from now on.
$ export WORKSPACE=$(pwd)
All the following commands will be run from this directory unless otherwise stated.
- Install FuseSoC
$ pip install fusesoc
- Add the FuseSoC standard library
$ fusesoc library add fusesoc_cores https://github.com/fusesoc/fusesoc-cores
- The FuseSoC standard library already contain a version of SERV, but if we want to make changes to SERV, run the bundled example or use the Zephyr support, it is better to add SERV as a separate library into the workspace
$ fusesoc library add serv https://github.com/olofk/serv
>:warning: The SERV repo will now be available in `$WORKSPACE/fusesoc_libraries/serv`. We will refer to that directory as `$SERV`.
- Install latest version of [Verilator](https://www.veripool.org/wiki/verilator)
- (Optional) To support RISC-V M-extension extension, Multiplication and Division unit (MDU) can be added included into the SERV as a separate library.
$ fusesoc library add mdu https://github.com/zeeshanrafique23/mdu
MDU will be available in `$WORKSPACE/fusesoc_libraries/mdu`
We are now ready to do our first exercises with SERV. If everything above is done correctly,we can use Verilator as a linter to check the SERV source code.
$ fusesoc run --target=lint serv
If everything worked, the output should look like
INFO: Preparing ::serv:1.3.0
INFO: Setting up project
INFO: Building simulation model
INFO: Running
After performing all the steps that are mentioned above, the directory structure from the `$WORKSPACE` should look like this:
.
$WORKSPACE
|
├── build
│   └── ...
├── fusesoc.conf
└── fusesoc_libraries
   ├── fusesoc_cores
   │   └── ...
   ├── mdu
   │   └── ...
   └── serv
   └── ...
## Running pre-built test software
## Running test software
Build and run the single threaded zephyr hello world example with verilator (should be stopped with Ctrl-C):
fusesoc run --target=verilator_tb servant --uart_baudrate=57600 --firmware=$SERV/sw/zephyr_hello.hex
cd $SERV/workspace
fusesoc run --target=verilator_tb servant --uart_baudrate=57600 --firmware=$SERV/serv/sw/zephyr_hello.hex
..or... the multithreaded version
fusesoc run --target=verilator_tb servant --uart_baudrate=57600 --firmware=$SERV/sw/zephyr_hello_mt.hex --memsize=16384
fusesoc run --target=verilator_tb servant --uart_baudrate=57600 --firmware=$SERV/serv/sw/zephyr_hello_mt.hex --memsize=16384
Both should yield an output ending with
...or... the philosophers example
***** Booting Zephyr OS zephyr-v1.14.1-4-gc7c2d62513fe *****
Hello World! service
For a more advanced example, we can also run the Dining philosophers demo
fusesoc run --target=verilator_tb servant --uart_baudrate=57600 --firmware=$SERV/sw/zephyr_phil.hex --memsize=32768
fusesoc run --target=verilator_tb servant --uart_baudrate=57600 --firmware=$SERV/serv/sw/zephyr_phil.hex --memsize=32768
...or... the synchronization example
fusesoc run --target=verilator_tb servant --uart_baudrate=57600 --firmware=$SERV/sw/zephyr_sync.hex --memsize=16384
fusesoc run --target=verilator_tb servant --uart_baudrate=57600 --firmware=$SERV/serv/sw/zephyr_sync.hex --memsize=16384
...or... the blinky example (note that the ```uart_baudrate``` should not be defined for the blinky test)
Other applications can be tested by compiling and converting to bin and then hex e.g. with makehex.py found in `$SERV/serv/riscv-target/serv`
fusesoc run --target=verilator_tb servant --firmware=$SERV/sw/blinky.hex --memsize=16384
## Run the compliance tests
Build the verilator model (if not already done)
`cd $SERV/workspace && fusesoc run --target=verilator_tb --setup --build servant`
If the [toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain) is installed, other applications can be tested by compiling the assembly program and converting to bin and then hex with makehex.py found in [`$SERV/sw`](/sw/).
Download the tests repo
:bulb:RISC-V Compressed Extension can be enabled by passing `--compressed=1` parameter.
`cd $SERV && git clone https://github.com/riscv/riscv-compliance`
## Verification
SERV is verified using RISC-V compliance tests for the base ISA (RV32I) and the implemented extensions (M, C, Zicsr). The instructions on running Compliance tests using RISCOF framework are given in [verif](/verif/) directory.
Run the compliance tests
`cd $SERV/riscv-compliance && make TARGETDIR=$SERV/serv/riscv-target RISCV_TARGET=serv RISCV_DECICE=rv32i RISCV_ISA=rv32i TARGET_SIM=$SERV/workspace/build/serv_0/verilator_tb-verilator/Vserv_wrapper`
## Run on hardware
Only supported so far is a single threaded Zephyr hello world example on the icebreaker tinyFPGA BX and arty A7 35T boards. Some
packages should be installed before running it (and shoud be accessible in your PATH variable):
- [icestorm](https://github.com/cliffordwolf/icestorm).
- [nextpnr](https://github.com/YosysHQ/nextpnr).
And do not forget to add fusesoc-cores in your fusesoc lib :
- locally:
```
fusesoc library add fusesoc-cores https://github.com/fusesoc/fusesoc-cores
```
- globally:
```
fusesoc library add --global fusesoc-cores https://github.com/fusesoc/fusesoc-cores
```
Run with `--memfile=$SERV/sw/blinky.hex` as the last argument to run the LED blink example instead of hello world.
### TinyFPGA BX
Pin A6 is used for UART output with 115200 baud rate.
cd $SERV/workspace
fusesoc run --target=tinyfpga_bx servant
tinyprog --program build/serv_0/tinyfpga_bx-icestorm/serv_0.bin
### Icebreaker
Pin 9 is used for UART output with 57600 baud rate.
cd $SERV/workspace
fusesoc run --target=icebreaker servant
### Arty A7 35T
Pin D10 (uart_rxd_out) is used for UART output with 57600 baud rate (to use
blinky.hex change D10 to H5 (led[4]) in data/arty_a7_35t.xdc).
cd $SERV/workspace
fusesoc run --target=arty_a7_35t servant
## Other targets
@ -154,42 +128,7 @@ This will synthesize for the default Vivado part. To synthesise for a specific d
fusesoc run --tool=vivado serv --pnr=none --part=xc7a100tcsg324-1
## Zephyr support
SERV, or rather the Servant SoC, can run the [Zephyr RTOS](https://www.zephyrproject.org). The Servant-specific drivers and BSP is located in the zephyr subdirectory of the SERV repository. In order to use Zephyr on Servant, a project directory structure must be set up that allows Zephyr to load the Servant-specific files as a module.
First, the Zephyr SDK and the "west" build tool must be installed. The [Zephyr getting started guide](https://docs.zephyrproject.org/latest/getting_started/index.html) describes these steps in more detail.
Assuming that SERV was installed into `$WORKSPACE/fusesoc_libraries/serv` as per the prerequisites, run the following command to make the workspace also work as a Zephyr workspace.
west init
Specify the SERV repository as the manifest repository, meaning it will be the main entry point when Zephyr is looking for modules.
west config manifest.path $SERV
Get the right versions of all Zephyr submodules
west update
It should now be possible to build Zephyr applications for the Servant SoC within the workspace. This can be tested e.g. by building the Zephyr Hello world samples application
cd zephyr/samples/hello_world
west build -b service
After a successful build, Zephyr will create an elf and a bin file of the application in `build/zephyr/zephyr.{elf,bin}`. The bin file can be converted to a verilog hex file, which in turn can be preloaded to FPGA on-chip memories and run on a target board, or loaded into simulated RAM model when running simulations.
To convert the newly built hello world example into a Verilog hex file, run
python3 $SERV/sw/makehex.py zephyr/samples/hello_world/build/zephyr/zephyr.bin 4096 > hello.hex
4096 is the number of 32-bit words to write and must be at least the size of the application binary. `hello.hex` is the resulting hex file. Running a simulation can now be done as described in [Running pre-built test software](#running-pre-built-test-software), e.g.
fusesoc run --target=verilator_tb servant --uart_baudrate=57600 --firmware=/path/to/hello.hex
Or to create an FPGA image with the application preloaded to on-chip RAM, e.g. for a Nexys A7 board, run
fusesoc run --target=nexys_a7 servant --memfile=/path/to/hello.hex
At the time of writing, only the icestorm and vivado backends support running synthesis only.
## Good to know
@ -198,3 +137,9 @@ Don't feed serv any illegal instructions after midnight. Many logic expressions
The bus interface is kind of Wishbone, but with most signals removed. There's an important difference though. Don't send acks on the instruction or data buses unless serv explicitly asks for something by raising its cyc signal. Otherwise serv becomes very confused.
Don't go changing the clock frequency on a whim when running Zephyr. Or well, it's ok I guess, but since the UART is bitbanged, this will change the baud rate as well. As of writing, the UART is running at 115200 baud rate when the CPU is 32 MHz. There are two NOPs in the driver to slow it down a bit, so if those are removed I think it could achieve baud rate 115200 on a 24MHz clock.. in case someone wants to try
## TODO
- Applications have to be preloaded to RAM at compile-time
- Store bootloader and register file together in a RAM
- Make it faster and smaller

View file

@ -1,17 +1,12 @@
`default_nettype none
module servant_sim
(input wire wb_clk,
input wire wb_rst,
output wire [31:0] pc_adr,
output wire pc_vld,
output wire q);
(input wire wb_clk,
input wire wb_rst,
output wire q);
parameter memfile = "";
parameter memsize = 8192;
parameter width = 1;
parameter with_csr = 1;
parameter compressed = 0;
parameter align = compressed;
reg [1023:0] firmware_file;
initial
@ -23,15 +18,8 @@ module servant_sim
servant
#(.memfile (memfile),
.memsize (memsize),
.width (width),
.debug (1'b1),
.sim (1),
.with_csr (with_csr),
.compress (compressed[0:0]),
.align (align[0:0]))
.with_csr (with_csr))
dut(wb_clk, wb_rst, q);
assign pc_adr = dut.wb_mem_adr;
assign pc_vld = dut.wb_mem_ack;
endmodule

View file

@ -1,12 +1,9 @@
#include <fcntl.h>
#include <stdint.h>
#include <signal.h>
#include "verilated_vcd_c.h"
#include "Vservant_sim.h"
#include <ctime>
using namespace std;
static bool done;
@ -22,8 +19,8 @@ double sc_time_stamp () { // Called by $time in Verilog
void INThandler(int signal)
{
printf("\nCaught ctrl-c\n");
done = true;
printf("\nCaught ctrl-c\n");
done = true;
}
typedef struct {
@ -49,7 +46,7 @@ void uart_init(uart_context_t *context, uint32_t baud_rate) {
context->state = 0;
}
bool do_uart(uart_context_t *context, bool rx) {
void do_uart(uart_context_t *context, bool rx) {
if (context->state == 0) {
if (rx)
context->state++;
@ -77,104 +74,80 @@ bool do_uart(uart_context_t *context, bool rx) {
else {
if (main_time > context->last_update) {
context->last_update += context->baud_t;
putchar(context->ch);
context->state=1;
return true;
}
}
return false;
}
int main(int argc, char **argv, char **env)
{
int baud_rate = 0;
vluint64_t sample_time = 0;
uint32_t insn = 0;
uint32_t ex_pc = 0;
int baud_rate = 0;
gpio_context_t gpio_context;
uart_context_t uart_context;
Verilated::commandArgs(argc, argv);
gpio_context_t gpio_context;
uart_context_t uart_context;
Verilated::commandArgs(argc, argv);
Vservant_sim* top = new Vservant_sim;
Vservant_sim* top = new Vservant_sim;
const char *arg = Verilated::commandArgsPlusMatch("uart_baudrate=");
if (arg[0]) {
baud_rate = atoi(arg+15);
if (baud_rate) {
uart_init(&uart_context, baud_rate);
}
}
const char *arg = Verilated::commandArgsPlusMatch("uart_baudrate=");
if (arg[0]) {
baud_rate = atoi(arg+15);
if (baud_rate) {
uart_init(&uart_context, baud_rate);
}
}
VerilatedVcdC * tfp = 0;
const char *vcd = Verilated::commandArgsPlusMatch("vcd=");
if (vcd[0]) {
Verilated::traceEverOn(true);
tfp = new VerilatedVcdC;
top->trace (tfp, 99);
tfp->open ("trace.vcd");
}
VerilatedVcdC * tfp = 0;
const char *vcd = Verilated::commandArgsPlusMatch("vcd=");
if (vcd[0]) {
Verilated::traceEverOn(true);
tfp = new VerilatedVcdC;
top->trace (tfp, 99);
tfp->open ("trace.vcd");
}
signal(SIGINT, INThandler);
signal(SIGINT, INThandler);
int tf = 0;
const char *arg_trace_pc = Verilated::commandArgsPlusMatch("trace_pc=");
if (arg_trace_pc[0])
tf = open("trace.bin", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
vluint64_t timeout = 0;
const char *arg_timeout = Verilated::commandArgsPlusMatch("timeout=");
if (arg_timeout[0])
timeout = atoi(arg_timeout+9);
vluint64_t timeout = 0;
const char *arg_timeout = Verilated::commandArgsPlusMatch("timeout=");
if (arg_timeout[0])
timeout = atoi(arg_timeout+9);
vluint64_t vcd_start = 0;
const char *arg_vcd_start = Verilated::commandArgsPlusMatch("vcd_start=");
if (arg_vcd_start[0])
vcd_start = atoi(arg_vcd_start+11);
vluint64_t vcd_start = 0;
const char *arg_vcd_start = Verilated::commandArgsPlusMatch("vcd_start=");
if (arg_vcd_start[0])
vcd_start = atoi(arg_vcd_start+11);
bool dump = false;
top->wb_clk = 1;
bool q = top->q;
while (!(done || Verilated::gotFinish())) {
if (tfp && !dump && (main_time > vcd_start)) {
dump = true;
}
top->wb_rst = main_time < 100;
top->eval();
if (dump)
tfp->dump(main_time);
if (baud_rate)
do_uart(&uart_context, top->q);
else
do_gpio(&gpio_context, top->q);
int cur_cycle = 0;
int last_cycle = 0;
std::time_t last_time = std::time(nullptr);
int cps_file = 0;
const char *arg_cps = Verilated::commandArgsPlusMatch("cps=");
if (arg_cps[0])
cps_file = open("cps", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
if (timeout && (main_time >= timeout)) {
printf("Timeout: Exiting at time %lu\n", main_time);
done = true;
}
bool dump = false;
top->wb_clk = 1;
bool q = top->q;
while (!(done || Verilated::gotFinish())) {
if (tfp && !dump && (main_time > vcd_start)) {
dump = true;
}
top->wb_rst = main_time < 100;
top->eval();
if (dump)
tfp->dump(main_time);
if (baud_rate) {
if (do_uart(&uart_context, top->q))
putchar(uart_context.ch);
} else {
do_gpio(&gpio_context, top->q);
}
if (tf && top->wb_clk && top->pc_vld)
if (write(tf, (void *)&top->pc_adr, 4) < 0) exit(1);
if (timeout && (main_time >= timeout)) {
printf("Timeout: Exiting at time %lu\n", main_time);
done = true;
}
top->wb_clk = !top->wb_clk;
main_time+=31.25;
top->wb_clk = !top->wb_clk;
main_time+=31.25;
if (cps_file) {
cur_cycle++;
if (std::time(nullptr) > last_time) {
dprintf(cps_file,"%d\n", (cur_cycle-last_cycle)/2);
last_cycle = cur_cycle;
last_time++;
}
}
}
close(cps_file);
close(tf);
if (tfp)
tfp->close();
exit(0);
}
if (tfp)
tfp->close();
exit(0);
}

View file

@ -1,37 +1,24 @@
`default_nettype none
module servant_tb;
parameter memfile = "hello_uart.hex";
parameter memfile = "";
parameter memsize = 8192;
parameter width = 1;
parameter with_csr = 1;
localparam baud_rate =
(width == 4) ? 57600*3 :
57600;
reg wb_clk = 1'b0;
reg wb_rst = 1'b1;
wire q;
always #31 wb_clk <= !wb_clk;
initial #62 wb_rst <= 1'b0;
vlog_tb_utils vtu();
uart_decoder #(baud_rate) uart_decoder (q);
uart_decoder #(57600) uart_decoder (q);
servant_sim
#(.memfile (memfile),
.memsize (memsize),
.width (width),
.with_csr (with_csr))
dut
(.wb_clk (wb_clk),
.wb_rst (wb_rst),
.pc_adr (),
.pc_vld (),
.q (q));
dut(wb_clk, wb_rst, q);
endmodule

View file

@ -1,163 +0,0 @@
#include <stdint.h>
#include <signal.h>
#include "verilated_vcd_c.h"
#include "Vservant_sim.h"
#include "vidbo.h"
using namespace std;
static bool done;
vluint64_t main_time = 0; // Current simulation time
// This is a 64-bit integer to reduce wrap over issues and
// allow modulus. You can also use a double, if you wish.
double sc_time_stamp () { // Called by $time in Verilog
return main_time; // converts to double, to match
// what SystemC does
}
void INThandler(int signal)
{
printf("\nCaught ctrl-c\n");
done = true;
}
typedef struct {
bool last_value;
} gpio_context_t;
void do_gpio(gpio_context_t *context, bool gpio) {
if (context->last_value != gpio) {
context->last_value = gpio;
printf("%lu output q is %s\n", main_time, gpio ? "ON" : "OFF");
}
}
typedef struct {
uint8_t state;
char ch;
uint32_t baud_t;
vluint64_t last_update;
} uart_context_t;
void uart_init(uart_context_t *context, uint32_t baud_rate) {
context->baud_t = 1000*1000*1000/baud_rate;
context->state = 0;
}
bool do_uart(uart_context_t *context, bool rx) {
if (context->state == 0) {
if (rx)
context->state++;
}
else if (context->state == 1) {
if (!rx) {
context->last_update = main_time + context->baud_t/2;
context->state++;
}
}
else if(context->state == 2) {
if (main_time > context->last_update) {
context->last_update += context->baud_t;
context->ch = 0;
context->state++;
}
}
else if (context->state < 11) {
if (main_time > context->last_update) {
context->last_update += context->baud_t;
context->ch |= rx << (context->state-3);
context->state++;
}
}
else {
if (main_time > context->last_update) {
context->last_update += context->baud_t;
context->state=1;
return true;
}
}
return false;
}
int main(int argc, char **argv, char **env)
{
int baud_rate = 0;
gpio_context_t gpio_context;
uart_context_t uart_context;
vidbo_context_t vidbo_context;
vidbo_init(&vidbo_context, 8081);
int poll_vidbo = 0;
Verilated::commandArgs(argc, argv);
Vservant_sim* top = new Vservant_sim;
const char *arg = Verilated::commandArgsPlusMatch("uart_baudrate=");
if (arg[0]) {
baud_rate = atoi(arg+15);
if (baud_rate) {
uart_init(&uart_context, baud_rate);
}
}
VerilatedVcdC * tfp = 0;
const char *vcd = Verilated::commandArgsPlusMatch("vcd=");
if (vcd[0]) {
Verilated::traceEverOn(true);
tfp = new VerilatedVcdC;
top->trace (tfp, 99);
tfp->open ("trace.vcd");
}
signal(SIGINT, INThandler);
vluint64_t timeout = 0;
const char *arg_timeout = Verilated::commandArgsPlusMatch("timeout=");
if (arg_timeout[0])
timeout = atoi(arg_timeout+9);
vluint64_t vcd_start = 0;
const char *arg_vcd_start = Verilated::commandArgsPlusMatch("vcd_start=");
if (arg_vcd_start[0])
vcd_start = atoi(arg_vcd_start+11);
bool dump = false;
top->wb_clk = 1;
bool q = top->q;
while (!(done || Verilated::gotFinish())) {
if (tfp && !dump && (main_time > vcd_start)) {
dump = true;
}
top->wb_rst = main_time < 100;
top->eval();
if (dump)
tfp->dump(main_time);
if (baud_rate) {
if (do_uart(&uart_context, top->q))
vidbo_send(&vidbo_context, main_time, "serial", "uart", uart_context.ch);
}
if (top->q != gpio_context.last_value) {
vidbo_send(&vidbo_context, main_time, "gpio", "LD0", (top->q) & 0x1);
gpio_context.last_value = top->q;
}
if (timeout && (main_time >= timeout)) {
printf("Timeout: Exiting at time %lu\n", main_time);
done = true;
}
if (!(poll_vidbo++ % 100000)) vidbo_recv(&vidbo_context, 0);
top->wb_clk = !top->wb_clk;
main_time+=31.25;
}
if (tfp)
tfp->close();
exit(0);
}

View file

@ -1,11 +0,0 @@
## Clock signal
set_property IOSTANDARD DIFF_SSTL15 [get_ports sys_clk_p]
set_property PACKAGE_PIN P3 [get_ports sys_clk_n]
set_property PACKAGE_PIN R3 [get_ports sys_clk_p]
set_property IOSTANDARD DIFF_SSTL15 [get_ports sys_clk_n]
create_clock -period 5.000 -name clk_p [get_nets sys_clk_p]
## UART TX
set_property PACKAGE_PIN U19 [get_ports q]
set_property IOSTANDARD LVCMOS18 [get_ports q]

View file

@ -1,16 +0,0 @@
## Clock signal
set_property -dict { PACKAGE_PIN N14 IOSTANDARD LVCMOS33 } [get_ports i_clk];
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports i_clk];
## Reset
set_property -dict {PACKAGE_PIN P6 IOSTANDARD LVCMOS33 } [get_ports i_rst_n];
## LED
## set_property -dict { PACKAGE_PIN L14 IOSTANDARD LVCMOS33 } [get_ports q];
## USB Serial output
set_property -dict { PACKAGE_PIN P16 IOSTANDARD LVCMOS33 } [get_ports q]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]

View file

@ -1,5 +0,0 @@
# 12 MHz clock
set_io i_clk 49
# RS232
set_io q 61

View file

@ -1,7 +1,5 @@
set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports i_clk];
set_property -dict {PACKAGE_PIN C2 IOSTANDARD LVCMOS33 } [get_ports i_rst_n];
set_property -dict {PACKAGE_PIN D10 IOSTANDARD LVCMOS33 } [get_ports q]
#set_property -dict {PACKAGE_PIN H5 IOSTANDARD LVCMOS33 } [get_ports q]

View file

@ -1,10 +0,0 @@
set_property -dict { PACKAGE_PIN R2 IOSTANDARD SSTL135 } [get_ports i_clk]; #IO_L12P_T1_MRCC_34 Sch=ddr3_clk[200]
set_property -dict { PACKAGE_PIN C18 IOSTANDARD LVCMOS33 } [get_ports i_rst_n]; #IO_L11N_T1_SRCC_15
set_property -dict { PACKAGE_PIN R12 IOSTANDARD LVCMOS33 } [get_ports q]; #IO_25_14 Sch=uart_rxd_out
#set_property -dict { PACKAGE_PIN E18 IOSTANDARD LVCMOS33 } [get_ports q]; #IO_L16N_T2_A27_15 Sch=led[2]
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports i_clk];

View file

@ -1,9 +0,0 @@
CONFIG VCCAUX=3.3;
NET i_clk LOC = T8 | IOSTANDARD = LVCMOS33;
NET i_rst LOC = L3 | IOSTANDARD = LVCMOS33;
NET q LOC = P4 | IOSTANDARD = LVCMOS33;
NET o_uart_tx LOC = D12 | IOSTANDARD = LVCMOS33;
NET i_clk TNM_NET = sys_clk_pin;
TIMESPEC TS_USER_CLOCK = PERIOD sys_clk_pin 50000 kHz;

View file

@ -1,28 +0,0 @@
module CV_96 (
output q,
output uart_txd
);
wire clk;
HPS u0(
.h2f_user0_clk( clk) //hps_0_h2f_user0_clock.clk
);
servive u1 (
.i_clk ( clk ),
.i_rst_n ( 1'b1),
.q ( q ),
.uart_txd( uart_txd )
);
endmodule

View file

@ -1,108 +0,0 @@
module HPS(
output wire [1 - 1 : 0 ] h2f_rst_n
,output wire [1 - 1 : 0 ] h2f_user0_clk
);
cyclonev_hps_interface_clocks_resets clocks_resets(
.f2h_pending_rst_ack({
1'b1 // 0:0
})
,.f2h_warm_rst_req_n({
1'b1 // 0:0
})
,.f2h_dbg_rst_req_n({
1'b1 // 0:0
})
,.h2f_rst_n({
h2f_rst_n[0:0] // 0:0
})
,.f2h_cold_rst_req_n({
1'b1 // 0:0
})
,.h2f_user0_clk({
h2f_user0_clk[0:0] // 0:0
})
);
cyclonev_hps_interface_dbg_apb debug_apb(
.DBG_APB_DISABLE({
1'b0 // 0:0
})
,.P_CLK_EN({
1'b0 // 0:0
})
);
cyclonev_hps_interface_tpiu_trace tpiu(
.traceclk_ctl({
1'b1 // 0:0
})
);
cyclonev_hps_interface_boot_from_fpga boot_from_fpga(
.boot_from_fpga_ready({
1'b0 // 0:0
})
,.boot_from_fpga_on_failure({
1'b0 // 0:0
})
,.bsel_en({
1'b0 // 0:0
})
,.csel_en({
1'b0 // 0:0
})
,.csel({
2'b01 // 1:0
})
,.bsel({
3'b001 // 2:0
})
);
cyclonev_hps_interface_fpga2hps fpga2hps(
.port_size_config({
2'b11 // 1:0
})
);
cyclonev_hps_interface_hps2fpga hps2fpga(
.port_size_config({
2'b11 // 1:0
})
);
cyclonev_hps_interface_fpga2sdram f2sdram(
.cfg_cport_rfifo_map({
18'b000000000000000000 // 17:0
})
,.cfg_axi_mm_select({
6'b000000 // 5:0
})
,.cfg_wfifo_cport_map({
16'b0000000000000000 // 15:0
})
,.cfg_cport_type({
12'b000000000000 // 11:0
})
,.cfg_rfifo_cport_map({
16'b0000000000000000 // 15:0
})
,.cfg_port_width({
12'b000000000000 // 11:0
})
,.cfg_cport_wfifo_map({
18'b000000000000000000 // 17:0
})
);
endmodule

View file

@ -1,15 +0,0 @@
/* Quartus Prime Version 17.1.0 Build 590 10/25/2017 SJ Lite Edition */
JedecChain;
FileRevision(JESD32A);
DefaultMfr(6E);
P ActionCode(Ign)
Device PartName(SOCVHPS) MfrSpec(OpMask(0));
P ActionCode(Cfg)
Device PartName(5CSEBA6U19) Path("/home/jordi/bin/fusesoc/build/fusesoc_utils_blinky_1.1/chameleon96-quartus/") File("fusesoc_utils_blinky_1_1.sof") MfrSpec(OpMask(1));
ChainEnd;
AlteraBegin;
ChainType(JTAG);
AlteraEnd;

View file

@ -1,8 +0,0 @@
# Main system clock (100 Mhz)
create_clock -name "clk" -period 10.000ns [get_pins -compatibility_mode u0|clocks_resets|h2f_user0_clk]
# Automatically constrain PLL and other generated clocks
derive_pll_clocks -create_base_clocks
# Automatically calculate clock uncertainty to jitter and other effects.
derive_clock_uncertainty

View file

@ -1,20 +0,0 @@
#
# Clock / Reset
#
# set_location_assignment PIN_xxxx -to i_clk
# No direct clock. Using internal HPS clock
#
# GPIO
#
# LED Y19 Yellow WIFI, Y20 Blue Bluetooth
set_location_assignment PIN_Y19 -to q
set_instance_assignment -name IO_STANDARD "2.5 V" -to q
# FPGA_1V8_HPS_EXP_UART1_TXD_PIN_W14 Pin 5 Low speed connector (WHITE C96 USB/serial cable) Pin 1 GND
set_location_assignment PIN_W14 -to uart_txd
set_instance_assignment -name IO_STANDARD "1.8 V" -to uart_txd
# No reset button wired to FPGA
# set_location_assignment PIN_xxx -to i_rst_n

View file

@ -1,16 +0,0 @@
## 12 MHz Clock Signal
set_property -dict { PACKAGE_PIN L17 IOSTANDARD LVCMOS33 } [get_ports { i_clk }]; #IO_L12P_T1_MRCC_14 Sch=gclk
create_clock -add -name sys_clk_pin -period 83.33 -waveform {0 41.66} [get_ports { i_clk }];
## LEDs
set_property -dict { PACKAGE_PIN A17 IOSTANDARD LVCMOS33 } [get_ports { q }]; #IO_L12N_T1_MRCC_16 Sch=led[1]
#set_property -dict { PACKAGE_PIN C16 IOSTANDARD LVCMOS33 } [get_ports { q }]; #IO_L13P_T2_MRCC_16 Sch=led[2]
## UART
set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVCMOS33 } [get_ports { o_uart_tx }]; #IO_L7N_T1_D10_14 Sch=uart_rxd_out
#set_property -dict { PACKAGE_PIN J17 IOSTANDARD LVCMOS33 } [get_ports { q }]; #IO_L7P_T1_D09_14 Sch=uart_txd_in
set_property -dict { PACKAGE_PIN A18 IOSTANDARD LVCMOS33 } [get_ports { i_rst }]; #IO_L19N_T3_VREF_16 Sch=btn[0]
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]

View file

@ -1,8 +0,0 @@
# Main system clock (50 Mhz)
create_clock -name "clk" -period 20.000ns [get_ports {i_clk}]
# Automatically constrain PLL and other generated clocks
derive_pll_clocks -create_base_clocks
# Automatically calculate clock uncertainty to jitter and other effects.
derive_clock_uncertainty

View file

@ -1,11 +0,0 @@
set_location_assignment PIN_R8 -to i_clk
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_clk
set_location_assignment PIN_A15 -to q
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to q
set_location_assignment PIN_D11 -to uart_txd
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to uart_txd
set_location_assignment PIN_J15 -to i_rst_n
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_rst_n

View file

@ -1,8 +0,0 @@
# Main system clock (50 Mhz)
create_clock -name "clk" -period 20.000ns [get_ports {i_clk}]
# Automatically constrain PLL and other generated clocks
derive_pll_clocks -create_base_clocks
# Automatically calculate clock uncertainty to jitter and other effects.
derive_clock_uncertainty

View file

@ -1,13 +0,0 @@
set_location_assignment PIN_V11 -to i_clk
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_clk
set_location_assignment PIN_W15 -to q
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to q
# GPIO0 Pin 1
set_location_assignment PIN_Y15 -to uart_txd
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to uart_txd
#KEY[0]
set_location_assignment PIN_AH17 -to i_rst_n
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_rst_n

View file

@ -1,8 +0,0 @@
# Main system clock (50 Mhz)
create_clock -name "clk" -period 20.000ns [get_ports {i_clk}]
# Automatically constrain PLL and other generated clocks
derive_pll_clocks -create_base_clocks
# Automatically calculate clock uncertainty to jitter and other effects.
derive_clock_uncertainty

View file

@ -1,11 +0,0 @@
set_location_assignment PIN_AF14 -to i_clk
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_clk
set_location_assignment PIN_AA14 -to i_rst_n
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_rst_n
set_location_assignment PIN_V16 -to q
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to q
set_location_assignment PIN_AC18 -to uart_txd
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to uart*

View file

@ -1,8 +0,0 @@
# Main system clock (50 Mhz)
create_clock -name "clk" -period 20.000ns [get_ports {i_clk}]
# Automatically constrain PLL and other generated clocks
derive_pll_clocks -create_base_clocks
# Automatically calculate clock uncertainty to jitter and other effects.
derive_clock_uncertainty

View file

@ -1,19 +0,0 @@
#MAX10_CLK2_50 3.3V
set_location_assignment PIN_P11 -to i_clk
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_clk
#LED[0]
set_location_assignment PIN_C7 -to q
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to q
#P8 3 GPIO0_D0 (P8 1 GND)
set_location_assignment PIN_W18 -to uart_txd
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to uart_txd
#KEY[0]
set_location_assignment PIN_H21 -to i_rst_n
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_rst_n
# Configuration mode that allows Memory Initialisation
set_global_assignment -name INTERNAL_FLASH_UPDATE_MODE "SINGLE IMAGE WITH ERAM"

View file

@ -1,10 +0,0 @@
## 33.333 MHz Clock signal
set_property -dict { PACKAGE_PIN N18 IOSTANDARD LVCMOS33 } [get_ports i_clk];
create_clock -add -name sys_clk_pin -period 30.00 -waveform {0 5} [get_ports i_clk];
## LED(s)
# set_property -dict { PACKAGE_PIN W13 IOSTANDARD LVCMOS33 } [get_ports { q_green }];
# set_property -dict { PACKAGE_PIN W14 IOSTANDARD LVCMOS33 } [get_ports { q }];
## UART on DATA1_8 pin
set_property -dict { PACKAGE_PIN B20 IOSTANDARD LVCMOS33 } [get_ports q];

View file

@ -1,16 +0,0 @@
# 12MHz clock from FTDI FT2232H
LOCATE COMP "clk" SITE "A10";
IOBUF PORT "clk" IO_TYPE=LVCMOS33;
FREQUENCY PORT "clk" 12 MHZ;
# SW4 button
LOCATE COMP "nreset" SITE "P4";
IOBUF PORT "nreset" IO_TYPE=LVCMOS33;
# LED0
LOCATE COMP "led0" SITE "A13";
IOBUF PORT "led0" IO_TYPE=LVCMOS25;
# J40 Header Pin #1
LOCATE COMP "uart_txd" SITE "K2";
IOBUF PORT "uart_txd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;

View file

@ -1,65 +0,0 @@
## GMM-7550 pins
# This file is a part of the GMM-7550 VHDL Examples
# <https://github.com/ak-fau/gmm7550-examples.git>
#
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2023 Anton Kuzmin <anton.kuzmin@cs.fau.de>
# Master clock input (100 MHz)
Pin_in "ser_clk" Loc = "SER_CLK";
### SPI
Pin_inout "CFG_SPI_nCS" Loc = "IO_WA_A8";
Pin_inout "CFG_SPI_CLK" Loc = "IO_WA_B8";
Pin_inout "CFG_SPI_IO0" Loc = "IO_WA_B7"; # MOSI
Pin_inout "CFG_SPI_IO1" Loc = "IO_WA_A7"; # MISO
# Pin_inout "CFG_SPI_IO2" Loc = "IO_WA_B6"; # May be reused on the HAT for UART
# Pin_inout "CFG_SPI_IO3" Loc = "IO_WA_A6"; # May be reused on the HAT for UART
## HAT Adapter board
# This file is a part of the GMM-7550 VHDL Examples
# <https://github.com/ak-fau/gmm7550-examples.git>
#
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2023 Anton Kuzmin <anton.kuzmin@cs.fau.de>
# D4 CFG_FAILED (Red)
Pin_out "led_red_n" Loc = "IO_WA_A2";
# D2 CFG_DONE (Green)
Pin_out "led_green" Loc = "IO_WA_B2";
### UART
Pin_out "uart_tx" Loc = "IO_WA_A6"; # SPI D3, GPIO pin 10
Pin_in "uart_rx" Loc = "IO_WA_B6"; # SPI D2, GPIO pin 8
### Pmod J10
Pin_out "J10_EN" Loc = "IO_SA_A7";
Pin_inout "J10_IO[0]" Loc = "IO_SA_A0";
Pin_inout "J10_IO[1]" Loc = "IO_SA_A1";
Pin_inout "J10_IO[2]" Loc = "IO_SA_A2";
Pin_inout "J10_IO[3]" Loc = "IO_SA_A3";
Pin_inout "J10_IO[4]" Loc = "IO_SA_B0";
Pin_inout "J10_IO[5]" Loc = "IO_SA_B1";
Pin_inout "J10_IO[6]" Loc = "IO_SA_B2";
Pin_inout "J10_IO[7]" Loc = "IO_SA_B3";
### Pmod J9
Pin_out "J9_EN" Loc = "IO_SB_B3";
Pin_inout "J9_IO[0]" Loc = "IO_SB_A6"; # CLK_2
Pin_inout "J9_IO[1]" Loc = "IO_SB_A7"; # CLK_1
Pin_inout "J9_IO[2]" Loc = "IO_SB_A8"; # CLK_0
Pin_inout "J9_IO[3]" Loc = "IO_SB_A5"; # CLK_3
Pin_inout "J9_IO[4]" Loc = "IO_SB_B6";
Pin_inout "J9_IO[5]" Loc = "IO_SB_B7";
Pin_inout "J9_IO[6]" Loc = "IO_SB_B8";
Pin_inout "J9_IO[7]" Loc = "IO_SB_B5";

View file

@ -1,10 +0,0 @@
# 20 MHz clock input
set_io i_clk 15
# Onboard LEDs 1-4
set_io o_led1 56
set_io o_led2 57
set_io o_led3 59
set_io o_led4 60
set_io o_uart_tx 74

View file

@ -1,8 +0,0 @@
# 12 MHz clock input
set_io i_clk 21
# Onboard LED (Green)
set_io q 95
# UART TX
#set_io q 62

View file

@ -1,5 +0,0 @@
# 12 MHz clock
set_io i_clk 35
# RS232
set_io q 6

View file

@ -1,7 +0,0 @@
# 12 MHz clock
set_io i_clk 35
# RS232
set_io q 9
# use q 39 for red, q 40 for green, q 41 for blue LED

View file

@ -1,9 +0,0 @@
CONFIG VCCAUX=3.3;
NET i_clk LOC = V10 | IOSTANDARD = LVCMOS33;
NET i_rst LOC = V4 | IOSTANDARD = LVCMOS33 | PULLDOWN;
NET q LOC = P4 | IOSTANDARD = LVCMOS18;
NET o_uart_tx LOC = T7 | IOSTANDARD = LVCMOS33;
NET i_clk TNM_NET = clk;
TIMESPEC TS_USER_CLOCK = PERIOD clk 40000 kHz;

View file

@ -1,4 +0,0 @@
set_io clk48 G1
set_io led B11
set_io tx B1
set_io rx B2

View file

@ -1,8 +0,0 @@
# Main system clock (50 Mhz)
create_clock -name "clk" -period 20.000ns [get_ports {i_clk}]
# Automatically constrain PLL and other generated clocks
derive_pll_clocks -create_base_clocks
# Automatically calculate clock uncertainty to jitter and other effects.
derive_clock_uncertainty

View file

@ -1,24 +0,0 @@
#MAX10_CLK2_50 3.3V
set_location_assignment PIN_27 -to i_clk
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_clk
#LED[0]
set_location_assignment PIN_132 -to q
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to q
#P8 3 ARDUINO_IO1
set_location_assignment PIN_75 -to uart_txd
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to uart_txd
#KEY[0]
set_location_assignment PIN_121 -to i_rst_n
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_rst_n
# Configuration mode that allows Memory Initialisation
set_global_assignment -name INTERNAL_FLASH_UPDATE_MODE "SINGLE IMAGE WITH ERAM"
# Generate SVF File for openFPGALoader
set_global_assignment -name GENERATE_SVF_FILE ON
# set_global_assignment -name GENERATE_JBC_FILE ON
set_global_assignment -name GENERATE_JAM_FILE ON

View file

@ -1 +0,0 @@
project set "Other XST Command Line Options" "-use_new_parser yes"

View file

@ -1,8 +0,0 @@
NET "i_clk" LOC = "B8" | IOSTANDARD = LVCMOS33 ;
NET "i_clk" CLOCK_DEDICATED_ROUTE = FALSE;
# Pin assignment for Uart tx
NET "q" LOC = "L15" | IOSTANDARD = LVTTL | SLEW = FAST | DRIVE = 8 ;
# Pin assignment for LED
# NET "q" LOC = "J14" | IOSTANDARD = LVTTL | SLEW = FAST | DRIVE = 8 ;

View file

@ -1,17 +0,0 @@
LOCATE COMP "clk" SITE "A9";
IOBUF PORT "clk" PULLMODE=NONE IO_TYPE=LVCMOS33;
FREQUENCY PORT "clk" 48.000 MHZ;
LOCATE COMP "r" SITE "K4";
LOCATE COMP "g" SITE "M3";
LOCATE COMP "b" SITE "J3";
IOBUF PORT "r" IO_TYPE=LVCMOS33;
IOBUF PORT "g" IO_TYPE=LVCMOS33;
IOBUF PORT "b" IO_TYPE=LVCMOS33;
LOCATE COMP "btn" SITE "J17"; # BTN_PWRn (inverted logic)
IOBUF PORT "btn" PULLMODE=UP IO_TYPE=LVCMOS33;
LOCATE COMP "tx" SITE "M18"; # FPGA serial output
IOBUF PORT "tx" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;

View file

@ -1,6 +0,0 @@
set ::env(CLOCK_PERIOD) "10"
set ::env(CLOCK_PORT) "clk"
set ::env(DIE_AREA) "0 0 200 200"
set ::env(FP_SIZING) absolute
set ::env(DESIGN_IS_CORE) 0
set ::env(GLB_RT_MAXLAYER) 5

View file

@ -1,12 +0,0 @@
CONFIG VCCAUX=3.3;
NET i_clk LOC = H17 | IOSTANDARD = LVTTL;
NET i_clk TNM_NET = i_clk;
TIMESPEC TS_USER_CLOCK = PERIOD i_clk 50000 kHz;
# uart tx
NET q LOC = A10 | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;
# led0
#NET q LOC = V16 | IOSTANDARD = LVCMOS33;

View file

@ -1,11 +0,0 @@
set_io -port_name {i_clk} -DIRECTION INPUT -pin_name H7 -io_std LVCMOS18 -fixed true
set_io -port_name {resetb} -DIRECTION INPUT -pin_name N4 -io_std LVCMOS18 -fixed true
set_io -port_name {o_led1} -DIRECTION OUTPUT -pin_name P7 -io_std LVCMOS18 -fixed true
set_io -port_name {o_led2} -DIRECTION OUTPUT -pin_name P8 -io_std LVCMOS18 -fixed true
set_io -port_name {o_led3} -DIRECTION OUTPUT -pin_name N7 -io_std LVCMOS18 -fixed true
set_io -port_name {o_led4} -DIRECTION OUTPUT -pin_name N8 -io_std LVCMOS18 -fixed true
set_io -port_name {o_led5} -DIRECTION OUTPUT -pin_name N6 -io_std LVCMOS18 -fixed true
set_io -port_name {o_led6} -DIRECTION OUTPUT -pin_name N5 -io_std LVCMOS18 -fixed true
set_io -port_name {o_led7} -DIRECTION OUTPUT -pin_name M8 -io_std LVCMOS18 -fixed true
set_io -port_name {o_led8} -DIRECTION OUTPUT -pin_name M9 -io_std LVCMOS18 -fixed true
set_io -port_name {o_uart_tx} -DIRECTION OUTPUT -pin_name R4 -io_std LVCMOS18 -fixed true

View file

@ -1,8 +0,0 @@
# Main system clock (50 Mhz)
create_clock -name "clk" -period 20.000ns [get_ports {i_clk}]
# Automatically constrain PLL and other generated clocks
derive_pll_clocks -create_base_clocks
# Automatically calculate clock uncertainty to jitter and other effects.
derive_clock_uncertainty

View file

@ -1,11 +0,0 @@
set_location_assignment PIN_AF14 -to i_clk
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_clk
set_location_assignment PIN_AF10 -to q
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to q
set_location_assignment PIN_F14 -to uart_txd
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to uart_txd
set_location_assignment PIN_AE9 -to i_rst_n
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to i_rst_n

View file

@ -1,22 +0,0 @@
## Clock signal
set_property -dict { PACKAGE_PIN J3 IOSTANDARD LVCMOS18 } [get_ports i_clk];
create_clock -add -name sys_clk_pin -period 40.00 [get_ports i_clk];
## LED 0
set_property -dict { PACKAGE_PIN P1 IOSTANDARD LVCMOS18 } [get_ports o_led_0];
# PMOD A, Connector J5
# Connector pin, Package pin, PMOD type 4 UART
# 1, F8, CTS
# 2, F7, TXD
# 3, E6, RXD
# 4, E5, RTS
# 5, GND
# 6, VCC
# 7, G6,
# 8, G5,
# 9, C8,
# 10, C7,
# 11, GND
# 12, VCC
set_property -dict { PACKAGE_PIN F7 IOSTANDARD LVCMOS33 } [get_ports o_uart_tx]

View file

@ -1,14 +0,0 @@
LOCATE COMP "clk" SITE "G2";
IOBUF PORT "clk" PULLMODE=NONE IO_TYPE=LVCMOS33;
FREQUENCY PORT "clk" 25.000 MHZ;
LOCATE COMP "q" SITE "B2";
IOBUF PORT "q" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
LOCATE COMP "btn0" SITE "D6"; # BTN_PWRn (inverted logic)
IOBUF PORT "btn0" PULLMODE=UP IO_TYPE=LVCMOS33;
LOCATE COMP "wifi_gpio0" SITE "L2";
IOBUF PORT "wifi_gpio0" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
LOCATE COMP "uart_txd" SITE "L4"; # FPGA transmits to ftdi
IOBUF PORT "uart_txd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;

View file

@ -1,4 +0,0 @@
set_io g 39
set_io b 40
set_io r 41
set_io q 14

View file

@ -1,12 +0,0 @@
`verilator_config
// Bits [1:0] in i_wb_rdt are not used at all
lint_off -rule UNUSED -file "*/serv_top.v" -lines 179
//Some bits in the instruction word are not used in serv_decode but it's easier
//to just send in the whole word than picking out bits
lint_off -rule UNUSED -file "*/serv_decode.v" -lines 8
//Some variables are only used when we connect an Extension with serv_decode
lint_off -rule UNUSED -file "*/serv_top.v" -lines 70

View file

@ -1,8 +0,0 @@
## Clock signal
set_property -dict { PACKAGE_PIN H9 IOSTANDARD LVDS } [get_ports i_clk_p];
set_property -dict { PACKAGE_PIN G9 IOSTANDARD LVDS } [get_ports i_clk_n];
create_clock -add -name sys_clk_pin -period 8 [get_nets i_clk];
## LED
set_property -dict { PACKAGE_PIN AL11 IOSTANDARD LVCMOS12 } [get_ports q];
set_property -dict { PACKAGE_PIN AL17 IOSTANDARD LVCMOS12 } [get_ports o_uart_tx]

View file

@ -1 +0,0 @@
This is published to GitHub Pages. It will ignore the _images path etc. otherwise.

View file

@ -1,19 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View file

@ -1,59 +0,0 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'SERV'
copyright = '2020, Olof Kindgren'
author = 'Olof Kindgren'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinxcontrib.wavedrom'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_extra_path = ['.nojekyll']
master_doc = 'index'
online_wavedrom_js_url = "https://wavedrom.com"

View file

@ -1,6 +0,0 @@
*********
Datasheet
*********
.. include:: overview.rst
.. include:: interface.rst

View file

@ -1,13 +0,0 @@
################
SERV user manual
################
Welcome to the user manual of the award-winning SERV, the world's smallest RISC-V CPU.
.. toctree::
:maxdepth: 4
:caption: Contents:
datasheet.rst
internals.rst
reservoir.rst

View file

@ -1,222 +0,0 @@
Interface
=========
Top level
---------
Users of SERV can choose to use either serv_top or serv_rf_top depending on what best fits the application. serv_top contains all the main logic of SERV except for the actual storage for the register file (RF).
.. image:: serv_top.png
serv_rf_top is a convenience wrapper that combines serv_top with a memory-based RF (serf_rf_ram) and an adapter between them (serv_rf_ram_if). This allows users to choose between a drop-in implementation when the memory-based RF is good enough or supply an alternative RF implementation, such as a shift-register based one or a combined SRAM for instruction/data memory and RF.
.. image:: serv_rf_top.png
This means that serv_top exposes some additional pins to connect the RF which are not in serv_rf_top, while serv_rf_top has some extra parameters for configuring the RF. Aside from that, both modules expose a timer interrupt (only used when WITH_CSR=1), a rvfi formal interface (only available when RISCV_FORMAL is defined), an instruction bus, a data bus, and the extension interface.
Parameters
----------
.. list-table:: Parameters
:header-rows: 1
:widths: 10 20 80
* - Parameter
- Values
- Description
* - MDU
- 0 (default), 1
- Enables the interface for connecting SERV and the Multiplication and Division Unit (MDU). Note that this only enables the interface and the decoder logic. The MDU itself must be connected externally as well.
* - PRE_REGISTER
- 0, 1 (default)
- | Register signals before or after the decoder
| 0 : Register after the decoder. Faster but uses more resources
| 1 : (default) Register before the decoder. Slower but uses less resources
* - RESET_PC
- 0x00000000 (default) - 0xFFFFFFFC
- Address of first instruction to fetch from memory after reset (Reset vector)
* - RESET_STRATEGY
- "MINI" (default), "NONE"
- | Amount of reset applied to design
| "NONE" : No reset at all. Relies on a POR to set correct initialization values and that core isn't reset during runtime
| "MINI" : Standard setting. Resets the minimal amount of FFs needed to restart execution from the instruction at RESET_PC.
* - RF_WIDTH
- 2 (default), 4, 8, 16, 32
- (serv_rf_top only) Width of the RF memory. Typically smaller values use less resources, but can be implementation-dependant.
* - WITH_CSR
- 0, 1 (default)
- Enable Zicsr extension. This also enables timer IRQ and exception handling. Note that SERV only implements a small subset of the CSR registers.
Signals
-------
.. list-table:: Signals
:header-rows: 1
:widths: 30 10 5 75
* - Signal
- Width
- Direction
- Description
* - clk
- 1
- in
- Clock
* - i_rst
- 1
- in
- Synchronous reset
* - i_timer_irq
- 1
- in
- Timer interrupt
* - o_ibus_adr
- 32
- out
- Instruction bus address
* - o_ibus_cyc
- 1
- out
- Instruction bus active cycle
* - i_ibus_rdt
- 32
- in
- Instruction bus read data
* - i_ibus_ack
- 1
- in
- Instruction bus cycle acknowledged
* - o_dbus_adr
- 32
- out
- Data bus address
* - o_dbus_dat
- 32
- out
- Data bus write data
* - o_dbus_sel
- 4
- out
- Data bus write data byte select mask
* - o_dbus_we
- 1
- out
- Data bus write transaction
* - o_dbus_cyc
- 1
- out
- Data bus active cycle
* - i_dbus_rdt
- 32
- in
- Data bus return data
* - i_dbus_ack
- 1
- in
- Data bus return data valid
* - o_ext_rs1
- 32
- out
- Extension interface RS1 contents
* - o_ext_rs2
- 32
- out
- Extension interface RS2 contents
* - o_ext_funct3
- 3
- out
- Extension interface funct3 contents
* - i_ext_rd
- 32
- in
- Extension interface RD contents
* - i_ext_ready
- 1
- in
- Extension interface RD contents valid
* - o_mdu_valid
- 1
- out
- MDU request
* - **serv_top only**
-
-
-
* - o_rf_rreq
- 1
- out
- RF interface read request
* - o_rf_wreq
- 1
- out
- RF interface write request
* - i_rf_ready
- 1
- in
- RF interface request acknowledged
* - o_wreg0
- 5 + WITH_CSR
- out
- RF interface channel 0 write address
* - o_wreg1
- 5 + WITH_CSR
- out
- RF interface channel 1 write address
* - o_wen0
- 1
- out
- RF interface channel 0 write enable
* - o_wen1
- 1
- out
- RF interface channel 1 write enable
* - o_wdata0
- 1
- out
- RF interface channel 0 write data
* - o_wdata1
- 1
- out
- RF interface channel 1 write data
* - o_rreg0
- 5 + WITH_CSR
- out
- RF interface channel 0 read address
* - o_rreg1
- 5 + WITH_CSR
- out
- RF interface channel 1 read address
* - i_rdata0
- 1
- in
- RF interface channel 0 read data
* - i_rdata1
- 1
- in
- RF interface channel 1 read data
Extension interface
-------------------
The SERV CPU, being the smallest RISC-V CPU, has a design that is primarily focused on simplicity and minimalism. However, to increase its utility without complicating the core design, it can be extended using an extension interface. The extension interface allows additional functionality to be added to the SERV CPU core through custom accelerators that are called for when specific instructions are encountered. SERV has built-in support for connecting an MDU (Multiplication/Division Unit) that implements the M ISA extension by setting the MDU parameter. Other accelerators need changes to the decoder.
When SERV detects instructions to be executed by an external accelerator through the extension interface, it will treat them as :ref:`two-stage operations`. In stage 1, the values in `o_ext_rs1` and `o_ext_rs2` are prepared to be sent to the accelerator. Once stage 1 is completed, SERV will assert the corresponding valid signal for the accelerator (e.g. `o_mdu_valid` for the M extension accelerator). The accelerator can now perform its work and when it has completed its result it will return that value in `i_ext_rd` and strobe `i_ext_ready` for one cycle. The following cycle, SERV will start stage two and store the received result. The waveform below explains this in more detail.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "P...|...|...|..."},
{ name: "init" , wave: "1...|..0|...|...", node: ".......d..", data: "r0"},
{ name: "o_rf_wreq" , wave: "0...|...|10.|...", node: ".........g", data: "r1"},
{ name: "i_rf_ready" , wave: "010.|...|10.|...", node: ".a.......h.", data: "r1"},
{ name: "cnt_en" , wave: "0.1.|..0|.1.|..0", node: "..b.......i"},
{ name: "cnt_done" , wave: "0...|.10|...|.10", node: "......c.."},
{ name: "o_ext_rs1" , wave: ".234567x|...|...", node: "..", data: "d0 d1 ... d30 d31"},
{ name: "o_ext_rs2" , wave: ".234567x|...|...", node: "..", data: "d0 d1 ... d30 d31"},
{ name: "o_mdu_valid", wave: "0...|..1|.0.|...", node: ".......e", data: "0 1 ... 30 31"},
{ name: "i_ext_ready", wave: "0...|...|10.|...", node: ".........f", data: "0 1 ... 30 31"},
{ name: "i_ext_rd" , wave: "....|...|234567x", node: "..", data: "d0 d1 ... d30 d31"},
],
edge : [
"a~>b", "c~>d", "e~>f", "f~>g", "h~>i"]
}

View file

@ -1,5 +0,0 @@
*********
Internals
*********
.. include:: modules.rst

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

View file

@ -1,35 +0,0 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd

View file

@ -1,417 +0,0 @@
Modules
-------
SERV is a bit-serial CPU which means that the internal datapath is one bit wide. :ref:`dataflow` shows the internal dataflow. For each instruction, data is read from the register file or the immediate fields of the instruction word and the result of the operation is stored back into the register file. Reading and writing memory is handled through the memory interface module.
.. _dataflow:
.. figure:: serv_dataflow.png
SERV internal dataflow
serv_rf_top
^^^^^^^^^^^
.. image:: serv_rf_top.png
serv_rf_top is a top-level convenience wrapper that includes SERV and the default RF implementation and just exposes the timer IRQ and instruction/data wishbone buses.
serv_top
^^^^^^^^
serv_top is the top-level of the SERV core without an RF
serv_alu
^^^^^^^^
.. image:: serv_alu.png
serv_alu handles alu operations. The first input operand (A) comes from i_rs1 and the second operand (B) comes from i_rs2 or i_imm depending on the type of operation. The data passes through the add/sub or bool logic unit and finally ends up in o_rd to be written to the destination register. The output o_cmp is used for conditional branches to decide whether or not to take the branch.
The add/sub unit can do additions A+B or subtractions A-B by converting it to A+B̅+1. Subtraction mode (i_sub = 1) is also used for the comparisons in the slt* and conditional branch instructions. The +1 used in subtraction mode is done by preloading the carry input with 1. Less-than comparisons are handled by converting the expression A<B to A-B<0 and checking the MSB, which will be set when the result is less than 0. This however requires sign-extending the operands to 33-bit inputs. For signed operands (when i_cmp_sig is set), the extra bit is the same as the MSB. For unsigned, the extra bit is always 0. Because the ALU is only active for 32 cycles, the 33rd bit must be calculated in parallel to the ordinary addition. The result from this operations is available in result_lt. For equality checks, result_eq checks that all bits are 0 from the subtraction.
.. image:: serv_alu_int.png
serv_bufreg
^^^^^^^^^^^
.. image:: serv_bufreg.png
For two-stage operations, serv_bufreg holds data between stages. This data can be the effective address for branches or load/stores or data to be shifted for shift ops. It has a serial output for streaming out results during stage two and a parallel output that forms the dbus address. serv_bufreg also keeps track of the two lsb when calculating addresses. This is used to check for alignment errors. In order to support these different modes, the input to the shift register can come from rs1, the immediate (imm), rs1+imm or looped back from the shift register output. The latter is used for shift operations. For some operations, the LSB of the immediate is cleared before written to the shift register. The two LSB of the shift register are special. When the shift register is loaded, these two get written first before the rest of the register is filled up. This allows the memory interface to check data/address alignment early.
.. image:: serv_bufreg_int.png
serv_bufreg2
^^^^^^^^^^^^
.. image:: serv_bufreg2.png
serv_bufreg2 is a 32-bit buffer register with some special features. It is used for shift operations to store the shift amount. It's used in load and store operations to store the data to be written or be read from the data bus, and it holds rs2 for the SERV extension interface. For shift and store operations, the register is shifted in from MSB when dat_en is asserted, while for loads and uses of the extension interface, the whole data word is written to when the i_load signal is asserted. Once the data is in the buffer, it is used differently depending on the operation. For stores and the extension interface the whole buffer is directly connected to the data bus as a 32-bit register. For load operations, the data is fed out serially once it has been fetched from the data bus. To better support load operations of varying sizes the buffer contains logic for reading out data serially from any of the byte LSBs of the 32-bit word. Finally, in shift mode, the 6 LSB of the register is used as a downcounter that is initialized during the init stage and then counts the remaining number of steps to shift the data and signals using sh_done and sh_done_r when finished.
.. image:: serv_bufreg2_int.png
serv_csr
^^^^^^^^
.. image:: serv_csr.png
serv_csr handles CSR accesses and all status related to (timer) interrupts. Out of the eight CSRs supported by SERV, only four resides in serv_csr (mstatus, mie, mcause and mip) and for those registers, SERV only implement the bits required for ecall, ebreak, misalignment and timer interrupts. The four remaining CSRs are commonly stored in the RF
.. image:: serv_csr_int.png
serv_ctrl
^^^^^^^^^
.. image:: serv_ctrl.png
serv_ctrl keeps track of the current PC and contains the logic needed to calculate the next PC. The PC is stored in shift register with a parallel output connected to the instruction bus.
The new PC can come from three sources. For normal instructions, it is incremented by four, which is the next 32-bit address. Jumps can be absolute or relative to the current PC. Absolute jumps are precalculated in serv_bufreg and written directly to the PC. PC relative jumps have the offset part precalculated in serv_bufreg which gets added to the current PC before storing as the new PC. The third source for the new PC comes from the CSR registers when entering or returning traps.
Some operations (LUI, AUIPC, jumps, entering or returning from traps) also update the destination register through o_rd. In the case of misaligned instruction traps, the invalid PC is written to o_bad_pc to be passed to mtval.
.. image:: serv_ctrl_int.png
serv_decode
^^^^^^^^^^^
.. image:: serv_decode.png
serv_decode is responsible for decoding the operation word coming from ibus into a set of control signals that are used internally in SERV.
.. image:: serv_decode_int.png
serv_immdec
^^^^^^^^^^^
.. image:: serv_immdec.png
The main responsibility of serv_immdec is to stitch together the pieces of immediates from the instruction word and push it out in the correct order. When a new instruction arrives, the relevant parts are placed into a number of shift registers, and the connections between the registers are setup differently depending on the type of operation.
serv_immdec also extracts the register addresses from the operation word.
.. image:: serv_immdec_int.png
serv_mem_if
^^^^^^^^^^^
.. image:: serv_mem_if.png
serv_mem_if contains the control logic for preparing the data to be sent out on the dbus during store operations and sign-extends the incoming data from bufreg2 during loads
The memory interface is centered around four serially connected byte-wide shift registers located in serv_bufreg2. During store operations, the `o_byte_valid` signal is asserted long enough to shift in the data from rs2 to the right place in the shift registers. The `Data bus byte mask`_ table summarizes the logic for when the individual byte select signals are asserted depending on the two LSB of the data address together with the size (byte, halfword, word) of the write operation.
During load operations, the data from the bus is read from serv_bufreg2. `dat_en` is again asserted to shift out data from the registers. `i_lsb` decides from which byte stage of the shift register to tap the data, depending on the alignment of the received data. The `dat_valid` signal makes sure to only present valid data to `o_rd` and otherwise fill in with zeros or sign extension.
When SERV is built with `WITH_CSR`, there is also logic to detect misaligned accesses which asserts the o_misalign flag to the core.
.. image:: serv_mem_if_int.png
.. _`Data bus byte mask`:
+-------+---+---------------------+-----------------+----------------------+-------------+
|op type|lsb| 3| 2| 1| 0|
+=======+===+=====================+=================+======================+=============+
|sb | 00| 0| 0| 0| 1|
+-------+---+---------------------+-----------------+----------------------+-------------+
|sb | 01| 0| 0| 1| 0|
+-------+---+---------------------+-----------------+----------------------+-------------+
|sb | 10| 0| 1| 0| 0|
+-------+---+---------------------+-----------------+----------------------+-------------+
|sb | 11| 1| 0| 0| 0|
+-------+---+---------------------+-----------------+----------------------+-------------+
|sh | 00| 0| 0| 1| 1|
+-------+---+---------------------+-----------------+----------------------+-------------+
|sh | 10| 1| 1| 0| 0|
+-------+---+---------------------+-----------------+----------------------+-------------+
|sw | 00| 1| 1| 1| 1|
+-------+---+---------------------+-----------------+----------------------+-------------+
|Logic |`(i_lsb == 11) |` |`(i_lsb == 10 |)`|`(i_lsb == 01) |` |`i_lsb == 0` |
|expression |`i_word |` |`i_word` |`i_word |` | |
| |`(i_half & i_lsb[1])`| |`(i_half & !i_lsb[1])`| |
+-------+---+---------------------+-----------------+----------------------+-------------+
serv_rf_if
^^^^^^^^^^
.. image:: serv_rf_if.png
serv_rf_if is the gateway between the core and an RF implementation. It transforms all control signals that affect register reads or writes and exposes two read and write ports to the RF. This allows implementors to plug in an RF implementation that is best suited for the technology to be used. The general purpose registers are allocated to address 0-31. In addition, four CSR are defined at addresses 32-35.
.. image:: serv_rf_if_int.png
serv_rf_ram
^^^^^^^^^^^
serv_rf_ram is the default RF implementation using an SRAM-like interface. Suitable for FPGA implementations
serv_rf_ram_if
^^^^^^^^^^^^^^
serv_rf_ram_if converts between the SERV RF IF and the serv_rf_ram interface
serv_state
^^^^^^^^^^
serv_state keeps track of the state for the core and contains all dynamic control signals during an operations life time. Also controls the accesses towards the RF, ibus and dbus
New instructions are fetched by asserting o_ibus_cyc until there is a response on i_ibus_ack. Instruction fetches occur when the reset signal is deasserted, which is what gets SERV started, or when the PC has finished updating its value.
Instruction life cycle
----------------------
The life cycle of an instruction starts by the core issuing a request for a new instruction on the ibus and ends when the PC has been updated with the address of the next instruction. This section goes through what happens between those points for the various types of instructions. SERV distinguishes between two-stage and one-stage operations with the former category being all jump (branch), shift, slt and load/store instructions and the latter all other operations. In addition to this, exceptions are a special case. Only two-stage operations (jump, load/store) can cause an exception. Regardless of instruction type, they all start out the same way.
.. image:: life_cycle.png
Fetch
^^^^^
The bus requests begin by SERV raising o_ibus_cyc until the memory responds with an i_ibus_ack and presents the instruction on i_ibus_rdt. Upon seeing the ack, SERV will lower cyc to indicate the end of the bus cycle.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "0P|......"},
{ name: "o_ibus_cyc", wave: "01|.0.....", node: ".a..."},
{ name: "i_ibus_ack", wave: "0.|10.....", node: "...b", data: "r0"},
{ name: "rf_rreq" , wave: "0.|10.....", node: "...c.", data: "r1"},
{ name: "i_ibus_rdt", wave: "x.|2x.....", node: "...", data: "i0"},
{ name: "opcode" , wave: "x.|.2.....", data: "i0[6:2]"},
{ name: "funct3" , wave: "x.|.2.....", data: "i0[14:12]"},
{ name: "imm30" , wave: "x.|.2.....", data: "i0[30]"},
{},
{ name: "r*_addr" , wave: "x.|.2.....", data: "i0[24:15,11:7]"},
{ name: "imm*" , wave: "x.|.2.....", data: "i0[31:7]"},
],
edge : [
"a~>b","b~>c"]
}
Decode
^^^^^^
When the ack appears, two things happen in SERV. The relevant portions of the instruction such as opcode, funct3 and immediate value are saved in serv_decode and serv_immdec. The saved bits of the instruction is then decoded to create the internal control signals that corresponds to the current instruction. The decoded control signals remain static throughout the instruction life cycle.
The other thing to happen is that a request to start accessing the register file is sent by strobing rf_rreq which prepares the register file for both read and write access.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "0P.|....."},
{ name: "rreq" , wave: "010|.....", node: ".a..."},
{ name: "rreg0" , wave: "x.2|.....", node: "....", data: "r0"},
{ name: "rreg1" , wave: "x.2|.....", node: "....", data: "r1"},
{ name: "ready" , wave: "0..|10...", node: "....b."},
{ name: "rdata0" , wave: "-..|12345", data: "0 1 2 3 4"},
{ name: "rdata1" , wave: "-..|12345", data: "0 1 2 3 4"},
],
edge : [
"a~>b"]
}
The interface between the core and the register file is described in a protocol where the core strobes rreq and present the registers to read on the following cycle. The register file will prepare to stream out data bit from the two requested registers. The cycle before it sends out the first bit (LSB) it will strobe rf_ready. Writes work in a similar way in that the registers to write has to be presented the cycle after rf_wreq is strobed and that the register file will start accepting data the cycle after it has strobed rf_ready. Note that the delay between rf_wreq and rf_ready does not have to be the same as from rf_rreq to rf_ready. Also note that register data will only be written to a register if the corresponding write enable signal is asserted. In the diagram below, only register r0 will be written to.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "0P....."},
{ name: "wreq" , wave: "010....", node: ".a..."},
{ name: "ready" , wave: "010....", node: ".b."},
{ name: "wreg0" , wave: "x.2....", node: "....", data: "r0"},
{ name: "wreg1" , wave: "x.2....", node: "....", data: "r1"},
{ name: "wen0" , wave: "0.1...."},
{ name: "wen1" , wave: "0......"},
{ name: "wdata0" , wave: "-123456", node: "..c.", data: "0 1 2 3 4"},
{ name: "wdata1" , wave: "-123456", node: "..d.", data: "0 1 2 3 4"},
],
edge : [
"a~>b", "b~>c", "b~>d"]
}
Execute
^^^^^^^
After the instruction has been decoded and the register file prepared for reads (and possibly writes) the core knows whether it is a one-stage or two-stage instruction. These are handled differently and we will begin by looking at one-stage instructions. A stage in SERV is 32 consecutive cycles during which the core is active and processes inputs and creates results one bit at a time, starting with the LSB.
One-stage instructions
::::::::::::::::::::::
Most operations are one-stage operations which finish in 32 cycles + fetch overhead. During a one-stage operation, the RF is read and written simultaneously as well as the PC which is increased by four to point to the next instruction. trap and init signals are low to distinguish from other stages.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "0P..|..."},
{ name: "cnt_en" , wave: "01..|..0", node: "...."},
{ name: "init" , wave: "0...|...", node: "....", data: "r0"},
{ name: "trap" , wave: "0...|...", node: "....", data: "r1"},
{ name: "pc_en" , wave: "01..|..0"},
{ name: "rs1" , wave: "x234|56x", node: "...", data: "0 1 ... 30 31"},
{ name: "rs2" , wave: "x234|56x", node: "...", data: "0 1 ... 30 31"},
{ name: "imm" , wave: "x234|56x", node: "...", data: "0 1 ... 30 31"},
{ name: "rd" , wave: "x234|56x", node: "...", data: "0 1 ... 30 31"},
],
edge : [
"a~>b", "b~>c", "b~>d"]
}
Interrupts and ecall/ebreak
:::::::::::::::::::::::::::
External timer interrupts and ecall/ebreak are also one-stage operations with some notable differences. The new PC is fetched from the MTVEC CSR and instead of writing to rd, the MEPC and MTVAL CSR registers are written. All this is handled by serv_state raising the trap signal during the instruction's execution.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "0P..|..."},
{ name: "cnt_en" , wave: "01..|..0", node: "...."},
{ name: "init" , wave: "0...|...", node: "....", data: "r0"},
{ name: "trap" , wave: "1...|...", node: "....", data: "r1"},
{ name: "pc_en" , wave: "01..|..0"},
{ name: "rs1" , wave: "x...|...", node: "...", data: "0 1 ... 30 31"},
{ name: "rs2" , wave: "x...|...", node: "...", data: "0 1 ... 30 31"},
{ name: "imm" , wave: "x...|...", node: "...", data: "0 1 ... 30 31"},
{ name: "rd" , wave: "x...|...", node: "...", data: "0 1 ... 30 31"},
],
edge : [
"a~>b", "b~>c", "b~>d"]
}
Two-stage operations
::::::::::::::::::::
Some operations need to be executed in two stages. In the first stage the operands are read out from the instruction immediate fields and the rs1/rs2 registers. In the second stage rd and the PC are updated with the results from the operation. The operation-specific things happen between the aforementioned stages. SERV has four types of four two-stage operations; memory, shift, slt and branch operations. In all cases the first stage is distinguished by having the init signal raised and only performing reads from the RF.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "0P..|..."},
{ name: "cnt_en" , wave: "01..|..0", node: "...."},
{ name: "init" , wave: "1...|..0", node: "....", data: "r0"},
{ name: "trap" , wave: "0...|...", node: "....", data: "r1"},
{ name: "pc_en" , wave: "0...|..."},
{ name: "rs1" , wave: "x234|56x", node: "...", data: "0 1 ... 30 31"},
{ name: "rs2" , wave: "x234|56x", node: "...", data: "0 1 ... 30 31"},
{ name: "imm" , wave: "x234|56x", node: "...", data: "0 1 ... 30 31"},
{ name: "rd" , wave: "x234|56x", node: "...", data: "0 1 ... 30 31"},
],
edge : [
"a~>b", "b~>c", "b~>d"]
}
memory operations
+++++++++++++++++
Loads and stores are memory operations. In the init stage, the data address to access is calculated, checked for alignment and stored in serv_bufreg. For stores, the data to write is also shifted into the data register in serv_bufreg2.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "P..|..."},
{ name: "trap" , wave: "0..|...", node: "....", data: "r1"},
{ name: "init" , wave: "1.0|...", node: "....", data: "r0"},
{ name: "cnt_en" , wave: "1.0|.1.", node: ".....d"},
{ name: "cnt_done" , wave: "010|.1.", node: ".a...."},
{ name: "o_dbus_cyc", wave: "0.1|.0.", node: "..b.", data: "0 1 ... 30 31"},
{ name: "i_dbus_ack", wave: "0..|10.", node: "....c", data: "0 1 ... 30 31"},
{ name: "o_dbus_adr", wave: "x.2|.x.", node: "...", data: "address"},
{ name: "rs2" , wave: "33x|...", node: ".e.", data: "d30 d31"},
{ name: "o_dbus_dat", wave: "x.3|.x.", node: "..f", data: "data"},
{ name: "o_dbus_sel", wave: "x.4|.x.", node: "...", data: ["write mask"]},
{ name: "o_dbus_we" , wave: "1..|..."},
],
edge : [
"a~>b", "b~>c", "c~>d", "e~>f"]
}
If the address has correct alignment, the o_dbus_cyc signal is raised to signal an access on the data bus after the init stage has finished and waits for an incoming i_dbus_ack, and incoming data in case of loads. After an incoming ack, o_dbus_cyc is lowered and stage 2 begins. For stores, the only remaining work in stage 2 is to update the PC. For loads, the incoming data is shifted into rd.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "P..|..."},
{ name: "trap" , wave: "0..|...", node: "....", data: "r1"},
{ name: "init" , wave: "1.0|...", node: "....", data: "r0"},
{ name: "cnt_en" , wave: "1.0|.1.", node: ".....d"},
{ name: "cnt_done" , wave: "010|.1.", node: ".a...."},
{ name: "o_dbus_cyc", wave: "0.1|.0.", node: "..b.", data: "0 1 ... 30 31"},
{ name: "i_dbus_ack", wave: "0..|10.", node: "....c", data: "0 1 ... 30 31"},
{ name: "o_dbus_adr", wave: "x.2|.x.", node: "...", data: "address"},
{ name: "o_dbus_we" , wave: "0..|..."},
{ name: "i_dbus_rdt", wave: "x..|3x.", node: "....e", data: "data"},
{ name: "rd" , wave: "x..|.33", node: ".....f", data: "d0 d1"},
],
edge : [
"a~>b", "b~>c", "c~>d", "e~>f"]
}
If the calculated address in the init stage was misaligned, SERV will raise a exception. Instead of performing an external bus access it will set mcause and raise the trap signal, which causes SERV to store the current PC to mepc, store misaligned address to mtval and set the new PC from mtvec which will enter the exception handler.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "P...."},
{ name: "misalign" , wave: "1....", node: "c..", data: ["write mask"]},
{ name: "trap" , wave: "0.1..", node: "..b.", data: "r1"},
{ name: "init" , wave: "1.0..", node: "....", data: "r0"},
{ name: "cnt_en" , wave: "1.01.", node: "...d"},
{ name: "cnt_done" , wave: "010..", node: ".a...."},
{ name: "o_dbus_cyc", wave: "0....", node: "....", data: "0 1 ... 30 31"},
{ name: "i_dbus_ack", wave: "0....", node: "....", data: "0 1 ... 30 31"},
],
edge : [
"a~>b", "c~>b", "b~>d"]
}
shift operations
++++++++++++++++
Left-shifts and right-shifts are handled somewhat differently in SERV. In both cases the data to be shifted (rs1) is stored in serv_bufreg and the shift amount (rs2 or imm) in serv_bufreg2 during the init stage, but after that the methods diverge.
For left shifts stage two is started immediately during which rd is updated, but data is not shifted out from serv_bufreg2 until the number of cycles corresponding to the shift amount have passed. This effectively "delays" the data written from rs1 into rd, causing a left shift.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "P...|......."},
{ name: "two_stage_op", wave: "1...|.......", node: "....", data: "r1"},
{ name: "shift_op" , wave: "1...|.......", node: "....", data: "r1"},
{ name: "sh_right" , wave: "0...|.......", node: "....", data: "r1"},
{ name: "trap" , wave: "0...|.......", node: "....", data: "r1"},
{ name: "init" , wave: "1.0.|.......", node: "....", data: "r0"},
{ name: "cnt_en" , wave: "1.01|.......", node: "...b."},
{ name: "cnt_done" , wave: "010.|.......", node: ".a....."},
{ name: "shamt" , wave: "x333|.333333", node: "......c.f", data: "N N-1 ... 0 31 30 29 28 27"},
{ name: "sh_done_r" , wave: "0...|...1...", node: "........d.", data: "0 1 ... 30 31"},
{ name: "bufreg_en" , wave: "1.0.|...1...", node: "........e", data: "0 1 ... 30 31"},
{ name: "bufreg_q" , wave: "x.3.|....456", node: "...", data: "d0 d1 d2 d3"},
{ name: "rd" , wave: "x..2|...3456", node: ".....f", data: "0 d0 d1 d2 d3"},
],
edge : [
"a~>b", "c~>d", "c~>d", "d~>e"]
}
For right shifts, the opposite happens. Data is immediately shifted out from serv_bufreg after stage one ends, but stage two (and writing to rd) doesn't start until shift amount cycles have passed. After all valid data has been written from serv_bufreg, the remaining cycles are zero-padded or sign-extended depending on logical or arithmetic shifts.
.. wavedrom::
{ signal: [
{ name: "clk" , wave: "P...|......|..|.."},
{ name: "two_stage_op", wave: "1...|......|..|..", node: "....", data: "r1"},
{ name: "shift_op" , wave: "1...|......|..|..", node: "....", data: "r1"},
{ name: "sh_right" , wave: "1...|......|..|..", node: "....", data: "r1"},
{ name: "trap" , wave: "0...|......|..|..", node: "....", data: "r1"},
{ name: "init" , wave: "1.0.|......|..|..", node: "....", data: "r0"},
{ name: "cnt_en" , wave: "1.0.|...1..|..|.0", node: "........e"},
{ name: "cnt_done" , wave: "010.|......|..|10", node: ".a......."},
{ name: "shamt" , wave: "x333|.3x...|..|..", node: "......c.f", data: "N N-1 ... 0 31 30 29 ... 27"},
{ name: "sh_done_r" , wave: "0...|...1..|..|..", node: "........d.", data: "0 1 ... 30 31"},
{ name: "bufreg_en" , wave: "1.01|......|..|..", node: "...b.....", data: "0 1 ... 30 31"},
{ name: "bufreg_q" , wave: "x.34|567893|45|..", node: "...", data: "d0 ... dN-3 dN-2 dN-1 dN dN+1 ... d31 sign"},
{ name: "rd" , wave: "x...|...893|45|.x", node: ".....f", data: "dN dN+1 ... d31 sign"},
],
edge : [
"a~>b", "c~>d", "c~>d", "d~>e"]
}

View file

@ -1,42 +0,0 @@
Overview
========
The SERV RISC-V CPU is an award-winning and highly compact processor core based on the RISC-V instruction set architecture (ISA). It is designed to be the smallest possible RISC-V compliant CPU and is particularly well-suited for embedded systems and applications where silicon area is critical.
Key Features
------------
* **ISA:** RISC-V RV32IZifencei
* **Optional ISA extensions:** C, M, Zicsr
* **Optional features:** Timer interrupts, Extension interface
* **Architecture:** Bit-serial (one bit processed per clock cycle)
* **License:** ISC (available under other commercial licenses upon request)
* **OS support:** Zephyr 3.7
* **SW support:** Compatible with standard RISC-V toolchains
* **Area:** Smallest RISC-V core available
Applications
------------
* **Embedded Systems:** Ideal for minimalistic embedded control tasks
* **IoT Devices:** Suitable for Internet of Things devices where space and power are limited
* **Education:** Excellent resource for teaching and understanding the RISC-V architecture and CPU design
* **Research:** Platform for research in minimalistic computing designs and for bringing up new fabrication processes
Area
----
.. list-table:: Area for minimal configuration [#]_
:widths: 25 25 25 25
:header-rows: 1
* - Lattice iCE40
- Altera Cyclone10LP
- AMD Artix-7
- CMOS
* - 198LUT/164FF
- 239LUT/164FF
- 125LUT/164FF
- 2.1kGE
.. [#] Excluding register file

View file

@ -1 +0,0 @@
git+https://github.com/bavovanachte/sphinx-wavedrom@master#egg=sphinxcontrib-wavedrom

View file

@ -1,12 +0,0 @@
**************************
Building systems with SERV
**************************
A CPU is only as good as its ecosystem. In order to make use of SERV, it needs to be combined with other components such as memories, accelerators and peripheral controllers.
Welcome to the reservoir, a pool of ready-made designs and subsystems for different purposes that you can use to quickly get started with SERV or integrate it into larger designs.
.. include:: servile.rst
.. include:: serving.rst
.. include:: servant.rst
.. include:: subservient.rst

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

View file

@ -1,293 +0,0 @@
Servant : FPGA Reference platform
=================================
.. figure:: servant.png
Servant FPGA Reference platform
SERV comes with a small FPGA-focused reference platform called Servant, which is capable of running Zephyr RTOS, the regression test suite and other software. The platform consists of SERV, a timer, memory and a 1-bit GPIO output pin.
Available targets
-----------------
The servant SoC has been ported to an increasing number of different FPGA boards and is easy to modify for new targets. To see all currently supported targets run:
fusesoc core show servant
By default, these targets have the program memory preloaded with a small Zephyr hello world example that writes its output on a UART pin. Don't forget to install the appropriate toolchain (e.g. icestorm, Vivado, Quartus...) and add to your PATH
Some targets also depend on functionality in the FuseSoC base library (fusesoc-cores). Running `fusesoc library list` should tell you if fusesoc-cores is already available. If not, add it to your workspace with
fusesoc library add fusesoc-cores https://github.com/fusesoc/fusesoc-cores
Now we're ready to build. Note, for all the cases below, it's possible to run with `--memfile=$SERV/sw/blinky.hex`
(or any other suitable program) as the last argument to preload the LED blink example
instead of hello world.
AC701
^^^^^
fusesoc run --target=ac701 servant
Alchistry AU
^^^^^^^^^^^^
fusesoc run --target=alchistry_au servant
Alhambra II
^^^^^^^^^^^
Pin 61 is used for UART output with 115200 baud rate. This pin is connected to a FT2232H chip in board, that manages the communications between the FPGA and the computer.
fusesoc run --target=alhambra servant
iceprog -d i:0x0403:0x6010:0 build/servant_1.0.1/alhambra-icestorm/servant_1.0.1.bin
Alinx ax309 (Spartan6 LX9)
^^^^^^^^^^^^^^^^^^^^^^^^^^
Pin D12 (the on-board RS232 TX pin) is used for UART output with 115200 baud rate and wired to Pin P4 (LED0).
fusesoc run --target=ax309 servant
Arty A7 35T
^^^^^^^^^^^
Pin D10 (uart_rxd_out) is used for UART output with 57600 baud rate (to use
blinky.hex change D10 to H5 (led[4]) in data/arty_a7_35t.xdc).
fusesoc run --target=arty_a7_35t servant
Arty S7 50T
^^^^^^^^^^^
Pin R12 (uart_rxd_out) is used for UART output with 57600 baud rate (to use
blinky.hex change R12 to E18 (led[4]) in data/arty_s7_50t.xdc).
fusesoc run --target=arty_s7_50t servant
Chameleon96 (Arrow 96 CV SoC Board)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FPGA Pin W14 (1V8, pin 5 low speed connector) is used for UART Tx output with 115200 baud rate. No reset key. Yellow Wifi led is q output.
fusesoc run --target=chameleon96 servant
CMOD A7 35t
^^^^^^^^^^^
FPGA Pin J18 is used for UART output with 57600 baud rate. btn0 is used for reset.
fusesoc run --target=cmod_a7_35t servant
CYC1000
^^^^^^^
fusesoc run --target=cyc1000 servant
DE0 Nano
^^^^^^^^
FPGA Pin D11 (Connector JP1, pin 38) is used for UART output with 57600 baud rate. DE0 Nano needs an external 3.3V UART to connect to this pin
fusesoc run --target=de0_nano servant
DE10 Nano
^^^^^^^^^
FPGA Pin Y15 (Connector JP7, pin 1) is used for UART output with 57600 baud rate. DE10 Nano needs an external 3.3V UART to connect to this pin
fusesoc run --target=de10_nano servant
DE1 SoC revF
^^^^^^^^^^^^
FPGA PIN_AC18 (Connector GPIO0, pin 0) is used for UART output with 57600 baud rate. DE1 SoC revF needs an external 3.3V UART to connect to this pin. The UART pin has not been tested.
fusesoc run --target=de1_soc_revF servant
DECA development kit
^^^^^^^^^^^^^^^^^^^^
FPGA Pin W18 (Pin 3 P8 connector) is used for UART output with 57600 baud rate. Key 0 is reset and Led 0 q output.
fusesoc run --target=deca servant
EBAZ4205 'Development' Board
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Pin B20 is used for UART output with 57600 baud rate. To use `blinky.hex`
change B20 to W14 (red led) in `data/ebaz4205.xdc` file).
fusesoc run --target=ebaz4205 servant
fusesoc run --target=ebaz4205 servant --memfile=$SERV/sw/blinky.hex
Reference: https://github.com/fusesoc/blinky#ebaz4205-development-board
ECP5 EVN
^^^^^^^^
fusesoc run --target=ecp5_evn servant
Icebreaker
^^^^^^^^^^
Pin 9 is used for UART output with 57600 baud rate.
fusesoc run --target=icebreaker servant
iCEstick
^^^^^^^^
Pin 95 is used as the GPIO output which is connected to the board's green LED. Due to this board's limited Embedded BRAM, programs with a maximum of 7168 bytes can be loaded. The default program for this board is blinky.hex.
fusesoc run --target=icestick servant
iceprog build/servant_1.3.0/icestick-icestorm/servant_1.3.0.bin
iCESugar
^^^^^^^^
Pin 6 is used for UART output with 115200 baud rate. Thanks to the onboard
debugger, you can just connect the USB Type-C connector to the PC, and a
serial console will show up.
fusesoc run --target=icesugar servant
ICE-V Wireless
^^^^^^^^^^^^^^
Pin 9 is used for UART output with 57600 baud rate.
fusesoc run --target=icev_wireless servant
iceprog build/servant_1.3.0/icestick-icestorm/servant_1.3.0.bin
GMM7550
^^^^^^^
fusesoc run --target=gmm7550 servant
LX9 Microboard
^^^^^^^^^^^^^^
fusesoc run --target=lx9_microboard servant
Machdyne Kolibri
^^^^^^^^^^^^^^^^
Pin B1 is used for UART output with 115200 baud rate. The serial port on Kolibri is accessible as a USB-CDC device.
fusesoc run --target=machdyne_kolibri servant
ldprog -Ks build/servant_1.3.0/machdyne_kolibri-icestorm/servant_1.3.0.bin
MAX10 10M08 Evaluation Kit
^^^^^^^^^^^^^^^^^^^^^^^^^^
FPGA Pin 75 (Arduino_IO01 J5 pin 7) is used for UART output with 57600 baud rate. SW1 is reset and Led 1 q output.
fusesoc run --target=max10_10m08evk servant
Nandland Go Board
^^^^^^^^^^^^^^^^^
Pin 56 is used as the GPIO output which is connected to the board's LED1. Due to this board's limited Embedded BRAM, programs with a maximum of 7168 bytes can be loaded. The default program for this board is blinky.hex.
fusesoc run --target=go_board servant
iceprog build/servant_1.3.0/go_board-icestorm/servant_1.3.0.bin
Nexys 2
^^^^^^^
Pmod pin JA1 is connected to UART tx with 57600 baud rate. A USB to TTL connector is used to display to hello world message on the serial monitor.
(To use blinky.hex change L15 to J14 (led[0]) in data/nexys_2.ucf).
fusesoc run --target=nexys_2_500 servant --uart_baudrate=57600 --firmware=$SERV/sw/zephyr_hello.hex
Nexys A7
^^^^^^^^
fusesoc run --target=nexys_a7 servant
OrangeCrab R0.2
^^^^^^^^^^^^^^^
Pin D1 is used for UART output with 115200 baud rate.
fusesoc run --target=orangecrab_r0.2 servant
dfu-util -d 1209:5af0 -D build/servant_1.3.0/orangecrab_r0.2-trellis/servant_1.3.0.bit
PolarFire Splash Kit
^^^^^^^^^^^^^^^^^^^^
Pin R5 is used for UART output with a 115200 baud rate, this is routed through
the onboard FTDI transceiver. LED1 (Pin P7) serves as the generic GPIO.
Pin P8 is used as the GPIO heartbeat with a 1Hz frequency and is connected to
the board's LED2.
Pin N4 (user reset) is used for the reset
fusesoc run --target=polarfire_splashkit servant --memfile=$SERV/sw/zephyr_hello.hex
Saanlima Pipistrello (Spartan6 LX45)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Pin A10 (usb_data<1>) is used for UART output with 57600 baud rate (to use
blinky.hex change A10 to V16 (led[0]) in data/pipistrello.ucf).
fusesoc run --target=pipistrello servant
SoCKit development kit
^^^^^^^^^^^^^^^^^^^^^^
FPGA Pin F14 (HSTC GPIO addon connector J2, pin 2) is used for UART output with 57600 baud rate.
fusesoc run --target=sockit servant
Trenz Electronic TE0802
^^^^^^^^^^^^^^^^^^^^^^^
PMOD A marked J5, pin two, on the board is used for UART output with 115200 baud rate.
fusesoc run --target=te0802 servant
TinyFPGA BX
^^^^^^^^^^^
Pin A6 is used for UART output with 115200 baud rate.
fusesoc run --target=tinyfpga_bx servant
tinyprog --program build/servant_1.0.1/tinyfpga_bx-icestorm/servant_1.0.1.bin
ULX3S
^^^^^
fusesoc run --target=ulx3s servant
Upduino2
^^^^^^^^
fusesoc run --target=upduino2 servant
zcu106
^^^^^^
fusesoc run --target=zcu106 servant
Porting Servant to a new target
-------------------------------
Mostly any FPGA board can be used to run the Servant SoC. In its simplest form it just needs an FPGA with a clock input and an output that can be used to connect an UART or a LED.
The porting process consists of FIXME steps.
We will use `<name>` as a placeholder for the name of the FPGA board.
1. Locate the pins used for clock input and for the outputs. Outputs should preferably be both a LED and an UART, but either works if not both are available. Optionally, locate an input pin connected to the reset as well. This is not required, but can be handy.
2. Write a pin constraints file with your located pins in the format of the FPGA toolchain you intend to use. For Vivado this would be an .xdc file. For Quartus a .tcl file, for nextpnr a .pcf file and so on. Save this as `data/<name>.{pcf,ucf,xdc...}` in the SERV repo.
3. Create a clock generation file
4. Create a top-level
5. Create a fileset
6. Create a target

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -1,166 +0,0 @@
Servile : Convenience wrapper
=============================
.. figure:: servile.png
Servile convenience wrapper
Servile is a helper component that takes care of common configuration and is used as a building block in the other designs and subsystems. It exposes a memory bus intended to be connected to a combined data and instruction memory, an extension bus for peripheral controllers and accelerators and an RF interface for connecting to an SRAM for GPR and CSR registers.
.. figure:: servile_int.png
Servile block diagram
Internally, Servile contains logic for optionally instantiating an MDU core for the M extension, an arbiter to route instruction fetches and main memory accesses through the same interface and a mux to split up the memory map into memory (0x00000000-0x3FFFFFFF) and external accesses (0x40000000-0xFFFFFFFF). The mux also contains simulation-only logic to write output to a log file and stop the simulation.
Parameters
----------
.. list-table:: Parameters
:header-rows: 1
:widths: 10 20 80
* - Parameter
- Values
- Description
* - reset_pc
- 0x00000000 (default) - 0xFFFFFFFC
- Address of first instruction to fetch from memory after reset (Reset vector)
* - reset_strategy
- "MINI" (default), "NONE"
- | Amount of reset applied to design
| "NONE" : No reset at all. Relies on a POR to set correct initialization values and that core isn't reset during runtime
| "MINI" : Standard setting. Resets the minimal amount of FFs needed to restart execution from the instruction at RESET_PC.
* - rf_width
- 2 (default), 4, 8, 16, 32
- Width of the data bus to the RF memory. Typically smaller values use less resources, but can be implementation-dependant.
* - sim
- 0 (default), 1
- | Enable simulation mode. In simulation mode, two memory addresses have special purposes.
| 0x80000000: Writes to this address puts the byte in the lowest data byte into a log file decided by the "signature" plusarg.
| 0x90000000: Writes to this address ends the simulation.
* - with_c
- 0 (default), 1
- Enable the C extension. This also makes SERV support misaligned loads and stores.
* - with_csr
- 0 (default), 1
- Enable the Zicsr extension. This also enables timer IRQ and exception handling. Note that SERV only implements a small subset of the CSR registers.
* - with_mdu
- 0 (default), 1
- Enables the Multiplication and Division Unit (MDU) to support the M extension. Note that this only enables the interface and the decoder logic. The MDU itself is external from SERV.
Signals
-------
.. list-table:: Signals
:header-rows: 1
:widths: 30 10 5 75
* - Signal
- Width
- Direction
- Description
* - i_clk
- 1
- in
- Clock
* - i_rst
- 1
- in
- Synchronous reset
* - i_timer_irq
- 1
- in
- Timer interrupt
* - **Memory interface**
-
-
- Connect to instruction/data memory
* - o_wb_mem_adr
- 32
- out
- Memory bus address
* - o_wb_mem_dat
- 32
- out
- Memory bus data
* - o_wb_mem_sel
- 4
- out
- Memory bus write data byte select mask
* - o_wb_mem_we
- 1
- out
- Memory bus write transaction
* - o_wb_mem_stb
- 1
- out
- Memory bus active strobe
* - i_wb_mem_rdt
- 32
- in
- Memory bus read data
* - i_wb_mem_ack
- 1
- in
- Memory bus cycle acknowledged
* - **Extension interface**
-
-
- Connect to peripheral controllers
* - o_wb_ext_adr
- 32
- out
- Data bus address
* - o_wb_ext_dat
- 32
- out
- Data bus write data
* - o_wb_ext_sel
- 4
- out
- Data bus write data byte select mask
* - o_wb_ext_we
- 1
- out
- Data bus write transaction
* - o_wb_ext_stb
- 1
- out
- Data bus active cycle
* - i_wb_ext_rdt
- 32
- in
- Data bus return data
* - i_wb_ext_ack
- 1
- in
- Data bus return data valid
* - **RF (SRAM) interface**
-
-
-
* - o_rf_waddr
- ceil(log2(regs*32/rf_width)
- out
- RF memory write address
* - o_rf_wdata
- rf_width
- out
- RF memory write data
* - o_rf_wen
- 1
- out
- RF memory write enable
* - o_rf_raddr
- ceil(log2(regs*32/rf_width)
- out
- RF memory read address
* - i_rf_rdata
- rf_width
- out
- RF memory read data
* - o_rf_ren
- 1
- out
- RF memory read enable

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -1,94 +0,0 @@
Serving : FPGA subsystem
========================
.. figure:: serving.png
Serving SoClet
Serving is ideal for FPGA implementations as it only uses a single combined block RAM for instructions, data and RF that can be preloaded with a RISC-V application. It exposes a wishbone data bus that can be used to connect peripheral controllers.
Parameters
----------
.. list-table:: Parameters
:header-rows: 1
:widths: 10 20 80
* - Parameter
- Values
- Description
* - memfile
- "" (default)
- Verilog hex file containing a RISC-V application to be preloaded into memory
* - memsize
- 1-4294967296 (default 8192)
- Size of memory (in bytes). Needs to be at least large enough to fit the application supplied by memsize. Note that the RF occupies the highest 128 bytes of memory in the RAM.
* - sim
- 0 (default), 1
- | Enable simulation mode. In simulation mode, two memory addresses have special purposes.
| 0x80000000: Writes to this address puts the byte in the lowest data byte into a log file decided by the "signature" plusarg.
| 0x90000000: Writes to this address ends the simulation.
* - RESET_STRATEGY
- "MINI" (default), "NONE"
- | Amount of reset applied to design
| "NONE" : No reset at all. Relies on a POR to set correct initialization values and that core isn't reset during runtime
| "MINI" : Standard setting. Resets the minimal amount of FFs needed to restart execution from the instruction at RESET_PC.
* - WITH_CSR
- 0 (default), 1
- Enable the Zicsr extension. This also enables timer IRQ and exception handling. Note that SERV only implements a small subset of the CSR registers.
Signals
-------
.. list-table:: Signals
:header-rows: 1
:widths: 30 10 5 75
* - Signal
- Width
- Direction
- Description
* - i_clk
- 1
- in
- Clock
* - i_rst
- 1
- in
- Synchronous reset
* - i_timer_irq
- 1
- in
- Timer interrupt
* - **Extension interface**
-
-
- Connect to peripheral controllers
* - o_wb_adr
- 32
- out
- Data bus address
* - o_wb_dat
- 32
- out
- Data bus write data
* - o_wb_sel
- 4
- out
- Data bus write data byte select mask
* - o_wb_we
- 1
- out
- Data bus write transaction
* - o_wb_cyc
- 1
- out
- Data bus active cycle
* - i_wb_rdt
- 32
- in
- Data bus return data
* - i_wb_ack
- 1
- in
- Data bus return data valid

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -1,10 +0,0 @@
Subservient : SERV ASIC macro
=============================
.. figure:: subservient.png
Subservient ASIC macro
`Subservient <https://github.com/olofk/subservient>`_ is a minimal reference system that just requires connecting one single-port SRAM for data, instructions and RF. It is intended to make ASIC integration easier.

Some files were not shown because too many files have changed in this diff Show more