Compare commits

...

157 commits
1.1.0 ... main

Author SHA1 Message Date
Olof Kindgren
f6116cf2ec Make right-shifts one cycle faster
Some checks failed
Run compliance test suite / RISC-V Compliance Test (push) Has been cancelled
Formal verification / Run RISCV-formal verification suite (push) Has been cancelled
Run linter / Linter (push) Has been cancelled
Build GDS using OpenLANE and sky130 PDK / build-openlane-sky130 (push) Has been cancelled
This allows removing the stage_two_req register as well.
2025-03-18 13:42:47 +01:00
Olof Kindgren
629e6727f5 Move shamt to top byte in bufreg2
Instead of shifting shamt all the way to bits 4:0, we just shift
8 steps. This saves some energy and allows us to be finished before
cnt_done.
2025-03-18 13:40:44 +01:00
Olof Kindgren
79768a8ac9 Silence warning about unused return value in testbench 2025-03-18 13:15:46 +01:00
Olof Kindgren
9e73e11d86 Fix verilator_waiver.vlt
Some checks failed
Run compliance test suite / RISC-V Compliance Test (push) Has been cancelled
Formal verification / Run RISCV-formal verification suite (push) Has been cancelled
Run linter / Linter (push) Has been cancelled
Build GDS using OpenLANE and sky130 PDK / build-openlane-sky130 (push) Has been cancelled
2025-03-01 23:18:59 +01:00
Olof Kindgren
a38110aea9 bufreg2: Split up dat_en to cnt_en and shift_en 2025-03-01 23:14:55 +01:00
Olof Kindgren
0a55885d3b Move bytecnt to bufreg2 2025-03-01 23:12:44 +01:00
Olof Kindgren
2f23449f0f Make branches, slt and left shifts one cycle faster 2025-03-01 23:12:44 +01:00
Olof Kindgren
91628a056a Simplify one-hot counter enable 2025-03-01 23:12:44 +01:00
Olof Kindgren
129a9294c6 Remove slt_or_branch control signal 2025-03-01 23:12:44 +01:00
Olof Kindgren
842c2df0ca Enable zicsr flag for GCC
Some checks are pending
Run compliance test suite / RISC-V Compliance Test (push) Waiting to run
Formal verification / Run RISCV-formal verification suite (push) Waiting to run
Run linter / Linter (push) Waiting to run
Build GDS using OpenLANE and sky130 PDK / build-openlane-sky130 (push) Waiting to run
2025-03-01 10:32:57 +01:00
Mikadore
01e74ef7fd Remove duplicate tools entry in servant.core
Some checks failed
Run compliance test suite / RISC-V Compliance Test (push) Has been cancelled
Formal verification / Run RISCV-formal verification suite (push) Has been cancelled
Run linter / Linter (push) Has been cancelled
Build GDS using OpenLANE and sky130 PDK / build-openlane-sky130 (push) Has been cancelled
In `servant.core`:`targets`:`machdyne_kolibri`
2025-01-29 21:27:09 +00:00
Olof Kindgren
de4c3b44ee Add Subservient documentation
Some checks failed
Run compliance test suite / RISC-V Compliance Test (push) Has been cancelled
Formal verification / Run RISCV-formal verification suite (push) Has been cancelled
Run linter / Linter (push) Has been cancelled
Build GDS using OpenLANE and sky130 PDK / build-openlane-sky130 (push) Has been cancelled
2024-12-18 13:48:40 +01:00
Markus Koch
1b06ae6033 Update to Zephyr 4.0.0
Some checks are pending
Run compliance test suite / RISC-V Compliance Test (push) Waiting to run
Formal verification / Run RISCV-formal verification suite (push) Waiting to run
Run linter / Linter (push) Waiting to run
Build GDS using OpenLANE and sky130 PDK / build-openlane-sky130 (push) Waiting to run
2024-12-17 11:39:13 +00:00
Erik Bånvik
7e7b453eb0 Added support for Trenz Electronic TE0802
Some checks failed
Run compliance test suite / RISC-V Compliance Test (push) Has been cancelled
Formal verification / Run RISCV-formal verification suite (push) Has been cancelled
Run linter / Linter (push) Has been cancelled
Build GDS using OpenLANE and sky130 PDK / build-openlane-sky130 (push) Has been cancelled
2024-12-03 13:54:23 +01:00
Olof Kindgren
007f42850d servile_mux: Declare variable before use
Some checks failed
Run compliance test suite / RISC-V Compliance Test (push) Has been cancelled
Formal verification / Run RISCV-formal verification suite (push) Has been cancelled
Run linter / Linter (push) Has been cancelled
Build GDS using OpenLANE and sky130 PDK / build-openlane-sky130 (push) Has been cancelled
2024-11-26 19:58:35 +01:00
Olof Kindgren
1e4ea0527e Fix CSR write detection in debug module
Some checks failed
Run compliance test suite / RISC-V Compliance Test (push) Has been cancelled
Formal verification / Run RISCV-formal verification suite (push) Has been cancelled
Run linter / Linter (push) Has been cancelled
Build GDS using OpenLANE and sky130 PDK / build-openlane-sky130 (push) Has been cancelled
2024-11-19 13:20:34 +01:00
Olof Kindgren
e97bdaf7a1 Update cad-suite GH action 2024-11-19 13:20:34 +01:00
cdwijs
8ad248d4e7 typo in README.md
Some checks failed
Run compliance test suite / RISC-V Compliance Test (push) Has been cancelled
Formal verification / Run RISCV-formal verification suite (push) Has been cancelled
Run linter / Linter (push) Has been cancelled
Build GDS using OpenLANE and sky130 PDK / build-openlane-sky130 (push) Has been cancelled
2024-11-11 07:43:01 +01:00
Olof Kindgren
cd60abe837 Fix CSR width issues in debug module
Some checks failed
Run compliance test suite / RISC-V Compliance Test (push) Has been cancelled
Formal verification / Run RISCV-formal verification suite (push) Has been cancelled
Run linter / Linter (push) Has been cancelled
Build GDS using OpenLANE and sky130 PDK / build-openlane-sky130 (push) Has been cancelled
2024-10-17 14:34:08 +02:00
Olof Kindgren
9bf8672fb2 Move RVFI signals into serv_debug 2024-10-17 13:59:23 +02:00
Olof Kindgren
2bcf4104d0 Add debug module 2024-10-13 22:24:00 +02:00
Olof Kindgren
376dcd701d Introduce width parameter
Expose the width (or W internally) parameter to set the SERV
datapath width.

Note: Only width=1 is function at this time.
2024-10-05 23:17:22 +02:00
Olof Kindgren
1c5d44e5c4 Make CSR module 4-bit compatible 2024-10-05 11:27:51 +02:00
Olof Kindgren
4f04e9d933 Use latest regression test suite in CI 2024-10-05 11:27:04 +02:00
Olof Kindgren
7865252e04 Fix CI badge in README 2024-10-02 09:26:00 +02:00
Olof Kindgren
7b1044c01c Fix GDS generation gh action 2024-10-02 08:59:48 +02:00
Olof Kindgren
f001e5b09e Update verilator waiver file 2024-10-01 22:52:58 +02:00
Olof Kindgren
c12fecb4f6 Syntax fix for Servant docs 2024-10-01 21:31:47 +02:00
Olof Kindgren
7065906532 Improve data sheet 2024-10-01 21:31:47 +02:00
Olof Kindgren
24006a7297 Fix symbols and linker script for compliance tests 2024-08-22 22:18:45 +02:00
Olof Kindgren
40d34d204a Avoid matching misa CSR as mstatus
Reads from the (unimplemented) misa register matched mstatus which
in turn caused garbage data to be written to other CSRs. Make the
matching mask slightly stricter to avoid this particular issue.
2024-08-22 22:18:45 +02:00
Olof Kindgren
1fc6e7da9b Set correct value of mstatus[mpp]
The correct value of mstatus[mpp] for machine-mode is 11, but SERV
had this set to 00. Changes in the regression test suite uncovered
this error.
2024-08-22 22:18:45 +02:00
Markus Koch
830d8084b0 Update to Zephyr 3.7.0 2024-08-02 22:42:38 +02:00
Olof Kindgren
28e4704ed8 Lock arch-tests in CI to 3.9 until incompatibility is resolved
riscv-arch-test 3.9.1 fails to run with errors about multiple
definitions of _start. Needs more investigation before updating.
2024-07-06 17:46:04 +02:00
Olof Kindgren
0cf26fe4f9 Prepare for release 2024-07-06 16:54:53 +02:00
Olof Kindgren
344940c655 Update to latest checkout GH action 2024-06-16 09:45:26 +02:00
Olof Kindgren
783e92b576 Add missing descriptions to core description files 2024-06-16 09:45:26 +02:00
Olof Kindgren
472f89d532 servant: Add default program to sim target 2024-06-16 09:45:26 +02:00
Greg Steiert
4d95bd0f21
added support for MAX10 10M08 Evaluation Kit 2024-06-16 09:44:13 +02:00
Olof Kindgren
bbd3920ce8 Add missing Servant ports to doc 2024-06-15 15:30:19 +02:00
Chandler Jearls
8381bcdc4b Adding support for DE1 SoC revF board for servant 2024-06-15 15:15:08 +02:00
Olof Kindgren
3ef80ce4e0 Make servile_mux more simulator-friendly 2024-06-15 15:05:51 +02:00
Nazar Kazakov
d577062672 Add ECP5 evaluation board target
Done by analogy with ulx3s target
2024-06-15 13:04:53 +00:00
BradWalker
3b9331b3b5 Add Servant support for Alchitry Au platform.
fusesoc core show servant
CORE INFO
Name:        ::servant:1.2.1
Description: <No description>
Core root:   fusesoc_libraries/serv
Core file:   servant.core
Targets:
alchitry_au         : Open-hardware Alchitry AU FPGA board

Creating bitstream...
Writing bitstream ./servix.bit...
Writing bitstream ./servix.bin...

Bitstream generation completed
INFO: [Common 17-206] Exiting Vivado at Fri Jun 14 16:49:54 2024...

Resources used
fusesoc run --target=alchitry_au servant --uart_baudrate=57600 --memfile ./fusesoc_libraries/serv/sw/zephyr_hello.hex

+----------------------------+------+-------+------------+-----------+-------+
|          Site Type         | Used | Fixed | Prohibited | Available | Util% |
+----------------------------+------+-------+------------+-----------+-------+
| Slice LUTs                 |  244 |     0 |          0 |     20800 |  1.17 |
|   LUT as Logic             |  243 |     0 |          0 |     20800 |  1.17 |
|   LUT as Memory            |    1 |     0 |          0 |      9600 |  0.01 |
|     LUT as Distributed RAM |    0 |     0 |            |           |       |
|     LUT as Shift Register  |    1 |     0 |            |           |       |
| Slice Registers            |  238 |     0 |          0 |     41600 |  0.57 |
|   Register as Flip Flop    |  238 |     0 |          0 |     41600 |  0.57 |
|   Register as Latch        |    0 |     0 |          0 |     41600 |  0.00 |
| F7 Muxes                   |    3 |     0 |          0 |     16300 |  0.02 |
| F8 Muxes                   |    0 |     0 |          0 |      8150 |  0.00 |
+----------------------------+------+-------+------------+-----------+-------+
2024-06-15 13:03:53 +00:00
Anton Kuzmin
1000ee3495 Add support for GMM-7550 module (Cologne Chip GateMate FPGA) 2024-06-15 12:55:42 +00:00
BradWalker
086090046c Allow the serv software Makefile to use a different compiler prefix if defined. 2024-05-26 06:29:49 +00:00
Nazar Kazakov
a26c2965c0 Fix typos 2024-04-06 16:35:45 +02:00
Olof Kindgren
bebc875353 Make serv_state more simulator-friendly
Refactor the counter generation code to avoid using combinatorial
always statements that rely on an event happening at time 0. This
make serv work with Icarus again.
2024-03-19 12:40:50 +01:00
Olof Kindgren
4537abb965 bufreg refactoring in preparation of qerv integration 2024-03-06 20:22:23 +01:00
Olof Kindgren
b937ef61aa Add width-agnostic serv_rf_ram_if 2024-02-23 14:35:38 +01:00
Olof Kindgren
907db143ea Support w=4 in serv_rf_if 2024-02-22 13:27:04 +01:00
Olof Kindgren
f68a0889aa Support w=4 in serv_ctrl 2024-02-22 13:12:07 +01:00
Olof Kindgren
6659811160 Add width-agnostic serv_csr 2024-02-22 12:29:20 +01:00
Olof Kindgren
923b53ce0b Add hello world ASM example 2024-02-20 09:48:34 +01:00
Olof Kindgren
a92965b359 Add cycle counter to servant testbench 2024-02-20 09:43:33 +01:00
Olof Kindgren
086fff75b6 servile_mux: Only catch writes to sim_sig_adr when signature file is open 2024-02-15 21:39:25 +01:00
Olof Kindgren
88a4711593 Use servile as base for serving 2024-02-14 22:06:57 +01:00
Olof Kindgren
970c6fddca Use Servile as a base for servant 2024-02-14 22:06:57 +01:00
Olof Kindgren
8d91e2d288 Add Servile convenience wrapper
Servile is a new convenience wrapper that implements common common configuration
for SERV-based systems so that they don't have to be repeated in every design.
2024-02-14 22:06:57 +01:00
Olof Kindgren
1dc37d9fd4 Fix path to GDS file in openlane CI runner 2024-02-14 22:06:57 +01:00
Olof Kindgren
51c7833fa8 Refactor docs 2024-02-14 22:06:57 +01:00
Busted Wing
c469c3174b Update README.md to add blinky to pre-built test software examples 2024-02-05 12:41:04 +00:00
inc
c0320fded4 add support for machdyne kolibri 2024-01-26 22:42:02 +00:00
Liam Beguin
40a9e99f77 add PolarFire Splash Kit support
Signed-off-by: Liam Beguin <liambeguin@gmail.com>
2023-12-29 22:39:11 +01:00
Liam Beguin
9d4ebaa358 servant: parameters: specify frequency is to be in MHz
Signed-off-by: Liam Beguin <liambeguin@gmail.com>
2023-12-29 22:39:11 +01:00
Liam Beguin
6e9a6601f3 servant: ice: rename service clock gen source
Make it more explicit that this clock generator is for the ICE FPGA
family.

Signed-off-by: Liam Beguin <liambeguin@gmail.com>
2023-12-29 22:39:11 +01:00
Markus
b2b1110e95
Port to Zephyr v3.5.0 + Fix System Timer (#111)
zephyr: Port to Zephyr v3.5.0
2023-12-11 08:49:08 +00:00
Olof Kindgren
adb3f4d5a4 Delete trailing whitespace from RTL 2023-12-03 18:21:01 +01:00
Olof Kindgren
7cc00c8627 Add build.tools to RTD config 2023-11-17 13:53:31 +01:00
Jani Alinikula
bc984f6639 Change timer wraparound behavior to be more useful 2023-11-17 09:27:52 +01:00
Olof Kindgren
bc74a9a1d7 Used named generate statements
Unnamed generate statements are not recommended and some tools throw
warnings or errors for these.
2023-11-16 21:38:10 +01:00
Katherine Watson
7a6d5d3fc9 Make serv_alu.v synthesizable with Vivado 2023-11-16 14:41:46 +01:00
Olof Kindgren
c7fc57213c Avoid releasing trap signal too early
The trap signal is used my the mux in serv_rf_if to decide which
registers to write to. If the trap signal is dropped too early,
the destination address changes while the register is still being
written to.
2023-10-31 22:21:12 +01:00
uhit332
46a820ee42 support for W=4 2023-10-31 15:53:36 +01:00
uhit332
f9d6b23543 support for W=4 2023-10-31 13:23:17 +01:00
Olof Kindgren
a8fbf688c5 Fix RTD CI action failures 2023-10-31 12:45:31 +01:00
uhit332
2e23b5313a alu with support for W=4 2023-10-31 12:42:52 +01:00
Olof Kindgren
ed4b8198ac Skip disassembly of test cases in riscof plugin
Disassembly takes a lot of time with some toolchains, so leave that
to the user instead.
2023-07-21 12:25:25 +02:00
Olof Kindgren
4567214721 Refactor counter in serv_state 2023-07-13 10:29:19 +02:00
Olof Kindgren
f0f2dba67f Add PC tracing capability
This adds the --trace_pc option to dump the PC after each instruction to a file
called trace.bin
2023-07-12 22:00:36 +02:00
Olof Kindgren
9bb2f95bf4 Tidy up GH Actions naming 2023-07-10 15:07:38 +02:00
Olof Kindgren
c6e5053c78 Clean up RISCOF support structure
The RISCOF regression test suite can now be run from a workspace instead
of having to be run from inside the repo. Also removes the need for a
submodule.
2023-07-10 15:06:13 +02:00
Olof Kindgren
cd3b587364 Add linting for servant and serving to Github actions 2023-06-22 15:49:04 +02:00
Olof Kindgren
8edd456b5d Rewrite serv_rf_ram_if
This adds some optimizations to serv_rf_ram_if. It also adds a read enable
signal and delays writes one cycle which has the added bonus that no reads
or writes happen in the same cycle for RF_WIDTH > 2. This allows SERV to be
used with single-port RAMs in most cases.
2023-06-22 15:48:25 +02:00
Olof Kindgren
a6e4d82a30 Enable support for tickless timer driver 2023-06-21 23:19:26 +02:00
Calder Coalson
1268538f9d Add Arty S7-50 support 2023-06-15 18:17:52 +00:00
Olof Kindgren
d4491f1060 Add initial Serving porting information 2023-06-13 09:30:36 +02:00
Olof Kindgren
6893791d01 Add resource table to README 2023-05-18 22:27:08 +02:00
Olof Kindgren
a03f283d74 Clean up servant core file 2023-05-17 12:20:56 +02:00
Olof Kindgren
1327774f02 Fix CMOD A7 servant target 2023-05-17 10:13:02 +02:00
Olof Kindgren
d7006634cb Remove old LibeCores badge 2023-05-14 22:17:37 +02:00
Olof Kindgren
6d980810f9 Update README 2023-05-11 23:27:25 +02:00
Olof Kindgren
f62062d481 Add readthedocs config file 2023-05-11 22:48:11 +02:00
Olof Kindgren
37724d8d9f Fix Github actions
Repair the lint and CI actions. Add formal verification.
2023-05-07 22:33:49 +02:00
Olof Kindgren
af230d517b Migrate lint, nexys_a7, tinyfpga_bx and verilator_tb targets to flow API 2023-04-30 21:49:42 +02:00
Olof Kindgren
109acd0a53 Prepare for release 2022-12-25 22:04:52 +01:00
Olof Kindgren
5fa5c5c3c3 Sort all sections in servant.core 2022-12-25 21:49:19 +01:00
Olof Kindgren
9be55f5cad Set align parameter to the value of compressed by default 2022-12-25 21:34:48 +01:00
Abdulwadoodd
04991380df GitHub actions for updated Compliance testing 2022-12-25 21:23:51 +01:00
Abdulwadoodd
174330d06e Updated readme and added instructions to run compliance tests 2022-12-25 21:23:51 +01:00
Abdulwadoodd
121099bf54 Add SAIL-RISCV binaries with reamde instructions 2022-12-25 21:23:51 +01:00
Abdulwadoodd
c1a275db49 Added arch-tests as a submodule 2022-12-25 21:23:51 +01:00
Abdulwadoodd
41ae06f6cf Update Compliance testing framework 2022-12-25 21:23:51 +01:00
Abdulwadoodd
1bdd42acb5 Deleted old compliance framework 2022-12-25 21:23:51 +01:00
Olof Kindgren
76a75995b9 Remove RVFI interface from synth wrapper 2022-12-25 20:05:52 +01:00
Olof Kindgren
9c1685e07e Add Servant documentation 2022-12-18 18:09:54 +01:00
Olof Kindgren
7c004e8f7b Add reset input for Arty A7 target 2022-10-16 20:04:56 +02:00
gojimmypi
6ad60f69a2 Add ICE-V Wireless support 2022-10-13 10:23:13 +02:00
Olof Kindgren
7abd9bbbe1 serving: Tie off extension interface 2022-10-13 09:58:21 +02:00
Eric Brombaugh
5cc7b0cbe1 Guarantee at least 2 cycles of o_rst after PLL locked. 2022-08-10 09:02:51 +00:00
Olof Kindgren
cb4276e8b2 Prepare for release 2022-07-26 01:19:28 +02:00
Olof Kindgren
efe8ba832a Set up Github CI testing matrix for compliance tests 2022-07-26 00:28:53 +02:00
Abd
0cce381316 Add Nexys 2 target info 2022-07-26 00:28:53 +02:00
Olof Kindgren
73508bc5de Expose with_csr in servant 2022-07-26 00:28:53 +02:00
Abdulwadoodd
be06cd21c2 Right version of riscv-arch-test 2022-07-26 00:28:53 +02:00
Abd
1beb9d33ec privilege tests fixed for rv32i 2022-07-26 00:28:53 +02:00
Inoki
302224834b Add description for AX309 board target 2022-06-13 12:21:05 +00:00
Inoki
e996b5498a Add Alinx AX309 board as a target
Running at 32MHz with 115200 baud rate UART (using the on-board RS232)
2022-06-13 12:21:05 +00:00
Olof Kindgren
2611c89cbe Update support for compliance tests version 2.7.4 2022-06-13 12:02:27 +00:00
Abd
a8f7de9e8c compressed parameter added for Nexys-2 FPGA target 2022-06-13 10:38:36 +00:00
Abd
82b410f500 Update reamde, comments and paramters 2022-06-13 10:38:11 +00:00
Abd
2655861447 Compressed Extension for SERV 2022-06-01 13:38:24 +02:00
Wadood
4ddd154b24
Nexys 2 Board support
Added nexys 2 target support for servant
2022-05-17 09:11:56 +02:00
Usman
2df592340f
Compliance update
Updated serv to support latest version of riscv-arch-test (v2.6.1)
2022-04-13 07:48:58 +02:00
Olof Kindgren
7d08abb92d Improve makehex.py 2022-03-09 21:00:06 +01:00
Olof Kindgren
2bb988b553 Add reset for mie_mtie 2022-02-09 18:15:08 +01:00
Olof Kindgren
b74344bb48 Store GDS file as artifact after OpenLANE build 2022-01-21 00:11:32 +01:00
somhi
09e49f784a added board_device_index : 2 to chameleon96 for programming 2022-01-13 23:11:12 +01:00
somhi
7624365325
chameleon96 board support added (#74)
* chameleon96 board added
2022-01-11 22:54:45 +01:00
Olof Kindgren
e59fe5346a Refactor docs and add interface documentation 2022-01-03 18:12:48 +01:00
Olof Kindgren
a121b19ec4 Document shift operations 2022-01-02 23:54:13 +01:00
Olof Kindgren
aa8550b937 Remove doc for removed modules 2022-01-02 22:10:33 +01:00
Olof Kindgren
d910becd7f Move dbus_dat/rs2/shamt storage to bufreg2 2022-01-02 22:10:33 +01:00
Olof Kindgren
f04a510393 Remove unused parameter from serv_mem_if 2022-01-01 22:50:28 +01:00
Olof Kindgren
0719381997 Add ViDBo support 2022-01-01 17:15:14 +01:00
Olof Kindgren
0ab7176d3b Fix testbench indentation 2022-01-01 17:15:14 +01:00
Olof Kindgren
7765567cf1 Add missing gate on mem_rd with CSR disabled 2021-12-29 00:17:00 +01:00
Olof Kindgren
28953fec4c Simplify shift_op signal 2021-10-08 22:42:02 +02:00
Olof Kindgren
9c4bdd4bfe Simplify branch_op/slt_op signals 2021-10-08 22:25:24 +02:00
Olof Kindgren
9d3ebf3e96 Replace mem_op with dedicated control signals 2021-10-05 12:52:29 +02:00
Olof Kindgren
e5c6e78820 Simplify MDU logic in serv_mem_if 2021-10-04 23:49:23 +02:00
Olof Kindgren
99f82af6eb Simplify optional MDU logic 2021-10-03 23:28:45 +02:00
Zeeshan Rafique
8843005407
updated vars declaration for modelsim (#63) 2021-10-03 23:15:54 +02:00
Olof Kindgren
48e250ea5e Clean up serv_state interface 2021-10-03 22:48:51 +02:00
Olof Kindgren
b929c31c29 Avoid printing directly in do_uart 2021-09-27 22:10:19 +02:00
Olof Kindgren
b15b5ed652 Add Github action to build with openlane+sky130 2021-08-30 22:13:42 +02:00
Klas Nordmark
52d0bf0938 Added openlane target and params.tcl with suitable openlane parameters for SERV 2021-08-30 22:13:42 +02:00
Olof Kindgren
2989051f44 Avoid enabling bufreg before instruction is decoded 2021-08-27 13:10:06 +02:00
Olof Kindgren
3971ca942e Fix up RVFI 2021-08-27 13:10:06 +02:00
Olof Kindgren
64f5ca0b7f Add missing reset on cnt_done 2021-08-27 13:10:06 +02:00
Olof Kindgren
781c07b7dc Properly reset stage_two_req signal 2021-08-27 13:10:06 +02:00
Olof Kindgren
b10a871499 Fix signedness bug on immediates
The sign bit on immediates relied on the value of csr_imm_en from
the previous instruction. This fixes by gating with csr_imm_en
after it has been latched instead of before
2021-08-27 13:10:06 +02:00
Olof Kindgren
d2a4243033 Add reset for new_irq register 2021-08-27 13:10:06 +02:00
Olof Kindgren
621baeff31 Always return 0 from reads to reg x0 in serv_rf_ram 2021-08-27 13:10:06 +02:00
Zeeshan Rafique
6e802cb9bc M-extension support for SERV
* modified serv(ant) for MDU
* added dependency for mdu
* M-extension for SERV
* Updated README for running RV32IM compliance tests
* waive some lint warnings related to mdu
* added mdu param for arty_a7_35t
2021-08-20 23:45:19 +02:00
Dhiru Kholia
dbe5236b4c Add support for EBAZ4205 'Development' Board
References:

- https://github.com/fusesoc/blinky/pull/68/files (EBAZ4205 blinky)
- https://github.com/fusesoc/blinky#ebaz4205-development-board
- Existing 'arty_a7_35t' example

This PR also cleans up a bunch of whitespace issues (no functional
change).
2021-08-15 19:46:37 +02:00
hakan-demirli
b845507e32 Update serv_timer.c
Fix a typo 'fro'
2021-08-10 09:38:21 +02:00
157 changed files with 6766 additions and 4124 deletions

View file

@ -1,46 +1,52 @@
name: CI
name: Run compliance test suite
on: [push, pull_request]
jobs:
compliance:
name: RISC-V Compliance Test
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
path: serv
- name: install fusesoc, verilator and gcc
- name: install fusesoc, verilator, gcc and riscof
run: |
sudo apt-get install -y python3-setuptools verilator gcc-riscv64-unknown-elf
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 SERV directory
run: echo "SERV=$GITHUB_WORKSPACE/serv" >> $GITHUB_ENV
- name: set root and SERV directory
run: |
echo "SERV=$GITHUB_WORKSPACE/serv" >> $GITHUB_ENV
- name: setup workspace
run: fusesoc library add serv $SERV
- name: build servant
run: fusesoc run --target=verilator_tb --build --build-root=servant_x servant
- name: download risc-v compliance
run: git clone https://github.com/riscv/riscv-compliance --branch 1.0
- name: run RV32i compliance tests
- name: Setup SAIL-RISCV Model
run: |
cd $GITHUB_WORKSPACE/riscv-compliance
make TARGETDIR=$SERV/riscv-target RISCV_TARGET=serv RISCV_DEVICE=rv32i RISCV_ISA=rv32i TARGET_SIM=$GITHUB_WORKSPACE/servant_x/verilator_tb-verilator/Vservant_sim
tar -xzf $SERV/verif/bin/sail-riscv.tar.gz
echo $GITHUB_WORKSPACE/sail-riscv >> $GITHUB_PATH
- name: run RV32Zicsr compliance tests
run: |
cd $GITHUB_WORKSPACE/riscv-compliance
make TARGETDIR=$SERV/riscv-target RISCV_TARGET=serv RISCV_DEVICE=rv32i RISCV_ISA=rv32Zicsr TARGET_SIM=$GITHUB_WORKSPACE/servant_x/verilator_tb-verilator/Vservant_sim
- name: Init arch tests
run: riscof arch-test --clone
- name: run RV32Zifencei compliance tests
run: |
cd $GITHUB_WORKSPACE/riscv-compliance
make TARGETDIR=$SERV/riscv-target RISCV_TARGET=serv RISCV_DEVICE=rv32i RISCV_ISA=rv32Zifencei TARGET_SIM=$GITHUB_WORKSPACE/servant_x/verilator_tb-verilator/Vservant_sim
- 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

67
.github/workflows/formal.yml vendored Normal file
View file

@ -0,0 +1,67 @@
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,16 +1,22 @@
name: Run linter
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
name: Linter
env:
REPO : serv
VLNV : serv
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Lint Verilog source files with Verilator
uses: librecores/ci-fusesoc-action@main
- name: Checkout repo
uses: actions/checkout@v4
with:
command: 'run'
core: 'serv'
target: 'lint'
tool: 'verilator'
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

24
.github/workflows/openlane.yml vendored Normal file
View file

@ -0,0 +1,24 @@
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,3 +1,5 @@
name: Build documentation
on:
push:
branches:
@ -9,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v1
- name: Install dependencies

16
.readthedocs.yml Normal file
View file

@ -0,0 +1,16 @@
# 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 Normal file
View file

@ -0,0 +1,38 @@
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

216
README.md
View file

@ -2,53 +2,103 @@
# SERV
[![LibreCores](https://www.librecores.org/olofk/serv/badge.svg?style=flat)](https://www.librecores.org/olofk/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)
[![CI status](https://github.com/olofk/serv/workflows/CI/badge.svg)](https://github.com/olofk/serv/actions?query=workflow%3ACI)
[![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.
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.
| Lattice iCE40 | Intel Cyclone 10LP | AMD Artix-7 | CMOS |
| ------------- | ------------------ | ----------- | ------ |
| 198 LUT | 239 LUT | 125 LUT | 2.1kGE |
| 164 FF | 164 FF | 164 FF | |
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)
There's also an official [SERV user manual](https://serv.readthedocs.io/en/latest/#) with fancy block diagrams, timing diagrams and an in-depth description of how some things work.
All SERV videos and more can also be found [here](https://www.award-winning.me/videos/).
## Prerequisites
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.
Create a directory to keep all the different parts of the project together. We
will refer to this directory as `$WORKSPACE` from now on. All commands will be run from this directory unless otherwise stated.
## Systems using SERV
Install FuseSoC
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:
`pip install 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.
Add the FuseSoC standard library
[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.
`fusesoc library add fusesoc_cores https://github.com/fusesoc/fusesoc-cores`
[Observer](https://github.com/olofk/observer) is a configurable and software-programmable sensor aggregation platform for heterogeneous sensors.
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
[Subservient](https://github.com/olofk/subservient/) is a small technology-independent SERV-based SoC intended for ASIC implementations together with a single-port SRAM.
`fusesoc library add serv https://github.com/olofk/serv`
[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.
The SERV repo will now be available in $WORKSPACE/fusesoc_libraries/serv. To save some typing, we will refer to that directory as `$SERV`.
We are now ready to do our first exercises with SERV
If [Verilator](https://www.veripool.org/wiki/verilator) is installed, we can use that as a linter to check the SERV source code
`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.1.0
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
Build and run the single threaded zephyr hello world example with verilator (should be stopped with Ctrl-C):
@ -72,130 +122,19 @@ 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_sync.hex --memsize=16384
Other applications can be tested by compiling and converting to bin and then hex e.g. with makehex.py found in `$SERV/sw`
...or... the blinky example (note that the ```uart_baudrate``` should not be defined for the blinky test)
## Run RISC-V compliance tests
fusesoc run --target=verilator_tb servant --firmware=$SERV/sw/blinky.hex --memsize=16384
**Note:** The following instructions are valid for version 1.0 of the RISC-V compliance tests. The target-specific support for SERV has not yet been ported to newer versions.
Build the verilator model (if not already done)
fusesoc run --target=verilator_tb --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.
git clone https://github.com/riscv/riscv-compliance --branch 1.0
## 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.
To run the RISC-V compliance tests, we need to supply the SERV-specific support files and point the test suite to where it can find a target to run (i.e. the previously built Verilator model)
Run the compliance tests
cd riscv-compliance && make TARGETDIR=$SERV/riscv-target RISCV_TARGET=serv RISCV_DEVICE=rv32i RISCV_ISA=rv32i TARGET_SIM=$WORKSPACE/build/servant_1.1.0/verilator_tb-verilator/Vservant_sim
The above will run all tests in the rv32i test suite. Since SERV also implement the `rv32Zicsr` and `rv32Zifencei` extensions, these can also be tested by choosing any of them instead of rv32i as the `RISCV_ISA` variable.
## Run on hardware
The servant SoC has been ported to an increasing number of different FPGA boards. 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.
### 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
### Icebreaker
Pin 9 is used for UART output with 57600 baud rate.
fusesoc run --target=icebreaker servant
### 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
### 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.1.0/orangecrab_r0.2-trellis/servant_1.1.0.bit
### 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
### 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
### 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
### 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
### 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
### 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
### 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.1.0/icestick-icestorm/servant_1.1.0.bin
### 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.1.0/go_board-icestorm/servant_1.1.0.bin
## Other targets
@ -219,7 +158,7 @@ This will synthesize for the default Vivado part. To synthesise for a specific d
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 too must be installed. The [Zephyr getting started guide](https://docs.zephyrproject.org/latest/getting_started/index.html) describes these steps in more detail.
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.
@ -259,8 +198,3 @@ 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
- Make it faster and smaller

View file

@ -1,12 +1,17 @@
`default_nettype none
module servant_sim
(input wire wb_clk,
input wire wb_rst,
output wire q);
(input wire wb_clk,
input wire wb_rst,
output wire [31:0] pc_adr,
output wire pc_vld,
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
@ -18,8 +23,15 @@ module servant_sim
servant
#(.memfile (memfile),
.memsize (memsize),
.width (width),
.debug (1'b1),
.sim (1),
.with_csr (with_csr))
.with_csr (with_csr),
.compress (compressed[0:0]),
.align (align[0:0]))
dut(wb_clk, wb_rst, q);
assign pc_adr = dut.wb_mem_adr;
assign pc_vld = dut.wb_mem_ack;
endmodule

View file

@ -1,9 +1,12 @@
#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;
@ -19,8 +22,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 {
@ -46,7 +49,7 @@ void uart_init(uart_context_t *context, uint32_t baud_rate) {
context->state = 0;
}
void do_uart(uart_context_t *context, bool rx) {
bool do_uart(uart_context_t *context, bool rx) {
if (context->state == 0) {
if (rx)
context->state++;
@ -74,80 +77,104 @@ void 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)
{
vluint64_t sample_time = 0;
uint32_t insn = 0;
uint32_t ex_pc = 0;
int baud_rate = 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);
vluint64_t timeout = 0;
const char *arg_timeout = Verilated::commandArgsPlusMatch("timeout=");
if (arg_timeout[0])
timeout = atoi(arg_timeout+9);
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 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 timeout = 0;
const char *arg_timeout = Verilated::commandArgsPlusMatch("timeout=");
if (arg_timeout[0])
timeout = atoi(arg_timeout+9);
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);
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);
if (timeout && (main_time >= timeout)) {
printf("Timeout: Exiting at time %lu\n", main_time);
done = true;
}
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);
top->wb_clk = !top->wb_clk;
main_time+=31.25;
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;
}
}
if (tfp)
tfp->close();
exit(0);
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);
}

View file

@ -1,10 +1,15 @@
`default_nettype none
module servant_tb;
parameter memfile = "";
parameter memfile = "hello_uart.hex";
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;
@ -15,12 +20,18 @@ module servant_tb;
vlog_tb_utils vtu();
uart_decoder #(57600) uart_decoder (q);
uart_decoder #(baud_rate) uart_decoder (q);
servant_sim
#(.memfile (memfile),
.memsize (memsize),
.width (width),
.with_csr (with_csr))
dut(wb_clk, wb_rst, q);
dut
(.wb_clk (wb_clk),
.wb_rst (wb_rst),
.pc_adr (),
.pc_vld (),
.q (q));
endmodule

163
bench/servant_tb_vidbo.cpp Normal file
View file

@ -0,0 +1,163 @@
#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);
}

16
data/alchitry_au.xdc Normal file
View file

@ -0,0 +1,16 @@
## 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 +1,7 @@
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]

10
data/arty_s7_50t.xdc Normal file
View file

@ -0,0 +1,10 @@
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];

9
data/ax309.ucf Normal file
View file

@ -0,0 +1,9 @@
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;

28
data/chameleon96/CV_96.v Normal file
View file

@ -0,0 +1,28 @@
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

108
data/chameleon96/HPS.sv Normal file
View file

@ -0,0 +1,108 @@
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

@ -0,0 +1,15 @@
/* 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

@ -0,0 +1,8 @@
# 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

20
data/chameleon96/pinmap.tcl Executable file
View file

@ -0,0 +1,20 @@
#
# 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

@ -7,8 +7,10 @@ set_property -dict { PACKAGE_PIN A17 IOSTANDARD LVCMOS33 } [get_ports { q }];
#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 { q }]; #IO_L7N_T1_D10_14 Sch=uart_rxd_out
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]

8
data/de1_soc_revF.sdc Normal file
View file

@ -0,0 +1,8 @@
# 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

11
data/de1_soc_revF.tcl Normal file
View file

@ -0,0 +1,11 @@
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*

10
data/ebaz4205.xdc Normal file
View file

@ -0,0 +1,10 @@
## 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];

16
data/ecp5_evn.lpf Normal file
View file

@ -0,0 +1,16 @@
# 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;

65
data/gmm7550.ccf Normal file
View file

@ -0,0 +1,65 @@
## 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";

7
data/icev_wireless.pcf Normal file
View file

@ -0,0 +1,7 @@
# 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

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

8
data/max10_10m08evk.sdc Normal file
View file

@ -0,0 +1,8 @@
# 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

24
data/max10_10m08evk.tcl Normal file
View file

@ -0,0 +1,24 @@
#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

1
data/nexys_2.tcl Normal file
View file

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

8
data/nexys_2.ucf Normal file
View file

@ -0,0 +1,8 @@
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 ;

6
data/params.tcl Normal file
View file

@ -0,0 +1,6 @@
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

@ -0,0 +1,11 @@
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

22
data/te0802.xdc Normal file
View file

@ -0,0 +1,22 @@
## 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,7 +1,12 @@
`verilator_config
// Bits [1:0] in i_ibus_rdt are not used at all
lint_off -rule UNUSED -file "*/serv_top.v" -lines 52
// 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 7
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

6
doc/datasheet.rst Normal file
View file

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

View file

@ -1,381 +1,13 @@
.. SERV documentation master file, created by
sphinx-quickstart on Mon Feb 24 00:01:33 2020.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
################
SERV user manual
================
################
Welcome to the user manual of the award-winning SERV, the world's smallest RISC-V CPU.
.. toctree::
:maxdepth: 2
:maxdepth: 4
:caption: Contents:
Modules
-------
SERV is a bit-serial CPU which means that the internal datapath is one bit wide. :ref:`dataflow` show 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 comparisions 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 adresses. 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_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 parellel 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 prepares the data to be sent out on the dbus during store operations and serializes the incoming data during loads
The memory interface is centered around four byte-wide shift registers connected in series. During store operations, the `dat_en` signal is asserted long enough to shift in the data from rs2 to the right place in the shift registers and the parallel output of the shift registers is then presented to the data bus as a 32-bit word together with a byte mask. 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 latched into the shift registers. `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.
The shift register used for stores and loads are also used to keep track of the number of steps to shift for left/right shift operations. In this mode, the six LSB of the register is loaded with the shift amount during the init stage and the used as a down counter which raises o_sh_done and o_sh_done_r when the number of shift steps have been completed.
.. 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.
shift_reg
^^^^^^^^^
shift_reg is a shift register implementation used in various places in SERV
serv_shift
^^^^^^^^^^
serv_shift lives inside the ALU and contains the control logic for shift operations
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 immediate and the rs1/rs2 registers and potential results are written to PC and rd in the second stage. Various things happen between the stages depending on the type of operation. SERV has 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
++++++
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_mem_if.
.. 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"]
}
datasheet.rst
internals.rst
reservoir.rst

222
doc/interface.rst Normal file
View file

@ -0,0 +1,222 @@
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"]
}

5
doc/internals.rst Normal file
View file

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

417
doc/modules.rst Normal file
View file

@ -0,0 +1,417 @@
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"]
}

42
doc/overview.rst Normal file
View file

@ -0,0 +1,42 @@
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

12
doc/reservoir.rst Normal file
View file

@ -0,0 +1,12 @@
**************************
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

BIN
doc/serv_bufreg2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
doc/serv_bufreg2_int.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

BIN
doc/serv_top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
doc/servant.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

293
doc/servant.rst Normal file
View file

@ -0,0 +1,293 @@
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

BIN
doc/servile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

166
doc/servile.rst Normal file
View file

@ -0,0 +1,166 @@
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

BIN
doc/servile_int.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
doc/serving.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

94
doc/serving.rst Normal file
View file

@ -0,0 +1,94 @@
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

BIN
doc/subservient.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

10
doc/subservient.rst Normal file
View file

@ -0,0 +1,10 @@
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.

View file

@ -1,36 +0,0 @@
// RISC-V Compliance IO Test Header File
/*
* Copyright (c) 2005-2018 Imperas Software Ltd., www.imperas.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef _COMPLIANCE_IO_H
#define _COMPLIANCE_IO_H
//-----------------------------------------------------------------------
// RV IO Macros (Non functional)
//-----------------------------------------------------------------------
#define RVTEST_IO_INIT
#define RVTEST_IO_WRITE_STR(_SP, _STR)
#define RVTEST_IO_CHECK()
#define RVTEST_IO_ASSERT_GPR_EQ(_SP, _R, _I)
#define RVTEST_IO_ASSERT_SFPR_EQ(_F, _R, _I)
#define RVTEST_IO_ASSERT_DFPR_EQ(_D, _R, _I)
#endif // _COMPLIANCE_IO_H

View file

@ -1,67 +0,0 @@
// RISC-V Compliance Test Header File
// Copyright (c) 2017, Codasip Ltd. All Rights Reserved.
// See LICENSE for license details.
//
// Description: Common header file for RV32I tests
#ifndef _COMPLIANCE_TEST_H
#define _COMPLIANCE_TEST_H
//-----------------------------------------------------------------------
// RV Compliance Macros
//-----------------------------------------------------------------------
#define RV_COMPLIANCE_HALT \
la a0, data_begin; \
la a1, data_end; \
li a2, 0x80000000; \
complience_halt_loop: \
beq a0, a1, complience_halt_break; \
addi a3, a0, 4; \
complience_halt_loop2: \
addi a3, a3, -1; \
\
lb a4, 0 (a3); \
srai a5, a4, 4; \
andi a5, a5, 0xF; \
li a6, 10; \
blt a5, a6, notLetter; \
addi a5, a5, 39; \
notLetter: \
addi a5, a5, 0x30; \
sw a5, 0 (a2); \
\
srai a5, a4, 0; \
andi a5, a5, 0xF; \
li a6, 10; \
blt a5, a6, notLetter2; \
addi a5, a5, 39; \
notLetter2: \
addi a5, a5, 0x30; \
sw a5, 0 (a2); \
bne a0, a3,complience_halt_loop2; \
addi a0, a0, 4; \
\
li a4, '\n'; \
sw a4, 0 (a2); \
j complience_halt_loop; \
j complience_halt_break; \
complience_halt_break:; \
lui a0,0x90000000>>12; \
sw a3,0(a0);
#define RV_COMPLIANCE_RV32M
#define RV_COMPLIANCE_CODE_BEGIN \
.section .text.init; \
.align 4; \
.globl _start; \
_start: \
#define RV_COMPLIANCE_CODE_END
#define RV_COMPLIANCE_DATA_BEGIN .align 4; .global data_begin; data_begin:
#define RV_COMPLIANCE_DATA_END .align 4; .global data_end; data_end:
#endif

View file

@ -1,26 +0,0 @@
TARGET_SIM ?= server
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RUN_TARGET=\
$(TARGET_SIM) \
+timeout=100000000 \
+signature=$(*).signature.output \
+firmware=$(<).hex 2> $@
RISCV_PREFIX ?= riscv32-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJCOPY ?= $(RISCV_PREFIX)objcopy
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
COMPILE_TARGET=\
$$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld $$< \
-o $$@; \
$$(RISCV_OBJCOPY) -O binary $$@ $$@.bin; \
$$(RISCV_OBJDUMP) -D $$@ > $$@.objdump; \
python3 $(TARGETDIR)/$(RISCV_TARGET)/makehex.py $$@.bin 2048 > $$@.hex;

View file

@ -1,27 +0,0 @@
#!/usr/bin/env python3
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
from sys import argv
binfile = argv[1]
nwords = int(argv[2])
with open(binfile, "rb") as f:
bindata = f.read()
assert len(bindata) < 4*nwords
assert len(bindata) % 4 == 0
for i in range(nwords):
if i < len(bindata) // 4:
w = bindata[4*i : 4*i+4]
print("%02x%02x%02x%02x" % (w[3], w[2], w[1], w[0]))
else:
print("0")

67
rtl/serv_aligner.v Normal file
View file

@ -0,0 +1,67 @@
module serv_aligner
(
input wire clk,
input wire rst,
// serv_top
input wire [31:0] i_ibus_adr,
input wire i_ibus_cyc,
output wire [31:0] o_ibus_rdt,
output wire o_ibus_ack,
// serv_rf_top
output wire [31:0] o_wb_ibus_adr,
output wire o_wb_ibus_cyc,
input wire [31:0] i_wb_ibus_rdt,
input wire i_wb_ibus_ack);
wire [31:0] ibus_rdt_concat;
wire ack_en;
reg [15:0] lower_hw;
reg ctrl_misal ;
/* From SERV core to Memory
o_wb_ibus_adr: Carries address of instruction to memory. In case of misaligned access,
which is caused by pc+2 due to compressed instruction, next instruction is fetched
by pc+4 and concatenation is done to make the instruction aligned.
o_wb_ibus_cyc: Simply forwarded from SERV to Memory and is only altered by memory or SERV core.
*/
assign o_wb_ibus_adr = ctrl_misal ? (i_ibus_adr+32'b100) : i_ibus_adr;
assign o_wb_ibus_cyc = i_ibus_cyc;
/* From Memory to SERV core
o_ibus_ack: Instruction bus acknowledge is send to SERV only when the aligned instruction,
either compressed or un-compressed, is ready to dispatch.
o_ibus_rdt: Carries the instruction from memory to SERV core. It can be either aligned
instruction coming from memory or made aligned by two bus transactions and concatenation.
*/
assign o_ibus_ack = i_wb_ibus_ack & ack_en;
assign o_ibus_rdt = ctrl_misal ? ibus_rdt_concat : i_wb_ibus_rdt;
/* 16-bit register used to hold the upper half word of the current instruction in-case
concatenation will be required with the upper half word of upcoming instruction
*/
always @(posedge clk) begin
if(i_wb_ibus_ack)begin
lower_hw <= i_wb_ibus_rdt[31:16];
end
end
assign ibus_rdt_concat = {i_wb_ibus_rdt[15:0],lower_hw};
/* Two control signals: ack_en, ctrl_misal are set to control the bus transactions between
SERV core and the memory
*/
assign ack_en = !(i_ibus_adr[1] & !ctrl_misal);
always @(posedge clk ) begin
if(rst)
ctrl_misal <= 0;
else if(i_wb_ibus_ack & i_ibus_adr[1])
ctrl_misal <= !ctrl_misal;
end
endmodule

View file

@ -1,5 +1,9 @@
`default_nettype none
module serv_alu
#(
parameter W = 1,
parameter B = W-1
)
(
input wire clk,
//State
@ -13,29 +17,30 @@ module serv_alu
input wire i_cmp_sig,
input wire [2:0] i_rd_sel,
//Data
input wire i_rs1,
input wire i_op_b,
input wire i_buf,
output wire o_rd);
input wire [B:0] i_rs1,
input wire [B:0] i_op_b,
input wire [B:0] i_buf,
output wire [B:0] o_rd);
wire result_add;
wire [B:0] result_add;
wire [B:0] result_slt;
reg cmp_r;
wire add_cy;
reg add_cy_r;
reg [B:0] add_cy_r;
//Sign-extended operands
wire rs1_sx = i_rs1 & i_cmp_sig;
wire op_b_sx = i_op_b & i_cmp_sig;
wire rs1_sx = i_rs1[B] & i_cmp_sig;
wire op_b_sx = i_op_b[B] & i_cmp_sig;
wire add_b = i_op_b^i_sub;
wire [B:0] add_b = i_op_b^{W{i_sub}};
assign {add_cy,result_add} = i_rs1+add_b+add_cy_r;
wire result_lt = rs1_sx + ~op_b_sx + add_cy;
wire result_eq = !result_add & (cmp_r | i_cnt0);
wire result_eq = !(|result_add) & (cmp_r | i_cnt0);
assign o_cmp = i_cmp_eq ? result_eq : result_lt;
@ -51,15 +56,23 @@ module serv_alu
i_bool_op will be 01 during shift operations, so by outputting zero under
this condition we can safely or result_bool with i_buf
*/
wire result_bool = ((i_rs1 ^ i_op_b) & ~ i_bool_op[0]) | (i_bool_op[1] & i_op_b & i_rs1);
wire [B:0] result_bool = ((i_rs1 ^ i_op_b) & ~{W{i_bool_op[0]}}) | ({W{i_bool_op[1]}} & i_op_b & i_rs1);
assign result_slt[0] = cmp_r & i_cnt0;
generate
if (W>1) begin : gen_w_gt_1
assign result_slt[B:1] = {B{1'b0}};
end
endgenerate
assign o_rd = i_buf |
(i_rd_sel[0] & result_add) |
(i_rd_sel[1] & cmp_r & i_cnt0) |
(i_rd_sel[2] & result_bool);
({W{i_rd_sel[0]}} & result_add) |
({W{i_rd_sel[1]}} & result_slt) |
({W{i_rd_sel[2]}} & result_bool);
always @(posedge clk) begin
add_cy_r <= i_en ? add_cy : i_sub;
add_cy_r <= {W{1'b0}};
add_cy_r[0] <= i_en ? add_cy : i_sub;
if (i_en)
cmp_r <= o_cmp;

View file

@ -1,45 +1,65 @@
module serv_bufreg
(
module serv_bufreg #(
parameter [0:0] MDU = 0,
parameter W = 1,
parameter B = W-1
)(
input wire i_clk,
//State
input wire i_cnt0,
input wire i_cnt1,
input wire i_en,
input wire i_init,
output reg [1:0] o_lsb,
input wire i_mdu_op,
output wire [1:0] o_lsb,
//Control
input wire i_rs1_en,
input wire i_imm_en,
input wire i_clr_lsb,
input wire i_sh_signed,
input wire i_sh_signed,
//Data
input wire i_rs1,
input wire i_imm,
output wire o_q,
input wire [B:0] i_rs1,
input wire [B:0] i_imm,
output wire [B:0] o_q,
//External
output wire [31:0] o_dbus_adr);
output wire [31:0] o_dbus_adr,
//Extension
output wire [31:0] o_ext_rs1);
wire c, q;
reg c_r;
reg [31:2] data;
wire c;
wire [B:0] q;
reg [B:0] c_r;
reg [31:0] data;
wire [B:0] clr_lsb;
wire clr_lsb = i_cnt0 & i_clr_lsb;
assign clr_lsb[0] = i_cnt0 & i_clr_lsb;
assign {c,q} = {1'b0,(i_rs1 & i_rs1_en)} + {1'b0,(i_imm & i_imm_en & !clr_lsb)} + c_r;
assign {c,q} = {1'b0,(i_rs1 & {W{i_rs1_en}})} + {1'b0,(i_imm & {W{i_imm_en}} & ~clr_lsb)} + c_r;
always @(posedge i_clk) begin
//Make sure carry is cleared before loading new data
c_r <= c & i_en;
if (i_en)
data <= {i_init ? q : (data[31] & i_sh_signed), data[31:3]};
if (i_init ? (i_cnt0 | i_cnt1) : i_en)
o_lsb <= {i_init ? q : data[2],o_lsb[1]};
c_r <= {W{1'b0}};
c_r[0] <= c & i_en;
end
assign o_q = o_lsb[0] & i_en;
assign o_dbus_adr = {data, 2'b00};
reg [1:0] lsb;
generate
if (W == 1) begin : gen_w_eq_1
always @(posedge i_clk) begin
if (i_en)
data[31:2] <= {i_init ? q : {W{data[31] & i_sh_signed}}, data[31:3]};
if (i_init ? (i_cnt0 | i_cnt1) : i_en)
data[1:0] <= {i_init ? q : data[2], data[1]};
end
always @(*) lsb = data[1:0];
assign o_q = data[0] & {W{i_en}};
end
endgenerate
assign o_dbus_adr = {data[31:2], 2'b00};
assign o_ext_rs1 = data;
assign o_lsb = (MDU & i_mdu_op) ? 2'b00 : lsb;
endmodule

82
rtl/serv_bufreg2.v Normal file
View file

@ -0,0 +1,82 @@
module serv_bufreg2
(
input wire i_clk,
//State
input wire i_en,
input wire i_init,
input wire i_cnt7,
input wire i_cnt_done,
input wire i_sh_right,
input wire [1:0] i_lsb,
input wire [1:0] i_bytecnt,
output wire o_sh_done,
//Control
input wire i_op_b_sel,
input wire i_shift_op,
//Data
input wire i_rs2,
input wire i_imm,
output wire o_op_b,
output wire o_q,
//External
output wire [31:0] o_dat,
input wire i_load,
input wire [31:0] i_dat);
reg [31:0] dat;
/*
Before a store operation, the data to be written needs to be shifted into
place. Depending on the address alignment, we need to shift different
amounts. One formula for calculating this is to say that we shift when
i_lsb + i_bytecnt < 4. Unfortunately, the synthesis tools don't seem to be
clever enough so the hideous expression below is used to achieve the same
thing in a more optimal way.
*/
wire byte_valid
= (!i_lsb[0] & !i_lsb[1]) |
(!i_bytecnt[0] & !i_bytecnt[1]) |
(!i_bytecnt[1] & !i_lsb[1]) |
(!i_bytecnt[1] & !i_lsb[0]) |
(!i_bytecnt[0] & !i_lsb[1]);
assign o_op_b = i_op_b_sel ? i_rs2 : i_imm;
wire shift_en = i_shift_op ? (i_en & i_init & (i_bytecnt == 2'b00)) : (i_en & byte_valid);
wire cnt_en = (i_shift_op & (!i_init | (i_cnt_done & i_sh_right)));
/* The dat register has three different use cases for store, load and
shift operations.
store : Data to be written is shifted to the correct position in dat during
init by shift_en and is presented on the data bus as o_wb_dat
load : Data from the bus gets latched into dat during i_wb_ack and is then
shifted out at the appropriate time to end up in the correct
position in rd
shift : Data is shifted in during init. After that, the six LSB are used as
a downcounter (with bit 5 initially set to 0) that trigger
o_sh_done when they wrap around to indicate that
the requested number of shifts have been performed
*/
wire [5:0] dat_shamt = cnt_en ?
//Down counter mode
dat[29:24]-1 :
//Shift reg mode with optional clearing of bit 5
{dat[30] & !(i_shift_op & i_cnt7),dat[29:25]};
assign o_sh_done = dat_shamt[5];
assign o_q =
((i_lsb == 2'd3) & dat[24]) |
((i_lsb == 2'd2) & dat[16]) |
((i_lsb == 2'd1) & dat[8]) |
((i_lsb == 2'd0) & dat[0]);
assign o_dat = dat;
always @(posedge i_clk) begin
if (shift_en | cnt_en | i_load)
dat <= i_load ? i_dat : {o_op_b, dat[31], dat_shamt, dat[24:1]};
end
endmodule

232
rtl/serv_compdec.v Normal file
View file

@ -0,0 +1,232 @@
/* Copyright lowRISC contributors.
Copyright 2018 ETH Zurich and University of Bologna, see also CREDITS.md.
Licensed under the Apache License, Version 2.0, see LICENSE for details.
SPDX-License-Identifier: Apache-2.0
* Adapted to SERV by @Abdulwadoodd as part of the project under spring '22 LFX Mentorship program */
/* Decodes RISC-V compressed instructions into their RV32i equivalent. */
module serv_compdec
(
input wire i_clk,
input wire [31:0] i_instr,
input wire i_ack,
output wire [31:0] o_instr,
output reg o_iscomp);
localparam OPCODE_LOAD = 7'h03;
localparam OPCODE_OP_IMM = 7'h13;
localparam OPCODE_STORE = 7'h23;
localparam OPCODE_OP = 7'h33;
localparam OPCODE_LUI = 7'h37;
localparam OPCODE_BRANCH = 7'h63;
localparam OPCODE_JALR = 7'h67;
localparam OPCODE_JAL = 7'h6f;
reg [31:0] comp_instr;
reg illegal_instr;
assign o_instr = illegal_instr ? i_instr : comp_instr;
always @(posedge i_clk) begin
if(i_ack)
o_iscomp <= !illegal_instr;
end
always @ (*) begin
// By default, forward incoming instruction, mark it as legal.
comp_instr = i_instr;
illegal_instr = 1'b0;
// Check if incoming instruction is compressed.
case (i_instr[1:0])
// C0
2'b00: begin
case (i_instr[15:14])
2'b00: begin
// c.addi4spn -> addi rd', x2, imm
comp_instr = {2'b0, i_instr[10:7], i_instr[12:11], i_instr[5],
i_instr[6], 2'b00, 5'h02, 3'b000, 2'b01, i_instr[4:2], {OPCODE_OP_IMM}};
end
2'b01: begin
// c.lw -> lw rd', imm(rs1')
comp_instr = {5'b0, i_instr[5], i_instr[12:10], i_instr[6],
2'b00, 2'b01, i_instr[9:7], 3'b010, 2'b01, i_instr[4:2], {OPCODE_LOAD}};
end
2'b11: begin
// c.sw -> sw rs2', imm(rs1')
comp_instr = {5'b0, i_instr[5], i_instr[12], 2'b01, i_instr[4:2],
2'b01, i_instr[9:7], 3'b010, i_instr[11:10], i_instr[6],
2'b00, {OPCODE_STORE}};
end
2'b10: begin
illegal_instr = 1'b1;
end
endcase
end
// C1
// Register address checks for RV32E are performed in the regular instruction decoder.
// If this check fails, an illegal instruction exception is triggered and the controller
// writes the actual faulting instruction to mtval.
2'b01: begin
case (i_instr[15:13])
3'b000: begin
// c.addi -> addi rd, rd, nzimm
// c.nop
comp_instr = {{6 {i_instr[12]}}, i_instr[12], i_instr[6:2],
i_instr[11:7], 3'b0, i_instr[11:7], {OPCODE_OP_IMM}};
end
3'b001, 3'b101: begin
// 001: c.jal -> jal x1, imm
// 101: c.j -> jal x0, imm
comp_instr = {i_instr[12], i_instr[8], i_instr[10:9], i_instr[6],
i_instr[7], i_instr[2], i_instr[11], i_instr[5:3],
{9 {i_instr[12]}}, 4'b0, ~i_instr[15], {OPCODE_JAL}};
end
3'b010: begin
// c.li -> addi rd, x0, nzimm
// (c.li hints are translated into an addi hint)
comp_instr = {{6 {i_instr[12]}}, i_instr[12], i_instr[6:2], 5'b0,
3'b0, i_instr[11:7], {OPCODE_OP_IMM}};
end
3'b011: begin
// c.lui -> lui rd, imm
// (c.lui hints are translated into a lui hint)
comp_instr = {{15 {i_instr[12]}}, i_instr[6:2], i_instr[11:7], {OPCODE_LUI}};
if (i_instr[11:7] == 5'h02) begin
// c.addi16sp -> addi x2, x2, nzimm
comp_instr = {{3 {i_instr[12]}}, i_instr[4:3], i_instr[5], i_instr[2],
i_instr[6], 4'b0, 5'h02, 3'b000, 5'h02, {OPCODE_OP_IMM}};
end
end
3'b100: begin
case (i_instr[11:10])
2'b00,
2'b01: begin
// 00: c.srli -> srli rd, rd, shamt
// 01: c.srai -> srai rd, rd, shamt
// (c.srli/c.srai hints are translated into a srli/srai hint)
comp_instr = {1'b0, i_instr[10], 5'b0, i_instr[6:2], 2'b01, i_instr[9:7],
3'b101, 2'b01, i_instr[9:7], {OPCODE_OP_IMM}};
end
2'b10: begin
// c.andi -> andi rd, rd, imm
comp_instr = {{6 {i_instr[12]}}, i_instr[12], i_instr[6:2], 2'b01, i_instr[9:7],
3'b111, 2'b01, i_instr[9:7], {OPCODE_OP_IMM}};
end
2'b11: begin
case (i_instr[6:5])
2'b00: begin
// c.sub -> sub rd', rd', rs2'
comp_instr = {2'b01, 5'b0, 2'b01, i_instr[4:2], 2'b01, i_instr[9:7],
3'b000, 2'b01, i_instr[9:7], {OPCODE_OP}};
end
2'b01: begin
// c.xor -> xor rd', rd', rs2'
comp_instr = {7'b0, 2'b01, i_instr[4:2], 2'b01, i_instr[9:7], 3'b100,
2'b01, i_instr[9:7], {OPCODE_OP}};
end
2'b10: begin
// c.or -> or rd', rd', rs2'
comp_instr = {7'b0, 2'b01, i_instr[4:2], 2'b01, i_instr[9:7], 3'b110,
2'b01, i_instr[9:7], {OPCODE_OP}};
end
2'b11: begin
// c.and -> and rd', rd', rs2'
comp_instr = {7'b0, 2'b01, i_instr[4:2], 2'b01, i_instr[9:7], 3'b111,
2'b01, i_instr[9:7], {OPCODE_OP}};
end
endcase
end
endcase
end
3'b110, 3'b111: begin
// 0: c.beqz -> beq rs1', x0, imm
// 1: c.bnez -> bne rs1', x0, imm
comp_instr = {{4 {i_instr[12]}}, i_instr[6:5], i_instr[2], 5'b0, 2'b01,
i_instr[9:7], 2'b00, i_instr[13], i_instr[11:10], i_instr[4:3],
i_instr[12], {OPCODE_BRANCH}};
end
endcase
end
// C2
// Register address checks for RV32E are performed in the regular instruction decoder.
// If this check fails, an illegal instruction exception is triggered and the controller
// writes the actual faulting instruction to mtval.
2'b10: begin
case (i_instr[15:14])
2'b00: begin
// c.slli -> slli rd, rd, shamt
// (c.ssli hints are translated into a slli hint)
comp_instr = {7'b0, i_instr[6:2], i_instr[11:7], 3'b001, i_instr[11:7], {OPCODE_OP_IMM}};
end
2'b01: begin
// c.lwsp -> lw rd, imm(x2)
comp_instr = {4'b0, i_instr[3:2], i_instr[12], i_instr[6:4], 2'b00, 5'h02,
3'b010, i_instr[11:7], OPCODE_LOAD};
end
2'b10: begin
if (i_instr[12] == 1'b0) begin
if (i_instr[6:2] != 5'b0) begin
// c.mv -> add rd/rs1, x0, rs2
// (c.mv hints are translated into an add hint)
comp_instr = {7'b0, i_instr[6:2], 5'b0, 3'b0, i_instr[11:7], {OPCODE_OP}};
end else begin
// c.jr -> jalr x0, rd/rs1, 0
comp_instr = {12'b0, i_instr[11:7], 3'b0, 5'b0, {OPCODE_JALR}};
end
end else begin
if (i_instr[6:2] != 5'b0) begin
// c.add -> add rd, rd, rs2
// (c.add hints are translated into an add hint)
comp_instr = {7'b0, i_instr[6:2], i_instr[11:7], 3'b0, i_instr[11:7], {OPCODE_OP}};
end else begin
if (i_instr[11:7] == 5'b0) begin
// c.ebreak -> ebreak
comp_instr = {32'h00_10_00_73};
end else begin
// c.jalr -> jalr x1, rs1, 0
comp_instr = {12'b0, i_instr[11:7], 3'b000, 5'b00001, {OPCODE_JALR}};
end
end
end
end
2'b11: begin
// c.swsp -> sw rs2, imm(x2)
comp_instr = {4'b0, i_instr[8:7], i_instr[12], i_instr[6:2], 5'h02, 3'b010,
i_instr[11:9], 2'b00, {OPCODE_STORE}};
end
endcase
end
// Incoming instruction is not compressed.
2'b11: illegal_instr = 1'b1;
endcase
end
endmodule

View file

@ -1,13 +1,21 @@
`default_nettype none
module serv_csr
#(
parameter RESET_STRATEGY = "MINI",
parameter W = 1,
parameter B = W-1
)
(
input wire i_clk,
input wire i_rst,
//State
input wire i_init,
input wire i_trig_irq,
input wire i_en,
input wire i_cnt0to3,
input wire i_cnt3,
input wire i_cnt7,
input wire i_cnt11,
input wire i_cnt12,
input wire i_cnt_done,
input wire i_mem_op,
input wire i_mtip,
@ -24,11 +32,11 @@ module serv_csr
input wire i_mret,
input wire i_csr_d_sel,
//Data
input wire i_rf_csr_out,
output wire o_csr_in,
input wire i_csr_imm,
input wire i_rs1,
output wire o_q);
input wire [B:0] i_rf_csr_out,
output wire [B:0] o_csr_in,
input wire [B:0] i_csr_imm,
input wire [B:0] i_rs1,
output wire [B:0] o_q);
localparam [1:0]
CSR_SOURCE_CSR = 2'b00,
@ -42,43 +50,53 @@ module serv_csr
reg mcause31;
reg [3:0] mcause3_0;
wire mcause;
wire [B:0] mcause;
wire csr_in;
wire csr_out;
wire [B:0] csr_in;
wire [B:0] csr_out;
reg timer_irq_r;
wire d = i_csr_d_sel ? i_csr_imm : i_rs1;
wire [B:0] d = i_csr_d_sel ? i_csr_imm : i_rs1;
assign csr_in = (i_csr_source == CSR_SOURCE_EXT) ? d :
(i_csr_source == CSR_SOURCE_SET) ? csr_out | d :
(i_csr_source == CSR_SOURCE_CLR) ? csr_out & ~d :
(i_csr_source == CSR_SOURCE_CSR) ? csr_out :
1'bx;
{W{1'bx}};
assign csr_out = (i_mstatus_en & mstatus_mie & i_cnt3) |
wire [B:0] mstatus;
generate
if (W==1) begin : gen_mstatus_w1
assign mstatus = ((mstatus_mie & i_cnt3) | (i_cnt11 | i_cnt12));
end else if (W==4) begin : gen_mstatus_w4
assign mstatus = {i_cnt11 | (mstatus_mie & i_cnt3), 2'b00, i_cnt12};
end
endgenerate
assign csr_out = ({W{i_mstatus_en & i_en}} & mstatus) |
i_rf_csr_out |
(i_mcause_en & i_en & mcause);
({W{i_mcause_en & i_en}} & mcause);
assign o_q = csr_out;
wire timer_irq = i_mtip & mstatus_mie & mie_mtie;
assign mcause = i_cnt0to3 ? mcause3_0[0] : //[3:0]
i_cnt_done ? mcause31 //[31]
: 1'b0;
assign mcause = i_cnt0to3 ? mcause3_0[B:0] : //[3:0]
i_cnt_done ? {mcause31,{B{1'b0}}} //[31]
: {W{1'b0}};
assign o_csr_in = csr_in;
always @(posedge i_clk) begin
if (!i_init & i_cnt_done) begin
if (i_trig_irq) begin
timer_irq_r <= timer_irq;
o_new_irq <= timer_irq & !timer_irq_r;
end
if (i_mie_en & i_cnt7)
mie_mtie <= csr_in;
mie_mtie <= csr_in[B];
/*
The mie bit in mstatus gets updated under three conditions
@ -88,10 +106,10 @@ module serv_csr
During a mstatus CSR access instruction it's assigned when
bit 3 gets updated
These conditions are all mutually exclusibe
These conditions are all mutually exclusive
*/
if ((i_trap & i_cnt_done) | i_mstatus_en & i_cnt3 | i_mret)
mstatus_mie <= !i_trap & (i_mret ? mstatus_mpie : csr_in);
if ((i_trap & i_cnt_done) | i_mstatus_en & i_cnt3 & i_en | i_mret)
mstatus_mie <= !i_trap & (i_mret ? mstatus_mpie : csr_in[B]);
/*
Note: To save resources mstatus_mpie (mstatus bit 7) is not
@ -123,13 +141,18 @@ module serv_csr
ctrl => 0000 (jump=0)
*/
if (i_mcause_en & i_en & i_cnt0to3 | (i_trap & i_cnt_done)) begin
mcause3_0[3] <= (i_e_op & !i_ebreak) | (!i_trap & csr_in);
mcause3_0[2] <= o_new_irq | i_mem_op | (!i_trap & mcause3_0[3]);
mcause3_0[1] <= o_new_irq | i_e_op | (i_mem_op & i_mem_cmd) | (!i_trap & mcause3_0[2]);
mcause3_0[0] <= o_new_irq | i_e_op | (!i_trap & mcause3_0[1]);
mcause3_0[3] <= (i_e_op & !i_ebreak) | (!i_trap & csr_in[B]);
mcause3_0[2] <= o_new_irq | i_mem_op | (!i_trap & ((W == 1) ? mcause3_0[3] : csr_in[(W == 1) ? 0 : 2]));
mcause3_0[1] <= o_new_irq | i_e_op | (i_mem_op & i_mem_cmd) | (!i_trap & ((W == 1) ? mcause3_0[2] : csr_in[(W == 1) ? 0 : 1]));
mcause3_0[0] <= o_new_irq | i_e_op | (!i_trap & ((W == 1) ? mcause3_0[1] : csr_in[0]));
end
if (i_mcause_en & i_cnt_done | i_trap)
mcause31 <= i_trap ? o_new_irq : csr_in;
mcause31 <= i_trap ? o_new_irq : csr_in[B];
if (i_rst)
if (RESET_STRATEGY != "NONE") begin
o_new_irq <= 1'b0;
mie_mtie <= 1'b0;
end
end
endmodule

View file

@ -2,7 +2,10 @@
module serv_ctrl
#(parameter RESET_STRATEGY = "MINI",
parameter RESET_PC = 32'd0,
parameter WITH_CSR = 1)
parameter WITH_CSR = 1,
parameter W = 1,
parameter B = W-1
)
(
input wire clk,
input wire i_rst,
@ -10,6 +13,7 @@ module serv_ctrl
input wire i_pc_en,
input wire i_cnt12to31,
input wire i_cnt0,
input wire i_cnt1,
input wire i_cnt2,
//Control
input wire i_jump,
@ -17,50 +21,76 @@ module serv_ctrl
input wire i_utype,
input wire i_pc_rel,
input wire i_trap,
input wire i_iscomp,
//Data
input wire i_imm,
input wire i_buf,
input wire i_csr_pc,
output wire o_rd,
output wire o_bad_pc,
input wire [B:0] i_imm,
input wire [B:0] i_buf,
input wire [B:0] i_csr_pc,
output wire [B:0] o_rd,
output wire [B:0] o_bad_pc,
//External
output reg [31:0] o_ibus_adr);
wire pc_plus_4;
wire [B:0] pc_plus_4;
wire pc_plus_4_cy;
reg pc_plus_4_cy_r;
wire pc_plus_offset;
wire [B:0] pc_plus_4_cy_r_w;
wire [B:0] pc_plus_offset;
wire pc_plus_offset_cy;
reg pc_plus_offset_cy_r;
wire pc_plus_offset_aligned;
wire plus_4;
reg pc_plus_offset_cy_r;
wire [B:0] pc_plus_offset_cy_r_w;
wire [B:0] pc_plus_offset_aligned;
wire [B:0] plus_4;
wire pc = o_ibus_adr[0];
wire [B:0] pc = o_ibus_adr[B:0];
wire new_pc;
wire [B:0] new_pc;
wire offset_a;
wire offset_b;
wire [B:0] offset_a;
wire [B:0] offset_b;
assign plus_4 = i_cnt2;
/* If i_iscomp=1: increment pc by 2 else increment pc by 4 */
generate
if (W == 1) begin : gen_plus_4_w_eq_1
assign plus_4 = i_iscomp ? i_cnt1 : i_cnt2;
end else if (W == 4) begin : gen_plus_4_w_eq_4
assign plus_4 = (i_cnt0 | i_cnt1) ? (i_iscomp ? 2 : 4) : 0;
end
endgenerate
assign o_bad_pc = pc_plus_offset_aligned;
assign {pc_plus_4_cy,pc_plus_4} = pc+plus_4+pc_plus_4_cy_r;
assign {pc_plus_4_cy,pc_plus_4} = pc+plus_4+pc_plus_4_cy_r_w;
generate
if (WITH_CSR)
assign new_pc = i_trap ? (i_csr_pc & !i_cnt0) : i_jump ? pc_plus_offset_aligned : pc_plus_4;
else
assign new_pc = i_jump ? pc_plus_offset_aligned : pc_plus_4;
if (|WITH_CSR) begin : gen_csr
if (W == 1) begin : gen_new_pc_w_eq_1
assign new_pc = i_trap ? (i_csr_pc & !(i_cnt0 || i_cnt1)) : i_jump ? pc_plus_offset_aligned : pc_plus_4;
end else if (W == 4) begin : gen_new_pc_w_eq_4
assign new_pc = i_trap ? (i_csr_pc & ((i_cnt0 || i_cnt1) ? 4'b1100 : 4'b1111)) : i_jump ? pc_plus_offset_aligned : pc_plus_4;
end
end else begin : gen_no_csr
assign new_pc = i_jump ? pc_plus_offset_aligned : pc_plus_4;
end
endgenerate
assign o_rd = (i_utype & pc_plus_offset_aligned) | (pc_plus_4 & i_jal_or_jalr);
assign o_rd = ({W{i_utype}} & pc_plus_offset_aligned) | (pc_plus_4 & {W{i_jal_or_jalr}});
assign offset_a = i_pc_rel & pc;
assign offset_b = i_utype ? (i_imm & i_cnt12to31): i_buf;
assign {pc_plus_offset_cy,pc_plus_offset} = offset_a+offset_b+pc_plus_offset_cy_r;
assign offset_a = {W{i_pc_rel}} & pc;
assign offset_b = i_utype ? (i_imm & {W{i_cnt12to31}}) : i_buf;
assign {pc_plus_offset_cy,pc_plus_offset} = offset_a+offset_b+pc_plus_offset_cy_r_w;
assign pc_plus_offset_aligned = pc_plus_offset & !i_cnt0;
generate
if (W>1) begin : gen_w_gt_1
assign pc_plus_offset_aligned[B:1] = pc_plus_offset[B:1];
assign pc_plus_offset_cy_r_w[B:1] = {B{1'b0}};
assign pc_plus_4_cy_r_w[B:1] = {B{1'b0}};
end
endgenerate
assign pc_plus_offset_aligned[0] = pc_plus_offset[0] & !i_cnt0;
assign pc_plus_offset_cy_r_w[0] = pc_plus_offset_cy_r;
assign pc_plus_4_cy_r_w[0] = pc_plus_4_cy_r;
initial if (RESET_STRATEGY == "NONE") o_ibus_adr = RESET_PC;
@ -70,10 +100,10 @@ module serv_ctrl
if (RESET_STRATEGY == "NONE") begin
if (i_pc_en)
o_ibus_adr <= {new_pc, o_ibus_adr[31:1]};
o_ibus_adr <= {new_pc, o_ibus_adr[31:W]};
end else begin
if (i_pc_en | i_rst)
o_ibus_adr <= i_rst ? RESET_PC : {new_pc, o_ibus_adr[31:1]};
o_ibus_adr <= i_rst ? RESET_PC : {new_pc, o_ibus_adr[31:W]};
end
end
endmodule

345
rtl/serv_debug.v Normal file
View file

@ -0,0 +1,345 @@
module serv_debug
#(parameter W = 1,
parameter RESET_PC = 0,
//Internally calculated. Do not touch
parameter B=W-1)
(
`ifdef RISCV_FORMAL
output reg rvfi_valid = 1'b0,
output reg [63:0] rvfi_order = 64'd0,
output reg [31:0] rvfi_insn = 32'd0,
output reg rvfi_trap = 1'b0,
output reg rvfi_halt = 1'b0, // Not used
output reg rvfi_intr = 1'b0, // Not used
output reg [1:0] rvfi_mode = 2'b11, // Not used
output reg [1:0] rvfi_ixl = 2'b01, // Not used
output reg [4:0] rvfi_rs1_addr,
output reg [4:0] rvfi_rs2_addr,
output reg [31:0] rvfi_rs1_rdata,
output reg [31:0] rvfi_rs2_rdata,
output reg [4:0] rvfi_rd_addr,
output wire [31:0] rvfi_rd_wdata,
output reg [31:0] rvfi_pc_rdata,
output wire [31:0] rvfi_pc_wdata,
output reg [31:0] rvfi_mem_addr,
output reg [3:0] rvfi_mem_rmask,
output reg [3:0] rvfi_mem_wmask,
output reg [31:0] rvfi_mem_rdata,
output reg [31:0] rvfi_mem_wdata,
input wire [31:0] i_dbus_adr,
input wire [31:0] i_dbus_dat,
input wire [3:0] i_dbus_sel,
input wire i_dbus_we,
input wire [31:0] i_dbus_rdt,
input wire i_dbus_ack,
input wire i_ctrl_pc_en,
input wire [B:0] rs1,
input wire [B:0] rs2,
input wire [4:0] rs1_addr,
input wire [4:0] rs2_addr,
input wire [3:0] immdec_en,
input wire rd_en,
input wire trap,
input wire i_rf_ready,
input wire i_ibus_cyc,
input wire two_stage_op,
input wire init,
input wire [31:0] i_ibus_adr,
`endif
input wire i_clk,
input wire i_rst,
input wire [31:0] i_ibus_rdt,
input wire i_ibus_ack,
input wire [4:0] i_rd_addr,
input wire i_cnt_en,
input wire [B:0] i_csr_in,
input wire i_csr_mstatus_en,
input wire i_csr_mie_en,
input wire i_csr_mcause_en,
input wire i_csr_en,
input wire [1:0] i_csr_addr,
input wire i_wen0,
input wire [B:0] i_wdata0,
input wire i_cnt_done);
reg update_rd = 1'b0;
reg update_mscratch;
reg update_mtvec;
reg update_mepc;
reg update_mtval;
reg update_mstatus;
reg update_mie;
reg update_mcause;
reg [31:0] dbg_rd = 32'hxxxxxxxx;
reg [31:0] dbg_csr = 32'hxxxxxxxx;
reg [31:0] dbg_mstatus = 32'hxxxxxxxx;
reg [31:0] dbg_mie = 32'hxxxxxxxx;
reg [31:0] dbg_mcause = 32'hxxxxxxxx;
reg [31:0] dbg_mscratch = 32'hxxxxxxxx;
reg [31:0] dbg_mtvec = 32'hxxxxxxxx;
reg [31:0] dbg_mepc = 32'hxxxxxxxx;
reg [31:0] dbg_mtval = 32'hxxxxxxxx;
reg [31:0] x1 = 32'hxxxxxxxx;
reg [31:0] x2 = 32'hxxxxxxxx;
reg [31:0] x3 = 32'hxxxxxxxx;
reg [31:0] x4 = 32'hxxxxxxxx;
reg [31:0] x5 = 32'hxxxxxxxx;
reg [31:0] x6 = 32'hxxxxxxxx;
reg [31:0] x7 = 32'hxxxxxxxx;
reg [31:0] x8 = 32'hxxxxxxxx;
reg [31:0] x9 = 32'hxxxxxxxx;
reg [31:0] x10 = 32'hxxxxxxxx;
reg [31:0] x11 = 32'hxxxxxxxx;
reg [31:0] x12 = 32'hxxxxxxxx;
reg [31:0] x13 = 32'hxxxxxxxx;
reg [31:0] x14 = 32'hxxxxxxxx;
reg [31:0] x15 = 32'hxxxxxxxx;
reg [31:0] x16 = 32'hxxxxxxxx;
reg [31:0] x17 = 32'hxxxxxxxx;
reg [31:0] x18 = 32'hxxxxxxxx;
reg [31:0] x19 = 32'hxxxxxxxx;
reg [31:0] x20 = 32'hxxxxxxxx;
reg [31:0] x21 = 32'hxxxxxxxx;
reg [31:0] x22 = 32'hxxxxxxxx;
reg [31:0] x23 = 32'hxxxxxxxx;
reg [31:0] x24 = 32'hxxxxxxxx;
reg [31:0] x25 = 32'hxxxxxxxx;
reg [31:0] x26 = 32'hxxxxxxxx;
reg [31:0] x27 = 32'hxxxxxxxx;
reg [31:0] x28 = 32'hxxxxxxxx;
reg [31:0] x29 = 32'hxxxxxxxx;
reg [31:0] x30 = 32'hxxxxxxxx;
reg [31:0] x31 = 32'hxxxxxxxx;
always @(posedge i_clk) begin
update_rd <= i_cnt_done & i_wen0;
if (i_wen0)
dbg_rd <= {i_wdata0,dbg_rd[31:W]};
//End of instruction that writes to RF
if (update_rd) begin
case (i_rd_addr)
5'd1 : x1 <= dbg_rd;
5'd2 : x2 <= dbg_rd;
5'd3 : x3 <= dbg_rd;
5'd4 : x4 <= dbg_rd;
5'd5 : x5 <= dbg_rd;
5'd6 : x6 <= dbg_rd;
5'd7 : x7 <= dbg_rd;
5'd8 : x8 <= dbg_rd;
5'd9 : x9 <= dbg_rd;
5'd10 : x10 <= dbg_rd;
5'd11 : x11 <= dbg_rd;
5'd12 : x12 <= dbg_rd;
5'd13 : x13 <= dbg_rd;
5'd14 : x14 <= dbg_rd;
5'd15 : x15 <= dbg_rd;
5'd16 : x16 <= dbg_rd;
5'd17 : x17 <= dbg_rd;
5'd18 : x18 <= dbg_rd;
5'd19 : x19 <= dbg_rd;
5'd20 : x20 <= dbg_rd;
5'd21 : x21 <= dbg_rd;
5'd22 : x22 <= dbg_rd;
5'd23 : x23 <= dbg_rd;
5'd24 : x24 <= dbg_rd;
5'd25 : x25 <= dbg_rd;
5'd26 : x26 <= dbg_rd;
5'd27 : x27 <= dbg_rd;
5'd28 : x28 <= dbg_rd;
5'd29 : x29 <= dbg_rd;
5'd30 : x30 <= dbg_rd;
5'd31 : x31 <= dbg_rd;
default : ;
endcase
end
update_mscratch <= i_cnt_done & i_csr_en & (i_csr_addr == 2'b00);
update_mtvec <= i_cnt_done & i_csr_en & (i_csr_addr == 2'b01);
update_mepc <= i_cnt_done & i_csr_en & (i_csr_addr == 2'b10);
update_mtval <= i_cnt_done & i_csr_en & (i_csr_addr == 2'b11);
update_mstatus <= i_cnt_done & i_csr_mstatus_en;
update_mie <= i_cnt_done & i_csr_mie_en;
update_mcause <= i_cnt_done & i_csr_mcause_en;
if (i_cnt_en)
dbg_csr <= {i_csr_in, dbg_csr[31:W]};
if (update_mscratch) dbg_mscratch <= dbg_csr;
if (update_mtvec) dbg_mtvec <= dbg_csr;
if (update_mepc ) dbg_mepc <= dbg_csr;
if (update_mtval) dbg_mtval <= dbg_csr;
if (update_mstatus) dbg_mstatus <= dbg_csr;
if (update_mie) dbg_mie <= dbg_csr;
if (update_mcause) dbg_mcause <= dbg_csr;
end
reg LUI, AUIPC, JAL, JALR, BEQ, BNE, BLT, BGE, BLTU, BGEU, LB, LH, LW, LBU, LHU, SB, SH, SW, ADDI, SLTI, SLTIU, XORI, ORI, ANDI,SLLI, SRLI, SRAI, ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND, FENCE, ECALL, EBREAK;
reg CSRRW, CSRRS, CSRRC, CSRRWI, CSRRSI, CSRRCI;
reg OTHER;
always @(posedge i_clk) begin
if (i_ibus_ack) begin
LUI <= 1'b0;
AUIPC <= 1'b0;
JAL <= 1'b0;
JALR <= 1'b0;
BEQ <= 1'b0;
BNE <= 1'b0;
BLT <= 1'b0;
BGE <= 1'b0;
BLTU <= 1'b0;
BGEU <= 1'b0;
LB <= 1'b0;
LH <= 1'b0;
LW <= 1'b0;
LBU <= 1'b0;
LHU <= 1'b0;
SB <= 1'b0;
SH <= 1'b0;
SW <= 1'b0;
ADDI <= 1'b0;
SLTI <= 1'b0;
SLTIU <= 1'b0;
XORI <= 1'b0;
ORI <= 1'b0;
ANDI <= 1'b0;
SLLI <= 1'b0;
SRLI <= 1'b0;
SRAI <= 1'b0;
ADD <= 1'b0;
SUB <= 1'b0;
SLL <= 1'b0;
SLT <= 1'b0;
SLTU <= 1'b0;
XOR <= 1'b0;
SRL <= 1'b0;
SRA <= 1'b0;
OR <= 1'b0;
AND <= 1'b0;
FENCE <= 1'b0;
ECALL <= 1'b0;
EBREAK <= 1'b0;
CSRRW <= 1'b0;
CSRRS <= 1'b0;
CSRRC <= 1'b0;
CSRRWI <= 1'b0;
CSRRSI <= 1'b0;
CSRRCI <= 1'b0;
OTHER <= 1'b0;
casez(i_ibus_rdt)
// 3322222_22222 11111_111 11
// 1098765_43210 98765_432 10987_65432_10
32'b???????_?????_?????_???_?????_01101_11 : LUI <= 1'b1;
32'b???????_?????_?????_???_?????_00101_11 : AUIPC <= 1'b1;
32'b???????_?????_?????_???_?????_11011_11 : JAL <= 1'b1;
32'b???????_?????_?????_000_?????_11001_11 : JALR <= 1'b1;
32'b???????_?????_?????_000_?????_11000_11 : BEQ <= 1'b1;
32'b???????_?????_?????_001_?????_11000_11 : BNE <= 1'b1;
32'b???????_?????_?????_100_?????_11000_11 : BLT <= 1'b1;
32'b???????_?????_?????_101_?????_11000_11 : BGE <= 1'b1;
32'b???????_?????_?????_110_?????_11000_11 : BLTU <= 1'b1;
32'b???????_?????_?????_111_?????_11000_11 : BGEU <= 1'b1;
32'b???????_?????_?????_000_?????_00000_11 : LB <= 1'b1;
32'b???????_?????_?????_001_?????_00000_11 : LH <= 1'b1;
32'b???????_?????_?????_010_?????_00000_11 : LW <= 1'b1;
32'b???????_?????_?????_100_?????_00000_11 : LBU <= 1'b1;
32'b???????_?????_?????_101_?????_00000_11 : LHU <= 1'b1;
32'b???????_?????_?????_000_?????_01000_11 : SB <= 1'b1;
32'b???????_?????_?????_001_?????_01000_11 : SH <= 1'b1;
32'b???????_?????_?????_010_?????_01000_11 : SW <= 1'b1;
32'b???????_?????_?????_000_?????_00100_11 : ADDI <= 1'b1;
32'b???????_?????_?????_010_?????_00100_11 : SLTI <= 1'b1;
32'b???????_?????_?????_011_?????_00100_11 : SLTIU <= 1'b1;
32'b???????_?????_?????_100_?????_00100_11 : XORI <= 1'b1;
32'b???????_?????_?????_110_?????_00100_11 : ORI <= 1'b1;
32'b???????_?????_?????_111_?????_00100_11 : ANDI <= 1'b1;
32'b0000000_?????_?????_001_?????_00100_11 : SLLI <= 1'b1;
32'b0000000_?????_?????_101_?????_00100_11 : SRLI <= 1'b1;
32'b0100000_?????_?????_101_?????_00100_11 : SRAI <= 1'b1;
32'b0000000_?????_?????_000_?????_01100_11 : ADD <= 1'b1;
32'b0100000_?????_?????_000_?????_01100_11 : SUB <= 1'b1;
32'b0000000_?????_?????_001_?????_01100_11 : SLL <= 1'b1;
32'b0000000_?????_?????_010_?????_01100_11 : SLT <= 1'b1;
32'b0000000_?????_?????_011_?????_01100_11 : SLTU <= 1'b1;
32'b???????_?????_?????_100_?????_01100_11 : XOR <= 1'b1;
32'b0000000_?????_?????_101_?????_01100_11 : SRL <= 1'b1;
32'b0100000_?????_?????_101_?????_01100_11 : SRA <= 1'b1;
32'b???????_?????_?????_110_?????_01100_11 : OR <= 1'b1;
32'b???????_?????_?????_111_?????_01100_11 : AND <= 1'b1;
32'b???????_?????_?????_000_?????_00011_11 : FENCE <= 1'b1;
32'b0000000_00000_00000_000_00000_11100_11 : ECALL <= 1'b1;
32'b0000000_00001_00000_000_00000_11100_11 : EBREAK <= 1'b1;
32'b???????_?????_?????_001_?????_11100_11 : CSRRW <= 1'b1;
32'b???????_?????_?????_010_?????_11100_11 : CSRRS <= 1'b1;
32'b???????_?????_?????_011_?????_11100_11 : CSRRC <= 1'b1;
32'b???????_?????_?????_101_?????_11100_11 : CSRRWI <= 1'b1;
32'b???????_?????_?????_110_?????_11100_11 : CSRRSI <= 1'b1;
32'b???????_?????_?????_111_?????_11100_11 : CSRRCI <= 1'b1;
default : OTHER <= 1'b1;
endcase
end
end
`ifdef RISCV_FORMAL
reg [31:0] pc = RESET_PC;
wire rs_en = two_stage_op ? init : i_ctrl_pc_en;
assign rvfi_rd_wdata = update_rd ? dbg_rd : 32'd0;
always @(posedge i_clk) begin
/* End of instruction */
rvfi_valid <= i_cnt_done & i_ctrl_pc_en & !i_rst;
rvfi_order <= rvfi_order + {63'd0,rvfi_valid};
/* Get instruction word when it's fetched from ibus */
if (i_ibus_cyc & i_ibus_ack)
rvfi_insn <= i_ibus_rdt;
if (i_cnt_done & i_ctrl_pc_en) begin
rvfi_pc_rdata <= pc;
if (!(rd_en & (|i_rd_addr))) begin
rvfi_rd_addr <= 5'd0;
end
end
rvfi_trap <= trap;
if (rvfi_valid) begin
rvfi_trap <= 1'b0;
pc <= rvfi_pc_wdata;
end
/* RS1 not valid during J, U instructions (immdec_en[1]) */
/* RS2 not valid during I, J, U instructions (immdec_en[2]) */
if (i_rf_ready) begin
rvfi_rs1_addr <= !immdec_en[1] ? rs1_addr : 5'd0;
rvfi_rs2_addr <= !immdec_en[2] /*rs2_valid*/ ? rs2_addr : 5'd0;
rvfi_rd_addr <= i_rd_addr;
end
if (rs_en) begin
rvfi_rs1_rdata <= {(!immdec_en[1] ? rs1 : {W{1'b0}}),rvfi_rs1_rdata[31:W]};
rvfi_rs2_rdata <= {(!immdec_en[2] ? rs2 : {W{1'b0}}),rvfi_rs2_rdata[31:W]};
end
if (i_dbus_ack) begin
rvfi_mem_addr <= i_dbus_adr;
rvfi_mem_rmask <= i_dbus_we ? 4'b0000 : i_dbus_sel;
rvfi_mem_wmask <= i_dbus_we ? i_dbus_sel : 4'b0000;
rvfi_mem_rdata <= i_dbus_rdt;
rvfi_mem_wdata <= i_dbus_dat;
end
if (i_ibus_ack) begin
rvfi_mem_rmask <= 4'b0000;
rvfi_mem_wmask <= 4'b0000;
end
end
assign rvfi_pc_wdata = i_ibus_adr;
`endif
endmodule

View file

@ -1,7 +1,8 @@
`default_nettype none
module serv_decode #(
parameter [0:0] PRE_REGISTER = 1
)(
module serv_decode
#(parameter [0:0] PRE_REGISTER = 1,
parameter [0:0] MDU = 0)
(
input wire clk,
//Input
input wire [31:2] i_wb_rdt,
@ -13,10 +14,14 @@ module serv_decode #(
output reg o_e_op,
output reg o_ebreak,
output reg o_branch_op,
output reg o_mem_op,
output reg o_shift_op,
output reg o_slt_op,
output reg o_rd_op,
output reg o_two_stage_op,
output reg o_dbus_en,
//MDU
output reg o_mdu_op,
//Extension
output reg [2:0] o_ext_funct3,
//To bufreg
output reg o_bufreg_rs1_en,
output reg o_bufreg_imm_en,
@ -47,10 +52,13 @@ module serv_decode #(
output reg [1:0] o_csr_source,
output reg o_csr_d_sel,
output reg o_csr_imm_en,
output reg o_mtval_pc,
//To top
output reg [3:0] o_immdec_ctrl,
output reg [3:0] o_immdec_en,
output reg o_op_b_source,
//To RF IF
output reg o_rd_mem_en,
output reg o_rd_csr_en,
output reg o_rd_alu_en);
@ -61,13 +69,22 @@ module serv_decode #(
reg op22;
reg op26;
reg imm25;
reg imm30;
//opcode
wire op_or_opimm = (!opcode[4] & opcode[2] & !opcode[0]);
wire co_mdu_op = MDU & (opcode == 5'b01100) & imm25;
wire co_mem_op = !opcode[4] & !opcode[2] & !opcode[0];
wire co_branch_op = opcode[4] & !opcode[2];
wire co_two_stage_op =
~opcode[2] | (funct3[0] & ~funct3[1] & ~opcode[0] & ~opcode[4]) |
(funct3[1] & ~funct3[2] & ~opcode[0] & ~opcode[4]) | co_mdu_op;
wire co_shift_op = (opcode[2] & ~funct3[1]) & !co_mdu_op;
wire co_branch_op = opcode[4];
wire co_dbus_en = ~opcode[2] & ~opcode[4];
wire co_mtval_pc = opcode[4];
wire co_mem_word = funct3[1];
wire co_rd_alu_en = !opcode[0] & opcode[2] & !opcode[4] & !co_mdu_op;
wire co_rd_mem_en = (!opcode[2] & !opcode[0]) | co_mdu_op;
wire [2:0] co_ext_funct3 = funct3;
//jal,branch = imm
//jalr = rs1+imm
@ -90,10 +107,11 @@ module serv_decode #(
wire co_ctrl_jal_or_jalr = opcode[4] & opcode[0];
//PC-relative operations
//True for jal, b* auipc
//True for jal, b* auipc, ebreak
//False for jalr, lui
wire co_ctrl_pc_rel = (opcode[2:0] == 3'b000) |
(opcode[1:0] == 2'b11) |
wire co_ctrl_pc_rel = (opcode[2:0] == 3'b000) |
(opcode[1:0] == 2'b11) |
(opcode[4] & opcode[2]) & op20|
(opcode[4:3] == 2'b00);
//Write to RD
//True for OP-IMM, AUIPC, OP, LUI, SYSTEM, JALR, JAL, LOAD
@ -109,13 +127,6 @@ module serv_decode #(
wire co_sh_right = funct3[2];
wire co_bne_or_bge = funct3[0];
//
// opcode & funct3
//
wire co_shift_op = op_or_opimm & (funct3[1:0] == 2'b01);
wire co_slt_op = op_or_opimm & (funct3[2:1] == 2'b01);
//Matches system ops except eceall/ebreak/mret
wire csr_op = opcode[4] & opcode[2] & (|funct3);
@ -175,7 +186,7 @@ module serv_decode #(
wire co_rd_csr_en = csr_op;
wire co_csr_en = csr_op & csr_valid;
wire co_csr_mstatus_en = csr_op & !op26 & !op22;
wire co_csr_mstatus_en = csr_op & !op26 & !op22 & !op20;
wire co_csr_mie_en = csr_op & !op26 & op22 & !op20;
wire co_csr_mcause_en = csr_op & op21 & !op20;
@ -190,7 +201,6 @@ module serv_decode #(
wire co_mem_cmd = opcode[3];
wire co_mem_signed = ~funct3[2];
wire co_mem_word = funct3[1];
wire co_mem_half = funct3[0];
wire [1:0] co_alu_bool_op = funct3[1:0];
@ -220,15 +230,14 @@ module serv_decode #(
//1 (OP_B_SOURCE_RS2) when BRANCH or OP
wire co_op_b_source = opcode[3];
wire co_rd_alu_en = !opcode[0] & opcode[2] & !opcode[4];
generate
if (PRE_REGISTER) begin
if (PRE_REGISTER) begin : gen_pre_register
always @(posedge clk) begin
if (i_wb_en) begin
funct3 <= i_wb_rdt[14:12];
imm30 <= i_wb_rdt[30];
imm25 <= i_wb_rdt[25];
opcode <= i_wb_rdt[6:2];
op20 <= i_wb_rdt[20];
op21 <= i_wb_rdt[21];
@ -241,13 +250,16 @@ module serv_decode #(
o_sh_right = co_sh_right;
o_bne_or_bge = co_bne_or_bge;
o_cond_branch = co_cond_branch;
o_dbus_en = co_dbus_en;
o_mtval_pc = co_mtval_pc;
o_two_stage_op = co_two_stage_op;
o_e_op = co_e_op;
o_ebreak = co_ebreak;
o_branch_op = co_branch_op;
o_mem_op = co_mem_op;
o_shift_op = co_shift_op;
o_slt_op = co_slt_op;
o_rd_op = co_rd_op;
o_mdu_op = co_mdu_op;
o_ext_funct3 = co_ext_funct3;
o_bufreg_rs1_en = co_bufreg_rs1_en;
o_bufreg_imm_en = co_bufreg_imm_en;
o_bufreg_clr_lsb = co_bufreg_clr_lsb;
@ -278,13 +290,15 @@ module serv_decode #(
o_op_b_source = co_op_b_source;
o_rd_csr_en = co_rd_csr_en;
o_rd_alu_en = co_rd_alu_en;
o_rd_mem_en = co_rd_mem_en;
end
end else begin
end else begin : gen_post_register
always @(*) begin
funct3 = i_wb_rdt[14:12];
imm30 = i_wb_rdt[30];
imm25 = i_wb_rdt[25];
opcode = i_wb_rdt[6:2];
op20 = i_wb_rdt[20];
op21 = i_wb_rdt[21];
@ -299,11 +313,14 @@ module serv_decode #(
o_cond_branch <= co_cond_branch;
o_e_op <= co_e_op;
o_ebreak <= co_ebreak;
o_two_stage_op <= co_two_stage_op;
o_dbus_en <= co_dbus_en;
o_mtval_pc <= co_mtval_pc;
o_branch_op <= co_branch_op;
o_mem_op <= co_mem_op;
o_shift_op <= co_shift_op;
o_slt_op <= co_slt_op;
o_rd_op <= co_rd_op;
o_mdu_op <= co_mdu_op;
o_ext_funct3 <= co_ext_funct3;
o_bufreg_rs1_en <= co_bufreg_rs1_en;
o_bufreg_imm_en <= co_bufreg_imm_en;
o_bufreg_clr_lsb <= co_bufreg_clr_lsb;
@ -334,6 +351,7 @@ module serv_decode #(
o_op_b_source <= co_op_b_source;
o_rd_csr_en <= co_rd_csr_en;
o_rd_alu_en <= co_rd_alu_en;
o_rd_mem_en <= co_rd_mem_en;
end
end

View file

@ -20,7 +20,7 @@ module serv_immdec
input wire i_wb_en,
input wire [31:7] i_wb_rdt);
reg signbit;
reg imm31;
reg [8:0] imm19_12_20;
reg imm7;
@ -28,11 +28,12 @@ module serv_immdec
reg [4:0] imm24_20;
reg [4:0] imm11_7;
assign o_imm = i_cnt_done ? signbit : i_ctrl[0] ? imm11_7[0] : imm24_20[0];
assign o_csr_imm = imm19_12_20[4];
wire signbit = imm31 & !i_csr_imm_en;
generate
if (SHARED_RFADDR_IMM_REGS) begin
if (SHARED_RFADDR_IMM_REGS) begin : gen_shared_imm_regs
assign o_rs1_addr = imm19_12_20[8:4];
assign o_rs2_addr = imm24_20;
assign o_rd_addr = imm11_7;
@ -40,7 +41,7 @@ module serv_immdec
always @(posedge i_clk) begin
if (i_wb_en) begin
/* CSR immediates are always zero-extended, hence clear the signbit */
signbit <= i_wb_rdt[31] & !i_csr_imm_en;
imm31 <= i_wb_rdt[31];
end
if (i_wb_en | (i_cnt_en & i_immdec_en[1]))
imm19_12_20 <= i_wb_en ? {i_wb_rdt[19:12],i_wb_rdt[20]} : {i_ctrl[3] ? signbit : imm24_20[0], imm19_12_20[8:1]};
@ -56,7 +57,7 @@ module serv_immdec
if (i_wb_en | (i_cnt_en & i_immdec_en[0]))
imm11_7 <= i_wb_en ? i_wb_rdt[11:7] : {imm30_25[0], imm11_7[4:1]};
end
end else begin
end else begin : gen_separate_imm_regs
reg [4:0] rd_addr;
reg [4:0] rs1_addr;
reg [4:0] rs2_addr;
@ -67,7 +68,7 @@ module serv_immdec
always @(posedge i_clk) begin
if (i_wb_en) begin
/* CSR immediates are always zero-extended, hence clear the signbit */
signbit <= i_wb_rdt[31] & !i_csr_imm_en;
imm31 <= i_wb_rdt[31];
imm19_12_20 <= {i_wb_rdt[19:12],i_wb_rdt[20]};
imm7 <= i_wb_rdt[7];
imm30_25 <= i_wb_rdt[30:25];
@ -88,5 +89,7 @@ module serv_immdec
end
end
endgenerate
assign o_imm = i_cnt_done ? signbit : i_ctrl[0] ? imm11_7[0] : imm24_20[0];
endmodule

View file

@ -1,99 +1,46 @@
`default_nettype none
module serv_mem_if
#(parameter WITH_CSR = 1)
#(
parameter [0:0] WITH_CSR = 1,
parameter W = 1,
parameter B = W-1
)
(
input wire i_clk,
input wire i_clk,
//State
input wire i_en,
input wire i_init,
input wire i_cnt_done,
input wire [1:0] i_bytecnt,
input wire [1:0] i_lsb,
output wire o_misalign,
output wire o_sh_done,
output wire o_sh_done_r,
input wire [1:0] i_bytecnt,
input wire [1:0] i_lsb,
output wire o_misalign,
//Control
input wire i_mem_op,
input wire i_shift_op,
input wire i_signed,
input wire i_word,
input wire i_half,
input wire i_signed,
input wire i_word,
input wire i_half,
//MDU
input wire i_mdu_op,
//Data
input wire i_op_b,
output wire o_rd,
input wire [B:0] i_bufreg2_q,
output wire [B:0] o_rd,
//External interface
output wire [31:0] o_wb_dat,
output wire [3:0] o_wb_sel,
input wire [31:0] i_wb_rdt,
input wire i_wb_ack);
output wire [3:0] o_wb_sel);
reg signbit;
reg [31:0] dat;
/*
Before a store operation, the data to be written needs to be shifted into
place. Depending on the address alignment, we need to shift different
amounts. One formula for calculating this is to say that we shift when
i_lsb + i_bytecnt < 4. Unfortunately, the synthesis tools don't seem to be
clever enough so the hideous expression below is used to achieve the same
thing in a more optimal way.
*/
wire byte_valid =
(!i_lsb[0] & !i_lsb[1]) |
(!i_bytecnt[0] & !i_bytecnt[1]) |
(!i_bytecnt[1] & !i_lsb[1]) |
(!i_bytecnt[1] & !i_lsb[0]) |
(!i_bytecnt[0] & !i_lsb[1]);
wire dat_en = i_shift_op | (i_en & byte_valid);
wire dat_cur =
((i_lsb == 2'd3) & dat[24]) |
((i_lsb == 2'd2) & dat[16]) |
((i_lsb == 2'd1) & dat[8]) |
((i_lsb == 2'd0) & dat[0]);
reg signbit;
wire dat_valid =
i_mdu_op |
i_word |
(i_bytecnt == 2'b00) |
(i_half & !i_bytecnt[1]);
assign o_rd = i_mem_op & (dat_valid ? dat_cur : signbit & i_signed);
assign o_rd = dat_valid ? i_bufreg2_q : {W{i_signed & signbit}};
assign o_wb_sel[3] = (i_lsb == 2'b11) | i_word | (i_half & i_lsb[1]);
assign o_wb_sel[2] = (i_lsb == 2'b10) | i_word;
assign o_wb_sel[1] = (i_lsb == 2'b01) | i_word | (i_half & !i_lsb[1]);
assign o_wb_sel[0] = (i_lsb == 2'b00);
assign o_wb_dat = dat;
/* The dat register has three different use cases for store, load and
shift operations.
store : Data to be written is shifted to the correct position in dat during
init by dat_en and is presented on the data bus as o_wb_dat
load : Data from the bus gets latched into dat during i_wb_ack and is then
shifted out at the appropriate time to end up in the correct
position in rd
shift : Data is shifted in during init. After that, the six LSB are used as
a downcounter (with bit 5 initially set to 0) that triggers
o_sh_done and o_sh_done_r when they wrap around to indicate that
the requested number of shifts have been performed
*/
wire [5:0] dat_shamt = (i_shift_op & !i_init) ?
//Down counter mode
dat[5:0]-1 :
//Shift reg mode with optional clearing of bit 5
{dat[6] & !(i_shift_op & i_cnt_done),dat[5:1]};
assign o_sh_done = dat_shamt[5];
assign o_sh_done_r = dat[5];
always @(posedge i_clk) begin
if (dat_en | i_wb_ack)
dat <= i_wb_ack ? i_wb_rdt : {i_op_b, dat[31:7], dat_shamt};
if (dat_valid)
signbit <= dat_cur;
signbit <= i_bufreg2_q[B];
end
/*

View file

@ -1,48 +1,52 @@
`default_nettype none
module serv_rf_if
#(parameter WITH_CSR = 1)
#(parameter WITH_CSR = 1,
parameter W = 1,
parameter B = W-1
)
(//RF Interface
input wire i_cnt_en,
output wire [4+WITH_CSR:0] o_wreg0,
output wire [4+WITH_CSR:0] o_wreg1,
output wire o_wen0,
output wire o_wen1,
output wire o_wdata0,
output wire o_wdata1,
output wire [B:0] o_wdata0,
output wire [B:0] o_wdata1,
output wire [4+WITH_CSR:0] o_rreg0,
output wire [4+WITH_CSR:0] o_rreg1,
input wire i_rdata0,
input wire i_rdata1,
input wire [B:0] i_rdata0,
input wire [B:0] i_rdata1,
//Trap interface
input wire i_trap,
input wire i_mret,
input wire i_mepc,
input wire i_mem_op,
input wire i_bufreg_q,
input wire i_bad_pc,
output wire o_csr_pc,
input wire [B:0] i_mepc,
input wire i_mtval_pc,
input wire [B:0] i_bufreg_q,
input wire [B:0] i_bad_pc,
output wire [B:0] o_csr_pc,
//CSR interface
input wire i_csr_en,
input wire [1:0] i_csr_addr,
input wire i_csr,
output wire o_csr,
input wire [B:0] i_csr,
output wire [B:0] o_csr,
//RD write port
input wire i_rd_wen,
input wire [4:0] i_rd_waddr,
input wire i_ctrl_rd,
input wire i_alu_rd,
input wire [B:0] i_ctrl_rd,
input wire [B:0] i_alu_rd,
input wire i_rd_alu_en,
input wire i_csr_rd,
input wire [B:0] i_csr_rd,
input wire i_rd_csr_en,
input wire i_mem_rd,
input wire [B:0] i_mem_rd,
input wire i_rd_mem_en,
//RS1 read port
input wire [4:0] i_rs1_raddr,
output wire o_rs1,
output wire [B:0] o_rs1,
//RS2 read port
input wire [4:0] i_rs2_raddr,
output wire o_rs2);
output wire [B:0] o_rs2);
/*
@ -52,13 +56,14 @@ module serv_rf_if
wire rd_wen = i_rd_wen & (|i_rd_waddr);
generate
if (WITH_CSR) begin
wire rd = (i_ctrl_rd ) |
(i_alu_rd & i_rd_alu_en) |
(i_csr_rd & i_rd_csr_en) |
(i_mem_rd);
if (|WITH_CSR) begin : gen_csr
wire [B:0] rd =
{W{i_rd_alu_en}} & i_alu_rd |
{W{i_rd_csr_en}} & i_csr_rd |
{W{i_rd_mem_en}} & i_mem_rd |
i_ctrl_rd;
wire mtval = i_mem_op ? i_bufreg_q : i_bad_pc;
wire [B:0] mtval = i_mtval_pc ? i_bad_pc : i_bufreg_q;
assign o_wdata0 = i_trap ? mtval : rd;
assign o_wdata1 = i_trap ? i_mepc : i_csr;
@ -115,16 +120,16 @@ module serv_rf_if
assign o_rs1 = i_rdata0;
assign o_rs2 = i_rdata1;
assign o_csr = i_rdata1 & i_csr_en;
assign o_csr = i_rdata1 & {W{i_csr_en}};
assign o_csr_pc = i_rdata1;
end else begin
wire rd = (i_ctrl_rd ) |
(i_alu_rd & i_rd_alu_en) |
(i_mem_rd);
end else begin : gen_no_csr
wire [B:0] rd = (i_ctrl_rd) |
i_alu_rd & {W{i_rd_alu_en}} |
i_mem_rd & {W{i_rd_mem_en}};
assign o_wdata0 = rd;
assign o_wdata1 = 1'b0;
assign o_wdata1 = {W{1'b0}};
assign o_wreg0 = i_rd_waddr;
assign o_wreg1 = 5'd0;
@ -141,8 +146,8 @@ module serv_rf_if
assign o_rs1 = i_rdata0;
assign o_rs2 = i_rdata1;
assign o_csr = 1'b0;
assign o_csr_pc = 1'b0;
assign o_csr = {W{1'b0}};
assign o_csr_pc = {W{1'b0}};
end // else: !if(WITH_CSR)
endgenerate
endmodule

View file

@ -7,16 +7,35 @@ module serv_rf_ram
input wire [width-1:0] i_wdata,
input wire i_wen,
input wire [$clog2(depth)-1:0] i_raddr,
output reg [width-1:0] o_rdata);
input wire i_ren,
output wire [width-1:0] o_rdata);
reg [width-1:0] memory [0:depth-1];
reg [width-1:0] rdata ;
always @(posedge i_clk) begin
if (i_wen)
memory[i_waddr] <= i_wdata;
o_rdata <= memory[i_raddr];
rdata <= i_ren ? memory[i_raddr] : {width{1'bx}};
end
/* Reads from reg x0 needs to return 0
Check that the part of the read address corresponding to the register
is zero and gate the output
width LSB of reg index $clog2(width)
2 4 1
4 3 2
8 2 3
16 1 4
32 0 5
*/
reg regzero;
always @(posedge i_clk)
regzero <= !(|i_raddr[$clog2(depth)-1:5-$clog2(width)]);
assign o_rdata = rdata & ~{width{regzero}};
`ifdef SERV_CLEAR_RAM
integer i;
initial

View file

@ -1,89 +1,106 @@
`default_nettype none
module serv_rf_ram_if
#(parameter width=8,
#(//Data width. Adjust to preferred width of SRAM data interface
parameter width=8,
parameter W = 1,
//Select reset strategy.
// "MINI" for resetting minimally required FFs
// "NONE" for relying on FFs having a defined value on startup
parameter reset_strategy="MINI",
//Number of CSR registers. These are allocated after the normal
// GPR registers in the RAM.
parameter csr_regs=4,
parameter depth=32*(32+csr_regs)/width,
parameter l2w = $clog2(width))
//Internal parameters calculated from above values. Do not change
parameter B=W-1,
parameter raw=$clog2(32+csr_regs), //Register address width
parameter l2w=$clog2(width), //log2 of width
parameter aw=5+raw-l2w) //Address width
(
//SERV side
input wire i_clk,
input wire i_rst,
input wire i_wreq,
input wire i_rreq,
output wire o_ready,
input wire [$clog2(32+csr_regs)-1:0] i_wreg0,
input wire [$clog2(32+csr_regs)-1:0] i_wreg1,
input wire i_wen0,
input wire i_wen1,
input wire i_wdata0,
input wire i_wdata1,
input wire [$clog2(32+csr_regs)-1:0] i_rreg0,
input wire [$clog2(32+csr_regs)-1:0] i_rreg1,
output wire o_rdata0,
output wire o_rdata1,
input wire i_clk,
input wire i_rst,
input wire i_wreq,
input wire i_rreq,
output wire o_ready,
input wire [raw-1:0] i_wreg0,
input wire [raw-1:0] i_wreg1,
input wire i_wen0,
input wire i_wen1,
input wire [B:0] i_wdata0,
input wire [B:0] i_wdata1,
input wire [raw-1:0] i_rreg0,
input wire [raw-1:0] i_rreg1,
output wire [B:0] o_rdata0,
output wire [B:0] o_rdata1,
//RAM side
output wire [$clog2(depth)-1:0] o_waddr,
output wire [width-1:0] o_wdata,
output wire o_wen,
output wire [$clog2(depth)-1:0] o_raddr,
input wire [width-1:0] i_rdata);
output wire [aw-1:0] o_waddr,
output wire [width-1:0] o_wdata,
output wire o_wen,
output wire [aw-1:0] o_raddr,
output wire o_ren,
input wire [width-1:0] i_rdata);
localparam ratio = width/W;
localparam CMSB = 4-$clog2(W); //Counter MSB
localparam l2r = $clog2(ratio);
reg rgnt;
assign o_ready = rgnt | i_wreq;
reg [4:0] rcnt;
reg [CMSB:0] rcnt;
reg rtrig1;
/*
********** Write side ***********
*/
wire [4:0] wcnt;
wire [CMSB:0] wcnt;
reg [width-2:0] wdata0_r;
reg [width-1:0] wdata1_r;
reg [width-1:0] wdata0_r;
reg [width+W-1:0] wdata1_r;
reg wen0_r;
reg wen1_r;
wire wtrig0;
wire wtrig1;
generate if (width == 2) begin
assign wtrig0 = ~wcnt[0];
assign wtrig0 = rtrig1;
generate if (ratio == 2) begin : gen_wtrig_ratio_eq_2
assign wtrig1 = wcnt[0];
end else begin
end else begin : gen_wtrig_ratio_neq_2
reg wtrig0_r;
always @(posedge i_clk) wtrig0_r <= wtrig0;
assign wtrig0 = (wcnt[l2w-1:0] == {{l2w-1{1'b1}},1'b0});
assign wtrig1 = wtrig0_r;
end
endgenerate
assign o_wdata = wtrig1 ?
wdata1_r :
{i_wdata0, wdata0_r};
wdata1_r[width-1:0] :
wdata0_r;
wire [$clog2(32+csr_regs)-1:0] wreg = wtrig1 ? i_wreg1 : i_wreg0;
generate if (width == 32)
assign o_waddr = wreg;
else
assign o_waddr = {wreg, wcnt[4:l2w]};
wire [raw-1:0] wreg = wtrig1 ? i_wreg1 : i_wreg0;
generate if (width == 32) begin : gen_w_eq_32
assign o_waddr = wreg;
end else begin : gen_w_neq_32
assign o_waddr = {wreg, wcnt[CMSB:l2r]};
end
endgenerate
assign o_wen = (wtrig0 & wen0_r) | (wtrig1 & wen1_r);
generate if (width > 2)
always @(posedge i_clk) wdata0_r <= {i_wdata0, wdata0_r[width-2:1]};
else
always @(posedge i_clk) wdata0_r <= i_wdata0;
endgenerate
assign wcnt = rcnt-3;
assign wcnt = rcnt-4;
always @(posedge i_clk) begin
wen0_r <= i_wen0;
wen1_r <= i_wen1;
if (wcnt[0]) begin
wen0_r <= i_wen0;
wen1_r <= i_wen1;
end
wdata1_r <= {i_wdata1,wdata1_r[width-1:1]};
wdata0_r <= {i_wdata0,wdata0_r[width-1:W]};
wdata1_r <= {i_wdata1,wdata1_r[width+W-1:W]};
end
@ -93,54 +110,67 @@ module serv_rf_ram_if
wire rtrig0;
reg rtrig1;
wire [$clog2(32+csr_regs)-1:0] rreg = rtrig0 ? i_rreg1 : i_rreg0;
generate if (width == 32)
assign o_raddr = rreg;
else
assign o_raddr = {rreg, rcnt[4:l2w]};
wire [raw-1:0] rreg = rtrig0 ? i_rreg1 : i_rreg0;
generate if (width == 32) begin : gen_rreg_eq_32
assign o_raddr = rreg;
end else begin : gen_rreg_neq_32
assign o_raddr = {rreg, rcnt[CMSB:l2r]};
end
endgenerate
reg [width-1:0] rdata0;
reg [width-2:0] rdata1;
reg [width-1-W:0] rdata1;
assign o_rdata0 = rdata0[0];
assign o_rdata1 = rtrig1 ? i_rdata[0] : rdata1[0];
reg rgate;
assign rtrig0 = (rcnt[l2w-1:0] == 1);
assign o_rdata0 = rdata0[B:0];
assign o_rdata1 = rtrig1 ? i_rdata[B:0] : rdata1[B:0];
assign rtrig0 = (rcnt[l2r-1:0] == 1);
generate if (ratio == 2) begin : gen_ren_w_eq_2
assign o_ren = rgate;
end else begin : gen_ren_w_neq_2
assign o_ren = rgate & (rcnt[l2r-1:1] == 0);
end
endgenerate
reg rreq_r;
generate if (width>2)
always @(posedge i_clk) begin
rdata1 <= {1'b0,rdata1[width-2:1]}; //Optimize?
if (rtrig1)
rdata1[width-2:0] <= i_rdata[width-1:1];
end
else
always @(posedge i_clk) if (rtrig1) rdata1 <= i_rdata[1];
generate if (ratio > 2) begin : gen_rdata1_w_neq_2
always @(posedge i_clk) begin
rdata1 <= {{W{1'b0}},rdata1[width-W-1:W]};
if (rtrig1)
rdata1[width-W-1:0] <= i_rdata[width-1:W];
end
end else begin : gen_rdata1_w_eq_2
always @(posedge i_clk) if (rtrig1) rdata1 <= i_rdata[W*2-1:W];
end
endgenerate
always @(posedge i_clk) begin
if (&rcnt | i_rreq)
rgate <= i_rreq;
rtrig1 <= rtrig0;
rcnt <= rcnt+5'd1;
if (i_rreq)
rcnt <= 5'd0;
if (i_wreq)
rcnt <= 5'd2;
rcnt <= rcnt+{{CMSB{1'b0}},1'b1};
if (i_rreq | i_wreq)
rcnt <= {{CMSB-1{1'b0}},i_wreq,1'b0};
rreq_r <= i_rreq;
rgnt <= rreq_r;
rdata0 <= {1'b0,rdata0[width-1:1]};
rdata0 <= {{W{1'b0}}, rdata0[width-1:W]};
if (rtrig0)
rdata0 <= i_rdata;
if (i_rst) begin
if (reset_strategy != "NONE") begin
rgate <= 1'b0;
rgnt <= 1'b0;
rreq_r <= 1'b0;
rcnt <= {CMSB+1{1'b0}};
end
end
end

View file

@ -2,7 +2,19 @@
module serv_rf_top
#(parameter RESET_PC = 32'd0,
/* COMPRESSED=1: Enable the compressed decoder and allowed misaligned jump of pc
COMPRESSED=0: Disable the compressed decoder and does not allow the misaligned jump of pc
*/
parameter [0:0] COMPRESSED = 0,
/*
ALIGN = 1: Fetch the aligned instruction by making two bus transactions if the misaligned address
is given to the instruction bus.
*/
parameter [0:0] ALIGN = COMPRESSED,
/* Multiplication and Division Unit
This parameter enables the interface for connecting SERV and MDU
*/
parameter [0:0] MDU = 0,
/* 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
@ -15,8 +27,10 @@ module serv_rf_top
restart execution from the instruction at RESET_PC
*/
parameter RESET_STRATEGY = "MINI",
parameter [0:0] DEBUG = 1'b0,
parameter WITH_CSR = 1,
parameter RF_WIDTH = 2,
parameter W = 1,
parameter RF_WIDTH = W * 2,
parameter RF_L2D = $clog2((32+(WITH_CSR*4))*32/RF_WIDTH))
(
input wire clk,
@ -55,7 +69,16 @@ module serv_rf_top
output wire o_dbus_we ,
output wire o_dbus_cyc,
input wire [31:0] i_dbus_rdt,
input wire i_dbus_ack);
input wire i_dbus_ack,
// Extension
output wire [31:0] o_ext_rs1,
output wire [31:0] o_ext_rs2,
output wire [ 2:0] o_ext_funct3,
input wire [31:0] i_ext_rd,
input wire i_ext_ready,
// MDU
output wire o_mdu_valid);
localparam CSR_REGS = WITH_CSR*4;
@ -65,24 +88,26 @@ module serv_rf_top
wire [4+WITH_CSR:0] wreg1;
wire wen0;
wire wen1;
wire wdata0;
wire wdata1;
wire [W-1:0] wdata0;
wire [W-1:0] wdata1;
wire [4+WITH_CSR:0] rreg0;
wire [4+WITH_CSR:0] rreg1;
wire rf_ready;
wire rdata0;
wire rdata1;
wire [W-1:0] rdata0;
wire [W-1:0] rdata1;
wire [RF_L2D-1:0] waddr;
wire [RF_WIDTH-1:0] wdata;
wire wen;
wire [RF_L2D-1:0] raddr;
wire ren;
wire [RF_WIDTH-1:0] rdata;
serv_rf_ram_if
#(.width (RF_WIDTH),
.reset_strategy (RESET_STRATEGY),
.csr_regs (CSR_REGS))
.csr_regs (CSR_REGS),
.W(W))
rf_ram_if
(.i_clk (clk),
.i_rst (i_rst),
@ -103,6 +128,7 @@ module serv_rf_top
.o_wdata (wdata),
.o_wen (wen),
.o_raddr (raddr),
.o_ren (ren),
.i_rdata (rdata));
serv_rf_ram
@ -114,13 +140,19 @@ module serv_rf_top
.i_wdata (wdata),
.i_wen (wen),
.i_raddr (raddr),
.i_ren (ren),
.o_rdata (rdata));
serv_top
#(.RESET_PC (RESET_PC),
.PRE_REGISTER (PRE_REGISTER),
.RESET_STRATEGY (RESET_STRATEGY),
.WITH_CSR (WITH_CSR))
.WITH_CSR (WITH_CSR),
.DEBUG (DEBUG),
.MDU(MDU),
.COMPRESSED(COMPRESSED),
.ALIGN(ALIGN),
.W(W))
cpu
(
.clk (clk),
@ -174,7 +206,16 @@ module serv_rf_top
.o_dbus_we (o_dbus_we),
.o_dbus_cyc (o_dbus_cyc),
.i_dbus_rdt (i_dbus_rdt),
.i_dbus_ack (i_dbus_ack));
.i_dbus_ack (i_dbus_ack),
//Extension
.o_ext_funct3 (o_ext_funct3),
.i_ext_ready (i_ext_ready),
.i_ext_rd (i_ext_rd),
.o_ext_rs1 (o_ext_rs1),
.o_ext_rs2 (o_ext_rs2),
//MDU
.o_mdu_valid (o_mdu_valid));
endmodule
`default_nettype wire

View file

@ -1,70 +1,85 @@
module serv_state
#(parameter RESET_STRATEGY = "MINI",
parameter [0:0] WITH_CSR = 1)
parameter [0:0] WITH_CSR = 1,
parameter [0:0] ALIGN =0,
parameter [0:0] MDU = 0,
parameter W = 1
)
(
input wire i_clk,
input wire i_rst,
//State
input wire i_new_irq,
input wire i_dbus_ack,
output wire o_ibus_cyc,
input wire i_ibus_ack,
output wire o_rf_rreq,
output wire o_rf_wreq,
input wire i_rf_ready,
output wire o_rf_rd_en,
input wire i_cond_branch,
input wire i_bne_or_bge,
input wire i_alu_cmp,
input wire i_branch_op,
input wire i_mem_op,
input wire i_shift_op,
input wire i_sh_right,
input wire i_slt_op,
input wire i_e_op,
input wire i_rd_op,
output wire o_init,
output wire o_cnt_en,
output wire o_cnt0,
output wire o_cnt_en,
output wire o_cnt0to3,
output wire o_cnt12to31,
output wire o_cnt0,
output wire o_cnt1,
output wire o_cnt2,
output wire o_cnt3,
output wire o_cnt7,
output wire o_cnt11,
output wire o_cnt12,
output wire o_cnt_done,
output wire o_bufreg_en,
output wire o_ctrl_pc_en,
output reg o_ctrl_jump,
output wire o_ctrl_trap,
input wire i_ctrl_misalign,
input wire i_sh_done,
input wire i_sh_done_r,
output wire o_dbus_cyc,
output wire [1:0] o_mem_bytecnt,
input wire i_mem_misalign,
output reg o_cnt_done,
output wire o_bufreg_en);
//Control
input wire i_bne_or_bge,
input wire i_cond_branch,
input wire i_dbus_en,
input wire i_two_stage_op,
input wire i_branch_op,
input wire i_shift_op,
input wire i_sh_right,
input wire i_alu_rd_sel1,
input wire i_rd_alu_en,
input wire i_e_op,
input wire i_rd_op,
//MDU
input wire i_mdu_op,
output wire o_mdu_valid,
//Extension
input wire i_mdu_ready,
//External
output wire o_dbus_cyc,
input wire i_dbus_ack,
output wire o_ibus_cyc,
input wire i_ibus_ack,
//RF Interface
output wire o_rf_rreq,
output wire o_rf_wreq,
input wire i_rf_ready,
output wire o_rf_rd_en);
reg stage_two_req;
reg init_done;
wire misalign_trap_sync;
reg [4:2] o_cnt;
reg [3:0] o_cnt_r;
wire [3:0] cnt_r;
reg ibus_cyc;
//Update PC in RUN or TRAP states
assign o_ctrl_pc_en = o_cnt_en & !o_init;
assign o_cnt_en = |o_cnt_r;
assign o_mem_bytecnt = o_cnt[4:3];
assign o_cnt0to3 = (o_cnt[4:2] == 3'd0);
assign o_cnt12to31 = (o_cnt[4] | (o_cnt[3:2] == 2'b11));
assign o_cnt0 = (o_cnt[4:2] == 3'd0) & o_cnt_r[0];
assign o_cnt1 = (o_cnt[4:2] == 3'd0) & o_cnt_r[1];
assign o_cnt2 = (o_cnt[4:2] == 3'd0) & o_cnt_r[2];
assign o_cnt3 = (o_cnt[4:2] == 3'd0) & o_cnt_r[3];
assign o_cnt7 = (o_cnt[4:2] == 3'd1) & o_cnt_r[3];
assign o_cnt0 = (o_cnt[4:2] == 3'd0) & cnt_r[0];
assign o_cnt1 = (o_cnt[4:2] == 3'd0) & cnt_r[1];
assign o_cnt2 = (o_cnt[4:2] == 3'd0) & cnt_r[2];
assign o_cnt3 = (o_cnt[4:2] == 3'd0) & cnt_r[3];
assign o_cnt7 = (o_cnt[4:2] == 3'd1) & cnt_r[3];
assign o_cnt11 = (o_cnt[4:2] == 3'd2) & cnt_r[3];
assign o_cnt12 = (o_cnt[4:2] == 3'd3) & cnt_r[0];
//Take branch for jump or branch instructions (opcode == 1x0xx) if
//a) It's an unconditional branch (opcode[0] == 1)
@ -74,46 +89,55 @@ module serv_state
//been calculated.
wire take_branch = i_branch_op & (!i_cond_branch | (i_alu_cmp^i_bne_or_bge));
//slt*, branch/jump, shift, load/store
wire two_stage_op = i_slt_op | i_mem_op | i_branch_op | i_shift_op;
wire last_init = o_cnt_done & o_init;
assign o_dbus_cyc = !o_cnt_en & init_done & i_mem_op & !i_mem_misalign;
//Prepare RF for reads when a new instruction is fetched
// or when stage one caused an exception (rreq implies a write request too)
assign o_rf_rreq = i_ibus_ack | (stage_two_req & misalign_trap_sync);
//valid signal for mdu
assign o_mdu_valid = MDU & !o_cnt_en & init_done & i_mdu_op;
//Prepare RF for writes when everything is ready to enter stage two
// and the first stage didn't cause a misalign exception
assign o_rf_wreq = !misalign_trap_sync &
((i_shift_op & (i_sh_done | !i_sh_right) & !o_cnt_en & init_done) |
(i_mem_op & i_dbus_ack) |
(stage_two_req & (i_slt_op | i_branch_op)));
//Left shifts, SLT & Branch ops. First cycle after init
//Right shift. o_sh_done
//Mem ops. i_dbus_ack
//MDU ops. i_mdu_ready
assign o_rf_wreq = (i_shift_op & (i_sh_right ? (i_sh_done & (last_init | !o_cnt_en & init_done)) : last_init)) |
i_dbus_ack | (MDU & i_mdu_ready) |
(i_branch_op & (last_init & !trap_pending)) |
(i_rd_alu_en & i_alu_rd_sel1 & last_init);
assign o_dbus_cyc = !o_cnt_en & init_done & i_dbus_en & !i_mem_misalign;
//Prepare RF for reads when a new instruction is fetched
// or when stage one caused an exception (rreq implies a write request too)
assign o_rf_rreq = i_ibus_ack | (trap_pending & last_init);
assign o_rf_rd_en = i_rd_op & !o_init;
/*
bufreg is used during mem. branch and shift operations
bufreg is used during mem, branch, and shift operations
mem : bufreg is used for dbus address. Shift in data during phase 1.
Shift out during phase 2 if there was an misalignment exception.
Shift out during phase 2 if there was a misalignment exception.
branch : Shift in during phase 1. Shift out during phase 2
shift : Shift in during phase 1. Continue shifting between phases (except
for the first cycle after init). Shift out during phase 2
*/
assign o_bufreg_en = (o_cnt_en & (o_init | o_ctrl_trap | i_branch_op)) | (i_shift_op & !stage_two_req & (i_sh_right | i_sh_done_r));
assign o_bufreg_en = (o_cnt_en & (o_init | ((o_ctrl_trap | i_branch_op) & i_two_stage_op))) | (i_shift_op & init_done & (i_sh_right | i_sh_done));
assign o_ibus_cyc = ibus_cyc & !i_rst;
assign o_init = two_stage_op & !i_new_irq & !init_done;
assign o_init = i_two_stage_op & !i_new_irq & !init_done;
assign o_cnt_done = (o_cnt[4:2] == 3'b111) & cnt_r[3];
always @(posedge i_clk) begin
//ibus_cyc changes on three conditions.
//1. i_rst is asserted. Together with the async gating above, o_ibus_cyc
// will be asserted as soon as the reset is released. This is how the
// first instruction is fetced
// first instruction is fetched
//2. o_cnt_done and o_ctrl_pc_en are asserted. This means that SERV just
// finished updating the PC, is done with the current instruction and
// o_ibus_cyc gets asserted to fetch a new instruction
@ -126,23 +150,28 @@ module serv_state
init_done <= o_init & !init_done;
o_ctrl_jump <= o_init & take_branch;
end
o_cnt_done <= (o_cnt[4:2] == 3'b111) & o_cnt_r[2];
//Need a strobe for the first cycle in the IDLE state after INIT
stage_two_req <= o_cnt_done & o_init;
if (i_rst) begin
if (RESET_STRATEGY != "NONE") begin
init_done <= 1'b0;
o_ctrl_jump <= 1'b0;
end
end
end
generate
/*
Because SERV is 32-bit bit-serial we need a counter than can count 0-31
to keep track of which bit we are currently processing. o_cnt and o_cnt_r
to keep track of which bit we are currently processing. o_cnt and cnt_r
are used together to create such a counter.
The top three bits (o_cnt) are implemented as a normal counter, but
instead of the two LSB, o_cnt_r is a 4-bit shift register which loops 0-3
When o_cnt_r[3] is 1, o_cnt will be increased.
instead of the two LSB, cnt_r is a 4-bit shift register which loops 0-3
When cnt_r[3] is 1, o_cnt will be increased.
The counting starts when the core is idle and the i_rf_ready signal
comes in from the RF module by shifting in the i_rf_ready bit as LSB of
the shift register. Counting is stopped by using o_cnt_done to block the
bit that was supposed to be shifted into bit 0 of o_cnt_r.
bit that was supposed to be shifted into bit 0 of cnt_r.
There are two benefit of doing the counter this way
1. We only need to check four bits instead of five when we want to check
@ -150,41 +179,55 @@ module serv_state
we only need one LUT instead of two for each comparison.
2. We don't need a separate enable signal to turn on and off the counter
between stages, which saves an extra FF and a unique control signal. We
just need to check if o_cnt_r is not zero to see if the counter is
just need to check if cnt_r is not zero to see if the counter is
currently running
*/
o_cnt <= o_cnt + {2'd0,o_cnt_r[3]};
o_cnt_r <= {o_cnt_r[2:0],(o_cnt_r[3] & !o_cnt_done) | (i_rf_ready & !o_cnt_en)};
if (i_rst) begin
if (RESET_STRATEGY != "NONE") begin
o_cnt <= 3'd0;
init_done <= 1'b0;
o_ctrl_jump <= 1'b0;
o_cnt_r <= 4'b0000;
if (W == 1) begin : gen_cnt_w_eq_1
reg [3:0] cnt_lsb;
always @(posedge i_clk) begin
o_cnt <= o_cnt + {2'd0,cnt_r[3]};
cnt_lsb <= {cnt_lsb[2:0],(cnt_lsb[3] & !o_cnt_done) | i_rf_ready};
if (i_rst & (RESET_STRATEGY != "NONE")) begin
o_cnt <= 3'd0;
cnt_lsb <= 4'b0000;
end
end
assign cnt_r = cnt_lsb;
assign o_cnt_en = |cnt_lsb;
end else if (W == 4) begin : gen_cnt_w_eq_4
reg cnt_en;
always @(posedge i_clk) begin
if (i_rf_ready) cnt_en <= 1; else
if (o_cnt_done) cnt_en <= 0;
o_cnt <= o_cnt + { 2'd0, cnt_en };
if (i_rst & (RESET_STRATEGY != "NONE")) begin
o_cnt <= 3'd0;
cnt_en <= 1'b0;
end
end
assign cnt_r = 4'b1111;
assign o_cnt_en = cnt_en;
end
end
endgenerate
assign o_ctrl_trap = WITH_CSR & (i_e_op | i_new_irq | misalign_trap_sync);
generate
if (WITH_CSR) begin
reg misalign_trap_sync_r;
//trap_pending is only guaranteed to have correct value during the
// last cycle of the init stage
wire trap_pending = WITH_CSR & ((take_branch & i_ctrl_misalign) |
(i_mem_op & i_mem_misalign));
wire trap_pending = WITH_CSR & ((take_branch & i_ctrl_misalign & !ALIGN) |
(i_dbus_en & i_mem_misalign));
generate
if (WITH_CSR) begin : gen_csr
reg misalign_trap_sync_r;
always @(posedge i_clk) begin
if (o_cnt_done)
misalign_trap_sync_r <= trap_pending & o_init;
if (i_rst)
if (RESET_STRATEGY != "NONE")
misalign_trap_sync_r <= 1'b0;
if (i_ibus_ack | o_cnt_done | i_rst)
misalign_trap_sync_r <= !(i_ibus_ack | i_rst) & ((trap_pending & o_init) | misalign_trap_sync_r);
end
assign misalign_trap_sync = misalign_trap_sync_r;
end else
assign misalign_trap_sync = 1'b0;
end else begin : gen_no_csr
assign misalign_trap_sync = 1'b0;
end
endgenerate
endmodule

132
rtl/serv_synth_wrapper.v Normal file
View file

@ -0,0 +1,132 @@
`default_nettype none
module serv_synth_wrapper
#(
/* 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
*/
parameter PRE_REGISTER = 1,
/* 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
*/
parameter RESET_STRATEGY = "MINI",
parameter WITH_CSR = 1,
parameter RF_WIDTH = 2,
parameter RF_L2D = $clog2((32+(WITH_CSR*4))*32/RF_WIDTH))
(
input wire clk,
input wire i_rst,
input wire i_timer_irq,
output wire [31:0] o_ibus_adr,
output wire o_ibus_cyc,
input wire [31:0] i_ibus_rdt,
input wire i_ibus_ack,
output wire [31:0] o_dbus_adr,
output wire [31:0] o_dbus_dat,
output wire [3:0] o_dbus_sel,
output wire o_dbus_we ,
output wire o_dbus_cyc,
input wire [31:0] i_dbus_rdt,
input wire i_dbus_ack,
output wire [RF_L2D-1:0] o_waddr,
output wire [RF_WIDTH-1:0] o_wdata,
output wire o_wen,
output wire [RF_L2D-1:0] o_raddr,
input wire [RF_WIDTH-1:0] i_rdata);
localparam CSR_REGS = WITH_CSR*4;
wire rf_wreq;
wire rf_rreq;
wire [4+WITH_CSR:0] wreg0;
wire [4+WITH_CSR:0] wreg1;
wire wen0;
wire wen1;
wire wdata0;
wire wdata1;
wire [4+WITH_CSR:0] rreg0;
wire [4+WITH_CSR:0] rreg1;
wire rf_ready;
wire rdata0;
wire rdata1;
serv_rf_ram_if
#(.width (RF_WIDTH),
.reset_strategy (RESET_STRATEGY),
.csr_regs (CSR_REGS))
rf_ram_if
(.i_clk (clk),
.i_rst (i_rst),
.i_wreq (rf_wreq),
.i_rreq (rf_rreq),
.o_ready (rf_ready),
.i_wreg0 (wreg0),
.i_wreg1 (wreg1),
.i_wen0 (wen0),
.i_wen1 (wen1),
.i_wdata0 (wdata0),
.i_wdata1 (wdata1),
.i_rreg0 (rreg0),
.i_rreg1 (rreg1),
.o_rdata0 (rdata0),
.o_rdata1 (rdata1),
.o_waddr (o_waddr),
.o_wdata (o_wdata),
.o_wen (o_wen),
.o_raddr (o_raddr),
.i_rdata (i_rdata));
serv_top
#(.RESET_PC (32'd0),
.PRE_REGISTER (PRE_REGISTER),
.RESET_STRATEGY (RESET_STRATEGY),
.WITH_CSR (WITH_CSR),
.MDU(1'b0))
cpu
(
.clk (clk),
.i_rst (i_rst),
.i_timer_irq (i_timer_irq),
.o_rf_rreq (rf_rreq),
.o_rf_wreq (rf_wreq),
.i_rf_ready (rf_ready),
.o_wreg0 (wreg0),
.o_wreg1 (wreg1),
.o_wen0 (wen0),
.o_wen1 (wen1),
.o_wdata0 (wdata0),
.o_wdata1 (wdata1),
.o_rreg0 (rreg0),
.o_rreg1 (rreg1),
.i_rdata0 (rdata0),
.i_rdata1 (rdata1),
.o_ibus_adr (o_ibus_adr),
.o_ibus_cyc (o_ibus_cyc),
.i_ibus_rdt (i_ibus_rdt),
.i_ibus_ack (i_ibus_ack),
.o_dbus_adr (o_dbus_adr),
.o_dbus_dat (o_dbus_dat),
.o_dbus_sel (o_dbus_sel),
.o_dbus_we (o_dbus_we),
.o_dbus_cyc (o_dbus_cyc),
.i_dbus_rdt (i_dbus_rdt),
.i_dbus_ack (i_dbus_ack),
//Extension
.o_ext_funct3 (),
.i_ext_ready (1'b0),
.i_ext_rd (32'd0),
.o_ext_rs1 (),
.o_ext_rs2 (),
//MDU
.o_mdu_valid ());
endmodule
`default_nettype wire

View file

@ -1,36 +1,42 @@
`default_nettype none
module serv_top
#(parameter WITH_CSR = 1,
parameter PRE_REGISTER = 1,
parameter RESET_STRATEGY = "MINI",
parameter RESET_PC = 32'd0)
#(parameter WITH_CSR = 1,
parameter W = 1,
parameter B = W-1,
parameter PRE_REGISTER = 1,
parameter RESET_STRATEGY = "MINI",
parameter RESET_PC = 32'd0,
parameter [0:0] DEBUG = 1'b0,
parameter [0:0] MDU = 1'b0,
parameter [0:0] COMPRESSED=0,
parameter [0:0] ALIGN = COMPRESSED)
(
input wire clk,
input wire i_rst,
input wire i_timer_irq,
`ifdef RISCV_FORMAL
output reg rvfi_valid = 1'b0,
output reg [63:0] rvfi_order = 64'd0,
output reg [31:0] rvfi_insn = 32'd0,
output reg rvfi_trap = 1'b0,
output reg rvfi_halt = 1'b0,
output reg rvfi_intr = 1'b0,
output reg [1:0] rvfi_mode = 2'b11,
output reg [1:0] rvfi_ixl = 2'b01,
output reg [4:0] rvfi_rs1_addr,
output reg [4:0] rvfi_rs2_addr,
output reg [31:0] rvfi_rs1_rdata,
output reg [31:0] rvfi_rs2_rdata,
output reg [4:0] rvfi_rd_addr,
output reg [31:0] rvfi_rd_wdata,
output reg [31:0] rvfi_pc_rdata,
output reg [31:0] rvfi_pc_wdata,
output reg [31:0] rvfi_mem_addr,
output reg [3:0] rvfi_mem_rmask,
output reg [3:0] rvfi_mem_wmask,
output reg [31:0] rvfi_mem_rdata,
output reg [31:0] rvfi_mem_wdata,
output wire rvfi_valid,
output wire [63:0] rvfi_order,
output wire [31:0] rvfi_insn,
output wire rvfi_trap,
output wire rvfi_halt,
output wire rvfi_intr,
output wire [1:0] rvfi_mode,
output wire [1:0] rvfi_ixl,
output wire [4:0] rvfi_rs1_addr,
output wire [4:0] rvfi_rs2_addr,
output wire [31:0] rvfi_rs1_rdata,
output wire [31:0] rvfi_rs2_rdata,
output wire [4:0] rvfi_rd_addr,
output wire [31:0] rvfi_rd_wdata,
output wire [31:0] rvfi_pc_rdata,
output wire [31:0] rvfi_pc_wdata,
output wire [31:0] rvfi_mem_addr,
output wire [3:0] rvfi_mem_rmask,
output wire [3:0] rvfi_mem_wmask,
output wire [31:0] rvfi_mem_rdata,
output wire [31:0] rvfi_mem_wdata,
`endif
//RF Interface
output wire o_rf_rreq,
@ -40,12 +46,12 @@ module serv_top
output wire [4+WITH_CSR:0] o_wreg1,
output wire o_wen0,
output wire o_wen1,
output wire o_wdata0,
output wire o_wdata1,
output wire [B:0] o_wdata0,
output wire [B:0] o_wdata1,
output wire [4+WITH_CSR:0] o_rreg0,
output wire [4+WITH_CSR:0] o_rreg1,
input wire i_rdata0,
input wire i_rdata1,
input wire [B:0] i_rdata0,
input wire [B:0] i_rdata1,
output wire [31:0] o_ibus_adr,
output wire o_ibus_cyc,
@ -57,7 +63,15 @@ module serv_top
output wire o_dbus_we ,
output wire o_dbus_cyc,
input wire [31:0] i_dbus_rdt,
input wire i_dbus_ack);
input wire i_dbus_ack,
//Extension
output wire [ 2:0] o_ext_funct3,
input wire i_ext_ready,
input wire [31:0] i_ext_rd,
output wire [31:0] o_ext_rs1,
output wire [31:0] o_ext_rs2,
//MDU
output wire o_mdu_valid);
wire [4:0] rd_addr;
wire [4:0] rs1_addr;
@ -69,29 +83,32 @@ module serv_top
wire sh_right;
wire bne_or_bge;
wire cond_branch;
wire two_stage_op;
wire e_op;
wire ebreak;
wire branch_op;
wire mem_op;
wire shift_op;
wire slt_op;
wire rd_op;
wire mdu_op;
wire rd_alu_en;
wire rd_csr_en;
wire ctrl_rd;
wire alu_rd;
wire mem_rd;
wire csr_rd;
wire rd_mem_en;
wire [B:0] ctrl_rd;
wire [B:0] alu_rd;
wire [B:0] mem_rd;
wire [B:0] csr_rd;
wire mtval_pc;
wire ctrl_pc_en;
wire jump;
wire jal_or_jalr;
wire utype;
wire mret;
wire imm;
wire [B:0] imm;
wire trap;
wire pc_rel;
wire iscomp;
wire init;
wire cnt_en;
@ -102,6 +119,8 @@ module serv_top
wire cnt2;
wire cnt3;
wire cnt7;
wire cnt11;
wire cnt12;
wire cnt_done;
@ -110,7 +129,10 @@ module serv_top
wire bufreg_rs1_en;
wire bufreg_imm_en;
wire bufreg_clr_lsb;
wire bufreg_q;
wire [B:0] bufreg_q;
wire [B:0] bufreg2_q;
wire [31:0] dbus_rdt;
wire dbus_ack;
wire alu_sub;
wire [1:0] alu_bool_op;
@ -119,45 +141,93 @@ module serv_top
wire alu_cmp;
wire [2:0] alu_rd_sel;
wire rs1;
wire rs2;
wire [B:0] rs1;
wire [B:0] rs2;
wire rd_en;
wire op_b_source;
wire [B:0] op_b;
wire op_b_sel;
wire mem_signed;
wire mem_word;
wire mem_half;
wire [1:0] mem_bytecnt;
wire mem_sh_done;
wire mem_sh_done_r;
wire sh_done;
wire mem_misalign;
wire bad_pc;
wire [B:0] bad_pc;
wire csr_mstatus_en;
wire csr_mie_en;
wire csr_mcause_en;
wire [1:0] csr_source;
wire csr_imm;
wire [B:0] csr_imm;
wire csr_d_sel;
wire csr_en;
wire [1:0] csr_addr;
wire csr_pc;
wire [B:0] csr_pc;
wire csr_imm_en;
wire csr_in;
wire rf_csr_out;
wire [B:0] csr_in;
wire [B:0] rf_csr_out;
wire dbus_en;
wire new_irq;
wire [1:0] lsb;
wire op_b = op_b_source ? rs2 : imm;
wire [31:0] i_wb_rdt;
wire [31:0] wb_ibus_adr;
wire wb_ibus_cyc;
wire [31:0] wb_ibus_rdt;
wire wb_ibus_ack;
generate
if (ALIGN) begin : gen_align
serv_aligner align
(
.clk(clk),
.rst(i_rst),
// serv_rf_top
.i_ibus_adr(wb_ibus_adr),
.i_ibus_cyc(wb_ibus_cyc),
.o_ibus_rdt(wb_ibus_rdt),
.o_ibus_ack(wb_ibus_ack),
// servant_arbiter
.o_wb_ibus_adr(o_ibus_adr),
.o_wb_ibus_cyc(o_ibus_cyc),
.i_wb_ibus_rdt(i_ibus_rdt),
.i_wb_ibus_ack(i_ibus_ack));
end else begin : gen_no_align
assign o_ibus_adr = wb_ibus_adr;
assign o_ibus_cyc = wb_ibus_cyc;
assign wb_ibus_rdt = i_ibus_rdt;
assign wb_ibus_ack = i_ibus_ack;
end
endgenerate
generate
if (COMPRESSED) begin : gen_compressed
serv_compdec compdec
(
.i_clk(clk),
.i_instr(wb_ibus_rdt),
.i_ack(wb_ibus_ack),
.o_instr(i_wb_rdt),
.o_iscomp(iscomp));
end else begin : gen_no_compressed
assign i_wb_rdt = wb_ibus_rdt;
assign iscomp = 1'b0;
end
endgenerate
serv_state
#(.RESET_STRATEGY (RESET_STRATEGY),
.WITH_CSR (WITH_CSR))
.WITH_CSR (WITH_CSR[0:0]),
.MDU(MDU),
.ALIGN(ALIGN),
.W(W))
state
(
.i_clk (clk),
@ -174,31 +244,39 @@ module serv_top
.o_cnt2 (cnt2),
.o_cnt3 (cnt3),
.o_cnt7 (cnt7),
.o_cnt11 (cnt11),
.o_cnt12 (cnt12),
.o_cnt_done (cnt_done),
.o_bufreg_en (bufreg_en),
.o_ctrl_pc_en (ctrl_pc_en),
.o_ctrl_jump (jump),
.o_ctrl_trap (trap),
.i_ctrl_misalign(lsb[1]),
.i_sh_done (mem_sh_done),
.i_sh_done_r (mem_sh_done_r),
.i_sh_done (sh_done),
.o_mem_bytecnt (mem_bytecnt),
.i_mem_misalign (mem_misalign),
//Control
.i_bne_or_bge (bne_or_bge),
.i_cond_branch (cond_branch),
.i_dbus_en (dbus_en),
.i_two_stage_op (two_stage_op),
.i_branch_op (branch_op),
.i_mem_op (mem_op),
.i_shift_op (shift_op),
.i_sh_right (sh_right),
.i_slt_op (slt_op),
.i_alu_rd_sel1 (alu_rd_sel[1]),
.i_rd_alu_en (rd_alu_en),
.i_e_op (e_op),
.i_rd_op (rd_op),
//MDU
.i_mdu_op (mdu_op),
.o_mdu_valid (o_mdu_valid),
//Extension
.i_mdu_ready (i_ext_ready),
//External
.o_dbus_cyc (o_dbus_cyc),
.i_dbus_ack (i_dbus_ack),
.o_ibus_cyc (o_ibus_cyc),
.i_ibus_ack (i_ibus_ack),
.o_ibus_cyc (wb_ibus_cyc),
.i_ibus_ack (wb_ibus_ack),
//RF Interface
.o_rf_rreq (o_rf_rreq),
.o_rf_wreq (o_rf_wreq),
@ -206,36 +284,42 @@ module serv_top
.o_rf_rd_en (rd_en));
serv_decode
#(.PRE_REGISTER (PRE_REGISTER))
#(.PRE_REGISTER (PRE_REGISTER),
.MDU(MDU))
decode
(
.clk (clk),
//Input
.i_wb_rdt (i_ibus_rdt[31:2]),
.i_wb_en (i_ibus_ack),
.i_wb_rdt (i_wb_rdt[31:2]),
.i_wb_en (wb_ibus_ack),
//To state
.o_bne_or_bge (bne_or_bge),
.o_cond_branch (cond_branch),
.o_dbus_en (dbus_en),
.o_e_op (e_op),
.o_ebreak (ebreak),
.o_branch_op (branch_op),
.o_mem_op (mem_op),
.o_shift_op (shift_op),
.o_slt_op (slt_op),
.o_rd_op (rd_op),
.o_sh_right (sh_right),
.o_mdu_op (mdu_op),
.o_two_stage_op (two_stage_op),
//Extension
.o_ext_funct3 (o_ext_funct3),
//To bufreg
.o_bufreg_rs1_en (bufreg_rs1_en),
.o_bufreg_imm_en (bufreg_imm_en),
.o_bufreg_clr_lsb (bufreg_clr_lsb),
.o_bufreg_sh_signed (bufreg_sh_signed),
//To bufreg2
.o_op_b_source (op_b_sel),
//To ctrl
.o_ctrl_jal_or_jalr (jal_or_jalr),
.o_ctrl_utype (utype),
.o_ctrl_pc_rel (pc_rel),
.o_ctrl_mret (mret),
//To alu
.o_op_b_source (op_b_source),
.o_alu_sub (alu_sub),
.o_alu_bool_op (alu_bool_op),
.o_alu_cmp_eq (alu_cmp_eq),
@ -255,9 +339,12 @@ module serv_top
.o_csr_source (csr_source),
.o_csr_d_sel (csr_d_sel),
.o_csr_imm_en (csr_imm_en),
.o_mtval_pc (mtval_pc ),
//To top
.o_immdec_ctrl (immdec_ctrl),
.o_immdec_en (immdec_en),
//To RF IF
.o_rd_mem_en (rd_mem_en),
.o_rd_csr_en (rd_csr_en),
.o_rd_alu_en (rd_alu_en));
@ -278,10 +365,12 @@ module serv_top
.o_csr_imm (csr_imm),
.o_imm (imm),
//External
.i_wb_en (i_ibus_ack),
.i_wb_rdt (i_ibus_rdt[31:7]));
.i_wb_en (wb_ibus_ack),
.i_wb_rdt (i_wb_rdt[31:7]));
serv_bufreg bufreg
serv_bufreg
#(.MDU(MDU))
bufreg
(
.i_clk (clk),
//State
@ -289,6 +378,7 @@ module serv_top
.i_cnt1 (cnt1),
.i_en (bufreg_en),
.i_init (init),
.i_mdu_op (mdu_op),
.o_lsb (lsb),
//Control
.i_sh_signed (bufreg_sh_signed),
@ -300,12 +390,39 @@ module serv_top
.i_imm (imm),
.o_q (bufreg_q),
//External
.o_dbus_adr (o_dbus_adr));
.o_dbus_adr (o_dbus_adr),
.o_ext_rs1 (o_ext_rs1));
serv_bufreg2 bufreg2
(
.i_clk (clk),
//State
.i_en (cnt_en),
.i_init (init),
.i_cnt7 (cnt7),
.i_cnt_done (cnt_done),
.i_sh_right (sh_right),
.i_lsb (lsb),
.i_bytecnt (mem_bytecnt),
.o_sh_done (sh_done),
//Control
.i_op_b_sel (op_b_sel),
.i_shift_op (shift_op),
//Data
.i_rs2 (rs2),
.i_imm (imm),
.o_op_b (op_b),
.o_q (bufreg2_q),
//External
.o_dat (o_dbus_dat),
.i_load (dbus_ack),
.i_dat (dbus_rdt));
serv_ctrl
#(.RESET_PC (RESET_PC),
.RESET_STRATEGY (RESET_STRATEGY),
.WITH_CSR (WITH_CSR))
.WITH_CSR (WITH_CSR),
.W (W))
ctrl
(
.clk (clk),
@ -314,6 +431,7 @@ module serv_top
.i_pc_en (ctrl_pc_en),
.i_cnt12to31 (cnt12to31),
.i_cnt0 (cnt0),
.i_cnt1 (cnt1),
.i_cnt2 (cnt2),
//Control
.i_jump (jump),
@ -321,6 +439,7 @@ module serv_top
.i_utype (utype),
.i_pc_rel (pc_rel),
.i_trap (trap | mret),
.i_iscomp (iscomp),
//Data
.i_imm (imm),
.i_buf (bufreg_q),
@ -328,9 +447,9 @@ module serv_top
.o_rd (ctrl_rd),
.o_bad_pc (bad_pc),
//External
.o_ibus_adr (o_ibus_adr));
.o_ibus_adr (wb_ibus_adr));
serv_alu alu
serv_alu #(.W (W)) alu
(
.clk (clk),
//State
@ -350,7 +469,7 @@ module serv_top
.o_rd (alu_rd));
serv_rf_if
#(.WITH_CSR (WITH_CSR))
#(.WITH_CSR (WITH_CSR), .W(W))
rf_if
(//RF interface
.i_cnt_en (cnt_en),
@ -368,8 +487,8 @@ module serv_top
//Trap interface
.i_trap (trap),
.i_mret (mret),
.i_mepc (o_ibus_adr[0]),
.i_mem_op (mem_op),
.i_mepc (wb_ibus_adr[B:0]),
.i_mtval_pc (mtval_pc),
.i_bufreg_q (bufreg_q),
.i_bad_pc (bad_pc),
.o_csr_pc (csr_pc),
@ -386,6 +505,7 @@ module serv_top
.i_csr_rd (csr_rd),
.i_rd_csr_en (rd_csr_en),
.i_mem_rd (mem_rd),
.i_rd_mem_en (rd_mem_en),
//RS1 read port
.i_rs1_raddr (rs1_addr),
@ -398,47 +518,45 @@ module serv_top
.o_csr (rf_csr_out));
serv_mem_if
#(.WITH_CSR (WITH_CSR))
#(.WITH_CSR (WITH_CSR[0:0]),
.W (W))
mem_if
(
.i_clk (clk),
.i_clk (clk),
//State
.i_en (cnt_en),
.i_init (init),
.i_cnt_done (cnt_done),
.i_bytecnt (mem_bytecnt),
.i_lsb (lsb),
.o_misalign (mem_misalign),
.o_sh_done (mem_sh_done),
.o_sh_done_r (mem_sh_done_r),
.i_bytecnt (mem_bytecnt),
.i_lsb (lsb),
.o_misalign (mem_misalign),
//Control
.i_mem_op (mem_op),
.i_shift_op (shift_op),
.i_signed (mem_signed),
.i_word (mem_word),
.i_half (mem_half),
.i_mdu_op (mdu_op),
.i_signed (mem_signed),
.i_word (mem_word),
.i_half (mem_half),
//Data
.i_op_b (op_b),
.o_rd (mem_rd),
.i_bufreg2_q (bufreg2_q),
.o_rd (mem_rd),
//External interface
.o_wb_dat (o_dbus_dat),
.o_wb_sel (o_dbus_sel),
.i_wb_rdt (i_dbus_rdt),
.i_wb_ack (i_dbus_ack));
.o_wb_sel (o_dbus_sel));
generate
if (WITH_CSR) begin
serv_csr csr
if (|WITH_CSR) begin : gen_csr
serv_csr
#(.RESET_STRATEGY (RESET_STRATEGY),
.W(W))
csr
(
.i_clk (clk),
.i_rst (i_rst),
//State
.i_init (init),
.i_trig_irq (wb_ibus_ack),
.i_en (cnt_en),
.i_cnt0to3 (cnt0to3),
.i_cnt3 (cnt3),
.i_cnt7 (cnt7),
.i_cnt11 (cnt11),
.i_cnt12 (cnt12),
.i_cnt_done (cnt_done),
.i_mem_op (mem_op),
.i_mem_op (!mtval_pc),
.i_mtip (i_timer_irq),
.i_trap (trap),
.o_new_irq (new_irq),
@ -458,72 +576,88 @@ module serv_top
.i_csr_imm (csr_imm),
.i_rs1 (rs1),
.o_q (csr_rd));
end else begin
assign csr_in = 1'b0;
assign csr_rd = 1'b0;
end else begin : gen_no_csr
assign csr_in = {W{1'b0}};
assign csr_rd = {W{1'b0}};
assign new_irq = 1'b0;
end
endgenerate
generate
if (DEBUG) begin : gen_debug
serv_debug #(.W (W), .RESET_PC (RESET_PC)) debug
(
`ifdef RISCV_FORMAL
reg [31:0] pc = RESET_PC;
wire rs_en = (branch_op|mem_op|shift_op|slt_op) ? init : ctrl_pc_en;
always @(posedge clk) begin
rvfi_valid <= cnt_done & ctrl_pc_en & !i_rst;
rvfi_order <= rvfi_order + {63'd0,rvfi_valid};
if (o_ibus_cyc & i_ibus_ack)
rvfi_insn <= i_ibus_rdt;
if (o_wen0)
rvfi_rd_wdata <= {o_wdata0,rvfi_rd_wdata[31:1]};
if (cnt_done & ctrl_pc_en) begin
rvfi_pc_rdata <= pc;
if (!(rd_en & (|rd_addr))) begin
rvfi_rd_addr <= 5'd0;
rvfi_rd_wdata <= 32'd0;
end
end
rvfi_trap <= trap;
if (rvfi_valid) begin
rvfi_trap <= 1'b0;
pc <= rvfi_pc_wdata;
end
rvfi_halt <= 1'b0;
rvfi_intr <= 1'b0;
rvfi_mode <= 2'd3;
rvfi_ixl = 2'd1;
if (i_rf_ready) begin
rvfi_rs1_addr <= rs1_addr;
rvfi_rs2_addr <= rs2_addr;
rvfi_rd_addr <= rd_addr;
end
if (rs_en) begin
rvfi_rs1_rdata <= {rs1,rvfi_rs1_rdata[31:1]};
rvfi_rs2_rdata <= {rs2,rvfi_rs2_rdata[31:1]};
end
if (i_dbus_ack) begin
rvfi_mem_addr <= o_dbus_adr;
rvfi_mem_rmask <= o_dbus_we ? 4'b0000 : o_dbus_sel;
rvfi_mem_wmask <= o_dbus_we ? o_dbus_sel : 4'b0000;
rvfi_mem_rdata <= i_dbus_rdt;
rvfi_mem_wdata <= o_dbus_dat;
end
if (i_ibus_ack) begin
rvfi_mem_rmask <= 4'b0000;
rvfi_mem_wmask <= 4'b0000;
end
end
/* verilator lint_off COMBDLY */
always @(o_ibus_adr)
rvfi_pc_wdata <= o_ibus_adr;
/* verilator lint_on COMBDLY */
.rvfi_valid (rvfi_valid ),
.rvfi_order (rvfi_order ),
.rvfi_insn (rvfi_insn ),
.rvfi_trap (rvfi_trap ),
.rvfi_halt (rvfi_halt ),
.rvfi_intr (rvfi_intr ),
.rvfi_mode (rvfi_mode ),
.rvfi_ixl (rvfi_ixl ),
.rvfi_rs1_addr (rvfi_rs1_addr ),
.rvfi_rs2_addr (rvfi_rs2_addr ),
.rvfi_rs1_rdata (rvfi_rs1_rdata),
.rvfi_rs2_rdata (rvfi_rs2_rdata),
.rvfi_rd_addr (rvfi_rd_addr ),
.rvfi_rd_wdata (rvfi_rd_wdata ),
.rvfi_pc_rdata (rvfi_pc_rdata ),
.rvfi_pc_wdata (rvfi_pc_wdata ),
.rvfi_mem_addr (rvfi_mem_addr ),
.rvfi_mem_rmask (rvfi_mem_rmask),
.rvfi_mem_wmask (rvfi_mem_wmask),
.rvfi_mem_rdata (rvfi_mem_rdata),
.rvfi_mem_wdata (rvfi_mem_wdata),
.i_dbus_adr (o_dbus_adr),
.i_dbus_dat (o_dbus_dat),
.i_dbus_sel (o_dbus_sel),
.i_dbus_we (o_dbus_we ),
.i_dbus_rdt (i_dbus_rdt),
.i_dbus_ack (i_dbus_ack),
.i_ctrl_pc_en (ctrl_pc_en),
.rs1 (rs1),
.rs2 (rs2),
.rs1_addr (rs1_addr),
.rs2_addr (rs2_addr),
.immdec_en (immdec_en),
.rd_en (rd_en),
.trap (trap),
.i_rf_ready (i_rf_ready),
.i_ibus_cyc (o_ibus_cyc),
.two_stage_op (two_stage_op),
.init (init),
.i_ibus_adr (o_ibus_adr),
`endif
.i_clk (clk),
.i_rst (i_rst),
.i_ibus_rdt (i_ibus_rdt),
.i_ibus_ack (i_ibus_ack),
.i_rd_addr (rd_addr ),
.i_cnt_en (cnt_en ),
.i_csr_in (csr_in ),
.i_csr_mstatus_en (csr_mstatus_en),
.i_csr_mie_en (csr_mie_en ),
.i_csr_mcause_en (csr_mcause_en ),
.i_csr_en (csr_en ),
.i_csr_addr (csr_addr),
.i_wen0 (o_wen0),
.i_wdata0 (o_wdata0),
.i_cnt_done (cnt_done));
end
endgenerate
generate
if (MDU) begin: gen_mdu
assign dbus_rdt = i_ext_ready ? i_ext_rd:i_dbus_rdt;
assign dbus_ack = i_dbus_ack | i_ext_ready;
end else begin : gen_no_mdu
assign dbus_rdt = i_dbus_rdt;
assign dbus_ack = i_dbus_ack;
end
assign o_ext_rs2 = o_dbus_dat;
endgenerate
endmodule
`default_nettype wire

View file

@ -1,12 +1,13 @@
CAPI=2:
name : ::serv:1.1.0
name : ::serv:1.3.0
filesets:
core:
files:
- "tool_verilator? (data/verilator_waiver.vlt)" : {file_type: vlt}
- rtl/serv_bufreg.v
- rtl/serv_bufreg2.v
- rtl/serv_alu.v
- rtl/serv_csr.v
- rtl/serv_ctrl.v
@ -17,14 +18,25 @@ filesets:
- rtl/serv_rf_ram_if.v
- rtl/serv_rf_ram.v
- rtl/serv_state.v
- rtl/serv_debug.v
- rtl/serv_top.v
- rtl/serv_rf_top.v
- rtl/serv_aligner.v
- rtl/serv_compdec.v
file_type : verilogSource
openlane:
files:
- data/params.tcl : {file_type : tclSource}
- rtl/serv_synth_wrapper.v : {file_type : verilogSource}
targets:
default:
filesets : [core]
parameters :
- "is_toplevel? (ALIGN)"
- "is_toplevel? (COMPRESSED)"
- "is_toplevel? (MDU)"
- "is_toplevel? (PRE_REGISTER)"
- "is_toplevel? (RESET_STRATEGY)"
- RISCV_FORMAL
@ -34,6 +46,7 @@ targets:
lint:
default_tool : verilator
description: Run static code checks (linting)
filesets : [core]
tools:
verilator:
@ -42,7 +55,18 @@ targets:
- "-Wall"
toplevel : serv_rf_top
sky130:
default_tool : openlane
description: Create GDSII for SkyWater 130nm using OpenLANE
filesets : [core, openlane]
toplevel : serv_synth_wrapper
parameters:
MDU:
datatype : int
description: Enables interface for RISC-V standard M-extension
paramtype : vlogparam
PRE_REGISTER:
datatype : int
description : Register signals before or after the decoder
@ -63,3 +87,14 @@ parameters:
WITH_CSR:
datatype : int
paramtype : vlogparam
COMPRESSED:
datatype : int
description : Enable/Disable the support for Compressed instructions
paramtype : vlogparam
ALIGN:
datatype : int
paramtype : vlogparam
description : Enable/Disable the support of misaligned instructions

View file

@ -1,13 +1,10 @@
CAPI=2:
name : ::servant:1.1.0
name : ::servant:1.3.0
description: Simple reference system for SERV
filesets:
service:
files: [servant/ice40_pll.v, servant/service.v]
file_type : verilogSource
depend : ["fusesoc:utils:generators"]
# Common filesets
mem_files:
files:
- sw/blinky.hex : {copyto : blinky.hex}
@ -16,30 +13,88 @@ filesets:
servant_tb:
files:
- sw/hello_uart.hex : {file_type: user, copyto: .}
- bench/servant_sim.v
- bench/uart_decoder.v
- bench/servant_tb.v
file_type : verilogSource
depend : [vlog_tb_utils]
verilator_tb:
service:
files:
- bench/servant_sim.v
- bench/servant_tb.cpp : {file_type : cppSource}
- servant/ice40_pll.v
- servant/service_clock_gen.v
- servant/service.v
file_type : verilogSource
depend : ["fusesoc:utils:generators"]
soc:
files:
- servant/servant_clock_gen.v
- servant/servant_timer.v
- servant/servant_gpio.v
- servant/servant_arbiter.v
- servant/servant_mux.v
- "tool_quartus? (servant/servant_ram_quartus.sv)" : {file_type : systemVerilogSource}
- "!tool_quartus? (servant/servant_ram.v)"
- servant/servant.v
file_type : verilogSource
depend : [serv]
depend : [servile, "mdu? (mdu)"]
verilator_tb:
files:
- bench/servant_sim.v
- "vidbo? (bench/servant_tb_vidbo.cpp)" : {file_type : cppSource}
- "!vidbo? (bench/servant_tb.cpp)" : {file_type : cppSource}
file_type : verilogSource
depend : ["vidbo? (vidbo)"]
# Target-specific filesets. Alphabetically sorted
ac701:
files:
- servant/servix.v : {file_type : verilogSource}
- servant/servant_ac701.v : {file_type : verilogSource}
- data/ac701.xdc : {file_type : xdc}
alhambra : {files: [data/alhambra.pcf : {file_type : PCF}]}
alchitry_au:
files:
- servant/servix_clock_gen.v : {file_type : verilogSource}
- servant/servix.v : {file_type : verilogSource}
- data/alchitry_au.xdc : {file_type : xdc}
arty_a7_35t:
files:
- servant/servix_clock_gen.v : {file_type : verilogSource}
- servant/servix.v : {file_type : verilogSource}
- data/arty_a7_35t.xdc : {file_type : xdc}
arty_s7_50t:
files:
- servant/servix_clock_gen.v : {file_type : verilogSource}
- servant/servix.v : {file_type : verilogSource}
- data/arty_s7_50t.xdc : {file_type : xdc}
ax309:
files:
- servant/servant_ax309_clock_gen.v : {file_type : verilogSource}
- servant/servant_ax309.v : {file_type : verilogSource}
- data/ax309.ucf : {file_type : UCF}
chameleon96:
files:
- data/chameleon96/chameleon96.sdc : {file_type : SDC}
- data/chameleon96/pinmap.tcl : {file_type: tclSource}
- data/chameleon96/HPS.sv : {file_type : systemVerilogSource}
- data/chameleon96/CV_96.v : {file_type : verilogSource}
- servant/servive_clock_gen.v : {file_type : verilogSource}
- servant/servive.v : {file_type : verilogSource}
cmod_a7_35t:
files:
- servant/servant_cmod_a7_clock_gen.v : {file_type : verilogSource}
- servant/servant_cmod_a7.v : {file_type : verilogSource}
- data/cmod_a7_35t.xdc : {file_type : xdc}
cyc1000:
files:
@ -48,13 +103,6 @@ filesets:
- servant/servclone10_clock_gen.v : {file_type : verilogSource}
- servant/servclone10.v : {file_type : verilogSource}
sockit:
files:
- data/sockit.sdc : {file_type : SDC}
- data/sockit.tcl : {file_type : tclSource}
- servant/servive_clock_gen.v : {file_type : verilogSource}
- servant/servive.v : {file_type : verilogSource}
deca:
files:
- data/deca.sdc : {file_type : SDC}
@ -69,6 +117,14 @@ filesets:
- servant/servive_clock_gen.v : {file_type : verilogSource}
- servant/servive.v : {file_type : verilogSource}
de1_soc_revF:
files:
- data/de1_soc_revF.sdc : {file_type : SDC}
- data/de1_soc_revF.tcl : {file_type : tclSource}
- servant/servde1_soc_revF_clock_gen.v : {file_type : verilogSource}
- servant/servde1_soc_revF.v : {file_type : verilogSource}
de10_nano:
files:
- data/de10_nano.sdc : {file_type : SDC}
@ -76,47 +132,68 @@ filesets:
- servant/servive_clock_gen.v : {file_type : verilogSource}
- servant/servive.v : {file_type : verilogSource}
tinyfpga_bx: {files: [data/tinyfpga_bx.pcf : {file_type : PCF}]}
icebreaker : {files: [data/icebreaker.pcf : {file_type : PCF}]}
icesugar : {files: [data/icesugar.pcf : {file_type : PCF}]}
alhambra : {files: [data/alhambra.pcf : {file_type : PCF}]}
icestick : {files: [data/icestick.pcf : {file_type : PCF}]}
ebaz4205:
files:
- servant/servix_ebaz4205_clock_gen.v : {file_type : verilogSource}
- servant/servix_ebaz4205.v : {file_type : verilogSource}
- data/ebaz4205.xdc : {file_type : xdc}
ecp5_evn:
files:
- data/ecp5_evn.lpf : {file_type : LPF}
- servant/ecp5_evn_pll.v : {file_type : verilogSource}
- servant/servant_ecp5_evn_clock_gen.v : {file_type : verilogSource}
- servant/servant_ecp5_evn.v : {file_type : verilogSource}
go_board:
files:
- data/go_board.pcf : {file_type : PCF}
- servant/service_go_board.v : {file_type : verilogSource}
icebreaker : {files: [data/icebreaker.pcf : {file_type : PCF}]}
icestick : {files: [data/icestick.pcf : {file_type : PCF}]}
icesugar : {files: [data/icesugar.pcf : {file_type : PCF}]}
icev_wireless : {files: [data/icev_wireless.pcf : {file_type : PCF}]}
gmm7550:
files:
- data/gmm7550.ccf : {file_type : CCF}
- servant/servant_gmm7550.v : {file_type : verilogSource}
lx9_microboard:
files:
- servant/servant_lx9_clock_gen.v : {file_type : verilogSource}
- servant/servant_lx9.v : {file_type : verilogSource}
- data/lx9_microboard.ucf : {file_type : UCF}
machdyne_kolibri:
files:
- servant/servant_md_kolibri.v : {file_type : verilogSource}
- data/machdyne_kolibri.pcf : {file_type : PCF}
max10_10m08evk:
files:
- data/max10_10m08evk.sdc : {file_type : SDC}
- data/max10_10m08evk.tcl : {file_type : tclSource}
- servant/servive_clock_gen.v : {file_type : verilogSource}
- servant/servive.v : {file_type : verilogSource}
nexys_2:
files:
- servant/servax_clock_gen.v : {file_type : verilogSource}
- servant/servax.v : {file_type : verilogSource}
- data/nexys_2.tcl : {file_type : tclSource}
- data/nexys_2.ucf : {file_type : UCF}
nexys_a7:
files:
- servant/servix_clock_gen.v : {file_type : verilogSource}
- servant/servix.v : {file_type : verilogSource}
- data/nexys_a7.xdc : {file_type : xdc}
cmod_a7_35t:
files:
- servant/servix_clock_gen.v : {file_type : verilogSource}
- servant/servix.v : {file_type : verilogSource}
- data/cmod_a7_35t.xdc : {file_type : xdc}
arty_a7_35t:
files:
- servant/servix_clock_gen.v : {file_type : verilogSource}
- servant/servix.v : {file_type : verilogSource}
- data/arty_a7_35t.xdc : {file_type : xdc}
ac701:
files:
- servant/servix.v : {file_type : verilogSource}
- servant/servant_ac701.v : {file_type : verilogSource}
- data/ac701.xdc : {file_type : xdc}
orangecrab:
files:
- data/orangecrab_r02.lpf : {file_type : LPF}
@ -128,6 +205,27 @@ filesets:
- servant/servis.v : {file_type : verilogSource}
- data/pipistrello.ucf : {file_type : UCF}
polarfire_splashkit:
files:
- servant/servant_pf.v : {file_type : verilogSource}
- servant/servant_pf_clock_gen.v : {file_type : verilogSource}
- data/polarfire_splashkit.pdc : {file_type : PDC}
sockit:
files:
- data/sockit.sdc : {file_type : SDC}
- data/sockit.tcl : {file_type : tclSource}
- servant/servive_clock_gen.v : {file_type : verilogSource}
- servant/servive.v : {file_type : verilogSource}
te0802:
files:
- servant/servant_te0802_clock_gen.v : {file_type : verilogSource}
- servant/servant_te0802.v : {file_type : verilogSource}
- data/te0802.xdc : {file_type : xdc}
tinyfpga_bx: {files: [data/tinyfpga_bx.pcf : {file_type : PCF}]}
ulx3s:
files:
- data/ulx3s.lpf : {file_type : LPF}
@ -150,6 +248,88 @@ targets:
default:
filesets : [soc]
ac701:
default_tool: vivado
description: AC701 Evaluation Kit
filesets : [mem_files, soc, ac701]
parameters : [memfile, memsize, frequency=32]
tools:
vivado: {part : xc7a200t-fbg676-2}
toplevel : servant_ac701
alchitry_au:
default_tool: vivado
description: Open-hardware Alchitry AU FPGA board
filesets : [mem_files, soc, alchitry_au]
parameters : [memfile, memsize, frequency=16, "mdu? (MDU=1)", WITH_RESET]
tools:
vivado: {part : xc7a35tftg256-1}
toplevel : servix
alhambra:
default_tool : icestorm
description: Open-hardware iCE40HX4K FPGA board
filesets : [mem_files, soc, service, alhambra]
generate: [ice40pll: {freq_in : 12, freq_out : 32}]
parameters : [memfile, memsize, PLL=ICE40_CORE]
tools:
icestorm:
nextpnr_options : [--hx8k, --package, "tq144:4k", --freq, 32]
pnr: next
toplevel : service
arty_a7_35t:
default_tool: vivado
description: Digilent Arty A7-35
filesets : [mem_files, soc, arty_a7_35t]
parameters : [memfile, memsize, frequency=16, "mdu? (MDU=1)", WITH_RESET]
tools:
vivado: {part : xc7a35ticsg324-1L}
toplevel : servix
arty_s7_50t:
default_tool: vivado
description: Digilent Arty S7-50
filesets : [mem_files, soc, arty_s7_50t]
parameters : [memfile, memsize, frequency=16, "mdu? (MDU=1)", WITH_RESET]
tools:
vivado: {part : xc7s50csga324-1}
toplevel : servix
ax309:
default_tool : ise
description : XILINX Spartan-6 XC6SLX9 FPGA Development Board
filesets : [mem_files, soc, ax309]
parameters : [memfile, memsize]
tools:
ise:
family : Spartan6
device : xc6slx9
package : ftg256
speed : -3
toplevel : servant_ax309
chameleon96:
default_tool : quartus
description : Chameleon96 (Arrow 96 CV SoC Board)
filesets : [mem_files, soc, chameleon96]
parameters : [memfile, memsize]
tools:
quartus:
family : Cyclone V
device : 5CSEBA6U19I7
board_device_index : 2
toplevel: CV_96
cmod_a7_35t:
description: Digilent CMOD A7-35
filesets : [mem_files, soc, cmod_a7_35t]
flow: vivado
flow_options:
part : xc7a35tcpg236-1
parameters : [memfile, memsize]
toplevel : servant_cmod_a7
cyc1000:
default_tool: quartus
description: cyc1000 FPGA board
@ -161,30 +341,9 @@ targets:
device : 10CL025YU256C8G
toplevel : servclone10
sockit:
default_tool : quartus
description: SoCKit development kit by Arrow / Terasic
filesets : [mem_files, soc, sockit]
parameters : [memfile, memsize]
tools:
quartus:
family : CycloneV
device : 5CSXFC6D6F31C6
toplevel: servive
deca:
default_tool : quartus
description: DECA development kit by Arrow / Terasic
filesets : [mem_files, soc, deca]
parameters : [memfile, memsize]
tools:
quartus:
family : MAX 10
device : 10M50DAF484C6GES
toplevel: servive
de0_nano:
default_tool : quartus
description: Terasic DE0 Nano
filesets : [mem_files, soc, de0_nano]
parameters : [memfile, memsize]
tools:
@ -205,10 +364,51 @@ targets:
board_device_index : 2
toplevel : servive
deca:
default_tool : quartus
description: DECA development kit by Arrow / Terasic
filesets : [mem_files, soc, deca]
parameters : [memfile, memsize]
tools:
quartus:
family : MAX 10
device : 10M50DAF484C6GES
toplevel: servive
ebaz4205:
default_tool: vivado
description: EBAZ4205 'Development' Board
filesets : [mem_files, soc, ebaz4205]
parameters : [memfile, memsize, frequency=16]
tools:
vivado: {part : xc7z010clg400-1}
toplevel : servix_ebaz4205
ecp5_evn:
default_tool: trellis
description : ECP5 evaluation board
filesets : [mem_files, soc, ecp5_evn]
parameters : [memfile, memsize]
tools:
trellis:
nextpnr_options : [--package, CABGA381, --um5g-85k]
toplevel: servant_ecp5_evn
go_board:
default_tool : icestorm
description: Nandland Go Board
filesets : [mem_files, soc, go_board]
tools:
icestorm:
nextpnr_options : [--hx1k, --package, vq100, --freq, 20]
pnr: next
toplevel : service_go_board
icebreaker:
default_tool : icestorm
description: 1Bit Squared iCEBreaker
filesets : [mem_files, soc, service, icebreaker]
generate: [icebreaker_pll]
generate: [ice40pll : {freq_out : 16}]
parameters : [memfile, memsize, PLL=ICE40_PAD]
tools:
icestorm:
@ -216,11 +416,23 @@ targets:
pnr: next
toplevel : service
icestick:
default_tool : icestorm
description: Lattice iCEstick
filesets : [mem_files, soc, service, icestick]
generate: [ice40pll: {freq_in : 12, freq_out : 32}]
parameters : [memfile=blinky.hex, memsize=7168, PLL=ICE40_CORE]
tools:
icestorm:
nextpnr_options : [--hx1k, --package, tq144, --freq, 32]
pnr: next
toplevel : service
icesugar:
default_tool : icestorm
description : iCE40UP5K Development Board by MuseLab
filesets : [mem_files, soc, service, icesugar]
generate: [icesugar_pll]
generate: [ice40pll : {freq_in : 12, freq_out : 32}]
parameters : [memfile, memsize, PLL=ICE40_PAD]
tools:
icestorm:
@ -228,6 +440,38 @@ targets:
pnr: next
toplevel : service
icev_wireless:
default_tool : icestorm
description: ICE-V Wireless
filesets : [mem_files, soc, service, icev_wireless]
generate: [ice40pll : {freq_out : 16}]
parameters : [memfile, memsize, PLL=ICE40_PAD]
tools:
icestorm:
nextpnr_options: [--up5k, --freq, 16]
pnr: next
toplevel : service
gmm7550:
default_tool: gatemate
description: CologneChip GateMate FPGA Module
filesets : [mem_files, soc, gmm7550]
parameters : [memfile=blinky.hex, memsize=8192]
toplevel : servant_gmm7550
tools:
gatemate:
device : CCGM1A1
yosys_synth_options : [ -nomx8 ]
p_r_options : [ +uCIO -cCP ]
lint:
description: Run static code checks (linting)
filesets : [soc]
flow: lint
flow_options:
tool : verilator
toplevel : servant
lx9_microboard:
default_tool: ise
description : LX9 Microboard
@ -241,69 +485,65 @@ targets:
speed : -2
toplevel : servant_lx9
tinyfpga_bx:
default_tool : icestorm
filesets : [mem_files, soc, service, tinyfpga_bx]
generate: [tinyfpga_bx_pll]
parameters : [memfile, memsize, PLL=ICE40_CORE]
machdyne_kolibri:
default_tool: icestorm
description : Machdyne Kolibri FPGA Dongle
filesets : [mem_files, soc, machdyne_kolibri]
parameters : [memfile, memsize]
tools:
icestorm:
nextpnr_options : [--lp8k, --package, cm81, --freq, 32]
nextpnr_options : [--hx4k, --package, bg121, --freq, 48]
pnr: next
toplevel : service
toplevel : servant_md_kolibri
alhambra:
default_tool : icestorm
description: Open-hardware iCE40HX4K FPGA board
filesets : [mem_files, soc, service, alhambra]
generate: [alhambra_pll]
parameters : [memfile, memsize, PLL=ICE40_CORE]
max10_10m08evk:
default_tool : quartus
description: MAX10 10M08 evaluation kit
hooks:
post_run: [jbc]
filesets : [mem_files, soc, max10_10m08evk]
parameters : [memfile, memsize]
tools:
icestorm:
nextpnr_options : [--hx8k, --package, "tq144:4k", --freq, 32]
pnr: next
toplevel : service
quartus:
family : MAX 10
device : 10M08SAE144C8G
toplevel: servive
lint:
default_tool : verilator
filesets : [soc]
nexys_2_500:
default_tool: ise
description: Digilent Nexys 2-500
filesets : [mem_files, soc, nexys_2]
parameters : [memfile, memsize, compressed]
tools:
verilator:
mode : lint-only
toplevel : servant
ise:
family : Spartan3E
device : xc3s500e
package : fg320
speed : -4
toplevel : servax
nexys_2_1200:
default_tool: ise
description: Digilent Nexys 2-1200
filesets : [mem_files, soc, nexys_2]
parameters : [memfile, memsize, compressed]
tools:
ise:
family : Spartan3E
device : xc3s1600e
package : fg320
speed : -4
toplevel : servax
nexys_a7:
default_tool: vivado
description: Digilent Nexys A7
filesets : [mem_files, soc, nexys_a7]
flow: vivado
flow_options:
part : xc7a100tcsg324-1
parameters : [memfile, memsize, frequency=32]
tools:
vivado: {part : xc7a100tcsg324-1}
toplevel : servix
cmod_a7_35t:
default_tool: vivado
filesets : [mem_files, soc, cmod_a7_35t]
parameters : [memfile=blinky.hex, memsize, frequency=12]
tools:
vivado: {part : xc7a35tcpg236-1}
toplevel : servix
arty_a7_35t:
default_tool: vivado
filesets : [mem_files, soc, arty_a7_35t]
parameters : [memfile, memsize, frequency=16]
tools:
vivado: {part : xc7a35ticsg324-1L}
toplevel : servix
ac701:
default_tool: vivado
filesets : [mem_files, soc, ac701]
parameters : [memfile, memsize, frequency=32]
tools:
vivado: {part : xc7a200t-fbg676-2}
toplevel : servant_ac701
orangecrab_r0.2:
default_tool: trellis
description : OrangeCrab R0.2
@ -327,16 +567,61 @@ targets:
speed : -3
toplevel : servis
polarfire_splashkit:
default_tool: libero
description : Microsemi Polarfire Splash Kit
filesets : [mem_files, soc, polarfire_splashkit]
parameters : [memfile, memsize]
tools:
libero:
family : PolarFire
die : MPF300TS
package : FCG484
toplevel : servant_pf
sim:
default_tool: icarus
description: Simulation target
filesets : [soc, servant_tb]
parameters :
- RISCV_FORMAL
- width
- "mdu? (MDU=1)"
- SERV_CLEAR_RAM=true
- firmware
- memsize
toplevel : servant_tb
sockit:
default_tool : quartus
description: SoCKit development kit by Arrow / Terasic
filesets : [mem_files, soc, sockit]
parameters : [memfile, memsize]
tools:
quartus:
family : CycloneV
device : 5CSXFC6D6F31C6
toplevel: servive
te0802:
default_tool: vivado
description : Trenz Electronic TE0802
filesets : [mem_files, soc, te0802]
parameters : [memfile, memsize]
tools:
vivado: {part : xczu2cg-sbva484-1-e}
toplevel : servant_te0802
tinyfpga_bx:
description: TinyFPGA BX
filesets : [mem_files, soc, service, tinyfpga_bx]
flow: icestorm
flow_options:
nextpnr_options : [--lp8k, --package, cm81, --freq, '32']
generate: [ice40pll: {freq_in : 16, freq_out : 32}]
parameters : [memfile, memsize, PLL=ICE40_CORE]
toplevel : service
ulx3s_85:
default_tool: diamond
description : ULX3S 85k version
@ -351,6 +636,7 @@ targets:
upduino2:
default_tool : icestorm
description: Upduino2
filesets : [mem_files, soc, upduino2]
parameters : [memfile, memsize]
tools:
@ -360,20 +646,28 @@ targets:
toplevel : servant_upduino2
verilator_tb:
default_tool: verilator
description: Verilator testbench
filesets : [soc, verilator_tb]
flow: sim
flow_options:
tool: verilator
verilator_options : [--trace]
parameters :
- RISCV_FORMAL
- "mdu? (MDU=1)"
- cps
- firmware
- memsize
- signature
- timeout
- trace_pc
- uart_baudrate
- vcd
- vcd_start
tools:
verilator:
verilator_options : [--trace]
- width
- compressed
- align
- with_csr=1
toplevel : servant_sim
zcu106:
@ -385,27 +679,48 @@ targets:
vivado: {part : xczu7ev-ffvc1156-2-e}
toplevel : servus
icestick:
default_tool : icestorm
filesets : [mem_files, soc, service, icestick]
generate: [icestick_pll]
parameters : [memfile=blinky.hex, memsize=7168, PLL=ICE40_CORE]
tools:
icestorm:
nextpnr_options : [--hx1k, --package, tq144, --freq, 32]
pnr: next
toplevel : service
go_board:
default_tool : icestorm
filesets : [mem_files, soc, go_board]
tools:
icestorm:
nextpnr_options : [--hx1k, --package, vq100, --freq, 20]
pnr: next
toplevel : service_go_board
parameters:
align:
datatype : int
description : Enable/Disable the Misaligned access of instruction
paramtype : vlogparam
compressed:
datatype : int
description : Enable/Disable the Compressed extension
paramtype : vlogparam
cps:
datatype : bool
description: Write simulated cycles per second to a text file named cps
paramtype : plusarg
firmware:
datatype : file
description : Preload RAM with a hex file at runtime (overrides memfile)
paramtype : plusarg
frequency:
datatype : int
description : PLL output frequency in MHz
paramtype : vlogparam
MDU:
datatype : int
description : Enables RISC-V standard M-extension
paramtype : vlogdefine
memfile:
datatype : file
description : Preload RAM with a hex file at compile-time
paramtype : vlogparam
memsize:
datatype : int
default : 8192
description : Memory size in bytes for RAM (default 8kiB)
paramtype : vlogparam
PLL:
datatype : str
description : PLL type to use for main clock generation
@ -419,39 +734,23 @@ parameters:
datatype : bool
paramtype : vlogdefine
firmware:
datatype : file
description : Preload RAM with a hex file at runtime (overrides memfile)
paramtype : plusarg
frequency:
datatype : int
description : PLL output frequency
paramtype : vlogparam
memfile:
datatype : file
description : Preload RAM with a hex file at compile-time
paramtype : vlogparam
memsize:
datatype : int
default : 8192
description : Memory size in bytes for RAM (default 8kiB)
paramtype : vlogparam
signature:
datatype : file
paramtype : plusarg
uart_baudrate:
datatype : int
description : Treat q output as an UART with the specified baudrate (0 or omitted parameter disables UART decoding)
paramtype : plusarg
timeout:
datatype : int
paramtype : plusarg
trace_pc:
datatype : bool
paramtype : plusarg
uart_baudrate:
datatype : int
description : Treat q output as an UART with the specified baudrate (0 or omitted parameter disables UART decoding)
paramtype : plusarg
vcd:
datatype : bool
paramtype : plusarg
@ -461,32 +760,27 @@ parameters:
description : Delay start of VCD dumping until the specified time
paramtype : plusarg
width:
datatype : int
description : Interal datapath width (1=SERV, 4=QERV)
paramtype : vlogparam
with_csr:
datatype : int
description : Enable/Disable CSR support
paramtype : vlogparam
WITH_RESET:
datatype : bool
default : true
description : Enable reset input (for supported targets)
paramtype : vlogdefine
generate:
icebreaker_pll:
ice40pll:
generator: icepll
parameters:
freq_out : 16
icesugar_pll:
generator: icepll
parameters:
freq_in : 12
freq_out : 32
scripts:
jbc:
cmd : [quartus_jbcc, "-n", servant_1_2_1_pof.jam, servant_1_2_1_pof.jbc]
tinyfpga_bx_pll:
generator: icepll
parameters:
freq_in : 16
freq_out : 32
alhambra_pll:
generator: icepll
parameters:
freq_in : 12
freq_out : 32
icestick_pll:
generator: icepll
parameters:
freq_in : 12
freq_out : 32

48
servant/ecp5_evn_pll.v Normal file
View file

@ -0,0 +1,48 @@
// generated with "ecppll -n ecp5_evn_pll -i 12 -o 16 --clkin_name clki --clkout0_name clko -f ecp5_evn_pll.v"
// diamond 3.7 accepts this PLL
// diamond 3.8-3.9 is untested
// diamond 3.10 or higher is likely to abort with error about unable to use feedback signal
// cause of this could be from wrong CPHASE/FPHASE parameters
module ecp5_evn_pll
(
input clki, // 12 MHz, 0 deg
output clko, // 16 MHz, 0 deg
output locked
);
(* FREQUENCY_PIN_CLKI="12" *)
(* FREQUENCY_PIN_CLKOP="16" *)
(* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
EHXPLLL #(
.PLLRST_ENA("DISABLED"),
.INTFB_WAKE("DISABLED"),
.STDBY_ENABLE("DISABLED"),
.DPHASE_SOURCE("DISABLED"),
.OUTDIVIDER_MUXA("DIVA"),
.OUTDIVIDER_MUXB("DIVB"),
.OUTDIVIDER_MUXC("DIVC"),
.OUTDIVIDER_MUXD("DIVD"),
.CLKI_DIV(3),
.CLKOP_ENABLE("ENABLED"),
.CLKOP_DIV(37),
.CLKOP_CPHASE(18),
.CLKOP_FPHASE(0),
.FEEDBK_PATH("CLKOP"),
.CLKFB_DIV(4)
) pll_i (
.RST(1'b0),
.STDBY(1'b0),
.CLKI(clki),
.CLKOP(clko),
.CLKFB(clko),
.CLKINTFB(),
.PHASESEL0(1'b0),
.PHASESEL1(1'b0),
.PHASEDIR(1'b1),
.PHASESTEP(1'b1),
.PHASELOADREG(1'b1),
.PLLWAKESYNC(1'b0),
.ENCLKOP(1'b0),
.LOCK(locked)
);
endmodule

View file

@ -11,8 +11,8 @@ module ice40_pll
reg [1:0] rst_reg;
always @(posedge o_clk)
rst_reg <= {!locked, rst_reg[1]};
assign o_rst = rst_reg[0];
rst_reg <= {rst_reg[0],locked};
assign o_rst = ~rst_reg[1];
generate
if (PLL == "ICE40_CORE") begin

View file

@ -8,99 +8,82 @@ module servant
parameter memfile = "zephyr_hello.hex";
parameter memsize = 8192;
parameter reset_strategy = "MINI";
parameter width = 1;
parameter sim = 0;
parameter [0:0] debug = 1'b0;
parameter with_csr = 1;
parameter [0:0] compress = 0;
parameter [0:0] align = compress;
`ifdef MDU
localparam [0:0] with_mdu = 1'b1;
`else
localparam [0:0] with_mdu = 1'b0;
`endif
localparam aw = $clog2(memsize);
localparam csr_regs = with_csr*4;
localparam rf_width = width * 2;
localparam rf_l2d = $clog2((32+csr_regs)*32/rf_width);
wire timer_irq;
wire [31:0] wb_ibus_adr;
wire wb_ibus_cyc;
wire [31:0] wb_ibus_rdt;
wire wb_ibus_ack;
wire [31:0] wb_dbus_adr;
wire [31:0] wb_dbus_dat;
wire [3:0] wb_dbus_sel;
wire wb_dbus_we;
wire wb_dbus_cyc;
wire [31:0] wb_dbus_rdt;
wire wb_dbus_ack;
wire [31:0] wb_dmem_adr;
wire [31:0] wb_dmem_dat;
wire [3:0] wb_dmem_sel;
wire wb_dmem_we;
wire wb_dmem_cyc;
wire [31:0] wb_dmem_rdt;
wire wb_dmem_ack;
wire [31:0] wb_mem_adr;
wire [31:0] wb_mem_dat;
wire [3:0] wb_mem_sel;
wire wb_mem_we;
wire wb_mem_cyc;
wire wb_mem_stb;
wire [31:0] wb_mem_rdt;
wire wb_mem_ack;
wire wb_gpio_dat;
wire wb_gpio_we;
wire wb_gpio_cyc;
wire wb_gpio_stb;
wire wb_gpio_rdt;
wire [31:0] wb_timer_dat;
wire wb_timer_we;
wire wb_timer_cyc;
wire wb_timer_stb;
wire [31:0] wb_timer_rdt;
servant_arbiter arbiter
(.i_wb_cpu_dbus_adr (wb_dmem_adr),
.i_wb_cpu_dbus_dat (wb_dmem_dat),
.i_wb_cpu_dbus_sel (wb_dmem_sel),
.i_wb_cpu_dbus_we (wb_dmem_we ),
.i_wb_cpu_dbus_cyc (wb_dmem_cyc),
.o_wb_cpu_dbus_rdt (wb_dmem_rdt),
.o_wb_cpu_dbus_ack (wb_dmem_ack),
wire [31:0] wb_ext_adr;
wire [31:0] wb_ext_dat;
wire [3:0] wb_ext_sel;
wire wb_ext_we;
wire wb_ext_stb;
wire [31:0] wb_ext_rdt;
wire wb_ext_ack;
.i_wb_cpu_ibus_adr (wb_ibus_adr),
.i_wb_cpu_ibus_cyc (wb_ibus_cyc),
.o_wb_cpu_ibus_rdt (wb_ibus_rdt),
.o_wb_cpu_ibus_ack (wb_ibus_ack),
wire [rf_l2d-1:0] rf_waddr;
wire [rf_width-1:0] rf_wdata;
wire rf_wen;
wire [rf_l2d-1:0] rf_raddr;
wire rf_ren;
wire [rf_width-1:0] rf_rdata;
.o_wb_cpu_adr (wb_mem_adr),
.o_wb_cpu_dat (wb_mem_dat),
.o_wb_cpu_sel (wb_mem_sel),
.o_wb_cpu_we (wb_mem_we ),
.o_wb_cpu_cyc (wb_mem_cyc),
.i_wb_cpu_rdt (wb_mem_rdt),
.i_wb_cpu_ack (wb_mem_ack));
servant_mux #(sim) servant_mux
servant_mux servant_mux
(
.i_clk (wb_clk),
.i_rst (wb_rst & (reset_strategy != "NONE")),
.i_wb_cpu_adr (wb_dbus_adr),
.i_wb_cpu_dat (wb_dbus_dat),
.i_wb_cpu_sel (wb_dbus_sel),
.i_wb_cpu_we (wb_dbus_we),
.i_wb_cpu_cyc (wb_dbus_cyc),
.o_wb_cpu_rdt (wb_dbus_rdt),
.o_wb_cpu_ack (wb_dbus_ack),
.o_wb_mem_adr (wb_dmem_adr),
.o_wb_mem_dat (wb_dmem_dat),
.o_wb_mem_sel (wb_dmem_sel),
.o_wb_mem_we (wb_dmem_we),
.o_wb_mem_cyc (wb_dmem_cyc),
.i_wb_mem_rdt (wb_dmem_rdt),
.i_wb_cpu_adr (wb_ext_adr),
.i_wb_cpu_dat (wb_ext_dat),
.i_wb_cpu_sel (wb_ext_sel),
.i_wb_cpu_we (wb_ext_we),
.i_wb_cpu_cyc (wb_ext_stb),
.o_wb_cpu_rdt (wb_ext_rdt),
.o_wb_cpu_ack (wb_ext_ack),
.o_wb_gpio_dat (wb_gpio_dat),
.o_wb_gpio_we (wb_gpio_we),
.o_wb_gpio_cyc (wb_gpio_cyc),
.o_wb_gpio_cyc (wb_gpio_stb),
.i_wb_gpio_rdt (wb_gpio_rdt),
.o_wb_timer_dat (wb_timer_dat),
.o_wb_timer_we (wb_timer_we),
.o_wb_timer_cyc (wb_timer_cyc),
.o_wb_timer_cyc (wb_timer_stb),
.i_wb_timer_rdt (wb_timer_rdt));
servant_ram
@ -112,84 +95,79 @@ module servant
.i_wb_clk (wb_clk),
.i_wb_rst (wb_rst),
.i_wb_adr (wb_mem_adr[$clog2(memsize)-1:2]),
.i_wb_cyc (wb_mem_cyc),
.i_wb_cyc (wb_mem_stb),
.i_wb_we (wb_mem_we) ,
.i_wb_sel (wb_mem_sel),
.i_wb_dat (wb_mem_dat),
.o_wb_rdt (wb_mem_rdt),
.o_wb_ack (wb_mem_ack));
generate
if (with_csr) begin
servant_timer
#(.RESET_STRATEGY (reset_strategy),
.WIDTH (32))
timer
(.i_clk (wb_clk),
.i_rst (wb_rst),
.o_irq (timer_irq),
.i_wb_cyc (wb_timer_cyc),
.i_wb_we (wb_timer_we) ,
.i_wb_dat (wb_timer_dat),
.o_wb_dat (wb_timer_rdt));
end else begin
assign wb_timer_rdt = 32'd0;
assign timer_irq = 1'b0;
end
endgenerate
servant_timer
#(.RESET_STRATEGY (reset_strategy),
.WIDTH (32))
timer
(.i_clk (wb_clk),
.i_rst (wb_rst),
.o_irq (timer_irq),
.i_wb_cyc (wb_timer_stb),
.i_wb_we (wb_timer_we) ,
.i_wb_dat (wb_timer_dat),
.o_wb_dat (wb_timer_rdt));
servant_gpio gpio
(.i_wb_clk (wb_clk),
.i_wb_dat (wb_gpio_dat),
.i_wb_we (wb_gpio_we),
.i_wb_cyc (wb_gpio_cyc),
.i_wb_cyc (wb_gpio_stb),
.o_wb_rdt (wb_gpio_rdt),
.o_gpio (q));
serv_rf_top
#(.RESET_PC (32'h0000_0000),
.RESET_STRATEGY (reset_strategy),
.WITH_CSR (with_csr))
serv_rf_ram
#(.width (rf_width),
.csr_regs (csr_regs))
rf_ram
(.i_clk (wb_clk),
.i_waddr (rf_waddr),
.i_wdata (rf_wdata),
.i_wen (rf_wen),
.i_raddr (rf_raddr),
.i_ren (rf_ren),
.o_rdata (rf_rdata));
servile
#(.width (width),
.sim (sim[0]),
.debug (debug),
.with_c (compress[0]),
.with_csr (with_csr[0]),
.with_mdu (with_mdu))
cpu
(
.clk (wb_clk),
.i_rst (wb_rst),
.i_clk (wb_clk),
.i_rst (wb_rst),
.i_timer_irq (timer_irq),
`ifdef RISCV_FORMAL
.rvfi_valid (),
.rvfi_order (),
.rvfi_insn (),
.rvfi_trap (),
.rvfi_halt (),
.rvfi_intr (),
.rvfi_mode (),
.rvfi_ixl (),
.rvfi_rs1_addr (),
.rvfi_rs2_addr (),
.rvfi_rs1_rdata (),
.rvfi_rs2_rdata (),
.rvfi_rd_addr (),
.rvfi_rd_wdata (),
.rvfi_pc_rdata (),
.rvfi_pc_wdata (),
.rvfi_mem_addr (),
.rvfi_mem_rmask (),
.rvfi_mem_wmask (),
.rvfi_mem_rdata (),
.rvfi_mem_wdata (),
`endif
.o_ibus_adr (wb_ibus_adr),
.o_ibus_cyc (wb_ibus_cyc),
.i_ibus_rdt (wb_ibus_rdt),
.i_ibus_ack (wb_ibus_ack),
.o_wb_mem_adr (wb_mem_adr),
.o_wb_mem_dat (wb_mem_dat),
.o_wb_mem_sel (wb_mem_sel),
.o_wb_mem_we (wb_mem_we),
.o_wb_mem_stb (wb_mem_stb),
.i_wb_mem_rdt (wb_mem_rdt),
.i_wb_mem_ack (wb_mem_ack),
.o_dbus_adr (wb_dbus_adr),
.o_dbus_dat (wb_dbus_dat),
.o_dbus_sel (wb_dbus_sel),
.o_dbus_we (wb_dbus_we),
.o_dbus_cyc (wb_dbus_cyc),
.i_dbus_rdt (wb_dbus_rdt),
.i_dbus_ack (wb_dbus_ack));
.o_wb_ext_adr (wb_ext_adr),
.o_wb_ext_dat (wb_ext_dat),
.o_wb_ext_sel (wb_ext_sel),
.o_wb_ext_we (wb_ext_we),
.o_wb_ext_stb (wb_ext_stb),
.i_wb_ext_rdt (wb_ext_rdt),
.i_wb_ext_ack (wb_ext_ack),
.o_rf_waddr (rf_waddr),
.o_rf_wdata (rf_wdata),
.o_rf_wen (rf_wen),
.o_rf_raddr (rf_raddr),
.o_rf_ren (rf_ren),
.i_rf_rdata (rf_rdata));
endmodule

View file

@ -1,39 +0,0 @@
/* Arbitrates between dbus and ibus accesses.
* Relies on the fact that not both masters are active at the same time
*/
module servant_arbiter
(
input wire [31:0] i_wb_cpu_dbus_adr,
input wire [31:0] i_wb_cpu_dbus_dat,
input wire [3:0] i_wb_cpu_dbus_sel,
input wire i_wb_cpu_dbus_we,
input wire i_wb_cpu_dbus_cyc,
output wire [31:0] o_wb_cpu_dbus_rdt,
output wire o_wb_cpu_dbus_ack,
input wire [31:0] i_wb_cpu_ibus_adr,
input wire i_wb_cpu_ibus_cyc,
output wire [31:0] o_wb_cpu_ibus_rdt,
output wire o_wb_cpu_ibus_ack,
output wire [31:0] o_wb_cpu_adr,
output wire [31:0] o_wb_cpu_dat,
output wire [3:0] o_wb_cpu_sel,
output wire o_wb_cpu_we,
output wire o_wb_cpu_cyc,
input wire [31:0] i_wb_cpu_rdt,
input wire i_wb_cpu_ack);
assign o_wb_cpu_dbus_rdt = i_wb_cpu_rdt;
assign o_wb_cpu_dbus_ack = i_wb_cpu_ack & !i_wb_cpu_ibus_cyc;
assign o_wb_cpu_ibus_rdt = i_wb_cpu_rdt;
assign o_wb_cpu_ibus_ack = i_wb_cpu_ack & i_wb_cpu_ibus_cyc;
assign o_wb_cpu_adr = i_wb_cpu_ibus_cyc ? i_wb_cpu_ibus_adr : i_wb_cpu_dbus_adr;
assign o_wb_cpu_dat = i_wb_cpu_dbus_dat;
assign o_wb_cpu_sel = i_wb_cpu_dbus_sel;
assign o_wb_cpu_we = i_wb_cpu_dbus_we & !i_wb_cpu_ibus_cyc;
assign o_wb_cpu_cyc = i_wb_cpu_ibus_cyc | i_wb_cpu_dbus_cyc;
endmodule

32
servant/servant_ax309.v Normal file
View file

@ -0,0 +1,32 @@
`default_nettype none
module servant_ax309
(
input wire i_clk,
input wire i_rst,
output wire o_uart_tx,
output wire q);
parameter memfile = "zephyr_hello.hex";
parameter memsize = 8192;
wire wb_clk;
wire wb_rst;
assign o_uart_tx = q;
servant_ax309_clock_gen
clock_gen
(.i_clk (i_clk),
.i_rst (i_rst),
.o_clk (wb_clk),
.o_rst (wb_rst));
servant
#(.memfile (memfile),
.memsize (memsize))
servant
(.wb_clk (wb_clk),
.wb_rst (wb_rst),
.q (q));
endmodule

View file

@ -0,0 +1,35 @@
`default_nettype none
module servant_ax309_clock_gen
(input wire i_clk,
input wire i_rst,
output wire o_clk,
output reg o_rst);
wire clkfb;
wire locked;
reg locked_r;
PLL_BASE
#(.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT(16),
.CLKIN_PERIOD(20.0), //50MHz
.CLKOUT1_DIVIDE(25), //32MHz
.DIVCLK_DIVIDE(1))
PLL_BASE_inst
(.CLKOUT1(o_clk),
.CLKOUT2(),
.CLKOUT3(),
.CLKOUT4(),
.CLKOUT5(),
.CLKFBOUT(clkfb),
.LOCKED(locked),
.CLKIN(i_clk),
.RST(~i_rst),
.CLKFBIN(clkfb));
always @(posedge o_clk) begin
locked_r <= locked;
o_rst <= !locked_r;
end
endmodule

33
servant/servant_cmod_a7.v Normal file
View file

@ -0,0 +1,33 @@
`default_nettype none
module servant_cmod_a7
(
input wire i_clk,
input wire i_rst,
output wire o_uart_tx,
output wire q);
parameter memfile = "zephyr_hello.hex";
parameter memsize = 8192;
wire wb_clk;
wire wb_rst;
assign o_uart_tx = q;
servant_cmod_a7_clock_gen
clock_gen
(.i_clk (i_clk),
.i_rst (i_rst),
.o_clk (wb_clk),
.o_rst (wb_rst));
servant
#(.memfile (memfile),
.memsize (memsize))
servant
(.wb_clk (wb_clk),
.wb_rst (wb_rst),
.q (q));
endmodule
`default_nettype wire

View file

@ -0,0 +1,35 @@
`default_nettype none
module servant_cmod_a7_clock_gen
(input wire i_clk,
input wire i_rst,
output wire o_clk,
output reg o_rst);
wire clkfb;
wire locked;
reg locked_r;
MMCME2_BASE
#(.CLKIN1_PERIOD (83.333), //12MHz
/* Set VCO frequency to 12*64=768 MHz
Allowed values are 2.0 to 64.0. Resulting VCO freq
needs to be 600-1200MHz */
.CLKFBOUT_MULT_F (64.000),
.CLKOUT0_DIVIDE_F (48.000)) // 768/48 = 16 MHz
pll
(.CLKIN1 (i_clk),
.RST (i_rst),
.CLKOUT0 (o_clk),
.LOCKED (locked),
.CLKFBOUT (clkfb),
.CLKFBIN (clkfb));
always @(posedge o_clk) begin
locked_r <= locked;
o_rst <= !locked_r;
end
endmodule
`default_nettype wire

View file

@ -0,0 +1,31 @@
`default_nettype none
module servant_ecp5_evn
(
input wire clk,
input wire nreset,
output wire led0,
output wire uart_txd);
parameter memfile = "zephyr_hello.hex";
parameter memsize = 8192;
wire wb_clk;
wire wb_rst;
assign led0 = nreset;
servant_ecp5_evn_clock_gen clock_gen
(.i_clk (clk),
.i_rst (!nreset),
.o_clk (wb_clk),
.o_rst (wb_rst));
servant
#(.memfile (memfile),
.memsize (memsize))
servant
(.wb_clk (wb_clk),
.wb_rst (wb_rst),
.q (uart_txd));
endmodule

View file

@ -0,0 +1,25 @@
`default_nettype none
module servant_ecp5_evn_clock_gen
(
input i_clk,
input i_rst,
output o_clk,
output o_rst);
wire locked;
reg [1:0] rst_reg;
always @(posedge o_clk)
if (i_rst)
rst_reg <= 2'b11;
else
rst_reg <= {!locked, rst_reg[1]};
assign o_rst = rst_reg[0];
ecp5_evn_pll pll
(.clki (i_clk),
.clko (o_clk),
.locked (locked));
endmodule

75
servant/servant_gmm7550.v Normal file
View file

@ -0,0 +1,75 @@
`timescale 1ns / 1ps
module servant_gmm7550(
input wire ser_clk,
output wire led_green,
output wire led_red_n,
output wire uart_tx);
// parameter memfile = "zephyr_hello.hex";
parameter memfile = "blinky.hex";
parameter memsize = 8192;
wire clk270, clk180, clk90, clk0, usr_ref_out;
wire usr_pll_lock_stdy, usr_pll_lock;
wire usr_rstn;
reg[4:0] rst;
wire sys_clk;
wire sys_rst;
wire sys_rst_n;
wire q;
assign led_red_n = 1'b1;
assign led_green = q;
assign uart_tx = q;
CC_PLL #(
.REF_CLK("100.0"), // reference input in MHz
.OUT_CLK("32.0"), // pll output frequency in MHz
.PERF_MD("SPEED"), // LOWPOWER, ECONOMY, SPEED
.LOCK_REQ(1), // Lock status required before PLL output enable
.LOW_JITTER(1), // 0: disable, 1: enable low jitter mode
.CI_FILTER_CONST(2), // optional CI filter constant
.CP_FILTER_CONST(4) // optional CP filter constant
) pll_inst (
.CLK_REF(ser_clk), .CLK_FEEDBACK(1'b0), .USR_CLK_REF(1'b0),
.USR_LOCKED_STDY_RST(1'b0), .USR_PLL_LOCKED_STDY(usr_pll_lock_stdy), .USR_PLL_LOCKED(usr_pll_lock),
.CLK270(clk270), .CLK180(clk180), .CLK90(clk90), .CLK0(clk0), .CLK_REF_OUT(usr_ref_out)
);
assign sys_clk = clk0;
CC_USR_RSTN usr_rst_inst
(.USR_RSTN(usr_rstn));
always @(posedge sys_clk or negedge usr_rstn)
begin
if (!usr_rstn) begin
rst <= 5'b01111;
end else begin
if (usr_pll_lock) begin
if (!rst[4]) begin
rst <= rst - 1;
end else begin
rst <= rst;
end
end else begin
rst <= 5'b01111;
end
end
end
assign sys_rst = !rst[4];
assign sys_rst_n = rst[4];
servant
#(.memfile (memfile),
.memsize (memsize))
servant
(.wb_clk (sys_clk),
.wb_rst (sys_rst),
.q (q));
endmodule

View file

@ -0,0 +1,46 @@
// based on servant_upduino2.v
`default_nettype none
module servant_md_kolibri
(
input wire clk48,
output wire led,
output wire tx);
parameter memfile = "zephyr_hello.hex";
parameter memsize = 8192;
wire clk32;
wire locked;
reg rst = 1'b1;
// 48MHz -> 32MHz
SB_PLL40_CORE #(
.FEEDBACK_PATH("SIMPLE"),
.DIVR(4'b0010), // DIVR = 2
.DIVF(7'b0111111), // DIVF = 63
.DIVQ(3'b101), // DIVQ = 5
.FILTER_RANGE(3'b001) // FILTER_RANGE = 1
) uut (
.LOCK(locked),
.RESETB(1'b1),
.BYPASS(1'b0),
.REFERENCECLK(clk48),
.PLLOUTCORE(clk32)
);
always @(posedge clk32)
rst <= !locked;
servant
#(.memfile (memfile),
.memsize (memsize))
servant
(.wb_clk (clk32),
.wb_rst (rst),
.q (tx));
assign led = tx;
endmodule

View file

@ -16,13 +16,6 @@ module servant_mux
output wire [31:0] o_wb_cpu_rdt,
output reg o_wb_cpu_ack,
output wire [31:0] o_wb_mem_adr,
output wire [31:0] o_wb_mem_dat,
output wire [3:0] o_wb_mem_sel,
output wire o_wb_mem_we,
output wire o_wb_mem_cyc,
input wire [31:0] i_wb_mem_rdt,
output wire o_wb_gpio_dat,
output wire o_wb_gpio_we,
output wire o_wb_gpio_cyc,
@ -37,8 +30,8 @@ module servant_mux
wire [1:0] s = i_wb_cpu_adr[31:30];
assign o_wb_cpu_rdt = s[1] ? i_wb_timer_rdt :
s[0] ? {31'd0,i_wb_gpio_rdt} : i_wb_mem_rdt;
assign o_wb_cpu_rdt = s[1] ? i_wb_timer_rdt : {31'd0,i_wb_gpio_rdt};
always @(posedge i_clk) begin
o_wb_cpu_ack <= 1'b0;
if (i_wb_cpu_cyc & !o_wb_cpu_ack)
@ -47,43 +40,12 @@ module servant_mux
o_wb_cpu_ack <= 1'b0;
end
assign o_wb_mem_adr = i_wb_cpu_adr;
assign o_wb_mem_dat = i_wb_cpu_dat;
assign o_wb_mem_sel = i_wb_cpu_sel;
assign o_wb_mem_we = i_wb_cpu_we;
assign o_wb_mem_cyc = i_wb_cpu_cyc & (s == 2'b00);
assign o_wb_gpio_dat = i_wb_cpu_dat[0];
assign o_wb_gpio_we = i_wb_cpu_we;
assign o_wb_gpio_cyc = i_wb_cpu_cyc & (s == 2'b01);
assign o_wb_gpio_cyc = i_wb_cpu_cyc & !s[1];
assign o_wb_timer_dat = i_wb_cpu_dat;
assign o_wb_timer_we = i_wb_cpu_we;
assign o_wb_timer_cyc = i_wb_cpu_cyc & s[1];
generate
if (sim) begin
wire sig_en = (i_wb_cpu_adr[31:28] == 4'h8) & i_wb_cpu_cyc & o_wb_cpu_ack;
wire halt_en = (i_wb_cpu_adr[31:28] == 4'h9) & i_wb_cpu_cyc & o_wb_cpu_ack;
reg [1023:0] signature_file;
integer f = 0;
initial
/* verilator lint_off WIDTH */
if ($value$plusargs("signature=%s", signature_file)) begin
$display("Writing signature to %0s", signature_file);
f = $fopen(signature_file, "w");
end
/* verilator lint_on WIDTH */
always @(posedge i_clk)
if (sig_en & (f != 0))
$fwrite(f, "%c", i_wb_cpu_dat[7:0]);
else if(halt_en) begin
$display("Test complete");
$finish;
end
end
endgenerate
endmodule

View file

@ -71,7 +71,7 @@ module servant_orangecrab
.CLKOS2(), // secondary output
.CLKOS3(), // secondary output
.LOCK(pll_locked), // lock indicator
.INTLOCK(), // internal lock indictor
.INTLOCK(), // internal lock indicator
.REFCLK(), // output of ref select mux
.CLKINTFB() // internal fb
);

66
servant/servant_pf.v Normal file
View file

@ -0,0 +1,66 @@
`default_nettype none
module servant_pf (
input wire i_clk,
input wire resetb,
output wire o_led1,
output wire o_led2,
output wire o_led3 = 1'b0,
output wire o_led4 = 1'b0,
output wire o_led5 = 1'b0,
output wire o_led6 = 1'b0,
output wire o_led7 = 1'b0,
output wire o_led8 = 1'b0,
output wire o_uart_tx);
parameter memfile = "zephyr_hello.hex";
parameter memsize = 8192;
wire clk;
wire rst;
wire q;
wire CLKINT_0_Y;
reg heartbeat;
CLKINT CLKINT_0(
.A (i_clk),
.Y (CLKINT_0_Y)
);
servant_pf_clock_gen #(
.refclk(50),
.frequency(32)
) clock_gen (
.i_clk (CLKINT_0_Y),
.o_clk (clk)
);
servant #(
.memfile (memfile),
.memsize (memsize)
) servant (
.wb_clk (clk),
.wb_rst (rst),
.q (q)
);
// heartbeat LED
reg [$clog2(32000000)-1:0] count = 0;
always @(posedge clk) begin
if (rst) begin
count <= 0;
heartbeat <= 0;
end else
count <= count + 1;
if (count == 32000000-1) begin
heartbeat <= !heartbeat;
count <= 0;
end
end
assign rst = ~resetb;
assign o_led1 = q;
assign o_led2 = heartbeat;
assign o_uart_tx = q;
endmodule

View file

@ -0,0 +1,157 @@
`timescale 1 ns/100 ps
module servant_pf_clock_gen(
input wire i_clk,
output wire o_clk,
output reg o_lock);
// for documentation
parameter refclk = 50;
parameter frequency = 32;
// PLL in internal Post-VCO Feedback mode
localparam [11:0] fbdiv = 12'b100111000000; // 2496
localparam [5:0] rfdiv = 6'b011001; // 25;
localparam vco = 4992; // refclk * fbdiv / rfdiv;
localparam [6:0] odiv = 7'b0100111; // vco / (4 * frequency);
wire gnd_net, vcc_net, pll_inst_0_clkint_0;
wire nc0, nc1, nc2, nc3, nc4, nc5, nc6, nc7, nc8, nc9, nc10, nc11, nc12,
nc13, nc14, nc15, nc16, nc17, nc18, nc19, nc20, nc21, nc22, nc23,
nc24, nc25, nc26, nc27, nc28, nc29, nc30, nc31, nc32, nc33, nc34,
nc35, nc36, nc37, nc38, nc39, nc40;
VCC vcc_inst (.Y(vcc_net));
GND gnd_inst (.Y(gnd_net));
PLL #(
.VCOFREQUENCY(vco),
.DELAY_LINE_SIMULATION_MODE(""),
.DATA_RATE(0.0),
.FORMAL_NAME(""),
.INTERFACE_NAME(""),
.INTERFACE_LEVEL(3'b0),
.SOFTRESET(1'b0),
.SOFT_POWERDOWN_N(1'b1),
.RFDIV_EN(1'b1),
.OUT0_DIV_EN(1'b1),
.OUT1_DIV_EN(1'b0),
.OUT2_DIV_EN(1'b0),
.OUT3_DIV_EN(1'b0),
.SOFT_REF_CLK_SEL(1'b0),
.RESET_ON_LOCK(1'b1),
.BYPASS_CLK_SEL(4'b0),
.BYPASS_GO_EN_N(1'b1),
.BYPASS_PLL(4'b0),
.BYPASS_OUT_DIVIDER(4'b0),
.FF_REQUIRES_LOCK(1'b0),
.FSE_N(1'b0),
.FB_CLK_SEL_0(2'b00),
.FB_CLK_SEL_1(1'b0),
.RFDIV(rfdiv),
.FRAC_EN(1'b0),
.FRAC_DAC_EN(1'b0),
.DIV0_RST_DELAY(3'b000),
.DIV0_VAL(odiv),
.DIV1_RST_DELAY(3'b0),
.DIV1_VAL(7'b1),
.DIV2_RST_DELAY(3'b0),
.DIV2_VAL(7'b1),
.DIV3_RST_DELAY(3'b0),
.DIV3_VAL(7'b1),
.DIV3_CLK_SEL(1'b0),
.BW_INT_CTRL(2'b0),
.BW_PROP_CTRL(2'b01),
.IREF_EN(1'b1),
.IREF_TOGGLE(1'b0),
.LOCK_CNT(4'b1000),
.DESKEW_CAL_CNT(3'b110),
.DESKEW_CAL_EN(1'b1),
.DESKEW_CAL_BYPASS(1'b0),
.SYNC_REF_DIV_EN(1'b0),
.SYNC_REF_DIV_EN_2(1'b0),
.OUT0_PHASE_SEL(3'b000),
.OUT1_PHASE_SEL(3'b0),
.OUT2_PHASE_SEL(3'b0),
.OUT3_PHASE_SEL(3'b0),
.SOFT_LOAD_PHASE_N(1'b1),
.SSM_DIV_VAL(6'b1),
.FB_FRAC_VAL(24'b0),
.SSM_SPREAD_MODE(1'b0),
.SSM_MODULATION(5'b00101),
.FB_INT_VAL(fbdiv),
.SSM_EN_N(1'b1),
.SSM_EXT_WAVE_EN(2'b0),
.SSM_EXT_WAVE_MAX_ADDR(8'b0),
.SSM_RANDOM_EN(1'b0),
.SSM_RANDOM_PATTERN_SEL(3'b0),
.CDMUX0_SEL(2'b0),
.CDMUX1_SEL(1'b1),
.CDMUX2_SEL(1'b0),
.CDELAY0_SEL(8'b0),
.CDELAY0_EN(1'b0),
.DRI_EN(1'b1)
) pll_inst_0 (
.LOCK(o_lock),
.SSCG_WAVE_TABLE_ADDR({
nc0, nc1, nc2, nc3, nc4, nc5, nc6, nc7
}),
.DELAY_LINE_OUT_OF_RANGE(),
.POWERDOWN_N(vcc_net),
.OUT0_EN(vcc_net),
.OUT1_EN(gnd_net),
.OUT2_EN(gnd_net),
.OUT3_EN(gnd_net),
.REF_CLK_SEL(gnd_net),
.BYPASS_EN_N(vcc_net),
.LOAD_PHASE_N(vcc_net),
.SSCG_WAVE_TABLE({
gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net,
gnd_net
}),
.PHASE_DIRECTION(gnd_net),
.PHASE_ROTATE(gnd_net),
.PHASE_OUT0_SEL(gnd_net),
.PHASE_OUT1_SEL(gnd_net),
.PHASE_OUT2_SEL(gnd_net),
.PHASE_OUT3_SEL(gnd_net),
.DELAY_LINE_MOVE(gnd_net),
.DELAY_LINE_DIRECTION(gnd_net),
.DELAY_LINE_WIDE(gnd_net),
.DELAY_LINE_LOAD(vcc_net),
.REFCLK_SYNC_EN(gnd_net),
.REF_CLK_0(i_clk),
.REF_CLK_1(gnd_net),
.FB_CLK(gnd_net),
.OUT0(pll_inst_0_clkint_0),
.OUT1(),
.OUT2(),
.OUT3(),
.DRI_CLK(gnd_net),
.DRI_CTRL({
gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net,
gnd_net, gnd_net, gnd_net, gnd_net
}),
.DRI_WDATA({
gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net,
gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net,
gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net,
gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net, gnd_net,
gnd_net, gnd_net, gnd_net, gnd_net, gnd_net
}),
.DRI_ARST_N(vcc_net),
.DRI_RDATA({
nc8, nc9, nc10, nc11, nc12, nc13, nc14, nc15, nc16, nc17, nc18,
nc19, nc20, nc21, nc22, nc23, nc24, nc25, nc26, nc27, nc28, nc29,
nc30, nc31, nc32, nc33, nc34, nc35, nc36, nc37, nc38, nc39,
nc40
}),
.DRI_INTERRUPT()
);
CLKINT clkint_0 (
.A(pll_inst_0_clkint_0),
.Y(o_clk)
);
endmodule

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