-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathyfcpu.v
More file actions
218 lines (179 loc) · 6.1 KB
/
yfcpu.v
File metadata and controls
218 lines (179 loc) · 6.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/* Chapter 3 - Your First Assembler
In this chapter we create our own assembler that generates binary code and
rom files for our soft cpu. This file contains only minor changes since
Chapter 2:
1. Added NOP instruction as 0x00 opcode.
2. Moved HALT instruction to 0xff opcode.
3. Removed hard-coded IMEM program, IMEM is now loaded from rom file.
See LOADROM line 86
*/
module yfcpu(clk, rst, PC);
// our cpu core parameters
parameter im_size = 8; // 2^n instruction word memory
parameter rf_size = 8; // 2^n word register file
// a parameter for the bus width of our cpu
parameter bw = 8;
input clk; // our system clock
output [ im_size-1 : 0 ] PC; // our program counter
input rst; // reset signal
// the cycle states of our cpu, i.e. the Control Unit states
parameter s_fetch = 3'b000; // fetch next instruction from memory
parameter s_decode = 3'b001; // decode instruction into opcode and operands
parameter s_execute = 3'b010; // execute the instruction inside the ALU
parameter s_store = 3'b011; // store the result back to memory
parameter s_nostore = 3'b100; // dont store any result, skips a cycle
// the parts of our instruction word
parameter opcode_size = 4; // size of opcode in bits
// Mnemonic Op Codes
parameter LRI = 4'b0001;
parameter ADD = 4'b0100;
parameter SUB = 4'b0101;
parameter OR = 4'b0110;
parameter XOR = 4'b0111;
// our new branch mnemonics!
parameter BRA = 4'b1000; // branch to address in memory location RB
parameter BRANZ = 4'b1001; // branch to address in memory location RB, if RA is zero
parameter BRAL = 4'b1010; // branch to literal address RB
parameter BRALNZ = 4'b1011; // branch to literal address RB, if RA is zero
parameter CALL = 4'b1100; // call subroutine; store current PC into RD and jump to address in literal location RB
parameter HALT = 4'b1111;
// our memory core consisting of Instruction Memory, Register File and an ALU working (W) register
reg [ opcode_size + (rf_size*3) -1 : 0 ] IMEM[0: 2 ** im_size -1 ] ; // instruction memory
reg [ bw-1:0 ] REGFILE[0: 2 ** rf_size -1 ]; // data memory
reg [ bw-1:0 ] W; // working (intermediate) register
// our cpu core registers
reg [ im_size-1 : 0 ] PC; // program counter
reg [ opcode_size + (rf_size*3) -1 : 0 ] IR; // instruction register
/* Control Unit registers
The control unit sequencer cycles through fetching the next instruction
from memory, decoding the instruction into the opcode and operands and
executing the opcode in the ALU.
*/
reg [ 2:0 ] current_state;
reg [ 2:0 ] next_state;
// our instruction registers
// an opcode typically loads registers addressed from RA and RB, and stores
// the result into destination register RD. RA:RB can also be used to form
// an 8bit immediate (literal) value.
reg [ opcode_size-1 : 0 ] OPCODE;
reg [ rf_size-1 : 0 ] RA; // left operand register address
reg [ rf_size-1 : 0 ] RB; // right operand register address
reg [ rf_size-1 : 0 ] RD; // destination register
// the initial cpu state bootstrap
initial begin
PC = 0;
current_state = s_fetch;
// LOADROM: here we load the rom file generated by our assembler!!!
$readmemh("test.rom",IMEM);
end
// at each clock cycle we sequence the Control Unit, or if rst is
// asserted we keep the cpu in reset.
always @ (clk, rst)
begin
if(rst) begin
current_state = s_fetch;
PC = 0;
end
else
begin
// sequence our Control Unit
case( current_state )
s_fetch: begin
// fetch instruction from instruction memory
IR = IMEM[ PC ];
next_state = s_decode;
end
s_decode: begin
// PC can be incremented as current instruction is loaded into IR
PC = PC + 1;
next_state = s_execute;
// decode the opcode and register operands
OPCODE = IR[ opcode_size + (rf_size*3) -1 : (rf_size*3) ];
RA = IR[ (rf_size*3) -1 : (rf_size*2) ];
RB = IR[ (rf_size*2) -1 : (rf_size ) ];
RD = IR[ (rf_size ) -1 : 0 ];
end
s_execute: begin
// Execute ALU instruction, process the OPCODE
case (OPCODE)
LRI: begin
// load register RD with immediate from RA:RB operands
W = {RA, RB};
next_state = s_store;
end
ADD: begin
// Add RA + RB
W = REGFILE[RA] + REGFILE[RB];
next_state = s_store;
end
SUB: begin
// Sub RA - RB
W = REGFILE[RA] - REGFILE[RB];
next_state = s_store;
end
OR: begin
// OR RA + RB
W = REGFILE[RA] | REGFILE[RB];
next_state = s_store;
end
XOR: begin
// Exclusive OR RA ^ RB
W = REGFILE[RA] ^ REGFILE[RB];
next_state = s_store;
end
HALT: begin
// Halt execution, loop indefinately
next_state = s_execute;
end
BRA: begin
// branch to REGFILE[RB]
PC = REGFILE[RB];
next_state = s_nostore;
end
BRAL: begin
// branch to RB
PC = RB;
next_state = s_nostore;
end
BRANZ: begin
// branch to REGFILE[RB] if REGFILE[RA] is zero
if(REGFILE[RA] !=8'd0)
PC = REGFILE[RB];
next_state = s_nostore;
end
BRALNZ: begin
// branch to RB if REGFILE[RA] is zero
if(REGFILE[RA] !=8'd0)
PC = RB;
next_state = s_nostore;
end
CALL: begin
// call a subroutine; store PC into RD and jump to location in REGFILE[RB]
W = PC;
PC = RB;
next_state = s_store; // note state s_store! because we store PC into REGFILE[RD]
end
// catch all
default: begin
next_state = s_nostore; // default, this is a NOP
end
endcase
end
s_store: begin
// store the ALU working register into the destination register RD
REGFILE[RD] = W;
next_state = s_fetch;
end
s_nostore: begin
// not a store instruction, skip this cycle
// this is typically referred to as a "wait state".
next_state = s_fetch;
end
// invalid state!
default: begin end
endcase
// move the control unit to the next state
current_state = next_state;
end
end
endmodule