From f9d92b31e4bea3037c75f7c7a508c1be09811fd0 Mon Sep 17 00:00:00 2001 From: kryptoish Date: Sat, 7 Dec 2024 22:12:43 -0800 Subject: Final Working Version --- src/alu.sv | 17 ++++ src/cpu.sv | 94 ++++++++++++++++++++ src/datapath.sv | 38 ++++++++ src/lab7bonus_top.sv | 79 +++++++++++++++++ src/regfile.sv | 54 +++++++++++ src/shifter.sv | 12 +++ src/statemachine.sv | 246 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 540 insertions(+) create mode 100644 src/alu.sv create mode 100644 src/cpu.sv create mode 100644 src/datapath.sv create mode 100644 src/lab7bonus_top.sv create mode 100644 src/regfile.sv create mode 100644 src/shifter.sv create mode 100644 src/statemachine.sv (limited to 'src') diff --git a/src/alu.sv b/src/alu.sv new file mode 100644 index 0000000..c6d00bd --- /dev/null +++ b/src/alu.sv @@ -0,0 +1,17 @@ +module ALU(Ain, Bin, op, out, status); + input [1:0] op; + input [15:0] Ain, Bin; + output [2:0] status; + output reg [15:0] out; + + assign status[2] = out[15]; + assign status[1] = (out[15] ^ Ain[15]) & ~(out[15] ^ Bin[15]); + assign status[0] = (out == 0); + + always_comb case (op) + 2'b00: out = Ain + Bin; + 2'b01: out = Ain - Bin; + 2'b10: out = Ain & Bin; + 2'b11: out = ~Bin; + endcase +endmodule: ALU diff --git a/src/cpu.sv b/src/cpu.sv new file mode 100644 index 0000000..0efcd6d --- /dev/null +++ b/src/cpu.sv @@ -0,0 +1,94 @@ +module cpu(clk, reset, mem_data, mem_cmd, mem_addr, out, N, V, Z, halt); + input clk, reset; + input [15:0] mem_data; + output N, V, Z, halt; + output [1:0] mem_cmd; + output [15:0] out; + output reg [8:0] mem_addr; + + wire pc_reset, pc_load, ir_load, addr_sel, write, loada, loadb, loadc, + loads, loadm, asel, bsel, csel; + wire [1:0] pc_sel, op, sh; + wire [2:0] opcode, cond, Rn, Rd, Rm, reg_w_sel, reg_a_sel, reg_b_sel; + wire [3:0] vsel; + wire [8:0] PC, data_address; + wire [15:0] instruction, sximm5, sximm8; + reg [2:0] reg_w, reg_a, reg_b; + reg [8:0] pc_next; + + register #(16) U0(mem_data, ir_load, clk, instruction); + register #(9) U1(pc_next, pc_load, clk, PC); + + statemachine FSM(clk, reset, opcode, op, pc_reset, pc_load, pc_sel, + ir_load, addr_sel, mem_cmd, reg_w_sel, reg_a_sel, + reg_b_sel, write, loada, loadb, loadc, loads, loadm, asel, bsel, + csel, vsel, halt); + + datapath DP(clk, reg_w, reg_a, reg_b, write, loada, loadb, loadc, loads, + loadm, op, sh, asel, bsel, csel, vsel, sximm5, sximm8, + mem_data, PC, N, V, Z, out, data_address); + + assign mem_addr = addr_sel ? PC : data_address; + + /* Instruction decoder. */ + assign {opcode, op, Rn, Rd, sh, Rm} = instruction; + assign cond = instruction[10:8]; + assign sximm8 = {{8{instruction[7]}}, instruction[7:0]}; + assign sximm5 = {{11{instruction[4]}}, instruction[4:0]}; + + always_comb begin + /* Selector for REGFILE read/write registers. */ + case (reg_w_sel) + 3'b100: reg_w = Rn; + 3'b010: reg_w = Rd; + 3'b001: reg_w = Rm; + default: reg_w = 3'bzzz; + endcase + case (reg_a_sel) + 3'b100: reg_a = Rn; + 3'b010: reg_a = Rd; + 3'b001: reg_a = Rm; + default: reg_a = 3'bzzz; + endcase + case (reg_b_sel) + 3'b100: reg_b = Rn; + 3'b010: reg_b = Rd; + 3'b001: reg_b = Rm; + default: reg_b = 3'bzzz; + endcase + + /* Branching logic. */ + casex ({pc_reset, pc_sel}) + 3'b1_xx: + pc_next = 9'b0; + 3'b0_01: begin + pc_next = PC; + case ({opcode, cond}) + /* B. */ + 6'b001_000: + pc_next = PC + sximm8[8:0]; + /* BEQ. */ + 6'b001_001: if (Z) + pc_next = PC + sximm8[8:0]; + /* BNE. */ + 6'b001_010: if (~Z) + pc_next = PC + sximm8[8:0]; + /* BLT. */ + 6'b001_011: if (N !== V) + pc_next = PC + sximm8[8:0]; + /* BLE. */ + 6'b001_100: if (N !== V | Z) + pc_next = PC + sximm8[8:0]; + endcase + end + /* BX, BLX. */ + 3'b0_10: + pc_next = out; + /* BL. */ + 3'b0_11: + pc_next = PC + sximm8[8:0]; + /* Next address in memory. */ + default: pc_next = PC + 1; + endcase + end +endmodule: cpu diff --git a/src/datapath.sv b/src/datapath.sv new file mode 100644 index 0000000..f0e85a5 --- /dev/null +++ b/src/datapath.sv @@ -0,0 +1,38 @@ +module datapath(clk, reg_w, reg_a, reg_b, write, loada, loadb, loadc, loads, + loadm, op, shift, asel, bsel, csel, vsel, sximm5, sximm8, + mdata, PC, N, V, Z, datapath_out, data_address); + input clk, write, loada, loadb, loadc, loads, loadm, asel, bsel, csel; + input [1:0] op, shift; + input [2:0] reg_w, reg_a, reg_b; + input [3:0] vsel; + input [8:0] PC; + input [15:0] sximm8, sximm5, mdata; + output N, V, Z; + output [8:0] data_address; + output reg [15:0] datapath_out; + + wire [2:0] status; + wire [15:0] out_a, out_b, aout, bout, sout, ain, bin, out; + reg [15:0] data_in; + + regfile REGFILE(clk, data_in, write, reg_w, reg_a, reg_b, out_a, out_b); + shifter U0(bout, shift, sout); + ALU U1(ain, bin, op, out, status); + register #(16) REG_A(out_a, loada, clk, aout); + register #(16) REG_B(out_b, loadb, clk, bout); + register #(16) REG_C((csel ? out_b : out), loadc, clk, datapath_out); + register #(9) REG_M(out[8:0], loadm, clk, data_address); + register #(3) REG_S(status, loads, clk, {N, V, Z}); + + assign ain = asel ? 16'b0 : aout; + assign bin = bsel ? sximm5 : sout; + + /* One-hot signal: vsel. */ + always_comb begin + data_in = {16{1'bz}}; + if (vsel[0]) data_in = datapath_out; + if (vsel[1]) data_in = mdata; + if (vsel[2]) data_in = sximm8; + if (vsel[3]) data_in = {7'b0, PC}; + end +endmodule: datapath diff --git a/src/lab7bonus_top.sv b/src/lab7bonus_top.sv new file mode 100644 index 0000000..2a9e805 --- /dev/null +++ b/src/lab7bonus_top.sv @@ -0,0 +1,79 @@ +`define M_NOP 2'b00 +`define M_READ 2'b10 +`define M_WRITE 2'b01 + +module lab7bonus_top(KEY, SW, LEDR, HEX0, HEX1, HEX2, HEX3, HEX4, HEX5, CLOCK_50); + input CLOCK_50; + input [3:0] KEY; + input [9:0] SW; + output [6:0] HEX0, HEX1, HEX2, HEX3, HEX4, HEX5; + output [9:0] LEDR; + + wire clk, write, N, V, Z, halt; + wire [1:0] mem_cmd; + wire [8:0] mem_addr; + wire [15:0] din, dout, mem_data; + + ram #(16, 8) MEM(clk, mem_addr[7:0], write, din, dout); + cpu CPU(clk, reset, mem_data, mem_cmd, mem_addr, din, N, V, Z, halt); + + disp U0(din[3:0], HEX0); + disp U1(din[7:4], HEX1); + disp U2(din[11:8], HEX2); + disp U3(din[15:12], HEX3); + disp U4({1'b0, N, V, Z}, HEX4); + + assign clk = CLOCK_50; + assign reset = ~KEY[1]; + assign LEDR[8] = halt; + + assign mem_data = (mem_cmd == `M_READ & ~mem_addr[8]) + ? dout : {16{1'bz}}; + assign write = mem_cmd == `M_WRITE & ~mem_addr[8]; +endmodule: lab7bonus_top + +module ram(clk, addr, write, din, dout); + parameter data_width = 32; + parameter addr_width = 4; + parameter filename = "data.txt"; + + input clk; + input [addr_width-1:0] addr; + input write; + input [data_width-1:0] din; + output [data_width-1:0] dout; + reg [data_width-1:0] dout; + + reg [data_width-1:0] mem [2**addr_width-1:0]; + + initial $readmemb(filename, mem); + + always @ (posedge clk) begin + if (write) mem[addr] <= din; + dout <= mem[addr]; + end +endmodule: ram + +module disp(in, out); + input [3:0] in; + output reg [6:0] out; + + always_comb case (in) + 4'h0: out = 7'b1000000; + 4'h1: out = 7'b1111001; + 4'h2: out = 7'b0100100; + 4'h3: out = 7'b0110000; + 4'h4: out = 7'b0011001; + 4'h5: out = 7'b0010010; + 4'h6: out = 7'b0000010; + 4'h7: out = 7'b1111000; + 4'h8: out = 7'b0000000; + 4'h9: out = 7'b0010000; + 4'ha: out = 7'b0001000; + 4'hb: out = 7'b0000011; + 4'hc: out = 7'b0100111; + 4'hd: out = 7'b0100001; + 4'he: out = 7'b0000110; + 4'hf: out = 7'b0001110; + endcase +endmodule: disp diff --git a/src/regfile.sv b/src/regfile.sv new file mode 100644 index 0000000..7c53807 --- /dev/null +++ b/src/regfile.sv @@ -0,0 +1,54 @@ +module regfile(clk, data_in, write, reg_w, reg_a, reg_b, out_a, out_b); + input clk, write; + input [2:0] reg_w, reg_a, reg_b; + input [15:0] data_in; + output reg [15:0] out_a, out_b; + + wire [7:0] load; + wire [15:0] R0, R1, R2, R3, R4, R5, R6, R7; + + register #(16) U0(data_in, load[0], clk, R0); + register #(16) U1(data_in, load[1], clk, R1); + register #(16) U2(data_in, load[2], clk, R2); + register #(16) U3(data_in, load[3], clk, R3); + register #(16) U4(data_in, load[4], clk, R4); + register #(16) U5(data_in, load[5], clk, R5); + register #(16) U6(data_in, load[6], clk, R6); + register #(16) U7(data_in, load[7], clk, R7); + + assign load = {8{write}} & (8'b1 << reg_w); + + always_comb begin + case (reg_a) + 0: out_a = R0; + 1: out_a = R1; + 2: out_a = R2; + 3: out_a = R3; + 4: out_a = R4; + 5: out_a = R5; + 6: out_a = R6; + 7: out_a = R7; + endcase + + case (reg_b) + 0: out_b = R0; + 1: out_b = R1; + 2: out_b = R2; + 3: out_b = R3; + 4: out_b = R4; + 5: out_b = R5; + 6: out_b = R6; + 7: out_b = R7; + endcase + end +endmodule: regfile + +module register(in, load, clk, out); + parameter n = 1; + input [n-1:0] in; + input load, clk; + output reg [n-1:0] out; + + always_ff @(posedge clk) + if (load) out = in; +endmodule: register diff --git a/src/shifter.sv b/src/shifter.sv new file mode 100644 index 0000000..66e5c74 --- /dev/null +++ b/src/shifter.sv @@ -0,0 +1,12 @@ +module shifter(in, shift, sout); + input [1:0] shift; + input [15:0] in; + output reg [15:0] sout; + + always_comb case (shift) + 2'b00: sout = in; + 2'b01: sout = {in[14:0], 1'b0}; + 2'b10: sout = {1'b0, in[15:1]}; + 2'b11: sout = {in[15], in[15:1]}; + endcase +endmodule: shifter diff --git a/src/statemachine.sv b/src/statemachine.sv new file mode 100644 index 0000000..1a5beb1 --- /dev/null +++ b/src/statemachine.sv @@ -0,0 +1,246 @@ +`define STATE_RESET 3'b000 +`define STATE_HALT 3'b001 +`define STATE_IF 3'b010 +`define STATE_DECODE 3'b011 +`define STATE_EXEC 3'b100 +`define STATE_MEM 3'b101 +`define STATE_WRITEBACK 3'b110 + +module statemachine(clk, reset, opcode, op, pc_reset, pc_load, pc_sel, ir_load, + addr_sel, mem_cmd, reg_w_sel, reg_a_sel, reg_b_sel, write, + loada, loadb, loadc, loads, loadm, asel, bsel, csel, vsel, + halt); + input clk, reset; + input [1:0] op; + input [2:0] opcode; + output reg pc_reset, pc_load, ir_load, addr_sel, write, loada, loadb, + loadc, loads, loadm, asel, bsel, csel, halt; + output reg [1:0] pc_sel, mem_cmd; + output reg [2:0] reg_w_sel, reg_a_sel, reg_b_sel; + output reg [3:0] vsel; + + reg [3:0] state; + + always_comb begin + {pc_reset, pc_load, pc_sel, ir_load, addr_sel, mem_cmd, + reg_w_sel, reg_a_sel, reg_b_sel, write, loada, loadb, + loadc, loads, loadm, asel, bsel, csel, vsel, halt} + = 31'b0; + + casex ({state, opcode, op}) + /* Reset state. */ + {`STATE_RESET, 5'bxxx_xx}: + {pc_reset, pc_load, addr_sel, mem_cmd} + = 5'b111_10; + + /* Instruction fetch. */ + {`STATE_IF, 5'bxxx_xx}: + {pc_load, ir_load, addr_sel, mem_cmd} = 5'b111_10; + + /* MOV immediate to register. */ + {`STATE_DECODE, 5'b110_10}: + {addr_sel, mem_cmd, reg_w_sel, write, vsel} + = 11'b1_10_100_1_0100; + + /* MOV register to register. */ + {`STATE_DECODE, 5'b110_00}: + {reg_b_sel, loadb} = 4'b001_1; + {`STATE_EXEC, 5'b110_00}: + {loadc, asel} = 2'b11; + {`STATE_WRITEBACK, 5'b110_00}: + {addr_sel, mem_cmd, reg_w_sel, write, vsel} + = 11'b1_10_010_1_0001; + + /* General ALU operations. */ + {`STATE_DECODE, 5'b101_xx}: + {reg_a_sel, reg_b_sel, loada, loadb} = 8'b100_001_11; + + /* ADD. */ + {`STATE_EXEC, 5'b101_00}: + loadc = 1'b1; + {`STATE_WRITEBACK, 5'b101_00}: + {addr_sel, mem_cmd, reg_w_sel, write, vsel} + = 11'b1_10_010_1_0001; + + /* CMP. */ + {`STATE_EXEC, 5'b101_01}: + {addr_sel, mem_cmd, loads} = 4'b1_10_1; + + /* AND. */ + {`STATE_EXEC, 5'b101_10}: + loadc = 1'b1; + {`STATE_WRITEBACK, 5'b101_10}: + {addr_sel, mem_cmd, reg_w_sel, write, vsel} + = 11'b1_10_010_1_0001; + + /* MVN. */ + {`STATE_EXEC, 5'b101_11}: + loadc = 1'b1; + {`STATE_WRITEBACK, 5'b101_11}: + {addr_sel, mem_cmd, reg_w_sel, write, vsel} + = 11'b1_10_010_1_0001; + + /* LDR. */ + {`STATE_DECODE, 5'b011_xx}: + {reg_a_sel, loada} = 4'b100_1; + {`STATE_EXEC, 5'b011_xx}: + {loadm, bsel} = 2'b11; + {`STATE_MEM, 5'b011_xx}: + mem_cmd = 2'b10; + {`STATE_WRITEBACK, 5'b011_xx}: + {addr_sel, mem_cmd, reg_w_sel, write, vsel} + = 11'b1_10_010_1_0010; + + /* STR. */ + {`STATE_DECODE, 5'b100_xx}: + {reg_a_sel, reg_b_sel, csel, loada, loadc} + = 9'b100_010_111; + {`STATE_EXEC, 5'b100_xx}: + {loadm, bsel} = 2'b11; + {`STATE_MEM, 5'b100_xx}: + mem_cmd = 2'b01; + {`STATE_WRITEBACK, 5'b100_xx}: + {addr_sel, mem_cmd} = 3'b1_10; + + /* B, BEQ, BNE, BLT, BLE. */ + {`STATE_DECODE, 5'b001_xx}: + {pc_load, pc_sel} = 3'b1_01; + {`STATE_EXEC, 5'b001_xx}: + {addr_sel, mem_cmd} = 3'b1_10; + + /* BL. */ + {`STATE_DECODE, 5'b010_11}: + {pc_load, pc_sel, reg_w_sel, write, vsel} + = 11'b1_11_100_1_1000; + {`STATE_EXEC, 5'b010_11}: + {addr_sel, mem_cmd} = 3'b1_10; + + /* BX, BLX. */ + {`STATE_DECODE, 5'b010_xx}: + {reg_b_sel, loadc, csel} = 5'b010_11; + + /* BX. */ + {`STATE_EXEC, 5'b010_00}: + {pc_load, pc_sel} = 3'b1_10; + {`STATE_WRITEBACK, 5'b010_00}: + {addr_sel, mem_cmd} = 3'b1_10; + + /* BLX. */ + {`STATE_EXEC, 5'b010_10}: + {pc_load, pc_sel, reg_w_sel, write, vsel} + = 11'b1_10_100_1_1000; + {`STATE_WRITEBACK, 5'b010_10}: + {addr_sel, mem_cmd} + = 3'b1_10; + + {`STATE_HALT, 5'bxxx_xx}: + halt = 1'b1; + + default: begin end + endcase + end + + always_ff @(posedge clk) casex ({reset, state, opcode, op}) + /* Reset. */ + 10'b1_xxxx_xxx_xx: + state <= `STATE_RESET; + {1'b0, `STATE_RESET, 5'bxxx_xx}: + state <= `STATE_IF; + + /* Beginning of instruction cycle. */ + {1'b0, `STATE_IF, 5'bxxx_xx}: + state <= `STATE_DECODE; + + /* HALT. */ + {1'b0, `STATE_DECODE, 5'b111_xx}: + state <= `STATE_HALT; + {1'b0, `STATE_HALT, 5'bxxx_xx}: + state <= `STATE_HALT; + + /* MOV immediate to register. */ + {1'b0, `STATE_DECODE, 5'b110_10}: + state <= `STATE_IF; + + /* MOV register to register. */ + {1'b0, `STATE_DECODE, 5'b110_00}: + state <= `STATE_EXEC; + {1'b0, `STATE_EXEC, 5'b110_00}: + state <= `STATE_WRITEBACK; + {1'b0, `STATE_WRITEBACK, 5'b110_00}: + state <= `STATE_IF; + + /* ADD. */ + {1'b0, `STATE_DECODE, 5'b101_00}: + state <= `STATE_EXEC; + {1'b0, `STATE_EXEC, 5'b101_00}: + state <= `STATE_WRITEBACK; + {1'b0, `STATE_WRITEBACK, 5'b101_00}: + state <= `STATE_IF; + + /* CMP. */ + {1'b0, `STATE_DECODE, 5'b101_01}: + state <= `STATE_EXEC; + {1'b0, `STATE_EXEC, 5'b101_01}: + state <= `STATE_IF; + + /* AND. */ + {1'b0, `STATE_DECODE, 5'b101_10}: + state <= `STATE_EXEC; + {1'b0, `STATE_EXEC, 5'b101_10}: + state <= `STATE_WRITEBACK; + {1'b0, `STATE_WRITEBACK, 5'b101_10}: + state <= `STATE_IF; + + /* MVN. */ + {1'b0, `STATE_DECODE, 5'b101_11}: + state <= `STATE_EXEC; + {1'b0, `STATE_EXEC, 5'b101_11}: + state <= `STATE_WRITEBACK; + {1'b0, `STATE_WRITEBACK, 5'b101_11}: + state <= `STATE_IF; + + /* LDR. */ + {1'b0, `STATE_DECODE, 5'b011_xx}: + state <= `STATE_EXEC; + {1'b0, `STATE_EXEC, 5'b011_xx}: + state <= `STATE_MEM; + {1'b0, `STATE_MEM, 5'b011_xx}: + state <= `STATE_WRITEBACK; + {1'b0, `STATE_WRITEBACK, 5'b011_xx}: + state <= `STATE_IF; + + /* STR. */ + {1'b0, `STATE_DECODE, 5'b100_xx}: + state <= `STATE_EXEC; + {1'b0, `STATE_EXEC, 5'b100_xx}: + state <= `STATE_MEM; + {1'b0, `STATE_MEM, 5'b100_xx}: + state <= `STATE_WRITEBACK; + {1'b0, `STATE_WRITEBACK, 5'b100_xx}: + state <= `STATE_IF; + + /* B, BEQ, BNE, BLT, BLE. */ + {1'b0, `STATE_DECODE, 5'b001_xx}: + state <= `STATE_EXEC; + {1'b0, `STATE_EXEC, 5'b001_xx}: + state <= `STATE_IF; + + /* BL. */ + {1'b0, `STATE_DECODE, 5'b010_11}: + state <= `STATE_EXEC; + {1'b0, `STATE_EXEC, 5'b010_11}: + state <= `STATE_IF; + + /* BX, BLX. */ + {1'b0, `STATE_DECODE, 5'b010_xx}: + state <= `STATE_EXEC; + {1'b0, `STATE_EXEC, 5'b010_xx}: + state <= `STATE_WRITEBACK; + {1'b0, `STATE_WRITEBACK, 5'b010_xx}: + state <= `STATE_IF; + + /* Should not happen. Otherwise, an error occurred. */ + default: + state <= `STATE_RESET; + endcase +endmodule: statemachine -- cgit v1.2.3