diff options
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | CMakeLists.txt | 31 | ||||
-rw-r--r-- | README.md | 14 | ||||
-rw-r--r-- | gui/CMakeLists.txt | 4 | ||||
-rw-r--r-- | gui/main.cc | 40 | ||||
-rw-r--r-- | inc/cache.h | 84 | ||||
-rw-r--r-- | inc/cli.h | 97 | ||||
-rw-r--r-- | inc/dram.h | 62 | ||||
-rw-r--r-- | inc/storage.h | 95 | ||||
m--------- | ram | 0 | ||||
-rw-r--r-- | src/cli/cli.cc | 214 | ||||
-rw-r--r-- | src/sim/if.cc | 4 | ||||
-rw-r--r-- | src/sim/mm.cc | 10 | ||||
-rw-r--r-- | src/storage/cache.cc | 180 | ||||
-rw-r--r-- | src/storage/dram.cc | 130 | ||||
-rw-r--r-- | src/storage/storage.cc | 16 | ||||
-rw-r--r-- | tests/cache.cc | 189 | ||||
-rw-r--r-- | tests/dram.cc | 351 |
18 files changed, 59 insertions, 1465 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ef77032 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "storage"] + path = ram + url = git@github.com:bdunahu/ram.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 9daac12..c7918b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,27 @@ cmake_minimum_required(VERSION 3.5) -set(CMAKE_CXX_COMPILER "g++") project(risc_vector) +find_package(Git QUIET) +if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --remote --recursive + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if(NOT GIT_SUBMOD_RESULT EQUAL "0") + message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + endif() +endif() + +if(NOT EXISTS "${PROJECT_SOURCE_DIR}/ram/CMakeLists.txt") + message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") +endif() + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_compile_options(-Wall -lstdc++ -g -O0) add_compile_options(-Wextra -Wpedantic) +set(RAM ram) + # cpp standard set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -16,7 +31,11 @@ include_directories( ${PROJECT_SOURCE_DIR}/inc ) -# add gui +# don't build RAM's tests +set(RAM_TESTS OF CACHE BOOL "" FORCE) + +# add submodules +add_subdirectory(${RAM}) add_subdirectory(gui) # gather source files @@ -28,7 +47,7 @@ qt_standard_project_setup() # binary executable add_library(${PROJECT_NAME}_lib ${SRCS}) -target_link_libraries(${PROJECT_NAME}_lib) +target_link_libraries(${PROJECT_NAME}_lib ${RAM}_lib) find_package(Catch2 REQUIRED) @@ -36,10 +55,10 @@ find_package(Catch2 REQUIRED) file(GLOB_RECURSE TESTS "tests/*.cc") # test executable -add_executable(tests ${SRCS} ${TESTS}) -target_link_libraries(tests PRIVATE Catch2::Catch2WithMain PRIVATE) +add_executable(test_rv ${SRCS} ${TESTS}) +target_link_libraries(test_rv PRIVATE Catch2::Catch2WithMain PRIVATE ${RAM}_lib) # test discovery include(CTest) include(Catch) -catch_discover_tests(tests) +catch_discover_tests(test_rv) @@ -1,10 +1,12 @@ -# Risc V[ECTOR] +# RISC V[ECTOR] + +## Dependencies -## dependencies - cmake - g++ (GCC) 11.4.0 - catch2 version 3.5.3 - Qt version 6.8.2 +- RAM (a custom memory submodule) ## to compile Generate the build directory with @@ -15,8 +17,12 @@ then compile both the simulator and tests with `cmake --build build` +To develop, the following git option is useful to keep modules updated.: + +`git config submodule.recurse true` + # about -University of Massachusetts, Amherst -CS535 -- Computer Architecture and ISA Design +Created at the University of Massachusetts, Amherst +CS535 -- Computer Architecture and ISA Design diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 6b5eb22..5b177d2 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -1,5 +1,4 @@ cmake_minimum_required(VERSION 3.5) -set(CMAKE_CXX_COMPILER "g++") add_compile_options(-Wall -lstdc++) add_compile_options(-Wextra -Wpedantic) @@ -22,9 +21,8 @@ file(GLOB SRCS qt_add_resources(GUI_RESOURCES "resources.qrc") add_executable(risc_vector ${SRCS} ${GUI_RESOURCES}) -target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}_lib Qt6::Widgets) +target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}_lib ram_lib Qt6::Widgets) set_target_properties(risc_vector PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" ) - diff --git a/gui/main.cc b/gui/main.cc index ce47d10..e873d1c 100644 --- a/gui/main.cc +++ b/gui/main.cc @@ -1,4 +1,3 @@ -#include "cli.h" #include "definitions.h" #include "gui.h" #include "logger.h" @@ -34,31 +33,24 @@ static void print_version_number() static void err() { std::cerr << "Usage:\n\trisc_vector [OPTIONS]\nOptions:\n\t--debug,\t-d: " - "turn on verbose output\n\t--memory-only,\t-m: run the memory " - "simulator only, without a GUI.\n\t--version,\t-v: print the " - "version information and exit\n" + "turn on verbose output\n\t--version,\t-v: print the version " + "information and exit\n" << std::endl; } -static void parseArguments(int argc, char **argv, bool &memory_only) +static void parseArguments(int argc, char **argv) { struct option long_options[] = { - {"debug", no_argument, 0, 'd'}, - {"memory-only", no_argument, 0, 'm'}, - {0, 0, 0, 0}}; + {"debug", no_argument, 0, 'd'}, {0, 0, 0, 0}}; int opt; - while ((opt = getopt_long(argc, argv, "d:m", long_options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "d", long_options, NULL)) != -1) { switch (opt) { case 'd': global_log->setLevel(DEBUG); global_log->log(DEBUG, "DEBUG output enabled."); break; - case 'm': - global_log->log(INFO, "Starting the storage CLI interface..."); - memory_only = true; - break; default: err(); exit(EXIT_FAILURE); @@ -71,21 +63,11 @@ int main(int argc, char **argv) print_version_number(); global_log->log(INFO, "Initializing..."); - bool memory_only = false; - parseArguments(argc, argv, memory_only); - - if (memory_only) { - Cli cli; - cli.run(); - } else { - global_log->log(INFO, "Starting QT..."); - QApplication a(argc, argv); - GUI w; - w.show(); - return a.exec(); - } + parseArguments(argc, argv); - global_log->log(INFO, "Cleaning up..."); - global_log->log(INFO, "Goodbye!"); - return EXIT_SUCCESS; + global_log->log(INFO, "Starting QT..."); + QApplication a(argc, argv); + GUI w; + w.show(); + return a.exec(); } diff --git a/inc/cache.h b/inc/cache.h deleted file mode 100644 index 88fd352..0000000 --- a/inc/cache.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef CACHE_H -#define CACHE_H -#include "definitions.h" -#include "storage.h" -#include <array> -#include <ostream> -#include <functional> - -class Cache : public Storage -{ - public: - /** - * Constructor. - * @param The number of `lines` contained in memory. The total number of - * words is this number multiplied by LINE_SIZE. - * @param The next lowest level in storage. Methods from this object are - * called in case of a cache miss. - * @param The number of clock cycles each access takes. - * @return A new cache object. - */ - Cache(Storage *lower, int delay); - ~Cache(); - - Response - write_word(Accessor accessor, signed int data, int address) override; - Response write_line( - Accessor accessor, - std::array<signed int, LINE_SIZE> data_line, - int address) override; - Response read_line( - Accessor accessor, - int address, - std::array<signed int, LINE_SIZE> &data_line) override; - Response - read_word(Accessor accessor, int address, signed int &data) override; - - /** - * Getter for the meta attribute. - * TODO this doesn't seem like good object-oriented practice. - * @return this->meta - */ - std::array<std::array<int, 2>, L1_CACHE_LINES> get_meta() const; - - private: - /** - * Helper for all access methods. - * Calls `request_handler` when `accessor` is allowed to complete its - * request cycle. - * @param the source making the request - * @param the address to write to - * @param the function to call when an access should be completed - */ - Response process( - Accessor accessor, - int address, - std::function<void(int index, int offset)> request_handler); - /** - * Returns OK if `accessor` is allowed to complete its request this cycle. - * Handles cache misses, wait times, and setting the current accessor this - * storage is serving. - * @param the accessor asking for a resource - * @return whether or not the access can be carried out this function call. - */ - Response is_access_cleared(Accessor accessor, int address); - /** - * Helper for access_cleared. - * Fetches `address` from a lower level of storage if it is not already - * present. If it is not, temporarily sets the is_blocked attribute of this - * cache level to true, and the victim line is chosen/written back. - * @param the address that must be present in cache. - */ - void handle_miss(int address); - /** - * An array of metadata about elements in `data`. - * If the first value of an element is negative, the corresponding - * element in `data` is invalid. If the most second value of an element - * is nonzero, the corresponding element in `data` is dirty. - */ - std::array<std::array<int, 2>, L1_CACHE_LINES> meta; -}; - -std::ostream &operator<<(std::ostream &os, const Cache &c); - -#endif /* CACHE_H_INCLUDED */ diff --git a/inc/cli.h b/inc/cli.h deleted file mode 100644 index a0c698a..0000000 --- a/inc/cli.h +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef CLI_H -#define CLI_H -#include "cache.h" -#include <functional> -#include <string> -#include <unordered_map> - -class Cli -{ - public: - /** - * Constructor. - * @return A newly allocated CLI object. - */ - Cli(); - ~Cli(); - - /** - * Prints all available commands to the console. - */ - void help(); - - /** - * Loads data from memory from the specified memory address. - * @param memory_address address of the memory where data needs to be loaded - * from - */ - void load(Accessor accessor, int memory_address); - - /** - * Stores data into memory at the specified address. - * @param accessor the pipline stage that is making this request - * @param data data value to be written to the memory - * @param address address of the memory where data needs to be stored - * @return the response from the storage device - */ - void store(Accessor accessor, int data, int address); - - /** - * Resets the memory configuration and cycles to their initial state. - * This function provides a side door reset interface to the memory system, - * allowing the user to reset the memory configuration directly. - */ - void reset(); - - /** - * Advance the clock one cycle, refreshing the storage devices. - */ - void clock(); - - /** - * Displays `lines` lines of the data in `level`, starting from `base`. - * - * - * This function provides a side door view into the storage system, showing - * its current state and configuration. - * @param level the level specifying the storage device. The first level - * one cache is level zero, with descending levels incrementing by a factor - * of one. - */ - void peek(int level); - - /** - * Runs the command line interface - * This function is the main entry point for the command line interface. - */ - void run(); - - private: - /** - * Initializes the cache object. - */ - void initialize(); - /** - * Attempts to match string to either fetch or mem, or throws - * std::invalid_argument otherwise. - * @param the string to be converted accessor - * @return the corresponding accessor - * @throws invalid_argument if the string is not fetch or mem - */ - Accessor match_accessor_or_die(std::string s); - /** Map of commands and their corresponding functions. - * This map is used to store the commands and their corresponding functions. - */ - std::unordered_map<char, std::function<void(std::vector<std::string>)>> - commands; - /** - * The cache object to interface with. - */ - Cache *cache; - /** - * The current cycle. - */ - int cycle; -}; - -#endif /* CLI_H_INCLUDED */ diff --git a/inc/dram.h b/inc/dram.h deleted file mode 100644 index 102ec0f..0000000 --- a/inc/dram.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef DRAM_H -#define DRAM_H -#include "definitions.h" -#include "storage.h" -#include <ostream> -#include <functional> - -class Dram : public Storage -{ - public: - /** - * Constructor. - * @param The number of clock cycles each access takes. - * @return A new memory object. - */ - Dram(int delay); - ~Dram(); - - Response - write_word(Accessor accessor, signed int data, int address) override; - Response read_line( - Accessor accessor, - int address, - std::array<signed int, LINE_SIZE> &data_line) override; - Response write_line( - Accessor accessor, - std::array<signed int, LINE_SIZE> data_line, - int address) override; - Response - read_word(Accessor accessor, int address, signed int &data) override; - - /** - * TODO This will accept a file at a later date. - */ - void load(std::vector<signed int> program); - - private: - /** - * Helper for all access methods. - * Calls `request_handler` when `accessor` is allowed to complete its - * request cycle. - * @param the source making the request - * @param the address to write to - * @param the function to call when an access should be completed - */ - Response process( - Accessor accessor, - int address, - std::function<void(int line, int word)> request_handler); - /** - * Returns OK if `accessor` is allowed to complete its request this cycle. - * Handles wait times, side door, and setting the current accessor this - * storage is serving. - * @param the accessor asking for a resource - * @return whether or not the access can be carried out this function call. - */ - Response is_access_cleared(Accessor accessor); -}; - -std::ostream &operator<<(std::ostream &os, const Dram &d); - -#endif /* DRAM_H_INCLUDED */ diff --git a/inc/storage.h b/inc/storage.h deleted file mode 100644 index d6fa094..0000000 --- a/inc/storage.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef STORAGE_H -#define STORAGE_H -#include "accessor.h" -#include "definitions.h" -#include "response.h" -#include <algorithm> -#include <array> -#include <map> -#include <vector> - -class Storage -{ - public: - virtual ~Storage() = default; - - /** - * Write `data` word into `address`. - * @param the source making the request. - * @param the data (hexadecimal) to write. - * @param the address to write to. - * @return a status code reflecting the state of the request. - */ - virtual Response write_word(Accessor accessor, signed int data, int address) = 0; - - /** - * Write a data line to given address in this level of storage - */ - virtual Response write_line(Accessor accessor, std::array<signed int, LINE_SIZE> data_line, int address) = 0; - - - /** - * Get the data line at `address`. - * @param the source making the request. - * @param the address being accessed. - * @return a status code reflecting the state of the request, and the - * data being returned. - */ - virtual Response read_line( - Accessor accessor, - int address, - std::array<signed int, LINE_SIZE> &data) = 0; - - /** - * Read a word from given address in this level of storage - */ - virtual Response read_word(Accessor accessor, int address, signed int &data) = 0; - - /** - * Sidedoor view of `lines` of memory starting at `base`. - * @param The base line to start getting memory from. - * @param The amount of lines to fetch. - * @return A matrix of data values, where each row is a line and each column - * is a word. - */ - std::vector<std::array<signed int, LINE_SIZE>> - view(int base, int lines) const; - - /** - * Getter for lower attribute. - * TODO this doesn't seem like good object-oriented practice. - * @return this->lower - */ - Storage *get_lower(); - - protected: - /** - * The data currently stored in this level of storage. - */ - std::vector<std::array<signed int, LINE_SIZE>> *data; - /** - * A pointer to the next lowest level of storage. - * Used in case of cache misses. - */ - Storage *lower; - /** - * The number of clock cycles this level of storage takes to complete - * requests. - */ - int delay; - /** - * The accessor currently being serviced. - */ - Accessor requester; - /** - * The number of cycles until the current request is completed. - */ - int wait_time; - /** - * A flag indicating whether this level of storage is currently waiting for - * a lower level. - */ - int is_waiting; -}; - -#endif /* STORAGE_H_INCLUDED */ diff --git a/ram b/ram new file mode 160000 +Subproject be2bc108dc112ae7e21d4a77f7bcbfac88d6fcd diff --git a/src/cli/cli.cc b/src/cli/cli.cc deleted file mode 100644 index 58575cb..0000000 --- a/src/cli/cli.cc +++ /dev/null @@ -1,214 +0,0 @@ -#include "cli.h" -#include "cache.h" -#include "definitions.h" -#include "dram.h" -#include "response.h" -#include "accessor.h" -#include "utils.h" -#include <iostream> - -Cli::Cli() -{ - this->cache = nullptr; - this->cycle = 0; - this->initialize(); - - commands['l'] = [this](std::vector<std::string> args) { - Accessor a; - if (args.size() >= 2) { - try { - a = match_accessor_or_die(args[0]); - load(a, std::stoi(args[1])); - } catch (const std::invalid_argument &e) { - std::cerr << "Invalid input: " << e.what() << std::endl; - } - } else { - std::cout << "Usage: l <memory-address>\n"; - } - return; - }; - - commands['s'] = [this](std::vector<std::string> args) { - Accessor a; - if (args.size() >= 3) { - try { - a = match_accessor_or_die(args[0]); - store(a, std::stoi(args[1]), std::stoi(args[2])); - } catch (const std::invalid_argument &e) { - std::cerr << "Invalid input: " << e.what() << std::endl; - } - } else { - std::cout << "Usage: s <memory-address> <data>\n"; - } - return; - }; - - commands['r'] = [this](std::vector<std::string> args) { - (void)args; - reset(); - return; - }; - - commands['p'] = [this](std::vector<std::string> args) { - if (args.size() >= 1) { - try { - peek(std::stoi(args[0])); - } catch (const std::invalid_argument &e) { - std::cerr << "Invalid input: " << e.what() << std::endl; - } - } else { - std::cout << "Usage: v <storage-level> <base> <lines>\n"; - } - return; - }; - - commands['h'] = [this](std::vector<std::string> args) { - (void)args; - help(); - return; - }; -} - -Cli::~Cli() { delete this->cache; } - -void Cli::help() -{ - std::cout - << "Available commands:" << std::endl - << " [l]oad <address> - Load data from memory at the specified " - "address" - << std::endl - << " [s]tore <accessor> <data> <address> - Stores data into memory at " - "specified address. Acessor must be one of: [f]etch, [m]em" - << " [p]eek <storage-level> <base> <lines> - side door function that " - "peeks the current status of the entire memory subsystem" - << std::endl - << " [r]eset - side door function that resets the memory " - "configuration and " - "cycles" - << std::endl - << " [h]elp - prints this help text" << std::endl - << " [q]uit - quits the program" << std::endl; -} - -void Cli::load(Accessor accessor, int address) -{ - address = wrap_address(address); - const auto default_flags = std::cout.flags(); - const auto default_fill = std::cout.fill(); - - signed int data; - Response r = this->cache->read_word(accessor, address, data); - std::cout << r << " to " << accessor << " reading " << address << std::endl; - if (r == OK) - std::cout << " Got: " << std::hex << data << std::endl; - - std::cout.flags(default_flags); - std::cout.fill(default_fill); -} - -void Cli::store(Accessor accessor, int data, int address) -{ - address = wrap_address(address); - Response r = this->cache->write_word(accessor, data, address); - std::cout << r << " to " << accessor << " storing " << data << " in " - << address << std::endl; -} - -void Cli::reset() -{ - this->initialize(); - std::cout << "Done." << std::endl; -} - -void Cli::peek(int level) -{ - Storage *curr = this->cache; - for (int i = 0; i < level; ++i) { - if (!curr) { - std::cerr << "Level " << level << " of storage does not exist." - << std::endl; - return; - } - curr = curr->get_lower(); - } - - Cache *c = dynamic_cast<Cache *>(curr); - if (c) { - std::cout << *c << std::endl; - } else { - std::cout << *dynamic_cast<Dram *>(curr) << std::endl; - ; - } -} - -void Cli::run() -{ - std::cout << "Memory Command Processor Started. Type 'h' for a list of " - "commands." - << std::endl; - std::string input; - - bool run = true; - while (run) { - std::cout << this->cycle << "> "; - std::getline(std::cin, input); - - std::istringstream iss1(input); - std::vector<std::string> words; - std::string sentence; - std::string word; - - while (std::getline(iss1, sentence, ';')) { - words.clear(); - std::istringstream iss2(sentence); - - while (iss2 >> word) { - words.push_back(word); - } - if (words.empty()) - continue; - - std::string command = words[0]; - words.erase(words.begin()); - - if (command == "q") { - run = false; - break; - } - - auto it = commands.find(tolower(command[0])); - if (it != commands.end()) { - it->second(words); - } else { - std::cout << "Unknown command: '" << command - << "'. Type 'help' for available commands." - << std::endl; - } - } - } -} - -void Cli::initialize() -{ - Logger *global_log = Logger::getInstance(); - - global_log->log(INFO, "Resetting memory configuration and cycle."); - - if (this->cache != nullptr) - delete this->cache; - - Dram *d = new Dram(MEM_DELAY); - this->cache = new Cache(d, L1_CACHE_DELAY); - this->cycle = 1; -} - -Accessor Cli::match_accessor_or_die(std::string s) -{ - if (tolower(s[0]) == 'f') - return FETCH; - else if (tolower(s[0]) == 'm') - return MEM; - else - throw std::invalid_argument(s); -} diff --git a/src/sim/if.cc b/src/sim/if.cc index 85fb27f..4ab7f3e 100644 --- a/src/sim/if.cc +++ b/src/sim/if.cc @@ -26,10 +26,12 @@ InstrDTO *IF::advance(Response p) void IF::advance_helper() { Response r; + int i; signed int bits; if (this->curr_instr == nullptr) { - r = this->storage->read_word(this->id, this->pc, bits); + i = this->storage->read_word(this, this->pc, bits); + r = i ? OK : STALLED; if (r == OK) { this->curr_instr = new InstrDTO(); this->curr_instr->set_instr_bits(bits); diff --git a/src/sim/mm.cc b/src/sim/mm.cc index 07a362b..a9a60c2 100644 --- a/src/sim/mm.cc +++ b/src/sim/mm.cc @@ -9,11 +9,12 @@ MM::MM(Stage *stage) : Stage(stage) { this->id = MEM; } void MM::advance_helper() { signed int data; + int i; switch (this->curr_instr->get_mnemonic()) { case LOAD: - this->status = this->storage->read_word( - this->id, this->curr_instr->get_s1(), data); + i = this->storage->read_word(this, this->curr_instr->get_s1(), data); + this->status = i ? OK : STALLED; if (this->status == OK) { this->curr_instr->set_s1(data); } else @@ -22,8 +23,9 @@ void MM::advance_helper() case STORE: // TODO signed issues, we aren't wrapping addresses - this->status = this->storage->write_word( - this->id, this->curr_instr->get_s2(), this->curr_instr->get_s1()); + i = this->storage->write_word( + this, this->curr_instr->get_s2(), this->curr_instr->get_s1()); + this->status = i ? OK : STALLED; if (this->status != OK) { this->status = STALLED; } diff --git a/src/storage/cache.cc b/src/storage/cache.cc deleted file mode 100644 index 80f59ef..0000000 --- a/src/storage/cache.cc +++ /dev/null @@ -1,180 +0,0 @@ -#include "cache.h" -#include "definitions.h" -#include "response.h" -#include "utils.h" -#include <bits/stdc++.h> -#include <iostream> -#include <iterator> - -Cache::Cache(Storage *lower, int delay) -{ - this->data = new std::vector<std::array<signed int, LINE_SIZE>>; - this->data->resize(L1_CACHE_LINES); - this->delay = delay; - this->is_waiting = false; - this->lower = lower; - this->meta.fill({-1, -1}); - this->requester = IDLE; - this->wait_time = this->delay; -} - -Cache::~Cache() -{ - delete this->lower; - delete this->data; -} - -Response Cache::write_word(Accessor accessor, signed int data, int address) -{ - return process(accessor, address, [&](int index, int offset) { - this->data->at(index).at(offset) = data; - this->meta[index].at(1) = 1; - }); -} - -Response Cache::write_line( - Accessor accessor, std::array<signed int, LINE_SIZE> data_line, int address) -{ - return process(accessor, address, [&](int index, int offset) { - (void)offset; - this->data->at(index) = data_line; - this->meta[index].at(1) = 1; - }); -} - -// TODO: tests for multi level cache -Response Cache::read_line( - Accessor accessor, - int address, - std::array<signed int, LINE_SIZE> &data_line) -{ - return process(accessor, address, [&](int index, int offset) { - (void)offset; - data_line = this->data->at(index); - }); -} - -Response Cache::read_word(Accessor accessor, int address, signed int &data) -{ - return process(accessor, address, [&](int index, int offset) { - data = this->data->at(index).at(offset); - }); -} - -Response Cache::process( - Accessor accessor, - int address, - std::function<void(int index, int offset)> request_handler) -{ - Response r = this->is_access_cleared(accessor, address); - if (r == OK) { - int tag, index, offset; - get_cache_fields(address, &tag, &index, &offset); - request_handler(index, offset); - } - return r; -} - -Response Cache::is_access_cleared(Accessor accessor, int address) -{ - Response r; - r = WAIT; - /* Do this first--then process the first cycle immediately. */ - if (this->requester == IDLE) - this->requester = accessor; - if (this->requester == accessor) { - handle_miss(address); - if (this->is_waiting) - r = BLOCKED; - else if (this->wait_time == 0) { - this->requester = IDLE; - this->wait_time = delay; - r = OK; - } else { - --this->wait_time; - } - } - return r; -} - -void Cache::handle_miss(int expected) -{ - Response r, q; - int tag, index, offset; - std::array<signed int, LINE_SIZE> *actual; - std::array<int, 2> *meta; - - get_cache_fields(expected, &tag, &index, &offset); - r = OK; - meta = &this->meta.at(index); - actual = &this->data->at(index); - - if (meta->at(0) != tag) { - r = WAIT; - // address not in cache - if (meta->at(1) >= 0) { - // occupant is dirty - // writing line to DRam in case of dirty cache eviction - q = this->lower->write_line( - L1CACHE, *actual, - ((index << LINE_SPEC) + - (meta->at(0) << (L1_CACHE_LINE_SPEC + LINE_SPEC)))); - if (q == OK) { - meta->at(1) = -1; - } - } else { - q = this->lower->read_line(L1CACHE, expected, *actual); - if (q == OK) { - meta->at(0) = tag; - } - } - } - - this->is_waiting = (r == OK) ? false : true; -} - -std::array<std::array<int, 2>, L1_CACHE_LINES> Cache::get_meta() const -{ - std::array<std::array<int, 2>, L1_CACHE_LINES> ret; - std::copy(std::begin(this->meta), std::end(this->meta), std::begin(ret)); - return ret; -} - -std::ostream &operator<<(std::ostream &os, const Cache &c) -{ - const auto default_flags = std::cout.flags(); - const auto default_fill = std::cout.fill(); - - std::vector<std::array<signed int, LINE_SIZE>> data = - c.view(0, L1_CACHE_LINES); - std::array<std::array<int, 2>, L1_CACHE_LINES> meta = c.get_meta(); - - os << " " << std::setfill(' ') << std::setw(L1_CACHE_LINE_SPEC + 2) - << "INDEX" - << " | " << std::setfill(' ') << std::setw((8 + 3) * 4 - 1) << "DATA" - << " | " << std::setfill(' ') - << std::setw(MEM_LINE_SPEC - L1_CACHE_LINE_SPEC + 2) << "TAG" - << " | D" << std::endl; - for (int i = 0; i < L1_CACHE_LINES; ++i) { - os << " 0b" << std::setw(L1_CACHE_LINE_SPEC) - << std::bitset<L1_CACHE_LINE_SPEC>(i) << " | "; - for (int j = 0; j < LINE_SIZE; ++j) { - os << "0x" << std::setfill('0') << std::setw(8) << std::hex - << data.at(i).at(j) << " "; - } - os << "| 0b" << std::setfill(' '); - - if (meta.at(i)[0] < 0) - os << std::setfill('?') - << std::setw(MEM_LINE_SPEC - L1_CACHE_LINE_SPEC) << ""; - else - os << std::bitset<MEM_LINE_SPEC - L1_CACHE_LINE_SPEC>( - meta.at(i)[0]); - - os << " | " << (int)(meta.at(i)[0] >= 0) << std::endl; - } - - std::cout.flags(default_flags); - std::cout.fill(default_fill); - return os; -} diff --git a/src/storage/dram.cc b/src/storage/dram.cc deleted file mode 100644 index f90f8db..0000000 --- a/src/storage/dram.cc +++ /dev/null @@ -1,130 +0,0 @@ -#include "dram.h" -#include "definitions.h" -#include "response.h" -#include <algorithm> -#include <bits/stdc++.h> -#include <bitset> -#include <iostream> -#include <iterator> -#include <utils.h> - -Dram::Dram(int delay) -{ - this->data = new std::vector<std::array<signed int, LINE_SIZE>>; - this->data->resize(MEM_LINES); - this->delay = delay; - this->is_waiting = false; - this->lower = nullptr; - this->requester = IDLE; - this->wait_time = this->delay; -} - -Dram::~Dram() { delete this->data; } - -Response Dram::write_line( - Accessor accessor, std::array<signed int, LINE_SIZE> data_line, int address) -{ - return process(accessor, address, [&](int line, int word) { - (void)word; - this->data->at(line) = data_line; - }); -} - -Response Dram::write_word(Accessor accessor, signed int data, int address) -{ - return process(accessor, address, [&](int line, int word) { - this->data->at(line).at(word) = data; - }); -} - -// TODO requires testing -Response Dram::read_line( - Accessor accessor, - int address, - std::array<signed int, LINE_SIZE> &data_line) -{ - return process(accessor, address, [&](int line, int word) { - (void)word; - data_line = this->data->at(line); - }); -} - -Response Dram::read_word(Accessor accessor, int address, signed int &data) -{ - return process(accessor, address, [&](int line, int word) { - data = this->data->at(line).at(word); - }); -} - -// TODO load a file instead and test this method -void Dram::load(std::vector<signed int> program) { - unsigned long i; - for (i = 0; i < program.size(); ++i) { - int line, word; - get_memory_index(i, line, word); - this->data->at(line).at(word) = program[i]; - } -} - -Response Dram::process( - Accessor accessor, - int address, - std::function<void(int line, int word)> request_handler) -{ - Response r = this->is_access_cleared(accessor); - if (r == OK) { - int line, word; - get_memory_index(address, line, word); - request_handler(line, word); - } - return r; -} - -Response Dram::is_access_cleared(Accessor accessor) -{ - Response r; - r = WAIT; - /* Do this first--then process the first cycle immediately. */ - if (accessor == SIDE) - r = OK; - else { - if (this->requester == IDLE) - this->requester = accessor; - if (this->requester == accessor) { - if (this->wait_time == 0) { - this->requester = IDLE; - this->wait_time = delay; - r = OK; - } else { - --this->wait_time; - } - } - } - return r; -} - -std::ostream &operator<<(std::ostream &os, const Dram &d) -{ - const auto default_flags = std::cout.flags(); - const auto default_fill = std::cout.fill(); - - std::vector<std::array<signed int, LINE_SIZE>> data = d.view(0, MEM_LINES); - - os << " " << std::setfill(' ') << std::setw(MEM_LINE_SPEC + 2 + LINE_SPEC) - << "ADDRESS" - << " | " << std::setfill(' ') << std::setw((8 + 3) * 4 - 1) << "DATA" - << std::endl; - for (int i = 0; i < MEM_LINES; ++i) { - os << " 0b" << std::setw(MEM_LINE_SPEC + LINE_SPEC) << left - << std::bitset<MEM_LINE_SPEC>(i) << " | "; - for (int j = 0; j < LINE_SIZE; ++j) { - os << "0x" << std::setfill('0') << std::setw(8) << std::hex - << data.at(i).at(j) << ' '; - } - os << std::endl; - } - - std::cout.flags(default_flags); - std::cout.fill(default_fill); - return os; -} diff --git a/src/storage/storage.cc b/src/storage/storage.cc deleted file mode 100644 index fed607b..0000000 --- a/src/storage/storage.cc +++ /dev/null @@ -1,16 +0,0 @@ -#include "storage.h" -#include "definitions.h" -#include <algorithm> - -std::vector<std::array<signed int, LINE_SIZE>> -Storage::view(int base, int lines) const -{ - base = (base / LINE_SIZE) * LINE_SIZE; - std::vector<std::array<signed int, LINE_SIZE>> ret(lines + 1); - std::copy( - this->data->begin() + base, this->data->begin() + base + lines, - ret.begin()); - return ret; -} - -Storage *Storage::get_lower() { return this->lower; } diff --git a/tests/cache.cc b/tests/cache.cc deleted file mode 100644 index 0b04bce..0000000 --- a/tests/cache.cc +++ /dev/null @@ -1,189 +0,0 @@ -#include "cache.h" -#include "dram.h" -#include <catch2/catch_test_macros.hpp> - -class CacheFixture -{ - public: - CacheFixture() - { - this->m_delay = 4; - this->c_delay = 2; - this->d = new Dram(this->m_delay); - this->c = new Cache(this->d, this->c_delay); - this->expected = {0, 0, 0, 0}; - this->actual = this->c->view(0, 1)[0]; - } - - ~CacheFixture() { delete this->c; } - - /** - * An operation that is done a lot. - */ - void - wait_for_storage(int delay, Response expected, std::function<Response()> f) - { - for (int i = 0; i < delay; ++i) { - Response r = f(); - - // check response - CHECK(r == expected); - // check for early modifications - actual = c->view(0, 1)[0]; - REQUIRE(this->expected == this->actual); - } - } - - int m_delay; - int c_delay; - Cache *c; - Dram *d; - std::array<signed int, LINE_SIZE> expected; - std::array<signed int, LINE_SIZE> actual; -}; - -TEST_CASE_METHOD(CacheFixture, "store 0th element in DELAY cycles", "[dram]") -{ - Response r; - signed int w; - CHECK(expected == actual); - - w = 0x11223344; - // delay + 1 due to internal logic, when mem - // finishes handle_miss still returns 'blocked' - this->wait_for_storage(this->m_delay + 1, BLOCKED, [this, w]() { - return this->c->write_word(MEM, w, 0b0); - }); - - this->wait_for_storage(this->c_delay, WAIT, [this, w]() { - return this->c->write_word(MEM, w, 0b0); - }); - - r = c->write_word(MEM, w, 0b0); - CHECK(r == OK); - - actual = this->d->view(0, 1)[0]; - // we do NOT write back now! - REQUIRE(expected == actual); - - expected.at(0) = w; - actual = c->view(0, 1)[0]; - REQUIRE(expected == actual); -} - -TEST_CASE_METHOD( - CacheFixture, - "store 0th, 1st element in DELAY cycles, with conflict", - "[cache]") -{ - Response r; - signed int w; - int i; - CHECK(expected == actual); - - w = 0x11223344; - // delay + 1 due to internal logic, when mem - // finishes handle_miss still returns 'blocked' - for (i = 0; i < this->m_delay + 1; ++i) { - r = c->write_word(MEM, w, 0b0); - CHECK(r == BLOCKED); - r = c->write_word(FETCH, w, 0b1); - CHECK(r == WAIT); - - // check for early modifications - actual = c->view(0, 1)[0]; - REQUIRE(this->expected == this->actual); - } - - for (i = 0; i < this->c_delay; ++i) { - r = c->write_word(MEM, w, 0b0); - CHECK(r == WAIT); - r = c->write_word(FETCH, w, 0b1); - CHECK(r == WAIT); - - // check for early modifications - actual = c->view(0, 1)[0]; - REQUIRE(this->expected == this->actual); - } - - r = c->write_word(MEM, w, 0b0); - CHECK(r == OK); - - actual = d->view(0, 1)[0]; - // we do NOT write back now! - REQUIRE(expected == actual); - - expected.at(0) = w; - actual = c->view(0, 1)[0]; - REQUIRE(expected == actual); - - // this should have been loaded already! - this->wait_for_storage(this->c_delay, WAIT, [this, w]() { - return this->c->write_word(FETCH, w, 0b1); - }); - - r = c->write_word(FETCH, w, 0b1); - CHECK(r == OK); - - expected.at(1) = w; - actual = c->view(0, 1)[0]; - REQUIRE(expected == actual); -} - -TEST_CASE_METHOD( - CacheFixture, - "store 0th, 1st element different tags, in DELAY cycles, no conflict", - "[cache]") -{ - Response r; - signed int w; - CHECK(expected == actual); - - w = 0x11223344; - // delay + 1 due to internal logic, when mem - // finishes handle_miss still returns 'blocked' - this->wait_for_storage(this->m_delay + 1, BLOCKED, [this, w]() { - return this->c->write_word(MEM, w, 0b0); - }); - - this->wait_for_storage(this->c_delay, WAIT, [this, w]() { - return this->c->write_word(MEM, w, 0b0); - }); - - r = c->write_word(MEM, w, 0b0); - CHECK(r == OK); - - expected.at(0) = w; - actual = c->view(0, 1)[0]; - REQUIRE(expected == actual); - - // write back to memory - this->wait_for_storage(this->m_delay + 1, BLOCKED, [this, w]() { - return this->c->write_word(FETCH, w, 0b10000001); - }); - - // fetch new address (don't run the completion cycle yet) - this->wait_for_storage(this->m_delay, BLOCKED, [this, w]() { - return this->c->write_word(FETCH, w, 0b10000001); - }); - - // after the fetch, this cache line should be empty - this->c->write_word(FETCH, w, 0b10000001); - CHECK(r == OK); - - expected.at(0) = 0; - actual = c->view(0, 1)[0]; - CHECK(expected == actual); - - this->wait_for_storage(this->c_delay, WAIT, [this, w]() { - return this->c->write_word(FETCH, w, 0b10000001); - }); - - r = c->write_word(FETCH, w, 0b10000001); - CHECK(r == OK); - - expected.at(0) = 0; - expected.at(1) = w; - actual = c->view(0, 1)[0]; - REQUIRE(expected == actual); -} diff --git a/tests/dram.cc b/tests/dram.cc deleted file mode 100644 index 0e97e81..0000000 --- a/tests/dram.cc +++ /dev/null @@ -1,351 +0,0 @@ -#include "dram.h" -#include <array> -#include <catch2/catch_test_macros.hpp> - -class DramFixture -{ - public: - DramFixture() - { - this->delay = 3; - this->d = new Dram(this->delay); - this->expected = {0, 0, 0, 0}; - this->actual = this->d->view(0, 1)[0]; - } - - ~DramFixture() { delete this->d; } - - /** - * An operation that is done a lot. - */ - void - wait_for_storage(int delay, Response expected, std::function<Response()> f) - { - for (int i = 0; i < delay; ++i) { - Response r = f(); - - // check response - CHECK(r == expected); - // check for early modifications - actual = d->view(0, 1)[0]; - REQUIRE(this->expected == this->actual); - } - } - - int delay; - Dram *d; - std::array<signed int, LINE_SIZE> expected; - std::array<signed int, LINE_SIZE> actual; -}; - -TEST_CASE_METHOD(DramFixture, "store 0th element in DELAY cycles", "[dram]") -{ - Response r; - signed int w; - CHECK(expected == actual); - - w = 0x11223344; - this->wait_for_storage(this->delay, WAIT, [this, w]() { - return this->d->write_word(MEM, w, 0x0); - }); - - r = this->d->write_word(MEM, w, 0x0); - - CHECK(r == OK); - expected.at(0) = w; - actual = this->d->view(0, 1)[0]; - REQUIRE(expected == actual); -} - -TEST_CASE_METHOD( - DramFixture, - "store 0th, 1st element in DELAY cycles, no conflict", - "[dram]") -{ - Response r; - signed int w; - CHECK(expected == actual); - - w = 0x11223344; - this->wait_for_storage(this->delay, WAIT, [this, w]() { - return this->d->write_word(MEM, w, 0x0); - }); - - r = d->write_word(MEM, w, 0x0); - REQUIRE(r == OK); - - expected.at(0) = w; - actual = d->view(0, 1)[0]; - REQUIRE(expected == actual); - - this->wait_for_storage(this->delay, WAIT, [this, w]() { - return this->d->write_word(FETCH, w, 0x1); - }); - - r = d->write_word(FETCH, w, 0x1); - CHECK(r == OK); - - actual = d->view(0, 1)[0]; - expected.at(1) = w; - REQUIRE(expected == actual); -} - -TEST_CASE_METHOD( - DramFixture, "store 0th element in DELAY cycles with conflict", "[dram]") -{ - Response r; - signed int w; - int i; - CHECK(expected == actual); - - w = 0x11223344; - for (i = 0; i < this->delay; ++i) { - r = this->d->write_word(MEM, w, 0x0); - CHECK(r == WAIT); - r = this->d->write_word(FETCH, w, 0x1); - CHECK(r == WAIT); - - // check for early modifications - actual = d->view(0, 1)[0]; - REQUIRE(expected == actual); - } - - r = d->write_word(MEM, w, 0x0); - REQUIRE(r == OK); - - expected.at(0) = w; - actual = d->view(0, 1)[0]; - REQUIRE(expected == actual); - - this->wait_for_storage(this->delay, WAIT, [this, w]() { - return this->d->write_word(FETCH, w, 0x1); - }); - - r = d->write_word(FETCH, w, 0x1); - CHECK(r == OK); - - actual = d->view(0, 1)[0]; - expected.at(1) = w; - REQUIRE(expected == actual); -} - -TEST_CASE_METHOD(DramFixture, "store line in DELAY cycles", "[dram]") -{ - Response r; - signed int w; - std::array<signed int, LINE_SIZE> buffer; - CHECK(expected == actual); - - w = 0x11223344; - buffer = {w, w + 1, w + 2, w + 3}; - this->wait_for_storage(this->delay, WAIT, [this, w, buffer]() { - return this->d->write_line(MEM, buffer, 0x0); - }); - - r = d->write_line(MEM, buffer, 0x0); - CHECK(r == OK); - - actual = d->view(0, 1)[0]; - expected = buffer; - REQUIRE(expected == actual); -} - -TEST_CASE_METHOD( - DramFixture, "store line in DELAY cycles no conflict", "[dram]") -{ - Response r; - signed int w; - std::array<signed int, LINE_SIZE> buffer; - CHECK(expected == actual); - - w = 0x11223344; - buffer = {w, w + 1, w + 2, w + 3}; - this->wait_for_storage(this->delay, WAIT, [this, w, buffer]() { - return this->d->write_line(MEM, buffer, 0x0); - }); - - r = this->d->write_line(MEM, buffer, 0x0); - REQUIRE(r == OK); - - expected = buffer; - actual = d->view(0, 1)[0]; - REQUIRE(expected == actual); - - buffer = {w + 4, w + 5, w + 6, w + 7}; - this->wait_for_storage(this->delay, WAIT, [this, w, buffer]() { - return this->d->write_line(FETCH, buffer, 0x1); - }); - - r = this->d->write_line(FETCH, buffer, 0x1); - CHECK(r == OK); - - expected = buffer; - actual = d->view(0, 1)[0]; - REQUIRE(expected == actual); -} - -TEST_CASE_METHOD( - DramFixture, "store line in DELAY cycles with conflict", "[dram]") -{ - Response r; - signed int w; - int i; - std::array<signed int, LINE_SIZE> buffer; - CHECK(expected == actual); - - w = 0x11223344; - buffer = {w, w + 1, w + 2, w + 3}; - for (i = 0; i < this->delay; ++i) { - r = this->d->write_line(MEM, buffer, 0x0); - CHECK(r == WAIT); - r = d->write_line(FETCH, buffer, 0x1); - CHECK(r == WAIT); - - // check for early modifications - actual = d->view(0, 1)[0]; - REQUIRE(expected == actual); - } - - r = d->write_line(MEM, buffer, 0x0); - CHECK(r == OK); - - actual = d->view(0, 1)[0]; - expected = buffer; - REQUIRE(expected == actual); - - buffer = {w + 4, w + 5, w + 6, w + 7}; - this->wait_for_storage(this->delay, WAIT, [this, w, buffer]() { - return this->d->write_line(FETCH, buffer, 0x1); - }); - - r = this->d->write_line(FETCH, buffer, 0x1); - CHECK(r == OK); - - expected = buffer; - actual = d->view(0, 1)[0]; - REQUIRE(expected == actual); -} - -TEST_CASE_METHOD( - DramFixture, - "store line in DELAY cycles, read in DELAY cycles, no conflict", - "[dram]") -{ - Response r; - signed int w; - int i, addr; - CHECK(expected == actual); - - w = 0x11223311; - addr = 0x0; - expected = {w, w + 1, w + 2, w + 3}; - for (i = 0; i < this->delay; ++i) { - r = d->write_line(MEM, expected, addr); - CHECK(r == WAIT); - } - r = d->write_line(MEM, expected, addr); - CHECK(r == OK); - - for (i = 0; i < this->delay; ++i) { - r = d->read_line(MEM, addr, actual); - - CHECK(r == WAIT); - REQUIRE(expected != actual); - } - - r = d->read_line(MEM, addr, actual); - - CHECK(r == OK); - REQUIRE(expected == actual); -} - -TEST_CASE_METHOD( - DramFixture, - "store line in DELAY cycles, read in DELAY cycles with conflict", - "[dram]") -{ - Response r; - signed int w; - int i, addr; - CHECK(expected == actual); - - w = 0x11223311; - addr = 0x0; - expected = {w, w + 1, w + 2, w + 3}; - for (i = 0; i < delay; ++i) { - r = d->write_line(MEM, expected, addr); - CHECK(r == WAIT); - - r = d->read_line(FETCH, addr, actual); - CHECK(r == WAIT); - } - r = d->write_line(MEM, expected, addr); - CHECK(r == OK); - - for (i = 0; i < this->delay; ++i) { - r = d->read_line(MEM, addr, actual); - - CHECK(r == WAIT); - REQUIRE(expected != actual); - } - - r = d->read_line(MEM, addr, actual); - - CHECK(r == OK); - REQUIRE(expected == actual); -} - -TEST_CASE_METHOD( - DramFixture, - "store line in DELAY cycles, read one element at a time in DELAY cycles " - "with conflict", - "[dram]") -{ - Response r; - signed int w, a; - int i, j, addr; - CHECK(expected == actual); - - w = 0x11223311; - a = 0x0; - addr = 0x0; - expected = {w, w + 1, w + 2, w + 3}; - for (i = 0; i < this->delay; ++i) { - r = d->write_line(MEM, expected, addr); - CHECK(r == WAIT); - } - r = d->write_line(MEM, expected, addr); - CHECK(r == OK); - - actual = d->view(0, 1)[0]; - REQUIRE(expected == actual); - - for (i = 0; i < LINE_SIZE; ++i) { - for (j = 0; j < this->delay; ++j) { - r = d->read_word(MEM, addr, a); - - CHECK(r == WAIT); - REQUIRE(0x0 == a); - } - r = d->read_word(MEM, addr++, a); - CHECK(r == OK); - REQUIRE(w++ == a); - - a = 0; - } -} - -TEST_CASE_METHOD(DramFixture, "Sidedoor bypasses delay", "[dram]") -{ - Response r; - signed int w; - CHECK(expected == actual); - - w = 0x11223344; - r = this->d->write_word(SIDE, w, 0x0); - CHECK(r == OK); - - expected.at(0) = w; - actual = d->view(0, 1)[0]; - REQUIRE(expected == actual); -} |