diff --git a/ModelSim/ByteIM.sv b/ModelSim/ByteIM.sv new file mode 100644 index 0000000..6e913a7 --- /dev/null +++ b/ModelSim/ByteIM.sv @@ -0,0 +1,14 @@ +`include "rom.sv" + +module ByteIM #(parameter m=5,n=2) ( + input logic [m-1:0] Ad, + output logic [(2**n)*8-1:0] Dout ); + + // 2**n byte wide words + // m bit wide byte address + // 2**m bytes of memory + // 2**(m-n) memory words + // 2**(n+3) bit wide words + // Reads the whole word that contains the addressed byte + ROM #(m-n,2**(n+3)) wordrom(Ad[m-1:n],Dout) +endmodule diff --git a/ModelSim/alu.sv b/ModelSim/alu.sv index 2687745..4bc9398 100644 --- a/ModelSim/alu.sv +++ b/ModelSim/alu.sv @@ -8,11 +8,13 @@ // flags[1:1] = Overflow module alu #(parameter width=2)(input logic [3:0] op, input logic [2**width-1:0] A, input logic [2**width-1:0] B, - output logic [2**width-1:0] OUT_1, output logic[1:0] flags); - + output logic [2**width-1:0] OUT_1, output logic[2:0] flags + ); + // intermediate logic for handling asu output logic [0:0] asu_op; logic [2**width-1:0] asu_out; - logic [1:0] asu_flags; + logic [2:0] asu_flags; + asu #(width) adder_unit(asu_op[op[1]], A, B, asu_out, asu_flags[0], asu_flags[1]); logic [2**width-1:0] lu_out; @@ -32,6 +34,7 @@ module alu #(parameter width=2)(input logic [3:0] op, 4'b1011: begin OUT_1 = ~asu_flags[0] & {{(width-1){1'b0}}, 1'b1}; flags = asu_flags; end default: begin OUT_1 = {(2**width){1'b0}}; flags = asu_flags; end endcase + asu_flags[2] = OUT_1 == 0; end endmodule -`endif \ No newline at end of file +`endif diff --git a/ModelSim/assignment2.sv b/ModelSim/assignment2.sv new file mode 100644 index 0000000..36426c5 --- /dev/null +++ b/ModelSim/assignment2.sv @@ -0,0 +1,107 @@ +`ifndef ASS_2_SV +`define ASS_2_SV + +`include "rom.sv" +`include "ram.sv" +`include "register_file.sv" +`include "instruction_counter.sv" +`include "control_unit.sv" +`include "alu.sv" +`include "shifter.sv" +`include "sign_extender.sv" +`include "seven_seg_4digit.sv" + + +module assignment2 #(width=5, ramsz=9, romsz=10) ( + input logic [0:0] clk +`ifdef QUARTUS_SYNTH + , + input logic [4:0] HID_Register, + input logic [0:0] HID_PC, + input logic [0:0] HID_Hi_Low, + + output logic[7:0] out_seg_0, + output logic[7:0] out_seg_1, + output logic[7:0] out_seg_2, + output logic[7:0] out_seg_3 +`endif + ); + // Intermediate for displaying high / low of PC or Register + logic[15:0] PC_Reg_Display; + + // Program / Instruction Counter Intermediates + logic [romsz-1:0] pc_addr; + logic [romsz-1:0] pc_addr_write; + logic [0:0] pc_we; + logic [2**width-1:0] instruction; + + // Instruction Classifier Intermediates + logic [0:0] M2R; + logic [0:0] M2W; + logic [0:0] B; + logic [3:0] ALU_FN; + logic [0:0] ALU_SRC; + logic [0:0] RDST; + logic [0:0] RW; + + // Register File intermediates + logic [2:0] flags; + logic [2**width-1:0] rs; + logic [2**width-1:0] rt; + logic [2**width-1:0] rd; + logic [0:0] rd_we; + logic [31:0] regs_out [32]; + + instruction_counter #(width) PC (clk, pc_we, pc_addr_write, pc_addr); + + rom #(romsz, 2**width) ROM (pc_addr, instruction); + + logic [2**width-1:0] rd_mem; + ram #(ramsz, 2**width) RAM (clk, rd_alu, M2W, rt, rd_mem); + + control_unit CONTROL_UNIT (instruction[31:26], instruction[5:0], + M2R, M2W, B, ALU_FN, ALU_SRC, RDST, RW + ); + + + register_file #(width, 5) REGS ( + clk, rd_we, + instruction[20:16], rd, + (RDST) ? instruction[15:11] : instruction[20:16], rt, + instruction[25:21], rs, + regs_out + ); + // Always use sign-extension + logic [2**width-1:0] rt_imm; + sign_extender #(4, width) SIGNEXT(1'b1, instruction[15:0], rt_imm); + // TODO: + // - ALU srcB + // - PCBranch (rel jump of 4-byte words) + + logic [2**width-1:0] rd_alu; + logic [2**width-1:0] rt_alu; + alu #(width) ALU (ALU_FN, rs, rt_alu, rd_alu, flags[1:0]); + assign rt_alu = (ALU_SRC) ? (rt_imm) : (rt); + + assign pc_we = B & flags[2]; + assign pc_addr_write = (rt_imm << 2) + pc_addr; + assign rd_we = RW; + assign rd = (M2R) ? rd_alu : rd_mem; + + + + // Temporary Test output + // assign PC_Reg_Display = 16'hC342; + + assign PC_Reg_Display = (HID_Hi_Low) ? regs_out[HID_Register][31:16] : regs_out[HID_Register][15:0]; + + seven_seg_4digit display ( + .num(PC_Reg_Display), + .segments0(out_seg_0), + .segments1(out_seg_1), + .segments2(out_seg_2), + .segments3(out_seg_3) + ); + +endmodule +`endif diff --git a/ModelSim/asu.sv b/ModelSim/asu.sv index 5b0440e..429db12 100644 --- a/ModelSim/asu.sv +++ b/ModelSim/asu.sv @@ -4,9 +4,11 @@ module asu #(parameter width = 2)( input logic [0:0] op, input logic[2**width-1:0] A, input logic[2**width-1:0] B, - output logic[2**width-1:0] OUT_1, output logic[0:0] COUT, output logic[0:0] OUT_CIN); + output logic[2**width-1:0] OUT_1, output logic[0:0] COUT, output logic[0:0] OUT_CIN + ); logic [2**width-1:0] operand; + always_comb begin // A + (sub) ? B : -B // Adds an extra bit to all operands to calculate the carry bit. Not optimal for hardware synthesis @@ -18,5 +20,6 @@ module asu #(parameter width = 2)( // Need to do this outside of the always_comb block because parameter-based bitslicing/indexing isn't supported? // Cin for final bit in adder to later be xor'ed with COUT to determine if a signed overflow occured for the MIPS architecture assign OUT_CIN = A[2**width-1] ^ operand[2**width-1] ^ OUT_1[2**width-1]; + endmodule -`endif \ No newline at end of file +`endif diff --git a/ModelSim/control_unit.sv b/ModelSim/control_unit.sv new file mode 100644 index 0000000..85a673c --- /dev/null +++ b/ModelSim/control_unit.sv @@ -0,0 +1,36 @@ +`ifndef CONTROL_UNIT_H +`define CONTROL_UNIT_H +`include "instruction_classifier.sv" + +module control_unit ( + input logic [5:0] op, input logic [5:0] func, + output logic [0:0] M2R, // Enables writing data stored in memory at the address (rs/A)+(ALU_SRC ? imm : rt/B) into rd + output logic [0:0] M2W, // Enable writing data stored in rt/B into memory at the address (rs/A)+(ALU_SRC ? imm : rt/B) + output logic [0:0] B, // AND'ed with flags to determine if branch is permitted + output logic [3:0] ALU_FN, // Controls what function the ALU is to perform + output logic [0:0] ALU_SRC, // Controls whether to use the rt/B register read port or 16-bit immediate + output logic [0:0] RDST, // Controls where to look in the instruction encoding for the register write address + output logic [0:0] RW // Enable register write port +); + logic [0:0] TYPE_R; + logic [0:0] TYPE_B1; + logic [0:0] TYPE_J; + logic [0:0] TYPE_B2; + logic [0:0] TYPE_I; + logic [0:0] TYPE_F; + logic [0:0] TYPE_M; + + instruction_classifier CLASSIFIER (op, TYPE_R, TYPE_B1, TYPE_J, TYPE_B2, TYPE_I, TYPE_F, TYPE_M); + + always_comb begin + M2R = TYPE_M & op[3]; + M2W = TYPE_M & ~op[3]; + B = TYPE_B1 | TYPE_B2 | TYPE_J; + ALU_FN = (func & {(6){TYPE_R}}) | ( {4'b0000, op[1:0]} & {(6){TYPE_I}}); + ALU_SRC = TYPE_B1 | TYPE_B2 | TYPE_I | TYPE_M; + RDST = TYPE_I; // Imm ops store back into rt + RW = TYPE_R | TYPE_I | (TYPE_M & op[3]); // All ALU R-type ops, Addi/Addiu ops, and memory loads + end + +endmodule +`endif diff --git a/ModelSim/instruction_counter.sv b/ModelSim/instruction_counter.sv new file mode 100644 index 0000000..780b99a --- /dev/null +++ b/ModelSim/instruction_counter.sv @@ -0,0 +1,21 @@ +`ifndef INSTRUCTION_COUNTER_H +`define INSTRUCTION_COUNTER_H + +module instruction_counter #(parameter width=5)( + input logic [0:0] clk, input logic [0:0] we, + input logic [2**width-1:0] W, + output logic [2**width-1:0] addr +); + + logic [2**width-1:0] pc; + always_ff @(posedge clk) begin + if (we) begin + pc <= W; + end else begin + pc <= pc + 4; + end + end + + assign addr = pc; +endmodule +`endif diff --git a/ModelSim/lu.sv b/ModelSim/lu.sv index ce372a3..e908a4c 100644 --- a/ModelSim/lu.sv +++ b/ModelSim/lu.sv @@ -1,13 +1,13 @@ `ifndef LU_H_SV `define LU_H_SV module lu #(parameter width = 2) ( - input logic [1:0] op, - input logic [2**width-1:0] A, + input logic [1:0] op, // 2 bit operand + input logic [2**width-1:0] A, // width defined as 2^n input logic [2**width-1:0] B, output logic [2**width-1:0] Y); always_comb begin - case (op) + case (op) // different operations of A and B based on operand 2'b00: Y = A & B; 2'b01: Y = A | B; 2'b10: Y = A ^ B; @@ -16,4 +16,4 @@ module lu #(parameter width = 2) ( endcase end endmodule -`endif \ No newline at end of file +`endif diff --git a/ModelSim/mips.sv b/ModelSim/mips.sv new file mode 100644 index 0000000..4ff213c --- /dev/null +++ b/ModelSim/mips.sv @@ -0,0 +1,95 @@ +`include "assignment2.sv" + +`define PRINT_REGISTERS(regs)\ + $display("\tt0-t8: \t0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x\n\ts0-s8: \t0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x 0x%0x\n",\ + regs[8], regs[9], regs[10], regs[11], regs[12], regs[13], regs[14], regs[15], \ + regs[16], regs[17], regs[18], regs[19], regs[20], regs[21], regs[22], regs[23] \ + ); +`define TEST_CHECK(cond) if (cond) begin end else begin \ + $display("Check %s failed\n", `"cond`"); \ + `PRINT_REGISTERS(CPU.REGS.regs); \ + num_errors += 1; \ + end + +module mips_tb(input logic [0:0] clk); + assignment2 #(5) CPU (clk); + + integer num_errors = 0; + initial begin + // Zero out lower registers + $display("Beginning MIPS tests"); + assign num_errors = 0; + + for (integer N = 1; N < 32; ++N) + CPU.REGS.regs[N] = 32'd0; + + // Check R-type / initialise + #1; + `TEST_CHECK(CPU.REGS.regs[8] == 32'd0); + #1; + `TEST_CHECK(CPU.REGS.regs[9] == (CPU.REGS.regs[8] + 32'd7)); + + // Check load-store + #1; + `TEST_CHECK(CPU.RAM.mem[0] == CPU.REGS.regs[9]); + #1; + `TEST_CHECK(CPU.REGS.regs[10] == CPU.RAM.mem[0]); + + // Check arithmetic + #1; + `TEST_CHECK(CPU.REGS.regs[11] == (CPU.REGS.regs[9] + CPU.REGS.regs[10])); + #1; + `TEST_CHECK(CPU.REGS.regs[12] == (CPU.REGS.regs[9] - CPU.REGS.regs[11])); + #1; + `TEST_CHECK(CPU.REGS.regs[13] == (CPU.REGS.regs[9] + 3)); + #1; + `TEST_CHECK(CPU.REGS.regs[14] == (CPU.REGS.regs[12] + CPU.REGS.regs[13])); + + // Check logic instructions + #1; + `TEST_CHECK(CPU.REGS.regs[16] == (CPU.REGS.regs[9] & CPU.REGS.regs[13])); + #1; + `TEST_CHECK(CPU.REGS.regs[17] == (CPU.REGS.regs[9] | CPU.REGS.regs[13])); + #1; + `TEST_CHECK(CPU.REGS.regs[18] == (CPU.REGS.regs[9] ^ CPU.REGS.regs[13])); + #1; + `TEST_CHECK(CPU.REGS.regs[19] == ( ~(CPU.REGS.regs[9] | CPU.REGS.regs[13]) )); + + // Check SLT instructions + #1; + `TEST_CHECK(CPU.REGS.regs[20] == $signed(CPU.REGS.regs[12]) < $signed(CPU.REGS.regs[14])); + #1; + `TEST_CHECK(CPU.REGS.regs[21] == $unsigned(CPU.REGS.regs[12]) < $unsigned(CPU.REGS.regs[14])); + + // Check loop branching + for (integer N = 1; N < 11; ++N) begin + #1; + `TEST_CHECK(CPU.REGS.regs[0] == 32'(N)); + #1; + `TEST_CHECK(CPU.REGS.regs[22] == $unsigned(CPU.REGS.regs[8]) < $unsigned(CPU.REGS.regs[13])); + end + + // Final state check + #1; + `TEST_CHECK(CPU.REGS.regs[8] == 32'd10); + `TEST_CHECK(CPU.REGS.regs[9] == 32'd7); + `TEST_CHECK(CPU.REGS.regs[10] == 32'd7); + `TEST_CHECK(CPU.REGS.regs[11] == 32'd14); + `TEST_CHECK(CPU.REGS.regs[12] == -32'sd7); + `TEST_CHECK(CPU.REGS.regs[13] == 32'd10); + `TEST_CHECK(CPU.REGS.regs[14] == 32'd3); + `TEST_CHECK(CPU.REGS.regs[15] == 32'd0); + + `TEST_CHECK(CPU.REGS.regs[16] == 32'd2); + `TEST_CHECK(CPU.REGS.regs[17] == 32'd15); + `TEST_CHECK(CPU.REGS.regs[18] == 32'd13); + `TEST_CHECK(CPU.REGS.regs[19] == -32'sd16); + `TEST_CHECK(CPU.REGS.regs[20] == 32'd1); + `TEST_CHECK(CPU.REGS.regs[21] == 32'd0); + `TEST_CHECK(CPU.REGS.regs[22] == 32'd0); + `TEST_CHECK(CPU.REGS.regs[23] == 32'd0); + + if (num_errors == 0) $display("\x1b[38;5;10mAll MIPS tests passing\x1b[m"); + else $display("\x1b[38;5;9m%0d MIPS test(s) failing\x1b[m", num_errors); + end +endmodule \ No newline at end of file diff --git a/ModelSim/ram.sv b/ModelSim/ram.sv new file mode 100644 index 0000000..eaddf77 --- /dev/null +++ b/ModelSim/ram.sv @@ -0,0 +1,31 @@ +`ifndef RAM_H +`define RAM_H + +module ram #(addrwidth=9, datawidth=5)( + input logic [0:0] clk, + input logic [2**addrwidth-1:0] addr, + input logic [0:0] we, + input logic [datawidth-1:0] data_in, + output logic [datawidth-1:0] data_out +); + + // TODO: Make this byte-sized, NOT word-sized! + logic [2**datawidth-1:0] mem[(2**(addrwidth+$clog2(datawidth)-3))-1:0]; + + // NOTE: Possible issue with assignments still pending by end of initial block + initial begin + for (integer ADDR = 0; ADDR < 2**(addrwidth+$clog2(datawidth)-3); ++ADDR) begin + mem[ADDR] <= 8'b0; + end + end + + always_ff @(posedge clk) begin + if (we) begin + mem[addr] <= data_in; + end + end + assign data_out = mem[addr]; + +endmodule + +`endif diff --git a/ModelSim/register_file.sv b/ModelSim/register_file.sv new file mode 100644 index 0000000..f159ccd --- /dev/null +++ b/ModelSim/register_file.sv @@ -0,0 +1,28 @@ +`ifndef REGISTER_FILE_H +`define REGISTER_FILE_H + +module register_file #(parameter width=5, parameter nregs=5)( + input logic [0:0] clk, input logic[0:0] we, + input logic [nregs-1:0] WA, input logic [2**width-1:0] WD, + input logic [nregs-1:0] RA1, output logic [2**width-1:0] RD1, + input logic [nregs-1:0] RA2, output logic [2**width-1:0] RD2, + output logic [2**width-1:0] regs_out [2**nregs] +); + localparam BW = 2**width; + logic [2**width-1:0] regs[2**nregs]; + + initial begin + regs[0] = {(2**width){1'b0}}; + end + + always_ff @(posedge clk) begin + if (we & (WA != {(2**width){1'b0}} )) begin + regs[WA] <= WD; + end + end + + assign RD1 = regs[RA1]; + assign RD2 = regs[RA2]; + assign regs_out = regs; // Expose registers externally +endmodule +`endif diff --git a/ModelSim/rom.sv b/ModelSim/rom.sv new file mode 100644 index 0000000..e18e3c7 --- /dev/null +++ b/ModelSim/rom.sv @@ -0,0 +1,24 @@ +`ifndef ROM_H +`define ROM_H + +module rom #(parameter m=7,p=4) ( + input logic [m-1:0] Ad, + output logic [p-1:0] Dout + ); + + logic [p-1:0] mem[2**m-1:0]; + assign Dout = mem[Ad]; + + //Load the program in memory + // The machine code stored in text file program.dat written in Hex format + initial begin + `ifdef QUARTUS_SYNTH + $readmemh("../../asm/bin/ELEC4720-2026s2-Ass-02-Demo.txt",mem); //$readmemb for binary + `else + $readmemh("./asm/bin/ELEC4720-2026s2-Ass-02-Demo.txt",mem); //$readmemb for binary + `endif + end +endmodule // end rom module + +`endif + diff --git a/ModelSim/seven_seg_4digit.sv b/ModelSim/seven_seg_4digit.sv new file mode 100644 index 0000000..3ccedd4 --- /dev/null +++ b/ModelSim/seven_seg_4digit.sv @@ -0,0 +1,44 @@ +`ifndef SEVEN_SEG_4_H_SV +`define SEVEN_SEG_4_H_SV + +`include "seven_seg.sv" +module seven_seg_4digit ( + input logic [15:0] num, + output logic [7:0] segments0, + output logic [7:0] segments1, + output logic [7:0] segments2, + output logic [7:0] segments3 +); + + // Rightmost digit + seven_seg digit0 ( + .num(num[3:0]), + .rbi_inv(1'b0), + .segments(segments0) + ); + + // Second digit from right + seven_seg digit1 ( + .num(num[7:4]), + .rbi_inv(1'b0), + .segments(segments1) + ); + + // Third digit from right + seven_seg digit2 ( + .num(num[11:8]), + .rbi_inv(1'b0), + .segments(segments2) + ); + + // Leftmost digit + seven_seg digit3 ( + .num(num[15:12]), + .rbi_inv(1'b0), + .segments(segments3) + ); + +endmodule + +`endif + diff --git a/ModelSim/shifter.sv b/ModelSim/shifter.sv index a4070a9..9c63dfc 100644 --- a/ModelSim/shifter.sv +++ b/ModelSim/shifter.sv @@ -1,9 +1,9 @@ `ifndef SHIFTER_H_SV `define SHIFTER_H_SV module shifter #(width=2)(input logic [2:0] op, - input logic[2**width-1:0] A, input logic[2**width-1:0] B, input logic[width-1:0] C, + input logic[2**width-1:0] A, input logic[2**width-1:0] B, input logic[width-1:0] C, output logic[2**width-1:0] OUT_1); - +// A and B are 2^n bits wide and C is n bits wide as according to the docs always_comb begin case (op) 3'b000: OUT_1 = A << C; @@ -16,4 +16,4 @@ module shifter #(width=2)(input logic [2:0] op, endcase end endmodule -`endif \ No newline at end of file +`endif diff --git a/ModelSim/sign_extender.sv b/ModelSim/sign_extender.sv new file mode 100644 index 0000000..77a9ae1 --- /dev/null +++ b/ModelSim/sign_extender.sv @@ -0,0 +1,17 @@ +`ifndef SIGN_EXTENDER_H +`define SIGN_EXTENDER_H + +module sign_extender #(in_width=4, out_width=5)( + input logic [0:0] op, + input logic [2**in_width-1:0] val, + output logic [2**out_width-1:0] ext_val +); + always_comb begin + if (op) begin + ext_val = { {(2**(out_width-in_width)){val[2**in_width-1]}}, val}; + end else begin + ext_val = ({{(2**(out_width-in_width)){1'b0}}, val}); + end + end +endmodule +`endif