diff options
author | Siddarth Suresh <155843085+SiddarthSuresh98@users.noreply.github.com> | 2025-03-11 11:28:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-11 11:28:25 -0400 |
commit | 33c7c78b1c65c375d0291fd435e02ddc9d35681b (patch) | |
tree | 25646d98b4bfcf4b9a664eabfc2651c481984c1d | |
parent | 66edce63597093cf5f3afa5b577fd9e3ecae0ef6 (diff) | |
parent | 202f9a05d449ddc1160584c4e8a87f397f248e94 (diff) |
Merge pull request #23 from bdunahu/bdunahu
Memory simulator CLI function implementation
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | inc/accessor.h | 15 | ||||
-rw-r--r-- | inc/cache.h | 10 | ||||
-rw-r--r-- | inc/cli.h | 172 | ||||
-rw-r--r-- | inc/definitions.h | 19 | ||||
-rw-r--r-- | inc/dram.h | 16 | ||||
-rw-r--r-- | inc/logger.h | 26 | ||||
-rw-r--r-- | inc/response.h | 3 | ||||
-rw-r--r-- | inc/storage.h | 24 | ||||
-rw-r--r-- | inc/utils.h | 9 | ||||
-rw-r--r-- | src/cli/cli.cc | 362 | ||||
-rw-r--r-- | src/logger/logger.cc | 22 | ||||
-rw-r--r-- | src/main.cc | 51 | ||||
-rw-r--r-- | src/storage/cache.cc | 47 | ||||
-rw-r--r-- | src/storage/dram.cc | 28 | ||||
-rw-r--r-- | src/storage/storage.cc | 6 | ||||
-rw-r--r-- | src/utils/accessor.cc | 8 | ||||
-rw-r--r-- | src/utils/response.cc | 8 | ||||
-rw-r--r-- | src/utils/utils.cc | 25 | ||||
-rw-r--r-- | tests/cache.cc | 31 | ||||
-rw-r--r-- | tests/logger.cc | 65 |
21 files changed, 562 insertions, 388 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 17ec29f..6a48922 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,9 @@ project(risc_vector) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +add_compile_options(-Wall -lstdc++) +add_compile_options(-Wextra -Wpedantic) + # cpp standard set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/inc/accessor.h b/inc/accessor.h new file mode 100644 index 0000000..fb4999d --- /dev/null +++ b/inc/accessor.h @@ -0,0 +1,15 @@ +#ifndef ACCESSOR_H +#define ACCESSOR_H +#include <iostream> + +enum Accessor { + IDLE, + MEM, + FETCH, + L1CACHE, + SIDE, +}; + +std::ostream &operator<<(std::ostream &os, Accessor a); + +#endif /* ACCESSOR_H_INCLUDED */ diff --git a/inc/cache.h b/inc/cache.h index e8b7030..04f6181 100644 --- a/inc/cache.h +++ b/inc/cache.h @@ -3,6 +3,7 @@ #include "definitions.h" #include "storage.h" #include <array> +#include <ostream> class Cache : public Storage { @@ -25,6 +26,13 @@ class Cache : public Storage int address, std::array<signed int, LINE_SIZE> &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_SIZE> get_meta() const; + private: /** * Fetches `address` from a lower level of storage if it is not already @@ -42,4 +50,6 @@ class Cache : public Storage std::array<std::array<int, 2>, L1_CACHE_SIZE> meta; }; +std::ostream &operator<<(std::ostream &os, const Cache &c); + #endif /* CACHE_H_INCLUDED */ @@ -1,95 +1,97 @@ #ifndef CLI_H #define CLI_H -#include <unordered_map> +#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(); -class Cli { - public: - - 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 - * @param pipeline_stage pipeline stage to be served by memory subsystem - */ - void load(int memory_address, int pipeline_stage); - - /** - * Stores data into memory at the specified address. - * @param memory_address address of the memory where data needs to be stored - * @param pipeline_stage pipeline stage to be served by memory subsystem - * @param data data value to be written to the memory - */ - void store(int memory_address, int pipeline_stage, int data); - - /** - * Loads a memory image from a file and configures memory to the image. - * This function provides a side door memory image loading interface to the memory system, - * allowing the user to load a memory image from a file and configure the memory subsystem to the image. - * @param filename name of file containing memory image - */ - void load_memory_image(const std::string& filename); - - /** - * 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(); - - /** - * Updates the memory at the specified address with the given data. - * This function provides a side door modification interface to the memory system, - * allowing the user to modify the memory configuration directly. - * @param memory_address address of the memory to be updated - * @param data data value to be written to the memory - */ - void update_memory(int memory_address, int data); - - /** - * Displays the current status of the entire memory subsystem. - * This function provides a side door view into the memory system, - * showing its current state and configuration. - */ - void view_memory(); - - /** - * Displays the data at the specified memory address. - * This function provides a side door view into the memory system, - * showing the data at the specified memory address. - * @param memory_address address of the memory to be viewed - */ - void view_memory_address(int memory_address); - - /** - * Updates the controls using a configuration file. - * This function provides a side door modification interface to the control system, - * allowing the user to update the controls directly. - * @param config_file name of file containing control configuration - */ - void update_controls(const std::string& config_file); + /** + * Advance the clock one cycle, refreshing the storage devices. + */ + void clock(); - /** - * Runs the command line interface - * This function is the main entry point for the command line interface. - */ - void run(); + /** + * 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); - private: + /** + * Runs the command line interface + * This function is the main entry point for the command line interface. + */ + void run(); - /** Map of commands and their corresponding functions. - * This map is used to store the commands and their corresponding functions. - */ - std::unordered_map<std::string, std::function<void(std::vector<std::string>)>> commands; + 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 */
\ No newline at end of file +#endif /* CLI_H_INCLUDED */ diff --git a/inc/definitions.h b/inc/definitions.h index 877065e..f015ce9 100644 --- a/inc/definitions.h +++ b/inc/definitions.h @@ -1,5 +1,6 @@ #ifndef DEFINITIONS_H #define DEFINITIONS_H +#include "logger.h" #include <cmath> /** @@ -9,17 +10,17 @@ /** * The total number of words in a line */ -#define LINE_SIZE (int)pow(2, 2) +#define LINE_SIZE static_cast<int>(pow(2, 2)) /** * The number of bits to specify a memory line * calculated as: (/ (expt 2 15) 4) */ -#define MEM_SPEC 13 +#define MEM_SPEC 8 /** * The total number of words in memory */ -#define MEM_SIZE (int)pow(2, MEM_SPEC) +#define MEM_SIZE static_cast<int>(pow(2, MEM_SPEC)) /** * The number of bits to specify a l1 cache line @@ -28,7 +29,17 @@ /** * The total number of words in l1 cache */ -#define L1_CACHE_SIZE (int)pow(2, L1_CACHE_SPEC) +#define L1_CACHE_SIZE static_cast<int>(pow(2, L1_CACHE_SPEC)) + +/** + * The total number of cycles a memory access takes. + */ +#define MEM_DELAY 4 + +/** + * The total number of cycles a level one cache access takes + */ +#define L1_CACHE_DELAY 1 /** * Return the N least-significant bits from integer K using a bit mask @@ -1,6 +1,8 @@ #ifndef DRAM_H #define DRAM_H -#include <storage.h> +#include "definitions.h" +#include "storage.h" +#include <ostream> class Dram : public Storage { @@ -16,9 +18,12 @@ class Dram : public Storage ~Dram(); Response write(Accessor accessor, signed int data, int address) override; - Response read(Accessor accessor, int address, std::array<signed int, LINE_SIZE>& data) override; + Response read( + Accessor accessor, + int address, + std::array<signed int, LINE_SIZE> &data) override; - private: + private: /** * Helper for `write`. */ @@ -26,8 +31,9 @@ class Dram : public Storage /** * Helper for `read`. */ - void do_read(std::array<signed int, LINE_SIZE>& data_line, int address); + void do_read(std::array<signed int, LINE_SIZE> &data_line, int address); }; -#endif /* DRAM_H_INCLUDED */ +std::ostream &operator<<(std::ostream &os, const Dram &d); +#endif /* DRAM_H_INCLUDED */ diff --git a/inc/logger.h b/inc/logger.h index 7ab3051..38527c8 100644 --- a/inc/logger.h +++ b/inc/logger.h @@ -9,13 +9,14 @@ enum LogLevel { DEBUG, INFO, WARNING, ERROR, CRITICAL }; class Logger { public: + static Logger* getInstance(); + + ~Logger(); + /** - * Constructor. - * @param The file name to log to. - * @return A new logger object. + * Do not allow copies. */ - Logger(const string &); - ~Logger(); + Logger(const Logger& obj) = delete; /** * Set the log level. @@ -31,10 +32,17 @@ class Logger void log(LogLevel, const string &); private: - LogLevel level = INFO; - ofstream logFile; - string levelToString(LogLevel); - int levelToInt(LogLevel); + /** + * Constructor. + * @param The file name to log to. + * @return A new logger object. + */ + Logger(const string &); + static Logger* logger_instance; + static LogLevel level; + static ofstream logFile; + static string level_to_string(LogLevel); + static int level_to_int(LogLevel); }; #endif /* LOGGER_H_INCLUDED */ diff --git a/inc/response.h b/inc/response.h index d945e0f..6cd6678 100644 --- a/inc/response.h +++ b/inc/response.h @@ -1,5 +1,6 @@ #ifndef RESPONSE_H #define RESPONSE_H +#include <iostream> enum Response { OK, @@ -7,4 +8,6 @@ enum Response { BLOCKED, }; +std::ostream &operator<<(std::ostream &os, Response r); + #endif /* RESPONSE_H_INCLUDED */ diff --git a/inc/storage.h b/inc/storage.h index 793b982..a30e74d 100644 --- a/inc/storage.h +++ b/inc/storage.h @@ -1,22 +1,18 @@ #ifndef STORAGE_H #define STORAGE_H +#include "accessor.h" #include "definitions.h" #include "response.h" #include <algorithm> #include <array> +#include <map> #include <vector> -enum Accessor { - IDLE, - MEM, - FETCH, - L1CACHE, - SIDE, -}; - class Storage { public: + virtual ~Storage() = default; + /** * Write `data` into `address`. * @param the source making the request. @@ -43,12 +39,20 @@ class Storage * @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); + std::vector<std::array<signed int, LINE_SIZE>> + view(int base, int lines) const; /** - * Advances to the next job if the current job is completed. + * Refreshes the state of this storage device and lower. */ void resolve(); + /** + * 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. diff --git a/inc/utils.h b/inc/utils.h index e258ed8..71e515b 100644 --- a/inc/utils.h +++ b/inc/utils.h @@ -1,5 +1,6 @@ #ifndef UTILS_H #define UTILS_H +#include <string> /** * Parse an address into a tag, index into the cache table, and a line @@ -11,4 +12,12 @@ */ void get_bit_fields(int address, int *tag, int *index, int *offset); +/** + * Formats a string using snprintf. + * @param an object that represents the format string + * @param arguments to be formatted + * @return a string object holding the formatted result + */ +const std::string string_format(const char *const zcFormat, ...); + #endif /* UTILS_H_INCLUDED */ diff --git a/src/cli/cli.cc b/src/cli/cli.cc index 5d78744..0729e00 100644 --- a/src/cli/cli.cc +++ b/src/cli/cli.cc @@ -1,187 +1,225 @@ #include "cli.h" -#include <sstream> +#include "cache.h" +#include "definitions.h" +#include "dram.h" +#include "response.h" +#include "utils.h" #include <iostream> -#include <vector> - -Cli::Cli() { - commands["load"] = [this](std::vector<std::string> args) { - if (args.size() >= 2){ - try{ - load(std::stoi(args[0]), std::stoi(args[1])); - } catch(const std::exception &e){ - std::cerr << "Invalid input: " << e.what() << std::endl; - } - } - else { - std::cout << "Usage: load <memory-address> <pipeline-stage>\n"; - } - return; - }; - - commands["store"] = [this](std::vector<std::string> args) { - if (args.size() >= 3) { - try{ - store(std::stoi(args[0]), std::stoi(args[1]), std::stoi(args[2])); - }catch(const std::exception &e) { - std::cerr << "Invalid input: " << e.what() << std::endl; - } - } - else { - std::cout << "Usage: store <memory-address> <pipeline-stage> <data>\n"; - } - return; - }; - - commands["load-memory-image"] = [this](std::vector<std::string> args) { - if (!args.empty()) { - load_memory_image(args[0]); - } - else { - std::cout << "Usage: load-memory-image <filename>\n"; - } - return; - }; - - commands["reset"] = [this](std::vector<std::string> args) { - reset(); - return; - }; - - commands["update-memory"] = [this](std::vector<std::string> args) { - if (args.size() >= 2) { - try { - update_memory(std::stoi(args[0]), std::stoi(args[1])); - } catch(const std::exception &e){ - std::cerr << "Invalid input: all arguments are integers" << e.what() << std::endl; - } - } - else{ - std::cout << "Usage: update-memory <memory-address> <data>\n"; - } - return; - }; - - commands["view-memory"] = [this](std::vector<std::string> args) { - view_memory(); - return; - }; - - commands["view-memory-address"] = [this](std::vector<std::string> args) { - if (!args.empty()) { - try{ - view_memory_address(std::stoi(args[0])); - } catch(const std::exception &e){ - std::cerr << "Invalid input: " << e.what() << std::endl; - } - } - else { - std::cout << "Usage: view-memory-address <memory-address>\n"; - } - return; - }; - - commands["update-controls"] = [this](std::vector<std::string> args) { - if (!args.empty()) { - update_controls(args[0]); - } - else { - std::cout << "Usage: update-controls <configuration-file>\n"; - } - return; - }; - - commands["help"] = [this](std::vector<std::string> args) { - help(); - return; - }; -} -Cli::~Cli() {} - -//TODO: These function stubs are to be improved after they have been implemented internally. -void Cli::help() { - std::cout << "Available commands:\n" - << " load <memory-address> <pipeline-stage> - Load data from memory at specified address\n" - << " store <memory-address> <pipeline-stage> <data> - Stores data into memory at specified address\n" - << " load-memory-image <filename> - side door function that loads a memory image from a file and configures memory to the image\n" - << " reset - side door function that resets the memory configuration and cycles\n" - << " update-memory <memory-address> <data> - side door function that updates the memory at the specified address with data provided\n" - << " view-memory - side door function that views the current status of the entire memory subsystem\n" - << " view-memory-address <memory-address> - side door function that views data at specific memory address\n" - << " update-controls <configuration-file> - side door function that takes in a configuration file and updates the controls\n" - << " exit - Quits the program\n"; +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) { + 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['c'] = [this](std::vector<std::string> args) { + clock(); + return; + }; + + commands['h'] = [this](std::vector<std::string> args) { + help(); + return; + }; } -void Cli::load(int memory_address, int pipeline_stage) { - std::cout << "Loading data from memory address " << memory_address - << " at pipeline stage " << pipeline_stage << ".\n"; +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 + << std::endl + << " [c]ycle - manually advances the clock" << std::endl + << " [f]orce - advances the clock until one operation reports " + "completion" + << 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) +{ + const auto default_flags = std::cout.flags(); + const auto default_fill = std::cout.fill(); -void Cli::store(int memory_address, int pipeline_stage, int data) { - std::cout << "Storing " << data << " into memory address " << memory_address - << " at pipeline stage " << pipeline_stage << ".\n"; -} - + 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 << "\tGot:" << std::hex << data; -void Cli::load_memory_image(const std::string& filename) { - std::cout << "Loading memory image from file: " << filename << ".\n"; + std::cout.flags(default_flags); + std::cout.fill(default_fill); } - -void Cli::reset() { - std::cout << "Resetting memory configuration and cycles.\n"; +void Cli::store(Accessor accessor, int data, int address) +{ + Response r = this->cache->write(accessor, data, address); + std::cout << r << " to " << accessor << " storing " << data << " in" + << address << std::endl; } +void Cli::clock() +{ + this->cache->resolve(); + ++this->cycle; +} -void Cli::update_memory(int memory_address, int data) { - std::cout << "Updating memory at address " << memory_address - << " with data " << data << ".\n"; +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::view_memory() { - std::cout << "Viewing current status of memory subsystem.\n"; +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(); -void Cli::view_memory_address(int memory_address) { - std::cout << "Viewing data at memory address " << memory_address << ".\n"; -} + global_log->log(INFO, "Resetting memory configuration and cycle."); + if (this->cache != nullptr) + delete this->cache; -void Cli::update_controls(const std::string& config_file) { - std::cout << "Updating controls using configuration file: " << config_file << ".\n"; + Dram *d = new Dram(MEM_SIZE, MEM_DELAY); + this->cache = new Cache(d, L1_CACHE_DELAY); + this->cycle = 1; } -void Cli::run(){ - std::cout << "Memory Command Processor Started. Type 'help' for a list of commands.\n"; - - std::string input; - while (true) { - std::cout << "> "; - std::getline(std::cin, input); - std::istringstream iss(input); - std::vector<std::string> tokens; - std::string word; - - while (iss >> word) tokens.push_back(word); - if (tokens.empty()) continue; - - std::string command = tokens[0]; - tokens.erase(tokens.begin()); - - if (command == "exit") { - std::cout << "Exiting...\n"; - break; - } - - auto it = commands.find(command); - if (it != commands.end()) { - it->second(tokens); - } else { - std::cout << "Unknown command. Type 'help' for available commands.\n"; - } - } +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/logger/logger.cc b/src/logger/logger.cc index fb5052a..55d7a15 100644 --- a/src/logger/logger.cc +++ b/src/logger/logger.cc @@ -5,6 +5,10 @@ #include <sstream> using namespace std; +LogLevel Logger::level = INFO; +ofstream Logger::logFile; +Logger *Logger::logger_instance; + Logger::Logger(const string &filename) { if (!filename.empty()) { @@ -17,11 +21,11 @@ Logger::Logger(const string &filename) Logger::~Logger() { logFile.close(); } -void Logger::setLevel(LogLevel level) { this->level = level; } +void Logger::setLevel(LogLevel level) { level = level; } void Logger::log(LogLevel level, const string &message) { - if (levelToInt(level) > levelToInt(this->level)) { + if (level_to_int(level) > level_to_int(level)) { return; } @@ -31,7 +35,7 @@ void Logger::log(LogLevel level, const string &message) strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", timeinfo); ostringstream logEntry; - logEntry << "[" << timestamp << "] " << levelToString(level) << ": " + logEntry << "[" << timestamp << "] " << level_to_string(level) << ": " << message << endl; cout << logEntry.str(); @@ -42,7 +46,15 @@ void Logger::log(LogLevel level, const string &message) } } -string Logger::levelToString(LogLevel level) +Logger *Logger::getInstance() +{ + if (logger_instance == nullptr) { + logger_instance = new Logger("vector.log"); + } + return logger_instance; +} + +string Logger::level_to_string(LogLevel level) { switch (level) { case DEBUG: @@ -60,7 +72,7 @@ string Logger::levelToString(LogLevel level) } } -int Logger::levelToInt(LogLevel level) +int Logger::level_to_int(LogLevel level) { switch (level) { case DEBUG: diff --git a/src/main.cc b/src/main.cc index 68b3cdf..08b38e6 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,18 +1,38 @@ -#include "logger.h" #include "cli.h" +#include "logger.h" +#include "definitions.h" #include <getopt.h> #include <iostream> +static Logger *global_log = Logger::getInstance(); +static std::string version_number = "v0.1"; +static std::string banner = + " _/_/_/ _/_/_/ _/_/_/ _/_/_/ \n" + " _/ _/ _/ _/ _/ \n" + " _/_/_/ _/ _/_/ _/ \n" + " _/ _/ _/ _/ _/ \n" + "_/ _/ _/_/_/ _/_/_/ _/_/_/ \n" + " \n" + " \n" + " _/_/ _/_/ \n" + " _/ _/ _/ _/_/_/_/ _/_/_/ _/_/_/_/_/ _/_/ _/_/_/ _/ \n" + " _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ \n" + "_/ _/ _/ _/_/_/ _/ _/ _/ _/ _/_/_/ _/ \n" + " _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ \n" + " _/ _/ _/_/_/_/ _/_/_/ _/ _/_/ _/ _/ _/ \n" + " _/_/ _/_/ "; +static void print_version_number() { std::cout << banner << version_number << '\n'; } -void err() +static void err() { std::cerr << "Usage:\n\trisc_vector [OPTIONS]\nOptions:\n\t--debug,\t-d: " - "turn on verbose output\n\t--no-python,\t-p: run without GUI\n" + "turn on verbose output\n\t--no-python,\t-p: run without " + "GUI\n\t--version,\t-v: print the version information and exit\n" << std::endl; } -void parseArguments(int argc, char **argv, Logger &logger, bool &python) +static void parseArguments(int argc, char **argv, bool &python) { struct option long_options[] = { {"debug", no_argument, 0, 'd'}, @@ -26,11 +46,11 @@ void parseArguments(int argc, char **argv, Logger &logger, bool &python) while ((opt = getopt_long(argc, argv, "d:p", long_options, NULL)) != -1) { switch (opt) { case 'd': - logger.setLevel(DEBUG); - logger.log(DEBUG, "DEBUG output enabled."); + global_log->setLevel(DEBUG); + global_log->log(DEBUG, "DEBUG output enabled."); break; case 'p': - logger.log(INFO, "Python will NOT be started!"); + global_log->log(INFO, "Python will NOT be started!"); python = false; break; default: @@ -42,19 +62,22 @@ void parseArguments(int argc, char **argv, Logger &logger, bool &python) int main(int argc, char **argv) { - Logger logger("vector.log"); - Cli cli; - logger.log(INFO, "Initializing..."); + print_version_number(); + Cli cli; + global_log->log(INFO, "Initializing..."); bool python = true; - parseArguments(argc, argv, logger, python); + parseArguments(argc, argv, python); if (python) { // fork off python here ; - logger.log(INFO, "Python started."); + global_log->log(INFO, "Python started."); } - cli.run(); + cli.run(); + + global_log->log(INFO, "Cleaning up..."); + global_log->log(INFO, "Goodbye!"); return EXIT_SUCCESS; -}
\ No newline at end of file +} diff --git a/src/storage/cache.cc b/src/storage/cache.cc index 2031367..1a8a10b 100644 --- a/src/storage/cache.cc +++ b/src/storage/cache.cc @@ -3,6 +3,9 @@ #include "response.h" #include "utils.h" #include <bits/stdc++.h> +#include <bitset> +#include <iostream> +#include <iterator> Cache::Cache(Storage *lower, int delay) { @@ -16,7 +19,11 @@ Cache::Cache(Storage *lower, int delay) this->wait_time = this->delay; } -Cache::~Cache() { delete this->data; } +Cache::~Cache() +{ + delete this->lower; + delete this->data; +} Response Cache::write(Accessor accessor, signed int data, int address) { @@ -76,3 +83,41 @@ void Cache::fetch_resource(int expected) this->is_waiting = (r == OK) ? false : true; } + +std::array<std::array<int, 2>, L1_CACHE_SIZE> Cache::get_meta() const +{ + std::array<std::array<int, 2>, L1_CACHE_SIZE> 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_SIZE); + std::array<std::array<int, 2>, L1_CACHE_SIZE> meta = c.get_meta(); + + os << " " << std::setfill(' ') << std::setw(L1_CACHE_SPEC + 2) << "INDEX" + << " | " << std::setfill(' ') << std::setw((8 + 3) * 4 - 1) << "DATA" + << " | " << std::setfill(' ') + << std::setw(MEM_SPEC - LINE_SPEC - L1_CACHE_SPEC + 2) << "TAG" + << " | D" << std::endl; + for (int i = 0; i < L1_CACHE_SIZE; ++i) { + os << " 0b" << std::setw(L1_CACHE_SPEC) << std::bitset<L1_CACHE_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 << "| 0x" << std::setfill(' ') + << std::bitset<MEM_SPEC - LINE_SPEC - L1_CACHE_SPEC>(meta.at(i)[0]) + << " | " << (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 index 441f10b..e755c2a 100644 --- a/src/storage/dram.cc +++ b/src/storage/dram.cc @@ -2,6 +2,10 @@ #include "definitions.h" #include "response.h" #include <algorithm> +#include <bits/stdc++.h> +#include <bitset> +#include <iostream> +#include <iterator> Dram::Dram(int lines, int delay) { @@ -70,3 +74,27 @@ Response Dram::read( 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_SIZE); + + os << " " << std::setfill(' ') << std::setw(MEM_SPEC + 2) << "INDEX" + << " | " << std::setfill(' ') << std::setw((8 + 3) * 4 - 1) << "DATA" + << std::endl; + for (int i = 0; i < MEM_SIZE; ++i) { + os << " 0b" << std::setw(MEM_SPEC) << std::bitset<MEM_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 index 61531d1..8e2e461 100644 --- a/src/storage/storage.cc +++ b/src/storage/storage.cc @@ -3,7 +3,7 @@ #include <algorithm> std::vector<std::array<signed int, LINE_SIZE>> -Storage::view(int base, int lines) +Storage::view(int base, int lines) const { base = (base / LINE_SIZE) * LINE_SIZE; std::vector<std::array<signed int, LINE_SIZE>> ret(lines + 1); @@ -13,8 +13,12 @@ Storage::view(int base, int lines) return ret; } +Storage *Storage::get_lower() { return this->lower; } + void Storage::resolve() { + if (this->lower) + this->lower->resolve(); if (this->wait_time == 0) { this->requester = IDLE; this->wait_time = delay; diff --git a/src/utils/accessor.cc b/src/utils/accessor.cc new file mode 100644 index 0000000..86484c5 --- /dev/null +++ b/src/utils/accessor.cc @@ -0,0 +1,8 @@ +#include "accessor.h" +#include <iostream> + +std::ostream &operator<<(std::ostream &os, Accessor a) +{ + const std::string nameA[] = {"IDLE", "MEM", "FETCH", "L1CACHE", "SIDE"}; + return os << nameA[a]; +} diff --git a/src/utils/response.cc b/src/utils/response.cc new file mode 100644 index 0000000..def6578 --- /dev/null +++ b/src/utils/response.cc @@ -0,0 +1,8 @@ +#include "response.h" +#include <iostream> + +std::ostream &operator<<(std::ostream &os, Response r) +{ + const std::string nameR[] = {"OK", "WAIT", "BLOCKED"}; + return os << nameR[r]; +} diff --git a/src/utils/utils.cc b/src/utils/utils.cc index dfeb2b3..5de8e89 100644 --- a/src/utils/utils.cc +++ b/src/utils/utils.cc @@ -1,11 +1,30 @@ #include "utils.h" #include "definitions.h" +#include <cstdarg> +#include <string> +#include <vector> void get_bit_fields(int address, int *tag, int *index, int *offset) { - *tag = - GET_MID_BITS(address, LINE_SPEC + L1_CACHE_SPEC, - MEM_SPEC + LINE_SPEC + L1_CACHE_SPEC); + *tag = GET_MID_BITS( + address, LINE_SPEC + L1_CACHE_SPEC, + MEM_SPEC + LINE_SPEC + L1_CACHE_SPEC); *index = GET_MID_BITS(address, LINE_SPEC, L1_CACHE_SPEC + LINE_SPEC); *offset = GET_LS_BITS(address, LINE_SPEC); } + +const std::string string_format(const char *const zcFormat, ...) +{ + va_list vaArgs; + va_start(vaArgs, zcFormat); + + va_list vaArgsCopy; + va_copy(vaArgsCopy, vaArgs); + const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy); + va_end(vaArgsCopy); + + std::vector<char> zc(iLen + 1); + std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs); + va_end(vaArgs); + return std::string(zc.data(), iLen); +} diff --git a/tests/cache.cc b/tests/cache.cc index 8d1e806..e8a257f 100644 --- a/tests/cache.cc +++ b/tests/cache.cc @@ -27,7 +27,6 @@ TEST_CASE("no delay stores instantly", "[cache]") r = c->write(MEM, w, 0b0); CHECK(r == OK); - d->resolve(); c->resolve(); actual = d->view(0, 1)[0]; @@ -38,7 +37,6 @@ TEST_CASE("no delay stores instantly", "[cache]") actual = c->view(0, 1)[0]; REQUIRE(expected == actual); - delete d; delete c; } @@ -62,12 +60,10 @@ TEST_CASE("cache takes \"forever\"", "[cache]") actual = c->view(0, 1)[0]; REQUIRE(expected == actual); c->resolve(); - d->resolve(); } r = c->write(MEM, w, 0b0); CHECK(r == OK); - d->resolve(); actual = d->view(0, 1)[0]; // we do NOT write back now! @@ -77,7 +73,6 @@ TEST_CASE("cache takes \"forever\"", "[cache]") actual = c->view(0, 1)[0]; REQUIRE(expected == actual); - delete d; delete c; } @@ -101,12 +96,10 @@ TEST_CASE("dram takes \"forever\"", "[cache]") actual = c->view(0, 1)[0]; REQUIRE(expected == actual); c->resolve(); - d->resolve(); } r = c->write(MEM, w, 0b0); CHECK(r == OK); - d->resolve(); actual = d->view(0, 1)[0]; // we do NOT write back now! @@ -116,7 +109,6 @@ TEST_CASE("dram takes \"forever\"", "[cache]") actual = c->view(0, 1)[0]; REQUIRE(expected == actual); - delete d; delete c; } @@ -140,7 +132,6 @@ TEST_CASE("dram and cache take \"forever\"", "[cache]") actual = c->view(0, 1)[0]; REQUIRE(expected == actual); c->resolve(); - d->resolve(); } for (i = 0; i < delay; ++i) { @@ -150,13 +141,11 @@ TEST_CASE("dram and cache take \"forever\"", "[cache]") actual = c->view(0, 1)[0]; REQUIRE(expected == actual); c->resolve(); - d->resolve(); } r = c->write(MEM, w, 0b0); CHECK(r == OK); c->resolve(); - d->resolve(); actual = d->view(0, 1)[0]; // we do NOT write back now! @@ -166,11 +155,11 @@ TEST_CASE("dram and cache take \"forever\"", "[cache]") actual = c->view(0, 1)[0]; REQUIRE(expected == actual); - delete d; delete c; } -TEST_CASE("dram takes \"forever\", two concurrent requests same index", "[cache]") +TEST_CASE( + "dram takes \"forever\", two concurrent requests same index", "[cache]") { int delay = 0; Dram *d = new Dram(MEM_SIZE, delay + 2); @@ -193,7 +182,6 @@ TEST_CASE("dram takes \"forever\", two concurrent requests same index", "[cache] actual = c->view(0, 1)[0]; REQUIRE(expected == actual); c->resolve(); - d->resolve(); } r = c->write(MEM, w, 0b0); @@ -202,7 +190,6 @@ TEST_CASE("dram takes \"forever\", two concurrent requests same index", "[cache] CHECK(r == WAIT); c->resolve(); - d->resolve(); actual = d->view(0, 1)[0]; // we do NOT write back now! @@ -217,17 +204,17 @@ TEST_CASE("dram takes \"forever\", two concurrent requests same index", "[cache] CHECK(r == OK); c->resolve(); - d->resolve(); expected.at(1) = w; actual = c->view(0, 1)[0]; REQUIRE(expected == actual); - delete d; delete c; } -TEST_CASE("dram takes \"forever\", two concurrent requests different index", "[cache]") +TEST_CASE( + "dram takes \"forever\", two concurrent requests different index", + "[cache]") { int delay = 0; Dram *d = new Dram(MEM_SIZE, delay + 2); @@ -250,7 +237,6 @@ TEST_CASE("dram takes \"forever\", two concurrent requests different index", "[c actual = c->view(0, 1)[0]; REQUIRE(expected == actual); c->resolve(); - d->resolve(); } r = c->write(MEM, w, 0b0); @@ -259,7 +245,6 @@ TEST_CASE("dram takes \"forever\", two concurrent requests different index", "[c CHECK(r == WAIT); c->resolve(); - d->resolve(); actual = d->view(0, 1)[0]; // we do NOT write back now! @@ -276,24 +261,22 @@ TEST_CASE("dram takes \"forever\", two concurrent requests different index", "[c actual = c->view(0, 1)[0]; REQUIRE(expected == actual); c->resolve(); - d->resolve(); } r = c->write(FETCH, w, 0b1); CHECK(r == OK); c->resolve(); - d->resolve(); expected.at(1) = w; actual = c->view(0, 1)[0]; REQUIRE(expected == actual); - delete d; delete c; } -TEST_CASE("dram takes \"forever\", two concurrent requests different tag", "[cache]") +TEST_CASE( + "dram takes \"forever\", two concurrent requests different tag", "[cache]") { // TODO } diff --git a/tests/logger.cc b/tests/logger.cc deleted file mode 100644 index 711dd0e..0000000 --- a/tests/logger.cc +++ /dev/null @@ -1,65 +0,0 @@ -#include "logger.h" -#include <catch2/catch_test_macros.hpp> -#include <iostream> -#include <regex> -#include <sstream> - -TEST_CASE("Logger logs higher log level", "[logger]") -{ - std::streambuf *coutBuffer = std::cout.rdbuf(); - std::ostringstream oss; - std::cout.rdbuf(oss.rdbuf()); - - Logger logger(""); - logger.setLevel(INFO); - - logger.log(ERROR, "foo bar baz qux"); - - std::cout.rdbuf(coutBuffer); - - std::string actual = oss.str(); - std::regex expected( - "\\[\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\] ERROR: " - "foo bar baz qux\\n"); - - REQUIRE(std::regex_match(actual, expected)); -} - -TEST_CASE("Logger logs equal log level", "[logger]") -{ - std::streambuf *coutBuffer = std::cout.rdbuf(); - std::ostringstream oss; - std::cout.rdbuf(oss.rdbuf()); - - Logger logger(""); - logger.setLevel(INFO); - - logger.log(INFO, "foo bar baz qux"); - - std::cout.rdbuf(coutBuffer); - - std::string actual = oss.str(); - std::regex expected("\\[\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\] INFO: " - "foo bar baz qux\\n"); - - REQUIRE(std::regex_match(actual, expected)); -} - -TEST_CASE("Logger ignores lower log level", "[logger]") -{ - std::streambuf *coutBuffer = std::cout.rdbuf(); - std::ostringstream oss; - std::cout.rdbuf(oss.rdbuf()); - - Logger logger(""); - logger.setLevel(INFO); - - logger.log(DEBUG, "foo bar baz qux"); - - std::cout.rdbuf(coutBuffer); - - std::string actual = oss.str(); - std::string expected(""); - - REQUIRE(actual == expected); -} |