aboutsummaryrefslogtreecommitdiffstats
path: root/ice40/regressions/issue0148/hdl/cpu16.sv
blob: 91d32b6d5b7fc859ac7d453cc09902566b5b38ce (plain)
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
302
303
// Copyright 2015, Brian Swetland <swetland@frotz.net>
// Licensed under the Apache License, Version 2.0.

`timescale 1ns / 1ps

module cpu16(
        input clk,
        output [15:0]ins_rd_addr,
        input [15:0]ins_rd_data,
	output ins_rd_req,
	input ins_rd_rdy,

	output [15:0]dat_rw_addr,
	output [15:0]dat_wr_data,
	input [15:0]dat_rd_data,
	output dat_rd_req,
	output dat_wr_req,
	input dat_rd_rdy,
	input dat_wr_rdy,

	input reset
        );

localparam INS_NOP = 16'h0001;

// ---- FETCH ----
reg [15:0]pc_next;
reg [15:0]ir_next;
reg ir_valid_next;

reg [15:0]pc = 16'd0;
reg [15:0]ir = 16'd0;
reg ir_valid = 1'b0;

assign ins_rd_addr = pc_next;
assign ins_rd_req = 1'b1;

always_comb begin
	if (reset) begin
		pc_next = 16'h0000;
	end else if (ex_do_branch_imm) begin
		pc_next = ex_branch_tgt;
	end else if (ins_rd_rdy) begin
		pc_next = pc + 16'h0001;
	end else begin
		pc_next = pc;
	end
	ir_next = ins_rd_rdy ? ins_rd_data : INS_NOP;
	ir_valid_next = ins_rd_rdy;
end

always_ff @(posedge clk) begin
	pc <= pc_next;
	ir <= ir_next;
	ir_valid <= ir_valid_next;
end

// ---- DECODE ----
reg [11:0]de_ext = 12'b0;
reg de_ext_rdy = 1'b0;

// s6   alu-reg-imm  load/store
// s7   pc-rel-cond-branch
// s9   mov-imm
// s11  pc-rel-branch
// s12  ext

// fields decoded from instruction
wire [15:0]ir_imm_s6_raw = { {11 {ir[15]}}, ir[14:10] };
wire [15:0]ir_imm_s7 = { {10 {ir[15]}}, ir[6], ir[14:10] };
wire [15:0]ir_imm_s9_raw = { {8 {ir[15]}}, ir[9:7], ir[14:10] };
wire [15:0]ir_imm_s11 = { {6 {ir[15]}}, ir[8:4], ir[14:10] };
wire [15:0]ir_imm_s12 = { {5 {ir[15]}}, ir[9:4], ir[14:10] };
wire [2:0]ir_csel = ir[6:4];
wire [2:0]ir_asel = ir[9:7];
wire [2:0]ir_bsel = ir[12:10];
wire [2:0]ir_alu_op = ir[3] ? ir[2:0] : ir[15:13];
wire [3:0]ir_opcode = ir[3:0];

reg [11:0]ir_ext_imm = 12'b0;
reg ir_ext_rdy = 1'b0;

wire [15:0]ir_imm_s6 = { (ir_ext_rdy ? ir_ext_imm : ir_imm_s6_raw[15:4]), ir_imm_s6_raw[3:0] };
wire [15:0]ir_imm_s9 = { (ir_ext_rdy ? ir_ext_imm : ir_imm_s9_raw[15:4]), ir_imm_s9_raw[3:0] };

// control signals
reg do_wreg_alu; // write from alu
reg do_wreg_mem; // write from memory
reg do_adata_zero; // pass 0 to alu.xdata (instead of adata)
reg do_bdata_imm; // pass imm to alu.ydata (instead of bdata)
reg do_wr_link;    // write PC + 1 to LR
reg do_branch_imm; // branch to imm
reg do_branch_reg; // branch to reg + imm
reg do_branch_cond; // branch if condition met
reg do_branch_zero; // condition zero(1) or notzero(0)
reg do_use_imm9_or_imm6;
reg do_mem_read;
reg do_mem_write;
reg do_set_ext;

always_comb begin
	do_wreg_alu = 1'b0;
	do_wreg_mem = 1'b0;
	do_adata_zero = 1'b0;
	do_bdata_imm = 1'b0;
	do_wr_link = 1'b0;
	do_branch_imm = 1'b0;
	do_branch_reg = 1'b0;
	do_branch_cond = 1'b0;
	do_branch_zero = 1'b0;
	do_use_imm9_or_imm6 = 1'b0;
	do_mem_read = 1'b0;
	do_mem_write = 1'b0;
	do_set_ext = 1'b0;

	casez (ir_opcode)
	4'b0000: begin // alu Rc, Ra, Rb
		do_wreg_alu = 1'b1;
	end
	4'b0001: begin // expansion (nop)
	end
	4'b0010: begin // ext si12
		do_set_ext = 1'b1;
	end
	4'b0011: begin // mov Rc, si9
		do_wreg_alu = 1'b1;
		do_adata_zero = 1'b1;
		do_bdata_imm = 1'b1;
		do_use_imm9_or_imm6 = 1'b1;
	end
	4'b0100: begin // lw Rc, [Ra, si6]
		do_mem_read = 1'b1;
		do_use_imm9_or_imm6 = 1'b0;
	end
	4'b0101: begin // sw Rc, [Ra, si6]
		do_mem_write = 1'b1;
		do_use_imm9_or_imm6 = 1'b0;
	end
	4'b0110: begin // b imm12 / bl imm12
		do_branch_imm = 1'b1;
		do_wr_link = ir[9];
	end
	4'b0111: begin // BZ/BNZ/B/BL
		if (ir[5]) begin // b Ra / bl Ra
			do_branch_reg = 1'b1;
			do_wr_link = ir[4];
		end else begin // bz imm7 / bnz imm7
			do_branch_cond = 1'b1;
			do_branch_zero = ~ir[4];
		end
	end
	4'b1???: begin // alu Rc, Ra, si6
		do_wreg_alu = 1'b1;
		do_bdata_imm = 1'b1;
		do_use_imm9_or_imm6 = 1'b0;
	end
	endcase
end

always_ff @(posedge clk) begin
	if (ir_valid)
		ir_ext_rdy <= do_set_ext;
	if (ir_valid & do_set_ext)
		ir_ext_imm <= ir_imm_s12[11:0];
end

wire [15:0]ex_adata;
wire [15:0]ex_bdata;
wire [15:0]ex_alu_rdata;

regs16 regs(
	.clk(clk),
	.asel(ir_asel),
	.bsel(do_mem_write ? ir_csel : ir_bsel),
	.wsel(ex_wsel),
	.wreg(ex_do_wreg_alu),
	.adata(ex_adata),
	.bdata(ex_bdata),
	.wdata(ex_alu_rdata)
	);

// ---- EXECUTE ----

reg [15:0]ex_branch_tgt = 16'b0;
reg [2:0]ex_alu_op = 3'b0;
reg [2:0]ex_wsel = 3'b0;
reg ex_do_wreg_alu = 1'b0;
reg ex_do_wreg_mem = 1'b0;
reg ex_do_adata_zero = 1'b0;
reg ex_do_bdata_imm = 1'b0;
reg ex_do_wr_link = 1'b0;
reg ex_do_branch_imm = 1'b0;
reg ex_do_branch_reg = 1'b0;
reg ex_do_branch_cond = 1'b0;
reg ex_do_branch_zero = 1'b0;
reg ex_do_mem_read = 1'b0;
reg ex_do_mem_write = 1'b0;

reg [15:0]ex_imm = 16'b0;

always_ff @(posedge clk) begin
	// for mem-read or mem-write we use the ALU for Ra + imm7
	ex_alu_op <= (do_adata_zero | do_mem_read | do_mem_write) ? 3'b0 : ir_alu_op;
	ex_wsel <= do_wr_link ? 3'd7 : ir_csel;
	ex_branch_tgt <= pc + (do_branch_imm ? ir_imm_s11 : ir_imm_s7);
	ex_do_wreg_alu <= do_wreg_alu;
	ex_do_wreg_mem <= do_wreg_mem;
	ex_do_adata_zero <= do_adata_zero;
	ex_do_bdata_imm <= do_bdata_imm;
	ex_do_wr_link <= do_wr_link;
	ex_do_branch_imm <= do_branch_imm;
	ex_do_branch_reg <= do_branch_reg;
	ex_do_branch_cond <= do_branch_cond;
	ex_do_branch_zero <= do_branch_zero;
	ex_do_mem_read = do_mem_read;
	ex_do_mem_write = do_mem_write;
	ex_imm <= (do_mem_read | do_mem_write) ? ir_imm_s7 : (do_use_imm9_or_imm6 ? ir_imm_s9 : ir_imm_s6);
end


alu16 alu(
	.op(ex_alu_op),
	.xdata(ex_do_adata_zero ? 16'b0 : ex_adata),
	.ydata((ex_do_mem_read | ex_do_mem_write | ex_do_bdata_imm) ? ex_imm : ex_bdata),
	.rdata(ex_alu_rdata)
	);

assign dat_rw_addr = ex_alu_rdata;
assign dat_wr_data = ex_bdata;
assign dat_rd_req = ex_do_mem_read;
assign dat_wr_req = ex_do_mem_write;

// ---- SIMULATION DEBUG ASSIST ----

`ifdef verilator
reg [15:0]dbg_addr = 16'd0;
wire [47:0]ir_dbg_dis;
reg [47:0]ex_dbg_dis = 48'd0;

assign ir_dbg_dis = { ir, 3'b0, ir_ext_rdy, ir_ext_imm, dbg_addr };

always_ff @(posedge clk) begin
	dbg_addr <= pc;
	ex_dbg_dis <= ir_dbg_dis;
end
`endif

endmodule

module regs16(
	input clk,
	input [2:0]asel,
	input [2:0]bsel,
	input [2:0]wsel,
	input wreg,
	input [15:0]wdata,
	output [15:0]adata,
	output [15:0]bdata
	);

reg [15:0]rmem[0:7];
reg [15:0]areg;
reg [15:0]breg;

always_ff @(posedge clk) begin
	if (wreg)
		rmem[wsel] <= wdata;
	areg <= rmem[asel];
	breg <= rmem[bsel];
end

assign adata = areg;
assign bdata = breg;

endmodule


module alu16(
	input [2:0]op,
	input [15:0]xdata,
	input [15:0]ydata,
	output [15:0]rdata
	);

reg [15:0]r;

always_comb begin
	case (op)
	3'b000: r = xdata + ydata;
	3'b001: r = xdata - ydata;
	3'b010: r = xdata & ydata;
	3'b011: r = xdata | ydata;
	3'b100: r = xdata ^ ydata;
	3'b101: r = { {15 {1'b0}}, xdata < ydata };
	3'b110: r = { {15 {1'b0}}, xdata >= ydata };
	3'b111: r = xdata * ydata;
	endcase
end

assign rdata = r;

endmodule