-
Notifications
You must be signed in to change notification settings - Fork 0
/
SoC.v
301 lines (284 loc) · 10.2 KB
/
SoC.v
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
`timescale 1ns / 1ps
`default_nettype none
//`define DBG
module SoC #(
parameter RAM_FILE = "RAM.mem",
parameter CLK_FREQ = 50_000_000,
parameter BAUD_RATE = 9600
)(
input wire clk,
input wire rst,
output wire [3:0] led,
output wire led0_b,
output wire led0_g,
output wire led0_r,
input wire uart_rx,
output wire uart_tx,
input wire btn
);
reg [31:0] pc; // program counter, byte addressed, next instruction to fetch
reg [31:0] pc_nxt; // next value of program counter
reg [31:0] pc_ir; // program counter of current instruction
wire [31:0] ir; // instruction register (one cycle delay due to ram access)
wire [6:0] opcode = ir[6:0];
wire [4:0] rd = ir[11:7]; // destination register
wire [2:0] funct3 = ir[14:12];
wire [4:0] rs1 = ir[19:15]; // source register 1
wire [4:0] rs2 = ir[24:20]; // source register 2
wire [6:0] funct7 = ir[31:25];
wire signed [31:0] I_imm12 = {{20{ir[31]}}, ir[31:20]};
wire [31:0] U_imm20 = {ir[31:12], {12{1'b0}}};
wire signed [31:0] S_imm12 = {{20{ir[31]}}, ir[31:25], ir[11:7]};
wire signed [31:0] B_imm12 = {{20{ir[31]}}, ir[7], ir[30:25], ir[11:8], 1'b0};
wire signed [31:0] J_imm20 = {{12{ir[31]}}, ir[19:12], ir[20], ir[30:21], 1'b0};
reg [31:0] regs_rd_wd; // data for write to register 'rd' if 'regs_rd_we' is enabled
reg regs_rd_we;
wire signed [31:0] regs_rd1; // register value of 'rs1'
wire signed [31:0] regs_rd2; // register value of 'rs2'
reg [1:0] ram_weA; // ram port A write enable
reg [2:0] ram_reA; // ram port A read enable
reg [31:0] ram_addrA; // ram port A address
reg [31:0] ram_dinA; // data to ram port A
wire [31:0] ram_doutA; // data from ram port A
reg is_ld; // current instruction is 'load'
reg [4:0] ld_rd; // previous instruction 'rd'
reg regs_we3; // enabled when previous instruction was 'load'
// will write 'ram_doutA' to register 'ld_rd'
reg signed [31:0] rs1_dat; // resolved rs1 value considering pipeline
reg signed [31:0] rs2_dat; // resolved rs2 value considering pipeline
reg bubble; // signals that next instruction is a bubble
reg is_bubble; // signals that current instruction is a bubble
always @(*) begin
regs_rd_we = 0;
regs_rd_wd = 0;
ram_addrA = 0;
ram_dinA = 0;
ram_weA = 0;
ram_reA = 0;
is_ld = 0;
bubble = 0;
rs1_dat = 0;
rs2_dat = 0;
pc_nxt = pc + 4;
if (!is_bubble) begin
// if last instruction was a load to a register and the same register
// is used in this instruction then use the output of ram since it is
// not in the register yet
rs1_dat = regs_we3 && rs1 == ld_rd ? ram_doutA : regs_rd1;
rs2_dat = regs_we3 && rs2 == ld_rd ? ram_doutA : regs_rd2;
case (opcode)
7'b0110111: begin // LUI
regs_rd_wd = U_imm20;
regs_rd_we = 1;
end
7'b0010011: begin // logical ops immediate
regs_rd_we = 1;
case (funct3)
3'b000: begin // ADDI
regs_rd_wd = rs1_dat + I_imm12;
end
3'b010: begin // SLTI
regs_rd_wd = rs1_dat < I_imm12;
end
3'b011: begin // SLTIU
regs_rd_wd = $unsigned(rs1_dat) < $unsigned(I_imm12);
end
3'b100: begin // XORI
regs_rd_wd = rs1_dat ^ I_imm12;
end
3'b110: begin // ORI
regs_rd_wd = rs1_dat | I_imm12;
end
3'b111: begin // ANDI
regs_rd_wd = rs1_dat & I_imm12;
end
3'b001: begin // SLLI
regs_rd_wd = rs1_dat << rs2;
end
3'b101: begin // SRLI and SRAI
regs_rd_wd = ir[30] ? rs1_dat >>> rs2 : rs1_dat >> rs2;
end
endcase // case (funct3)
end
7'b0110011: begin // logical ops
regs_rd_we = 1;
case (funct3)
3'b000: begin // ADD and SUB
regs_rd_wd = ir[30] ? rs1_dat - rs2_dat : rs1_dat + rs2_dat;
end
3'b001: begin // SLL
regs_rd_wd = rs1_dat << rs2_dat[4:0];
end
3'b010: begin // SLT
regs_rd_wd = rs1_dat < rs2_dat;
end
3'b011: begin // SLTU
regs_rd_wd = $unsigned(rs1_dat) < $unsigned(rs2_dat);
end
3'b100: begin // XOR
regs_rd_wd = rs1_dat ^ rs2_dat;
end
3'b101: begin // SRL and SRA
regs_rd_wd = ir[30] ? rs1_dat >>> rs2_dat[4:0] : rs1_dat >> rs2_dat[4:0];
end
3'b110: begin // OR
regs_rd_wd = rs1_dat | rs2_dat;
end
3'b111: begin // AND
regs_rd_wd = rs1_dat & rs2_dat;
end
endcase // case (funct3)
end
7'b0100011: begin // store
ram_addrA = rs1_dat + S_imm12;
ram_dinA = rs2_dat;
case (funct3)
3'b000: begin // SB
ram_weA = 2'b01; // write byte
end
3'b001: begin // SH
ram_weA = 2'b10; // write half word
end
3'b010: begin // SW
ram_weA = 2'b11; // write word
end
endcase // case (funct3)
end
7'b0000011: begin // load
ram_addrA = rs1_dat + I_imm12;
is_ld = 1;
case (funct3)
3'b000: begin // LB
ram_reA = 3'b101; // read sign extended byte
end
3'b001: begin // LH
ram_reA = 3'b110; // read sign extended half word
end
3'b010: begin // LW
ram_reA = 3'b111; // read word (signed)
end
3'b100: begin // LBU
ram_reA = 3'b001; // read unsigned byte
end
3'b101: begin // LHU
ram_reA = 3'b010; // read unsigned half word
end
endcase // case (funct3)
end
7'b0010111: begin // AUIPC
regs_rd_wd = pc_ir + U_imm20;
regs_rd_we = 1;
end
7'b1101111: begin // JAL
regs_rd_wd = pc; // note. 'pc' is ahead one instruction (+4)
regs_rd_we = 1;
pc_nxt = pc_ir + J_imm20;
bubble = 1;
end
7'b1100111: begin // JALR
regs_rd_wd = pc; // note. 'pc' is ahead one instruction (+4)
regs_rd_we = 1;
pc_nxt = rs1_dat + I_imm12;
bubble = 1;
end
7'b1100011: begin // branches
case (funct3)
3'b000: begin // BEQ
if (rs1_dat == rs2_dat) begin
pc_nxt = pc_ir + B_imm12;
bubble = 1;
end
end
3'b001: begin // BNE
if (rs1_dat != rs2_dat) begin
pc_nxt = pc_ir + B_imm12;
bubble = 1;
end
end
3'b100: begin // BLT
if (rs1_dat < rs2_dat) begin
pc_nxt = pc_ir + B_imm12;
bubble = 1;
end
end
3'b101: begin // BGE
if (rs1_dat >= rs2_dat) begin
pc_nxt = pc_ir + B_imm12;
bubble = 1;
end
end
3'b110: begin // BLTU
if ($unsigned(rs1_dat) < $unsigned(rs2_dat)) begin
pc_nxt = pc_ir + B_imm12;
bubble = 1;
end
end
3'b111: begin // BGEU
if ($unsigned(rs1_dat) >= $unsigned(rs2_dat)) begin
pc_nxt = pc_ir + B_imm12;
bubble = 1;
end
end
endcase // case (funct3)
end
endcase // case (opcode)
end
end
always @(posedge clk) begin
if (rst) begin
pc <= 0;
pc_ir <= 0;
is_bubble <= 0;
end else begin
regs_we3 <= is_ld; // if this is a 'load' from ram enable write to
// register 'ld_rd' during next cycle due to one
// cycle delay for data ready from ram
ld_rd <= rd; // save the destination register for next cycle write
is_bubble <= bubble; // if instruction generates bubble of next
// instruction (branch, jumps instructions)
pc <= pc_nxt;
pc_ir <= pc_nxt - 4; // -4 because 'pc' is the next instruction to be
// fetched. when branching there is a bubble and
// 'pc' is incremented by 4 during that
end
end
Registers regs (
.clk(clk),
.rs1(rs1), // register source 1
.rs2(rs2), // register source 2
.rd(rd), // destination register
.rd_wd(regs_rd_wd), // write data to destination register
.rd_we(regs_rd_we), // write enable to destination register
.rd1(regs_rd1), // data out of register 'rs1'
.rd2(regs_rd2), // data out of register 'rs2'
.ra3(ld_rd), // register to write from ram out (load instructions)
.wd3(ram_doutA), // data for write to register 'ra3' when 'we3' is enabled
.we3(regs_we3) // write enable port 3
);
RAM_Interface #(
.ADDR_WIDTH(15), // 2**15 = RAM depth in 4 byte words
.ADDR_LEDS(17'h1_ffff), // leds address
.ADDR_UART_OUT(17'h1_fffe), // byte from uart address
.ADDR_UART_IN(17'h1_fffd), // byte to uart address
.DATA_FILE(RAM_FILE), // initial memory content
.CLK_FREQ(CLK_FREQ),
.BAUD_RATE(BAUD_RATE)
) ram (
.rst(rst),
// port A: data memory, read / write byte addressable ram
.clk(clk),
.weA(ram_weA), // write: b01 - byte, b10 - half word, b11 - word
.reA(ram_reA), // read: reA[2] sign extended, b01 - byte, b10 - half word, b11 - word
.addrA(ram_addrA), // byte addressable
.dinA(ram_dinA), // data to write to 'ram_addrA' depending on 'ram_weA'
.doutA(ram_doutA), // data out from 'ram_addrA' depending on 'ram_reA' one cycle later
// port B: instruction memory, byte addressed, bottom 2 bits ignored, word aligned
.addrB(pc), // program counter
.doutB(ir), // instruction register
.leds({led0_b, led0_g, led0_r, led[3:0]}),
.uart_tx(uart_tx),
.uart_rx(uart_rx)
);
endmodule
`undef DBG
`default_nettype wire