diff --git a/Makefile b/Makefile index 3c9a4ed..8e00265 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ PDataProcess.scala: gen_plugin data_Zpn.txt ./gen_plugin -n PDataProcess -i data_Zpn.txt -I Zpn >| $@ PSlowDataProcess.scala: gen_plugin data_Zpn.txt - ./gen_plugin -n PSlowDataProcess -i data_Zpn.txt -I Zpnslow >| $@ + ./gen_plugin -n PSlowDataProcess -i data_Zpn_2cycles.txt -I Zpn >| $@ P64DataProcess.scala: gen_plugin data_Zp64.txt ./gen_plugin -w -n P64DataProcess -i data_Zp64.txt -I '*' >| $@ diff --git a/data_Zpn_2cycles.txt b/data_Zpn_2cycles.txt new file mode 100644 index 0000000..98196d5 --- /dev/null +++ b/data_Zpn_2cycles.txt @@ -0,0 +1,46 @@ + +I SMAQA SMAQA 1100100----------000----01110111 pdpiumul8 Zpn +I UMAQA UMAQA 1100110----------000----01110111 pdpismul8 Zpn + + +S SMAQA "fun_smaqa1(input(SRC1), input(SRC2), input(SRC3))" +S UMAQA "fun_umaqa1(input(SRC1), input(SRC2), input(SRC3))" +T SMAQA 96 "fun_smaqa2" +T UMAQA 96 "fun_umaqa2" + +P """ + def fun_smaqa1(rs1: Bits, rs2: Bits, rs3: Bits) : Bits = { + // 18 bits needed so that intermediate sums don't overflow + val h0 = (rs1( 7 downto 0).asSInt * rs2( 7 downto 0).asSInt) + val h1 = (rs1(15 downto 8).asSInt * rs2(15 downto 8).asSInt) + val h2 = (rs1(23 downto 16).asSInt * rs2(23 downto 16).asSInt) + val h3 = (rs1(31 downto 24).asSInt * rs2(31 downto 24).asSInt) + rs3 ## h3 ## h2 ## h1 ## h0 // return value 96 bits + } + def fun_smaqa2(input:Bits ) : Bits = { + val r = input(95 downto 64).asSInt + ( + input(63 downto 48).asSInt.resize(18) + + input(47 downto 32).asSInt.resize(18) + + input(31 downto 16).asSInt.resize(18) + + input(15 downto 0).asSInt.resize(18)) + + r.asBits.resize(32) // return value + } + def fun_umaqa1(rs1: Bits, rs2: Bits, rs3: Bits) : Bits = { + // 18 bits needed so that intermediate sums don't overflow + val h0 = (rs1( 7 downto 0).asUInt * rs2( 7 downto 0).asUInt) + val h1 = (rs1(15 downto 8).asUInt * rs2(15 downto 8).asUInt) + val h2 = (rs1(23 downto 16).asUInt * rs2(23 downto 16).asUInt) + val h3 = (rs1(31 downto 24).asUInt * rs2(31 downto 24).asUInt) + rs3 ## h3 ## h2 ## h1 ## h0 // return value 96 bits + } + def fun_umaqa2(input:Bits ) : Bits = { + val r = input(95 downto 64).asUInt + ( + input(63 downto 48).asUInt.resize(18) + + input(47 downto 32).asUInt.resize(18) + + input(31 downto 16).asUInt.resize(18) + + input(15 downto 0).asUInt.resize(18)) + + r.asBits.resize(32) // return value + } +""" diff --git a/gen_plugin.cpp b/gen_plugin.cpp index 11468c4..692e4e8 100644 --- a/gen_plugin.cpp +++ b/gen_plugin.cpp @@ -28,6 +28,8 @@ extern int yydebug; std::set instructions; std::map semantics; +std::map em_widths; +std::map mem_semantics; std::vector prologues; std::vector extras; @@ -77,7 +79,15 @@ void add_inst5(const char* name, const char* opname, const char* key, const char instructions.insert(i); } void add_sem(const char* name, const char* sem) { - semantics[std::string(name)] = std::string(sem); + const std::string key(name); + semantics[key] = std::string(sem); +} +void add_memsem(const char* name, const int em_width, const char* memsem) { + const std::string key(name); + if ((em_width > 0) && (memsem != NULL)) { + em_widths[key] = em_width; + mem_semantics[key] = std::string(memsem); + } } void add_prol(const char *prol) { prologues.push_back(std::string(prol)); @@ -155,7 +165,18 @@ int main(int argc, char **argv) { //printf("adding %s\n", inst->name.c_str()); } - unparse(std::cout, pluginName, filtered_instructions, semantics, prologues, extras, wide == 1); + if ((em_widths.size() != 0) || (mem_semantics.size() !=0)) { + if (em_widths.size() != mem_semantics.size()) { + std::cerr << "Multicycle error: emwidths.size() != mem_semantics.size()" << std::endl; + exit(-1); + } + if (semantics.size() != mem_semantics.size()) { + std::cerr << "Multicycle error: semantics.size() != mem_semantics.size() (all instructions should have the same nuber of cycles in the same plugin)" << std::endl; + exit(-1); + } + } + + unparse(std::cout, pluginName, filtered_instructions, semantics, em_widths, mem_semantics, prologues, extras, wide == 1); return 0; } diff --git a/gen_plugin.hpp b/gen_plugin.hpp index 0651564..7ee0e8b 100644 --- a/gen_plugin.hpp +++ b/gen_plugin.hpp @@ -17,6 +17,7 @@ extern "C" { void add_inst4(const char* name, const char* opname, const char* key, const char* group, const char* e1, const char* e2, const char *e3, const char *e4); void add_inst5(const char* name, const char* opname, const char* key, const char* group, const char* e1, const char* e2, const char *e3, const char *e4, const char *e5); void add_sem(const char* name, const char* sem); + void add_memsem(const char* name, const int em_width, const char* memsem); void add_prol(const char *prol); void add_extra(const char *extra); #ifdef __cplusplus diff --git a/inst_lex.l b/inst_lex.l index bd508a3..a0e00d5 100644 --- a/inst_lex.l +++ b/inst_lex.l @@ -5,35 +5,44 @@ * See the LICENSE file at the top level of this software distribution for details. */ #include +#include #include "inst_par.h" %} CHAR [[:alnum:] ,'&./()-] +FCHARNAME [[:alpha:]] CHARNAME [[:alnum:]_+-] SPACE [ \t] %% -^"I" { return INST; } +^"I" { return INST; } -^"S" { return SEM; } +^"S" { return SEM; } -^"P" { return PROL; } +^"T" { return MEMSEM; } -^"E" { return EXTRA; } +^"P" { return PROL; } -"//".* { } +^"E" { return EXTRA; } -{CHARNAME}{CHARNAME}{CHARNAME}* { yylval.string = strdup(yytext); return NAME; } +"//".* { } -"\"\"\""[^ù]*"\"\"\"" { yylval.string = strndup(yytext+3, strlen(yytext)-6); return STRING; } +{FCHARNAME}{CHARNAME}{CHARNAME}* { yylval.string = strdup(yytext); return NAME; } -"'''"[^ù]*"'''" { yylval.string = strndup(yytext+3, strlen(yytext)-6); return STRING; } +{CHARNAME}{32} { yylval.string = strdup(yytext); return NAME; } -"\"".*"\"" { yylval.string = strndup(yytext+1, strlen(yytext)-2); return STRING; } -\n { return yytext[0]; } +"\"\"\""[^ù]*"\"\"\"" { yylval.string = strndup(yytext+3, strlen(yytext)-6); return STRING; } -{SPACE}+ { } +"'''"[^ù]*"'''" { yylval.string = strndup(yytext+3, strlen(yytext)-6); return STRING; } + +"\"".*"\"" { yylval.string = strndup(yytext+1, strlen(yytext)-2); return STRING; } + +[0-9][0-9]* { yylval.num = atoi(yytext); return NUM; } + +\n { return yytext[0]; } + +{SPACE}+ { } %% diff --git a/inst_par.h b/inst_par.h index ba1d41a..0e152be 100644 --- a/inst_par.h +++ b/inst_par.h @@ -47,10 +47,12 @@ extern int yydebug; { NAME = 258, STRING = 259, - INST = 260, - SEM = 261, - PROL = 262, - EXTRA = 263 + NUM = 260, + INST = 261, + SEM = 262, + MEMSEM = 263, + PROL = 264, + EXTRA = 265 }; #endif @@ -61,9 +63,10 @@ union YYSTYPE { #line 14 "inst_par.y" /* yacc.c:1909 */ + int num; char* string; -#line 67 "inst_par.h" /* yacc.c:1909 */ +#line 70 "inst_par.h" /* yacc.c:1909 */ }; typedef union YYSTYPE YYSTYPE; diff --git a/inst_par.y b/inst_par.y index 1f543f9..a939c25 100644 --- a/inst_par.y +++ b/inst_par.y @@ -12,14 +12,17 @@ %union { + int num; char* string; } %token NAME %token STRING +%token NUM %token INST %token SEM +%token MEMSEM %token PROL %token EXTRA @@ -36,6 +39,7 @@ INST NAME NAME NAME NAME { /* printf("0 - %s\n", $2); */ | INST NAME NAME NAME NAME NAME NAME NAME NAME { /* printf("4 - %s\n", $2); */ add_inst4($2, $3, $4, $5, $6, $7, $8, $9); } | INST NAME NAME NAME NAME NAME NAME NAME NAME NAME { /* printf("5 - %s\n", $2); */ add_inst5($2, $3, $4, $5, $6, $7, $8, $9, $10); } | SEM NAME STRING { add_sem($2, $3); } +| MEMSEM NAME NUM STRING { add_memsem($2, $3, $4); } | PROL STRING { add_prol($2); } | EXTRA STRING { add_extra($2); } | '\n' diff --git a/unparse.cpp b/unparse.cpp index abfec30..19cd76a 100644 --- a/unparse.cpp +++ b/unparse.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "inst.hpp" #include "group.hpp" @@ -39,6 +40,8 @@ void unparse(std::ostream& output, const std::string prefix, const std::set instructions, std::map semantics, + std::map em_widths, + std::map mem_semantics, std::vector prologues, std::vector extras, bool wide) { @@ -48,6 +51,8 @@ void unparse(std::ostream& output, const std::string ctrlEnumString = prefix + "CtrlEnum"; const std::string outputString = prefix + "_FINAL_OUTPUT"; const std::string isString = "IS_" + prefix; + const bool two_cycles = em_widths.size() > 0; + const std::string bypassableExecuteString = two_cycles ? "False" : "Bool(earlyInjection)"; output << "// WARNING: this is auto-generated code!" << std::endl; output << "// See https://github.com/rdolbeau/VexRiscvBPluginGenerator/" << std::endl; @@ -104,10 +109,17 @@ void unparse(std::ostream& output, output << "} // object Plugin" << std::endl; // Plugin class - output << "class " << prefix << "Plugin(earlyInjection : Boolean = true) extends Plugin[VexRiscv] {" << std::endl; + output << "class " << prefix << "Plugin"; if (!two_cycles) output << "(earlyInjection : Boolean = true)"; output << " extends Plugin[VexRiscv] {" << std::endl; output << '\t' << "import " << prefix << "Plugin._" << std::endl; output << '\t' << "object " << isString << " extends Stageable(Bool)" << std::endl; output << '\t' << "object " << outputString << " extends Stageable(Bits(" << (wide ? 64 : 32) << " bits))" << std::endl; + + if (two_cycles) { + for (auto const& pair : em_widths) { + std::string regName = prefix + "_INTERMEDIATE_" + pair.first + "" + std::to_string(pair.second); + output << '\t' << "object " << regName << " extends Stageable(Bits(" << pair.second << " bits))" << std::endl; + } + } output << '\t' << "override def setup(pipeline: VexRiscv): Unit = {" << std::endl; output << '\t' << '\t' << "import pipeline.config._" << std::endl; @@ -118,7 +130,7 @@ void unparse(std::ostream& output, output << '\t' << '\t' << "\tSRC2_CTRL -> Src2CtrlEnum.IMI," << std::endl; output << '\t' << '\t' << "\tREGFILE_WRITE_VALID -> True," << std::endl; if (wide) output << '\t' << '\t' << "\tREGFILE_WRITE_VALID_ODD -> True," << std::endl; - output << '\t' << '\t' << "\tBYPASSABLE_EXECUTE_STAGE -> Bool(earlyInjection)," << std::endl; + output << '\t' << '\t' << "\tBYPASSABLE_EXECUTE_STAGE -> " << bypassableExecuteString << "," << std::endl; output << '\t' << '\t' << "\tBYPASSABLE_MEMORY_STAGE -> True," << std::endl; output << '\t' << '\t' << "\tRS1_USE -> True," << std::endl; output << '\t' << '\t' << "\t" << isString << " -> True" << std::endl; @@ -129,7 +141,7 @@ void unparse(std::ostream& output, output << '\t' << '\t' << "\tSRC2_CTRL -> Src2CtrlEnum.RS," << std::endl; output << '\t' << '\t' << "\tREGFILE_WRITE_VALID -> True," << std::endl; if (wide) output << '\t' << '\t' << "\tREGFILE_WRITE_VALID_ODD -> True," << std::endl; - output << '\t' << '\t' << "\tBYPASSABLE_EXECUTE_STAGE -> Bool(earlyInjection)," << std::endl; + output << '\t' << '\t' << "\tBYPASSABLE_EXECUTE_STAGE -> " << bypassableExecuteString << "," << std::endl; output << '\t' << '\t' << "\tBYPASSABLE_MEMORY_STAGE -> True," << std::endl; output << '\t' << '\t' << "\tRS1_USE -> True," << std::endl; output << '\t' << '\t' << "\tRS2_USE -> True," << std::endl; @@ -140,7 +152,7 @@ void unparse(std::ostream& output, output << '\t' << '\t' << "\tSRC1_CTRL -> Src1CtrlEnum.RS," << std::endl; output << '\t' << '\t' << "\tREGFILE_WRITE_VALID -> True," << std::endl; if (wide) output << '\t' << '\t' << "\tREGFILE_WRITE_VALID_ODD -> True," << std::endl; - output << '\t' << '\t' << "\tBYPASSABLE_EXECUTE_STAGE -> Bool(earlyInjection)," << std::endl; + output << '\t' << '\t' << "\tBYPASSABLE_EXECUTE_STAGE -> " << bypassableExecuteString << "," << std::endl; output << '\t' << '\t' << "\tBYPASSABLE_MEMORY_STAGE -> True," << std::endl; output << '\t' << '\t' << "\tRS1_USE -> True," << std::endl; output << '\t' << '\t' << "\t" << isString << " -> True" << std::endl; @@ -152,7 +164,7 @@ void unparse(std::ostream& output, output << '\t' << '\t' << "\tSRC3_CTRL -> Src3CtrlEnum.RS," << std::endl; output << '\t' << '\t' << "\tREGFILE_WRITE_VALID -> True," << std::endl; if (wide) output << '\t' << '\t' << "\tREGFILE_WRITE_VALID_ODD -> True," << std::endl; - output << '\t' << '\t' << "\tBYPASSABLE_EXECUTE_STAGE -> Bool(earlyInjection)," << std::endl; + output << '\t' << '\t' << "\tBYPASSABLE_EXECUTE_STAGE -> " << bypassableExecuteString << "," << std::endl; output << '\t' << '\t' << "\tBYPASSABLE_MEMORY_STAGE -> True," << std::endl; output << '\t' << '\t' << "\tRS1_USE -> True," << std::endl; output << '\t' << '\t' << "\tRS2_USE -> True," << std::endl; @@ -166,7 +178,7 @@ void unparse(std::ostream& output, output << '\t' << '\t' << "\tSRC3_CTRL -> Src3CtrlEnum.RS," << std::endl; output << '\t' << '\t' << "\tREGFILE_WRITE_VALID -> True," << std::endl; if (wide) output << '\t' << '\t' << "\tREGFILE_WRITE_VALID_ODD -> True," << std::endl; - output << '\t' << '\t' << "\tBYPASSABLE_EXECUTE_STAGE -> Bool(earlyInjection)," << std::endl; + output << '\t' << '\t' << "\tBYPASSABLE_EXECUTE_STAGE -> " << bypassableExecuteString << "," << std::endl; output << '\t' << '\t' << "\tBYPASSABLE_MEMORY_STAGE -> True," << std::endl; output << '\t' << '\t' << "\tRS1_USE -> True," << std::endl; output << '\t' << '\t' << "\tRS3_USE -> True," << std::endl; @@ -228,7 +240,8 @@ void unparse(std::ostream& output, output << extra << std::endl; output << "// End Extra" << std::endl; } - + + if (!two_cycles) { output << '\t' << '\t' << "execute plug new Area{" << std::endl; output << '\t' << '\t' << '\t' << "import execute._" << std::endl; @@ -276,8 +289,73 @@ void unparse(std::ostream& output, output << '\t' << '\t' << '\t' << '\t' << "output(REGFILE_WRITE_DATA) := input(" << outputString << ")" << std::endl; } output << '\t' << '\t' << '\t' << "} // when input is" << std::endl; - output << '\t' << '\t' << "} // injectionStage plug newArea" << std::endl; + } else { // two-cycles + output << '\t' << '\t' << "execute plug new Area{" << std::endl; + output << '\t' << '\t' << '\t' << "import execute._" << std::endl; + for (auto const& pair : em_widths) { + std::string regName = prefix + "_INTERMEDIATE_" + pair.first + "" + std::to_string(pair.second); + output << '\t' << '\t' << '\t' << "insert(" << regName << ") := " << semantics[pair.first] << ".asBits" << std::endl; + } + output << '\t' << '\t' << "} // execute plug newArea" << std::endl; + output << '\t' << '\t' << "memory plug new Area{" << std::endl; + output << '\t' << '\t' << '\t' << "import memory._" << std::endl; + // 2nd level MUXes + for (const group* g : *groups) { + if (g->opnames.size() > 1) { + output << '\t' << '\t' << '\t' << "val val_" << g->name << " = input("<< ctrlString << g->name << ").mux(" << std::endl; + for (auto it = g->opnames.begin() ; it != g->opnames.end() ; it++) { + std::string opname = *it; + std::string semantic = semantics[opname]; + std::string regName = prefix + "_INTERMEDIATE_" + opname + "" + std::to_string(em_widths[opname]); + output << '\t' << '\t' << '\t' << '\t' << ctrlString << g->name << "Enum.CTRL_" << opname << " -> " << mem_semantics[opname] << "(input(" << regName << ")).asBits"; + if (std::next(it, 1) == g->opnames.end()) + output << std::endl; + else + output << "," << std::endl; + } + output << '\t' << '\t' << '\t' << ") // mux " << g->name << std::endl; + } + } + // conditional last level mux + output << '\t' << '\t' << '\t' << "when (arbitration.isValid && input(" << isString << ")) {" << std::endl; + output << '\t' << '\t' << '\t' << '\t' << "output(REGFILE_WRITE_DATA) := input(" << ctrlString << ").mux(" << std::endl; + for (auto it = groups->begin() ; it != groups->end() ; it++) { + group* g = *it; + if (g->opnames.size() > 1) { + output << '\t' << '\t' << '\t' << '\t' <<'\t' << ctrlEnumString << "." << g->ctrlName() << " -> val_" << g->name << ".asBits"; + } else { + std::string opname = *g->opnames.begin(); + std::string regName = prefix + "_INTERMEDIATE_" + opname + "" + std::to_string(em_widths[opname]); + output << '\t' << '\t' << '\t' << '\t' << '\t' << ctrlEnumString << ".CTRL_" << opname << " -> " << mem_semantics[opname] << "(input(" << regName << ")).asBits(31 downto 0)"; + } + if (std::next(it, 1) == groups->end()) + output << std::endl; + else + output << "," << std::endl; + } + output << '\t' << '\t' << '\t' << '\t' << ") // primary mux" << std::endl; + if (wide) { + output << '\t' << '\t' << '\t' << '\t' << "output(REGFILE_WRITE_DATA_ODD) := input(" << ctrlString << ").mux(" << std::endl; + for (auto it = groups->begin() ; it != groups->end() ; it++) { + group* g = *it; + if (g->opnames.size() > 1) { + output << '\t' << '\t' << '\t' << '\t' <<'\t' << ctrlEnumString << "." << g->ctrlName() << " -> val_" << g->name << ".asBits"; + } else { + std::string opname = *g->opnames.begin(); + std::string regName = prefix + "_INTERMEDIATE_" + opname + "" + std::to_string(em_widths[opname]); + output << '\t' << '\t' << '\t' << '\t' << '\t' << ctrlEnumString << ".CTRL_" << opname << " -> " << mem_semantics[opname] << "(input(" << regName << ")).asBits(63 downto 32)"; + } + if (std::next(it, 1) == groups->end()) + output << std::endl; + else + output << "," << std::endl; + } + } + output << '\t' << '\t' << '\t' << '\t' << ") // primary mux" << std::endl; + output << '\t' << '\t' << '\t' << "} // when input is" << std::endl; + output << '\t' << '\t' << "} // memory plug newArea" << std::endl; + } output << '\t' << "} // override def build" << std::endl; output << "} // class Plugin" << std::endl; } diff --git a/unparse.hpp b/unparse.hpp index f10ba32..cfb9e56 100644 --- a/unparse.hpp +++ b/unparse.hpp @@ -13,6 +13,8 @@ void unparse(std::ostream& output, const std::string prefix, const std::set instructions, std::map semantics, + std::map em_widths, + std::map mem_semantics, std::vector prologues, std::vector extras, bool wide);