summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSiddarth Suresh <155843085+SiddarthSuresh98@users.noreply.github.com>2025-03-11 11:28:25 -0400
committerGitHub <noreply@github.com>2025-03-11 11:28:25 -0400
commit33c7c78b1c65c375d0291fd435e02ddc9d35681b (patch)
tree25646d98b4bfcf4b9a664eabfc2651c481984c1d
parent66edce63597093cf5f3afa5b577fd9e3ecae0ef6 (diff)
parent202f9a05d449ddc1160584c4e8a87f397f248e94 (diff)
Merge pull request #23 from bdunahu/bdunahu
Memory simulator CLI function implementation
-rw-r--r--CMakeLists.txt3
-rw-r--r--inc/accessor.h15
-rw-r--r--inc/cache.h10
-rw-r--r--inc/cli.h172
-rw-r--r--inc/definitions.h19
-rw-r--r--inc/dram.h16
-rw-r--r--inc/logger.h26
-rw-r--r--inc/response.h3
-rw-r--r--inc/storage.h24
-rw-r--r--inc/utils.h9
-rw-r--r--src/cli/cli.cc362
-rw-r--r--src/logger/logger.cc22
-rw-r--r--src/main.cc51
-rw-r--r--src/storage/cache.cc47
-rw-r--r--src/storage/dram.cc28
-rw-r--r--src/storage/storage.cc6
-rw-r--r--src/utils/accessor.cc8
-rw-r--r--src/utils/response.cc8
-rw-r--r--src/utils/utils.cc25
-rw-r--r--tests/cache.cc31
-rw-r--r--tests/logger.cc65
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 */
diff --git a/inc/cli.h b/inc/cli.h
index 69bd3a9..a0c698a 100644
--- a/inc/cli.h
+++ b/inc/cli.h
@@ -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
diff --git a/inc/dram.h b/inc/dram.h
index 20221b7..2d4088f 100644
--- a/inc/dram.h
+++ b/inc/dram.h
@@ -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);
-}