1// Copyright 2015, Brian Swetland <swetland@frotz.net>
2// Licensed under the Apache License, Version 2.0.
3
4`timescale 1ns / 1ps
5
6module cpu16(
7        input clk,
8        output [15:0]ins_rd_addr,
9        input [15:0]ins_rd_data,
10	output ins_rd_req,
11	input ins_rd_rdy,
12
13	output [15:0]dat_rw_addr,
14	output [15:0]dat_wr_data,
15	input [15:0]dat_rd_data,
16	output dat_rd_req,
17	output dat_wr_req,
18	input dat_rd_rdy,
19	input dat_wr_rdy,
20
21	input reset
22        );
23
24localparam INS_NOP = 16'h0001;
25
26// ---- FETCH ----
27reg [15:0]pc_next;
28reg [15:0]ir_next;
29reg ir_valid_next;
30
31reg [15:0]pc = 16'd0;
32reg [15:0]ir = 16'd0;
33reg ir_valid = 1'b0;
34
35assign ins_rd_addr = pc_next;
36assign ins_rd_req = 1'b1;
37
38always_comb begin
39	if (reset) begin
40		pc_next = 16'h0000;
41	end else if (ex_do_branch_imm) begin
42		pc_next = ex_branch_tgt;
43	end else if (ins_rd_rdy) begin
44		pc_next = pc + 16'h0001;
45	end else begin
46		pc_next = pc;
47	end
48	ir_next = ins_rd_rdy ? ins_rd_data : INS_NOP;
49	ir_valid_next = ins_rd_rdy;
50end
51
52always_ff @(posedge clk) begin
53	pc <= pc_next;
54	ir <= ir_next;
55	ir_valid <= ir_valid_next;
56end
57
58// ---- DECODE ----
59reg [11:0]de_ext = 12'b0;
60reg de_ext_rdy = 1'b0;
61
62// s6   alu-reg-imm  load/store
63// s7   pc-rel-cond-branch
64// s9   mov-imm
65// s11  pc-rel-branch
66// s12  ext
67
68// fields decoded from instruction
69wire [15:0]ir_imm_s6_raw = { {11 {ir[15]}}, ir[14:10] };
70wire [15:0]ir_imm_s7 = { {10 {ir[15]}}, ir[6], ir[14:10] };
71wire [15:0]ir_imm_s9_raw = { {8 {ir[15]}}, ir[9:7], ir[14:10] };
72wire [15:0]ir_imm_s11 = { {6 {ir[15]}}, ir[8:4], ir[14:10] };
73wire [15:0]ir_imm_s12 = { {5 {ir[15]}}, ir[9:4], ir[14:10] };
74wire [2:0]ir_csel = ir[6:4];
75wire [2:0]ir_asel = ir[9:7];
76wire [2:0]ir_bsel = ir[12:10];
77wire [2:0]ir_alu_op = ir[3] ? ir[2:0] : ir[15:13];
78wire [3:0]ir_opcode = ir[3:0];
79
80reg [11:0]ir_ext_imm = 12'b0;
81reg ir_ext_rdy = 1'b0;
82
83wire [15:0]ir_imm_s6 = { (ir_ext_rdy ? ir_ext_imm : ir_imm_s6_raw[15:4]), ir_imm_s6_raw[3:0] };
84wire [15:0]ir_imm_s9 = { (ir_ext_rdy ? ir_ext_imm : ir_imm_s9_raw[15:4]), ir_imm_s9_raw[3:0] };
85
86// control signals
87reg do_wreg_alu; // write from alu
88reg do_wreg_mem; // write from memory
89reg do_adata_zero; // pass 0 to alu.xdata (instead of adata)
90reg do_bdata_imm; // pass imm to alu.ydata (instead of bdata)
91reg do_wr_link;    // write PC + 1 to LR
92reg do_branch_imm; // branch to imm
93reg do_branch_reg; // branch to reg + imm
94reg do_branch_cond; // branch if condition met
95reg do_branch_zero; // condition zero(1) or notzero(0)
96reg do_use_imm9_or_imm6;
97reg do_mem_read;
98reg do_mem_write;
99reg do_set_ext;
100
101always_comb begin
102	do_wreg_alu = 1'b0;
103	do_wreg_mem = 1'b0;
104	do_adata_zero = 1'b0;
105	do_bdata_imm = 1'b0;
106	do_wr_link = 1'b0;
107	do_branch_imm = 1'b0;
108	do_branch_reg = 1'b0;
109	do_branch_cond = 1'b0;
110	do_branch_zero = 1'b0;
111	do_use_imm9_or_imm6 = 1'b0;
112	do_mem_read = 1'b0;
113	do_mem_write = 1'b0;
114	do_set_ext = 1'b0;
115
116	casez (ir_opcode)
117	4'b0000: begin // alu Rc, Ra, Rb
118		do_wreg_alu = 1'b1;
119	end
120	4'b0001: begin // expansion (nop)
121	end
122	4'b0010: begin // ext si12
123		do_set_ext = 1'b1;
124	end
125	4'b0011: begin // mov Rc, si9
126		do_wreg_alu = 1'b1;
127		do_adata_zero = 1'b1;
128		do_bdata_imm = 1'b1;
129		do_use_imm9_or_imm6 = 1'b1;
130	end
131	4'b0100: begin // lw Rc, [Ra, si6]
132		do_mem_read = 1'b1;
133		do_use_imm9_or_imm6 = 1'b0;
134	end
135	4'b0101: begin // sw Rc, [Ra, si6]
136		do_mem_write = 1'b1;
137		do_use_imm9_or_imm6 = 1'b0;
138	end
139	4'b0110: begin // b imm12 / bl imm12
140		do_branch_imm = 1'b1;
141		do_wr_link = ir[9];
142	end
143	4'b0111: begin // BZ/BNZ/B/BL
144		if (ir[5]) begin // b Ra / bl Ra
145			do_branch_reg = 1'b1;
146			do_wr_link = ir[4];
147		end else begin // bz imm7 / bnz imm7
148			do_branch_cond = 1'b1;
149			do_branch_zero = ~ir[4];
150		end
151	end
152	4'b1???: begin // alu Rc, Ra, si6
153		do_wreg_alu = 1'b1;
154		do_bdata_imm = 1'b1;
155		do_use_imm9_or_imm6 = 1'b0;
156	end
157	endcase
158end
159
160always_ff @(posedge clk) begin
161	if (ir_valid)
162		ir_ext_rdy <= do_set_ext;
163	if (ir_valid & do_set_ext)
164		ir_ext_imm <= ir_imm_s12[11:0];
165end
166
167wire [15:0]ex_adata;
168wire [15:0]ex_bdata;
169wire [15:0]ex_alu_rdata;
170
171regs16 regs(
172	.clk(clk),
173	.asel(ir_asel),
174	.bsel(do_mem_write ? ir_csel : ir_bsel),
175	.wsel(ex_wsel),
176	.wreg(ex_do_wreg_alu),
177	.adata(ex_adata),
178	.bdata(ex_bdata),
179	.wdata(ex_alu_rdata)
180	);
181
182// ---- EXECUTE ----
183
184reg [15:0]ex_branch_tgt = 16'b0;
185reg [2:0]ex_alu_op = 3'b0;
186reg [2:0]ex_wsel = 3'b0;
187reg ex_do_wreg_alu = 1'b0;
188reg ex_do_wreg_mem = 1'b0;
189reg ex_do_adata_zero = 1'b0;
190reg ex_do_bdata_imm = 1'b0;
191reg ex_do_wr_link = 1'b0;
192reg ex_do_branch_imm = 1'b0;
193reg ex_do_branch_reg = 1'b0;
194reg ex_do_branch_cond = 1'b0;
195reg ex_do_branch_zero = 1'b0;
196reg ex_do_mem_read = 1'b0;
197reg ex_do_mem_write = 1'b0;
198
199reg [15:0]ex_imm = 16'b0;
200
201always_ff @(posedge clk) begin
202	// for mem-read or mem-write we use the ALU for Ra + imm7
203	ex_alu_op <= (do_adata_zero | do_mem_read | do_mem_write) ? 3'b0 : ir_alu_op;
204	ex_wsel <= do_wr_link ? 3'd7 : ir_csel;
205	ex_branch_tgt <= pc + (do_branch_imm ? ir_imm_s11 : ir_imm_s7);
206	ex_do_wreg_alu <= do_wreg_alu;
207	ex_do_wreg_mem <= do_wreg_mem;
208	ex_do_adata_zero <= do_adata_zero;
209	ex_do_bdata_imm <= do_bdata_imm;
210	ex_do_wr_link <= do_wr_link;
211	ex_do_branch_imm <= do_branch_imm;
212	ex_do_branch_reg <= do_branch_reg;
213	ex_do_branch_cond <= do_branch_cond;
214	ex_do_branch_zero <= do_branch_zero;
215	ex_do_mem_read = do_mem_read;
216	ex_do_mem_write = do_mem_write;
217	ex_imm <= (do_mem_read | do_mem_write) ? ir_imm_s7 : (do_use_imm9_or_imm6 ? ir_imm_s9 : ir_imm_s6);
218end
219
220
221alu16 alu(
222	.op(ex_alu_op),
223	.xdata(ex_do_adata_zero ? 16'b0 : ex_adata),
224	.ydata((ex_do_mem_read | ex_do_mem_write | ex_do_bdata_imm) ? ex_imm : ex_bdata),
225	.rdata(ex_alu_rdata)
226	);
227
228assign dat_rw_addr = ex_alu_rdata;
229assign dat_wr_data = ex_bdata;
230assign dat_rd_req = ex_do_mem_read;
231assign dat_wr_req = ex_do_mem_write;
232
233// ---- SIMULATION DEBUG ASSIST ----
234
235`ifdef verilator
236reg [15:0]dbg_addr = 16'd0;
237wire [47:0]ir_dbg_dis;
238reg [47:0]ex_dbg_dis = 48'd0;
239
240assign ir_dbg_dis = { ir, 3'b0, ir_ext_rdy, ir_ext_imm, dbg_addr };
241
242always_ff @(posedge clk) begin
243	dbg_addr <= pc;
244	ex_dbg_dis <= ir_dbg_dis;
245end
246`endif
247
248endmodule
249
250module regs16(
251	input clk,
252	input [2:0]asel,
253	input [2:0]bsel,
254	input [2:0]wsel,
255	input wreg,
256	input [15:0]wdata,
257	output [15:0]adata,
258	output [15:0]bdata
259	);
260
261reg [15:0]rmem[0:7];
262reg [15:0]areg;
263reg [15:0]breg;
264
265always_ff @(posedge clk) begin
266	if (wreg)
267		rmem[wsel] <= wdata;
268	areg <= rmem[asel];
269	breg <= rmem[bsel];
270end
271
272assign adata = areg;
273assign bdata = breg;
274
275endmodule
276
277
278module alu16(
279	input [2:0]op,
280	input [15:0]xdata,
281	input [15:0]ydata,
282	output [15:0]rdata
283	);
284
285reg [15:0]r;
286
287always_comb begin
288	case (op)
289	3'b000: r = xdata + ydata;
290	3'b001: r = xdata - ydata;
291	3'b010: r = xdata & ydata;
292	3'b011: r = xdata | ydata;
293	3'b100: r = xdata ^ ydata;
294	3'b101: r = { {15 {1'b0}}, xdata < ydata };
295	3'b110: r = { {15 {1'b0}}, xdata >= ydata };
296	3'b111: r = xdata * ydata;
297	endcase
298end
299
300assign rdata = r;
301
302endmodule
303
304