This project was developed as a part of the MEST course "ChipCraft: The Art of Chip Design for Non-Experts". As a part of the course, students are walked through the design and implementation of a RISC-V core. At the time that I took this course, students opting to tape out their RISC-V core were limited to a single, hard-wired program in place of a true instruction "memory". This led me to put together a simple UART controller tied to a 64-byte register file that could act as programmable instruction memory. Future students (or anyone experimenting with processor design) can utilize the UART modules in this design to enable programmability of their processor designs.
I decided on UART over other protocols because of its simplicity and my own familiarity with it. USB-serial adapters are easy to find and there are several serial terminals out there. I also decided to implement a very simple auto-baud detection in my design instead of a fixed baud rate. This was done due to my own uncertainty in the clock frequency.
This project implements a simplified RISC-V core that runs instructions from a 64-byte register file that is programmed by the user via a UART interface.
The RISC-V core adheres to RV32I with the following exceptions:
Instruction memory and data memory are isolated. Instruction memory and data memory are implemented as 64-byte (16-word) and 16-byte (4-word) register files that are written to and read from via a UART interface.
The UART controller operates in two modes: "PROGRAM" and "DATA READ". Upon reset of the device, the controller enters "PROGRAM MODE". During this time, the user sends a sync packet, followed by the RV32I binary (64-bytes max). Once 64 bytes have been written (unused space can be filled with "ADD x0, x0, x0" instructions), the controller will enter "DATA READ" mode. In this mode, the user can read the contents of data memory by sending a single packet (the contents of this packet do not matter).
For those wanting to implement their own processor design, use the template TL-Verilog file /src/tlv/uart_template.tlv
and modify line 89 with a URL pointing to your design. See /src/tlv/cpu_custom.tlv
for an example processor design.
Step-by-step usage:
/test/bin
and go to step 12. If the serial terminal does not support this, you can manually program via steps 9-11.To run cocotb tests, run make
in /test
directory. The cocotb test will iterate through each program name in /test/programs.f
and load/execute each program's corresponding binary file in /test/bin
. Data memory contents are compared to each program's corresponding text file in /test/solutions
to determine pass/fail.
To add new programs to the test, my current workflow is as follows. I did not get around to figuring out a simple, straightforward way of assembling in python, so the current workflow is admittedly cumbersome.
/test/asm/[program_name].asm
. Keep in mind that there is a max of 16 instructions!.asm
file with an online RV32I assembler. Paste output hex into a text file and save it as /test/hexstr/[program_name].hexstr
./scripts/hexstr_to_bin.py
. This will convert the hex strings to a binary file and save to /test/hexstr/[program_name].bin
. It will also write the UART sync word to the beginning of the file (0x55) and backfill unused instructions with ADD x0, x0, x0 instructions.Use Makerchip IDE (makerchip.com) for testing of RISC-V core only.
/src/tlv/uart_template.tlv
.set(MAKERCHIP, 0)
to set(MAKERCHIP, 1)
# | Input | Output | Bidirectional |
---|---|---|---|
0 | |||
1 | |||
2 | RX | TX | |
3 | |||
4 | Data on TX | ||
5 | Data on RX | ||
6 | UART Controller in Program Mode | ||
7 | System Reset | System Reset (LED) |