summaryrefslogtreecommitdiff
path: root/src/main/resources/emulator_api.h
diff options
context:
space:
mode:
authorjackbackrack2015-04-27 16:25:59 -0700
committerjackbackrack2015-04-27 16:25:59 -0700
commit189f2a123300f0c2ffe4487bb67c71a08736bd3e (patch)
tree42f7d27fef7d5ea61ab13659b2743d332b2434fd /src/main/resources/emulator_api.h
parent8c8f6b99d2eb8bc783179c7c0113fc4a734c9585 (diff)
add headers
Diffstat (limited to 'src/main/resources/emulator_api.h')
-rw-r--r--src/main/resources/emulator_api.h684
1 files changed, 684 insertions, 0 deletions
diff --git a/src/main/resources/emulator_api.h b/src/main/resources/emulator_api.h
new file mode 100644
index 00000000..38b50963
--- /dev/null
+++ b/src/main/resources/emulator_api.h
@@ -0,0 +1,684 @@
+// Header for Chisel emulator API
+#ifndef __IS_EMULATOR_API__
+#define __IS_EMULATOR_API__
+
+#include "emulator_mod.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wunused-variable"
+
+#include <string>
+#include <sstream>
+#include <map>
+#include <cassert>
+#include <cerrno>
+
+/**
+ * Converts an integer to a std::string without needing additional libraries
+ * or C++11.
+ */
+static std::string itos(int in) {
+ std::stringstream out;
+ out << in;
+ return out.str();
+}
+
+/**
+ * Copy one val_t array to another.
+ * nb must be the exact number of bits the val_t represents.
+ */
+static __attribute__((unused)) void val_cpy(val_t* dst, val_t* src, int nb) {
+ for (int i=0; i<val_n_words(nb); i++) {
+ dst[i] = src[i];
+ }
+}
+
+/**
+ * Empty a val_t array (sets to zero).
+ * nb must be the exact number of bits the val_t represents.
+ */
+static void val_empty(val_t* dst, int nb) {
+ for (int i=0; i<val_n_words(nb); i++) {
+ dst[i] = 0;
+ }
+}
+
+/**
+ * Set a val_t array to a integer number. Obviously, the maximum integer
+ * is capped by the width of a single val_t element.
+ * nb must be the exact number of bits the val_t represents.
+ */
+static __attribute__((unused)) void val_set(val_t* dst, val_t nb, val_t num) {
+ val_empty(dst, nb);
+ dst[0] = num;
+}
+
+/**
+ * Sets a dat_t from a string, where the input radix is automatically determined
+ * from the string (or defaults to 10).
+ * Returns true on success.
+ */
+template <int w>
+bool dat_from_str(std::string in, dat_t<w>& res, int pos = 0) {
+ int radix = 10;
+ int negate = 0;
+
+ /* Handle leading minus sign. */
+ if (!in.substr(pos, 1).compare("-")) {
+ pos++;
+ negate = 1;
+ }
+
+ if (!in.substr(pos, 1).compare("d")) {
+ radix = 10;
+ pos++;
+ } else if (!in.substr(pos, 1).compare("h")
+ || !in.substr(pos, 1).compare("x")) {
+ radix = 16;
+ pos++;
+ } else if (!in.substr(pos, 2).compare("0h")
+ || !in.substr(pos, 2).compare("0x")) {
+ radix = 16;
+ pos += 2;
+ } else if (!in.substr(pos, 1).compare("b")) {
+ radix = 2;
+ pos++;
+ } else if (!in.substr(pos, 2).compare("0b")) {
+ radix = 2;
+ pos += 2;
+ }
+
+ const int log_max_radix = 4;
+ assert(radix <= (1 << log_max_radix));
+
+ dat_t<w> curr_base = 1;
+ res = 0;
+
+ for (int rpos=in.length()-1; rpos>=pos; rpos--) {
+ char c = in[rpos];
+ val_t c_val = 0;
+ if (c == '_') {
+ continue;
+ }
+ if (c >= '0' && c <= '9') {
+ c_val = c - '0';
+ } else if (c >= 'a' && c <= 'z') {
+ c_val = c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'Z') {
+ c_val = c - 'A' + 10;
+ } else {
+ std::cerr << "dat_from_str: Invalid character '" << c << "' in '" << in << "'" << std::endl;
+ return false;
+ }
+ if (c_val > radix /* || c_val < 0 */) {
+ std::cerr << "dat_from_str: Invalid character '" << c << "'" <<
+ std::endl;
+ return false;
+ }
+
+ dat_t<w> temp_prod = curr_base * dat_t<log_max_radix>(c_val);
+ res = res + temp_prod;
+ curr_base = curr_base * dat_t<log_max_radix+1>(radix);
+ }
+ if (negate) {
+ res = -res;
+ }
+ return true;
+}
+
+// API base class, providing common functions
+class api_base {
+public:
+ api_base(const char* new_name, const char* new_path) :
+ name(new_name),
+ path(new_path)
+ {}
+ // returns the fully qualified name of this object (path + dot + name)
+ std::string get_pathname() {
+ if (*path == '\0') {
+ return name;
+ } else {
+ return get_path() + "." + name;
+ }
+ }
+ // returns the short name of this object
+ std::string get_name() {
+ return name;
+ }
+ // returns the path of this object (without a trailing dot)
+ std::string get_path() {
+ return path;
+ }
+protected:
+ const char* name;
+ const char* path;
+};
+
+// API base (non width templated) class for API accessors to dat_t
+class dat_api_base : public api_base {
+public:
+ dat_api_base(const char* new_name, const char* new_path) :
+ api_base(new_name, new_path)
+ {}
+ // returns the value of this wire as a string, or empty string on failure
+ virtual std::string get_value() = 0;
+ // sets the value of this wire from a string, returning true on success
+ virtual bool set_value(std::string value) = 0;
+ // returns the bitwidth of this wire
+ virtual std::string get_width() = 0;
+};
+
+// dat_api dummy class, does nothing except for return errors
+// to be used when a real dat_api object can't be found
+class dat_dummy : public dat_api_base {
+public:
+ dat_dummy() :
+ dat_api_base("error", "")
+ {}
+ std::string get_value() {
+ return "error";
+ }
+
+ bool set_value(std::string value) {
+ return false;
+ }
+
+ std::string get_width() {
+ return "error";
+ }
+};
+
+template<int w> class dat_api : public dat_api_base {
+public:
+ dat_api(dat_t<w>* new_dat, const char* new_name, const char* new_path) :
+ dat_api_base(new_name, new_path),
+ dat_ptr(new_dat)
+ {}
+
+ std::string get_value() {
+ return dat_ptr->to_str();
+ }
+
+ bool set_value(std::string value) {
+ return dat_from_str<w>(value, *dat_ptr);
+ }
+
+ std::string get_width() {
+ return itos(w);
+ }
+
+protected:
+ dat_t<w>* dat_ptr;
+};
+
+// API base (non width/depth templated) class for API accessors to mem_t
+class mem_api_base : public api_base {
+public:
+ mem_api_base(const char* new_name, const char* new_path) :
+ api_base(new_name, new_path)
+ {}
+ // return the value of an element as a string, or empty string on failure
+ virtual std::string get_element(std::string index) = 0;
+ // sets the value of an element from a string, returning true on success
+ virtual bool set_element(std::string index, std::string value) = 0;
+ // returns the bitwidth of a memory element
+ virtual std::string get_width() = 0;
+ // returns the number of memory elements
+ virtual std::string get_depth() = 0;
+};
+
+// mem_api dummy class, does nothing except for return errors
+// to be used when a real mem_api object can't be found
+class mem_dummy : public mem_api_base {
+public:
+ mem_dummy() :
+ mem_api_base("error", "")
+ {}
+ string get_element(std::string index) {
+ return "error";
+ }
+
+ bool set_element(std::string index, std::string value) {
+ return false;
+ }
+
+ std::string get_width() {
+ return "error";
+ }
+
+ std::string get_depth() {
+ return "error";
+ }
+};
+
+template<int w, int d> class mem_api : public mem_api_base {
+public:
+ mem_api(mem_t<w, d>* new_mem, const char* new_name, const char* new_path) :
+ mem_api_base(new_name, new_path),
+ mem_ptr(new_mem)
+ {}
+
+ string get_element(std::string index) {
+ int index_int = atoi(index.c_str());
+ return mem_ptr->contents[index_int].to_str();
+ }
+
+ bool set_element(std::string index, std::string value) {
+ int index_int = atoi(index.c_str());
+ return dat_from_str<w>(value, mem_ptr->contents[index_int]);
+ }
+
+ std::string get_width() {
+ return itos(w);
+ }
+
+ std::string get_depth() {
+ return itos(d);
+ }
+
+protected:
+ mem_t<w, d>* mem_ptr;
+};
+
+class mod_api_t {
+public:
+ mod_api_t():
+ teefile(NULL)
+ {}
+
+ void init(mod_t* new_module) {
+ module = new_module;
+ init_mapping_table();
+ }
+
+ void set_teefile(FILE* new_teefile) {
+ teefile = new_teefile;
+ }
+
+ mod_t* get_module() {
+ return module;
+ }
+
+ // API basic functions
+ std::string get_host_name() {return "C++ Emulator API";}
+ std::string get_api_version() {return "0";}
+ std::string get_api_support() {return "PeekPoke Introspection";}
+
+ // External access functions & helpers
+ std::vector< std::string > tokenize(std::string str) {
+ std::vector< std::string > res;
+ int i = 0;
+ int c = ' ';
+ while ( i < str.size() ) {
+ while (isspace(c)) {
+ if (i >= str.size()) return res;
+ c = str[i++];
+ }
+ std::string s;
+ while (!isspace(c) && i < str.size()) {
+ s.push_back(c);
+ c = str[i++];
+ }
+ if (i >= str.size()) s.push_back(c);
+ if (s.size() > 0)
+ res.push_back(s);
+ }
+ return res;
+ }
+
+ // helper to verify command length, returning false and printing an error
+ // to stderr if the length isn't in the specified range
+ bool check_command_length(std::vector<std::string>& tokenized_command,
+ int min_args, int max_args=-1) {
+ if (tokenized_command.size() - 1 < min_args) {
+ std::cerr << tokenized_command[0] << " expects at least " << min_args
+ << " args, got " << tokenized_command.size() - 1
+ << std::endl;
+ return false;
+ } else if (max_args >= 0 && tokenized_command.size() - 1 > max_args) {
+ std::cerr << tokenized_command[0] << " expects at most " << max_args
+ << " args, got " << tokenized_command.size() - 1
+ << std::endl;
+ return false;
+ }
+ return true;
+ }
+
+ // Evaluates an API command, returning the reply as a string (without
+ // the trailing newline).
+ // Errors return "error", printing a more detailed description to stderr.
+ // TODO: find a way to pass errors in-line, so transport layers other than
+ // stdin/stdout (like TCP/IP) are possible while also communicating errors.
+ std::string eval_command(string command) {
+ std::vector<std::string> tokens = tokenize(command);
+ if (tokens.size() == 0) {
+ std::cerr << "Empty command: '" << command << "'" << std::endl;
+ return "error";
+ }
+ if (tokens[0] == "get_host_name") {
+ // IN: get_host_name
+ // OUT: API host's name (arbitrary string)
+ if (!check_command_length(tokens, 0, 0)) { return "error"; }
+ return get_host_name();
+ } else if (tokens[0] == "get_api_version") {
+ // BETA FUNCTION: semantics subject to change, use with caution
+ // IN: get_api_version
+ // OUT: API version supported by this host
+ if (!check_command_length(tokens, 0, 0)) { return "error"; }
+ return get_api_version();
+ } else if (tokens[0] == "get_api_support") {
+ // BETA FUNCTION: semantics subject to change, use with caution
+ // IN: get_api_support
+ // OUT: list of supported API features
+ if (!check_command_length(tokens, 0, 0)) { return "error"; }
+ return get_api_support();
+ } else if (tokens[0] == "clock") {
+ // BETA FUNCTION: semantics subject to change, use with caution
+ // IN: clock <num_cycles>
+ // OUT: actual number of cycles stepped
+ if (!check_command_length(tokens, 1, 1)) { return "error"; }
+ int cycles = atoi(tokens[1].c_str());
+ module->propagate_changes();
+ for (int i=0; i<cycles; i++) {
+ module->clock(dat_t<1>(0));
+ }
+ return itos(cycles);
+ } else if (tokens[0] == "tick") {
+ // BETA FUNCTION: semantics subject to change, use with caution
+ // IN: tick
+ // OUT: ok (on success)
+ // Update registers without propagation
+ // updating registers.
+ module->clock_hi(dat_t<1>(0));
+ return "ok";
+ } else if (tokens[0] == "propagate") {
+ // BETA FUNCTION: semantics subject to change, use with caution
+ // IN: propagate
+ // OUT: ok (on success)
+ // This function propagates the combinational logic, without
+ // updating registers.
+ module->propagate_changes();
+ return "ok";
+ } else if (tokens[0] == "step") {
+ // IN: step <num_cycles>
+ // OUT: actual number of cycles stepped
+ if (!check_command_length(tokens, 1, 1)) { return "error"; }
+ int n = atoi(tokens[1].c_str());
+ module->propagate_changes();
+ int ret = module->step(false, n);
+ // Do we have print output to report?
+ int nBytes = module->has_output();
+ if (nBytes > 0) {
+ cout << "PRINT" << " " << nBytes << " " << module->drain_output();
+ }
+ return itos(ret);
+ } else if (tokens[0] == "set_clocks") {
+ // BETA FUNCTION: semantics subject to change, use with caution
+ // IN: set_clocks
+ // OUT: ???
+ // I'm not really sure what this is supposed to do, but it was
+ // in the old command API, so it's here now
+ std::vector< int > periods;
+ for (int i = 1; i < tokens.size(); i++) {
+ int period = atoi(tokens[i].c_str());
+ periods.push_back(period);
+ }
+ module->setClocks(periods);
+ return "ok";
+
+ } else if (tokens[0] == "reset") {
+ // IN: reset <num_cycles>
+ // OUT: actual number of cycles in reset
+ if (!check_command_length(tokens, 0, 1)) { return "error"; }
+ int cycles = 1;
+ if (tokens.size() >= 2) {
+ cycles = atoi(tokens[1].c_str());
+ }
+ for (int i=0; i<cycles; i++) {
+ module->clock_lo(dat_t<1>(1));
+ module->clock_hi(dat_t<1>(1));
+ }
+ module->clock_lo(dat_t<1>(0));
+ return itos(cycles);
+ } else if (tokens[0] == "peek") {
+ // LEGACY FUNCTION: do not use in new code
+ // IN: peek <node_name> | peek <mem_name> <mem_index>
+ // OUT: value
+ if (!check_command_length(tokens, 1, 2)) { return "error"; }
+ cerr << "peek is deprecated, use wire_peek or mem_peek" << std::endl;
+ module->propagate_changes();
+ if (tokens.size() == 2) {
+ return get_dat_by_name(tokens[1])->get_value();
+ } else if (tokens.size() == 3) {
+ return get_mem_by_name(tokens[1])->get_element(tokens[2]);
+ }
+ } else if (tokens[0] == "poke") {
+ // LEGACY FUNCTION: do not use in new code
+ // IN: poke <node_name> <value> | poke <mem_name> <mem_index> <value>
+ // OUT: true (on success), false (on failure)
+ if (!check_command_length(tokens, 2, 3)) { return ""; }
+ cerr << "poke is deprecated, use wire_poke or mem_poke" << std::endl;
+ bool success;
+ if (tokens.size() == 3) {
+ success = get_dat_by_name(tokens[1])->set_value(tokens[2]);
+ } else if (tokens.size() == 4) {
+ success = get_mem_by_name(tokens[1])->set_element(tokens[2], tokens[3]);
+ }
+ std::string result;
+ if (success) {
+ result = "true";
+ module->mark_stale();
+ } else {
+ result = "false";
+ }
+ return result;
+ } else if (tokens[0] == "wire_peek") {
+ // IN: wire_peek <node_name>
+ // OUT: value
+ if (!check_command_length(tokens, 1, 1)) { return "error"; }
+ module->propagate_changes();
+ return get_dat_by_name(tokens[1])->get_value();
+ } else if (tokens[0] == "wire_poke") {
+ // IN: wire_poke <node_name> <value>
+ // OUT: ok (on success)
+ if (!check_command_length(tokens, 2, 2)) { return "error"; }
+ bool success = get_dat_by_name(tokens[1])->set_value(tokens[2]);
+ std::string result;
+ if (success) {
+ result = "ok";
+ module->mark_stale();
+ } else {
+ result = "error";
+ }
+ return result;
+ } else if (tokens[0] == "mem_peek") {
+ // IN: mem_peek <mem_name> <mem_index>
+ // OUT: value
+ if (!check_command_length(tokens, 2, 2)) { return "error"; }
+ module->propagate_changes();
+ return get_mem_by_name(tokens[1])->get_element(tokens[2]);
+ } else if (tokens[0] == "mem_poke") {
+ // IN: mem_poke <mem_name> <mem_index> <value>
+ // OUT: ok (on success)
+ if (!check_command_length(tokens, 3, 3)) { return "error"; }
+ bool success = get_mem_by_name(tokens[1])->set_element(tokens[2], tokens[3]);
+ std::string result;
+ if (success) {
+ result = "ok";
+ module->mark_stale();
+ } else {
+ result = "error";
+ }
+ return result;
+ return success ? "ok" : "error";
+ } else if (tokens[0] == "trace") {
+ // IN: trace n <node_name>+
+ // OUT: values
+ // TODO: ADD MEM PEEK SUPPORT
+ stringstream ss;
+ if (!check_command_length(tokens, 2)) { return "bad"; }
+ int n = atoi(tokens[1].c_str());
+ for (int t = 0; t < n; t++) {
+ for (int i = 2; i < tokens.size(); i++)
+ ss << " " << get_dat_by_name(tokens[i])->get_value();
+ int ret = module->step(false, 1);
+ // if (!ret)
+ // return "error";
+ }
+ return ss.str();
+ } else if (tokens[0] == "list_wires") {
+ // IN: list_wires
+ // OUT: list of wires
+ if (!check_command_length(tokens, 0, 0)) { return "error"; }
+ std::string out = "";
+ for (std::map<const char*, dat_api_base*>::iterator it = dat_table.begin(); it != dat_table.end(); it++) {
+ out = out + it->second->get_pathname() + " ";
+ }
+ if (out.size() >= 1) {
+ return out.substr(0, out.size() - 1);
+ } else {
+ return "";
+ }
+ } else if (tokens[0] == "list_mems") {
+ // IN: list_mems
+ // OUT: list of memories
+ if (!check_command_length(tokens, 0, 0)) { return "error"; }
+ std::string out = "";
+ for (std::map<const char*, mem_api_base*>::iterator it = mem_table.begin(); it != mem_table.end(); it++) {
+ out = out + it->second->get_pathname() + " ";
+ }
+ if (out.size() >= 1) {
+ return out.substr(0, out.size() - 1);
+ } else {
+ return "";
+ }
+ } else if (tokens[0] == "wire_width") {
+ // IN: wire_width <node>
+ // OUT: bitwidth of wire
+ if (!check_command_length(tokens, 1, 1)) { return "error"; }
+ return get_dat_by_name(tokens[1])->get_width();
+ } else if (tokens[0] == "mem_width") {
+ // IN: mem_width <node>
+ // OUT: bitwidth of memory element
+ if (!check_command_length(tokens, 1, 1)) { return "error"; }
+ return get_mem_by_name(tokens[1])->get_width();
+ } else if (tokens[0] == "mem_depth") {
+ // IN: mem_depth <node>
+ // OUT: elements in memory
+ if (!check_command_length(tokens, 1, 1)) { return "error"; }
+ return get_mem_by_name(tokens[1])->get_depth();
+ } else if (tokens[0] == "referenced_snapshot_save") {
+ // BETA FUNCTION: semantics subject to change, use with caution
+ // IN: referenced_snapshot_save <name>
+ // OUT: Reference name (an arbitrary string) for saved snapshot
+ // of current state, should be equivalent to the input.
+ // Caution: the state may not be self-consistent (i.e. clk_lo
+ // does not need to have been applied before this, and calls to
+ // clk_lo immediately after restoring may change the state).
+ if (!check_command_length(tokens, 1, 1)) { return "error"; }
+ module->propagate_changes();
+ mod_t *snapshot = module->clone();
+ snapshot_table[tokens[1]] = snapshot;
+ return tokens[1];
+ } else if (tokens[0] == "referenced_snapshot_restore") {
+ // BETA FUNCTION: semantics subject to change, use with caution
+ // IN: referenced_snapshot_restore <name>
+ // OUT: ok (on success)
+ if (!check_command_length(tokens, 1, 1)) { return "error"; }
+ mod_t *snapshot = get_snapshot_by_reference(tokens[1]);
+ if (snapshot == NULL) { return "error"; }
+ bool success = module->set_circuit_from(snapshot);
+ std::string result;
+ if (success) {
+ result = "ok";
+ module->mark_stale();
+ } else {
+ result = "error";
+ }
+ return result;
+ } else {
+ std::cerr << "Unknown command: '" << tokens[0] << "'" << std::endl;
+ }
+ return "error";
+ }
+
+ void read_eval_print_loop() {
+ while (true) {
+ std::string str_in;
+ do {
+ std::getline(cin, str_in);
+ } while (cin.fail() && errno == EINTR);
+
+ if (!cin.good()) {
+ break;
+ }
+
+ if (teefile != NULL) {
+ fprintf(teefile, "%s\n", str_in.c_str());
+ fflush(teefile);
+ }
+ if (str_in == "quit") {
+ break;
+ } else {
+ cout << eval_command(str_in) << std::endl;
+ }
+ }
+ }
+
+protected:
+ FILE* teefile;
+ mod_t* module;
+
+ // Mapping table functions
+ virtual void init_mapping_table() = 0;
+
+ dat_api_base* get_dat_by_name(std::string name) {
+ if (dat_table.find(name.c_str()) != dat_table.end()) {
+ return dat_table[name.c_str()];
+ } else {
+ std::cerr << "Unable to find dat '" << name << "'" << std::endl;
+ return &this_dat_dummy;
+ }
+ }
+ mem_api_base* get_mem_by_name(std::string name) {
+ if (mem_table.find(name.c_str()) != mem_table.end()) {
+ return mem_table[name.c_str()];
+ } else {
+ std::cerr << "Unable to find mem '" << name << "'" << std::endl;
+ return &this_mem_dummy;
+ }
+ }
+
+ mod_t* get_snapshot_by_reference(std::string name) {
+ if (snapshot_table.find(name) != snapshot_table.end()) {
+ return snapshot_table[name];
+ } else {
+ std::cerr << "Unable to find snapshot reference '" << name << "'" << std::endl;
+ return NULL;
+ }
+ }
+
+ class string_comparator {
+ public:
+ bool operator()(const char* x, const char* y) const {
+ return strcmp(x, y) < 0;
+ }
+ };
+
+ std::map<const char*, dat_api_base*, string_comparator> dat_table;
+ std::map<const char*, mem_api_base*, string_comparator> mem_table;
+ // TODO: replace the dummy with explicit NULL checks - this is simple
+ // but a bit inelegant
+ dat_dummy this_dat_dummy;
+ mem_dummy this_mem_dummy;
+
+ // Snapshot functions
+ std::map<std::string, mod_t*> snapshot_table;
+};
+
+#pragma GCC diagnostic pop
+
+#endif