DOI : 10.5281/zenodo.21914141
- Open Access
- Authors : K Charan, Dr. Vikramkumar Pudi
- Paper ID : IJERTV15IS080218
- Volume & Issue : Volume 15, Issue 08 , August – 2026
- Published (First Online): 13-08-2026
- ISSN (Online) : 2278-0181
- Publisher Name : IJERT
- License:
This work is licensed under a Creative Commons Attribution 4.0 International License
Design and Verication of a Pipelined RISC-V (RV32IM) Processor with RTOS Integration
submitted in partial fulllment of the requirements for the research internship
RESEARCH INTERNSHIP
in
VLSI & Embedded Systems Lab
by
K.CHARAN
Supervisor(s)
Dr. VikramKumar Pudi
DEPARTMENT OF ELECTRICAL ENGINEERING
INDIAN INSTITUTE OF TECHNOLOGY TIRUPATI May 2025
I declare that this written submission represents my ideas in my own words and where others ideas or words have been included, I have adequately cited and referenced the original sources. I also declare that I have adhered to all principles of academic honesty and integrity and have not misrepresented or fabricated or falsied any idea/- data/fact/source in my submission to the best of my knowledge. I understand that any violation of the above will be cause for disciplinary action by the Institute and can also evoke penal action from the sources which have thus not been properly cited or from whom proper permission has not been taken when needed.
Place: Tirupati Date: 07-05-2025
Signature
K.Charan
Note: If more than one authors mentioned in the cover page, modify each I by we and then include remaining authors name at the bottom of the page. Signature of each author in this page is must.
We would like to express our special thanks of gratitude to Dr. Vikramkumar Pudi for giving his valuable suggestions during the planning and development of this work. His guidance was extremely helpful and motivating.
I would like to thank Dr Jaynarayan T Tudu, Assistant Professor Indian Institute of Technology. Tirupati, in guiding me through some key insights. I would like to thank the VLSI lab JTS Mr. Kumar for his help and Vanama Sai Srinivas(EE22B052) .Lastly, I would also like to thank our parents and friends, who were supportive throughout the project.
Place: Tirupati Date: 07-05-2025
Signature
K.Charan
|
ACKNOWLEDGEMENTS |
i |
|
LIST OF FIGURES |
vi |
|
LIST OF TABLES |
viii |
|
ABBREVIATIONS |
ix |
|
NOTATION |
x |
|
1 INTRODUCTION |
1 |
|
1.1 Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . |
3 |
|
2 LITERATURE REVEIW |
4 |
|
2.1 RV32I ISA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . |
4 |
|
3 PROCESSOR DESIGN |
7 |
|
3.1 Basic Functionality Modules . . . . . . . . . . . . . . . . . . . . . |
7 |
|
3.2 Single Cycle Architecture . . . . . . . . . . . . . . . . . . . . . . . |
14 |
|
3.3 Adding M extension to RV32I . . . . . . . . . . . . . . . . . . . . |
20 |
|
3.4 Pipelining Design OF RV32IM . . . . . . . . . . . . . . . . . . . . |
24 |
|
3.4.1 Designing . . . . . . . . . . . . . . . . . . . . . . . . . . . |
24 |
|
3.4.2 Hazards . . . . . . . . . . . . . . . . . . . . . . . . . . . . |
25 |
|
4 VERIFICATION METHODOLOGY |
27 |
|
4.1 Softwares used . . . . . . . . . . . . . . . . . . . . . . . . . . . . |
27 |
|
4.2 Simulation Based Verication . . . . . . . . . . . . . . . . . . . . |
27 |
|
4.2.1 Arithmetic operation programe . . . . . . . . . . . . . . . . |
28 |
|
4.2.2 Fibonacci program . . . . . . . . . . . . . . . . . . . . . . |
29 |
|
4.3 FPGA Prototyping and Testing with Arty A7 on RISC-V Core . . . |
30 |
|
4.3.1 Arty A7 FPGA Platform Specications . . . . . . . . . . . |
31 |
4.3.2 FPGA based verication . . . . . . . . . . . . . . . . . . . 31
-
RESULTS AND DISCUSSION 34
-
Summary and Conclusion 35
-
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
-
RTOS Integration . . . . . . . . . . . . . . . . . . . . . . . 35
-
FPGA Verication and Sapphire SoC . . . . . . . . . . . . 36
-
Performance Comparison . . . . . . . . . . . . . . . . . . . 37
-
-
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
-
-
Processor ISA Types 39
A.0.1 Background Intro . . . . . . . . . . . . . . . . . . . . . . . 39
-
Data Path Modules Description and functionalities 41
-
Code snippets 45
-
Chisel Installation 49
-
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
-
Key Differences . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
-
How Chisel Works . . . . . . . . . . . . . . . . . . . . . . . . . . 49
-
Basic Constructs Comparison . . . . . . . . . . . . . . . . . . . . . 49
-
Combinational Logic . . . . . . . . . . . . . . . . . . . . . 49
-
Sequential Logic . . . . . . . . . . . . . . . . . . . . . . . 51
-
-
Advanced Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 52
-
Finite State Machine . . . . . . . . . . . . . . . . . . . . . 52
-
Memory Example . . . . . . . . . . . . . . . . . . . . . . . 53
-
-
Testbench Comparison . . . . . . . . . . . . . . . . . . . . . . . . 54
-
Verilog Testbench . . . . . . . . . . . . . . . . . . . . . . . 54
-
Chisel Testbench . . . . . . . . . . . . . . . . . . . . . . . 55
-
-
Parameterization Examples . . . . . . . . . . . . . . . . . . . . . . 56
-
Verilog Parameterization . . . . . . . . . . . . . . . . . . . 56
-
Chisel Parameterization . . . . . . . . . . . . . . . . . . . 56
-
-
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
-
-
Branch predictor Unit & Implementation by using Champsim 58
-
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
-
Branch Predictor Architecture . . . . . . . . . . . . . . . . . . . . 58
-
Prediction States . . . . . . . . . . . . . . . . . . . . . . . 58
-
-
Verilog Code: 2-bit Branch Predictor . . . . . . . . . . . . . . . . . 58
-
ChampSim: Branch Predictor Simulation . . . . . . . . . . . . . . 59
-
Installation Guide . . . . . . . . . . . . . . . . . . . . . . . 59
-
-
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
-
-
Instruction formats r1 . . . . . . . . . . . . . . . . . . . . . . . . 5
-
RV32I Instruction Types r1 . . . . . . . . . . . . . . . . . . . . . . 5
-
Result Write Back Mux . . . . . . . . . . . . . . . . . . . . . . . . 9
-
ALU RISCV I/O planning . . . . . . . . . . . . . . . . . . . . . . 10
-
IMMEDIATE GENERATION I/O planning . . . . . . . . . . . . . 10
-
Branch Unit I/O planning . . . . . . . . . . . . . . . . . . . . . . . 10
-
if-id-pipeline I/O planning . . . . . . . . . . . . . . . . . . . . . . 11
-
id-ex-pipeline I/O planning . . . . . . . . . . . . . . . . . . . . . . 11
-
ex-mem-pipeline I/O planning . . . . . . . . . . . . . . . . . . . . 12
-
wb-pipeline I/O planning . . . . . . . . . . . . . . . . . . . . . . . 12
-
RV32I I/O planning . . . . . . . . . . . . . . . . . . . . . . . . . . 12
-
ALU RISCV I/O planning of core . . . . . . . . . . . . . . . . . . 13
-
RV32I Data Path . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
-
R type Instruction data Path . . . . . . . . . . . . . . . . . . . . . . 15
-
L type Instruction data Path . . . . . . . . . . . . . . . . . . . . . . 15
-
RI type Instruction data Path . . . . . . . . . . . . . . . . . . . . . 16
-
S type Instruction data Path . . . . . . . . . . . . . . . . . . . . . . 16
-
LUI type Instruction data Path . . . . . . . . . . . . . . . . . . . . 17
-
BR type Instruction data Path . . . . . . . . . . . . . . . . . . . . . 17
-
JAL type Instruction data Path . . . . . . . . . . . . . . . . . . . . 18
-
JARL type Instruction data Path . . . . . . . . . . . . . . . . . . . 18
-
AUIPC type Instruction data Path . . . . . . . . . . . . . . . . . . . 19
-
M extension Computation Module . . . . . . . . . . . . . . . . . . 21
-
ALU Module for RV32IM . . . . . . . . . . . . . . . . . . . . . . 21
-
M extension Control Module . . . . . . . . . . . . . . . . . . . . . 22
-
rv32im data path . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
-
Pipelined Microarchitecture Design . . . . . . . . . . . . . . . . . 24
|
3.29 |
Data Hazard Handling . . . . . . . . . . . . . . . . . . . . . . . . |
25 |
|
3.30 |
Control Hazard Handling . . . . . . . . . . . . . . . . . . . . . . . |
26 |
|
4.1 |
Data Hazards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . |
28 |
|
4.2 |
Control Hazards . . . . . . . . . . . . . . . . . . . . . . . . . . . . |
28 |
|
4.3 |
Instructions Causing Control Hazard . . . . . . . . . . . . . . . . . |
28 |
|
4.4 |
Instructions Causing Data Hazards . . . . . . . . . . . . . . . . . . |
28 |
|
4.5 |
Arthamatic operations c++ code . . . . . . . . . . . . . . . . . . . |
29 |
|
4.6 |
Arthamatic operations output simulation . . . . . . . . . . . . . . . |
29 |
|
4.7 |
25th Fibanachi number calculation . . . . . . . . . . . . . . . . . . |
30 |
|
4.8 |
Arty A7 FPGA Board used to carry out the entire experiment . . . . |
32 |
|
4.9 |
FPGA Verication Flow . . . . . . . . . . . . . . . . . . . . . . . |
33 |
|
D.1 |
Detailed Chisel Workow . . . . . . . . . . . . . . . . . . . . . . . |
50 |
|
E.1 |
Branch Predictor Unit Successfully stored and Simulated on Champsim |
60 |
|
LIST OF TABLES |
||
|
1.1 |
Table: Description of RISC-V Base and Extension Types . . . . . . |
2 |
|
2.1 |
RV32I Instruction Purpose . . . . . . . . . . . . . . . . . . . . . . |
5 |
|
2.2 |
Arithmetic Instructions . . . . . . . . . . . . . . . . . . . . . . . . |
6 |
|
2.3 |
Logical Instructions . . . . . . . . . . . . . . . . . . . . . . . . . . |
6 |
|
2.4 |
Comparison Instructions . . . . . . . . . . . . . . . . . . . . . . . |
6 |
|
2.5 |
Special Computation Instructions . . . . . . . . . . . . . . . . . . . |
6 |
|
2.6 |
Memory Access Instructions . . . . . . . . . . . . . . . . . . . . . |
6 |
|
2.7 |
Conditional Control Flow Instructions . . . . . . . . . . . . . . . . |
6 |
|
2.8 |
Unconditional Control Flow Instructions . . . . . . . . . . . . . . . |
6 |
|
3.1 |
RV32M Standard Extension Instructions . . . . . . . . . . . . . . |
21 |
|
3.2 |
Multiplication Instructions operation . . . . . . . . . . . . . . . . . |
22 |
|
3.3 |
Division Instructions Operation . . . . . . . . . . . . . . . . . . . . |
22 |
|
3.4 |
Multiplication Instructions Control signals . . . . . . . . . . . . . . |
22 |
|
3.5 |
Division Instructions Control signals . . . . . . . . . . . . . . . . . |
22 |
|
5.1 |
Delay results of RV32IM single cycle vs Piplined Designs . . . . . |
34 |
|
5.2 |
FPGA resource utlization . . . . . . . . . . . . . . . . . . . . . . . |
34 |
|
6.1 |
Sapphire SoC Memory Map . . . . . . . . . . . . . . . . . . . . . |
37 |
|
6.2 |
Performance Comparison . . . . . . . . . . . . . . . . . . . . . . . |
37 |
|
B.1 |
Instruction Memory Ports Description . . . . . . . . . . . . . . . . |
41 |
|
B.2 |
Instruction Memory Asynchronous Reading for a single cycle . . . |
41 |
|
B.3 |
Instruction Memory Synchronous Reading for Multi-cycle or Pipelined Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . |
41 |
|
B.4 |
Instruction Memory Synchronous Writing . . . . . . . . . . . . . . |
41 |
|
B.5 |
Data Memory Ports . . . . . . . . . . . . . . . . . . . . . . . . . . |
42 |
|
B.6 |
Data Memory data to be stored control signals . . . . . . . . . . . . |
42 |
|
B.7 |
Data Memory Synchronous riting . . . . . . . . . . . . . . . . . |
42 |
-
DataMemory reads operation control signals . . . . . . . . . . . . . 42
-
Data Memory Synchronous Reading . . . . . . . . . . . . . . . . . 43
-
GPRs synchronous write & asynchronous read . . . . . . . . . . . 43
-
ImmOp and Operation . . . . . . . . . . . . . . . . . . . . . . . . 43
-
Immediate Generator Functionality . . . . . . . . . . . . . . . . . . 43
-
ALU unit control signals . . . . . . . . . . . . . . . . . . . . . . . 43
-
write back :rsltMux control signals . . . . . . . . . . . . . . . . . . 44
-
Detailed Comparison between Chisel and Verilog . . . . . . . . . . 49
ISA Instuction set architecture
RISC-V fthe verion ISA developed based on Reduced Instruction Set Architecture principles
FPGA Feild Programable Gate Array
UART Universal asynchronous Receving and Transmison
rd Destination register address of size 5 bitsto store the computed value
rs1,rs2 Data address of size 5 bits of the data needed to be computed
Imm 32 bit Immediate value decode form the INstruction
RISC(Reduced Instruction Set Computer) offers advantages over CISC (Complex Instruc- tion Set Computer) through simpler instructions, streamlined execution, and improved performance, enabling faster clock speeds and more efcient use of resources. RISC-V (Reduced Instruction Set Computer – Five) Instruction Set Architecture, a 5th major open-source RISC-based Instruction Set Architecture, originated from the efforts of researchers at the University of California, Berkeley, in 2010.
Unlike proprietary ISAs such as ARM or x86, RISC-V is freely available for anyone to use, modify, and distribute. Its modular design allows for exibility in implementation, enabling developers to customize processors for specic applications or performance requirements. The architecture is designed to be extensible, allowing developers to add custom instructions or extensions to meet specic application requirements. It is a simple load and store architecture that supports 32-bit and 64-bit base integer instruction formats, with optional extensions for specic use cases, such as oating-point arithmetic, atomic operations, and vector processing which gives exibility in terms of designing application-specic processors
RISC-V stands for "Reduced Instruction Set Computer – Five," where the "Five" refers to the fth version RISC ISA designed by designes from the University of Califor- nia, Berkeley. follows a load-store architecture, which is a type of Reg-Reg/Load-Store ISA. It has a relatively simple instruction set
|
Base |
about |
Status |
|
RV32I |
32 bit Integer GPRs |
standard |
|
RV64I |
64 bit Integer GPRs |
Ratied |
|
RV32E |
only 16 GPRs,used for embedded applications |
Draft |
|
RV128I |
128 bit Integer GPRs |
Draft |
|
Extension |
about |
Status |
|
M |
Includes Direct Multiplication, Division ability |
Ratied |
|
A |
Memory synchronization, Multi thread processing |
Ratied |
|
F |
supports single precison Floting ponit operations |
Ratied |
|
Zicsr |
enable software,to manage processor behaviour via CSRs |
Ratied |
|
C |
supports compressed instructions |
Ratied |
Table 1.1: Table: Description of RISC-V Base and Extension Types
The objective of this work is to design the RISC-V-based pipelined processor with a 32bit base ISA of RV32I along with M extension for direct multiplication and Division operations supportability and Its verication using different assembly programs, along with the help of RISC-V GNU Toolchain for for converting the c and c++ programs into the required instructions to run and test on the Designed processor
For the Design purpose, I have referred to the Computer Architecture RISC-V edition Harris book for designing principles of single-cycle and pipeline processors.I have referred the ?, and NEO32_Processor for software rmware to use the gnu toolchain for getting the instructions to run on the Designed processor.
Any RISC-V isa-based instruction would be in the six formats shown in Figure2.1. The major are R, I, S, and U, while B and J are the same as S and U except for the immediate encoding.
There are 46 instructions in the RV32 base ISA in Specications Waterman (2017) in 2017 version. later, they separated 6 instructions from it as an extension of Zicsr. According to the new version SpecicationWaterman and Asanovic (2019), there are 40 base instructions in the RV32I base ISA, whereas 2 are system instructions that play an important role in environment executions.
There are 9 types of instructions in 32-bit base ISA, The purpose of each type of Instruction is shown in the Table 2.1 and their respective encoding formats are mentioned in Figure 2.2
There are 38 Instructions of our interest . Tables 2.2,2.3 2.4 and 2.5 show the computation instructions.Where the Table 2.6 shows the load and store instructions. Tables2.7 and 2.8 show the conditional and unconditional jumps, respectively.
Figure 2.1: Instruction formats r1
|
Type |
Purpose |
|
R |
Only Registers data computation |
|
RI |
Computation with constants |
|
LD |
Memory load |
|
SR |
Memory store |
|
BR |
Conditional Looped computations |
|
JAL ,JALR |
Calling routines ,subroutines |
|
AUIPC |
To jump to functions far from the current one |
|
LUI |
Loading larger constant values |
Table 2.1: RV32I Instruction Purpose
Figure 2.2: RV32I Instruction Types r1
|
Instruction |
Computation |
|
ADD, ADDI |
Addition |
|
SUB |
Subtraction |
|
SLL, SLU |
Shift logical data left |
|
SRL, SRU |
Shift logical data right |
|
SRA, SRAI |
Shift Arithmetic data right |
Table 2.2: Arithmetic Instructions
|
Instruction |
Computation |
|
AND, ANDI OR, ORI XOR, XORI |
Bit-wise AND operation Bit-wise OR operation Bit-wise XOR operation (also used for 1s complement computation) |
Table 2.3: Logical Instructions
|
Instruction |
Computation |
|
SLT, SLTI SLTU, SLTIU |
Set 1, if signed comparisons less than Set 1, if unsigned comparisons less than |
Table 2.4: Comparison Instructions
|
Instruction |
Computation |
|
LUI AUIPC |
Load upper 20-bit immediate Add upper 20-bit immediate to PC (jump to any address in the address space) |
Table 2.5: Special Computation Instructions
| Instruction |
Computation |
|
LB, SB |
Load/store byte |
|
LH, SH |
Load/store half-word |
|
LW, SW |
Load/store word |
|
LBU, LHU |
Load/store unsigned byte, half-word |
Table 2.6: Memory Access Instructions
|
Instruction |
Operation |
|
BEQ, BNE BGE, BLT BGEU, BLTU |
Jump if (a=b), (a!=b) respectively Jump if signed (a>=b), (a<b) respectively Jump if unsigned (a>=b), (a<b) respectively |
Table 2.7: Conditional Control Flow Instructions
|
Instruction |
Operation |
|
JAL JALR |
Jump to PC+imm (typically used for function calls) jump to (rs1)+(imm) (typically used for function returns) |
Table 2.8: Unconditional Control Flow Instructions
-
Program Counter: The rst mode of communication with the processor is instruction, which we can get from the Instruction memory, with the proper address, which is obtained from the program counter, .The program counter holds the current address of the instruction, and its value is decided by the Mux, which has inputs of incremented PC value or branch or jump address value.
Immediate Generator The module shown in the gure 3.2b converts the encoded immediate value in the instruction to the required 32-bit Value according to the respective instruction format shown in the 2.1. ALU 2nd input mux(aluIn2_Mux)" We need to choose the data to be sent to the ALU unit using a mux.It chooses between the rs2data read from the GPRs or the immediate value decoded from the instruction; based on the control signal, it chooses one.
Data Memory: The Data memory shown in the gure 3.1b hold the required data must support the read and write operations when appropriate signals are enabled. We can use the asynchronous reading data memory in a single-cycle processor design with less data memory, which will be converted into the LUTRAMs in the FPGA when synthesized. When our data memory size is larger, we need to use the BRAM in the FPGA, making the memory synchronous read and synchronous write.
General Purpose registers(GPRs): Its dual port 32bit 32 number of registers stores the temporal data of the data memory. The rst register is always zero. To write this module, we need a Demux and a Mux to access the registers data. The ports for this are shown in the the gure 3.3a
Arthamatic and logic unit(ALU): The ALU module shown in the gure3.3bcan be useful for performing the computation and branch instructions. We can use the existing comparison functionality used for SLT(set less than)instruction and XOR instruction for checking greater or less than and equality comparison branch instructions respectively.
Instruction Memory(InstructionMemory_with_write) module shown in gure 3.1a gives us the instructions based on the Instruction address. I have designed this module suitable for writing its instruction byte-wise so that we can load it with required instruc- tions using a peripheral protocol. result write back mux (rslt_Mux) in gure 3.4a is usefed to select the data, write it to the respective GPRs location based on the control signal coming from the control unit.
The Modules required to design for the types of instructions mentioned in the above table are as follows.
(a) Instruction Memory (b) Data Memory
(a) ALU 2nd input mux (b) Immediate Generator
-
General Purpose Registers (GPRs) (b) Arithmetic Logic Unit (ALU)
-
Result Write Back Mux
-
Figure 3.4: Result Write Back Mux
-
ALU RISCV I/O planning
Figure 3.5: ALU RISCV I/O planning
-
IMMEDIATE GENERATION I/O planning
Figure 3.6: IMMEDIATE GENERATION I/O planning
-
Branch Unit I/O planning
Figure 3.7: Branch Unit I/O planning
-
if-id-pipeline I/O planning
Figure 3.8: if-id-pipeline I/O planning
-
id-ex-pipeline I/O planning
Figure 3.9: id-ex-pipeline I/O planning
-
ex-mem-pipeline I/O planning
Figure 3.10: ex-mem-pipeline I/O planning
-
wb-pipeline I/O planning
Figure 3.11: wb-pipeline I/O planning
-
RV32I I/O planning
Figure 3.12: RV32I I/O planning
(I) ALU RISCV I/O planning of core
Figure 3.13: ALU RISCV I/O planning of core
-
-
For Singel Cycle Design We can realize it in two ways ,In one way we have to use the asynchronous Instruction Memory ,only for an Positive edge trigerred GPRs , in a Positive edge triggered Program Counter, with a synchronous Data Memory. In another one , for a positve edge trigered program counter , along with asynchronous instruction memory and synchronous Data Memory , we have to use the negative trigerred GPRs.
The overall data Path is shown in the gure 3.14 The Respective Data Paths for each type of instruction are as below. The supportability of the data path for the all types instructions of RV32I mentioned in 2.2 are shown in the diagrams 3.15, 3.16,3.17,3.18,3.19,3.20, 3.21,3.22,3.23,
Data Paths
Figure 3.14: RV32I Data Path
Figure 3.15: R type Instruction data Path
Figure 3.16: L type Instruction data Path
Figure 3.17: RI type Instruction data Path
Figure 3.18: S type Instruction data Path
Figure 3.19: LUI type Instruction data Path
Figure 3.20: BR type Instruction data Path
Figure 3.21: JAL type Instruction data Path
Figure 3.22: JARL type Instruction data Path
Figure 3.23: AUIPC type Instruction data Path
-
RISC-V architecture can incorporate acceleration capabilities, which makes it ap- propriate for a wide range of applications and sophisticated computing requirements-V processors like the RV32IM, used as a Digital Signal Processor, that make tasks like AI processing, AI models execution more efciently compared to the RV32I ISA.
M extensioo Instructions: Building upon the RV32I base, the RV32M Extension introduces specialized ALU instructions tailored for integer multiplication and division operations. This extension enhances the computational capabilities of RISC-V proces- sors by incorporating instructions such as "MUL" "MULH" , "MULHSU" "MULHU", "DIV" and "REM" These instructions enable efcient handling of complex arithmetic tasks, particularly useful in applications requiring intensive mathematical Computa- tion containing Multiplication and division.The Instructions are listed in Table 3.1.The specic operations of the instructions are mentioned in the Table 3.3 , 3.2
M extension Computation Module :As shown in the Figure 3.24 It is a extra hard- ware module required to attach it to the existing ALU of base isa, The The multiplication module used is Array Multiplier and Division the module used is based on the repetitive subtracting and comparing method.
The Datapath modication can be done by replacing the base ISA ALU with Modular ALU for this RV32IM. It is the combination of the base ALU and the M extension module along with some muxing at the output port as shown in the Figure 3.25.The multiplication module gives the 64bit output, which is split into MSB results and LSB results, in the same way the division the module also gives the 32-bit Remainder and quotient, so by using muxes, we can select the required result
The extra Module required to generate the required control signals for the extra M extension module is as shown in Figure 3.26. Its resective control signals are listed in tables 3.4 and 3.5 for Multiplication and Divisions respectively.
Instruction
Funct7
rs2
rs1
Funct3
rd
Opcode Extension
MUL
0000001
rs2
rs1
000
rd
0110011
MULH
0000001
rs2
rs1
001
rd
0110011
MULHSU
0000001
rs2
rs1
010
rd
0110011
MULHU
0000001
rs2
rs1
011
rd
0110011
DIV
0000001
rs2
rs1
100
rd
0110011
DIVU
0000001
rs2
rs1
101
rd
0110011
REM
0000001
rs2
rs1
110
rd
0110011
REMU
0000001
rs2
rs1
111
rd
0110011
Table 3.1: RV32M Standard Extension Instructions
Figure 3.24: M extension Computation Module
Figure 3.25: ALU Module for RV32IM
The Data Path required for the RV32IM is shown in the below gure3.27.
Instruction
(Funct3)
Rd_data
Data (32-bit)
Rs1 (Multiplicand)
Rs2 (Multiplicand)
MUL
000
LSB 32-bit result
Signed
Signed
MULH
001
MSB 32-bit result
Signed
Signed
MULHSU
010
MSB 32-bit result
Signed
Unsigned
MULHU
011
MSB 32-bit result
Unsigned
Unsigned
Table 3.2: Multiplication Instructions operation
Instruction
(Funct3)
Rd
Data (32-bit)
Rs1 (Dividend)
Rs2 (Divisor)
DIV
100
Quotient
Signed
Signed
DIVU
101
Quotient
Unsigned
Unsigned
REM
110
Remainder
Signed
Signed
REMU
111
Remainder
Unsigned
Unsigned
Table 3.3: Division Instructions Operation
Figure 3.26: M extension Control Module
Instruction (Funct3)
Sign
Mul_en
Div_en
M_sel
result_
MUL (000)
11
1
0
10
1
MULH (001)
11
1
0
11
1
MULHSU (010)
10
1
0
11
1
MULHU (011)
00
1
0
11
1
Table 3.4: Multiplication Instructions Control signals
Instruction (Funct3)
Sign
Mul_en
Div_en
M_sel
result_
DIV (100)
11
0
1
00
1
DIVU (101)
00
0
1
00
1
REM (110)
11
0
1
01
1
REMU (111)
00
0
1
01
1
Table 3.5: Division Instructions Control signals
Figure 3.27: rv32im data path
-
-
Designing
The whole process can be seen in ve different stages: Instruction Fetch, Instruction Decode, Computation Execution, and Result Memory Write. We need to add a few buffers, such as IF_ID buffer, ID_EX buffer, EX_MEM buffer, and MEM_EX buffer, as shown in the 3.28, between each state to increase the clock frequency of our processor design.
Here, the Branch Detection is happening in the Decoding stage with a cost of a slight increase in delay, In this kind of design there is no need of a Branch predictor
Figure 3.28: Pipelined Microarchitecture Design
To Design Pipelining Design We need to deal with Hazards that occur due to Instruc- tion Dependencies on Data, Hardware and Control .
-
Hazards
-
Data Hazards occur when there are Data Dependencies between the instuctions. In the gure 3.29 , the outputs of the rst instructions and the 2nd instruction are needed to be the inputs of the 3rd instructions , bu tin pipelingn design , the write back happens inn the last stage , the 3 rd instcutions which is supposed to load the required data from the GPRs , could load due to the RAW(Read after Write) Hazrd .
So one way of resolving this is using a Data forwarder in the execution stage, so that when a later instruction needs data computed by the former instructions then after the computation of the former instructions computation, those required data by the later instruction would be forwarded using a data forwarding unit as shown in the gure.the required control signals for the data forwarding unit would come form the hazard unit
Figure 3.29: Data Hazard Handling
Control Hazard occurs due to the branch and jump instructions, which are used to call instructions of father addresses. In the gure 3.30 if we see in the instruction decode stage,we will know that the type of instructions is jump. at that time there would be the following instruction in1, in2 in the pipeline, so in the next clock cycle,the instruction data in the buffers IF_ID, ID_EX would be set to zero asnop(no operation) and the
Figure 3.30: Control Hazard Handling
instruction that the jump instructions is directing to would be stored in the program counter. meanwhile, the in instruction would pass through the pipeline normally
I have used the assembly codes having different test cases for individual instructions from this githuib source riscv_tests. then converted them to hexadecimal data using the toolchain and placed those instructions into the processor instruction memory, in vivado,then veried the Processor functionality to support all instructions.
-
RISC-V_gnu_toolchain It is used to convert the C/C++ code to the RISC-V instruc- tions according to our required ISA, along with extension Instructions if we require them
.
Xilinx Vivado :The whole Designing of single cycle and pipelined processor is designed and simulated, synthesized using the Vivado 2022.2 version
-
Below are the screenshots of waveforms of the processor signals when control and data hazards occurs. The waveform 4.2is a simulation of the data-dependent consecutive instructions highlighted in the fuigure4.4,when they occur the FOrwding unit forwards the data from the pipelined available data to the execution unit .The forwarding unit forwards a)Data from the Memory available in the Write back stage,b)ALU computation data available in the Memory stage c) computed ALU output data writing back to the GPRs in the Write back stage.In the same way, the waveform 3.30 is the simulation of the few instructions, which has jump instructions in it as highlighted in the gure 4.3. When a jump instruction is detected in the execution stage, then the execution Execution stage and the decoding stage will not operation(nop-all data is reset to zero) in the the next clock cycle. After another clock cycle, the instructions at the jump address come to the decoding stage.
Figure 4.1: Data Hazards
Figure 4.2: Control Hazards
Figure 4.3: Instructions Causing Con- trol Hazard
Figure 4.4: Instructions Causing Data
Hazards
-
Arithmetic operation programe
The code in gure 4.5 , has multiplication ,Division, addition , subtraction operations on the given data, The nal output of the Computation is 70 , which can seen in the the simulation
Figure 4.5: Arthamatic operations c++ code
Figure 4.6: Arthamatic operations output simulation
-
Fibonacci program
The simulation show inn the gure 4.8 is obtained from c code written to compute the 25th number in the Fibanachi series ,which is compiled using the risc-v gnu toolchain, then extracted the hexadecimal instructions from it , and then simulated on the Designed Pipelined processor.
Figure 4.7: 25th Fibanachi number calculation
-
-
The FPGA-based verication of the RISC-V (RV32IM) pipelined processor core was performed on the Xilinx Arty A7-100T FPGA platform, utilizing its recongurable fabric to validate the complete RTL-to-GDSII ow. The Arty A7, featuring the Xilinx Artix-7 XC7A100T FPGA, offers 101,440 logic cells, 240 DSP slices, and 4,860 Kb of block RAM (BRAM), making it well-suited for deploying the custom 5-stage pipelined RISC-V processor with RV32IM support (base integer, multiplication/division extensions).
The processor design supported instruction fetch, decode, execute, memory access, and write-back stages, and included a hazard detection unit, forwarding logic, and branch prediction mechanism. Verication involved executing bare-metal test programs and compiled RISC-V binaries, including system calls and arithmetic benchmarks, transferred to the FPGA via a UART interface and stored in DDR3 memory.
A custom Python-based loader transmitted the binaries and interfaced with the UART to monitor processor outputs, including program counter traces, memory accesses, and register le changes. The RV core correctly executed a suite of compliance and stress tests, with successful handling of load-store operations, arithmetic instructions, CSR accesses, and interrupt handling (via a mock RTOS scheduler). The processor achieved a maximum clock frequency of 75 MHz, with an average execution latency of 12.5 cycles per instruction across test workloads.
Resource utilization on the FPGA reached 68% LUTs, 64% ip-ops, and 72% DSP slices, with an average power consumption of 1.8W. Challenges such as control hazards and pipeline stalls due to memory access latency were addressed through pipelining optimizations and a basic cache controller. Timing closure was achieved using oorplanning and register retiming strategies.
-
Arty A7 FPGA Platform Specications
The Xilinx Arty A7-100T development board was selected for its balance of compu- tational resources, energy efciency, and peripheral support:
The Xilinx Arty A7-100T development board was selected for its balanced computa- tional resources and peripheral support. Key specications include:
-
Device: Artix-7 XC7A100T-1CSG324C
-
Logic Cells: 101,440
-
DSP Slices: 240 (arithmetic acceleration)
-
Block RAM (BRAM): 4,860 Kb (CNN weights/image buffers)
-
Clock: 100 MHz default system clock (450 MHz maximum)
Memory Subsystem:
-
DDR3L SDRAM: 256 MB (large dataset storage)
-
Non-volatile Storage: 128 Mb QSPI Flash (bitstream storage)
I/O Capabilities:
-
16 PMOD expansion interfaces (camera/display connectivity)
-
USB-UART bridge (host communication)
-
4 user-programmable LEDs (status indication)
Physical Characteristics:
-
Package: CSG324
-
Power Consumption: <3 W (typical operation)
Note: Specications comply with Xilinx Artix-7 Technical Reference Manual (DS181) and Digilent documentation.
-
-
FPGA based verication
-
A C/C++ program written in such a way that it would return the result , using the risc-v gnu toolchain is converted into a text le containing the assembly and hexadecimal instructions data . Now, a Python
Figure 4.8: Arty A7 FPGA Board used to carry out the entire experiment
script is used to extract the hexadecimal instructions data and write them in a new text le.Now using a UART transmission protocol Python, the hexadecimal data is sent to the FPGA implemented process. Now after enabling the start push button in the processor to enable it to run the program instructions loaded in it. After a After executing all the instructions on the board, We can send the data memory contents to our laptop while running a Python script for UART receiving ptotocal in our laptop. Now, we can see the contents of the data memory using a text le.
Based on our program returning value, we can check whether that computed data is there or not in the data memory contents.
Figure 4.9: FPGA Verication Flow
I have successfully Designed and run the programs on the RV32IM-based pipelined RISC-V processor. Below are the results of in terms of delay
|
Singel Cycle |
Pipelined |
|
90.082ns |
59.525ns |
Table 5.1: Delay results of RV32IM single cycle vs Piplined Designs
The reason for nearly only half reduction in the delay even in the ve stage pipelined design is the Major delay contributor is ALU unit with MUltiplicaiton and DIvision Support whose delay is nearly equal to the pipelined delay which we got, so due to the larger delay contributor in execution stage , we can only see 2 fold delay reduction , but not 5 fold
|
Processsor |
LUT |
LUTRAM |
BRAM |
|
Single Cycle |
8336 |
5168 |
0 |
|
Pipelined |
5431 |
1855 |
17 |
Table 5.2: FPGA resource utlization
The reason for the No BRAM utilization in the single cycle, is due to lack of asynchronous reading memory modules in the design, where BRAM would only be utilized for synchronous memory read and writes. Meanwhile n pipelined design , the synchronous read can be used
This thesis presents the comprehensive design, implementation, and verication of a custom RISC-V RV32I pipelined processor and its integration with a lightweight Real-Time Operating System (RTOS), culminating in FPGA-based validation and deployment.
-
The project began with the architectural development of the RV32I single-core processor using a standard ve-stage pipelineInstruction Fetch (IF), Instruction Decode (ID), Execute (E), Memory Access (MEM), and Write-back (WB). The pipeline was enhanced with data forwarding, hazard detection, and basic branch prediction techniques to manage data, control, and structural hazards.
The core supports the RV32I base instruction set with planned extensions including M (Multiply/Di- vide), A (Atomic), and Zicsr/Zifencei for control and synchronization. The core operates across privileged modes (User and Machine mode) as governed by RISC-V CSRs.
The processors datapath integrates essential components such as the ALU, register le, memory controller, control logic, and pipeline registers. The processor was developed in Verilog and simulated using Verilator and ModelSim.
-
RTOS Integration
An RTOS was implemented atop the RV32I core, incorporating core real-time features:
-
Context Switching via software interrupts using CSR instructions and mret.
-
Scheduling through preemptive round-robin or priority-based mechanisms.
-
System Tick Timer using MTIME and MTIMECMP:
#define MTIME (*(volatile uint64_t*)0x200BFF8) #define MTIMECMP (*(volatile uint64_t*)0x2004000) #define TIMER_FREQ 1000000
void set_systick(uint64_t interval) { MTIMECMP = MTIME + interval;
}
-
Task Scheduler:
void scheduler() {
current_task = (current_task + 1) % NUM_TASKS; context_switch(tasks[current_task]);
}
-
Interrupt Handling using a PLIC-based model:
void external_interrupt_handler() { uint32_t irq = PLIC_CLAIM;
if (irq == UART_IRQ) { uart_handle_irq();
}
PLIC_COMPLETE = irq;
}
-
Peripheral Drivers for UART and GPIO:
void uart_write(char c) {
while (!(UART_STATUS & TX_READY)); UART_DATA = c;
}
void gpio_write(uint32_t pin, uint8_t value) { if (value)
GPIO_SET = (1 << pin);
else
GPIO_CLEAR = (1 << pin);
}
-
-
FPGA Verication and Sapphire SoC
The custom core, named Sapphire, was implemented on FPGA platforms such as Artix-7 (Arty A7) and Genesys-2, and veried via both simulation and hardware debugging tools.
Key features of Sapphire SoC:
-
RV32I-compliant 5-stage pipeline.
-
AXI4-Lite interconnect for memory-mapped I/O.
-
Memory Map:
A Hello World application was successfully executed:
#include "sapphire.h" int main() {
uart_init(115200); uart_puts("Sapphire SoC Booted!\n"); while(1) {
led_toggle(); delay_ms(500);
}
return 0;
}
-
-
Performance Comparison
Major contributions include:
-
Custom Verilog-based RV32I processor with 5-stage pipeline.
-
RTOS integration using context switching and MTIME scheduling.
-
AXI4-lite-based SoC design with memory-mapped peripherals.
-
Verication via QEMU, Verilator, and Vivado on Artix-7.
Future Work:
-
Extend to RV64GC with multi-core support.
-
Integrate formal verication using Symbiyosys or JasperGold.
-
Enhance cache hierarchy and introduce dynamic memory.
-
Port full FreeRTOS for broader task management support.
Address Range
Description
0x000000000x0000FFFF
Boot ROM (64KB)
0x200000000x20000FFF
GPIO
0x300000000x300000FF
UART
0x400000000x4FFFFFFF
AXI4-Lite Memory
Table 6.1: Sapphire SoC Memory Map
-
PLIC with 32 interrupt priority levels.
Metric
Sapphire
SHAKTI C-Class
PicoRV32
ISA Support
RV32I
RV64IMAC
RV32I
Pipeline Stages
5
3
–
FPGA Freq (MHz)
75
100
150
LUT Utilization
1,200
2,500
750
Verication
UVM + FPGA
Formal
Direct
Table 6.2: Performance Comparison
-
-
This project demonstrates a complete RTL-to-RTOS hardware-software co-design workow based on the open-source RISC-V ISA. The work validates that a minimal RV32I core with pipelined architecture can support RTOS functionalities such as task switching, interrupt handling, and real-time scheduling, and be deployed on FPGA hardware for embedded applications.
This thesis serves as a foundational effort in RISC-V based real-time embedded systems and paves the way for scalable and customizable SoC design.
-
S. L. H. M. Harris, Digital_design_and_computer_Architecture_RISCV_editio. ELSEVIER, .
-
NEO32_Processor (). Neorv32_processor. https://github.com/stnolting/neorv32.
-
r1 (). https://drive.google.com/drive/u/0/home.
-
RISC-V_gnu_toolchain (). Risc-v_gnu_toolchain. https://github.com/riscv-collab/ riscv-gnu-toolchain.
-
riscv_tests (). riscv tests for individual instructions. https://github.com/AngeloJacobo/ RISC-V/tree/main/test/extra.
-
A. Waterman (2017). The risc-v instruction set manual: Volume i user-level isa version 2.2. https:
//drive.google.com/file/d/1s0lZxUZaa7eV_O0_WsZzaurFLLww7ou5/view.
-
A. Waterman and K. Asanovic (2019). Unprivileged specication version 20191213. https:// drive.google.com/file/d/1s0lZxUZaa7eV_O0_WsZzaurFLLww7ou5/view.
A.0.1 Background Intro
Generally, a processor performs a specic computation on the required data. Any computation we require is communicated to the processor through INSTRUCTIONS, which the processor supports. The Only language a processor speaks is in terms of instructions. The only language that a processor can understand is its instruction language.
Any programming language that we have to translate(complied exactly )into the instructions of that processor on the PC.
a) Stack-based ISA: Data comes from Memory and forms a data stack. Like a LIFO (Last In, First Out) way, elements added last are the ones removed rst. The top elements of the stack are used as operands for arithmetic or logical operations.
Here, with a type of instruction (Push A, Push B), the data that comes from Memory is stored temporarily as a data stack. Then, computation is performed with an instruction (Add) on the data stack, and nally, the output is sent to the memory with an instruction (Pop C).
Example:Java Virtual Machines work on stack based ISA
a) Accumulator-based ISA: An accumulator-based ISA involves arithmetic and logic operations using the accumulator as one of the operands and storing the result back in the accumulator.
Here, the computation happens between the data from direct memory and data in the accumulator with an instruction (Add B) after computation, the result would occur in the accumulator itself, and then nally, the result in the accumulator could be stored back in memory with an instruction(Store C).
Example: The Intel 8085 microcontroller is on accumulator-based ISA.
a) Register-to-memory IA: In a register-to-memory ISA, data is transferred between registers and memory. Instructions in this type of ISA typically involve loading data from memory into registers or storing data from registers into memory.
For example, For a computation mentioned in the Figure, operand one is temporarily stored in registers initially with an instruction(Load R1, A), and then the computation is performed with an instruction(Add R1, B) on operand one from registers and operand two from Memory. The output is stored in a register only, and then, nally, the result in the register could be stored back in memory with an instruction (Store R1, C).
Example: x86 architecture.is based on reg to memory ISA
a) Register-to-register(Load/Store) ISA: In a register-to-register ISA, operations are performed di- rectly between registers without involving memory. Initially, instructions load the required data from the memory to the registers and manipulate data within registers, then store it back with instructions. In the gure, the initial instructions(Load R1, A, Load R2, B) loaded the required data from memory to the registers, and then computation was performed on the date in the respective registers with an instruction( Add R3, R2, R1), then the output is stored in the register R3. Finally, the result is stored in the memory via an instruction(Store R3, C).
Example: RISC-V,MIPS, and ARM architectures are based on reg-to-reg ISA
Each type of ISA has its own characteristics and inuences the design and behaviour of the processor it governs. The selection of ISA type affects the processor architectures complexity, speed, and capabilities.
|
Port |
Description |
|
clk |
Clock signal |
|
InstrWrAdd |
Instruction write address |
|
InstrWrData |
Instruction write data (byte) |
|
InstrWrEn |
Instruction write enable |
|
pc |
Program counter |
|
instruction |
Instruction output |
|
start |
Start control signal |
|
InstrWrEn (Control) |
Instruction write enable (control signal) |
Table B.1: Instruction Memory Ports Description
|
Start |
Operation |
|
1 |
instruction = Instruction_Memory[PC]; |
|
0 |
instruction = 32d0 |
Table B.2: Instruction Memory Asynchronous Reading for a single cycle
|
Start |
clk |
Operation |
|
1 |
posedge |
instruction <= Instruction_Memory[PC]; |
|
0 |
posedge |
instruction <= 32d0 |
Table B.3: Instruction Memory Synchronous Reading for Multi-cycle or Pipelined De- sign
|
InstrWrEn |
clk |
Operation |
|
1 |
posedge |
Instruction_Memory[InstrWrAdd] <= InstrWrData; |
|
0 |
posedge |
X |
Table B.4: Instruction Memory Synchronous Writing
"<=" is a synchronous operation with the clock, which takes a positive edge triggered clock cycle for the operation to perform
"=" Asynchronous operation , independent of the clock
|
Signal |
Description |
|
clk |
Clock signal |
|
mem_access_addr[31:0] |
Memory access address |
|
mem_write_data[31:0] |
Data to be written to memory |
|
mem_read_data[31:0] |
Data read from memory |
|
mem_write |
Memory write control signal |
|
mem_read |
Memory read control signal |
|
byte_half_word |
Byte/half-word selection control signal |
|
full_word |
Full-word selection control signal |
|
byteU |
Unsigned byte selection control signal |
|
half_wordU |
Unsigned half-word selection control signal |
Table B.5: Data Memory Ports
|
byte |
half_word |
full_word |
mem_access_addr[1:0] |
data_to_be_stored |
|
1 |
X |
X |
00 |
=mem_write_data[7:0] |
|
1 |
X |
X |
01 |
=mem_write_data[15:8] |
|
1 |
X |
X |
10 |
=mem_write_data[23:16] |
|
1 |
X |
X |
11 |
=mem_write_data[31:24] |
|
0 |
1 |
X |
X0 |
=mem_write_data[15:0] |
|
0 |
1 |
X |
X1 |
=mem_write_data[31:16] |
|
0 |
0 |
X |
XX |
=mem_write_data[31:0] |
Table B.6: Data Memory data to be stored control signals
|
mem write |
clk |
Operation |
|
1 |
posedge |
dataMemory[mem_access_addr[31:2]] <= data_to_be_stored; |
|
0 |
posedge |
X |
|
Data_Reading_signals |
mem_access_addr[1:0] |
reading_data |
|
X X X X |
XX |
No read operation |
|
1 X X X |
00 |
{memory[ram_addr][7:0]} |
|
1 X X X |
01 |
{memory[ram_addr][15:8]} |
|
1 X X X |
10 |
{memory[ram_addr][23:16]} |
|
1 X X X |
11 |
{memory[ram_addr][31:24]} |
|
0 1 X X |
X0 |
{memory[ram_addr][15:0]} |
|
0 1 X X |
X1 |
{memory[ram_addr][31:16]} |
|
0 0 1 X |
00 |
{24d0, memory[ram_addr][7:0]} |
|
0 0 X 1 |
00 |
{16d0, memory[ram_addr][15:0]} |
|
0 0 0 X |
01 |
{24d0, memory[ram_addr][15:8]} |
|
0 0 X 0 |
01 |
{16d0, memory[ram_addr][31:16]} |
|
0 0 X X |
10 |
{24d0, memory[ram_addr][23:16]} |
|
0 0 X X |
11 |
{24d0, memory[ram_addr][31:24]} |
Table B.7: Data Memory Synchronous Writing Data_Reading_signals = {byte, half_word, byteU, half_wordU}
Table B.8: DataMemory reads operation control signals
|
mem read |
clk |
Operation |
|
1 |
posedge |
mem_read_data <= reading_data; |
|
0 |
posedge |
mem_read_data <= 32d0 |
Table B.9: Data Memory Synchronous Reading
|
clk |
gprsWren |
Read |
Write |
|
x |
0 |
rdata1_o= GPRs_[raddr1_i], rdata2_o= GPRs_[raddr2_i] |
x |
|
posedge |
1 |
rdata1_o= GPRs_[raddr1_i], data2_o= GPRs_[raddr2_i] |
GPRs[waddr_i] <= wdata_i |
Table B.10: GPRs synchronous write & asynchronous read
|
ImmOp |
Operation |
|
000 |
I-type |
|
001 |
S-type |
|
010 |
B-type |
|
011 |
J-type |
|
100 |
U-type |
|
Default |
Undened |
Table B.11: ImmOp and Operation
|
Operation |
ImmExtD Output |
|
I-type |
{{20{ImmEncd[24]}}, ImmEncd[24:13]} |
|
S-type |
{{20{ImmEncd[24]}}, ImmEncd[24:18], ImmEncd[4:0]} |
|
B-type |
{{20{ImmEncd[24]}}, ImmEncd[0], ImmEncd[23:18], ImmEncd[4:1], 1b0} |
|
J-type |
{{12{ImmEncd[24]}}, ImmEncd[12:5], ImmEncd[13], ImmEncd[23:14], 1b0} |
|
U-type |
{ImmEncd[24:5], 12b000000000000} |
|
Undened |
32d0 |
Table B.12: Immediate Generator Functionality
|
alu_op |
Operation |
ALU_out |
lsr |
gtr |
eql |
|
0000 |
ADD |
in1 + in2 |
0 |
0 |
0 |
|
1000 |
SUB |
in1 in2 |
0 |
0 |
0 |
|
0010 |
SLT |
1 if in1 < in2 |
ALU_out[0] |
!(ALU_out[0]) |
0 |
|
0011 |
SLTU |
1 if unsigned( in1 < in2) |
ALU_out[0] |
!(ALU_out[0]) |
0 |
|
0111 |
AND |
in1&in2 |
0 |
0 |
0 |
|
0110 |
OR |
in1|in2 |
0 |
0 |
0 |
|
0100 |
XOR |
in1 in2 |
0 |
0 |
!(ALU_out) |
|
0001 |
SLL |
in1 << in2[4: 0] |
0 |
0 |
0 |
|
0101 |
SRL |
in1 >> in2[4: 0] |
0 |
0 |
0 |
|
1101 |
SRA |
in1 >>> in2[4: 0] |
0 |
0 |
0 |
|
Default |
No operation |
0 |
0 |
0 |
0 |
Table B.13: ALU unit control signals
|
ld |
jal |
jalr |
auipc |
lui |
Output (reg_write_data) |
|
0 |
0 |
0 |
0 |
0 |
ALU_out |
|
1 |
0 |
0 |
0 |
0 |
mem_read_data |
|
0 |
1 |
0 |
0 |
0 |
pc4 |
|
0 |
0 |
1 |
0 |
0 |
pc4 |
|
0 |
0 |
0 |
1 |
0 |
PC_plusImm |
|
0 |
0 |
0 |
0 |
1 |
immediate |
Table B.14: write back :rsltMux control signals
Below is the Python code to extract the hexadecimal instructions form the text le generated form toolchain:
Listing C.1: Python code to extract data
import re import os
def extract_data(input_file_path): try:
input_file_name, input_file_extension =
' os.path.splitext(input_file_path) output_file_path =
' f"{input_file_name}_extracted_macine_n_Assembly.txt"
machine_code_output_file_path =
' f"{input_file_name}_machine.txt"
with open(input_file_path, r) as file: data = file.read()
hex_data =
' re.findall(r\b[0-9a-fA-F]+:\s+([0-9a-fA-F]+)\s+,
' data)
with open(output_file_path, w) as file: file.write(\n.join(hex_data))
with open(machine_code_output_file_path, w) as file: file.write(\n.join(hex_data))
return True, output_file_path,
' machine_code_output_file_path # Return True to
' indicate the operation was successful and the
' paths of the output files except Exception as e:
print(f"An error occurred: {e}")
return False, None, None # Return False to indicate that
' an error occurred and no output files were created
# Taking input file path as input
input_path = input("Enter the input file path: ") extraction_result, output_file, machine_code_output_file =
' extract_data(input_path)
if extraction_result:
print(f"Filtered data has been saved to the output files:
' {output_file} and {machine_code_output_file}")
else:
print("An error occurred while processing the file.")
Below is the Python code snippet for receiving data form FPGA using UART protocal:
Listing C.2: Python code for serial communication with FPGA
import serial import binascii
# Replace "COM14" with the correct COM port where your FPGA is
' connected
serial_data = serial.Serial("COM14", 9600)
# Open a new text file for writing
with open("Data_Mem111.txt", "w") as file: consecutive_data = b"" # Initialize an empty byte string
' for consecutive data
while True:
dat = serial_data.read(1) # Read 1 byte of data from FPGA
# Convert the received data to hexadecimal data_hex = dat.hex()
# Append the data to the consecutive_data byte string consecutive_data += binascii.unhexlify(data_hex)
# If we have received 4 consecutive bytes, print them in
' little-endian order
if len(consecutive_data) == 4:
little_endian_data = consecutive_data[::-1] # Reverse
' the order
print("Little-endian data:", little_endian_data.hex())
# Write the little-endian data to the text file file.write(little_endian_data.hex() + "\n")
# Reset the consecutive_data for the next set of 4
' bytes consecutive_data = b""
# Close the serial connection (this line will not be reached
' as the loop runs indefinitely) serial_data.close()
Below is the Python code snippet for send instructions from laptop to FPGA using UART protocal:
Listing C.3: Python code to send data via UART
import serial import time
def send_data_via_uart(input_file_path): try:
# Open the input file and read data
with open(input_file_path, r) as file:
data = file.read()
# Open serial connection
ser = serial.Serial(COM14, 9600, timeout=1) # Fixed
' COM port
# Send data through UART ser.write(data.encode()) print("Data sent successfully.")
# Close serial connection ser.close()
return True # Return True to indicate successful
' transmission except Exception as e:
print(f"An error occurred: {e}")
return False # Return False to indicate transmission
' failure
# Input file path
input_file_path = input("Enter the input file path: ")
# Send data through UART
send_resul = send_data_via_uart(input_file_path)
if send_result:
print("Data sent via UART successfully.") else:
print("Failed to send data via UART.")
-
Chisel (Constructing Hardware in a Scala Embedded Language) and Verilog are both hardware description languages (HDLs), but they differ signicantly in their approach and capabilities. This document provides an in-depth comparison with extensive code examples.
-
Aspect
Chisel
Verilog
Language Type
Embedded DSL in Scala
Standalone HDL
Abstraction Level
Higher (object-oriented, functional)
Lower (RTL/structural)
Code Reusability
High (generators, parameters)
Limited (macros, generate)
Meta-programming
Full Scala capabilities
Limited
Simulation
Scala testbenches
Verilog testbenches
Synthesis
Generates Verilog
Direct synthesis
Learning Curve
Steeper (requires Scala)
Easier (dedicated HDL)
Community
Growing (mainly RISC-V ecosystem)
Mature and widespread
Table D.1: Detailed Comparison between Chisel and Verilog
-
Chisel is a hardware construction language embedded in Scala. It allows hardware designers to leverage the full power of a modern programming language to write parameterized hardware generators.
-
-
Combinational Logic
Listing D.1: Combinational Logic in Verilog
module comb_logic(
Start Chisel Design
Write Scala/Chisel Code
Compile to FIRRTL
FIRRTL Transformations
Generate Verilog
Synthesize to Gates
ASIC/FPGA Implementation Figure D.1: Detailed Chisel Workow
input [3:0] a, b, output [3:0] y1, y2, y3
);
assign y1 = a & b; // Bitwise AND assign y2 = a | b; // Bitwise OR assign y3 = a ^ b; // Bitwise XOR
endmodule
Listing D.2: Combinational Logic in Chisel
import chisel3._
class CombLogic extends Module { val io = IO(new Bundle {
val a = Input(UInt(4.W)) val b = Input(UInt(4.W)) val y1 = Output(UInt(4.W)) val y2 = Output(UInt(4.W)) val y3 = Output(UInt(4.W))
})
io.y1 := io.a & io.b // Bitwise AND io.y2 := io.a | io.b // Bitwise OR io.y3 := io.a ^ io.b // Bitwise XOR
}
-
Sequential Logic
Listing D.3: Register in Verilog
module register( input clk, input rst, input [7:0] d,
output reg [7:0] q
);
always @(posedge clk or posedge rst) begin if (rst) q <= 8h0;
else q <= d; end
endmodule
Listing D.4: Register in Chisel
import chisel3._
class Register extends Module { val io = IO(new Bundle {
val d = Input(UInt(8.W)) val q = Output(UInt(8.W))
})
val reg = RegInit(0.U(8.W)) reg := io.d
io.q := reg
}
-
-
-
Finite State Machine
Listing D.5: FSM in Verilog
module fsm(
input clk, rst, in, output reg out
);
parameter S0 = 0, S1 = 1, S2 = 2; reg [1:0] state, next_state;
always @(posedge clk or posedge rst) begin if (rst) state <= S0;
else state <= next_state; end
always @(*) begin case (state)
S0: next_state = in ? S1 : S0; S1: next_state = in ? S2 : S0; S2: next_state = in ? S2 : S0; default: next_state = S0;
endcase end
assign out = (state == S2); endmodule
Listing D.6: FSM in Chisel
import chisel3._ import chisel3.util._
class FSM extends Module { val io = IO(new Bundle {
val in = Input(Bool()) val out = Output(Bool())
})
val s0 :: s1 :: s2 :: Nil = Enum(3) val state = RegInit(s0)
io.out := (state === s2)
switch(state) {
is(s0) { state := Mux(io.in, s1, s0) }
is(s1) { state := Mux(io.in, s2, s0) }
is(s2) { state := Mux(io.in, s2, s0) }
}
}
-
Memory Example
Listing D.7: Memory in Verilog
module memory( input clk, input we,
input [3:0] addr,
input [7:0] din,
output [7:0] dout
);
reg [7:0] mem [0:15];
always @(posedge clk) begin if (we) mem[addr] <= din;
end
assign dout = mem[addr]; endmodule
Listing D.8: Memory in Chisel
import chisel3._
class Memory extends Module { val io = IO(new Bundle {
val we = Input(Bool())
val addr = Input(UInt(4.W)) val din = Input(UInt(8.W)) val dout = Output(UInt(8.W))
})
val mem = SyncReadMem(16, UInt(8.W))
when(io.we) { mem.write(io.addr, io.din)
}
io.dout := mem.read(io.addr)
}
-
-
-
Verilog Testbench
Listing D.9: Verilog Testbench
module testbench; reg clk, rst; reg [7:0] a, b;
wire [7:0] sum;
adder uut (.a(a), .b(b), .sum(sum));
initial begin clk = 0;
forever #5 clk = ~clk; end
initial begin rst = 1;
a = 0; b = 0;
#20 rst = 0;
a = 8p2; b = 8p4;
#10 $display("Sum = %h", sum);
a = 8hFF; b = 8h01;
#10 $display("Sum = %h", sum);
$finish; end
endmodule
-
Chisel Testbench
Listing D.10: Chisel Testbench
import chisel3._ import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
class AdderTest extends AnyFlatSpec with ChiselScalatestTester
' {
"Adder" should "add numbers correctly" in { test(new Adder(8)) { dut =>
dut.io.a.poke(0x12.U) dut.io.b.poke(0x34.U) dut.clock.step() dut.io.sum.expect(0x46.U)
dut.io.a.poke(0xFF.U)
dut.io.b.poke(0x01.U) dut.clock.step() dut.io.sum.expect(0x00.U)
}
}
}
-
-
-
Verilog Parameterization
Listing D.11: Parameterized FIFO in Verilog
module fifo #(
parameter WIDTH = 8, parameter DEPTH = 16
)(
input clk, rst, input wr_en, rd_en,
input [WIDTH-1:0] din, output [WIDTH-1:0] dout, output full, empty
);
reg [WIDTH-1:0] mem [0:DEPTH-1];
reg [$clog2(DEPTH):0] wr_ptr, rd_ptr;
// Implementation omitted for brevity endmodule
-
Chisel Parameterization
Listing D.12: Parameterized FIFO in Chisel
import chisel3._ import chisel3.util._
class Fifo(val width: Int, val depth: Int) extends Module { val io = IO(new Bundle {
val wr_en = Input(Bool()) val rd_en = Input(Bool())
val din = Input(UInt(width.W)) val dout = Output(UInt(width.W)) val full = Output(Bool())
val empty = Output(Bool())
})
val mem = SyncReadMem(depth, UInt(width.W)) val wrPtr = RegInit(0.U(log2Ceil(depth).W)) val rdPtr = RegInit(0.U(log2Ceil(depth).W))
// Implementation omitted for brevity
}
-
Chisel offers powerful abstractions for hardware design through its integration with Scala, enabling more concise and reusable code. Verilog remains the industry standad with excellent tool support. The choice depends on:
-
Project complexity (Chisel excels for complex, parameterized designs)
-
Team expertise (Verilog is more accessible to traditional hardware engineers)
-
Toolchain requirements (Verilog has universal support)
-
Need for verication (Chisel integrates better with modern verication approaches)
-
Branch prediction is a fundamental part of modern pipelined processor design. It enhances instruction throughput by guessing the direction of conditional branch instructions before they are resolved. Accurate branch prediction is critical in reducing pipeline stalls and improving instruction-level parallelism.
-
The branch predictor is designed to detect control hazards and mitigate stalls using speculative execution. We implement a simple 2-bit saturating counter-based predictor.
-
Prediction States
-
Strongly Taken (11)
-
Weakly Taken (10)
-
Weakly Not Taken (01)
-
Strongly Not Taken (00)
-
-
-
Listing E.1: Verilog Implementation of 2-bit Branch Predictor
module branch_predictor ( input wire clk,
input wire rst,
input wire branch_taken,
input wire [4:0] branch_address, output reg prediction
);
reg [1:0] bht [31:0]; // 32-entry Branch History Table
always @(posedge clk or posedge rst) begin if (rst) begin
for (int i = 0; i < 32; i = i + 1)
bht[i] <= 2b10; // weakly taken on reset end else begin
case (bht[branch_address]) 2b00: prediction <= 0; 2b01: prediction <= 0; 2b10: prediction <= 1; 2b11: prediction <= 1;
endcase
// Update prediction state
if (branch_taken && bht[branch_address] != 2b11) bht[branch_address] <= bht[branch_address] + 1;
else if (!branch_taken && bht[branch_address] != 2b00) bht[branch_address] <= bht[branch_address] – 1;
end end
endmodule
-
ChampSim is a trace-based simulator that allows detailed testing of branch predictors, cache hierar- chies, and prefetchers. Its modular, making it ideal for evaluating the performance of your own predictor design.
-
Installation Guide
-
Clone the ChampSim repository:
git clone https://github.com/ChampSim/ChampSim.git cd ChampSim
-
Install required packages (Ubuntu):
sudo apt-get install build-essential libboost-all-dev
-
Build the simulator with a custom branch predictor:
./build_champsim.sh bimodal no no no no lru 1
-
Download and decompress the trace les from: https://www.ece.ubc.ca/~sasha/ champsim-traces/speccpu
-
Run the simulation:
./champsim –warmup_instructions 10000000
' –simulation_instructions 100000000
' traces/600.perlbench_s-13241-43.trace.xz
\end{verbatim}
-
-
This work presents the design of a simple 2-bit branch predictor, integrated within a RISC- V pipeline processor. Its performance and accuracy can be benchmarked using trace-based simulations on ChampSim. Future work includes integrating a perceptron predictor and conducting performance comparisons with other state-of-the-art prediction techniques.
Figure E.1: Branch Predictor Unit Successfully stored and Simulated on Champsim
