`define FMT_R 3'b000 `define FMT_I 3'b001 `define FMT_L 3'b010 `define FMT_S 3'b011 `define FMT_J 3'b100 `define FMT_UD 3'b111 `define CWB_MEM_WR 0 `define CWB_MEM_EN 1 `define CWB_PC_WR 2 `define CWB_IR_WR 3 `define CWB_WB 4 `define CW_MEM_WR (16'b1 << `CWB_MEM_WR) `define CW_MEM_EN (16'b1 << `CWB_MEM_EN) `define CW_PC_WR (16'b1 << `CWB_PC_WR) `define CW_IR_WR (16'b1 << `CWB_IR_WR) `define CW_WB (16'b1 << `CWB_WB) function [15:0] lshift(input [15:0] value, input [15:0] shamt); if (shamt[15] == 1'b0) lshift = value << shamt; else lshift = value >> -shamt; endfunction module ALU( input [15:0] in1, input [15:0] in2, input [2:0] op, output reg [15:0] out ); wire signed [15:0] in1s = in1; wire signed [15:0] in2s = in2; always@ (*) begin case (op) 3'b000: out = in1 + in2; 3'b001: out = in1 - in2; 3'b010: out = in1 & in2; 3'b011: out = in1 | in2; 3'b100: out = lshift(in1, in2); 3'b101: out = in1 ^ in2; 3'b110: out = { 15'b0, (in1 < in2) }; 3'b111: out = { 15'b0, (in1s < in2s) }; endcase end endmodule module Processor( output [7:0] debug, output reg [15:0] mem_addr, inout [15:0] mem_data, output mem_wr, output mem_en, input mem_busy, input clk, input rst ); wire [15:0] alu_in2, alu_out; reg [15:0] regs [7:1]; reg [15:0] rd, pc, next_pc, instr; reg [15:0] control_word; reg [3:0] state = 0; reg [2:0] ifmt; reg [15:0] imm, imm2; wire [2:0] rd_sel = instr[15:12] != 4'b1110 ? instr[10:8] : 3'h7; wire [2:0] rs_sel = ifmt == `FMT_I ? instr[10:8] : instr[6:4]; wire [2:0] rt_sel = instr[2:0]; wire [2:0] alu_op = ifmt == `FMT_R ? { instr[11], instr[7], instr[3] } : { instr[11], 2'b00 }; wire [15:0] rs = regs[rs_sel]; wire [15:0] rt = regs[rt_sel]; wire [15:0] alu_in1 = rs; assign debug = control_word[7:0]; ALU _alu( .in1(alu_in1), .in2(alu_in2), .op(alu_op), .out(alu_out) ); always@ (posedge clk or posedge rst) begin if (rst) pc <= 16'b0; else if (control_word[`CWB_PC_WR] ) pc <= next_pc; end always@ (posedge clk or posedge rst) begin if (rst) instr <= 0; else if (control_word[`CWB_IR_WR]) instr <= mem_data; end always@ (negedge clk or posedge rst) begin if (rst) state <= 0; else begin if (mem_busy) control_word <= 0; else begin case (state) // Fetch 4'b0000: begin mem_addr <= pc; next_pc <= pc + 2; control_word <= `CW_MEM_EN | `CW_IR_WR | `CW_PC_WR; state <= state + 1; end // Execute 4'b0001: begin casez (instr[15:12]) // alu rd, rs, rt 4'b0000: begin rd <= alu_out; control_word <= `CW_WB; state <= 0; end // jalr rd, rt 4'b0001: begin next_pc <= rt; rd <= pc; control_word <= `CW_WB | `CW_PC_WR; state <= 0; end // alui rd, imm 4'b0010: begin rd <= alu_out; control_word <= `CW_WB; state <= 0; end // lui/li rd, imm 4'b0011: begin rd <= instr[11] ? imm : (imm << 8); control_word <= `CW_WB; state <= 0; end // lw rd, [rt, imm] 4'b0100: begin mem_addr <= rt; control_word <= `CW_MEM_EN; state <= state + 1; end // TODO: lb, lbu // sw rs, [rt, imm] 4'b1000: begin mem_addr <= rt; control_word <= `CW_MEM_WR; state <= 0; end // TODO: sb // bcc rs, rt, imm 4'b101?: begin if ((rs == rt) ^ instr[12]) begin next_pc <= pc + imm; control_word <= `CW_PC_WR; end else control_word <= 0; state <= 0; end // jal imm 4'b1110: begin rd <= pc; next_pc <= pc + imm; control_word <= `CW_WB | `CW_PC_WR; state <= 0; end // j imm 4'b1111: begin next_pc <= pc + imm; control_word <= `CW_PC_WR; state <= 0; end endcase end // case: 4'b0001 // WB Memory 4'b0010: begin case (instr[15:12]) 4'b0100: begin rd <= mem_data; control_word <= `CW_WB; state <= 0; end default: state <= 0; endcase end default: state <= 0; endcase end end end always@ (posedge clk) begin if (control_word[`CWB_WB] && rd_sel != 0) regs[rd_sel] <= rd; end always@ (*) begin casez (instr[15:12]) 4'b000?: ifmt = `FMT_R; 4'b001?: ifmt = `FMT_I; 4'b01??: ifmt = `FMT_L; 4'b10??: ifmt = `FMT_S; 4'b110?: ifmt = `FMT_UD; 4'b111?: ifmt = `FMT_J; endcase end always@ (*) begin case (ifmt) `FMT_I: imm2 = { {8{instr[7]}}, instr[7:0] }; `FMT_L: imm2 = { {10{instr[11]}}, instr[11], instr[7:3] }; `FMT_S: imm2 = { {10{instr[11]}}, instr[11:7], instr[3] }; `FMT_J: imm2 = { {4{instr[11]}}, instr[11:0] }; default: imm2 = 0; endcase casez (instr[15:12]) 4'b1000: imm = imm2 << 1; 4'b101?: imm = imm2 << 1; 4'b111?: imm = imm2 << 1; default: imm = imm2; endcase end assign alu_in2 = ifmt == `FMT_R ? rt : imm; assign mem_wr = control_word[`CWB_MEM_WR]; assign mem_en = control_word[`CWB_MEM_EN]; assign mem_data = control_word[`CWB_MEM_WR] ? rs : 16'bz; endmodule