lab5: add assembler and dynamic pipeline system emulator
This commit is contained in:
@@ -26,6 +26,23 @@ target_include_directories(syntaxer PRIVATE
|
|||||||
${GLOBAL_INC}
|
${GLOBAL_INC}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_library(dyn-pipeline STATIC)
|
||||||
|
target_sources(dyn-pipeline PRIVATE
|
||||||
|
src/dyn-pipeline.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(dyn-pipeline PRIVATE
|
||||||
|
${GLOBAL_INC}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(assembler STATIC)
|
||||||
|
target_sources(assembler PRIVATE
|
||||||
|
src/assembler.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(assembler PRIVATE
|
||||||
|
${GLOBAL_INC}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
add_executable(tester)
|
add_executable(tester)
|
||||||
target_sources(tester PRIVATE
|
target_sources(tester PRIVATE
|
||||||
src/test.cpp
|
src/test.cpp
|
||||||
@@ -51,4 +68,6 @@ target_include_directories(main PRIVATE
|
|||||||
target_link_libraries(main PRIVATE
|
target_link_libraries(main PRIVATE
|
||||||
parser
|
parser
|
||||||
syntaxer
|
syntaxer
|
||||||
|
assembler
|
||||||
|
dyn-pipeline
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define DEBUG 1
|
#define PRINT_DEBUG 1
|
||||||
|
#define PRINT_INFO 1
|
||||||
#define PRINT_DISABLE_PREFIXES 1
|
#define PRINT_DISABLE_PREFIXES 1
|
||||||
|
|
||||||
#define MAX_TOKEN_STRING_LEN 256
|
#define MAX_TOKEN_STRING_LEN 256
|
||||||
|
|
||||||
#define LAB2_EXCLUDE_COMPTIME_OP_WRITES 1
|
#define LAB2_EXCLUDE_COMPTIME_OP_WRITES 1
|
||||||
#define LAB2_PREVENT_NON_FOLDED_DIV_BY_ZERO 1
|
#define LAB2_PREVENT_NON_FOLDED_DIV_BY_ZERO 1
|
||||||
|
|
||||||
|
#define DYN_PIPELINE_MEM_SIZE 512
|
||||||
|
#define DYN_PIPELINE_LAYERS 3
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "expression.hpp"
|
||||||
|
#include "dyn-pipeline.hpp"
|
||||||
|
|
||||||
|
namespace Assembler {
|
||||||
|
|
||||||
|
void assemble(
|
||||||
|
const Expression::TreeNode& root,
|
||||||
|
std::vector<DynamicPipeline::Command>& program,
|
||||||
|
std::array<double, DYN_PIPELINE_MEM_SIZE>& memory,
|
||||||
|
int& mem_used);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
namespace DynamicPipeline {
|
||||||
|
|
||||||
|
enum CommandType : char
|
||||||
|
{
|
||||||
|
ADD,
|
||||||
|
SUB,
|
||||||
|
MUL,
|
||||||
|
DIV,
|
||||||
|
POW,
|
||||||
|
CMD_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Command
|
||||||
|
{
|
||||||
|
enum CommandType t;
|
||||||
|
uint16_t op1;
|
||||||
|
uint16_t op2;
|
||||||
|
uint16_t res;
|
||||||
|
|
||||||
|
Command(enum CommandType t, uint16_t op1, uint16_t op2, uint16_t res)
|
||||||
|
: t(t), op1(op1), op2(op2), res(res)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MemCell
|
||||||
|
{
|
||||||
|
double v;
|
||||||
|
int ready;
|
||||||
|
};
|
||||||
|
|
||||||
|
class System
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::vector<Command> *program_memory;
|
||||||
|
std::array<MemCell, DYN_PIPELINE_MEM_SIZE> data_memory;
|
||||||
|
std::array<int, CMD_COUNT> delays {2, 2, 5, 5, 9};
|
||||||
|
std::array<int, DYN_PIPELINE_LAYERS> layer_cmd {-1};
|
||||||
|
unsigned int pc {0};
|
||||||
|
unsigned int system_time;
|
||||||
|
|
||||||
|
void print_pipeline(void) const;
|
||||||
|
bool pipeline_empty(void) const;
|
||||||
|
bool next_instruction(void) const;
|
||||||
|
bool instruction_ready(void) const;
|
||||||
|
void step(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void init(const std::vector<Command> * const prg, const std::array<double, DYN_PIPELINE_MEM_SIZE>& mem, const int init_mem_size);
|
||||||
|
void run(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
+3
-2
@@ -24,7 +24,8 @@ public:
|
|||||||
SUB,
|
SUB,
|
||||||
MUL,
|
MUL,
|
||||||
DIV,
|
DIV,
|
||||||
POW
|
POW,
|
||||||
|
NOP // not an operator
|
||||||
};
|
};
|
||||||
|
|
||||||
union TokenValue {
|
union TokenValue {
|
||||||
@@ -358,7 +359,7 @@ public:
|
|||||||
// if value, left and right hands should be null
|
// if value, left and right hands should be null
|
||||||
|
|
||||||
explicit TreeNode(void)
|
explicit TreeNode(void)
|
||||||
: l(nullptr), r(nullptr), o()
|
: l(nullptr), r(nullptr), o(Token::NOP)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-1
@@ -1,9 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#if DEBUG == 1
|
#if PRINT_DEBUG == 1
|
||||||
#if PRINT_DISABLE_PREFIXES == 1
|
#if PRINT_DISABLE_PREFIXES == 1
|
||||||
#define pr_debug(...) std::cout << "debug: " << __VA_ARGS__ << "\n"
|
#define pr_debug(...) std::cout << "debug: " << __VA_ARGS__ << "\n"
|
||||||
#define pri_debug(...) std::cout << __VA_ARGS__
|
#define pri_debug(...) std::cout << __VA_ARGS__
|
||||||
@@ -19,6 +20,22 @@
|
|||||||
#define prs_debug(...) do {} while (0)
|
#define prs_debug(...) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if PRINT_INFO == 1
|
||||||
|
#if PRINT_DISABLE_PREFIXES == 1
|
||||||
|
#define pr_info(...) std::cout << "info: " << __VA_ARGS__ << "\n"
|
||||||
|
#define pri_info(...) std::cout << __VA_ARGS__
|
||||||
|
#define prs_info(...) std::cout << "info: " << __VA_ARGS__
|
||||||
|
#else
|
||||||
|
#define pr_info(...) std::cout << "[" << __FILE__ << ":" << __LINE__ << " " << __func__ << "] info: " << __VA_ARGS__ << "\n"
|
||||||
|
#define pri_info(...) std::cout << __VA_ARGS__
|
||||||
|
#define prs_info(...) std::cout << "[" << __FILE__ << ":" << __LINE__ << " " << __func__ << "] info: " << __VA_ARGS__
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define pr_info(...) do {} while (0)
|
||||||
|
#define pri_info(...) do {} while (0)
|
||||||
|
#define prs_info(...) do {} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if PRINT_DISABLE_PREFIXES == 1
|
#if PRINT_DISABLE_PREFIXES == 1
|
||||||
#define pr_warn(...) std::cout << "warning: " << __VA_ARGS__ << "\n"
|
#define pr_warn(...) std::cout << "warning: " << __VA_ARGS__ << "\n"
|
||||||
#else
|
#else
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ namespace Syntax
|
|||||||
|
|
||||||
int prepare_token_vector(std::vector<Expression::Token>& tv);
|
int prepare_token_vector(std::vector<Expression::Token>& tv);
|
||||||
std::vector<Expression::Operation> build_op_vector(const std::vector<Expression::Token>& tv);
|
std::vector<Expression::Operation> build_op_vector(const std::vector<Expression::Token>& tv);
|
||||||
Expression::TreeNode ov_to_tree(const std::vector<Expression::Token> tv, const std::vector<Expression::Operation>& ov);
|
Expression::TreeNode ov_to_tree(const std::vector<Expression::Token>& tv, const std::vector<Expression::Operation>& ov);
|
||||||
void print_op_tree(const std::vector<Expression::Token> tv, const std::vector<Expression::Operation>& ov);
|
void print_op_tree(const std::vector<Expression::Token>& tv, const std::vector<Expression::Operation>& ov);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
#include "assembler.hpp"
|
||||||
|
|
||||||
|
namespace Assembler {
|
||||||
|
|
||||||
|
// operand source
|
||||||
|
enum OpSrc
|
||||||
|
{
|
||||||
|
PRELOAD, // static memory
|
||||||
|
DYNAMIC // dynamic memory
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Command
|
||||||
|
{
|
||||||
|
DynamicPipeline::CommandType opcode;
|
||||||
|
OpSrc left_src;
|
||||||
|
int left_index;
|
||||||
|
OpSrc right_src;
|
||||||
|
int right_index;
|
||||||
|
|
||||||
|
Command(DynamicPipeline::CommandType opcode, OpSrc left_src, OpSrc right_src, int left_index, int right_index)
|
||||||
|
: opcode(opcode), left_src(left_src), left_index(left_index), right_src(right_src), right_index(right_index)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int descend_var(const Expression::TreeNode& node, int& preload_mem)
|
||||||
|
{
|
||||||
|
(void) node;
|
||||||
|
|
||||||
|
preload_mem++;
|
||||||
|
return preload_mem - 1; // this index in preload memory
|
||||||
|
}
|
||||||
|
|
||||||
|
int descend_operator(const Expression::TreeNode& node, int& preload_mem, std::vector<Command>& commands)
|
||||||
|
{
|
||||||
|
int left, right;
|
||||||
|
OpSrc lo, ro;
|
||||||
|
|
||||||
|
if (node.l->o.t == Expression::Token::OPERATOR)
|
||||||
|
{
|
||||||
|
left = descend_operator(*node.l, preload_mem, commands);
|
||||||
|
lo = DYNAMIC;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
left = descend_var(*node.l, preload_mem);
|
||||||
|
lo = PRELOAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.r->o.t == Expression::Token::OPERATOR)
|
||||||
|
{
|
||||||
|
right = descend_operator(*node.r, preload_mem, commands);
|
||||||
|
ro = DYNAMIC;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
right = descend_var(*node.r, preload_mem);
|
||||||
|
ro = PRELOAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.emplace_back((DynamicPipeline::CommandType) (node.o.t - 2), lo, ro, left, right);
|
||||||
|
|
||||||
|
return commands.size() - 1; // this index in dynamic memory
|
||||||
|
}
|
||||||
|
|
||||||
|
void assemble(
|
||||||
|
const Expression::TreeNode& root,
|
||||||
|
std::vector<DynamicPipeline::Command>& program,
|
||||||
|
std::array<double, DYN_PIPELINE_MEM_SIZE>& memory,
|
||||||
|
int& mem_used)
|
||||||
|
{
|
||||||
|
(void) memory;
|
||||||
|
|
||||||
|
int preload_mem = 0;
|
||||||
|
std::vector<Command> commands;
|
||||||
|
|
||||||
|
if (root.o.t == Expression::Token::OPERATOR)
|
||||||
|
{
|
||||||
|
(void) descend_operator(root, preload_mem, commands);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(void) descend_var(root, preload_mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
mem_used = preload_mem;
|
||||||
|
|
||||||
|
program.reserve(commands.size());
|
||||||
|
|
||||||
|
for (int i = 0, l = commands.size(); i < l; i++)
|
||||||
|
{
|
||||||
|
Command &cmd = commands[i];
|
||||||
|
program.emplace_back(cmd.opcode, cmd.left_src * preload_mem + cmd.left_index, cmd.right_src * preload_mem + cmd.right_index, preload_mem + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
#include "dyn-pipeline.hpp"
|
||||||
|
#include "print.h"
|
||||||
|
|
||||||
|
namespace DynamicPipeline {
|
||||||
|
|
||||||
|
void System::init(const std::vector<Command> * const prg, const std::array<double, DYN_PIPELINE_MEM_SIZE>& mem, const int init_mem_size)
|
||||||
|
{
|
||||||
|
pr_info("Dynamic Pipeline System initialized with:\n"
|
||||||
|
<< prg->size() << " commands in program memory\n"
|
||||||
|
<< init_mem_size << " values in data memory");
|
||||||
|
|
||||||
|
program_memory = const_cast<std::vector<Command> *>(prg);
|
||||||
|
|
||||||
|
for (int i = 0; i < init_mem_size; i++)
|
||||||
|
{
|
||||||
|
data_memory[i].v = mem[i];
|
||||||
|
data_memory[i].ready = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < DYN_PIPELINE_LAYERS; i++)
|
||||||
|
{
|
||||||
|
layer_cmd[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pc = 0;
|
||||||
|
system_time = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::print_pipeline(void) const
|
||||||
|
{
|
||||||
|
prs_info(" ");
|
||||||
|
|
||||||
|
for (int i = 0; i < DYN_PIPELINE_LAYERS - 1; i++)
|
||||||
|
{
|
||||||
|
if (layer_cmd[i] != -1)
|
||||||
|
{
|
||||||
|
pri_info(std::setw(3) << layer_cmd[i] << " | ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pri_info("IDL | ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer_cmd[DYN_PIPELINE_LAYERS - 1] != -1)
|
||||||
|
{
|
||||||
|
pri_info(std::setw(3) << layer_cmd[DYN_PIPELINE_LAYERS - 1] << "\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pri_info("IDL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool System::pipeline_empty(void) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < DYN_PIPELINE_LAYERS; i++)
|
||||||
|
{
|
||||||
|
if (layer_cmd[i] != -1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool System::next_instruction(void) const
|
||||||
|
{
|
||||||
|
return pc < program_memory->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool System::instruction_ready(void) const
|
||||||
|
{
|
||||||
|
if (!next_instruction())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Command &cmd = (*program_memory)[pc];
|
||||||
|
|
||||||
|
if (!data_memory[cmd.op1].ready)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data_memory[cmd.op2].ready)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::step(void)
|
||||||
|
{
|
||||||
|
pr_info("Step start time: " << system_time << " ticks");
|
||||||
|
|
||||||
|
// count time
|
||||||
|
int time_taken = 1;
|
||||||
|
for (int i = 0; i < DYN_PIPELINE_LAYERS; i++)
|
||||||
|
{
|
||||||
|
if (layer_cmd[i] == -1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const int d = delays[(*program_memory)[layer_cmd[i]].t]; d > time_taken)
|
||||||
|
{
|
||||||
|
time_taken = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
system_time += time_taken;
|
||||||
|
|
||||||
|
// handle output
|
||||||
|
if (int prg_mem_addr = layer_cmd[DYN_PIPELINE_LAYERS - 1]; prg_mem_addr != -1)
|
||||||
|
{
|
||||||
|
Command &cmd = (*program_memory)[prg_mem_addr];
|
||||||
|
pr_info("Instruction " << prg_mem_addr << " completes, writes output to " << cmd.res);
|
||||||
|
|
||||||
|
data_memory[cmd.res].ready = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// progress pipeline
|
||||||
|
for (int i = DYN_PIPELINE_LAYERS - 2; i >= 0; i--)
|
||||||
|
{
|
||||||
|
layer_cmd[i+1] = layer_cmd[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
layer_cmd[0] = -1;
|
||||||
|
|
||||||
|
// load new operation if ready
|
||||||
|
if (instruction_ready())
|
||||||
|
{
|
||||||
|
Command &cmd = (*program_memory)[pc];
|
||||||
|
pr_info("Instruction " << pc << " reads " << cmd.op1 << " and " << cmd.op2 << " and enters the pipeline");
|
||||||
|
|
||||||
|
layer_cmd[0] = pc;
|
||||||
|
|
||||||
|
pc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("Pipeline step took " << time_taken << " ticks");
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::run(void)
|
||||||
|
{
|
||||||
|
while (next_instruction() || !pipeline_empty())
|
||||||
|
{
|
||||||
|
print_pipeline();
|
||||||
|
pri_info("\n");
|
||||||
|
|
||||||
|
step();
|
||||||
|
pri_info("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("System finished in " << system_time << " ticks");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
|
#include "config.h"
|
||||||
|
|
||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
#include "syntaxer.hpp"
|
#include "syntaxer.hpp"
|
||||||
|
#include "assembler.hpp"
|
||||||
|
#include "dyn-pipeline.hpp"
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -18,4 +22,13 @@ int main(int argc, char **argv)
|
|||||||
std::vector<Expression::Operation> ov = Syntax::build_op_vector(tv);
|
std::vector<Expression::Operation> ov = Syntax::build_op_vector(tv);
|
||||||
|
|
||||||
Expression::TreeNode root = Syntax::ov_to_tree(tv, ov);
|
Expression::TreeNode root = Syntax::ov_to_tree(tv, ov);
|
||||||
|
|
||||||
|
std::vector<DynamicPipeline::Command> program;
|
||||||
|
std::array<double, DYN_PIPELINE_MEM_SIZE> memory;
|
||||||
|
int mem_used;
|
||||||
|
Assembler::assemble(root, program, memory, mem_used);
|
||||||
|
|
||||||
|
DynamicPipeline::System machine;
|
||||||
|
machine.init(&program, memory, mem_used);
|
||||||
|
machine.run();
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -1388,7 +1388,7 @@ finish:
|
|||||||
return ov;
|
return ov;
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression::TreeNode ov_to_tree(const std::vector<Expression::Token> tv, const std::vector<Expression::Operation>& ov)
|
Expression::TreeNode ov_to_tree(const std::vector<Expression::Token>& tv, const std::vector<Expression::Operation>& ov)
|
||||||
{
|
{
|
||||||
Expression::TreeNode root;
|
Expression::TreeNode root;
|
||||||
std::queue<std::pair<Expression::TreeNode *, int>> queued_ops;
|
std::queue<std::pair<Expression::TreeNode *, int>> queued_ops;
|
||||||
@@ -1434,6 +1434,8 @@ Expression::TreeNode ov_to_tree(const std::vector<Expression::Token> tv, const s
|
|||||||
|
|
||||||
void print_op_tree(const Expression::TreeNode& root)
|
void print_op_tree(const Expression::TreeNode& root)
|
||||||
{
|
{
|
||||||
|
(void) root;
|
||||||
|
|
||||||
std::vector<std::pair<Expression::TreeNode *, int>> items_to_display;
|
std::vector<std::pair<Expression::TreeNode *, int>> items_to_display;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user