This project involves the development of a Turing-complete Simple RISC Machine capable of executing 16-bit ARM instruction sets. Below, I'll discuss the challenges faced, key learnings, and future plans for the project. Afterwards I will give a detailed project overview.
It goes without saying that a project like this involves countless hours of debugging and staring at the same piece of code trying to figure out what exactly went wrong. Here are some notable challenges I faced:
-
Error or Typo in Code: Debugging required meticulous scrutiny due to errors or typos in the code.
-
Inefficient Code Implementation: Initially, I started writing code without proper planning. I soon realized that some parts of my code were too lengthy, and the implementation was inefficient. I had also assigned too many states for my finite state machine which made my code complicated
-
Inferred Latches: Synthesis issues arose in Quartus due to inferred latches in the initial design.
To overcome these challenges, these are some of the strategies I employed and my learnings
-
Modularization: I made separate modules for each blob of hardware displayed in the diagrams below. This made it easier for debugging and implementing new features.
-
Design Planning: I learned the importance of planning and designing the entire system before you start writing code. For example, I made diagrams to identify how each hardware should communicate with one another. I also create flowcharts to figure out the states I need for my finite state machine in order to execute all the instructions.
Strategies to expedite debugging processes:
-
ModelSim WaveForm Generator: After writing my simulation testbenches, I like to view the waveform generated in ModelSim to ensure correct behavior of the hardware. I also put random values for my input to check if I am getting the desired result.
-
Display Messages: Sometimes my code doesn’t simply because of a typo, wrong bit assignments for my states in my finite state machine and many more. That’s why I like to employ display messages almost everywhere in my simulation testbench which displays the output in every step. This helps me pinpoint where exactly the code went wrong.
-
Error Bit: I have an error bit that turns high whenever there is an error (e.g. I do not get the desired output). I also write code so that the simulation stops immediately when the error is high. This approach aids in pinpointing the root cause of the issue promptly.
-
Edge Cases: I like to identify the edge cases for my projects and write simulation testbenches that cover all these cases. These scenarios encompass the extreme or unexpected conditions that the system may encounter. I am currently learning about formal verification methods and UVM so that I can employ it on my RISC machine.
-
Web Search: Last but not least, I can only find answers from StackOverflow and other platforms to troubleshoot the problem. It is especially useful when I receive error code from ModelSim and Quartus as I can learn more about it online
Currently in progress, I am expanding the functionality of the project by incorporating branches, utilizing both for
and while
loops in C. This enhancement aims to bring more versatility and control flow capabilities to the codebase.
As part of ongoing efforts, I am deepening my understanding of Universal Verification Methodology (UVM), with a specific emphasis on constrained random verification techniques. This knowledge acquisition will enable me to implement robust testing methodologies, leveraging randomized inputs to enhance test coverage for my RISC machine.
Aiming for the next phase of project development, I have plans to implement a 2-way superscalar pipelined processor. This advanced processor architecture introduces parallelism, enhancing the execution efficiency of instructions. The implementation of this feature will contribute to the project's overall performance and computational capabilities.
The RISC machine was subdivided into various modules which will be explained below. After it is synthesized and put it into the FPGA, the input can be sent through the switches (SW0 to SW7) and the output is displayed in the LEDs of the DE1-SoC. Here is a high-level illustration of the machine:
The following diagram illustrates the ARM instruction encodings that the machine can run:
Description: SystemVerilog is a hardware description and verification language that combines hardware description features of Verilog with advanced verification capabilities.
Reason for Use: Chosen for designing and describing the hardware components of the RISC machine. Its support for both design and verification aspects makes it suitable for developing complex digital systems.
Description: ModelSim is a hardware simulation and debug environment used for testing and verifying hardware designs.
Reason for Use: Employed for simulation testbenches to verify the correctness of the RISC machine's behavior before implementation on the FPGA. ModelSim's waveform viewer aids in analyzing and debugging simulation results.
Description: Quartus is an integrated development environment (IDE) used for the design and programming of Field-Programmable Gate Arrays (FPGAs).
Reason for Use: Selected for synthesis and implementation of the RISC machine design onto the DE1-SoC FPGA board. Quartus provides tools for compiling, mapping, and generating configuration files for FPGA programming.
Description: A Field-Programmable Gate Array (FPGA) is a customizable hardware component that allows for reconfigurable digital circuit design.
Reason for Use: The DE1-SoC FPGA board serves as the hardware platform for implementing and testing the RISC machine design. FPGAs offer flexibility and versatility, allowing the reconfiguration of digital circuits, making them suitable for prototyping and development.
Description: ARM (Acorn RISC Machine) is a family of Reduced Instruction Set Computing (RISC) architectures for computer processors.
Reason for Use: Utilized for the 16-bit ARM instruction set that the RISC machine is designed to execute. ARM instructions provide a standardized set of operations for programming the RISC machine, contributing to compatibility and ease of programming.
Responsible for decoding instructions and transmitting data to Datapath and FSM. It also receives instructions from the FSM based on what state it is in.
This module contains the register file for storing data to registers (8 in total), ALU for carrying out all the arithmetic operations, shifter to shift the bits, multiplexers to choose correct data_in and load enable registers to store the inputs and outputs.
These are the encodings for the ALU operations:
These are the encodings for the shift operations:
The RTL diagram of the hardware after synthesis can be found here.
The RISC_top_tb
testbench executes the following ARM instructions (encoding is stored in data.txt)
I did a test run by putting the value 37 through the switches SW0 through SW7 (100101 in binary). After pressing the clock (KEY0) a couple of times, I get the desired output 74 (1001010 in binary) displayed on the red LEDs.
This is a picture of the DE1-SoC board after I received the output.
Here is the video of me demonstrating it on the board: https://drive.google.com/file/d/1t0AIagljHpC8mslxzSB-MaZslU0uAJmQ/view?usp=sharing