From 6dbb60457a6e1efac13d3af988f6f064b6d2c635 Mon Sep 17 00:00:00 2001 From: cag-uconn Date: Fri, 5 Nov 2021 14:13:52 -0400 Subject: [PATCH] . --- pa3/Makefile | 17 ++ pa3/README.md | 27 +++ pa3/src/instruction_map.c | 40 +++++ pa3/src/instruction_map.h | 43 +++++ pa3/src/register_map.c | 49 ++++++ pa3/src/register_map.h | 16 ++ pa3/src/sim_core.c | 290 +++++++++++++++++++++++++++++++ pa3/src/sim_core.h | 102 +++++++++++ pa3/src/sim_stages.c | 127 ++++++++++++++ pa3/src/sim_stages.h | 29 ++++ pa3/src/util.c | 227 ++++++++++++++++++++++++ pa3/src/util.h | 24 +++ pa3/unittests/.gitignore | 1 + pa3/unittests/array_adder.asm | 59 +++++++ pa3/unittests/beq_no_dep.asm | 22 +++ pa3/unittests/fibonacci.asm | 50 ++++++ pa3/unittests/j_no_dep_test1.asm | 24 +++ pa3/unittests/jal_test1.asm | 23 +++ pa3/unittests/lw_sw_test1.asm | 59 +++++++ pa3/unittests/lw_sw_test2.asm | 60 +++++++ pa3/unittests/no_dep_test1.asm | 14 ++ 21 files changed, 1303 insertions(+) create mode 100644 pa3/Makefile create mode 100644 pa3/README.md create mode 100644 pa3/src/instruction_map.c create mode 100644 pa3/src/instruction_map.h create mode 100644 pa3/src/register_map.c create mode 100644 pa3/src/register_map.h create mode 100644 pa3/src/sim_core.c create mode 100644 pa3/src/sim_core.h create mode 100644 pa3/src/sim_stages.c create mode 100644 pa3/src/sim_stages.h create mode 100644 pa3/src/util.c create mode 100644 pa3/src/util.h create mode 100644 pa3/unittests/.gitignore create mode 100644 pa3/unittests/array_adder.asm create mode 100644 pa3/unittests/beq_no_dep.asm create mode 100644 pa3/unittests/fibonacci.asm create mode 100644 pa3/unittests/j_no_dep_test1.asm create mode 100644 pa3/unittests/jal_test1.asm create mode 100644 pa3/unittests/lw_sw_test1.asm create mode 100644 pa3/unittests/lw_sw_test2.asm create mode 100644 pa3/unittests/no_dep_test1.asm diff --git a/pa3/Makefile b/pa3/Makefile new file mode 100644 index 0000000..b17f6b5 --- /dev/null +++ b/pa3/Makefile @@ -0,0 +1,17 @@ +SRCS = $(wildcard src/*.c) +HEADERS = $(wildcard src/*.h) +CC = gcc +CFLAGS = -g -std=c99 +LDFLAGS = -lm + +default: simulator + +simulator: $(SRCS) $(HEADERS) + @echo "Building $@..." + @echo "Sources: $(SRCS)" + @echo "Headers: $(HEADERS)" + $(CC) $(CFLAGS) -o $@ $(SRCS) $(LDFLAGS) + +clean: + -rm -f simulator + -rm -f pipe_trace.txt *.out mdump.txt cdump.txt diff --git a/pa3/README.md b/pa3/README.md new file mode 100644 index 0000000..28ad64d --- /dev/null +++ b/pa3/README.md @@ -0,0 +1,27 @@ +# Programming Assignment 3: Pipelined riscy-uconn Simulator with Multi-cycle Operations and Data Cache + +A 5-stage pipelined CPU simulator with multi-cycle operations and data cache for the MIPS-like +riscy-uconn instruction set architecture. The simulator translates machine code created by the +riscy-uconn assembler, and executes instructions one at a time. Each instruction goes through a +Fetch, Decode, Execute, Memory and Writeback stage of processing. + +## Build Instructions + $ make + +## Usage + $ ./simulator assembled_program_file.out FORWARDING_ENABLED DATA_CACHE_ENABLED + +where `assembled_program_file.out` may be any assembled program file generated by the riscy-uconn +assembler, `FORWARDING_ENABLED` may be 0 (disabled) or 1 (enabled), and `DATA_CACHE_ENABLED` may be +0 (disabled) or 1 (enabled). + +## Unit Tests +Several unit tests are provided in the `unittests` directory. These unit tests must be assembled +before use with the simulator by executing the following command: + + $ ../assembler/assembler unittests/unit_test_file.asm unittests/unit_test_file.out + +where `unit_test_file` is any of the unit test files (written in riscy-uconn assembly) in the +`unittests` directory. + + diff --git a/pa3/src/instruction_map.c b/pa3/src/instruction_map.c new file mode 100644 index 0000000..f29ac4a --- /dev/null +++ b/pa3/src/instruction_map.c @@ -0,0 +1,40 @@ +/** + * University of Connecticut + * CSE 4302 / CSE 5302 / ECE 5402: Computer Architecture + * Fall 2021 + * + * Programming Assignment 3: Pipelined Simulator with Multi-cycle Operations and Data Cache + * + * riscy-uconn: instruction_map.c + * + * DO NOT MODIFY THIS FILE + * + */ + +#include "instruction_map.h" + +char* opcode_map[] = { + [RTYPEOP] = "RTYPEOP", + [LW] = "lw", + [SW] = "sw", + [ANDI] = "andi", + [ADDI] = "addi", + [ORI] = "ori", + [SLTI] = "slti", + [LUI] = "lui", + [BEQ] = "beq", + [BNE] = "bne", + [J] = "j", + [JAL] = "jal" +}; + +char* func_map[] = { + [ADD] = "add", + [SUB] = "sub", + [AND] = "and", + [OR] = "or", + [SLL] = "sll", + [SRL] = "srl", + [SLT] = "slt", + [JR] = "jr" +}; \ No newline at end of file diff --git a/pa3/src/instruction_map.h b/pa3/src/instruction_map.h new file mode 100644 index 0000000..3401e4e --- /dev/null +++ b/pa3/src/instruction_map.h @@ -0,0 +1,43 @@ +/** + * University of Connecticut + * CSE 4302 / CSE 5302 / ECE 5402: Computer Architecture + * Fall 2021 + * + * Programming Assignment 3: Pipelined Simulator with Multi-cycle Operations and Data Cache + * + * riscy-uconn: instruction_map.h + * + * DO NOT MODIFY THIS FILE + * + */ + +#pragma once + +extern char* opcode_map[]; +extern char* func_map[]; + +/* R-Type Instructions */ +#define RTYPEOP 0x0 +#define ADD 0x20 +#define SUB 0x21 +#define AND 0x24 +#define OR 0x25 +#define SLL 0x0 +#define SLT 0x2A +#define SRL 0x2 +#define JR 0x8 + +/* I-Type Instructions */ +#define LW 0x23 +#define SW 0x2B +#define ANDI 0xC +#define ORI 0xD +#define LUI 0xF +#define BEQ 0x4 +#define BNE 0x5 +#define SLTI 0xA +#define ADDI 0x8 + +/* J-Type Instructions */ +#define J 0x2 +#define JAL 0x3 \ No newline at end of file diff --git a/pa3/src/register_map.c b/pa3/src/register_map.c new file mode 100644 index 0000000..94025e9 --- /dev/null +++ b/pa3/src/register_map.c @@ -0,0 +1,49 @@ +/** + * University of Connecticut + * CSE 4302 / CSE 5302 / ECE 5402: Computer Architecture + * Fall 2021 + * + * Programming Assignment 3: Pipelined Simulator with Multi-cycle Operations and Data Cache + * + * riscy-uconn: register_map.c + * + * DO NOT MODIFY THIS FILE + * + */ + +#include "register_map.h" + +const char* register_map[] = { + [0] = "zero", + [1] = "at", + [2] = "v0", + [3] = "v1", + [4] = "a0", + [5] = "a1", + [6] = "a2", + [7] = "a3", + [8] = "t0", + [9] = "t1", + [10] = "t2", + [11] = "t3", + [12] = "t4", + [13] = "t5", + [14] = "t6", + [15] = "t7", + [16] = "s0", + [17] = "s1", + [18] = "s2", + [19] = "s3", + [20] = "s4", + [21] = "s5", + [22] = "s6", + [23] = "s7", + [24] = "t8", + [25] = "t9", + [26] = "k0", + [27] = "k1", + [28] = "gp", + [29] = "sp", + [30] = "fp", + [31] = "ra", +}; \ No newline at end of file diff --git a/pa3/src/register_map.h b/pa3/src/register_map.h new file mode 100644 index 0000000..d8a7e1b --- /dev/null +++ b/pa3/src/register_map.h @@ -0,0 +1,16 @@ +/** + * University of Connecticut + * CSE 4302 / CSE 5302 / ECE 5402: Computer Architecture + * Fall 2021 + * + * Programming Assignment 3: Pipelined Simulator with Multi-cycle Operations and Data Cache + * + * riscy-uconn: register_map.h + * + * DO NOT MODIFY THIS FILE + * + */ + +#pragma once + +extern const char* register_map[]; \ No newline at end of file diff --git a/pa3/src/sim_core.c b/pa3/src/sim_core.c new file mode 100644 index 0000000..b3d73d7 --- /dev/null +++ b/pa3/src/sim_core.c @@ -0,0 +1,290 @@ +/** + * University of Connecticut + * CSE 4302 / CSE 5302 / ECE 5402: Computer Architecture + * Fall 2021 + * + * Programming Assignment 3: Pipelined Simulator with Multi-cycle Operations and Data Cache + * + * riscy-uconn: sim_core.c + * + * DO NOT MODIFY THIS FILE + * + */ + +#include +#include +#include + +#include "instruction_map.h" +#include "register_map.h" +#include "sim_core.h" +#include "sim_stages.h" +#include "util.h" + +/** + * Initial CPU state + */ +int cycle = 0; // CPU cycle +int registers[MAX_LENGTH] = {0}; // Registers +unsigned int pc = 0; // Program Counter (PC) register +unsigned int pc_n = 0; +int *memory = NULL; // Data & instruction memory + +/* Pipeline-related initialization */ +int forwarding_enabled = 0; +int pipe_stall = 0; +int br_taken = 0; +int lw_in_exe = 0; +int we_exe = 0, ws_exe = 0, dout_exe = 0; +int we_mem = 0, ws_mem = 0, dout_mem = 0; +int we_wb = 0, ws_wb = 0, dout_wb = 0; + +/* Multi-cycle operation-related initialization */ +const int dmem_access_cycles = 10; +const int rtype_execute_cycles = 5; +int dmem_busy = 0; +int dmem_cycles = 0; +int exe_busy = 0; +int exe_cycles = 0; + +/* Data cache-related */ +int dcache_enabled = 0; +int dcache_accesses = 0; +int dcache_hits = 0; + +/** + * Utility + */ +FILE *fptr_pt; + +/** + * Simulator entry point + */ +int main(int argc, char *argv[]) { + if (argc != 4) { + fprintf(stderr, "[ERROR] incorrect number of arguments.\n"); + printf("usage: simulator PROGRAM_FILE FORWARDING_ENABLED DATA_CACHE_ENABLED\n"); + exit(1); + } else { + /* Open input program file */ + FILE *fp; + fp = fopen(argv[1], "r"); + + /* Enable/disable forwarding */ + sscanf(argv[2],"%d", &forwarding_enabled); + if (forwarding_enabled > 1) { + fprintf(stderr, "[ERROR] FORWARDING_ENABLED must be either 0 (disabled) or 1 (enabled).\n"); + exit(1); + } + printf("Pipeline Forwarding: %s\n", forwarding_enabled ? "Enabled" : "Disabled"); + + /* Enable/disable data cache */ + sscanf(argv[3], "%d", &dcache_enabled); + if (dcache_enabled > 1) { + fprintf(stderr, "[ERROR] DATA_CACHE_ENABLED must be either 0 (disabled) or 1 (enabled).\n"); + exit(1); + } + printf("Data Cache: %s\n", dcache_enabled ? "Enabled" : "Disabled"); + + /* Open pipe trace */ + if (pipe_trace) { + fptr_pt = fopen("pipe_trace.txt", "w"); + } + + /* Initialize registers and instruction/data memory */ + initialize(fp); + + puts("\n"); + printf("Simulating...\n"); + + /* Process instructions one at a time */ + process_instructions(); + + puts(""); + + /* Output state after termination */ + rdump(); // Register dump + mdump(); // Memory dump + cdump(); // Cache dump + printf("\nData Cache Accesses = %d\nData Cache Hits = %d\n", dcache_accesses, dcache_hits); + + /* Cleanup */ + free(memory); + free(dcache); + fclose(fp); + if (pipe_trace) { + fclose(fptr_pt); + } + + return 0; + } +} + +void process_instructions() { + int terminate = 0; + int instruction_counter = 0; //committed instruction count + + /* Initialize pipeline state */ + unsigned int committed_inst; + fetch_out = 0; + decode_out = (struct State) {0}; + ex_out = (struct State) {0}; + mem_out = (struct State) {0}; + + while (terminate != 1) { + /* Update pipeline state */ + committed_inst = write_back_stage(mem_out); + mem_out_n = memory_stage(ex_out); + ex_out_n = execute(decode_out); + decode_out_n = decode(fetch_out); + fetch_out_n = fetch(fetch_out); + + if (pipe_trace == 1) { + fprintf(fptr_pt, "Cycle %d, PC %d, Next PC %d\n", cycle, pc, pc_n); + inst_dump("[Fetch]", fetch_out_n); + inst_dump("[Decode]", decode_out_n.inst); + inst_dump("[Execute]", ex_out_n.inst); + inst_dump("[Memory]", mem_out_n.inst); + inst_dump("[Writeback]", committed_inst); + fprintf(fptr_pt, "\n"); + rdump_pt(); + fprintf(fptr_pt, "\n"); + fprintf(fptr_pt, "=================================================================================================================================\n"); + fprintf(fptr_pt, "\n"); + } + + if (debug) { + fprintf(stderr, "[DEBUG] Cycle: %d, Instruction Memory Address: %d, Instruction: 0x%08x\n", cycle, pc / 4, fetch_out_n); + } + + if ((committed_inst != 0xffffffff) & (committed_inst != 0x00000000)) { + instruction_counter++; + } + + if (debug) { + fprintf(stderr, "[DEBUG] Cycle: %d, Committed Instruction: 0x%08x\n", cycle, committed_inst); + } + + if (registers[0] != 0) { + terminate = 1; // set terminate flag when $zero is updated + } + + /* Update state for next cycle */ + update_simulator_state(); + cycle++; + + /* Potential infinite loop detected */ + if (cycle == 10000) { + fprintf(stderr, "\n[WARNING] Simulation has simulated 10,000 cycles without terminating. Something might be wrong. Press CTRL + C to force termination.\n"); + } + + /* Flush pipe trace to file */ + if (pipe_trace) { + fflush(fptr_pt); + } + } + printf("\nFinished simulation!\n"); + printf("\nTOTAL INSTRUCTIONS COMMITTED: %d\n", instruction_counter); + printf("TOTAL CYCLES SIMULATED: %d\n", cycle); +} + +void initialize(FILE *fp) { + printf("======================================\n"); + printf("=== BEGIN SIMULATOR INITIALIZATION ===\n"); + printf("======================================\n"); + if (fp == NULL) { + fprintf(stderr, "[ERROR] opening input file. Aborting.\n"); + exit(1); + } + + /* Zero initialize registers */ + memset(registers, 0, sizeof(registers)); + printf("Initialized Registers\n"); + + /* Allocate and zero-initialize data cache */ + dcache = (CacheBlock*) calloc(NUM_DCACHE_LINES, sizeof(CacheBlock)); + if (dcache == NULL) { + fprintf(stderr, "[ERROR] not enough memory. Aborting.\n"); + exit(1); + } + printf("Initialized Data Cache\n"); + + /* Allocate instruction and data memory */ + memory = (int*) malloc(16384 * sizeof(int)); + if (memory == NULL) { + fprintf(stderr, "[ERROR] not enough memory. Aborting.\n"); + exit(1); + } + + /* Initialize memory to -1 */ + for (int i = 0; i < 16384; i++) { + memory[i] = -1; + } + printf("Initialized Memory\n"); + puts(""); + + printf("----------------------\n"); + printf("--- Section: .text ---\n"); + printf("----------------------\n"); + + /* Initialize parsing variables */ + char line[MAX_LENGTH + 2]; + char *p; + int i = 0, line_num = 0; + + /* Copy .text section to memory, break at nop */ + while (fgets(line, MAX_LENGTH + 2, fp) != NULL) { + line_num++; + + /* Remove '\n' from 'line' */ + p = strchr(line, '\n'); + if (p != NULL) { + *p = '\0'; + } + + memory[i] = getDec(line); + + /* If 'nop' found, move to 0x800 / 2048 in memory and break */ + if (strcmp(line, "11111111111111111111111111111111") == 0) { + memory[i] = 0; + i = 0x800; + break; + } else { + printf("memory[%d] = 0x%08x\n", i, memory[i]); + i++; + } + } + + int j = 2048; //Data Memory Starts at 2048 + for (j = i; j < 16384; j++) { + memory[j] = 0; + } + + puts(""); + + printf("----------------------\n"); + printf("--- Section: .data ---\n"); + printf("----------------------\n"); + + /* Seek fp to first instruction in .data */ + char data[MAX_LENGTH + 2]; + int bytes = 33 * line_num; + fseek(fp, bytes, SEEK_SET); + + /* Copy .data section to memory */ + while (fgets(line, MAX_LENGTH + 2, fp) != NULL) { + /* Remove '\n' from 'line' */ + p = strchr(line, '\n'); + if (p != NULL) { + *p = '\0'; + } + + memory[i] = getDec(line); + printf("memory[%d] = 0x%08x\n", i, memory[i]); + i++; + } + + printf("====================================\n"); + printf("=== END SIMULATOR INITIALIZATION ===\n"); + printf("===================================="); +} diff --git a/pa3/src/sim_core.h b/pa3/src/sim_core.h new file mode 100644 index 0000000..4a87ce6 --- /dev/null +++ b/pa3/src/sim_core.h @@ -0,0 +1,102 @@ +/** + * University of Connecticut + * CSE 4302 / CSE 5302 / ECE 5402: Computer Architecture + * Fall 2021 + * + * Programming Assignment 3: Pipelined Simulator with Multi-cycle Operations and Data Cache + * + * riscy-uconn: sim_core.h + * + * DO NOT MODIFY THIS FILE + * + */ + +#pragma once + +#include + +extern FILE *fptr_pt; + +/* Max number of registers, and instruction length in bits */ +#define MAX_LENGTH 32 + +/* Array of registers (register file) */ +extern int registers[MAX_LENGTH]; + +/* Clock cycle */ +extern int cycle; + +/* Program Counter (PC) register */ +extern unsigned int pc; // Current PC +extern unsigned int pc_n; // Next PC + +/* Microarchitectural state */ +unsigned int fetch_out, fetch_out_n; +struct State decode_out, decode_out_n; +struct State ex_out, ex_out_n; +struct State mem_out, mem_out_n; + +/* Instruction and data memory */ +extern int *memory; + +/* CPU state */ +struct State { + /* Fetched instruction */ + unsigned int inst; + + /* Decoded instruction fields */ + unsigned int opcode; + unsigned int func; + unsigned int rs; + unsigned int rt; + unsigned int rd; + unsigned int sa; + unsigned short imm; + + /* Memory related */ + unsigned int mem_flag; + unsigned int mem_addr; + unsigned int mem_out; + + /* JAL related */ + unsigned int jmp_out_31; + + /* ALU */ + unsigned int alu_in1; + unsigned int alu_in2; + unsigned int alu_out; +}; + +/* Pipeline-related */ +extern int forwarding_enabled; +extern int pipe_stall; +extern int br_taken; +extern int lw_in_exe; +extern int we_exe, ws_exe, dout_exe; +extern int we_mem, ws_mem, dout_mem; +extern int we_wb, ws_wb, dout_wb; + +/* Multi-cycle operation-related */ +extern const int dmem_access_cycles; +extern const int rtype_execute_cycles; +extern int dmem_busy; +extern int dmem_cycles; +extern int exe_busy; +extern int exe_cycles; + +/* Data cache-related */ +extern int dcache_enabled; +extern int dcache_accesses; +extern int dcache_hits; + +#define NUM_DCACHE_LINES 32 // 1024 bytes (cache size) / 32 bytes (block size) + +typedef struct { + int valid; + int tag; +} CacheBlock; + +CacheBlock *dcache; + +void initialize(FILE *fp); +void process_instructions(); \ No newline at end of file diff --git a/pa3/src/sim_stages.c b/pa3/src/sim_stages.c new file mode 100644 index 0000000..ba94057 --- /dev/null +++ b/pa3/src/sim_stages.c @@ -0,0 +1,127 @@ +/** + * University of Connecticut + * CSE 4302 / CSE 5302 / ECE 5402: Computer Architecture + * Fall 2021 + * YOUR NAME HERE + * + * Programming Assignment 3: Pipelined Simulator with Multi-cycle Operations and Data Cache + * + * riscy-uconn: sim_stages.c + * + */ + +#include +#include +#include + +#include "instruction_map.h" +#include "sim_core.h" +#include "sim_stages.h" + +/** + * Debug flags + */ +int debug = 0; // Set to 1 for additional debugging information. +int pipe_trace = 1; // Set to 1 for pipe trace. + +/** + * Fetch stage implementation. + */ +unsigned int fetch(unsigned int fetch_in) { + unsigned int inst = 0; + + /* Your code for the fetch stage goes here. */ + + return inst; +} + +/** + * Decode stage implementation + */ +struct State decode(unsigned int fetch_out) { + struct State decode_out = {0}; + + /* Your code for the decode stage goes here. */ + + return decode_out; +} + +/** + * Execute stage implementation + */ +struct State execute(struct State decode_out) { + + /* Your code for the execute stage goes here. */ + + return decode_out; +} + +/** + * Memory stage implementation + */ +struct State memory_stage(struct State ex_out) { + + /* Your code for the memory stage goes here. */ + + return ex_out; +} + +/** + * Write-back stage implementation + */ +unsigned int write_back_stage(struct State mem_out) { + + /* Your code for the write-back stage goes here. */ + + return mem_out.inst; +} + +/** + * Update simulator state for next cycle + */ +void update_simulator_state() { + if (dmem_busy == 1) { + + /* Your code to hold fetch, decode, execute, and memory stages goes here. */ + + } else if (exe_busy == 1) { + + /* Your code to hold fetch, decode, and execute stages goes here. */ + + } else { + /* DO NOT MODIFY. */ + pc = pc_n; + fetch_out = fetch_out_n; + decode_out = decode_out_n; + ex_out = ex_out_n; + mem_out = mem_out_n; + } +} + +/** + * Data cache lookup + */ +int dcache_lookup(int addr_mem) { + int hit = 0; + + /* Your code for data cache lookup goes here. */ + + return hit; +} + +/** + * Data cache update + */ +void dcache_update(int addr_mem) { + + /* Your code for data cache update logic goes here. */ + +} + +/** + * Advance PC. + * DO NOT MODIFY. + */ +void advance_pc(int step) { + pc_n += step; +} \ No newline at end of file diff --git a/pa3/src/sim_stages.h b/pa3/src/sim_stages.h new file mode 100644 index 0000000..360164f --- /dev/null +++ b/pa3/src/sim_stages.h @@ -0,0 +1,29 @@ +/** + * University of Connecticut + * CSE 4302 / CSE 5302 / ECE 5402: Computer Architecture + * Fall 2021 + * + * Programming Assignment 3: Pipelined Simulator with Multi-cycle Operations and Data Cache + * + * riscy-uconn: sim_stages.h + * + * DO NOT MODIFY THIS FILE + * + */ + +#pragma once + +#include "sim_core.h" + +extern int debug; +extern int pipe_trace; + +unsigned int fetch(unsigned int instuction_fetch); +struct State decode(unsigned int instuction_fetch); +struct State execute(struct State decode_out); +struct State memory_stage(struct State alu_out); +unsigned int write_back_stage(struct State memory_out); +void update_simulator_state(); +int dcache_lookup(int addr_mem); +void dcache_update(int addr_mem); +void advance_pc(int step); \ No newline at end of file diff --git a/pa3/src/util.c b/pa3/src/util.c new file mode 100644 index 0000000..0f57b1a --- /dev/null +++ b/pa3/src/util.c @@ -0,0 +1,227 @@ +/** + * University of Connecticut + * CSE 4302 / CSE 5302 / ECE 5402: Computer Architecture + * Fall 2021 + * + * Programming Assignment 3: Pipelined Simulator with Multi-cycle Operations and Data Cache + * + * riscy-uconn: util.c + * + * DO NOT MODIFY THIS FILE + * + */ + +#include +#include +#include +#include +#include + +#include "instruction_map.h" +#include "register_map.h" +#include "sim_core.h" + +/** + * Dump register contents. + * Will format for desired number of columns and output in specified file. + */ +void rdump_file_columns(FILE* file, unsigned columns) { + static const unsigned int index_col_width = 4; + static const unsigned int name_col_width = 5; + static const unsigned int value_col_width = 8; + static const unsigned int tab_spaces = 4; + static const unsigned int col_sep = 2; + + assert(columns > 0); + + /* Calculate number of rows and total row length*/ + const unsigned int rows = (int)ceil((double) MAX_LENGTH / columns); + const unsigned int row_length = columns * (index_col_width + name_col_width + value_col_width + 2 + 2 * tab_spaces) + (columns - 1) * (col_sep * tab_spaces); + + /* Print header */ + fprintf(file, "---------------------\n"); + fprintf(file, "--- Register Dump ---\n"); + fprintf(file, "---------------------\n"); + for (int col = 0; col < columns; col++) { + fprintf(file, "%-*s%-*s%-*s", index_col_width + tab_spaces, "Index", name_col_width + tab_spaces, "Name", value_col_width + 2, "Value"); + if (col == columns - 1) { + fprintf(file, "\n"); + } else { + fprintf(file, "%*s", col_sep * tab_spaces, ""); + } + } + for (int col = 0; col < columns; col++) { + fprintf(file, "%-*s%-*s%-*s", index_col_width + tab_spaces, "-----", name_col_width + tab_spaces, "----", value_col_width + 2, "-----"); + if (col == columns - 1) { + fprintf(file, "\n"); + } else { + fprintf(file, "%*s", col_sep * tab_spaces, ""); + } + } + + /* Print rows */ + for (int row = 0; row < rows; row++) { + for (int col = 0; col < columns; col++) { + unsigned int i = row + col * rows; + + if (i < MAX_LENGTH) { + fprintf(file, "$%-*i%*s$%-*s%*s0x%0*x", index_col_width, i, tab_spaces - 1, "", name_col_width, register_map[i], tab_spaces - 1, "", value_col_width, registers[i]); + } else { + fprintf(file, "\n"); + break; + } + + if (col == columns - 1) { + fprintf(file, "\n"); + } else { + fprintf(file, "%*s", col_sep * tab_spaces, ""); + } + } + } + fprintf(file, "%-*s%*s%-*s%*s0x%08x\n", index_col_width, "N/A", tab_spaces, "", name_col_width, "pc", tab_spaces, "", pc); +} + +void rdump_pt() { + rdump_file_columns(fptr_pt, 4); +} + +void rdump() { + rdump_file_columns(stdout, 4); +} + +/** + * Dump memory contents. + */ +void mdump() { + FILE* fptr; + fptr = fopen("mdump.txt", "w"); + int i = 0; + for (i = 0; i < 16384; i++) { + fprintf(fptr, "Memory[%d] = 0x%08x\n", i, memory[i]); + } + fclose(fptr); +} + +/** + * Dump cache information + */ +void cdump() { + FILE *fptr; + fptr = fopen("cdump.txt","w"); + + for (int i = 0; i < NUM_DCACHE_LINES; i++){ + fprintf(fptr,"DataCache[%d].valid = %d, DataCache[%d].tag = %d\n", i, dcache[i].valid, i, dcache[i].tag); + } +} + +/** + * Print instruction information + */ +void inst_dump(const char stage[], const unsigned int inst) { + int opcode = inst >> 26; + + unsigned int func = inst << 26; + func = func >> 26; + + int rs = (inst >> 21) & 0x1F; + int rt = (inst >> 16) & 0x1F; + int rd = (inst >> 11) & 0x1F; + int sa = (inst >> 6) & 0x1F; + int imm = inst & 0xFFFF; + short shortImm = (short)imm; + int target = inst & 0x03ffffff; + + fprintf(fptr_pt, "%-12s ", stage); + + if (inst == 0xffffffff) { + fprintf(fptr_pt, "INVALID INSTRUCTION\n"); + return; + } + + switch (opcode) { + case RTYPEOP: + switch (func) { + case JR: + fprintf(fptr_pt, "%-4s $%d\n", func_map[func], rs); + break; + + case SLL: + case SRL: + fprintf(fptr_pt, "%-4s $%d, $%d, %d\n", func_map[func], rd, rt, sa); + break; + + case ADD: + case SUB: + case AND: + case OR: + case SLT: + fprintf(fptr_pt, "%-4s $%d, $%d, $%d\n", func_map[func], rd, rs, rt); + break; + + default: + fprintf(fptr_pt, "INVALID INSTRUCTION\n"); + return; + break; + } + break; + + case LW: + case SW: + fprintf(fptr_pt, "%-4s $%d %d($%d)\n", opcode_map[opcode], rt, imm, rs); + break; + + case ANDI: + case ADDI: + case ORI: + case SLTI: + fprintf(fptr_pt, "%-4s $%d, $%d, %d\n", opcode_map[opcode], rt, rs, imm); + break; + + case LUI: + fprintf(fptr_pt, "%-4s $%d, %d\n", opcode_map[opcode], rt, imm); + break; + + case BEQ: + case BNE: + fprintf(fptr_pt, "%-4s $%d, $%d, %d\n", opcode_map[opcode], rs, rt, shortImm); + break; + + case J: + case JAL: + fprintf(fptr_pt, "%-4s %d\n", opcode_map[opcode], target); + break; + + default: + fprintf(fptr_pt, "INVALID INSTRUCTION\n"); + return; + break; + } +} + +/** + * Convert a binary string to an integer + */ +int getDec(char *bin) { + int b, k, m, n; + int len, sum = 0; + + len = strlen(bin) - 1; + + /* Iterate over the string */ + for (k = 0; k <= len; k++) { + // Convert char to numeric value + n = (bin[k] - '0'); + + // Check the character is binary + if ((n > 1) || (n < 0)) { + return 0; + } + + for (b = 1, m = len; m > k; m--) + b *= 2; + + // sum it up + sum = sum + n * b; + } + return sum; +} \ No newline at end of file diff --git a/pa3/src/util.h b/pa3/src/util.h new file mode 100644 index 0000000..83a3b2d --- /dev/null +++ b/pa3/src/util.h @@ -0,0 +1,24 @@ +/** + * University of Connecticut + * CSE 4302 / CSE 5302 / ECE 5402: Computer Architecture + * Fall 2021 + * + * Programming Assignment 3: Pipelined Simulator with Multi-cycle Operations and Data Cache + * + * riscy-uconn: util.h + * + * DO NOT MODIFY THIS FILE + * + */ + +#pragma once + +#include + +void rdump_file_columns(FILE* file, unsigned columns); +void rdump(); +void rdump_pt(); +void mdump(); +void cdump(); +void inst_dump(const char stage[], const unsigned int inst); +int getDec(char *bin); \ No newline at end of file diff --git a/pa3/unittests/.gitignore b/pa3/unittests/.gitignore new file mode 100644 index 0000000..e2e7327 --- /dev/null +++ b/pa3/unittests/.gitignore @@ -0,0 +1 @@ +/out diff --git a/pa3/unittests/array_adder.asm b/pa3/unittests/array_adder.asm new file mode 100644 index 0000000..f4bf43d --- /dev/null +++ b/pa3/unittests/array_adder.asm @@ -0,0 +1,59 @@ +.text +addi $t9, $zero, 10 +loop: +lw $t2, 2049($t1) #t2 = mem[2049 + t1] +lw $t3, 2065($t1) #t3 = mem[2065 + t1] +addi $t1, $t1, 1 #t1++ +lw $t4, 2049($t1) #t4 = mem[2049 + t1] +lw $t5, 2065($t1) #t5 = mem[2065 + t1] +addi $t1, $t1, 1 #t1++ + +add $t6, $t2, $t3 #t6 = 20, 60, 100, 140, 180 +add $t7, $t4, $t5 #t7 = 40, 80, 120, 160, 200 + +sw $t6, 2048($t8) +addi $t8, $t8, 1 +sw $t7, 2048($t8) +addi $t8, $t8, 1 + +bne $t1, $t9, loop + +addi $zero, $zero, 1 + + +.data +2048: .word 10 +2049: .word 10 +2050: .word 20 +2051: .word 30 +2052: .word 40 +2053: .word 50 +2054: .word 60 +2055: .word 70 +2056: .word 80 +2057: .word 90 +2058: .word 100 +2059: .word 3 +2060: .word 3 +2061: .word 3 +2062: .word 3 +2063: .word +2064: .word 11 +2065: .word 10 +2066: .word 20 +2067: .word 30 +2068: .word 40 +2069: .word 50 +2070: .word 60 +2071: .word 70 +2072: .word 80 +2073: .word 90 +2074: .word 100 +2075: .word 3 +2076: .word 3 +2077: .word 3 +2078: .word 3 +2079: .word 3 +2080: .word 5 + + diff --git a/pa3/unittests/beq_no_dep.asm b/pa3/unittests/beq_no_dep.asm new file mode 100644 index 0000000..894c249 --- /dev/null +++ b/pa3/unittests/beq_no_dep.asm @@ -0,0 +1,22 @@ +.text +addi $t0, $zero, 10 +addi $t1, $zero, 10 +addi $t2, $zero, 20 +addi $t3, $zero, 21 +addi $t4, $zero, 22 +addi $t5, $zero, 23 +beq $t0, $t1, loop +sw $t0, 2048($zero) +sw $t1, 2049($zero) +sw $t2, 2050($zero) +addi $t0, $zero, 30 + +loop: +lw $t0, 2048($zero) +addi, $zero, $zero, 1 # $zero register should never be updated, so detect this change and quit simulator + +.data +2048: .word 5119 +2049: .word 32 + + diff --git a/pa3/unittests/fibonacci.asm b/pa3/unittests/fibonacci.asm new file mode 100644 index 0000000..f086574 --- /dev/null +++ b/pa3/unittests/fibonacci.asm @@ -0,0 +1,50 @@ +.text +addi $t9, $zero, 10 +addi $t1, $zero, 0 +addi $t2, $zero, 1 +j fibonacci + +fibonacci: +add $t3, $t2, $t1 +add $t1, $zero, $t2 +add $t2, $zero, $t3 +addi $t5, $t5, 1 +bne $t5, $t9, fibonacci +addi $zero $zero, 1 # $zero register should never be updated, so detect this change and quit simulator + +.data +2048: .word 10 +2049: .word 10 +2050: .word 20 +2051: .word 30 +2052: .word 40 +2053: .word 50 +2054: .word 60 +2055: .word 70 +2056: .word 80 +2057: .word 90 +2058: .word 100 +2059: .word 3 +2060: .word 3 +2061: .word 3 +2062: .word 3 +2063: .word +2064: .word 11 +2065: .word 10 +2066: .word 20 +2067: .word 30 +2068: .word 40 +2069: .word 50 +2070: .word 60 +2071: .word 70 +2072: .word 80 +2073: .word 90 +2074: .word 100 +2075: .word 3 +2076: .word 3 +2077: .word 3 +2078: .word 3 +2079: .word 3 +2080: .word 5 + + diff --git a/pa3/unittests/j_no_dep_test1.asm b/pa3/unittests/j_no_dep_test1.asm new file mode 100644 index 0000000..16aaf13 --- /dev/null +++ b/pa3/unittests/j_no_dep_test1.asm @@ -0,0 +1,24 @@ +.text +add $t0, $zero, $zero # iterator i = 0 +addi $t2, $zero, 1 # init t2 = 1 +add $t3, $zero, $zero # initialize temporary register to zero +add $t4, $zero, $zero # initialize temporary register to zero +j jump_test1 # Jump to procedure "jump_test1" + +jump_test2: +lw $a1, 2048($t0) # Load a1 = 2, Mem[2048] = 2, 2 in simulator +lw $a2, 2048($t2) # Load a2 = 10, Mem[2049] = 10, 10 in simulator +j end # Jump to procedure "end" + +jump_test1: +add $t5, $zero, $zero # initialize temporary register to zero +add $t6, $zero, $zero # initialize temporary register to zero +j jump_test2 # Jump to procedure "jump_test2" + +end: +addi, $zero, $zero, 1 # $zero register should never be updated, so detect this change and quit simulator + +.data +2048: .word 2 +2049: .word 10 + diff --git a/pa3/unittests/jal_test1.asm b/pa3/unittests/jal_test1.asm new file mode 100644 index 0000000..6e7131d --- /dev/null +++ b/pa3/unittests/jal_test1.asm @@ -0,0 +1,23 @@ +.text +addi $a0, $zero, 2 # argument 0 = 2 +addi $a1, $zero, 3 # argument 1 = 3 +addi $a2, $zero, 4 # argument 2 = 4 +addi $a3, $zero, 5 # argument 3 = 5 +add $t4, $zero, $zero +jal diffofsums # call procedure +addi, $zero, $zero, 1 # $zero register should never be updated, so detect this change and quit simulator +sll $zero, $zero, 1 # +sll $zero, $zero, 1 # + + + +diffofsums: +add $t0, $a0, $a1 # $t0 = f + g +add $t1, $a2, $a3 # $t1 = h + i +sub $s0, $t0, $t1 # result = (f+g)-(h+i) +add $v0, $s0, $zero # put return value in $v0 +jr $ra # return to caller + +.data +2048: .word 10 +2049: .word 10 diff --git a/pa3/unittests/lw_sw_test1.asm b/pa3/unittests/lw_sw_test1.asm new file mode 100644 index 0000000..2811d0c --- /dev/null +++ b/pa3/unittests/lw_sw_test1.asm @@ -0,0 +1,59 @@ +.text +add $t0, $zero, $zero # i = 0 +add $t1, $zero, $zero # initialize the sum to zero +add $t2, $zero, $zero # for second loop compare 2 +add $t3, $zero, $zero +add $t5, $zero, $zero # initialize temporary register to zero +add $t6, $zero, $zero # for sw later +add $t7, $zero, $zero + +lw $t1, 2048($t0) # $t1=20 +lw $t2, 2048($t1) # $t2=4 +add $t4, $t1, $t2 # $t4=24 +lw $t3, 2048($t4) # $t3=8 +add $t4, $t4, $t3 # $t4=32 +sw $t4, 2048($t0) # mem[2048]=32 +lw $t1, 2048($t0) # $t1=32 <-- observation of stored value 32 as $t1=0x00000020 +lw $t2, 2048($t1) # $t2=5 +add $t4, $t1, $t2 # $t4=37 +sw $t4, 2048($t1) # mem[2080]=37 +lw $t5, 2048($t1) # $t5=37 <--- observation of stored value 37 as $t5=0x00000025 +addi, $zero, $zero, 1 # $zero register should never be updated, so detect this change and quit simulator + + +.data +2048: .word 20 +2049: .word 32 +2050: .word 2 +2051: .word 2 +2052: .word 3 +2053: .word 3 +2054: .word 3 +2055: .word 3 +2056: .word 3 +2057: .word 3 +2058: .word 3 +2059: .word 3 +2060: .word 3 +2061: .word 3 +2062: .word 3 +2063: .word 3 +2064: .word 3 +2065: .word 3 +2066: .word 3 +2067: .word 3 +2068: .word 4 +2069: .word 3 +2070: .word 3 +2071: .word 3 +2072: .word 8 +2073: .word 3 +2074: .word 3 +2075: .word 3 +2076: .word 3 +2077: .word 3 +2078: .word 3 +2079: .word 3 +2080: .word 5 + + diff --git a/pa3/unittests/lw_sw_test2.asm b/pa3/unittests/lw_sw_test2.asm new file mode 100644 index 0000000..a266623 --- /dev/null +++ b/pa3/unittests/lw_sw_test2.asm @@ -0,0 +1,60 @@ +.text +add $t0, $zero, $zero # i = 0 +add $t1, $zero, $zero # initialize the sum to zero +add $t2, $zero, $zero # for second loop compare 2 +add $t3, $zero, $zero +add $t5, $zero, $zero # initialize temporary register to zero +add $t6, $zero, $zero # for sw later +add $t7, $zero, $zero + +lw $t1, 2048($t0) # $t1=20 +add $t4, $t1, $t1 # $t4=40 +lw $t3, 2048($t4) # $t3=57 +addi, $zero, $zero, 1 # $zero register should never be updated, so detect this change and quit simulator + + +.data +2048: .word 20 +2049: .word 32 +2050: .word 2 +2051: .word 2 +2052: .word 3 +2053: .word 3 +2054: .word 3 +2055: .word 3 +2056: .word 3 +2057: .word 3 +2058: .word 3 +2059: .word 3 +2060: .word 3 +2061: .word 3 +2062: .word 3 +2063: .word 3 +2064: .word 3 +2065: .word 3 +2066: .word 3 +2067: .word 3 +2068: .word 4 +2069: .word 3 +2070: .word 3 +2071: .word 3 +2072: .word 8 +2073: .word 3 +2074: .word 3 +2075: .word 3 +2076: .word 3 +2077: .word 3 +2078: .word 3 +2079: .word 3 +2080: .word 5 +2081: .word 50 +2082: .word 51 +2083: .word 52 +2084: .word 53 +2085: .word 54 +2086: .word 55 +2087: .word 56 +2088: .word 57 + + + diff --git a/pa3/unittests/no_dep_test1.asm b/pa3/unittests/no_dep_test1.asm new file mode 100644 index 0000000..619e23a --- /dev/null +++ b/pa3/unittests/no_dep_test1.asm @@ -0,0 +1,14 @@ +.text +addi $t0, $zero, 10 +addi $t1, $zero, 25 +addi $t2, $zero, 20 +addi $t3, $zero, 19 +addi $t4, $zero, 18 +sw $t0, 2048($zero) +addi, $zero, $zero, 1 # $zero register should never be updated, so detect this change and quit simulator + +.data +2048: .word 5119 +2049: .word 32 + +