Ramon - Development of A RISC-V Processor Optimized For Control Applications To Be Used in The Le...
Ramon - Development of A RISC-V Processor Optimized For Control Applications To Be Used in The Le...
D EVELOPMENT OF A RISC-V
PROCESSOR OPTIMISED FOR CONTROL
APPLICATIONS TO BE USED IN THE
LEVITATION SYSTEM OF H YPERLOOP
I would like to thank my tutors Cristian Ariel Olguı́n and José Marı́a Monzó for the
consistent support, keen interest, and invaluable assistance provided throughout the
development of this project. And also for introducing me to the field of programmable
logic devices in the subject Digital electronics.
I would also like to thank my gratitude to my parents for their unwavering support,
I am quite sure they understand microcontrollers better now. I would like to extend to
my siblings and friends.
I
Abstract
The present work develops, in SystemVerilog, a RISC-V IP core, both single-cycle and
multicycle, employing the RV32I (32-bit integer handling RISC-V architecture) ISA
(Instruction Set Architecture). A PID controller core IP has been developed to work
alongside the processor. The main objective is to create a microprocessor optimised
for control applications to be used in a hyperloop prototype. Hyperloop is a means
of transportation concept that consists of a capsule that levitates in a vacuum tube
to achieve high speeds with the lowest possible energy usage. Levitation control is a
resource-intensive task in a microcontroller. A dedicated control peripheral reduces
the use of the CPU, allowing it to perform other tasks. The use of FPGAs increases
hardware flexibility to be modified, improving its performance without altering the
PCBs, allowing process parallelisation and reducing power consumption. This project
is based on the control system of the hyperloop prototype, Auran, developed by the
Hyperloop UPV team for the 2022 European Hyperloop Week.
II
Resumen
III
Resum
IV
Contents
Abstract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . II
Contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . V
List of Figures . . . . . . . . . . . . . . . . . . . . . . . . . . . . X
List of Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XIII
List of Code snippets . . . . . . . . . . . . . . . . . . . . . . . . . XV
Acronyms & Initials . . . . . . . . . . . . . . . . . . . . . . . . . . XVIII
I Memory report 1
1 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1 RISC-V . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Programmable Logic Devices . . . . . . . . . . . . . . . . . . . . . 3
1.3 SystemVerilog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 Hyperloop concept & Hyperloop UPV . . . . . . . . . . . . . . . . 4
2 Needs study . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3 Alternative solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4 Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.1 RISC-V . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.1.1 Instruction Set Architecture . . . . . . . . . . . . . . . . . 13
4.1.1.1 Registers . . . . . . . . . . . . . . . . . . . . . . . 13
4.1.1.2 Instructions . . . . . . . . . . . . . . . . . . . . . 15
4.2 Single-cycle processor . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.2.1 Datapath . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.2.1.1 Register file . . . . . . . . . . . . . . . . . . . . . 18
4.2.1.2 Immediate generator . . . . . . . . . . . . . . . . 19
4.2.1.3 Arithmetic Logic Unit . . . . . . . . . . . . . . . 21
4.2.1.4 Data memory . . . . . . . . . . . . . . . . . . . . 24
4.2.1.5 Instruction memory . . . . . . . . . . . . . . . . 25
4.2.2 Control logic . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.2.2.1 Control unit . . . . . . . . . . . . . . . . . . . . . 26
4.2.2.2 ALU control unit . . . . . . . . . . . . . . . . . . 28
V
4.2.2.3 Branch logic . . . . . . . . . . . . . . . . . . . . . 29
4.2.3 Analysis & Synthesis . . . . . . . . . . . . . . . . . . . . . 31
4.3 Multicycle processor . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.3.1 Datapath . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.3.1.1 Instruction and Data memory . . . . . . . . . . 35
4.3.2 Control logic . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.3.2.1 Control unit . . . . . . . . . . . . . . . . . . . . . 36
4.3.2.2 Branch logic . . . . . . . . . . . . . . . . . . . . . 40
4.3.3 Memory bus & Peripherals . . . . . . . . . . . . . . . . . 42
4.3.3.1 Memory Controller . . . . . . . . . . . . . . . . 45
4.3.3.2 PID-Timer Link . . . . . . . . . . . . . . . . . . . 47
4.3.3.3 Seven-segments decoder . . . . . . . . . . . . . 48
4.3.3.4 Analog to Digital Converter . . . . . . . . . . . 49
4.3.3.5 Timer . . . . . . . . . . . . . . . . . . . . . . . . 51
4.3.3.6 PID controller . . . . . . . . . . . . . . . . . . . . 54
4.3.4 Analysis & Synthesis . . . . . . . . . . . . . . . . . . . . . 58
4.4 Current controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.4.1 Controller design . . . . . . . . . . . . . . . . . . . . . . . 60
4.4.2 Software development . . . . . . . . . . . . . . . . . . . . 63
4.4.2.1 PID configuration . . . . . . . . . . . . . . . . . 64
4.4.2.2 Timer configuration . . . . . . . . . . . . . . . . 64
5 Verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
5.1 Single-cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
5.1.1 Modules verification . . . . . . . . . . . . . . . . . . . . . 67
5.1.2 Processor verification . . . . . . . . . . . . . . . . . . . . 70
5.1.2.1 Instruction set verification . . . . . . . . . . . . 71
5.1.2.2 Fibonacci sequence . . . . . . . . . . . . . . . . . 76
5.1.2.3 Bubble sort . . . . . . . . . . . . . . . . . . . . . 76
5.2 Multicycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.2.1 Processor . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.2.2 Memory bus . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.2.3 Peripherals . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.2.3.1 PID-Timer link . . . . . . . . . . . . . . . . . . . 80
5.2.3.2 Seven-Segments decoder . . . . . . . . . . . . . 81
5.2.3.3 Timer . . . . . . . . . . . . . . . . . . . . . . . . 82
5.2.3.4 PID controller . . . . . . . . . . . . . . . . . . . . 84
5.2.3.5 Analog to Digital Converter . . . . . . . . . . . 85
6 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
VI
6.1 Future lines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Annexes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
A Detailed peripheral memory map . . . . . . . . . . . . . . . . . . 87
B LPM MULT configuration . . . . . . . . . . . . . . . . . . . . . . 89
C Pinout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
D Sustainable Development Goals . . . . . . . . . . . . . . . . . . . 96
Bibliography . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
V Budget 106
1 Materials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
2 Labour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
3 Proyect costs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
VII
1.2.2 Control logic . . . . . . . . . . . . . . . . . . . . . . . . . 127
1.2.2.1 Control unit . . . . . . . . . . . . . . . . . . . . . 127
1.2.2.2 Branch logic . . . . . . . . . . . . . . . . . . . . . 130
1.2.2.3 ALU Control . . . . . . . . . . . . . . . . . . . . 131
1.2.3 Processor implementation . . . . . . . . . . . . . . . . . . 132
1.3 Multicycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
1.3.1 Datapath . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
1.3.1.1 Memory . . . . . . . . . . . . . . . . . . . . . . . 136
1.3.2 Control logic . . . . . . . . . . . . . . . . . . . . . . . . . 137
1.3.2.1 Branch logic . . . . . . . . . . . . . . . . . . . . . 147
1.3.3 Multicycle processor . . . . . . . . . . . . . . . . . . . . . 148
1.3.4 Memory bus . . . . . . . . . . . . . . . . . . . . . . . . . 152
1.3.4.1 Memory controller . . . . . . . . . . . . . . . . . 152
1.3.4.2 PID-Timer link . . . . . . . . . . . . . . . . . . . 155
1.3.4.3 Seven-segment decoder . . . . . . . . . . . . . . 157
1.3.4.4 Analog to Digital Converter . . . . . . . . . . . 160
1.3.4.5 Timer . . . . . . . . . . . . . . . . . . . . . . . . 161
1.3.4.6 PID Controller . . . . . . . . . . . . . . . . . . . 165
1.3.5 Multicycle microcontroller . . . . . . . . . . . . . . . . . 170
2 Verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
2.1 Single-cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
2.1.1 Datapath . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
2.1.1.1 Register file . . . . . . . . . . . . . . . . . . . . . 176
2.1.1.2 Immediate generator . . . . . . . . . . . . . . . . 179
2.1.1.3 Arithmetic Logic Unit . . . . . . . . . . . . . . . 181
2.1.1.4 Data memory . . . . . . . . . . . . . . . . . . . . 183
2.1.1.5 Instruction memory . . . . . . . . . . . . . . . . 185
2.1.2 Control logic . . . . . . . . . . . . . . . . . . . . . . . . . 188
2.1.2.1 Control unit . . . . . . . . . . . . . . . . . . . . . 188
2.1.2.2 Branch logic . . . . . . . . . . . . . . . . . . . . . 191
2.1.2.3 ALU Control . . . . . . . . . . . . . . . . . . . . 194
2.1.3 Processor . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
2.2 Multicycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
2.2.1 Multicycle microcontroller . . . . . . . . . . . . . . . . . 206
2.2.2 Memory bus . . . . . . . . . . . . . . . . . . . . . . . . . 207
2.2.3 Peripherals . . . . . . . . . . . . . . . . . . . . . . . . . . 208
2.2.3.1 PID-Timer link . . . . . . . . . . . . . . . . . . . 208
2.2.3.2 Seven-segment decoder . . . . . . . . . . . . . . 210
VIII
2.2.3.3 Timer . . . . . . . . . . . . . . . . . . . . . . . . 212
2.2.3.4 PID controller . . . . . . . . . . . . . . . . . . . . 215
IX
List of Figures
X
1.4.26 ADC - IP Parameter Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
1.4.27 Memory controller interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
1.4.28 Timer interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
1.4.29 Timer interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
1.4.30 PID circuit diagram. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
1.4.31 PID controller interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
1.4.32 Pin Planner. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
1.4.33 Analysis & Synthesis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
1.4.34 Simulink diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
1.4.34 Continuous PID simulation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
1.4.35 PID circuit Simulink block. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
1.4.36 Discrete PID simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
1.5.1 Verification error log. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
1.5.2 Register file verification results. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
1.5.3 Immediate generator verification results. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
1.5.4 ALU verification results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
1.5.5 Data memory verification results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
1.5.6 Program memory verification results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
1.5.7 Control unit verification results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
1.5.8 ALU control verification results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
1.5.9 Branch logic verification results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
1.5.10 Types I, S verification waveforms. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1.5.11 Type I, S register file waveforms. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1.5.12 Types R, U, J verification waveforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
1.5.13 Types R, U, J register file waveforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.5.14 Type B register file waveforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.5.15 Fibonacci sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
1.5.16 Bubble sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
1.5.17 Fibonacci sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.5.18 Bubble sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.5.19 PID 1 registers verification. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.5.20 PID 2 registers verification. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.5.21 ADC registers verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.5.22 Timer registers verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
1.5.23 7-segment and PID-Timer link registers verification. . . . . . . . . . . . . . . . . . 80
1.5.24 PID-Timer link verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
1.5.25 Seven-segment decoder verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
1.5.26 Seven-segment decoder verification FPGA. . . . . . . . . . . . . . . . . . . . . . . . . . 81
XI
1.5.27 Timer counter limit verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.5.28 Timer prescaler verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.5.29 Timer outputs verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
1.5.30 Timer bypass and dead-time verification . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
1.5.31 Golden model simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.5.32 Control action comparison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.5.33 PID saturation verification. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.5.34 ADC verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
XII
List of Tables
1.C.1 Pinout. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
1.D.1 Degree to which the project relates to the Sustainable Development Goals . . . . 96
XIII
5.1.2 Materials costs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
5.2.1 Labour unitary prices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
5.2.2 Labour costs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
5.3.1 Project costs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
XIV
List of Code snippets
XV
6.1.24 Branch logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
6.1.25 RV32I - Multicycle processor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
6.1.26 Memory controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
6.1.27 PID-Timer link . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
6.1.28 Seven-segment decoder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
6.1.29 Analog to Digital Converter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
6.1.30 PWM Generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6.1.31 Timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
6.1.32 PID Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
6.1.33 PID peripheral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
6.1.34 RV32I - Mulcicycle microcontroller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
6.1.35 Current controller assembly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
6.2.36 Register file testbench. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
6.2.37 Register file verification vector 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
6.2.38 Register file verification vector 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
6.2.39 Register file verification vector 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
6.2.40 Immediate generator testbench. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
6.2.41 Immediate generator verification vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
6.2.42 ALU testbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
6.2.43 ALU verification vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
6.2.44 Data memory testbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
6.2.45 Data memory verification vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
6.2.46 Instruction memory testbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
6.2.47 Intruction memory verification instructions file . . . . . . . . . . . . . . . . . . . . . 187
6.2.48 Intruction memory verification vector. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
6.2.49 Control unit testbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
6.2.50 Control unit verification vector. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
6.2.51 Branch logic testbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
6.2.52 Branch logic verification vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
6.2.53 ALU control testbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
6.2.54 ALU control verification vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
6.2.55 RV32I single-cycle processor testbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
6.2.56 Types I, S verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
6.2.57 Types R, U, J verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
6.2.58 Type B verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
6.2.59 Fibonacci sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
6.2.60 Bubble sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
6.2.61 RV32I Multicycle microcontroller testbench . . . . . . . . . . . . . . . . . . . . . . . . . 206
XVI
6.2.62 Memory bus verification assembly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
6.2.63 PID-Timer link testbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
6.2.64 Seven-segment decoder testbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
6.2.65 Seven-segment display. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
6.2.66 Timer testbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
6.2.67 PID controller testbench. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
6.2.68 PID controller MATLAB simulation script . . . . . . . . . . . . . . . . . . . . . . . . . . 220
6.2.69 PID Peripheral testbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
XVII
Acronyms & Initials
IC Integrated Circuit
XVIII
IF Instruction Fetch – Instruction stage
IP Intellectual Property
PC Program Counter
SoC System-on-Chip
XIX
Part I
Memory report
1 Scope . . . . . . . . . . . . . . . . . . . . . . . 2
2 Needs study . . . . . . . . . . . . . . . . . . . 8
3 Alternative solutions . . . . . . . . . . . . . . 9
4 Development . . . . . . . . . . . . . . . . . . . 12
5 Verification . . . . . . . . . . . . . . . . . . . . 66
6 Conclusions . . . . . . . . . . . . . . . . . . . 86
Annexes . . . . . . . . . . . . . . . . . . . 87
A Detailed peripheral memory map . . . . . . 87
B LPM MULT configuration . . . . . . . . . 89
C Pinout . . . . . . . . . . . . . . . . . . 94
D Sustainable Development Goals . . . . . . 96
Bibliography . . . . . . . . . . . . . . . . . 98
1. Scope
1 Scope
This project aims to develop a RISC-V core IP (Intellectual Property) focused on being
employed in control applications. The MCU (microcontroller unit) has been developed
employing SystemVerilog, a hardware description language. This microcontroller
has been designed employing the RISC-V architecture, and partially supports the
Instruction Set Architecture (ISA) RV32I1 .
In the following sections, two different implementations of the MCU core IP have
been described. The first version, which is the implementation of a single-cycle micro-
controller, has been developed for simulation purposes only. Moreover, a multicycle
version has also been developed, which has not only been simulated but also synthe-
sized and loaded into a FPGA based PLD (Programmable Logic Device).
Regarding the control application of the MCU, a series of peripherals have also
been developed to allow the parallel computation of up to two PID controllers and
the MCU core itself. This fact is possible thanks to the implementation of a PID
controller peripheral that, along with a timer and ADC (Analogue to Digital Converter)
peripherals, can run a control loop autonomously.
1.1 RISC-V
RISC-V is an open-source ISA based on the RISC concept, which stands for Reduced
Instruction Set Computer. The basis of RISC is to have a reduced number of instructions
that can be executed faster. This approach contrasts with the CISC (Complex Instruction
Set Computer) architectures, which implement a large number of instructions, including
complex operations, therefore sacrificing hardware simplicity and speed but simplifying
programming.
The RISC-V architecture was born at the University of California, Berkeley, in 2010
and “was originally designed to support computer architecture research and education”
(Waterman and Asanović, 2019). Nevertheless, this architecture is gaining popularity at
all levels (educational, industrial, for hobbyists) and becoming a genuine alternative
1
RV32I is the RISC-V ISA base that supports 32-bit integer architecture.
2
1. Scope
in the industry because of its open-source character that allows anyone to create their
own implementation without paying royalties.
Modern FPGAs do not only incorporate programmable logic. They also include
ADC circuitry, memory block and arithmetic circuits, among others. Furthermore, they
can have outstanding performance and characteristics. For example, the Xilinx Virtex
UltraScale+ VU19P FPGA has 9 million logic cells, over 2,000 GPIOs (General Purpose
Input Output) and supports up to eight DDR4 at 1.5 Tb/s bandwidth. (Xilinx® , nd)
While currently only a tiny share of FPGAs are used for data processing in
data centers, this is likely going to change in the near future as FPGAs not
only provide very high performance, but they are also extremely energy
efficient computing devices. (Koch et al., 2016)
3
1. Scope
1.3 SystemVerilog
SystemVerilog is a hardware description language (HDL) presented in 2002 as an
extension of Verilog. Since then, SystemVerilog has become one of the mainly used
HDLs. A hardware description language is a computer language used to define the
structure and behaviour of electronic circuits. They also allow to simulate or synthesise
the circuits to generate hardware implementations that can be implemented in an FPGA
or an ASIC. Other HDLs are VHDL and Verilog.
They differ from programming languages (such as C/C++, Python or Java) mainly
in their purpose. While HDLs are used to describe and design digital electronics circuits,
programming languages generate a set of instructions (software) executed by a CPU.
The main idea behind the hyperloop concept is to achieve a high-speed means
of transportation, up to 1,000 km/h, with minimal energy consumption. Hyperloop
consists of a capsule, or pod, that circulates levitating electromagnetically inside a
vacuum tube propelled by a linear electric motor. Both the electromagnetic levitation
and the linear electric motor eliminate the friction with the infrastructure. Moreover,
the vacuum tube reduces the air resistance effect, which is proportional to the square of
4
1. Scope
the speed.
In 2015 Spacex began organising the Hyperloop Pod Competition. That led to the
creation of a team at the Polytechnic University of Valencia, Hyperloop UPV (in which
I have been involved since 2021 as a firmware and hardware engineer), to participate in
the competition. Since then, the team has participated in all editions until 2019, but due
to COVID-19, the event was discontinued.
Later, in 2021, four European teams (Hyperloop UPV, Delft Hyperloop, Swissloop
and HYPED) founded the European Hyperloop Week (EHW), a worldwide competition
that would maintain the university hyperloop ecosystem.
For the second edition of the EHW, Hyperloop UPV developed the prototype Auran
(Figure 1.1.4a). Auran was an approach to a full-scale hyperloop vehicle. A functional
electromagnetic levitation and guiding system was successfully implemented. And
along with the dual linear induction motor (DLIM) allowed the prototype to travel
through a 20-meter-long tubular infrastructure (Figure 1.1.4b) without any contact with
the infraestructure.
5
1. Scope
The magnetic force generated by the permanent magnets decreases with the distance
to the infrastructure. Therefore, there is a distance where the magnetic force and the
gravitational force cancel each other. This equilibrium point depends on the mass of the
vehicle and the electromagnetic unit. However, the equilibrium is unstable, meaning
that a small perturbance or position error results in the vehicle either falling or adhering
to the track.
The levitation and guiding system is mainly composed of two types of electromag-
nets. The levitation-oriented electromagnets are a Hybrid Electromagnetic Suspension
(HEMS) system. They consist of two permanent magnets and a coil around a laminated
steel yoke, as shown in Figure 1.1.5a. The guiding-oriented electromagnets are an Elec-
6
1. Scope
tromagnetic Suspension (EMS) system. They consist only of a coil around a laminated
steel yoke, as shown in Figure 1.1.5b.
The prototype has individual PCBs (Printed Circuit Board) known as LPUs (Levi-
tation Power Units) whose function is to drive the coils of the levitation and guiding
systems. These PCBs also include a microcontroller from the STM32F3 family that
executes a PI controller to generate the control signals for the power stage of the driver,
given a current reference.
7
2. Needs study
2 Needs study
As per the nature of the project, there are no significant limiting conditions. The
microcontroller is intended to be employed in different control-oriented applications.
However, a fundamental reason for the development of this project is the application of
the system in the levitation system of a hyperloop prototype. Specifically, to control the
current through electromagnets of the Hyperloop UPV prototype, Auran. Therefore,
the new system must be able to fit with the hardware of the prototype. It must also
be able to handle the current control loop. Given the aforementioned conditions, the
requirements are defined in Table 1.2.1
ID Requirement Peripheral
RQMT 6 The PWM signals must have a configurable dead time. Timer
2
Transistor-Transistor Logic
3
Complementary Metal-oxide Semiconductor
8
3. Alternative solutions
3 Alternative solutions
Embedded control systems can be developed employing a variety of hardware solutions.
For instance, some alternatives are microcontrollers, programmable logic devices,
digital signal processors (DSP) or System-on-Chip (SoC). Selecting one alternative or
another may be based on technical, economic or know-how criteria. In this document,
the focus will be placed on the MCU and PLD solutions.
Regarding the technical criteria, both the microcontroller-based and the programma-
ble logic-based solutions have their benefits and drawbacks. On the one hand, MCUs
consume less power than PLDs. This fact makes them more suitable for battery-
powered applications, such as hyperloop.
However, on the other hand, PLDs have the main benefit of being reconfigurable.
This characteristic allows the integrated circuit (IC) to generate the logic needed for a
specific application. That means that overheads are generated on a microcontroller due
to the ability to run a single instruction per clock cycle. They are also more suitable for
high-speed processing and data throughput is demanded. Additionally, they can adapt
to changing needs at lower costs (Zhang, 2010), which is crucial in fast-developing
environments such as Hyperloop UPV.
Furthermore, FPGAs allow the configuration of a soft IP of a CPU; that is that the
hardware of a processing unit can be programmed into the FPGA. It is even possible to
generate a multicore processor inside an FPGA and provide it with custom peripherals.
9
3. Alternative solutions
The device selected for this project is the Intel MAX10 10M50DAF484C7G FPGA
(Figure 1.3.1 The reasons behind this decision are that it is a low-price device and that
Terasic has developed an evaluation board for this device. Some of its characteristics
are:
• 484 pins
• 4 PLLs4
The software employed for the development of the project is the following:
10
3. Alternative solutions
• Quartus Prime Lite (Version 18.1.0): Used for synthesis and timing analysis.
• MATLAB (Version: R2022a) with add-ons Simulink (Version: 10.5) and Control
System Toolbox (Version 10.11.1): Development and simulation of the controller.
11
4. Development
4 Development
Throughout Sections 4 and 5, the project development and verification process will
be described. Section 4 details the design of the microcontroller. Firstly, the RISC-V
architecture and instruction set architecture will be further explained. Then a single-
cycle version, Section 4.2, will be introduced to evaluate the RISC-V architecture,
followed by the multicycle implementation, Section 4.3 along with the memory bus
and the peripherals implemented. The multicycle implementation will not remain as
a simulation model, as it will be synthesised in the FPGA. The SystemVerilog that
describes every module developed can be found in Part VI. Section 5 describes the
verification process of the different modules developed and reports the results obtained.
The different testbenches, assembly code, and other testing files can also be found in
Part VI.
4.1 RISC-V
RISC-V architecture is based on a modular basis. The concept of modularity allows
the architecture to support extensive customisation and specialisation. “The main
advantage of explicitly separating base ISAs is that each base ISA can be optimized
for its needs without requiring to support all the operations needed for other base
ISAs” (Waterman and Asanović, 2019). This modularity is obtained by designing a
base ISA with minimal support and a batch of instruction-set extensions that allow
increasing functionality of the base ISA. RISC-V base ISA has been developed to stand
integer numbers. Then, depending on the application of the designed processor, one or
multiple of the previously mentioned instruction-set extensions are added to create an
application-designed ISA.
There are three types of instruction-set extensions: standard, reserved and custom.
Standard instruction sets are defined by the RISC-V Foundation. Reserved instruction
sets are encodings that have not been defined yet but that the RISC-V Foundation has
earmarked for future ISA extensions or updates. Finally, custom instruction sets are
encodings left for designers to create new instructions outside standard instruction sets.
If a custom extension employs a reserved encoding or an encoding already defined
inside the RISV-C standard sets, this extension is defined as non-conforming.
12
4. Development
The RISC-V base integer ISA is named “I”, preceded by the word size of the architec-
ture (RV32 for 32-bit word size, RV64 for 64-bit word size and RV128 for 128-bit word
size). When an instruction-set extension is added to the base ISA, a suffix is added to
the ISA name. Suffixes are shown in Table 1.4.1.
Suffix Extension
For example, an ISA including a 32-bit integer base, integer multiplication and
division, atomic instructions, and single-precision floating-point extension will be
named RV32IMAF.
4.1.1.1 Registers
The selected instruction set architecture for the project is the RV32I. This ISA de-
termines that there are 32, 32-bit wide, registers (x0-x31). From which, register x0
is hardwired to 0, and the registers x1-x31 are defined as general-purpose registers.
Even though the ISA does not specify the function of the general-purpose registers,
there is a usage convention (shown in Table 1.4.2).
This table also indicates which function (caller or callee) must save the registers in
the stack when calling another function.
Finally, a register called PC (program counter) is also defined in the RV32I. The
program counter stores the memory address of the instruction to be executed in the
following cycle.
6
Defines a 16-bit form for common instructions.
13
4. Development
14
4. Development
4.1.1.2 Instructions
Regarding the instructions defined in the ISA, there are four types of instructions
(R, I, S and U) and two variations; B, derived from S-type; and J, derived from U-type.
Table 1.4.3 depicts the format of the instructions.
The definition RV32I includes 37 instructions divided into three main groups, integer
computational instructions, control transfer instructions and load and store instructions.
It also incorporates two system instructions (ebreak and ecall). Table 1.4.4 displays the
instruction along with its format, and the operation it performs.
lb I rd = SignExt([Address]7:0 )
lh I rd = SignExt([Address]15:0 )
lw I rd = [Address]31:0
lbu I rd = ZeroExt([Address]7:0 )
lhu I rd = ZeroExt([Address]15:0 )
addi I rd = rs1 + SignExt(imm)
slli I rd = rs1 << uimm
slti I rd = (rs1 < SignExt(imm))
sltiu I rd = (rs1 < SignExt(imm))
xori I rd = rs1 ˆ SignExt(imm)
15
4. Development
16
4. Development
In Figure 1.4.1, the datapath is represented in black and the control in blue. The
following sections explain every building block implementation, beginning with the
datapath and then moving to the control.
17
4. Development
32'h4
+ 0
RAM WE
Branch
Force Jump
6:0
OpCode
ALU Op
ALU Src 2
Branch Branch
ALU Src 1 logic
Force Jump
Reg WE 3
OpCode 3 PC Src
14:12
Funct3
2 Flags
19:15 WE
instruction 31:0 Reg 1 32'h0 1
PC Instruction
24:20
PC Reg 2 Reg Data 1 0
2
11:7
Reg 3 Reg Data 2 WE
1
7:2
Instruction ALU Addr
Register file Data Out 0
memory 0 Data In
Data In
1
Data
memory
Immediate ALU Ctrl
generator
ALU
{14:12, 30, 5}
control
4.2.1 Datapath
Following the requirements given in the ISA, the register file must implement 32
registers (32-bit wide), two reading ports and one writing port, and the register x0
must be hardwired to zero. Furthermore, as required in a single-cycle processor, the
reading port must be asynchronous, and the writing port must be synchronous.
Therefore, the final implementation will result, as shown in Figure 1.4.2. The register
file has four input ports corresponding to the addresses of the ports and the writing
port itself. The address inputs are 5-bit, needed to map all 32 registers (log2 32 = 5).
There are two output ports, which are the reading ports. Additionally, a write enable
input has been added in order not to modify the contents of the registers if it is not
specified in the current instruction.
18
4. Development
5 WE
Reg 1
5 32
Reg 2 Reg Data 1
5 32
Reg 3 Reg Data 2
Register file
32
Data In
Finally, the synchronous read has been developed using an always ff block, in the
sensitivity list the rising edge of the clock and the falling edge of the reset. The registers
are set to zero if the reset is active (low level). Furthermore, if the reset is not active
and the write enable signal is, the “dataIn” port value is assigned to the corresponding
register.
19
4. Development
Immediate
Instruction 32
32 Immediate
generator
Type Immediate
I {{21{inst[31]}}, inst[30:20]}
S {{21{inst[31]}}, inst[30:25], inst[11:7]}
B {{20{inst[31]}}, inst[7], inst[30:25], inst[11:8], 1’b0}
U {inst[31:12], 12’b0}
J {{12{inst[31]}}, inst[19:12], inst[20], inst[30:21], 1’b0}
The immediate generator is the implementation of Table 1.4.5 inside a always comb
block in order to be able to use the case statement. The condition has been defined
employing the OpCode. In Table 1.4.6, each OpCode is shown with its corresponding
instruction type.
Type OpCode
20
4. Development
The Arithmetic Logic Unit (ALU) is the combinational circuit that performs the
mathematical (i.e. addition) and logical operations (i.e. logical AND). The ALU im-
plements an interface with two inputs for the operands and one output for the result.
Additionally, it has an input for the control signals and the flags output that will be
used in the branch logic module. The former will be further explained in Section 4.2.2.3.
The interface of this module is shown in Figure 1.4.4.
4
Flags
32
Operand 1
Result
32
ALU
32
Operand 2
Control
4
The ALU must perform the following operations: addition, subtraction, AND,
OR, XOR, logical and arithmetical shift and numerical comparison. Each instruction
requires a determined operation. In Table 1.4.7, it is specified which arithmetic or
logical operation demands each instruction.
21
4. Development
For each pair of operands, the ALU performs every operation concurrently. Each
result is then input to a multiplexer, where the control input signals select the output of
the ALU. The internal diagram of the ALU is depicted in Figure 1.4.5.
Another relevant circuit of the ALU is the “flags” signals, which have two purposes.
The first is to allow the computation of the jump condition in type B instructions. This
calculation is performed in the branch logic module. The second purpose is to compute
the result of the “slt” and “sltu” instructions.
There are four flags “Zero” (Z), “Negative” (N), “Carry” (C) and “oVerflow”7 (V).
“Zero” is active when all values of the “result” signal are set to zero. The “negative”
signal corresponds to the MSB (most significant bit) of the “result” signal. The “carry”
signal is set to a high level when the full adder produces a carry, and the operation
performed is an addition or a subtraction (control bit 1 equal to zero). Finally, the
7
Overflow is denoted by letter V to avoid confusions with number zero.
22
4. Development
31
31
sum 31
Control 1
Control 0
1'h0
1'h0
sum 31
V
C
0 1
Cout
Cout
31'h0 31'h0
sum
0
0 1 2 3 4 5 6 7 8 9
Control
31
Result Z N V C
“overflow” signal is asserted when the addition of two signed numbers produces a
result with the opposite sign. Three conditions must occur to assert the “overflow”
signal: An addition or subtraction must be performed. The first operand and the MSB of
the full adder output must differ. Overflow can occur. If an addition is being calculated,
A and B must have the same sign. In case a subtraction is being performed, A and B
must have different signs. Hence, to execute “slt” (set less than) and “sltu” (set less than
unsigned) instructions, a comparison8 must be made. A subtraction operation shall
be conducted to perform a comparison, and then the flags shall be used to evaluate
the conditions. To perform a “less than” operation, N∧V must be evaluated for signed
operands and C for unsigned operands.
Lastly, it is worthwhile to indicate the logic behind the control signals encoding.
Table 1.4.8 shows the requirements for each operation and the final encoding selected.
8
Comparisons are further explained in Section 4.2.2.3
23
4. Development
The data memory is the module where the program variables can be stored. As
variables can be either read or written, this module implements a RAM (Random
Access Memory). Also, as stated in Section 4.2.1.1, the memory must be designed for
asynchronous reading and synchronous reading.
The data memory module has been designed to be versatile by employing the
parameters functionality of SystemVerilog. Both word size and memory depth are
defined by the parameters WORD SIZE and DEPTH, respectively. For this application,
the default values selected for the data memory are WORD SIZE = 32 and DEPTH = 1024,
resulting in a 4 KiB memory.
The interface defines WORD SIZE wide (32-bit) read and write ports. Moreover,
a write enable input has been added in order not to modify stored data in other
instructions different from “sw”. Finally, the address port depends on the DEPTH
parameter. Its width is defined as log2 (DEPTH). Figure 1.4.6 shows data memory
interface.
Figure 1.4.6: Data memory interface
WE
5
Addr
32
Data Out
32
Data In
Data memory
24
4. Development
The instruction memory is the module where the instructions are stored. These
instructions cannot be written on runtime, and as the processor has not been designed
to be programmed, this memory shall only be read. Hence, the module implements
a Read Only Memory (ROM). In order to load the compiled program into the mem-
ory, an initial block is used along with the $readmemh() instruction (see Code
snippet 1.4.1). As previously mentioned, the memory must feature asynchronous
reading.
3 initial begin
4 $readmemh("compiledcode.hex", memory);
5 end
Similarly to the data memory, the instruction memory has been designed to be
configurable. Also, it implements the DEPTH parameter, which implies the number of
instructions that can be stored. The default value selected for the parameter is 1024
(4 KiB memory), though it could be extended to up to 230 (4 GiB memory).
The interface defines a 32-bit wide read port from where the instruction is output
and a 32-bit address port to which the program counter is input. Figure 1.4.7 shows
instruction memory interface.
32 32
PC Instruction
Instruction
memory
25
4. Development
The main building blocks of the datapath have been explained in the previous sections.
However, as shown in Figure 1.4.8, the datapath is not only built from these blocks.
Several multiplexors modify the datapath depending on the instruction being executed.
Additionally, even the main modules have some control inputs that modify their
behaviour. Throughout the following sections, the control modules that modify those
multiplexors and control signals will be described.
32'h4
+ 0
19:15 WE
instruction 31:0 Reg 1 32'h0 1
PC Instruction
24:20
PC Reg 2 Reg Data 1 0
2
11:7
Reg 3 Reg Data 2 WE
1
7:2
Instruction ALU Addr
Register file Data Out 0
memory 0 Data In
Data In
1
Data
memory
Immediate
generator
The Control unit is the core module of the processor. It implements the logic that
manages the datapath by controlling the multiplexers. Furthermore, it also governs the
other control and datapath modules.
The Control unit module inputs the “OpCode” of the instruction and then outputs
the control signals of the ALU source multiplexers, the write enable signals of the
register file and the data memory, the ALU operation signals, “branch” and “ForceJump”
signals. Figure 1.4.9 depicts the module interface.
26
4. Development
Control Branch
Force Jump
RAM WE
Reg WE
7
OpCode
ALU Src 2
2
ALU Src 1
2
ALU Op
2
Reg Write Src
The “OpCode” determines the type of the instruction being executed. Table 1.4.9
shows the corresponding group of instructions per each “OpCode”.
OpCode Instruction
00000112 lw
00100112 addi, slli, slti, sltiu, xori, srli, srai, ori, andi
00101112 auipc
01000112 sw
01100112 add, sub, sll, slt, sltu, xor, srl, sra, or, and
01101112 lui
11000112 beq, bne, blt, bge, bltu, bgeu
11001112 jalr
11011112 jal
27
4. Development
Regarding the programming of the control unit, the module implements pure com-
binational logic. Therefore, the module has been coded employing an always comb
block with a case statement where the case items are the possible values of the “Op-
Code” and determines the output values. Table 1.4.10 shows the values of each output
concerning the “OpCode”.
OpCode Branch Jump RAM WE RF WE ALU src 2 ALU op ALU src 1 RF WB Src
The ALU control unit is the module in charge of controlling the result multiplexer
of the arithmetic logic unit. To perform this task, the module takes as input the value
of the “ALU op” signal output from the control unit, as well as the instruction bits 30,
14-12 (“Funct3”) and 5 (see Figure 1.4.10).
ALU Ctrl
2
ALU Op
5 ALU
Func
control
28
4. Development
With the defined inputs, the ALU control unit determines the “ALU op” according
to Table 1.4.11.
As it was explained in Section 4.2.2.1, the “ALU op” is determined by the “OpCode”.
Therefore, for those groups of instructions that share the same “OpCode” and require
different ALU operations, the value 0 is read at the “ALU op” input. For those groups
of instructions that demand the ALU to perform a subtraction, the value 1 will be read,
and finally, when an addition is compelled, the value read is 2.
0000X2 00002 +
000012 00002 +
000112 00012 –
0010X2 01102 <<
010XX2 01012 SLT
002 011XX2 10012 SLTU
100XX2 01002 ∧
1010X2 01112 >>
1011X2 10002 >>>
110XX2 00112 |
111XX2 00102 &
012 N/A 00012 –
102 N/A 00002 +
The program counter stores the value of the address where the current instruction
is placed in the program memory. This section is aimed to define how the value of the
PC changes, that is, how jumps between instructions are performed.
For most of the instructions, the following instruction to execute is the one that
is stored next to it in the memory. It means that the address where the following
instruction is stored is the current address plus four. Since this operation is so recurrent,
29
4. Development
an adder has been explicitly added to perform this function. One of its inputs is the PC,
and a constant value of four has been hardwired in the other.
For the rest of the instructions, which are seven in the RV32I ISA, the next instruction
to execute is not necessarily the next one in memory. There are two types of instructions,
jump and branch. The former performs a jump without any condition, and the latter
only jumps if the condition (determined by the instruction) is met. A dedicated adder
that adds the current PC plus the immediate encoded in the instruction has been
implemented to calculate the new PC value. The main reason behind this design
decision is that the ALU is performing the subtraction for the branch condition. The
instruction “jalr” is an exception and employs the ALU to compute the new PC. The
ALU is used because no condition has to be evaluated, and a multiplexor (with its
corresponding control signal) is not needed to be implemented to allow the input in
the adder of the value of “Reg Data 1”.
Branch Branch
logic
Force Jump
2
OpCode 3 PC Src
4
Flags
3
Funct3
30
4. Development
31
4. Development
32
4. Development
multicycle processor has a higher throughput of four instructions per 800 units of time,
even though it takes 200 units of time more to execute each instruction.
Single-cycle processor
0 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600
Time
IF ID EX MEM WB
IF ID EX MEM WB
Multicycle processor
0 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600
Time
IF ID EX MEM WB
IF ID EX MEM WB
IF ID EX MEM WB
IF ID EX MEM WB
• Structural hazard: A needed resource for the execution of the instruction is not
available.
• Data forwarding allows the utilisation of a calculated result before it has been
written in the register files.
• Jump prediction, hardware predicts if a jump will be taken or not. It can be based
either on static or dynamic criteria based on the previous results.
9
nop is a pseudoinstruction equivalent to addi x0, x0, 0
33
4. Development
The solution applied in the multicycle processor is not to pipeline the instructions,
therefore avoiding these hazards but maintaining the advantages of a multicycle core,
such as using external memories, since synchronous reading is possible in this configu-
ration or using a single memory. Figure 1.4.14 shows the scheme of the processor, the
datapath is drawn in black and the control in blue.
PC Reg RST
PC Reg EN
Result Src
6:0
OpCode ALU Op
ALU Src 1
RF Reg RST
RF Reg EN
Fetch Reg EN
Reg WE
Instruction read
Branch logic
14:12
Funct3 Branch
Flags
memoryRead 2
1'h0 1'h1 EN RST
EN RST 1 d q
Read WE CS d q
0
Addr 3
Data Out
32'h0 Data In 2
Program 19:15
Reg 1
WE EN RST 32'h0 1
memory EN RST
24:20
Reg 2 Reg Data 1 d0 q0 0 ALU d q memoryAddr
11:7
Reg 3 Reg Data 2 d1 q1 memoryWrite
Register file
Data In 2
32'h4
Immediate 0
generator ALU Ctrl
ALU
control
34
4. Development
4.3.1 Datapath
The datapath is not significantly modified compared to the single-cycle versions, al-
though it is not free of modifications. These modifications are mainly due to processor
segmentation. There are two noteworthy changes. The first of them is the segmen-
tation itself, meaning that registers have been added to make the execution phases
independent from each other.
Read WE CS
log2(DEPTH)
Addr WORD_SIZE
WORD_SIZE Data Out
Data In
Memory
35
4. Development
In contrast to the datapath, the control logic must be refactored to support the processor
segmentation. The appearance of different stages that must be coordinated and the fact
that every type of instruction requires a different behaviour of the datapath forces the
control logic to transition from basic combinational logic to sequential logic and the
implementation of a state machine to govern the processor behaviour. Implementing a
state machine permits optimising hardware utilisation and reducing circuitry, further
explanation in Section 4.3.2.2.
As explained in the previous section, the segmentation of the processor forces the
control unit to transition from a decoder to a state machine. The Control unit module
uses the “OpCode” of the instruction and the “branch” signal generated by the branch
logic module to determine the following state (Figure 1.4.16 shows the control unit
module interface).
36
4. Development
Memory WE
PC Reg RST
PC Reg EN
2
Result Src
2
Branch ALU Src 2
2
ALU Src 1
RF Reg RST
RF Reg EN
Fetch Reg EN
Reg WE
Instruction read
The datapath is divided into five stages: instruction fetch, instruction decode,
execution, memory access, and write back. The first two stages are common for every
instruction, meaning that the outputs generated by the control unit module shall be
the same. Each type of instruction requires a different set of outputs for the rest of the
stages. The state machine diagram is shown in Figure 1.4.17.
37
4. Development
OpCode Branch = 02
Comparison
11000112
PC
Branch = 12
Update B
OpCode PC
11011112 Update J
Rd
Calculation
OpCode PC
11001112 Update I
OpCode
01101112 Execute
Lui
Fetch Decode
OpCode Execute
Auipc
00101112
Register
Write
OpCode
Execute R
01100112
OpCode
00100112 Execute I
Memory
OpCode5 = 02 Memory
Write
Read
Back
OpCode
01000112 Memory
Addr
00000112
OpCode5 = 12 Memory
Write
38
4. Development
ALUOutRegRST
InstructionRead
ALUOutRegEN
FetchRegRST
FetchRegEN
DataRegEN
PCRegRST
ALUIn1Src
ALUIn2Src
RFRegRST
PCRegEN
RFRegEN
ResultSrc
memWE
ALUOp
RegWE
State
Fetch 12 12 12 02 02 02 12 12 12 12 112 102 012 02 02 102
Decode 02 02 02 12 02 02 12 12 12 12 002 002 002 02 02 002
Memory Addr 02 02 02 02 12 02 12 12 12 12 002 002 002 02 02 102
Memory Read 02 02 02 02 02 12 12 12 12 12 002 002 002 02 02 002
Memory WB 02 02 02 02 02 02 12 12 12 12 002 002 102 02 12 002
Memory Write 02 02 02 02 02 02 12 12 12 12 002 002 002 12 02 002
Execute R 02 02 02 02 12 02 12 12 12 12 002 012 002 02 02 002
Execute I 02 02 02 02 12 02 12 12 12 12 002 002 002 02 02 002
Execute Auipc 02 02 02 02 12 02 12 12 12 12 102 002 002 02 02 102
Execute Lui 02 02 02 02 12 02 12 12 12 12 012 002 002 02 02 102
PC Update I 12 02 02 02 02 02 12 12 12 12 002 002 012 02 02 102
PC Update J 12 02 02 02 02 02 12 12 12 12 102 002 012 02 02 102
Rd Calculation 02 02 02 02 12 02 12 12 12 12 102 102 002 02 02 102
Comparison 02 02 02 02 02 02 12 12 12 12 002 012 002 02 02 012
PC Update B 12 02 02 02 02 02 12 12 12 12 102 002 012 02 02 102
Register Write 02 02 02 02 02 02 12 12 12 12 002 002 002 02 12 002
The implementation of the state machine is a Moore state machine. Thus, the
outputs only depend on the current state. Table 1.4.14 shows the output value for each
state.
39
4. Development
7 // State register
8 always_ff @(posedge clk or negedge arst) begin
9 if(˜arst) state <= Fetch;
10 else if(en) state <= nextstate;
11 end
12
21 // Output logic
22 always_comb begin
23 case(state)
24 <STATE>: begin
25 <OUTPUTS>;
26 end
27 endcase
28 end
The segmentation of the processor and the approach taken of not pipelining in-
structions result in only one portion of the hardware being utilised in each clock cycle.
Despite the fact that it might sound like a waste of resources, it means that it can be
employed in other stages of the instruction. This characteristic has been implemented
in the processor to reduce the hardware employed in jump and branch instructions.
Jump and branch instructions require the use of two mathematical operations.
Branch instructions, in particular, perform a comparison between two registers (a
subtraction) and an addition, which can either be PC + 4 or PC + immediate, to calculate
the new PC. On the other side, jump instructions must perform two additions, the
following value of PC (register + immediate or PC + immediate) and PC + 4, which
will be stored in the Register file.
40
4. Development
In the single-cycle implementation, it was impossible to use the ALU for two differ-
ent operations in a single instruction. As a consequence, two 32-bit adders had to be
included. In the multicycle implementation, is it possible to use the ALU twice during
the execution of an instruction just by designing the control state machine to perform
those operations. Therefore, both adders and the multiplexor and its control logic have
been removed from the single-cycle implementation. Figure 1.4.18 shows in red the
components that have been removed.
32'h4
+ 0
RAM WE
Branch
Force Jump
6:0
OpCode
ALU Op
ALU Src 2
Branch Branch
ALU Src 1 logic
Force Jump
Reg WE 3
OpCode 3 PC Src
14:12
Funct3
2 Flags
19:15 WE
instruction 31:0 Reg 1 32'h0 1
PC Instruction
24:20
PC Reg 2 Reg Data 1 0
2
11:7
Reg 3 Reg Data 2 WE
1
7:2
ALU Addr
Instruction Register file Data Out 0
memory 0 Data In
Data In
1
Data
memory
Immediate ALU Ctrl
generator
ALU
{14:12, 30, 5}
control
The branch logic module (see Figure 1.4.19), as a consequence, has reduced its
functionality. Its only function is to analyse the ALU flags when a comparison is made
in a branch instruction and indicate to the state machine whether a jump must be
performed. The logic implemented in the Branch logic is depicted in Table 1.4.15.
41
4. Development
Branch logic
7
Funct3 Branch
4
Flags
In order to connect the peripherals to the central processing unit, a memory bus has
been designed inspired by the Intel Avalon Memory-Mapped Interface (Avalon-MM),
which is an address-based read/write interface. The resultant memory bus employs
a master/slave architecture where the CPU acquires the role of master and the data
memory and peripherals the role of slave. An interface has been designed for each role
(described in Table 1.4.16). That means that every peripheral must implement the same
interface to be connected to the bus.
42
4. Development
Master interface
Signal Width In/Out Description
Read 32 In Master data input
Write 32 Out Master data output
Addr 32 Out Read/Write address
WE 1 Out Slave write enable
RE 1 Out Slave read request
Slave interface
Signal Width In/Out Description
DataRead 32 Out Slave data output
DataWrite 32 In Slave data input
Addr 0-32 In Offset in the slave memory space
CS 1 In Chip Select - if not active the slave ignores all signals
Read 1 In Read request
Write 1 In Write enable
The memory bus consists of “Address” and “Write” busses, individual “Read”
lines and enable signals. A complete scheme of the memory bus can be observed in
Figure 1.4.20. The “Address” bus is connected to every component connected to the
bus. The peripherals connected to the memory bus are assigned a range of addresses to
identify their internal peripherals (memory mapping). Even though each peripheral
is assigned a specific range of addresses, their address port is dimensioned to the
minimum size to map their internal peripherals; this port is used as an offset to their
base address10 . Since the peripherals cannot identify whether the address on the bus
belongs to their range, a “Chip select” signal is assigned to each peripheral. It will be
active whenever the address on the bus belongs to the particular peripheral. “Chip
select” signals are managed by the memory controller (further explained in Section
4.3.3.1).
10
The base address is the lowest address assigned to a specific peripheral.
43
4. Development
CS PID-Timer
CPU Link
memoryWE Write
PID in
memoryRE Read
TIM Comp 1
memoryAddr
TIM Comp 2
memoryWrite Write Data
CS 7-Segment
Write
Read
Hex
Write Data
Read Data
CS Timer
Write
PWM
Read
5:2 TIM Comp 1
Addr
TIM Comp 2
Write Data
Read Data
ADC
CS
Read Channel 0
6 2
Addr Channel 1
5 Read Data
EN 4
q d 3
CS PID 2
2
Write
1 Read Feedback
5:2
Addr Control out
0
Write Data
ReadData
Read Mux Src
CS PID-Timer Link
CS PID 1
CS 7 Segments
Write
Write CS PID 1
Data
CS RAM CS
memory
Write
Memory
controller Read
7:2
Addr
Write Data
ReadData
44
4. Development
The “Write” bus is connected to the CPU and those peripherals that allow writing
operations. For a write operation to occur, the CPU must send the data through the
“Write” bus and activate the “Write enable” signal. The “Chip select” signal must be
active for the aimed peripheral. Reading topology differs slightly from writing. Since
there is no “Read” bus, the individual signals are multiplexed. Read operation requires
the “Read” signal and the “Chip select” signal to be active.
The memory map diagram (Figure 1.4.21) comprises the address ranges assigned
to each peripheral11 and memory. For this project, it has been implemented two PID
controllers, a timer, an ADC, a six-digit seven-segments decoder and PID-Timer Link
peripheral. These modules will be further explained in the upcoming sections.
Data
PID 2
0xC000 0040
Reserved
The Memory controller is the module that decodes the addresses to activate the
correct “Chip select”. It also uses the addresses to control the multiplexer of the “Read”
signals. The module takes as input the address and the “memoryWE” and “memoryRE”
signals and generates the previously mentioned signal (see Figure 1.4.22).
11
A detailed memory map and peripheral registers list can be found at Appendix A
45
4. Development
3
Read Mux Src
CS PID-TIM Link
CS 7-Segment
CS Timer
32
Addr
CS ADC
Read
CS PID 2
Write
CS PID 1
CS RAM
Memory
controller
The memory controller accepts as parameters the base address of each peripheral
and the data memory, allowing a rapid modification of the reserved memory for each
peripheral if necessary. The inner logic compares the address received with the value
of the parameters to generate the outputs. To activate a ”Chip select”, either the
”memoryWE” or the ”memoryRE” signals must be active. Table 1.4.17 indicates the
base addresses of each of the peripherals.
46
4. Development
The PID-Timer Link peripheral bypasses the output of the PID peripheral to the
timer peripheral. As it will be explained in Section 4.4, the electromagnets are driven
by an H-bridge controlled by two PWM signals and their complementaries. These
electromagnets require both positive and negative currents flowing through them. The
way to achieve the change in the sign of the current is by changing the branch of the
H-bridge that is commuting. However, the PID control outputs positive and negative
values but cannot generate the duty cycle value for each branch. The PID-Timer Link
interprets the output sign and generates the duty cycle values for each branch (a
functional diagram is shown in Figure 1.4.23). It also incorporates a shifter (see Table
1.4.18), that can be programmed from the CPU, to adequate the control output value to
fit the duty cycle range. Figure 1.4.24 shows the interface of the module.
32'h0
<
32'h0 1
DT PWM 2
2's 0
Complement
CS PID-Timer
Link
Write
32
Read PID in
32
TIM Comp 1
32 32
Write Data TIM Comp 2
32
Read Data
47
4. Development
Memory Read/
Name Function
offset Write
7-Segment
7
CS Hex 0
7
Write Hex 1
7
Read Hex 2
7
Hex 3
32 7
Write Data Hex 4
32 7
Read Data Hex 5
For this application, a single-digit decoder has been designed and then instantiated
six times inside the peripheral. The single digit receives a hexadecimal digit (4 bits)
and generates the decoded 7-bit wide output. It has also been developed to adapt to
different displays (common cathode or common anode) by means of a parameter that
depends on its value inverts or not the output signal.
48
4. Development
Memory Read/
Name Function
offset Write
49
4. Development
A wrapper has been designed to fit the IP block generated to the microcontroller.
The wrapper is developed around the ADC block. It implements two 32-bit registers
that are updated with the values of the ADC channels. Then, the wrapper implements
a read-only slave interface to connect the peripheral to the memory bus. The ADC
values are available to the processor at the addresses 0xC0000078 and 0xC000007C
(Table 1.4.20), channel one and channel two, respectively. Additionally, both channels
have been bypassed as outputs of the module (as shown in Figure 1.4.27) to be used as
feedback by the PID modules.
Memory Read/
Name Function
offset Write
ADC
CS
32
Read Channel 0
32
Addr Channel 1
32
Read Data
50
4. Development
4.3.3.5 Timer
CS Timer
2
Write PWM 1
2
Read PWM 2
4 32
Addr Compare 1
32 32
Write Data Compare 2
32
Read Data
The timer peripheral has been designed by developing smaller modules that perform
a single task and are then interconnected to generate the peripheral. The modules are
a counter, a prescaler, and two PWM generators. The counter is the primary element
of the timer. It is based on a 32-bit register that increments its value by one each clock
cycle if the enable signal is active. This register must be accessible from the CPU,
allowing reading and writing operations; therefore, it is mapped in memory, as it will
be explained further in this section. Writing on the counter register allows resetting,
modifying or setting an initial value to the timer. On the other side, reading the counter
allows knowing the current value of the count. An enable signal is also assigned to a
register to be accessible from the microcontroller. When the counter value arrives at its
maximum value, a flag sets. The maximum value can be settable from the CPU.
51
4. Development
Lastly, the PWM generator is a module able to generate a PWM and its complemen-
tary modifying their duty cycle using the counter value. The module inputs the count
value, the maximum value and a programmable value or the bypassed PID output,
which will actuate as the duty cycle. While the value of the counter remains below
the comparison value, the output will remain in LOW state. However, if the counter
exceeds the comparison value, the output changes to HIGH state (see Figure 1.4.29).
The PWM can be configured to output the complemented signal of the PWM with a
programmable dead-time. A 2-bit number must be input to the PWM generator where
the least significant bit enables the PWM signal, and the most significant bit enables the
PWMN (complementary PWM). To enable PWMN, the PWM signal must be enabled.
The PWM generator takes an 8-bit integer to configure the dead-time value. A pro-
gressive formula, designed by ST Microelectronics12 , has been applied to calculate the
dead-time values to obtain a larger range without losing precision for short dead-times.
The formula is shown in Table 1.4.21. The values obtained in the formula refer to the
number of clock cycles while the dead-time is active. To obtain the dead-time they shall
be multiplied by the clock period.
Max Value
Compare Value
PWM Generated
12
The formula is defined in the application note AN4043 - ST Microcontroelectronics (2016)
52
4. Development
Memory Read/
Name Function
offset Write
0016 R/W Count Stores counter value
0416 R/W ARR Count maximum value
0816 R/W Start Counter enable — ‘1’ Enable, ‘0’ Disable
0C16 R/W IRQ Sets when maximum value is reached
1016 R/W Prescaler Prescaler value
1416 R/W Dead-time Dead-time configuration value
1816 R/W Compare 1 PWM generator 1 compare value
1C16 R/W Compare 2 PWM generator 2 compare value
2016 R/W Output enable Output enable — ‘1’ Enable, ‘0’ Disable
Bit [0]: PWM 1
Bit [1]: PWMN 1
Bit [2]: PWM 2
Bit [3]: PWMN 2
2416 R/W Bypass Activates duty cycle bypass
53
4. Development
As the main objective, and requirement of the project, the development of a control-
oriented microcontroller demands the implementation of a PID controller peripheral.
The aim of this peripheral is to relieve the computational cost of performing this
operation by software. The hardware implementation of the controller allows reducing
the computational cost to just the parameter setting operations.
1. The proportional term (Kp ) generates a control action proportional to the current
error of the system.
2. The integral term (1/Ti ) considers the past of the signal integrating the accumu-
lated error, reducing the steady-state error.
3. The derivative term (Td ) is intended to anticipate future trends by actuating over
the variation of error.
By adding the three terms, it is obtained the control action. The equation of the PID
controller results as follows:
Z t
1 de(t)
u(t) = Kp · e(t) + e(τ ) dτ + Td (1)
Ti 0 dt
Where:
t is the time
The previous equation represents the PID controller equation for continuous time.
However, digital systems operate under discrete time conditions, meaning that the
presented equation must be adapted to discrete time. For this application, the integral
54
4. Development
has been approximated by employing the rectangular rule, while the differentiator
employed is the first-difference differentiator. The obtained difference equation is the
following:
k−1
T X Td
u(k) = Kp · e(k) + e(i) + (e(k) − e(k − 1)) (2)
Ti i=0 T
k−2
T X Td
u(k − 1) = Kp · e(k − 1) + e(i) + (e(k − 1) − e(k − 2)) (3)
Ti i=0 T
Subtracting Equations 2 & 3 and grouping terms, the recursive PID algorithm is
obtained.
k−1 k−2
T X T X
u(k) − u(k − 1) = Kp · e(k) − Kp · e(k − 1) + e(i) − e(i)+
Ti i=0 Ti i=0 (4)
Td Td
+ (e(k) − e(k − 1)) − (e(k − 1) − e(k − 2))
T T
T
u(k) − u(k − 1) = Kp · (e(k) − e(k − 1)) + e(k − 1)+
Ti (5)
Td
(e(k) − 2e(k − 1) + e(k − 2))
T
Td T Td Td
u(k) = u(k − 1) + e(k) · Kp + + e(k − 1) · −Kp + −2 + e(k − 2) (6)
T Ti T T
Td T Td Td
Where: K1 = Kp + K2 = −Kp + −2 K3 = (8)
T Ti T T
55
4. Development
Once obtained the difference equation has been obtained, the electronic circuit can
be implemented. The electronic diagram is shown in Figure 1.4.30.
K1
r(k)
_ e(k)
y(k)
K2 +
e(k-1)
K3 + u(k)
e(k-2)
+
u(k-1)
The prescaler module of the PID controller peripheral is an instance of the prescaler
designed for the timer peripheral. This module, as in the timer, actuates over the enable
signal of the controller. The aim of this module is to configure the step time of the PID
controller.
The saturation module has been designed in order to protect the system and the
electronics from output values that fall outside the maximum or minimum rating of
56
4. Development
Memory Read/
Name Function
offset Write
the system or values that can harm either the system operated or the operator. The
saturation module operates over the output value of the controller. Regarding the
configuration of the module, it depends on three values. (1) The enable/disable register,
whose default value is zero, meaning that the saturation module is enabled by default.
(2) The upper saturation value, and (3) the lower saturation value; both registers default
value is zero. This configuration forces the PID peripheral to have a zero value in its
default state that has to be manually disabled or modified.
Finally, the feedback bypass is a multiplexor that selects whether the feedback
value of the controller is taken from the feedback registers (thus must be written by
the CPU) or by the bypass port of the peripheral (see peripheral interface diagram in
Figure 1.4.31) which is directly connected to an ADC.
57
4. Development
CS PID
Write
32
Read Feedback
4 32
Addr Control
32
Write Data
32
Read Data
To bring the design from the simulation to the physical world, it is necessary to locate
the input and output pins in the FPGA. Quartus incorporates the “Pin Planner” tool to
locate the inputs and outputs. Figure 1.4.32 shows the pinout of the FPGA. However,
the complete list is located in Annex C.
12
Used pins are marked in red.
58
4. Development
As a difference with the single-cycle, and as mentioned in Section 4.3.1.1, the new
memory module allows the use of the embedded memories. The usage of the memories
can be observed in the “Total memory bits” field of Figure 1.4.33. Furthermore, it can be
seen that the logic elements employed are only 9% of the total, meaning that other logic
could be running in parallel to the design. Even the same design could be duplicated
in the same FPGA, resulting in an IC with two independent MCUs. Lastly, the timing
analysis indicates that the maximum clock frequency for the MCU is 52.33 MHz.
59
4. Development
Regarding the coil, it has been defined as a first-order system with the following
transfer function:
48 1
G(s) = · (9)
4096 0.0048 · s + 0.4488
Now that the system has been described, it is necessary to consider the control restric-
tions. Two restrictions must be followed. Firstly, the system cannot be underdamped,
which means that no overshoot is accepted. The other parameter affected is the settling
time, which must be within 0.6 to 0.8 seconds.
60
4. Development
(c) PID
(d) System
(e) Sensor
The system has been replicated in MATLAB Simulink to tune the PI controller. The
control loop consists of four subsystems (shown in Figure 1.4.34):
2. PID: Contains the blocks corresponding to the parallel PID controller. The “slider
gain” block has been chosen to implement the control gains because their value
can be modified in runtime, so that the control can be tuned dynamically. Since
the controller to be implemented is a PI, the gain for the Kd has been fixed to zero.
4. Sensor: This block includes the transformation from the current measurement to
the ADC read value.
61
4. Development
To tune the control dynamically, the simulation time has been set as infinite and a
square waveform as the reference. The two gain sliders will be carefully modified to
achieve a settling time of around 0.6-0.8 seconds and 0% of overshoot. The result for
the gains Kp = 1 and 1/Ti = Ki = 20 can be observed in Figure 1.4.34.
6
Current [A]
0
0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5
Time [t]
Having checked that the control simulation operates correctly, the parameters must
be transformed to adequate to the PID peripheral implementation employing the
expressions defined in Equation 8, considering T = 0.001, since the PI will be executed
at 1 kHz. The final values of the control are the following:
Td T Td 0.001
K1 = Kp + = Kp = 1 K2 = −Kp + − 2 = −1 + −1 = −0.98
T Ti T 20
Td
K3 = =0
T
Since K2 is a decimal number and by rounding to units, the integral term would
disappear, the solution is to shift the gains to the left by 10 (in their binary expression)
or multiply by 210 resulting in the following values:
With the values calculated and substituting the continuous PID module from the
Simulink diagram with a model of the implemented PID (see Figure 1.4.35), it can now
be simulated. Figure 1.4.36 shows the time response of the control design. It can be
observed that this control fulfils the requirements set by the Hyperloop UPV team.
62
4. Development
6
Current [A]
0
0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5
Time [t]
Once the hardware and the controller have been designed and in order to get the
microcontroller to operate, a software must be developed. This software aims to set
the peripherals to the desired conditions for the PI controller to operate without using
the CPU. The peripherals involved in the operation of the controller are the ADC, the
PID 1, the PID-Timer Link and the timer. Their configuration is following:
63
4. Development
to not allow output currents over 30 amperes. Also, the controller gains must be
loaded to the peripheral.
• PID-Timer Link: The shift value of the peripheral must be set to 10, to undo the
shift in the PID constants.
• Timer: The timer must be set to output a PWM and its complementary in both
channels, with a dead time of 200 ns. The frequency of the PWM signals must be
set to 10 kHz. Finally, the duty cycle bypass must be activated.
To ease the configuration of the peripherals, the base addresses of the peripherals
are stored in registers x1 to x3.
To achieve an operation value of 1 kHz, the prescaler value must be modified. The
value has been calculated by dividing the frequency of the clock signal by the desired
operating frequency.
fclk 50 M Hz
Prescaler value = = = 50, 000 (10)
fP ID 1 kHz
Once configured the prescaler, gains calculated in Section 4.4.1 must be introduced.
Finally, the saturation values must be configured. Since the maximum current allowed
in the system is 30 amperes, according to Equation 11, the saturation values have been
set to 1,179,648 and -1,179,648 for the high and low saturation values, respectively.
Vsupply −1
Saturation = ±Isat · · ADCmax ·Shif t
Rcoil (11)
48 −1
Saturation = ±30 · · 4096 · 1024 = ±1, 179, 648
0.45
64
4. Development
fclk 50 M Hz
fP W M = = = 12.207 kHz (12)
ADCmax 4096
The obtained frequency has an error of 22.07% compared to the target. However,
this error cannot be reduced. If the shifting value of the PID-Link is reduced to 9, the
obtained frequency would be 6.104 kHz, what will represent an error of 38.96%.
In order to output two PWM and their complementary, the output enable register
of the timer must be set to 11112 .
Finally, the dead time must be configured to 200 ns. It is necessary to calculate the
number of clock cycles that add up to 200 ns.
Applying the formula described in Table 1.4.21, the value for the dead time register
is 10.
Once the configuration of each peripheral has been explained, the resultant assembly
code can be found in Section 1.3.5 of Part VI.
65
5. Verification
5 Verification
Verification is the process of checking the correct operation and functionality of a
hardware. Hardware description languages (HDL) offer commands that cannot be
synthesisable (hardware cannot be generated) but support functionality for verification
and simulation purposes. The non-synthesisable code can be used to create testbenches.
One of the main reasons behind the selection of SystemVerilog is its support for verifi-
cation tools and commands, for example, assertion-based verification, coverage and
testbench constructs and hierarchical design interfaces.
A testbench is a module able to apply inputs to a module, the device under test
(DUT), and analyse the correctness of the outputs generated by the DUT. Testbenchs
have been employed to verify every module designed for this project. The testbenches
have been structured in the following way:
• Signal declaration
• Clock generation
• Initialisation
• Output verification
Complete testbenches, input vectors, expected outputs and assembly files can be
found in Section 2 of Document VI.
5.1 Single-cycle
As mentioned previously, the modules have been verified, employing testbenches. For
the single-cycle processor modules, the testbenches have been implemented using the
specified structure.
The testbenches load a file containing all the test cases defined for each module.
Every test case comprehends a set of input signals and their expected output values.
When a test case is applied to the DUT, the outputs are compared to the expected
output values. If the output value coincides with the expected value, the test case
is considered as passed. However, an error message is printed in the simulation
console if a discrepancy is encountered between the output and the expected value.
Each testbench incorporates specific error messages to identify better the issues that
66
5. Verification
generated that error (see Figure 1.5.1). Finally, a message is printed showing the number
of tests executed and the errors that have arisen in the verification process (Figure 1.5.1).
The following figures show the results obtained for the different modules of the single-
cycle processor.
Register file
• Correctness in the data input and output by assuring the data outputted coincides
with the input data and that the register is the same as the one in which it was
written.
67
5. Verification
Immediate generator:
Data memory:
• Assure that data writes to the correct address and can be read.
68
5. Verification
Program memory:
Control unit:
69
5. Verification
ALU control:
• ALU control signals are generated according to the instruction being executed.
Branch logic:
• “PC Src control” signals are generated according to the instruction being executed
and the flags output from the ALU.
To verify the correct implementation of the modules, a testbench has been developed to
simulate the complete implementation of the processor. The input of the testbench is
the assembled files for different software scripts that allow the implementation of the
microcontroller.
The verification has been developed in two different stages. The first one is the input
of sequences with all the different instructions supported by the CPU. The second is the
execution of two programs for actual application conditions. The programs developed
are the bubble sort algorithm and the Fibonacci series, which were both implemented
in RISC-V assembly and then assembled and input as machine code.
70
5. Verification
In the first place, type I and type S instructions have been tested. The script devel-
oped executes each instruction (at least once), and the results of each operation are
stored in a different register so the proper functioning of the CPU can be verified at the
end of the simulation. Figure 1.5.10 shows the simulation waveforms. Figure 1.5.11, on
the other side, shows the register file and data memory (only the written address) at
the end of the simulation.
71
5. Verification
Proceeding as with types I and S, types R, U and J have been tested altogether.
Figure 1.5.12 shows an overview of the complete CPU, while Figure 1.5.12 shows a
detailed view of the registers file.
72
5. Verification
73
5. Verification
Finally, the same procedure is applied to branch instructions (type B). However, for
this case, the main focus is on the PC value. As it can be seen in the Code Snippet 1.5.5,
every instruction is evaluated for a case in which it must perform a jump and a situation
for which it is not supposed to perform it. The results can be seen in Figure 1.5.14.
74
5. Verification
26 nop
27 .jump6:
28 bge x1, x2, .jump6 # if x1 >= x2 then target
29 bge x2, x3, .jump7 # if x2 >= x3 then target
30 nop
31 .jump7:
32 bge x3, x2, .jump7 # if x3 >= x2 then target
33 bge x3, x4, .jump8 # if x3 >= x4 then target
34 nop
35 .jump8:
36 bge x4, x3, .jump8 # if x4 >= x3 then target
37 .back1:
38 bge x3, x3, .jump9 # if x3 >= x3 then target
39 jal x0, .back1
40 .jump9:
41 bltu x1, x3, .jump10 # if x1 < x3 unsigned then target
42 nop
43 .jump10:
44 bltu x3, x1, .jump10 # if x3 < x1 unsigned then target
45 bgeu x3, x2, .jump11 # if x3 >= x2 unsigned then target
46 nop
47 .jump11:
48 bgeu x2, x3, .jump11 # if x2 >= x3 unsigned then target
49 .back2:
50 bgeu x1, x1, .jump12 # if x1 >= x1 unsigned then target
51 jal x0, .back2
52 .jump12:
53 nop
75
5. Verification
Once all the instructions have been individually verified, two test programs have
been developed. In the first place the Fibonacci sequence. The program starts with
the first two values and calculates the 15 first numbers (it could be modified by the
initialisation value of register x1). Figure 1.5.15 shows the value of the registers
modified during the execution. Register x3 shall have the 16 first values of the series: 1,
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987.
Bubble sort is a simple sorting algorithm that steps through an array of elements
swapping the element n with the element n+1 if they are in the wrong order. The
algorithm goes through the array until no swaps have been performed.
An example can be found in Table 1.5.1. The first column holds the unordered array,
and the last column shows the result of the bubble sort algorithm.
The assembly code generates a 10-number array and applies bubble sort to order
the array. The simulation waveforms are shown in Figure 1.5.16
76
5. Verification
Note: Red cells stand for a comparison that results in a swap action.
Green cells indicate a comparison that do not perform a swap
5.2 Multicycle
5.2.1 Processor
The most significant module modification of the migration from the single-cycle proces-
sor to the multicycle processor is the control module which changes from an instruction
decoder (combinational logic) to a state machine (sequential logic). Even though this
module could be independently verified, the module has been tested integrated into the
CPU, so not only has the control module been designed according to the specification,
but the specifications have been correctly designed.
Therefore the test conducted to test the CPU14 has been the Fibonacci sequence. It
can be seen in Figure 1.5.17 that the Fibonacci sequence is generated in the register file
correctly.
14
Even though the test was aimed to test the CPU, the complete MCU was simulated. Including
memories and peripherals.
77
5. Verification
Stepping to the test of the memory bus, the bubble sort algorithm allows to test that
the logic for interacting with the memory bus works adequately and that data can be
written into the bus and read from it. Figure 1.5.18 shows the result of the bubble sort
algorithm simulation. It is noteworthy to indicate that the bubble sort assembly code
has been modified to fit with the memory map.
Finally, it has been tested that peripheral registers are accessible from the CPU,
therefore thoroughly validating the memory bus. However, the peripherals themselves
have not been tested as they have been individually validated, as shown in Section 5.2.3.
Figures 1.5.19 to 1.5.23 show the validation process results.
The test code (Code snippet 1.5.6) writes into the registers and then reads the same
register to check. Then if the written value is both in the peripheral register and the
register file x4 register, both write and read operations were successful. For read-only
addresses, the read value is not determined by the write operation. In the PID case, it
can be observed that the output is generated by the control signal generated by the PID
78
5. Verification
controller. Also, in the ADC peripheral, it can be seen that the outputs are the values of
channels 1 and 2.
5 .bucle:
6 sw x3, 0(x1) # Write register
7 lw x4, 0(x1) # Read Register
8 addi x1, x1, 0x4 # Next address
9 addi x3, x3, 1 # Increase value
10 bne x1, x2, .bucle # while address != End address
11 nop
79
5. Verification
It can be observed in Figure 1.5.22 how modifying the registers affect the timer
peripheral, as it starts counting once the ”Start” (timer register 2) register has been
written. Also, the effect of the prescaler on the counter period can be seen.
5.2.3 Peripherals
The procedure followed to test the PID-Timer Link peripheral has been to input a
value (alternating positive and negative) through the PID input port and modify the
”Shift” register utilising the bus memory interface.
The expected results were that the timer compare outputs acquire the value of the
PID input shifted by the “Shift” register or zero depending on the input value sign. The
obtained results are shown in Figure 1.5.24.
80
5. Verification
81
5. Verification
5.2.3.3 Timer
The different features of the timer have been tested individually. Firstly, it has been
verified that the maximum count value limits the count value and that the end of the
count indicator is asserted. Figure 1.5.27 shows how the output is limited to 10 and that
the “IRQ” value is asserted at the end of the count.
The following feature verified is the prescaler module. For the verification process,
it was set to reduce the frequency by five. Therefore counter period was increased to
five clock cycles, as shown in Figure 1.5.28.
To check the proper functioning of the PWM outputs, they have been configured
to output in channel one a PWM and its complementary and in channel two just the
82
5. Verification
PWM signal, both channels with a duty cycle of 50% (Figure 1.5.29).
Finally, the compare bypass and the dead-time have been verified. The compare
bypassed values have been set to two and eight (channel one and channel two, re-
spectively) and the dead-time to one clock cycle. Figure 1.5.30 shows the result of the
verification.
83
5. Verification
The PID controller has been verified by comparing it with a golden model. The
golden model has been generated as a continuous PID controller in MATLAB Simulink.
With this model, the reference and the feedback are stored in a file and then are input to
the hardware simulation. Moreover, every controller signal is stored in files for further
analysis if necessary. Figure 1.5.31 shows the golden model simulation. Figure 1.5.32
compares the control action of the golden model and the hardware-implemented
controller.
10
-5
-10
-15
-20
0 0.5 1 1.5 2 2.5
#10 4
10
-5
-10
-15
-20
0 0.5 1 1.5 2 2.5
#10 4
84
5. Verification
Finally, it is necessary to verify that the control action is saturated correctly. The
testbench has been programmed to generate a control signal ranging from 20 to 0. Then
the saturation limits were set to 15 and 5. Thus, the output of the peripheral should
range within the saturation limits. The results are displayed in Figure 1.5.33.
To verify the ADC, it has been used the final implementation assembly code since
after the configuration of the peripherals, it performs a loop that updates the seven-
segment display with the value measured in the channel one of the ADC. It has been
tested with varying the voltage at the pin with a potenciometer and compared with a
voltmeter. Figure 1.5.34 shows the results for different input voltages.
(a) Input voltage 5 V (b) Input voltage 2.5 V (c) Input voltage 0 V
85
6. Conclusions
6 Conclusions
To conclude, it can be said that the project developed has achieved its objectives.
A RISC-V microcontroller has been successfully developed for the application in a
hyperloop prototype. However, due to the characteristics of the microcontroller, it
could be used for general control applications.
The project also accomplishes the requirements defined in Section 2 by being able
to perform the control without the necessity of employing the CPU for the operation.
It has also shown how programmable logic devices can replace general-purpose mi-
crocontrollers by demonstrating their versatility and adaptability capacities. Also, the
system can be escalated by replicating the hardware designed to achieve more control
outputs or generate more controllers connected to the same CPU.
Regarding the processor, there are multiple future lines to follow. Among them,
increase the instruction set architecture by adding commonly used extensions such as
the multiplication and division extension or float support. A significant improvement
to be developed is to implement the pipeline structure with hardware hazard handling.
On the control side, future lines could be developing a PID controller able to execute
multiple control loops by modifying the current registers for FIFO (first in, first out)
buffers. This would be possible because the clock frequency is greater than the operating
frequency of the control loops. For a clock frequency of 2 kHz, two control loops could
be executed at 1 kHz by updating the values of the first control loop at one clock cycle,
and then the following clock cycle would update the second control loop.
Lastly, about the hyperloop application, the current control is just a stage of the
whole levitation control. It would be attractive to develop a hardware-based control for
the complete levitation system.
86
Appendix A
87
0xC0000040 R/W Reference
0xC0000044 R/W K1
0xC0000048 R/W K2
0xC000004C R/W K3
0xC0000050 R/W Feedback
0xC0000054 R/W Clk prescaler
0xC0000058 R/W Status PID 2
0xC000005C R/W Clear
0xC0000060 R/W Bypass
0xC0000064 R/W Saturation
0xC0000068 R/W Upper saturation
0xC000006C R/W Lower saturation
0xC0000070 R Control
0xC0000074 Reserved
0xC0000078 R Channel 1
ADC
0xC000007C R Channel 2
0xC0000080 R/W Count
0xC0000084 R/W ARR
0xC0000088 R/W Start
0xC000008C R/W IRQ
0xC0000090 R/W Prescaler
Timer
0xC0000094 R/W Dead-time
0xC0000098 R/W Compare 1
0xC000009C R/W Compare 2
0xC00000A0 R/W Output Enable
0xC00000A4 R/W Bypass
0xC00000A8 R/W Value 7-Segment
0xC00000AC R/W Shift PID-Timer Link
88
Appendix B
The appendix shows the steps to configure the multiplier module employed in the PID
peripheral. The LPM MULT IP can be generated through the IP Catalog from Quartus.
Tools > IP Catalog > Library > Basic Functions > Arithmetic > LPM MULT
89
Figure 1.B.2: LPM MULT configuration screen 2
90
Figure 1.B.3: LPM MULT configuration screen 3
91
Figure 1.B.4: LPM MULT configuration screen 4
92
Figure 1.B.5: LPM MULT configuration screen 5
93
Appendix C
Pinout
94
hex2[5] Output PIN C22 hex5[1] Output PIN K20
hex2[4] Output PIN B21 hex5[0] Output PIN J20
hex2[3] Output PIN A21 pwm1out[1] Output PIN V10
hex2[2] Output PIN B19 pwm1out[0] Output PIN V9
hex2[1] Output PIN A20 pwm2out[1] Output PIN V8
hex2[0] Output PIN B20 pwm2out[0] Output PIN V7
hex3[6] Output PIN E17 rst Input PIN C10
hex3[5] Output PIN D19
95
Appendix D
Table 1.D.1: Degree to which the project relates to the Sustainable Development Goals
SDG 1. No poverty X
SDG 2. Zero hunger X
SDG 3. Good health and well-being X
SDG 4. Quality education X
SDG 5. Gender equality X
SDG 6. Clean water and sanitation X
SDG 7. Affordable and clean energy X
SDG 8. Decent work and economic growth X
SDG 9. Industry, innovation and infrastructure X
SDG 10. Reduced inequalities X
SDG 11. Sustainable cities and comunities X
SDG 12. Responsible consumption and production X
SDG 13. Climate action X
SDG 14. Life below water X
SDG 15. Life on land X
SDG 16. Peace, justice and strong institutions X
SDG 17. Partnerships for the goals X
96
The Sustainable Development Goals can be developed from two points of view,
the first would be the development of a microcontroller, and the second would be the
development related to hyperloop technology.
Regarding the microcontroller, the project is firmly related to goal nine, ”Industry,
Innovation and Infrastructure”, concretely to target 9.B. This target aims to support
domestic technology development. This project is based on an open-source architecture
(RISC-V). It demonstrates the capabilities this architecture offers as an open-source
standard to develop new technology without spending resources on proprietary archi-
tectures.
On the other side, hyperloop technology is related to goal 7 (”affordable and clean
energy”) by focusing on creating a means of transportation with the lowest energy
consumption possible. It is also associated with target 9.1, as the concept for the
hyperloop infrastructure is to build an auto-sufficient infrastructure that would be
powered by renewable energies.
97
Bibliography
Bibliography
Antmicro (2019). SystemVerilog Logotype.
Cerny, E., Dudani, S., Havlicek, J., and Korchemny, D. (2010). Introduction, pages 3–28.
Springer US, Boston, MA.
Harris, S. L. (2022). Digital design and computer architecture : RISC-V edition. Morgan
Kaufmann, Cambridge.
Koch, D., Ziener, D., and Hannig, F. (2016). FPGA Versus Software Programming: Why,
When, and How?, pages 1–21. Springer International Publishing, Cham.
Osier-Mixon, J. (2019). Semico Forecasts Strong Growth for RISC-V. RISC-V Foundation.
Waterman, A. and Asanović, K. (2019). The RISC-V Instruction Set Manual, volume
Volume I: Unprivileged ISA. RISC-V Foundation, 20191213 edition.
Xilinx® (n.d.). Xilinx Virtex UltraScale+ VU19P FPGA product brief. Accessed on
April 2, 2023.
98
Part II
1 Scope . . . . . . . . . . . . . . . . . . . . . . . 100
1. Scope
1 Scope
Due to the nature of the project, development of programmable logic, there are no plans
to be added to this document.
100
Part III
Written specifications
1 Scope . . . . . . . . . . . . . . . . . . . . . . . 102
2 Materials conditions . . . . . . . . . . . . . . 102
3 Test prior to commissioning . . . . . . . . . . 103
1. Scope
1 Scope
The written specifications document refers to the project “Development of a RISC-V
processor optimised for control applications to be used in the levitation system of
hyperloop”. And it contains the work developed on the development and verification
processes of the RISC-V processor. However, the work related to neither the electronics
nor the guiding and levitation units of the hyperloop prototype Auran is not included
in this document.
The selected programmable logic device chosen for this application is the FPGA
10M50DAF484C7G from the family Intel FPGA MAX10, implemented in the evaluation
board DE10-Lite from Terasic.
In the development of the project, it has only been considered the particularities of
the selected device. However, due to the availability of several alternatives for this de-
vice, the functioning of the work developed can only be assured in the aforementioned
device.
The objective of this document is to define the criteria and requirements for the
implementation of the project.
2 Materials conditions
In advance of the beginning of the project, it must be assured that the materials needed
for the correct development of the project are available and that the software is properly
installed and ready for operation.
2.2 Software
The software employed for the synthesis and programming of the FPGA is Quartus
Prime Lite 18.1. It can only be assured that the project will function with the indicated
102
3. Test prior to commissioning
For the development of the SystemVerilog code, any text editor able to work with
plain text or SystemVerilog files could be used, even though it is recommended to use a
software with SystemVerilog language support.
Finally, for the RISC-V assembly files development, any software able to assemble
the RISC-V assembly files supporting the RV32I ISA can be used. If the assembly code
is wanted to be modified, it is left to the user to ensure the correctness of the new code.
103
Part IV
Project schedule
1 Gantt diagram
Figure 4.1.1 represents the scheduling of the project.
r
be
be
be
y
y
ar
er
em
em
t
ar
em
us
ch
ru
ob
il
nu
ne
ay
pt
ov
g
ar
pr
Tasks
ec
b
ct
Au
Ju
Ja
M
Se
Fe
M
N
A
D
O
Documentation
Single-cycle
development
Single-cycle
verification
Multicycle CPU
development
Multicycle CPU
verification
Multicycle Peripherals
development
Multicycle Peripherals
verification
Report
105
Part V
Budget
1 Materials . . . . . . . . . . . . . . . . . . . . . 107
2 Labour . . . . . . . . . . . . . . . . . . . . . . 108
3 Proyect costs . . . . . . . . . . . . . . . . . . . 108
1. Materials
The budget document encompasses the expenses associated with the development
of the microcontroller for control applications. The budget is divided into materials
and labour costs.
1 Materials
Firstly, the unitary prices for the equipment not used exclusively for the project must
be calculated.
Subtotal: 290.64 €
107
2. Labour
2 Labour
Subtotal: 7,500.00 €
3 Proyect costs
Description Cost
The total cost of the project (including VAT) is: ELEVEN THOUSAND TWENTY- NINE
EUROS AND TWENTY- TWO CENTS (11,029.22 €)
1
Unitary labour cost considered as the mean salary of graduated engineer
108
Part VI
Development files
1 Development . . . . . . . . . . . . . . . . . . . 110
2 Verification . . . . . . . . . . . . . . . . . . . . 176
1. Development
This document contains the development and verification files of the different
modules explained throughout Part I. They are structured in the same way as in the
Memory part. Additionally, a common modules section has been added for those
shared between other modules.
1 Development
9 module Adder #(
10 parameter WIDTH = 32
11 ) (
12 input logic[WIDTH - 1:0] in1, in2,
13 output logic[WIDTH - 1:0] s
14 );
15
18 endmodule
110
1. Development
9 module FlipFlop #(
10 parameter WIDTH = 32
11 ) (
12 input logic clk, en, rst,
13 input logic[WIDTH - 1:0] d,
14 output logic[WIDTH - 1:0] q
15 );
16
22 endmodule
111
1. Development
9 module FlipFlop2 #(
10 parameter WIDTH = 32
11 ) (
12 input logic clk, en, srst, arst,
13 input logic[WIDTH - 1:0] d0, d1,
14 output logic[WIDTH - 1:0] q0, q1
15 );
16
112
1. Development
9 module fullAdder #(
10 parameter WIDTH = 32
11 ) (
12 input logic[WIDTH-1:0] operand1, operand2,
13 input logic cin,
14 output logic[WIDTH-1:0] result,
15 output logic cout
16 );
17
20 endmodule
9 module mux2 #(
10 parameter WIDTH = 32
11 ) (
12 input logic[WIDTH-1:0] data1, data2,
13 input logic s,
14 output logic[WIDTH-1:0] dout
15 );
16
19 endmodule
113
1. Development
9 module mux3 #(
10 parameter WIDTH = 32
11 ) (
12 input logic[WIDTH - 1:0] data0, data1, data2,
13 input logic[1:0] s,
14 output logic[WIDTH - 1:0] dout
15 );
16
17 always_comb begin
18 case (s)
19 2'b00: dout = data0;
20 2'b01: dout = data1;
21 2'b10: dout = data2;
22 default: dout = 'bx;
23 endcase
24 end
25
26 endmodule
114
1. Development
9 module mux4 #(
10 parameter WIDTH = 32
11 ) (
12 input logic[WIDTH - 1:0] data0, data1, data2, data3,
13 input logic[1:0] s,
14 output logic[WIDTH - 1:0] dout
15 );
16
17 always_comb begin
18 case (s)
19 2'b00: dout = data0;
20 2'b01: dout = data1;
21 2'b10: dout = data2;
22 2'b11: dout = data3;
23 default: dout = 'bx;
24 endcase
25 end
26
27 endmodule
115
1. Development
9 module mux6 #(
10 parameter WIDTH = 32
11 ) (
12 input logic[WIDTH - 1:0] data0, data1, data2, data3, data4, data5,
13 input logic[2:0] s,
14 output logic[WIDTH - 1:0] dout
15 );
16
17 always_comb begin
18 case (s)
19 3'b000: dout = data0;
20 3'b001: dout = data1;
21 3'b010: dout = data2;
22 3'b011: dout = data3;
23 3'b100: dout = data4;
24 3'b101: dout = data5;
25 default: dout = 'bx;
26 endcase
27 end
28
29 endmodule
116
1. Development
9 module mux7 #(
10 parameter WIDTH = 32
11 ) (
12 input logic[WIDTH - 1:0] data0, data1, data2, data3, data4, data5,
,→ data6,
13 input logic[2:0] s,
14 output logic[WIDTH - 1:0] dout
15 );
16
17 always_comb begin
18 case (s)
19 3'b000: dout = data0;
20 3'b001: dout = data1;
21 3'b010: dout = data2;
22 3'b011: dout = data3;
23 3'b100: dout = data4;
24 3'b101: dout = data5;
25 3'b110: dout = data6;
26 default: dout = 'bx;
27 endcase
28 end
29
30 endmodule
117
1. Development
9 module mux10 (
10 input logic[31:0] data[9:0],
11 input logic[3:0] s,
12 output logic[31:0] dout
13 );
14
15 always_comb begin
16 if(s < 10) begin
17 dout = data[s];
18 end
19 else begin
20 dout = 32'bx;
21 end
22 end
23 endmodule
118
1. Development
9 module Prescaler #(
10 parameter INITIAL_VALUE = 1
11 )
12 (
13 input logic clk, arst, en,
14 input logic[31:0] new_value,
15 output logic clk_en
16 );
17
40 endmodule
119
1. Development
9 module zeroExtender (
10 input logic inbit,
11 output logic[31:0] extended
12 );
13
16 endmodule
120
1. Development
1.2 Single-cycle
1.2.1 Datapath
9 module RegisterFile(
10 input logic clk, we, rst,
11 input logic[4:0] reg1, reg2, reg3,
12 input logic[31:0] dataIn,
13 output logic[31:0] regData1, regData2
14 );
15
16 logic[31:0] registers[31:0];
17
18 // Asynchronous read
19 assign regData1 = (reg1 != 0) ? registers[reg1] : 32'b0;
20 assign regData2 = (reg2 != 0) ? registers[reg2] : 32'b0;
21
22 // Synchronous write
23 always_ff @(posedge clk or negedge rst) begin
24 if (˜rst) begin
25 registers <= '{default: '0};
26 end
27 else if(we) begin // Write enable
28 registers[reg3] <= dataIn;
29 end
30 end
31
32 endmodule
121
1. Development
9 module ImmediateGenerator(
10 input logic[31:0] instruction,
11 output logic[31:0] immediate
12 );
13
14 always_comb begin
15 case (instruction[6:0]) // Opcode
16 7'b0000011, 7'b0010011, 7'b1100111: immediate = {{21{
,→ instruction[31]}}, instruction[30:20]}; // I type
17 7'b0100011: immediate = {{21{instruction[31]}}, instruction
,→ [30:25], instruction[11:7]}; // S type
18 7'b1100011: immediate = {{20{instruction[31]}}, instruction[7],
,→ instruction[30:25], instruction[11:8], 1'b0}; // B type
19 7'b0010111, 7'b0110111: immediate = {instruction[31:12], 12'b0
,→ }; // U type
20 7'b1101111: immediate = {{12{instruction[31]}}, instruction
,→ [19:12], instruction[20], instruction[30:21], 1'b0}; // J type
21 default: immediate = 32'dx;
22 endcase
23 end
24 endmodule
122
1. Development
9 module ALU(
10 input logic[31:0] operand1, operand2,
11 input logic[3:0] control,
12 output logic[31:0] result,
13 output logic[3:0] flags
14 );
15
16 logic[31:0] muxedOperand2, sum, ored, anded, xored, slt, sll, srl, sra,
,→ sltu;
17 logic cout, sltnonext, sltunonext;
18
35 // SLT implementation
36 assign sltnonext = sum[31] ˆ flags[3];
37 zeroExtender zero(sltnonext, slt);
38
39 // SLTU implementation
40 assign sltunonext = ˜flags[2];
41 zeroExtender zero_u(sltunonext, sltu);
42
123
1. Development
43 // Flags
44 // Flags = {Overflow, Carry, Negative, Zero}
45
52 // Result
53 mux10 resultmux('{sltu, sra, srl, sll, slt, xored, ored, anded, sum, sum
,→ }, control, result);
54
55 endmodule
124
1. Development
9 module RAM #(
10 parameter DEPTH = 1024,
11 parameter WORD_SIZE = 32
12 )(
13 input logic clk, we,
14 input logic[$clog2(DEPTH)-1:0] addr,
15 input logic[WORD_SIZE-1:0] dataIn,
16 output logic[WORD_SIZE-1:0] dataOut
17 );
18 logic[WORD_SIZE-1:0] ram[DEPTH-1:0];
19
28 endmodule
125
1. Development
9 module ROM #(
10 parameter DEPTH = 1024
11 )
12 (
13 input logic[31:0] pc,
14 output logic[31:0] instruction
15 );
16
17 logic[31:0] memory[DEPTH-1:0];
18
19 initial begin
20 $readmemh("test_instructions.txt", memory);
21 end
22
25 endmodule
126
1. Development
9 module Control(
10 input logic[6:0] opCode,
11 output logic branch, forceJump, RAMwe, Regwe, ALUSrc2,
12 output logic[1:0] ALUOp, ALUSrc1, RegWriteSrc
13 );
14
15 always_comb begin
16 case (opCode)
17 7'd3: begin
18 branch = 1'b0;
19 forceJump = 1'b0;
20 RAMwe = 1'b0;
21 Regwe = 1'b1;
22 ALUSrc2 = 1'b0;
23 ALUOp = 2'b10;
24 ALUSrc1 = 2'b00;
25 RegWriteSrc = 2'b00;
26 end
27 7'd19: begin
28 branch = 1'b0;
29 forceJump = 1'b0;
30 RAMwe = 1'b0;
31 Regwe = 1'b1;
32 ALUSrc2 = 1'b0;
33 ALUOp = 2'b00;
34 ALUSrc1 = 2'b00;
35 RegWriteSrc = 2'b01;
36 end
37 7'd23: begin
38 branch = 1'b0;
39 forceJump = 1'b0;
40 RAMwe = 1'b0;
41 Regwe = 1'b1;
127
1. Development
42 ALUSrc2 = 1'b0;
43 ALUOp = 2'b10;
44 ALUSrc1 = 2'b10;
45 RegWriteSrc = 2'b01;
46 end
47 7'd35: begin
48 branch = 1'b0;
49 forceJump = 1'b0;
50 RAMwe = 1'b1;
51 Regwe = 1'b0;
52 ALUSrc2 = 1'b0;
53 ALUOp = 2'b10;
54 ALUSrc1 = 2'b00;
55 RegWriteSrc = 2'b01;
56 end
57 7'd51: begin
58 branch = 1'b0;
59 forceJump = 1'b0;
60 RAMwe = 1'b0;
61 Regwe = 1'b1;
62 ALUSrc2 = 1'b1;
63 ALUOp = 2'b00;
64 ALUSrc1 = 2'b00;
65 RegWriteSrc = 2'b01;
66 end
67 7'd55: begin
68 branch = 1'b0;
69 forceJump = 1'b0;
70 RAMwe = 1'b0;
71 Regwe = 1'b1;
72 ALUSrc2 = 1'b0;
73 ALUOp = 2'b10;
74 ALUSrc1 = 2'b01;
75 RegWriteSrc = 2'b01;
76 end
77 7'd99: begin
78 branch = 1'b1;
79 forceJump = 1'b0;
80 RAMwe = 1'b0;
81 Regwe = 1'b0;
82 ALUSrc2 = 1'b1;
83 ALUOp = 2'b01;
84 ALUSrc1 = 2'b00;
85 RegWriteSrc = 2'b01;
86 end
87 7'd103: begin
128
1. Development
88 branch = 1'b0;
89 forceJump = 1'b1;
90 RAMwe = 1'b0;
91 Regwe = 1'b1;
92 ALUSrc2 = 1'b0;
93 ALUOp = 2'b10;
94 ALUSrc1 = 2'b00;
95 RegWriteSrc = 2'b10;
96 end
97 7'd111: begin
98 branch = 1'b0;
99 forceJump = 1'b1;
100 RAMwe = 1'b0;
101 Regwe = 1'b1;
102 ALUSrc2 = 1'b0;
103 ALUOp = 2'b10;
104 ALUSrc1 = 2'b00;
105 RegWriteSrc = 2'b10;
106 end
107 default: begin
108 branch = 1'bx;
109 forceJump = 1'bx;
110 RAMwe = 1'bx;
111 Regwe = 1'bx;
112 ALUSrc2 = 1'bx;
113 ALUOp = 2'bx;
114 ALUSrc1 = 2'bx;
115 RegWriteSrc = 2'bx;
116 end
117 endcase
118 end
119 endmodule
129
1. Development
9 module BranchLogic(
10 input logic branch, forceJump, opCode_3,
11 input logic[2:0] funct3,
12 input logic[3:0] flags,
13 output logic[1:0] PCSrc
14 );
15
16 always_comb begin
17 if(forceJump) begin
18 if(opCode_3) PCSrc = 2'b01;
19 else PCSrc = 2'b10;
20 end
21
130
1. Development
9 module ALUControl(
10 input logic[1:0] ALUOp,
11 input logic[4:0] func, // {Funct3, Funct7_5, OP_5}
12 output logic[3:0] ALUCtrl
13 );
14
15 always_comb begin
16 case (ALUOp)
17 2'b00: begin
18 casez (func[4:1])
19 4'b000?: begin
20 if(func[0] === 1) begin
21 if(func[1] === 0) ALUCtrl = 4'd0; // add
22 else ALUCtrl = 4'd1; // sub
23 end
24 else ALUCtrl = 4'd0; // addi
25 end
26 4'b0010: ALUCtrl = 4'd6; // sll, slli
27 4'b010?: ALUCtrl = 4'd5; // slt, slti
28 4'b011?: ALUCtrl = 4'd9; // sltu, sltiu
29 4'b100?: ALUCtrl = 4'd4; // xor, xori
30 4'b1010: ALUCtrl = 4'd7; // srl, srli
31 4'b1011: ALUCtrl = 4'd8; // sra, srai
32 4'b110?: ALUCtrl = 4'd3; // or, ori
33 4'b111?: ALUCtrl = 4'd2; // and, andi
34 default: ALUCtrl = 4'bx;
35 endcase
36 end
37 2'b01: ALUCtrl = 4'd1;
38 2'b10: ALUCtrl = 4'd0;
39 default: ALUCtrl = 4'bx;
40 endcase
41 end
42 endmodule
131
1. Development
9 module RV32I_SC #(
10 parameter ROM_SIZE = 64,
11 parameter RAM_DEPTH = 64,
12 parameter WORD_SIZE = 32
13 )
14 (
15 input logic clk, en, rst,
16 input logic[WORD_SIZE - 1:0] ramOut,
17 input logic[31:0] instruction,
18 output logic RAMwe,
19 output logic[$clog2(RAM_DEPTH)-1:0] RAMaddr,
20 output logic[WORD_SIZE - 1:0] rs2,
21 output logic[31:0] pc
22 );
23
24 // Signal definition
25 logic Regwe, branch, forceJump, ALUSrc2;
26 logic[1:0] ALUOp, ALUSrc1, RegWriteSrc, PCSrc;
27 logic[3:0] ALUCtrl, flags;
28 logic[31:0] rd, rs1, ALUIn1, ALUIn2, ALUOut, immediate, pcAdded,
29 pcImmediate, nextPC;
30
132
1. Development
44 );
45
46 ALU RV_ALU (
47 .operand1(ALUIn1),
48 .operand2(ALUIn2),
49 .control(ALUCtrl),
50 .result(ALUOut),
51 .flags(flags)
52 );
53
54 ImmediateGenerator RV_ImmGenerator(
55 .instruction(instruction),
56 .immediate(immediate)
57 );
58
72 BranchLogic RV_BranchLogic(
73 .branch(branch),
74 .forceJump(forceJump),
75 .opCode_3(instruction[3]),
76 .funct3(instruction[14:12]),
77 .flags(flags),
78 .PCSrc(PCSrc)
79 );
80
81 ALUControl RV_ALUControl(
82 .ALUOp(ALUOp),
83 .func({instruction[14:12], instruction[30], instruction[5]}),
84 .ALUCtrl(ALUCtrl)
85 );
86
133
1. Development
90 .in2(pc),
91 .s(pcAdded)
92 );
93
94 Adder RV_PCImmediateAdder(
95 .in1(pc),
96 .in2(immediate),
97 .s(pcImmediate)
98 );
99
134
1. Development
136 .q(pc)
137 );
138
139 endmodule
135
1. Development
1.3 Multicycle
1.3.1 Datapath
1.3.1.1 Memory
9 module Memory #(
10 parameter DEPTH = 10,
11 parameter WORD_SIZE = 32,
12 parameter INITIALIZE = 0,
13 parameter FILE = "Memory.txt"
14 )(
15 input logic clk, we, read, cs,
16 input logic[$clog2(DEPTH)-1:0] addr,
17 input logic[WORD_SIZE-1:0] dataIn,
18 output logic[WORD_SIZE-1:0] dataOut
19 );
20 logic[WORD_SIZE-1:0] memory[DEPTH-1:0];
21
22 initial begin
23 if(INITIALIZE == 1'b1) begin
24 $readmemh(FILE, memory);
25 end
26 end
27
136
1. Development
9 module Control(
10 input logic clk, arst, en, branch,
11 input logic[6:0] opCode,
12 output logic PCRegEN, FetchRegEN, RFRegEN, ALUOutRegEN, DataRegEN,
13 PCRegRST, FetchRegRST, RFRegRST,
14 ALUOutRegRST, memWE, Regwe, InstructionRead,
15 output logic[1:0] ALUIn1Src, ALUIn2Src, ResultSrc, ALUOp
16 );
17
25 // State register
26 always_ff @(posedge clk or negedge arst) begin
27 if(˜arst) state <= Fetch;
28 else if(en) state <= nextstate;
29 end
30
137
1. Development
69 // Output logic
70 always_comb begin
71 case(state)
72 Fetch: begin
73 PCRegEN = 1'b1;
74 FetchRegEN = 1'b1;
75 InstructionRead = 1'b1;
76 RFRegEN = 1'b0;
77 ALUOutRegEN = 1'b0;
78 DataRegEN = 1'b0;
79
80 PCRegRST = 1'b1;
81 FetchRegRST = 1'b1;
82 RFRegRST = 1'b1;
83 ALUOutRegRST = 1'b1;
84
85 ALUIn1Src = 2'b11;
86 ALUIn2Src = 2'b10;
87 ResultSrc = 2'b01;
88
89 memWE = 1'b0;
138
1. Development
90 Regwe = 1'b0;
91
92 ALUOp = 2'b10;
93 end
94
95 Decode: begin
96 PCRegEN = 1'b0;
97 FetchRegEN = 1'b0;
98 InstructionRead = 1'b0;
99 RFRegEN = 1'b1;
100 ALUOutRegEN = 1'b0;
101 DataRegEN = 1'b0;
102
139
1. Development
140
1. Development
141
1. Development
142
1. Development
143
1. Development
144
1. Development
145
1. Development
440 endcase
441 end
442
443 endmodule
146
1. Development
9 module BranchLogic(
10 input logic[2:0] funct3,
11 input logic[3:0] flags,
12 output logic branch
13 );
14
15 always_comb begin
16 case (funct3)
17 3'b000: branch = flags[0]; // beq
18 3'b001: branch = ˜flags[0]; // bne
19 3'b100: branch = flags[1] ˆ flags[3]; // blt
20 3'b101: branch = ˜(flags[1] ˆ flags[3]); // bge
21 3'b110: branch = ˜flags[2]; // bltu
22 3'b111: branch = flags[2]; // bgeu
23 default: branch = 0;
24 endcase
25 end
26 endmodule
147
1. Development
9 module RV32I_Multicycle(
10 input logic clk, arst, en,
11 input logic[31:0] data, instruction,
12 output memWE, InstructionRead, DataRegEN,
13 output[31:0] memAddr, memIn, pcOut
14 );
15 // Signal definition
16 logic RegWE, PCRegEN, FetchRegEN, RFRegEN, ALUOutRegEN, PCRegRST,
17 FetchRegRST, RFRegRST, ALUOutRegRST, branch;
18 logic[1:0] ALUIn1Src, ALUIn2Src, ResultSrc, ALUOp;
19 logic[3:0] ALUCtrl, flags;
20 logic[31:0] rs1, rs2, ALUIn1, ALUIn2, ALUOut,
21 pc, pcOld, rs1Reg, rs2Reg, ALUOutReg, result, immediate;
22
41 ALU RV_ALU(
42 .operand1(ALUIn1),
43 .operand2(ALUIn2),
148
1. Development
44 .control(ALUCtrl),
45 .result(ALUOut),
46 .flags(flags)
47 );
48
49 ImmediateGenerator RV_ImmediateGenerator(
50 .instruction(instruction),
51 .immediate(immediate)
52 );
53
79 ALUControl RV_ALUControl(
80 .ALUOp(ALUOp),
81 .func({instruction[14:12], instruction[30], instruction[5]}),
82 .ALUCtrl(ALUCtrl)
83 );
84
85 BranchLogic RV_BranchLogic(
86 .funct3(instruction[14:12]),
87 .flags(flags),
88 .branch(branch)
89 );
149
1. Development
90
91 // Multiplexers
92
93 mux4 ALUIn1Mux(
94 .data0(rs1Reg),
95 .data1(32'b0),
96 .data2(pcOld),
97 .data3(pc),
98 .s(ALUIn1Src),
99 .dout(ALUIn1)
100 );
101
118 // Registers
119
150
1. Development
136 );
137
158 endmodule
151
1. Development
9 module MemoryController #(
10 parameter RAM_ADDR = 32'h1000_0000,
11 parameter PID1_ADDR = 32'hC000_0000,
12 parameter PID2_ADDR = 32'hC000_0040,
13 parameter ADC_ADDR = 32'hC000_0078,
14 parameter TIM_ADDR = 32'hC000_0080,
15 parameter H7S_ADDR = 32'hC000_00A8,
16 parameter PTL_ADDR = 32'hC000_00AC
17 )(
18 input logic read, write,
19 input logic[31:0] addr,
20 output logic cs_RAM, cs_PID1, cs_PID2, cs_ADC, cs_TIM, cs_H7S, cs_PTL,
21 output logic[2:0] read_mux
22 );
23
24 always_comb begin
25 if((addr >= RAM_ADDR) && (addr < PID1_ADDR) && (read || write))
,→ begin
26 cs_RAM = 1'b1;
27 cs_PID1 = 1'b0;
28 cs_PID2 = 1'b0;
29 cs_ADC = 1'b0;
30 cs_TIM = 1'b0;
31 cs_H7S = 1'b0;
32 cs_PTL = 1'b0;
33 end
34 else if ((addr >= PID1_ADDR) && (addr < PID2_ADDR) && (read ||
,→ write)) begin
35 cs_RAM = 1'b0;
36 cs_PID1 = 1'b1;
37 cs_PID2 = 1'b0;
38 cs_ADC = 1'b0;
39 cs_TIM = 1'b0;
152
1. Development
40 cs_H7S = 1'b0;
41 cs_PTL = 1'b0;
42 end
43 else if ((addr >= PID2_ADDR) && (addr < ADC_ADDR) && (read || write
,→ )) begin
44 cs_RAM = 1'b0;
45 cs_PID1 = 1'b0;
46 cs_PID2 = 1'b1;
47 cs_ADC = 1'b0;
48 cs_TIM = 1'b0;
49 cs_H7S = 1'b0;
50 cs_PTL = 1'b0;
51 end
52 else if ((addr >= ADC_ADDR) && (addr < TIM_ADDR) && (read || write)
,→ ) begin
53 cs_RAM = 1'b0;
54 cs_PID1 = 1'b0;
55 cs_PID2 = 1'b0;
56 cs_ADC = 1'b1;
57 cs_TIM = 1'b0;
58 cs_H7S = 1'b0;
59 cs_PTL = 1'b0;
60 end
61 else if ((addr >= TIM_ADDR) && (addr < H7S_ADDR) && (read || write)
,→ ) begin
62 cs_RAM = 1'b0;
63 cs_PID1 = 1'b0;
64 cs_PID2 = 1'b0;
65 cs_ADC = 1'b0;
66 cs_TIM = 1'b1;
67 cs_H7S = 1'b0;
68 cs_PTL = 1'b0;
69 end
70 else if (addr == H7S_ADDR && (read || write)) begin
71 cs_RAM = 1'b0;
72 cs_PID1 = 1'b0;
73 cs_PID2 = 1'b0;
74 cs_ADC = 1'b0;
75 cs_TIM = 1'b0;
76 cs_H7S = 1'b1;
77 cs_PTL = 1'b0;
78 end
79 else if (addr == PTL_ADDR && (read || write)) begin
80 cs_RAM = 1'b0;
81 cs_PID1 = 1'b0;
82 cs_PID2 = 1'b0;
153
1. Development
83 cs_ADC = 1'b0;
84 cs_TIM = 1'b0;
85 cs_H7S = 1'b0;
86 cs_PTL = 1'b1;
87 end
88 else begin
89 cs_RAM = 1'b0;
90 cs_PID1 = 1'b0;
91 cs_PID2 = 1'b0;
92 cs_ADC = 1'b0;
93 cs_TIM = 1'b0;
94 cs_H7S = 1'b0;
95 cs_PTL = 1'b0;
96 end
97 end
98
99 always_comb begin
100 if((addr >= RAM_ADDR) && (addr < PID1_ADDR)) begin
101 read_mux = 3'b000;
102 end
103 else if ((addr >= PID1_ADDR) && (addr < PID2_ADDR)) begin
104 read_mux = 3'b001;
105 end
106 else if ((addr >= PID2_ADDR) && (addr < ADC_ADDR)) begin
107 read_mux = 3'b010;
108 end
109 else if ((addr >= ADC_ADDR) && (addr < TIM_ADDR)) begin
110 read_mux = 3'b011;
111 end
112 else if ((addr >= TIM_ADDR) && (addr < H7S_ADDR)) begin
113 read_mux = 3'b100;
114 end
115 else if (addr == H7S_ADDR) begin
116 read_mux = 3'b101;
117 end
118 else if (addr == PTL_ADDR) begin
119 read_mux = 3'b110;
120 end
121 else begin
122 read_mux = 3'b000;
123 end
124 end
125 endmodule
154
1. Development
9 module PID_TIM_Link (
10 input clk, chipSelect, write, read, rst, en,
11 input logic[31:0] pid_in,
12 input logic[31:0] writeData,
13 output logic[31:0] timOut1, timOut2,
14 output logic[31:0] readData
15 );
16
36 always_comb begin
37 if($signed(pid_in) >= $signed(32'd0)) begin
38 timOut1 = compare;
39 timOut2 = 32'd0;
40 end
41 else begin
42 timOut1 = 32'd0;
43 timOut2 = -compare;
155
1. Development
44 end
45 end
46
47 endmodule
156
1. Development
9 module Hex7Segments (
10 input logic clk, chipSelect, write, read, rst,
11 input logic[31:0] writeData,
12 output logic[6:0] hex5, hex4, hex3, hex2, hex1, hex0,
13 output logic[31:0] readData
14 );
15 logic[31:0] register;
16
157
1. Development
44 .inhex(register[11:8]),
45 .out7seg(hex2)
46 );
47
63 endmodule
64
65 module hex7seg #(
66 parameter INVERT = 0
67 )(
68 input [3:0] inhex,
69 output [6:0] out7seg
70 );
71
74 always_comb begin
75 case (inhex)
76 4'h0: out7seg_aux = 7'h3F;
77 4'h1: out7seg_aux = 7'h06;
78 4'h2: out7seg_aux = 7'h5B;
79 4'h3: out7seg_aux = 7'h4F;
80 4'h4: out7seg_aux = 7'h66;
81 4'h5: out7seg_aux = 7'h6D;
82 4'h6: out7seg_aux = 7'h7D;
83 4'h7: out7seg_aux = 7'h07;
84 4'h8: out7seg_aux = 7'h7F;
85 4'h9: out7seg_aux = 7'h67;
86 4'hA: out7seg_aux = 7'h77;
87 4'hB: out7seg_aux = 7'h7C;
88 4'hC: out7seg_aux = 7'h39;
89 4'hD: out7seg_aux = 7'h5E;
158
1. Development
98 endmodule
159
1. Development
9 module ADC_reg (
10 input logic clk, chipSelect, read, rst, en,
11 input logic addr,
12 output logic[31:0] channel0, channel1, readData
13 );
14
37 ADC_block IP_block(
38 .rst(rst),
39 .clk(clk),
40 .CH0(ch0),
41 .CH1(ch1)
42 );
43 endmodule
160
1. Development
1.3.4.5 Timer
9 module PWMGenerator(
10 input logic[7:0] value,
11 input logic[31:0] compare, maxval, count,
12 output logic pwm, pwmn
13 );
14 logic[9:0] deadtime;
15
16 always_comb begin
17 if (value <= 8'd127) begin
18 deadtime = value;
19 end
20 else if (value <= 8'd191) begin
21 deadtime = (64 + value[5:0]) * 2;
22 end
23 else if (value <= 8'd223) begin
24 deadtime = (32 + value[4:0]) * 8;
25 end
26 else begin
27 deadtime = (32 + value[4:0]) * 16;
28 end
29 if(count < compare) begin
30 pwm = 1'b1;
31 end
32 else begin
33 pwm = 1'b0;
34 end
35 if(count >= (compare + deadtime) && count < (maxval - deadtime))
,→ begin
36 pwmn = 1'b1;
37 end
38 else begin
39 pwmn = 1'b0;
40 end
41 end
42 endmodule
161
1. Development
9 module Timer #(
10 parameter PRESCALER_DEFAULT_VALUE = 1
11 ) (
12 input logic clk, chipSelect, write, read, rst, en,
13 input logic[4:0] addr,
14 input logic[31:0] writeData, bypass1, bypass2,
15 output logic[1:0] pwm1out, pwm2out,
16 output logic[31:0] readData
17 );
18
162
1. Development
46 if(!rst) begin
47 registers <= '{default: '0};
48 end
49 else if (en) begin
50
51 if(chipSelect) begin
52 if (˜(write && (addr == 4'b0))) begin
53 registers[0] <= tim_en ? (registers[0] + 1) : registers
,→ [0];
54 end
55
56 if (write) begin
57 registers[addr] <= writeData;
58 end
59 else if (read) begin
60 readData <= registers[addr];
61 end
62 end
63 else begin
64 registers[0] <= tim_en ? (registers[0] + 1) : registers[0];
65 end
66 if(registers[0] >= (registers[1] - 1) && tim_en == 1) begin
67 registers[0] <= '0;
68 registers[3] <= 32'h0000_0001;
69 end
70 end
71 end
72
73 Prescaler #(
74 .INITIAL_VALUE(PRESCALER_DEFAULT_VALUE)
75 ) prescaler (
76 .clk(clk),
77 .arst(rst),
78 .en(en),
79 .new_value(registers[4]),
80 .clk_en(clk_en)
81 );
82
83 PWMGenerator PWMGen1 (
84 .value(registers[5][7:0]),
85 .count(registers[0]),
86 .compare(compare1),
87 .maxval(registers[1]),
88 .pwm(pwm1),
89 .pwmn(pwmn1)
90 );
163
1. Development
91
92 PWMGenerator PWMGen2 (
93 .value(registers[5][7:0]),
94 .count(registers[0]),
95 .compare(compare2),
96 .maxval(registers[1]),
97 .pwm(pwm2),
98 .pwmn(pwmn2)
99 );
100
101 endmodule
164
1. Development
9 module PID (
10 input clk, en, arst, srst,
11 input logic[31:0] reference, feedback, k1, k2, k3,
12 output logic[31:0] control
13 );
14
21 FlipFlop delay1(
22 .clk(clk),
23 .en(en),
24 .srst(srst),
25 .arst(arst),
26 .d(error),
27 .q(error1)
28 );
29
30 FlipFlop delay2(
31 .clk(clk),
32 .en(en),
33 .srst(srst),
34 .arst(arst),
35 .d(error1),
36 .q(error2)
37 );
38
39 FlipFlop delayCtrl(
40 .clk(clk),
41 .en(en),
42 .srst(srst),
165
1. Development
43 .arst(arst),
44 .d(control),
45 .q(control1)
46 );
47
48 FlipFlop feedback_ff(
49 .clk(clk),
50 .en(en),
51 .srst(srst),
52 .arst(arst),
53 .d(feedback),
54 .q(feedback_d)
55 );
56
57 Multiplier_test mult_1(
58 .dataa(k1),
59 .datab(error),
60 .result(multiplied1)
61 );
62
63 Multiplier_test mult_2(
64 .dataa(k2),
65 .datab(error1),
66 .result(multiplied2)
67 );
68
69 Multiplier_test mult_3(
70 .dataa(k3),
71 .datab(error2),
72 .result(multiplied3)
73 );
74
75 endmodule
166
1. Development
9 module PID_Reg #(
10 parameter PRESCALER_DEFAULT_VALUE = 1
11 ) (
12 input logic clk, chipSelect, write, read, rst, en,
13 input logic [3:0] addr,
14 input logic [31:0] writeData, feedback_bypass,
15 output logic [31:0] readData, control_bypass
16 );
17
42 always_comb begin
43 if(control_reg > registers[10]) begin
44 control_sat = registers[10];
45 end
167
1. Development
82 PID pid(
83 .clk(clk),
84 .en(pid_en),
85 .arst(rst),
86 .srst(˜registers[7][0]),
87 .reference(registers[0]),
88 .feedback(feedback),
89 .k1(registers[1]),
90 .k2(registers[2]),
91 .k3(registers[3]),
168
1. Development
92 .control(control_reg)
93 );
94
95 Prescaler #(
96 .INITIAL_VALUE(PRESCALER_DEFAULT_VALUE)
97 ) prescaler (
98 .clk(clk),
99 .arst(rst),
100 .en(en),
101 .new_value(registers[5]),
102 .clk_en(clk_en)
103 );
104
105 endmodule
169
1. Development
9 module RV32I_MC #(
10 parameter DEPTH = 64,
11 parameter PROGRAM_FILE = "memotest.hex"
12 )(
13 input logic clk, rst, en,
14 output logic[1:0] pwm1out, pwm2out,
15 output logic[6:0] hex0, hex1, hex2, hex3, hex4, hex5
16 );
17
18 // Signals definition
19 logic memWE, InstructionRead, DataRegEN,
20 cs_RAM, cs_PID1, cs_PID2, cs_ADC, cs_TIM, cs_H7S, cs_PTL;
21 logic[2:0] read_mux_src;
22 logic[31:0] memOut, memAddr, memIn, instruction, pcOut,
23 RAMOut, PID1Out, PID2Out, ADCOut, TIMOut, H7SOut, PTLOut,
,→ ADCChannel0,
24 ADCChannel1, pid_in, timOut1, timOut2, nc;
25
41 MemoryController RV_MemoryController (
42 .read(DataRegEN),
170
1. Development
43 .write(memWE),
44 .addr(memAddr),
45 .cs_RAM(cs_RAM),
46 .cs_PID1(cs_PID1),
47 .cs_PID2(cs_PID2),
48 .cs_ADC(cs_ADC),
49 .cs_TIM(cs_TIM),
50 .cs_H7S(cs_H7S),
51 .cs_PTL(cs_PTL),
52 .read_mux(read_mux_src)
53 );
54
55 mux7 RV_ReadMux (
56 .data0(RAMOut),
57 .data1(PID1Out),
58 .data2(PID2Out),
59 .data3(ADCOut),
60 .data4(TIMOut),
61 .data5(H7SOut),
62 .data6(PTLOut),
63 .s(read_mux_src),
64 .dout(memOut)
65 );
66
67 Memory #(
68 .DEPTH(DEPTH),
69 .WORD_SIZE(32),
70 .INITIALIZE(1),
71 .FILE(PROGRAM_FILE)
72 ) RV_ROM (
73 .clk(clk),
74 .we(1'b0),
75 .read(InstructionRead),
76 .cs(1'b1),
77 .addr(pcOut[$clog2(DEPTH)+1:2]),
78 .dataIn(0),
79 .dataOut(instruction)
80 );
81
82 Memory #(
83 .DEPTH(DEPTH),
84 .WORD_SIZE(32),
85 .INITIALIZE(0),
86 .FILE("")
87 ) RV_RAM (
88 .clk(clk),
171
1. Development
89 .we(memWE),
90 .read(DataRegEN),
91 .cs(cs_RAM),
92 .addr(memAddr[$clog2(DEPTH)+1:2]),
93 .dataIn(memIn),
94 .dataOut(RAMOut)
95 );
96
97 PID_Reg #(
98 .PRESCALER_DEFAULT_VALUE(50000)
99 ) RV_pid1 (
100 .clk(clk),
101 .chipSelect(cs_PID1),
102 .write(memWE),
103 .read(DataRegEN),
104 .rst(rst),
105 .en(en),
106 .addr(memAddr[5:2]),
107 .writeData(memIn),
108 .feedback_bypass(ADCChannel0),
109 .readData(PID1Out),
110 .control_bypass(pid_in)
111 );
112
113 PID_Reg #(
114 .PRESCALER_DEFAULT_VALUE(50000)
115 ) RV_pid2 (
116 .clk(clk),
117 .chipSelect(cs_PID2),
118 .write(memWE),
119 .read(DataRegEN),
120 .rst(rst),
121 .en(en),
122 .addr(memAddr[5:2]),
123 .writeData(memIn),
124 .feedback_bypass(ADCChannel1),
125 .readData(PID2Out),
126 .control_bypass(nc)
127 );
128
172
1. Development
135 .addr(memAddr[2]),
136 .channel0(ADCChannel0),
137 .channel1(ADCChannel1),
138 .readData(ADCOut)
139 );
140
141 Timer #(
142 .PRESCALER_DEFAULT_VALUE(1)
143 ) RV_Timer (
144 .clk(clk),
145 .chipSelect(cs_TIM),
146 .write(memWE),
147 .read(DataRegEN),
148 .rst(rst),
149 .en(en),
150 .addr(memAddr[5:2]),
151 .writeData(memIn),
152 .bypass1(timOut1),
153 .bypass2(timOut2),
154 .pwm1out(pwm1out),
155 .pwm2out(pwm2out),
156 .readData(TIMOut)
157 );
158
173
1. Development
181 .pid_in(pid_in),
182 .writeData(memIn),
183 .timOut1(timOut1),
184 .timOut2(timOut2),
185 .readData(PTLOut)
186 );
187
188 endmodule
174
1. Development
7 # PID 1 Config
8 li x4, 1433
9 sw x4, 0x00(x1) # Set reference to 5 A
10 li x4, 1024
11 sw x4, 0x04(x1) # Set K1 to 1024
12 li x4, -1004
13 sw x4, 0x08(x1) # Set K2 to -1004
14 li x4, 50001
15 sw x4, 0x14(x1) # Set PID clk to 1kHz
16 sw x5, 0x20(x1) # Activate bypass
17 li x4, 1179648
18 sw x4, 0x28(x1) # Upper saturation
19 li x4, -1179648
20 sw x4, 0x2C(x1) # Lower saturation
21
22 # Timer Config
23 li x4, 4096
24 sw x4, 0x04(x2) # Set ARR
25 li x4, 10
26 sw x4, 0x14(x2) # Set Dead time
27 li x4, 0xF
28 sw x4, 0x20(x2) # Output enable
29 sw x5, 0x24(x2) # Activate bypass
30
35 # Start peripherals
36 sw x5, 0x18(x1) # Start PID 1
37 sw x5, 0x08(x2) # Start Timer
38
39 .loop:
40 lw x6, 0x78(x1)
41 sw x6, 0x00(x7) # Display ADC value
42 j .loop
175
2. Verification
2 Verification
2.1 Single-cycle
2.1.1 Datapath
9 module tb_RegisterFile();
10 logic clk, we, rst, testEN;
11 logic[4:0] reg1, reg2, reg3;
12 logic[31:0] dataIn, regData1, regData2;
13 logic[31:0] expected_regData1, expected_regData2;
14 logic[7:0] testIndex, errors;
15 logic[36:0] testvector1[25:0];
16 logic[36:0] testvector2[25:0];
17 logic[37:0] testvector3[25:0];
18
22 // Generate Clock
23 always begin
24 clk = 1; #5;
25 clk = 0; #5;
26 end
27
28 // Initialization
29 initial begin
30 $display("Register File Testbench");
31 $readmemh("vector_RF1.txt", testvector1);
32 $readmemh("vector_RF2.txt", testvector2);
33 $readmemh("vector_RF3.txt", testvector3);
34 testIndex = 0; errors = 0;
35 testEN = 1; rst = 0; #7; testEN = 0; rst = 1;
176
2. Verification
36 #60; rst = 0;
37 end
38
47 // Check results
48 always @(negedge clk) begin
49 if(˜testEN) begin
50 if(regData1 !== expected_regData1) begin
51 $display("Error in Register 1 (expecting %h).",
,→ expected_regData1);
52 errors = errors + 1;
53 end
54
60 testIndex = testIndex + 1;
61
177
2. Verification
178
2. Verification
9 module tb_ImmediateGenerator();
10
11 logic clk;
12 logic[31:0] instruction, immediate, expected_imm;
13 logic[7:0] testIndex, errors;
14 logic[63:0] testvector[25:0];
15
19 // Generate clock
20 always begin
21 clk = 1; #5;
22 clk = 0; #5;
23 end
24
25 // Initialization
26 initial begin
27 $display("Immediate Generator Testbench");
28 $readmemh("vector_ImmediateGenerator.txt", testvector); // Load
,→ test vector
29 testIndex = 0; errors = 0;
30 end
31
37 // Check results
38 always @(negedge clk) begin
39 if(immediate !== expected_imm) begin
40 $display("Error: instruction = %h (Op = %d)", instruction,
,→ instruction[6:0]);
179
2. Verification
45 testIndex = testIndex + 1;
46
53 endmodule
180
2. Verification
9 module tb_ALU();
10 logic[31:0] operand1, operand2, result, expected_result;
11 logic[3:0] control;
12 logic[3:0] flags, expected_flags;
13 logic clk;
14 logic[7:0] testIndex, errors;
15 logic[103:0] testvector[25:0];
16
20 // Generate clock
21 always begin
22 clk = 1; #5;
23 clk = 0; #5;
24 end
25
26 // Initialization
27 initial begin
28 $display("Arithmetic Logic Unit Testbench");
29 $readmemh("vector_ALU.txt", testvector);
30 testIndex = 0; errors = 0;
31 end
32
38 // Check results
39 always @(negedge clk) begin
40 if(expected_result !== result) begin
41 $display("Error: result = %h (expecting %h).", result,
,→ expected_result);
181
2. Verification
42 errors = errors + 1;
43 end
44
50 testIndex = testIndex + 1;
51
182
2. Verification
9 module tb_RAM();
10 logic clk, we, testEN;
11 logic [31:0] dataIn, dataOut, expected_dataOut;
12 logic [$clog2(1024)-1:0] addr;
13 logic[7:0] testIndex, errors;
14 logic[96:0] testvector[25:0];
15
19 // Generate clock
20 always begin
21 clk = 1; #5;
22 clk = 0; #5;
23 end
24
25 // Initialization
26 initial begin
27 $display("Random Access Memory Testbench");
28 $readmemh("vector_RAM.txt", testvector);
29 testIndex = 0; errors = 0;
30 testEN = 1; #7; testEN = 0;
31 end
32
38 // Check results
39 always @(negedge clk) begin
40 if(˜testEN) begin
41 if(dataOut !== expected_dataOut) begin
42 $display("Error: Data = %h (expecting %h).", dataOut,
,→ expected_dataOut);
183
2. Verification
43 errors = errors + 1;
44 end
45
46 testIndex = testIndex + 1;
47
184
2. Verification
9 module tb_ROM();
10 logic clk;
11 logic [31:0] pc, instruction, expected_inst;
12 logic[7:0] testIndex, errors;
13 logic[63:0] testvector[25:0];
14
18 // Generate clock
19 always begin
20 clk = 1; #5;
21 clk = 0; #5;
22 end
23
24 // Initialization
25 initial begin
26 $display("Read Only Memory Testbench");
27 $readmemh("vector_ROM.txt", testvector);
28 testIndex = 0; errors = 0;
29 end
30
36 // Check results
37 always @(negedge clk) begin
38 if(instruction !== expected_inst) begin
39 $display("Error: instruction = %h (expecting %h).", instruction
,→ , expected_inst);
40 errors = errors + 1;
41 end
42
185
2. Verification
43 testIndex = testIndex + 1;
44
186
2. Verification
187
2. Verification
9 module tb_Control();
10 logic clk;
11 logic[6:0] opCode;
12 logic branch, forceJump, RAMwe, Regwe, ALUSrc2;
13 logic expected_branch, expected_forceJump, expected_RAMwe,
,→ expected_Regwe, expected_ALUSrc2;
14 logic[1:0] ALUOp, ALUSrc1, RegWriteSrc;
15 logic[1:0] expected_ALUOp, expected_ALUSrc1, expected_RegWriteSrc;
16 logic[7:0] testIndex;
17 logic[15:0] errors;
18 logic[17:0] testvector[20:0];
19
23 // Generate clock
24 always begin
25 clk = 1; #5;
26 clk = 0; #5;
27 end
28
29 // Initialization
30 initial begin
31 $display("Control Testbench");
32 $readmemb("vector_Control.txt", testvector); // Load test vector
33 testIndex = 0; errors = 0;
34 end
35
188
2. Verification
42 // Check results
43 always @(negedge clk) begin
44 if(expected_branch !== branch) begin
45 $display("Error: branch = %b (Expecting: %b)", branch,
,→ expected_branch);
46 errors = errors + 1;
47 end
48
189
2. Verification
76 errors = errors + 1;
77 end
78
84 testIndex = testIndex + 1;
85
190
2. Verification
9 module tb_BranchLogic();
10
21 // Generate clock
22 always begin
23 clk = 1; #5;
24 clk = 0; #5;
25 end
26
27 // Initialization
28 initial begin
29 $display("Branch Logic Testbench");
30 $readmemb("vector_BranchLogic.txt", testvector); // Load test
,→ vector
31 testIndex = 0; errors = 0;
32 end
33
40 // Check results
41 always @(negedge clk) begin
191
2. Verification
47 testIndex = testIndex + 1;
48
55 endmodule
192
2. Verification
193
2. Verification
9 module tb_ALUControl();
10 logic clk;
11 logic[1:0] ALUOp;
12 logic[4:0] func;
13 logic[3:0] ALUCtrl, expected_ALUCtrl;
14 logic[7:0] testIndex, errors;
15 logic[10:0] testvector[200:0];
16
20 // Generate Clock
21 always begin
22 clk = 1; #5;
23 clk = 0; #5;
24 end
25
26 // Initialization
27 initial begin
28 $display("ALU Control Testbench");
29 $readmemb("vector_ALUControl.txt", testvector);
30 testIndex = 0; errors = 0;
31 end
32
39 // Check results
40 always @(negedge clk) begin
41 if(ALUCtrl !== expected_ALUCtrl) begin
42 $display("Error: ALUCtrl = %h (expecting %h).", ALUCtrl,
,→ expected_ALUCtrl);
194
2. Verification
43 errors = errors + 1;
44 end
45
46 testIndex = testIndex + 1;
47
195
2. Verification
196
2. Verification
46 01_01101_0001
47 01_01110_0001
48 01_01111_0001
49 01_10000_0001
50 01_10001_0001
51 01_10010_0001
52 01_10011_0001
53 01_10100_0001
54 01_10101_0001
55 01_10110_0001
56 01_10111_0001
57 01_11000_0001
58 01_11001_0001
59 01_11010_0001
60 01_11011_0001
61 01_11100_0001
62 01_11101_0001
63 01_11110_0001
64 01_11111_0001
65 10_00000_0000
66 10_00001_0000
67 10_00010_0000
68 10_00011_0000
69 10_00100_0000
70 10_00101_0000
71 10_00110_0000
72 10_00111_0000
73 10_01000_0000
74 10_01001_0000
75 10_01010_0000
76 10_01011_0000
77 10_01100_0000
78 10_01101_0000
79 10_01110_0000
80 10_01111_0000
81 10_10000_0000
82 10_10001_0000
83 10_10010_0000
84 10_10011_0000
85 10_10100_0000
86 10_10101_0000
87 10_10110_0000
88 10_10111_0000
89 10_11000_0000
90 10_11001_0000
91 10_11010_0000
197
2. Verification
92 10_11011_0000
93 10_11100_0000
94 10_11101_0000
95 10_11110_0000
96 10_11111_0000
97 11_00000_xxxx
98 11_00001_xxxx
99 11_00010_xxxx
100 11_00011_xxxx
101 11_00100_xxxx
102 11_00101_xxxx
103 11_00110_xxxx
104 11_00111_xxxx
105 11_01000_xxxx
106 11_01001_xxxx
107 11_01010_xxxx
108 11_01011_xxxx
109 11_01100_xxxx
110 11_01101_xxxx
111 11_01110_xxxx
112 11_01111_xxxx
113 11_10000_xxxx
114 11_10001_xxxx
115 11_10010_xxxx
116 11_10011_xxxx
117 11_10100_xxxx
118 11_10101_xxxx
119 11_10110_xxxx
120 11_10111_xxxx
121 11_11000_xxxx
122 11_11001_xxxx
123 11_11010_xxxx
124 11_11011_xxxx
125 11_11100_xxxx
126 11_11101_xxxx
127 11_11110_xxxx
128 11_11111_xxxx
198
2. Verification
2.1.3 Processor
9 module tb_RV32I_SC #(
10 parameter ROM_SIZE = 64,
11 parameter RAM_DEPTH = 64,
12 parameter WORD_SIZE = 32
13 )();
14
199
2. Verification
49 // Generate clock
50 always begin
51 clk = 1; #5;
52 clk = 0; #5;
53 end
54
55 // Initialization
56 initial begin
57 //$readmemh("vector_ALU.txt", testvector);
58 //testIndex = 0; errors = 0;
59 en = 0;
60 rst = 0; #7 rst = 1;
61 en = 1;
62 end
63
64 endmodule
65
66 module ROM #(
67 parameter DEPTH = 1024
68 )
69 (
70 input logic[31:0] pc,
71 output logic[31:0] instruction
72 );
73
74 logic[31:0] memory[DEPTH-1:0];
75
76 initial begin
77 $readmemh("bubblesort.hex", memory);
78 end
79
82 endmodule
83
84 module RAM #(
85 parameter DEPTH = 1024,
86 parameter WORD_SIZE = 32
87 )(
88 input logic clk, we,
89 input logic[$clog2(DEPTH)-1:0] addr,
200
2. Verification
103 endmodule
201
2. Verification
202
2. Verification
203
2. Verification
46 nop
47 .jump11:
48 bgeu x2, x3, .jump11 # if x2 >= x3 unsigned then target
49 .back2:
50 bgeu x1, x1, .jump12 # if x1 >= x1 unsigned then target
51 jal x0, .back2
52 .jump12:
53 nop
204
2. Verification
205
2. Verification
2.2 Multicycle
2.2.1 Multicycle microcontroller
9 `timescale 1 ns / 1 ps
10 module tb_RV32I_MC();
11 logic clk, rst, en;
12 logic[1:0] pwm1out, pwm2out;
13 logic[6:0] hex0, hex1, hex2, hex3, hex4, hex5;
14
15 RV32I_MC RISCV(
16 .clk(clk),
17 .rst(rst),
18 .en(en),
19 .pwm1out(pwm1out),
20 .pwm2out(pwm2out),
21 .hex0(hex0),
22 .hex1(hex1),
23 .hex2(hex2),
24 .hex3(hex3),
25 .hex4(hex4),
26 .hex5(hex5)
27 );
28
29 // Generate clock
30 always begin
31 clk = 1; #5;
32 clk = 0; #5;
33 end
34
35 // Initialization
36 initial begin
37 en = 0;
38 rst = 0; #7 rst = 1;
39 en = 1;
40 end
41 endmodule
206
2. Verification
5 .bucle:
6 sw x3, 0(x1) # Write register
7 lw x4, 0(x1) # Read Register
8 addi x1, x1, 0x4 # Next address
9 addi x3, x3, 1 # Increase value
10 bne x1, x2, .bucle # while address != End address
11 nop
207
2. Verification
2.2.3 Peripherals
9 module tb_TIM_PID_Link();
10
11 // Signals definition
12 logic clk, rst, en, cs, write, read;
13 logic [31:0] writeData, readData, pid_in, compare1, compare2;
14
15 // DUT
16 PID_TIM_Link dut(
17 .clk(clk),
18 .chipSelect(cs),
19 .write(write),
20 .read(read),
21 .rst(rst),
22 .en(en),
23 .pid_in(pid_in),
24 .writeData(writeData),
25 .timOut1(compare1),
26 .timOut2(compare2),
27 .readData(readData)
28 );
29
30 // Generate clock
31 always begin
32 clk = 1; #5;
33 clk = 0; #5;
34 end
35
36 // Initialization
37 initial begin
38 en = 0;
39 rst = 0;
40 cs = 0;
41 write = 0;
208
2. Verification
42 read = 0;
43 pid_in = 2048;
44 writeData = 0;
45 #7;
46 rst = 1;
47 en = 1;
48 #10;
49 pid_in = -2048;
50 #10;
51 pid_in = 2048;
52 write = 1;
53 cs = 1;
54 writeData = 1;
55 #10;
56 write = 0;
57 read = 1;
58 pid_in = -2048;
59 #10;
60 write = 1;
61 read = 0;
62 writeData = 5;
63 pid_in = 2048;
64 #10;
65 write = 0;
66 read = 1;
67 pid_in = -2048;
68 #10;
69 write = 1;
70 read = 0;
71 writeData = 10;
72 pid_in = 2048;
73 #10;
74 write = 0;
75 read = 1;
76 pid_in = -2048;
77 end
78
79 endmodule
209
2. Verification
11 // Signals definition
12 logic clk, rst, cs, write, read;
13 logic[6:0] hex5, hex4, hex3, hex2, hex1, hex0;
14 logic[31:0] writeData, readData;
15
16 Hex7Segments dut(
17 .clk(clk),
18 .chipSelect(cs),
19 .write(write),
20 .read(read),
21 .rst(rst),
22 .writeData(writeData),
23 .hex5(hex5),
24 .hex4(hex4),
25 .hex3(hex3),
26 .hex2(hex2),
27 .hex1(hex1),
28 .hex0(hex0),
29 .readData(readData)
30 );
31
32 // Generate clock
33 always begin
34 clk = 1; #5;
35 clk = 0; #5;
36 end
37
38 // Initialization
39 initial begin
40 rst = 0;
41 cs = 0;
42 write = 0;
43 read = 0;
210
2. Verification
44 writeData = 0;
45 #7;
46 rst = 1;
47 cs = 1;
48 write = 1;
49 writeData = 32'h00654321;
50 end
51 endmodule
4 sw x2, 0(x1)
5 .loop:
6 j .loop
211
2. Verification
2.2.3.3 Timer
9 `timescale 1 ns / 1 ps
10
13 // Signals definition
14 logic clk, rst, en, cs, write, read;
15 logic[4:0] addr;
16 logic[1:0] pwm1out, pwm2out;
17 logic[31:0] writeData, readData, compare1, compare2;
18
19 Timer /*#(
20 .PRESCALER_DEFAULT_VALUE(1)
21 )*/ dut (
22 .clk(clk),
23 .chipSelect(cs),
24 .write(write),
25 .read(read),
26 .rst(rst),
27 .en(en),
28 .addr(addr),
29 .writeData(writeData),
30 .bypass1(compare1),
31 .bypass2(compare2),
32 .pwm1out(pwm1out),
33 .pwm2out(pwm2out),
34 .readData(readData)
35 );
36
37 // Generate clock
38 always begin
39 clk = 1; #5;
40 clk = 0; #5;
41 end
42
43 // Initialization
212
2. Verification
44 initial begin
45 en = 0;
46 rst = 0;
47 cs = 0;
48 write = 0;
49 read = 0;
50 addr = 0;
51 compare1 = 2;
52 compare2 = 8;
53 writeData = 0;
54 #7;
55 en = 1;
56 rst = 1;
57 cs = 1;
58 write = 1;
59 writeData = 10;
60 addr = 1;
61 #10;
62 addr = 2;
63 writeData = 1;
64 #200;
65 writeData = 0;
66 #10;
67 writeData = 0;
68 addr = 0;
69 #10;
70 addr = 4;
71 writeData = 4;
72 #10;
73 addr = 2;
74 writeData = 1;
75 #520;
76 addr = 2;
77 writeData = 0;
78 #10;
79 addr = 4;
80 writeData = 0;
81 #10;
82 addr = 0;
83 writeData = 0;
84 #10;
85 addr = 8;
86 writeData = 32'b0111;
87 #10;
88 addr = 6;
89 writeData = 5;
213
2. Verification
90 #10;
91 addr = 7;
92 #10;
93 addr = 2;
94 writeData = 1;
95 #100;
96 writeData = 1;
97 addr = 5;
98 #10;
99 addr = 9;
100 writeData = 1;
101 end
102 endmodule
214
2. Verification
9 `timescale 1 ns / 1 ps
10
13 // Signals definition
14 logic clk, rst, en;
15 logic[31:0] reference, feedback, k1, k2, k3;
16 logic[31:0] control;
17 int reffile, fbfile, cfile, efile, e1file, e2file, m1file, m2file,
18 m3file, ufile, kfile;
19
20 PID dut(
21 .clk(clk),
22 .en(en),
23 .arst(rst),
24 .srst(1'b1),
25 .reference(reference),
26 .feedback(feedback),
27 .k1(k1),
28 .k2(k2),
29 .k3(k3),
30 .control(control)
31 );
32
33 // Generate clock
34 always begin
35 clk = 1; #5;
36 $fdisplay(cfile, "%0d", $signed(control));
37 $fdisplay(efile, "%0d", $signed(dut.error));
38 $fdisplay(e1file, "%0d", $signed(dut.error1));
39 $fdisplay(e2file, "%0d", $signed(dut.error2));
40 $fdisplay(m1file, "%0d", $signed(dut.multiplied1));
41 $fdisplay(m2file, "%0d", $signed(dut.multiplied2));
42 $fdisplay(m3file, "%0d", $signed(dut.multiplied3));
43 $fdisplay(ufile, "%0d", $signed(dut.control1));
215
2. Verification
63 // Initialization
64 initial begin
65 en = 0;
66 rst = 0;
67
216
2. Verification
90 end
91
217
2. Verification
136 end
137
218
2. Verification
182
204 #7 rst = 1;
205 en = 1;
206 end
207 endmodule
219
2. Verification
3 kp = 1;
4 ki = 0;
5 kd = 0;
6 t = 0.001;
7 td = kd;
8 ti = 1/ki;
9 k1 = kp+td/t;
10 k2 = -kp + t/ti - 2*td/t;
11 k3 = td/t;
12 k = [k1; k2; k3];
13
14 out = sim("Continuous_PID.slx");
15
20 hold off
21 figure(1)
22 plot(out.control,'LineWidth',2)
23 hold on
24 plot(out.reference,'LineWidth',2)
25 plot(out.feedback,'LineWidth',2)
26 %plot(out.reference - out.feedback,'LineWidth',2)
27 grid minor
28 get(gca,'fontname');
29 set(gca,'fontname','Palatino Linotype')
30 legend("Control", "Reference", "Feedback")
31 fig = gca;
32 exportgraphics(fig, '5_2_ContinuousControl.pdf');
33
38 hold off
39 figure(1)
40 plot(out.control,'LineWidth',2)
41 hold on
42 plot(discU,'LineWidth',2)
43 grid minor
44 get(gca,'fontname');
45 set(gca,'fontname','Palatino Linotype')
220
2. Verification
221
2. Verification
9 `timescale 1 ns / 1 ps
10
13 // Signals definition
14 logic clk, rst, en, cs, write, read;
15 logic[3:0] addr;
16 logic[31:0] writeData, readData, control_bypass;
17 int response = 0;
18
19 PID_Reg #(
20 .PRESCALER_DEFAULT_VALUE(1)
21 ) dut (
22 .clk(clk),
23 .chipSelect(cs),
24 .write(write),
25 .read(read),
26 .rst(rst),
27 .en(en),
28 .addr(addr),
29 .writeData(writeData),
30 .feedback_bypass(32'd19),
31 .readData(readData),
32 .control_bypass(control_bypass)
33 );
34
35 // Generate clock
36 always begin
37 clk = 1; #5;
38 clk = 0; #5;
39 end
40
41 // Initialization
42 initial begin
43 en = 0;
44 rst = 0;
45 cs = 0;
222
2. Verification
46 write = 0;
47 read = 0;
48 addr = 0;
49 writeData = 0;
50 #7;
51 rst = 1;
52 en = 1;
53 write = 1;
54 cs = 1;
55 addr = 0;
56 writeData = 20;
57 #10;
58 addr = 1;
59 writeData = 1;
60 #10;
61 addr = 2;
62 writeData = -1;
63 #10;
64 addr = 4;
65 writeData = response;
66 #10;
67 addr = 10;
68 writeData = 15;
69 #10;
70 addr = 11;
71 writeData = 5;
72 #10;
73 addr = 6;
74 writeData = 1;
75 while(response < 20) begin
76 #10;
77 read = 1;
78 write = 0;
79 addr = 12;
80 response = response + 1;
81 #10;
82 read = 0;
83 write = 1;
84 addr = 4;
85 writeData = response;
86 end
87 end
88 endmodule
223
Intentionally blank page
224